diff --git a/Documentation/ABI/testing/sysfs-timecard b/Documentation/ABI/testing/sysfs-timecard index 97f6773794a5..220478156297 100644 --- a/Documentation/ABI/testing/sysfs-timecard +++ b/Documentation/ABI/testing/sysfs-timecard @@ -37,8 +37,15 @@ Description: (RO) Set of available destinations (sinks) for a SMA PPS2 signal is sent to the PPS2 selector TS1 signal is sent to timestamper 1 TS2 signal is sent to timestamper 2 + TS3 signal is sent to timestamper 3 + TS4 signal is sent to timestamper 4 IRIG signal is sent to the IRIG-B module DCF signal is sent to the DCF module + FREQ1 signal is sent to frequency counter 1 + FREQ2 signal is sent to frequency counter 2 + FREQ3 signal is sent to frequency counter 3 + FREQ4 signal is sent to frequency counter 4 + None signal input is disabled ===== ================================================ What: /sys/class/timecard/ocpN/available_sma_outputs @@ -50,10 +57,16 @@ Description: (RO) Set of available sources for a SMA output signal. 10Mhz output is from the 10Mhz reference clock PHC output PPS is from the PHC clock MAC output PPS is from the Miniature Atomic Clock - GNSS output PPS is from the GNSS module + GNSS1 output PPS is from the first GNSS module GNSS2 output PPS is from the second GNSS module IRIG output is from the PHC, in IRIG-B format DCF output is from the PHC, in DCF format + GEN1 output is from frequency generator 1 + GEN2 output is from frequency generator 2 + GEN3 output is from frequency generator 3 + GEN4 output is from frequency generator 4 + GND output is GND + VCC output is VCC ===== ================================================ What: /sys/class/timecard/ocpN/clock_source @@ -63,6 +76,97 @@ Description: (RW) Contains the current synchronization source used by the PHC. May be changed by writing one of the listed values from the available_clock_sources attribute set. +What: /sys/class/timecard/ocpN/clock_status_drift +Date: March 2022 +Contact: Jonathan Lemon +Description: (RO) Contains the current drift value used by the firmware + for internal disciplining of the atomic clock. + +What: /sys/class/timecard/ocpN/clock_status_offset +Date: March 2022 +Contact: Jonathan Lemon +Description: (RO) Contains the current offset value used by the firmware + for internal disciplining of the atomic clock. + +What: /sys/class/timecard/ocpN/freqX +Date: March 2022 +Contact: Jonathan Lemon +Description: (RO) Optional directory containing the sysfs nodes for + frequency counter . + +What: /sys/class/timecard/ocpN/freqX/frequency +Date: March 2022 +Contact: Jonathan Lemon +Description: (RO) Contains the measured frequency over the specified + measurement period. + +What: /sys/class/timecard/ocpN/freqX/seconds +Date: March 2022 +Contact: Jonathan Lemon +Description: (RW) Specifies the number of seconds from 0-255 that the + frequency should be measured over. Write 0 to disable. + +What: /sys/class/timecard/ocpN/genX +Date: March 2022 +Contact: Jonathan Lemon +Description: (RO) Optional directory containing the sysfs nodes for + frequency generator . + +What: /sys/class/timecard/ocpN/genX/duty +Date: March 2022 +Contact: Jonathan Lemon +Description: (RO) Specifies the signal duty cycle as a percentage from 1-99. + +What: /sys/class/timecard/ocpN/genX/period +Date: March 2022 +Contact: Jonathan Lemon +Description: (RO) Specifies the signal period in nanoseconds. + +What: /sys/class/timecard/ocpN/genX/phase +Date: March 2022 +Contact: Jonathan Lemon +Description: (RO) Specifies the signal phase offset in nanoseconds. + +What: /sys/class/timecard/ocpN/genX/polarity +Date: March 2022 +Contact: Jonathan Lemon +Description: (RO) Specifies the signal polarity, either 1 or 0. + +What: /sys/class/timecard/ocpN/genX/running +Date: March 2022 +Contact: Jonathan Lemon +Description: (RO) Either 0 or 1, showing if the signal generator is running. + +What: /sys/class/timecard/ocpN/genX/start +Date: March 2022 +Contact: Jonathan Lemon +Description: (RO) Shows the time in . that the signal generator + started running. + +What: /sys/class/timecard/ocpN/genX/signal +Date: March 2022 +Contact: Jonathan Lemon +Description: (RW) Used to start the signal generator, and summarize + the current status. + + The signal generator may be started by writing the signal + period, followed by the optional signal values. If the + optional values are not provided, they default to the current + settings, which may be obtained from the other sysfs nodes. + + period [duty [phase [polarity]]] + + echo 500000000 > signal # 1/2 second period + echo 1000000 40 100 > signal + echo 0 > signal # turn off generator + + Period and phase are specified in nanoseconds. Duty cycle is + a percentage from 1-99. Polarity is 1 or 0. + + Reading this node will return: + + period duty phase polarity start_time + What: /sys/class/timecard/ocpN/gnss_sync Date: September 2021 Contact: Jonathan Lemon @@ -126,6 +230,16 @@ Description: (RW) These attributes specify the direction of the signal The 10Mhz reference clock input is currently only valid on SMA1 and may not be combined with other destination sinks. +What: /sys/class/timecard/ocpN/tod_correction +Date: March 2022 +Contact: Jonathan Lemon +Description: (RW) The incoming GNSS signal is in UTC time, and the NMEA + format messages do not provide a TAI offset. This sets the + correction value for the incoming time. + + If UBX_LS is enabled, this should be 0, and the offset is + taken from the UBX-NAV-TIMELS message. + What: /sys/class/timecard/ocpN/ts_window_adjust Date: September 2021 Contact: Jonathan Lemon diff --git a/Documentation/admin-guide/sysctl/net.rst b/Documentation/admin-guide/sysctl/net.rst index 4150f74c521a..f86b5e1623c6 100644 --- a/Documentation/admin-guide/sysctl/net.rst +++ b/Documentation/admin-guide/sysctl/net.rst @@ -365,6 +365,15 @@ new netns has been created. Default : 0 (for compatibility reasons) +txrehash +-------- + +Controls default hash rethink behaviour on listening socket when SO_TXREHASH +option is set to SOCK_TXREHASH_DEFAULT (i. e. not overridden by setsockopt). + +If set to 1 (default), hash rethink is performed on listening socket. +If set to 0, hash rethink is not performed. + 2. /proc/sys/net/unix - Parameters for Unix domain sockets ---------------------------------------------------------- diff --git a/Documentation/bpf/bpf_prog_run.rst b/Documentation/bpf/bpf_prog_run.rst new file mode 100644 index 000000000000..4868c909df5c --- /dev/null +++ b/Documentation/bpf/bpf_prog_run.rst @@ -0,0 +1,117 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=================================== +Running BPF programs from userspace +=================================== + +This document describes the ``BPF_PROG_RUN`` facility for running BPF programs +from userspace. + +.. contents:: + :local: + :depth: 2 + + +Overview +-------- + +The ``BPF_PROG_RUN`` command can be used through the ``bpf()`` syscall to +execute a BPF program in the kernel and return the results to userspace. This +can be used to unit test BPF programs against user-supplied context objects, and +as way to explicitly execute programs in the kernel for their side effects. The +command was previously named ``BPF_PROG_TEST_RUN``, and both constants continue +to be defined in the UAPI header, aliased to the same value. + +The ``BPF_PROG_RUN`` command can be used to execute BPF programs of the +following types: + +- ``BPF_PROG_TYPE_SOCKET_FILTER`` +- ``BPF_PROG_TYPE_SCHED_CLS`` +- ``BPF_PROG_TYPE_SCHED_ACT`` +- ``BPF_PROG_TYPE_XDP`` +- ``BPF_PROG_TYPE_SK_LOOKUP`` +- ``BPF_PROG_TYPE_CGROUP_SKB`` +- ``BPF_PROG_TYPE_LWT_IN`` +- ``BPF_PROG_TYPE_LWT_OUT`` +- ``BPF_PROG_TYPE_LWT_XMIT`` +- ``BPF_PROG_TYPE_LWT_SEG6LOCAL`` +- ``BPF_PROG_TYPE_FLOW_DISSECTOR`` +- ``BPF_PROG_TYPE_STRUCT_OPS`` +- ``BPF_PROG_TYPE_RAW_TRACEPOINT`` +- ``BPF_PROG_TYPE_SYSCALL`` + +When using the ``BPF_PROG_RUN`` command, userspace supplies an input context +object and (for program types operating on network packets) a buffer containing +the packet data that the BPF program will operate on. The kernel will then +execute the program and return the results to userspace. Note that programs will +not have any side effects while being run in this mode; in particular, packets +will not actually be redirected or dropped, the program return code will just be +returned to userspace. A separate mode for live execution of XDP programs is +provided, documented separately below. + +Running XDP programs in "live frame mode" +----------------------------------------- + +The ``BPF_PROG_RUN`` command has a separate mode for running live XDP programs, +which can be used to execute XDP programs in a way where packets will actually +be processed by the kernel after the execution of the XDP program as if they +arrived on a physical interface. This mode is activated by setting the +``BPF_F_TEST_XDP_LIVE_FRAMES`` flag when supplying an XDP program to +``BPF_PROG_RUN``. + +The live packet mode is optimised for high performance execution of the supplied +XDP program many times (suitable for, e.g., running as a traffic generator), +which means the semantics are not quite as straight-forward as the regular test +run mode. Specifically: + +- When executing an XDP program in live frame mode, the result of the execution + will not be returned to userspace; instead, the kernel will perform the + operation indicated by the program's return code (drop the packet, redirect + it, etc). For this reason, setting the ``data_out`` or ``ctx_out`` attributes + in the syscall parameters when running in this mode will be rejected. In + addition, not all failures will be reported back to userspace directly; + specifically, only fatal errors in setup or during execution (like memory + allocation errors) will halt execution and return an error. If an error occurs + in packet processing, like a failure to redirect to a given interface, + execution will continue with the next repetition; these errors can be detected + via the same trace points as for regular XDP programs. + +- Userspace can supply an ifindex as part of the context object, just like in + the regular (non-live) mode. The XDP program will be executed as though the + packet arrived on this interface; i.e., the ``ingress_ifindex`` of the context + object will point to that interface. Furthermore, if the XDP program returns + ``XDP_PASS``, the packet will be injected into the kernel networking stack as + though it arrived on that ifindex, and if it returns ``XDP_TX``, the packet + will be transmitted *out* of that same interface. Do note, though, that + because the program execution is not happening in driver context, an + ``XDP_TX`` is actually turned into the same action as an ``XDP_REDIRECT`` to + that same interface (i.e., it will only work if the driver has support for the + ``ndo_xdp_xmit`` driver op). + +- When running the program with multiple repetitions, the execution will happen + in batches. The batch size defaults to 64 packets (which is same as the + maximum NAPI receive batch size), but can be specified by userspace through + the ``batch_size`` parameter, up to a maximum of 256 packets. For each batch, + the kernel executes the XDP program repeatedly, each invocation getting a + separate copy of the packet data. For each repetition, if the program drops + the packet, the data page is immediately recycled (see below). Otherwise, the + packet is buffered until the end of the batch, at which point all packets + buffered this way during the batch are transmitted at once. + +- When setting up the test run, the kernel will initialise a pool of memory + pages of the same size as the batch size. Each memory page will be initialised + with the initial packet data supplied by userspace at ``BPF_PROG_RUN`` + invocation. When possible, the pages will be recycled on future program + invocations, to improve performance. Pages will generally be recycled a full + batch at a time, except when a packet is dropped (by return code or because + of, say, a redirection error), in which case that page will be recycled + immediately. If a packet ends up being passed to the regular networking stack + (because the XDP program returns ``XDP_PASS``, or because it ends up being + redirected to an interface that injects it into the stack), the page will be + released and a new one will be allocated when the pool is empty. + + When recycling, the page content is not rewritten; only the packet boundary + pointers (``data``, ``data_end`` and ``data_meta``) in the context object will + be reset to the original values. This means that if a program rewrites the + packet contents, it has to be prepared to see either the original content or + the modified version on subsequent invocations. diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst index 1ebf4c5c7ddc..7940da9bc6c1 100644 --- a/Documentation/bpf/btf.rst +++ b/Documentation/bpf/btf.rst @@ -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 ================= @@ -565,18 +578,15 @@ A map can be created with ``btf_fd`` and specified key/value type id.:: In libbpf, the map can be defined with extra annotation like below: :: - struct bpf_map_def SEC("maps") btf_map = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(int), - .value_size = sizeof(struct ipv_counts), - .max_entries = 4, - }; - BPF_ANNOTATE_KV_PAIR(btf_map, int, struct ipv_counts); + struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, struct ipv_counts); + __uint(max_entries, 4); + } btf_map SEC(".maps"); -Here, the parameters for macro BPF_ANNOTATE_KV_PAIR are map name, key and -value types for the map. During ELF parsing, libbpf is able to extract -key/value type_id's and assign them to BPF_MAP_CREATE attributes -automatically. +During ELF parsing, libbpf is able to extract key/value type_id's and assign +them to BPF_MAP_CREATE attributes automatically. .. _BPF_Prog_Load: @@ -824,13 +834,12 @@ structure has bitfields. For example, for the following map,:: ___A b1:4; enum A b2:4; }; - struct bpf_map_def SEC("maps") tmpmap = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(__u32), - .value_size = sizeof(struct tmp_t), - .max_entries = 1, - }; - BPF_ANNOTATE_KV_PAIR(tmpmap, int, struct tmp_t); + struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, struct tmp_t); + __uint(max_entries, 1); + } tmpmap SEC(".maps"); bpftool is able to pretty print like below: :: diff --git a/Documentation/bpf/index.rst b/Documentation/bpf/index.rst index ef5c996547ec..96056a7447c7 100644 --- a/Documentation/bpf/index.rst +++ b/Documentation/bpf/index.rst @@ -21,6 +21,7 @@ that goes into great technical depth about the BPF Architecture. helpers programs maps + bpf_prog_run classic_vs_extended.rst bpf_licensing test_debug diff --git a/Documentation/bpf/instruction-set.rst b/Documentation/bpf/instruction-set.rst index 3704836fe6df..5300837ac2c9 100644 --- a/Documentation/bpf/instruction-set.rst +++ b/Documentation/bpf/instruction-set.rst @@ -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 | | 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 | | BPF_STX`` means:: *(size *) (dst_reg + off) = src_reg -BPF_MEM | | BPF_ST means:: +``BPF_MEM | | BPF_ST`` means:: *(size *) (dst_reg + off) = imm32 -BPF_MEM | | BPF_LDX means:: +``BPF_MEM | | 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 | | BPF_LD) and -(BPF_IND | | 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 | | BPF_LD`` and +``BPF_IND | | 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. diff --git a/Documentation/bpf/verifier.rst b/Documentation/bpf/verifier.rst index fae5f6273bac..d4326caf01f9 100644 --- a/Documentation/bpf/verifier.rst +++ b/Documentation/bpf/verifier.rst @@ -329,7 +329,7 @@ Program with unreachable instructions:: BPF_EXIT_INSN(), }; -Error: +Error:: unreachable insn 1 diff --git a/Documentation/devicetree/bindings/i2c/i2c.txt b/Documentation/devicetree/bindings/i2c/i2c.txt index b864916e087f..fc3dd7ec0445 100644 --- a/Documentation/devicetree/bindings/i2c/i2c.txt +++ b/Documentation/devicetree/bindings/i2c/i2c.txt @@ -95,6 +95,10 @@ wants to support one of the below features, it should adapt these bindings. - smbus-alert states that the optional SMBus-Alert feature apply to this bus. +- mctp-controller + indicates that the system is accessible via this bus as an endpoint for + MCTP over I2C transport. + Required properties (per child device) -------------------------------------- diff --git a/Documentation/devicetree/bindings/net/can/allwinner,sun4i-a10-can.yaml b/Documentation/devicetree/bindings/net/can/allwinner,sun4i-a10-can.yaml index c93fe9d3ea82..3c51b2d02957 100644 --- a/Documentation/devicetree/bindings/net/can/allwinner,sun4i-a10-can.yaml +++ b/Documentation/devicetree/bindings/net/can/allwinner,sun4i-a10-can.yaml @@ -10,6 +10,9 @@ maintainers: - Chen-Yu Tsai - Maxime Ripard +allOf: + - $ref: can-controller.yaml# + properties: compatible: oneOf: diff --git a/Documentation/devicetree/bindings/net/can/bosch,m_can.yaml b/Documentation/devicetree/bindings/net/can/bosch,m_can.yaml index 401ab7cdb379..b7f9803c1c6d 100644 --- a/Documentation/devicetree/bindings/net/can/bosch,m_can.yaml +++ b/Documentation/devicetree/bindings/net/can/bosch,m_can.yaml @@ -9,7 +9,10 @@ title: Bosch MCAN controller Bindings description: Bosch MCAN controller for CAN bus maintainers: - - Sriram Dash + - Chandrasekar Ramakrishnan + +allOf: + - $ref: can-controller.yaml# properties: compatible: @@ -66,8 +69,8 @@ properties: M_CAN includes the following elements according to user manual: 11-bit Filter 0-128 elements / 0-128 words 29-bit Filter 0-64 elements / 0-128 words - Rx FIFO 0 0-64 elements / 0-1152 words - Rx FIFO 1 0-64 elements / 0-1152 words + Rx FIFO 0 0-64 elements / 0-1152 words + Rx FIFO 1 0-64 elements / 0-1152 words Rx Buffers 0-64 elements / 0-1152 words Tx Event FIFO 0-32 elements / 0-64 words Tx Buffers 0-32 elements / 0-576 words diff --git a/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml b/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml index 2a884c1fe0e0..b3826af6bd6e 100644 --- a/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml +++ b/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml @@ -11,6 +11,9 @@ title: maintainers: - Marc Kleine-Budde +allOf: + - $ref: can-controller.yaml# + properties: compatible: oneOf: diff --git a/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml b/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml index 546c6e6d2fb0..91a3554ca950 100644 --- a/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml +++ b/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml @@ -35,6 +35,8 @@ properties: - renesas,r9a07g044-canfd # RZ/G2{L,LC} - const: renesas,rzg2l-canfd # RZ/G2L family + - const: renesas,r8a779a0-canfd # R-Car V3U + reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/net/can/xilinx,can.yaml b/Documentation/devicetree/bindings/net/can/xilinx,can.yaml new file mode 100644 index 000000000000..65af8183cb9c --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/xilinx,can.yaml @@ -0,0 +1,161 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/can/xilinx,can.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: + Xilinx Axi CAN/Zynq CANPS controller + +maintainers: + - Appana Durga Kedareswara rao + +properties: + compatible: + enum: + - xlnx,zynq-can-1.0 + - xlnx,axi-can-1.00.a + - xlnx,canfd-1.0 + - xlnx,canfd-2.0 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + maxItems: 2 + + power-domains: + maxItems: 1 + + tx-fifo-depth: + $ref: "/schemas/types.yaml#/definitions/uint32" + description: CAN Tx fifo depth (Zynq, Axi CAN). + + rx-fifo-depth: + $ref: "/schemas/types.yaml#/definitions/uint32" + description: CAN Rx fifo depth (Zynq, Axi CAN, CAN FD in sequential Rx mode) + + tx-mailbox-count: + $ref: "/schemas/types.yaml#/definitions/uint32" + description: CAN Tx mailbox buffer count (CAN FD) + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +allOf: + - $ref: can-controller.yaml# + - if: + properties: + compatible: + contains: + enum: + - xlnx,zynq-can-1.0 + + then: + properties: + clock-names: + items: + - const: can_clk + - const: pclk + required: + - tx-fifo-depth + - rx-fifo-depth + + - if: + properties: + compatible: + contains: + enum: + - xlnx,axi-can-1.00.a + + then: + properties: + clock-names: + items: + - const: can_clk + - const: s_axi_aclk + required: + - tx-fifo-depth + - rx-fifo-depth + + - if: + properties: + compatible: + contains: + enum: + - xlnx,canfd-1.0 + - xlnx,canfd-2.0 + + then: + properties: + clock-names: + items: + - const: can_clk + - const: s_axi_aclk + required: + - tx-mailbox-count + - rx-fifo-depth + +examples: + - | + #include + + can@e0008000 { + compatible = "xlnx,zynq-can-1.0"; + reg = <0xe0008000 0x1000>; + clocks = <&clkc 19>, <&clkc 36>; + clock-names = "can_clk", "pclk"; + interrupts = ; + interrupt-parent = <&intc>; + tx-fifo-depth = <0x40>; + rx-fifo-depth = <0x40>; + }; + + - | + can@40000000 { + compatible = "xlnx,axi-can-1.00.a"; + reg = <0x40000000 0x10000>; + clocks = <&clkc 0>, <&clkc 1>; + clock-names = "can_clk", "s_axi_aclk"; + interrupt-parent = <&intc>; + interrupts = ; + tx-fifo-depth = <0x40>; + rx-fifo-depth = <0x40>; + }; + + - | + can@40000000 { + compatible = "xlnx,canfd-1.0"; + reg = <0x40000000 0x2000>; + clocks = <&clkc 0>, <&clkc 1>; + clock-names = "can_clk", "s_axi_aclk"; + interrupt-parent = <&intc>; + interrupts = ; + tx-mailbox-count = <0x20>; + rx-fifo-depth = <0x20>; + }; + + - | + can@ff060000 { + compatible = "xlnx,canfd-2.0"; + reg = <0xff060000 0x6000>; + clocks = <&clkc 0>, <&clkc 1>; + clock-names = "can_clk", "s_axi_aclk"; + interrupt-parent = <&intc>; + interrupts = ; + tx-mailbox-count = <0x20>; + rx-fifo-depth = <0x40>; + }; diff --git a/Documentation/devicetree/bindings/net/can/xilinx_can.txt b/Documentation/devicetree/bindings/net/can/xilinx_can.txt deleted file mode 100644 index 100cc40b8510..000000000000 --- a/Documentation/devicetree/bindings/net/can/xilinx_can.txt +++ /dev/null @@ -1,61 +0,0 @@ -Xilinx Axi CAN/Zynq CANPS controller Device Tree Bindings ---------------------------------------------------------- - -Required properties: -- compatible : Should be: - - "xlnx,zynq-can-1.0" for Zynq CAN controllers - - "xlnx,axi-can-1.00.a" for Axi CAN controllers - - "xlnx,canfd-1.0" for CAN FD controllers - - "xlnx,canfd-2.0" for CAN FD 2.0 controllers -- reg : Physical base address and size of the controller - registers map. -- interrupts : Property with a value describing the interrupt - number. -- clock-names : List of input clock names - - "can_clk", "pclk" (For CANPS), - - "can_clk", "s_axi_aclk" (For AXI CAN and CAN FD). - (See clock bindings for details). -- clocks : Clock phandles (see clock bindings for details). -- tx-fifo-depth : Can Tx fifo depth (Zynq, Axi CAN). -- rx-fifo-depth : Can Rx fifo depth (Zynq, Axi CAN, CAN FD in - sequential Rx mode). -- tx-mailbox-count : Can Tx mailbox buffer count (CAN FD). -- rx-mailbox-count : Can Rx mailbox buffer count (CAN FD in mailbox Rx - mode). - - -Example: - -For Zynq CANPS Dts file: - zynq_can_0: can@e0008000 { - compatible = "xlnx,zynq-can-1.0"; - clocks = <&clkc 19>, <&clkc 36>; - clock-names = "can_clk", "pclk"; - reg = <0xe0008000 0x1000>; - interrupts = <0 28 4>; - interrupt-parent = <&intc>; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - }; -For Axi CAN Dts file: - axi_can_0: axi-can@40000000 { - compatible = "xlnx,axi-can-1.00.a"; - clocks = <&clkc 0>, <&clkc 1>; - clock-names = "can_clk","s_axi_aclk" ; - reg = <0x40000000 0x10000>; - interrupt-parent = <&intc>; - interrupts = <0 59 1>; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - }; -For CAN FD Dts file: - canfd_0: canfd@40000000 { - compatible = "xlnx,canfd-1.0"; - clocks = <&clkc 0>, <&clkc 1>; - clock-names = "can_clk", "s_axi_aclk"; - reg = <0x40000000 0x2000>; - interrupt-parent = <&intc>; - interrupts = <0 59 1>; - tx-mailbox-count = <0x20>; - rx-fifo-depth = <0x20>; - }; diff --git a/Documentation/devicetree/bindings/net/cdns,macb.yaml b/Documentation/devicetree/bindings/net/cdns,macb.yaml index 8dd06db34169..6cd3d853dcba 100644 --- a/Documentation/devicetree/bindings/net/cdns,macb.yaml +++ b/Documentation/devicetree/bindings/net/cdns,macb.yaml @@ -81,6 +81,25 @@ properties: phy-handle: true + phys: + maxItems: 1 + + phy-names: + const: sgmii-phy + description: + Required with ZynqMP SoC when in SGMII mode. + Should reference PS-GTR generic PHY device for this controller + instance. See ZynqMP example. + + resets: + maxItems: 1 + description: + Recommended with ZynqMP, specify reset control for this + controller instance with zynqmp-reset driver. + + reset-names: + maxItems: 1 + fixed-link: true iommus: @@ -157,3 +176,40 @@ examples: reset-gpios = <&pioE 6 1>; }; }; + + - | + #include + #include + #include + #include + + bus { + #address-cells = <2>; + #size-cells = <2>; + gem1: ethernet@ff0c0000 { + compatible = "cdns,zynqmp-gem", "cdns,gem"; + interrupt-parent = <&gic>; + interrupts = <0 59 4>, <0 59 4>; + reg = <0x0 0xff0c0000 0x0 0x1000>; + clocks = <&zynqmp_clk LPD_LSBUS>, <&zynqmp_clk GEM1_REF>, + <&zynqmp_clk GEM1_TX>, <&zynqmp_clk GEM1_RX>, + <&zynqmp_clk GEM_TSU>; + clock-names = "pclk", "hclk", "tx_clk", "rx_clk", "tsu_clk"; + #address-cells = <1>; + #size-cells = <0>; + #stream-id-cells = <1>; + iommus = <&smmu 0x875>; + power-domains = <&zynqmp_firmware PD_ETH_1>; + resets = <&zynqmp_reset ZYNQMP_RESET_GEM1>; + reset-names = "gem1_rst"; + status = "okay"; + phy-mode = "sgmii"; + phy-names = "sgmii-phy"; + phys = <&psgtr 1 PHY_TYPE_SGMII 1 1>; + fixed-link { + speed = <1000>; + full-duplex; + pause; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/net/davicom,dm9051.yaml b/Documentation/devicetree/bindings/net/davicom,dm9051.yaml new file mode 100644 index 000000000000..52e852fef753 --- /dev/null +++ b/Documentation/devicetree/bindings/net/davicom,dm9051.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/davicom,dm9051.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Davicom DM9051 SPI Ethernet Controller + +maintainers: + - Joseph CHANG + +description: | + The DM9051 is a fully integrated and cost-effective low pin count single + chip Fast Ethernet controller with a Serial Peripheral Interface (SPI). + +allOf: + - $ref: ethernet-controller.yaml# + +properties: + compatible: + const: davicom,dm9051 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 45000000 + + interrupts: + maxItems: 1 + + local-mac-address: true + + mac-address: true + +required: + - compatible + - reg + - spi-max-frequency + - interrupts + +additionalProperties: false + +examples: + # Raspberry Pi platform + - | + /* for Raspberry Pi with pin control stuff for GPIO irq */ + #include + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + ethernet@0 { + compatible = "davicom,dm9051"; + reg = <0>; /* spi chip select */ + local-mac-address = [00 00 00 00 00 00]; + interrupt-parent = <&gpio>; + interrupts = <26 IRQ_TYPE_LEVEL_LOW>; + spi-max-frequency = <31200000>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/dsa/dsa-port.yaml b/Documentation/devicetree/bindings/net/dsa/dsa-port.yaml index 702df848a71d..e60867c7c571 100644 --- a/Documentation/devicetree/bindings/net/dsa/dsa-port.yaml +++ b/Documentation/devicetree/bindings/net/dsa/dsa-port.yaml @@ -51,6 +51,8 @@ properties: - edsa - ocelot - ocelot-8021q + - rtl8_4 + - rtl8_4t - seville phy-handle: true diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml index 84985f53bffd..184152087b60 100644 --- a/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml +++ b/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml @@ -42,6 +42,12 @@ properties: description: Set if the output SYNCLKO frequency should be set to 125MHz instead of 25MHz. + microchip,synclko-disable: + $ref: /schemas/types.yaml#/definitions/flag + description: + Set if the output SYNCLKO clock should be disabled. Do not mix with + microchip,synclko-125. + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/net/dsa/realtek-smi.txt b/Documentation/devicetree/bindings/net/dsa/realtek-smi.txt deleted file mode 100644 index 7959ec237983..000000000000 --- a/Documentation/devicetree/bindings/net/dsa/realtek-smi.txt +++ /dev/null @@ -1,240 +0,0 @@ -Realtek SMI-based Switches -========================== - -The SMI "Simple Management Interface" is a two-wire protocol using -bit-banged GPIO that while it reuses the MDIO lines MCK and MDIO does -not use the MDIO protocol. This binding defines how to specify the -SMI-based Realtek devices. - -Required properties: - -- compatible: must be exactly one of: - "realtek,rtl8365mb" (4+1 ports) - "realtek,rtl8366" - "realtek,rtl8366rb" (4+1 ports) - "realtek,rtl8366s" (4+1 ports) - "realtek,rtl8367" - "realtek,rtl8367b" - "realtek,rtl8368s" (8 port) - "realtek,rtl8369" - "realtek,rtl8370" (8 port) - -Required properties: -- mdc-gpios: GPIO line for the MDC clock line. -- mdio-gpios: GPIO line for the MDIO data line. -- reset-gpios: GPIO line for the reset signal. - -Optional properties: -- realtek,disable-leds: if the LED drivers are not used in the - hardware design this will disable them so they are not turned on - and wasting power. - -Required subnodes: - -- interrupt-controller - - This defines an interrupt controller with an IRQ line (typically - a GPIO) that will demultiplex and handle the interrupt from the single - interrupt line coming out of one of the SMI-based chips. It most - importantly provides link up/down interrupts to the PHY blocks inside - the ASIC. - -Required properties of interrupt-controller: - -- interrupt: parent interrupt, see interrupt-controller/interrupts.txt -- interrupt-controller: see interrupt-controller/interrupts.txt -- #address-cells: should be <0> -- #interrupt-cells: should be <1> - -- mdio - - This defines the internal MDIO bus of the SMI device, mostly for the - purpose of being able to hook the interrupts to the right PHY and - the right PHY to the corresponding port. - -Required properties of mdio: - -- compatible: should be set to "realtek,smi-mdio" for all SMI devices - -See net/mdio.txt for additional MDIO bus properties. - -See net/dsa/dsa.txt for a list of additional required and optional properties -and subnodes of DSA switches. - -Examples: - -An example for the RTL8366RB: - -switch { - compatible = "realtek,rtl8366rb"; - /* 22 = MDIO (has input reads), 21 = MDC (clock, output only) */ - mdc-gpios = <&gpio0 21 GPIO_ACTIVE_HIGH>; - mdio-gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>; - reset-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>; - - switch_intc: interrupt-controller { - /* GPIO 15 provides the interrupt */ - interrupt-parent = <&gpio0>; - interrupts = <15 IRQ_TYPE_LEVEL_LOW>; - interrupt-controller; - #address-cells = <0>; - #interrupt-cells = <1>; - }; - - ports { - #address-cells = <1>; - #size-cells = <0>; - reg = <0>; - port@0 { - reg = <0>; - label = "lan0"; - phy-handle = <&phy0>; - }; - port@1 { - reg = <1>; - label = "lan1"; - phy-handle = <&phy1>; - }; - port@2 { - reg = <2>; - label = "lan2"; - phy-handle = <&phy2>; - }; - port@3 { - reg = <3>; - label = "lan3"; - phy-handle = <&phy3>; - }; - port@4 { - reg = <4>; - label = "wan"; - phy-handle = <&phy4>; - }; - port@5 { - reg = <5>; - label = "cpu"; - ethernet = <&gmac0>; - phy-mode = "rgmii"; - fixed-link { - speed = <1000>; - full-duplex; - }; - }; - }; - - mdio { - compatible = "realtek,smi-mdio", "dsa-mdio"; - #address-cells = <1>; - #size-cells = <0>; - - phy0: phy@0 { - reg = <0>; - interrupt-parent = <&switch_intc>; - interrupts = <0>; - }; - phy1: phy@1 { - reg = <1>; - interrupt-parent = <&switch_intc>; - interrupts = <1>; - }; - phy2: phy@2 { - reg = <2>; - interrupt-parent = <&switch_intc>; - interrupts = <2>; - }; - phy3: phy@3 { - reg = <3>; - interrupt-parent = <&switch_intc>; - interrupts = <3>; - }; - phy4: phy@4 { - reg = <4>; - interrupt-parent = <&switch_intc>; - interrupts = <12>; - }; - }; -}; - -An example for the RTL8365MB-VC: - -switch { - compatible = "realtek,rtl8365mb"; - mdc-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>; - mdio-gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>; - reset-gpios = <&gpio5 0 GPIO_ACTIVE_LOW>; - - switch_intc: interrupt-controller { - interrupt-parent = <&gpio5>; - interrupts = <1 IRQ_TYPE_LEVEL_LOW>; - interrupt-controller; - #address-cells = <0>; - #interrupt-cells = <1>; - }; - - ports { - #address-cells = <1>; - #size-cells = <0>; - reg = <0>; - port@0 { - reg = <0>; - label = "swp0"; - phy-handle = <ðphy0>; - }; - port@1 { - reg = <1>; - label = "swp1"; - phy-handle = <ðphy1>; - }; - port@2 { - reg = <2>; - label = "swp2"; - phy-handle = <ðphy2>; - }; - port@3 { - reg = <3>; - label = "swp3"; - phy-handle = <ðphy3>; - }; - port@6 { - reg = <6>; - label = "cpu"; - ethernet = <&fec1>; - phy-mode = "rgmii"; - tx-internal-delay-ps = <2000>; - rx-internal-delay-ps = <2000>; - - fixed-link { - speed = <1000>; - full-duplex; - pause; - }; - }; - }; - - mdio { - compatible = "realtek,smi-mdio"; - #address-cells = <1>; - #size-cells = <0>; - - ethphy0: phy@0 { - reg = <0>; - interrupt-parent = <&switch_intc>; - interrupts = <0>; - }; - ethphy1: phy@1 { - reg = <1>; - interrupt-parent = <&switch_intc>; - interrupts = <1>; - }; - ethphy2: phy@2 { - reg = <2>; - interrupt-parent = <&switch_intc>; - interrupts = <2>; - }; - ethphy3: phy@3 { - reg = <3>; - interrupt-parent = <&switch_intc>; - interrupts = <3>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/net/dsa/realtek.yaml b/Documentation/devicetree/bindings/net/dsa/realtek.yaml new file mode 100644 index 000000000000..8756060895a8 --- /dev/null +++ b/Documentation/devicetree/bindings/net/dsa/realtek.yaml @@ -0,0 +1,394 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/dsa/realtek.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Realtek switches for unmanaged switches + +allOf: + - $ref: dsa.yaml# + +maintainers: + - Linus Walleij + +description: + Realtek advertises these chips as fast/gigabit switches or unmanaged + switches. They can be controlled using different interfaces, like SMI, + MDIO or SPI. + + The SMI "Simple Management Interface" is a two-wire protocol using + bit-banged GPIO that while it reuses the MDIO lines MCK and MDIO does + not use the MDIO protocol. This binding defines how to specify the + SMI-based Realtek devices. The realtek-smi driver is a platform driver + and it must be inserted inside a platform node. + + The MDIO-connected switches use MDIO protocol to access their registers. + The realtek-mdio driver is an MDIO driver and it must be inserted inside + an MDIO node. + +properties: + compatible: + enum: + - realtek,rtl8365mb + - realtek,rtl8366 + - realtek,rtl8366rb + - realtek,rtl8366s + - realtek,rtl8367 + - realtek,rtl8367b + - realtek,rtl8367rb + - realtek,rtl8367s + - realtek,rtl8368s + - realtek,rtl8369 + - realtek,rtl8370 + description: | + realtek,rtl8365mb: 4+1 ports + realtek,rtl8366: 5+1 ports + realtek,rtl8366rb: 5+1 ports + realtek,rtl8366s: 5+1 ports + realtek,rtl8367: + realtek,rtl8367b: + realtek,rtl8367rb: 5+2 ports + realtek,rtl8367s: 5+2 ports + realtek,rtl8368s: 8 ports + realtek,rtl8369: 8+1 ports + realtek,rtl8370: 8+2 ports + + mdc-gpios: + description: GPIO line for the MDC clock line. + maxItems: 1 + + mdio-gpios: + description: GPIO line for the MDIO data line. + maxItems: 1 + + reset-gpios: + description: GPIO to be used to reset the whole device + maxItems: 1 + + realtek,disable-leds: + type: boolean + description: | + if the LED drivers are not used in the hardware design, + this will disable them so they are not turned on + and wasting power. + + interrupt-controller: + type: object + description: | + This defines an interrupt controller with an IRQ line (typically + a GPIO) that will demultiplex and handle the interrupt from the single + interrupt line coming out of one of the Realtek switch chips. It most + importantly provides link up/down interrupts to the PHY blocks inside + the ASIC. + + properties: + + interrupt-controller: true + + interrupts: + maxItems: 1 + description: + A single IRQ line from the switch, either active LOW or HIGH + + '#address-cells': + const: 0 + + '#interrupt-cells': + const: 1 + + required: + - interrupt-controller + - '#address-cells' + - '#interrupt-cells' + + mdio: + $ref: /schemas/net/mdio.yaml# + unevaluatedProperties: false + + properties: + compatible: + const: realtek,smi-mdio + +if: + required: + - reg + +then: + not: + required: + - mdc-gpios + - mdio-gpios + - mdio + + properties: + mdc-gpios: false + mdio-gpios: false + mdio: false + +else: + required: + - mdc-gpios + - mdio-gpios + - mdio + - reset-gpios + +required: + - compatible + + # - mdc-gpios + # - mdio-gpios + # - reset-gpios + # - mdio + +unevaluatedProperties: false + +examples: + - | + #include + #include + + platform { + switch { + compatible = "realtek,rtl8366rb"; + /* 22 = MDIO (has input reads), 21 = MDC (clock, output only) */ + mdc-gpios = <&gpio0 21 GPIO_ACTIVE_HIGH>; + mdio-gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>; + + switch_intc1: interrupt-controller { + /* GPIO 15 provides the interrupt */ + interrupt-parent = <&gpio0>; + interrupts = <15 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + label = "lan0"; + phy-handle = <&phy0>; + }; + port@1 { + reg = <1>; + label = "lan1"; + phy-handle = <&phy1>; + }; + port@2 { + reg = <2>; + label = "lan2"; + phy-handle = <&phy2>; + }; + port@3 { + reg = <3>; + label = "lan3"; + phy-handle = <&phy3>; + }; + port@4 { + reg = <4>; + label = "wan"; + phy-handle = <&phy4>; + }; + port@5 { + reg = <5>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "rgmii"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + + mdio { + compatible = "realtek,smi-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + reg = <0>; + interrupt-parent = <&switch_intc1>; + interrupts = <0>; + }; + phy1: ethernet-phy@1 { + reg = <1>; + interrupt-parent = <&switch_intc1>; + interrupts = <1>; + }; + phy2: ethernet-phy@2 { + reg = <2>; + interrupt-parent = <&switch_intc1>; + interrupts = <2>; + }; + phy3: ethernet-phy@3 { + reg = <3>; + interrupt-parent = <&switch_intc1>; + interrupts = <3>; + }; + phy4: ethernet-phy@4 { + reg = <4>; + interrupt-parent = <&switch_intc1>; + interrupts = <12>; + }; + }; + }; + }; + + - | + #include + #include + + platform { + switch { + compatible = "realtek,rtl8365mb"; + mdc-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>; + mdio-gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio5 0 GPIO_ACTIVE_LOW>; + + switch_intc2: interrupt-controller { + interrupt-parent = <&gpio5>; + interrupts = <1 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + label = "swp0"; + phy-handle = <ðphy0>; + }; + port@1 { + reg = <1>; + label = "swp1"; + phy-handle = <ðphy1>; + }; + port@2 { + reg = <2>; + label = "swp2"; + phy-handle = <ðphy2>; + }; + port@3 { + reg = <3>; + label = "swp3"; + phy-handle = <ðphy3>; + }; + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&fec1>; + phy-mode = "rgmii"; + tx-internal-delay-ps = <2000>; + rx-internal-delay-ps = <2000>; + + fixed-link { + speed = <1000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "realtek,smi-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + ethphy0: ethernet-phy@0 { + reg = <0>; + interrupt-parent = <&switch_intc2>; + interrupts = <0>; + }; + ethphy1: ethernet-phy@1 { + reg = <1>; + interrupt-parent = <&switch_intc2>; + interrupts = <1>; + }; + ethphy2: ethernet-phy@2 { + reg = <2>; + interrupt-parent = <&switch_intc2>; + interrupts = <2>; + }; + ethphy3: ethernet-phy@3 { + reg = <3>; + interrupt-parent = <&switch_intc2>; + interrupts = <3>; + }; + }; + }; + }; + + - | + #include + #include + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + switch@29 { + compatible = "realtek,rtl8367s"; + reg = <29>; + + reset-gpios = <&gpio2 20 GPIO_ACTIVE_LOW>; + + switch_intc3: interrupt-controller { + interrupt-parent = <&gpio0>; + interrupts = <11 IRQ_TYPE_EDGE_FALLING>; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan4"; + }; + + port@1 { + reg = <1>; + label = "lan3"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan1"; + }; + + port@4 { + reg = <4>; + label = "wan"; + }; + + port@7 { + reg = <7>; + ethernet = <ðernet>; + phy-mode = "rgmii"; + tx-internal-delay-ps = <2000>; + rx-internal-delay-ps = <0>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/net/fsl-fman.txt b/Documentation/devicetree/bindings/net/fsl-fman.txt index 020337f3c05f..801efc7d6818 100644 --- a/Documentation/devicetree/bindings/net/fsl-fman.txt +++ b/Documentation/devicetree/bindings/net/fsl-fman.txt @@ -388,14 +388,24 @@ PROPERTIES Value type: Definition: A standard property. -- bus-frequency +- clocks + Usage: optional + Value type: + Definition: A reference to the input clock of the controller + from which the MDC frequency is derived. + +- clock-frequency Usage: optional Value type: - Definition: Specifies the external MDIO bus clock speed to - be used, if different from the standard 2.5 MHz. - This may be due to the standard speed being unsupported (e.g. - due to a hardware problem), or to advertise that all relevant - components in the system support a faster speed. + Definition: Specifies the external MDC frequency, in Hertz, to + be used. Requires that the input clock is specified in the + "clocks" property. See also: mdio.yaml. + +- suppress-preamble + Usage: optional + Value type: + Definition: Disable generation of preamble bits. See also: + mdio.yaml. - interrupts Usage: required for external MDIO diff --git a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt index 691f886cfc4a..2bf31572b08d 100644 --- a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt +++ b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt @@ -5,6 +5,7 @@ Required properties: "marvell,armada-370-neta" "marvell,armada-xp-neta" "marvell,armada-3700-neta" + "marvell,armada-ac5-neta" - reg: address and length of the register set for the device. - interrupts: interrupt for the device - phy: See ethernet.txt file in the same directory. diff --git a/Documentation/devicetree/bindings/net/mctp-i2c-controller.yaml b/Documentation/devicetree/bindings/net/mctp-i2c-controller.yaml new file mode 100644 index 000000000000..afd11c9422fa --- /dev/null +++ b/Documentation/devicetree/bindings/net/mctp-i2c-controller.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/mctp-i2c-controller.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MCTP I2C transport binding + +maintainers: + - Matt Johnston + +description: | + An mctp-i2c-controller defines a local MCTP endpoint on an I2C controller. + MCTP I2C is specified by DMTF DSP0237. + + An mctp-i2c-controller must be attached to an I2C adapter which supports + slave functionality. I2C busses (either directly or as subordinate mux + busses) are attached to the mctp-i2c-controller with a 'mctp-controller' + property on each used bus. Each mctp-controller I2C bus will be presented + to the host system as a separate MCTP I2C instance. + +properties: + compatible: + const: mctp-i2c-controller + + reg: + minimum: 0x40000000 + maximum: 0x4000007f + description: | + 7 bit I2C address of the local endpoint. + I2C_OWN_SLAVE_ADDRESS (1<<30) flag must be set. + +additionalProperties: false + +required: + - compatible + - reg + +examples: + - | + // Basic case of a single I2C bus + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + mctp-controller; + + mctp@30 { + compatible = "mctp-i2c-controller"; + reg = <(0x30 | I2C_OWN_SLAVE_ADDRESS)>; + }; + }; + + - | + // Mux topology with multiple MCTP-handling busses under + // a single mctp-i2c-controller. + // i2c1 and i2c6 can have MCTP devices, i2c5 does not. + #include + + i2c1: i2c { + #address-cells = <1>; + #size-cells = <0>; + mctp-controller; + + mctp@50 { + compatible = "mctp-i2c-controller"; + reg = <(0x50 | I2C_OWN_SLAVE_ADDRESS)>; + }; + }; + + i2c-mux { + #address-cells = <1>; + #size-cells = <0>; + i2c-parent = <&i2c1>; + + i2c5: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + eeprom@33 { + reg = <0x33>; + }; + }; + + i2c6: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + mctp-controller; + }; + }; diff --git a/Documentation/devicetree/bindings/net/mediatek-dwmac.txt b/Documentation/devicetree/bindings/net/mediatek-dwmac.txt deleted file mode 100644 index afbcaebf062e..000000000000 --- a/Documentation/devicetree/bindings/net/mediatek-dwmac.txt +++ /dev/null @@ -1,91 +0,0 @@ -MediaTek DWMAC glue layer controller - -This file documents platform glue layer for stmmac. -Please see stmmac.txt for the other unchanged properties. - -The device node has following properties. - -Required properties: -- compatible: Should be "mediatek,mt2712-gmac" for MT2712 SoC -- reg: Address and length of the register set for the device -- interrupts: Should contain the MAC interrupts -- interrupt-names: Should contain a list of interrupt names corresponding to - the interrupts in the interrupts property, if available. - Should be "macirq" for the main MAC IRQ -- clocks: Must contain a phandle for each entry in clock-names. -- clock-names: The name of the clock listed in the clocks property. These are - "axi", "apb", "mac_main", "ptp_ref", "rmii_internal" for MT2712 SoC. -- mac-address: See ethernet.txt in the same directory -- phy-mode: See ethernet.txt in the same directory -- mediatek,pericfg: A phandle to the syscon node that control ethernet - interface and timing delay. - -Optional properties: -- mediatek,tx-delay-ps: TX clock delay macro value. Default is 0. - It should be defined for RGMII/MII interface. - It should be defined for RMII interface when the reference clock is from MT2712 SoC. -- mediatek,rx-delay-ps: RX clock delay macro value. Default is 0. - It should be defined for RGMII/MII interface. - It should be defined for RMII interface. -Both delay properties need to be a multiple of 170 for RGMII interface, -or will round down. Range 0~31*170. -Both delay properties need to be a multiple of 550 for MII/RMII interface, -or will round down. Range 0~31*550. - -- mediatek,rmii-rxc: boolean property, if present indicates that the RMII - reference clock, which is from external PHYs, is connected to RXC pin - on MT2712 SoC. - Otherwise, is connected to TXC pin. -- mediatek,rmii-clk-from-mac: boolean property, if present indicates that - MT2712 SoC provides the RMII reference clock, which outputs to TXC pin only. -- mediatek,txc-inverse: boolean property, if present indicates that - 1. tx clock will be inversed in MII/RGMII case, - 2. tx clock inside MAC will be inversed relative to reference clock - which is from external PHYs in RMII case, and it rarely happen. - 3. the reference clock, which outputs to TXC pin will be inversed in RMII case - when the reference clock is from MT2712 SoC. -- mediatek,rxc-inverse: boolean property, if present indicates that - 1. rx clock will be inversed in MII/RGMII case. - 2. reference clock will be inversed when arrived at MAC in RMII case, when - the reference clock is from external PHYs. - 3. the inside clock, which be sent to MAC, will be inversed in RMII case when - the reference clock is from MT2712 SoC. -- assigned-clocks: mac_main and ptp_ref clocks -- assigned-clock-parents: parent clocks of the assigned clocks - -Example: - eth: ethernet@1101c000 { - compatible = "mediatek,mt2712-gmac"; - reg = <0 0x1101c000 0 0x1300>; - interrupts = ; - interrupt-names = "macirq"; - phy-mode ="rgmii-rxid"; - mac-address = [00 55 7b b5 7d f7]; - clock-names = "axi", - "apb", - "mac_main", - "ptp_ref", - "rmii_internal"; - clocks = <&pericfg CLK_PERI_GMAC>, - <&pericfg CLK_PERI_GMAC_PCLK>, - <&topckgen CLK_TOP_ETHER_125M_SEL>, - <&topckgen CLK_TOP_ETHER_50M_SEL>, - <&topckgen CLK_TOP_ETHER_50M_RMII_SEL>; - assigned-clocks = <&topckgen CLK_TOP_ETHER_125M_SEL>, - <&topckgen CLK_TOP_ETHER_50M_SEL>, - <&topckgen CLK_TOP_ETHER_50M_RMII_SEL>; - assigned-clock-parents = <&topckgen CLK_TOP_ETHERPLL_125M>, - <&topckgen CLK_TOP_APLL1_D3>, - <&topckgen CLK_TOP_ETHERPLL_50M>; - power-domains = <&scpsys MT2712_POWER_DOMAIN_AUDIO>; - mediatek,pericfg = <&pericfg>; - mediatek,tx-delay-ps = <1530>; - mediatek,rx-delay-ps = <1530>; - mediatek,rmii-rxc; - mediatek,txc-inverse; - mediatek,rxc-inverse; - snps,txpbl = <1>; - snps,rxpbl = <1>; - snps,reset-gpio = <&pio 87 GPIO_ACTIVE_LOW>; - snps,reset-active-low; - }; diff --git a/Documentation/devicetree/bindings/net/mediatek-dwmac.yaml b/Documentation/devicetree/bindings/net/mediatek-dwmac.yaml new file mode 100644 index 000000000000..901944683322 --- /dev/null +++ b/Documentation/devicetree/bindings/net/mediatek-dwmac.yaml @@ -0,0 +1,175 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/mediatek-dwmac.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek DWMAC glue layer controller + +maintainers: + - Biao Huang + +description: + This file documents platform glue layer for stmmac. + +# We need a select here so we don't match all nodes with 'snps,dwmac' +select: + properties: + compatible: + contains: + enum: + - mediatek,mt2712-gmac + - mediatek,mt8195-gmac + required: + - compatible + +allOf: + - $ref: "snps,dwmac.yaml#" + +properties: + compatible: + oneOf: + - items: + - enum: + - mediatek,mt2712-gmac + - const: snps,dwmac-4.20a + - items: + - enum: + - mediatek,mt8195-gmac + - const: snps,dwmac-5.10a + + clocks: + minItems: 5 + items: + - description: AXI clock + - description: APB clock + - description: MAC Main clock + - description: PTP clock + - description: RMII reference clock provided by MAC + - description: MAC clock gate + + clock-names: + minItems: 5 + items: + - const: axi + - const: apb + - const: mac_main + - const: ptp_ref + - const: rmii_internal + - const: mac_cg + + mediatek,pericfg: + $ref: /schemas/types.yaml#/definitions/phandle + description: + The phandle to the syscon node that control ethernet + interface and timing delay. + + mediatek,tx-delay-ps: + description: + The internal TX clock delay (provided by this driver) in nanoseconds. + For MT2712 RGMII interface, Allowed value need to be a multiple of 170, + or will round down. Range 0~31*170. + For MT2712 RMII/MII interface, Allowed value need to be a multiple of 550, + or will round down. Range 0~31*550. + For MT8195 RGMII/RMII/MII interface, Allowed value need to be a multiple of 290, + or will round down. Range 0~31*290. + + mediatek,rx-delay-ps: + description: + The internal RX clock delay (provided by this driver) in nanoseconds. + For MT2712 RGMII interface, Allowed value need to be a multiple of 170, + or will round down. Range 0~31*170. + For MT2712 RMII/MII interface, Allowed value need to be a multiple of 550, + or will round down. Range 0~31*550. + For MT8195 RGMII/RMII/MII interface, Allowed value need to be a multiple + of 290, or will round down. Range 0~31*290. + + mediatek,rmii-rxc: + type: boolean + description: + If present, indicates that the RMII reference clock, which is from external + PHYs, is connected to RXC pin. Otherwise, is connected to TXC pin. + + mediatek,rmii-clk-from-mac: + type: boolean + description: + If present, indicates that MAC provides the RMII reference clock, which + outputs to TXC pin only. + + mediatek,txc-inverse: + type: boolean + description: + If present, indicates that + 1. tx clock will be inversed in MII/RGMII case, + 2. tx clock inside MAC will be inversed relative to reference clock + which is from external PHYs in RMII case, and it rarely happen. + 3. the reference clock, which outputs to TXC pin will be inversed in RMII case + when the reference clock is from MAC. + + mediatek,rxc-inverse: + type: boolean + description: + If present, indicates that + 1. rx clock will be inversed in MII/RGMII case. + 2. reference clock will be inversed when arrived at MAC in RMII case, when + the reference clock is from external PHYs. + 3. the inside clock, which be sent to MAC, will be inversed in RMII case when + the reference clock is from MAC. + + mediatek,mac-wol: + type: boolean + description: + If present, indicates that MAC supports WOL(Wake-On-LAN), and MAC WOL will be enabled. + Otherwise, PHY WOL is perferred. + +required: + - compatible + - reg + - interrupts + - interrupt-names + - clocks + - clock-names + - phy-mode + - mediatek,pericfg + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + #include + + eth: ethernet@1101c000 { + compatible = "mediatek,mt2712-gmac", "snps,dwmac-4.20a"; + reg = <0x1101c000 0x1300>; + interrupts = ; + interrupt-names = "macirq"; + phy-mode ="rgmii-rxid"; + mac-address = [00 55 7b b5 7d f7]; + clock-names = "axi", + "apb", + "mac_main", + "ptp_ref", + "rmii_internal"; + clocks = <&pericfg CLK_PERI_GMAC>, + <&pericfg CLK_PERI_GMAC_PCLK>, + <&topckgen CLK_TOP_ETHER_125M_SEL>, + <&topckgen CLK_TOP_ETHER_50M_SEL>, + <&topckgen CLK_TOP_ETHER_50M_RMII_SEL>; + assigned-clocks = <&topckgen CLK_TOP_ETHER_125M_SEL>, + <&topckgen CLK_TOP_ETHER_50M_SEL>, + <&topckgen CLK_TOP_ETHER_50M_RMII_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_ETHERPLL_125M>, + <&topckgen CLK_TOP_APLL1_D3>, + <&topckgen CLK_TOP_ETHERPLL_50M>; + power-domains = <&scpsys MT2712_POWER_DOMAIN_AUDIO>; + mediatek,pericfg = <&pericfg>; + mediatek,tx-delay-ps = <1530>; + snps,txpbl = <1>; + snps,rxpbl = <1>; + snps,reset-gpio = <&pio 87 GPIO_ACTIVE_LOW>; + snps,reset-delays-us = <0 10000 10000>; + }; diff --git a/Documentation/devicetree/bindings/net/micrel.txt b/Documentation/devicetree/bindings/net/micrel.txt index 8d157f0295a5..c5ab62c39133 100644 --- a/Documentation/devicetree/bindings/net/micrel.txt +++ b/Documentation/devicetree/bindings/net/micrel.txt @@ -45,3 +45,20 @@ Optional properties: In fiber mode, auto-negotiation is disabled and the PHY can only work in 100base-fx (full and half duplex) modes. + + - lan8814,ignore-ts: If present the PHY will not support timestamping. + + This option acts as check whether Timestamping is supported by + hardware or not. LAN8814 phy support hardware tmestamping. + + - lan8814,latency_rx_10: Configures Latency value of phy in ingress at 10 Mbps. + + - lan8814,latency_tx_10: Configures Latency value of phy in egress at 10 Mbps. + + - lan8814,latency_rx_100: Configures Latency value of phy in ingress at 100 Mbps. + + - lan8814,latency_tx_100: Configures Latency value of phy in egress at 100 Mbps. + + - lan8814,latency_rx_1000: Configures Latency value of phy in ingress at 1000 Mbps. + + - lan8814,latency_tx_1000: Configures Latency value of phy in egress at 1000 Mbps. diff --git a/Documentation/devicetree/bindings/net/microchip,lan966x-switch.yaml b/Documentation/devicetree/bindings/net/microchip,lan966x-switch.yaml index e79e4e166ad8..13812768b923 100644 --- a/Documentation/devicetree/bindings/net/microchip,lan966x-switch.yaml +++ b/Documentation/devicetree/bindings/net/microchip,lan966x-switch.yaml @@ -38,6 +38,7 @@ properties: - description: register based extraction - description: frame dma based extraction - description: analyzer interrupt + - description: ptp interrupt interrupt-names: minItems: 1 @@ -45,6 +46,7 @@ properties: - const: xtr - const: fdma - const: ana + - const: ptp resets: items: diff --git a/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml b/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml index 347b912a46bb..6c86d3d85e99 100644 --- a/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml +++ b/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml @@ -53,12 +53,14 @@ properties: items: - description: register based extraction - description: frame dma based extraction + - description: ptp interrupt interrupt-names: minItems: 1 items: - const: xtr - const: fdma + - const: ptp resets: items: diff --git a/Documentation/devicetree/bindings/net/mscc-miim.txt b/Documentation/devicetree/bindings/net/mscc-miim.txt index 7104679cf59d..70e0cb1ee485 100644 --- a/Documentation/devicetree/bindings/net/mscc-miim.txt +++ b/Documentation/devicetree/bindings/net/mscc-miim.txt @@ -2,7 +2,7 @@ Microsemi MII Management Controller (MIIM) / MDIO ================================================= Properties: -- compatible: must be "mscc,ocelot-miim" +- compatible: must be "mscc,ocelot-miim" or "microchip,lan966x-miim" - reg: The base address of the MDIO bus controller register bank. Optionally, a second register bank can be defined if there is an associated reset register for internal PHYs diff --git a/Documentation/devicetree/bindings/net/renesas,etheravb.yaml b/Documentation/devicetree/bindings/net/renesas,etheravb.yaml index bda821065a2b..ee2ccacc39ff 100644 --- a/Documentation/devicetree/bindings/net/renesas,etheravb.yaml +++ b/Documentation/devicetree/bindings/net/renesas,etheravb.yaml @@ -45,8 +45,10 @@ properties: - items: - enum: + - renesas,r9a07g043-gbeth # RZ/G2UL - renesas,r9a07g044-gbeth # RZ/G2{L,LC} - - const: renesas,rzg2l-gbeth # RZ/G2L + - renesas,r9a07g054-gbeth # RZ/V2L + - const: renesas,rzg2l-gbeth # RZ/{G2L,G2UL,V2L} family reg: true diff --git a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml index 269cd63fb544..3216c999f0e1 100644 --- a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml +++ b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml @@ -18,7 +18,7 @@ description: | wireless device. The node is expected to be specified as a child node of the PCI controller to which the wireless chip is connected. Alternatively, it can specify the wireless part of the MT7628/MT7688 - or MT7622 SoC. + or MT7622/MT7986 SoC. allOf: - $ref: ieee80211.yaml# @@ -29,9 +29,13 @@ properties: - mediatek,mt76 - mediatek,mt7628-wmac - mediatek,mt7622-wmac + - mediatek,mt7986-wmac reg: - maxItems: 1 + minItems: 1 + maxItems: 3 + description: + MT7986 should contain 3 regions consys, dcm, and sku, in this order. interrupts: maxItems: 1 @@ -39,6 +43,17 @@ properties: power-domains: maxItems: 1 + memory-region: + maxItems: 1 + + resets: + maxItems: 1 + description: + Specify the consys reset for mt7986. + + reset-name: + const: consys + mediatek,infracfg: $ref: /schemas/types.yaml#/definitions/phandle description: @@ -69,6 +84,15 @@ properties: calibration data is generic and specific calibration data should be pulled from the OTP ROM + mediatek,disable-radar-background: + type: boolean + description: + Disable/enable radar/CAC detection running on a dedicated offchannel + chain available on some hw. + Background radar/CAC detection allows to avoid the CAC downtime + switching on a different channel during CAC detection on the selected + radar channel. + led: type: object $ref: /schemas/leds/common.yaml# @@ -165,7 +189,7 @@ required: - compatible - reg -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -231,3 +255,15 @@ examples: power-domains = <&scpsys 3>; }; + + - | + wifi@18000000 { + compatible = "mediatek,mt7986-wmac"; + resets = <&watchdog 23>; + reset-names = "consys"; + reg = <0x18000000 0x1000000>, + <0x10003000 0x1000>, + <0x11d10000 0x1000>; + interrupts = ; + memory-region = <&wmcpu_emi>; + }; diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml new file mode 100644 index 000000000000..4d91e2f4f247 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/fsl,lynx-28g.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale Lynx 28G SerDes PHY binding + +maintainers: + - Ioana Ciornei + +properties: + compatible: + enum: + - fsl,lynx-28g + + reg: + maxItems: 1 + + "#phy-cells": + const: 1 + +required: + - compatible + - reg + - "#phy-cells" + +additionalProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + serdes_1: phy@1ea0000 { + compatible = "fsl,lynx-28g"; + reg = <0x0 0x1ea0000 0x0 0x1e30>; + #phy-cells = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/phy/transmit-amplitude.yaml b/Documentation/devicetree/bindings/phy/transmit-amplitude.yaml new file mode 100644 index 000000000000..51492fe738ec --- /dev/null +++ b/Documentation/devicetree/bindings/phy/transmit-amplitude.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/transmit-amplitude.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Common PHY and network PCS transmit amplitude property binding + +description: + Binding describing the peak-to-peak transmit amplitude for common PHYs + and network PCSes. + +maintainers: + - Marek Behún + +properties: + tx-p2p-microvolt: + description: + Transmit amplitude voltages in microvolts, peak-to-peak. If this property + contains multiple values for various PHY modes, the + 'tx-p2p-microvolt-names' property must be provided and contain + corresponding mode names. + + tx-p2p-microvolt-names: + description: | + Names of the modes corresponding to voltages in the 'tx-p2p-microvolt' + property. Required only if multiple voltages are provided. + + If a value of 'default' is provided, the system should use it for any PHY + mode that is otherwise not defined here. If 'default' is not provided, the + system should use manufacturer default value. + minItems: 1 + maxItems: 16 + items: + enum: + - default + + # ethernet modes + - sgmii + - qsgmii + - xgmii + - 1000base-x + - 2500base-x + - 5gbase-r + - rxaui + - xaui + - 10gbase-kr + - usxgmii + - 10gbase-r + - 25gbase-r + + # PCIe modes + - pcie + - pcie1 + - pcie2 + - pcie3 + - pcie4 + - pcie5 + - pcie6 + + # USB modes + - usb + - usb-ls + - usb-fs + - usb-hs + - usb-ss + - usb-ss+ + - usb-4 + + # storage modes + - sata + - ufs-hs + - ufs-hs-a + - ufs-hs-b + + # display modes + - lvds + - dp + - dp-rbr + - dp-hbr + - dp-hbr2 + - dp-hbr3 + - dp-uhbr-10 + - dp-uhbr-13.5 + - dp-uhbr-20 + + # camera modes + - mipi-dphy + - mipi-dphy-univ + - mipi-dphy-v2.5-univ + +dependencies: + tx-p2p-microvolt-names: [ tx-p2p-microvolt ] + +additionalProperties: true + +examples: + - | + phy: phy { + #phy-cells = <1>; + tx-p2p-microvolt = <915000>, <1100000>, <1200000>; + tx-p2p-microvolt-names = "2500base-x", "usb-hs", "usb-ss"; + }; diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst index ab98373535ea..525e6842dd33 100644 --- a/Documentation/networking/bonding.rst +++ b/Documentation/networking/bonding.rst @@ -313,6 +313,17 @@ arp_ip_target maximum number of targets that can be specified is 16. The default value is no IP addresses. +ns_ip6_target + + Specifies the IPv6 addresses to use as IPv6 monitoring peers when + arp_interval is > 0. These are the targets of the NS request + sent to determine the health of the link to the targets. + Specify these values in ffff:ffff::ffff:ffff format. Multiple IPv6 + addresses must be separated by a comma. At least one IPv6 + address must be given for NS/NA monitoring to function. The + maximum number of targets that can be specified is 16. The + default value is no IPv6 addresses. + arp_validate Specifies whether or not ARP probes and replies should be diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst index 443123772f44..c17cdb079611 100644 --- a/Documentation/networking/devlink/index.rst +++ b/Documentation/networking/devlink/index.rst @@ -4,6 +4,22 @@ Linux Devlink Documentation devlink is an API to expose device information and resources not directly related to any device class, such as chip-wide/switch-ASIC-wide configuration. +Locking +------- + +Driver facing APIs are currently transitioning to allow more explicit +locking. Drivers can use the existing ``devlink_*`` set of APIs, or +new APIs prefixed by ``devl_*``. The older APIs handle all the locking +in devlink core, but don't allow registration of most sub-objects once +the main devlink object is itself registered. The newer ``devl_*`` APIs assume +the devlink instance lock is already held. Drivers can take the instance +lock by calling ``devl_lock()``. It is also held in most of the callbacks. +Eventually all callbacks will be invoked under the devlink instance lock, +refer to the use of the ``DEVLINK_NL_FLAG_NO_LOCK`` flag in devlink core +to find out which callbacks are not converted, yet. + +Drivers are encouraged to use the devlink instance lock for their own needs. + Interface documentation ----------------------- diff --git a/Documentation/networking/dsa/sja1105.rst b/Documentation/networking/dsa/sja1105.rst index 29b1bae0cf00..e0219c1452ab 100644 --- a/Documentation/networking/dsa/sja1105.rst +++ b/Documentation/networking/dsa/sja1105.rst @@ -293,6 +293,33 @@ of dropped frames, which is a sum of frames dropped due to timing violations, lack of destination ports and MTU enforcement checks). Byte-level counters are not available. +Limitations +=========== + +The SJA1105 switch family always performs VLAN processing. When configured as +VLAN-unaware, frames carry a different VLAN tag internally, depending on +whether the port is standalone or under a VLAN-unaware bridge. + +The virtual link keys are always fixed at {MAC DA, VLAN ID, VLAN PCP}, but the +driver asks for the VLAN ID and VLAN PCP when the port is under a VLAN-aware +bridge. Otherwise, it fills in the VLAN ID and PCP automatically, based on +whether the port is standalone or in a VLAN-unaware bridge, and accepts only +"VLAN-unaware" tc-flower keys (MAC DA). + +The existing tc-flower keys that are offloaded using virtual links are no +longer operational after one of the following happens: + +- port was standalone and joins a bridge (VLAN-aware or VLAN-unaware) +- port is part of a bridge whose VLAN awareness state changes +- port was part of a bridge and becomes standalone +- port was standalone, but another port joins a VLAN-aware bridge and this + changes the global VLAN awareness state of the bridge + +The driver cannot veto all these operations, and it cannot update/remove the +existing tc-flower filters either. So for proper operation, the tc-flower +filters should be installed only after the forwarding configuration of the port +has been made, and removed by user space before making any changes to it. + Device Tree bindings and board design ===================================== diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 9d98e0511249..24d9be69065d 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -860,8 +860,17 @@ Kernel response contents: ``ETHTOOL_A_RINGS_RX_JUMBO`` u32 size of RX jumbo ring ``ETHTOOL_A_RINGS_TX`` u32 size of TX ring ``ETHTOOL_A_RINGS_RX_BUF_LEN`` u32 size of buffers on the ring + ``ETHTOOL_A_RINGS_TCP_DATA_SPLIT`` u8 TCP header / data split + ``ETHTOOL_A_RINGS_CQE_SIZE`` u32 Size of TX/RX CQE ==================================== ====== =========================== +``ETHTOOL_A_RINGS_TCP_DATA_SPLIT`` indicates whether the device is usable with +page-flipping TCP zero-copy receive (``getsockopt(TCP_ZEROCOPY_RECEIVE)``). +If enabled the device is configured to place frame headers and data into +separate buffers. The device configuration must make it possible to receive +full memory pages of data, for example because MTU is high enough or through +HW-GRO. + RINGS_SET ========= @@ -877,6 +886,7 @@ Request contents: ``ETHTOOL_A_RINGS_RX_JUMBO`` u32 size of RX jumbo ring ``ETHTOOL_A_RINGS_TX`` u32 size of TX ring ``ETHTOOL_A_RINGS_RX_BUF_LEN`` u32 size of buffers on the ring + ``ETHTOOL_A_RINGS_CQE_SIZE`` u32 Size of TX/RX CQE ==================================== ====== =========================== Kernel checks that requested ring sizes do not exceed limits reported by @@ -884,6 +894,15 @@ driver. Driver may impose additional constraints and may not suspport all attributes. +``ETHTOOL_A_RINGS_CQE_SIZE`` specifies the completion queue event size. +Completion queue events(CQE) are the events posted by NIC to indicate the +completion status of a packet when the packet is sent(like send success or +error) or received(like pointers to packet fragments). The CQE size parameter +enables to modify the CQE size other than default size if NIC supports it. +A bigger CQE can have more receive buffer pointers inturn NIC can transfer +a bigger frame from wire. Based on the NIC hardware, the overall completion +queue size can be adjusted in the driver if CQE size is modified. + CHANNELS_GET ============ diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst index 58bc8cd367c6..ce017136ab05 100644 --- a/Documentation/networking/index.rst +++ b/Documentation/networking/index.rst @@ -96,6 +96,7 @@ Contents: sctp secid seg6-sysctl + smc-sysctl statistics strparser switchdev diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index 2572eecc3e86..b0024aa7b051 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -878,6 +878,29 @@ tcp_min_tso_segs - INTEGER Default: 2 +tcp_tso_rtt_log - INTEGER + Adjustment of TSO packet sizes based on min_rtt + + Starting from linux-5.18, TCP autosizing can be tweaked + for flows having small RTT. + + Old autosizing was splitting the pacing budget to send 1024 TSO + per second. + + tso_packet_size = sk->sk_pacing_rate / 1024; + + With the new mechanism, we increase this TSO sizing using: + + distance = min_rtt_usec / (2^tcp_tso_rtt_log) + tso_packet_size += gso_max_size >> distance; + + This means that flows between very close hosts can use bigger + TSO packets, reducing their cpu costs. + + If you want to use the old autosizing, set this sysctl to 0. + + Default: 9 (2^9 = 512 usec) + tcp_pacing_ss_ratio - INTEGER sk->sk_pacing_rate is set by TCP stack using a ratio applied to current rate. (current_rate = cwnd * mss / srtt) diff --git a/Documentation/networking/mctp.rst b/Documentation/networking/mctp.rst index 46f74bffce0f..c628cb5406d2 100644 --- a/Documentation/networking/mctp.rst +++ b/Documentation/networking/mctp.rst @@ -212,6 +212,54 @@ remote address is already known, or the message does not require a reply. Like the send calls, sockets will only receive responses to requests they have sent (TO=1) and may only respond (TO=0) to requests they have received. +``ioctl(SIOCMCTPALLOCTAG)`` and ``ioctl(SIOCMCTPDROPTAG)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These tags give applications more control over MCTP message tags, by allocating +(and dropping) tag values explicitly, rather than the kernel automatically +allocating a per-message tag at ``sendmsg()`` time. + +In general, you will only need to use these ioctls if your MCTP protocol does +not fit the usual request/response model. For example, if you need to persist +tags across multiple requests, or a request may generate more than one response. +In these cases, the ioctls allow you to decouple the tag allocation (and +release) from individual message send and receive operations. + +Both ioctls are passed a pointer to a ``struct mctp_ioc_tag_ctl``: + +.. code-block:: C + + struct mctp_ioc_tag_ctl { + mctp_eid_t peer_addr; + __u8 tag; + __u16 flags; + }; + +``SIOCMCTPALLOCTAG`` allocates a tag for a specific peer, which an application +can use in future ``sendmsg()`` calls. The application populates the +``peer_addr`` member with the remote EID. Other fields must be zero. + +On return, the ``tag`` member will be populated with the allocated tag value. +The allocated tag will have the following tag bits set: + + - ``MCTP_TAG_OWNER``: it only makes sense to allocate tags if you're the tag + owner + + - ``MCTP_TAG_PREALLOC``: to indicate to ``sendmsg()`` that this is a + preallocated tag. + + - ... and the actual tag value, within the least-significant three bits + (``MCTP_TAG_MASK``). Note that zero is a valid tag value. + +The tag value should be used as-is for the ``smctp_tag`` member of ``struct +sockaddr_mctp``. + +``SIOCMCTPDROPTAG`` releases a tag that has been previously allocated by a +``SIOCMCTPALLOCTAG`` ioctl. The ``peer_addr`` must be the same as used for the +allocation, and the ``tag`` value must match exactly the tag returned from the +allocation (including the ``MCTP_TAG_OWNER`` and ``MCTP_TAG_PREALLOC`` bits). +The ``flags`` field must be zero. + Kernel internals ================ diff --git a/Documentation/networking/page_pool.rst b/Documentation/networking/page_pool.rst index a147591ce203..5db8c263b0c6 100644 --- a/Documentation/networking/page_pool.rst +++ b/Documentation/networking/page_pool.rst @@ -105,6 +105,47 @@ a page will cause no race conditions is enough. Please note the caller must not use data area after running page_pool_put_page_bulk(), as this function overwrites it. +* page_pool_get_stats(): Retrieve statistics about the page_pool. This API + is only available if the kernel has been configured with + ``CONFIG_PAGE_POOL_STATS=y``. A pointer to a caller allocated ``struct + page_pool_stats`` structure is passed to this API which is filled in. The + caller can then report those stats to the user (perhaps via ethtool, + debugfs, etc.). See below for an example usage of this API. + +Stats API and structures +------------------------ +If the kernel is configured with ``CONFIG_PAGE_POOL_STATS=y``, the API +``page_pool_get_stats()`` and structures described below are available. It +takes a pointer to a ``struct page_pool`` and a pointer to a ``struct +page_pool_stats`` allocated by the caller. + +The API will fill in the provided ``struct page_pool_stats`` with +statistics about the page_pool. + +The stats structure has the following fields:: + + struct page_pool_stats { + struct page_pool_alloc_stats alloc_stats; + struct page_pool_recycle_stats recycle_stats; + }; + + +The ``struct page_pool_alloc_stats`` has the following fields: + * ``fast``: successful fast path allocations + * ``slow``: slow path order-0 allocations + * ``slow_high_order``: slow path high order allocations + * ``empty``: ptr ring is empty, so a slow path allocation was forced. + * ``refill``: an allocation which triggered a refill of the cache + * ``waive``: pages obtained from the ptr ring that cannot be added to + the cache due to a NUMA mismatch. + +The ``struct page_pool_recycle_stats`` has the following fields: + * ``cached``: recycling placed page in the page pool cache + * ``cache_full``: page pool cache was full + * ``ring``: page placed into the ptr ring + * ``ring_full``: page released from page pool because the ptr ring was full + * ``released_refcnt``: page released (and not recycled) because refcnt > 1 + Coding examples =============== @@ -157,6 +198,21 @@ NAPI poller } } +Stats +----- + +.. code-block:: c + + #ifdef CONFIG_PAGE_POOL_STATS + /* retrieve stats */ + struct page_pool_stats stats = { 0 }; + if (page_pool_get_stats(page_pool, &stats)) { + /* perhaps the driver reports statistics with ethool */ + ethtool_print_allocation_stats(&stats.alloc_stats); + ethtool_print_recycle_stats(&stats.recycle_stats); + } + #endif + Driver unload ------------- diff --git a/Documentation/networking/smc-sysctl.rst b/Documentation/networking/smc-sysctl.rst new file mode 100644 index 000000000000..0987fd1bc220 --- /dev/null +++ b/Documentation/networking/smc-sysctl.rst @@ -0,0 +1,23 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========== +SMC Sysctl +========== + +/proc/sys/net/smc/* Variables +============================= + +autocorking_size - INTEGER + Setting SMC auto corking size: + SMC auto corking is like TCP auto corking from the application's + perspective of view. When applications do consecutive small + write()/sendmsg() system calls, we try to coalesce these small writes + as much as possible, to lower total amount of CDC and RDMA Write been + sent. + autocorking_size limits the maximum corked bytes that can be sent to + the under device in 1 single sending. If set to 0, the SMC auto corking + is disabled. + Applications can still use TCP_CORK for optimal behavior when they + know how/when to uncork their sockets. + + Default: 64K diff --git a/Documentation/networking/timestamping.rst b/Documentation/networking/timestamping.rst index f5809206eb93..be4eb1242057 100644 --- a/Documentation/networking/timestamping.rst +++ b/Documentation/networking/timestamping.rst @@ -668,7 +668,7 @@ timestamping: (through another RX timestamping FIFO). Deferral on RX is typically necessary when retrieving the timestamp needs a sleepable context. In that case, it is the responsibility of the DSA driver to call - ``netif_rx_ni()`` on the freshly timestamped skb. + ``netif_rx()`` on the freshly timestamped skb. 3.2.2 Ethernet PHYs ^^^^^^^^^^^^^^^^^^^ diff --git a/Documentation/trace/fprobe.rst b/Documentation/trace/fprobe.rst new file mode 100644 index 000000000000..b64bec1ce144 --- /dev/null +++ b/Documentation/trace/fprobe.rst @@ -0,0 +1,174 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================================== +Fprobe - Function entry/exit probe +================================== + +.. Author: Masami Hiramatsu + +Introduction +============ + +Fprobe is a function entry/exit probe mechanism based on ftrace. +Instead of using ftrace full feature, if you only want to attach callbacks +on function entry and exit, similar to the kprobes and kretprobes, you can +use fprobe. Compared with kprobes and kretprobes, fprobe gives faster +instrumentation for multiple functions with single handler. This document +describes how to use fprobe. + +The usage of fprobe +=================== + +The fprobe is a wrapper of ftrace (+ kretprobe-like return callback) to +attach callbacks to multiple function entry and exit. User needs to set up +the `struct fprobe` and pass it to `register_fprobe()`. + +Typically, `fprobe` data structure is initialized with the `entry_handler` +and/or `exit_handler` as below. + +.. code-block:: c + + struct fprobe fp = { + .entry_handler = my_entry_callback, + .exit_handler = my_exit_callback, + }; + +To enable the fprobe, call one of register_fprobe(), register_fprobe_ips(), and +register_fprobe_syms(). These functions register the fprobe with different types +of parameters. + +The register_fprobe() enables a fprobe by function-name filters. +E.g. this enables @fp on "func*()" function except "func2()".:: + + register_fprobe(&fp, "func*", "func2"); + +The register_fprobe_ips() enables a fprobe by ftrace-location addresses. +E.g. + +.. code-block:: c + + unsigned long ips[] = { 0x.... }; + + register_fprobe_ips(&fp, ips, ARRAY_SIZE(ips)); + +And the register_fprobe_syms() enables a fprobe by symbol names. +E.g. + +.. code-block:: c + + char syms[] = {"func1", "func2", "func3"}; + + register_fprobe_syms(&fp, syms, ARRAY_SIZE(syms)); + +To disable (remove from functions) this fprobe, call:: + + unregister_fprobe(&fp); + +You can temporally (soft) disable the fprobe by:: + + disable_fprobe(&fp); + +and resume by:: + + enable_fprobe(&fp); + +The above is defined by including the header:: + + #include + +Same as ftrace, the registered callbacks will start being called some time +after the register_fprobe() is called and before it returns. See +:file:`Documentation/trace/ftrace.rst`. + +Also, the unregister_fprobe() will guarantee that the both enter and exit +handlers are no longer being called by functions after unregister_fprobe() +returns as same as unregister_ftrace_function(). + +The fprobe entry/exit handler +============================= + +The prototype of the entry/exit callback function is as follows: + +.. code-block:: c + + void callback_func(struct fprobe *fp, unsigned long entry_ip, struct pt_regs *regs); + +Note that both entry and exit callbacks have same ptototype. The @entry_ip is +saved at function entry and passed to exit handler. + +@fp + This is the address of `fprobe` data structure related to this handler. + You can embed the `fprobe` to your data structure and get it by + container_of() macro from @fp. The @fp must not be NULL. + +@entry_ip + This is the ftrace address of the traced function (both entry and exit). + Note that this may not be the actual entry address of the function but + the address where the ftrace is instrumented. + +@regs + This is the `pt_regs` data structure at the entry and exit. Note that + the instruction pointer of @regs may be different from the @entry_ip + in the entry_handler. If you need traced instruction pointer, you need + to use @entry_ip. On the other hand, in the exit_handler, the instruction + pointer of @regs is set to the currect return address. + +Share the callbacks with kprobes +================================ + +Since the recursion safeness of the fprobe (and ftrace) is a bit different +from the kprobes, this may cause an issue if user wants to run the same +code from the fprobe and the kprobes. + +Kprobes has per-cpu 'current_kprobe' variable which protects the kprobe +handler from recursion in all cases. On the other hand, fprobe uses +only ftrace_test_recursion_trylock(). This allows interrupt context to +call another (or same) fprobe while the fprobe user handler is running. + +This is not a matter if the common callback code has its own recursion +detection, or it can handle the recursion in the different contexts +(normal/interrupt/NMI.) +But if it relies on the 'current_kprobe' recursion lock, it has to check +kprobe_running() and use kprobe_busy_*() APIs. + +Fprobe has FPROBE_FL_KPROBE_SHARED flag to do this. If your common callback +code will be shared with kprobes, please set FPROBE_FL_KPROBE_SHARED +*before* registering the fprobe, like: + +.. code-block:: c + + fprobe.flags = FPROBE_FL_KPROBE_SHARED; + + register_fprobe(&fprobe, "func*", NULL); + +This will protect your common callback from the nested call. + +The missed counter +================== + +The `fprobe` data structure has `fprobe::nmissed` counter field as same as +kprobes. +This counter counts up when; + + - fprobe fails to take ftrace_recursion lock. This usually means that a function + which is traced by other ftrace users is called from the entry_handler. + + - fprobe fails to setup the function exit because of the shortage of rethook + (the shadow stack for hooking the function return.) + +The `fprobe::nmissed` field counts up in both cases. Therefore, the former +skips both of entry and exit callback and the latter skips the exit +callback, but in both case the counter will increase by 1. + +Note that if you set the FTRACE_OPS_FL_RECURSION and/or FTRACE_OPS_FL_RCU to +`fprobe::ops::flags` (ftrace_ops::flags) when registering the fprobe, this +counter may not work correctly, because ftrace skips the fprobe function which +increase the counter. + + +Functions and structures +======================== + +.. kernel-doc:: include/linux/fprobe.h +.. kernel-doc:: kernel/trace/fprobe.c + diff --git a/Documentation/trace/index.rst b/Documentation/trace/index.rst index 3a47aa8341c6..f9b7bcb5a630 100644 --- a/Documentation/trace/index.rst +++ b/Documentation/trace/index.rst @@ -9,6 +9,7 @@ Linux Tracing Technologies tracepoint-analysis ftrace ftrace-uses + fprobe kprobes kprobetrace uprobetracer diff --git a/MAINTAINERS b/MAINTAINERS index 3230ad5db30c..91c04cb65247 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3538,6 +3538,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/ @@ -3830,9 +3832,6 @@ BROADCOM BRCM80211 IEEE802.11n WIRELESS DRIVER M: Arend van Spriel M: Franky Lin M: Hante Meuleman -M: Chi-hsien Lin -M: Wright Feng -M: Chung-hsien Hsu L: linux-wireless@vger.kernel.org L: brcm80211-dev-list.pdl@broadcom.com L: SHA-cyfmac-dev-list@infineon.com @@ -7949,6 +7948,12 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/fujitsu-tablet.c +FUNGIBLE ETHERNET DRIVERS +M: Dimitris Michailidis +L: netdev@vger.kernel.org +S: Supported +F: drivers/net/ethernet/fungible/ + FUSE: FILESYSTEM IN USERSPACE M: Miklos Szeredi L: linux-fsdevel@vger.kernel.org @@ -10809,7 +10814,6 @@ L7 BPF FRAMEWORK M: John Fastabend M: Daniel Borkmann M: Jakub Sitnicki -M: Lorenz Bauer L: netdev@vger.kernel.org L: bpf@vger.kernel.org S: Maintained @@ -11377,6 +11381,13 @@ S: Maintained W: http://linux-test-project.github.io/ T: git git://github.com/linux-test-project/ltp.git +LYNX 28G SERDES PHY DRIVER +M: Ioana Ciornei +L: netdev@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml +F: drivers/phy/freescale/phy-fsl-lynx-28g.c + LYNX PCS MODULE M: Ioana Ciornei L: netdev@vger.kernel.org @@ -12217,6 +12228,7 @@ R: Shayne Chen R: Sean Wang L: linux-wireless@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml F: drivers/net/wireless/mediatek/mt76/ MEDIATEK MT7601U WIRELESS LAN DRIVER @@ -15996,8 +16008,8 @@ M: Kalle Valo L: ath11k@lists.infradead.org S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git +F: Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml F: drivers/net/wireless/ath/ath11k/ -F: Documentation/devicetree/bindings/net/wireless/qcom,ath11k.txt QUALCOMM ATHEROS ATH9K WIRELESS DRIVER M: Toke Høiland-Jørgensen @@ -16446,9 +16458,8 @@ REALTEK RTL83xx SMI DSA ROUTER CHIPS M: Linus Walleij M: Alvin Å ipraga S: Maintained -F: Documentation/devicetree/bindings/net/dsa/realtek-smi.txt -F: drivers/net/dsa/realtek-smi* -F: drivers/net/dsa/rtl83* +F: Documentation/devicetree/bindings/net/dsa/realtek.yaml +F: drivers/net/dsa/realtek/* REALTEK WIRELESS DRIVER (rtlwifi family) M: Ping-Ke Shih diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h index 284d28755b8d..7d81535893af 100644 --- a/arch/alpha/include/uapi/asm/socket.h +++ b/arch/alpha/include/uapi/asm/socket.h @@ -133,6 +133,8 @@ #define SO_RESERVE_MEM 73 +#define SO_TXREHASH 74 + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64 diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c index 10ceebb7530b..9e457156ad4d 100644 --- a/arch/arm/net/bpf_jit_32.c +++ b/arch/arm/net/bpf_jit_32.c @@ -1864,7 +1864,7 @@ static int build_body(struct jit_ctx *ctx) if (ctx->target == NULL) ctx->offsets[i] = ctx->idx; - /* If unsuccesfull, return with error code */ + /* If unsuccesful, return with error code */ if (ret) return ret; } @@ -1973,7 +1973,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) * for jit, although it can decrease the size of the image. * * As each arm instruction is of length 32bit, we are translating - * number of JITed intructions into the size required to store these + * number of JITed instructions into the size required to store these * JITed code. */ image_size = sizeof(u32) * ctx.idx; diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-itx.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-itx.dtsi index 17f8e733972a..41702e7386e3 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-itx.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-itx.dtsi @@ -63,21 +63,25 @@ sfp3: sfp-3 { &dpmac7 { sfp = <&sfp0>; managed = "in-band-status"; + phys = <&serdes_1 3>; }; &dpmac8 { sfp = <&sfp1>; managed = "in-band-status"; + phys = <&serdes_1 2>; }; &dpmac9 { sfp = <&sfp2>; managed = "in-band-status"; + phys = <&serdes_1 1>; }; &dpmac10 { sfp = <&sfp3>; managed = "in-band-status"; + phys = <&serdes_1 0>; }; &emdio2 { diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi index 3c611cb4f5fe..c5daa15b020d 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi @@ -612,6 +612,12 @@ soc { ranges; dma-ranges = <0x0 0x0 0x0 0x0 0x10000 0x00000000>; + serdes_1: phy@1ea0000 { + compatible = "fsl,lynx-28g"; + reg = <0x0 0x1ea0000 0x0 0x1e30>; + #phy-cells = <1>; + }; + crypto: crypto@8000000 { compatible = "fsl,sec-v5.0", "fsl,sec-v4.0"; fsl,sec-era = <10>; diff --git a/arch/arm64/boot/dts/mediatek/mt2712-evb.dts b/arch/arm64/boot/dts/mediatek/mt2712-evb.dts index 7d369fdd3117..11aa135aa0f3 100644 --- a/arch/arm64/boot/dts/mediatek/mt2712-evb.dts +++ b/arch/arm64/boot/dts/mediatek/mt2712-evb.dts @@ -110,6 +110,7 @@ ð { phy-handle = <ðernet_phy0>; mediatek,tx-delay-ps = <1530>; snps,reset-gpio = <&pio 87 GPIO_ACTIVE_LOW>; + snps,reset-delays-us = <0 10000 10000>; pinctrl-names = "default", "sleep"; pinctrl-0 = <ð_default>; pinctrl-1 = <ð_sleep>; diff --git a/arch/arm64/boot/dts/mediatek/mt2712e.dtsi b/arch/arm64/boot/dts/mediatek/mt2712e.dtsi index de16c0d80c30..a27b7628c5f7 100644 --- a/arch/arm64/boot/dts/mediatek/mt2712e.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt2712e.dtsi @@ -726,7 +726,7 @@ queue2 { }; eth: ethernet@1101c000 { - compatible = "mediatek,mt2712-gmac"; + compatible = "mediatek,mt2712-gmac", "snps,dwmac-4.20a"; reg = <0 0x1101c000 0 0x1300>; interrupts = ; interrupt-names = "macirq"; @@ -734,15 +734,19 @@ eth: ethernet@1101c000 { clock-names = "axi", "apb", "mac_main", - "ptp_ref"; + "ptp_ref", + "rmii_internal"; clocks = <&pericfg CLK_PERI_GMAC>, <&pericfg CLK_PERI_GMAC_PCLK>, <&topckgen CLK_TOP_ETHER_125M_SEL>, - <&topckgen CLK_TOP_ETHER_50M_SEL>; + <&topckgen CLK_TOP_ETHER_50M_SEL>, + <&topckgen CLK_TOP_ETHER_50M_RMII_SEL>; assigned-clocks = <&topckgen CLK_TOP_ETHER_125M_SEL>, - <&topckgen CLK_TOP_ETHER_50M_SEL>; + <&topckgen CLK_TOP_ETHER_50M_SEL>, + <&topckgen CLK_TOP_ETHER_50M_RMII_SEL>; assigned-clock-parents = <&topckgen CLK_TOP_ETHERPLL_125M>, - <&topckgen CLK_TOP_APLL1_D3>; + <&topckgen CLK_TOP_APLL1_D3>, + <&topckgen CLK_TOP_ETHERPLL_50M>; power-domains = <&scpsys MT2712_POWER_DOMAIN_AUDIO>; mediatek,pericfg = <&pericfg>; snps,axi-config = <&stmmac_axi_setup>; diff --git a/arch/arm64/boot/dts/microchip/sparx5.dtsi b/arch/arm64/boot/dts/microchip/sparx5.dtsi index 787ebcec121d..2dd5e38820b1 100644 --- a/arch/arm64/boot/dts/microchip/sparx5.dtsi +++ b/arch/arm64/boot/dts/microchip/sparx5.dtsi @@ -471,9 +471,10 @@ switch: switch@0x600000000 { <0x6 0x10004000 0x7fc000>, <0x6 0x11010000 0xaf0000>; reg-names = "cpu", "dev", "gcb"; - interrupt-names = "xtr", "fdma"; + interrupt-names = "xtr", "fdma", "ptp"; interrupts = , - ; + , + ; resets = <&reset 0>; reset-names = "switch"; }; diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi index 3e15391e5b37..056761c974fd 100644 --- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi +++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi @@ -510,6 +510,8 @@ gem0: ethernet@ff0b0000 { #size-cells = <0>; iommus = <&smmu 0x874>; power-domains = <&zynqmp_firmware PD_ETH_0>; + resets = <&zynqmp_reset ZYNQMP_RESET_GEM0>; + reset-names = "gem0_rst"; }; gem1: ethernet@ff0c0000 { @@ -523,6 +525,8 @@ gem1: ethernet@ff0c0000 { #size-cells = <0>; iommus = <&smmu 0x875>; power-domains = <&zynqmp_firmware PD_ETH_1>; + resets = <&zynqmp_reset ZYNQMP_RESET_GEM1>; + reset-names = "gem1_rst"; }; gem2: ethernet@ff0d0000 { @@ -536,6 +540,8 @@ gem2: ethernet@ff0d0000 { #size-cells = <0>; iommus = <&smmu 0x876>; power-domains = <&zynqmp_firmware PD_ETH_2>; + resets = <&zynqmp_reset ZYNQMP_RESET_GEM2>; + reset-names = "gem2_rst"; }; gem3: ethernet@ff0e0000 { @@ -549,6 +555,8 @@ gem3: ethernet@ff0e0000 { #size-cells = <0>; iommus = <&smmu 0x877>; power-domains = <&zynqmp_firmware PD_ETH_3>; + resets = <&zynqmp_reset ZYNQMP_RESET_GEM3>; + reset-names = "gem3_rst"; }; gpio: gpio@ff0a0000 { diff --git a/arch/arm64/net/bpf_jit.h b/arch/arm64/net/bpf_jit.h index 9d9250c7cc72..dd59b5ad8fe4 100644 --- a/arch/arm64/net/bpf_jit.h +++ b/arch/arm64/net/bpf_jit.h @@ -88,17 +88,42 @@ /* [Rn] = Rt; (atomic) Rs = [state] */ #define A64_STXR(sf, Rt, Rn, Rs) \ A64_LSX(sf, Rt, Rn, Rs, STORE_EX) +/* [Rn] = Rt (store release); (atomic) Rs = [state] */ +#define A64_STLXR(sf, Rt, Rn, Rs) \ + aarch64_insn_gen_load_store_ex(Rt, Rn, Rs, A64_SIZE(sf), \ + AARCH64_INSN_LDST_STORE_REL_EX) /* * LSE atomics * - * STADD is simply encoded as an alias for LDADD with XZR as - * the destination register. + * ST{ADD,CLR,SET,EOR} is simply encoded as an alias for + * LDD{ADD,CLR,SET,EOR} with XZR as the destination register. */ -#define A64_STADD(sf, Rn, Rs) \ +#define A64_ST_OP(sf, Rn, Rs, op) \ aarch64_insn_gen_atomic_ld_op(A64_ZR, Rn, Rs, \ - A64_SIZE(sf), AARCH64_INSN_MEM_ATOMIC_ADD, \ + A64_SIZE(sf), AARCH64_INSN_MEM_ATOMIC_##op, \ AARCH64_INSN_MEM_ORDER_NONE) +/* [Rn] = Rs */ +#define A64_STADD(sf, Rn, Rs) A64_ST_OP(sf, Rn, Rs, ADD) +#define A64_STCLR(sf, Rn, Rs) A64_ST_OP(sf, Rn, Rs, CLR) +#define A64_STEOR(sf, Rn, Rs) A64_ST_OP(sf, Rn, Rs, EOR) +#define A64_STSET(sf, Rn, Rs) A64_ST_OP(sf, Rn, Rs, SET) + +#define A64_LD_OP_AL(sf, Rt, Rn, Rs, op) \ + aarch64_insn_gen_atomic_ld_op(Rt, Rn, Rs, \ + A64_SIZE(sf), AARCH64_INSN_MEM_ATOMIC_##op, \ + AARCH64_INSN_MEM_ORDER_ACQREL) +/* Rt = [Rn] (load acquire); [Rn] = Rs (store release) */ +#define A64_LDADDAL(sf, Rt, Rn, Rs) A64_LD_OP_AL(sf, Rt, Rn, Rs, ADD) +#define A64_LDCLRAL(sf, Rt, Rn, Rs) A64_LD_OP_AL(sf, Rt, Rn, Rs, CLR) +#define A64_LDEORAL(sf, Rt, Rn, Rs) A64_LD_OP_AL(sf, Rt, Rn, Rs, EOR) +#define A64_LDSETAL(sf, Rt, Rn, Rs) A64_LD_OP_AL(sf, Rt, Rn, Rs, SET) +/* Rt = [Rn] (load acquire); [Rn] = Rs (store release) */ +#define A64_SWPAL(sf, Rt, Rn, Rs) A64_LD_OP_AL(sf, Rt, Rn, Rs, SWP) +/* Rs = CAS(Rn, Rs, Rt) (load acquire & store release) */ +#define A64_CASAL(sf, Rt, Rn, Rs) \ + aarch64_insn_gen_cas(Rt, Rn, Rs, A64_SIZE(sf), \ + AARCH64_INSN_MEM_ORDER_ACQREL) /* Add/subtract (immediate) */ #define A64_ADDSUB_IMM(sf, Rd, Rn, imm12, type) \ @@ -203,6 +228,9 @@ #define A64_ANDS(sf, Rd, Rn, Rm) A64_LOGIC_SREG(sf, Rd, Rn, Rm, AND_SETFLAGS) /* Rn & Rm; set condition flags */ #define A64_TST(sf, Rn, Rm) A64_ANDS(sf, A64_ZR, Rn, Rm) +/* Rd = ~Rm (alias of ORN with A64_ZR as Rn) */ +#define A64_MVN(sf, Rd, Rm) \ + A64_LOGIC_SREG(sf, Rd, A64_ZR, Rm, ORN) /* Logical (immediate) */ #define A64_LOGIC_IMM(sf, Rd, Rn, imm, type) ({ \ @@ -226,4 +254,7 @@ #define A64_BTI_J A64_HINT(AARCH64_INSN_HINT_BTIJ) #define A64_BTI_JC A64_HINT(AARCH64_INSN_HINT_BTIJC) +/* DMB */ +#define A64_DMB_ISH aarch64_insn_gen_dmb(AARCH64_INSN_MB_ISH) + #endif /* _BPF_JIT_H */ diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index e96d4d87291f..e850c69e128c 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -27,6 +27,17 @@ #define TCALL_CNT (MAX_BPF_JIT_REG + 2) #define TMP_REG_3 (MAX_BPF_JIT_REG + 3) +#define check_imm(bits, imm) do { \ + if ((((imm) > 0) && ((imm) >> (bits))) || \ + (((imm) < 0) && (~(imm) >> (bits)))) { \ + pr_info("[%2d] imm=%d(0x%x) out of range\n", \ + i, imm, imm); \ + return -EINVAL; \ + } \ +} while (0) +#define check_imm19(imm) check_imm(19, imm) +#define check_imm26(imm) check_imm(26, imm) + /* Map BPF registers to A64 registers */ static const int bpf2a64[] = { /* return value from in-kernel function, and exit value from eBPF */ @@ -329,6 +340,170 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx) #undef jmp_offset } +#ifdef CONFIG_ARM64_LSE_ATOMICS +static int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) +{ + const u8 code = insn->code; + const u8 dst = bpf2a64[insn->dst_reg]; + const u8 src = bpf2a64[insn->src_reg]; + const u8 tmp = bpf2a64[TMP_REG_1]; + const u8 tmp2 = bpf2a64[TMP_REG_2]; + const bool isdw = BPF_SIZE(code) == BPF_DW; + const s16 off = insn->off; + u8 reg; + + if (!off) { + reg = dst; + } else { + emit_a64_mov_i(1, tmp, off, ctx); + emit(A64_ADD(1, tmp, tmp, dst), ctx); + reg = tmp; + } + + switch (insn->imm) { + /* lock *(u32/u64 *)(dst_reg + off) = src_reg */ + case BPF_ADD: + emit(A64_STADD(isdw, reg, src), ctx); + break; + case BPF_AND: + emit(A64_MVN(isdw, tmp2, src), ctx); + emit(A64_STCLR(isdw, reg, tmp2), ctx); + break; + case BPF_OR: + emit(A64_STSET(isdw, reg, src), ctx); + break; + case BPF_XOR: + emit(A64_STEOR(isdw, reg, src), ctx); + break; + /* src_reg = atomic_fetch_(dst_reg + off, src_reg) */ + case BPF_ADD | BPF_FETCH: + emit(A64_LDADDAL(isdw, src, reg, src), ctx); + break; + case BPF_AND | BPF_FETCH: + emit(A64_MVN(isdw, tmp2, src), ctx); + emit(A64_LDCLRAL(isdw, src, reg, tmp2), ctx); + break; + case BPF_OR | BPF_FETCH: + emit(A64_LDSETAL(isdw, src, reg, src), ctx); + break; + case BPF_XOR | BPF_FETCH: + emit(A64_LDEORAL(isdw, src, reg, src), ctx); + break; + /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ + case BPF_XCHG: + emit(A64_SWPAL(isdw, src, reg, src), ctx); + break; + /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ + case BPF_CMPXCHG: + emit(A64_CASAL(isdw, src, reg, bpf2a64[BPF_REG_0]), ctx); + break; + default: + pr_err_once("unknown atomic op code %02x\n", insn->imm); + return -EINVAL; + } + + return 0; +} +#else +static inline int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) +{ + return -EINVAL; +} +#endif + +static int emit_ll_sc_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) +{ + const u8 code = insn->code; + const u8 dst = bpf2a64[insn->dst_reg]; + const u8 src = bpf2a64[insn->src_reg]; + const u8 tmp = bpf2a64[TMP_REG_1]; + const u8 tmp2 = bpf2a64[TMP_REG_2]; + const u8 tmp3 = bpf2a64[TMP_REG_3]; + const int i = insn - ctx->prog->insnsi; + const s32 imm = insn->imm; + const s16 off = insn->off; + const bool isdw = BPF_SIZE(code) == BPF_DW; + u8 reg; + s32 jmp_offset; + + if (!off) { + reg = dst; + } else { + emit_a64_mov_i(1, tmp, off, ctx); + emit(A64_ADD(1, tmp, tmp, dst), ctx); + reg = tmp; + } + + if (imm == BPF_ADD || imm == BPF_AND || + imm == BPF_OR || imm == BPF_XOR) { + /* lock *(u32/u64 *)(dst_reg + off) = src_reg */ + emit(A64_LDXR(isdw, tmp2, reg), ctx); + if (imm == BPF_ADD) + emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); + else if (imm == BPF_AND) + emit(A64_AND(isdw, tmp2, tmp2, src), ctx); + else if (imm == BPF_OR) + emit(A64_ORR(isdw, tmp2, tmp2, src), ctx); + else + emit(A64_EOR(isdw, tmp2, tmp2, src), ctx); + emit(A64_STXR(isdw, tmp2, reg, tmp3), ctx); + jmp_offset = -3; + check_imm19(jmp_offset); + emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); + } else if (imm == (BPF_ADD | BPF_FETCH) || + imm == (BPF_AND | BPF_FETCH) || + imm == (BPF_OR | BPF_FETCH) || + imm == (BPF_XOR | BPF_FETCH)) { + /* src_reg = atomic_fetch_(dst_reg + off, src_reg) */ + const u8 ax = bpf2a64[BPF_REG_AX]; + + emit(A64_MOV(isdw, ax, src), ctx); + emit(A64_LDXR(isdw, src, reg), ctx); + if (imm == (BPF_ADD | BPF_FETCH)) + emit(A64_ADD(isdw, tmp2, src, ax), ctx); + else if (imm == (BPF_AND | BPF_FETCH)) + emit(A64_AND(isdw, tmp2, src, ax), ctx); + else if (imm == (BPF_OR | BPF_FETCH)) + emit(A64_ORR(isdw, tmp2, src, ax), ctx); + else + emit(A64_EOR(isdw, tmp2, src, ax), ctx); + emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); + jmp_offset = -3; + check_imm19(jmp_offset); + emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); + emit(A64_DMB_ISH, ctx); + } else if (imm == BPF_XCHG) { + /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ + emit(A64_MOV(isdw, tmp2, src), ctx); + emit(A64_LDXR(isdw, src, reg), ctx); + emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); + jmp_offset = -2; + check_imm19(jmp_offset); + emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); + emit(A64_DMB_ISH, ctx); + } else if (imm == BPF_CMPXCHG) { + /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ + const u8 r0 = bpf2a64[BPF_REG_0]; + + emit(A64_MOV(isdw, tmp2, r0), ctx); + emit(A64_LDXR(isdw, r0, reg), ctx); + emit(A64_EOR(isdw, tmp3, r0, tmp2), ctx); + jmp_offset = 4; + check_imm19(jmp_offset); + emit(A64_CBNZ(isdw, tmp3, jmp_offset), ctx); + emit(A64_STLXR(isdw, src, reg, tmp3), ctx); + jmp_offset = -4; + check_imm19(jmp_offset); + emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); + emit(A64_DMB_ISH, ctx); + } else { + pr_err_once("unknown atomic op code %02x\n", imm); + return -EINVAL; + } + + return 0; +} + static void build_epilogue(struct jit_ctx *ctx) { const u8 r0 = bpf2a64[BPF_REG_0]; @@ -434,29 +609,16 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, const u8 src = bpf2a64[insn->src_reg]; const u8 tmp = bpf2a64[TMP_REG_1]; const u8 tmp2 = bpf2a64[TMP_REG_2]; - const u8 tmp3 = bpf2a64[TMP_REG_3]; const s16 off = insn->off; const s32 imm = insn->imm; const int i = insn - ctx->prog->insnsi; const bool is64 = BPF_CLASS(code) == BPF_ALU64 || BPF_CLASS(code) == BPF_JMP; - const bool isdw = BPF_SIZE(code) == BPF_DW; - u8 jmp_cond, reg; + u8 jmp_cond; s32 jmp_offset; u32 a64_insn; int ret; -#define check_imm(bits, imm) do { \ - if ((((imm) > 0) && ((imm) >> (bits))) || \ - (((imm) < 0) && (~(imm) >> (bits)))) { \ - pr_info("[%2d] imm=%d(0x%x) out of range\n", \ - i, imm, imm); \ - return -EINVAL; \ - } \ -} while (0) -#define check_imm19(imm) check_imm(19, imm) -#define check_imm26(imm) check_imm(26, imm) - switch (code) { /* dst = src */ case BPF_ALU | BPF_MOV | BPF_X: @@ -891,33 +1053,12 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, case BPF_STX | BPF_ATOMIC | BPF_W: case BPF_STX | BPF_ATOMIC | BPF_DW: - if (insn->imm != BPF_ADD) { - pr_err_once("unknown atomic op code %02x\n", insn->imm); - return -EINVAL; - } - - /* STX XADD: lock *(u32 *)(dst + off) += src - * and - * STX XADD: lock *(u64 *)(dst + off) += src - */ - - if (!off) { - reg = dst; - } else { - emit_a64_mov_i(1, tmp, off, ctx); - emit(A64_ADD(1, tmp, tmp, dst), ctx); - reg = tmp; - } - if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) { - emit(A64_STADD(isdw, reg, src), ctx); - } else { - emit(A64_LDXR(isdw, tmp2, reg), ctx); - emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); - emit(A64_STXR(isdw, tmp2, reg, tmp3), ctx); - jmp_offset = -3; - check_imm19(jmp_offset); - emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); - } + if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) + ret = emit_lse_atomic(insn, ctx); + else + ret = emit_ll_sc_atomic(insn, ctx); + if (ret) + return ret; break; default: @@ -1049,15 +1190,18 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) goto out_off; } - /* 1. Initial fake pass to compute ctx->idx. */ - - /* Fake pass to fill in ctx->offset. */ - if (build_body(&ctx, extra_pass)) { + /* + * 1. Initial fake pass to compute ctx->idx and ctx->offset. + * + * BPF line info needs ctx->offset[i] to be the offset of + * instruction[i] in jited image, so build prologue first. + */ + if (build_prologue(&ctx, was_classic)) { prog = orig_prog; goto out_off; } - if (build_prologue(&ctx, was_classic)) { + if (build_body(&ctx, extra_pass)) { prog = orig_prog; goto out_off; } @@ -1130,6 +1274,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) prog->jited_len = prog_size; if (!prog->is_func || extra_pass) { + int i; + + /* offset[prog->len] is the size of program */ + for (i = 0; i <= prog->len; i++) + ctx.offset[i] *= AARCH64_INSN_SIZE; bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); out_off: kfree(ctx.offset); @@ -1143,6 +1292,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) return prog; } +bool bpf_jit_supports_kfunc_call(void) +{ + return true; +} + u64 bpf_jit_alloc_exec_limit(void) { return VMALLOC_END - VMALLOC_START; diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h index 24e0efb360f6..1d55e57b8466 100644 --- a/arch/mips/include/uapi/asm/socket.h +++ b/arch/mips/include/uapi/asm/socket.h @@ -144,6 +144,8 @@ #define SO_RESERVE_MEM 73 +#define SO_TXREHASH 74 + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64 diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h index 845ddc63c882..654061e0964e 100644 --- a/arch/parisc/include/uapi/asm/socket.h +++ b/arch/parisc/include/uapi/asm/socket.h @@ -125,6 +125,8 @@ #define SO_RESERVE_MEM 0x4047 +#define SO_TXREHASH 0x4048 + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64 diff --git a/arch/powerpc/include/asm/checksum.h b/arch/powerpc/include/asm/checksum.h index 350de8f90250..ab3832b93f0a 100644 --- a/arch/powerpc/include/asm/checksum.h +++ b/arch/powerpc/include/asm/checksum.h @@ -112,6 +112,13 @@ static __always_inline __wsum csum_add(__wsum csum, __wsum addend) #endif } +#define HAVE_ARCH_CSUM_SHIFT +static __always_inline __wsum csum_shift(__wsum sum, int offset) +{ + /* rotate sum to align it with a 16b boundary */ + return (__force __wsum)rol32((__force u32)sum, (offset & 1) << 3); +} + /* * This is a version of ip_compute_csum() optimized for IP headers, * which always checksum on 4 octet boundaries. ihl is the number diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 56dd1f4e3e44..a4f4d347e6bd 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -264,7 +264,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) 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); diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h index 2672dd03faf3..666f81e617ea 100644 --- a/arch/sparc/include/uapi/asm/socket.h +++ b/arch/sparc/include/uapi/asm/socket.h @@ -126,6 +126,8 @@ #define SO_RESERVE_MEM 0x0052 +#define SO_TXREHASH 0x0053 + #if !defined(__KERNEL__) diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c index b1e38784eb23..fa0759bfe498 100644 --- a/arch/sparc/net/bpf_jit_comp_64.c +++ b/arch/sparc/net/bpf_jit_comp_64.c @@ -1599,7 +1599,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 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); diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 61e511c51890..b037fe7be374 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -161,6 +161,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 X86_64 select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_JUMP_LABEL_RELATIVE select HAVE_ARCH_KASAN if X86_64 diff --git a/arch/x86/include/asm/text-patching.h b/arch/x86/include/asm/text-patching.h index b7421780e4e9..4cc18ba1b75e 100644 --- a/arch/x86/include/asm/text-patching.h +++ b/arch/x86/include/asm/text-patching.h @@ -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); diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index b4470eabf151..b4e576600969 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -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(); diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 0ecb140864b2..6efbb87f65ed 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -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 @@ st: if (is_imm8(insn->off)) 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,13 +2326,22 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 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); + if (header) { + bpf_arch_text_copy(&header->size, &rw_header->size, + sizeof(rw_header->size)); + bpf_jit_binary_pack_free(header, rw_header); + } + /* Fall back to interpreter mode */ prog = orig_prog; + if (extra_pass) { + prog->bpf_func = NULL; + prog->jited = 0; + prog->jited_len = 0; + } goto out_addrs; } if (image) { @@ -2361,8 +2364,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 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 +2382,27 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 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))) { + /* header has been freed */ + header = NULL; + goto out_image; + } + 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 +2430,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; +} diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index 962e5e145209..9fb99d18e3c2 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -304,7 +304,7 @@ static int iss_net_rx(struct net_device *dev) lp->stats.rx_bytes += skb->len; lp->stats.rx_packets++; - netif_rx_ni(skb); + netif_rx(skb); return pkt_len; } kfree_skb(skb); diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c index bc5a6ab6fa4b..1a50de39f5b5 100644 --- a/drivers/atm/nicstar.c +++ b/drivers/atm/nicstar.c @@ -861,7 +861,6 @@ static void ns_init_card_error(ns_dev *card, int error) static scq_info *get_scq(ns_dev *card, int size, u32 scd) { scq_info *scq; - int i; if (size != VBR_SCQSIZE && size != CBR_SCQSIZE) return NULL; @@ -875,9 +874,8 @@ static scq_info *get_scq(ns_dev *card, int size, u32 scd) kfree(scq); return NULL; } - scq->skb = kmalloc_array(size / NS_SCQE_SIZE, - sizeof(*scq->skb), - GFP_KERNEL); + scq->skb = kcalloc(size / NS_SCQE_SIZE, sizeof(*scq->skb), + GFP_KERNEL); if (!scq->skb) { dma_free_coherent(&card->pcidev->dev, 2 * size, scq->org, scq->dma); @@ -890,15 +888,11 @@ static scq_info *get_scq(ns_dev *card, int size, u32 scd) scq->last = scq->base + (scq->num_entries - 1); scq->tail = scq->last; scq->scd = scd; - scq->num_entries = size / NS_SCQE_SIZE; scq->tbd_count = 0; init_waitqueue_head(&scq->scqfull_waitq); scq->full = 0; spin_lock_init(&scq->lock); - for (i = 0; i < scq->num_entries; i++) - scq->skb[i] = NULL; - return scq; } diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c index 62f5bfa5065d..fd91a39f02c7 100644 --- a/drivers/bcma/driver_chipcommon.c +++ b/drivers/bcma/driver_chipcommon.c @@ -303,7 +303,7 @@ u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value) EXPORT_SYMBOL_GPL(bcma_chipco_gpio_outen); /* - * If the bit is set to 0, chipcommon controlls this GPIO, + * If the bit is set to 0, chipcommon controls this GPIO, * if the bit is set to 1, it is used by some part of the chip and not our code. */ u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value) diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c index 3056f81efca4..263ef6fa1d0f 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c +++ b/drivers/bcma/driver_chipcommon_pmu.c @@ -206,7 +206,7 @@ static void bcma_pmu_resources_init(struct bcma_drv_cc *cc) usleep_range(2000, 2500); } -/* Disable to allow reading SPROM. Don't know the adventages of enabling it. */ +/* Disable to allow reading SPROM. Don't know the advantages of enabling it. */ void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable) { struct bcma_bus *bus = cc->core->bus; @@ -234,7 +234,7 @@ static void bcma_pmu_workarounds(struct bcma_drv_cc *cc) switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM4313: /* - * enable 12 mA drive strenth for 4313 and set chipControl + * enable 12 mA drive strength for 4313 and set chipControl * register bit 1 */ bcma_chipco_chipctl_maskset(cc, 0, @@ -249,7 +249,7 @@ static void bcma_pmu_workarounds(struct bcma_drv_cc *cc) case BCMA_CHIP_ID_BCM43224: case BCMA_CHIP_ID_BCM43421: /* - * enable 12 mA drive strenth for 43224 and set chipControl + * enable 12 mA drive strength for 43224 and set chipControl * register bit 15 */ if (bus->chipinfo.rev == 0) { diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c index 8a1e4705bc87..1e74ec1c7f23 100644 --- a/drivers/bcma/driver_gpio.c +++ b/drivers/bcma/driver_gpio.c @@ -181,7 +181,6 @@ int bcma_gpio_init(struct bcma_drv_cc *cc) chip->set = bcma_gpio_set_value; chip->direction_input = bcma_gpio_direction_input; chip->direction_output = bcma_gpio_direction_output; - chip->owner = THIS_MODULE; chip->parent = bus->dev; #if IS_BUILTIN(CONFIG_OF) chip->of_node = cc->core->dev.of_node; diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index 6f8fc5f587fe..aa0581cda718 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -61,7 +61,7 @@ static u32 bcma_get_cfgspace_addr(struct bcma_drv_pci *pc, unsigned int dev, { u32 addr = 0; - /* Issue config commands only when the data link is up (atleast + /* Issue config commands only when the data link is up (at least * one external pcie device is present). */ if (dev >= 2 || !(bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_LSREG) @@ -295,7 +295,7 @@ static u8 bcma_find_pci_capability(struct bcma_drv_pci *pc, unsigned int dev, if (cap_ptr == 0x00) return cap_ptr; - /* loop thr'u the capability list and see if the requested capabilty + /* loop through the capability list and see if the requested capability * exists */ bcma_extpci_read_config(pc, dev, func, cap_ptr, &cap_id, sizeof(u8)); while (cap_id != req_cap_id) { @@ -317,7 +317,7 @@ static u8 bcma_find_pci_capability(struct bcma_drv_pci *pc, unsigned int dev, *buflen = 0; - /* copy the cpability data excluding cap ID and next ptr */ + /* copy the capability data excluding cap ID and next ptr */ cap_data = cap_ptr + 2; if ((bufsize + cap_data) > PCI_CONFIG_SPACE_SIZE) bufsize = PCI_CONFIG_SPACE_SIZE - cap_data; diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 8e7ca3e4c8c4..44392b624b20 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -293,7 +293,7 @@ static int bcma_register_devices(struct bcma_bus *bus) int err; list_for_each_entry(core, &bus->cores, list) { - /* We support that cores ourself */ + /* We support that core ourselves */ switch (core->id.id) { case BCMA_CORE_4706_CHIPCOMMON: case BCMA_CORE_CHIPCOMMON: @@ -369,7 +369,7 @@ void bcma_unregister_cores(struct bcma_bus *bus) if (bus->hosttype == BCMA_HOSTTYPE_SOC) platform_device_unregister(bus->drv_cc.watchdog); - /* Now noone uses internally-handled cores, we can free them */ + /* Now no one uses internally-handled cores, we can free them */ list_for_each_entry_safe(core, tmp, &bus->cores, list) { list_del(&core->list); put_device(&core->dev); diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index bd2c923a6586..3da01f173c63 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -28,7 +28,7 @@ static int(*get_fallback_sprom)(struct bcma_bus *dev, struct ssb_sprom *out); * callback handler which fills the SPROM data structure. The fallback is * used for PCI based BCMA devices, where no valid SPROM can be found * in the shadow registers and to provide the SPROM for SoCs where BCMA is - * to controll the system bus. + * to control the system bus. * * This function is useful for weird architectures that have a half-assed * BCMA device hardwired to their PCI bus. @@ -281,7 +281,7 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8); SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0); - /* Extract cores power info info */ + /* Extract core's power info */ for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) { o = pwr_info_offset[i]; SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI, diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 36380e618ba4..e30707405455 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -400,6 +400,7 @@ config BT_MTKSDIO config BT_MTKUART tristate "MediaTek HCI UART driver" depends on SERIAL_DEV_BUS + select BT_MTK help MediaTek Bluetooth HCI UART driver. This driver is required if you want to use MediaTek Bluetooth diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 759d7828931d..88262d3a9392 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c index e667933c3d70..c738ad0408cb 100644 --- a/drivers/bluetooth/bcm203x.c +++ b/drivers/bluetooth/bcm203x.c @@ -9,7 +9,6 @@ #include -#include #include #include #include diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 1a4f8b227eac..06514ed66022 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -2428,10 +2428,15 @@ static int btintel_setup_combined(struct hci_dev *hdev) /* Apply the device specific HCI quirks * - * WBS for SdP - SdP and Stp have a same hw_varaint but - * different fw_variant + * WBS for SdP - For the Legacy ROM products, only SdP + * supports the WBS. But the version information is not + * enough to use here because the StP2 and SdP have same + * hw_variant and fw_variant. So, this flag is set by + * the transport driver (btusb) based on the HW info + * (idProduct) */ - if (ver.hw_variant == 0x08 && ver.fw_variant == 0x22) + if (!btintel_test_flag(hdev, + INTEL_ROM_LEGACY_NO_WBS_SUPPORT)) set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index c9b24e9299e2..e0060e58573c 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -152,6 +152,7 @@ enum { INTEL_BROKEN_INITIAL_NCMD, INTEL_BROKEN_SHUTDOWN_LED, INTEL_ROM_LEGACY, + INTEL_ROM_LEGACY_NO_WBS_SUPPORT, __INTEL_NUM_FLAGS, }; diff --git a/drivers/bluetooth/btmrvl_debugfs.c b/drivers/bluetooth/btmrvl_debugfs.c index c4867576be00..db35b917aecf 100644 --- a/drivers/bluetooth/btmrvl_debugfs.c +++ b/drivers/bluetooth/btmrvl_debugfs.c @@ -1,4 +1,4 @@ -/** +/* * Marvell Bluetooth driver: debugfs related functions * * Copyright (C) 2009, Marvell International Ltd. diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 68378b42ea7f..b8ef66f89fc1 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -1,4 +1,4 @@ -/** +/* * Marvell BT-over-SDIO driver: SDIO interface related functions. * * Copyright (C) 2009, Marvell International Ltd. diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index 526dfdf1fe01..809762d64fc6 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -285,6 +285,7 @@ MODULE_AUTHOR("Mark Chen "); MODULE_DESCRIPTION("Bluetooth support for MediaTek devices ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(FIRMWARE_MT7622); MODULE_FIRMWARE(FIRMWARE_MT7663); MODULE_FIRMWARE(FIRMWARE_MT7668); MODULE_FIRMWARE(FIRMWARE_MT7961); diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h index 6e7b0c7567c0..2a88ea8e475e 100644 --- a/drivers/bluetooth/btmtk.h +++ b/drivers/bluetooth/btmtk.h @@ -1,14 +1,26 @@ /* SPDX-License-Identifier: ISC */ /* Copyright (C) 2021 MediaTek Inc. */ +#define FIRMWARE_MT7622 "mediatek/mt7622pr2h.bin" #define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin" #define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin" #define FIRMWARE_MT7961 "mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin" +#define HCI_EV_WMT 0xe4 #define HCI_WMT_MAX_EVENT_SIZE 64 +#define BTMTK_WMT_REG_WRITE 0x1 #define BTMTK_WMT_REG_READ 0x2 +#define MT7921_BTSYS_RST 0x70002610 +#define MT7921_BTSYS_RST_WITH_GPIO BIT(7) + +#define MT7921_PINMUX_0 0x70005050 +#define MT7921_PINMUX_1 0x70005054 + +#define MT7921_DLSTATUS 0x7c053c10 +#define BT_DL_STATE BIT(1) + enum { BTMTK_WMT_PATCH_DWNLD = 0x1, BTMTK_WMT_TEST = 0x2, @@ -68,6 +80,37 @@ struct btmtk_tci_sleep { u8 time_compensation; } __packed; +struct btmtk_wakeon { + u8 mode; + u8 gpo; + u8 active_high; + __le16 enable_delay; + __le16 wakeup_delay; +} __packed; + +struct btmtk_sco { + u8 clock_config; + u8 transmit_format_config; + u8 channel_format_config; + u8 channel_select_config; +} __packed; + +struct reg_read_cmd { + u8 type; + u8 rsv; + u8 num; + __le32 addr; +} __packed; + +struct reg_write_cmd { + u8 type; + u8 rsv; + u8 num; + __le32 addr; + __le32 data; + __le32 mask; +} __packed; + struct btmtk_hci_wmt_params { u8 op; u8 flag; diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index b5ea8d3bffaa..f3dc5881fff7 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -12,10 +12,12 @@ #include #include +#include #include #include #include #include +#include #include #include @@ -31,28 +33,32 @@ #define VERSION "0.1" -#define MTKBTSDIO_AUTOSUSPEND_DELAY 8000 +#define MTKBTSDIO_AUTOSUSPEND_DELAY 1000 -static bool enable_autosuspend; +static bool enable_autosuspend = true; struct btmtksdio_data { const char *fwname; u16 chipid; + bool lp_mbox_supported; }; static const struct btmtksdio_data mt7663_data = { .fwname = FIRMWARE_MT7663, .chipid = 0x7663, + .lp_mbox_supported = false, }; static const struct btmtksdio_data mt7668_data = { .fwname = FIRMWARE_MT7668, .chipid = 0x7668, + .lp_mbox_supported = false, }; static const struct btmtksdio_data mt7921_data = { .fwname = FIRMWARE_MT7961, .chipid = 0x7921, + .lp_mbox_supported = true, }; static const struct sdio_device_id btmtksdio_table[] = { @@ -79,6 +85,7 @@ MODULE_DEVICE_TABLE(sdio, btmtksdio_table); #define MTK_REG_CHCR 0xc #define C_INT_CLR_CTRL BIT(1) +#define BT_RST_DONE BIT(8) /* CHISR have the same bits field definition with CHIER */ #define MTK_REG_CHISR 0x10 @@ -87,8 +94,17 @@ MODULE_DEVICE_TABLE(sdio, btmtksdio_table); #define RX_DONE_INT BIT(1) #define TX_EMPTY BIT(2) #define TX_FIFO_OVERFLOW BIT(8) +#define FW_MAILBOX_INT BIT(15) +#define INT_MASK GENMASK(15, 0) #define RX_PKT_LEN GENMASK(31, 16) +#define MTK_REG_CSICR 0xc0 +#define CSICR_CLR_MBOX_ACK BIT(0) +#define MTK_REG_PH2DSM0R 0xc4 +#define PH2DSM0R_DRIVER_OWN BIT(0) +#define MTK_REG_PD2HRM0R 0xdc +#define PD2HRM0R_DRV_OWN BIT(0) + #define MTK_REG_CTDR 0x18 #define MTK_REG_CRDR 0x1c @@ -100,6 +116,8 @@ MODULE_DEVICE_TABLE(sdio, btmtksdio_table); #define BTMTKSDIO_TX_WAIT_VND_EVT 1 #define BTMTKSDIO_HW_TX_READY 2 #define BTMTKSDIO_FUNC_ENABLED 3 +#define BTMTKSDIO_PATCH_ENABLED 4 +#define BTMTKSDIO_HW_RESET_ACTIVE 5 struct mtkbtsdio_hdr { __le16 len; @@ -119,6 +137,8 @@ struct btmtksdio_dev { struct sk_buff *evt_skb; const struct btmtksdio_data *data; + + struct gpio_desc *reset; }; static int mtk_hci_wmt_sync(struct hci_dev *hdev, @@ -278,19 +298,89 @@ static u32 btmtksdio_drv_own_query(struct btmtksdio_dev *bdev) return sdio_readl(bdev->func, MTK_REG_CHLPCR, NULL); } +static u32 btmtksdio_drv_own_query_79xx(struct btmtksdio_dev *bdev) +{ + return sdio_readl(bdev->func, MTK_REG_PD2HRM0R, NULL); +} + +static u32 btmtksdio_chcr_query(struct btmtksdio_dev *bdev) +{ + return sdio_readl(bdev->func, MTK_REG_CHCR, NULL); +} + +static int btmtksdio_fw_pmctrl(struct btmtksdio_dev *bdev) +{ + u32 status; + int err; + + sdio_claim_host(bdev->func); + + if (bdev->data->lp_mbox_supported && + test_bit(BTMTKSDIO_PATCH_ENABLED, &bdev->tx_state)) { + sdio_writel(bdev->func, CSICR_CLR_MBOX_ACK, MTK_REG_CSICR, + &err); + err = readx_poll_timeout(btmtksdio_drv_own_query_79xx, bdev, + status, !(status & PD2HRM0R_DRV_OWN), + 2000, 1000000); + if (err < 0) { + bt_dev_err(bdev->hdev, "mailbox ACK not cleared"); + goto out; + } + } + + /* Return ownership to the device */ + sdio_writel(bdev->func, C_FW_OWN_REQ_SET, MTK_REG_CHLPCR, &err); + if (err < 0) + goto out; + + err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, + !(status & C_COM_DRV_OWN), 2000, 1000000); + +out: + sdio_release_host(bdev->func); + + if (err < 0) + bt_dev_err(bdev->hdev, "Cannot return ownership to device"); + + return err; +} + +static int btmtksdio_drv_pmctrl(struct btmtksdio_dev *bdev) +{ + u32 status; + int err; + + sdio_claim_host(bdev->func); + + /* Get ownership from the device */ + sdio_writel(bdev->func, C_FW_OWN_REQ_CLR, MTK_REG_CHLPCR, &err); + if (err < 0) + goto out; + + err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, + status & C_COM_DRV_OWN, 2000, 1000000); + + if (!err && bdev->data->lp_mbox_supported && + test_bit(BTMTKSDIO_PATCH_ENABLED, &bdev->tx_state)) + err = readx_poll_timeout(btmtksdio_drv_own_query_79xx, bdev, + status, status & PD2HRM0R_DRV_OWN, + 2000, 1000000); + +out: + sdio_release_host(bdev->func); + + if (err < 0) + bt_dev_err(bdev->hdev, "Cannot get ownership from device"); + + return err; +} + static int btmtksdio_recv_event(struct hci_dev *hdev, struct sk_buff *skb) { struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); struct hci_event_hdr *hdr = (void *)skb->data; int err; - /* Fix up the vendor event id with 0xff for vendor specific instead - * of 0xe4 so that event send via monitoring socket can be parsed - * properly. - */ - if (hdr->evt == 0xe4) - hdr->evt = HCI_EV_VENDOR; - /* When someone waits for the WMT event, the skb is being cloned * and being processed the events from there then. */ @@ -306,7 +396,7 @@ static int btmtksdio_recv_event(struct hci_dev *hdev, struct sk_buff *skb) if (err < 0) goto err_free_skb; - if (hdr->evt == HCI_EV_VENDOR) { + if (hdr->evt == HCI_EV_WMT) { if (test_and_clear_bit(BTMTKSDIO_TX_WAIT_VND_EVT, &bdev->tx_state)) { /* Barrier to sync with other CPUs */ @@ -480,6 +570,13 @@ static void btmtksdio_txrx_work(struct work_struct *work) * FIFO. */ sdio_writel(bdev->func, int_status, MTK_REG_CHISR, NULL); + int_status &= INT_MASK; + + if ((int_status & FW_MAILBOX_INT) && + bdev->data->chipid == 0x7921) { + sdio_writel(bdev->func, PH2DSM0R_DRIVER_OWN, + MTK_REG_PH2DSM0R, 0); + } if (int_status & FW_OWN_BACK_INT) bt_dev_dbg(bdev->hdev, "Get fw own back"); @@ -531,7 +628,7 @@ static void btmtksdio_interrupt(struct sdio_func *func) static int btmtksdio_open(struct hci_dev *hdev) { struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); - u32 status, val; + u32 val; int err; sdio_claim_host(bdev->func); @@ -542,18 +639,10 @@ static int btmtksdio_open(struct hci_dev *hdev) set_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state); - /* Get ownership from the device */ - sdio_writel(bdev->func, C_FW_OWN_REQ_CLR, MTK_REG_CHLPCR, &err); + err = btmtksdio_drv_pmctrl(bdev); if (err < 0) goto err_disable_func; - err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, - status & C_COM_DRV_OWN, 2000, 1000000); - if (err < 0) { - bt_dev_err(bdev->hdev, "Cannot get ownership from device"); - goto err_disable_func; - } - /* Disable interrupt & mask out all interrupt sources */ sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, &err); if (err < 0) @@ -623,8 +712,6 @@ static int btmtksdio_open(struct hci_dev *hdev) static int btmtksdio_close(struct hci_dev *hdev) { struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); - u32 status; - int err; sdio_claim_host(bdev->func); @@ -635,13 +722,7 @@ static int btmtksdio_close(struct hci_dev *hdev) cancel_work_sync(&bdev->txrx_work); - /* Return ownership to the device */ - sdio_writel(bdev->func, C_FW_OWN_REQ_SET, MTK_REG_CHLPCR, NULL); - - err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, - !(status & C_COM_DRV_OWN), 2000, 1000000); - if (err < 0) - bt_dev_err(bdev->hdev, "Cannot return ownership to device"); + btmtksdio_fw_pmctrl(bdev); clear_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state); sdio_disable_func(bdev->func); @@ -686,6 +767,7 @@ static int btmtksdio_func_query(struct hci_dev *hdev) static int mt76xx_setup(struct hci_dev *hdev, const char *fwname) { + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); struct btmtk_hci_wmt_params wmt_params; struct btmtk_tci_sleep tci_sleep; struct sk_buff *skb; @@ -746,6 +828,8 @@ static int mt76xx_setup(struct hci_dev *hdev, const char *fwname) return err; } + set_bit(BTMTKSDIO_PATCH_ENABLED, &bdev->tx_state); + ignore_func_on: /* Apply the low power environment setup */ tci_sleep.mode = 0x5; @@ -768,6 +852,7 @@ static int mt76xx_setup(struct hci_dev *hdev, const char *fwname) static int mt79xx_setup(struct hci_dev *hdev, const char *fwname) { + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); struct btmtk_hci_wmt_params wmt_params; u8 param = 0x1; int err; @@ -793,19 +878,15 @@ static int mt79xx_setup(struct hci_dev *hdev, const char *fwname) hci_set_msft_opcode(hdev, 0xFD30); hci_set_aosp_capable(hdev); + set_bit(BTMTKSDIO_PATCH_ENABLED, &bdev->tx_state); return err; } -static int btsdio_mtk_reg_read(struct hci_dev *hdev, u32 reg, u32 *val) +static int btmtksdio_mtk_reg_read(struct hci_dev *hdev, u32 reg, u32 *val) { struct btmtk_hci_wmt_params wmt_params; - struct reg_read_cmd { - u8 type; - u8 rsv; - u8 num; - __le32 addr; - } __packed reg_read = { + struct reg_read_cmd reg_read = { .type = 1, .num = 1, }; @@ -821,7 +902,7 @@ static int btsdio_mtk_reg_read(struct hci_dev *hdev, u32 reg, u32 *val) err = mtk_hci_wmt_sync(hdev, &wmt_params); if (err < 0) { - bt_dev_err(hdev, "Failed to read reg(%d)", err); + bt_dev_err(hdev, "Failed to read reg (%d)", err); return err; } @@ -830,6 +911,151 @@ static int btsdio_mtk_reg_read(struct hci_dev *hdev, u32 reg, u32 *val) return err; } +static int btmtksdio_mtk_reg_write(struct hci_dev *hdev, u32 reg, u32 val, u32 mask) +{ + struct btmtk_hci_wmt_params wmt_params; + const struct reg_write_cmd reg_write = { + .type = 1, + .num = 1, + .addr = cpu_to_le32(reg), + .data = cpu_to_le32(val), + .mask = cpu_to_le32(mask), + }; + int err, status; + + wmt_params.op = BTMTK_WMT_REGISTER; + wmt_params.flag = BTMTK_WMT_REG_WRITE; + wmt_params.dlen = sizeof(reg_write); + wmt_params.data = ®_write; + wmt_params.status = &status; + + err = mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) + bt_dev_err(hdev, "Failed to write reg (%d)", err); + + return err; +} + +static int btmtksdio_get_data_path_id(struct hci_dev *hdev, __u8 *data_path_id) +{ + /* uses 1 as data path id for all the usecases */ + *data_path_id = 1; + return 0; +} + +static int btmtksdio_get_codec_config_data(struct hci_dev *hdev, + __u8 link, struct bt_codec *codec, + __u8 *ven_len, __u8 **ven_data) +{ + int err = 0; + + if (!ven_data || !ven_len) + return -EINVAL; + + *ven_len = 0; + *ven_data = NULL; + + if (link != ESCO_LINK) { + bt_dev_err(hdev, "Invalid link type(%u)", link); + return -EINVAL; + } + + *ven_data = kmalloc(sizeof(__u8), GFP_KERNEL); + if (!ven_data) { + err = -ENOMEM; + goto error; + } + + /* supports only CVSD and mSBC offload codecs */ + switch (codec->id) { + case 0x02: + **ven_data = 0x00; + break; + case 0x05: + **ven_data = 0x01; + break; + default: + err = -EINVAL; + bt_dev_err(hdev, "Invalid codec id(%u)", codec->id); + goto error; + } + /* codec and its capabilities are pre-defined to ids + * preset id = 0x00 represents CVSD codec with sampling rate 8K + * preset id = 0x01 represents mSBC codec with sampling rate 16K + */ + *ven_len = sizeof(__u8); + return err; + +error: + kfree(*ven_data); + *ven_data = NULL; + return err; +} + +static int btmtksdio_sco_setting(struct hci_dev *hdev) +{ + const struct btmtk_sco sco_setting = { + .clock_config = 0x49, + .channel_format_config = 0x80, + }; + struct sk_buff *skb; + u32 val; + int err; + + /* Enable SCO over I2S/PCM for MediaTek chipset */ + skb = __hci_cmd_sync(hdev, 0xfc72, sizeof(sco_setting), + &sco_setting, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + kfree_skb(skb); + + err = btmtksdio_mtk_reg_read(hdev, MT7921_PINMUX_0, &val); + if (err < 0) + return err; + + val |= 0x11000000; + err = btmtksdio_mtk_reg_write(hdev, MT7921_PINMUX_0, val, ~0); + if (err < 0) + return err; + + err = btmtksdio_mtk_reg_read(hdev, MT7921_PINMUX_1, &val); + if (err < 0) + return err; + + val |= 0x00000101; + err = btmtksdio_mtk_reg_write(hdev, MT7921_PINMUX_1, val, ~0); + if (err < 0) + return err; + + hdev->get_data_path_id = btmtksdio_get_data_path_id; + hdev->get_codec_config_data = btmtksdio_get_codec_config_data; + + return err; +} + +static int btmtksdio_reset_setting(struct hci_dev *hdev) +{ + int err; + u32 val; + + err = btmtksdio_mtk_reg_read(hdev, MT7921_PINMUX_1, &val); + if (err < 0) + return err; + + val |= 0x20; /* set the pin (bit field 11:8) work as GPIO mode */ + err = btmtksdio_mtk_reg_write(hdev, MT7921_PINMUX_1, val, ~0); + if (err < 0) + return err; + + err = btmtksdio_mtk_reg_read(hdev, MT7921_BTSYS_RST, &val); + if (err < 0) + return err; + + val |= MT7921_BTSYS_RST_WITH_GPIO; + return btmtksdio_mtk_reg_write(hdev, MT7921_BTSYS_RST, val, ~0); +} + static int btmtksdio_setup(struct hci_dev *hdev) { struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); @@ -837,20 +1063,39 @@ static int btmtksdio_setup(struct hci_dev *hdev) unsigned long long duration; char fwname[64]; int err, dev_id; - u32 fw_version = 0; + u32 fw_version = 0, val; calltime = ktime_get(); set_bit(BTMTKSDIO_HW_TX_READY, &bdev->tx_state); switch (bdev->data->chipid) { case 0x7921: - err = btsdio_mtk_reg_read(hdev, 0x70010200, &dev_id); + if (test_bit(BTMTKSDIO_HW_RESET_ACTIVE, &bdev->tx_state)) { + err = btmtksdio_mtk_reg_read(hdev, MT7921_DLSTATUS, + &val); + if (err < 0) + return err; + + val &= ~BT_DL_STATE; + err = btmtksdio_mtk_reg_write(hdev, MT7921_DLSTATUS, + val, ~0); + if (err < 0) + return err; + + btmtksdio_fw_pmctrl(bdev); + msleep(20); + btmtksdio_drv_pmctrl(bdev); + + clear_bit(BTMTKSDIO_HW_RESET_ACTIVE, &bdev->tx_state); + } + + err = btmtksdio_mtk_reg_read(hdev, 0x70010200, &dev_id); if (err < 0) { bt_dev_err(hdev, "Failed to get device id (%d)", err); return err; } - err = btsdio_mtk_reg_read(hdev, 0x80021004, &fw_version); + err = btmtksdio_mtk_reg_read(hdev, 0x80021004, &fw_version); if (err < 0) { bt_dev_err(hdev, "Failed to get fw version (%d)", err); return err; @@ -862,6 +1107,38 @@ static int btmtksdio_setup(struct hci_dev *hdev) err = mt79xx_setup(hdev, fwname); if (err < 0) return err; + + err = btmtksdio_fw_pmctrl(bdev); + if (err < 0) + return err; + + err = btmtksdio_drv_pmctrl(bdev); + if (err < 0) + return err; + + /* Enable SCO over I2S/PCM */ + err = btmtksdio_sco_setting(hdev); + if (err < 0) { + bt_dev_err(hdev, "Failed to enable SCO setting (%d)", err); + return err; + } + + /* Enable WBS with mSBC codec */ + set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); + + /* Enable GPIO reset mechanism */ + if (bdev->reset) { + err = btmtksdio_reset_setting(hdev); + if (err < 0) { + bt_dev_err(hdev, "Failed to enable Reset setting (%d)", err); + devm_gpiod_put(bdev->dev, bdev->reset); + bdev->reset = NULL; + } + } + + /* Valid LE States quirk for MediaTek 7921 */ + set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); + break; case 0x7663: case 0x7668: @@ -958,6 +1235,73 @@ static int btmtksdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb) return 0; } +static void btmtksdio_cmd_timeout(struct hci_dev *hdev) +{ + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); + u32 status; + int err; + + if (!bdev->reset || bdev->data->chipid != 0x7921) + return; + + pm_runtime_get_sync(bdev->dev); + + if (test_and_set_bit(BTMTKSDIO_HW_RESET_ACTIVE, &bdev->tx_state)) + return; + + sdio_claim_host(bdev->func); + + sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, NULL); + skb_queue_purge(&bdev->txq); + cancel_work_sync(&bdev->txrx_work); + + gpiod_set_value_cansleep(bdev->reset, 1); + msleep(100); + gpiod_set_value_cansleep(bdev->reset, 0); + + err = readx_poll_timeout(btmtksdio_chcr_query, bdev, status, + status & BT_RST_DONE, 100000, 2000000); + if (err < 0) { + bt_dev_err(hdev, "Failed to reset (%d)", err); + goto err; + } + + clear_bit(BTMTKSDIO_PATCH_ENABLED, &bdev->tx_state); +err: + sdio_release_host(bdev->func); + + pm_runtime_put_noidle(bdev->dev); + pm_runtime_disable(bdev->dev); + + hci_reset_dev(hdev); +} + +static bool btmtksdio_sdio_wakeup(struct hci_dev *hdev) +{ + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); + bool may_wakeup = device_may_wakeup(bdev->dev); + const struct btmtk_wakeon bt_awake = { + .mode = 0x1, + .gpo = 0, + .active_high = 0x1, + .enable_delay = cpu_to_le16(0xc80), + .wakeup_delay = cpu_to_le16(0x20), + }; + + if (may_wakeup && bdev->data->chipid == 0x7921) { + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, 0xfc27, sizeof(bt_awake), + &bt_awake, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) + may_wakeup = false; + else + kfree_skb(skb); + } + + return may_wakeup; +} + static int btmtksdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { @@ -993,10 +1337,12 @@ static int btmtksdio_probe(struct sdio_func *func, hdev->open = btmtksdio_open; hdev->close = btmtksdio_close; + hdev->cmd_timeout = btmtksdio_cmd_timeout; hdev->flush = btmtksdio_flush; hdev->setup = btmtksdio_setup; hdev->shutdown = btmtksdio_shutdown; hdev->send = btmtksdio_send_frame; + hdev->wakeup = btmtksdio_sdio_wakeup; hdev->set_bdaddr = btmtk_set_bdaddr; SET_HCIDEV_DEV(hdev, &func->dev); @@ -1004,6 +1350,8 @@ static int btmtksdio_probe(struct sdio_func *func, hdev->manufacturer = 70; set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); + sdio_set_drvdata(func, bdev); + err = hci_register_dev(hdev); if (err < 0) { dev_err(&func->dev, "Can't register HCI device\n"); @@ -1011,8 +1359,6 @@ static int btmtksdio_probe(struct sdio_func *func, return err; } - sdio_set_drvdata(func, bdev); - /* pm_runtime_enable would be done after the firmware is being * downloaded because the core layer probably already enables * runtime PM for this func such as the case host->caps & @@ -1032,7 +1378,18 @@ static int btmtksdio_probe(struct sdio_func *func, */ pm_runtime_put_noidle(bdev->dev); - return 0; + err = device_init_wakeup(bdev->dev, true); + if (err) + bt_dev_err(hdev, "failed to initialize device wakeup"); + + bdev->dev->of_node = of_find_compatible_node(NULL, NULL, + "mediatek,mt7921s-bluetooth"); + bdev->reset = devm_gpiod_get_optional(bdev->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(bdev->reset)) + err = PTR_ERR(bdev->reset); + + return err; } static void btmtksdio_remove(struct sdio_func *func) @@ -1058,7 +1415,6 @@ static int btmtksdio_runtime_suspend(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); struct btmtksdio_dev *bdev; - u32 status; int err; bdev = sdio_get_drvdata(func); @@ -1070,18 +1426,9 @@ static int btmtksdio_runtime_suspend(struct device *dev) sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); - sdio_claim_host(bdev->func); + err = btmtksdio_fw_pmctrl(bdev); - sdio_writel(bdev->func, C_FW_OWN_REQ_SET, MTK_REG_CHLPCR, &err); - if (err < 0) - goto out; - - err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, - !(status & C_COM_DRV_OWN), 2000, 1000000); -out: - bt_dev_info(bdev->hdev, "status (%d) return ownership to device", err); - - sdio_release_host(bdev->func); + bt_dev_dbg(bdev->hdev, "status (%d) return ownership to device", err); return err; } @@ -1090,7 +1437,6 @@ static int btmtksdio_runtime_resume(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); struct btmtksdio_dev *bdev; - u32 status; int err; bdev = sdio_get_drvdata(func); @@ -1100,18 +1446,9 @@ static int btmtksdio_runtime_resume(struct device *dev) if (!test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state)) return 0; - sdio_claim_host(bdev->func); + err = btmtksdio_drv_pmctrl(bdev); - sdio_writel(bdev->func, C_FW_OWN_REQ_CLR, MTK_REG_CHLPCR, &err); - if (err < 0) - goto out; - - err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, - status & C_COM_DRV_OWN, 2000, 1000000); -out: - bt_dev_info(bdev->hdev, "status (%d) get ownership from device", err); - - sdio_release_host(bdev->func); + bt_dev_dbg(bdev->hdev, "status (%d) get ownership from device", err); return err; } diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c index 9ba22b13b4fa..c98691cdbbd5 100644 --- a/drivers/bluetooth/btmtkuart.c +++ b/drivers/bluetooth/btmtkuart.c @@ -28,13 +28,10 @@ #include #include "h4_recv.h" +#include "btmtk.h" #define VERSION "0.2" -#define FIRMWARE_MT7622 "mediatek/mt7622pr2h.bin" -#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin" -#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin" - #define MTK_STP_TLR_SIZE 2 #define BTMTKUART_TX_STATE_ACTIVE 1 @@ -44,25 +41,6 @@ #define BTMTKUART_FLAG_STANDALONE_HW BIT(0) -enum { - MTK_WMT_PATCH_DWNLD = 0x1, - MTK_WMT_TEST = 0x2, - MTK_WMT_WAKEUP = 0x3, - MTK_WMT_HIF = 0x4, - MTK_WMT_FUNC_CTRL = 0x6, - MTK_WMT_RST = 0x7, - MTK_WMT_SEMAPHORE = 0x17, -}; - -enum { - BTMTK_WMT_INVALID, - BTMTK_WMT_PATCH_UNDONE, - BTMTK_WMT_PATCH_DONE, - BTMTK_WMT_ON_UNDONE, - BTMTK_WMT_ON_DONE, - BTMTK_WMT_ON_PROGRESS, -}; - struct mtk_stp_hdr { u8 prefix; __be16 dlen; @@ -74,44 +52,6 @@ struct btmtkuart_data { const char *fwname; }; -struct mtk_wmt_hdr { - u8 dir; - u8 op; - __le16 dlen; - u8 flag; -} __packed; - -struct mtk_hci_wmt_cmd { - struct mtk_wmt_hdr hdr; - u8 data[256]; -} __packed; - -struct btmtk_hci_wmt_evt { - struct hci_event_hdr hhdr; - struct mtk_wmt_hdr whdr; -} __packed; - -struct btmtk_hci_wmt_evt_funcc { - struct btmtk_hci_wmt_evt hwhdr; - __be16 status; -} __packed; - -struct btmtk_tci_sleep { - u8 mode; - __le16 duration; - __le16 host_duration; - u8 host_wakeup_pin; - u8 time_compensation; -} __packed; - -struct btmtk_hci_wmt_params { - u8 op; - u8 flag; - u16 dlen; - const void *data; - u32 *status; -}; - struct btmtkuart_dev { struct hci_dev *hdev; struct serdev_device *serdev; @@ -153,29 +93,36 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc; u32 hlen, status = BTMTK_WMT_INVALID; struct btmtk_hci_wmt_evt *wmt_evt; - struct mtk_hci_wmt_cmd wc; - struct mtk_wmt_hdr *hdr; + struct btmtk_hci_wmt_cmd *wc; + struct btmtk_wmt_hdr *hdr; int err; + /* Send the WMT command and wait until the WMT event returns */ hlen = sizeof(*hdr) + wmt_params->dlen; if (hlen > 255) { err = -EINVAL; goto err_free_skb; } - hdr = (struct mtk_wmt_hdr *)&wc; + wc = kzalloc(hlen, GFP_KERNEL); + if (!wc) { + err = -ENOMEM; + goto err_free_skb; + } + + hdr = &wc->hdr; hdr->dir = 1; hdr->op = wmt_params->op; hdr->dlen = cpu_to_le16(wmt_params->dlen + 1); hdr->flag = wmt_params->flag; - memcpy(wc.data, wmt_params->data, wmt_params->dlen); + memcpy(wc->data, wmt_params->data, wmt_params->dlen); set_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state); - err = __hci_cmd_send(hdev, 0xfc6f, hlen, &wc); + err = __hci_cmd_send(hdev, 0xfc6f, hlen, wc); if (err < 0) { clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state); - goto err_free_skb; + goto err_free_wc; } /* The vendor specific WMT commands are all answered by a vendor @@ -192,14 +139,14 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, if (err == -EINTR) { bt_dev_err(hdev, "Execution of wmt command interrupted"); clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state); - goto err_free_skb; + goto err_free_wc; } if (err) { bt_dev_err(hdev, "Execution of wmt command timed out"); clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state); err = -ETIMEDOUT; - goto err_free_skb; + goto err_free_wc; } /* Parse and handle the return WMT event */ @@ -208,17 +155,17 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, bt_dev_err(hdev, "Wrong op received %d expected %d", wmt_evt->whdr.op, hdr->op); err = -EIO; - goto err_free_skb; + goto err_free_wc; } switch (wmt_evt->whdr.op) { - case MTK_WMT_SEMAPHORE: + case BTMTK_WMT_SEMAPHORE: if (wmt_evt->whdr.flag == 2) status = BTMTK_WMT_PATCH_UNDONE; else status = BTMTK_WMT_PATCH_DONE; break; - case MTK_WMT_FUNC_CTRL: + case BTMTK_WMT_FUNC_CTRL: wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt; if (be16_to_cpu(wmt_evt_funcc->status) == 0x404) status = BTMTK_WMT_ON_DONE; @@ -232,6 +179,8 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, if (wmt_params->status) *wmt_params->status = status; +err_free_wc: + kfree(wc); err_free_skb: kfree_skb(bdev->evt_skb); bdev->evt_skb = NULL; @@ -239,95 +188,12 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, return err; } -static int mtk_setup_firmware(struct hci_dev *hdev, const char *fwname) -{ - struct btmtk_hci_wmt_params wmt_params; - const struct firmware *fw; - const u8 *fw_ptr; - size_t fw_size; - int err, dlen; - u8 flag; - - err = request_firmware(&fw, fwname, &hdev->dev); - if (err < 0) { - bt_dev_err(hdev, "Failed to load firmware file (%d)", err); - return err; - } - - fw_ptr = fw->data; - fw_size = fw->size; - - /* The size of patch header is 30 bytes, should be skip */ - if (fw_size < 30) { - err = -EINVAL; - goto free_fw; - } - - fw_size -= 30; - fw_ptr += 30; - flag = 1; - - wmt_params.op = MTK_WMT_PATCH_DWNLD; - wmt_params.status = NULL; - - while (fw_size > 0) { - dlen = min_t(int, 250, fw_size); - - /* Tell device the position in sequence */ - if (fw_size - dlen <= 0) - flag = 3; - else if (fw_size < fw->size - 30) - flag = 2; - - wmt_params.flag = flag; - wmt_params.dlen = dlen; - wmt_params.data = fw_ptr; - - err = mtk_hci_wmt_sync(hdev, &wmt_params); - if (err < 0) { - bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)", - err); - goto free_fw; - } - - fw_size -= dlen; - fw_ptr += dlen; - } - - wmt_params.op = MTK_WMT_RST; - wmt_params.flag = 4; - wmt_params.dlen = 0; - wmt_params.data = NULL; - wmt_params.status = NULL; - - /* Activate funciton the firmware providing to */ - err = mtk_hci_wmt_sync(hdev, &wmt_params); - if (err < 0) { - bt_dev_err(hdev, "Failed to send wmt rst (%d)", err); - goto free_fw; - } - - /* Wait a few moments for firmware activation done */ - usleep_range(10000, 12000); - -free_fw: - release_firmware(fw); - return err; -} - static int btmtkuart_recv_event(struct hci_dev *hdev, struct sk_buff *skb) { struct btmtkuart_dev *bdev = hci_get_drvdata(hdev); struct hci_event_hdr *hdr = (void *)skb->data; int err; - /* Fix up the vendor event id with 0xff for vendor specific instead - * of 0xe4 so that event send via monitoring socket can be parsed - * properly. - */ - if (hdr->evt == 0xe4) - hdr->evt = HCI_EV_VENDOR; - /* When someone waits for the WMT event, the skb is being cloned * and being processed the events from there then. */ @@ -343,7 +209,7 @@ static int btmtkuart_recv_event(struct hci_dev *hdev, struct sk_buff *skb) if (err < 0) goto err_free_skb; - if (hdr->evt == HCI_EV_VENDOR) { + if (hdr->evt == HCI_EV_WMT) { if (test_and_clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state)) { /* Barrier to sync with other CPUs */ @@ -645,7 +511,7 @@ static int btmtkuart_func_query(struct hci_dev *hdev) u8 param = 0; /* Query whether the function is enabled */ - wmt_params.op = MTK_WMT_FUNC_CTRL; + wmt_params.op = BTMTK_WMT_FUNC_CTRL; wmt_params.flag = 4; wmt_params.dlen = sizeof(param); wmt_params.data = ¶m; @@ -672,7 +538,7 @@ static int btmtkuart_change_baudrate(struct hci_dev *hdev) * ready to change a new baudrate. */ baudrate = cpu_to_le32(bdev->desired_speed); - wmt_params.op = MTK_WMT_HIF; + wmt_params.op = BTMTK_WMT_HIF; wmt_params.flag = 1; wmt_params.dlen = 4; wmt_params.data = &baudrate; @@ -706,7 +572,7 @@ static int btmtkuart_change_baudrate(struct hci_dev *hdev) usleep_range(20000, 22000); /* Test the new baudrate */ - wmt_params.op = MTK_WMT_TEST; + wmt_params.op = BTMTK_WMT_TEST; wmt_params.flag = 7; wmt_params.dlen = 0; wmt_params.data = NULL; @@ -741,7 +607,7 @@ static int btmtkuart_setup(struct hci_dev *hdev) * do any setups. */ if (test_bit(BTMTKUART_REQUIRED_WAKEUP, &bdev->tx_state)) { - wmt_params.op = MTK_WMT_WAKEUP; + wmt_params.op = BTMTK_WMT_WAKEUP; wmt_params.flag = 3; wmt_params.dlen = 0; wmt_params.data = NULL; @@ -760,7 +626,7 @@ static int btmtkuart_setup(struct hci_dev *hdev) btmtkuart_change_baudrate(hdev); /* Query whether the firmware is already download */ - wmt_params.op = MTK_WMT_SEMAPHORE; + wmt_params.op = BTMTK_WMT_SEMAPHORE; wmt_params.flag = 1; wmt_params.dlen = 0; wmt_params.data = NULL; @@ -778,7 +644,7 @@ static int btmtkuart_setup(struct hci_dev *hdev) } /* Setup a firmware which the device definitely requires */ - err = mtk_setup_firmware(hdev, bdev->data->fwname); + err = btmtk_setup_firmware(hdev, bdev->data->fwname, mtk_hci_wmt_sync); if (err < 0) return err; @@ -801,7 +667,7 @@ static int btmtkuart_setup(struct hci_dev *hdev) } /* Enable Bluetooth protocol */ - wmt_params.op = MTK_WMT_FUNC_CTRL; + wmt_params.op = BTMTK_WMT_FUNC_CTRL; wmt_params.flag = 0; wmt_params.dlen = sizeof(param); wmt_params.data = ¶m; @@ -846,7 +712,7 @@ static int btmtkuart_shutdown(struct hci_dev *hdev) int err; /* Disable the device */ - wmt_params.op = MTK_WMT_FUNC_CTRL; + wmt_params.op = BTMTK_WMT_FUNC_CTRL; wmt_params.flag = 0; wmt_params.dlen = sizeof(param); wmt_params.data = ¶m; @@ -1007,6 +873,7 @@ static int btmtkuart_probe(struct serdev_device *serdev) hdev->setup = btmtkuart_setup; hdev->shutdown = btmtkuart_shutdown; hdev->send = btmtkuart_send_frame; + hdev->set_bdaddr = btmtk_set_bdaddr; SET_HCIDEV_DEV(hdev, &serdev->dev); hdev->manufacturer = 70; @@ -1131,6 +998,3 @@ MODULE_AUTHOR("Sean Wang "); MODULE_DESCRIPTION("MediaTek Bluetooth Serial driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(FIRMWARE_MT7622); -MODULE_FIRMWARE(FIRMWARE_MT7663); -MODULE_FIRMWARE(FIRMWARE_MT7668); diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index c2bdd1e6060e..481d488bca0f 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -49,6 +49,7 @@ enum btrtl_chip_id { CHIP_ID_8822C = 13, CHIP_ID_8761B, CHIP_ID_8852A = 18, + CHIP_ID_8852B = 20, }; struct id_table { @@ -148,6 +149,14 @@ static const struct id_table ic_id_table[] = { .fw_name = "rtl_bt/rtl8761bu_fw.bin", .cfg_name = "rtl_bt/rtl8761bu_config" }, + /* 8822C with UART interface */ + { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0x8, HCI_UART), + .config_needed = true, + .has_rom_version = true, + .has_msft_ext = true, + .fw_name = "rtl_bt/rtl8822cs_fw.bin", + .cfg_name = "rtl_bt/rtl8822cs_config" }, + /* 8822C with UART interface */ { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_UART), .config_needed = true, @@ -179,6 +188,14 @@ static const struct id_table ic_id_table[] = { .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852au_fw.bin", .cfg_name = "rtl_bt/rtl8852au_config" }, + + /* 8852B */ + { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_USB), + .config_needed = false, + .has_rom_version = true, + .has_msft_ext = true, + .fw_name = "rtl_bt/rtl8852bu_fw.bin", + .cfg_name = "rtl_bt/rtl8852bu_config" }, }; static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, @@ -287,6 +304,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, { RTL_ROM_LMP_8822B, 13 }, /* 8822C */ { RTL_ROM_LMP_8761A, 14 }, /* 8761B */ { RTL_ROM_LMP_8852A, 18 }, /* 8852A */ + { RTL_ROM_LMP_8852A, 20 }, /* 8852B */ }; min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3; @@ -749,6 +767,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) switch (btrtl_dev->project_id) { case CHIP_ID_8822C: case CHIP_ID_8852A: + case CHIP_ID_8852B: set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); hci_set_aosp_capable(hdev); @@ -926,3 +945,5 @@ MODULE_FIRMWARE("rtl_bt/rtl8822b_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8822b_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852au_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852au_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8852bu_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8852bu_config.bin"); diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index c30d131da784..50df417207af 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -36,32 +36,33 @@ static bool reset = true; static struct usb_driver btusb_driver; -#define BTUSB_IGNORE 0x01 -#define BTUSB_DIGIANSWER 0x02 -#define BTUSB_CSR 0x04 -#define BTUSB_SNIFFER 0x08 -#define BTUSB_BCM92035 0x10 -#define BTUSB_BROKEN_ISOC 0x20 -#define BTUSB_WRONG_SCO_MTU 0x40 -#define BTUSB_ATH3012 0x80 -#define BTUSB_INTEL_COMBINED 0x100 -#define BTUSB_INTEL_BOOT 0x200 -#define BTUSB_BCM_PATCHRAM 0x400 -#define BTUSB_MARVELL 0x800 -#define BTUSB_SWAVE 0x1000 -#define BTUSB_AMP 0x4000 -#define BTUSB_QCA_ROME 0x8000 -#define BTUSB_BCM_APPLE 0x10000 -#define BTUSB_REALTEK 0x20000 -#define BTUSB_BCM2045 0x40000 -#define BTUSB_IFNUM_2 0x80000 -#define BTUSB_CW6622 0x100000 -#define BTUSB_MEDIATEK 0x200000 -#define BTUSB_WIDEBAND_SPEECH 0x400000 -#define BTUSB_VALID_LE_STATES 0x800000 -#define BTUSB_QCA_WCN6855 0x1000000 -#define BTUSB_INTEL_BROKEN_SHUTDOWN_LED 0x2000000 -#define BTUSB_INTEL_BROKEN_INITIAL_NCMD 0x4000000 +#define BTUSB_IGNORE BIT(0) +#define BTUSB_DIGIANSWER BIT(1) +#define BTUSB_CSR BIT(2) +#define BTUSB_SNIFFER BIT(3) +#define BTUSB_BCM92035 BIT(4) +#define BTUSB_BROKEN_ISOC BIT(5) +#define BTUSB_WRONG_SCO_MTU BIT(6) +#define BTUSB_ATH3012 BIT(7) +#define BTUSB_INTEL_COMBINED BIT(8) +#define BTUSB_INTEL_BOOT BIT(9) +#define BTUSB_BCM_PATCHRAM BIT(10) +#define BTUSB_MARVELL BIT(11) +#define BTUSB_SWAVE BIT(12) +#define BTUSB_AMP BIT(13) +#define BTUSB_QCA_ROME BIT(14) +#define BTUSB_BCM_APPLE BIT(15) +#define BTUSB_REALTEK BIT(16) +#define BTUSB_BCM2045 BIT(17) +#define BTUSB_IFNUM_2 BIT(18) +#define BTUSB_CW6622 BIT(19) +#define BTUSB_MEDIATEK BIT(20) +#define BTUSB_WIDEBAND_SPEECH BIT(21) +#define BTUSB_VALID_LE_STATES BIT(22) +#define BTUSB_QCA_WCN6855 BIT(23) +#define BTUSB_INTEL_BROKEN_SHUTDOWN_LED BIT(24) +#define BTUSB_INTEL_BROKEN_INITIAL_NCMD BIT(25) +#define BTUSB_INTEL_NO_WBS_SUPPORT BIT(26) static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -383,11 +384,14 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x8087, 0x0029), .driver_info = BTUSB_INTEL_COMBINED }, { USB_DEVICE(0x8087, 0x0032), .driver_info = BTUSB_INTEL_COMBINED }, { USB_DEVICE(0x8087, 0x0033), .driver_info = BTUSB_INTEL_COMBINED }, + { USB_DEVICE(0x8087, 0x0035), .driver_info = BTUSB_INTEL_COMBINED }, { USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR }, { USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL_COMBINED | + BTUSB_INTEL_NO_WBS_SUPPORT | BTUSB_INTEL_BROKEN_INITIAL_NCMD | BTUSB_INTEL_BROKEN_SHUTDOWN_LED }, { USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL_COMBINED | + BTUSB_INTEL_NO_WBS_SUPPORT | BTUSB_INTEL_BROKEN_SHUTDOWN_LED }, { USB_DEVICE(0x8087, 0x0a2b), .driver_info = BTUSB_INTEL_COMBINED }, { USB_DEVICE(0x8087, 0x0aa7), .driver_info = BTUSB_INTEL_COMBINED | @@ -405,6 +409,8 @@ static const struct usb_device_id blacklist_table[] = { BTUSB_WIDEBAND_SPEECH }, /* Realtek 8852AE Bluetooth devices */ + { USB_DEVICE(0x0bda, 0x2852), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0bda, 0xc852), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0bda, 0x385a), .driver_info = BTUSB_REALTEK | @@ -429,6 +435,11 @@ static const struct usb_device_id blacklist_table[] = { /* Additional MediaTek MT7615E Bluetooth devices */ { USB_DEVICE(0x13d3, 0x3560), .driver_info = BTUSB_MEDIATEK}, + /* Additional MediaTek MT7663 Bluetooth devices */ + { USB_DEVICE(0x043e, 0x310c), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + /* Additional MediaTek MT7668 Bluetooth devices */ { USB_DEVICE(0x043e, 0x3109), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH | @@ -444,6 +455,9 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x13d3, 0x3564), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH | BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x13d3, 0x3567), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, { USB_DEVICE(0x0489, 0xe0cd), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH | BTUSB_VALID_LE_STATES }, @@ -463,6 +477,7 @@ static const struct usb_device_id blacklist_table[] = { /* Additional Realtek 8723BE Bluetooth devices */ { USB_DEVICE(0x0489, 0xe085), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x0489, 0xe08b), .driver_info = BTUSB_REALTEK }, + { USB_DEVICE(0x04f2, 0xb49f), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3410), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3416), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3459), .driver_info = BTUSB_REALTEK }, @@ -482,6 +497,8 @@ static const struct usb_device_id blacklist_table[] = { /* Additional Realtek 8761BU Bluetooth devices */ { USB_DEVICE(0x0b05, 0x190e), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x2550, 0x8761), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Additional Realtek 8821AE Bluetooth devices */ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK }, @@ -2041,6 +2058,8 @@ static int btusb_setup_csr(struct hci_dev *hdev) */ set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks); set_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks); + set_bit(HCI_QUIRK_BROKEN_FILTER_CLEAR_ALL, &hdev->quirks); + set_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks); /* Clear the reset quirk since this is not an actual * early Bluetooth 1.1 device from CSR. @@ -2051,16 +2070,16 @@ static int btusb_setup_csr(struct hci_dev *hdev) /* * Special workaround for these BT 4.0 chip clones, and potentially more: * - * - 0x0134: a Barrot 8041a02 (HCI rev: 0x1012 sub: 0x0810) + * - 0x0134: a Barrot 8041a02 (HCI rev: 0x0810 sub: 0x1012) * - 0x7558: IC markings FR3191AHAL 749H15143 (HCI rev/sub-version: 0x0709) * * These controllers are really messed-up. * * 1. Their bulk RX endpoint will never report any data unless - * the device was suspended at least once (yes, really). + * the device was suspended at least once (yes, really). * 2. They will not wakeup when autosuspended and receiving data - * on their bulk RX endpoint from e.g. a keyboard or mouse - * (IOW remote-wakeup support is broken for the bulk endpoint). + * on their bulk RX endpoint from e.g. a keyboard or mouse + * (IOW remote-wakeup support is broken for the bulk endpoint). * * To fix 1. enable runtime-suspend, force-suspend the * HCI and then wake-it up by disabling runtime-suspend. @@ -2080,7 +2099,7 @@ static int btusb_setup_csr(struct hci_dev *hdev) if (ret >= 0) msleep(200); else - bt_dev_err(hdev, "CSR: Failed to suspend the device for our Barrot 8041a02 receive-issue workaround"); + bt_dev_warn(hdev, "CSR: Couldn't suspend the device for our Barrot 8041a02 receive-issue workaround"); pm_runtime_forbid(&data->udev->dev); @@ -2245,7 +2264,6 @@ static void btusb_mtk_wmt_recv(struct urb *urb) { struct hci_dev *hdev = urb->context; struct btusb_data *data = hci_get_drvdata(hdev); - struct hci_event_hdr *hdr; struct sk_buff *skb; int err; @@ -2265,13 +2283,6 @@ static void btusb_mtk_wmt_recv(struct urb *urb) hci_skb_pkt_type(skb) = HCI_EVENT_PKT; skb_put_data(skb, urb->transfer_buffer, urb->actual_length); - hdr = (void *)skb->data; - /* Fix up the vendor event id with 0xff for vendor specific - * instead of 0xe4 so that event send via monitoring socket can - * be parsed properly. - */ - hdr->evt = 0xff; - /* When someone waits for the WMT event, the skb is being cloned * and being processed the events from there then. */ @@ -2988,6 +2999,7 @@ static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev, #define QCA_PATCH_UPDATED 0x80 #define QCA_DFU_TIMEOUT 3000 #define QCA_FLAG_MULTI_NVM 0x80 +#define QCA_BT_RESET_WAIT_MS 100 #define WCN6855_2_0_RAM_VERSION_GF 0x400c1200 #define WCN6855_2_1_RAM_VERSION_GF 0x400c1211 @@ -3314,6 +3326,13 @@ static int btusb_setup_qca(struct hci_dev *hdev) err = btusb_setup_qca_load_nvm(hdev, &ver, info); if (err < 0) return err; + + /* WCN6855 2.1 will reset to apply firmware downloaded here, so + * wait ~100ms for reset Done then go ahead, otherwise, it maybe + * cause potential enable failure. + */ + if (info->rom_version == 0x00130201) + msleep(QCA_BT_RESET_WAIT_MS); } return 0; @@ -3737,6 +3756,9 @@ static int btusb_probe(struct usb_interface *intf, hdev->send = btusb_send_frame_intel; hdev->cmd_timeout = btusb_intel_cmd_timeout; + if (id->driver_info & BTUSB_INTEL_NO_WBS_SUPPORT) + btintel_set_flag(hdev, INTEL_ROM_LEGACY_NO_WBS_SUPPORT); + if (id->driver_info & BTUSB_INTEL_BROKEN_INITIAL_NCMD) btintel_set_flag(hdev, INTEL_BROKEN_INITIAL_NCMD); diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index d634a27bc850..785f445dd60d 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -870,7 +871,23 @@ static int bcm_resume(struct device *dev) #endif /* Some firmware reports an IRQ which does not work (wrong pin in fw table?) */ +static struct gpiod_lookup_table asus_tf103c_irq_gpios = { + .dev_id = "serial0-0", + .table = { + GPIO_LOOKUP("INT33FC:02", 17, "host-wakeup-alt", GPIO_ACTIVE_HIGH), + { } + }, +}; + static const struct dmi_system_id bcm_broken_irq_dmi_table[] = { + { + .ident = "Asus TF103C", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), + }, + .driver_data = &asus_tf103c_irq_gpios, + }, { .ident = "Meegopad T08", .matches = { @@ -1027,7 +1044,8 @@ static struct clk *bcm_get_txco(struct device *dev) static int bcm_get_resources(struct bcm_device *dev) { - const struct dmi_system_id *dmi_id; + const struct dmi_system_id *broken_irq_dmi_id; + const char *irq_con_id = "host-wakeup"; int err; dev->name = dev_name(dev->dev); @@ -1083,23 +1101,33 @@ static int bcm_get_resources(struct bcm_device *dev) if (err) return err; + broken_irq_dmi_id = dmi_first_match(bcm_broken_irq_dmi_table); + if (broken_irq_dmi_id && broken_irq_dmi_id->driver_data) { + gpiod_add_lookup_table(broken_irq_dmi_id->driver_data); + irq_con_id = "host-wakeup-alt"; + dev->irq_active_low = false; + dev->irq = 0; + } + /* IRQ can be declared in ACPI table as Interrupt or GpioInt */ if (dev->irq <= 0) { struct gpio_desc *gpio; - gpio = devm_gpiod_get_optional(dev->dev, "host-wakeup", - GPIOD_IN); + gpio = devm_gpiod_get_optional(dev->dev, irq_con_id, GPIOD_IN); if (IS_ERR(gpio)) return PTR_ERR(gpio); dev->irq = gpiod_to_irq(gpio); } - dmi_id = dmi_first_match(bcm_broken_irq_dmi_table); - if (dmi_id) { - dev_info(dev->dev, "%s: Has a broken IRQ config, disabling IRQ support / runtime-pm\n", - dmi_id->ident); - dev->irq = 0; + if (broken_irq_dmi_id) { + if (broken_irq_dmi_id->driver_data) { + gpiod_remove_lookup_table(broken_irq_dmi_id->driver_data); + } else { + dev_info(dev->dev, "%s: Has a broken IRQ config, disabling IRQ support / runtime-pm\n", + broken_irq_dmi_id->ident); + dev->irq = 0; + } } dev_dbg(dev->dev, "BCM irq: %d\n", dev->irq); @@ -1513,6 +1541,8 @@ static const struct of_device_id bcm_bluetooth_of_match[] = { { .compatible = "brcm,bcm4330-bt" }, { .compatible = "brcm,bcm4334-bt" }, { .compatible = "brcm,bcm4345c5" }, + { .compatible = "brcm,bcm43430a0-bt" }, + { .compatible = "brcm,bcm43430a1-bt" }, { .compatible = "brcm,bcm43438-bt", .data = &bcm43438_device_data }, { .compatible = "brcm,bcm43540-bt", .data = &bcm4354_device_data }, { .compatible = "brcm,bcm4335a0" }, diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 34286ffe0568..c5a0409ef84f 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -629,9 +629,11 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb) break; } - pm_runtime_get_sync(&hu->serdev->dev); - pm_runtime_mark_last_busy(&hu->serdev->dev); - pm_runtime_put_autosuspend(&hu->serdev->dev); + if (hu->serdev) { + pm_runtime_get_sync(&hu->serdev->dev); + pm_runtime_mark_last_busy(&hu->serdev->dev); + pm_runtime_put_autosuspend(&hu->serdev->dev); + } return 0; } @@ -966,6 +968,11 @@ static void h5_btrtl_open(struct h5 *h5) pm_runtime_enable(&h5->hu->serdev->dev); } + /* The controller needs reset to startup */ + gpiod_set_value_cansleep(h5->enable_gpio, 0); + gpiod_set_value_cansleep(h5->device_wake_gpio, 0); + msleep(100); + /* The controller needs up to 500ms to wakeup */ gpiod_set_value_cansleep(h5->enable_gpio, 1); gpiod_set_value_cansleep(h5->device_wake_gpio, 1); diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index eb1e736efeeb..4eb420a9ed04 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -509,7 +509,7 @@ static int send_command_from_firmware(struct ll_device *lldev, return 0; } -/** +/* * download_firmware - * internal function which parses through the .bts firmware * script file intreprets SEND, DELAY actions only as of now diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c index 3b00d82d36cf..4cda890ce647 100644 --- a/drivers/bluetooth/hci_serdev.c +++ b/drivers/bluetooth/hci_serdev.c @@ -305,6 +305,8 @@ int hci_uart_register_device(struct hci_uart *hu, if (err) return err; + percpu_init_rwsem(&hu->proto_lock); + err = p->open(hu); if (err) goto err_open; @@ -327,7 +329,6 @@ int hci_uart_register_device(struct hci_uart *hu, INIT_WORK(&hu->init_ready, hci_uart_init_work); INIT_WORK(&hu->write_work, hci_uart_write_work); - percpu_init_rwsem(&hu->proto_lock); /* Only when vendor specific setup callback is provided, consider * the manufacturer information valid. This avoids filling in the diff --git a/drivers/infiniband/hw/mlx5/cong.c b/drivers/infiniband/hw/mlx5/cong.c index 0b61df52332a..290ea8ac3838 100644 --- a/drivers/infiniband/hw/mlx5/cong.c +++ b/drivers/infiniband/hw/mlx5/cong.c @@ -433,8 +433,7 @@ void mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u32 port_num) dev->port[port_num].dbg_cc_params = dbg_cc_params; - dbg_cc_params->root = debugfs_create_dir("cc_params", - mdev->priv.dbg_root); + dbg_cc_params->root = debugfs_create_dir("cc_params", mlx5_debugfs_get_dev_root(mdev)); for (i = 0; i < MLX5_IB_DBG_CC_MAX; i++) { dbg_cc_params->params[i].offset = i; diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 08b7f6bc56c3..fc036b4794fd 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1055,7 +1055,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OTHER)( int cmd_out_len = uverbs_attr_get_len(attrs, MLX5_IB_ATTR_DEVX_OTHER_CMD_OUT); void *cmd_out; - int err; + int err, err2; int uid; c = devx_ufile2uctx(attrs); @@ -1076,14 +1076,16 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OTHER)( return PTR_ERR(cmd_out); MLX5_SET(general_obj_in_cmd_hdr, cmd_in, uid, uid); - err = mlx5_cmd_exec(dev->mdev, cmd_in, - uverbs_attr_get_len(attrs, MLX5_IB_ATTR_DEVX_OTHER_CMD_IN), - cmd_out, cmd_out_len); - if (err) + err = mlx5_cmd_do(dev->mdev, cmd_in, + uverbs_attr_get_len(attrs, MLX5_IB_ATTR_DEVX_OTHER_CMD_IN), + cmd_out, cmd_out_len); + if (err && err != -EREMOTEIO) return err; - return uverbs_copy_to(attrs, MLX5_IB_ATTR_DEVX_OTHER_CMD_OUT, cmd_out, + err2 = uverbs_copy_to(attrs, MLX5_IB_ATTR_DEVX_OTHER_CMD_OUT, cmd_out, cmd_out_len); + + return err2 ?: err; } static void devx_obj_build_destroy_cmd(void *in, void *out, void *din, @@ -1457,7 +1459,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)( u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)]; struct devx_obj *obj; u16 obj_type = 0; - int err; + int err, err2 = 0; int uid; u32 obj_id; u16 opcode; @@ -1497,15 +1499,18 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)( !is_apu_cq(dev, cmd_in)) { obj->flags |= DEVX_OBJ_FLAGS_CQ; obj->core_cq.comp = devx_cq_comp; - err = mlx5_core_create_cq(dev->mdev, &obj->core_cq, - cmd_in, cmd_in_len, cmd_out, - cmd_out_len); + err = mlx5_create_cq(dev->mdev, &obj->core_cq, + cmd_in, cmd_in_len, cmd_out, + cmd_out_len); } else { - err = mlx5_cmd_exec(dev->mdev, cmd_in, - cmd_in_len, - cmd_out, cmd_out_len); + err = mlx5_cmd_do(dev->mdev, cmd_in, cmd_in_len, + cmd_out, cmd_out_len); } + if (err == -EREMOTEIO) + err2 = uverbs_copy_to(attrs, + MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_OUT, + cmd_out, cmd_out_len); if (err) goto obj_free; @@ -1548,7 +1553,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)( sizeof(out)); obj_free: kfree(obj); - return err; + return err2 ?: err; } static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_MODIFY)( @@ -1563,7 +1568,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_MODIFY)( &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext); struct mlx5_ib_dev *mdev = to_mdev(c->ibucontext.device); void *cmd_out; - int err; + int err, err2; int uid; if (MLX5_GET(general_obj_in_cmd_hdr, cmd_in, vhca_tunnel_id)) @@ -1586,14 +1591,16 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_MODIFY)( MLX5_SET(general_obj_in_cmd_hdr, cmd_in, uid, uid); devx_set_umem_valid(cmd_in); - err = mlx5_cmd_exec(mdev->mdev, cmd_in, - uverbs_attr_get_len(attrs, MLX5_IB_ATTR_DEVX_OBJ_MODIFY_CMD_IN), - cmd_out, cmd_out_len); - if (err) + err = mlx5_cmd_do(mdev->mdev, cmd_in, + uverbs_attr_get_len(attrs, MLX5_IB_ATTR_DEVX_OBJ_MODIFY_CMD_IN), + cmd_out, cmd_out_len); + if (err && err != -EREMOTEIO) return err; - return uverbs_copy_to(attrs, MLX5_IB_ATTR_DEVX_OBJ_MODIFY_CMD_OUT, + err2 = uverbs_copy_to(attrs, MLX5_IB_ATTR_DEVX_OBJ_MODIFY_CMD_OUT, cmd_out, cmd_out_len); + + return err2 ?: err; } static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_QUERY)( @@ -1607,7 +1614,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_QUERY)( struct mlx5_ib_ucontext *c = rdma_udata_to_drv_context( &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext); void *cmd_out; - int err; + int err, err2; int uid; struct mlx5_ib_dev *mdev = to_mdev(c->ibucontext.device); @@ -1629,14 +1636,16 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_QUERY)( return PTR_ERR(cmd_out); MLX5_SET(general_obj_in_cmd_hdr, cmd_in, uid, uid); - err = mlx5_cmd_exec(mdev->mdev, cmd_in, - uverbs_attr_get_len(attrs, MLX5_IB_ATTR_DEVX_OBJ_QUERY_CMD_IN), - cmd_out, cmd_out_len); - if (err) + err = mlx5_cmd_do(mdev->mdev, cmd_in, + uverbs_attr_get_len(attrs, MLX5_IB_ATTR_DEVX_OBJ_QUERY_CMD_IN), + cmd_out, cmd_out_len); + if (err && err != -EREMOTEIO) return err; - return uverbs_copy_to(attrs, MLX5_IB_ATTR_DEVX_OBJ_QUERY_CMD_OUT, + err2 = uverbs_copy_to(attrs, MLX5_IB_ATTR_DEVX_OBJ_QUERY_CMD_OUT, cmd_out, cmd_out_len); + + return err2 ?: err; } struct devx_async_event_queue { diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 85f526c861e9..32a0ea820573 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -4178,7 +4178,7 @@ static int mlx5_ib_stage_delay_drop_init(struct mlx5_ib_dev *dev) if (!mlx5_debugfs_root) return 0; - root = debugfs_create_dir("delay_drop", dev->mdev->priv.dbg_root); + root = debugfs_create_dir("delay_drop", mlx5_debugfs_get_dev_root(dev->mdev)); dev->delay_drop.dir_debugfs = root; debugfs_create_atomic_t("num_timeout_events", 0400, root, diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 157d862fb864..32cb7068f0ca 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -140,6 +140,19 @@ static int destroy_mkey(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) return mlx5_core_destroy_mkey(dev->mdev, mr->mmkey.key); } +static void create_mkey_warn(struct mlx5_ib_dev *dev, int status, void *out) +{ + if (status == -ENXIO) /* core driver is not available */ + return; + + mlx5_ib_warn(dev, "async reg mr failed. status %d\n", status); + if (status != -EREMOTEIO) /* driver specific failure */ + return; + + /* Failed in FW, print cmd out failure details */ + mlx5_cmd_out_err(dev->mdev, MLX5_CMD_OP_CREATE_MKEY, 0, out); +} + static void create_mkey_callback(int status, struct mlx5_async_work *context) { struct mlx5_ib_mr *mr = @@ -149,7 +162,7 @@ static void create_mkey_callback(int status, struct mlx5_async_work *context) unsigned long flags; if (status) { - mlx5_ib_warn(dev, "async reg mr failed. status %d\n", status); + create_mkey_warn(dev, status, mr->out); kfree(mr); spin_lock_irqsave(&ent->lock, flags); ent->pending--; @@ -683,7 +696,7 @@ static void mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev) if (!mlx5_debugfs_root || dev->is_rep) return; - cache->root = debugfs_create_dir("mr_cache", dev->mdev->priv.dbg_root); + cache->root = debugfs_create_dir("mr_cache", mlx5_debugfs_get_dev_root(dev->mdev)); for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) { ent = &cache->ent[i]; diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 29475cf8c7c3..b7fe47107d76 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -4465,6 +4465,7 @@ static int mlx5_ib_modify_dct(struct ib_qp *ibqp, struct ib_qp_attr *attr, err = mlx5_core_create_dct(dev, &qp->dct.mdct, qp->dct.in, MLX5_ST_SZ_BYTES(create_dct_in), out, sizeof(out)); + err = mlx5_cmd_check(dev->mdev, err, qp->dct.in, out); if (err) return err; resp.dctn = qp->dct.mdct.mqp.qpn; diff --git a/drivers/infiniband/hw/mlx5/qpc.c b/drivers/infiniband/hw/mlx5/qpc.c index 8844eacf2380..542e4c63a8de 100644 --- a/drivers/infiniband/hw/mlx5/qpc.c +++ b/drivers/infiniband/hw/mlx5/qpc.c @@ -220,7 +220,7 @@ int mlx5_core_create_dct(struct mlx5_ib_dev *dev, struct mlx5_core_dct *dct, init_completion(&dct->drained); MLX5_SET(create_dct_in, in, opcode, MLX5_CMD_OP_CREATE_DCT); - err = mlx5_cmd_exec(dev->mdev, in, inlen, out, outlen); + err = mlx5_cmd_do(dev->mdev, in, inlen, out, outlen); if (err) return err; diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c index 39f841b42488..4f8d85bb3ce1 100644 --- a/drivers/isdn/hardware/mISDN/mISDNipac.c +++ b/drivers/isdn/hardware/mISDN/mISDNipac.c @@ -1062,7 +1062,7 @@ ipac_rme(struct hscx_hw *hx) if (!hx->bch.rx_skb) return; if (hx->bch.rx_skb->len < 2) { - pr_debug("%s: B%1d frame to short %d\n", + pr_debug("%s: B%1d frame too short %d\n", hx->ip->name, hx->bch.nr, hx->bch.rx_skb->len); skb_trim(hx->bch.rx_skb, 0); } else { diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c index 56943409b60d..48b3d43e2502 100644 --- a/drivers/isdn/hardware/mISDN/mISDNisar.c +++ b/drivers/isdn/hardware/mISDN/mISDNisar.c @@ -466,7 +466,7 @@ isar_rcv_frame(struct isar_ch *ch) rcv_mbox(ch->is, ptr); if (ch->is->cmsb & HDLC_FED) { if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */ - pr_debug("%s: ISAR frame to short %d\n", + pr_debug("%s: ISAR frame too short %d\n", ch->is->name, ch->bch.rx_skb->len); skb_trim(ch->bch.rx_skb, 0); break; @@ -542,7 +542,7 @@ isar_rcv_frame(struct isar_ch *ch) rcv_mbox(ch->is, ptr); if (ch->is->cmsb & HDLC_FED) { if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */ - pr_info("%s: ISAR frame to short %d\n", + pr_info("%s: ISAR frame too short %d\n", ch->is->name, ch->bch.rx_skb->len); skb_trim(ch->bch.rx_skb, 0); break; diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index dab7b92db790..50644f83e78c 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c @@ -247,7 +247,7 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg) xpnet_device->stats.rx_packets++; xpnet_device->stats.rx_bytes += skb->len + ETH_HLEN; - netif_rx_ni(skb); + netif_rx(skb); xpc_received(partid, channel, (void *)msg); } diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 50b23e71065f..3f1192d3c52d 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -31,7 +31,7 @@ obj-$(CONFIG_TUN) += tun.o obj-$(CONFIG_TAP) += tap.o obj-$(CONFIG_VETH) += veth.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o -obj-$(CONFIG_VXLAN) += vxlan.o +obj-$(CONFIG_VXLAN) += vxlan/ obj-$(CONFIG_GENEVE) += geneve.o obj-$(CONFIG_BAREUDP) += bareudp.o obj-$(CONFIG_GTP) += gtp.o diff --git a/drivers/net/amt.c b/drivers/net/amt.c index f1a36d7e2151..10455c9b9da0 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -2373,7 +2373,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt, skb->pkt_type = PACKET_MULTICAST; skb->ip_summed = CHECKSUM_NONE; len = skb->len; - if (netif_rx(skb) == NET_RX_SUCCESS) { + if (__netif_rx(skb) == NET_RX_SUCCESS) { amt_update_gw_status(amt, AMT_STATUS_RECEIVED_QUERY, true); dev_sw_netstats_rx_add(amt->dev, len); } else { @@ -2470,7 +2470,7 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb) skb->pkt_type = PACKET_MULTICAST; skb->ip_summed = CHECKSUM_NONE; len = skb->len; - if (netif_rx(skb) == NET_RX_SUCCESS) { + if (__netif_rx(skb) == NET_RX_SUCCESS) { amt_update_relay_status(tunnel, AMT_STATUS_RECEIVED_UPDATE, true); dev_sw_netstats_rx_add(amt->dev, len); diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c index ba587e5fc24f..683203f87ae2 100644 --- a/drivers/net/bareudp.c +++ b/drivers/net/bareudp.c @@ -148,14 +148,14 @@ static int bareudp_udp_encap_recv(struct sock *sk, struct sk_buff *skb) skb_reset_network_header(skb); skb_reset_mac_header(skb); - if (!IS_ENABLED(CONFIG_IPV6) || family == AF_INET) + if (!ipv6_mod_enabled() || family == AF_INET) err = IP_ECN_decapsulate(oiph, skb); else err = IP6_ECN_decapsulate(oiph, skb); if (unlikely(err)) { if (log_ecn_error) { - if (!IS_ENABLED(CONFIG_IPV6) || family == AF_INET) + if (!ipv6_mod_enabled() || family == AF_INET) net_info_ratelimited("non-ECT from %pI4 " "with TOS=%#x\n", &((struct iphdr *)oiph)->saddr, @@ -221,11 +221,12 @@ static struct socket *bareudp_create_sock(struct net *net, __be16 port) int err; memset(&udp_conf, 0, sizeof(udp_conf)); -#if IS_ENABLED(CONFIG_IPV6) - udp_conf.family = AF_INET6; -#else - udp_conf.family = AF_INET; -#endif + + if (ipv6_mod_enabled()) + udp_conf.family = AF_INET6; + else + udp_conf.family = AF_INET; + udp_conf.local_udp_port = port; /* Open UDP socket */ err = udp_sock_create(net, &udp_conf, &sock); @@ -448,7 +449,7 @@ static netdev_tx_t bareudp_xmit(struct sk_buff *skb, struct net_device *dev) } rcu_read_lock(); - if (IS_ENABLED(CONFIG_IPV6) && info->mode & IP_TUNNEL_INFO_IPV6) + if (ipv6_mod_enabled() && info->mode & IP_TUNNEL_INFO_IPV6) err = bareudp6_xmit_skb(skb, dev, bareudp, info); else err = bareudp_xmit_skb(skb, dev, bareudp, info); @@ -478,7 +479,7 @@ static int bareudp_fill_metadata_dst(struct net_device *dev, use_cache = ip_tunnel_dst_cache_usable(skb, info); - if (!IS_ENABLED(CONFIG_IPV6) || ip_tunnel_info_af(info) == AF_INET) { + if (!ipv6_mod_enabled() || ip_tunnel_info_af(info) == AF_INET) { struct rtable *rt; __be32 saddr; diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 533e476988f2..303c8d32d451 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1269,6 +1270,27 @@ static int alb_set_mac_address(struct bonding *bond, void *addr) return res; } +/* determine if the packet is NA or NS */ +static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond) +{ + struct ipv6hdr *ip6hdr; + struct icmp6hdr *hdr; + + if (!pskb_network_may_pull(skb, sizeof(*ip6hdr))) + return true; + + ip6hdr = ipv6_hdr(skb); + if (ip6hdr->nexthdr != IPPROTO_ICMPV6) + return false; + + if (!pskb_network_may_pull(skb, sizeof(*ip6hdr) + sizeof(*hdr))) + return true; + + hdr = icmp6_hdr(skb); + return hdr->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT || + hdr->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION; +} + /************************ exported alb functions ************************/ int bond_alb_initialize(struct bonding *bond, int rlb_enabled) @@ -1348,8 +1370,11 @@ struct slave *bond_xmit_tlb_slave_get(struct bonding *bond, /* Do not TX balance any multicast or broadcast */ if (!is_multicast_ether_addr(eth_data->h_dest)) { switch (skb->protocol) { - case htons(ETH_P_IP): case htons(ETH_P_IPV6): + if (alb_determine_nd(skb, bond)) + break; + fallthrough; + case htons(ETH_P_IP): hash_index = bond_xmit_hash(bond, skb); if (bond->params.tlb_dynamic_lb) { tx_slave = tlb_choose_channel(bond, @@ -1432,10 +1457,12 @@ struct slave *bond_xmit_alb_slave_get(struct bonding *bond, break; } - if (!pskb_network_may_pull(skb, sizeof(*ip6hdr))) { + if (alb_determine_nd(skb, bond)) { do_tx_balance = false; break; } + + /* The IPv6 header is pulled by alb_determine_nd */ /* Additionally, DAD probes should not be tx-balanced as that * will lead to false positives for duplicate addresses and * prevent address configuration from working. diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index aebeb46e6fa6..15eddca7b4b6 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -88,6 +88,7 @@ #if IS_ENABLED(CONFIG_TLS_DEVICE) #include #endif +#include #include "bonding_priv.h" @@ -2793,31 +2794,15 @@ static bool bond_has_this_ip(struct bonding *bond, __be32 ip) return ret; } -/* We go to the (large) trouble of VLAN tagging ARP frames because - * switches in VLAN mode (especially if ports are configured as - * "native" to a VLAN) might not pass non-tagged frames. - */ -static void bond_arp_send(struct slave *slave, int arp_op, __be32 dest_ip, - __be32 src_ip, struct bond_vlan_tag *tags) +static bool bond_handle_vlan(struct slave *slave, struct bond_vlan_tag *tags, + struct sk_buff *skb) { - struct sk_buff *skb; - struct bond_vlan_tag *outer_tag = tags; - struct net_device *slave_dev = slave->dev; struct net_device *bond_dev = slave->bond->dev; - - slave_dbg(bond_dev, slave_dev, "arp %d on slave: dst %pI4 src %pI4\n", - arp_op, &dest_ip, &src_ip); - - skb = arp_create(arp_op, ETH_P_ARP, dest_ip, slave_dev, src_ip, - NULL, slave_dev->dev_addr, NULL); - - if (!skb) { - net_err_ratelimited("ARP packet allocation failed\n"); - return; - } + struct net_device *slave_dev = slave->dev; + struct bond_vlan_tag *outer_tag = tags; if (!tags || tags->vlan_proto == VLAN_N_VID) - goto xmit; + return true; tags++; @@ -2834,7 +2819,7 @@ static void bond_arp_send(struct slave *slave, int arp_op, __be32 dest_ip, tags->vlan_id); if (!skb) { net_err_ratelimited("failed to insert inner VLAN tag\n"); - return; + return false; } tags++; @@ -2847,8 +2832,34 @@ static void bond_arp_send(struct slave *slave, int arp_op, __be32 dest_ip, outer_tag->vlan_id); } -xmit: - arp_xmit(skb); + return true; +} + +/* We go to the (large) trouble of VLAN tagging ARP frames because + * switches in VLAN mode (especially if ports are configured as + * "native" to a VLAN) might not pass non-tagged frames. + */ +static void bond_arp_send(struct slave *slave, int arp_op, __be32 dest_ip, + __be32 src_ip, struct bond_vlan_tag *tags) +{ + struct net_device *bond_dev = slave->bond->dev; + struct net_device *slave_dev = slave->dev; + struct sk_buff *skb; + + slave_dbg(bond_dev, slave_dev, "arp %d on slave: dst %pI4 src %pI4\n", + arp_op, &dest_ip, &src_ip); + + skb = arp_create(arp_op, ETH_P_ARP, dest_ip, slave_dev, src_ip, + NULL, slave_dev->dev_addr, NULL); + + if (!skb) { + net_err_ratelimited("ARP packet allocation failed\n"); + return; + } + + if (bond_handle_vlan(slave, tags, skb)) + arp_xmit(skb); + return; } /* Validate the device path between the @start_dev and the @end_dev. @@ -2965,30 +2976,17 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32 slave->target_last_arp_rx[i] = jiffies; } -int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, - struct slave *slave) +static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, + struct slave *slave) { struct arphdr *arp = (struct arphdr *)skb->data; struct slave *curr_active_slave, *curr_arp_slave; unsigned char *arp_ptr; __be32 sip, tip; - int is_arp = skb->protocol == __cpu_to_be16(ETH_P_ARP); unsigned int alen; - if (!slave_do_arp_validate(bond, slave)) { - if ((slave_do_arp_validate_only(bond) && is_arp) || - !slave_do_arp_validate_only(bond)) - slave->last_rx = jiffies; - return RX_HANDLER_ANOTHER; - } else if (!is_arp) { - return RX_HANDLER_ANOTHER; - } - alen = arp_hdr_len(bond->dev); - slave_dbg(bond->dev, slave->dev, "%s: skb->dev %s\n", - __func__, skb->dev->name); - if (alen > skb_headlen(skb)) { arp = kmalloc(alen, GFP_ATOMIC); if (!arp) @@ -3059,6 +3057,216 @@ int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, return RX_HANDLER_ANOTHER; } +#if IS_ENABLED(CONFIG_IPV6) +static void bond_ns_send(struct slave *slave, const struct in6_addr *daddr, + const struct in6_addr *saddr, struct bond_vlan_tag *tags) +{ + struct net_device *bond_dev = slave->bond->dev; + struct net_device *slave_dev = slave->dev; + struct in6_addr mcaddr; + struct sk_buff *skb; + + slave_dbg(bond_dev, slave_dev, "NS on slave: dst %pI6c src %pI6c\n", + daddr, saddr); + + skb = ndisc_ns_create(slave_dev, daddr, saddr, 0); + if (!skb) { + net_err_ratelimited("NS packet allocation failed\n"); + return; + } + + addrconf_addr_solict_mult(daddr, &mcaddr); + if (bond_handle_vlan(slave, tags, skb)) + ndisc_send_skb(skb, &mcaddr, saddr); +} + +static void bond_ns_send_all(struct bonding *bond, struct slave *slave) +{ + struct in6_addr *targets = bond->params.ns_targets; + struct bond_vlan_tag *tags; + struct dst_entry *dst; + struct in6_addr saddr; + struct flowi6 fl6; + int i; + + for (i = 0; i < BOND_MAX_NS_TARGETS && !ipv6_addr_any(&targets[i]); i++) { + slave_dbg(bond->dev, slave->dev, "%s: target %pI6c\n", + __func__, &targets[i]); + tags = NULL; + + /* Find out through which dev should the packet go */ + memset(&fl6, 0, sizeof(struct flowi6)); + fl6.daddr = targets[i]; + fl6.flowi6_oif = bond->dev->ifindex; + + dst = ip6_route_output(dev_net(bond->dev), NULL, &fl6); + if (dst->error) { + dst_release(dst); + /* there's no route to target - try to send arp + * probe to generate any traffic (arp_validate=0) + */ + if (bond->params.arp_validate) + pr_warn_once("%s: no route to ns_ip6_target %pI6c and arp_validate is set\n", + bond->dev->name, + &targets[i]); + bond_ns_send(slave, &targets[i], &in6addr_any, tags); + continue; + } + + /* bond device itself */ + if (dst->dev == bond->dev) + goto found; + + rcu_read_lock(); + tags = bond_verify_device_path(bond->dev, dst->dev, 0); + rcu_read_unlock(); + + if (!IS_ERR_OR_NULL(tags)) + goto found; + + /* Not our device - skip */ + slave_dbg(bond->dev, slave->dev, "no path to ns_ip6_target %pI6c via dst->dev %s\n", + &targets[i], dst->dev ? dst->dev->name : "NULL"); + + dst_release(dst); + continue; + +found: + if (!ipv6_dev_get_saddr(dev_net(dst->dev), dst->dev, &targets[i], 0, &saddr)) + bond_ns_send(slave, &targets[i], &saddr, tags); + dst_release(dst); + kfree(tags); + } +} + +static int bond_confirm_addr6(struct net_device *dev, + struct netdev_nested_priv *priv) +{ + struct in6_addr *addr = (struct in6_addr *)priv->data; + + return ipv6_chk_addr(dev_net(dev), addr, dev, 0); +} + +static bool bond_has_this_ip6(struct bonding *bond, struct in6_addr *addr) +{ + struct netdev_nested_priv priv = { + .data = addr, + }; + int ret = false; + + if (bond_confirm_addr6(bond->dev, &priv)) + return true; + + rcu_read_lock(); + if (netdev_walk_all_upper_dev_rcu(bond->dev, bond_confirm_addr6, &priv)) + ret = true; + rcu_read_unlock(); + + return ret; +} + +static void bond_validate_ns(struct bonding *bond, struct slave *slave, + struct in6_addr *saddr, struct in6_addr *daddr) +{ + int i; + + if (ipv6_addr_any(saddr) || !bond_has_this_ip6(bond, daddr)) { + slave_dbg(bond->dev, slave->dev, "%s: sip %pI6c tip %pI6c not found\n", + __func__, saddr, daddr); + return; + } + + i = bond_get_targets_ip6(bond->params.ns_targets, saddr); + if (i == -1) { + slave_dbg(bond->dev, slave->dev, "%s: sip %pI6c not found in targets\n", + __func__, saddr); + return; + } + slave->last_rx = jiffies; + slave->target_last_arp_rx[i] = jiffies; +} + +static int bond_na_rcv(const struct sk_buff *skb, struct bonding *bond, + struct slave *slave) +{ + struct slave *curr_active_slave, *curr_arp_slave; + struct icmp6hdr *hdr = icmp6_hdr(skb); + struct in6_addr *saddr, *daddr; + + if (skb->pkt_type == PACKET_OTHERHOST || + skb->pkt_type == PACKET_LOOPBACK || + hdr->icmp6_type != NDISC_NEIGHBOUR_ADVERTISEMENT) + goto out; + + saddr = &ipv6_hdr(skb)->saddr; + daddr = &ipv6_hdr(skb)->daddr; + + slave_dbg(bond->dev, slave->dev, "%s: %s/%d av %d sv %d sip %pI6c tip %pI6c\n", + __func__, slave->dev->name, bond_slave_state(slave), + bond->params.arp_validate, slave_do_arp_validate(bond, slave), + saddr, daddr); + + curr_active_slave = rcu_dereference(bond->curr_active_slave); + curr_arp_slave = rcu_dereference(bond->current_arp_slave); + + /* We 'trust' the received ARP enough to validate it if: + * see bond_arp_rcv(). + */ + if (bond_is_active_slave(slave)) + bond_validate_ns(bond, slave, saddr, daddr); + else if (curr_active_slave && + time_after(slave_last_rx(bond, curr_active_slave), + curr_active_slave->last_link_up)) + bond_validate_ns(bond, slave, saddr, daddr); + else if (curr_arp_slave && + bond_time_in_interval(bond, + dev_trans_start(curr_arp_slave->dev), 1)) + bond_validate_ns(bond, slave, saddr, daddr); + +out: + return RX_HANDLER_ANOTHER; +} +#endif + +int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond, + struct slave *slave) +{ +#if IS_ENABLED(CONFIG_IPV6) + bool is_ipv6 = skb->protocol == __cpu_to_be16(ETH_P_IPV6); +#endif + bool is_arp = skb->protocol == __cpu_to_be16(ETH_P_ARP); + + slave_dbg(bond->dev, slave->dev, "%s: skb->dev %s\n", + __func__, skb->dev->name); + + /* Use arp validate logic for both ARP and NS */ + if (!slave_do_arp_validate(bond, slave)) { + if ((slave_do_arp_validate_only(bond) && is_arp) || +#if IS_ENABLED(CONFIG_IPV6) + (slave_do_arp_validate_only(bond) && is_ipv6) || +#endif + !slave_do_arp_validate_only(bond)) + slave->last_rx = jiffies; + return RX_HANDLER_ANOTHER; + } else if (is_arp) { + return bond_arp_rcv(skb, bond, slave); +#if IS_ENABLED(CONFIG_IPV6) + } else if (is_ipv6) { + return bond_na_rcv(skb, bond, slave); +#endif + } else { + return RX_HANDLER_ANOTHER; + } +} + +static void bond_send_validate(struct bonding *bond, struct slave *slave) +{ + bond_arp_send_all(bond, slave); +#if IS_ENABLED(CONFIG_IPV6) + bond_ns_send_all(bond, slave); +#endif +} + /* function to verify if we're in the arp_interval timeslice, returns true if * (last_act - arp_interval) <= jiffies <= (last_act + mod * arp_interval + * arp_interval/2) . the arp_interval/2 is needed for really fast networks. @@ -3154,7 +3362,7 @@ static void bond_loadbalance_arp_mon(struct bonding *bond) * to be unstable during low/no traffic periods */ if (bond_slave_is_up(slave)) - bond_arp_send_all(bond, slave); + bond_send_validate(bond, slave); } rcu_read_unlock(); @@ -3368,7 +3576,7 @@ static bool bond_ab_arp_probe(struct bonding *bond) curr_active_slave->dev->name); if (curr_active_slave) { - bond_arp_send_all(bond, curr_active_slave); + bond_send_validate(bond, curr_active_slave); return should_notify_rtnl; } @@ -3420,7 +3628,7 @@ static bool bond_ab_arp_probe(struct bonding *bond) bond_set_slave_link_state(new_slave, BOND_LINK_BACK, BOND_SLAVE_NOTIFY_LATER); bond_set_slave_active_flags(new_slave, BOND_SLAVE_NOTIFY_LATER); - bond_arp_send_all(bond, new_slave); + bond_send_validate(bond, new_slave); new_slave->last_link_up = jiffies; rcu_assign_pointer(bond->current_arp_slave, new_slave); @@ -3956,7 +4164,7 @@ static int bond_open(struct net_device *bond_dev) if (bond->params.arp_interval) { /* arp interval, in milliseconds. */ queue_delayed_work(bond->wq, &bond->arp_work, 0); - bond->recv_probe = bond_arp_rcv; + bond->recv_probe = bond_rcv_validate; } if (BOND_MODE(bond) == BOND_MODE_8023AD) { @@ -4912,7 +5120,7 @@ static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb, if (xmit_suc) return NETDEV_TX_OK; - atomic_long_inc(&bond_dev->tx_dropped); + dev_core_stats_tx_dropped_inc(bond_dev); return NET_XMIT_DROP; } @@ -5937,6 +6145,7 @@ static int bond_check_params(struct bond_params *params) strscpy_pad(params->primary, primary, sizeof(params->primary)); memcpy(params->arp_targets, arp_target, sizeof(arp_target)); + memset(params->ns_targets, 0, sizeof(struct in6_addr) * BOND_MAX_NS_TARGETS); return 0; } @@ -6047,27 +6256,38 @@ static int __net_init bond_net_init(struct net *net) return 0; } -static void __net_exit bond_net_exit(struct net *net) +static void __net_exit bond_net_exit_batch(struct list_head *net_list) { - struct bond_net *bn = net_generic(net, bond_net_id); - struct bonding *bond, *tmp_bond; + struct bond_net *bn; + struct net *net; LIST_HEAD(list); - bond_destroy_sysfs(bn); + list_for_each_entry(net, net_list, exit_list) { + bn = net_generic(net, bond_net_id); + bond_destroy_sysfs(bn); + } /* Kill off any bonds created after unregistering bond rtnl ops */ rtnl_lock(); - list_for_each_entry_safe(bond, tmp_bond, &bn->dev_list, bond_list) - unregister_netdevice_queue(bond->dev, &list); + list_for_each_entry(net, net_list, exit_list) { + struct bonding *bond, *tmp_bond; + + bn = net_generic(net, bond_net_id); + list_for_each_entry_safe(bond, tmp_bond, &bn->dev_list, bond_list) + unregister_netdevice_queue(bond->dev, &list); + } unregister_netdevice_many(&list); rtnl_unlock(); - bond_destroy_proc_dir(bn); + list_for_each_entry(net, net_list, exit_list) { + bn = net_generic(net, bond_net_id); + bond_destroy_proc_dir(bn); + } } static struct pernet_operations bond_net_ops = { .init = bond_net_init, - .exit = bond_net_exit, + .exit_batch = bond_net_exit_batch, .id = &bond_net_id, .size = sizeof(struct bond_net), }; diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index 1007bf6d385d..f427fa1737c7 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -14,6 +14,7 @@ #include #include #include +#include static size_t bond_get_slave_size(const struct net_device *bond_dev, const struct net_device *slave_dev) @@ -111,6 +112,7 @@ static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = { [IFLA_BOND_TLB_DYNAMIC_LB] = { .type = NLA_U8 }, [IFLA_BOND_PEER_NOTIF_DELAY] = { .type = NLA_U32 }, [IFLA_BOND_MISSED_MAX] = { .type = NLA_U8 }, + [IFLA_BOND_NS_IP6_TARGET] = { .type = NLA_NESTED }, }; static const struct nla_policy bond_slave_policy[IFLA_BOND_SLAVE_MAX + 1] = { @@ -272,6 +274,40 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[], if (err) return err; } +#if IS_ENABLED(CONFIG_IPV6) + if (data[IFLA_BOND_NS_IP6_TARGET]) { + struct nlattr *attr; + int i = 0, rem; + + bond_option_ns_ip6_targets_clear(bond); + nla_for_each_nested(attr, data[IFLA_BOND_NS_IP6_TARGET], rem) { + struct in6_addr addr6; + + if (nla_len(attr) < sizeof(addr6)) { + NL_SET_ERR_MSG(extack, "Invalid IPv6 address"); + return -EINVAL; + } + + addr6 = nla_get_in6_addr(attr); + + if (ipv6_addr_type(&addr6) & IPV6_ADDR_LINKLOCAL) { + NL_SET_ERR_MSG(extack, "Invalid IPv6 addr6"); + return -EINVAL; + } + + bond_opt_initextra(&newval, &addr6, sizeof(addr6)); + err = __bond_opt_set(bond, BOND_OPT_NS_TARGETS, + &newval); + if (err) + break; + i++; + } + if (i == 0 && bond->params.arp_interval) + netdev_warn(bond->dev, "Removing last ns target with arp_interval on\n"); + if (err) + return err; + } +#endif if (data[IFLA_BOND_ARP_VALIDATE]) { int arp_validate = nla_get_u32(data[IFLA_BOND_ARP_VALIDATE]); @@ -526,6 +562,9 @@ static size_t bond_get_size(const struct net_device *bond_dev) nla_total_size(sizeof(u8)) + /* IFLA_BOND_TLB_DYNAMIC_LB */ nla_total_size(sizeof(u32)) + /* IFLA_BOND_PEER_NOTIF_DELAY */ nla_total_size(sizeof(u8)) + /* IFLA_BOND_MISSED_MAX */ + /* IFLA_BOND_NS_IP6_TARGET */ + nla_total_size(sizeof(struct nlattr)) + + nla_total_size(sizeof(struct in6_addr)) * BOND_MAX_NS_TARGETS + 0; } @@ -603,6 +642,26 @@ static int bond_fill_info(struct sk_buff *skb, bond->params.arp_all_targets)) goto nla_put_failure; +#if IS_ENABLED(CONFIG_IPV6) + targets = nla_nest_start(skb, IFLA_BOND_NS_IP6_TARGET); + if (!targets) + goto nla_put_failure; + + targets_added = 0; + for (i = 0; i < BOND_MAX_NS_TARGETS; i++) { + if (!ipv6_addr_any(&bond->params.ns_targets[i])) { + if (nla_put_in6_addr(skb, i, &bond->params.ns_targets[i])) + goto nla_put_failure; + targets_added = 1; + } + } + + if (targets_added) + nla_nest_end(skb, targets); + else + nla_nest_cancel(skb, targets); +#endif + primary = rtnl_dereference(bond->primary_slave); if (primary && nla_put_u32(skb, IFLA_BOND_PRIMARY, primary->dev->ifindex)) diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 2e8484a91a0e..64f7db2627ce 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -34,6 +34,10 @@ static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target); static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target); static int bond_option_arp_ip_targets_set(struct bonding *bond, const struct bond_opt_value *newval); +#if IS_ENABLED(CONFIG_IPV6) +static int bond_option_ns_ip6_targets_set(struct bonding *bond, + const struct bond_opt_value *newval); +#endif static int bond_option_arp_validate_set(struct bonding *bond, const struct bond_opt_value *newval); static int bond_option_arp_all_targets_set(struct bonding *bond, @@ -295,6 +299,15 @@ static const struct bond_option bond_opts[BOND_OPT_LAST] = { .flags = BOND_OPTFLAG_RAWVAL, .set = bond_option_arp_ip_targets_set }, +#if IS_ENABLED(CONFIG_IPV6) + [BOND_OPT_NS_TARGETS] = { + .id = BOND_OPT_NS_TARGETS, + .name = "ns_ip6_target", + .desc = "NS targets in ffff:ffff::ffff:ffff form", + .flags = BOND_OPTFLAG_RAWVAL, + .set = bond_option_ns_ip6_targets_set + }, +#endif [BOND_OPT_DOWNDELAY] = { .id = BOND_OPT_DOWNDELAY, .name = "downdelay", @@ -1052,7 +1065,7 @@ static int bond_option_arp_interval_set(struct bonding *bond, cancel_delayed_work_sync(&bond->arp_work); } else { /* arp_validate can be set only in active-backup mode */ - bond->recv_probe = bond_arp_rcv; + bond->recv_probe = bond_rcv_validate; cancel_delayed_work_sync(&bond->mii_work); queue_delayed_work(bond->wq, &bond->arp_work, 0); } @@ -1184,6 +1197,65 @@ static int bond_option_arp_ip_targets_set(struct bonding *bond, return ret; } +#if IS_ENABLED(CONFIG_IPV6) +static void _bond_options_ns_ip6_target_set(struct bonding *bond, int slot, + struct in6_addr *target, + unsigned long last_rx) +{ + struct in6_addr *targets = bond->params.ns_targets; + struct list_head *iter; + struct slave *slave; + + if (slot >= 0 && slot < BOND_MAX_NS_TARGETS) { + bond_for_each_slave(bond, slave, iter) + slave->target_last_arp_rx[slot] = last_rx; + targets[slot] = *target; + } +} + +void bond_option_ns_ip6_targets_clear(struct bonding *bond) +{ + struct in6_addr addr_any = in6addr_any; + int i; + + for (i = 0; i < BOND_MAX_NS_TARGETS; i++) + _bond_options_ns_ip6_target_set(bond, i, &addr_any, 0); +} + +static int bond_option_ns_ip6_targets_set(struct bonding *bond, + const struct bond_opt_value *newval) +{ + struct in6_addr *target = (struct in6_addr *)newval->extra; + struct in6_addr *targets = bond->params.ns_targets; + struct in6_addr addr_any = in6addr_any; + int index; + + if (!bond_is_ip6_target_ok(target)) { + netdev_err(bond->dev, "invalid NS target %pI6c specified for addition\n", + target); + return -EINVAL; + } + + if (bond_get_targets_ip6(targets, target) != -1) { /* dup */ + netdev_err(bond->dev, "NS target %pI6c is already present\n", + target); + return -EINVAL; + } + + index = bond_get_targets_ip6(targets, &addr_any); /* first free slot */ + if (index == -1) { + netdev_err(bond->dev, "NS target table is full!\n"); + return -EINVAL; + } + + netdev_dbg(bond->dev, "Adding NS target %pI6c\n", target); + + _bond_options_ns_ip6_target_set(bond, index, target, jiffies); + + return 0; +} +#endif + static int bond_option_arp_validate_set(struct bonding *bond, const struct bond_opt_value *newval) { diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index 46b150e6289e..cfe37be42be4 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -307,7 +307,6 @@ void __net_init bond_create_proc_dir(struct bond_net *bn) } /* Destroy the bonding directory under /proc/net, if empty. - * Caller must hold rtnl_lock. */ void __net_exit bond_destroy_proc_dir(struct bond_net *bn) { diff --git a/drivers/net/bonding/bond_sysfs_slave.c b/drivers/net/bonding/bond_sysfs_slave.c index 6a6cdd0bb258..69b0a3751dff 100644 --- a/drivers/net/bonding/bond_sysfs_slave.c +++ b/drivers/net/bonding/bond_sysfs_slave.c @@ -15,14 +15,8 @@ struct slave_attribute { ssize_t (*show)(struct slave *, char *); }; -#define SLAVE_ATTR(_name, _mode, _show) \ -const struct slave_attribute slave_attr_##_name = { \ - .attr = {.name = __stringify(_name), \ - .mode = _mode }, \ - .show = _show, \ -}; #define SLAVE_ATTR_RO(_name) \ - SLAVE_ATTR(_name, 0444, _name##_show) +const struct slave_attribute slave_attr_##_name = __ATTR_RO(_name) static ssize_t state_show(struct slave *slave, char *buf) { diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index 2a7af611d43a..688075859ae4 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -196,7 +196,7 @@ static void ldisc_receive(struct tty_struct *tty, const u8 *data, skb_reset_mac_header(skb); debugfs_rx(ser, data, count); /* Push received packet up the stack. */ - ret = netif_rx_ni(skb); + ret = netif_rx(skb); if (!ret) { ser->dev->stats.rx_packets++; ser->dev->stats.rx_bytes += count; diff --git a/drivers/net/can/c_can/c_can_ethtool.c b/drivers/net/can/c_can/c_can_ethtool.c index 6655146294fc..8a826a6813bd 100644 --- a/drivers/net/can/c_can/c_can_ethtool.c +++ b/drivers/net/can/c_can/c_can_ethtool.c @@ -11,14 +11,6 @@ #include "c_can.h" -static void c_can_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *info) -{ - struct c_can_priv *priv = netdev_priv(netdev); - strscpy(info->driver, "c_can", sizeof(info->driver)); - strscpy(info->bus_info, dev_name(priv->device), sizeof(info->bus_info)); -} - static void c_can_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam *kernel_ring, @@ -33,7 +25,6 @@ static void c_can_get_ringparam(struct net_device *netdev, } static const struct ethtool_ops c_can_ethtool_ops = { - .get_drvinfo = c_can_get_drvinfo, .get_ringparam = c_can_get_ringparam, }; diff --git a/drivers/net/can/dev/bittiming.c b/drivers/net/can/dev/bittiming.c index d5fca3bfaf9a..2103bcca9012 100644 --- a/drivers/net/can/dev/bittiming.c +++ b/drivers/net/can/dev/bittiming.c @@ -24,7 +24,7 @@ */ static int can_update_sample_point(const struct can_bittiming_const *btc, - unsigned int sample_point_nominal, unsigned int tseg, + const unsigned int sample_point_nominal, const unsigned int tseg, unsigned int *tseg1_ptr, unsigned int *tseg2_ptr, unsigned int *sample_point_error_ptr) { @@ -63,7 +63,7 @@ can_update_sample_point(const struct can_bittiming_const *btc, return best_sample_point; } -int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt, +int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt, const struct can_bittiming_const *btc) { struct can_priv *priv = netdev_priv(dev); @@ -208,10 +208,10 @@ void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const, * prescaler value brp. You can find more information in the header * file linux/can/netlink.h. */ -static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt, +static int can_fixup_bittiming(const struct net_device *dev, struct can_bittiming *bt, const struct can_bittiming_const *btc) { - struct can_priv *priv = netdev_priv(dev); + const struct can_priv *priv = netdev_priv(dev); unsigned int tseg1, alltseg; u64 brp64; @@ -244,25 +244,21 @@ static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt, /* Checks the validity of predefined bitrate settings */ static int -can_validate_bitrate(struct net_device *dev, struct can_bittiming *bt, +can_validate_bitrate(const struct net_device *dev, const struct can_bittiming *bt, const u32 *bitrate_const, const unsigned int bitrate_const_cnt) { - struct can_priv *priv = netdev_priv(dev); unsigned int i; for (i = 0; i < bitrate_const_cnt; i++) { if (bt->bitrate == bitrate_const[i]) - break; + return 0; } - if (i >= priv->bitrate_const_cnt) - return -EINVAL; - - return 0; + return -EINVAL; } -int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt, +int can_get_bittiming(const struct net_device *dev, struct can_bittiming *bt, const struct can_bittiming_const *btc, const u32 *bitrate_const, const unsigned int bitrate_const_cnt) diff --git a/drivers/net/can/dev/dev.c b/drivers/net/can/dev/dev.c index c192f25f9695..e7ab45f1c43b 100644 --- a/drivers/net/can/dev/dev.c +++ b/drivers/net/can/dev/dev.c @@ -154,7 +154,7 @@ static void can_restart(struct net_device *dev) cf->can_id |= CAN_ERR_RESTARTED; - netif_rx_ni(skb); + netif_rx(skb); restart: netdev_dbg(dev, "restarted\n"); diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index acd74725831f..1e121e04208c 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -44,6 +44,7 @@ enum rcanfd_chip_id { RENESAS_RCAR_GEN3 = 0, RENESAS_RZG2L, + RENESAS_R8A779A0, }; /* Global register bits */ @@ -79,6 +80,7 @@ enum rcanfd_chip_id { #define RCANFD_GSTS_GNOPM (BIT(0) | BIT(1) | BIT(2) | BIT(3)) /* RSCFDnCFDGERFL / RSCFDnGERFL */ +#define RCANFD_GERFL_EEF0_7 GENMASK(23, 16) #define RCANFD_GERFL_EEF1 BIT(17) #define RCANFD_GERFL_EEF0 BIT(16) #define RCANFD_GERFL_CMPOF BIT(3) /* CAN FD only */ @@ -86,20 +88,26 @@ enum rcanfd_chip_id { #define RCANFD_GERFL_MES BIT(1) #define RCANFD_GERFL_DEF BIT(0) -#define RCANFD_GERFL_ERR(gpriv, x) ((x) & (RCANFD_GERFL_EEF1 |\ - RCANFD_GERFL_EEF0 | RCANFD_GERFL_MES |\ - (gpriv->fdmode ?\ - RCANFD_GERFL_CMPOF : 0))) +#define RCANFD_GERFL_ERR(gpriv, x) \ + ((x) & (reg_v3u(gpriv, RCANFD_GERFL_EEF0_7, \ + RCANFD_GERFL_EEF0 | RCANFD_GERFL_EEF1) | \ + RCANFD_GERFL_MES | \ + ((gpriv)->fdmode ? RCANFD_GERFL_CMPOF : 0))) /* AFL Rx rules registers */ /* RSCFDnCFDGAFLCFG0 / RSCFDnGAFLCFG0 */ -#define RCANFD_GAFLCFG_SETRNC(n, x) (((x) & 0xff) << (24 - n * 8)) -#define RCANFD_GAFLCFG_GETRNC(n, x) (((x) >> (24 - n * 8)) & 0xff) +#define RCANFD_GAFLCFG_SETRNC(gpriv, n, x) \ + (((x) & reg_v3u(gpriv, 0x1ff, 0xff)) << \ + (reg_v3u(gpriv, 16, 24) - (n) * reg_v3u(gpriv, 16, 8))) + +#define RCANFD_GAFLCFG_GETRNC(gpriv, n, x) \ + (((x) >> (reg_v3u(gpriv, 16, 24) - (n) * reg_v3u(gpriv, 16, 8))) & \ + reg_v3u(gpriv, 0x1ff, 0xff)) /* RSCFDnCFDGAFLECTR / RSCFDnGAFLECTR */ #define RCANFD_GAFLECTR_AFLDAE BIT(8) -#define RCANFD_GAFLECTR_AFLPN(x) ((x) & 0x1f) +#define RCANFD_GAFLECTR_AFLPN(gpriv, x) ((x) & reg_v3u(gpriv, 0x7f, 0x1f)) /* RSCFDnCFDGAFLIDj / RSCFDnGAFLIDj */ #define RCANFD_GAFLID_GAFLLB BIT(29) @@ -116,9 +124,15 @@ enum rcanfd_chip_id { #define RCANFD_CFG_BRP(x) (((x) & 0x3ff) << 0) /* RSCFDnCFDCmNCFG - CAN FD only */ -#define RCANFD_NCFG_NTSEG2(x) (((x) & 0x1f) << 24) -#define RCANFD_NCFG_NTSEG1(x) (((x) & 0x7f) << 16) -#define RCANFD_NCFG_NSJW(x) (((x) & 0x1f) << 11) +#define RCANFD_NCFG_NTSEG2(gpriv, x) \ + (((x) & reg_v3u(gpriv, 0x7f, 0x1f)) << reg_v3u(gpriv, 25, 24)) + +#define RCANFD_NCFG_NTSEG1(gpriv, x) \ + (((x) & reg_v3u(gpriv, 0xff, 0x7f)) << reg_v3u(gpriv, 17, 16)) + +#define RCANFD_NCFG_NSJW(gpriv, x) \ + (((x) & reg_v3u(gpriv, 0x7f, 0x1f)) << reg_v3u(gpriv, 10, 11)) + #define RCANFD_NCFG_NBRP(x) (((x) & 0x3ff) << 0) /* RSCFDnCFDCmCTR / RSCFDnCmCTR */ @@ -180,11 +194,18 @@ enum rcanfd_chip_id { /* RSCFDnCFDCmDCFG */ #define RCANFD_DCFG_DSJW(x) (((x) & 0x7) << 24) -#define RCANFD_DCFG_DTSEG2(x) (((x) & 0x7) << 20) -#define RCANFD_DCFG_DTSEG1(x) (((x) & 0xf) << 16) + +#define RCANFD_DCFG_DTSEG2(gpriv, x) \ + (((x) & reg_v3u(gpriv, 0x0f, 0x7)) << reg_v3u(gpriv, 16, 20)) + +#define RCANFD_DCFG_DTSEG1(gpriv, x) \ + (((x) & reg_v3u(gpriv, 0x1f, 0xf)) << reg_v3u(gpriv, 8, 16)) + #define RCANFD_DCFG_DBRP(x) (((x) & 0xff) << 0) /* RSCFDnCFDCmFDCFG */ +#define RCANFD_FDCFG_CLOE BIT(30) +#define RCANFD_FDCFG_FDOE BIT(28) #define RCANFD_FDCFG_TDCE BIT(9) #define RCANFD_FDCFG_TDCOC BIT(8) #define RCANFD_FDCFG_TDCO(x) (((x) & 0x7f) >> 16) @@ -219,10 +240,10 @@ enum rcanfd_chip_id { /* Common FIFO bits */ /* RSCFDnCFDCFCCk */ -#define RCANFD_CFCC_CFTML(x) (((x) & 0xf) << 20) -#define RCANFD_CFCC_CFM(x) (((x) & 0x3) << 16) +#define RCANFD_CFCC_CFTML(gpriv, x) (((x) & 0xf) << reg_v3u(gpriv, 16, 20)) +#define RCANFD_CFCC_CFM(gpriv, x) (((x) & 0x3) << reg_v3u(gpriv, 8, 16)) #define RCANFD_CFCC_CFIM BIT(12) -#define RCANFD_CFCC_CFDC(x) (((x) & 0x7) << 8) +#define RCANFD_CFCC_CFDC(gpriv, x) (((x) & 0x7) << reg_v3u(gpriv, 21, 8)) #define RCANFD_CFCC_CFPLS(x) (((x) & 0x7) << 4) #define RCANFD_CFCC_CFTXIE BIT(2) #define RCANFD_CFCC_CFE BIT(0) @@ -282,33 +303,31 @@ enum rcanfd_chip_id { #define RCANFD_GTSC (0x0094) /* RSCFDnCFDGAFLECTR / RSCFDnGAFLECTR */ #define RCANFD_GAFLECTR (0x0098) -/* RSCFDnCFDGAFLCFG0 / RSCFDnGAFLCFG0 */ -#define RCANFD_GAFLCFG0 (0x009c) -/* RSCFDnCFDGAFLCFG1 / RSCFDnGAFLCFG1 */ -#define RCANFD_GAFLCFG1 (0x00a0) +/* RSCFDnCFDGAFLCFG / RSCFDnGAFLCFG */ +#define RCANFD_GAFLCFG(ch) (0x009c + (0x04 * ((ch) / 2))) /* RSCFDnCFDRMNB / RSCFDnRMNB */ #define RCANFD_RMNB (0x00a4) /* RSCFDnCFDRMND / RSCFDnRMND */ #define RCANFD_RMND(y) (0x00a8 + (0x04 * (y))) /* RSCFDnCFDRFCCx / RSCFDnRFCCx */ -#define RCANFD_RFCC(x) (0x00b8 + (0x04 * (x))) +#define RCANFD_RFCC(gpriv, x) (reg_v3u(gpriv, 0x00c0, 0x00b8) + (0x04 * (x))) /* RSCFDnCFDRFSTSx / RSCFDnRFSTSx */ -#define RCANFD_RFSTS(x) (0x00d8 + (0x04 * (x))) +#define RCANFD_RFSTS(gpriv, x) (RCANFD_RFCC(gpriv, x) + 0x20) /* RSCFDnCFDRFPCTRx / RSCFDnRFPCTRx */ -#define RCANFD_RFPCTR(x) (0x00f8 + (0x04 * (x))) +#define RCANFD_RFPCTR(gpriv, x) (RCANFD_RFCC(gpriv, x) + 0x40) /* Common FIFO Control registers */ /* RSCFDnCFDCFCCx / RSCFDnCFCCx */ -#define RCANFD_CFCC(ch, idx) (0x0118 + (0x0c * (ch)) + \ - (0x04 * (idx))) +#define RCANFD_CFCC(gpriv, ch, idx) \ + (reg_v3u(gpriv, 0x0120, 0x0118) + (0x0c * (ch)) + (0x04 * (idx))) /* RSCFDnCFDCFSTSx / RSCFDnCFSTSx */ -#define RCANFD_CFSTS(ch, idx) (0x0178 + (0x0c * (ch)) + \ - (0x04 * (idx))) +#define RCANFD_CFSTS(gpriv, ch, idx) \ + (reg_v3u(gpriv, 0x01e0, 0x0178) + (0x0c * (ch)) + (0x04 * (idx))) /* RSCFDnCFDCFPCTRx / RSCFDnCFPCTRx */ -#define RCANFD_CFPCTR(ch, idx) (0x01d8 + (0x0c * (ch)) + \ - (0x04 * (idx))) +#define RCANFD_CFPCTR(gpriv, ch, idx) \ + (reg_v3u(gpriv, 0x0240, 0x01d8) + (0x0c * (ch)) + (0x04 * (idx))) /* RSCFDnCFDFESTS / RSCFDnFESTS */ #define RCANFD_FESTS (0x0238) @@ -387,22 +406,23 @@ enum rcanfd_chip_id { #define RCANFD_C_RMDF1(q) (0x060c + (0x10 * (q))) /* RSCFDnRFXXx -> RCANFD_C_RFXX(x) */ -#define RCANFD_C_RFOFFSET (0x0e00) -#define RCANFD_C_RFID(x) (RCANFD_C_RFOFFSET + (0x10 * (x))) -#define RCANFD_C_RFPTR(x) (RCANFD_C_RFOFFSET + 0x04 + \ - (0x10 * (x))) -#define RCANFD_C_RFDF(x, df) (RCANFD_C_RFOFFSET + 0x08 + \ - (0x10 * (x)) + (0x04 * (df))) +#define RCANFD_C_RFOFFSET (0x0e00) +#define RCANFD_C_RFID(x) (RCANFD_C_RFOFFSET + (0x10 * (x))) +#define RCANFD_C_RFPTR(x) (RCANFD_C_RFOFFSET + 0x04 + (0x10 * (x))) +#define RCANFD_C_RFDF(x, df) \ + (RCANFD_C_RFOFFSET + 0x08 + (0x10 * (x)) + (0x04 * (df))) /* RSCFDnCFXXk -> RCANFD_C_CFXX(ch, k) */ #define RCANFD_C_CFOFFSET (0x0e80) -#define RCANFD_C_CFID(ch, idx) (RCANFD_C_CFOFFSET + (0x30 * (ch)) + \ - (0x10 * (idx))) -#define RCANFD_C_CFPTR(ch, idx) (RCANFD_C_CFOFFSET + 0x04 + \ - (0x30 * (ch)) + (0x10 * (idx))) -#define RCANFD_C_CFDF(ch, idx, df) (RCANFD_C_CFOFFSET + 0x08 + \ - (0x30 * (ch)) + (0x10 * (idx)) + \ - (0x04 * (df))) + +#define RCANFD_C_CFID(ch, idx) \ + (RCANFD_C_CFOFFSET + (0x30 * (ch)) + (0x10 * (idx))) + +#define RCANFD_C_CFPTR(ch, idx) \ + (RCANFD_C_CFOFFSET + 0x04 + (0x30 * (ch)) + (0x10 * (idx))) + +#define RCANFD_C_CFDF(ch, idx, df) \ + (RCANFD_C_CFOFFSET + 0x08 + (0x30 * (ch)) + (0x10 * (idx)) + (0x04 * (df))) /* RSCFDnTMXXp -> RCANFD_C_TMXX(p) */ #define RCANFD_C_TMID(p) (0x1000 + (0x10 * (p))) @@ -415,6 +435,12 @@ enum rcanfd_chip_id { /* RSCFDnRPGACCr */ #define RCANFD_C_RPGACC(r) (0x1900 + (0x04 * (r))) +/* R-Car V3U Classical and CAN FD mode specific register map */ +#define RCANFD_V3U_CFDCFG (0x1314) +#define RCANFD_V3U_DCFG(m) (0x1400 + (0x20 * (m))) + +#define RCANFD_V3U_GAFL_OFFSET (0x1800) + /* CAN FD mode specific register map */ /* RSCFDnCFDCmXXX -> RCANFD_F_XXX(m) */ @@ -434,26 +460,28 @@ enum rcanfd_chip_id { #define RCANFD_F_RMDF(q, b) (0x200c + (0x04 * (b)) + (0x20 * (q))) /* RSCFDnCFDRFXXx -> RCANFD_F_RFXX(x) */ -#define RCANFD_F_RFOFFSET (0x3000) -#define RCANFD_F_RFID(x) (RCANFD_F_RFOFFSET + (0x80 * (x))) -#define RCANFD_F_RFPTR(x) (RCANFD_F_RFOFFSET + 0x04 + \ - (0x80 * (x))) -#define RCANFD_F_RFFDSTS(x) (RCANFD_F_RFOFFSET + 0x08 + \ - (0x80 * (x))) -#define RCANFD_F_RFDF(x, df) (RCANFD_F_RFOFFSET + 0x0c + \ - (0x80 * (x)) + (0x04 * (df))) +#define RCANFD_F_RFOFFSET(gpriv) reg_v3u(gpriv, 0x6000, 0x3000) +#define RCANFD_F_RFID(gpriv, x) (RCANFD_F_RFOFFSET(gpriv) + (0x80 * (x))) +#define RCANFD_F_RFPTR(gpriv, x) (RCANFD_F_RFOFFSET(gpriv) + 0x04 + (0x80 * (x))) +#define RCANFD_F_RFFDSTS(gpriv, x) (RCANFD_F_RFOFFSET(gpriv) + 0x08 + (0x80 * (x))) +#define RCANFD_F_RFDF(gpriv, x, df) \ + (RCANFD_F_RFOFFSET(gpriv) + 0x0c + (0x80 * (x)) + (0x04 * (df))) /* RSCFDnCFDCFXXk -> RCANFD_F_CFXX(ch, k) */ -#define RCANFD_F_CFOFFSET (0x3400) -#define RCANFD_F_CFID(ch, idx) (RCANFD_F_CFOFFSET + (0x180 * (ch)) + \ - (0x80 * (idx))) -#define RCANFD_F_CFPTR(ch, idx) (RCANFD_F_CFOFFSET + 0x04 + \ - (0x180 * (ch)) + (0x80 * (idx))) -#define RCANFD_F_CFFDCSTS(ch, idx) (RCANFD_F_CFOFFSET + 0x08 + \ - (0x180 * (ch)) + (0x80 * (idx))) -#define RCANFD_F_CFDF(ch, idx, df) (RCANFD_F_CFOFFSET + 0x0c + \ - (0x180 * (ch)) + (0x80 * (idx)) + \ - (0x04 * (df))) +#define RCANFD_F_CFOFFSET(gpriv) reg_v3u(gpriv, 0x6400, 0x3400) + +#define RCANFD_F_CFID(gpriv, ch, idx) \ + (RCANFD_F_CFOFFSET(gpriv) + (0x180 * (ch)) + (0x80 * (idx))) + +#define RCANFD_F_CFPTR(gpriv, ch, idx) \ + (RCANFD_F_CFOFFSET(gpriv) + 0x04 + (0x180 * (ch)) + (0x80 * (idx))) + +#define RCANFD_F_CFFDCSTS(gpriv, ch, idx) \ + (RCANFD_F_CFOFFSET(gpriv) + 0x08 + (0x180 * (ch)) + (0x80 * (idx))) + +#define RCANFD_F_CFDF(gpriv, ch, idx, df) \ + (RCANFD_F_CFOFFSET(gpriv) + 0x0c + (0x180 * (ch)) + (0x80 * (idx)) + \ + (0x04 * (df))) /* RSCFDnCFDTMXXp -> RCANFD_F_TMXX(p) */ #define RCANFD_F_TMID(p) (0x4000 + (0x20 * (p))) @@ -470,7 +498,7 @@ enum rcanfd_chip_id { #define RCANFD_FIFO_DEPTH 8 /* Tx FIFO depth */ #define RCANFD_NAPI_WEIGHT 8 /* Rx poll quota */ -#define RCANFD_NUM_CHANNELS 2 /* Two channels max */ +#define RCANFD_NUM_CHANNELS 8 /* Eight channels max */ #define RCANFD_CHANNELS_MASK BIT((RCANFD_NUM_CHANNELS) - 1) #define RCANFD_GAFL_PAGENUM(entry) ((entry) / 16) @@ -521,6 +549,7 @@ struct rcar_canfd_global { struct reset_control *rstc1; struct reset_control *rstc2; enum rcanfd_chip_id chip_id; + u32 max_channels; }; /* CAN FD mode nominal rate constants */ @@ -563,6 +592,17 @@ static const struct can_bittiming_const rcar_canfd_bittiming_const = { }; /* Helper functions */ +static inline bool is_v3u(struct rcar_canfd_global *gpriv) +{ + return gpriv->chip_id == RENESAS_R8A779A0; +} + +static inline u32 reg_v3u(struct rcar_canfd_global *gpriv, + u32 v3u, u32 not_v3u) +{ + return is_v3u(gpriv) ? v3u : not_v3u; +} + static inline void rcar_canfd_update(u32 mask, u32 val, u32 __iomem *reg) { u32 data = readl(reg); @@ -628,6 +668,25 @@ static void rcar_canfd_tx_failure_cleanup(struct net_device *ndev) can_free_echo_skb(ndev, i, NULL); } +static void rcar_canfd_set_mode(struct rcar_canfd_global *gpriv) +{ + if (is_v3u(gpriv)) { + if (gpriv->fdmode) + rcar_canfd_set_bit(gpriv->base, RCANFD_V3U_CFDCFG, + RCANFD_FDCFG_FDOE); + else + rcar_canfd_set_bit(gpriv->base, RCANFD_V3U_CFDCFG, + RCANFD_FDCFG_CLOE); + } else { + if (gpriv->fdmode) + rcar_canfd_set_bit(gpriv->base, RCANFD_GRMCFG, + RCANFD_GRMCFG_RCMC); + else + rcar_canfd_clear_bit(gpriv->base, RCANFD_GRMCFG, + RCANFD_GRMCFG_RCMC); + } +} + static int rcar_canfd_reset_controller(struct rcar_canfd_global *gpriv) { u32 sts, ch; @@ -660,15 +719,10 @@ static int rcar_canfd_reset_controller(struct rcar_canfd_global *gpriv) rcar_canfd_write(gpriv->base, RCANFD_GERFL, 0x0); /* Set the controller into appropriate mode */ - if (gpriv->fdmode) - rcar_canfd_set_bit(gpriv->base, RCANFD_GRMCFG, - RCANFD_GRMCFG_RCMC); - else - rcar_canfd_clear_bit(gpriv->base, RCANFD_GRMCFG, - RCANFD_GRMCFG_RCMC); + rcar_canfd_set_mode(gpriv); /* Transition all Channels to reset mode */ - for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { rcar_canfd_clear_bit(gpriv->base, RCANFD_CCTR(ch), RCANFD_CCTR_CSLPR); @@ -709,7 +763,7 @@ static void rcar_canfd_configure_controller(struct rcar_canfd_global *gpriv) rcar_canfd_set_bit(gpriv->base, RCANFD_GCFG, cfg); /* Channel configuration settings */ - for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { rcar_canfd_set_bit(gpriv->base, RCANFD_CCTR(ch), RCANFD_CCTR_ERRD); rcar_canfd_update_bit(gpriv->base, RCANFD_CCTR(ch), @@ -729,20 +783,22 @@ static void rcar_canfd_configure_afl_rules(struct rcar_canfd_global *gpriv, start = 0; /* Channel 0 always starts from 0th rule */ } else { /* Get number of Channel 0 rules and adjust */ - cfg = rcar_canfd_read(gpriv->base, RCANFD_GAFLCFG0); - start = RCANFD_GAFLCFG_GETRNC(0, cfg); + cfg = rcar_canfd_read(gpriv->base, RCANFD_GAFLCFG(ch)); + start = RCANFD_GAFLCFG_GETRNC(gpriv, 0, cfg); } /* Enable write access to entry */ page = RCANFD_GAFL_PAGENUM(start); rcar_canfd_set_bit(gpriv->base, RCANFD_GAFLECTR, - (RCANFD_GAFLECTR_AFLPN(page) | + (RCANFD_GAFLECTR_AFLPN(gpriv, page) | RCANFD_GAFLECTR_AFLDAE)); /* Write number of rules for channel */ - rcar_canfd_set_bit(gpriv->base, RCANFD_GAFLCFG0, - RCANFD_GAFLCFG_SETRNC(ch, num_rules)); - if (gpriv->fdmode) + rcar_canfd_set_bit(gpriv->base, RCANFD_GAFLCFG(ch), + RCANFD_GAFLCFG_SETRNC(gpriv, ch, num_rules)); + if (is_v3u(gpriv)) + offset = RCANFD_V3U_GAFL_OFFSET; + else if (gpriv->fdmode) offset = RCANFD_F_GAFL_OFFSET; else offset = RCANFD_C_GAFL_OFFSET; @@ -754,8 +810,8 @@ static void rcar_canfd_configure_afl_rules(struct rcar_canfd_global *gpriv, /* Any data length accepted */ rcar_canfd_write(gpriv->base, RCANFD_GAFLP0(offset, start), 0); /* Place the msg in corresponding Rx FIFO entry */ - rcar_canfd_write(gpriv->base, RCANFD_GAFLP1(offset, start), - RCANFD_GAFLP1_GAFLFDP(ridx)); + rcar_canfd_set_bit(gpriv->base, RCANFD_GAFLP1(offset, start), + RCANFD_GAFLP1_GAFLFDP(ridx)); /* Disable write access to page */ rcar_canfd_clear_bit(gpriv->base, @@ -779,7 +835,7 @@ static void rcar_canfd_configure_rx(struct rcar_canfd_global *gpriv, u32 ch) cfg = (RCANFD_RFCC_RFIM | RCANFD_RFCC_RFDC(rfdc) | RCANFD_RFCC_RFPLS(rfpls) | RCANFD_RFCC_RFIE); - rcar_canfd_write(gpriv->base, RCANFD_RFCC(ridx), cfg); + rcar_canfd_write(gpriv->base, RCANFD_RFCC(gpriv, ridx), cfg); } static void rcar_canfd_configure_tx(struct rcar_canfd_global *gpriv, u32 ch) @@ -801,15 +857,15 @@ static void rcar_canfd_configure_tx(struct rcar_canfd_global *gpriv, u32 ch) else cfpls = 0; /* b000 - Max 8 bytes payload */ - cfg = (RCANFD_CFCC_CFTML(cftml) | RCANFD_CFCC_CFM(cfm) | - RCANFD_CFCC_CFIM | RCANFD_CFCC_CFDC(cfdc) | + cfg = (RCANFD_CFCC_CFTML(gpriv, cftml) | RCANFD_CFCC_CFM(gpriv, cfm) | + RCANFD_CFCC_CFIM | RCANFD_CFCC_CFDC(gpriv, cfdc) | RCANFD_CFCC_CFPLS(cfpls) | RCANFD_CFCC_CFTXIE); - rcar_canfd_write(gpriv->base, RCANFD_CFCC(ch, RCANFD_CFFIFO_IDX), cfg); + rcar_canfd_write(gpriv->base, RCANFD_CFCC(gpriv, ch, RCANFD_CFFIFO_IDX), cfg); if (gpriv->fdmode) /* Clear FD mode specific control/status register */ rcar_canfd_write(gpriv->base, - RCANFD_F_CFFDCSTS(ch, RCANFD_CFFIFO_IDX), 0); + RCANFD_F_CFFDCSTS(gpriv, ch, RCANFD_CFFIFO_IDX), 0); } static void rcar_canfd_enable_global_interrupts(struct rcar_canfd_global *gpriv) @@ -890,20 +946,20 @@ static void rcar_canfd_global_error(struct net_device *ndev) } if (gerfl & RCANFD_GERFL_MES) { sts = rcar_canfd_read(priv->base, - RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX)); + RCANFD_CFSTS(gpriv, ch, RCANFD_CFFIFO_IDX)); if (sts & RCANFD_CFSTS_CFMLT) { netdev_dbg(ndev, "Tx Message Lost flag\n"); stats->tx_dropped++; rcar_canfd_write(priv->base, - RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX), + RCANFD_CFSTS(gpriv, ch, RCANFD_CFFIFO_IDX), sts & ~RCANFD_CFSTS_CFMLT); } - sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx)); + sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(gpriv, ridx)); if (sts & RCANFD_RFSTS_RFMLT) { netdev_dbg(ndev, "Rx Message Lost flag\n"); stats->rx_dropped++; - rcar_canfd_write(priv->base, RCANFD_RFSTS(ridx), + rcar_canfd_write(priv->base, RCANFD_RFSTS(gpriv, ridx), sts & ~RCANFD_RFSTS_RFMLT); } } @@ -1038,6 +1094,7 @@ static void rcar_canfd_error(struct net_device *ndev, u32 cerfl, static void rcar_canfd_tx_done(struct net_device *ndev) { struct rcar_canfd_channel *priv = netdev_priv(ndev); + struct rcar_canfd_global *gpriv = priv->gpriv; struct net_device_stats *stats = &ndev->stats; u32 sts; unsigned long flags; @@ -1053,7 +1110,7 @@ static void rcar_canfd_tx_done(struct net_device *ndev) spin_lock_irqsave(&priv->tx_lock, flags); priv->tx_tail++; sts = rcar_canfd_read(priv->base, - RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX)); + RCANFD_CFSTS(gpriv, ch, RCANFD_CFFIFO_IDX)); unsent = RCANFD_CFSTS_CFMC(sts); /* Wake producer only when there is room */ @@ -1069,7 +1126,7 @@ static void rcar_canfd_tx_done(struct net_device *ndev) } while (1); /* Clear interrupt */ - rcar_canfd_write(priv->base, RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX), + rcar_canfd_write(priv->base, RCANFD_CFSTS(gpriv, ch, RCANFD_CFFIFO_IDX), sts & ~RCANFD_CFSTS_CFTXIF); can_led_event(ndev, CAN_LED_EVENT_TX); } @@ -1091,7 +1148,7 @@ static irqreturn_t rcar_canfd_global_err_interrupt(int irq, void *dev_id) struct rcar_canfd_global *gpriv = dev_id; u32 ch; - for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) rcar_canfd_handle_global_err(gpriv, ch); return IRQ_HANDLED; @@ -1104,12 +1161,12 @@ static void rcar_canfd_handle_global_receive(struct rcar_canfd_global *gpriv, u3 u32 sts; /* Handle Rx interrupts */ - sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx)); + sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(gpriv, ridx)); if (likely(sts & RCANFD_RFSTS_RFIF)) { if (napi_schedule_prep(&priv->napi)) { /* Disable Rx FIFO interrupts */ rcar_canfd_clear_bit(priv->base, - RCANFD_RFCC(ridx), + RCANFD_RFCC(gpriv, ridx), RCANFD_RFCC_RFIE); __napi_schedule(&priv->napi); } @@ -1121,7 +1178,7 @@ static irqreturn_t rcar_canfd_global_receive_fifo_interrupt(int irq, void *dev_i struct rcar_canfd_global *gpriv = dev_id; u32 ch; - for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) rcar_canfd_handle_global_receive(gpriv, ch); return IRQ_HANDLED; @@ -1135,7 +1192,7 @@ static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id) /* Global error interrupts still indicate a condition specific * to a channel. RxFIFO interrupt is a global interrupt. */ - for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { rcar_canfd_handle_global_err(gpriv, ch); rcar_canfd_handle_global_receive(gpriv, ch); } @@ -1181,7 +1238,7 @@ static void rcar_canfd_handle_channel_tx(struct rcar_canfd_global *gpriv, u32 ch /* Handle Tx interrupts */ sts = rcar_canfd_read(priv->base, - RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX)); + RCANFD_CFSTS(gpriv, ch, RCANFD_CFFIFO_IDX)); if (likely(sts & RCANFD_CFSTS_CFTXIF)) rcar_canfd_tx_done(ndev); } @@ -1191,7 +1248,7 @@ static irqreturn_t rcar_canfd_channel_tx_interrupt(int irq, void *dev_id) struct rcar_canfd_global *gpriv = dev_id; u32 ch; - for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) rcar_canfd_handle_channel_tx(gpriv, ch); return IRQ_HANDLED; @@ -1223,7 +1280,7 @@ static irqreturn_t rcar_canfd_channel_err_interrupt(int irq, void *dev_id) struct rcar_canfd_global *gpriv = dev_id; u32 ch; - for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) rcar_canfd_handle_channel_err(gpriv, ch); return IRQ_HANDLED; @@ -1235,7 +1292,7 @@ static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id) u32 ch; /* Common FIFO is a per channel resource */ - for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { rcar_canfd_handle_channel_err(gpriv, ch); rcar_canfd_handle_channel_tx(gpriv, ch); } @@ -1246,6 +1303,7 @@ static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id) static void rcar_canfd_set_bittiming(struct net_device *dev) { struct rcar_canfd_channel *priv = netdev_priv(dev); + struct rcar_canfd_global *gpriv = priv->gpriv; const struct can_bittiming *bt = &priv->can.bittiming; const struct can_bittiming *dbt = &priv->can.data_bittiming; u16 brp, sjw, tseg1, tseg2; @@ -1260,8 +1318,8 @@ static void rcar_canfd_set_bittiming(struct net_device *dev) if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { /* CAN FD only mode */ - cfg = (RCANFD_NCFG_NTSEG1(tseg1) | RCANFD_NCFG_NBRP(brp) | - RCANFD_NCFG_NSJW(sjw) | RCANFD_NCFG_NTSEG2(tseg2)); + cfg = (RCANFD_NCFG_NTSEG1(gpriv, tseg1) | RCANFD_NCFG_NBRP(brp) | + RCANFD_NCFG_NSJW(gpriv, sjw) | RCANFD_NCFG_NTSEG2(gpriv, tseg2)); rcar_canfd_write(priv->base, RCANFD_CCFG(ch), cfg); netdev_dbg(priv->ndev, "nrate: brp %u, sjw %u, tseg1 %u, tseg2 %u\n", @@ -1273,16 +1331,25 @@ static void rcar_canfd_set_bittiming(struct net_device *dev) tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; tseg2 = dbt->phase_seg2 - 1; - cfg = (RCANFD_DCFG_DTSEG1(tseg1) | RCANFD_DCFG_DBRP(brp) | - RCANFD_DCFG_DSJW(sjw) | RCANFD_DCFG_DTSEG2(tseg2)); + cfg = (RCANFD_DCFG_DTSEG1(gpriv, tseg1) | RCANFD_DCFG_DBRP(brp) | + RCANFD_DCFG_DSJW(sjw) | RCANFD_DCFG_DTSEG2(gpriv, tseg2)); rcar_canfd_write(priv->base, RCANFD_F_DCFG(ch), cfg); netdev_dbg(priv->ndev, "drate: brp %u, sjw %u, tseg1 %u, tseg2 %u\n", brp, sjw, tseg1, tseg2); } else { /* Classical CAN only mode */ - cfg = (RCANFD_CFG_TSEG1(tseg1) | RCANFD_CFG_BRP(brp) | - RCANFD_CFG_SJW(sjw) | RCANFD_CFG_TSEG2(tseg2)); + if (is_v3u(gpriv)) { + cfg = (RCANFD_NCFG_NTSEG1(gpriv, tseg1) | + RCANFD_NCFG_NBRP(brp) | + RCANFD_NCFG_NSJW(gpriv, sjw) | + RCANFD_NCFG_NTSEG2(gpriv, tseg2)); + } else { + cfg = (RCANFD_CFG_TSEG1(tseg1) | + RCANFD_CFG_BRP(brp) | + RCANFD_CFG_SJW(sjw) | + RCANFD_CFG_TSEG2(tseg2)); + } rcar_canfd_write(priv->base, RCANFD_CCFG(ch), cfg); netdev_dbg(priv->ndev, @@ -1294,6 +1361,7 @@ static void rcar_canfd_set_bittiming(struct net_device *dev) static int rcar_canfd_start(struct net_device *ndev) { struct rcar_canfd_channel *priv = netdev_priv(ndev); + struct rcar_canfd_global *gpriv = priv->gpriv; int err = -EOPNOTSUPP; u32 sts, ch = priv->channel; u32 ridx = ch + RCANFD_RFFIFO_IDX; @@ -1315,9 +1383,9 @@ static int rcar_canfd_start(struct net_device *ndev) } /* Enable Common & Rx FIFO */ - rcar_canfd_set_bit(priv->base, RCANFD_CFCC(ch, RCANFD_CFFIFO_IDX), + rcar_canfd_set_bit(priv->base, RCANFD_CFCC(gpriv, ch, RCANFD_CFFIFO_IDX), RCANFD_CFCC_CFE); - rcar_canfd_set_bit(priv->base, RCANFD_RFCC(ridx), RCANFD_RFCC_RFE); + rcar_canfd_set_bit(priv->base, RCANFD_RFCC(gpriv, ridx), RCANFD_RFCC_RFE); priv->can.state = CAN_STATE_ERROR_ACTIVE; return 0; @@ -1365,6 +1433,7 @@ static int rcar_canfd_open(struct net_device *ndev) static void rcar_canfd_stop(struct net_device *ndev) { struct rcar_canfd_channel *priv = netdev_priv(ndev); + struct rcar_canfd_global *gpriv = priv->gpriv; int err; u32 sts, ch = priv->channel; u32 ridx = ch + RCANFD_RFFIFO_IDX; @@ -1382,9 +1451,9 @@ static void rcar_canfd_stop(struct net_device *ndev) rcar_canfd_disable_channel_interrupts(priv); /* Disable Common & Rx FIFO */ - rcar_canfd_clear_bit(priv->base, RCANFD_CFCC(ch, RCANFD_CFFIFO_IDX), + rcar_canfd_clear_bit(priv->base, RCANFD_CFCC(gpriv, ch, RCANFD_CFFIFO_IDX), RCANFD_CFCC_CFE); - rcar_canfd_clear_bit(priv->base, RCANFD_RFCC(ridx), RCANFD_RFCC_RFE); + rcar_canfd_clear_bit(priv->base, RCANFD_RFCC(gpriv, ridx), RCANFD_RFCC_RFE); /* Set the state as STOPPED */ priv->can.state = CAN_STATE_STOPPED; @@ -1408,6 +1477,7 @@ static netdev_tx_t rcar_canfd_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct rcar_canfd_channel *priv = netdev_priv(ndev); + struct rcar_canfd_global *gpriv = priv->gpriv; struct canfd_frame *cf = (struct canfd_frame *)skb->data; u32 sts = 0, id, dlc; unsigned long flags; @@ -1428,11 +1498,11 @@ static netdev_tx_t rcar_canfd_start_xmit(struct sk_buff *skb, dlc = RCANFD_CFPTR_CFDLC(can_fd_len2dlc(cf->len)); - if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + if ((priv->can.ctrlmode & CAN_CTRLMODE_FD) || is_v3u(gpriv)) { rcar_canfd_write(priv->base, - RCANFD_F_CFID(ch, RCANFD_CFFIFO_IDX), id); + RCANFD_F_CFID(gpriv, ch, RCANFD_CFFIFO_IDX), id); rcar_canfd_write(priv->base, - RCANFD_F_CFPTR(ch, RCANFD_CFFIFO_IDX), dlc); + RCANFD_F_CFPTR(gpriv, ch, RCANFD_CFFIFO_IDX), dlc); if (can_is_canfd_skb(skb)) { /* CAN FD frame format */ @@ -1445,10 +1515,10 @@ static netdev_tx_t rcar_canfd_start_xmit(struct sk_buff *skb, } rcar_canfd_write(priv->base, - RCANFD_F_CFFDCSTS(ch, RCANFD_CFFIFO_IDX), sts); + RCANFD_F_CFFDCSTS(gpriv, ch, RCANFD_CFFIFO_IDX), sts); rcar_canfd_put_data(priv, cf, - RCANFD_F_CFDF(ch, RCANFD_CFFIFO_IDX, 0)); + RCANFD_F_CFDF(gpriv, ch, RCANFD_CFFIFO_IDX, 0)); } else { rcar_canfd_write(priv->base, RCANFD_C_CFID(ch, RCANFD_CFFIFO_IDX), id); @@ -1471,7 +1541,7 @@ static netdev_tx_t rcar_canfd_start_xmit(struct sk_buff *skb, * pointer for the Common FIFO */ rcar_canfd_write(priv->base, - RCANFD_CFPCTR(ch, RCANFD_CFFIFO_IDX), 0xff); + RCANFD_CFPCTR(gpriv, ch, RCANFD_CFFIFO_IDX), 0xff); spin_unlock_irqrestore(&priv->tx_lock, flags); return NETDEV_TX_OK; @@ -1480,18 +1550,21 @@ static netdev_tx_t rcar_canfd_start_xmit(struct sk_buff *skb, static void rcar_canfd_rx_pkt(struct rcar_canfd_channel *priv) { struct net_device_stats *stats = &priv->ndev->stats; + struct rcar_canfd_global *gpriv = priv->gpriv; struct canfd_frame *cf; struct sk_buff *skb; u32 sts = 0, id, dlc; u32 ch = priv->channel; u32 ridx = ch + RCANFD_RFFIFO_IDX; - if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { - id = rcar_canfd_read(priv->base, RCANFD_F_RFID(ridx)); - dlc = rcar_canfd_read(priv->base, RCANFD_F_RFPTR(ridx)); + if ((priv->can.ctrlmode & CAN_CTRLMODE_FD) || is_v3u(gpriv)) { + id = rcar_canfd_read(priv->base, RCANFD_F_RFID(gpriv, ridx)); + dlc = rcar_canfd_read(priv->base, RCANFD_F_RFPTR(gpriv, ridx)); - sts = rcar_canfd_read(priv->base, RCANFD_F_RFFDSTS(ridx)); - if (sts & RCANFD_RFFDSTS_RFFDF) + sts = rcar_canfd_read(priv->base, RCANFD_F_RFFDSTS(gpriv, ridx)); + + if ((priv->can.ctrlmode & CAN_CTRLMODE_FD) && + sts & RCANFD_RFFDSTS_RFFDF) skb = alloc_canfd_skb(priv->ndev, &cf); else skb = alloc_can_skb(priv->ndev, @@ -1529,12 +1602,14 @@ static void rcar_canfd_rx_pkt(struct rcar_canfd_channel *priv) if (sts & RCANFD_RFFDSTS_RFBRS) cf->flags |= CANFD_BRS; - rcar_canfd_get_data(priv, cf, RCANFD_F_RFDF(ridx, 0)); + rcar_canfd_get_data(priv, cf, RCANFD_F_RFDF(gpriv, ridx, 0)); } } else { cf->len = can_cc_dlc2len(RCANFD_RFPTR_RFDLC(dlc)); if (id & RCANFD_RFID_RFRTR) cf->can_id |= CAN_RTR_FLAG; + else if (is_v3u(gpriv)) + rcar_canfd_get_data(priv, cf, RCANFD_F_RFDF(gpriv, ridx, 0)); else rcar_canfd_get_data(priv, cf, RCANFD_C_RFDF(ridx, 0)); } @@ -1542,7 +1617,7 @@ static void rcar_canfd_rx_pkt(struct rcar_canfd_channel *priv) /* Write 0xff to RFPC to increment the CPU-side * pointer of the Rx FIFO */ - rcar_canfd_write(priv->base, RCANFD_RFPCTR(ridx), 0xff); + rcar_canfd_write(priv->base, RCANFD_RFPCTR(gpriv, ridx), 0xff); can_led_event(priv->ndev, CAN_LED_EVENT_RX); @@ -1556,13 +1631,14 @@ static int rcar_canfd_rx_poll(struct napi_struct *napi, int quota) { struct rcar_canfd_channel *priv = container_of(napi, struct rcar_canfd_channel, napi); + struct rcar_canfd_global *gpriv = priv->gpriv; int num_pkts; u32 sts; u32 ch = priv->channel; u32 ridx = ch + RCANFD_RFFIFO_IDX; for (num_pkts = 0; num_pkts < quota; num_pkts++) { - sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx)); + sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(gpriv, ridx)); /* Check FIFO empty condition */ if (sts & RCANFD_RFSTS_RFEMP) break; @@ -1571,7 +1647,7 @@ static int rcar_canfd_rx_poll(struct napi_struct *napi, int quota) /* Clear interrupt bit */ if (sts & RCANFD_RFSTS_RFIF) - rcar_canfd_write(priv->base, RCANFD_RFSTS(ridx), + rcar_canfd_write(priv->base, RCANFD_RFSTS(gpriv, ridx), sts & ~RCANFD_RFSTS_RFIF); } @@ -1579,7 +1655,7 @@ static int rcar_canfd_rx_poll(struct napi_struct *napi, int quota) if (num_pkts < quota) { if (napi_complete_done(napi, num_pkts)) { /* Enable Rx FIFO interrupts */ - rcar_canfd_set_bit(priv->base, RCANFD_RFCC(ridx), + rcar_canfd_set_bit(priv->base, RCANFD_RFCC(gpriv, ridx), RCANFD_RFCC_RFIE); } } @@ -1756,21 +1832,24 @@ static int rcar_canfd_probe(struct platform_device *pdev) int g_err_irq, g_recc_irq; bool fdmode = true; /* CAN FD only mode - default */ enum rcanfd_chip_id chip_id; + int max_channels; + char name[9] = "channelX"; + int i; chip_id = (uintptr_t)of_device_get_match_data(&pdev->dev); + max_channels = chip_id == RENESAS_R8A779A0 ? 8 : 2; if (of_property_read_bool(pdev->dev.of_node, "renesas,no-can-fd")) fdmode = false; /* Classical CAN only mode */ - of_child = of_get_child_by_name(pdev->dev.of_node, "channel0"); - if (of_child && of_device_is_available(of_child)) - channels_mask |= BIT(0); /* Channel 0 */ + for (i = 0; i < max_channels; ++i) { + name[7] = '0' + i; + of_child = of_get_child_by_name(pdev->dev.of_node, name); + if (of_child && of_device_is_available(of_child)) + channels_mask |= BIT(i); + } - of_child = of_get_child_by_name(pdev->dev.of_node, "channel1"); - if (of_child && of_device_is_available(of_child)) - channels_mask |= BIT(1); /* Channel 1 */ - - if (chip_id == RENESAS_RCAR_GEN3) { + if (chip_id != RENESAS_RZG2L) { ch_irq = platform_get_irq_byname_optional(pdev, "ch_int"); if (ch_irq < 0) { /* For backward compatibility get irq by index */ @@ -1806,6 +1885,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) gpriv->channels_mask = channels_mask; gpriv->fdmode = fdmode; gpriv->chip_id = chip_id; + gpriv->max_channels = max_channels; if (gpriv->chip_id == RENESAS_RZG2L) { gpriv->rstc1 = devm_reset_control_get_exclusive(&pdev->dev, "rstp_n"); @@ -1847,7 +1927,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) } fcan_freq = clk_get_rate(gpriv->can_clk); - if (gpriv->fcan == RCANFD_CANFDCLK && gpriv->chip_id == RENESAS_RCAR_GEN3) + if (gpriv->fcan == RCANFD_CANFDCLK && gpriv->chip_id != RENESAS_RZG2L) /* CANFD clock is further divided by (1/2) within the IP */ fcan_freq /= 2; @@ -1859,7 +1939,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) gpriv->base = addr; /* Request IRQ that's common for both channels */ - if (gpriv->chip_id == RENESAS_RCAR_GEN3) { + if (gpriv->chip_id != RENESAS_RZG2L) { err = devm_request_irq(&pdev->dev, ch_irq, rcar_canfd_channel_interrupt, 0, "canfd.ch_int", gpriv); @@ -1925,7 +2005,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) rcar_canfd_configure_controller(gpriv); /* Configure per channel attributes */ - for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + for_each_set_bit(ch, &gpriv->channels_mask, max_channels) { /* Configure Channel's Rx fifo */ rcar_canfd_configure_rx(gpriv, ch); @@ -1951,7 +2031,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) goto fail_mode; } - for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + for_each_set_bit(ch, &gpriv->channels_mask, max_channels) { err = rcar_canfd_channel_probe(gpriv, ch, fcan_freq); if (err) goto fail_channel; @@ -1963,7 +2043,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) return 0; fail_channel: - for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) + for_each_set_bit(ch, &gpriv->channels_mask, max_channels) rcar_canfd_channel_remove(gpriv, ch); fail_mode: rcar_canfd_disable_global_interrupts(gpriv); @@ -1984,7 +2064,7 @@ static int rcar_canfd_remove(struct platform_device *pdev) rcar_canfd_reset_controller(gpriv); rcar_canfd_disable_global_interrupts(gpriv); - for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { rcar_canfd_disable_channel_interrupts(gpriv->ch[ch]); rcar_canfd_channel_remove(gpriv, ch); } @@ -2014,6 +2094,7 @@ static SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend, static const __maybe_unused struct of_device_id rcar_canfd_of_table[] = { { .compatible = "renesas,rcar-gen3-canfd", .data = (void *)RENESAS_RCAR_GEN3 }, { .compatible = "renesas,rzg2l-canfd", .data = (void *)RENESAS_RZG2L }, + { .compatible = "renesas,r8a779a0-canfd", .data = (void *)RENESAS_R8A779A0 }, { } }; diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index 27783fbf011f..ec294d0c5722 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -221,7 +221,7 @@ static void slc_bump(struct slcan *sl) if (!(cf.can_id & CAN_RTR_FLAG)) sl->dev->stats.rx_bytes += cf.len; - netif_rx_ni(skb); + netif_rx(skb); } /* parse tty input stream */ diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c index d74e895bddf7..8d27ac66ca7f 100644 --- a/drivers/net/can/softing/softing_main.c +++ b/drivers/net/can/softing/softing_main.c @@ -392,13 +392,10 @@ static int softing_netdev_open(struct net_device *ndev) static int softing_netdev_stop(struct net_device *ndev) { - int ret; - netif_stop_queue(ndev); /* softing cycle does close_candev() */ - ret = softing_startstop(ndev, 0); - return ret; + return softing_startstop(ndev, 0); } static int softing_candev_set_mode(struct net_device *ndev, enum can_mode mode) diff --git a/drivers/net/can/spi/hi311x.c b/drivers/net/can/spi/hi311x.c index 664b8f14d7b0..a5b2952b8d0f 100644 --- a/drivers/net/can/spi/hi311x.c +++ b/drivers/net/can/spi/hi311x.c @@ -356,7 +356,7 @@ static void hi3110_hw_rx(struct spi_device *spi) can_led_event(priv->net, CAN_LED_EVENT_RX); - netif_rx_ni(skb); + netif_rx(skb); } static void hi3110_hw_sleep(struct spi_device *spi) @@ -677,7 +677,7 @@ static irqreturn_t hi3110_can_ist(int irq, void *dev_id) tx_state = txerr >= rxerr ? new_state : 0; rx_state = txerr <= rxerr ? new_state : 0; can_change_state(net, cf, tx_state, rx_state); - netif_rx_ni(skb); + netif_rx(skb); if (new_state == CAN_STATE_BUS_OFF) { can_bus_off(net); @@ -718,7 +718,7 @@ static irqreturn_t hi3110_can_ist(int irq, void *dev_id) cf->data[6] = hi3110_read(spi, HI3110_READ_TEC); cf->data[7] = hi3110_read(spi, HI3110_READ_REC); netdev_dbg(priv->net, "Bus Error\n"); - netif_rx_ni(skb); + netif_rx(skb); } } diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c index d23edaf22420..fc747bff5eeb 100644 --- a/drivers/net/can/spi/mcp251x.c +++ b/drivers/net/can/spi/mcp251x.c @@ -740,7 +740,7 @@ static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx) can_led_event(priv->net, CAN_LED_EVENT_RX); - netif_rx_ni(skb); + netif_rx(skb); } static void mcp251x_hw_sleep(struct spi_device *spi) @@ -987,7 +987,7 @@ static void mcp251x_error_skb(struct net_device *net, int can_id, int data1) if (skb) { frame->can_id |= can_id; frame->data[1] = data1; - netif_rx_ni(skb); + netif_rx(skb); } else { netdev_err(net, "cannot allocate error skb\n"); } diff --git a/drivers/net/can/spi/mcp251xfd/Makefile b/drivers/net/can/spi/mcp251xfd/Makefile index a83d685d64e0..94d7de954294 100644 --- a/drivers/net/can/spi/mcp251xfd/Makefile +++ b/drivers/net/can/spi/mcp251xfd/Makefile @@ -6,6 +6,8 @@ mcp251xfd-objs := mcp251xfd-objs += mcp251xfd-chip-fifo.o mcp251xfd-objs += mcp251xfd-core.o mcp251xfd-objs += mcp251xfd-crc16.o +mcp251xfd-objs += mcp251xfd-ethtool.o +mcp251xfd-objs += mcp251xfd-ram.o mcp251xfd-objs += mcp251xfd-regmap.o mcp251xfd-objs += mcp251xfd-ring.o mcp251xfd-objs += mcp251xfd-rx.o diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c index 2f9a623d381d..0d96097a2547 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c @@ -78,7 +78,7 @@ int mcp251xfd_chip_fifo_init(const struct mcp251xfd_priv *priv) if (err) return err; - /* FIFO 1 - TX */ + /* TX FIFO */ val = FIELD_PREP(MCP251XFD_REG_FIFOCON_FSIZE_MASK, tx_ring->obj_num - 1) | MCP251XFD_REG_FIFOCON_TXEN | @@ -99,7 +99,7 @@ int mcp251xfd_chip_fifo_init(const struct mcp251xfd_priv *priv) MCP251XFD_REG_FIFOCON_TXAT_UNLIMITED); err = regmap_write(priv->map_reg, - MCP251XFD_REG_FIFOCON(MCP251XFD_TX_FIFO), + MCP251XFD_REG_FIFOCON(priv->tx->fifo_nr), val); if (err) return err; diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c index 65c9b31666a6..325024be7b04 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c @@ -112,6 +112,22 @@ static const char *mcp251xfd_get_mode_str(const u8 mode) return ""; } +static const char * +mcp251xfd_get_osc_str(const u32 osc, const u32 osc_reference) +{ + switch (~osc & osc_reference & + (MCP251XFD_REG_OSC_OSCRDY | MCP251XFD_REG_OSC_PLLRDY)) { + case MCP251XFD_REG_OSC_PLLRDY: + return "PLL"; + case MCP251XFD_REG_OSC_OSCRDY: + return "Oscillator"; + case MCP251XFD_REG_OSC_PLLRDY | MCP251XFD_REG_OSC_OSCRDY: + return "Oscillator/PLL"; + } + + return ""; +} + static inline int mcp251xfd_vdd_enable(const struct mcp251xfd_priv *priv) { if (!priv->reg_vdd) @@ -178,6 +194,11 @@ static int mcp251xfd_clks_and_vdd_disable(const struct mcp251xfd_priv *priv) return 0; } +static inline bool mcp251xfd_reg_invalid(u32 reg) +{ + return reg == 0x0 || reg == 0xffffffff; +} + static inline int mcp251xfd_chip_get_mode(const struct mcp251xfd_priv *priv, u8 *mode) { @@ -197,34 +218,55 @@ static int __mcp251xfd_chip_set_mode(const struct mcp251xfd_priv *priv, const u8 mode_req, bool nowait) { - u32 con, con_reqop; + u32 con = 0, con_reqop, osc = 0; + u8 mode; int err; con_reqop = FIELD_PREP(MCP251XFD_REG_CON_REQOP_MASK, mode_req); err = regmap_update_bits(priv->map_reg, MCP251XFD_REG_CON, MCP251XFD_REG_CON_REQOP_MASK, con_reqop); - if (err) + if (err == -EBADMSG) { + netdev_err(priv->ndev, + "Failed to set Requested Operation Mode.\n"); + + return -ENODEV; + } else if (err) { return err; + } if (mode_req == MCP251XFD_REG_CON_MODE_SLEEP || nowait) return 0; err = regmap_read_poll_timeout(priv->map_reg, MCP251XFD_REG_CON, con, + !mcp251xfd_reg_invalid(con) && FIELD_GET(MCP251XFD_REG_CON_OPMOD_MASK, con) == mode_req, MCP251XFD_POLL_SLEEP_US, MCP251XFD_POLL_TIMEOUT_US); - if (err) { - u8 mode = FIELD_GET(MCP251XFD_REG_CON_OPMOD_MASK, con); - - netdev_err(priv->ndev, - "Controller failed to enter mode %s Mode (%u) and stays in %s Mode (%u).\n", - mcp251xfd_get_mode_str(mode_req), mode_req, - mcp251xfd_get_mode_str(mode), mode); + if (err != -ETIMEDOUT && err != -EBADMSG) return err; + + /* Ignore return value. + * Print below error messages, even if this fails. + */ + regmap_read(priv->map_reg, MCP251XFD_REG_OSC, &osc); + + if (mcp251xfd_reg_invalid(con)) { + netdev_err(priv->ndev, + "Failed to read CAN Control Register (con=0x%08x, osc=0x%08x).\n", + con, osc); + + return -ENODEV; } - return 0; + mode = FIELD_GET(MCP251XFD_REG_CON_OPMOD_MASK, con); + netdev_err(priv->ndev, + "Controller failed to enter mode %s Mode (%u) and stays in %s Mode (%u) (con=0x%08x, osc=0x%08x).\n", + mcp251xfd_get_mode_str(mode_req), mode_req, + mcp251xfd_get_mode_str(mode), mode, + con, osc); + + return -ETIMEDOUT; } static inline int @@ -241,27 +283,58 @@ mcp251xfd_chip_set_mode_nowait(const struct mcp251xfd_priv *priv, return __mcp251xfd_chip_set_mode(priv, mode_req, true); } -static inline bool mcp251xfd_osc_invalid(u32 reg) +static int +mcp251xfd_chip_wait_for_osc_ready(const struct mcp251xfd_priv *priv, + u32 osc_reference, u32 osc_mask) { - return reg == 0x0 || reg == 0xffffffff; + u32 osc; + int err; + + err = regmap_read_poll_timeout(priv->map_reg, MCP251XFD_REG_OSC, osc, + !mcp251xfd_reg_invalid(osc) && + (osc & osc_mask) == osc_reference, + MCP251XFD_OSC_STAB_SLEEP_US, + MCP251XFD_OSC_STAB_TIMEOUT_US); + if (err != -ETIMEDOUT) + return err; + + if (mcp251xfd_reg_invalid(osc)) { + netdev_err(priv->ndev, + "Failed to read Oscillator Configuration Register (osc=0x%08x).\n", + osc); + return -ENODEV; + } + + netdev_err(priv->ndev, + "Timeout waiting for %s ready (osc=0x%08x, osc_reference=0x%08x, osc_mask=0x%08x).\n", + mcp251xfd_get_osc_str(osc, osc_reference), + osc, osc_reference, osc_mask); + + return -ETIMEDOUT; } -static int mcp251xfd_chip_clock_enable(const struct mcp251xfd_priv *priv) +static int mcp251xfd_chip_wake(const struct mcp251xfd_priv *priv) { u32 osc, osc_reference, osc_mask; int err; - /* Set Power On Defaults for "Clock Output Divisor" and remove - * "Oscillator Disable" bit. + /* For normal sleep on MCP2517FD and MCP2518FD, clearing + * "Oscillator Disable" will wake the chip. For low power mode + * on MCP2518FD, asserting the chip select will wake the + * chip. Writing to the Oscillator register will wake it in + * both cases. */ osc = FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, MCP251XFD_REG_OSC_CLKODIV_10); - osc_reference = MCP251XFD_REG_OSC_OSCRDY; - osc_mask = MCP251XFD_REG_OSC_OSCRDY | MCP251XFD_REG_OSC_PLLRDY; - /* Note: - * - * If the controller is in Sleep Mode the following write only + /* We cannot check for the PLL ready bit (either set or + * unset), as the PLL might be enabled. This can happen if the + * system reboots, while the mcp251xfd stays powered. + */ + osc_reference = MCP251XFD_REG_OSC_OSCRDY; + osc_mask = MCP251XFD_REG_OSC_OSCRDY; + + /* If the controller is in Sleep Mode the following write only * removes the "Oscillator Disable" bit and powers it up. All * other bits are unaffected. */ @@ -269,24 +342,31 @@ static int mcp251xfd_chip_clock_enable(const struct mcp251xfd_priv *priv) if (err) return err; - /* Wait for "Oscillator Ready" bit */ - err = regmap_read_poll_timeout(priv->map_reg, MCP251XFD_REG_OSC, osc, - (osc & osc_mask) == osc_reference, - MCP251XFD_OSC_STAB_SLEEP_US, - MCP251XFD_OSC_STAB_TIMEOUT_US); - if (mcp251xfd_osc_invalid(osc)) { - netdev_err(priv->ndev, - "Failed to detect %s (osc=0x%08x).\n", - mcp251xfd_get_model_str(priv), osc); - return -ENODEV; - } else if (err == -ETIMEDOUT) { - netdev_err(priv->ndev, - "Timeout waiting for Oscillator Ready (osc=0x%08x, osc_reference=0x%08x)\n", - osc, osc_reference); - return -ETIMEDOUT; + /* Sometimes the PLL is stuck enabled, the controller never + * sets the OSC Ready bit, and we get an -ETIMEDOUT. Our + * caller takes care of retry. + */ + return mcp251xfd_chip_wait_for_osc_ready(priv, osc_reference, osc_mask); +} + +static inline int mcp251xfd_chip_sleep(const struct mcp251xfd_priv *priv) +{ + if (priv->pll_enable) { + u32 osc; + int err; + + /* Turn off PLL */ + osc = FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, + MCP251XFD_REG_OSC_CLKODIV_10); + err = regmap_write(priv->map_reg, MCP251XFD_REG_OSC, osc); + if (err) + netdev_err(priv->ndev, + "Failed to disable PLL.\n"); + + priv->spi->max_speed_hz = priv->spi_max_speed_hz_slow; } - return err; + return mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP); } static int mcp251xfd_chip_softreset_do(const struct mcp251xfd_priv *priv) @@ -294,10 +374,10 @@ static int mcp251xfd_chip_softreset_do(const struct mcp251xfd_priv *priv) const __be16 cmd = mcp251xfd_cmd_reset(); int err; - /* The Set Mode and SPI Reset command only seems to works if - * the controller is not in Sleep Mode. + /* The Set Mode and SPI Reset command only works if the + * controller is not in Sleep Mode. */ - err = mcp251xfd_chip_clock_enable(priv); + err = mcp251xfd_chip_wake(priv); if (err) return err; @@ -311,10 +391,21 @@ static int mcp251xfd_chip_softreset_do(const struct mcp251xfd_priv *priv) static int mcp251xfd_chip_softreset_check(const struct mcp251xfd_priv *priv) { - u32 osc, osc_reference; + u32 osc_reference, osc_mask; u8 mode; int err; + /* Check for reset defaults of OSC reg. + * This will take care of stabilization period. + */ + osc_reference = MCP251XFD_REG_OSC_OSCRDY | + FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, + MCP251XFD_REG_OSC_CLKODIV_10); + osc_mask = osc_reference | MCP251XFD_REG_OSC_PLLRDY; + err = mcp251xfd_chip_wait_for_osc_ready(priv, osc_reference, osc_mask); + if (err) + return err; + err = mcp251xfd_chip_get_mode(priv, &mode); if (err) return err; @@ -326,22 +417,6 @@ static int mcp251xfd_chip_softreset_check(const struct mcp251xfd_priv *priv) return -ETIMEDOUT; } - osc_reference = MCP251XFD_REG_OSC_OSCRDY | - FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, - MCP251XFD_REG_OSC_CLKODIV_10); - - /* check reset defaults of OSC reg */ - err = regmap_read(priv->map_reg, MCP251XFD_REG_OSC, &osc); - if (err) - return err; - - if (osc != osc_reference) { - netdev_info(priv->ndev, - "Controller failed to reset. osc=0x%08x, reference value=0x%08x.\n", - osc, osc_reference); - return -ETIMEDOUT; - } - return 0; } @@ -374,7 +449,7 @@ static int mcp251xfd_chip_softreset(const struct mcp251xfd_priv *priv) static int mcp251xfd_chip_clock_init(const struct mcp251xfd_priv *priv) { - u32 osc; + u32 osc, osc_reference, osc_mask; int err; /* Activate Low Power Mode on Oscillator Disable. This only @@ -384,10 +459,29 @@ static int mcp251xfd_chip_clock_init(const struct mcp251xfd_priv *priv) osc = MCP251XFD_REG_OSC_LPMEN | FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, MCP251XFD_REG_OSC_CLKODIV_10); + osc_reference = MCP251XFD_REG_OSC_OSCRDY; + osc_mask = MCP251XFD_REG_OSC_OSCRDY | MCP251XFD_REG_OSC_PLLRDY; + + if (priv->pll_enable) { + osc |= MCP251XFD_REG_OSC_PLLEN; + osc_reference |= MCP251XFD_REG_OSC_PLLRDY; + } + err = regmap_write(priv->map_reg, MCP251XFD_REG_OSC, osc); if (err) return err; + err = mcp251xfd_chip_wait_for_osc_ready(priv, osc_reference, osc_mask); + if (err) + return err; + + priv->spi->max_speed_hz = priv->spi_max_speed_hz_fast; + + return 0; +} + +static int mcp251xfd_chip_timestamp_init(const struct mcp251xfd_priv *priv) +{ /* Set Time Base Counter Prescaler to 1. * * This means an overflow of the 32 bit Time Base Counter @@ -628,14 +722,14 @@ static int mcp251xfd_chip_interrupts_disable(const struct mcp251xfd_priv *priv) return regmap_write(priv->map_reg, MCP251XFD_REG_CRC, 0); } -static int mcp251xfd_chip_stop(struct mcp251xfd_priv *priv, - const enum can_state state) +static void mcp251xfd_chip_stop(struct mcp251xfd_priv *priv, + const enum can_state state) { priv->can.state = state; mcp251xfd_chip_interrupts_disable(priv); mcp251xfd_chip_rx_int_disable(priv); - return mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP); + mcp251xfd_chip_sleep(priv); } static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv) @@ -650,6 +744,10 @@ static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv) if (err) goto out_chip_stop; + err = mcp251xfd_chip_timestamp_init(priv); + if (err) + goto out_chip_stop; + err = mcp251xfd_set_bittiming(priv); if (err) goto out_chip_stop; @@ -662,7 +760,9 @@ static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv) if (err) goto out_chip_stop; - mcp251xfd_ring_init(priv); + err = mcp251xfd_ring_init(priv); + if (err) + goto out_chip_stop; err = mcp251xfd_chip_fifo_init(priv); if (err) @@ -1284,6 +1384,20 @@ static int mcp251xfd_handle_spicrcif(struct mcp251xfd_priv *priv) return 0; } +static int mcp251xfd_read_regs_status(struct mcp251xfd_priv *priv) +{ + const int val_bytes = regmap_get_val_bytes(priv->map_reg); + size_t len; + + if (priv->rx_ring_num == 1) + len = sizeof(priv->regs_status.intf); + else + len = sizeof(priv->regs_status); + + return regmap_bulk_read(priv->map_reg, MCP251XFD_REG_INT, + &priv->regs_status, len / val_bytes); +} + #define mcp251xfd_handle(priv, irq, ...) \ ({ \ struct mcp251xfd_priv *_priv = (priv); \ @@ -1300,7 +1414,6 @@ static int mcp251xfd_handle_spicrcif(struct mcp251xfd_priv *priv) static irqreturn_t mcp251xfd_irq(int irq, void *dev_id) { struct mcp251xfd_priv *priv = dev_id; - const int val_bytes = regmap_get_val_bytes(priv->map_reg); irqreturn_t handled = IRQ_NONE; int err; @@ -1312,21 +1425,28 @@ static irqreturn_t mcp251xfd_irq(int irq, void *dev_id) if (!rx_pending) break; + /* Assume 1st RX-FIFO pending, if other FIFOs + * are pending the main IRQ handler will take + * care. + */ + priv->regs_status.rxif = BIT(priv->rx[0]->fifo_nr); err = mcp251xfd_handle(priv, rxif); if (err) goto out_fail; handled = IRQ_HANDLED; - } while (1); + + /* We don't know which RX-FIFO is pending, but only + * handle the 1st RX-FIFO. Leave loop here if we have + * more than 1 RX-FIFO to avoid starvation. + */ + } while (priv->rx_ring_num == 1); do { u32 intf_pending, intf_pending_clearable; bool set_normal_mode = false; - err = regmap_bulk_read(priv->map_reg, MCP251XFD_REG_INT, - &priv->regs_status, - sizeof(priv->regs_status) / - val_bytes); + err = mcp251xfd_read_regs_status(priv); if (err) goto out_fail; @@ -1478,6 +1598,7 @@ static int mcp251xfd_open(struct net_device *ndev) goto out_transceiver_disable; mcp251xfd_timestamp_init(priv); + clear_bit(MCP251XFD_FLAGS_DOWN, priv->flags); can_rx_offload_enable(&priv->offload); err = request_threaded_irq(spi->irq, NULL, mcp251xfd_irq, @@ -1498,6 +1619,7 @@ static int mcp251xfd_open(struct net_device *ndev) free_irq(spi->irq, priv); out_can_rx_offload_disable: can_rx_offload_disable(&priv->offload); + set_bit(MCP251XFD_FLAGS_DOWN, priv->flags); mcp251xfd_timestamp_stop(priv); out_transceiver_disable: mcp251xfd_transceiver_disable(priv); @@ -1517,6 +1639,8 @@ static int mcp251xfd_stop(struct net_device *ndev) struct mcp251xfd_priv *priv = netdev_priv(ndev); netif_stop_queue(ndev); + set_bit(MCP251XFD_FLAGS_DOWN, priv->flags); + hrtimer_cancel(&priv->rx_irq_timer); mcp251xfd_chip_interrupts_disable(priv); free_irq(ndev->irq, priv); can_rx_offload_disable(&priv->offload); @@ -1621,8 +1745,9 @@ static int mcp251xfd_register_check_rx_int(struct mcp251xfd_priv *priv) } static int -mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, - u32 *dev_id, u32 *effective_speed_hz) +mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, u32 *dev_id, + u32 *effective_speed_hz_slow, + u32 *effective_speed_hz_fast) { struct mcp251xfd_map_buf_nocrc *buf_rx; struct mcp251xfd_map_buf_nocrc *buf_tx; @@ -1641,16 +1766,20 @@ mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, xfer[0].tx_buf = buf_tx; xfer[0].len = sizeof(buf_tx->cmd); + xfer[0].speed_hz = priv->spi_max_speed_hz_slow; xfer[1].rx_buf = buf_rx->data; xfer[1].len = sizeof(dev_id); + xfer[1].speed_hz = priv->spi_max_speed_hz_fast; mcp251xfd_spi_cmd_read_nocrc(&buf_tx->cmd, MCP251XFD_REG_DEVID); + err = spi_sync_transfer(priv->spi, xfer, ARRAY_SIZE(xfer)); if (err) goto out_kfree_buf_tx; *dev_id = be32_to_cpup((__be32 *)buf_rx->data); - *effective_speed_hz = xfer->effective_speed_hz; + *effective_speed_hz_slow = xfer[0].effective_speed_hz; + *effective_speed_hz_fast = xfer[1].effective_speed_hz; out_kfree_buf_tx: kfree(buf_tx); @@ -1666,34 +1795,45 @@ mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, static int mcp251xfd_register_done(const struct mcp251xfd_priv *priv) { - u32 dev_id, effective_speed_hz; + u32 dev_id, effective_speed_hz_slow, effective_speed_hz_fast; + unsigned long clk_rate; int err; err = mcp251xfd_register_get_dev_id(priv, &dev_id, - &effective_speed_hz); + &effective_speed_hz_slow, + &effective_speed_hz_fast); if (err) return err; + clk_rate = clk_get_rate(priv->clk); + netdev_info(priv->ndev, - "%s rev%lu.%lu (%cRX_INT %cMAB_NO_WARN %cCRC_REG %cCRC_RX %cCRC_TX %cECC %cHD c:%u.%02uMHz m:%u.%02uMHz r:%u.%02uMHz e:%u.%02uMHz) successfully initialized.\n", + "%s rev%lu.%lu (%cRX_INT %cPLL %cMAB_NO_WARN %cCRC_REG %cCRC_RX %cCRC_TX %cECC %cHD o:%lu.%02luMHz c:%u.%02uMHz m:%u.%02uMHz rs:%u.%02uMHz es:%u.%02uMHz rf:%u.%02uMHz ef:%u.%02uMHz) successfully initialized.\n", mcp251xfd_get_model_str(priv), FIELD_GET(MCP251XFD_REG_DEVID_ID_MASK, dev_id), FIELD_GET(MCP251XFD_REG_DEVID_REV_MASK, dev_id), priv->rx_int ? '+' : '-', + priv->pll_enable ? '+' : '-', MCP251XFD_QUIRK_ACTIVE(MAB_NO_WARN), MCP251XFD_QUIRK_ACTIVE(CRC_REG), MCP251XFD_QUIRK_ACTIVE(CRC_RX), MCP251XFD_QUIRK_ACTIVE(CRC_TX), MCP251XFD_QUIRK_ACTIVE(ECC), MCP251XFD_QUIRK_ACTIVE(HALF_DUPLEX), + clk_rate / 1000000, + clk_rate % 1000000 / 1000 / 10, priv->can.clock.freq / 1000000, priv->can.clock.freq % 1000000 / 1000 / 10, priv->spi_max_speed_hz_orig / 1000000, priv->spi_max_speed_hz_orig % 1000000 / 1000 / 10, - priv->spi->max_speed_hz / 1000000, - priv->spi->max_speed_hz % 1000000 / 1000 / 10, - effective_speed_hz / 1000000, - effective_speed_hz % 1000000 / 1000 / 10); + priv->spi_max_speed_hz_slow / 1000000, + priv->spi_max_speed_hz_slow % 1000000 / 1000 / 10, + effective_speed_hz_slow / 1000000, + effective_speed_hz_slow % 1000000 / 1000 / 10, + priv->spi_max_speed_hz_fast / 1000000, + priv->spi_max_speed_hz_fast % 1000000 / 1000 / 10, + effective_speed_hz_fast / 1000000, + effective_speed_hz_fast % 1000000 / 1000 / 10); return 0; } @@ -1719,19 +1859,27 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv) if (err == -ENODEV) goto out_runtime_disable; if (err) - goto out_chip_set_mode_sleep; + goto out_chip_sleep; + + err = mcp251xfd_chip_clock_init(priv); + if (err == -ENODEV) + goto out_runtime_disable; + if (err) + goto out_chip_sleep; err = mcp251xfd_register_chip_detect(priv); if (err) - goto out_chip_set_mode_sleep; + goto out_chip_sleep; err = mcp251xfd_register_check_rx_int(priv); if (err) - goto out_chip_set_mode_sleep; + goto out_chip_sleep; + + mcp251xfd_ethtool_init(priv); err = register_candev(ndev); if (err) - goto out_chip_set_mode_sleep; + goto out_chip_sleep; err = mcp251xfd_register_done(priv); if (err) @@ -1741,7 +1889,7 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv) * disable the clocks and vdd. If CONFIG_PM is not enabled, * the clocks and vdd will stay powered. */ - err = mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP); + err = mcp251xfd_chip_sleep(priv); if (err) goto out_unregister_candev; @@ -1751,8 +1899,8 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv) out_unregister_candev: unregister_candev(ndev); - out_chip_set_mode_sleep: - mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP); + out_chip_sleep: + mcp251xfd_chip_sleep(priv); out_runtime_disable: pm_runtime_disable(ndev->dev.parent); out_runtime_put_noidle: @@ -1768,10 +1916,10 @@ static inline void mcp251xfd_unregister(struct mcp251xfd_priv *priv) unregister_candev(ndev); - pm_runtime_get_sync(ndev->dev.parent); - pm_runtime_put_noidle(ndev->dev.parent); - mcp251xfd_clks_and_vdd_disable(priv); - pm_runtime_disable(ndev->dev.parent); + if (pm_runtime_enabled(ndev->dev.parent)) + pm_runtime_disable(ndev->dev.parent); + else + mcp251xfd_clks_and_vdd_disable(priv); } static const struct of_device_id mcp251xfd_of_match[] = { @@ -1814,6 +1962,7 @@ static int mcp251xfd_probe(struct spi_device *spi) struct gpio_desc *rx_int; struct regulator *reg_vdd, *reg_xceiver; struct clk *clk; + bool pll_enable = false; u32 freq = 0; int err; @@ -1864,12 +2013,8 @@ static int mcp251xfd_probe(struct spi_device *spi) return -ERANGE; } - if (freq <= MCP251XFD_SYSCLOCK_HZ_MAX / MCP251XFD_OSC_PLL_MULTIPLIER) { - dev_err(&spi->dev, - "Oscillator frequency (%u Hz) is too low and PLL is not supported.\n", - freq); - return -ERANGE; - } + if (freq <= MCP251XFD_SYSCLOCK_HZ_MAX / MCP251XFD_OSC_PLL_MULTIPLIER) + pll_enable = true; ndev = alloc_candev(sizeof(struct mcp251xfd_priv), MCP251XFD_TX_OBJ_NUM_MAX); @@ -1885,6 +2030,8 @@ static int mcp251xfd_probe(struct spi_device *spi) priv = netdev_priv(ndev); spi_set_drvdata(spi, priv); priv->can.clock.freq = freq; + if (pll_enable) + priv->can.clock.freq *= MCP251XFD_OSC_PLL_MULTIPLIER; priv->can.do_set_mode = mcp251xfd_set_mode; priv->can.do_get_berr_counter = mcp251xfd_get_berr_counter; priv->can.bittiming_const = &mcp251xfd_bittiming_const; @@ -1893,10 +2040,12 @@ static int mcp251xfd_probe(struct spi_device *spi) CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO | CAN_CTRLMODE_CC_LEN8_DLC; + set_bit(MCP251XFD_FLAGS_DOWN, priv->flags); priv->ndev = ndev; priv->spi = spi; priv->rx_int = rx_int; priv->clk = clk; + priv->pll_enable = pll_enable; priv->reg_vdd = reg_vdd; priv->reg_xceiver = reg_xceiver; @@ -1934,7 +2083,16 @@ static int mcp251xfd_probe(struct spi_device *spi) * */ priv->spi_max_speed_hz_orig = spi->max_speed_hz; - spi->max_speed_hz = min(spi->max_speed_hz, freq / 2 / 1000 * 850); + priv->spi_max_speed_hz_slow = min(spi->max_speed_hz, + freq / 2 / 1000 * 850); + if (priv->pll_enable) + priv->spi_max_speed_hz_fast = min(spi->max_speed_hz, + freq * + MCP251XFD_OSC_PLL_MULTIPLIER / + 2 / 1000 * 850); + else + priv->spi_max_speed_hz_fast = priv->spi_max_speed_hz_slow; + spi->max_speed_hz = priv->spi_max_speed_hz_slow; spi->bits_per_word = 8; spi->rt = true; err = spi_setup(spi); @@ -1951,8 +2109,11 @@ static int mcp251xfd_probe(struct spi_device *spi) goto out_free_candev; err = mcp251xfd_register(priv); - if (err) + if (err) { + dev_err_probe(&spi->dev, err, "Failed to detect %s.\n", + mcp251xfd_get_model_str(priv)); goto out_can_rx_offload_del; + } return 0; diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c index ffae8fdd3af0..c991b30bc9f0 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c @@ -207,10 +207,10 @@ static void mcp251xfd_dump_tx_ring(const struct mcp251xfd_priv *priv, .val = tx->base, }, { .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR, - .val = 0, + .val = tx->nr, }, { .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR, - .val = MCP251XFD_TX_FIFO, + .val = tx->fifo_nr, }, { .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM, .val = tx->obj_num, diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c new file mode 100644 index 000000000000..6c7a57f16cc6 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2021, 2022 Pengutronix, +// Marc Kleine-Budde +// + +#include + +#include "mcp251xfd.h" +#include "mcp251xfd-ram.h" + +static void +mcp251xfd_ring_get_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + const struct mcp251xfd_priv *priv = netdev_priv(ndev); + const bool fd_mode = mcp251xfd_is_fd_mode(priv); + struct can_ram_layout layout; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, fd_mode); + ring->rx_max_pending = layout.max_rx; + ring->tx_max_pending = layout.max_tx; + + ring->rx_pending = priv->rx_obj_num; + ring->tx_pending = priv->tx->obj_num; +} + +static int +mcp251xfd_ring_set_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + const bool fd_mode = mcp251xfd_is_fd_mode(priv); + struct can_ram_layout layout; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, ring, NULL, fd_mode); + if ((layout.cur_rx != priv->rx_obj_num || + layout.cur_tx != priv->tx->obj_num) && + netif_running(ndev)) + return -EBUSY; + + priv->rx_obj_num = layout.cur_rx; + priv->rx_obj_num_coalesce_irq = layout.rx_coalesce; + priv->tx->obj_num = layout.cur_tx; + + return 0; +} + +static int mcp251xfd_ring_get_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kec, + struct netlink_ext_ack *ext_ack) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + u32 rx_max_frames, tx_max_frames; + + /* The ethtool doc says: + * To disable coalescing, set usecs = 0 and max_frames = 1. + */ + if (priv->rx_obj_num_coalesce_irq == 0) + rx_max_frames = 1; + else + rx_max_frames = priv->rx_obj_num_coalesce_irq; + + ec->rx_max_coalesced_frames_irq = rx_max_frames; + ec->rx_coalesce_usecs_irq = priv->rx_coalesce_usecs_irq; + + if (priv->tx_obj_num_coalesce_irq == 0) + tx_max_frames = 1; + else + tx_max_frames = priv->tx_obj_num_coalesce_irq; + + ec->tx_max_coalesced_frames_irq = tx_max_frames; + ec->tx_coalesce_usecs_irq = priv->tx_coalesce_usecs_irq; + + return 0; +} + +static int mcp251xfd_ring_set_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kec, + struct netlink_ext_ack *ext_ack) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + const bool fd_mode = mcp251xfd_is_fd_mode(priv); + const struct ethtool_ringparam ring = { + .rx_pending = priv->rx_obj_num, + .tx_pending = priv->tx->obj_num, + }; + struct can_ram_layout layout; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, &ring, ec, fd_mode); + + if ((layout.rx_coalesce != priv->rx_obj_num_coalesce_irq || + ec->rx_coalesce_usecs_irq != priv->rx_coalesce_usecs_irq || + layout.tx_coalesce != priv->tx_obj_num_coalesce_irq || + ec->tx_coalesce_usecs_irq != priv->tx_coalesce_usecs_irq) && + netif_running(ndev)) + return -EBUSY; + + priv->rx_obj_num = layout.cur_rx; + priv->rx_obj_num_coalesce_irq = layout.rx_coalesce; + priv->rx_coalesce_usecs_irq = ec->rx_coalesce_usecs_irq; + + priv->tx->obj_num = layout.cur_tx; + priv->tx_obj_num_coalesce_irq = layout.tx_coalesce; + priv->tx_coalesce_usecs_irq = ec->tx_coalesce_usecs_irq; + + return 0; +} + +static const struct ethtool_ops mcp251xfd_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS_IRQ | + ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ | + ETHTOOL_COALESCE_TX_USECS_IRQ | + ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ, + .get_ringparam = mcp251xfd_ring_get_ringparam, + .set_ringparam = mcp251xfd_ring_set_ringparam, + .get_coalesce = mcp251xfd_ring_get_coalesce, + .set_coalesce = mcp251xfd_ring_set_coalesce, +}; + +void mcp251xfd_ethtool_init(struct mcp251xfd_priv *priv) +{ + struct can_ram_layout layout; + + priv->ndev->ethtool_ops = &mcp251xfd_ethtool_ops; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, false); + priv->rx_obj_num = layout.default_rx; + priv->tx->obj_num = layout.default_tx; + + priv->rx_obj_num_coalesce_irq = 0; + priv->tx_obj_num_coalesce_irq = 0; + priv->rx_coalesce_usecs_irq = 0; + priv->tx_coalesce_usecs_irq = 0; +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.c new file mode 100644 index 000000000000..9e8e82cdba46 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2021, 2022 Pengutronix, +// Marc Kleine-Budde +// + +#include "mcp251xfd-ram.h" + +static inline u8 can_ram_clamp(const struct can_ram_config *config, + const struct can_ram_obj_config *obj, + u8 val) +{ + u8 max; + + max = min_t(u8, obj->max, obj->fifo_num * config->fifo_depth); + return clamp(val, obj->min, max); +} + +static u8 +can_ram_rounddown_pow_of_two(const struct can_ram_config *config, + const struct can_ram_obj_config *obj, + const u8 coalesce, u8 val) +{ + u8 fifo_num = obj->fifo_num; + u8 ret = 0, i; + + val = can_ram_clamp(config, obj, val); + + if (coalesce) { + /* Use 1st FIFO for coalescing, if requested. + * + * Either use complete FIFO (and FIFO Full IRQ) for + * coalescing or only half of FIFO (FIFO Half Full + * IRQ) and use remaining half for normal objects. + */ + ret = min_t(u8, coalesce * 2, config->fifo_depth); + val -= ret; + fifo_num--; + } + + for (i = 0; i < fifo_num && val; i++) { + u8 n; + + n = min_t(u8, rounddown_pow_of_two(val), + config->fifo_depth); + + /* skip small FIFOs */ + if (n < obj->fifo_depth_min) + return ret; + + ret += n; + val -= n; + } + + return ret; +} + +void can_ram_get_layout(struct can_ram_layout *layout, + const struct can_ram_config *config, + const struct ethtool_ringparam *ring, + const struct ethtool_coalesce *ec, + const bool fd_mode) +{ + u8 num_rx, num_tx; + u16 ram_free; + + /* default CAN */ + + num_tx = config->tx.def[fd_mode]; + num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx); + + ram_free = config->size; + ram_free -= config->tx.size[fd_mode] * num_tx; + + num_rx = ram_free / config->rx.size[fd_mode]; + + layout->default_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx); + layout->default_tx = num_tx; + + /* MAX CAN */ + + ram_free = config->size; + ram_free -= config->tx.size[fd_mode] * config->tx.min; + num_rx = ram_free / config->rx.size[fd_mode]; + + ram_free = config->size; + ram_free -= config->rx.size[fd_mode] * config->rx.min; + num_tx = ram_free / config->tx.size[fd_mode]; + + layout->max_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx); + layout->max_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx); + + /* cur CAN */ + + if (ring) { + u8 num_rx_coalesce = 0, num_tx_coalesce = 0; + + num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, ring->rx_pending); + + /* The ethtool doc says: + * To disable coalescing, set usecs = 0 and max_frames = 1. + */ + if (ec && !(ec->rx_coalesce_usecs_irq == 0 && + ec->rx_max_coalesced_frames_irq == 1)) { + u8 max; + + /* use only max half of available objects for coalescing */ + max = min_t(u8, num_rx / 2, config->fifo_depth); + num_rx_coalesce = clamp(ec->rx_max_coalesced_frames_irq, + (u32)config->rx.fifo_depth_coalesce_min, + (u32)max); + num_rx_coalesce = rounddown_pow_of_two(num_rx_coalesce); + + num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, + num_rx_coalesce, num_rx); + } + + ram_free = config->size - config->rx.size[fd_mode] * num_rx; + num_tx = ram_free / config->tx.size[fd_mode]; + num_tx = min_t(u8, ring->tx_pending, num_tx); + num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx); + + /* The ethtool doc says: + * To disable coalescing, set usecs = 0 and max_frames = 1. + */ + if (ec && !(ec->tx_coalesce_usecs_irq == 0 && + ec->tx_max_coalesced_frames_irq == 1)) { + u8 max; + + /* use only max half of available objects for coalescing */ + max = min_t(u8, num_tx / 2, config->fifo_depth); + num_tx_coalesce = clamp(ec->tx_max_coalesced_frames_irq, + (u32)config->tx.fifo_depth_coalesce_min, + (u32)max); + num_tx_coalesce = rounddown_pow_of_two(num_tx_coalesce); + + num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, + num_tx_coalesce, num_tx); + } + + layout->cur_rx = num_rx; + layout->cur_tx = num_tx; + layout->rx_coalesce = num_rx_coalesce; + layout->tx_coalesce = num_tx_coalesce; + } else { + layout->cur_rx = layout->default_rx; + layout->cur_tx = layout->default_tx; + layout->rx_coalesce = 0; + layout->tx_coalesce = 0; + } +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.h new file mode 100644 index 000000000000..7558c1510cbf --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * mcp251xfd - Microchip MCP251xFD Family CAN controller driver + * + * Copyright (c) 2021, 2022 Pengutronix, + * Marc Kleine-Budde + */ + +#ifndef _MCP251XFD_RAM_H +#define _MCP251XFD_RAM_H + +#include + +#define CAN_RAM_NUM_MAX (-1) + +enum can_ram_mode { + CAN_RAM_MODE_CAN, + CAN_RAM_MODE_CANFD, + __CAN_RAM_MODE_MAX +}; + +struct can_ram_obj_config { + u8 size[__CAN_RAM_MODE_MAX]; + + u8 def[__CAN_RAM_MODE_MAX]; + u8 min; + u8 max; + + u8 fifo_num; + u8 fifo_depth_min; + u8 fifo_depth_coalesce_min; +}; + +struct can_ram_config { + const struct can_ram_obj_config rx; + const struct can_ram_obj_config tx; + + u16 size; + u8 fifo_depth; +}; + +struct can_ram_layout { + u8 default_rx; + u8 default_tx; + + u8 max_rx; + u8 max_tx; + + u8 cur_rx; + u8 cur_tx; + + u8 rx_coalesce; + u8 tx_coalesce; +}; + +void can_ram_get_layout(struct can_ram_layout *layout, + const struct can_ram_config *config, + const struct ethtool_ringparam *ring, + const struct ethtool_coalesce *ec, + const bool fd_mode); + +#endif diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c index 7b120c716228..217510c12af5 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c @@ -2,8 +2,8 @@ // // mcp251xfd - Microchip MCP251xFD Family CAN controller driver // -// Copyright (c) 2019, 2020 Pengutronix, -// Marc Kleine-Budde +// Copyright (c) 2019, 2020, 2021 Pengutronix, +// Marc Kleine-Budde // #include "mcp251xfd.h" @@ -47,22 +47,32 @@ mcp251xfd_regmap_nocrc_gather_write(void *context, return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); } -static inline bool mcp251xfd_update_bits_read_reg(unsigned int reg) +static inline bool +mcp251xfd_update_bits_read_reg(const struct mcp251xfd_priv *priv, + unsigned int reg) { + struct mcp251xfd_rx_ring *ring; + int n; + switch (reg) { case MCP251XFD_REG_INT: case MCP251XFD_REG_TEFCON: - case MCP251XFD_REG_FIFOCON(MCP251XFD_RX_FIFO(0)): case MCP251XFD_REG_FLTCON(0): case MCP251XFD_REG_ECCSTAT: case MCP251XFD_REG_CRC: return false; case MCP251XFD_REG_CON: - case MCP251XFD_REG_FIFOSTA(MCP251XFD_RX_FIFO(0)): case MCP251XFD_REG_OSC: case MCP251XFD_REG_ECCCON: return true; default: + mcp251xfd_for_each_rx_ring(priv, ring, n) { + if (reg == MCP251XFD_REG_FIFOCON(ring->fifo_nr)) + return false; + if (reg == MCP251XFD_REG_FIFOSTA(ring->fifo_nr)) + return true; + } + WARN(1, "Status of reg 0x%04x unknown.\n", reg); } @@ -92,7 +102,7 @@ mcp251xfd_regmap_nocrc_update_bits(void *context, unsigned int reg, last_byte = mcp251xfd_last_byte_set(mask); len = last_byte - first_byte + 1; - if (mcp251xfd_update_bits_read_reg(reg)) { + if (mcp251xfd_update_bits_read_reg(priv, reg)) { struct spi_transfer xfer[2] = { }; struct spi_message msg; @@ -368,7 +378,7 @@ mcp251xfd_regmap_crc_read(void *context, * to the caller. It will take care of both cases. * */ - if (reg == MCP251XFD_REG_OSC) { + if (reg == MCP251XFD_REG_OSC && val_len == sizeof(__le32)) { err = 0; goto out; } diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c index 92f9e9b01289..bf3f0f150199 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c @@ -15,6 +15,7 @@ #include #include "mcp251xfd.h" +#include "mcp251xfd-ram.h" static inline u8 mcp251xfd_cmd_prepare_write_reg(const struct mcp251xfd_priv *priv, @@ -52,6 +53,72 @@ mcp251xfd_cmd_prepare_write_reg(const struct mcp251xfd_priv *priv, return len; } +static void +mcp251xfd_ring_init_tef(struct mcp251xfd_priv *priv, u16 *base) +{ + struct mcp251xfd_tef_ring *tef_ring; + struct spi_transfer *xfer; + u32 val; + u16 addr; + u8 len; + int i; + + /* TEF */ + tef_ring = priv->tef; + tef_ring->head = 0; + tef_ring->tail = 0; + + /* TEF- and TX-FIFO have same number of objects */ + *base = mcp251xfd_get_tef_obj_addr(priv->tx->obj_num); + + /* FIFO IRQ enable */ + addr = MCP251XFD_REG_TEFCON; + val = MCP251XFD_REG_TEFCON_TEFOVIE | MCP251XFD_REG_TEFCON_TEFNEIE; + + len = mcp251xfd_cmd_prepare_write_reg(priv, &tef_ring->irq_enable_buf, + addr, val, val); + tef_ring->irq_enable_xfer.tx_buf = &tef_ring->irq_enable_buf; + tef_ring->irq_enable_xfer.len = len; + spi_message_init_with_transfers(&tef_ring->irq_enable_msg, + &tef_ring->irq_enable_xfer, 1); + + /* FIFO increment TEF tail pointer */ + addr = MCP251XFD_REG_TEFCON; + val = MCP251XFD_REG_TEFCON_UINC; + len = mcp251xfd_cmd_prepare_write_reg(priv, &tef_ring->uinc_buf, + addr, val, val); + + for (i = 0; i < ARRAY_SIZE(tef_ring->uinc_xfer); i++) { + xfer = &tef_ring->uinc_xfer[i]; + xfer->tx_buf = &tef_ring->uinc_buf; + xfer->len = len; + xfer->cs_change = 1; + xfer->cs_change_delay.value = 0; + xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + } + + /* "cs_change == 1" on the last transfer results in an active + * chip select after the complete SPI message. This causes the + * controller to interpret the next register access as + * data. Set "cs_change" of the last transfer to "0" to + * properly deactivate the chip select at the end of the + * message. + */ + xfer->cs_change = 0; + + if (priv->tx_coalesce_usecs_irq || priv->tx_obj_num_coalesce_irq) { + val = MCP251XFD_REG_TEFCON_UINC | + MCP251XFD_REG_TEFCON_TEFOVIE | + MCP251XFD_REG_TEFCON_TEFHIE; + + len = mcp251xfd_cmd_prepare_write_reg(priv, + &tef_ring->uinc_irq_disable_buf, + addr, val, val); + xfer->tx_buf = &tef_ring->uinc_irq_disable_buf; + xfer->len = len; + } +} + static void mcp251xfd_tx_ring_init_tx_obj(const struct mcp251xfd_priv *priv, const struct mcp251xfd_tx_ring *ring, @@ -88,84 +155,68 @@ mcp251xfd_tx_ring_init_tx_obj(const struct mcp251xfd_priv *priv, ARRAY_SIZE(tx_obj->xfer)); } -void mcp251xfd_ring_init(struct mcp251xfd_priv *priv) +static void +mcp251xfd_ring_init_tx(struct mcp251xfd_priv *priv, u16 *base, u8 *fifo_nr) { - struct mcp251xfd_tef_ring *tef_ring; struct mcp251xfd_tx_ring *tx_ring; - struct mcp251xfd_rx_ring *rx_ring, *prev_rx_ring = NULL; struct mcp251xfd_tx_obj *tx_obj; - struct spi_transfer *xfer; u32 val; u16 addr; u8 len; - int i, j; + int i; - netdev_reset_queue(priv->ndev); - - /* TEF */ - tef_ring = priv->tef; - tef_ring->head = 0; - tef_ring->tail = 0; - - /* FIFO increment TEF tail pointer */ - addr = MCP251XFD_REG_TEFCON; - val = MCP251XFD_REG_TEFCON_UINC; - len = mcp251xfd_cmd_prepare_write_reg(priv, &tef_ring->uinc_buf, - addr, val, val); - - for (j = 0; j < ARRAY_SIZE(tef_ring->uinc_xfer); j++) { - xfer = &tef_ring->uinc_xfer[j]; - xfer->tx_buf = &tef_ring->uinc_buf; - xfer->len = len; - xfer->cs_change = 1; - xfer->cs_change_delay.value = 0; - xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; - } - - /* "cs_change == 1" on the last transfer results in an active - * chip select after the complete SPI message. This causes the - * controller to interpret the next register access as - * data. Set "cs_change" of the last transfer to "0" to - * properly deactivate the chip select at the end of the - * message. - */ - xfer->cs_change = 0; - - /* TX */ tx_ring = priv->tx; tx_ring->head = 0; tx_ring->tail = 0; - tx_ring->base = mcp251xfd_get_tef_obj_addr(tx_ring->obj_num); + tx_ring->base = *base; + tx_ring->nr = 0; + tx_ring->fifo_nr = *fifo_nr; + + *base = mcp251xfd_get_tx_obj_addr(tx_ring, tx_ring->obj_num); + *fifo_nr += 1; /* FIFO request to send */ - addr = MCP251XFD_REG_FIFOCON(MCP251XFD_TX_FIFO); + addr = MCP251XFD_REG_FIFOCON(tx_ring->fifo_nr); val = MCP251XFD_REG_FIFOCON_TXREQ | MCP251XFD_REG_FIFOCON_UINC; len = mcp251xfd_cmd_prepare_write_reg(priv, &tx_ring->rts_buf, addr, val, val); mcp251xfd_for_each_tx_obj(tx_ring, tx_obj, i) mcp251xfd_tx_ring_init_tx_obj(priv, tx_ring, tx_obj, len, i); +} + +static void +mcp251xfd_ring_init_rx(struct mcp251xfd_priv *priv, u16 *base, u8 *fifo_nr) +{ + struct mcp251xfd_rx_ring *rx_ring; + struct spi_transfer *xfer; + u32 val; + u16 addr; + u8 len; + int i, j; - /* RX */ mcp251xfd_for_each_rx_ring(priv, rx_ring, i) { rx_ring->head = 0; rx_ring->tail = 0; + rx_ring->base = *base; rx_ring->nr = i; - rx_ring->fifo_nr = MCP251XFD_RX_FIFO(i); + rx_ring->fifo_nr = *fifo_nr; - if (!prev_rx_ring) - rx_ring->base = - mcp251xfd_get_tx_obj_addr(tx_ring, - tx_ring->obj_num); - else - rx_ring->base = prev_rx_ring->base + - prev_rx_ring->obj_size * - prev_rx_ring->obj_num; + *base = mcp251xfd_get_rx_obj_addr(rx_ring, rx_ring->obj_num); + *fifo_nr += 1; - prev_rx_ring = rx_ring; + /* FIFO IRQ enable */ + addr = MCP251XFD_REG_FIFOCON(rx_ring->fifo_nr); + val = MCP251XFD_REG_FIFOCON_RXOVIE | + MCP251XFD_REG_FIFOCON_TFNRFNIE; + len = mcp251xfd_cmd_prepare_write_reg(priv, &rx_ring->irq_enable_buf, + addr, val, val); + rx_ring->irq_enable_xfer.tx_buf = &rx_ring->irq_enable_buf; + rx_ring->irq_enable_xfer.len = len; + spi_message_init_with_transfers(&rx_ring->irq_enable_msg, + &rx_ring->irq_enable_xfer, 1); /* FIFO increment RX tail pointer */ - addr = MCP251XFD_REG_FIFOCON(rx_ring->fifo_nr); val = MCP251XFD_REG_FIFOCON_UINC; len = mcp251xfd_cmd_prepare_write_reg(priv, &rx_ring->uinc_buf, addr, val, val); @@ -187,9 +238,149 @@ void mcp251xfd_ring_init(struct mcp251xfd_priv *priv) * the chip select at the end of the message. */ xfer->cs_change = 0; + + /* Use 1st RX-FIFO for IRQ coalescing. If enabled + * (rx_coalesce_usecs_irq or rx_max_coalesce_frames_irq + * is activated), use the last transfer to disable: + * + * - TFNRFNIE (Receive FIFO Not Empty Interrupt) + * + * and enable: + * + * - TFHRFHIE (Receive FIFO Half Full Interrupt) + * - or - + * - TFERFFIE (Receive FIFO Full Interrupt) + * + * depending on rx_max_coalesce_frames_irq. + * + * The RXOVIE (Overflow Interrupt) is always enabled. + */ + if (rx_ring->nr == 0 && (priv->rx_coalesce_usecs_irq || + priv->rx_obj_num_coalesce_irq)) { + val = MCP251XFD_REG_FIFOCON_UINC | + MCP251XFD_REG_FIFOCON_RXOVIE; + + if (priv->rx_obj_num_coalesce_irq == rx_ring->obj_num) + val |= MCP251XFD_REG_FIFOCON_TFERFFIE; + else if (priv->rx_obj_num_coalesce_irq) + val |= MCP251XFD_REG_FIFOCON_TFHRFHIE; + + len = mcp251xfd_cmd_prepare_write_reg(priv, + &rx_ring->uinc_irq_disable_buf, + addr, val, val); + xfer->tx_buf = &rx_ring->uinc_irq_disable_buf; + xfer->len = len; + } } } +int mcp251xfd_ring_init(struct mcp251xfd_priv *priv) +{ + const struct mcp251xfd_rx_ring *rx_ring; + u16 base = 0, ram_used; + u8 fifo_nr = 1; + int i; + + netdev_reset_queue(priv->ndev); + + mcp251xfd_ring_init_tef(priv, &base); + mcp251xfd_ring_init_rx(priv, &base, &fifo_nr); + mcp251xfd_ring_init_tx(priv, &base, &fifo_nr); + + /* mcp251xfd_handle_rxif() will iterate over all RX rings. + * Rings with their corresponding bit set in + * priv->regs_status.rxif are read out. + * + * If the chip is configured for only 1 RX-FIFO, and if there + * is an RX interrupt pending (RXIF in INT register is set), + * it must be the 1st RX-FIFO. + * + * We mark the RXIF of the 1st FIFO as pending here, so that + * we can skip the read of the RXIF register in + * mcp251xfd_read_regs_status() for the 1 RX-FIFO only case. + * + * If we use more than 1 RX-FIFO, this value gets overwritten + * in mcp251xfd_read_regs_status(), so set it unconditionally + * here. + */ + priv->regs_status.rxif = BIT(priv->rx[0]->fifo_nr); + + if (priv->tx_obj_num_coalesce_irq) { + netdev_dbg(priv->ndev, + "FIFO setup: TEF: 0x%03x: %2d*%zu bytes = %4zu bytes (coalesce)\n", + mcp251xfd_get_tef_obj_addr(0), + priv->tx_obj_num_coalesce_irq, + sizeof(struct mcp251xfd_hw_tef_obj), + priv->tx_obj_num_coalesce_irq * + sizeof(struct mcp251xfd_hw_tef_obj)); + + netdev_dbg(priv->ndev, + " 0x%03x: %2d*%zu bytes = %4zu bytes\n", + mcp251xfd_get_tef_obj_addr(priv->tx_obj_num_coalesce_irq), + priv->tx->obj_num - priv->tx_obj_num_coalesce_irq, + sizeof(struct mcp251xfd_hw_tef_obj), + (priv->tx->obj_num - priv->tx_obj_num_coalesce_irq) * + sizeof(struct mcp251xfd_hw_tef_obj)); + } else { + netdev_dbg(priv->ndev, + "FIFO setup: TEF: 0x%03x: %2d*%zu bytes = %4zu bytes\n", + mcp251xfd_get_tef_obj_addr(0), + priv->tx->obj_num, sizeof(struct mcp251xfd_hw_tef_obj), + priv->tx->obj_num * sizeof(struct mcp251xfd_hw_tef_obj)); + } + + mcp251xfd_for_each_rx_ring(priv, rx_ring, i) { + if (rx_ring->nr == 0 && priv->rx_obj_num_coalesce_irq) { + netdev_dbg(priv->ndev, + "FIFO setup: RX-%u: FIFO %u/0x%03x: %2u*%u bytes = %4u bytes (coalesce)\n", + rx_ring->nr, rx_ring->fifo_nr, + mcp251xfd_get_rx_obj_addr(rx_ring, 0), + priv->rx_obj_num_coalesce_irq, rx_ring->obj_size, + priv->rx_obj_num_coalesce_irq * rx_ring->obj_size); + + if (priv->rx_obj_num_coalesce_irq == MCP251XFD_FIFO_DEPTH) + continue; + + netdev_dbg(priv->ndev, + " 0x%03x: %2u*%u bytes = %4u bytes\n", + mcp251xfd_get_rx_obj_addr(rx_ring, + priv->rx_obj_num_coalesce_irq), + rx_ring->obj_num - priv->rx_obj_num_coalesce_irq, + rx_ring->obj_size, + (rx_ring->obj_num - priv->rx_obj_num_coalesce_irq) * + rx_ring->obj_size); + } else { + netdev_dbg(priv->ndev, + "FIFO setup: RX-%u: FIFO %u/0x%03x: %2u*%u bytes = %4u bytes\n", + rx_ring->nr, rx_ring->fifo_nr, + mcp251xfd_get_rx_obj_addr(rx_ring, 0), + rx_ring->obj_num, rx_ring->obj_size, + rx_ring->obj_num * rx_ring->obj_size); + } + } + + netdev_dbg(priv->ndev, + "FIFO setup: TX: FIFO %u/0x%03x: %2u*%u bytes = %4u bytes\n", + priv->tx->fifo_nr, + mcp251xfd_get_tx_obj_addr(priv->tx, 0), + priv->tx->obj_num, priv->tx->obj_size, + priv->tx->obj_num * priv->tx->obj_size); + + netdev_dbg(priv->ndev, + "FIFO setup: free: %4d bytes\n", + MCP251XFD_RAM_SIZE - (base - MCP251XFD_RAM_START)); + + ram_used = base - MCP251XFD_RAM_START; + if (ram_used > MCP251XFD_RAM_SIZE) { + netdev_err(priv->ndev, + "Error during ring configuration, using more RAM (%u bytes) than available (%u bytes).\n", + ram_used, MCP251XFD_RAM_SIZE); + return -ENOMEM; + } + + return 0; +} + void mcp251xfd_ring_free(struct mcp251xfd_priv *priv) { int i; @@ -200,40 +391,103 @@ void mcp251xfd_ring_free(struct mcp251xfd_priv *priv) } } +static enum hrtimer_restart mcp251xfd_rx_irq_timer(struct hrtimer *t) +{ + struct mcp251xfd_priv *priv = container_of(t, struct mcp251xfd_priv, + rx_irq_timer); + struct mcp251xfd_rx_ring *ring = priv->rx[0]; + + if (test_bit(MCP251XFD_FLAGS_DOWN, priv->flags)) + return HRTIMER_NORESTART; + + spi_async(priv->spi, &ring->irq_enable_msg); + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart mcp251xfd_tx_irq_timer(struct hrtimer *t) +{ + struct mcp251xfd_priv *priv = container_of(t, struct mcp251xfd_priv, + tx_irq_timer); + struct mcp251xfd_tef_ring *ring = priv->tef; + + if (test_bit(MCP251XFD_FLAGS_DOWN, priv->flags)) + return HRTIMER_NORESTART; + + spi_async(priv->spi, &ring->irq_enable_msg); + + return HRTIMER_NORESTART; +} + +const struct can_ram_config mcp251xfd_ram_config = { + .rx = { + .size[CAN_RAM_MODE_CAN] = sizeof(struct mcp251xfd_hw_rx_obj_can), + .size[CAN_RAM_MODE_CANFD] = sizeof(struct mcp251xfd_hw_rx_obj_canfd), + .min = MCP251XFD_RX_OBJ_NUM_MIN, + .max = MCP251XFD_RX_OBJ_NUM_MAX, + .def[CAN_RAM_MODE_CAN] = CAN_RAM_NUM_MAX, + .def[CAN_RAM_MODE_CANFD] = CAN_RAM_NUM_MAX, + .fifo_num = MCP251XFD_FIFO_RX_NUM, + .fifo_depth_min = MCP251XFD_RX_FIFO_DEPTH_MIN, + .fifo_depth_coalesce_min = MCP251XFD_RX_FIFO_DEPTH_COALESCE_MIN, + }, + .tx = { + .size[CAN_RAM_MODE_CAN] = sizeof(struct mcp251xfd_hw_tef_obj) + + sizeof(struct mcp251xfd_hw_tx_obj_can), + .size[CAN_RAM_MODE_CANFD] = sizeof(struct mcp251xfd_hw_tef_obj) + + sizeof(struct mcp251xfd_hw_tx_obj_canfd), + .min = MCP251XFD_TX_OBJ_NUM_MIN, + .max = MCP251XFD_TX_OBJ_NUM_MAX, + .def[CAN_RAM_MODE_CAN] = MCP251XFD_TX_OBJ_NUM_CAN_DEFAULT, + .def[CAN_RAM_MODE_CANFD] = MCP251XFD_TX_OBJ_NUM_CANFD_DEFAULT, + .fifo_num = MCP251XFD_FIFO_TX_NUM, + .fifo_depth_min = MCP251XFD_TX_FIFO_DEPTH_MIN, + .fifo_depth_coalesce_min = MCP251XFD_TX_FIFO_DEPTH_COALESCE_MIN, + }, + .size = MCP251XFD_RAM_SIZE, + .fifo_depth = MCP251XFD_FIFO_DEPTH, +}; + int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv) { - struct mcp251xfd_tx_ring *tx_ring; + const bool fd_mode = mcp251xfd_is_fd_mode(priv); + struct mcp251xfd_tx_ring *tx_ring = priv->tx; struct mcp251xfd_rx_ring *rx_ring; - int tef_obj_size, tx_obj_size, rx_obj_size; - int tx_obj_num; - int ram_free, i; + u8 tx_obj_size, rx_obj_size; + u8 rem, i; - tef_obj_size = sizeof(struct mcp251xfd_hw_tef_obj); - if (mcp251xfd_is_fd_mode(priv)) { - tx_obj_num = MCP251XFD_TX_OBJ_NUM_CANFD; - tx_obj_size = sizeof(struct mcp251xfd_hw_tx_obj_canfd); - rx_obj_size = sizeof(struct mcp251xfd_hw_rx_obj_canfd); - } else { - tx_obj_num = MCP251XFD_TX_OBJ_NUM_CAN; - tx_obj_size = sizeof(struct mcp251xfd_hw_tx_obj_can); - rx_obj_size = sizeof(struct mcp251xfd_hw_rx_obj_can); + /* switching from CAN-2.0 to CAN-FD mode or vice versa */ + if (fd_mode != test_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags)) { + struct can_ram_layout layout; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, fd_mode); + priv->rx_obj_num = layout.default_rx; + tx_ring->obj_num = layout.default_tx; + } + + if (fd_mode) { + tx_obj_size = sizeof(struct mcp251xfd_hw_tx_obj_canfd); + rx_obj_size = sizeof(struct mcp251xfd_hw_rx_obj_canfd); + set_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags); + } else { + tx_obj_size = sizeof(struct mcp251xfd_hw_tx_obj_can); + rx_obj_size = sizeof(struct mcp251xfd_hw_rx_obj_can); + clear_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags); } - tx_ring = priv->tx; - tx_ring->obj_num = tx_obj_num; tx_ring->obj_size = tx_obj_size; - ram_free = MCP251XFD_RAM_SIZE - tx_obj_num * - (tef_obj_size + tx_obj_size); + rem = priv->rx_obj_num; + for (i = 0; i < ARRAY_SIZE(priv->rx) && rem; i++) { + u8 rx_obj_num; - for (i = 0; - i < ARRAY_SIZE(priv->rx) && ram_free >= rx_obj_size; - i++) { - int rx_obj_num; - - rx_obj_num = ram_free / rx_obj_size; - rx_obj_num = min(1 << (fls(rx_obj_num) - 1), - MCP251XFD_RX_OBJ_NUM_MAX); + if (i == 0 && priv->rx_obj_num_coalesce_irq) + rx_obj_num = min_t(u8, priv->rx_obj_num_coalesce_irq * 2, + MCP251XFD_FIFO_DEPTH); + else + rx_obj_num = min_t(u8, rounddown_pow_of_two(rem), + MCP251XFD_FIFO_DEPTH); + rem -= rx_obj_num; rx_ring = kzalloc(sizeof(*rx_ring) + rx_obj_size * rx_obj_num, GFP_KERNEL); @@ -241,29 +495,18 @@ int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv) mcp251xfd_ring_free(priv); return -ENOMEM; } + rx_ring->obj_num = rx_obj_num; rx_ring->obj_size = rx_obj_size; priv->rx[i] = rx_ring; - - ram_free -= rx_ring->obj_num * rx_ring->obj_size; } priv->rx_ring_num = i; - netdev_dbg(priv->ndev, - "FIFO setup: TEF: %d*%d bytes = %d bytes, TX: %d*%d bytes = %d bytes\n", - tx_obj_num, tef_obj_size, tef_obj_size * tx_obj_num, - tx_obj_num, tx_obj_size, tx_obj_size * tx_obj_num); + hrtimer_init(&priv->rx_irq_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + priv->rx_irq_timer.function = mcp251xfd_rx_irq_timer; - mcp251xfd_for_each_rx_ring(priv, rx_ring, i) { - netdev_dbg(priv->ndev, - "FIFO setup: RX-%d: %d*%d bytes = %d bytes\n", - i, rx_ring->obj_num, rx_ring->obj_size, - rx_ring->obj_size * rx_ring->obj_num); - } - - netdev_dbg(priv->ndev, - "FIFO setup: free: %d bytes\n", - ram_free); + hrtimer_init(&priv->tx_irq_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + priv->tx_irq_timer.function = mcp251xfd_tx_irq_timer; return 0; } diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c index 63f2526464b3..d09f7fbf2ba7 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c @@ -19,7 +19,7 @@ static inline int mcp251xfd_rx_head_get_from_chip(const struct mcp251xfd_priv *priv, const struct mcp251xfd_rx_ring *ring, - u8 *rx_head) + u8 *rx_head, bool *fifo_empty) { u32 fifo_sta; int err; @@ -30,6 +30,7 @@ mcp251xfd_rx_head_get_from_chip(const struct mcp251xfd_priv *priv, return err; *rx_head = FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, fifo_sta); + *fifo_empty = !(fifo_sta & MCP251XFD_REG_FIFOSTA_TFNRFNIF); return 0; } @@ -84,10 +85,12 @@ mcp251xfd_rx_ring_update(const struct mcp251xfd_priv *priv, { u32 new_head; u8 chip_rx_head; + bool fifo_empty; int err; - err = mcp251xfd_rx_head_get_from_chip(priv, ring, &chip_rx_head); - if (err) + err = mcp251xfd_rx_head_get_from_chip(priv, ring, &chip_rx_head, + &fifo_empty); + if (err || fifo_empty) return err; /* chip_rx_head, is the next RX-Object filled by the HW. @@ -251,10 +254,23 @@ int mcp251xfd_handle_rxif(struct mcp251xfd_priv *priv) int err, n; mcp251xfd_for_each_rx_ring(priv, ring, n) { + /* - if RX IRQ coalescing is active always handle ring 0 + * - only handle rings if RX IRQ is active + */ + if ((ring->nr > 0 || !priv->rx_obj_num_coalesce_irq) && + !(priv->regs_status.rxif & BIT(ring->fifo_nr))) + continue; + err = mcp251xfd_handle_rxif_ring(priv, ring); if (err) return err; } + if (priv->rx_coalesce_usecs_irq) + hrtimer_start(&priv->rx_irq_timer, + ns_to_ktime(priv->rx_coalesce_usecs_irq * + NSEC_PER_USEC), + HRTIMER_MODE_REL); + return 0; } diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c index 406166005b99..237617b0c125 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c @@ -256,5 +256,11 @@ int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv) netif_wake_queue(priv->ndev); } + if (priv->tx_coalesce_usecs_irq) + hrtimer_start(&priv->tx_irq_timer, + ns_to_ktime(priv->tx_coalesce_usecs_irq * + NSEC_PER_USEC), + HRTIMER_MODE_REL); + return 0; } diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h index f551c900803e..9cb6b5ad8dda 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h @@ -2,8 +2,8 @@ * * mcp251xfd - Microchip MCP251xFD Family CAN controller driver * - * Copyright (c) 2019 Pengutronix, - * Marc Kleine-Budde + * Copyright (c) 2019, 2020, 2021 Pengutronix, + * Marc Kleine-Budde * Copyright (c) 2019 Martin Sperl */ @@ -367,25 +367,6 @@ #define MCP251XFD_REG_DEVID_ID_MASK GENMASK(7, 4) #define MCP251XFD_REG_DEVID_REV_MASK GENMASK(3, 0) -/* number of TX FIFO objects, depending on CAN mode - * - * FIFO setup: tef: 8*12 bytes = 96 bytes, tx: 8*16 bytes = 128 bytes - * FIFO setup: tef: 4*12 bytes = 48 bytes, tx: 4*72 bytes = 288 bytes - */ -#define MCP251XFD_RX_OBJ_NUM_MAX 32 -#define MCP251XFD_TX_OBJ_NUM_CAN 8 -#define MCP251XFD_TX_OBJ_NUM_CANFD 4 - -#if MCP251XFD_TX_OBJ_NUM_CAN > MCP251XFD_TX_OBJ_NUM_CANFD -#define MCP251XFD_TX_OBJ_NUM_MAX MCP251XFD_TX_OBJ_NUM_CAN -#else -#define MCP251XFD_TX_OBJ_NUM_MAX MCP251XFD_TX_OBJ_NUM_CANFD -#endif - -#define MCP251XFD_NAPI_WEIGHT 32 -#define MCP251XFD_TX_FIFO 1 -#define MCP251XFD_RX_FIFO(x) (MCP251XFD_TX_FIFO + 1 + (x)) - /* SPI commands */ #define MCP251XFD_SPI_INSTRUCTION_RESET 0x0000 #define MCP251XFD_SPI_INSTRUCTION_WRITE 0x2000 @@ -406,12 +387,38 @@ static_assert(MCP251XFD_TIMESTAMP_WORK_DELAY_SEC < #define MCP251XFD_OSC_STAB_TIMEOUT_US (10 * MCP251XFD_OSC_STAB_SLEEP_US) #define MCP251XFD_POLL_SLEEP_US (10) #define MCP251XFD_POLL_TIMEOUT_US (USEC_PER_MSEC) + +/* Misc */ +#define MCP251XFD_NAPI_WEIGHT 32 #define MCP251XFD_SOFTRESET_RETRIES_MAX 3 #define MCP251XFD_READ_CRC_RETRIES_MAX 3 #define MCP251XFD_ECC_CNT_MAX 2 #define MCP251XFD_SANITIZE_SPI 1 #define MCP251XFD_SANITIZE_CAN 1 +/* FIFO and Ring */ +#define MCP251XFD_FIFO_TEF_NUM 1U +#define MCP251XFD_FIFO_RX_NUM 3U +#define MCP251XFD_FIFO_TX_NUM 1U + +#define MCP251XFD_FIFO_DEPTH 32U + +#define MCP251XFD_RX_OBJ_NUM_MIN 16U +#define MCP251XFD_RX_OBJ_NUM_MAX (MCP251XFD_FIFO_RX_NUM * MCP251XFD_FIFO_DEPTH) +#define MCP251XFD_RX_FIFO_DEPTH_MIN 4U +#define MCP251XFD_RX_FIFO_DEPTH_COALESCE_MIN 8U + +#define MCP251XFD_TX_OBJ_NUM_MIN 2U +#define MCP251XFD_TX_OBJ_NUM_MAX 16U +#define MCP251XFD_TX_OBJ_NUM_CAN_DEFAULT 8U +#define MCP251XFD_TX_OBJ_NUM_CANFD_DEFAULT 4U +#define MCP251XFD_TX_FIFO_DEPTH_MIN 2U +#define MCP251XFD_TX_FIFO_DEPTH_COALESCE_MIN 2U + +static_assert(MCP251XFD_FIFO_TEF_NUM == 1U); +static_assert(MCP251XFD_FIFO_TEF_NUM == MCP251XFD_FIFO_TX_NUM); +static_assert(MCP251XFD_FIFO_RX_NUM <= 4U); + /* Silence TX MAB overflow warnings */ #define MCP251XFD_QUIRK_MAB_NO_WARN BIT(0) /* Use CRC to access registers */ @@ -512,7 +519,12 @@ struct mcp251xfd_tef_ring { /* u8 obj_num equals tx_ring->obj_num */ /* u8 obj_size equals sizeof(struct mcp251xfd_hw_tef_obj) */ + union mcp251xfd_write_reg_buf irq_enable_buf; + struct spi_transfer irq_enable_xfer; + struct spi_message irq_enable_msg; + union mcp251xfd_write_reg_buf uinc_buf; + union mcp251xfd_write_reg_buf uinc_irq_disable_buf; struct spi_transfer uinc_xfer[MCP251XFD_TX_OBJ_NUM_MAX]; }; @@ -521,6 +533,8 @@ struct mcp251xfd_tx_ring { unsigned int tail; u16 base; + u8 nr; + u8 fifo_nr; u8 obj_num; u8 obj_size; @@ -538,8 +552,13 @@ struct mcp251xfd_rx_ring { u8 obj_num; u8 obj_size; + union mcp251xfd_write_reg_buf irq_enable_buf; + struct spi_transfer irq_enable_xfer; + struct spi_message irq_enable_msg; + union mcp251xfd_write_reg_buf uinc_buf; - struct spi_transfer uinc_xfer[MCP251XFD_RX_OBJ_NUM_MAX]; + union mcp251xfd_write_reg_buf uinc_irq_disable_buf; + struct spi_transfer uinc_xfer[MCP251XFD_FIFO_DEPTH]; struct mcp251xfd_hw_rx_obj_canfd obj[]; }; @@ -561,6 +580,7 @@ struct mcp251xfd_ecc { struct mcp251xfd_regs_status { u32 intf; + u32 rxif; }; enum mcp251xfd_model { @@ -574,6 +594,13 @@ struct mcp251xfd_devtype_data { u32 quirks; }; +enum mcp251xfd_flags { + MCP251XFD_FLAGS_DOWN, + MCP251XFD_FLAGS_FD_MODE, + + __MCP251XFD_FLAGS_SIZE__ +}; + struct mcp251xfd_priv { struct can_priv can; struct can_rx_offload offload; @@ -592,12 +619,24 @@ struct mcp251xfd_priv { struct spi_device *spi; u32 spi_max_speed_hz_orig; + u32 spi_max_speed_hz_fast; + u32 spi_max_speed_hz_slow; - struct mcp251xfd_tef_ring tef[1]; - struct mcp251xfd_tx_ring tx[1]; - struct mcp251xfd_rx_ring *rx[1]; + struct mcp251xfd_tef_ring tef[MCP251XFD_FIFO_TEF_NUM]; + struct mcp251xfd_rx_ring *rx[MCP251XFD_FIFO_RX_NUM]; + struct mcp251xfd_tx_ring tx[MCP251XFD_FIFO_TX_NUM]; + + DECLARE_BITMAP(flags, __MCP251XFD_FLAGS_SIZE__); u8 rx_ring_num; + u8 rx_obj_num; + u8 rx_obj_num_coalesce_irq; + u8 tx_obj_num_coalesce_irq; + + u32 rx_coalesce_usecs_irq; + u32 tx_coalesce_usecs_irq; + struct hrtimer rx_irq_timer; + struct hrtimer tx_irq_timer; struct mcp251xfd_ecc ecc; struct mcp251xfd_regs_status regs_status; @@ -608,6 +647,7 @@ struct mcp251xfd_priv { struct gpio_desc *rx_int; struct clk *clk; + bool pll_enable; struct regulator *reg_vdd; struct regulator *reg_xceiver; @@ -776,7 +816,7 @@ mcp251xfd_tx_tail_get_from_chip(const struct mcp251xfd_priv *priv, int err; err = regmap_read(priv->map_reg, - MCP251XFD_REG_FIFOSTA(MCP251XFD_TX_FIFO), + MCP251XFD_REG_FIFOSTA(priv->tx->fifo_nr), &fifo_sta); if (err) return err; @@ -878,8 +918,10 @@ int mcp251xfd_chip_fifo_init(const struct mcp251xfd_priv *priv); u16 mcp251xfd_crc16_compute2(const void *cmd, size_t cmd_size, const void *data, size_t data_size); u16 mcp251xfd_crc16_compute(const void *data, size_t data_size); +void mcp251xfd_ethtool_init(struct mcp251xfd_priv *priv); int mcp251xfd_regmap_init(struct mcp251xfd_priv *priv); -void mcp251xfd_ring_init(struct mcp251xfd_priv *priv); +extern const struct can_ram_config mcp251xfd_ram_config; +int mcp251xfd_ring_init(struct mcp251xfd_priv *priv); void mcp251xfd_ring_free(struct mcp251xfd_priv *priv); int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv); int mcp251xfd_handle_rxif(struct mcp251xfd_priv *priv); diff --git a/drivers/net/can/usb/etas_es58x/es58x_fd.c b/drivers/net/can/usb/etas_es58x/es58x_fd.c index ec87126e1a7d..c97ffa71fd75 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_fd.c +++ b/drivers/net/can/usb/etas_es58x/es58x_fd.c @@ -69,7 +69,8 @@ static int es58x_fd_echo_msg(struct net_device *netdev, int i, num_element; u32 rcv_packet_idx; - const u32 mask = GENMASK(31, sizeof(echo_msg->packet_idx) * 8); + const u32 mask = GENMASK(BITS_PER_TYPE(mask) - 1, + BITS_PER_TYPE(echo_msg->packet_idx)); num_element = es58x_msg_num_element(es58x_dev->dev, es58x_fd_urb_cmd->echo_msg, @@ -172,12 +173,11 @@ static int es58x_fd_rx_event_msg(struct net_device *netdev, const struct es58x_fd_rx_event_msg *rx_event_msg; int ret; + rx_event_msg = &es58x_fd_urb_cmd->rx_event_msg; ret = es58x_check_msg_len(es58x_dev->dev, *rx_event_msg, msg_len); if (ret) return ret; - rx_event_msg = &es58x_fd_urb_cmd->rx_event_msg; - return es58x_rx_err_msg(netdev, rx_event_msg->error_code, rx_event_msg->event_code, get_unaligned_le64(&rx_event_msg->timestamp)); diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index d35749fad1ef..67408e316062 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -9,11 +9,12 @@ * Many thanks to all socketcan devs! */ +#include #include #include -#include #include #include +#include #include #include @@ -21,14 +22,20 @@ #include /* Device specific constants */ -#define USB_GSUSB_1_VENDOR_ID 0x1d50 -#define USB_GSUSB_1_PRODUCT_ID 0x606f +#define USB_GSUSB_1_VENDOR_ID 0x1d50 +#define USB_GSUSB_1_PRODUCT_ID 0x606f -#define USB_CANDLELIGHT_VENDOR_ID 0x1209 +#define USB_CANDLELIGHT_VENDOR_ID 0x1209 #define USB_CANDLELIGHT_PRODUCT_ID 0x2323 -#define GSUSB_ENDPOINT_IN 1 -#define GSUSB_ENDPOINT_OUT 2 +#define USB_CES_CANEXT_FD_VENDOR_ID 0x1cd2 +#define USB_CES_CANEXT_FD_PRODUCT_ID 0x606f + +#define USB_ABE_CANDEBUGGER_FD_VENDOR_ID 0x16d0 +#define USB_ABE_CANDEBUGGER_FD_PRODUCT_ID 0x10b8 + +#define GSUSB_ENDPOINT_IN 1 +#define GSUSB_ENDPOINT_OUT 2 /* Device specific constants */ enum gs_usb_breq { @@ -40,6 +47,11 @@ enum gs_usb_breq { GS_USB_BREQ_DEVICE_CONFIG, GS_USB_BREQ_TIMESTAMP, GS_USB_BREQ_IDENTIFY, + GS_USB_BREQ_GET_USER_ID, + GS_USB_BREQ_QUIRK_CANTACT_PRO_DATA_BITTIMING = GS_USB_BREQ_GET_USER_ID, + GS_USB_BREQ_SET_USER_ID, + GS_USB_BREQ_DATA_BITTIMING, + GS_USB_BREQ_BT_CONST_EXT, }; enum gs_can_mode { @@ -87,11 +99,18 @@ struct gs_device_config { __le32 hw_version; } __packed; -#define GS_CAN_MODE_NORMAL 0 -#define GS_CAN_MODE_LISTEN_ONLY BIT(0) -#define GS_CAN_MODE_LOOP_BACK BIT(1) -#define GS_CAN_MODE_TRIPLE_SAMPLE BIT(2) -#define GS_CAN_MODE_ONE_SHOT BIT(3) +#define GS_CAN_MODE_NORMAL 0 +#define GS_CAN_MODE_LISTEN_ONLY BIT(0) +#define GS_CAN_MODE_LOOP_BACK BIT(1) +#define GS_CAN_MODE_TRIPLE_SAMPLE BIT(2) +#define GS_CAN_MODE_ONE_SHOT BIT(3) +#define GS_CAN_MODE_HW_TIMESTAMP BIT(4) +/* GS_CAN_FEATURE_IDENTIFY BIT(5) */ +/* GS_CAN_FEATURE_USER_ID BIT(6) */ +#define GS_CAN_MODE_PAD_PKTS_TO_MAX_PKT_SIZE BIT(7) +#define GS_CAN_MODE_FD BIT(8) +/* GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX BIT(9) */ +/* GS_CAN_FEATURE_BT_CONST_EXT BIT(10) */ struct gs_device_mode { __le32 mode; @@ -116,12 +135,25 @@ struct gs_identify_mode { __le32 mode; } __packed; -#define GS_CAN_FEATURE_LISTEN_ONLY BIT(0) -#define GS_CAN_FEATURE_LOOP_BACK BIT(1) -#define GS_CAN_FEATURE_TRIPLE_SAMPLE BIT(2) -#define GS_CAN_FEATURE_ONE_SHOT BIT(3) -#define GS_CAN_FEATURE_HW_TIMESTAMP BIT(4) -#define GS_CAN_FEATURE_IDENTIFY BIT(5) +#define GS_CAN_FEATURE_LISTEN_ONLY BIT(0) +#define GS_CAN_FEATURE_LOOP_BACK BIT(1) +#define GS_CAN_FEATURE_TRIPLE_SAMPLE BIT(2) +#define GS_CAN_FEATURE_ONE_SHOT BIT(3) +#define GS_CAN_FEATURE_HW_TIMESTAMP BIT(4) +#define GS_CAN_FEATURE_IDENTIFY BIT(5) +#define GS_CAN_FEATURE_USER_ID BIT(6) +#define GS_CAN_FEATURE_PAD_PKTS_TO_MAX_PKT_SIZE BIT(7) +#define GS_CAN_FEATURE_FD BIT(8) +#define GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX BIT(9) +#define GS_CAN_FEATURE_BT_CONST_EXT BIT(10) +#define GS_CAN_FEATURE_MASK GENMASK(10, 0) + +/* internal quirks - keep in GS_CAN_FEATURE space for now */ + +/* CANtact Pro original firmware: + * BREQ DATA_BITTIMING overlaps with GET_USER_ID + */ +#define GS_CAN_FEATURE_QUIRK_BREQ_CANTACT_PRO BIT(31) struct gs_device_bt_const { __le32 feature; @@ -136,7 +168,50 @@ struct gs_device_bt_const { __le32 brp_inc; } __packed; -#define GS_CAN_FLAG_OVERFLOW 1 +struct gs_device_bt_const_extended { + __le32 feature; + __le32 fclk_can; + __le32 tseg1_min; + __le32 tseg1_max; + __le32 tseg2_min; + __le32 tseg2_max; + __le32 sjw_max; + __le32 brp_min; + __le32 brp_max; + __le32 brp_inc; + + __le32 dtseg1_min; + __le32 dtseg1_max; + __le32 dtseg2_min; + __le32 dtseg2_max; + __le32 dsjw_max; + __le32 dbrp_min; + __le32 dbrp_max; + __le32 dbrp_inc; +} __packed; + +#define GS_CAN_FLAG_OVERFLOW BIT(0) +#define GS_CAN_FLAG_FD BIT(1) +#define GS_CAN_FLAG_BRS BIT(2) +#define GS_CAN_FLAG_ESI BIT(3) + +struct classic_can { + u8 data[8]; +} __packed; + +struct classic_can_quirk { + u8 data[8]; + u8 quirk; +} __packed; + +struct canfd { + u8 data[64]; +} __packed; + +struct canfd_quirk { + u8 data[64]; + u8 quirk; +} __packed; struct gs_host_frame { u32 echo_id; @@ -147,7 +222,12 @@ struct gs_host_frame { u8 flags; u8 reserved; - u8 data[8]; + union { + DECLARE_FLEX_ARRAY(struct classic_can, classic_can); + DECLARE_FLEX_ARRAY(struct classic_can_quirk, classic_can_quirk); + DECLARE_FLEX_ARRAY(struct canfd, canfd); + DECLARE_FLEX_ARRAY(struct canfd_quirk, canfd_quirk); + }; } __packed; /* The GS USB devices make use of the same flags and masks as in * linux/can.h and linux/can/error.h, and no additional mapping is necessary. @@ -158,9 +238,9 @@ struct gs_host_frame { /* Only launch a max of GS_MAX_RX_URBS usb requests at a time. */ #define GS_MAX_RX_URBS 30 /* Maximum number of interfaces the driver supports per device. - * Current hardware only supports 2 interfaces. The future may vary. + * Current hardware only supports 3 interfaces. The future may vary. */ -#define GS_MAX_INTF 2 +#define GS_MAX_INTF 3 struct gs_tx_context { struct gs_can *dev; @@ -176,9 +256,12 @@ struct gs_can { struct usb_device *udev; struct usb_interface *iface; - struct can_bittiming_const bt_const; + struct can_bittiming_const bt_const, data_bt_const; unsigned int channel; /* channel number */ + u32 feature; + unsigned int hf_size_tx; + /* This lock prevents a race condition between xmit and receive. */ spinlock_t tx_ctx_lock; struct gs_tx_context tx_context[GS_MAX_TX_URBS]; @@ -192,6 +275,7 @@ struct gs_usb { struct gs_can *canch[GS_MAX_INTF]; struct usb_anchor rx_submitted; struct usb_device *udev; + unsigned int hf_size_rx; u8 active_channels; }; @@ -258,11 +342,7 @@ static int gs_cmd_reset(struct gs_can *gsdev) usb_sndctrlpipe(interface_to_usbdev(intf), 0), GS_USB_BREQ_MODE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - gsdev->channel, - 0, - dm, - sizeof(*dm), - 1000); + gsdev->channel, 0, dm, sizeof(*dm), 1000); kfree(dm); @@ -304,6 +384,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) struct gs_host_frame *hf = urb->transfer_buffer; struct gs_tx_context *txc; struct can_frame *cf; + struct canfd_frame *cfd; struct sk_buff *skb; BUG_ON(!usbcan); @@ -332,18 +413,33 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) return; if (hf->echo_id == -1) { /* normal rx */ - skb = alloc_can_skb(dev->netdev, &cf); - if (!skb) - return; + if (hf->flags & GS_CAN_FLAG_FD) { + skb = alloc_canfd_skb(dev->netdev, &cfd); + if (!skb) + return; - cf->can_id = le32_to_cpu(hf->can_id); + cfd->can_id = le32_to_cpu(hf->can_id); + cfd->len = can_fd_dlc2len(hf->can_dlc); + if (hf->flags & GS_CAN_FLAG_BRS) + cfd->flags |= CANFD_BRS; + if (hf->flags & GS_CAN_FLAG_ESI) + cfd->flags |= CANFD_ESI; - can_frame_set_cc_len(cf, hf->can_dlc, dev->can.ctrlmode); - memcpy(cf->data, hf->data, 8); + memcpy(cfd->data, hf->canfd->data, cfd->len); + } else { + skb = alloc_can_skb(dev->netdev, &cf); + if (!skb) + return; - /* ERROR frames tell us information about the controller */ - if (le32_to_cpu(hf->can_id) & CAN_ERR_FLAG) - gs_update_state(dev, cf); + cf->can_id = le32_to_cpu(hf->can_id); + can_frame_set_cc_len(cf, hf->can_dlc, dev->can.ctrlmode); + + memcpy(cf->data, hf->classic_can->data, 8); + + /* ERROR frames tell us information about the controller */ + if (le32_to_cpu(hf->can_id) & CAN_ERR_FLAG) + gs_update_state(dev, cf); + } netdev->stats.rx_packets++; netdev->stats.rx_bytes += hf->can_dlc; @@ -392,14 +488,10 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) } resubmit_urb: - usb_fill_bulk_urb(urb, - usbcan->udev, + usb_fill_bulk_urb(urb, usbcan->udev, usb_rcvbulkpipe(usbcan->udev, GSUSB_ENDPOINT_IN), - hf, - sizeof(struct gs_host_frame), - gs_usb_receive_bulk_callback, - usbcan - ); + hf, dev->parent->hf_size_rx, + gs_usb_receive_bulk_callback, usbcan); rc = usb_submit_urb(urb, GFP_ATOMIC); @@ -436,11 +528,7 @@ static int gs_usb_set_bittiming(struct net_device *netdev) usb_sndctrlpipe(interface_to_usbdev(intf), 0), GS_USB_BREQ_BITTIMING, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - dev->channel, - 0, - dbt, - sizeof(*dbt), - 1000); + dev->channel, 0, dbt, sizeof(*dbt), 1000); kfree(dbt); @@ -451,6 +539,44 @@ static int gs_usb_set_bittiming(struct net_device *netdev) return (rc > 0) ? 0 : rc; } +static int gs_usb_set_data_bittiming(struct net_device *netdev) +{ + struct gs_can *dev = netdev_priv(netdev); + struct can_bittiming *bt = &dev->can.data_bittiming; + struct usb_interface *intf = dev->iface; + struct gs_device_bittiming *dbt; + u8 request = GS_USB_BREQ_DATA_BITTIMING; + int rc; + + dbt = kmalloc(sizeof(*dbt), GFP_KERNEL); + if (!dbt) + return -ENOMEM; + + dbt->prop_seg = cpu_to_le32(bt->prop_seg); + dbt->phase_seg1 = cpu_to_le32(bt->phase_seg1); + dbt->phase_seg2 = cpu_to_le32(bt->phase_seg2); + dbt->sjw = cpu_to_le32(bt->sjw); + dbt->brp = cpu_to_le32(bt->brp); + + if (dev->feature & GS_CAN_FEATURE_QUIRK_BREQ_CANTACT_PRO) + request = GS_USB_BREQ_QUIRK_CANTACT_PRO_DATA_BITTIMING; + + /* request bit timings */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + dev->channel, 0, dbt, sizeof(*dbt), 1000); + + kfree(dbt); + + if (rc < 0) + dev_err(netdev->dev.parent, + "Couldn't set data bittimings (err=%d)", rc); + + return (rc > 0) ? 0 : rc; +} + static void gs_usb_xmit_callback(struct urb *urb) { struct gs_tx_context *txc = urb->context; @@ -460,10 +586,8 @@ static void gs_usb_xmit_callback(struct urb *urb) if (urb->status) netdev_info(netdev, "usb xmit fail %u\n", txc->echo_id); - usb_free_coherent(urb->dev, - urb->transfer_buffer_length, - urb->transfer_buffer, - urb->transfer_dma); + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); } static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, @@ -474,6 +598,7 @@ static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, struct urb *urb; struct gs_host_frame *hf; struct can_frame *cf; + struct canfd_frame *cfd; int rc; unsigned int idx; struct gs_tx_context *txc; @@ -491,7 +616,7 @@ static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, if (!urb) goto nomem_urb; - hf = usb_alloc_coherent(dev->udev, sizeof(*hf), GFP_ATOMIC, + hf = usb_alloc_coherent(dev->udev, dev->hf_size_tx, GFP_ATOMIC, &urb->transfer_dma); if (!hf) { netdev_err(netdev, "No memory left for USB buffer\n"); @@ -510,19 +635,31 @@ static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, hf->flags = 0; hf->reserved = 0; - cf = (struct can_frame *)skb->data; + if (can_is_canfd_skb(skb)) { + cfd = (struct canfd_frame *)skb->data; - hf->can_id = cpu_to_le32(cf->can_id); - hf->can_dlc = can_get_cc_dlc(cf, dev->can.ctrlmode); + hf->can_id = cpu_to_le32(cfd->can_id); + hf->can_dlc = can_fd_len2dlc(cfd->len); + hf->flags |= GS_CAN_FLAG_FD; + if (cfd->flags & CANFD_BRS) + hf->flags |= GS_CAN_FLAG_BRS; + if (cfd->flags & CANFD_ESI) + hf->flags |= GS_CAN_FLAG_ESI; - memcpy(hf->data, cf->data, cf->len); + memcpy(hf->canfd->data, cfd->data, cfd->len); + } else { + cf = (struct can_frame *)skb->data; + + hf->can_id = cpu_to_le32(cf->can_id); + hf->can_dlc = can_get_cc_dlc(cf, dev->can.ctrlmode); + + memcpy(hf->classic_can->data, cf->data, cf->len); + } usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, GSUSB_ENDPOINT_OUT), - hf, - sizeof(*hf), - gs_usb_xmit_callback, - txc); + hf, dev->hf_size_tx, + gs_usb_xmit_callback, txc); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_anchor_urb(urb, &dev->tx_submitted); @@ -539,10 +676,8 @@ static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, gs_free_tx_context(txc); usb_unanchor_urb(urb); - usb_free_coherent(dev->udev, - sizeof(*hf), - hf, - urb->transfer_dma); + usb_free_coherent(dev->udev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); if (rc == -ENODEV) { netif_device_detach(netdev); @@ -562,10 +697,8 @@ static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; badidx: - usb_free_coherent(dev->udev, - sizeof(*hf), - hf, - urb->transfer_dma); + usb_free_coherent(dev->udev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); nomem_hf: usb_free_urb(urb); @@ -582,6 +715,7 @@ static int gs_can_open(struct net_device *netdev) struct gs_usb *parent = dev->parent; int rc, i; struct gs_device_mode *dm; + struct gs_host_frame *hf; u32 ctrlmode; u32 flags = 0; @@ -589,6 +723,21 @@ static int gs_can_open(struct net_device *netdev) if (rc) return rc; + ctrlmode = dev->can.ctrlmode; + if (ctrlmode & CAN_CTRLMODE_FD) { + flags |= GS_CAN_MODE_FD; + + if (dev->feature & GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX) + dev->hf_size_tx = struct_size(hf, canfd_quirk, 1); + else + dev->hf_size_tx = struct_size(hf, canfd, 1); + } else { + if (dev->feature & GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX) + dev->hf_size_tx = struct_size(hf, classic_can_quirk, 1); + else + dev->hf_size_tx = struct_size(hf, classic_can, 1); + } + if (!parent->active_channels) { for (i = 0; i < GS_MAX_RX_URBS; i++) { struct urb *urb; @@ -601,7 +750,7 @@ static int gs_can_open(struct net_device *netdev) /* alloc rx buffer */ buf = usb_alloc_coherent(dev->udev, - sizeof(struct gs_host_frame), + dev->parent->hf_size_rx, GFP_KERNEL, &urb->transfer_dma); if (!buf) { @@ -617,9 +766,8 @@ static int gs_can_open(struct net_device *netdev) usb_rcvbulkpipe(dev->udev, GSUSB_ENDPOINT_IN), buf, - sizeof(struct gs_host_frame), - gs_usb_receive_bulk_callback, - parent); + dev->parent->hf_size_rx, + gs_usb_receive_bulk_callback, parent); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_anchor_urb(urb, &parent->rx_submitted); @@ -630,8 +778,7 @@ static int gs_can_open(struct net_device *netdev) netif_device_detach(dev->netdev); netdev_err(netdev, - "usb_submit failed (err=%d)\n", - rc); + "usb_submit failed (err=%d)\n", rc); usb_unanchor_urb(urb); usb_free_urb(urb); @@ -650,8 +797,6 @@ static int gs_can_open(struct net_device *netdev) return -ENOMEM; /* flags */ - ctrlmode = dev->can.ctrlmode; - if (ctrlmode & CAN_CTRLMODE_LOOPBACK) flags |= GS_CAN_MODE_LOOP_BACK; else if (ctrlmode & CAN_CTRLMODE_LISTENONLY) @@ -672,13 +817,8 @@ static int gs_can_open(struct net_device *netdev) rc = usb_control_msg(interface_to_usbdev(dev->iface), usb_sndctrlpipe(interface_to_usbdev(dev->iface), 0), GS_USB_BREQ_MODE, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE, - dev->channel, - 0, - dm, - sizeof(*dm), - 1000); + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + dev->channel, 0, dm, sizeof(*dm), 1000); if (rc < 0) { netdev_err(netdev, "Couldn't start device (err=%d)\n", rc); @@ -755,16 +895,10 @@ static int gs_usb_set_identify(struct net_device *netdev, bool do_identify) imode->mode = cpu_to_le32(GS_CAN_IDENTIFY_OFF); rc = usb_control_msg(interface_to_usbdev(dev->iface), - usb_sndctrlpipe(interface_to_usbdev(dev->iface), - 0), + usb_sndctrlpipe(interface_to_usbdev(dev->iface), 0), GS_USB_BREQ_IDENTIFY, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE, - dev->channel, - 0, - imode, - sizeof(*imode), - 100); + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + dev->channel, 0, imode, sizeof(*imode), 100); kfree(imode); @@ -803,6 +937,7 @@ static struct gs_can *gs_make_candev(unsigned int channel, struct net_device *netdev; int rc; struct gs_device_bt_const *bt_const; + struct gs_device_bt_const_extended *bt_const_extended; u32 feature; bt_const = kmalloc(sizeof(*bt_const), GFP_KERNEL); @@ -814,11 +949,7 @@ static struct gs_can *gs_make_candev(unsigned int channel, usb_rcvctrlpipe(interface_to_usbdev(intf), 0), GS_USB_BREQ_BT_CONST, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - channel, - 0, - bt_const, - sizeof(*bt_const), - 1000); + channel, 0, bt_const, sizeof(*bt_const), 1000); if (rc < 0) { dev_err(&intf->dev, @@ -875,6 +1006,7 @@ static struct gs_can *gs_make_candev(unsigned int channel, dev->can.ctrlmode_supported = CAN_CTRLMODE_CC_LEN8_DLC; feature = le32_to_cpu(bt_const->feature); + dev->feature = FIELD_GET(GS_CAN_FEATURE_MASK, feature); if (feature & GS_CAN_FEATURE_LISTEN_ONLY) dev->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; @@ -887,7 +1019,37 @@ static struct gs_can *gs_make_candev(unsigned int channel, if (feature & GS_CAN_FEATURE_ONE_SHOT) dev->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT; - SET_NETDEV_DEV(netdev, &intf->dev); + if (feature & GS_CAN_FEATURE_FD) { + dev->can.ctrlmode_supported |= CAN_CTRLMODE_FD; + /* The data bit timing will be overwritten, if + * GS_CAN_FEATURE_BT_CONST_EXT is set. + */ + dev->can.data_bittiming_const = &dev->bt_const; + dev->can.do_set_data_bittiming = gs_usb_set_data_bittiming; + } + + /* The CANtact Pro from LinkLayer Labs is based on the + * LPC54616 µC, which is affected by the NXP LPC USB transfer + * erratum. However, the current firmware (version 2) doesn't + * set the GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX bit. Set the + * feature GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX to workaround + * this issue. + * + * For the GS_USB_BREQ_DATA_BITTIMING USB control message the + * CANtact Pro firmware uses a request value, which is already + * used by the candleLight firmware for a different purpose + * (GS_USB_BREQ_GET_USER_ID). Set the feature + * GS_CAN_FEATURE_QUIRK_BREQ_CANTACT_PRO to workaround this + * issue. + */ + if (dev->udev->descriptor.idVendor == cpu_to_le16(USB_GSUSB_1_VENDOR_ID) && + dev->udev->descriptor.idProduct == cpu_to_le16(USB_GSUSB_1_PRODUCT_ID) && + dev->udev->manufacturer && dev->udev->product && + !strcmp(dev->udev->manufacturer, "LinkLayer Labs") && + !strcmp(dev->udev->product, "CANtact Pro") && + (le32_to_cpu(dconf->sw_version) <= 2)) + dev->feature |= GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX | + GS_CAN_FEATURE_QUIRK_BREQ_CANTACT_PRO; if (le32_to_cpu(dconf->sw_version) > 1) if (feature & GS_CAN_FEATURE_IDENTIFY) @@ -895,6 +1057,45 @@ static struct gs_can *gs_make_candev(unsigned int channel, kfree(bt_const); + /* fetch extended bit timing constants if device has feature + * GS_CAN_FEATURE_FD and GS_CAN_FEATURE_BT_CONST_EXT + */ + if (feature & GS_CAN_FEATURE_FD && + feature & GS_CAN_FEATURE_BT_CONST_EXT) { + bt_const_extended = kmalloc(sizeof(*bt_const_extended), GFP_KERNEL); + if (!bt_const_extended) + return ERR_PTR(-ENOMEM); + + rc = usb_control_msg(interface_to_usbdev(intf), + usb_rcvctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_BT_CONST_EXT, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + channel, 0, bt_const_extended, + sizeof(*bt_const_extended), + 1000); + if (rc < 0) { + dev_err(&intf->dev, + "Couldn't get extended bit timing const for channel (err=%d)\n", + rc); + kfree(bt_const_extended); + return ERR_PTR(rc); + } + + strcpy(dev->data_bt_const.name, "gs_usb"); + dev->data_bt_const.tseg1_min = le32_to_cpu(bt_const_extended->dtseg1_min); + dev->data_bt_const.tseg1_max = le32_to_cpu(bt_const_extended->dtseg1_max); + dev->data_bt_const.tseg2_min = le32_to_cpu(bt_const_extended->dtseg2_min); + dev->data_bt_const.tseg2_max = le32_to_cpu(bt_const_extended->dtseg2_max); + dev->data_bt_const.sjw_max = le32_to_cpu(bt_const_extended->dsjw_max); + dev->data_bt_const.brp_min = le32_to_cpu(bt_const_extended->dbrp_min); + dev->data_bt_const.brp_max = le32_to_cpu(bt_const_extended->dbrp_max); + dev->data_bt_const.brp_inc = le32_to_cpu(bt_const_extended->dbrp_inc); + + dev->can.data_bittiming_const = &dev->data_bt_const; + } + + SET_NETDEV_DEV(netdev, &intf->dev); + rc = register_candev(dev->netdev); if (rc) { free_candev(dev->netdev); @@ -915,6 +1116,8 @@ static void gs_destroy_candev(struct gs_can *dev) static int gs_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { + struct usb_device *udev = interface_to_usbdev(intf); + struct gs_host_frame *hf; struct gs_usb *dev; int rc = -ENOMEM; unsigned int icount, i; @@ -928,21 +1131,16 @@ static int gs_usb_probe(struct usb_interface *intf, hconf->byte_order = cpu_to_le32(0x0000beef); /* send host config */ - rc = usb_control_msg(interface_to_usbdev(intf), - usb_sndctrlpipe(interface_to_usbdev(intf), 0), + rc = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), GS_USB_BREQ_HOST_FORMAT, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 1, - intf->cur_altsetting->desc.bInterfaceNumber, - hconf, - sizeof(*hconf), - 1000); + 1, intf->cur_altsetting->desc.bInterfaceNumber, + hconf, sizeof(*hconf), 1000); kfree(hconf); if (rc < 0) { - dev_err(&intf->dev, "Couldn't send data format (err=%d)\n", - rc); + dev_err(&intf->dev, "Couldn't send data format (err=%d)\n", rc); return rc; } @@ -951,15 +1149,11 @@ static int gs_usb_probe(struct usb_interface *intf, return -ENOMEM; /* read device config */ - rc = usb_control_msg(interface_to_usbdev(intf), - usb_rcvctrlpipe(interface_to_usbdev(intf), 0), + rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), GS_USB_BREQ_DEVICE_CONFIG, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 1, - intf->cur_altsetting->desc.bInterfaceNumber, - dconf, - sizeof(*dconf), - 1000); + 1, intf->cur_altsetting->desc.bInterfaceNumber, + dconf, sizeof(*dconf), 1000); if (rc < 0) { dev_err(&intf->dev, "Couldn't get device config: (err=%d)\n", rc); @@ -985,9 +1179,13 @@ static int gs_usb_probe(struct usb_interface *intf, } init_usb_anchor(&dev->rx_submitted); + /* default to classic CAN, switch to CAN-FD if at least one of + * our channels support CAN-FD. + */ + dev->hf_size_rx = struct_size(hf, classic_can, 1); usb_set_intfdata(intf, dev); - dev->udev = interface_to_usbdev(intf); + dev->udev = udev; for (i = 0; i < icount; i++) { dev->canch[i] = gs_make_candev(i, intf, dconf); @@ -1006,6 +1204,9 @@ static int gs_usb_probe(struct usb_interface *intf, return rc; } dev->canch[i]->parent = dev; + + if (dev->canch[i]->can.ctrlmode_supported & CAN_CTRLMODE_FD) + dev->hf_size_rx = struct_size(hf, canfd, 1); } kfree(dconf); @@ -1015,8 +1216,9 @@ static int gs_usb_probe(struct usb_interface *intf, static void gs_usb_disconnect(struct usb_interface *intf) { - unsigned i; struct gs_usb *dev = usb_get_intfdata(intf); + unsigned int i; + usb_set_intfdata(intf, NULL); if (!dev) { @@ -1037,16 +1239,20 @@ static const struct usb_device_id gs_usb_table[] = { USB_GSUSB_1_PRODUCT_ID, 0) }, { USB_DEVICE_INTERFACE_NUMBER(USB_CANDLELIGHT_VENDOR_ID, USB_CANDLELIGHT_PRODUCT_ID, 0) }, + { USB_DEVICE_INTERFACE_NUMBER(USB_CES_CANEXT_FD_VENDOR_ID, + USB_CES_CANEXT_FD_PRODUCT_ID, 0) }, + { USB_DEVICE_INTERFACE_NUMBER(USB_ABE_CANDEBUGGER_FD_VENDOR_ID, + USB_ABE_CANDEBUGGER_FD_PRODUCT_ID, 0) }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, gs_usb_table); static struct usb_driver gs_usb_driver = { - .name = "gs_usb", - .probe = gs_usb_probe, + .name = "gs_usb", + .probe = gs_usb_probe, .disconnect = gs_usb_disconnect, - .id_table = gs_usb_table, + .id_table = gs_usb_table, }; module_usb_driver(gs_usb_driver); diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index c4b4d3d0a387..e67658b53d02 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -205,12 +205,10 @@ MODULE_DEVICE_TABLE(usb, kvaser_usb_table); int kvaser_usb_send_cmd(const struct kvaser_usb *dev, void *cmd, int len) { - int actual_len; /* Not used */ - return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out->bEndpointAddress), - cmd, len, &actual_len, KVASER_USB_TIMEOUT); + cmd, len, NULL, KVASER_USB_TIMEOUT); } int kvaser_usb_recv_cmd(const struct kvaser_usb *dev, void *cmd, int len, diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c index c7c41d1fd038..5ae0d7c017cc 100644 --- a/drivers/net/can/usb/ucan.c +++ b/drivers/net/can/usb/ucan.c @@ -1392,7 +1392,7 @@ static int ucan_probe(struct usb_interface *intf, * Stage 3 for the final driver initialisation. */ - /* Prepare Memory for control transferes */ + /* Prepare Memory for control transfers */ ctl_msg_buffer = devm_kzalloc(&udev->dev, sizeof(union ucan_ctl_payload), GFP_KERNEL); @@ -1526,7 +1526,7 @@ static int ucan_probe(struct usb_interface *intf, ret = ucan_device_request_in(up, UCAN_DEVICE_GET_FW_STRING, 0, sizeof(union ucan_ctl_payload)); if (ret > 0) { - /* copy string while ensuring zero terminiation */ + /* copy string while ensuring zero termination */ strncpy(firmware_str, up->ctl_msg_buffer->raw, sizeof(union ucan_ctl_payload)); firmware_str[sizeof(union ucan_ctl_payload)] = '\0'; diff --git a/drivers/net/can/vcan.c b/drivers/net/can/vcan.c index c42f18845b02..a15619d883ec 100644 --- a/drivers/net/can/vcan.c +++ b/drivers/net/can/vcan.c @@ -80,7 +80,7 @@ static void vcan_rx(struct sk_buff *skb, struct net_device *dev) skb->dev = dev; skb->ip_summed = CHECKSUM_UNNECESSARY; - netif_rx_ni(skb); + netif_rx(skb); } static netdev_tx_t vcan_tx(struct sk_buff *skb, struct net_device *dev) diff --git a/drivers/net/can/vxcan.c b/drivers/net/can/vxcan.c index 47ccc15a3486..577a80300514 100644 --- a/drivers/net/can/vxcan.c +++ b/drivers/net/can/vxcan.c @@ -33,28 +33,33 @@ struct vxcan_priv { struct net_device __rcu *peer; }; -static netdev_tx_t vxcan_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev) { struct vxcan_priv *priv = netdev_priv(dev); struct net_device *peer; - struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + struct canfd_frame *cfd = (struct canfd_frame *)oskb->data; struct net_device_stats *peerstats, *srcstats = &dev->stats; + struct sk_buff *skb; u8 len; - if (can_dropped_invalid_skb(dev, skb)) + if (can_dropped_invalid_skb(dev, oskb)) return NETDEV_TX_OK; rcu_read_lock(); peer = rcu_dereference(priv->peer); if (unlikely(!peer)) { - kfree_skb(skb); + kfree_skb(oskb); dev->stats.tx_dropped++; goto out_unlock; } - skb = can_create_echo_skb(skb); - if (!skb) + skb = skb_clone(oskb, GFP_ATOMIC); + if (skb) { + consume_skb(oskb); + } else { + kfree_skb(oskb); goto out_unlock; + } /* reset CAN GW hop counter */ skb->csum_start = 0; @@ -63,7 +68,7 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *skb, struct net_device *dev) skb->ip_summed = CHECKSUM_UNNECESSARY; len = cfd->can_id & CAN_RTR_FLAG ? 0 : cfd->len; - if (netif_rx_ni(skb) == NET_RX_SUCCESS) { + if (netif_rx(skb) == NET_RX_SUCCESS) { srcstats->tx_packets++; srcstats->tx_bytes += len; peerstats = &peer->stats; @@ -148,7 +153,7 @@ static void vxcan_setup(struct net_device *dev) dev->hard_header_len = 0; dev->addr_len = 0; dev->tx_queue_len = 0; - dev->flags = (IFF_NOARP|IFF_ECHO); + dev->flags = IFF_NOARP; dev->netdev_ops = &vxcan_netdev_ops; dev->needs_free_netdev = true; diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c index 1674b561c9a2..e562c5ab1149 100644 --- a/drivers/net/can/xilinx_can.c +++ b/drivers/net/can/xilinx_can.c @@ -1215,10 +1215,11 @@ static int xcan_rx_poll(struct napi_struct *napi, int quota) } if (work_done < quota) { - napi_complete_done(napi, work_done); - ier = priv->read_reg(priv, XCAN_IER_OFFSET); - ier |= xcan_rx_int_mask(priv); - priv->write_reg(priv, XCAN_IER_OFFSET, ier); + if (napi_complete_done(napi, work_done)) { + ier = priv->read_reg(priv, XCAN_IER_OFFSET); + ier |= xcan_rx_int_mask(priv); + priv->write_reg(priv, XCAN_IER_OFFSET, ier); + } } return work_done; } diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 0029d279616f..37a3dabdce31 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -68,17 +68,7 @@ config NET_DSA_QCA8K This enables support for the Qualcomm Atheros QCA8K Ethernet switch chips. -config NET_DSA_REALTEK_SMI - tristate "Realtek SMI Ethernet switch family support" - select NET_DSA_TAG_RTL4_A - select NET_DSA_TAG_RTL8_4 - select FIXED_PHY - select IRQ_DOMAIN - select REALTEK_PHY - select REGMAP - help - This enables support for the Realtek SMI-based switch - chips, currently only RTL8366RB. +source "drivers/net/dsa/realtek/Kconfig" config NET_DSA_SMSC_LAN9303 tristate diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index 8da1569a34e6..e73838c12256 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -9,8 +9,6 @@ obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o -obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o -realtek-smi-objs := realtek-smi-core.o rtl8366.o rtl8366rb.o rtl8365mb.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303_I2C) += lan9303_i2c.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o @@ -23,5 +21,6 @@ obj-y += microchip/ obj-y += mv88e6xxx/ obj-y += ocelot/ obj-y += qca/ +obj-y += realtek/ obj-y += sja1105/ obj-y += xrs700x/ diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 3867f3d4545f..77501f9c5915 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1309,46 +1309,50 @@ void b53_port_event(struct dsa_switch *ds, int port) } EXPORT_SYMBOL(b53_port_event); -void b53_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void b53_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { struct b53_device *dev = ds->priv; - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - if (dev->ops->serdes_phylink_validate) - dev->ops->serdes_phylink_validate(dev, port, mask, state); + /* Internal ports need GMII for PHYLIB */ + __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces); - /* Allow all the expected bits */ - phylink_set(mask, Autoneg); - phylink_set_port_modes(mask); - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - - /* With the exclusion of 5325/5365, MII, Reverse MII and 802.3z, we - * support Gigabit, including Half duplex. + /* These switches appear to support MII and RevMII too, but beyond + * this, the code gives very few clues. FIXME: We probably need more + * interface modes here. + * + * According to b53_srab_mux_init(), ports 3..5 can support: + * SGMII, MII, GMII, RGMII or INTERNAL depending on the MUX setting. + * However, the interface mode read from the MUX configuration is + * not passed back to DSA, so phylink uses NA. + * DT can specify RGMII for ports 0, 1. + * For MDIO, port 8 can be RGMII_TXID. */ - if (state->interface != PHY_INTERFACE_MODE_MII && - state->interface != PHY_INTERFACE_MODE_REVMII && - !phy_interface_mode_is_8023z(state->interface) && - !(is5325(dev) || is5365(dev))) { - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseT_Half); - } + __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_REVMII, config->supported_interfaces); - if (!phy_interface_mode_is_8023z(state->interface)) { - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - } + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100; - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); + /* 5325/5365 are not capable of gigabit speeds, everything else is. + * Note: the original code also exclulded Gigagbit for MII, RevMII + * and 802.3z modes. MII and RevMII are not able to work above 100M, + * so will be excluded by the generic validator implementation. + * However, the exclusion of Gigabit for 802.3z just seems wrong. + */ + if (!(is5325(dev) || is5365(dev))) + config->mac_capabilities |= MAC_1000; - phylink_helper_basex_speed(state); + /* Get the implementation specific capabilities */ + if (dev->ops->phylink_get_caps) + dev->ops->phylink_get_caps(dev, port, config); + + /* This driver does not make use of the speed, duplex, pause or the + * advertisement in its mac_config, so it is safe to mark this driver + * as non-legacy. + */ + config->legacy_pre_march2020 = false; } -EXPORT_SYMBOL(b53_phylink_validate); int b53_phylink_mac_link_state(struct dsa_switch *ds, int port, struct phylink_link_state *state) @@ -1704,7 +1708,8 @@ static int b53_arl_op(struct b53_device *dev, int op, int port, } int b53_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct b53_device *priv = ds->priv; int ret; @@ -1724,7 +1729,8 @@ int b53_fdb_add(struct dsa_switch *ds, int port, EXPORT_SYMBOL(b53_fdb_add); int b53_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct b53_device *priv = ds->priv; int ret; @@ -1825,7 +1831,8 @@ int b53_fdb_dump(struct dsa_switch *ds, int port, EXPORT_SYMBOL(b53_fdb_dump); int b53_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct b53_device *priv = ds->priv; int ret; @@ -1845,7 +1852,8 @@ int b53_mdb_add(struct dsa_switch *ds, int port, EXPORT_SYMBOL(b53_mdb_add); int b53_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct b53_device *priv = ds->priv; int ret; @@ -1861,7 +1869,7 @@ int b53_mdb_del(struct dsa_switch *ds, int port, EXPORT_SYMBOL(b53_mdb_del); int b53_br_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, - bool *tx_fwd_offload) + bool *tx_fwd_offload, struct netlink_ext_ack *extack) { struct b53_device *dev = ds->priv; s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; @@ -2102,7 +2110,8 @@ enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port, EXPORT_SYMBOL(b53_get_tag_protocol); int b53_mirror_add(struct dsa_switch *ds, int port, - struct dsa_mall_mirror_tc_entry *mirror, bool ingress) + struct dsa_mall_mirror_tc_entry *mirror, bool ingress, + struct netlink_ext_ack *extack) { struct b53_device *dev = ds->priv; u16 reg, loc; @@ -2186,7 +2195,7 @@ int b53_eee_init(struct dsa_switch *ds, int port, struct phy_device *phy) { int ret; - ret = phy_init_eee(phy, 0); + ret = phy_init_eee(phy, false); if (ret) return 0; @@ -2259,7 +2268,7 @@ static const struct dsa_switch_ops b53_switch_ops = { .phy_read = b53_phy_read16, .phy_write = b53_phy_write16, .adjust_link = b53_adjust_link, - .phylink_validate = b53_phylink_validate, + .phylink_get_caps = b53_phylink_get_caps, .phylink_mac_link_state = b53_phylink_mac_link_state, .phylink_mac_config = b53_phylink_mac_config, .phylink_mac_an_restart = b53_phylink_mac_an_restart, diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index b41dc8ac2ca8..3085b6cc7d40 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -46,6 +46,8 @@ struct b53_io_ops { int (*phy_write16)(struct b53_device *dev, int addr, int reg, u16 value); int (*irq_enable)(struct b53_device *dev, int port); void (*irq_disable)(struct b53_device *dev, int port); + void (*phylink_get_caps)(struct b53_device *dev, int port, + struct phylink_config *config); u8 (*serdes_map_lane)(struct b53_device *dev, int port); int (*serdes_link_state)(struct b53_device *dev, int port, struct phylink_link_state *state); @@ -56,9 +58,6 @@ struct b53_io_ops { void (*serdes_link_set)(struct b53_device *dev, int port, unsigned int mode, phy_interface_t interface, bool link_up); - void (*serdes_phylink_validate)(struct b53_device *dev, int port, - unsigned long *supported, - struct phylink_link_state *state); }; #define B53_INVALID_LANE 0xff @@ -325,7 +324,7 @@ void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data); int b53_get_sset_count(struct dsa_switch *ds, int port, int sset); void b53_get_ethtool_phy_stats(struct dsa_switch *ds, int port, uint64_t *data); int b53_br_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, - bool *tx_fwd_offload); + bool *tx_fwd_offload, struct netlink_ext_ack *extack); void b53_br_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge); void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state); void b53_br_fast_age(struct dsa_switch *ds, int port); @@ -337,9 +336,6 @@ int b53_br_flags(struct dsa_switch *ds, int port, struct netlink_ext_ack *extack); int b53_setup_devlink_resources(struct dsa_switch *ds); void b53_port_event(struct dsa_switch *ds, int port); -void b53_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state); int b53_phylink_mac_link_state(struct dsa_switch *ds, int port, struct phylink_link_state *state); void b53_phylink_mac_config(struct dsa_switch *ds, int port, @@ -363,17 +359,22 @@ int b53_vlan_add(struct dsa_switch *ds, int port, int b53_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan); int b53_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); + const unsigned char *addr, u16 vid, + struct dsa_db db); int b53_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); + const unsigned char *addr, u16 vid, + struct dsa_db db); int b53_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data); int b53_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb); + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); int b53_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb); + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); int b53_mirror_add(struct dsa_switch *ds, int port, - struct dsa_mall_mirror_tc_entry *mirror, bool ingress); + struct dsa_mall_mirror_tc_entry *mirror, bool ingress, + struct netlink_ext_ack *extack); enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol mprot); void b53_mirror_del(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/b53/b53_serdes.c b/drivers/net/dsa/b53/b53_serdes.c index 5ae3d9783b68..555e5b372321 100644 --- a/drivers/net/dsa/b53/b53_serdes.c +++ b/drivers/net/dsa/b53/b53_serdes.c @@ -158,9 +158,8 @@ void b53_serdes_link_set(struct b53_device *dev, int port, unsigned int mode, } EXPORT_SYMBOL(b53_serdes_link_set); -void b53_serdes_phylink_validate(struct b53_device *dev, int port, - unsigned long *supported, - struct phylink_link_state *state) +void b53_serdes_phylink_get_caps(struct b53_device *dev, int port, + struct phylink_config *config) { u8 lane = b53_serdes_map_lane(dev, port); @@ -169,16 +168,24 @@ void b53_serdes_phylink_validate(struct b53_device *dev, int port, switch (lane) { case 0: - phylink_set(supported, 2500baseX_Full); + /* It appears lane 0 supports 2500base-X and 1000base-X */ + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + config->supported_interfaces); + config->mac_capabilities |= MAC_2500FD; fallthrough; case 1: - phylink_set(supported, 1000baseX_Full); + /* It appears lane 1 only supports 1000base-X and SGMII */ + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); + config->mac_capabilities |= MAC_1000FD; break; default: break; } } -EXPORT_SYMBOL(b53_serdes_phylink_validate); +EXPORT_SYMBOL(b53_serdes_phylink_get_caps); int b53_serdes_init(struct b53_device *dev, int port) { diff --git a/drivers/net/dsa/b53/b53_serdes.h b/drivers/net/dsa/b53/b53_serdes.h index 55d280fe38e4..f47d5caa7557 100644 --- a/drivers/net/dsa/b53/b53_serdes.h +++ b/drivers/net/dsa/b53/b53_serdes.h @@ -115,9 +115,8 @@ void b53_serdes_config(struct b53_device *dev, int port, unsigned int mode, void b53_serdes_an_restart(struct b53_device *dev, int port); void b53_serdes_link_set(struct b53_device *dev, int port, unsigned int mode, phy_interface_t interface, bool link_up); -void b53_serdes_phylink_validate(struct b53_device *dev, int port, - unsigned long *supported, - struct phylink_link_state *state); +void b53_serdes_phylink_get_caps(struct b53_device *dev, int port, + struct phylink_config *config); #if IS_ENABLED(CONFIG_B53_SERDES) int b53_serdes_init(struct b53_device *dev, int port); #else diff --git a/drivers/net/dsa/b53/b53_srab.c b/drivers/net/dsa/b53/b53_srab.c index 4591bb1c05d2..c51b716657db 100644 --- a/drivers/net/dsa/b53/b53_srab.c +++ b/drivers/net/dsa/b53/b53_srab.c @@ -443,6 +443,39 @@ static void b53_srab_irq_disable(struct b53_device *dev, int port) } } +static void b53_srab_phylink_get_caps(struct b53_device *dev, int port, + struct phylink_config *config) +{ + struct b53_srab_priv *priv = dev->priv; + struct b53_srab_port_priv *p = &priv->port_intrs[port]; + + switch (p->mode) { + case PHY_INTERFACE_MODE_SGMII: +#if IS_ENABLED(CONFIG_B53_SERDES) + /* If p->mode indicates SGMII mode, that essentially means we + * are using a serdes. As the serdes for the capabilities. + */ + b53_serdes_phylink_get_caps(dev, port, config); +#endif + break; + + case PHY_INTERFACE_MODE_NA: + break; + + case PHY_INTERFACE_MODE_RGMII: + /* If we support RGMII, support all RGMII modes, since + * that dictates the PHY delay settings. + */ + phy_interface_set_rgmii(config->supported_interfaces); + break; + + default: + /* Some other mode (e.g. MII, GMII etc) */ + __set_bit(p->mode, config->supported_interfaces); + break; + } +} + static const struct b53_io_ops b53_srab_ops = { .read8 = b53_srab_read8, .read16 = b53_srab_read16, @@ -456,13 +489,13 @@ static const struct b53_io_ops b53_srab_ops = { .write64 = b53_srab_write64, .irq_enable = b53_srab_irq_enable, .irq_disable = b53_srab_irq_disable, + .phylink_get_caps = b53_srab_phylink_get_caps, #if IS_ENABLED(CONFIG_B53_SERDES) .serdes_map_lane = b53_srab_serdes_map_lane, .serdes_link_state = b53_serdes_link_state, .serdes_config = b53_serdes_config, .serdes_an_restart = b53_serdes_an_restart, .serdes_link_set = b53_serdes_link_set, - .serdes_phylink_validate = b53_serdes_phylink_validate, #endif }; diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 6afb5db8244c..cf82b1fa9725 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -712,49 +712,25 @@ static u32 bcm_sf2_sw_get_phy_flags(struct dsa_switch *ds, int port) PHY_BRCM_IDDQ_SUSPEND; } -static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void bcm_sf2_sw_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { + unsigned long *interfaces = config->supported_interfaces; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - if (!phy_interface_mode_is_rgmii(state->interface) && - state->interface != PHY_INTERFACE_MODE_MII && - state->interface != PHY_INTERFACE_MODE_REVMII && - state->interface != PHY_INTERFACE_MODE_GMII && - state->interface != PHY_INTERFACE_MODE_INTERNAL && - state->interface != PHY_INTERFACE_MODE_MOCA) { - linkmode_zero(supported); - if (port != core_readl(priv, CORE_IMP0_PRT_ID)) - dev_err(ds->dev, - "Unsupported interface: %d for port %d\n", - state->interface, port); - return; + if (priv->int_phy_mask & BIT(port)) { + __set_bit(PHY_INTERFACE_MODE_INTERNAL, interfaces); + } else if (priv->moca_port == port) { + __set_bit(PHY_INTERFACE_MODE_MOCA, interfaces); + } else { + __set_bit(PHY_INTERFACE_MODE_MII, interfaces); + __set_bit(PHY_INTERFACE_MODE_REVMII, interfaces); + __set_bit(PHY_INTERFACE_MODE_GMII, interfaces); + phy_interface_set_rgmii(interfaces); } - /* Allow all the expected bits */ - phylink_set(mask, Autoneg); - phylink_set_port_modes(mask); - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - - /* With the exclusion of MII and Reverse MII, we support Gigabit, - * including Half duplex - */ - if (state->interface != PHY_INTERFACE_MODE_MII && - state->interface != PHY_INTERFACE_MODE_REVMII) { - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseT_Half); - } - - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000; } static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port, @@ -1221,7 +1197,7 @@ static const struct dsa_switch_ops bcm_sf2_ops = { .get_sset_count = bcm_sf2_sw_get_sset_count, .get_ethtool_phy_stats = b53_get_ethtool_phy_stats, .get_phy_flags = bcm_sf2_sw_get_phy_flags, - .phylink_validate = bcm_sf2_sw_validate, + .phylink_get_caps = bcm_sf2_sw_get_caps, .phylink_mac_config = bcm_sf2_sw_mac_config, .phylink_mac_link_down = bcm_sf2_sw_mac_link_down, .phylink_mac_link_up = bcm_sf2_sw_mac_link_up, diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index 33daaf10c488..263e41191c29 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -168,7 +168,8 @@ static int dsa_loop_phy_write(struct dsa_switch *ds, int port, static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, - bool *tx_fwd_offload) + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { dev_dbg(ds->dev, "%s: port: %d, bridge: %s\n", __func__, port, bridge.dev->name); diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c index 726f267cb228..ac1f3b3a7040 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.c +++ b/drivers/net/dsa/hirschmann/hellcreek.c @@ -675,7 +675,8 @@ static int hellcreek_bridge_flags(struct dsa_switch *ds, int port, static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, - bool *tx_fwd_offload) + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { struct hellcreek *hellcreek = ds->priv; @@ -827,7 +828,8 @@ static int hellcreek_fdb_get(struct hellcreek *hellcreek, } static int hellcreek_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct hellcreek_fdb_entry entry = { 0 }; struct hellcreek *hellcreek = ds->priv; @@ -872,7 +874,8 @@ static int hellcreek_fdb_add(struct dsa_switch *ds, int port, } static int hellcreek_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct hellcreek_fdb_entry entry = { 0 }; struct hellcreek *hellcreek = ds->priv; diff --git a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c index b3bc948d6145..ffd06cf8c44f 100644 --- a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c +++ b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c @@ -331,7 +331,7 @@ static void hellcreek_get_rxts(struct hellcreek *hellcreek, shwt = skb_hwtstamps(skb); memset(shwt, 0, sizeof(*shwt)); shwt->hwtstamp = ns_to_ktime(ns); - netif_rx_ni(skb); + netif_rx(skb); } } diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 3969d89fa4db..e03ff1f267bb 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -1111,7 +1111,8 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port) static int lan9303_port_bridge_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, - bool *tx_fwd_offload) + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { struct lan9303 *chip = ds->priv; @@ -1188,7 +1189,8 @@ static void lan9303_port_fast_age(struct dsa_switch *ds, int port) } static int lan9303_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct lan9303 *chip = ds->priv; @@ -1200,8 +1202,8 @@ static int lan9303_port_fdb_add(struct dsa_switch *ds, int port, } static int lan9303_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) - + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct lan9303 *chip = ds->priv; @@ -1245,7 +1247,8 @@ static int lan9303_port_mdb_prepare(struct dsa_switch *ds, int port, } static int lan9303_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct lan9303 *chip = ds->priv; int err; @@ -1260,7 +1263,8 @@ static int lan9303_port_mdb_add(struct dsa_switch *ds, int port, } static int lan9303_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct lan9303 *chip = ds->priv; diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 8a7a8093a156..a416240d001b 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -213,6 +213,7 @@ #define GSWIP_MAC_CTRL_0_GMII_MII 0x0001 #define GSWIP_MAC_CTRL_0_GMII_RGMII 0x0002 #define GSWIP_MAC_CTRL_2p(p) (0x905 + ((p) * 0xC)) +#define GSWIP_MAC_CTRL_2_LCHKL BIT(2) /* Frame Length Check Long Enable */ #define GSWIP_MAC_CTRL_2_MLEN BIT(3) /* Maximum Untagged Frame Lnegth */ /* Ethernet Switch Fetch DMA Port Control Register */ @@ -239,6 +240,15 @@ #define XRX200_GPHY_FW_ALIGN (16 * 1024) +/* Maximum packet size supported by the switch. In theory this should be 10240, + * but long packets currently cause lock-ups with an MTU of over 2526. Medium + * packets are sometimes dropped (e.g. TCP over 2477, UDP over 2516-2519, ICMP + * over 2526), hence an MTU value of 2400 seems safe. This issue only affects + * packet reception. This is probably caused by the PPA engine, which is on the + * RX part of the device. Packet transmission works properly up to 10240. + */ +#define GSWIP_MAX_PACKET_LENGTH 2400 + struct gswip_hw_info { int max_ports; int cpu_port; @@ -863,10 +873,6 @@ static int gswip_setup(struct dsa_switch *ds) gswip_switch_mask(priv, 0, GSWIP_PCE_PCTRL_0_INGRESS, GSWIP_PCE_PCTRL_0p(cpu_port)); - gswip_switch_mask(priv, 0, GSWIP_MAC_CTRL_2_MLEN, - GSWIP_MAC_CTRL_2p(cpu_port)); - gswip_switch_w(priv, VLAN_ETH_FRAME_LEN + 8 + ETH_FCS_LEN, - GSWIP_MAC_FLEN); gswip_switch_mask(priv, 0, GSWIP_BM_QUEUE_GCTRL_GL_MOD, GSWIP_BM_QUEUE_GCTRL); @@ -883,6 +889,8 @@ static int gswip_setup(struct dsa_switch *ds) return err; } + ds->mtu_enforcement_ingress = true; + gswip_port_enable(ds, cpu_port, NULL); ds->configure_vlan_while_not_filtering = false; @@ -1152,7 +1160,8 @@ static int gswip_vlan_remove(struct gswip_priv *priv, static int gswip_port_bridge_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, - bool *tx_fwd_offload) + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { struct net_device *br = bridge.dev; struct gswip_priv *priv = ds->priv; @@ -1389,13 +1398,15 @@ static int gswip_port_fdb(struct dsa_switch *ds, int port, } static int gswip_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { return gswip_port_fdb(ds, port, addr, vid, true); } static int gswip_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { return gswip_port_fdb(ds, port, addr, vid, false); } @@ -1446,6 +1457,39 @@ static int gswip_port_fdb_dump(struct dsa_switch *ds, int port, return 0; } +static int gswip_port_max_mtu(struct dsa_switch *ds, int port) +{ + /* Includes 8 bytes for special header. */ + return GSWIP_MAX_PACKET_LENGTH - VLAN_ETH_HLEN - ETH_FCS_LEN; +} + +static int gswip_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + struct gswip_priv *priv = ds->priv; + int cpu_port = priv->hw_info->cpu_port; + + /* CPU port always has maximum mtu of user ports, so use it to set + * switch frame size, including 8 byte special header. + */ + if (port == cpu_port) { + new_mtu += 8; + gswip_switch_w(priv, VLAN_ETH_HLEN + new_mtu + ETH_FCS_LEN, + GSWIP_MAC_FLEN); + } + + /* Enable MLEN for ports with non-standard MTUs, including the special + * header on the CPU port added above. + */ + if (new_mtu != ETH_DATA_LEN) + gswip_switch_mask(priv, 0, GSWIP_MAC_CTRL_2_MLEN, + GSWIP_MAC_CTRL_2p(port)); + else + gswip_switch_mask(priv, GSWIP_MAC_CTRL_2_MLEN, 0, + GSWIP_MAC_CTRL_2p(port)); + + return 0; +} + static void gswip_xrx200_phylink_get_caps(struct dsa_switch *ds, int port, struct phylink_config *config) { @@ -1791,6 +1835,8 @@ static const struct dsa_switch_ops gswip_xrx200_switch_ops = { .port_fdb_add = gswip_port_fdb_add, .port_fdb_del = gswip_port_fdb_del, .port_fdb_dump = gswip_port_fdb_dump, + .port_change_mtu = gswip_port_change_mtu, + .port_max_mtu = gswip_port_max_mtu, .phylink_get_caps = gswip_xrx200_phylink_get_caps, .phylink_mac_config = gswip_phylink_mac_config, .phylink_mac_link_down = gswip_phylink_mac_link_down, @@ -1815,6 +1861,8 @@ static const struct dsa_switch_ops gswip_xrx300_switch_ops = { .port_fdb_add = gswip_port_fdb_add, .port_fdb_del = gswip_port_fdb_del, .port_fdb_dump = gswip_port_fdb_dump, + .port_change_mtu = gswip_port_change_mtu, + .port_max_mtu = gswip_port_max_mtu, .phylink_get_caps = gswip_xrx300_phylink_get_caps, .phylink_mac_config = gswip_phylink_mac_config, .phylink_mac_link_down = gswip_phylink_mac_link_down, diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h index 9d611895d3cf..03da369675c6 100644 --- a/drivers/net/dsa/microchip/ksz8.h +++ b/drivers/net/dsa/microchip/ksz8.h @@ -16,6 +16,7 @@ enum ksz_regs { REG_IND_DATA_HI, REG_IND_DATA_LO, REG_IND_MIB_CHECK, + REG_IND_BYTE, P_FORCE_CTRL, P_LINK_STATUS, P_LOCAL_CTRL, diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index 991b9c6b6ce7..b2752978cb09 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -33,6 +33,7 @@ static const u8 ksz8795_regs[] = { [REG_IND_DATA_HI] = 0x71, [REG_IND_DATA_LO] = 0x75, [REG_IND_MIB_CHECK] = 0x74, + [REG_IND_BYTE] = 0xA0, [P_FORCE_CTRL] = 0x0C, [P_LINK_STATUS] = 0x0E, [P_LOCAL_CTRL] = 0x07, @@ -222,6 +223,25 @@ static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits, bits, set ? bits : 0); } +static int ksz8_ind_write8(struct ksz_device *dev, u8 table, u16 addr, u8 data) +{ + struct ksz8 *ksz8 = dev->priv; + const u8 *regs = ksz8->regs; + u16 ctrl_addr; + int ret = 0; + + mutex_lock(&dev->alu_mutex); + + ctrl_addr = IND_ACC_TABLE(table) | addr; + ret = ksz_write8(dev, regs[REG_IND_BYTE], data); + if (!ret) + ret = ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); + + mutex_unlock(&dev->alu_mutex); + + return ret; +} + static int ksz8_reset_switch(struct ksz_device *dev) { if (ksz_is_ksz88x3(dev)) { @@ -1213,7 +1233,7 @@ static int ksz8_port_vlan_del(struct dsa_switch *ds, int port, static int ksz8_port_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, - bool ingress) + bool ingress, struct netlink_ext_ack *extack) { struct ksz_device *dev = ds->priv; @@ -1391,6 +1411,23 @@ static void ksz8_config_cpu_port(struct dsa_switch *ds) } } +static int ksz8_handle_global_errata(struct dsa_switch *ds) +{ + struct ksz_device *dev = ds->priv; + int ret = 0; + + /* KSZ87xx Errata DS80000687C. + * Module 2: Link drops with some EEE link partners. + * An issue with the EEE next page exchange between the + * KSZ879x/KSZ877x/KSZ876x and some EEE link partners may result in + * the link dropping. + */ + if (dev->ksz87xx_eee_link_erratum) + ret = ksz8_ind_write8(dev, TABLE_EEE, REG_IND_EEE_GLOB2_HI, 0); + + return ret; +} + static int ksz8_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; @@ -1458,30 +1495,25 @@ static int ksz8_setup(struct dsa_switch *ds) ds->configure_vlan_while_not_filtering = false; - return 0; + return ksz8_handle_global_errata(ds); } -static void ksz8_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void ksz8_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; struct ksz_device *dev = ds->priv; if (port == dev->cpu_port) { - if (state->interface != PHY_INTERFACE_MODE_RMII && - state->interface != PHY_INTERFACE_MODE_MII && - state->interface != PHY_INTERFACE_MODE_NA) - goto unsupported; + __set_bit(PHY_INTERFACE_MODE_RMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_MII, + config->supported_interfaces); } else { - if (state->interface != PHY_INTERFACE_MODE_INTERNAL && - state->interface != PHY_INTERFACE_MODE_NA) - goto unsupported; + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); } - /* Allow all the expected bits */ - phylink_set_port_modes(mask); - phylink_set(mask, Autoneg); + config->mac_capabilities = MAC_10 | MAC_100; /* Silicon Errata Sheet (DS80000830A): * "Port 1 does not respond to received flow control PAUSE frames" @@ -1489,27 +1521,11 @@ static void ksz8_validate(struct dsa_switch *ds, int port, * switches. */ if (!ksz_is_ksz88x3(dev) || port) - phylink_set(mask, Pause); + config->mac_capabilities |= MAC_SYM_PAUSE; /* Asym pause is not supported on KSZ8863 and KSZ8873 */ if (!ksz_is_ksz88x3(dev)) - phylink_set(mask, Asym_Pause); - - /* 10M and 100M are only supported */ - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); - - return; - -unsupported: - linkmode_zero(supported); - dev_err(ds->dev, "Unsupported interface: %s, port: %d\n", - phy_modes(state->interface), port); + config->mac_capabilities |= MAC_ASYM_PAUSE; } static const struct dsa_switch_ops ksz8_switch_ops = { @@ -1518,7 +1534,7 @@ static const struct dsa_switch_ops ksz8_switch_ops = { .setup = ksz8_setup, .phy_read = ksz_phy_read16, .phy_write = ksz_phy_write16, - .phylink_validate = ksz8_validate, + .phylink_get_caps = ksz8_get_caps, .phylink_mac_link_down = ksz_mac_link_down, .port_enable = ksz_enable_port, .get_strings = ksz8_get_strings, @@ -1596,6 +1612,7 @@ struct ksz_chip_data { int num_statics; int cpu_ports; int port_cnt; + bool ksz87xx_eee_link_erratum; }; static const struct ksz_chip_data ksz8_switch_chips[] = { @@ -1607,6 +1624,7 @@ static const struct ksz_chip_data ksz8_switch_chips[] = { .num_statics = 8, .cpu_ports = 0x10, /* can be configured as cpu port */ .port_cnt = 5, /* total cpu and user ports */ + .ksz87xx_eee_link_erratum = true, }, { /* @@ -1630,6 +1648,7 @@ static const struct ksz_chip_data ksz8_switch_chips[] = { .num_statics = 8, .cpu_ports = 0x10, /* can be configured as cpu port */ .port_cnt = 4, /* total cpu and user ports */ + .ksz87xx_eee_link_erratum = true, }, { .chip_id = 0x8765, @@ -1639,6 +1658,7 @@ static const struct ksz_chip_data ksz8_switch_chips[] = { .num_statics = 8, .cpu_ports = 0x10, /* can be configured as cpu port */ .port_cnt = 5, /* total cpu and user ports */ + .ksz87xx_eee_link_erratum = true, }, { .chip_id = 0x8830, @@ -1673,6 +1693,8 @@ static int ksz8_switch_init(struct ksz_device *dev) dev->host_mask = chip->cpu_ports; dev->port_mask = (BIT(dev->phy_port_cnt) - 1) | chip->cpu_ports; + dev->ksz87xx_eee_link_erratum = + chip->ksz87xx_eee_link_erratum; break; } } diff --git a/drivers/net/dsa/microchip/ksz8795_reg.h b/drivers/net/dsa/microchip/ksz8795_reg.h index 6b40bc25f7ff..d74defcd86b4 100644 --- a/drivers/net/dsa/microchip/ksz8795_reg.h +++ b/drivers/net/dsa/microchip/ksz8795_reg.h @@ -812,6 +812,10 @@ #define IND_ACC_TABLE(table) ((table) << 8) +/* */ +#define REG_IND_EEE_GLOB2_LO 0x34 +#define REG_IND_EEE_GLOB2_HI 0x35 + /* Driver set switch broadcast storm protection at 10% rate. */ #define BROADCAST_STORM_PROT_RATE 10 diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 353b5f981740..8222c8a6c5ec 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,100 @@ static const struct { { 0x83, "tx_discards" }, }; +struct ksz9477_stats_raw { + u64 rx_hi; + u64 rx_undersize; + u64 rx_fragments; + u64 rx_oversize; + u64 rx_jabbers; + u64 rx_symbol_err; + u64 rx_crc_err; + u64 rx_align_err; + u64 rx_mac_ctrl; + u64 rx_pause; + u64 rx_bcast; + u64 rx_mcast; + u64 rx_ucast; + u64 rx_64_or_less; + u64 rx_65_127; + u64 rx_128_255; + u64 rx_256_511; + u64 rx_512_1023; + u64 rx_1024_1522; + u64 rx_1523_2000; + u64 rx_2001; + u64 tx_hi; + u64 tx_late_col; + u64 tx_pause; + u64 tx_bcast; + u64 tx_mcast; + u64 tx_ucast; + u64 tx_deferred; + u64 tx_total_col; + u64 tx_exc_col; + u64 tx_single_col; + u64 tx_mult_col; + u64 rx_total; + u64 tx_total; + u64 rx_discards; + u64 tx_discards; +}; + +static void ksz9477_r_mib_stats64(struct ksz_device *dev, int port) +{ + struct rtnl_link_stats64 *stats; + struct ksz9477_stats_raw *raw; + struct ksz_port_mib *mib; + + mib = &dev->ports[port].mib; + stats = &mib->stats64; + raw = (struct ksz9477_stats_raw *)mib->counters; + + spin_lock(&mib->stats64_lock); + + stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast; + stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast; + + /* HW counters are counting bytes + FCS which is not acceptable + * for rtnl_link_stats64 interface + */ + stats->rx_bytes = raw->rx_total - stats->rx_packets * ETH_FCS_LEN; + stats->tx_bytes = raw->tx_total - stats->tx_packets * ETH_FCS_LEN; + + stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments + + raw->rx_oversize; + + stats->rx_crc_errors = raw->rx_crc_err; + stats->rx_frame_errors = raw->rx_align_err; + stats->rx_dropped = raw->rx_discards; + stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors + + stats->rx_frame_errors + stats->rx_dropped; + + stats->tx_window_errors = raw->tx_late_col; + stats->tx_fifo_errors = raw->tx_discards; + stats->tx_aborted_errors = raw->tx_exc_col; + stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors + + stats->tx_aborted_errors; + + stats->multicast = raw->rx_mcast; + stats->collisions = raw->tx_total_col; + + spin_unlock(&mib->stats64_lock); +} + +static void ksz9477_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *s) +{ + struct ksz_device *dev = ds->priv; + struct ksz_port_mib *mib; + + mib = &dev->ports[port].mib; + + spin_lock(&mib->stats64_lock); + memcpy(s, &mib->stats64, sizeof(*s)); + spin_unlock(&mib->stats64_lock); +} + static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) { regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0); @@ -88,6 +183,29 @@ static void ksz9477_port_cfg32(struct ksz_device *dev, int port, int offset, bits, set ? bits : 0); } +static int ksz9477_change_mtu(struct dsa_switch *ds, int port, int mtu) +{ + struct ksz_device *dev = ds->priv; + u16 frame_size, max_frame = 0; + int i; + + frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; + + /* Cache the per-port MTU setting */ + dev->ports[port].max_frame = frame_size; + + for (i = 0; i < dev->port_cnt; i++) + max_frame = max(max_frame, dev->ports[i].max_frame); + + return regmap_update_bits(dev->regmap[1], REG_SW_MTU__2, + REG_SW_MTU_MASK, max_frame); +} + +static int ksz9477_max_mtu(struct dsa_switch *ds, int port) +{ + return KSZ9477_MAX_FRAME_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; +} + static int ksz9477_wait_vlan_ctrl_ready(struct ksz_device *dev) { unsigned int val; @@ -222,9 +340,12 @@ static int ksz9477_reset_switch(struct ksz_device *dev) (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100); - if (dev->synclko_125) - ksz_write8(dev, REG_SW_GLOBAL_OUTPUT_CTRL__1, - SW_ENABLE_REFCLKO | SW_REFCLKO_IS_125MHZ); + data8 = SW_ENABLE_REFCLKO; + if (dev->synclko_disable) + data8 = 0; + else if (dev->synclko_125) + data8 = SW_ENABLE_REFCLKO | SW_REFCLKO_IS_125MHZ; + ksz_write8(dev, REG_SW_GLOBAL_OUTPUT_CTRL__1, data8); return 0; } @@ -543,7 +664,8 @@ static int ksz9477_port_vlan_del(struct dsa_switch *ds, int port, } static int ksz9477_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct ksz_device *dev = ds->priv; u32 alu_table[4]; @@ -600,7 +722,8 @@ static int ksz9477_port_fdb_add(struct dsa_switch *ds, int port, } static int ksz9477_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct ksz_device *dev = ds->priv; u32 alu_table[4]; @@ -742,7 +865,8 @@ static int ksz9477_port_fdb_dump(struct dsa_switch *ds, int port, } static int ksz9477_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct ksz_device *dev = ds->priv; u32 static_table[4]; @@ -817,7 +941,8 @@ static int ksz9477_port_mdb_add(struct dsa_switch *ds, int port, } static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct ksz_device *dev = ds->priv; u32 static_table[4]; @@ -893,7 +1018,7 @@ static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port, static int ksz9477_port_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, - bool ingress) + bool ingress, struct netlink_ext_ack *extack) { struct ksz_device *dev = ds->priv; @@ -1315,8 +1440,14 @@ static int ksz9477_setup(struct dsa_switch *ds) /* Do not work correctly with tail tagging. */ ksz_cfg(dev, REG_SW_MAC_CTRL_0, SW_CHECK_LENGTH, false); - /* accept packet up to 2000bytes */ - ksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_LEGAL_PACKET_DISABLE, true); + /* Enable REG_SW_MTU__2 reg by setting SW_JUMBO_PACKET */ + ksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_JUMBO_PACKET, true); + + /* Now we can configure default MTU value */ + ret = regmap_update_bits(dev->regmap[1], REG_SW_MTU__2, REG_SW_MTU_MASK, + VLAN_ETH_FRAME_LEN + ETH_FCS_LEN); + if (ret) + return ret; ksz9477_config_cpu_port(ds); @@ -1362,6 +1493,9 @@ static const struct dsa_switch_ops ksz9477_switch_ops = { .port_mdb_del = ksz9477_port_mdb_del, .port_mirror_add = ksz9477_port_mirror_add, .port_mirror_del = ksz9477_port_mirror_del, + .get_stats64 = ksz9477_get_stats64, + .port_change_mtu = ksz9477_change_mtu, + .port_max_mtu = ksz9477_max_mtu, }; static u32 ksz9477_get_port_addr(int port, int offset) @@ -1521,6 +1655,7 @@ static int ksz9477_switch_init(struct ksz_device *dev) if (!dev->ports) return -ENOMEM; for (i = 0; i < dev->port_cnt; i++) { + spin_lock_init(&dev->ports[i].mib.stats64_lock); mutex_init(&dev->ports[i].mib.cnt_mutex); dev->ports[i].mib.counters = devm_kzalloc(dev->dev, @@ -1549,6 +1684,7 @@ static const struct ksz_dev_ops ksz9477_dev_ops = { .port_setup = ksz9477_port_setup, .r_mib_cnt = ksz9477_r_mib_cnt, .r_mib_pkt = ksz9477_r_mib_pkt, + .r_mib_stat64 = ksz9477_r_mib_stats64, .freeze_mib = ksz9477_freeze_mib, .port_init_cnt = ksz9477_port_init_cnt, .shutdown = ksz9477_reset_switch, diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index f3afb8b8c4cc..cbc0b20e7e1b 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -92,6 +92,7 @@ static const struct of_device_id ksz9477_dt_ids[] = { { .compatible = "microchip,ksz9893" }, { .compatible = "microchip,ksz9563" }, { .compatible = "microchip,ksz9567" }, + { .compatible = "microchip,ksz8563" }, {}, }; MODULE_DEVICE_TABLE(of, ksz9477_dt_ids); diff --git a/drivers/net/dsa/microchip/ksz9477_reg.h b/drivers/net/dsa/microchip/ksz9477_reg.h index 16939f29faa5..0bd58467181f 100644 --- a/drivers/net/dsa/microchip/ksz9477_reg.h +++ b/drivers/net/dsa/microchip/ksz9477_reg.h @@ -176,6 +176,7 @@ #define REG_SW_MAC_ADDR_5 0x0307 #define REG_SW_MTU__2 0x0308 +#define REG_SW_MTU_MASK GENMASK(13, 0) #define REG_SW_ISP_TPID__2 0x030A @@ -1662,4 +1663,6 @@ /* 148,800 frames * 67 ms / 100 */ #define BROADCAST_STORM_VALUE 9969 +#define KSZ9477_MAX_FRAME_SIZE 9000 + #endif /* KSZ9477_REGS_H */ diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 243f8ad6d06e..8014b18d9391 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -130,6 +130,10 @@ static void ksz_mib_read_work(struct work_struct *work) } port_r_cnt(dev, i); p->read = false; + + if (dev->dev_ops->r_mib_stat64) + dev->dev_ops->r_mib_stat64(dev, i); + mutex_unlock(&mib->cnt_mutex); } @@ -213,7 +217,8 @@ EXPORT_SYMBOL_GPL(ksz_get_ethtool_stats); int ksz_port_bridge_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, - bool *tx_fwd_offload) + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { /* port_stp_state_set() will be called after to put the port in * appropriate state so there is no need to do anything. @@ -272,7 +277,8 @@ int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, EXPORT_SYMBOL_GPL(ksz_port_fdb_dump); int ksz_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct ksz_device *dev = ds->priv; struct alu_struct alu; @@ -317,7 +323,8 @@ int ksz_port_mdb_add(struct dsa_switch *ds, int port, EXPORT_SYMBOL_GPL(ksz_port_mdb_add); int ksz_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct ksz_device *dev = ds->priv; struct alu_struct alu; @@ -454,6 +461,12 @@ int ksz_switch_register(struct ksz_device *dev, } dev->synclko_125 = of_property_read_bool(dev->dev->of_node, "microchip,synclko-125"); + dev->synclko_disable = of_property_read_bool(dev->dev->of_node, + "microchip,synclko-disable"); + if (dev->synclko_125 && dev->synclko_disable) { + dev_err(dev->dev, "inconsistent synclko settings\n"); + return -EINVAL; + } } ret = dsa_register_switch(dev->ds); @@ -463,7 +476,7 @@ int ksz_switch_register(struct ksz_device *dev, } /* Read MIB counters every 30 seconds to avoid overflow. */ - dev->mib_read_interval = msecs_to_jiffies(30000); + dev->mib_read_interval = msecs_to_jiffies(5000); /* Start the MIB timer. */ schedule_delayed_work(&dev->mib_read, 0); diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index df8ae59c8525..485d4a948c38 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -22,6 +22,8 @@ struct ksz_port_mib { struct mutex cnt_mutex; /* structure access */ u8 cnt_ptr; u64 *counters; + struct rtnl_link_stats64 stats64; + struct spinlock stats64_lock; }; struct ksz_port { @@ -39,6 +41,7 @@ struct ksz_port { struct ksz_port_mib mib; phy_interface_t interface; + u16 max_frame; }; struct ksz_device { @@ -74,7 +77,9 @@ struct ksz_device { phy_interface_t compat_interface; u32 regs_size; bool phy_errata_9477; + bool ksz87xx_eee_link_erratum; bool synclko_125; + bool synclko_disable; struct vlan_table *vlan_cache; @@ -127,6 +132,7 @@ struct ksz_dev_ops { u64 *cnt); void (*r_mib_pkt)(struct ksz_device *dev, int port, u16 addr, u64 *dropped, u64 *cnt); + void (*r_mib_stat64)(struct ksz_device *dev, int port); void (*freeze_mib)(struct ksz_device *dev, int port, bool freeze); void (*port_init_cnt)(struct ksz_device *dev, int port); int (*shutdown)(struct ksz_device *dev); @@ -155,16 +161,19 @@ void ksz_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, int ksz_sset_count(struct dsa_switch *ds, int port, int sset); void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf); int ksz_port_bridge_join(struct dsa_switch *ds, int port, - struct dsa_bridge bridge, bool *tx_fwd_offload); + struct dsa_bridge bridge, bool *tx_fwd_offload, + struct netlink_ext_ack *extack); void ksz_port_bridge_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge); void ksz_port_fast_age(struct dsa_switch *ds, int port); int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data); int ksz_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb); + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); int ksz_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb); + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy); /* Common register access functions */ diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index a251bc55727f..19f0035d4410 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1186,7 +1186,8 @@ mt7530_port_bridge_flags(struct dsa_switch *ds, int port, static int mt7530_port_bridge_join(struct dsa_switch *ds, int port, - struct dsa_bridge bridge, bool *tx_fwd_offload) + struct dsa_bridge bridge, bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; u32 port_bitmap = BIT(MT7530_CPU_PORT); @@ -1349,7 +1350,8 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port, static int mt7530_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct mt7530_priv *priv = ds->priv; int ret; @@ -1365,7 +1367,8 @@ mt7530_port_fdb_add(struct dsa_switch *ds, int port, static int mt7530_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct mt7530_priv *priv = ds->priv; int ret; @@ -1416,7 +1419,8 @@ mt7530_port_fdb_dump(struct dsa_switch *ds, int port, static int mt7530_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct mt7530_priv *priv = ds->priv; const u8 *addr = mdb->addr; @@ -1442,7 +1446,8 @@ mt7530_port_mdb_add(struct dsa_switch *ds, int port, static int mt7530_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct mt7530_priv *priv = ds->priv; const u8 *addr = mdb->addr; @@ -1709,7 +1714,7 @@ static int mt753x_mirror_port_set(unsigned int id, u32 val) static int mt753x_port_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, - bool ingress) + bool ingress, struct netlink_ext_ack *extack) { struct mt7530_priv *priv = ds->priv; int monitor_port; @@ -2846,7 +2851,7 @@ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port, mcr |= PMCR_RX_FC_EN; } - if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, 0) >= 0) { + if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, false) >= 0) { switch (speed) { case SPEED_1000: mcr |= PMCR_FORCE_EEE1G; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index ab1676553714..64f4fdd02902 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -86,12 +86,16 @@ int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val) int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask, u16 val) { + const unsigned long timeout = jiffies + msecs_to_jiffies(50); u16 data; int err; int i; - /* There's no bus specific operation to wait for a mask */ - for (i = 0; i < 16; i++) { + /* There's no bus specific operation to wait for a mask. Even + * if the initial poll takes longer than 50ms, always do at + * least one more attempt. + */ + for (i = 0; time_before(jiffies, timeout) || (i < 2); i++) { err = mv88e6xxx_read(chip, addr, reg, &data); if (err) return err; @@ -99,7 +103,10 @@ int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg, if ((data & mask) == val) return 0; - usleep_range(1000, 2000); + if (i < 2) + cpu_relax(); + else + usleep_range(1000, 2000); } dev_err(chip->dev, "Timeout while waiting for switch\n"); @@ -563,133 +570,268 @@ static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, return 0; } -static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static const u8 mv88e6185_phy_interface_modes[] = { + [MV88E6185_PORT_STS_CMODE_GMII_FD] = PHY_INTERFACE_MODE_GMII, + [MV88E6185_PORT_STS_CMODE_MII_100_FD_PS] = PHY_INTERFACE_MODE_MII, + [MV88E6185_PORT_STS_CMODE_MII_100] = PHY_INTERFACE_MODE_MII, + [MV88E6185_PORT_STS_CMODE_MII_10] = PHY_INTERFACE_MODE_MII, + [MV88E6185_PORT_STS_CMODE_SERDES] = PHY_INTERFACE_MODE_1000BASEX, + [MV88E6185_PORT_STS_CMODE_1000BASE_X] = PHY_INTERFACE_MODE_1000BASEX, + [MV88E6185_PORT_STS_CMODE_PHY] = PHY_INTERFACE_MODE_SGMII, +}; + +static void mv88e6095_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - if (!phy_interface_mode_is_8023z(state->interface)) { - /* 10M and 100M are only supported in non-802.3z mode */ - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); + u8 cmode = chip->ports[port].cmode; + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100; + + if (mv88e6xxx_phy_is_internal(chip->ds, port)) { + __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); + } else { + if (cmode < ARRAY_SIZE(mv88e6185_phy_interface_modes) && + mv88e6185_phy_interface_modes[cmode]) + __set_bit(mv88e6185_phy_interface_modes[cmode], + config->supported_interfaces); + + config->mac_capabilities |= MAC_1000FD; } } -static void mv88e6185_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6185_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - /* FIXME: if the port is in 1000Base-X mode, then it only supports - * 1000M FD speeds. In this case, CMODE will indicate 5. + u8 cmode = chip->ports[port].cmode; + + if (cmode < ARRAY_SIZE(mv88e6185_phy_interface_modes) && + mv88e6185_phy_interface_modes[cmode]) + __set_bit(mv88e6185_phy_interface_modes[cmode], + config->supported_interfaces); + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; +} + +static const u8 mv88e6xxx_phy_interface_modes[] = { + [MV88E6XXX_PORT_STS_CMODE_MII_PHY] = PHY_INTERFACE_MODE_MII, + [MV88E6XXX_PORT_STS_CMODE_MII] = PHY_INTERFACE_MODE_MII, + [MV88E6XXX_PORT_STS_CMODE_GMII] = PHY_INTERFACE_MODE_GMII, + [MV88E6XXX_PORT_STS_CMODE_RMII_PHY] = PHY_INTERFACE_MODE_RMII, + [MV88E6XXX_PORT_STS_CMODE_RMII] = PHY_INTERFACE_MODE_RMII, + [MV88E6XXX_PORT_STS_CMODE_100BASEX] = PHY_INTERFACE_MODE_100BASEX, + [MV88E6XXX_PORT_STS_CMODE_1000BASEX] = PHY_INTERFACE_MODE_1000BASEX, + [MV88E6XXX_PORT_STS_CMODE_SGMII] = PHY_INTERFACE_MODE_SGMII, + /* higher interface modes are not needed here, since ports supporting + * them are writable, and so the supported interfaces are filled in the + * corresponding .phylink_set_interfaces() implementation below */ - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); +}; - mv88e6065_phylink_validate(chip, port, mask, state); +static void mv88e6xxx_translate_cmode(u8 cmode, unsigned long *supported) +{ + if (cmode < ARRAY_SIZE(mv88e6xxx_phy_interface_modes) && + mv88e6xxx_phy_interface_modes[cmode]) + __set_bit(mv88e6xxx_phy_interface_modes[cmode], supported); + else if (cmode == MV88E6XXX_PORT_STS_CMODE_RGMII) + phy_interface_set_rgmii(supported); } -static void mv88e6341_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6250_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - if (port >= 5) - phylink_set(mask, 2500baseX_Full); + unsigned long *supported = config->supported_interfaces; - /* No ethtool bits for 200Mbps */ - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); + /* Translate the default cmode */ + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); - mv88e6065_phylink_validate(chip, port, mask, state); + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100; } -static void mv88e6352_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static int mv88e6352_get_port4_serdes_cmode(struct mv88e6xxx_chip *chip) { - /* No ethtool bits for 200Mbps */ - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); + u16 reg, val; + int err; - mv88e6065_phylink_validate(chip, port, mask, state); + err = mv88e6xxx_port_read(chip, 4, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + /* If PHY_DETECT is zero, then we are not in auto-media mode */ + if (!(reg & MV88E6XXX_PORT_STS_PHY_DETECT)) + return 0xf; + + val = reg & ~MV88E6XXX_PORT_STS_PHY_DETECT; + err = mv88e6xxx_port_write(chip, 4, MV88E6XXX_PORT_STS, val); + if (err) + return err; + + err = mv88e6xxx_port_read(chip, 4, MV88E6XXX_PORT_STS, &val); + if (err) + return err; + + /* Restore PHY_DETECT value */ + err = mv88e6xxx_port_write(chip, 4, MV88E6XXX_PORT_STS, reg); + if (err) + return err; + + return val & MV88E6XXX_PORT_STS_CMODE_MASK; } -static void mv88e6390_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6352_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - if (port >= 9) { - phylink_set(mask, 2500baseX_Full); - phylink_set(mask, 2500baseT_Full); + unsigned long *supported = config->supported_interfaces; + int err, cmode; + + /* Translate the default cmode */ + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; + + /* Port 4 supports automedia if the serdes is associated with it. */ + if (port == 4) { + mv88e6xxx_reg_lock(chip); + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err < 0) + dev_err(chip->dev, "p%d: failed to read scratch\n", + port); + if (err <= 0) + goto unlock; + + cmode = mv88e6352_get_port4_serdes_cmode(chip); + if (cmode < 0) + dev_err(chip->dev, "p%d: failed to read serdes cmode\n", + port); + else + mv88e6xxx_translate_cmode(cmode, supported); +unlock: + mv88e6xxx_reg_unlock(chip); } +} + +static void mv88e6341_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) +{ + unsigned long *supported = config->supported_interfaces; + + /* Translate the default cmode */ + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); /* No ethtool bits for 200Mbps */ - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; - mv88e6065_phylink_validate(chip, port, mask, state); -} + /* The C_Mode field is programmable on port 5 */ + if (port == 5) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); -static void mv88e6390x_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) -{ - if (port >= 9) { - phylink_set(mask, 10000baseT_Full); - phylink_set(mask, 10000baseKR_Full); + config->mac_capabilities |= MAC_2500FD; } - - mv88e6390_phylink_validate(chip, port, mask, state); } -static void mv88e6393x_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6390_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { + unsigned long *supported = config->supported_interfaces; + + /* Translate the default cmode */ + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); + + /* No ethtool bits for 200Mbps */ + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; + + /* The C_Mode field is programmable on ports 9 and 10 */ + if (port == 9 || port == 10) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); + + config->mac_capabilities |= MAC_2500FD; + } +} + +static void mv88e6390x_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) +{ + unsigned long *supported = config->supported_interfaces; + + mv88e6390_phylink_get_caps(chip, port, config); + + /* For the 6x90X, ports 2-7 can be in automedia mode. + * (Note that 6x90 doesn't support RXAUI nor XAUI). + * + * Port 2 can also support 1000BASE-X in automedia mode if port 9 is + * configured for 1000BASE-X, SGMII or 2500BASE-X. + * Port 3-4 can also support 1000BASE-X in automedia mode if port 9 is + * configured for RXAUI, 1000BASE-X, SGMII or 2500BASE-X. + * + * Port 5 can also support 1000BASE-X in automedia mode if port 10 is + * configured for 1000BASE-X, SGMII or 2500BASE-X. + * Port 6-7 can also support 1000BASE-X in automedia mode if port 10 is + * configured for RXAUI, 1000BASE-X, SGMII or 2500BASE-X. + * + * For now, be permissive (as the old code was) and allow 1000BASE-X + * on ports 2..7. + */ + if (port >= 2 && port <= 7) + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + + /* The C_Mode field can also be programmed for 10G speeds */ + if (port == 9 || port == 10) { + __set_bit(PHY_INTERFACE_MODE_XAUI, supported); + __set_bit(PHY_INTERFACE_MODE_RXAUI, supported); + + config->mac_capabilities |= MAC_10000FD; + } +} + +static void mv88e6393x_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) +{ + unsigned long *supported = config->supported_interfaces; bool is_6191x = chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6191X; - if (((port == 0 || port == 9) && !is_6191x) || port == 10) { - phylink_set(mask, 10000baseT_Full); - phylink_set(mask, 10000baseKR_Full); - phylink_set(mask, 10000baseCR_Full); - phylink_set(mask, 10000baseSR_Full); - phylink_set(mask, 10000baseLR_Full); - phylink_set(mask, 10000baseLRM_Full); - phylink_set(mask, 10000baseER_Full); - phylink_set(mask, 5000baseT_Full); - phylink_set(mask, 2500baseX_Full); - phylink_set(mask, 2500baseT_Full); + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; + + /* The C_Mode field can be programmed for ports 0, 9 and 10 */ + if (port == 0 || port == 9 || port == 10) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + + /* 6191X supports >1G modes only on port 10 */ + if (!is_6191x || port == 10) { + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); + __set_bit(PHY_INTERFACE_MODE_5GBASER, supported); + __set_bit(PHY_INTERFACE_MODE_10GBASER, supported); + /* FIXME: USXGMII is not supported yet */ + /* __set_bit(PHY_INTERFACE_MODE_USXGMII, supported); */ + + config->mac_capabilities |= MAC_2500FD | MAC_5000FD | + MAC_10000FD; + } } - - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); - - mv88e6065_phylink_validate(chip, port, mask, state); } -static void mv88e6xxx_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void mv88e6xxx_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; struct mv88e6xxx_chip *chip = ds->priv; - /* Allow all the expected bits */ - phylink_set(mask, Autoneg); - phylink_set(mask, Pause); - phylink_set_port_modes(mask); + chip->info->ops->phylink_get_caps(chip, port, config); - if (chip->info->ops->phylink_validate) - chip->info->ops->phylink_validate(chip, port, mask, state); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); - - /* We can only operate at 2500BaseX or 1000BaseX. If requested - * to advertise both, only report advertising at 2500BaseX. - */ - phylink_helper_basex_speed(state); + /* Internal ports need GMII for PHYLIB */ + if (mv88e6xxx_phy_is_internal(ds, port)) + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); } static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, @@ -1283,8 +1425,15 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) pvlan = 0; - /* Frames from user ports can egress any local DSA links and CPU ports, - * as well as any local member of their bridge group. + /* Frames from standalone user ports can only egress on the + * upstream port. + */ + if (!dsa_port_bridge_dev_get(dp)) + return BIT(dsa_switch_upstream_port(ds)); + + /* Frames from bridged user ports can egress any local DSA + * links and CPU ports, as well as any local member of their + * bridge group. */ dsa_switch_for_each_port(other_dp, ds) if (other_dp->type == DSA_PORT_TYPE_CPU || @@ -1476,15 +1625,16 @@ static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port) ds = dsa_switch_find(dst->index, dev); dp = ds ? dsa_to_port(ds, port) : NULL; - if (dp && dp->lag_dev) { + if (dp && dp->lag) { /* As the PVT is used to limit flooding of * FORWARD frames, which use the LAG ID as the * source port, we must translate dev/port to * the special "LAG device" in the PVT, using - * the LAG ID as the port number. + * the LAG ID (one-based) as the port number + * (zero-based). */ dev = MV88E6XXX_G2_PVT_ADDR_DEV_TRUNK; - port = dsa_lag_id(dst, dp->lag_dev); + port = dsa_port_lag_id_get(dp) - 1; } } @@ -1517,24 +1667,31 @@ static int mv88e6xxx_pvt_setup(struct mv88e6xxx_chip *chip) return 0; } +static int mv88e6xxx_port_fast_age_fid(struct mv88e6xxx_chip *chip, int port, + u16 fid) +{ + if (dsa_to_port(chip->ds, port)->lag) + /* Hardware is incapable of fast-aging a LAG through a + * regular ATU move operation. Until we have something + * more fancy in place this is a no-op. + */ + return -EOPNOTSUPP; + + return mv88e6xxx_g1_atu_remove(chip, fid, port, false); +} + static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) { struct mv88e6xxx_chip *chip = ds->priv; int err; - if (dsa_to_port(ds, port)->lag_dev) - /* Hardware is incapable of fast-aging a LAG through a - * regular ATU move operation. Until we have something - * more fancy in place this is a no-op. - */ - return; - mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_g1_atu_remove(chip, 0, port, false); + err = mv88e6xxx_port_fast_age_fid(chip, port, 0); mv88e6xxx_reg_unlock(chip); if (err) - dev_err(ds->dev, "p%d: failed to flush ATU\n", port); + dev_err(chip->ds->dev, "p%d: failed to flush ATU: %d\n", + port, err); } static int mv88e6xxx_vtu_setup(struct mv88e6xxx_chip *chip) @@ -1616,21 +1773,11 @@ static int mv88e6xxx_fid_map_vlan(struct mv88e6xxx_chip *chip, int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *fid_bitmap) { - int i, err; - u16 fid; - bitmap_zero(fid_bitmap, MV88E6XXX_N_FID); - /* Set every FID bit used by the (un)bridged ports */ - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - err = mv88e6xxx_port_get_fid(chip, i, &fid); - if (err) - return err; - - set_bit(fid, fid_bitmap); - } - - /* Set every FID bit used by the VLAN entries */ + /* Every FID has an associated VID, so walking the VTU + * will discover the full set of FIDs in use. + */ return mv88e6xxx_vtu_walk(chip, mv88e6xxx_fid_map_vlan, fid_bitmap); } @@ -1643,10 +1790,7 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) if (err) return err; - /* The reset value 0x000 is used to indicate that multiple address - * databases are not needed. Return the next positive available. - */ - *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1); + *fid = find_first_zero_bit(fid_bitmap, MV88E6XXX_N_FID); if (unlikely(*fid >= mv88e6xxx_num_databases(chip))) return -ENOSPC; @@ -1654,6 +1798,187 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) return mv88e6xxx_g1_atu_flush(chip, *fid, true); } +static int mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry) +{ + if (!chip->info->ops->stu_loadpurge) + return -EOPNOTSUPP; + + return chip->info->ops->stu_loadpurge(chip, entry); +} + +static int mv88e6xxx_stu_setup(struct mv88e6xxx_chip *chip) +{ + struct mv88e6xxx_stu_entry stu = { + .valid = true, + .sid = 0 + }; + + if (!mv88e6xxx_has_stu(chip)) + return 0; + + /* Make sure that SID 0 is always valid. This is used by VTU + * entries that do not make use of the STU, e.g. when creating + * a VLAN upper on a port that is also part of a VLAN + * filtering bridge. + */ + return mv88e6xxx_stu_loadpurge(chip, &stu); +} + +static int mv88e6xxx_sid_get(struct mv88e6xxx_chip *chip, u8 *sid) +{ + DECLARE_BITMAP(busy, MV88E6XXX_N_SID) = { 0 }; + struct mv88e6xxx_mst *mst; + + __set_bit(0, busy); + + list_for_each_entry(mst, &chip->msts, node) + __set_bit(mst->stu.sid, busy); + + *sid = find_first_zero_bit(busy, MV88E6XXX_N_SID); + + return (*sid >= mv88e6xxx_max_sid(chip)) ? -ENOSPC : 0; +} + +static int mv88e6xxx_mst_put(struct mv88e6xxx_chip *chip, u8 sid) +{ + struct mv88e6xxx_mst *mst, *tmp; + int err; + + if (!sid) + return 0; + + list_for_each_entry_safe(mst, tmp, &chip->msts, node) { + if (mst->stu.sid != sid) + continue; + + if (!refcount_dec_and_test(&mst->refcnt)) + return 0; + + mst->stu.valid = false; + err = mv88e6xxx_stu_loadpurge(chip, &mst->stu); + if (err) { + refcount_set(&mst->refcnt, 1); + return err; + } + + list_del(&mst->node); + kfree(mst); + return 0; + } + + return -ENOENT; +} + +static int mv88e6xxx_mst_get(struct mv88e6xxx_chip *chip, struct net_device *br, + u16 msti, u8 *sid) +{ + struct mv88e6xxx_mst *mst; + int err, i; + + if (!mv88e6xxx_has_stu(chip)) { + err = -EOPNOTSUPP; + goto err; + } + + if (!msti) { + *sid = 0; + return 0; + } + + list_for_each_entry(mst, &chip->msts, node) { + if (mst->br == br && mst->msti == msti) { + refcount_inc(&mst->refcnt); + *sid = mst->stu.sid; + return 0; + } + } + + err = mv88e6xxx_sid_get(chip, sid); + if (err) + goto err; + + mst = kzalloc(sizeof(*mst), GFP_KERNEL); + if (!mst) { + err = -ENOMEM; + goto err; + } + + INIT_LIST_HEAD(&mst->node); + refcount_set(&mst->refcnt, 1); + mst->br = br; + mst->msti = msti; + mst->stu.valid = true; + mst->stu.sid = *sid; + + /* The bridge starts out all ports in the disabled state. But + * a STU state of disabled means to go by the port-global + * state. So we set all user port's initial state to blocking, + * to match the bridge's behavior. + */ + for (i = 0; i < mv88e6xxx_num_ports(chip); i++) + mst->stu.state[i] = dsa_is_user_port(chip->ds, i) ? + MV88E6XXX_PORT_CTL0_STATE_BLOCKING : + MV88E6XXX_PORT_CTL0_STATE_DISABLED; + + err = mv88e6xxx_stu_loadpurge(chip, &mst->stu); + if (err) + goto err_free; + + list_add_tail(&mst->node, &chip->msts); + return 0; + +err_free: + kfree(mst); +err: + return err; +} + +static int mv88e6xxx_port_mst_state_set(struct dsa_switch *ds, int port, + const struct switchdev_mst_state *st) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct mv88e6xxx_chip *chip = ds->priv; + struct mv88e6xxx_mst *mst; + u8 state; + int err; + + if (!mv88e6xxx_has_stu(chip)) + return -EOPNOTSUPP; + + switch (st->state) { + case BR_STATE_DISABLED: + case BR_STATE_BLOCKING: + case BR_STATE_LISTENING: + state = MV88E6XXX_PORT_CTL0_STATE_BLOCKING; + break; + case BR_STATE_LEARNING: + state = MV88E6XXX_PORT_CTL0_STATE_LEARNING; + break; + case BR_STATE_FORWARDING: + state = MV88E6XXX_PORT_CTL0_STATE_FORWARDING; + break; + default: + return -EINVAL; + } + + list_for_each_entry(mst, &chip->msts, node) { + if (mst->br == dsa_port_bridge_dev_get(dp) && + mst->msti == st->msti) { + if (mst->stu.state[port] == state) + return 0; + + mst->stu.state[port] = state; + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_stu_loadpurge(chip, &mst->stu); + mv88e6xxx_reg_unlock(chip); + return err; + } + } + + return -ENOENT; +} + static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, u16 vid) { @@ -2138,6 +2463,9 @@ static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port, if (!vlan.valid) { memset(&vlan, 0, sizeof(vlan)); + if (vid == MV88E6XXX_VID_STANDALONE) + vlan.policy = true; + err = mv88e6xxx_atu_new(chip, &vlan.fid); if (err) return err; @@ -2270,6 +2598,12 @@ static int mv88e6xxx_port_vlan_leave(struct mv88e6xxx_chip *chip, if (err) return err; + if (!vlan.valid) { + err = mv88e6xxx_mst_put(chip, vlan.sid); + if (err) + return err; + } + return mv88e6xxx_g1_atu_remove(chip, vlan.fid, port, false); } @@ -2315,8 +2649,75 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, return err; } +static int mv88e6xxx_port_vlan_fast_age(struct dsa_switch *ds, int port, u16 vid) +{ + struct mv88e6xxx_chip *chip = ds->priv; + struct mv88e6xxx_vtu_entry vlan; + int err; + + mv88e6xxx_reg_lock(chip); + + err = mv88e6xxx_vtu_get(chip, vid, &vlan); + if (err) + goto unlock; + + err = mv88e6xxx_port_fast_age_fid(chip, port, vlan.fid); + +unlock: + mv88e6xxx_reg_unlock(chip); + + return err; +} + +static int mv88e6xxx_vlan_msti_set(struct dsa_switch *ds, + struct dsa_bridge bridge, + const struct switchdev_vlan_msti *msti) +{ + struct mv88e6xxx_chip *chip = ds->priv; + struct mv88e6xxx_vtu_entry vlan; + u8 old_sid, new_sid; + int err; + + if (!mv88e6xxx_has_stu(chip)) + return -EOPNOTSUPP; + + mv88e6xxx_reg_lock(chip); + + err = mv88e6xxx_vtu_get(chip, msti->vid, &vlan); + if (err) + goto unlock; + + if (!vlan.valid) { + err = -EINVAL; + goto unlock; + } + + old_sid = vlan.sid; + + err = mv88e6xxx_mst_get(chip, bridge.dev, msti->msti, &new_sid); + if (err) + goto unlock; + + if (new_sid != old_sid) { + vlan.sid = new_sid; + + err = mv88e6xxx_vtu_loadpurge(chip, &vlan); + if (err) { + mv88e6xxx_mst_put(chip, new_sid); + goto unlock; + } + } + + err = mv88e6xxx_mst_put(chip, old_sid); + +unlock: + mv88e6xxx_reg_unlock(chip); + return err; +} + static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct mv88e6xxx_chip *chip = ds->priv; int err; @@ -2330,7 +2731,8 @@ static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, } static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct mv88e6xxx_chip *chip = ds->priv; int err; @@ -2476,7 +2878,8 @@ static int mv88e6xxx_map_virtual_bridge_to_pvt(struct dsa_switch *ds, static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, - bool *tx_fwd_offload) + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { struct mv88e6xxx_chip *chip = ds->priv; int err; @@ -2487,6 +2890,10 @@ static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, if (err) goto unlock; + err = mv88e6xxx_port_set_map_da(chip, port, true); + if (err) + goto unlock; + err = mv88e6xxx_port_commit_pvid(chip, port); if (err) goto unlock; @@ -2521,6 +2928,12 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port, mv88e6xxx_port_vlan_map(chip, port)) dev_err(ds->dev, "failed to remap in-chip Port VLAN\n"); + err = mv88e6xxx_port_set_map_da(chip, port, false); + if (err) + dev_err(ds->dev, + "port %d failed to restore map-DA: %pe\n", + port, ERR_PTR(err)); + err = mv88e6xxx_port_commit_pvid(chip, port); if (err) dev_err(ds->dev, @@ -2532,7 +2945,8 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port, static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, int tree_index, int sw_index, - int port, struct dsa_bridge bridge) + int port, struct dsa_bridge bridge, + struct netlink_ext_ack *extack) { struct mv88e6xxx_chip *chip = ds->priv; int err; @@ -2864,7 +3278,10 @@ static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port) static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) { + struct device_node *phy_handle = NULL; struct dsa_switch *ds = chip->ds; + struct dsa_port *dp; + int tx_amp; int err; u16 reg; @@ -2918,12 +3335,13 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return err; /* Port Control 2: don't force a good FCS, set the MTU size to - * 10222 bytes, disable 802.1q tags checking, don't discard tagged or - * untagged frames on this port, do a destination address lookup on all - * received packets as usual, disable ARP mirroring and don't send a - * copy of all transmitted/received frames on this port to the CPU. + * 10222 bytes, disable 802.1q tags checking, don't discard + * tagged or untagged frames on this port, skip destination + * address lookup on user ports, disable ARP mirroring and don't + * send a copy of all transmitted/received frames on this port + * to the CPU. */ - err = mv88e6xxx_port_set_map_da(chip, port); + err = mv88e6xxx_port_set_map_da(chip, port, !dsa_is_user_port(ds, port)); if (err) return err; @@ -2931,8 +3349,44 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (err) return err; + /* On chips that support it, set all downstream DSA ports' + * VLAN policy to TRAP. In combination with loading + * MV88E6XXX_VID_STANDALONE as a policy entry in the VTU, this + * provides a better isolation barrier between standalone + * ports, as the ATU is bypassed on any intermediate switches + * between the incoming port and the CPU. + */ + if (dsa_is_downstream_port(ds, port) && + chip->info->ops->port_set_policy) { + err = chip->info->ops->port_set_policy(chip, port, + MV88E6XXX_POLICY_MAPPING_VTU, + MV88E6XXX_POLICY_ACTION_TRAP); + if (err) + return err; + } + + /* User ports start out in standalone mode and 802.1Q is + * therefore disabled. On DSA ports, all valid VIDs are always + * loaded in the VTU - therefore, enable 802.1Q in order to take + * advantage of VLAN policy on chips that supports it. + */ err = mv88e6xxx_port_set_8021q_mode(chip, port, - MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED); + dsa_is_user_port(ds, port) ? + MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED : + MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE); + if (err) + return err; + + /* Bind MV88E6XXX_VID_STANDALONE to MV88E6XXX_FID_STANDALONE by + * virtue of the fact that mv88e6xxx_atu_new() will pick it as + * the first free FID. This will be used as the private PVID for + * unbridged ports. Shared (DSA and CPU) ports must also be + * members of this VID, in order to trap all frames assigned to + * it to the CPU. + */ + err = mv88e6xxx_port_vlan_join(chip, port, MV88E6XXX_VID_STANDALONE, + MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED, + false); if (err) return err; @@ -2945,7 +3399,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) * relying on their port default FID. */ err = mv88e6xxx_port_vlan_join(chip, port, MV88E6XXX_VID_BRIDGED, - MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED, + MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED, false); if (err) return err; @@ -3018,6 +3472,23 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return err; } + if (chip->info->ops->serdes_set_tx_amplitude) { + dp = dsa_to_port(ds, port); + if (dp) + phy_handle = of_parse_phandle(dp->dn, "phy-handle", 0); + + if (phy_handle && !of_property_read_u32(phy_handle, + "tx-p2p-microvolt", + &tx_amp)) + err = chip->info->ops->serdes_set_tx_amplitude(chip, + port, tx_amp); + if (phy_handle) { + of_node_put(phy_handle); + if (err) + return err; + } + } + /* Port based VLAN map: give each port the same default address * database, and allow bidirectional communication between the * CPU and DSA port(s), and the other ports. @@ -3216,6 +3687,13 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) if (err) goto unlock; + /* Must be called after mv88e6xxx_vtu_setup (which flushes the + * VTU, thereby also flushing the STU). + */ + err = mv88e6xxx_stu_setup(chip); + if (err) + goto unlock; + /* Setup Switch Port Registers */ for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { if (dsa_is_unused_port(ds, i)) @@ -3589,7 +4067,9 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .rmu_disable = mv88e6085_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3623,7 +4103,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6095_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3639,6 +4119,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_sync_link = mv88e6185_port_sync_link, .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, @@ -3669,7 +4150,9 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .rmu_disable = mv88e6085_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6095_phylink_get_caps, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3706,7 +4189,9 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3747,7 +4232,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6141_ops = { @@ -3795,6 +4280,8 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6341_serdes_get_lane, /* Check status register pause & lpa register */ @@ -3811,7 +4298,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, - .phylink_validate = mv88e6341_phylink_validate, + .phylink_get_caps = mv88e6341_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -3851,9 +4338,11 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .avb_ops = &mv88e6165_avb_ops, .ptp_ops = &mv88e6165_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3887,9 +4376,11 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .avb_ops = &mv88e6165_avb_ops, .ptp_ops = &mv88e6165_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -3931,7 +4422,9 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -3977,6 +4470,8 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, .serdes_pcs_config = mv88e6352_serdes_pcs_config, @@ -3986,7 +4481,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6352_phylink_validate, + .phylink_get_caps = mv88e6352_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -4028,7 +4523,9 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -4074,6 +4571,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, .serdes_pcs_config = mv88e6352_serdes_pcs_config, @@ -4085,8 +4584,9 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .serdes_irq_status = mv88e6352_serdes_irq_status, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, + .serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6352_phylink_validate, + .phylink_get_caps = mv88e6352_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -4125,7 +4625,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -4172,6 +4672,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, /* Check status register pause & lpa register */ @@ -4187,7 +4689,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6390_phylink_validate, + .phylink_get_caps = mv88e6390_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -4233,6 +4735,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390x_serdes_get_lane, /* Check status register pause & lpa register */ @@ -4248,7 +4752,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6390x_phylink_validate, + .phylink_get_caps = mv88e6390x_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -4292,6 +4796,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, /* Check status register pause & lpa register */ @@ -4308,7 +4814,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .serdes_get_regs = mv88e6390_serdes_get_regs, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6390_phylink_validate, + .phylink_get_caps = mv88e6390_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -4354,6 +4860,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, .serdes_pcs_config = mv88e6352_serdes_pcs_config, @@ -4365,10 +4873,11 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .serdes_irq_status = mv88e6352_serdes_irq_status, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, + .serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6352_phylink_validate, + .phylink_get_caps = mv88e6352_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6250_ops = { @@ -4408,7 +4917,7 @@ static const struct mv88e6xxx_ops mv88e6250_ops = { .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6250_ptp_ops, - .phylink_validate = mv88e6065_phylink_validate, + .phylink_get_caps = mv88e6250_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -4453,6 +4962,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, /* Check status register pause & lpa register */ @@ -4470,7 +4981,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6390_phylink_validate, + .phylink_get_caps = mv88e6390_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -4514,7 +5025,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -4556,7 +5067,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6341_ops = { @@ -4604,6 +5115,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6341_serdes_get_lane, /* Check status register pause & lpa register */ @@ -4622,7 +5135,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, - .phylink_validate = mv88e6341_phylink_validate, + .phylink_get_caps = mv88e6341_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -4664,7 +5177,9 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -4706,9 +5221,11 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -4754,6 +5271,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, .serdes_pcs_config = mv88e6352_serdes_pcs_config, @@ -4771,7 +5290,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .serdes_get_stats = mv88e6352_serdes_get_stats, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, - .phylink_validate = mv88e6352_phylink_validate, + .serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude, + .phylink_get_caps = mv88e6352_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6390_ops = { @@ -4818,6 +5338,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, /* Check status register pause & lpa register */ @@ -4836,7 +5358,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, - .phylink_validate = mv88e6390_phylink_validate, + .phylink_get_caps = mv88e6390_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -4883,6 +5405,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390x_serdes_get_lane, .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, @@ -4900,7 +5424,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6390x_phylink_validate, + .phylink_get_caps = mv88e6390x_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6393x_ops = { @@ -4951,6 +5475,8 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6393x_serdes_power, .serdes_get_lane = mv88e6393x_serdes_get_lane, .serdes_pcs_get_state = mv88e6393x_serdes_pcs_get_state, @@ -4964,7 +5490,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6393x_phylink_validate, + .phylink_get_caps = mv88e6393x_phylink_get_caps, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { @@ -4977,6 +5503,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 10, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5019,6 +5546,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 11, .num_internal_phys = 8, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5042,6 +5570,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 3, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5086,6 +5615,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 5, .num_gpio = 11, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x10, .global1_addr = 0x1b, @@ -5109,6 +5639,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 6, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5133,6 +5664,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 6, .num_internal_phys = 0, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5156,6 +5688,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 7, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5180,6 +5713,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 5, .num_gpio = 15, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5203,6 +5737,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 7, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5227,6 +5762,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 5, .num_gpio = 15, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5272,6 +5808,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 9, .num_gpio = 16, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5295,6 +5832,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 9, .num_gpio = 16, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5317,6 +5855,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5339,6 +5878,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5361,6 +5901,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5411,6 +5952,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 5, .num_gpio = 15, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5456,6 +5998,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 9, .num_gpio = 16, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5529,6 +6072,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 6, .num_gpio = 11, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x10, .global1_addr = 0x1b, @@ -5553,6 +6097,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 7, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5576,6 +6121,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 7, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5600,6 +6146,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 5, .num_gpio = 15, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5624,6 +6171,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 9, .num_gpio = 16, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5648,6 +6196,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 9, .num_gpio = 16, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5671,6 +6220,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5739,6 +6289,7 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev) mutex_init(&chip->reg_lock); INIT_LIST_HEAD(&chip->mdios); idr_init(&chip->policies); + INIT_LIST_HEAD(&chip->msts); return chip; } @@ -5791,7 +6342,8 @@ static int mv88e6xxx_change_tag_protocol(struct dsa_switch *ds, int port, } static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct mv88e6xxx_chip *chip = ds->priv; int err; @@ -5805,7 +6357,8 @@ static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port, } static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct mv88e6xxx_chip *chip = ds->priv; int err; @@ -5819,7 +6372,8 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port, static int mv88e6xxx_port_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, - bool ingress) + bool ingress, + struct netlink_ext_ack *extack) { enum mv88e6xxx_egress_direction direction = ingress ? MV88E6XXX_EGRESS_DIR_INGRESS : @@ -5893,7 +6447,7 @@ static int mv88e6xxx_port_pre_bridge_flags(struct dsa_switch *ds, int port, const struct mv88e6xxx_ops *ops; if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | - BR_BCAST_FLOOD)) + BR_BCAST_FLOOD | BR_PORT_LOCKED)) return -EINVAL; ops = chip->info->ops; @@ -5951,6 +6505,13 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port, goto out; } + if (flags.mask & BR_PORT_LOCKED) { + bool locked = !!(flags.val & BR_PORT_LOCKED); + + err = mv88e6xxx_port_set_lock(chip, port, locked); + if (err) + goto out; + } out: mv88e6xxx_reg_unlock(chip); @@ -5958,21 +6519,20 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port, } static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds, - struct net_device *lag, + struct dsa_lag lag, struct netdev_lag_upper_info *info) { struct mv88e6xxx_chip *chip = ds->priv; struct dsa_port *dp; - int id, members = 0; + int members = 0; if (!mv88e6xxx_has_lag(chip)) return false; - id = dsa_lag_id(ds->dst, lag); - if (id < 0 || id >= ds->num_lag_ids) + if (!lag.id) return false; - dsa_lag_foreach_port(dp, ds->dst, lag) + dsa_lag_foreach_port(dp, ds->dst, &lag) /* Includes the port joining the LAG */ members++; @@ -5992,20 +6552,21 @@ static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds, return true; } -static int mv88e6xxx_lag_sync_map(struct dsa_switch *ds, struct net_device *lag) +static int mv88e6xxx_lag_sync_map(struct dsa_switch *ds, struct dsa_lag lag) { struct mv88e6xxx_chip *chip = ds->priv; struct dsa_port *dp; u16 map = 0; int id; - id = dsa_lag_id(ds->dst, lag); + /* DSA LAG IDs are one-based, hardware is zero-based */ + id = lag.id - 1; /* Build the map of all ports to distribute flows destined for * this LAG. This can be either a local user port, or a DSA * port if the LAG port is on a remote chip. */ - dsa_lag_foreach_port(dp, ds->dst, lag) + dsa_lag_foreach_port(dp, ds->dst, &lag) map |= BIT(dsa_towards_port(ds, dp->ds->index, dp->index)); return mv88e6xxx_g2_trunk_mapping_write(chip, id, map); @@ -6050,8 +6611,8 @@ static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds) { struct mv88e6xxx_chip *chip = ds->priv; unsigned int id, num_tx; - struct net_device *lag; struct dsa_port *dp; + struct dsa_lag *lag; int i, err, nth; u16 mask[8]; u16 ivec; @@ -6060,8 +6621,8 @@ static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds) ivec = BIT(mv88e6xxx_num_ports(chip)) - 1; /* Disable all masks for ports that _are_ members of a LAG. */ - list_for_each_entry(dp, &ds->dst->ports, list) { - if (!dp->lag_dev || dp->ds != ds) + dsa_switch_for_each_port(dp, ds) { + if (!dp->lag) continue; ivec &= ~BIT(dp->index); @@ -6074,7 +6635,7 @@ static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds) * are in the Tx set. */ dsa_lags_foreach_id(id, ds->dst) { - lag = dsa_lag_dev(ds->dst, id); + lag = dsa_lag_by_id(ds->dst, id); if (!lag) continue; @@ -6110,7 +6671,7 @@ static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds) } static int mv88e6xxx_lag_sync_masks_map(struct dsa_switch *ds, - struct net_device *lag) + struct dsa_lag lag) { int err; @@ -6134,7 +6695,7 @@ static int mv88e6xxx_port_lag_change(struct dsa_switch *ds, int port) } static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port, - struct net_device *lag, + struct dsa_lag lag, struct netdev_lag_upper_info *info) { struct mv88e6xxx_chip *chip = ds->priv; @@ -6143,7 +6704,8 @@ static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port, if (!mv88e6xxx_lag_can_offload(ds, lag, info)) return -EOPNOTSUPP; - id = dsa_lag_id(ds->dst, lag); + /* DSA LAG IDs are one-based */ + id = lag.id - 1; mv88e6xxx_reg_lock(chip); @@ -6166,7 +6728,7 @@ static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port, } static int mv88e6xxx_port_lag_leave(struct dsa_switch *ds, int port, - struct net_device *lag) + struct dsa_lag lag) { struct mv88e6xxx_chip *chip = ds->priv; int err_sync, err_trunk; @@ -6191,7 +6753,7 @@ static int mv88e6xxx_crosschip_lag_change(struct dsa_switch *ds, int sw_index, } static int mv88e6xxx_crosschip_lag_join(struct dsa_switch *ds, int sw_index, - int port, struct net_device *lag, + int port, struct dsa_lag lag, struct netdev_lag_upper_info *info) { struct mv88e6xxx_chip *chip = ds->priv; @@ -6214,7 +6776,7 @@ static int mv88e6xxx_crosschip_lag_join(struct dsa_switch *ds, int sw_index, } static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index, - int port, struct net_device *lag) + int port, struct dsa_lag lag) { struct mv88e6xxx_chip *chip = ds->priv; int err_sync, err_pvt; @@ -6233,7 +6795,7 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .teardown = mv88e6xxx_teardown, .port_setup = mv88e6xxx_port_setup, .port_teardown = mv88e6xxx_port_teardown, - .phylink_validate = mv88e6xxx_validate, + .phylink_get_caps = mv88e6xxx_get_caps, .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state, .phylink_mac_config = mv88e6xxx_mac_config, .phylink_mac_an_restart = mv88e6xxx_serdes_pcs_an_restart, @@ -6261,10 +6823,13 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .port_pre_bridge_flags = mv88e6xxx_port_pre_bridge_flags, .port_bridge_flags = mv88e6xxx_port_bridge_flags, .port_stp_state_set = mv88e6xxx_port_stp_state_set, + .port_mst_state_set = mv88e6xxx_port_mst_state_set, .port_fast_age = mv88e6xxx_port_fast_age, + .port_vlan_fast_age = mv88e6xxx_port_vlan_fast_age, .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, .port_vlan_add = mv88e6xxx_port_vlan_add, .port_vlan_del = mv88e6xxx_port_vlan_del, + .vlan_msti_set = mv88e6xxx_vlan_msti_set, .port_fdb_add = mv88e6xxx_port_fdb_add, .port_fdb_del = mv88e6xxx_port_fdb_del, .port_fdb_dump = mv88e6xxx_port_fdb_dump, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 8271b8aa7b71..5e03cfe50156 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -20,6 +20,7 @@ #define EDSA_HLEN 8 #define MV88E6XXX_N_FID 4096 +#define MV88E6XXX_N_SID 64 #define MV88E6XXX_FID_STANDALONE 0 #define MV88E6XXX_FID_BRIDGED 1 @@ -130,6 +131,7 @@ struct mv88e6xxx_info { unsigned int num_internal_phys; unsigned int num_gpio; unsigned int max_vid; + unsigned int max_sid; unsigned int port_base_addr; unsigned int phy_base_addr; unsigned int global1_addr; @@ -179,7 +181,14 @@ struct mv88e6xxx_vtu_entry { u16 fid; u8 sid; bool valid; + bool policy; u8 member[DSA_MAX_PORTS]; + u8 state[DSA_MAX_PORTS]; /* Older silicon has no STU */ +}; + +struct mv88e6xxx_stu_entry { + u8 sid; + bool valid; u8 state[DSA_MAX_PORTS]; }; @@ -278,6 +287,7 @@ enum mv88e6xxx_region_id { MV88E6XXX_REGION_GLOBAL2, MV88E6XXX_REGION_ATU, MV88E6XXX_REGION_VTU, + MV88E6XXX_REGION_STU, MV88E6XXX_REGION_PVT, _MV88E6XXX_REGION_MAX, @@ -287,6 +297,16 @@ struct mv88e6xxx_region_priv { enum mv88e6xxx_region_id id; }; +struct mv88e6xxx_mst { + struct list_head node; + + refcount_t refcnt; + struct net_device *br; + u16 msti; + + struct mv88e6xxx_stu_entry stu; +}; + struct mv88e6xxx_chip { const struct mv88e6xxx_info *info; @@ -387,11 +407,15 @@ struct mv88e6xxx_chip { /* devlink regions */ struct devlink_region *regions[_MV88E6XXX_REGION_MAX]; + + /* Bridge MST to SID mappings */ + struct list_head msts; }; struct mv88e6xxx_bus_ops { int (*read)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val); int (*write)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); + int (*init)(struct mv88e6xxx_chip *chip); }; struct mv88e6xxx_mdio_bus { @@ -586,6 +610,10 @@ struct mv88e6xxx_ops { void (*serdes_get_regs)(struct mv88e6xxx_chip *chip, int port, void *_p); + /* SERDES SGMII/Fiber Output Amplitude */ + int (*serdes_set_tx_amplitude)(struct mv88e6xxx_chip *chip, int port, + int val); + /* Address Translation Unit operations */ int (*atu_get_hash)(struct mv88e6xxx_chip *chip, u8 *hash); int (*atu_set_hash)(struct mv88e6xxx_chip *chip, u8 hash); @@ -596,6 +624,12 @@ struct mv88e6xxx_ops { int (*vtu_loadpurge)(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); + /* Spanning Tree Unit operations */ + int (*stu_getnext)(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry); + int (*stu_loadpurge)(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry); + /* GPIO operations */ const struct mv88e6xxx_gpio_ops *gpio_ops; @@ -609,9 +643,8 @@ struct mv88e6xxx_ops { const struct mv88e6xxx_ptp_ops *ptp_ops; /* Phylink */ - void (*phylink_validate)(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state); + void (*phylink_get_caps)(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config); /* Max Frame Size */ int (*set_max_frame_size)(struct mv88e6xxx_chip *chip, int mtu); @@ -695,6 +728,13 @@ struct mv88e6xxx_hw_stat { int type; }; +static inline bool mv88e6xxx_has_stu(struct mv88e6xxx_chip *chip) +{ + return chip->info->max_sid > 0 && + chip->info->ops->stu_loadpurge && + chip->info->ops->stu_getnext; +} + static inline bool mv88e6xxx_has_pvt(struct mv88e6xxx_chip *chip) { return chip->info->pvt; @@ -725,6 +765,11 @@ static inline unsigned int mv88e6xxx_max_vid(struct mv88e6xxx_chip *chip) return chip->info->max_vid; } +static inline unsigned int mv88e6xxx_max_sid(struct mv88e6xxx_chip *chip) +{ + return chip->info->max_sid; +} + static inline u16 mv88e6xxx_port_mask(struct mv88e6xxx_chip *chip) { return GENMASK((s32)mv88e6xxx_num_ports(chip) - 1, 0); diff --git a/drivers/net/dsa/mv88e6xxx/devlink.c b/drivers/net/dsa/mv88e6xxx/devlink.c index 381068395c63..1266eabee086 100644 --- a/drivers/net/dsa/mv88e6xxx/devlink.c +++ b/drivers/net/dsa/mv88e6xxx/devlink.c @@ -503,6 +503,85 @@ static int mv88e6xxx_region_vtu_snapshot(struct devlink *dl, return 0; } +/** + * struct mv88e6xxx_devlink_stu_entry - Devlink STU entry + * @sid: Global1/3: SID, unknown filters and learning. + * @vid: Global1/6: Valid bit. + * @data: Global1/7-9: Membership data and priority override. + * @resvd: Reserved. In case we forgot something. + * + * The STU entry format varies between chipset generations. Peridot + * and Amethyst packs the STU data into Global1/7-8. Older silicon + * spreads the information across all three VTU data registers - + * inheriting the layout of even older hardware that had no STU at + * all. Since this is a low-level debug interface, copy all data + * verbatim and defer parsing to the consumer. + */ +struct mv88e6xxx_devlink_stu_entry { + u16 sid; + u16 vid; + u16 data[3]; + u16 resvd; +}; + +static int mv88e6xxx_region_stu_snapshot(struct devlink *dl, + const struct devlink_region_ops *ops, + struct netlink_ext_ack *extack, + u8 **data) +{ + struct mv88e6xxx_devlink_stu_entry *table, *entry; + struct dsa_switch *ds = dsa_devlink_to_ds(dl); + struct mv88e6xxx_chip *chip = ds->priv; + struct mv88e6xxx_stu_entry stu; + int err; + + table = kcalloc(mv88e6xxx_max_sid(chip) + 1, + sizeof(struct mv88e6xxx_devlink_stu_entry), + GFP_KERNEL); + if (!table) + return -ENOMEM; + + entry = table; + stu.sid = mv88e6xxx_max_sid(chip); + stu.valid = false; + + mv88e6xxx_reg_lock(chip); + + do { + err = mv88e6xxx_g1_stu_getnext(chip, &stu); + if (err) + break; + + if (!stu.valid) + break; + + err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID, + &entry->sid); + err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID, + &entry->vid); + err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1, + &entry->data[0]); + err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA2, + &entry->data[1]); + err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA3, + &entry->data[2]); + if (err) + break; + + entry++; + } while (stu.sid < mv88e6xxx_max_sid(chip)); + + mv88e6xxx_reg_unlock(chip); + + if (err) { + kfree(table); + return err; + } + + *data = (u8 *)table; + return 0; +} + static int mv88e6xxx_region_pvt_snapshot(struct devlink *dl, const struct devlink_region_ops *ops, struct netlink_ext_ack *extack, @@ -605,6 +684,12 @@ static struct devlink_region_ops mv88e6xxx_region_vtu_ops = { .destructor = kfree, }; +static struct devlink_region_ops mv88e6xxx_region_stu_ops = { + .name = "stu", + .snapshot = mv88e6xxx_region_stu_snapshot, + .destructor = kfree, +}; + static struct devlink_region_ops mv88e6xxx_region_pvt_ops = { .name = "pvt", .snapshot = mv88e6xxx_region_pvt_snapshot, @@ -640,6 +725,11 @@ static struct mv88e6xxx_region mv88e6xxx_regions[] = { .ops = &mv88e6xxx_region_vtu_ops /* calculated at runtime */ }, + [MV88E6XXX_REGION_STU] = { + .ops = &mv88e6xxx_region_stu_ops, + .cond = mv88e6xxx_has_stu, + /* calculated at runtime */ + }, [MV88E6XXX_REGION_PVT] = { .ops = &mv88e6xxx_region_pvt_ops, .size = MV88E6XXX_MAX_PVT_ENTRIES * sizeof(u16), @@ -706,6 +796,10 @@ int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds) size = (mv88e6xxx_max_vid(chip) + 1) * sizeof(struct mv88e6xxx_devlink_vtu_entry); break; + case MV88E6XXX_REGION_STU: + size = (mv88e6xxx_max_sid(chip) + 1) * + sizeof(struct mv88e6xxx_devlink_stu_entry); + break; } region = dsa_devlink_region_create(ds, ops, 1, size); diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 4f3dbb015f77..65958b2a0d3a 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -46,6 +46,7 @@ /* Offset 0x02: VTU FID Register */ #define MV88E6352_G1_VTU_FID 0x02 +#define MV88E6352_G1_VTU_FID_VID_POLICY 0x1000 #define MV88E6352_G1_VTU_FID_MASK 0x0fff /* Offset 0x03: VTU SID Register */ @@ -347,6 +348,16 @@ int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip, int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); +int mv88e6xxx_g1_stu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry); +int mv88e6352_g1_stu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry); +int mv88e6352_g1_stu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry); +int mv88e6390_g1_stu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry); +int mv88e6390_g1_stu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry); int mv88e6xxx_g1_vtu_prob_irq_setup(struct mv88e6xxx_chip *chip); void mv88e6xxx_g1_vtu_prob_irq_free(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_atu_get_next(struct mv88e6xxx_chip *chip, u16 fid); diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index ae12c981923e..38e18f5811bf 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -27,7 +27,7 @@ static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip, return err; entry->fid = val & MV88E6352_G1_VTU_FID_MASK; - + entry->policy = !!(val & MV88E6352_G1_VTU_FID_VID_POLICY); return 0; } @@ -36,13 +36,15 @@ static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, { u16 val = entry->fid & MV88E6352_G1_VTU_FID_MASK; + if (entry->policy) + val |= MV88E6352_G1_VTU_FID_VID_POLICY; + return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_FID, val); } /* Offset 0x03: VTU SID Register */ -static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, u8 *sid) { u16 val; int err; @@ -51,15 +53,14 @@ static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, if (err) return err; - entry->sid = val & MV88E6352_G1_VTU_SID_MASK; + *sid = val & MV88E6352_G1_VTU_SID_MASK; return 0; } -static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, u8 sid) { - u16 val = entry->sid & MV88E6352_G1_VTU_SID_MASK; + u16 val = sid & MV88E6352_G1_VTU_SID_MASK; return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_SID, val); } @@ -88,7 +89,7 @@ static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op) /* Offset 0x06: VTU VID Register */ static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) + bool *valid, u16 *vid) { u16 val; int err; @@ -97,25 +98,28 @@ static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, if (err) return err; - entry->vid = val & 0xfff; + if (vid) { + *vid = val & 0xfff; - if (val & MV88E6390_G1_VTU_VID_PAGE) - entry->vid |= 0x1000; + if (val & MV88E6390_G1_VTU_VID_PAGE) + *vid |= 0x1000; + } - entry->valid = !!(val & MV88E6XXX_G1_VTU_VID_VALID); + if (valid) + *valid = !!(val & MV88E6XXX_G1_VTU_VID_VALID); return 0; } static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) + bool valid, u16 vid) { - u16 val = entry->vid & 0xfff; + u16 val = vid & 0xfff; - if (entry->vid & 0x1000) + if (vid & 0x1000) val |= MV88E6390_G1_VTU_VID_PAGE; - if (entry->valid) + if (valid) val |= MV88E6XXX_G1_VTU_VID_VALID; return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_VID, val); @@ -144,7 +148,7 @@ static int mv88e6185_g1_vtu_stu_data_read(struct mv88e6xxx_chip *chip, } static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) + u8 *member, u8 *state) { u16 regs[3]; int err; @@ -157,36 +161,20 @@ static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, /* Extract MemberTag data */ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { unsigned int member_offset = (i % 4) * 4; + unsigned int state_offset = member_offset + 2; - entry->member[i] = (regs[i / 4] >> member_offset) & 0x3; - } + if (member) + member[i] = (regs[i / 4] >> member_offset) & 0x3; - return 0; -} - -static int mv88e6185_g1_stu_data_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) -{ - u16 regs[3]; - int err; - int i; - - err = mv88e6185_g1_vtu_stu_data_read(chip, regs); - if (err) - return err; - - /* Extract PortState data */ - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - unsigned int state_offset = (i % 4) * 4 + 2; - - entry->state[i] = (regs[i / 4] >> state_offset) & 0x3; + if (state) + state[i] = (regs[i / 4] >> state_offset) & 0x3; } return 0; } static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) + u8 *member, u8 *state) { u16 regs[3] = { 0 }; int i; @@ -196,8 +184,11 @@ static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, unsigned int member_offset = (i % 4) * 4; unsigned int state_offset = member_offset + 2; - regs[i / 4] |= (entry->member[i] & 0x3) << member_offset; - regs[i / 4] |= (entry->state[i] & 0x3) << state_offset; + if (member) + regs[i / 4] |= (member[i] & 0x3) << member_offset; + + if (state) + regs[i / 4] |= (state[i] & 0x3) << state_offset; } /* Write all 3 VTU/STU Data registers */ @@ -265,48 +256,6 @@ static int mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip *chip, u8 *data) /* VLAN Translation Unit Operations */ -static int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) -{ - int err; - - err = mv88e6xxx_g1_vtu_sid_write(chip, entry); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_GET_NEXT); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_sid_read(chip, entry); - if (err) - return err; - - return mv88e6xxx_g1_vtu_vid_read(chip, entry); -} - -static int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *vtu) -{ - struct mv88e6xxx_vtu_entry stu; - int err; - - err = mv88e6xxx_g1_vtu_sid_read(chip, vtu); - if (err) - return err; - - stu.sid = vtu->sid - 1; - - err = mv88e6xxx_g1_vtu_stu_getnext(chip, &stu); - if (err) - return err; - - if (stu.sid != vtu->sid || !stu.valid) - return -EINVAL; - - return 0; -} - int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { @@ -324,7 +273,7 @@ int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, * write the VID only once, when the entry is given as invalid. */ if (!entry->valid) { - err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + err = mv88e6xxx_g1_vtu_vid_write(chip, false, entry->vid); if (err) return err; } @@ -333,7 +282,7 @@ int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, if (err) return err; - return mv88e6xxx_g1_vtu_vid_read(chip, entry); + return mv88e6xxx_g1_vtu_vid_read(chip, &entry->valid, &entry->vid); } int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, @@ -347,11 +296,7 @@ int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, return err; if (entry->valid) { - err = mv88e6185_g1_vtu_data_read(chip, entry); - if (err) - return err; - - err = mv88e6185_g1_stu_data_read(chip, entry); + err = mv88e6185_g1_vtu_data_read(chip, entry->member, entry->state); if (err) return err; @@ -381,7 +326,7 @@ int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, return err; if (entry->valid) { - err = mv88e6185_g1_vtu_data_read(chip, entry); + err = mv88e6185_g1_vtu_data_read(chip, entry->member, NULL); if (err) return err; @@ -389,12 +334,7 @@ int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, if (err) return err; - /* Fetch VLAN PortState data from the STU */ - err = mv88e6xxx_g1_vtu_stu_get(chip, entry); - if (err) - return err; - - err = mv88e6185_g1_stu_data_read(chip, entry); + err = mv88e6xxx_g1_vtu_sid_read(chip, &entry->sid); if (err) return err; } @@ -417,18 +357,13 @@ int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip, if (err) return err; - /* Fetch VLAN PortState data from the STU */ - err = mv88e6xxx_g1_vtu_stu_get(chip, entry); - if (err) - return err; - - err = mv88e6390_g1_vtu_data_read(chip, entry->state); - if (err) - return err; - err = mv88e6xxx_g1_vtu_fid_read(chip, entry); if (err) return err; + + err = mv88e6xxx_g1_vtu_sid_read(chip, &entry->sid); + if (err) + return err; } return 0; @@ -444,12 +379,12 @@ int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, if (err) return err; - err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, entry->vid); if (err) return err; if (entry->valid) { - err = mv88e6185_g1_vtu_data_write(chip, entry); + err = mv88e6185_g1_vtu_data_write(chip, entry->member, entry->state); if (err) return err; @@ -476,29 +411,23 @@ int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, if (err) return err; - err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, entry->vid); if (err) return err; if (entry->valid) { - /* Write MemberTag and PortState data */ - err = mv88e6185_g1_vtu_data_write(chip, entry); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_sid_write(chip, entry); - if (err) - return err; - - /* Load STU entry */ - err = mv88e6xxx_g1_vtu_op(chip, - MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE); + /* Write MemberTag data */ + err = mv88e6185_g1_vtu_data_write(chip, entry->member, NULL); if (err) return err; err = mv88e6xxx_g1_vtu_fid_write(chip, entry); if (err) return err; + + err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid); + if (err) + return err; } /* Load/Purge VTU entry */ @@ -514,26 +443,11 @@ int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, if (err) return err; - err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, entry->vid); if (err) return err; if (entry->valid) { - /* Write PortState data */ - err = mv88e6390_g1_vtu_data_write(chip, entry->state); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_sid_write(chip, entry); - if (err) - return err; - - /* Load STU entry */ - err = mv88e6xxx_g1_vtu_op(chip, - MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE); - if (err) - return err; - /* Write MemberTag data */ err = mv88e6390_g1_vtu_data_write(chip, entry->member); if (err) @@ -542,6 +456,10 @@ int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, err = mv88e6xxx_g1_vtu_fid_write(chip, entry); if (err) return err; + + err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid); + if (err) + return err; } /* Load/Purge VTU entry */ @@ -559,13 +477,139 @@ int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_FLUSH_ALL); } +/* Spanning Tree Unit Operations */ + +int mv88e6xxx_g1_stu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_vtu_op_wait(chip); + if (err) + return err; + + /* To get the next higher active SID, the STU GetNext operation can be + * started again without setting the SID registers since it already + * contains the last SID. + * + * To save a few hardware accesses and abstract this to the caller, + * write the SID only once, when the entry is given as invalid. + */ + if (!entry->valid) { + err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid); + if (err) + return err; + } + + err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_GET_NEXT); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_vid_read(chip, &entry->valid, NULL); + if (err) + return err; + + if (entry->valid) { + err = mv88e6xxx_g1_vtu_sid_read(chip, &entry->sid); + if (err) + return err; + } + + return 0; +} + +int mv88e6352_g1_stu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_stu_getnext(chip, entry); + if (err) + return err; + + if (!entry->valid) + return 0; + + return mv88e6185_g1_vtu_data_read(chip, NULL, entry->state); +} + +int mv88e6390_g1_stu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_stu_getnext(chip, entry); + if (err) + return err; + + if (!entry->valid) + return 0; + + return mv88e6390_g1_vtu_data_read(chip, entry->state); +} + +int mv88e6352_g1_stu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_vtu_op_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, 0); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid); + if (err) + return err; + + if (entry->valid) { + err = mv88e6185_g1_vtu_data_write(chip, NULL, entry->state); + if (err) + return err; + } + + /* Load/Purge STU entry */ + return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE); +} + +int mv88e6390_g1_stu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_vtu_op_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, 0); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid); + if (err) + return err; + + if (entry->valid) { + err = mv88e6390_g1_vtu_data_write(chip, entry->state); + if (err) + return err; + } + + /* Load/Purge STU entry */ + return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE); +} + +/* VTU Violation Management */ + static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id) { struct mv88e6xxx_chip *chip = dev_id; - struct mv88e6xxx_vtu_entry entry; + u16 val, vid; int spid; int err; - u16 val; mv88e6xxx_reg_lock(chip); @@ -577,7 +621,7 @@ static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id) if (err) goto out; - err = mv88e6xxx_g1_vtu_vid_read(chip, &entry); + err = mv88e6xxx_g1_vtu_vid_read(chip, NULL, &vid); if (err) goto out; @@ -585,13 +629,13 @@ static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id) if (val & MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION) { dev_err_ratelimited(chip->dev, "VTU member violation for vid %d, source port %d\n", - entry.vid, spid); + vid, spid); chip->ports[spid].vtu_member_violation++; } if (val & MV88E6XXX_G1_VTU_OP_MISS_VIOLATION) { dev_dbg_ratelimited(chip->dev, "VTU miss violation for vid %d, source port %d\n", - entry.vid, spid); + vid, spid); chip->ports[spid].vtu_miss_violation++; } diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index f3e27573a386..807aeaad9830 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -299,6 +299,8 @@ #define MV88E6352_G2_SCRATCH_CONFIG_DATA1_NO_CPU BIT(2) #define MV88E6352_G2_SCRATCH_CONFIG_DATA2 0x72 #define MV88E6352_G2_SCRATCH_CONFIG_DATA2_P0_MODE_MASK 0x3 +#define MV88E6352_G2_SCRATCH_CONFIG_DATA3 0x73 +#define MV88E6352_G2_SCRATCH_CONFIG_DATA3_S_SEL BIT(1) #define MV88E6352_G2_SCRATCH_GPIO_PCTL_GPIO 0 #define MV88E6352_G2_SCRATCH_GPIO_PCTL_TRIG 1 @@ -370,6 +372,7 @@ extern const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops; int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, bool external); +int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port); int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, u16 kind, u16 bin); int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip, u16 *stats); diff --git a/drivers/net/dsa/mv88e6xxx/global2_scratch.c b/drivers/net/dsa/mv88e6xxx/global2_scratch.c index eda710062933..a9d6e40321a2 100644 --- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c +++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c @@ -289,3 +289,31 @@ int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, return mv88e6xxx_g2_scratch_write(chip, misc_cfg, val); } + +/** + * mv88e6352_g2_scratch_port_has_serdes - indicate if a port can have a serdes + * @chip: chip private data + * @port: port number to check for serdes + * + * Indicates whether the port may have a serdes attached according to the + * pin strapping. Returns negative error number, 0 if the port is not + * configured to have a serdes, and 1 if the port is configured to have a + * serdes attached. + */ +int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port) +{ + u8 config3, p; + int err; + + err = mv88e6xxx_g2_scratch_read(chip, MV88E6352_G2_SCRATCH_CONFIG_DATA3, + &config3); + if (err) + return err; + + if (config3 & MV88E6352_G2_SCRATCH_CONFIG_DATA3_S_SEL) + p = 5; + else + p = 4; + + return port == p; +} diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c index 389f8a6ec0ab..331b4ca089ff 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c @@ -301,7 +301,7 @@ static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip, shwt->hwtstamp = ns_to_ktime(ns); status &= ~MV88E6XXX_PTP_TS_VALID; } - netif_rx_ni(skb); + netif_rx(skb); } } diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index ab41619a809b..795b3128768f 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -550,6 +550,9 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, mode = PHY_INTERFACE_MODE_1000BASEX; switch (mode) { + case PHY_INTERFACE_MODE_RMII: + cmode = MV88E6XXX_PORT_STS_CMODE_RMII; + break; case PHY_INTERFACE_MODE_1000BASEX: cmode = MV88E6XXX_PORT_STS_CMODE_1000BASEX; break; @@ -610,6 +613,8 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, chip->ports[port].cmode = cmode; lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane == -ENODEV) + return 0; if (lane < 0) return lane; @@ -1234,6 +1239,35 @@ int mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port, return err; } +int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port, + bool locked) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®); + if (err) + return err; + + reg &= ~MV88E6XXX_PORT_CTL0_SA_FILT_MASK; + if (locked) + reg |= MV88E6XXX_PORT_CTL0_SA_FILT_DROP_ON_LOCK; + + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg); + if (err) + return err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, ®); + if (err) + return err; + + reg &= ~MV88E6XXX_PORT_ASSOC_VECTOR_LOCKED_PORT; + if (locked) + reg |= MV88E6XXX_PORT_ASSOC_VECTOR_LOCKED_PORT; + + return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, reg); +} + int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, u16 mode) { @@ -1278,7 +1312,7 @@ int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port, return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, new); } -int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port) +int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port, bool map) { u16 reg; int err; @@ -1287,7 +1321,10 @@ int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port) if (err) return err; - reg |= MV88E6XXX_PORT_CTL2_MAP_DA; + if (map) + reg |= MV88E6XXX_PORT_CTL2_MAP_DA; + else + reg &= ~MV88E6XXX_PORT_CTL2_MAP_DA; return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg); } diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 03382b66f800..e0a705d82019 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -42,6 +42,11 @@ #define MV88E6XXX_PORT_STS_TX_PAUSED 0x0020 #define MV88E6XXX_PORT_STS_FLOW_CTL 0x0010 #define MV88E6XXX_PORT_STS_CMODE_MASK 0x000f +#define MV88E6XXX_PORT_STS_CMODE_MII_PHY 0x0001 +#define MV88E6XXX_PORT_STS_CMODE_MII 0x0002 +#define MV88E6XXX_PORT_STS_CMODE_GMII 0x0003 +#define MV88E6XXX_PORT_STS_CMODE_RMII_PHY 0x0004 +#define MV88E6XXX_PORT_STS_CMODE_RMII 0x0005 #define MV88E6XXX_PORT_STS_CMODE_RGMII 0x0007 #define MV88E6XXX_PORT_STS_CMODE_100BASEX 0x0008 #define MV88E6XXX_PORT_STS_CMODE_1000BASEX 0x0009 @@ -142,7 +147,11 @@ /* Offset 0x04: Port Control Register */ #define MV88E6XXX_PORT_CTL0 0x04 #define MV88E6XXX_PORT_CTL0_USE_CORE_TAG 0x8000 -#define MV88E6XXX_PORT_CTL0_DROP_ON_LOCK 0x4000 +#define MV88E6XXX_PORT_CTL0_SA_FILT_MASK 0xc000 +#define MV88E6XXX_PORT_CTL0_SA_FILT_DISABLED 0x0000 +#define MV88E6XXX_PORT_CTL0_SA_FILT_DROP_ON_LOCK 0x4000 +#define MV88E6XXX_PORT_CTL0_SA_FILT_DROP_ON_UNLOCK 0x8000 +#define MV88E6XXX_PORT_CTL0_SA_FILT_DROP_ON_CPU 0xc000 #define MV88E6XXX_PORT_CTL0_EGRESS_MODE_MASK 0x3000 #define MV88E6XXX_PORT_CTL0_EGRESS_MODE_UNMODIFIED 0x0000 #define MV88E6XXX_PORT_CTL0_EGRESS_MODE_UNTAGGED 0x1000 @@ -365,6 +374,9 @@ int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid); int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid); int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid); +int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port, + bool locked); + int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, u16 mode); int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port); @@ -425,7 +437,7 @@ int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port, bool drop_untagged); -int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port); +int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port, bool map); int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, int upstream_port); int mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port, diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 2b05ead515cd..7b37d45bc9fb 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -272,14 +272,6 @@ int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) return lane; } -static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) -{ - if (mv88e6xxx_serdes_get_lane(chip, port) >= 0) - return true; - - return false; -} - struct mv88e6352_serdes_hw_stat { char string[ETH_GSTRING_LEN]; int sizeof_stat; @@ -293,20 +285,24 @@ static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = { int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) { - if (mv88e6352_port_has_serdes(chip, port)) - return ARRAY_SIZE(mv88e6352_serdes_hw_stats); + int err; - return 0; + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err <= 0) + return err; + + return ARRAY_SIZE(mv88e6352_serdes_hw_stats); } int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int port, uint8_t *data) { struct mv88e6352_serdes_hw_stat *stat; - int i; + int err, i; - if (!mv88e6352_port_has_serdes(chip, port)) - return 0; + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err <= 0) + return err; for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { stat = &mv88e6352_serdes_hw_stats[i]; @@ -348,11 +344,12 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, { struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port]; struct mv88e6352_serdes_hw_stat *stat; + int i, err; u64 value; - int i; - if (!mv88e6352_port_has_serdes(chip, port)) - return 0; + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err <= 0) + return err; BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) > ARRAY_SIZE(mv88e6xxx_port->serdes_stats)); @@ -419,8 +416,13 @@ unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port) { - if (!mv88e6352_port_has_serdes(chip, port)) - return 0; + int err; + + mv88e6xxx_reg_lock(chip); + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + mv88e6xxx_reg_unlock(chip); + if (err <= 0) + return err; return 32 * sizeof(u16); } @@ -432,7 +434,8 @@ void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) int err; int i; - if (!mv88e6352_port_has_serdes(chip, port)) + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err <= 0) return; for (i = 0 ; i < 32; i++) { @@ -1310,6 +1313,44 @@ void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) } } +static const int mv88e6352_serdes_p2p_to_reg[] = { + /* Index of value in microvolts corresponds to the register value */ + 14000, 112000, 210000, 308000, 406000, 504000, 602000, 700000, +}; + +int mv88e6352_serdes_set_tx_amplitude(struct mv88e6xxx_chip *chip, int port, + int val) +{ + bool found = false; + u16 ctrl, reg; + int err; + int i; + + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err <= 0) + return err; + + for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_p2p_to_reg); ++i) { + if (mv88e6352_serdes_p2p_to_reg[i] == val) { + reg = i; + found = true; + break; + } + } + + if (!found) + return -EINVAL; + + err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_SPEC_CTRL2, &ctrl); + if (err) + return err; + + ctrl &= ~MV88E6352_SERDES_OUT_AMP_MASK; + ctrl |= reg; + + return mv88e6352_serdes_write(chip, MV88E6352_SERDES_SPEC_CTRL2, ctrl); +} + static int mv88e6393x_serdes_power_lane(struct mv88e6xxx_chip *chip, int lane, bool on) { diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index 8dd8ed225b45..29bb4e91e2f6 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -27,6 +27,8 @@ #define MV88E6352_SERDES_INT_FIBRE_ENERGY BIT(4) #define MV88E6352_SERDES_INT_STATUS 0x13 +#define MV88E6352_SERDES_SPEC_CTRL2 0x1a +#define MV88E6352_SERDES_OUT_AMP_MASK 0x0007 #define MV88E6341_PORT5_LANE 0x15 @@ -176,6 +178,9 @@ void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p); int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port); void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p); +int mv88e6352_serdes_set_tx_amplitude(struct mv88e6xxx_chip *chip, int port, + int val); + /* Return the (first) SERDES lane address a port is using, -errno otherwise. */ static inline int mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) diff --git a/drivers/net/dsa/mv88e6xxx/smi.c b/drivers/net/dsa/mv88e6xxx/smi.c index 282fe08db050..a990271b7482 100644 --- a/drivers/net/dsa/mv88e6xxx/smi.c +++ b/drivers/net/dsa/mv88e6xxx/smi.c @@ -55,11 +55,15 @@ static int mv88e6xxx_smi_direct_write(struct mv88e6xxx_chip *chip, static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip, int dev, int reg, int bit, int val) { + const unsigned long timeout = jiffies + msecs_to_jiffies(50); u16 data; int err; int i; - for (i = 0; i < 16; i++) { + /* Even if the initial poll takes longer than 50ms, always do + * at least one more attempt. + */ + for (i = 0; time_before(jiffies, timeout) || (i < 2); i++) { err = mv88e6xxx_smi_direct_read(chip, dev, reg, &data); if (err) return err; @@ -67,7 +71,10 @@ static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip, if (!!(data & BIT(bit)) == !!val) return 0; - usleep_range(1000, 2000); + if (i < 2) + cpu_relax(); + else + usleep_range(1000, 2000); } return -ETIMEDOUT; @@ -104,11 +111,6 @@ static int mv88e6xxx_smi_indirect_read(struct mv88e6xxx_chip *chip, { int err; - err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, - MV88E6XXX_SMI_CMD, 15, 0); - if (err) - return err; - err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, MV88E6XXX_SMI_CMD, MV88E6XXX_SMI_CMD_BUSY | @@ -132,11 +134,6 @@ static int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip, { int err; - err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, - MV88E6XXX_SMI_CMD, 15, 0); - if (err) - return err; - err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, MV88E6XXX_SMI_DATA, data); if (err) @@ -155,9 +152,20 @@ static int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip, MV88E6XXX_SMI_CMD, 15, 0); } +static int mv88e6xxx_smi_indirect_init(struct mv88e6xxx_chip *chip) +{ + /* Ensure that the chip starts out in the ready state. As both + * reads and writes always ensure this on return, they can + * safely depend on the chip not being busy on entry. + */ + return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, + MV88E6XXX_SMI_CMD, 15, 0); +} + static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_indirect_ops = { .read = mv88e6xxx_smi_indirect_read, .write = mv88e6xxx_smi_indirect_write, + .init = mv88e6xxx_smi_indirect_init, }; int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, @@ -175,5 +183,8 @@ int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, chip->bus = bus; chip->sw_addr = sw_addr; + if (chip->smi_ops->init) + return chip->smi_ops->init(chip); + return 0; } diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 9957772201d5..413b0006e9a2 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -25,21 +25,151 @@ #include #include "felix.h" -static int felix_tag_8021q_rxvlan_add(struct felix *felix, int port, u16 vid, - bool pvid, bool untagged) +/* Translate the DSA database API into the ocelot switch library API, + * which uses VID 0 for all ports that aren't part of a bridge, + * and expects the bridge_dev to be NULL in that case. + */ +static struct net_device *felix_classify_db(struct dsa_db db) +{ + switch (db.type) { + case DSA_DB_PORT: + case DSA_DB_LAG: + return NULL; + case DSA_DB_BRIDGE: + return db.bridge.dev; + default: + return ERR_PTR(-EOPNOTSUPP); + } +} + +/* We are called before felix_npi_port_init(), so ocelot->npi is -1. */ +static int felix_migrate_fdbs_to_npi_port(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct net_device *bridge_dev = felix_classify_db(db); + struct ocelot *ocelot = ds->priv; + int cpu = ocelot->num_phys_ports; + int err; + + err = ocelot_fdb_del(ocelot, port, addr, vid, bridge_dev); + if (err) + return err; + + return ocelot_fdb_add(ocelot, cpu, addr, vid, bridge_dev); +} + +static int felix_migrate_mdbs_to_npi_port(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct net_device *bridge_dev = felix_classify_db(db); + struct switchdev_obj_port_mdb mdb; + struct ocelot *ocelot = ds->priv; + int cpu = ocelot->num_phys_ports; + int err; + + memset(&mdb, 0, sizeof(mdb)); + ether_addr_copy(mdb.addr, addr); + mdb.vid = vid; + + err = ocelot_port_mdb_del(ocelot, port, &mdb, bridge_dev); + if (err) + return err; + + return ocelot_port_mdb_add(ocelot, cpu, &mdb, bridge_dev); +} + +static void felix_migrate_pgid_bit(struct dsa_switch *ds, int from, int to, + int pgid) +{ + struct ocelot *ocelot = ds->priv; + bool on; + u32 val; + + val = ocelot_read_rix(ocelot, ANA_PGID_PGID, pgid); + on = !!(val & BIT(from)); + val &= ~BIT(from); + if (on) + val |= BIT(to); + else + val &= ~BIT(to); + + ocelot_write_rix(ocelot, val, ANA_PGID_PGID, pgid); +} + +static void felix_migrate_flood_to_npi_port(struct dsa_switch *ds, int port) +{ + struct ocelot *ocelot = ds->priv; + + felix_migrate_pgid_bit(ds, port, ocelot->num_phys_ports, PGID_UC); + felix_migrate_pgid_bit(ds, port, ocelot->num_phys_ports, PGID_MC); + felix_migrate_pgid_bit(ds, port, ocelot->num_phys_ports, PGID_BC); +} + +static void +felix_migrate_flood_to_tag_8021q_port(struct dsa_switch *ds, int port) +{ + struct ocelot *ocelot = ds->priv; + + felix_migrate_pgid_bit(ds, ocelot->num_phys_ports, port, PGID_UC); + felix_migrate_pgid_bit(ds, ocelot->num_phys_ports, port, PGID_MC); + felix_migrate_pgid_bit(ds, ocelot->num_phys_ports, port, PGID_BC); +} + +/* ocelot->npi was already set to -1 by felix_npi_port_deinit, so + * ocelot_fdb_add() will not redirect FDB entries towards the + * CPU port module here, which is what we want. + */ +static int +felix_migrate_fdbs_to_tag_8021q_port(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct net_device *bridge_dev = felix_classify_db(db); + struct ocelot *ocelot = ds->priv; + int cpu = ocelot->num_phys_ports; + int err; + + err = ocelot_fdb_del(ocelot, cpu, addr, vid, bridge_dev); + if (err) + return err; + + return ocelot_fdb_add(ocelot, port, addr, vid, bridge_dev); +} + +static int +felix_migrate_mdbs_to_tag_8021q_port(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct net_device *bridge_dev = felix_classify_db(db); + struct switchdev_obj_port_mdb mdb; + struct ocelot *ocelot = ds->priv; + int cpu = ocelot->num_phys_ports; + int err; + + memset(&mdb, 0, sizeof(mdb)); + ether_addr_copy(mdb.addr, addr); + mdb.vid = vid; + + err = ocelot_port_mdb_del(ocelot, cpu, &mdb, bridge_dev); + if (err) + return err; + + return ocelot_port_mdb_add(ocelot, port, &mdb, bridge_dev); +} + +/* Set up VCAP ES0 rules for pushing a tag_8021q VLAN towards the CPU such that + * the tagger can perform RX source port identification. + */ +static int felix_tag_8021q_vlan_add_rx(struct felix *felix, int port, u16 vid) { struct ocelot_vcap_filter *outer_tagging_rule; struct ocelot *ocelot = &felix->ocelot; struct dsa_switch *ds = felix->ds; int key_length, upstream, err; - /* We don't need to install the rxvlan into the other ports' filtering - * tables, because we're just pushing the rxvlan when sending towards - * the CPU - */ - if (!pvid) - return 0; - key_length = ocelot->vcap[VCAP_ES0].keys[VCAP_ES0_IGR_PORT].length; upstream = dsa_upstream_port(ds, port); @@ -50,7 +180,7 @@ static int felix_tag_8021q_rxvlan_add(struct felix *felix, int port, u16 vid, outer_tagging_rule->key_type = OCELOT_VCAP_KEY_ANY; outer_tagging_rule->prio = 1; - outer_tagging_rule->id.cookie = port; + outer_tagging_rule->id.cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port); outer_tagging_rule->id.tc_offload = false; outer_tagging_rule->block_id = VCAP_ES0; outer_tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -71,21 +201,32 @@ static int felix_tag_8021q_rxvlan_add(struct felix *felix, int port, u16 vid, return err; } -static int felix_tag_8021q_txvlan_add(struct felix *felix, int port, u16 vid, - bool pvid, bool untagged) +static int felix_tag_8021q_vlan_del_rx(struct felix *felix, int port, u16 vid) +{ + struct ocelot_vcap_filter *outer_tagging_rule; + struct ocelot_vcap_block *block_vcap_es0; + struct ocelot *ocelot = &felix->ocelot; + + block_vcap_es0 = &ocelot->block[VCAP_ES0]; + + outer_tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_es0, + port, false); + if (!outer_tagging_rule) + return -ENOENT; + + return ocelot_vcap_filter_del(ocelot, outer_tagging_rule); +} + +/* Set up VCAP IS1 rules for stripping the tag_8021q VLAN on TX and VCAP IS2 + * rules for steering those tagged packets towards the correct destination port + */ +static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid) { struct ocelot_vcap_filter *untagging_rule, *redirect_rule; struct ocelot *ocelot = &felix->ocelot; struct dsa_switch *ds = felix->ds; int upstream, err; - /* tag_8021q.c assumes we are implementing this via port VLAN - * membership, which we aren't. So we don't need to add any VCAP filter - * for the CPU port. - */ - if (ocelot->ports[port]->is_dsa_8021q_cpu) - return 0; - untagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); if (!untagging_rule) return -ENOMEM; @@ -103,7 +244,7 @@ static int felix_tag_8021q_txvlan_add(struct felix *felix, int port, u16 vid, untagging_rule->vlan.vid.value = vid; untagging_rule->vlan.vid.mask = VLAN_VID_MASK; untagging_rule->prio = 1; - untagging_rule->id.cookie = port; + untagging_rule->id.cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port); untagging_rule->id.tc_offload = false; untagging_rule->block_id = VCAP_IS1; untagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -124,7 +265,7 @@ static int felix_tag_8021q_txvlan_add(struct felix *felix, int port, u16 vid, redirect_rule->ingress_port_mask = BIT(upstream); redirect_rule->pag = port; redirect_rule->prio = 1; - redirect_rule->id.cookie = port; + redirect_rule->id.cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port); redirect_rule->id.tc_offload = false; redirect_rule->block_id = VCAP_IS2; redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -142,49 +283,7 @@ static int felix_tag_8021q_txvlan_add(struct felix *felix, int port, u16 vid, return 0; } -static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, - u16 flags) -{ - bool untagged = flags & BRIDGE_VLAN_INFO_UNTAGGED; - bool pvid = flags & BRIDGE_VLAN_INFO_PVID; - struct ocelot *ocelot = ds->priv; - - if (vid_is_dsa_8021q_rxvlan(vid)) - return felix_tag_8021q_rxvlan_add(ocelot_to_felix(ocelot), - port, vid, pvid, untagged); - - if (vid_is_dsa_8021q_txvlan(vid)) - return felix_tag_8021q_txvlan_add(ocelot_to_felix(ocelot), - port, vid, pvid, untagged); - - return 0; -} - -static int felix_tag_8021q_rxvlan_del(struct felix *felix, int port, u16 vid) -{ - struct ocelot_vcap_filter *outer_tagging_rule; - struct ocelot_vcap_block *block_vcap_es0; - struct ocelot *ocelot = &felix->ocelot; - - block_vcap_es0 = &ocelot->block[VCAP_ES0]; - - outer_tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_es0, - port, false); - /* In rxvlan_add, we had the "if (!pvid) return 0" logic to avoid - * installing outer tagging ES0 rules where they weren't needed. - * But in rxvlan_del, the API doesn't give us the "flags" anymore, - * so that forces us to be slightly sloppy here, and just assume that - * if we didn't find an outer_tagging_rule it means that there was - * none in the first place, i.e. rxvlan_del is called on a non-pvid - * port. This is most probably true though. - */ - if (!outer_tagging_rule) - return 0; - - return ocelot_vcap_filter_del(ocelot, outer_tagging_rule); -} - -static int felix_tag_8021q_txvlan_del(struct felix *felix, int port, u16 vid) +static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid) { struct ocelot_vcap_filter *untagging_rule, *redirect_rule; struct ocelot_vcap_block *block_vcap_is1; @@ -192,16 +291,13 @@ static int felix_tag_8021q_txvlan_del(struct felix *felix, int port, u16 vid) struct ocelot *ocelot = &felix->ocelot; int err; - if (ocelot->ports[port]->is_dsa_8021q_cpu) - return 0; - block_vcap_is1 = &ocelot->block[VCAP_IS1]; block_vcap_is2 = &ocelot->block[VCAP_IS2]; untagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1, port, false); if (!untagging_rule) - return 0; + return -ENOENT; err = ocelot_vcap_filter_del(ocelot, untagging_rule); if (err) @@ -210,22 +306,54 @@ static int felix_tag_8021q_txvlan_del(struct felix *felix, int port, u16 vid) redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, port, false); if (!redirect_rule) - return 0; + return -ENOENT; return ocelot_vcap_filter_del(ocelot, redirect_rule); } +static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, + u16 flags) +{ + struct ocelot *ocelot = ds->priv; + int err; + + /* tag_8021q.c assumes we are implementing this via port VLAN + * membership, which we aren't. So we don't need to add any VCAP filter + * for the CPU port. + */ + if (!dsa_is_user_port(ds, port)) + return 0; + + err = felix_tag_8021q_vlan_add_rx(ocelot_to_felix(ocelot), port, vid); + if (err) + return err; + + err = felix_tag_8021q_vlan_add_tx(ocelot_to_felix(ocelot), port, vid); + if (err) { + felix_tag_8021q_vlan_del_rx(ocelot_to_felix(ocelot), port, vid); + return err; + } + + return 0; +} + static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid) { struct ocelot *ocelot = ds->priv; + int err; - if (vid_is_dsa_8021q_rxvlan(vid)) - return felix_tag_8021q_rxvlan_del(ocelot_to_felix(ocelot), - port, vid); + if (!dsa_is_user_port(ds, port)) + return 0; - if (vid_is_dsa_8021q_txvlan(vid)) - return felix_tag_8021q_txvlan_del(ocelot_to_felix(ocelot), - port, vid); + err = felix_tag_8021q_vlan_del_rx(ocelot_to_felix(ocelot), port, vid); + if (err) + return err; + + err = felix_tag_8021q_vlan_del_tx(ocelot_to_felix(ocelot), port, vid); + if (err) { + felix_tag_8021q_vlan_add_rx(ocelot_to_felix(ocelot), port, vid); + return err; + } return 0; } @@ -241,8 +369,7 @@ static void felix_8021q_cpu_port_init(struct ocelot *ocelot, int port) { mutex_lock(&ocelot->fwd_domain_lock); - ocelot->ports[port]->is_dsa_8021q_cpu = true; - ocelot->npi = -1; + ocelot_port_set_dsa_8021q_cpu(ocelot, port); /* Overwrite PGID_CPU with the non-tagging port */ ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, PGID_CPU); @@ -256,7 +383,7 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port) { mutex_lock(&ocelot->fwd_domain_lock); - ocelot->ports[port]->is_dsa_8021q_cpu = false; + ocelot_port_unset_dsa_8021q_cpu(ocelot, port); /* Restore PGID_CPU */ ocelot_write_rix(ocelot, BIT(ocelot->num_phys_ports), ANA_PGID_PGID, @@ -267,148 +394,81 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port) mutex_unlock(&ocelot->fwd_domain_lock); } -/* Set up a VCAP IS2 rule for delivering PTP frames to the CPU port module. - * If the quirk_no_xtr_irq is in place, then also copy those PTP frames to the - * tag_8021q CPU port. +/* On switches with no extraction IRQ wired, trapped packets need to be + * replicated over Ethernet as well, otherwise we'd get no notification of + * their arrival when using the ocelot-8021q tagging protocol. */ -static int felix_setup_mmio_filtering(struct felix *felix) +static int felix_update_trapping_destinations(struct dsa_switch *ds, + bool using_tag_8021q) { - unsigned long user_ports = dsa_user_ports(felix->ds); - struct ocelot_vcap_filter *redirect_rule; - struct ocelot_vcap_filter *tagging_rule; - struct ocelot *ocelot = &felix->ocelot; - struct dsa_switch *ds = felix->ds; - int cpu = -1, port, ret; + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + struct ocelot_vcap_filter *trap; + enum ocelot_mask_mode mask_mode; + unsigned long port_mask; + struct dsa_port *dp; + bool cpu_copy_ena; + int cpu = -1, err; - tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); - if (!tagging_rule) - return -ENOMEM; + if (!felix->info->quirk_no_xtr_irq) + return 0; - redirect_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); - if (!redirect_rule) { - kfree(tagging_rule); - return -ENOMEM; + /* Figure out the current CPU port */ + dsa_switch_for_each_cpu_port(dp, ds) { + cpu = dp->index; + break; } - for (port = 0; port < ocelot->num_phys_ports; port++) { - if (dsa_is_cpu_port(ds, port)) { - cpu = port; - break; - } - } - - if (cpu < 0) { - kfree(tagging_rule); - kfree(redirect_rule); - return -EINVAL; - } - - tagging_rule->key_type = OCELOT_VCAP_KEY_ETYPE; - *(__be16 *)tagging_rule->key.etype.etype.value = htons(ETH_P_1588); - *(__be16 *)tagging_rule->key.etype.etype.mask = htons(0xffff); - tagging_rule->ingress_port_mask = user_ports; - tagging_rule->prio = 1; - tagging_rule->id.cookie = ocelot->num_phys_ports; - tagging_rule->id.tc_offload = false; - tagging_rule->block_id = VCAP_IS1; - tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; - tagging_rule->lookup = 0; - tagging_rule->action.pag_override_mask = 0xff; - tagging_rule->action.pag_val = ocelot->num_phys_ports; - - ret = ocelot_vcap_filter_add(ocelot, tagging_rule, NULL); - if (ret) { - kfree(tagging_rule); - kfree(redirect_rule); - return ret; - } - - redirect_rule->key_type = OCELOT_VCAP_KEY_ANY; - redirect_rule->ingress_port_mask = user_ports; - redirect_rule->pag = ocelot->num_phys_ports; - redirect_rule->prio = 1; - redirect_rule->id.cookie = ocelot->num_phys_ports; - redirect_rule->id.tc_offload = false; - redirect_rule->block_id = VCAP_IS2; - redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; - redirect_rule->lookup = 0; - redirect_rule->action.cpu_copy_ena = true; - if (felix->info->quirk_no_xtr_irq) { - /* Redirect to the tag_8021q CPU but also copy PTP packets to - * the CPU port module - */ - redirect_rule->action.mask_mode = OCELOT_MASK_MODE_REDIRECT; - redirect_rule->action.port_mask = BIT(cpu); - } else { - /* Trap PTP packets only to the CPU port module (which is - * redirected to the NPI port) - */ - redirect_rule->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; - redirect_rule->action.port_mask = 0; - } - - ret = ocelot_vcap_filter_add(ocelot, redirect_rule, NULL); - if (ret) { - ocelot_vcap_filter_del(ocelot, tagging_rule); - kfree(redirect_rule); - return ret; - } - - /* The ownership of the CPU port module's queues might have just been - * transferred to the tag_8021q tagger from the NPI-based tagger. - * So there might still be all sorts of crap in the queues. On the - * other hand, the MMIO-based matching of PTP frames is very brittle, - * so we need to be careful that there are no extra frames to be - * dequeued over MMIO, since we would never know to discard them. + /* We are sure that "cpu" was found, otherwise + * dsa_tree_setup_default_cpu() would have failed earlier. */ - ocelot_drain_cpu_queue(ocelot, 0); + + /* Make sure all traps are set up for that destination */ + list_for_each_entry(trap, &ocelot->traps, trap_list) { + /* Figure out the current trapping destination */ + if (using_tag_8021q) { + /* Redirect to the tag_8021q CPU port. If timestamps + * are necessary, also copy trapped packets to the CPU + * port module. + */ + mask_mode = OCELOT_MASK_MODE_REDIRECT; + port_mask = BIT(cpu); + cpu_copy_ena = !!trap->take_ts; + } else { + /* Trap packets only to the CPU port module, which is + * redirected to the NPI port (the DSA CPU port) + */ + mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; + port_mask = 0; + cpu_copy_ena = true; + } + + if (trap->action.mask_mode == mask_mode && + trap->action.port_mask == port_mask && + trap->action.cpu_copy_ena == cpu_copy_ena) + continue; + + trap->action.mask_mode = mask_mode; + trap->action.port_mask = port_mask; + trap->action.cpu_copy_ena = cpu_copy_ena; + + err = ocelot_vcap_filter_replace(ocelot, trap); + if (err) + return err; + } return 0; } -static int felix_teardown_mmio_filtering(struct felix *felix) -{ - struct ocelot_vcap_filter *tagging_rule, *redirect_rule; - struct ocelot_vcap_block *block_vcap_is1; - struct ocelot_vcap_block *block_vcap_is2; - struct ocelot *ocelot = &felix->ocelot; - int err; - - block_vcap_is1 = &ocelot->block[VCAP_IS1]; - block_vcap_is2 = &ocelot->block[VCAP_IS2]; - - tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1, - ocelot->num_phys_ports, - false); - if (!tagging_rule) - return -ENOENT; - - err = ocelot_vcap_filter_del(ocelot, tagging_rule); - if (err) - return err; - - redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, - ocelot->num_phys_ports, - false); - if (!redirect_rule) - return -ENOENT; - - return ocelot_vcap_filter_del(ocelot, redirect_rule); -} - static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) { struct ocelot *ocelot = ds->priv; - struct felix *felix = ocelot_to_felix(ocelot); - unsigned long cpu_flood; - int port, err; + struct dsa_port *dp; + int err; felix_8021q_cpu_port_init(ocelot, cpu); - for (port = 0; port < ds->num_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - + dsa_switch_for_each_available_port(dp, ds) { /* This overwrites ocelot_init(): * Do not forward BPDU frames to the CPU port module, * for 2 reasons: @@ -421,28 +481,43 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) */ ocelot_write_gix(ocelot, ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0), - ANA_PORT_CPU_FWD_BPDU_CFG, port); + ANA_PORT_CPU_FWD_BPDU_CFG, dp->index); } - /* In tag_8021q mode, the CPU port module is unused, except for PTP - * frames. So we want to disable flooding of any kind to the CPU port - * module, since packets going there will end in a black hole. - */ - cpu_flood = ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)); - ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_UC); - ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_MC); - ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_BC); - err = dsa_tag_8021q_register(ds, htons(ETH_P_8021AD)); if (err) return err; - err = felix_setup_mmio_filtering(felix); + err = dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_tag_8021q_port); if (err) goto out_tag_8021q_unregister; + err = dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_tag_8021q_port); + if (err) + goto out_migrate_fdbs; + + felix_migrate_flood_to_tag_8021q_port(ds, cpu); + + err = felix_update_trapping_destinations(ds, true); + if (err) + goto out_migrate_flood; + + /* The ownership of the CPU port module's queues might have just been + * transferred to the tag_8021q tagger from the NPI-based tagger. + * So there might still be all sorts of crap in the queues. On the + * other hand, the MMIO-based matching of PTP frames is very brittle, + * so we need to be careful that there are no extra frames to be + * dequeued over MMIO, since we would never know to discard them. + */ + ocelot_drain_cpu_queue(ocelot, 0); + return 0; +out_migrate_flood: + felix_migrate_flood_to_npi_port(ds, cpu); + dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_npi_port); +out_migrate_fdbs: + dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_npi_port); out_tag_8021q_unregister: dsa_tag_8021q_unregister(ds); return err; @@ -451,27 +526,24 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu) { struct ocelot *ocelot = ds->priv; - struct felix *felix = ocelot_to_felix(ocelot); - int err, port; + struct dsa_port *dp; + int err; - err = felix_teardown_mmio_filtering(felix); + err = felix_update_trapping_destinations(ds, false); if (err) dev_err(ds->dev, "felix_teardown_mmio_filtering returned %d", err); dsa_tag_8021q_unregister(ds); - for (port = 0; port < ds->num_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - + dsa_switch_for_each_available_port(dp, ds) { /* Restore the logic from ocelot_init: * do not forward BPDU frames to the front ports. */ ocelot_write_gix(ocelot, ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff), ANA_PORT_CPU_FWD_BPDU_CFG, - port); + dp->index); } felix_8021q_cpu_port_deinit(ocelot, cpu); @@ -523,27 +595,26 @@ static void felix_npi_port_deinit(struct ocelot *ocelot, int port) static int felix_setup_tag_npi(struct dsa_switch *ds, int cpu) { struct ocelot *ocelot = ds->priv; - unsigned long cpu_flood; + int err; + + err = dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_npi_port); + if (err) + return err; + + err = dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_npi_port); + if (err) + goto out_migrate_fdbs; + + felix_migrate_flood_to_npi_port(ds, cpu); felix_npi_port_init(ocelot, cpu); - /* Include the CPU port module (and indirectly, the NPI port) - * in the forwarding mask for unknown unicast - the hardware - * default value for ANA_FLOODING_FLD_UNICAST excludes - * BIT(ocelot->num_phys_ports), and so does ocelot_init, - * since Ocelot relies on whitelisting MAC addresses towards - * PGID_CPU. - * We do this because DSA does not yet perform RX filtering, - * and the NPI port does not perform source address learning, - * so traffic sent to Linux is effectively unknown from the - * switch's perspective. - */ - cpu_flood = ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)); - ocelot_rmw_rix(ocelot, cpu_flood, cpu_flood, ANA_PGID_PGID, PGID_UC); - ocelot_rmw_rix(ocelot, cpu_flood, cpu_flood, ANA_PGID_PGID, PGID_MC); - ocelot_rmw_rix(ocelot, cpu_flood, cpu_flood, ANA_PGID_PGID, PGID_BC); - return 0; + +out_migrate_fdbs: + dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_tag_8021q_port); + + return err; } static void felix_teardown_tag_npi(struct dsa_switch *ds, int cpu) @@ -659,35 +730,97 @@ static int felix_fdb_dump(struct dsa_switch *ds, int port, } static int felix_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { + struct net_device *bridge_dev = felix_classify_db(db); struct ocelot *ocelot = ds->priv; - return ocelot_fdb_add(ocelot, port, addr, vid); + if (IS_ERR(bridge_dev)) + return PTR_ERR(bridge_dev); + + if (dsa_is_cpu_port(ds, port) && !bridge_dev && + dsa_fdb_present_in_other_db(ds, port, addr, vid, db)) + return 0; + + return ocelot_fdb_add(ocelot, port, addr, vid, bridge_dev); } static int felix_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { + struct net_device *bridge_dev = felix_classify_db(db); struct ocelot *ocelot = ds->priv; - return ocelot_fdb_del(ocelot, port, addr, vid); + if (IS_ERR(bridge_dev)) + return PTR_ERR(bridge_dev); + + if (dsa_is_cpu_port(ds, port) && !bridge_dev && + dsa_fdb_present_in_other_db(ds, port, addr, vid, db)) + return 0; + + return ocelot_fdb_del(ocelot, port, addr, vid, bridge_dev); +} + +static int felix_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct net_device *bridge_dev = felix_classify_db(db); + struct ocelot *ocelot = ds->priv; + + if (IS_ERR(bridge_dev)) + return PTR_ERR(bridge_dev); + + return ocelot_lag_fdb_add(ocelot, lag.dev, addr, vid, bridge_dev); +} + +static int felix_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct net_device *bridge_dev = felix_classify_db(db); + struct ocelot *ocelot = ds->priv; + + if (IS_ERR(bridge_dev)) + return PTR_ERR(bridge_dev); + + return ocelot_lag_fdb_del(ocelot, lag.dev, addr, vid, bridge_dev); } static int felix_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { + struct net_device *bridge_dev = felix_classify_db(db); struct ocelot *ocelot = ds->priv; - return ocelot_port_mdb_add(ocelot, port, mdb); + if (IS_ERR(bridge_dev)) + return PTR_ERR(bridge_dev); + + if (dsa_is_cpu_port(ds, port) && !bridge_dev && + dsa_mdb_present_in_other_db(ds, port, mdb, db)) + return 0; + + return ocelot_port_mdb_add(ocelot, port, mdb, bridge_dev); } static int felix_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { + struct net_device *bridge_dev = felix_classify_db(db); struct ocelot *ocelot = ds->priv; - return ocelot_port_mdb_del(ocelot, port, mdb); + if (IS_ERR(bridge_dev)) + return PTR_ERR(bridge_dev); + + if (dsa_is_cpu_port(ds, port) && !bridge_dev && + dsa_mdb_present_in_other_db(ds, port, mdb, db)) + return 0; + + return ocelot_port_mdb_del(ocelot, port, mdb, bridge_dev); } static void felix_bridge_stp_state_set(struct dsa_switch *ds, int port, @@ -719,13 +852,13 @@ static int felix_bridge_flags(struct dsa_switch *ds, int port, } static int felix_bridge_join(struct dsa_switch *ds, int port, - struct dsa_bridge bridge, bool *tx_fwd_offload) + struct dsa_bridge bridge, bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { struct ocelot *ocelot = ds->priv; - ocelot_port_bridge_join(ocelot, port, bridge.dev); - - return 0; + return ocelot_port_bridge_join(ocelot, port, bridge.dev, bridge.num, + extack); } static void felix_bridge_leave(struct dsa_switch *ds, int port, @@ -737,20 +870,20 @@ static void felix_bridge_leave(struct dsa_switch *ds, int port, } static int felix_lag_join(struct dsa_switch *ds, int port, - struct net_device *bond, + struct dsa_lag lag, struct netdev_lag_upper_info *info) { struct ocelot *ocelot = ds->priv; - return ocelot_port_lag_join(ocelot, port, bond, info); + return ocelot_port_lag_join(ocelot, port, lag.dev, info); } static int felix_lag_leave(struct dsa_switch *ds, int port, - struct net_device *bond) + struct dsa_lag lag) { struct ocelot *ocelot = ds->priv; - ocelot_port_lag_leave(ocelot, port, bond); + ocelot_port_lag_leave(ocelot, port, lag.dev); return 0; } @@ -822,6 +955,21 @@ static int felix_vlan_del(struct dsa_switch *ds, int port, return ocelot_vlan_del(ocelot, port, vlan->vid); } +static void felix_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + struct ocelot *ocelot = ds->priv; + + /* This driver does not make use of the speed, duplex, pause or the + * advertisement in its mac_config, so it is safe to mark this driver + * as non-legacy. + */ + config->legacy_pre_march2020 = false; + + __set_bit(ocelot->ports[port]->phy_mode, + config->supported_interfaces); +} + static void felix_phylink_validate(struct dsa_switch *ds, int port, unsigned long *supported, struct phylink_link_state *state) @@ -833,16 +981,18 @@ static void felix_phylink_validate(struct dsa_switch *ds, int port, felix->info->phylink_validate(ocelot, port, supported, state); } -static void felix_phylink_mac_config(struct dsa_switch *ds, int port, - unsigned int link_an_mode, - const struct phylink_link_state *state) +static struct phylink_pcs *felix_phylink_mac_select_pcs(struct dsa_switch *ds, + int port, + phy_interface_t iface) { struct ocelot *ocelot = ds->priv; struct felix *felix = ocelot_to_felix(ocelot); - struct dsa_port *dp = dsa_to_port(ds, port); + struct phylink_pcs *pcs = NULL; if (felix->pcs && felix->pcs[port]) - phylink_set_pcs(dp->pl, felix->pcs[port]); + pcs = felix->pcs[port]; + + return pcs; } static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, @@ -924,11 +1074,28 @@ static int felix_get_ts_info(struct dsa_switch *ds, int port, return ocelot_get_ts_info(ocelot, port, info); } +static const u32 felix_phy_match_table[PHY_INTERFACE_MODE_MAX] = { + [PHY_INTERFACE_MODE_INTERNAL] = OCELOT_PORT_MODE_INTERNAL, + [PHY_INTERFACE_MODE_SGMII] = OCELOT_PORT_MODE_SGMII, + [PHY_INTERFACE_MODE_QSGMII] = OCELOT_PORT_MODE_QSGMII, + [PHY_INTERFACE_MODE_USXGMII] = OCELOT_PORT_MODE_USXGMII, + [PHY_INTERFACE_MODE_2500BASEX] = OCELOT_PORT_MODE_2500BASEX, +}; + +static int felix_validate_phy_mode(struct felix *felix, int port, + phy_interface_t phy_mode) +{ + u32 modes = felix->info->port_modes[port]; + + if (felix_phy_match_table[phy_mode] & modes) + return 0; + return -EOPNOTSUPP; +} + static int felix_parse_ports_node(struct felix *felix, struct device_node *ports_node, phy_interface_t *port_phy_modes) { - struct ocelot *ocelot = &felix->ocelot; struct device *dev = felix->ocelot.dev; struct device_node *child; @@ -955,7 +1122,7 @@ static int felix_parse_ports_node(struct felix *felix, return -ENODEV; } - err = felix->info->prevalidate_phy_mode(ocelot, port, phy_mode); + err = felix_validate_phy_mode(felix, port, phy_mode); if (err < 0) { dev_err(dev, "Unsupported PHY mode %s on port %d\n", phy_modes(phy_mode), port); @@ -1192,7 +1359,9 @@ static int felix_setup(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; struct felix *felix = ocelot_to_felix(ocelot); - int port, err; + unsigned long cpu_flood; + struct dsa_port *dp; + int err; err = felix_init_structs(felix, ds->num_ports); if (err) @@ -1211,45 +1380,45 @@ static int felix_setup(struct dsa_switch *ds) } } - for (port = 0; port < ds->num_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - - ocelot_init_port(ocelot, port); + dsa_switch_for_each_available_port(dp, ds) { + ocelot_init_port(ocelot, dp->index); /* Set the default QoS Classification based on PCP and DEI * bits of vlan tag. */ - felix_port_qos_map_init(ocelot, port); + felix_port_qos_map_init(ocelot, dp->index); } err = ocelot_devlink_sb_register(ocelot); if (err) goto out_deinit_ports; - for (port = 0; port < ds->num_ports; port++) { - if (!dsa_is_cpu_port(ds, port)) - continue; - + dsa_switch_for_each_cpu_port(dp, ds) { /* The initial tag protocol is NPI which always returns 0, so * there's no real point in checking for errors. */ - felix_set_tag_protocol(ds, port, felix->tag_proto); + felix_set_tag_protocol(ds, dp->index, felix->tag_proto); + + /* Start off with flooding disabled towards the NPI port + * (actually CPU port module). + */ + cpu_flood = ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)); + ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_UC); + ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_MC); + break; } ds->mtu_enforcement_ingress = true; ds->assisted_learning_on_cpu_port = true; + ds->fdb_isolation = true; + ds->max_num_bridges = ds->num_ports; return 0; out_deinit_ports: - for (port = 0; port < ocelot->num_phys_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - - ocelot_deinit_port(ocelot, port); - } + dsa_switch_for_each_available_port(dp, ds) + ocelot_deinit_port(ocelot, dp->index); ocelot_deinit_timestamp(ocelot); ocelot_deinit(ocelot); @@ -1265,22 +1434,15 @@ static void felix_teardown(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; struct felix *felix = ocelot_to_felix(ocelot); - int port; + struct dsa_port *dp; - for (port = 0; port < ds->num_ports; port++) { - if (!dsa_is_cpu_port(ds, port)) - continue; - - felix_del_tag_protocol(ds, port, felix->tag_proto); + dsa_switch_for_each_cpu_port(dp, ds) { + felix_del_tag_protocol(ds, dp->index, felix->tag_proto); break; } - for (port = 0; port < ocelot->num_phys_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - - ocelot_deinit_port(ocelot, port); - } + dsa_switch_for_each_available_port(dp, ds) + ocelot_deinit_port(ocelot, dp->index); ocelot_devlink_sb_unregister(ocelot); ocelot_deinit_timestamp(ocelot); @@ -1302,14 +1464,23 @@ static int felix_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr) { struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + bool using_tag_8021q; + int err; - return ocelot_hwstamp_set(ocelot, port, ifr); + err = ocelot_hwstamp_set(ocelot, port, ifr); + if (err) + return err; + + using_tag_8021q = felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q; + + return felix_update_trapping_destinations(ds, using_tag_8021q); } -static bool felix_check_xtr_pkt(struct ocelot *ocelot, unsigned int ptp_type) +static bool felix_check_xtr_pkt(struct ocelot *ocelot) { struct felix *felix = ocelot_to_felix(ocelot); - int err, grp = 0; + int err = 0, grp = 0; if (felix->tag_proto != DSA_TAG_PROTO_OCELOT_8021Q) return false; @@ -1317,9 +1488,6 @@ static bool felix_check_xtr_pkt(struct ocelot *ocelot, unsigned int ptp_type) if (!felix->info->quirk_no_xtr_irq) return false; - if (ptp_type == PTP_CLASS_NONE) - return false; - while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) { struct sk_buff *skb; unsigned int type; @@ -1349,8 +1517,12 @@ static bool felix_check_xtr_pkt(struct ocelot *ocelot, unsigned int ptp_type) } out: - if (err < 0) + if (err < 0) { + dev_err_ratelimited(ocelot->dev, + "Error during packet extraction: %pe\n", + ERR_PTR(err)); ocelot_drain_cpu_queue(ocelot, 0); + } return true; } @@ -1370,7 +1542,7 @@ static bool felix_rxtstamp(struct dsa_switch *ds, int port, * MMIO in the CPU port module, and inject that into the stack from * ocelot_xtr_poll(). */ - if (felix_check_xtr_pkt(ocelot, type)) { + if (felix_check_xtr_pkt(ocelot)) { kfree_skb(skb); return true; } @@ -1430,8 +1602,17 @@ static int felix_cls_flower_add(struct dsa_switch *ds, int port, struct flow_cls_offload *cls, bool ingress) { struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + bool using_tag_8021q; + int err; - return ocelot_cls_flower_replace(ocelot, port, cls, ingress); + err = ocelot_cls_flower_replace(ocelot, port, cls, ingress); + if (err) + return err; + + using_tag_8021q = felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q; + + return felix_update_trapping_destinations(ds, using_tag_8021q); } static int felix_cls_flower_del(struct dsa_switch *ds, int port, @@ -1469,6 +1650,24 @@ static void felix_port_policer_del(struct dsa_switch *ds, int port) ocelot_port_policer_del(ocelot, port); } +static int felix_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_port_mirror_add(ocelot, port, mirror->to_local_port, + ingress, extack); +} + +static void felix_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror) +{ + struct ocelot *ocelot = ds->priv; + + ocelot_port_mirror_del(ocelot, port, mirror->ingress); +} + static int felix_port_setup_tc(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data) @@ -1618,6 +1817,44 @@ felix_mrp_del_ring_role(struct dsa_switch *ds, int port, return ocelot_mrp_del_ring_role(ocelot, port, mrp); } +static int felix_port_get_default_prio(struct dsa_switch *ds, int port) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_port_get_default_prio(ocelot, port); +} + +static int felix_port_set_default_prio(struct dsa_switch *ds, int port, + u8 prio) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_port_set_default_prio(ocelot, port, prio); +} + +static int felix_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_port_get_dscp_prio(ocelot, port, dscp); +} + +static int felix_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, + u8 prio) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_port_add_dscp_prio(ocelot, port, dscp, prio); +} + +static int felix_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, + u8 prio) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_port_del_dscp_prio(ocelot, port, dscp, prio); +} + const struct dsa_switch_ops felix_switch_ops = { .get_tag_protocol = felix_get_tag_protocol, .change_tag_protocol = felix_change_tag_protocol, @@ -1629,14 +1866,17 @@ const struct dsa_switch_ops felix_switch_ops = { .get_ethtool_stats = felix_get_ethtool_stats, .get_sset_count = felix_get_sset_count, .get_ts_info = felix_get_ts_info, + .phylink_get_caps = felix_phylink_get_caps, .phylink_validate = felix_phylink_validate, - .phylink_mac_config = felix_phylink_mac_config, + .phylink_mac_select_pcs = felix_phylink_mac_select_pcs, .phylink_mac_link_down = felix_phylink_mac_link_down, .phylink_mac_link_up = felix_phylink_mac_link_up, .port_fast_age = felix_port_fast_age, .port_fdb_dump = felix_fdb_dump, .port_fdb_add = felix_fdb_add, .port_fdb_del = felix_fdb_del, + .lag_fdb_add = felix_lag_fdb_add, + .lag_fdb_del = felix_lag_fdb_del, .port_mdb_add = felix_mdb_add, .port_mdb_del = felix_mdb_del, .port_pre_bridge_flags = felix_pre_bridge_flags, @@ -1658,6 +1898,8 @@ const struct dsa_switch_ops felix_switch_ops = { .port_max_mtu = felix_get_max_mtu, .port_policer_add = felix_port_policer_add, .port_policer_del = felix_port_policer_del, + .port_mirror_add = felix_port_mirror_add, + .port_mirror_del = felix_port_mirror_del, .cls_flower_add = felix_cls_flower_add, .cls_flower_del = felix_cls_flower_del, .cls_flower_stats = felix_cls_flower_stats, @@ -1678,6 +1920,11 @@ const struct dsa_switch_ops felix_switch_ops = { .port_mrp_del_ring_role = felix_mrp_del_ring_role, .tag_8021q_vlan_add = felix_tag_8021q_vlan_add, .tag_8021q_vlan_del = felix_tag_8021q_vlan_del, + .port_get_default_prio = felix_port_get_default_prio, + .port_set_default_prio = felix_port_set_default_prio, + .port_get_dscp_prio = felix_port_get_dscp_prio, + .port_add_dscp_prio = felix_port_add_dscp_prio, + .port_del_dscp_prio = felix_port_del_dscp_prio, }; struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port) diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index 9395ac119d33..f083b06fdfe9 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -7,6 +7,12 @@ #define ocelot_to_felix(o) container_of((o), struct felix, ocelot) #define FELIX_MAC_QUIRKS OCELOT_QUIRK_PCS_PERFORMS_RATE_ADAPTATION +#define OCELOT_PORT_MODE_INTERNAL BIT(0) +#define OCELOT_PORT_MODE_SGMII BIT(1) +#define OCELOT_PORT_MODE_QSGMII BIT(2) +#define OCELOT_PORT_MODE_2500BASEX BIT(3) +#define OCELOT_PORT_MODE_USXGMII BIT(4) + /* Platform-specific information */ struct felix_info { const struct resource *target_io_res; @@ -15,6 +21,7 @@ struct felix_info { const struct reg_field *regfields; const u32 *const *map; const struct ocelot_ops *ops; + const u32 *port_modes; int num_mact_rows; const struct ocelot_stat_layout *stats_layout; unsigned int num_stats; @@ -44,8 +51,6 @@ struct felix_info { void (*phylink_validate)(struct ocelot *ocelot, int port, unsigned long *supported, struct phylink_link_state *state); - int (*prevalidate_phy_mode)(struct ocelot *ocelot, int port, - phy_interface_t phy_mode); int (*port_setup_tc)(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data); void (*port_sched_speed_set)(struct ocelot *ocelot, int port, diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 33f0ceae381d..62d52e0874e9 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -18,12 +18,28 @@ #include #include "felix.h" +#define VSC9959_NUM_PORTS 6 + #define VSC9959_TAS_GCL_ENTRY_MAX 63 #define VSC9959_VCAP_POLICER_BASE 63 #define VSC9959_VCAP_POLICER_MAX 383 #define VSC9959_SWITCH_PCI_BAR 4 #define VSC9959_IMDIO_PCI_BAR 0 +#define VSC9959_PORT_MODE_SERDES (OCELOT_PORT_MODE_SGMII | \ + OCELOT_PORT_MODE_QSGMII | \ + OCELOT_PORT_MODE_2500BASEX | \ + OCELOT_PORT_MODE_USXGMII) + +static const u32 vsc9959_port_modes[VSC9959_NUM_PORTS] = { + VSC9959_PORT_MODE_SERDES, + VSC9959_PORT_MODE_SERDES, + VSC9959_PORT_MODE_SERDES, + VSC9959_PORT_MODE_SERDES, + OCELOT_PORT_MODE_INTERNAL, + OCELOT_PORT_MODE_INTERNAL, +}; + static const u32 vsc9959_ana_regmap[] = { REG(ANA_ADVLEARN, 0x0089a0), REG(ANA_VLANMASK, 0x0089a4), @@ -944,15 +960,8 @@ static void vsc9959_phylink_validate(struct ocelot *ocelot, int port, unsigned long *supported, struct phylink_link_state *state) { - struct ocelot_port *ocelot_port = ocelot->ports[port]; __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - if (state->interface != PHY_INTERFACE_MODE_NA && - state->interface != ocelot_port->phy_mode) { - linkmode_zero(supported); - return; - } - phylink_set_port_modes(mask); phylink_set(mask, Autoneg); phylink_set(mask, Pause); @@ -975,27 +984,6 @@ static void vsc9959_phylink_validate(struct ocelot *ocelot, int port, linkmode_and(state->advertising, state->advertising, mask); } -static int vsc9959_prevalidate_phy_mode(struct ocelot *ocelot, int port, - phy_interface_t phy_mode) -{ - switch (phy_mode) { - case PHY_INTERFACE_MODE_INTERNAL: - if (port != 4 && port != 5) - return -ENOTSUPP; - return 0; - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_QSGMII: - case PHY_INTERFACE_MODE_USXGMII: - case PHY_INTERFACE_MODE_2500BASEX: - /* Not supported on internal to-CPU ports */ - if (port == 4 || port == 5) - return -ENOTSUPP; - return 0; - default: - return -ENOTSUPP; - } -} - /* Watermark encode * Bit 8: Unit; 0:1, 1:16 * Bit 7-0: Value to be multiplied with unit @@ -2231,14 +2219,14 @@ static const struct felix_info felix_info_vsc9959 = { .vcap_pol_base2 = 0, .vcap_pol_max2 = 0, .num_mact_rows = 2048, - .num_ports = 6, + .num_ports = VSC9959_NUM_PORTS, .num_tx_queues = OCELOT_NUM_TC, .quirk_no_xtr_irq = true, .ptp_caps = &vsc9959_ptp_caps, .mdio_bus_alloc = vsc9959_mdio_bus_alloc, .mdio_bus_free = vsc9959_mdio_bus_free, .phylink_validate = vsc9959_phylink_validate, - .prevalidate_phy_mode = vsc9959_prevalidate_phy_mode, + .port_modes = vsc9959_port_modes, .port_setup_tc = vsc9959_port_setup_tc, .port_sched_speed_set = vsc9959_sched_speed_set, .init_regmap = ocelot_regmap_init, diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index f2f1608a476c..68ef8f111bbe 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -14,11 +14,29 @@ #include #include "felix.h" +#define VSC9953_NUM_PORTS 10 + #define VSC9953_VCAP_POLICER_BASE 11 #define VSC9953_VCAP_POLICER_MAX 31 #define VSC9953_VCAP_POLICER_BASE2 120 #define VSC9953_VCAP_POLICER_MAX2 161 +#define VSC9953_PORT_MODE_SERDES (OCELOT_PORT_MODE_SGMII | \ + OCELOT_PORT_MODE_QSGMII) + +static const u32 vsc9953_port_modes[VSC9953_NUM_PORTS] = { + VSC9953_PORT_MODE_SERDES, + VSC9953_PORT_MODE_SERDES, + VSC9953_PORT_MODE_SERDES, + VSC9953_PORT_MODE_SERDES, + VSC9953_PORT_MODE_SERDES, + VSC9953_PORT_MODE_SERDES, + VSC9953_PORT_MODE_SERDES, + VSC9953_PORT_MODE_SERDES, + OCELOT_PORT_MODE_INTERNAL, + OCELOT_PORT_MODE_INTERNAL, +}; + static const u32 vsc9953_ana_regmap[] = { REG(ANA_ADVLEARN, 0x00b500), REG(ANA_VLANMASK, 0x00b504), @@ -917,15 +935,8 @@ static void vsc9953_phylink_validate(struct ocelot *ocelot, int port, unsigned long *supported, struct phylink_link_state *state) { - struct ocelot_port *ocelot_port = ocelot->ports[port]; __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - if (state->interface != PHY_INTERFACE_MODE_NA && - state->interface != ocelot_port->phy_mode) { - linkmode_zero(supported); - return; - } - phylink_set_port_modes(mask); phylink_set(mask, Autoneg); phylink_set(mask, Pause); @@ -945,25 +956,6 @@ static void vsc9953_phylink_validate(struct ocelot *ocelot, int port, linkmode_and(state->advertising, state->advertising, mask); } -static int vsc9953_prevalidate_phy_mode(struct ocelot *ocelot, int port, - phy_interface_t phy_mode) -{ - switch (phy_mode) { - case PHY_INTERFACE_MODE_INTERNAL: - if (port != 8 && port != 9) - return -ENOTSUPP; - return 0; - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_QSGMII: - /* Not supported on internal to-CPU ports */ - if (port == 8 || port == 9) - return -ENOTSUPP; - return 0; - default: - return -ENOTSUPP; - } -} - /* Watermark encode * Bit 9: Unit; 0:1, 1:16 * Bit 8-0: Value to be multiplied with unit @@ -1101,12 +1093,12 @@ static const struct felix_info seville_info_vsc9953 = { .vcap_pol_base2 = VSC9953_VCAP_POLICER_BASE2, .vcap_pol_max2 = VSC9953_VCAP_POLICER_MAX2, .num_mact_rows = 2048, - .num_ports = 10, + .num_ports = VSC9953_NUM_PORTS, .num_tx_queues = OCELOT_NUM_TC, .mdio_bus_alloc = vsc9953_mdio_bus_alloc, .mdio_bus_free = vsc9953_mdio_bus_free, .phylink_validate = vsc9953_phylink_validate, - .prevalidate_phy_mode = vsc9953_prevalidate_phy_mode, + .port_modes = vsc9953_port_modes, .init_regmap = ocelot_regmap_init, }; diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index c39de2a4c1fe..e5098cfe44bc 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -499,52 +499,27 @@ static enum dsa_tag_protocol ar9331_sw_get_tag_protocol(struct dsa_switch *ds, return DSA_TAG_PROTO_AR9331; } -static void ar9331_sw_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void ar9331_sw_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100; switch (port) { case 0: - if (state->interface != PHY_INTERFACE_MODE_GMII) - goto unsupported; - - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseT_Half); + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); + config->mac_capabilities |= MAC_1000; break; case 1: case 2: case 3: case 4: case 5: - if (state->interface != PHY_INTERFACE_MODE_INTERNAL) - goto unsupported; + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); break; - default: - linkmode_zero(supported); - dev_err(ds->dev, "Unsupported port: %i\n", port); - return; } - - phylink_set_port_modes(mask); - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); - - return; - -unsupported: - linkmode_zero(supported); - dev_err(ds->dev, "Unsupported interface: %d, port: %d\n", - state->interface, port); } static void ar9331_sw_phylink_mac_config(struct dsa_switch *ds, int port, @@ -697,7 +672,7 @@ static const struct dsa_switch_ops ar9331_sw_ops = { .get_tag_protocol = ar9331_sw_get_tag_protocol, .setup = ar9331_sw_setup, .port_disable = ar9331_sw_port_disable, - .phylink_validate = ar9331_sw_phylink_validate, + .phylink_get_caps = ar9331_sw_phylink_get_caps, .phylink_mac_config = ar9331_sw_phylink_mac_config, .phylink_mac_link_down = ar9331_sw_phylink_mac_link_down, .phylink_mac_link_up = ar9331_sw_phylink_mac_link_up, diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 039694518788..d3ed0a7f8077 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "qca8k.h" @@ -74,12 +75,6 @@ static const struct qca8k_mib_desc ar8327_mib[] = { MIB_DESC(1, 0xac, "TXUnicast"), }; -/* The 32bit switch registers are accessed indirectly. To achieve this we need - * to set the page of the register. Track the last page that was set to reduce - * mdio writes - */ -static u16 qca8k_current_page = 0xffff; - static void qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) { @@ -93,6 +88,44 @@ qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) *page = regaddr & 0x3ff; } +static int +qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo) +{ + u16 *cached_lo = &priv->mdio_cache.lo; + struct mii_bus *bus = priv->bus; + int ret; + + if (lo == *cached_lo) + return 0; + + ret = bus->write(bus, phy_id, regnum, lo); + if (ret < 0) + dev_err_ratelimited(&bus->dev, + "failed to write qca8k 32bit lo register\n"); + + *cached_lo = lo; + return 0; +} + +static int +qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi) +{ + u16 *cached_hi = &priv->mdio_cache.hi; + struct mii_bus *bus = priv->bus; + int ret; + + if (hi == *cached_hi) + return 0; + + ret = bus->write(bus, phy_id, regnum, hi); + if (ret < 0) + dev_err_ratelimited(&bus->dev, + "failed to write qca8k 32bit hi register\n"); + + *cached_hi = hi; + return 0; +} + static int qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val) { @@ -116,7 +149,7 @@ qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val) } static void -qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val) +qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val) { u16 lo, hi; int ret; @@ -124,20 +157,19 @@ qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val) lo = val & 0xffff; hi = (u16)(val >> 16); - ret = bus->write(bus, phy_id, regnum, lo); + ret = qca8k_set_lo(priv, phy_id, regnum, lo); if (ret >= 0) - ret = bus->write(bus, phy_id, regnum + 1, hi); - if (ret < 0) - dev_err_ratelimited(&bus->dev, - "failed to write qca8k 32bit register\n"); + ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi); } static int -qca8k_set_page(struct mii_bus *bus, u16 page) +qca8k_set_page(struct qca8k_priv *priv, u16 page) { + u16 *cached_page = &priv->mdio_cache.page; + struct mii_bus *bus = priv->bus; int ret; - if (page == qca8k_current_page) + if (page == *cached_page) return 0; ret = bus->write(bus, 0x18, 0, page); @@ -147,7 +179,7 @@ qca8k_set_page(struct mii_bus *bus, u16 page) return ret; } - qca8k_current_page = page; + *cached_page = page; usleep_range(1000, 2000); return 0; } @@ -170,6 +202,252 @@ qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) return regmap_update_bits(priv->regmap, reg, mask, write_val); } +static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb) +{ + struct qca8k_mgmt_eth_data *mgmt_eth_data; + struct qca8k_priv *priv = ds->priv; + struct qca_mgmt_ethhdr *mgmt_ethhdr; + u8 len, cmd; + + mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb); + mgmt_eth_data = &priv->mgmt_eth_data; + + cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command); + len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command); + + /* Make sure the seq match the requested packet */ + if (mgmt_ethhdr->seq == mgmt_eth_data->seq) + mgmt_eth_data->ack = true; + + if (cmd == MDIO_READ) { + mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data; + + /* Get the rest of the 12 byte of data. + * The read/write function will extract the requested data. + */ + if (len > QCA_HDR_MGMT_DATA1_LEN) + memcpy(mgmt_eth_data->data + 1, skb->data, + QCA_HDR_MGMT_DATA2_LEN); + } + + complete(&mgmt_eth_data->rw_done); +} + +static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val, + int priority, unsigned int len) +{ + struct qca_mgmt_ethhdr *mgmt_ethhdr; + unsigned int real_len; + struct sk_buff *skb; + u32 *data2; + u16 hdr; + + skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN); + if (!skb) + return NULL; + + /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte + * Actually for some reason the steps are: + * 0: nothing + * 1-4: first 4 byte + * 5-6: first 12 byte + * 7-15: all 16 byte + */ + if (len == 16) + real_len = 15; + else + real_len = len; + + skb_reset_mac_header(skb); + skb_set_network_header(skb, skb->len); + + mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN); + + hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION); + hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority); + hdr |= QCA_HDR_XMIT_FROM_CPU; + hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0)); + hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG); + + mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg); + mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len); + mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd); + mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE, + QCA_HDR_MGMT_CHECK_CODE_VAL); + + if (cmd == MDIO_WRITE) + mgmt_ethhdr->mdio_data = *val; + + mgmt_ethhdr->hdr = htons(hdr); + + data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN); + if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) + memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN); + + return skb; +} + +static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num) +{ + struct qca_mgmt_ethhdr *mgmt_ethhdr; + + mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data; + mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num); +} + +static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +{ + struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; + struct sk_buff *skb; + bool ack; + int ret; + + skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL, + QCA8K_ETHERNET_MDIO_PRIORITY, len); + if (!skb) + return -ENOMEM; + + mutex_lock(&mgmt_eth_data->mutex); + + /* Check mgmt_master if is operational */ + if (!priv->mgmt_master) { + kfree_skb(skb); + mutex_unlock(&mgmt_eth_data->mutex); + return -EINVAL; + } + + skb->dev = priv->mgmt_master; + + reinit_completion(&mgmt_eth_data->rw_done); + + /* Increment seq_num and set it in the mdio pkt */ + mgmt_eth_data->seq++; + qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); + mgmt_eth_data->ack = false; + + dev_queue_xmit(skb); + + ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, + msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); + + *val = mgmt_eth_data->data[0]; + if (len > QCA_HDR_MGMT_DATA1_LEN) + memcpy(val + 1, mgmt_eth_data->data + 1, len - QCA_HDR_MGMT_DATA1_LEN); + + ack = mgmt_eth_data->ack; + + mutex_unlock(&mgmt_eth_data->mutex); + + if (ret <= 0) + return -ETIMEDOUT; + + if (!ack) + return -EINVAL; + + return 0; +} + +static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +{ + struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; + struct sk_buff *skb; + bool ack; + int ret; + + skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, val, + QCA8K_ETHERNET_MDIO_PRIORITY, len); + if (!skb) + return -ENOMEM; + + mutex_lock(&mgmt_eth_data->mutex); + + /* Check mgmt_master if is operational */ + if (!priv->mgmt_master) { + kfree_skb(skb); + mutex_unlock(&mgmt_eth_data->mutex); + return -EINVAL; + } + + skb->dev = priv->mgmt_master; + + reinit_completion(&mgmt_eth_data->rw_done); + + /* Increment seq_num and set it in the mdio pkt */ + mgmt_eth_data->seq++; + qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); + mgmt_eth_data->ack = false; + + dev_queue_xmit(skb); + + ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, + msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); + + ack = mgmt_eth_data->ack; + + mutex_unlock(&mgmt_eth_data->mutex); + + if (ret <= 0) + return -ETIMEDOUT; + + if (!ack) + return -EINVAL; + + return 0; +} + +static int +qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) +{ + u32 val = 0; + int ret; + + ret = qca8k_read_eth(priv, reg, &val, sizeof(val)); + if (ret) + return ret; + + val &= ~mask; + val |= write_val; + + return qca8k_write_eth(priv, reg, &val, sizeof(val)); +} + +static int +qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +{ + int i, count = len / sizeof(u32), ret; + + if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len)) + return 0; + + for (i = 0; i < count; i++) { + ret = regmap_read(priv->regmap, reg + (i * 4), val + i); + if (ret < 0) + return ret; + } + + return 0; +} + +static int +qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +{ + int i, count = len / sizeof(u32), ret; + u32 tmp; + + if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len)) + return 0; + + for (i = 0; i < count; i++) { + tmp = val[i]; + + ret = regmap_write(priv->regmap, reg + (i * 4), tmp); + if (ret < 0) + return ret; + } + + return 0; +} + static int qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) { @@ -178,11 +456,14 @@ qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) u16 r1, r2, page; int ret; + if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) + return 0; + qca8k_split_addr(reg, &r1, &r2, &page); mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); - ret = qca8k_set_page(bus, page); + ret = qca8k_set_page(priv, page); if (ret < 0) goto exit; @@ -201,15 +482,18 @@ qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val) u16 r1, r2, page; int ret; + if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) + return 0; + qca8k_split_addr(reg, &r1, &r2, &page); mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); - ret = qca8k_set_page(bus, page); + ret = qca8k_set_page(priv, page); if (ret < 0) goto exit; - qca8k_mii_write32(bus, 0x10 | r2, r1, val); + qca8k_mii_write32(priv, 0x10 | r2, r1, val); exit: mutex_unlock(&bus->mdio_lock); @@ -225,11 +509,14 @@ qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_ u32 val; int ret; + if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val)) + return 0; + qca8k_split_addr(reg, &r1, &r2, &page); mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); - ret = qca8k_set_page(bus, page); + ret = qca8k_set_page(priv, page); if (ret < 0) goto exit; @@ -239,7 +526,7 @@ qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_ val &= ~mask; val |= write_val; - qca8k_mii_write32(bus, 0x10 | r2, r1, val); + qca8k_mii_write32(priv, 0x10 | r2, r1, val); exit: mutex_unlock(&bus->mdio_lock); @@ -296,17 +583,13 @@ qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) static int qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) { - u32 reg[4], val; - int i, ret; + u32 reg[3]; + int ret; /* load the ARL table into an array */ - for (i = 0; i < 4; i++) { - ret = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4), &val); - if (ret < 0) - return ret; - - reg[i] = val; - } + ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); + if (ret) + return ret; /* vid - 83:72 */ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); @@ -330,7 +613,6 @@ qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, u8 aging) { u32 reg[3] = { 0 }; - int i; /* vid - 83:72 */ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); @@ -347,8 +629,7 @@ qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); /* load the array into the ARL table */ - for (i = 0; i < 3; i++) - qca8k_write(priv, QCA8K_REG_ATU_DATA0 + (i * 4), reg[i]); + qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); } static int @@ -632,7 +913,10 @@ qca8k_mib_init(struct qca8k_priv *priv) int ret; mutex_lock(&priv->reg_mutex); - ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); + ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, + QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, + FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | + QCA8K_MIB_BUSY); if (ret) goto exit; @@ -666,6 +950,199 @@ qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); } +static int +qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, + struct sk_buff *read_skb, u32 *val) +{ + struct sk_buff *skb = skb_copy(read_skb, GFP_KERNEL); + bool ack; + int ret; + + reinit_completion(&mgmt_eth_data->rw_done); + + /* Increment seq_num and set it in the copy pkt */ + mgmt_eth_data->seq++; + qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); + mgmt_eth_data->ack = false; + + dev_queue_xmit(skb); + + ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, + QCA8K_ETHERNET_TIMEOUT); + + ack = mgmt_eth_data->ack; + + if (ret <= 0) + return -ETIMEDOUT; + + if (!ack) + return -EINVAL; + + *val = mgmt_eth_data->data[0]; + + return 0; +} + +static int +qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy, + int regnum, u16 data) +{ + struct sk_buff *write_skb, *clear_skb, *read_skb; + struct qca8k_mgmt_eth_data *mgmt_eth_data; + u32 write_val, clear_val = 0, val; + struct net_device *mgmt_master; + int ret, ret1; + bool ack; + + if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) + return -EINVAL; + + mgmt_eth_data = &priv->mgmt_eth_data; + + write_val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | + QCA8K_MDIO_MASTER_PHY_ADDR(phy) | + QCA8K_MDIO_MASTER_REG_ADDR(regnum); + + if (read) { + write_val |= QCA8K_MDIO_MASTER_READ; + } else { + write_val |= QCA8K_MDIO_MASTER_WRITE; + write_val |= QCA8K_MDIO_MASTER_DATA(data); + } + + /* Prealloc all the needed skb before the lock */ + write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &write_val, + QCA8K_ETHERNET_PHY_PRIORITY, sizeof(write_val)); + if (!write_skb) + return -ENOMEM; + + clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val, + QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); + if (!clear_skb) { + ret = -ENOMEM; + goto err_clear_skb; + } + + read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, &clear_val, + QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); + if (!read_skb) { + ret = -ENOMEM; + goto err_read_skb; + } + + /* Actually start the request: + * 1. Send mdio master packet + * 2. Busy Wait for mdio master command + * 3. Get the data if we are reading + * 4. Reset the mdio master (even with error) + */ + mutex_lock(&mgmt_eth_data->mutex); + + /* Check if mgmt_master is operational */ + mgmt_master = priv->mgmt_master; + if (!mgmt_master) { + mutex_unlock(&mgmt_eth_data->mutex); + ret = -EINVAL; + goto err_mgmt_master; + } + + read_skb->dev = mgmt_master; + clear_skb->dev = mgmt_master; + write_skb->dev = mgmt_master; + + reinit_completion(&mgmt_eth_data->rw_done); + + /* Increment seq_num and set it in the write pkt */ + mgmt_eth_data->seq++; + qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq); + mgmt_eth_data->ack = false; + + dev_queue_xmit(write_skb); + + ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, + QCA8K_ETHERNET_TIMEOUT); + + ack = mgmt_eth_data->ack; + + if (ret <= 0) { + ret = -ETIMEDOUT; + kfree_skb(read_skb); + goto exit; + } + + if (!ack) { + ret = -EINVAL; + kfree_skb(read_skb); + goto exit; + } + + ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1, + !(val & QCA8K_MDIO_MASTER_BUSY), 0, + QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, + mgmt_eth_data, read_skb, &val); + + if (ret < 0 && ret1 < 0) { + ret = ret1; + goto exit; + } + + if (read) { + reinit_completion(&mgmt_eth_data->rw_done); + + /* Increment seq_num and set it in the read pkt */ + mgmt_eth_data->seq++; + qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq); + mgmt_eth_data->ack = false; + + dev_queue_xmit(read_skb); + + ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, + QCA8K_ETHERNET_TIMEOUT); + + ack = mgmt_eth_data->ack; + + if (ret <= 0) { + ret = -ETIMEDOUT; + goto exit; + } + + if (!ack) { + ret = -EINVAL; + goto exit; + } + + ret = mgmt_eth_data->data[0] & QCA8K_MDIO_MASTER_DATA_MASK; + } else { + kfree_skb(read_skb); + } +exit: + reinit_completion(&mgmt_eth_data->rw_done); + + /* Increment seq_num and set it in the clear pkt */ + mgmt_eth_data->seq++; + qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq); + mgmt_eth_data->ack = false; + + dev_queue_xmit(clear_skb); + + wait_for_completion_timeout(&mgmt_eth_data->rw_done, + QCA8K_ETHERNET_TIMEOUT); + + mutex_unlock(&mgmt_eth_data->mutex); + + return ret; + + /* Error handling before lock */ +err_mgmt_master: + kfree_skb(read_skb); +err_read_skb: + kfree_skb(clear_skb); +err_clear_skb: + kfree_skb(write_skb); + + return ret; +} + static u32 qca8k_port_to_phy(int port) { @@ -704,8 +1181,9 @@ qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) } static int -qca8k_mdio_write(struct mii_bus *bus, int phy, int regnum, u16 data) +qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data) { + struct mii_bus *bus = priv->bus; u16 r1, r2, page; u32 val; int ret; @@ -722,18 +1200,18 @@ qca8k_mdio_write(struct mii_bus *bus, int phy, int regnum, u16 data) mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); - ret = qca8k_set_page(bus, page); + ret = qca8k_set_page(priv, page); if (ret) goto exit; - qca8k_mii_write32(bus, 0x10 | r2, r1, val); + qca8k_mii_write32(priv, 0x10 | r2, r1, val); ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, QCA8K_MDIO_MASTER_BUSY); exit: /* even if the busy_wait timeouts try to clear the MASTER_EN */ - qca8k_mii_write32(bus, 0x10 | r2, r1, 0); + qca8k_mii_write32(priv, 0x10 | r2, r1, 0); mutex_unlock(&bus->mdio_lock); @@ -741,8 +1219,9 @@ qca8k_mdio_write(struct mii_bus *bus, int phy, int regnum, u16 data) } static int -qca8k_mdio_read(struct mii_bus *bus, int phy, int regnum) +qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum) { + struct mii_bus *bus = priv->bus; u16 r1, r2, page; u32 val; int ret; @@ -758,11 +1237,11 @@ qca8k_mdio_read(struct mii_bus *bus, int phy, int regnum) mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); - ret = qca8k_set_page(bus, page); + ret = qca8k_set_page(priv, page); if (ret) goto exit; - qca8k_mii_write32(bus, 0x10 | r2, r1, val); + qca8k_mii_write32(priv, 0x10 | r2, r1, val); ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, QCA8K_MDIO_MASTER_BUSY); @@ -773,7 +1252,7 @@ qca8k_mdio_read(struct mii_bus *bus, int phy, int regnum) exit: /* even if the busy_wait timeouts try to clear the MASTER_EN */ - qca8k_mii_write32(bus, 0x10 | r2, r1, 0); + qca8k_mii_write32(priv, 0x10 | r2, r1, 0); mutex_unlock(&bus->mdio_lock); @@ -787,24 +1266,35 @@ static int qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data) { struct qca8k_priv *priv = slave_bus->priv; - struct mii_bus *bus = priv->bus; + int ret; - return qca8k_mdio_write(bus, phy, regnum, data); + /* Use mdio Ethernet when available, fallback to legacy one on error */ + ret = qca8k_phy_eth_command(priv, false, phy, regnum, data); + if (!ret) + return 0; + + return qca8k_mdio_write(priv, phy, regnum, data); } static int qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum) { struct qca8k_priv *priv = slave_bus->priv; - struct mii_bus *bus = priv->bus; + int ret; - return qca8k_mdio_read(bus, phy, regnum); + /* Use mdio Ethernet when available, fallback to legacy one on error */ + ret = qca8k_phy_eth_command(priv, true, phy, regnum, 0); + if (ret >= 0) + return ret; + + return qca8k_mdio_read(priv, phy, regnum); } static int qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data) { struct qca8k_priv *priv = ds->priv; + int ret; /* Check if the legacy mapping should be used and the * port is not correctly mapped to the right PHY in the @@ -813,7 +1303,12 @@ qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data) if (priv->legacy_phy_port_mapping) port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; - return qca8k_mdio_write(priv->bus, port, regnum, data); + /* Use mdio Ethernet when available, fallback to legacy one on error */ + ret = qca8k_phy_eth_command(priv, false, port, regnum, 0); + if (!ret) + return ret; + + return qca8k_mdio_write(priv, port, regnum, data); } static int @@ -829,7 +1324,12 @@ qca8k_phy_read(struct dsa_switch *ds, int port, int regnum) if (priv->legacy_phy_port_mapping) port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; - ret = qca8k_mdio_read(priv->bus, port, regnum); + /* Use mdio Ethernet when available, fallback to legacy one on error */ + ret = qca8k_phy_eth_command(priv, true, port, regnum, 0); + if (ret >= 0) + return ret; + + ret = qca8k_mdio_read(priv, port, regnum); if (ret < 0) return 0xffff; @@ -1132,220 +1632,6 @@ qca8k_parse_port_config(struct qca8k_priv *priv) return 0; } -static int -qca8k_setup(struct dsa_switch *ds) -{ - struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; - int cpu_port, ret, i; - u32 mask; - - cpu_port = qca8k_find_cpu_port(ds); - if (cpu_port < 0) { - dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6"); - return cpu_port; - } - - /* Parse CPU port config to be later used in phy_link mac_config */ - ret = qca8k_parse_port_config(priv); - if (ret) - return ret; - - ret = qca8k_setup_mdio_bus(priv); - if (ret) - return ret; - - ret = qca8k_setup_of_pws_reg(priv); - if (ret) - return ret; - - ret = qca8k_setup_mac_pwr_sel(priv); - if (ret) - return ret; - - /* Make sure MAC06 is disabled */ - ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL, - QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); - if (ret) { - dev_err(priv->dev, "failed disabling MAC06 exchange"); - return ret; - } - - /* Enable CPU Port */ - ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, - QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); - if (ret) { - dev_err(priv->dev, "failed enabling CPU port"); - return ret; - } - - /* Enable MIB counters */ - ret = qca8k_mib_init(priv); - if (ret) - dev_warn(priv->dev, "mib init failed"); - - /* Initial setup of all ports */ - for (i = 0; i < QCA8K_NUM_PORTS; i++) { - /* Disable forwarding by default on all ports */ - ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_MEMBER, 0); - if (ret) - return ret; - - /* Enable QCA header mode on all cpu ports */ - if (dsa_is_cpu_port(ds, i)) { - ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), - FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | - FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); - if (ret) { - dev_err(priv->dev, "failed enabling QCA header mode"); - return ret; - } - } - - /* Disable MAC by default on all user ports */ - if (dsa_is_user_port(ds, i)) - qca8k_port_set_status(priv, i, 0); - } - - /* Forward all unknown frames to CPU port for Linux processing - * Notice that in multi-cpu config only one port should be set - * for igmp, unknown, multicast and broadcast packet - */ - ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, - FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | - FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | - FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | - FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); - if (ret) - return ret; - - /* Setup connection between CPU port & user ports - * Configure specific switch configuration for ports - */ - for (i = 0; i < QCA8K_NUM_PORTS; i++) { - /* CPU port gets connected to all user ports of the switch */ - if (dsa_is_cpu_port(ds, i)) { - ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); - if (ret) - return ret; - } - - /* Individual user ports get connected to CPU port only */ - if (dsa_is_user_port(ds, i)) { - ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_MEMBER, - BIT(cpu_port)); - if (ret) - return ret; - - /* Enable ARP Auto-learning by default */ - ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_LEARN); - if (ret) - return ret; - - /* For port based vlans to work we need to set the - * default egress vid - */ - ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), - QCA8K_EGREES_VLAN_PORT_MASK(i), - QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); - if (ret) - return ret; - - ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), - QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | - QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); - if (ret) - return ret; - } - - /* The port 5 of the qca8337 have some problem in flood condition. The - * original legacy driver had some specific buffer and priority settings - * for the different port suggested by the QCA switch team. Add this - * missing settings to improve switch stability under load condition. - * This problem is limited to qca8337 and other qca8k switch are not affected. - */ - if (priv->switch_id == QCA8K_ID_QCA8337) { - switch (i) { - /* The 2 CPU port and port 5 requires some different - * priority than any other ports. - */ - case 0: - case 5: - case 6: - mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | - QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | - QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | - QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | - QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | - QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | - QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); - break; - default: - mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | - QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | - QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | - QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | - QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); - } - qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); - - mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | - QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | - QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | - QCA8K_PORT_HOL_CTRL1_WRED_EN; - qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), - QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | - QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | - QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | - QCA8K_PORT_HOL_CTRL1_WRED_EN, - mask); - } - - /* Set initial MTU for every port. - * We have only have a general MTU setting. So track - * every port and set the max across all port. - * Set per port MTU to 1500 as the MTU change function - * will add the overhead and if its set to 1518 then it - * will apply the overhead again and we will end up with - * MTU of 1536 instead of 1518 - */ - priv->port_mtu[i] = ETH_DATA_LEN; - } - - /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ - if (priv->switch_id == QCA8K_ID_QCA8327) { - mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) | - QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496); - qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH, - QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK | - QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, - mask); - } - - /* Setup our port MTUs to match power on defaults */ - ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); - if (ret) - dev_warn(priv->dev, "failed setting MTU settings"); - - /* Flush the FDB table */ - qca8k_fdb_flush(priv); - - /* We don't have interrupts for link changes, so we need to poll */ - ds->pcs_poll = true; - - /* Set min a max ageing value supported */ - ds->ageing_time_min = 7000; - ds->ageing_time_max = 458745000; - - /* Set max number of LAGs supported */ - ds->num_lag_ids = QCA8K_NUM_LAGS; - - return 0; -} - static void qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index, u32 reg) @@ -1387,13 +1673,41 @@ qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_inde cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6); } +static struct phylink_pcs * +qca8k_phylink_mac_select_pcs(struct dsa_switch *ds, int port, + phy_interface_t interface) +{ + struct qca8k_priv *priv = ds->priv; + struct phylink_pcs *pcs = NULL; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + switch (port) { + case 0: + pcs = &priv->pcs_port_0.pcs; + break; + + case 6: + pcs = &priv->pcs_port_6.pcs; + break; + } + break; + + default: + break; + } + + return pcs; +} + static void qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, const struct phylink_link_state *state) { struct qca8k_priv *priv = ds->priv; - int cpu_port_index, ret; - u32 reg, val; + int cpu_port_index; + u32 reg; switch (port) { case 0: /* 1st CPU port */ @@ -1459,70 +1773,6 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, case PHY_INTERFACE_MODE_1000BASEX: /* Enable SGMII on the port */ qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); - - /* Enable/disable SerDes auto-negotiation as necessary */ - ret = qca8k_read(priv, QCA8K_REG_PWS, &val); - if (ret) - return; - if (phylink_autoneg_inband(mode)) - val &= ~QCA8K_PWS_SERDES_AEN_DIS; - else - val |= QCA8K_PWS_SERDES_AEN_DIS; - qca8k_write(priv, QCA8K_REG_PWS, val); - - /* Configure the SGMII parameters */ - ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); - if (ret) - return; - - val |= QCA8K_SGMII_EN_SD; - - if (priv->ports_config.sgmii_enable_pll) - val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | - QCA8K_SGMII_EN_TX; - - if (dsa_is_cpu_port(ds, port)) { - /* CPU port, we're talking to the CPU MAC, be a PHY */ - val &= ~QCA8K_SGMII_MODE_CTRL_MASK; - val |= QCA8K_SGMII_MODE_CTRL_PHY; - } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { - val &= ~QCA8K_SGMII_MODE_CTRL_MASK; - val |= QCA8K_SGMII_MODE_CTRL_MAC; - } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { - val &= ~QCA8K_SGMII_MODE_CTRL_MASK; - val |= QCA8K_SGMII_MODE_CTRL_BASEX; - } - - qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); - - /* From original code is reported port instability as SGMII also - * require delay set. Apply advised values here or take them from DT. - */ - if (state->interface == PHY_INTERFACE_MODE_SGMII) - qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); - - /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and - * falling edge is set writing in the PORT0 PAD reg - */ - if (priv->switch_id == QCA8K_ID_QCA8327 || - priv->switch_id == QCA8K_ID_QCA8337) - reg = QCA8K_REG_PORT0_PAD_CTRL; - - val = 0; - - /* SGMII Clock phase configuration */ - if (priv->ports_config.sgmii_rx_clk_falling_edge) - val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; - - if (priv->ports_config.sgmii_tx_clk_falling_edge) - val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE; - - if (val) - ret = qca8k_rmw(priv, reg, - QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | - QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, - val); - break; default: dev_err(ds->dev, "xMII mode %s not supported for port %d\n", @@ -1531,109 +1781,41 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, } } -static void -qca8k_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void qca8k_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - switch (port) { case 0: /* 1st CPU port */ - if (state->interface != PHY_INTERFACE_MODE_NA && - state->interface != PHY_INTERFACE_MODE_RGMII && - state->interface != PHY_INTERFACE_MODE_RGMII_ID && - state->interface != PHY_INTERFACE_MODE_RGMII_TXID && - state->interface != PHY_INTERFACE_MODE_RGMII_RXID && - state->interface != PHY_INTERFACE_MODE_SGMII) - goto unsupported; + phy_interface_set_rgmii(config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); break; + case 1: case 2: case 3: case 4: case 5: /* Internal PHY */ - if (state->interface != PHY_INTERFACE_MODE_NA && - state->interface != PHY_INTERFACE_MODE_GMII && - state->interface != PHY_INTERFACE_MODE_INTERNAL) - goto unsupported; + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); break; + case 6: /* 2nd CPU port / external PHY */ - if (state->interface != PHY_INTERFACE_MODE_NA && - state->interface != PHY_INTERFACE_MODE_RGMII && - state->interface != PHY_INTERFACE_MODE_RGMII_ID && - state->interface != PHY_INTERFACE_MODE_RGMII_TXID && - state->interface != PHY_INTERFACE_MODE_RGMII_RXID && - state->interface != PHY_INTERFACE_MODE_SGMII && - state->interface != PHY_INTERFACE_MODE_1000BASEX) - goto unsupported; - break; - default: -unsupported: - linkmode_zero(supported); - return; - } - - phylink_set_port_modes(mask); - phylink_set(mask, Autoneg); - - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - - if (state->interface == PHY_INTERFACE_MODE_1000BASEX) - phylink_set(mask, 1000baseX_Full); - - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); -} - -static int -qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, - struct phylink_link_state *state) -{ - struct qca8k_priv *priv = ds->priv; - u32 reg; - int ret; - - ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); - if (ret < 0) - return ret; - - state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); - state->an_complete = state->link; - state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); - state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : - DUPLEX_HALF; - - switch (reg & QCA8K_PORT_STATUS_SPEED) { - case QCA8K_PORT_STATUS_SPEED_10: - state->speed = SPEED_10; - break; - case QCA8K_PORT_STATUS_SPEED_100: - state->speed = SPEED_100; - break; - case QCA8K_PORT_STATUS_SPEED_1000: - state->speed = SPEED_1000; - break; - default: - state->speed = SPEED_UNKNOWN; + phy_interface_set_rgmii(config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + config->supported_interfaces); break; } - state->pause = MLO_PAUSE_NONE; - if (reg & QCA8K_PORT_STATUS_RXFLOW) - state->pause |= MLO_PAUSE_RX; - if (reg & QCA8K_PORT_STATUS_TXFLOW) - state->pause |= MLO_PAUSE_TX; + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000FD; - return 1; + config->legacy_pre_march2020 = false; } static void @@ -1686,6 +1868,164 @@ qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); } +static struct qca8k_pcs *pcs_to_qca8k_pcs(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct qca8k_pcs, pcs); +} + +static void qca8k_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv; + int port = pcs_to_qca8k_pcs(pcs)->port; + u32 reg; + int ret; + + ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); + if (ret < 0) { + state->link = false; + return; + } + + state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); + state->an_complete = state->link; + state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); + state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : + DUPLEX_HALF; + + switch (reg & QCA8K_PORT_STATUS_SPEED) { + case QCA8K_PORT_STATUS_SPEED_10: + state->speed = SPEED_10; + break; + case QCA8K_PORT_STATUS_SPEED_100: + state->speed = SPEED_100; + break; + case QCA8K_PORT_STATUS_SPEED_1000: + state->speed = SPEED_1000; + break; + default: + state->speed = SPEED_UNKNOWN; + break; + } + + if (reg & QCA8K_PORT_STATUS_RXFLOW) + state->pause |= MLO_PAUSE_RX; + if (reg & QCA8K_PORT_STATUS_TXFLOW) + state->pause |= MLO_PAUSE_TX; +} + +static int qca8k_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv; + int cpu_port_index, ret, port; + u32 reg, val; + + port = pcs_to_qca8k_pcs(pcs)->port; + switch (port) { + case 0: + reg = QCA8K_REG_PORT0_PAD_CTRL; + cpu_port_index = QCA8K_CPU_PORT0; + break; + + case 6: + reg = QCA8K_REG_PORT6_PAD_CTRL; + cpu_port_index = QCA8K_CPU_PORT6; + break; + + default: + WARN_ON(1); + return -EINVAL; + } + + /* Enable/disable SerDes auto-negotiation as necessary */ + ret = qca8k_read(priv, QCA8K_REG_PWS, &val); + if (ret) + return ret; + if (phylink_autoneg_inband(mode)) + val &= ~QCA8K_PWS_SERDES_AEN_DIS; + else + val |= QCA8K_PWS_SERDES_AEN_DIS; + qca8k_write(priv, QCA8K_REG_PWS, val); + + /* Configure the SGMII parameters */ + ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); + if (ret) + return ret; + + val |= QCA8K_SGMII_EN_SD; + + if (priv->ports_config.sgmii_enable_pll) + val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | + QCA8K_SGMII_EN_TX; + + if (dsa_is_cpu_port(priv->ds, port)) { + /* CPU port, we're talking to the CPU MAC, be a PHY */ + val &= ~QCA8K_SGMII_MODE_CTRL_MASK; + val |= QCA8K_SGMII_MODE_CTRL_PHY; + } else if (interface == PHY_INTERFACE_MODE_SGMII) { + val &= ~QCA8K_SGMII_MODE_CTRL_MASK; + val |= QCA8K_SGMII_MODE_CTRL_MAC; + } else if (interface == PHY_INTERFACE_MODE_1000BASEX) { + val &= ~QCA8K_SGMII_MODE_CTRL_MASK; + val |= QCA8K_SGMII_MODE_CTRL_BASEX; + } + + qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); + + /* From original code is reported port instability as SGMII also + * require delay set. Apply advised values here or take them from DT. + */ + if (interface == PHY_INTERFACE_MODE_SGMII) + qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); + /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and + * falling edge is set writing in the PORT0 PAD reg + */ + if (priv->switch_id == QCA8K_ID_QCA8327 || + priv->switch_id == QCA8K_ID_QCA8337) + reg = QCA8K_REG_PORT0_PAD_CTRL; + + val = 0; + + /* SGMII Clock phase configuration */ + if (priv->ports_config.sgmii_rx_clk_falling_edge) + val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; + + if (priv->ports_config.sgmii_tx_clk_falling_edge) + val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE; + + if (val) + ret = qca8k_rmw(priv, reg, + QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | + QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, + val); + + return 0; +} + +static void qca8k_pcs_an_restart(struct phylink_pcs *pcs) +{ +} + +static const struct phylink_pcs_ops qca8k_pcs_ops = { + .pcs_get_state = qca8k_pcs_get_state, + .pcs_config = qca8k_pcs_config, + .pcs_an_restart = qca8k_pcs_an_restart, +}; + +static void qca8k_setup_pcs(struct qca8k_priv *priv, struct qca8k_pcs *qpcs, + int port) +{ + qpcs->pcs.ops = &qca8k_pcs_ops; + + /* We don't have interrupts for link changes, so we need to poll */ + qpcs->pcs.poll = true; + qpcs->priv = priv; + qpcs->port = port; +} + static void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) { @@ -1703,6 +2043,97 @@ qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) ETH_GSTRING_LEN); } +static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) +{ + const struct qca8k_match_data *match_data; + struct qca8k_mib_eth_data *mib_eth_data; + struct qca8k_priv *priv = ds->priv; + const struct qca8k_mib_desc *mib; + struct mib_ethhdr *mib_ethhdr; + int i, mib_len, offset = 0; + u64 *data; + u8 port; + + mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb); + mib_eth_data = &priv->mib_eth_data; + + /* The switch autocast every port. Ignore other packet and + * parse only the requested one. + */ + port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr)); + if (port != mib_eth_data->req_port) + goto exit; + + match_data = device_get_match_data(priv->dev); + data = mib_eth_data->data; + + for (i = 0; i < match_data->mib_count; i++) { + mib = &ar8327_mib[i]; + + /* First 3 mib are present in the skb head */ + if (i < 3) { + data[i] = mib_ethhdr->data[i]; + continue; + } + + mib_len = sizeof(uint32_t); + + /* Some mib are 64 bit wide */ + if (mib->size == 2) + mib_len = sizeof(uint64_t); + + /* Copy the mib value from packet to the */ + memcpy(data + i, skb->data + offset, mib_len); + + /* Set the offset for the next mib */ + offset += mib_len; + } + +exit: + /* Complete on receiving all the mib packet */ + if (refcount_dec_and_test(&mib_eth_data->port_parsed)) + complete(&mib_eth_data->rw_done); +} + +static int +qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct qca8k_mib_eth_data *mib_eth_data; + struct qca8k_priv *priv = ds->priv; + int ret; + + mib_eth_data = &priv->mib_eth_data; + + mutex_lock(&mib_eth_data->mutex); + + reinit_completion(&mib_eth_data->rw_done); + + mib_eth_data->req_port = dp->index; + mib_eth_data->data = data; + refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS); + + mutex_lock(&priv->reg_mutex); + + /* Send mib autocast request */ + ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, + QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, + FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) | + QCA8K_MIB_BUSY); + + mutex_unlock(&priv->reg_mutex); + + if (ret) + goto exit; + + ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT); + +exit: + mutex_unlock(&mib_eth_data->mutex); + + return ret; +} + static void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) @@ -1714,6 +2145,10 @@ qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, u32 hi = 0; int ret; + if (priv->mgmt_master && + qca8k_get_ethtool_stats_eth(ds, port, data) > 0) + return; + match_data = of_device_get_match_data(priv->dev); for (i = 0; i < match_data->mib_count; i++) { @@ -1812,7 +2247,8 @@ qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) static int qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, - bool *tx_fwd_offload) + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; int port_mask, cpu_port; @@ -1963,7 +2399,8 @@ qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, static int qca8k_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; u16 port_mask = BIT(port); @@ -1973,7 +2410,8 @@ qca8k_port_fdb_add(struct dsa_switch *ds, int port, static int qca8k_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; u16 port_mask = BIT(port); @@ -2010,7 +2448,8 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port, static int qca8k_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct qca8k_priv *priv = ds->priv; const u8 *addr = mdb->addr; @@ -2021,7 +2460,8 @@ qca8k_port_mdb_add(struct dsa_switch *ds, int port, static int qca8k_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct qca8k_priv *priv = ds->priv; const u8 *addr = mdb->addr; @@ -2033,7 +2473,7 @@ qca8k_port_mdb_del(struct dsa_switch *ds, int port, static int qca8k_port_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, - bool ingress) + bool ingress, struct netlink_ext_ack *extack) { struct qca8k_priv *priv = ds->priv; int monitor_port, ret; @@ -2212,18 +2652,16 @@ qca8k_get_tag_protocol(struct dsa_switch *ds, int port, } static bool -qca8k_lag_can_offload(struct dsa_switch *ds, - struct net_device *lag, +qca8k_lag_can_offload(struct dsa_switch *ds, struct dsa_lag lag, struct netdev_lag_upper_info *info) { struct dsa_port *dp; - int id, members = 0; + int members = 0; - id = dsa_lag_id(ds->dst, lag); - if (id < 0 || id >= ds->num_lag_ids) + if (!lag.id) return false; - dsa_lag_foreach_port(dp, ds->dst, lag) + dsa_lag_foreach_port(dp, ds->dst, &lag) /* Includes the port joining the LAG */ members++; @@ -2241,16 +2679,14 @@ qca8k_lag_can_offload(struct dsa_switch *ds, } static int -qca8k_lag_setup_hash(struct dsa_switch *ds, - struct net_device *lag, +qca8k_lag_setup_hash(struct dsa_switch *ds, struct dsa_lag lag, struct netdev_lag_upper_info *info) { + struct net_device *lag_dev = lag.dev; struct qca8k_priv *priv = ds->priv; bool unique_lag = true; + unsigned int i; u32 hash = 0; - int i, id; - - id = dsa_lag_id(ds->dst, lag); switch (info->hash_type) { case NETDEV_LAG_HASH_L23: @@ -2267,7 +2703,7 @@ qca8k_lag_setup_hash(struct dsa_switch *ds, /* Check if we are the unique configured LAG */ dsa_lags_foreach_id(i, ds->dst) - if (i != id && dsa_lag_dev(ds->dst, i)) { + if (i != lag.id && dsa_lag_by_id(ds->dst, i)) { unique_lag = false; break; } @@ -2282,7 +2718,7 @@ qca8k_lag_setup_hash(struct dsa_switch *ds, if (unique_lag) { priv->lag_hash_mode = hash; } else if (priv->lag_hash_mode != hash) { - netdev_err(lag, "Error: Mismatched Hash Mode across different lag is not supported\n"); + netdev_err(lag_dev, "Error: Mismatched Hash Mode across different lag is not supported\n"); return -EOPNOTSUPP; } @@ -2292,13 +2728,14 @@ qca8k_lag_setup_hash(struct dsa_switch *ds, static int qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port, - struct net_device *lag, bool delete) + struct dsa_lag lag, bool delete) { struct qca8k_priv *priv = ds->priv; int ret, id, i; u32 val; - id = dsa_lag_id(ds->dst, lag); + /* DSA LAG IDs are one-based, hardware is zero-based */ + id = lag.id - 1; /* Read current port member */ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val); @@ -2360,8 +2797,7 @@ qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port, } static int -qca8k_port_lag_join(struct dsa_switch *ds, int port, - struct net_device *lag, +qca8k_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag, struct netdev_lag_upper_info *info) { int ret; @@ -2378,11 +2814,265 @@ qca8k_port_lag_join(struct dsa_switch *ds, int port, static int qca8k_port_lag_leave(struct dsa_switch *ds, int port, - struct net_device *lag) + struct dsa_lag lag) { return qca8k_lag_refresh_portmap(ds, port, lag, true); } +static void +qca8k_master_change(struct dsa_switch *ds, const struct net_device *master, + bool operational) +{ + struct dsa_port *dp = master->dsa_ptr; + struct qca8k_priv *priv = ds->priv; + + /* Ethernet MIB/MDIO is only supported for CPU port 0 */ + if (dp->index != 0) + return; + + mutex_lock(&priv->mgmt_eth_data.mutex); + mutex_lock(&priv->mib_eth_data.mutex); + + priv->mgmt_master = operational ? (struct net_device *)master : NULL; + + mutex_unlock(&priv->mib_eth_data.mutex); + mutex_unlock(&priv->mgmt_eth_data.mutex); +} + +static int qca8k_connect_tag_protocol(struct dsa_switch *ds, + enum dsa_tag_protocol proto) +{ + struct qca_tagger_data *tagger_data; + + switch (proto) { + case DSA_TAG_PROTO_QCA: + tagger_data = ds->tagger_data; + + tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler; + tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler; + + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int +qca8k_setup(struct dsa_switch *ds) +{ + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + int cpu_port, ret, i; + u32 mask; + + cpu_port = qca8k_find_cpu_port(ds); + if (cpu_port < 0) { + dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6"); + return cpu_port; + } + + /* Parse CPU port config to be later used in phy_link mac_config */ + ret = qca8k_parse_port_config(priv); + if (ret) + return ret; + + ret = qca8k_setup_mdio_bus(priv); + if (ret) + return ret; + + ret = qca8k_setup_of_pws_reg(priv); + if (ret) + return ret; + + ret = qca8k_setup_mac_pwr_sel(priv); + if (ret) + return ret; + + qca8k_setup_pcs(priv, &priv->pcs_port_0, 0); + qca8k_setup_pcs(priv, &priv->pcs_port_6, 6); + + /* Make sure MAC06 is disabled */ + ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL, + QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); + if (ret) { + dev_err(priv->dev, "failed disabling MAC06 exchange"); + return ret; + } + + /* Enable CPU Port */ + ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, + QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); + if (ret) { + dev_err(priv->dev, "failed enabling CPU port"); + return ret; + } + + /* Enable MIB counters */ + ret = qca8k_mib_init(priv); + if (ret) + dev_warn(priv->dev, "mib init failed"); + + /* Initial setup of all ports */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) { + /* Disable forwarding by default on all ports */ + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, 0); + if (ret) + return ret; + + /* Enable QCA header mode on all cpu ports */ + if (dsa_is_cpu_port(ds, i)) { + ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), + FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | + FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); + if (ret) { + dev_err(priv->dev, "failed enabling QCA header mode"); + return ret; + } + } + + /* Disable MAC by default on all user ports */ + if (dsa_is_user_port(ds, i)) + qca8k_port_set_status(priv, i, 0); + } + + /* Forward all unknown frames to CPU port for Linux processing + * Notice that in multi-cpu config only one port should be set + * for igmp, unknown, multicast and broadcast packet + */ + ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); + if (ret) + return ret; + + /* Setup connection between CPU port & user ports + * Configure specific switch configuration for ports + */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) { + /* CPU port gets connected to all user ports of the switch */ + if (dsa_is_cpu_port(ds, i)) { + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); + if (ret) + return ret; + } + + /* Individual user ports get connected to CPU port only */ + if (dsa_is_user_port(ds, i)) { + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, + BIT(cpu_port)); + if (ret) + return ret; + + /* Enable ARP Auto-learning by default */ + ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_LEARN); + if (ret) + return ret; + + /* For port based vlans to work we need to set the + * default egress vid + */ + ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), + QCA8K_EGREES_VLAN_PORT_MASK(i), + QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); + if (ret) + return ret; + + ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), + QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | + QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); + if (ret) + return ret; + } + + /* The port 5 of the qca8337 have some problem in flood condition. The + * original legacy driver had some specific buffer and priority settings + * for the different port suggested by the QCA switch team. Add this + * missing settings to improve switch stability under load condition. + * This problem is limited to qca8337 and other qca8k switch are not affected. + */ + if (priv->switch_id == QCA8K_ID_QCA8337) { + switch (i) { + /* The 2 CPU port and port 5 requires some different + * priority than any other ports. + */ + case 0: + case 5: + case 6: + mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | + QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | + QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | + QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | + QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | + QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | + QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); + break; + default: + mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | + QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | + QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | + QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | + QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); + } + qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); + + mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | + QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | + QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | + QCA8K_PORT_HOL_CTRL1_WRED_EN; + qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), + QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | + QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | + QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | + QCA8K_PORT_HOL_CTRL1_WRED_EN, + mask); + } + + /* Set initial MTU for every port. + * We have only have a general MTU setting. So track + * every port and set the max across all port. + * Set per port MTU to 1500 as the MTU change function + * will add the overhead and if its set to 1518 then it + * will apply the overhead again and we will end up with + * MTU of 1536 instead of 1518 + */ + priv->port_mtu[i] = ETH_DATA_LEN; + } + + /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ + if (priv->switch_id == QCA8K_ID_QCA8327) { + mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) | + QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496); + qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH, + QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK | + QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, + mask); + } + + /* Setup our port MTUs to match power on defaults */ + ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); + if (ret) + dev_warn(priv->dev, "failed setting MTU settings"); + + /* Flush the FDB table */ + qca8k_fdb_flush(priv); + + /* Set min a max ageing value supported */ + ds->ageing_time_min = 7000; + ds->ageing_time_max = 458745000; + + /* Set max number of LAGs supported */ + ds->num_lag_ids = QCA8K_NUM_LAGS; + + return 0; +} + static const struct dsa_switch_ops qca8k_switch_ops = { .get_tag_protocol = qca8k_get_tag_protocol, .setup = qca8k_setup, @@ -2410,14 +3100,16 @@ static const struct dsa_switch_ops qca8k_switch_ops = { .port_vlan_filtering = qca8k_port_vlan_filtering, .port_vlan_add = qca8k_port_vlan_add, .port_vlan_del = qca8k_port_vlan_del, - .phylink_validate = qca8k_phylink_validate, - .phylink_mac_link_state = qca8k_phylink_mac_link_state, + .phylink_get_caps = qca8k_phylink_get_caps, + .phylink_mac_select_pcs = qca8k_phylink_mac_select_pcs, .phylink_mac_config = qca8k_phylink_mac_config, .phylink_mac_link_down = qca8k_phylink_mac_link_down, .phylink_mac_link_up = qca8k_phylink_mac_link_up, .get_phy_flags = qca8k_get_phy_flags, .port_lag_join = qca8k_port_lag_join, .port_lag_leave = qca8k_port_lag_leave, + .master_state_change = qca8k_master_change, + .connect_tag_protocol = qca8k_connect_tag_protocol, }; static int qca8k_read_switch_id(struct qca8k_priv *priv) @@ -2488,6 +3180,10 @@ qca8k_sw_probe(struct mdio_device *mdiodev) return PTR_ERR(priv->regmap); } + priv->mdio_cache.page = 0xffff; + priv->mdio_cache.lo = 0xffff; + priv->mdio_cache.hi = 0xffff; + /* Check the detected switch id */ ret = qca8k_read_switch_id(priv); if (ret) @@ -2497,6 +3193,12 @@ qca8k_sw_probe(struct mdio_device *mdiodev) if (!priv->ds) return -ENOMEM; + mutex_init(&priv->mgmt_eth_data.mutex); + init_completion(&priv->mgmt_eth_data.rw_done); + + mutex_init(&priv->mib_eth_data.mutex); + init_completion(&priv->mib_eth_data.rw_done); + priv->ds->dev = &mdiodev->dev; priv->ds->num_ports = QCA8K_NUM_PORTS; priv->ds->priv = priv; diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h index ab4a417b25a9..f375627174c8 100644 --- a/drivers/net/dsa/qca8k.h +++ b/drivers/net/dsa/qca8k.h @@ -11,6 +11,11 @@ #include #include #include +#include + +#define QCA8K_ETHERNET_MDIO_PRIORITY 7 +#define QCA8K_ETHERNET_PHY_PRIORITY 6 +#define QCA8K_ETHERNET_TIMEOUT 100 #define QCA8K_NUM_PORTS 7 #define QCA8K_NUM_CPU_PORTS 2 @@ -63,7 +68,7 @@ #define QCA8K_REG_MODULE_EN 0x030 #define QCA8K_MODULE_EN_MIB BIT(0) #define QCA8K_REG_MIB 0x034 -#define QCA8K_MIB_FLUSH BIT(24) +#define QCA8K_MIB_FUNC GENMASK(26, 24) #define QCA8K_MIB_CPU_KEEP BIT(20) #define QCA8K_MIB_BUSY BIT(17) #define QCA8K_MDIO_MASTER_CTRL 0x3c @@ -313,6 +318,12 @@ enum qca8k_vlan_cmd { QCA8K_VLAN_READ = 6, }; +enum qca8k_mid_cmd { + QCA8K_MIB_FLUSH = 1, + QCA8K_MIB_FLUSH_PORT = 2, + QCA8K_MIB_CAST = 3, +}; + struct ar8xxx_port_status { int enabled; }; @@ -328,6 +339,22 @@ enum { QCA8K_CPU_PORT6, }; +struct qca8k_mgmt_eth_data { + struct completion rw_done; + struct mutex mutex; /* Enforce one mdio read/write at time */ + bool ack; + u32 seq; + u32 data[4]; +}; + +struct qca8k_mib_eth_data { + struct completion rw_done; + struct mutex mutex; /* Process one command at time */ + refcount_t port_parsed; /* Counter to track parsed port */ + u8 req_port; + u64 *data; /* pointer to ethtool data */ +}; + struct qca8k_ports_config { bool sgmii_rx_clk_falling_edge; bool sgmii_tx_clk_falling_edge; @@ -336,6 +363,25 @@ struct qca8k_ports_config { u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ }; +struct qca8k_mdio_cache { +/* The 32bit switch registers are accessed indirectly. To achieve this we need + * to set the page of the register. Track the last page that was set to reduce + * mdio writes + */ + u16 page; +/* lo and hi can also be cached and from Documentation we can skip one + * extra mdio write if lo or hi is didn't change. + */ + u16 lo; + u16 hi; +}; + +struct qca8k_pcs { + struct phylink_pcs pcs; + struct qca8k_priv *priv; + int port; +}; + struct qca8k_priv { u8 switch_id; u8 switch_revision; @@ -353,6 +399,12 @@ struct qca8k_priv { struct dsa_switch_ops ops; struct gpio_desc *reset_gpio; unsigned int port_mtu[QCA8K_NUM_PORTS]; + struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ + struct qca8k_mgmt_eth_data mgmt_eth_data; + struct qca8k_mib_eth_data mib_eth_data; + struct qca8k_mdio_cache mdio_cache; + struct qca8k_pcs pcs_port_0; + struct qca8k_pcs pcs_port_6; }; struct qca8k_mib_desc { diff --git a/drivers/net/dsa/realtek-smi-core.c b/drivers/net/dsa/realtek-smi-core.c deleted file mode 100644 index aae46ada8d83..000000000000 --- a/drivers/net/dsa/realtek-smi-core.c +++ /dev/null @@ -1,523 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* Realtek Simple Management Interface (SMI) driver - * It can be discussed how "simple" this interface is. - * - * The SMI protocol piggy-backs the MDIO MDC and MDIO signals levels - * but the protocol is not MDIO at all. Instead it is a Realtek - * pecularity that need to bit-bang the lines in a special way to - * communicate with the switch. - * - * ASICs we intend to support with this driver: - * - * RTL8366 - The original version, apparently - * RTL8369 - Similar enough to have the same datsheet as RTL8366 - * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite - * different register layout from the other two - * RTL8366S - Is this "RTL8366 super"? - * RTL8367 - Has an OpenWRT driver as well - * RTL8368S - Seems to be an alternative name for RTL8366RB - * RTL8370 - Also uses SMI - * - * Copyright (C) 2017 Linus Walleij - * Copyright (C) 2010 Antti Seppälä - * Copyright (C) 2010 Roman Yeryomin - * Copyright (C) 2011 Colin Leitner - * Copyright (C) 2009-2010 Gabor Juhos - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "realtek-smi-core.h" - -#define REALTEK_SMI_ACK_RETRY_COUNT 5 -#define REALTEK_SMI_HW_STOP_DELAY 25 /* msecs */ -#define REALTEK_SMI_HW_START_DELAY 100 /* msecs */ - -static inline void realtek_smi_clk_delay(struct realtek_smi *smi) -{ - ndelay(smi->clk_delay); -} - -static void realtek_smi_start(struct realtek_smi *smi) -{ - /* Set GPIO pins to output mode, with initial state: - * SCK = 0, SDA = 1 - */ - gpiod_direction_output(smi->mdc, 0); - gpiod_direction_output(smi->mdio, 1); - realtek_smi_clk_delay(smi); - - /* CLK 1: 0 -> 1, 1 -> 0 */ - gpiod_set_value(smi->mdc, 1); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 0); - realtek_smi_clk_delay(smi); - - /* CLK 2: */ - gpiod_set_value(smi->mdc, 1); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdio, 0); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 0); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdio, 1); -} - -static void realtek_smi_stop(struct realtek_smi *smi) -{ - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdio, 0); - gpiod_set_value(smi->mdc, 1); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdio, 1); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 1); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 0); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 1); - - /* Add a click */ - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 0); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 1); - - /* Set GPIO pins to input mode */ - gpiod_direction_input(smi->mdio); - gpiod_direction_input(smi->mdc); -} - -static void realtek_smi_write_bits(struct realtek_smi *smi, u32 data, u32 len) -{ - for (; len > 0; len--) { - realtek_smi_clk_delay(smi); - - /* Prepare data */ - gpiod_set_value(smi->mdio, !!(data & (1 << (len - 1)))); - realtek_smi_clk_delay(smi); - - /* Clocking */ - gpiod_set_value(smi->mdc, 1); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 0); - } -} - -static void realtek_smi_read_bits(struct realtek_smi *smi, u32 len, u32 *data) -{ - gpiod_direction_input(smi->mdio); - - for (*data = 0; len > 0; len--) { - u32 u; - - realtek_smi_clk_delay(smi); - - /* Clocking */ - gpiod_set_value(smi->mdc, 1); - realtek_smi_clk_delay(smi); - u = !!gpiod_get_value(smi->mdio); - gpiod_set_value(smi->mdc, 0); - - *data |= (u << (len - 1)); - } - - gpiod_direction_output(smi->mdio, 0); -} - -static int realtek_smi_wait_for_ack(struct realtek_smi *smi) -{ - int retry_cnt; - - retry_cnt = 0; - do { - u32 ack; - - realtek_smi_read_bits(smi, 1, &ack); - if (ack == 0) - break; - - if (++retry_cnt > REALTEK_SMI_ACK_RETRY_COUNT) { - dev_err(smi->dev, "ACK timeout\n"); - return -ETIMEDOUT; - } - } while (1); - - return 0; -} - -static int realtek_smi_write_byte(struct realtek_smi *smi, u8 data) -{ - realtek_smi_write_bits(smi, data, 8); - return realtek_smi_wait_for_ack(smi); -} - -static int realtek_smi_write_byte_noack(struct realtek_smi *smi, u8 data) -{ - realtek_smi_write_bits(smi, data, 8); - return 0; -} - -static int realtek_smi_read_byte0(struct realtek_smi *smi, u8 *data) -{ - u32 t; - - /* Read data */ - realtek_smi_read_bits(smi, 8, &t); - *data = (t & 0xff); - - /* Send an ACK */ - realtek_smi_write_bits(smi, 0x00, 1); - - return 0; -} - -static int realtek_smi_read_byte1(struct realtek_smi *smi, u8 *data) -{ - u32 t; - - /* Read data */ - realtek_smi_read_bits(smi, 8, &t); - *data = (t & 0xff); - - /* Send an ACK */ - realtek_smi_write_bits(smi, 0x01, 1); - - return 0; -} - -static int realtek_smi_read_reg(struct realtek_smi *smi, u32 addr, u32 *data) -{ - unsigned long flags; - u8 lo = 0; - u8 hi = 0; - int ret; - - spin_lock_irqsave(&smi->lock, flags); - - realtek_smi_start(smi); - - /* Send READ command */ - ret = realtek_smi_write_byte(smi, smi->cmd_read); - if (ret) - goto out; - - /* Set ADDR[7:0] */ - ret = realtek_smi_write_byte(smi, addr & 0xff); - if (ret) - goto out; - - /* Set ADDR[15:8] */ - ret = realtek_smi_write_byte(smi, addr >> 8); - if (ret) - goto out; - - /* Read DATA[7:0] */ - realtek_smi_read_byte0(smi, &lo); - /* Read DATA[15:8] */ - realtek_smi_read_byte1(smi, &hi); - - *data = ((u32)lo) | (((u32)hi) << 8); - - ret = 0; - - out: - realtek_smi_stop(smi); - spin_unlock_irqrestore(&smi->lock, flags); - - return ret; -} - -static int realtek_smi_write_reg(struct realtek_smi *smi, - u32 addr, u32 data, bool ack) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&smi->lock, flags); - - realtek_smi_start(smi); - - /* Send WRITE command */ - ret = realtek_smi_write_byte(smi, smi->cmd_write); - if (ret) - goto out; - - /* Set ADDR[7:0] */ - ret = realtek_smi_write_byte(smi, addr & 0xff); - if (ret) - goto out; - - /* Set ADDR[15:8] */ - ret = realtek_smi_write_byte(smi, addr >> 8); - if (ret) - goto out; - - /* Write DATA[7:0] */ - ret = realtek_smi_write_byte(smi, data & 0xff); - if (ret) - goto out; - - /* Write DATA[15:8] */ - if (ack) - ret = realtek_smi_write_byte(smi, data >> 8); - else - ret = realtek_smi_write_byte_noack(smi, data >> 8); - if (ret) - goto out; - - ret = 0; - - out: - realtek_smi_stop(smi); - spin_unlock_irqrestore(&smi->lock, flags); - - return ret; -} - -/* There is one single case when we need to use this accessor and that - * is when issueing soft reset. Since the device reset as soon as we write - * that bit, no ACK will come back for natural reasons. - */ -int realtek_smi_write_reg_noack(struct realtek_smi *smi, u32 addr, - u32 data) -{ - return realtek_smi_write_reg(smi, addr, data, false); -} -EXPORT_SYMBOL_GPL(realtek_smi_write_reg_noack); - -/* Regmap accessors */ - -static int realtek_smi_write(void *ctx, u32 reg, u32 val) -{ - struct realtek_smi *smi = ctx; - - return realtek_smi_write_reg(smi, reg, val, true); -} - -static int realtek_smi_read(void *ctx, u32 reg, u32 *val) -{ - struct realtek_smi *smi = ctx; - - return realtek_smi_read_reg(smi, reg, val); -} - -static const struct regmap_config realtek_smi_mdio_regmap_config = { - .reg_bits = 10, /* A4..A0 R4..R0 */ - .val_bits = 16, - .reg_stride = 1, - /* PHY regs are at 0x8000 */ - .max_register = 0xffff, - .reg_format_endian = REGMAP_ENDIAN_BIG, - .reg_read = realtek_smi_read, - .reg_write = realtek_smi_write, - .cache_type = REGCACHE_NONE, -}; - -static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) -{ - struct realtek_smi *smi = bus->priv; - - return smi->ops->phy_read(smi, addr, regnum); -} - -static int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum, - u16 val) -{ - struct realtek_smi *smi = bus->priv; - - return smi->ops->phy_write(smi, addr, regnum, val); -} - -int realtek_smi_setup_mdio(struct realtek_smi *smi) -{ - struct device_node *mdio_np; - int ret; - - mdio_np = of_get_compatible_child(smi->dev->of_node, "realtek,smi-mdio"); - if (!mdio_np) { - dev_err(smi->dev, "no MDIO bus node\n"); - return -ENODEV; - } - - smi->slave_mii_bus = devm_mdiobus_alloc(smi->dev); - if (!smi->slave_mii_bus) { - ret = -ENOMEM; - goto err_put_node; - } - smi->slave_mii_bus->priv = smi; - smi->slave_mii_bus->name = "SMI slave MII"; - smi->slave_mii_bus->read = realtek_smi_mdio_read; - smi->slave_mii_bus->write = realtek_smi_mdio_write; - snprintf(smi->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d", - smi->ds->index); - smi->slave_mii_bus->dev.of_node = mdio_np; - smi->slave_mii_bus->parent = smi->dev; - smi->ds->slave_mii_bus = smi->slave_mii_bus; - - ret = devm_of_mdiobus_register(smi->dev, smi->slave_mii_bus, mdio_np); - if (ret) { - dev_err(smi->dev, "unable to register MDIO bus %s\n", - smi->slave_mii_bus->id); - goto err_put_node; - } - - return 0; - -err_put_node: - of_node_put(mdio_np); - - return ret; -} - -static int realtek_smi_probe(struct platform_device *pdev) -{ - const struct realtek_smi_variant *var; - struct device *dev = &pdev->dev; - struct realtek_smi *smi; - struct device_node *np; - int ret; - - var = of_device_get_match_data(dev); - np = dev->of_node; - - smi = devm_kzalloc(dev, sizeof(*smi) + var->chip_data_sz, GFP_KERNEL); - if (!smi) - return -ENOMEM; - smi->chip_data = (void *)smi + sizeof(*smi); - smi->map = devm_regmap_init(dev, NULL, smi, - &realtek_smi_mdio_regmap_config); - if (IS_ERR(smi->map)) { - ret = PTR_ERR(smi->map); - dev_err(dev, "regmap init failed: %d\n", ret); - return ret; - } - - /* Link forward and backward */ - smi->dev = dev; - smi->clk_delay = var->clk_delay; - smi->cmd_read = var->cmd_read; - smi->cmd_write = var->cmd_write; - smi->ops = var->ops; - - dev_set_drvdata(dev, smi); - spin_lock_init(&smi->lock); - - /* TODO: if power is software controlled, set up any regulators here */ - - /* Assert then deassert RESET */ - smi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(smi->reset)) { - dev_err(dev, "failed to get RESET GPIO\n"); - return PTR_ERR(smi->reset); - } - msleep(REALTEK_SMI_HW_STOP_DELAY); - gpiod_set_value(smi->reset, 0); - msleep(REALTEK_SMI_HW_START_DELAY); - dev_info(dev, "deasserted RESET\n"); - - /* Fetch MDIO pins */ - smi->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW); - if (IS_ERR(smi->mdc)) - return PTR_ERR(smi->mdc); - smi->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW); - if (IS_ERR(smi->mdio)) - return PTR_ERR(smi->mdio); - - smi->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); - - ret = smi->ops->detect(smi); - if (ret) { - dev_err(dev, "unable to detect switch\n"); - return ret; - } - - smi->ds = devm_kzalloc(dev, sizeof(*smi->ds), GFP_KERNEL); - if (!smi->ds) - return -ENOMEM; - - smi->ds->dev = dev; - smi->ds->num_ports = smi->num_ports; - smi->ds->priv = smi; - - smi->ds->ops = var->ds_ops; - ret = dsa_register_switch(smi->ds); - if (ret) { - dev_err_probe(dev, ret, "unable to register switch\n"); - return ret; - } - return 0; -} - -static int realtek_smi_remove(struct platform_device *pdev) -{ - struct realtek_smi *smi = platform_get_drvdata(pdev); - - if (!smi) - return 0; - - dsa_unregister_switch(smi->ds); - if (smi->slave_mii_bus) - of_node_put(smi->slave_mii_bus->dev.of_node); - gpiod_set_value(smi->reset, 1); - - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static void realtek_smi_shutdown(struct platform_device *pdev) -{ - struct realtek_smi *smi = platform_get_drvdata(pdev); - - if (!smi) - return; - - dsa_switch_shutdown(smi->ds); - - platform_set_drvdata(pdev, NULL); -} - -static const struct of_device_id realtek_smi_of_match[] = { - { - .compatible = "realtek,rtl8366rb", - .data = &rtl8366rb_variant, - }, - { - /* FIXME: add support for RTL8366S and more */ - .compatible = "realtek,rtl8366s", - .data = NULL, - }, - { - .compatible = "realtek,rtl8365mb", - .data = &rtl8365mb_variant, - }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, realtek_smi_of_match); - -static struct platform_driver realtek_smi_driver = { - .driver = { - .name = "realtek-smi", - .of_match_table = of_match_ptr(realtek_smi_of_match), - }, - .probe = realtek_smi_probe, - .remove = realtek_smi_remove, - .shutdown = realtek_smi_shutdown, -}; -module_platform_driver(realtek_smi_driver); - -MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/realtek/Kconfig b/drivers/net/dsa/realtek/Kconfig new file mode 100644 index 000000000000..b7427a8292b2 --- /dev/null +++ b/drivers/net/dsa/realtek/Kconfig @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig NET_DSA_REALTEK + tristate "Realtek Ethernet switch family support" + depends on NET_DSA + select FIXED_PHY + select IRQ_DOMAIN + select REALTEK_PHY + select REGMAP + help + Select to enable support for Realtek Ethernet switch chips. + +config NET_DSA_REALTEK_MDIO + tristate "Realtek MDIO connected switch driver" + depends on NET_DSA_REALTEK + help + Select to enable support for registering switches configured + through MDIO. + +config NET_DSA_REALTEK_SMI + tristate "Realtek SMI connected switch driver" + depends on NET_DSA_REALTEK + help + Select to enable support for registering switches connected + through SMI. + +config NET_DSA_REALTEK_RTL8365MB + tristate "Realtek RTL8365MB switch subdriver" + depends on NET_DSA_REALTEK + depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO + select NET_DSA_TAG_RTL8_4 + help + Select to enable support for Realtek RTL8365MB-VC and RTL8367S. + +config NET_DSA_REALTEK_RTL8366RB + tristate "Realtek RTL8366RB switch subdriver" + depends on NET_DSA_REALTEK + depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO + select NET_DSA_TAG_RTL4_A + help + Select to enable support for Realtek RTL8366RB diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Makefile new file mode 100644 index 000000000000..0aab57252a7c --- /dev/null +++ b/drivers/net/dsa/realtek/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o +obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o +obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o +rtl8366-objs := rtl8366-core.o rtl8366rb.o +obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o diff --git a/drivers/net/dsa/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c new file mode 100644 index 000000000000..31e1f100e48e --- /dev/null +++ b/drivers/net/dsa/realtek/realtek-mdio.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Realtek MDIO interface driver + * + * ASICs we intend to support with this driver: + * + * RTL8366 - The original version, apparently + * RTL8369 - Similar enough to have the same datsheet as RTL8366 + * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite + * different register layout from the other two + * RTL8366S - Is this "RTL8366 super"? + * RTL8367 - Has an OpenWRT driver as well + * RTL8368S - Seems to be an alternative name for RTL8366RB + * RTL8370 - Also uses SMI + * + * Copyright (C) 2017 Linus Walleij + * Copyright (C) 2010 Antti Seppälä + * Copyright (C) 2010 Roman Yeryomin + * Copyright (C) 2011 Colin Leitner + * Copyright (C) 2009-2010 Gabor Juhos + */ + +#include +#include +#include + +#include "realtek.h" + +/* Read/write via mdiobus */ +#define REALTEK_MDIO_CTRL0_REG 31 +#define REALTEK_MDIO_START_REG 29 +#define REALTEK_MDIO_CTRL1_REG 21 +#define REALTEK_MDIO_ADDRESS_REG 23 +#define REALTEK_MDIO_DATA_WRITE_REG 24 +#define REALTEK_MDIO_DATA_READ_REG 25 + +#define REALTEK_MDIO_START_OP 0xFFFF +#define REALTEK_MDIO_ADDR_OP 0x000E +#define REALTEK_MDIO_READ_OP 0x0001 +#define REALTEK_MDIO_WRITE_OP 0x0003 + +static int realtek_mdio_write(void *ctx, u32 reg, u32 val) +{ + struct realtek_priv *priv = ctx; + struct mii_bus *bus = priv->bus; + int ret; + + mutex_lock(&bus->mdio_lock); + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, REALTEK_MDIO_ADDR_OP); + if (ret) + goto out_unlock; + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, reg); + if (ret) + goto out_unlock; + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_DATA_WRITE_REG, val); + if (ret) + goto out_unlock; + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, REALTEK_MDIO_WRITE_OP); + +out_unlock: + mutex_unlock(&bus->mdio_lock); + + return ret; +} + +static int realtek_mdio_read(void *ctx, u32 reg, u32 *val) +{ + struct realtek_priv *priv = ctx; + struct mii_bus *bus = priv->bus; + int ret; + + mutex_lock(&bus->mdio_lock); + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, REALTEK_MDIO_ADDR_OP); + if (ret) + goto out_unlock; + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, reg); + if (ret) + goto out_unlock; + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, REALTEK_MDIO_READ_OP); + if (ret) + goto out_unlock; + + ret = bus->read(bus, priv->mdio_addr, REALTEK_MDIO_DATA_READ_REG); + if (ret >= 0) { + *val = ret; + ret = 0; + } + +out_unlock: + mutex_unlock(&bus->mdio_lock); + + return ret; +} + +static void realtek_mdio_lock(void *ctx) +{ + struct realtek_priv *priv = ctx; + + mutex_lock(&priv->map_lock); +} + +static void realtek_mdio_unlock(void *ctx) +{ + struct realtek_priv *priv = ctx; + + mutex_unlock(&priv->map_lock); +} + +static const struct regmap_config realtek_mdio_regmap_config = { + .reg_bits = 10, /* A4..A0 R4..R0 */ + .val_bits = 16, + .reg_stride = 1, + /* PHY regs are at 0x8000 */ + .max_register = 0xffff, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .reg_read = realtek_mdio_read, + .reg_write = realtek_mdio_write, + .cache_type = REGCACHE_NONE, + .lock = realtek_mdio_lock, + .unlock = realtek_mdio_unlock, +}; + +static const struct regmap_config realtek_mdio_nolock_regmap_config = { + .reg_bits = 10, /* A4..A0 R4..R0 */ + .val_bits = 16, + .reg_stride = 1, + /* PHY regs are at 0x8000 */ + .max_register = 0xffff, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .reg_read = realtek_mdio_read, + .reg_write = realtek_mdio_write, + .cache_type = REGCACHE_NONE, + .disable_locking = true, +}; + +static int realtek_mdio_probe(struct mdio_device *mdiodev) +{ + struct realtek_priv *priv; + struct device *dev = &mdiodev->dev; + const struct realtek_variant *var; + struct regmap_config rc; + struct device_node *np; + int ret; + + var = of_device_get_match_data(dev); + if (!var) + return -EINVAL; + + priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->map_lock); + + rc = realtek_mdio_regmap_config; + rc.lock_arg = priv; + priv->map = devm_regmap_init(dev, NULL, priv, &rc); + if (IS_ERR(priv->map)) { + ret = PTR_ERR(priv->map); + dev_err(dev, "regmap init failed: %d\n", ret); + return ret; + } + + rc = realtek_mdio_nolock_regmap_config; + priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); + if (IS_ERR(priv->map_nolock)) { + ret = PTR_ERR(priv->map_nolock); + dev_err(dev, "regmap init failed: %d\n", ret); + return ret; + } + + priv->mdio_addr = mdiodev->addr; + priv->bus = mdiodev->bus; + priv->dev = &mdiodev->dev; + priv->chip_data = (void *)priv + sizeof(*priv); + + priv->clk_delay = var->clk_delay; + priv->cmd_read = var->cmd_read; + priv->cmd_write = var->cmd_write; + priv->ops = var->ops; + + priv->write_reg_noack = realtek_mdio_write; + + np = dev->of_node; + + dev_set_drvdata(dev, priv); + + /* TODO: if power is software controlled, set up any regulators here */ + priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); + + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) { + dev_err(dev, "failed to get RESET GPIO\n"); + return PTR_ERR(priv->reset); + } + + if (priv->reset) { + gpiod_set_value(priv->reset, 1); + dev_dbg(dev, "asserted RESET\n"); + msleep(REALTEK_HW_STOP_DELAY); + gpiod_set_value(priv->reset, 0); + msleep(REALTEK_HW_START_DELAY); + dev_dbg(dev, "deasserted RESET\n"); + } + + ret = priv->ops->detect(priv); + if (ret) { + dev_err(dev, "unable to detect switch\n"); + return ret; + } + + priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) + return -ENOMEM; + + priv->ds->dev = dev; + priv->ds->num_ports = priv->num_ports; + priv->ds->priv = priv; + priv->ds->ops = var->ds_ops_mdio; + + ret = dsa_register_switch(priv->ds); + if (ret) { + dev_err(priv->dev, "unable to register switch ret = %d\n", ret); + return ret; + } + + return 0; +} + +static void realtek_mdio_remove(struct mdio_device *mdiodev) +{ + struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); + + if (!priv) + return; + + dsa_unregister_switch(priv->ds); + + /* leave the device reset asserted */ + if (priv->reset) + gpiod_set_value(priv->reset, 1); + + dev_set_drvdata(&mdiodev->dev, NULL); +} + +static void realtek_mdio_shutdown(struct mdio_device *mdiodev) +{ + struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); + + if (!priv) + return; + + dsa_switch_shutdown(priv->ds); + + dev_set_drvdata(&mdiodev->dev, NULL); +} + +static const struct of_device_id realtek_mdio_of_match[] = { +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) + { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, }, +#endif +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) + { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, + { .compatible = "realtek,rtl8367s", .data = &rtl8365mb_variant, }, +#endif + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, realtek_mdio_of_match); + +static struct mdio_driver realtek_mdio_driver = { + .mdiodrv.driver = { + .name = "realtek-mdio", + .of_match_table = of_match_ptr(realtek_mdio_of_match), + }, + .probe = realtek_mdio_probe, + .remove = realtek_mdio_remove, + .shutdown = realtek_mdio_shutdown, +}; + +mdio_module_driver(realtek_mdio_driver); + +MODULE_AUTHOR("Luiz Angelo Daros de Luca "); +MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c new file mode 100644 index 000000000000..2243d3da55b2 --- /dev/null +++ b/drivers/net/dsa/realtek/realtek-smi.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Realtek Simple Management Interface (SMI) driver + * It can be discussed how "simple" this interface is. + * + * The SMI protocol piggy-backs the MDIO MDC and MDIO signals levels + * but the protocol is not MDIO at all. Instead it is a Realtek + * pecularity that need to bit-bang the lines in a special way to + * communicate with the switch. + * + * ASICs we intend to support with this driver: + * + * RTL8366 - The original version, apparently + * RTL8369 - Similar enough to have the same datsheet as RTL8366 + * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite + * different register layout from the other two + * RTL8366S - Is this "RTL8366 super"? + * RTL8367 - Has an OpenWRT driver as well + * RTL8368S - Seems to be an alternative name for RTL8366RB + * RTL8370 - Also uses SMI + * + * Copyright (C) 2017 Linus Walleij + * Copyright (C) 2010 Antti Seppälä + * Copyright (C) 2010 Roman Yeryomin + * Copyright (C) 2011 Colin Leitner + * Copyright (C) 2009-2010 Gabor Juhos + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "realtek.h" + +#define REALTEK_SMI_ACK_RETRY_COUNT 5 + +static inline void realtek_smi_clk_delay(struct realtek_priv *priv) +{ + ndelay(priv->clk_delay); +} + +static void realtek_smi_start(struct realtek_priv *priv) +{ + /* Set GPIO pins to output mode, with initial state: + * SCK = 0, SDA = 1 + */ + gpiod_direction_output(priv->mdc, 0); + gpiod_direction_output(priv->mdio, 1); + realtek_smi_clk_delay(priv); + + /* CLK 1: 0 -> 1, 1 -> 0 */ + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + realtek_smi_clk_delay(priv); + + /* CLK 2: */ + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdio, 0); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdio, 1); +} + +static void realtek_smi_stop(struct realtek_priv *priv) +{ + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdio, 0); + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdio, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 1); + + /* Add a click */ + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 1); + + /* Set GPIO pins to input mode */ + gpiod_direction_input(priv->mdio); + gpiod_direction_input(priv->mdc); +} + +static void realtek_smi_write_bits(struct realtek_priv *priv, u32 data, u32 len) +{ + for (; len > 0; len--) { + realtek_smi_clk_delay(priv); + + /* Prepare data */ + gpiod_set_value(priv->mdio, !!(data & (1 << (len - 1)))); + realtek_smi_clk_delay(priv); + + /* Clocking */ + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + } +} + +static void realtek_smi_read_bits(struct realtek_priv *priv, u32 len, u32 *data) +{ + gpiod_direction_input(priv->mdio); + + for (*data = 0; len > 0; len--) { + u32 u; + + realtek_smi_clk_delay(priv); + + /* Clocking */ + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + u = !!gpiod_get_value(priv->mdio); + gpiod_set_value(priv->mdc, 0); + + *data |= (u << (len - 1)); + } + + gpiod_direction_output(priv->mdio, 0); +} + +static int realtek_smi_wait_for_ack(struct realtek_priv *priv) +{ + int retry_cnt; + + retry_cnt = 0; + do { + u32 ack; + + realtek_smi_read_bits(priv, 1, &ack); + if (ack == 0) + break; + + if (++retry_cnt > REALTEK_SMI_ACK_RETRY_COUNT) { + dev_err(priv->dev, "ACK timeout\n"); + return -ETIMEDOUT; + } + } while (1); + + return 0; +} + +static int realtek_smi_write_byte(struct realtek_priv *priv, u8 data) +{ + realtek_smi_write_bits(priv, data, 8); + return realtek_smi_wait_for_ack(priv); +} + +static int realtek_smi_write_byte_noack(struct realtek_priv *priv, u8 data) +{ + realtek_smi_write_bits(priv, data, 8); + return 0; +} + +static int realtek_smi_read_byte0(struct realtek_priv *priv, u8 *data) +{ + u32 t; + + /* Read data */ + realtek_smi_read_bits(priv, 8, &t); + *data = (t & 0xff); + + /* Send an ACK */ + realtek_smi_write_bits(priv, 0x00, 1); + + return 0; +} + +static int realtek_smi_read_byte1(struct realtek_priv *priv, u8 *data) +{ + u32 t; + + /* Read data */ + realtek_smi_read_bits(priv, 8, &t); + *data = (t & 0xff); + + /* Send an ACK */ + realtek_smi_write_bits(priv, 0x01, 1); + + return 0; +} + +static int realtek_smi_read_reg(struct realtek_priv *priv, u32 addr, u32 *data) +{ + unsigned long flags; + u8 lo = 0; + u8 hi = 0; + int ret; + + spin_lock_irqsave(&priv->lock, flags); + + realtek_smi_start(priv); + + /* Send READ command */ + ret = realtek_smi_write_byte(priv, priv->cmd_read); + if (ret) + goto out; + + /* Set ADDR[7:0] */ + ret = realtek_smi_write_byte(priv, addr & 0xff); + if (ret) + goto out; + + /* Set ADDR[15:8] */ + ret = realtek_smi_write_byte(priv, addr >> 8); + if (ret) + goto out; + + /* Read DATA[7:0] */ + realtek_smi_read_byte0(priv, &lo); + /* Read DATA[15:8] */ + realtek_smi_read_byte1(priv, &hi); + + *data = ((u32)lo) | (((u32)hi) << 8); + + ret = 0; + + out: + realtek_smi_stop(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static int realtek_smi_write_reg(struct realtek_priv *priv, + u32 addr, u32 data, bool ack) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&priv->lock, flags); + + realtek_smi_start(priv); + + /* Send WRITE command */ + ret = realtek_smi_write_byte(priv, priv->cmd_write); + if (ret) + goto out; + + /* Set ADDR[7:0] */ + ret = realtek_smi_write_byte(priv, addr & 0xff); + if (ret) + goto out; + + /* Set ADDR[15:8] */ + ret = realtek_smi_write_byte(priv, addr >> 8); + if (ret) + goto out; + + /* Write DATA[7:0] */ + ret = realtek_smi_write_byte(priv, data & 0xff); + if (ret) + goto out; + + /* Write DATA[15:8] */ + if (ack) + ret = realtek_smi_write_byte(priv, data >> 8); + else + ret = realtek_smi_write_byte_noack(priv, data >> 8); + if (ret) + goto out; + + ret = 0; + + out: + realtek_smi_stop(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +/* There is one single case when we need to use this accessor and that + * is when issueing soft reset. Since the device reset as soon as we write + * that bit, no ACK will come back for natural reasons. + */ +static int realtek_smi_write_reg_noack(void *ctx, u32 reg, u32 val) +{ + return realtek_smi_write_reg(ctx, reg, val, false); +} + +/* Regmap accessors */ + +static int realtek_smi_write(void *ctx, u32 reg, u32 val) +{ + struct realtek_priv *priv = ctx; + + return realtek_smi_write_reg(priv, reg, val, true); +} + +static int realtek_smi_read(void *ctx, u32 reg, u32 *val) +{ + struct realtek_priv *priv = ctx; + + return realtek_smi_read_reg(priv, reg, val); +} + +static void realtek_smi_lock(void *ctx) +{ + struct realtek_priv *priv = ctx; + + mutex_lock(&priv->map_lock); +} + +static void realtek_smi_unlock(void *ctx) +{ + struct realtek_priv *priv = ctx; + + mutex_unlock(&priv->map_lock); +} + +static const struct regmap_config realtek_smi_regmap_config = { + .reg_bits = 10, /* A4..A0 R4..R0 */ + .val_bits = 16, + .reg_stride = 1, + /* PHY regs are at 0x8000 */ + .max_register = 0xffff, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .reg_read = realtek_smi_read, + .reg_write = realtek_smi_write, + .cache_type = REGCACHE_NONE, + .lock = realtek_smi_lock, + .unlock = realtek_smi_unlock, +}; + +static const struct regmap_config realtek_smi_nolock_regmap_config = { + .reg_bits = 10, /* A4..A0 R4..R0 */ + .val_bits = 16, + .reg_stride = 1, + /* PHY regs are at 0x8000 */ + .max_register = 0xffff, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .reg_read = realtek_smi_read, + .reg_write = realtek_smi_write, + .cache_type = REGCACHE_NONE, + .disable_locking = true, +}; + +static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) +{ + struct realtek_priv *priv = bus->priv; + + return priv->ops->phy_read(priv, addr, regnum); +} + +static int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum, + u16 val) +{ + struct realtek_priv *priv = bus->priv; + + return priv->ops->phy_write(priv, addr, regnum, val); +} + +static int realtek_smi_setup_mdio(struct dsa_switch *ds) +{ + struct realtek_priv *priv = ds->priv; + struct device_node *mdio_np; + int ret; + + mdio_np = of_get_compatible_child(priv->dev->of_node, "realtek,smi-mdio"); + if (!mdio_np) { + dev_err(priv->dev, "no MDIO bus node\n"); + return -ENODEV; + } + + priv->slave_mii_bus = devm_mdiobus_alloc(priv->dev); + if (!priv->slave_mii_bus) { + ret = -ENOMEM; + goto err_put_node; + } + priv->slave_mii_bus->priv = priv; + priv->slave_mii_bus->name = "SMI slave MII"; + priv->slave_mii_bus->read = realtek_smi_mdio_read; + priv->slave_mii_bus->write = realtek_smi_mdio_write; + snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d", + ds->index); + priv->slave_mii_bus->dev.of_node = mdio_np; + priv->slave_mii_bus->parent = priv->dev; + ds->slave_mii_bus = priv->slave_mii_bus; + + ret = devm_of_mdiobus_register(priv->dev, priv->slave_mii_bus, mdio_np); + if (ret) { + dev_err(priv->dev, "unable to register MDIO bus %s\n", + priv->slave_mii_bus->id); + goto err_put_node; + } + + return 0; + +err_put_node: + of_node_put(mdio_np); + + return ret; +} + +static int realtek_smi_probe(struct platform_device *pdev) +{ + const struct realtek_variant *var; + struct device *dev = &pdev->dev; + struct realtek_priv *priv; + struct regmap_config rc; + struct device_node *np; + int ret; + + var = of_device_get_match_data(dev); + np = dev->of_node; + + priv = devm_kzalloc(dev, sizeof(*priv) + var->chip_data_sz, GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->chip_data = (void *)priv + sizeof(*priv); + + mutex_init(&priv->map_lock); + + rc = realtek_smi_regmap_config; + rc.lock_arg = priv; + priv->map = devm_regmap_init(dev, NULL, priv, &rc); + if (IS_ERR(priv->map)) { + ret = PTR_ERR(priv->map); + dev_err(dev, "regmap init failed: %d\n", ret); + return ret; + } + + rc = realtek_smi_nolock_regmap_config; + priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); + if (IS_ERR(priv->map_nolock)) { + ret = PTR_ERR(priv->map_nolock); + dev_err(dev, "regmap init failed: %d\n", ret); + return ret; + } + + /* Link forward and backward */ + priv->dev = dev; + priv->clk_delay = var->clk_delay; + priv->cmd_read = var->cmd_read; + priv->cmd_write = var->cmd_write; + priv->ops = var->ops; + + priv->setup_interface = realtek_smi_setup_mdio; + priv->write_reg_noack = realtek_smi_write_reg_noack; + + dev_set_drvdata(dev, priv); + spin_lock_init(&priv->lock); + + /* TODO: if power is software controlled, set up any regulators here */ + + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) { + dev_err(dev, "failed to get RESET GPIO\n"); + return PTR_ERR(priv->reset); + } + if (priv->reset) { + gpiod_set_value(priv->reset, 1); + dev_dbg(dev, "asserted RESET\n"); + msleep(REALTEK_HW_STOP_DELAY); + gpiod_set_value(priv->reset, 0); + msleep(REALTEK_HW_START_DELAY); + dev_dbg(dev, "deasserted RESET\n"); + } + + /* Fetch MDIO pins */ + priv->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW); + if (IS_ERR(priv->mdc)) + return PTR_ERR(priv->mdc); + priv->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW); + if (IS_ERR(priv->mdio)) + return PTR_ERR(priv->mdio); + + priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); + + ret = priv->ops->detect(priv); + if (ret) { + dev_err(dev, "unable to detect switch\n"); + return ret; + } + + priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) + return -ENOMEM; + + priv->ds->dev = dev; + priv->ds->num_ports = priv->num_ports; + priv->ds->priv = priv; + + priv->ds->ops = var->ds_ops_smi; + ret = dsa_register_switch(priv->ds); + if (ret) { + dev_err_probe(dev, ret, "unable to register switch\n"); + return ret; + } + return 0; +} + +static int realtek_smi_remove(struct platform_device *pdev) +{ + struct realtek_priv *priv = platform_get_drvdata(pdev); + + if (!priv) + return 0; + + dsa_unregister_switch(priv->ds); + if (priv->slave_mii_bus) + of_node_put(priv->slave_mii_bus->dev.of_node); + + /* leave the device reset asserted */ + if (priv->reset) + gpiod_set_value(priv->reset, 1); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void realtek_smi_shutdown(struct platform_device *pdev) +{ + struct realtek_priv *priv = platform_get_drvdata(pdev); + + if (!priv) + return; + + dsa_switch_shutdown(priv->ds); + + platform_set_drvdata(pdev, NULL); +} + +static const struct of_device_id realtek_smi_of_match[] = { +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) + { + .compatible = "realtek,rtl8366rb", + .data = &rtl8366rb_variant, + }, +#endif + { + /* FIXME: add support for RTL8366S and more */ + .compatible = "realtek,rtl8366s", + .data = NULL, + }, +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) + { + .compatible = "realtek,rtl8365mb", + .data = &rtl8365mb_variant, + }, + { + .compatible = "realtek,rtl8367s", + .data = &rtl8365mb_variant, + }, +#endif + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, realtek_smi_of_match); + +static struct platform_driver realtek_smi_driver = { + .driver = { + .name = "realtek-smi", + .of_match_table = of_match_ptr(realtek_smi_of_match), + }, + .probe = realtek_smi_probe, + .remove = realtek_smi_remove, + .shutdown = realtek_smi_shutdown, +}; +module_platform_driver(realtek_smi_driver); + +MODULE_AUTHOR("Linus Walleij "); +MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/realtek-smi-core.h b/drivers/net/dsa/realtek/realtek.h similarity index 51% rename from drivers/net/dsa/realtek-smi-core.h rename to drivers/net/dsa/realtek/realtek.h index 5bfa53e2480a..4fa7c6ba874a 100644 --- a/drivers/net/dsa/realtek-smi-core.h +++ b/drivers/net/dsa/realtek/realtek.h @@ -5,15 +5,18 @@ * Copyright (C) 2009-2010 Gabor Juhos */ -#ifndef _REALTEK_SMI_H -#define _REALTEK_SMI_H +#ifndef _REALTEK_H +#define _REALTEK_H #include #include #include #include -struct realtek_smi_ops; +#define REALTEK_HW_STOP_DELAY 25 /* msecs */ +#define REALTEK_HW_START_DELAY 100 /* msecs */ + +struct realtek_ops; struct dentry; struct inode; struct file; @@ -25,7 +28,7 @@ struct rtl8366_mib_counter { const char *name; }; -/** +/* * struct rtl8366_vlan_mc - Virtual LAN member configuration */ struct rtl8366_vlan_mc { @@ -43,13 +46,17 @@ struct rtl8366_vlan_4k { u8 fid; }; -struct realtek_smi { +struct realtek_priv { struct device *dev; struct gpio_desc *reset; struct gpio_desc *mdc; struct gpio_desc *mdio; struct regmap *map; + struct regmap *map_nolock; + struct mutex map_lock; struct mii_bus *slave_mii_bus; + struct mii_bus *bus; + int mdio_addr; unsigned int clk_delay; u8 cmd_read; @@ -65,7 +72,9 @@ struct realtek_smi { unsigned int num_mib_counters; struct rtl8366_mib_counter *mib_counters; - const struct realtek_smi_ops *ops; + const struct realtek_ops *ops; + int (*setup_interface)(struct dsa_switch *ds); + int (*write_reg_noack)(void *ctx, u32 addr, u32 data); int vlan_enabled; int vlan4k_enabled; @@ -74,61 +83,57 @@ struct realtek_smi { void *chip_data; /* Per-chip extra variant data */ }; -/** - * struct realtek_smi_ops - vtable for the per-SMI-chiptype operations +/* + * struct realtek_ops - vtable for the per-SMI-chiptype operations * @detect: detects the chiptype */ -struct realtek_smi_ops { - int (*detect)(struct realtek_smi *smi); - int (*reset_chip)(struct realtek_smi *smi); - int (*setup)(struct realtek_smi *smi); - void (*cleanup)(struct realtek_smi *smi); - int (*get_mib_counter)(struct realtek_smi *smi, +struct realtek_ops { + int (*detect)(struct realtek_priv *priv); + int (*reset_chip)(struct realtek_priv *priv); + int (*setup)(struct realtek_priv *priv); + void (*cleanup)(struct realtek_priv *priv); + int (*get_mib_counter)(struct realtek_priv *priv, int port, struct rtl8366_mib_counter *mib, u64 *mibvalue); - int (*get_vlan_mc)(struct realtek_smi *smi, u32 index, + int (*get_vlan_mc)(struct realtek_priv *priv, u32 index, struct rtl8366_vlan_mc *vlanmc); - int (*set_vlan_mc)(struct realtek_smi *smi, u32 index, + int (*set_vlan_mc)(struct realtek_priv *priv, u32 index, const struct rtl8366_vlan_mc *vlanmc); - int (*get_vlan_4k)(struct realtek_smi *smi, u32 vid, + int (*get_vlan_4k)(struct realtek_priv *priv, u32 vid, struct rtl8366_vlan_4k *vlan4k); - int (*set_vlan_4k)(struct realtek_smi *smi, + int (*set_vlan_4k)(struct realtek_priv *priv, const struct rtl8366_vlan_4k *vlan4k); - int (*get_mc_index)(struct realtek_smi *smi, int port, int *val); - int (*set_mc_index)(struct realtek_smi *smi, int port, int index); - bool (*is_vlan_valid)(struct realtek_smi *smi, unsigned int vlan); - int (*enable_vlan)(struct realtek_smi *smi, bool enable); - int (*enable_vlan4k)(struct realtek_smi *smi, bool enable); - int (*enable_port)(struct realtek_smi *smi, int port, bool enable); - int (*phy_read)(struct realtek_smi *smi, int phy, int regnum); - int (*phy_write)(struct realtek_smi *smi, int phy, int regnum, + int (*get_mc_index)(struct realtek_priv *priv, int port, int *val); + int (*set_mc_index)(struct realtek_priv *priv, int port, int index); + bool (*is_vlan_valid)(struct realtek_priv *priv, unsigned int vlan); + int (*enable_vlan)(struct realtek_priv *priv, bool enable); + int (*enable_vlan4k)(struct realtek_priv *priv, bool enable); + int (*enable_port)(struct realtek_priv *priv, int port, bool enable); + int (*phy_read)(struct realtek_priv *priv, int phy, int regnum); + int (*phy_write)(struct realtek_priv *priv, int phy, int regnum, u16 val); }; -struct realtek_smi_variant { - const struct dsa_switch_ops *ds_ops; - const struct realtek_smi_ops *ops; +struct realtek_variant { + const struct dsa_switch_ops *ds_ops_smi; + const struct dsa_switch_ops *ds_ops_mdio; + const struct realtek_ops *ops; unsigned int clk_delay; u8 cmd_read; u8 cmd_write; size_t chip_data_sz; }; -/* SMI core calls */ -int realtek_smi_write_reg_noack(struct realtek_smi *smi, u32 addr, - u32 data); -int realtek_smi_setup_mdio(struct realtek_smi *smi); - /* RTL8366 library helpers */ -int rtl8366_mc_is_used(struct realtek_smi *smi, int mc_index, int *used); -int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, +int rtl8366_mc_is_used(struct realtek_priv *priv, int mc_index, int *used); +int rtl8366_set_vlan(struct realtek_priv *priv, int vid, u32 member, u32 untag, u32 fid); -int rtl8366_set_pvid(struct realtek_smi *smi, unsigned int port, +int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port, unsigned int vid); -int rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable); -int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable); -int rtl8366_reset_vlan(struct realtek_smi *smi); +int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable); +int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable); +int rtl8366_reset_vlan(struct realtek_priv *priv); int rtl8366_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan, struct netlink_ext_ack *extack); @@ -139,7 +144,7 @@ void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset, int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset); void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data); -extern const struct realtek_smi_variant rtl8366rb_variant; -extern const struct realtek_smi_variant rtl8365mb_variant; +extern const struct realtek_variant rtl8366rb_variant; +extern const struct realtek_variant rtl8365mb_variant; -#endif /* _REALTEK_SMI_H */ +#endif /* _REALTEK_H */ diff --git a/drivers/net/dsa/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c similarity index 73% rename from drivers/net/dsa/rtl8365mb.c rename to drivers/net/dsa/realtek/rtl8365mb.c index 3b729544798b..3d70e8a77ecf 100644 --- a/drivers/net/dsa/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -99,18 +99,28 @@ #include #include -#include "realtek-smi-core.h" +#include "realtek.h" /* Chip-specific data and limits */ -#define RTL8365MB_CHIP_ID_8365MB_VC 0x6367 -#define RTL8365MB_CPU_PORT_NUM_8365MB_VC 6 -#define RTL8365MB_LEARN_LIMIT_MAX_8365MB_VC 2112 +#define RTL8365MB_CHIP_ID_8365MB_VC 0x6367 +#define RTL8365MB_CHIP_VER_8365MB_VC 0x0040 + +#define RTL8365MB_CHIP_ID_8367S 0x6367 +#define RTL8365MB_CHIP_VER_8367S 0x00A0 + +#define RTL8365MB_CHIP_ID_8367RB 0x6367 +#define RTL8365MB_CHIP_VER_8367RB 0x0020 /* Family-specific data and limits */ -#define RTL8365MB_PHYADDRMAX 7 -#define RTL8365MB_NUM_PHYREGS 32 -#define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1) -#define RTL8365MB_MAX_NUM_PORTS (RTL8365MB_CPU_PORT_NUM_8365MB_VC + 1) +#define RTL8365MB_PHYADDRMAX 7 +#define RTL8365MB_NUM_PHYREGS 32 +#define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1) +/* RTL8370MB and RTL8310SR, possibly suportable by this driver, have 10 ports */ +#define RTL8365MB_MAX_NUM_PORTS 10 +#define RTL8365MB_LEARN_LIMIT_MAX 2112 + +/* valid for all 6-port or less variants */ +static const int rtl8365mb_extint_port_map[] = { -1, -1, -1, -1, -1, -1, 1, 2, -1, -1}; /* Chip identification registers */ #define RTL8365MB_CHIP_ID_REG 0x1300 @@ -191,7 +201,7 @@ /* The PHY OCP addresses of PHY registers 0~31 start here */ #define RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE 0xA400 -/* EXT port interface mode values - used in DIGITAL_INTERFACE_SELECT */ +/* EXT interface port mode values - used in DIGITAL_INTERFACE_SELECT */ #define RTL8365MB_EXT_PORT_MODE_DISABLE 0 #define RTL8365MB_EXT_PORT_MODE_RGMII 1 #define RTL8365MB_EXT_PORT_MODE_MII_MAC 2 @@ -207,39 +217,56 @@ #define RTL8365MB_EXT_PORT_MODE_1000X 12 #define RTL8365MB_EXT_PORT_MODE_100FX 13 -/* EXT port interface mode configuration registers 0~1 */ -#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 -#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 -#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extport) \ - (RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 + \ - ((_extport) >> 1) * (0x13C3 - 0x1305)) -#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extport) \ - (0xF << (((_extport) % 2))) -#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(_extport) \ - (((_extport) % 2) * 4) +/* Realtek docs and driver uses logic number as EXT_PORT0=16, EXT_PORT1=17, + * EXT_PORT2=18, to interact with switch ports. That logic number is internally + * converted to either a physical port number (0..9) or an external interface id (0..2), + * depending on which function was called. The external interface id is calculated as + * (ext_id=logic_port-15), while the logical to physical map depends on the chip id/version. + * + * EXT_PORT0 mentioned in datasheets and rtl8367c driver is used in this driver + * as extid==1, EXT_PORT2, mentioned in Realtek rtl8367c driver for 10-port switches, + * would have an ext_id of 3 (out of range for most extint macros) and ext_id 0 does + * not seem to be used as well for this family. + */ -/* EXT port RGMII TX/RX delay configuration registers 1~2 */ -#define RTL8365MB_EXT_RGMXF_REG1 0x1307 -#define RTL8365MB_EXT_RGMXF_REG2 0x13C5 -#define RTL8365MB_EXT_RGMXF_REG(_extport) \ - (RTL8365MB_EXT_RGMXF_REG1 + \ - (((_extport) >> 1) * (0x13C5 - 0x1307))) +/* EXT interface mode configuration registers 0~1 */ +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT1 */ +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 /* EXT2 */ +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extint) \ + ((_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \ + (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \ + 0x0) +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \ + (0xF << (((_extint) % 2))) +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(_extint) \ + (((_extint) % 2) * 4) + +/* EXT interface RGMII TX/RX delay configuration registers 0~2 */ +#define RTL8365MB_EXT_RGMXF_REG0 0x1306 /* EXT0 */ +#define RTL8365MB_EXT_RGMXF_REG1 0x1307 /* EXT1 */ +#define RTL8365MB_EXT_RGMXF_REG2 0x13C5 /* EXT2 */ +#define RTL8365MB_EXT_RGMXF_REG(_extint) \ + ((_extint) == 0 ? RTL8365MB_EXT_RGMXF_REG0 : \ + (_extint) == 1 ? RTL8365MB_EXT_RGMXF_REG1 : \ + (_extint) == 2 ? RTL8365MB_EXT_RGMXF_REG2 : \ + 0x0) #define RTL8365MB_EXT_RGMXF_RXDELAY_MASK 0x0007 #define RTL8365MB_EXT_RGMXF_TXDELAY_MASK 0x0008 -/* External port speed values - used in DIGITAL_INTERFACE_FORCE */ +/* External interface port speed values - used in DIGITAL_INTERFACE_FORCE */ #define RTL8365MB_PORT_SPEED_10M 0 #define RTL8365MB_PORT_SPEED_100M 1 #define RTL8365MB_PORT_SPEED_1000M 2 -/* EXT port force configuration registers 0~2 */ -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 0x1310 -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 0x1311 -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 0x13C4 -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(_extport) \ - (RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 + \ - ((_extport) & 0x1) + \ - ((((_extport) >> 1) & 0x1) * (0x13C4 - 0x1310))) +/* EXT interface force configuration registers 0~2 */ +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 0x1310 /* EXT0 */ +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 0x1311 /* EXT1 */ +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 0x13C4 /* EXT2 */ +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(_extint) \ + ((_extint) == 0 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 : \ + (_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 : \ + (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 : \ + 0x0) #define RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK 0x1000 #define RTL8365MB_DIGITAL_INTERFACE_FORCE_NWAY_MASK 0x0080 #define RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK 0x0040 @@ -516,7 +543,7 @@ struct rtl8365mb_cpu { /** * struct rtl8365mb_port - private per-port data - * @smi: pointer to parent realtek_smi data + * @priv: pointer to parent realtek_priv data * @index: DSA port index, same as dsa_port::index * @stats: link statistics populated by rtl8365mb_stats_poll, ready for atomic * access via rtl8365mb_get_stats64 @@ -524,7 +551,7 @@ struct rtl8365mb_cpu { * @mib_work: delayed work for polling MIB counters */ struct rtl8365mb_port { - struct realtek_smi *smi; + struct realtek_priv *priv; unsigned int index; struct rtnl_link_stats64 stats; spinlock_t stats_lock; @@ -533,7 +560,7 @@ struct rtl8365mb_port { /** * struct rtl8365mb - private chip-specific driver data - * @smi: pointer to parent realtek_smi data + * @priv: pointer to parent realtek_priv data * @irq: registered IRQ or zero * @chip_id: chip identifier * @chip_ver: chip silicon revision @@ -548,7 +575,7 @@ struct rtl8365mb_port { * Private data for this driver. */ struct rtl8365mb { - struct realtek_smi *smi; + struct realtek_priv *priv; int irq; u32 chip_id; u32 chip_ver; @@ -561,16 +588,16 @@ struct rtl8365mb { size_t jam_size; }; -static int rtl8365mb_phy_poll_busy(struct realtek_smi *smi) +static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv) { u32 val; - return regmap_read_poll_timeout(smi->map, + return regmap_read_poll_timeout(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_STATUS_REG, val, !val, 10, 100); } -static int rtl8365mb_phy_ocp_prepare(struct realtek_smi *smi, int phy, +static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, u32 ocp_addr) { u32 val; @@ -579,7 +606,7 @@ static int rtl8365mb_phy_ocp_prepare(struct realtek_smi *smi, int phy, /* Set OCP prefix */ val = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK, ocp_addr); ret = regmap_update_bits( - smi->map, RTL8365MB_GPHY_OCP_MSB_0_REG, + priv->map_nolock, RTL8365MB_GPHY_OCP_MSB_0_REG, RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, FIELD_PREP(RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, val)); if (ret) @@ -592,89 +619,101 @@ static int rtl8365mb_phy_ocp_prepare(struct realtek_smi *smi, int phy, ocp_addr >> 1); val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK, ocp_addr >> 6); - ret = regmap_write(smi->map, RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, - val); + ret = regmap_write(priv->map_nolock, + RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, val); if (ret) return ret; return 0; } -static int rtl8365mb_phy_ocp_read(struct realtek_smi *smi, int phy, +static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy, u32 ocp_addr, u16 *data) { u32 val; int ret; - ret = rtl8365mb_phy_poll_busy(smi); - if (ret) - return ret; + mutex_lock(&priv->map_lock); - ret = rtl8365mb_phy_ocp_prepare(smi, phy, ocp_addr); + ret = rtl8365mb_phy_poll_busy(priv); if (ret) - return ret; + goto out; + + ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); + if (ret) + goto out; /* Execute read operation */ val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ); - ret = regmap_write(smi->map, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, val); + ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, + val); if (ret) - return ret; + goto out; - ret = rtl8365mb_phy_poll_busy(smi); + ret = rtl8365mb_phy_poll_busy(priv); if (ret) - return ret; + goto out; /* Get PHY register data */ - ret = regmap_read(smi->map, RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, - &val); + ret = regmap_read(priv->map_nolock, + RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, &val); if (ret) - return ret; + goto out; *data = val & 0xFFFF; - return 0; +out: + mutex_unlock(&priv->map_lock); + + return ret; } -static int rtl8365mb_phy_ocp_write(struct realtek_smi *smi, int phy, +static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy, u32 ocp_addr, u16 data) { u32 val; int ret; - ret = rtl8365mb_phy_poll_busy(smi); - if (ret) - return ret; + mutex_lock(&priv->map_lock); - ret = rtl8365mb_phy_ocp_prepare(smi, phy, ocp_addr); + ret = rtl8365mb_phy_poll_busy(priv); if (ret) - return ret; + goto out; + + ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); + if (ret) + goto out; /* Set PHY register data */ - ret = regmap_write(smi->map, RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, - data); + ret = regmap_write(priv->map_nolock, + RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, data); if (ret) - return ret; + goto out; /* Execute write operation */ val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE); - ret = regmap_write(smi->map, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, val); + ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, + val); if (ret) - return ret; + goto out; - ret = rtl8365mb_phy_poll_busy(smi); + ret = rtl8365mb_phy_poll_busy(priv); if (ret) - return ret; + goto out; + +out: + mutex_unlock(&priv->map_lock); return 0; } -static int rtl8365mb_phy_read(struct realtek_smi *smi, int phy, int regnum) +static int rtl8365mb_phy_read(struct realtek_priv *priv, int phy, int regnum) { u32 ocp_addr; u16 val; @@ -688,21 +727,21 @@ static int rtl8365mb_phy_read(struct realtek_smi *smi, int phy, int regnum) ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2; - ret = rtl8365mb_phy_ocp_read(smi, phy, ocp_addr, &val); + ret = rtl8365mb_phy_ocp_read(priv, phy, ocp_addr, &val); if (ret) { - dev_err(smi->dev, + dev_err(priv->dev, "failed to read PHY%d reg %02x @ %04x, ret %d\n", phy, regnum, ocp_addr, ret); return ret; } - dev_dbg(smi->dev, "read PHY%d register 0x%02x @ %04x, val <- %04x\n", + dev_dbg(priv->dev, "read PHY%d register 0x%02x @ %04x, val <- %04x\n", phy, regnum, ocp_addr, val); return val; } -static int rtl8365mb_phy_write(struct realtek_smi *smi, int phy, int regnum, +static int rtl8365mb_phy_write(struct realtek_priv *priv, int phy, int regnum, u16 val) { u32 ocp_addr; @@ -716,46 +755,67 @@ static int rtl8365mb_phy_write(struct realtek_smi *smi, int phy, int regnum, ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2; - ret = rtl8365mb_phy_ocp_write(smi, phy, ocp_addr, val); + ret = rtl8365mb_phy_ocp_write(priv, phy, ocp_addr, val); if (ret) { - dev_err(smi->dev, + dev_err(priv->dev, "failed to write PHY%d reg %02x @ %04x, ret %d\n", phy, regnum, ocp_addr, ret); return ret; } - dev_dbg(smi->dev, "write PHY%d register 0x%02x @ %04x, val -> %04x\n", + dev_dbg(priv->dev, "write PHY%d register 0x%02x @ %04x, val -> %04x\n", phy, regnum, ocp_addr, val); return 0; } +static int rtl8365mb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) +{ + return rtl8365mb_phy_read(ds->priv, phy, regnum); +} + +static int rtl8365mb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, + u16 val) +{ + return rtl8365mb_phy_write(ds->priv, phy, regnum, val); +} + static enum dsa_tag_protocol rtl8365mb_get_tag_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp) { + struct realtek_priv *priv = ds->priv; + struct rtl8365mb_cpu *cpu; + struct rtl8365mb *mb; + + mb = priv->chip_data; + cpu = &mb->cpu; + + if (cpu->position == RTL8365MB_CPU_POS_BEFORE_CRC) + return DSA_TAG_PROTO_RTL8_4T; + return DSA_TAG_PROTO_RTL8_4; } -static int rtl8365mb_ext_config_rgmii(struct realtek_smi *smi, int port, +static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port, phy_interface_t interface) { struct device_node *dn; struct dsa_port *dp; int tx_delay = 0; int rx_delay = 0; - int ext_port; + int ext_int; u32 val; int ret; - if (port == smi->cpu_port) { - ext_port = 1; - } else { - dev_err(smi->dev, "only one EXT port is currently supported\n"); + ext_int = rtl8365mb_extint_port_map[port]; + + if (ext_int <= 0) { + dev_err(priv->dev, "Port %d is not an external interface port\n", port); return -EINVAL; } - dp = dsa_to_port(smi->ds, port); + dp = dsa_to_port(priv->ds, port); dn = dp->dn; /* Set the RGMII TX/RX delay @@ -786,8 +846,8 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_smi *smi, int port, if (val == 0 || val == 2) tx_delay = val / 2; else - dev_warn(smi->dev, - "EXT port TX delay must be 0 or 2 ns\n"); + dev_warn(priv->dev, + "EXT interface TX delay must be 0 or 2 ns\n"); } if (!of_property_read_u32(dn, "rx-internal-delay-ps", &val)) { @@ -796,12 +856,12 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_smi *smi, int port, if (val <= 7) rx_delay = val; else - dev_warn(smi->dev, - "EXT port RX delay must be 0 to 2.1 ns\n"); + dev_warn(priv->dev, + "EXT interface RX delay must be 0 to 2.1 ns\n"); } ret = regmap_update_bits( - smi->map, RTL8365MB_EXT_RGMXF_REG(ext_port), + priv->map, RTL8365MB_EXT_RGMXF_REG(ext_int), RTL8365MB_EXT_RGMXF_TXDELAY_MASK | RTL8365MB_EXT_RGMXF_RXDELAY_MASK, FIELD_PREP(RTL8365MB_EXT_RGMXF_TXDELAY_MASK, tx_delay) | @@ -810,18 +870,18 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_smi *smi, int port, return ret; ret = regmap_update_bits( - smi->map, RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(ext_port), - RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(ext_port), + priv->map, RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(ext_int), + RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(ext_int), RTL8365MB_EXT_PORT_MODE_RGMII << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET( - ext_port)); + ext_int)); if (ret) return ret; return 0; } -static int rtl8365mb_ext_config_forcemode(struct realtek_smi *smi, int port, +static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port, bool link, int speed, int duplex, bool tx_pause, bool rx_pause) { @@ -830,14 +890,14 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_smi *smi, int port, u32 r_duplex; u32 r_speed; u32 r_link; - int ext_port; + int ext_int; int val; int ret; - if (port == smi->cpu_port) { - ext_port = 1; - } else { - dev_err(smi->dev, "only one EXT port is currently supported\n"); + ext_int = rtl8365mb_extint_port_map[port]; + + if (ext_int <= 0) { + dev_err(priv->dev, "Port %d is not an external interface port\n", port); return -EINVAL; } @@ -854,7 +914,7 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_smi *smi, int port, } else if (speed == SPEED_10) { r_speed = RTL8365MB_PORT_SPEED_10M; } else { - dev_err(smi->dev, "unsupported port speed %s\n", + dev_err(priv->dev, "unsupported port speed %s\n", phy_speed_to_str(speed)); return -EINVAL; } @@ -864,7 +924,7 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_smi *smi, int port, } else if (duplex == DUPLEX_HALF) { r_duplex = 0; } else { - dev_err(smi->dev, "unsupported duplex %s\n", + dev_err(priv->dev, "unsupported duplex %s\n", phy_duplex_to_str(duplex)); return -EINVAL; } @@ -886,8 +946,8 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_smi *smi, int port, FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK, r_duplex) | FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK, r_speed); - ret = regmap_write(smi->map, - RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(ext_port), + ret = regmap_write(priv->map, + RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(ext_int), val); if (ret) return ret; @@ -898,13 +958,17 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_smi *smi, int port, static bool rtl8365mb_phy_mode_supported(struct dsa_switch *ds, int port, phy_interface_t interface) { - if (dsa_is_user_port(ds, port) && + int ext_int; + + ext_int = rtl8365mb_extint_port_map[port]; + + if (ext_int < 0 && (interface == PHY_INTERFACE_MODE_NA || interface == PHY_INTERFACE_MODE_INTERNAL || interface == PHY_INTERFACE_MODE_GMII)) /* Internal PHY */ return true; - else if (dsa_is_cpu_port(ds, port) && + else if ((ext_int >= 1) && phy_interface_mode_is_rgmii(interface)) /* Extension MAC */ return true; @@ -912,65 +976,43 @@ static bool rtl8365mb_phy_mode_supported(struct dsa_switch *ds, int port, return false; } -static void rtl8365mb_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - struct realtek_smi *smi = ds->priv; - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0 }; + if (dsa_is_user_port(ds, port)) + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); + else if (dsa_is_cpu_port(ds, port)) + phy_interface_set_rgmii(config->supported_interfaces); - /* include/linux/phylink.h says: - * When @state->interface is %PHY_INTERFACE_MODE_NA, phylink - * expects the MAC driver to return all supported link modes. - */ - if (state->interface != PHY_INTERFACE_MODE_NA && - !rtl8365mb_phy_mode_supported(ds, port, state->interface)) { - dev_err(smi->dev, "phy mode %s is unsupported on port %d\n", - phy_modes(state->interface), port); - linkmode_zero(supported); - return; - } - - phylink_set_port_modes(mask); - - phylink_set(mask, Autoneg); - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - phylink_set(mask, 1000baseT_Full); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); + config->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000FD; } static void rtl8365mb_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, const struct phylink_link_state *state) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret; if (!rtl8365mb_phy_mode_supported(ds, port, state->interface)) { - dev_err(smi->dev, "phy mode %s is unsupported on port %d\n", + dev_err(priv->dev, "phy mode %s is unsupported on port %d\n", phy_modes(state->interface), port); return; } if (mode != MLO_AN_PHY && mode != MLO_AN_FIXED) { - dev_err(smi->dev, + dev_err(priv->dev, "port %d supports only conventional PHY or fixed-link\n", port); return; } if (phy_interface_mode_is_rgmii(state->interface)) { - ret = rtl8365mb_ext_config_rgmii(smi, port, state->interface); + ret = rtl8365mb_ext_config_rgmii(priv, port, state->interface); if (ret) - dev_err(smi->dev, + dev_err(priv->dev, "failed to configure RGMII mode on port %d: %d\n", port, ret); return; @@ -985,20 +1027,20 @@ static void rtl8365mb_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb_port *p; struct rtl8365mb *mb; int ret; - mb = smi->chip_data; + mb = priv->chip_data; p = &mb->ports[port]; cancel_delayed_work_sync(&p->mib_work); if (phy_interface_mode_is_rgmii(interface)) { - ret = rtl8365mb_ext_config_forcemode(smi, port, false, 0, 0, + ret = rtl8365mb_ext_config_forcemode(priv, port, false, 0, 0, false, false); if (ret) - dev_err(smi->dev, + dev_err(priv->dev, "failed to reset forced mode on port %d: %d\n", port, ret); @@ -1013,21 +1055,21 @@ static void rtl8365mb_phylink_mac_link_up(struct dsa_switch *ds, int port, int duplex, bool tx_pause, bool rx_pause) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb_port *p; struct rtl8365mb *mb; int ret; - mb = smi->chip_data; + mb = priv->chip_data; p = &mb->ports[port]; schedule_delayed_work(&p->mib_work, 0); if (phy_interface_mode_is_rgmii(interface)) { - ret = rtl8365mb_ext_config_forcemode(smi, port, true, speed, + ret = rtl8365mb_ext_config_forcemode(priv, port, true, speed, duplex, tx_pause, rx_pause); if (ret) - dev_err(smi->dev, + dev_err(priv->dev, "failed to force mode on port %d: %d\n", port, ret); @@ -1038,7 +1080,7 @@ static void rtl8365mb_phylink_mac_link_up(struct dsa_switch *ds, int port, static void rtl8365mb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; enum rtl8365mb_stp_state val; int msti = 0; @@ -1057,36 +1099,36 @@ static void rtl8365mb_port_stp_state_set(struct dsa_switch *ds, int port, val = RTL8365MB_STP_STATE_FORWARDING; break; default: - dev_err(smi->dev, "invalid STP state: %u\n", state); + dev_err(priv->dev, "invalid STP state: %u\n", state); return; } - regmap_update_bits(smi->map, RTL8365MB_MSTI_CTRL_REG(msti, port), + regmap_update_bits(priv->map, RTL8365MB_MSTI_CTRL_REG(msti, port), RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(port), val << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(port)); } -static int rtl8365mb_port_set_learning(struct realtek_smi *smi, int port, +static int rtl8365mb_port_set_learning(struct realtek_priv *priv, int port, bool enable) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; /* Enable/disable learning by limiting the number of L2 addresses the * port can learn. Realtek documentation states that a limit of zero * disables learning. When enabling learning, set it to the chip's * maximum. */ - return regmap_write(smi->map, RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(port), + return regmap_write(priv->map, RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(port), enable ? mb->learn_limit_max : 0); } -static int rtl8365mb_port_set_isolation(struct realtek_smi *smi, int port, +static int rtl8365mb_port_set_isolation(struct realtek_priv *priv, int port, u32 mask) { - return regmap_write(smi->map, RTL8365MB_PORT_ISOLATION_REG(port), mask); + return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), mask); } -static int rtl8365mb_mib_counter_read(struct realtek_smi *smi, int port, +static int rtl8365mb_mib_counter_read(struct realtek_priv *priv, int port, u32 offset, u32 length, u64 *mibvalue) { u64 tmpvalue = 0; @@ -1098,13 +1140,13 @@ static int rtl8365mb_mib_counter_read(struct realtek_smi *smi, int port, * and then poll the control register before reading the value from some * counter registers. */ - ret = regmap_write(smi->map, RTL8365MB_MIB_ADDRESS_REG, + ret = regmap_write(priv->map, RTL8365MB_MIB_ADDRESS_REG, RTL8365MB_MIB_ADDRESS(port, offset)); if (ret) return ret; /* Poll for completion */ - ret = regmap_read_poll_timeout(smi->map, RTL8365MB_MIB_CTRL0_REG, val, + ret = regmap_read_poll_timeout(priv->map, RTL8365MB_MIB_CTRL0_REG, val, !(val & RTL8365MB_MIB_CTRL0_BUSY_MASK), 10, 100); if (ret) @@ -1126,7 +1168,7 @@ static int rtl8365mb_mib_counter_read(struct realtek_smi *smi, int port, /* Read the MIB counter 16 bits at a time */ for (i = 0; i < length; i++) { - ret = regmap_read(smi->map, + ret = regmap_read(priv->map, RTL8365MB_MIB_COUNTER_REG(offset - i), &val); if (ret) return ret; @@ -1142,21 +1184,21 @@ static int rtl8365mb_mib_counter_read(struct realtek_smi *smi, int port, static void rtl8365mb_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb *mb; int ret; int i; - mb = smi->chip_data; + mb = priv->chip_data; mutex_lock(&mb->mib_lock); for (i = 0; i < RTL8365MB_MIB_END; i++) { struct rtl8365mb_mib_counter *mib = &rtl8365mb_mib_counters[i]; - ret = rtl8365mb_mib_counter_read(smi, port, mib->offset, + ret = rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, &data[i]); if (ret) { - dev_err(smi->dev, + dev_err(priv->dev, "failed to read port %d counters: %d\n", port, ret); break; @@ -1190,15 +1232,15 @@ static int rtl8365mb_get_sset_count(struct dsa_switch *ds, int port, int sset) static void rtl8365mb_get_phy_stats(struct dsa_switch *ds, int port, struct ethtool_eth_phy_stats *phy_stats) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb_mib_counter *mib; struct rtl8365mb *mb; - mb = smi->chip_data; + mb = priv->chip_data; mib = &rtl8365mb_mib_counters[RTL8365MB_MIB_dot3StatsSymbolErrors]; mutex_lock(&mb->mib_lock); - rtl8365mb_mib_counter_read(smi, port, mib->offset, mib->length, + rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, &phy_stats->SymbolErrorDuringCarrier); mutex_unlock(&mb->mib_lock); } @@ -1226,12 +1268,12 @@ static void rtl8365mb_get_mac_stats(struct dsa_switch *ds, int port, [RTL8365MB_MIB_dot3StatsExcessiveCollisions] = 1, }; - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb *mb; int ret; int i; - mb = smi->chip_data; + mb = priv->chip_data; mutex_lock(&mb->mib_lock); for (i = 0; i < RTL8365MB_MIB_END; i++) { @@ -1241,7 +1283,7 @@ static void rtl8365mb_get_mac_stats(struct dsa_switch *ds, int port, if (!cnt[i]) continue; - ret = rtl8365mb_mib_counter_read(smi, port, mib->offset, + ret = rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, &cnt[i]); if (ret) break; @@ -1291,20 +1333,20 @@ static void rtl8365mb_get_mac_stats(struct dsa_switch *ds, int port, static void rtl8365mb_get_ctrl_stats(struct dsa_switch *ds, int port, struct ethtool_eth_ctrl_stats *ctrl_stats) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb_mib_counter *mib; struct rtl8365mb *mb; - mb = smi->chip_data; + mb = priv->chip_data; mib = &rtl8365mb_mib_counters[RTL8365MB_MIB_dot3ControlInUnknownOpcodes]; mutex_lock(&mb->mib_lock); - rtl8365mb_mib_counter_read(smi, port, mib->offset, mib->length, + rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, &ctrl_stats->UnsupportedOpcodesReceived); mutex_unlock(&mb->mib_lock); } -static void rtl8365mb_stats_update(struct realtek_smi *smi, int port) +static void rtl8365mb_stats_update(struct realtek_priv *priv, int port) { u64 cnt[RTL8365MB_MIB_END] = { [RTL8365MB_MIB_ifOutOctets] = 1, @@ -1323,7 +1365,7 @@ static void rtl8365mb_stats_update(struct realtek_smi *smi, int port) [RTL8365MB_MIB_dot3StatsFCSErrors] = 1, [RTL8365MB_MIB_dot3StatsLateCollisions] = 1, }; - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; struct rtnl_link_stats64 *stats; int ret; int i; @@ -1338,7 +1380,7 @@ static void rtl8365mb_stats_update(struct realtek_smi *smi, int port) if (!cnt[i]) continue; - ret = rtl8365mb_mib_counter_read(smi, port, c->offset, + ret = rtl8365mb_mib_counter_read(priv, port, c->offset, c->length, &cnt[i]); if (ret) break; @@ -1388,9 +1430,9 @@ static void rtl8365mb_stats_poll(struct work_struct *work) struct rtl8365mb_port *p = container_of(to_delayed_work(work), struct rtl8365mb_port, mib_work); - struct realtek_smi *smi = p->smi; + struct realtek_priv *priv = p->priv; - rtl8365mb_stats_update(smi, p->index); + rtl8365mb_stats_update(priv, p->index); schedule_delayed_work(&p->mib_work, RTL8365MB_STATS_INTERVAL_JIFFIES); } @@ -1398,11 +1440,11 @@ static void rtl8365mb_stats_poll(struct work_struct *work) static void rtl8365mb_get_stats64(struct dsa_switch *ds, int port, struct rtnl_link_stats64 *s) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb_port *p; struct rtl8365mb *mb; - mb = smi->chip_data; + mb = priv->chip_data; p = &mb->ports[port]; spin_lock(&p->stats_lock); @@ -1410,9 +1452,9 @@ static void rtl8365mb_get_stats64(struct dsa_switch *ds, int port, spin_unlock(&p->stats_lock); } -static void rtl8365mb_stats_setup(struct realtek_smi *smi) +static void rtl8365mb_stats_setup(struct realtek_priv *priv) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; int i; /* Per-chip global mutex to protect MIB counter access, since doing @@ -1420,10 +1462,10 @@ static void rtl8365mb_stats_setup(struct realtek_smi *smi) */ mutex_init(&mb->mib_lock); - for (i = 0; i < smi->num_ports; i++) { + for (i = 0; i < priv->num_ports; i++) { struct rtl8365mb_port *p = &mb->ports[i]; - if (dsa_is_unused_port(smi->ds, i)) + if (dsa_is_unused_port(priv->ds, i)) continue; /* Per-port spinlock to protect the stats64 data */ @@ -1436,45 +1478,45 @@ static void rtl8365mb_stats_setup(struct realtek_smi *smi) } } -static void rtl8365mb_stats_teardown(struct realtek_smi *smi) +static void rtl8365mb_stats_teardown(struct realtek_priv *priv) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; int i; - for (i = 0; i < smi->num_ports; i++) { + for (i = 0; i < priv->num_ports; i++) { struct rtl8365mb_port *p = &mb->ports[i]; - if (dsa_is_unused_port(smi->ds, i)) + if (dsa_is_unused_port(priv->ds, i)) continue; cancel_delayed_work_sync(&p->mib_work); } } -static int rtl8365mb_get_and_clear_status_reg(struct realtek_smi *smi, u32 reg, +static int rtl8365mb_get_and_clear_status_reg(struct realtek_priv *priv, u32 reg, u32 *val) { int ret; - ret = regmap_read(smi->map, reg, val); + ret = regmap_read(priv->map, reg, val); if (ret) return ret; - return regmap_write(smi->map, reg, *val); + return regmap_write(priv->map, reg, *val); } static irqreturn_t rtl8365mb_irq(int irq, void *data) { - struct realtek_smi *smi = data; + struct realtek_priv *priv = data; unsigned long line_changes = 0; struct rtl8365mb *mb; u32 stat; int line; int ret; - mb = smi->chip_data; + mb = priv->chip_data; - ret = rtl8365mb_get_and_clear_status_reg(smi, RTL8365MB_INTR_STATUS_REG, + ret = rtl8365mb_get_and_clear_status_reg(priv, RTL8365MB_INTR_STATUS_REG, &stat); if (ret) goto out_error; @@ -1485,14 +1527,14 @@ static irqreturn_t rtl8365mb_irq(int irq, void *data) u32 val; ret = rtl8365mb_get_and_clear_status_reg( - smi, RTL8365MB_PORT_LINKUP_IND_REG, &val); + priv, RTL8365MB_PORT_LINKUP_IND_REG, &val); if (ret) goto out_error; linkup_ind = FIELD_GET(RTL8365MB_PORT_LINKUP_IND_MASK, val); ret = rtl8365mb_get_and_clear_status_reg( - smi, RTL8365MB_PORT_LINKDOWN_IND_REG, &val); + priv, RTL8365MB_PORT_LINKDOWN_IND_REG, &val); if (ret) goto out_error; @@ -1504,8 +1546,8 @@ static irqreturn_t rtl8365mb_irq(int irq, void *data) if (!line_changes) goto out_none; - for_each_set_bit(line, &line_changes, smi->num_ports) { - int child_irq = irq_find_mapping(smi->irqdomain, line); + for_each_set_bit(line, &line_changes, priv->num_ports) { + int child_irq = irq_find_mapping(priv->irqdomain, line); handle_nested_irq(child_irq); } @@ -1513,7 +1555,7 @@ static irqreturn_t rtl8365mb_irq(int irq, void *data) return IRQ_HANDLED; out_error: - dev_err(smi->dev, "failed to read interrupt status: %d\n", ret); + dev_err(priv->dev, "failed to read interrupt status: %d\n", ret); out_none: return IRQ_NONE; @@ -1548,27 +1590,27 @@ static const struct irq_domain_ops rtl8365mb_irqdomain_ops = { .xlate = irq_domain_xlate_onecell, }; -static int rtl8365mb_set_irq_enable(struct realtek_smi *smi, bool enable) +static int rtl8365mb_set_irq_enable(struct realtek_priv *priv, bool enable) { - return regmap_update_bits(smi->map, RTL8365MB_INTR_CTRL_REG, + return regmap_update_bits(priv->map, RTL8365MB_INTR_CTRL_REG, RTL8365MB_INTR_LINK_CHANGE_MASK, FIELD_PREP(RTL8365MB_INTR_LINK_CHANGE_MASK, enable ? 1 : 0)); } -static int rtl8365mb_irq_enable(struct realtek_smi *smi) +static int rtl8365mb_irq_enable(struct realtek_priv *priv) { - return rtl8365mb_set_irq_enable(smi, true); + return rtl8365mb_set_irq_enable(priv, true); } -static int rtl8365mb_irq_disable(struct realtek_smi *smi) +static int rtl8365mb_irq_disable(struct realtek_priv *priv) { - return rtl8365mb_set_irq_enable(smi, false); + return rtl8365mb_set_irq_enable(priv, false); } -static int rtl8365mb_irq_setup(struct realtek_smi *smi) +static int rtl8365mb_irq_setup(struct realtek_priv *priv) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; struct device_node *intc; u32 irq_trig; int virq; @@ -1577,9 +1619,9 @@ static int rtl8365mb_irq_setup(struct realtek_smi *smi) int ret; int i; - intc = of_get_child_by_name(smi->dev->of_node, "interrupt-controller"); + intc = of_get_child_by_name(priv->dev->of_node, "interrupt-controller"); if (!intc) { - dev_err(smi->dev, "missing child interrupt-controller node\n"); + dev_err(priv->dev, "missing child interrupt-controller node\n"); return -EINVAL; } @@ -1587,24 +1629,24 @@ static int rtl8365mb_irq_setup(struct realtek_smi *smi) irq = of_irq_get(intc, 0); if (irq <= 0) { if (irq != -EPROBE_DEFER) - dev_err(smi->dev, "failed to get parent irq: %d\n", + dev_err(priv->dev, "failed to get parent irq: %d\n", irq); ret = irq ? irq : -EINVAL; goto out_put_node; } - smi->irqdomain = irq_domain_add_linear(intc, smi->num_ports, - &rtl8365mb_irqdomain_ops, smi); - if (!smi->irqdomain) { - dev_err(smi->dev, "failed to add irq domain\n"); + priv->irqdomain = irq_domain_add_linear(intc, priv->num_ports, + &rtl8365mb_irqdomain_ops, priv); + if (!priv->irqdomain) { + dev_err(priv->dev, "failed to add irq domain\n"); ret = -ENOMEM; goto out_put_node; } - for (i = 0; i < smi->num_ports; i++) { - virq = irq_create_mapping(smi->irqdomain, i); + for (i = 0; i < priv->num_ports; i++) { + virq = irq_create_mapping(priv->irqdomain, i); if (!virq) { - dev_err(smi->dev, + dev_err(priv->dev, "failed to create irq domain mapping\n"); ret = -EINVAL; goto out_remove_irqdomain; @@ -1625,40 +1667,40 @@ static int rtl8365mb_irq_setup(struct realtek_smi *smi) val = RTL8365MB_INTR_POLARITY_LOW; break; default: - dev_err(smi->dev, "unsupported irq trigger type %u\n", + dev_err(priv->dev, "unsupported irq trigger type %u\n", irq_trig); ret = -EINVAL; goto out_remove_irqdomain; } - ret = regmap_update_bits(smi->map, RTL8365MB_INTR_POLARITY_REG, + ret = regmap_update_bits(priv->map, RTL8365MB_INTR_POLARITY_REG, RTL8365MB_INTR_POLARITY_MASK, FIELD_PREP(RTL8365MB_INTR_POLARITY_MASK, val)); if (ret) goto out_remove_irqdomain; /* Disable the interrupt in case the chip has it enabled on reset */ - ret = rtl8365mb_irq_disable(smi); + ret = rtl8365mb_irq_disable(priv); if (ret) goto out_remove_irqdomain; /* Clear the interrupt status register */ - ret = regmap_write(smi->map, RTL8365MB_INTR_STATUS_REG, + ret = regmap_write(priv->map, RTL8365MB_INTR_STATUS_REG, RTL8365MB_INTR_ALL_MASK); if (ret) goto out_remove_irqdomain; ret = request_threaded_irq(irq, NULL, rtl8365mb_irq, IRQF_ONESHOT, - "rtl8365mb", smi); + "rtl8365mb", priv); if (ret) { - dev_err(smi->dev, "failed to request irq: %d\n", ret); + dev_err(priv->dev, "failed to request irq: %d\n", ret); goto out_remove_irqdomain; } /* Store the irq so that we know to free it during teardown */ mb->irq = irq; - ret = rtl8365mb_irq_enable(smi); + ret = rtl8365mb_irq_enable(priv); if (ret) goto out_free_irq; @@ -1667,17 +1709,17 @@ static int rtl8365mb_irq_setup(struct realtek_smi *smi) return 0; out_free_irq: - free_irq(mb->irq, smi); + free_irq(mb->irq, priv); mb->irq = 0; out_remove_irqdomain: - for (i = 0; i < smi->num_ports; i++) { - virq = irq_find_mapping(smi->irqdomain, i); + for (i = 0; i < priv->num_ports; i++) { + virq = irq_find_mapping(priv->irqdomain, i); irq_dispose_mapping(virq); } - irq_domain_remove(smi->irqdomain); - smi->irqdomain = NULL; + irq_domain_remove(priv->irqdomain); + priv->irqdomain = NULL; out_put_node: of_node_put(intc); @@ -1685,36 +1727,36 @@ static int rtl8365mb_irq_setup(struct realtek_smi *smi) return ret; } -static void rtl8365mb_irq_teardown(struct realtek_smi *smi) +static void rtl8365mb_irq_teardown(struct realtek_priv *priv) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; int virq; int i; if (mb->irq) { - free_irq(mb->irq, smi); + free_irq(mb->irq, priv); mb->irq = 0; } - if (smi->irqdomain) { - for (i = 0; i < smi->num_ports; i++) { - virq = irq_find_mapping(smi->irqdomain, i); + if (priv->irqdomain) { + for (i = 0; i < priv->num_ports; i++) { + virq = irq_find_mapping(priv->irqdomain, i); irq_dispose_mapping(virq); } - irq_domain_remove(smi->irqdomain); - smi->irqdomain = NULL; + irq_domain_remove(priv->irqdomain); + priv->irqdomain = NULL; } } -static int rtl8365mb_cpu_config(struct realtek_smi *smi) +static int rtl8365mb_cpu_config(struct realtek_priv *priv) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; struct rtl8365mb_cpu *cpu = &mb->cpu; u32 val; int ret; - ret = regmap_update_bits(smi->map, RTL8365MB_CPU_PORT_MASK_REG, + ret = regmap_update_bits(priv->map, RTL8365MB_CPU_PORT_MASK_REG, RTL8365MB_CPU_PORT_MASK_MASK, FIELD_PREP(RTL8365MB_CPU_PORT_MASK_MASK, cpu->mask)); @@ -1726,26 +1768,57 @@ static int rtl8365mb_cpu_config(struct realtek_smi *smi) FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_POSITION_MASK, cpu->position) | FIELD_PREP(RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK, cpu->rx_length) | FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK, cpu->format) | - FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_MASK, cpu->trap_port) | + FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_MASK, cpu->trap_port & 0x7) | FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK, - cpu->trap_port >> 3); - ret = regmap_write(smi->map, RTL8365MB_CPU_CTRL_REG, val); + cpu->trap_port >> 3 & 0x1); + ret = regmap_write(priv->map, RTL8365MB_CPU_CTRL_REG, val); if (ret) return ret; return 0; } -static int rtl8365mb_switch_init(struct realtek_smi *smi) +static int rtl8365mb_change_tag_protocol(struct dsa_switch *ds, int cpu_index, + enum dsa_tag_protocol proto) { - struct rtl8365mb *mb = smi->chip_data; + struct realtek_priv *priv = ds->priv; + struct rtl8365mb_cpu *cpu; + struct rtl8365mb *mb; + + mb = priv->chip_data; + cpu = &mb->cpu; + + switch (proto) { + case DSA_TAG_PROTO_RTL8_4: + cpu->format = RTL8365MB_CPU_FORMAT_8BYTES; + cpu->position = RTL8365MB_CPU_POS_AFTER_SA; + break; + case DSA_TAG_PROTO_RTL8_4T: + cpu->format = RTL8365MB_CPU_FORMAT_8BYTES; + cpu->position = RTL8365MB_CPU_POS_BEFORE_CRC; + break; + /* The switch also supports a 4-byte format, similar to rtl4a but with + * the same 0x04 8-bit version and probably 8-bit port source/dest. + * There is no public doc about it. Not supported yet and it will probably + * never be. + */ + default: + return -EPROTONOSUPPORT; + } + + return rtl8365mb_cpu_config(priv); +} + +static int rtl8365mb_switch_init(struct realtek_priv *priv) +{ + struct rtl8365mb *mb = priv->chip_data; int ret; int i; /* Do any chip-specific init jam before getting to the common stuff */ if (mb->jam_table) { for (i = 0; i < mb->jam_size; i++) { - ret = regmap_write(smi->map, mb->jam_table[i].reg, + ret = regmap_write(priv->map, mb->jam_table[i].reg, mb->jam_table[i].val); if (ret) return ret; @@ -1754,7 +1827,7 @@ static int rtl8365mb_switch_init(struct realtek_smi *smi) /* Common init jam */ for (i = 0; i < ARRAY_SIZE(rtl8365mb_init_jam_common); i++) { - ret = regmap_write(smi->map, rtl8365mb_init_jam_common[i].reg, + ret = regmap_write(priv->map, rtl8365mb_init_jam_common[i].reg, rtl8365mb_init_jam_common[i].val); if (ret) return ret; @@ -1763,75 +1836,80 @@ static int rtl8365mb_switch_init(struct realtek_smi *smi) return 0; } -static int rtl8365mb_reset_chip(struct realtek_smi *smi) +static int rtl8365mb_reset_chip(struct realtek_priv *priv) { u32 val; - realtek_smi_write_reg_noack(smi, RTL8365MB_CHIP_RESET_REG, - FIELD_PREP(RTL8365MB_CHIP_RESET_HW_MASK, - 1)); + priv->write_reg_noack(priv, RTL8365MB_CHIP_RESET_REG, + FIELD_PREP(RTL8365MB_CHIP_RESET_HW_MASK, 1)); /* Realtek documentation says the chip needs 1 second to reset. Sleep * for 100 ms before accessing any registers to prevent ACK timeouts. */ msleep(100); - return regmap_read_poll_timeout(smi->map, RTL8365MB_CHIP_RESET_REG, val, + return regmap_read_poll_timeout(priv->map, RTL8365MB_CHIP_RESET_REG, val, !(val & RTL8365MB_CHIP_RESET_HW_MASK), 20000, 1e6); } static int rtl8365mb_setup(struct dsa_switch *ds) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; + struct rtl8365mb_cpu *cpu; + struct dsa_port *cpu_dp; struct rtl8365mb *mb; int ret; int i; - mb = smi->chip_data; + mb = priv->chip_data; + cpu = &mb->cpu; - ret = rtl8365mb_reset_chip(smi); + ret = rtl8365mb_reset_chip(priv); if (ret) { - dev_err(smi->dev, "failed to reset chip: %d\n", ret); + dev_err(priv->dev, "failed to reset chip: %d\n", ret); goto out_error; } /* Configure switch to vendor-defined initial state */ - ret = rtl8365mb_switch_init(smi); + ret = rtl8365mb_switch_init(priv); if (ret) { - dev_err(smi->dev, "failed to initialize switch: %d\n", ret); + dev_err(priv->dev, "failed to initialize switch: %d\n", ret); goto out_error; } /* Set up cascading IRQs */ - ret = rtl8365mb_irq_setup(smi); + ret = rtl8365mb_irq_setup(priv); if (ret == -EPROBE_DEFER) return ret; else if (ret) - dev_info(smi->dev, "no interrupt support\n"); + dev_info(priv->dev, "no interrupt support\n"); /* Configure CPU tagging */ - ret = rtl8365mb_cpu_config(smi); + dsa_switch_for_each_cpu_port(cpu_dp, priv->ds) { + cpu->mask |= BIT(cpu_dp->index); + + if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS) + cpu->trap_port = cpu_dp->index; + } + cpu->enable = cpu->mask > 0; + ret = rtl8365mb_cpu_config(priv); if (ret) goto out_teardown_irq; /* Configure ports */ - for (i = 0; i < smi->num_ports; i++) { + for (i = 0; i < priv->num_ports; i++) { struct rtl8365mb_port *p = &mb->ports[i]; - if (dsa_is_unused_port(smi->ds, i)) + if (dsa_is_unused_port(priv->ds, i)) continue; - /* Set up per-port private data */ - p->smi = smi; - p->index = i; - /* Forward only to the CPU */ - ret = rtl8365mb_port_set_isolation(smi, i, BIT(smi->cpu_port)); + ret = rtl8365mb_port_set_isolation(priv, i, cpu->mask); if (ret) goto out_teardown_irq; /* Disable learning */ - ret = rtl8365mb_port_set_learning(smi, i, false); + ret = rtl8365mb_port_set_learning(priv, i, false); if (ret) goto out_teardown_irq; @@ -1839,29 +1917,35 @@ static int rtl8365mb_setup(struct dsa_switch *ds) * ports will still forward frames to the CPU despite being * administratively down by default. */ - rtl8365mb_port_stp_state_set(smi->ds, i, BR_STATE_DISABLED); + rtl8365mb_port_stp_state_set(priv->ds, i, BR_STATE_DISABLED); + + /* Set up per-port private data */ + p->priv = priv; + p->index = i; } /* Set maximum packet length to 1536 bytes */ - ret = regmap_update_bits(smi->map, RTL8365MB_CFG0_MAX_LEN_REG, + ret = regmap_update_bits(priv->map, RTL8365MB_CFG0_MAX_LEN_REG, RTL8365MB_CFG0_MAX_LEN_MASK, FIELD_PREP(RTL8365MB_CFG0_MAX_LEN_MASK, 1536)); if (ret) goto out_teardown_irq; - ret = realtek_smi_setup_mdio(smi); - if (ret) { - dev_err(smi->dev, "could not set up MDIO bus\n"); - goto out_teardown_irq; + if (priv->setup_interface) { + ret = priv->setup_interface(ds); + if (ret) { + dev_err(priv->dev, "could not set up MDIO bus\n"); + goto out_teardown_irq; + } } /* Start statistics counter polling */ - rtl8365mb_stats_setup(smi); + rtl8365mb_stats_setup(priv); return 0; out_teardown_irq: - rtl8365mb_irq_teardown(smi); + rtl8365mb_irq_teardown(priv); out_error: return ret; @@ -1869,10 +1953,10 @@ static int rtl8365mb_setup(struct dsa_switch *ds) static void rtl8365mb_teardown(struct dsa_switch *ds) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; - rtl8365mb_stats_teardown(smi); - rtl8365mb_irq_teardown(smi); + rtl8365mb_stats_teardown(priv); + rtl8365mb_irq_teardown(priv); } static int rtl8365mb_get_chip_id_and_ver(struct regmap *map, u32 *id, u32 *ver) @@ -1902,40 +1986,55 @@ static int rtl8365mb_get_chip_id_and_ver(struct regmap *map, u32 *id, u32 *ver) return 0; } -static int rtl8365mb_detect(struct realtek_smi *smi) +static int rtl8365mb_detect(struct realtek_priv *priv) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; u32 chip_id; u32 chip_ver; int ret; - ret = rtl8365mb_get_chip_id_and_ver(smi->map, &chip_id, &chip_ver); + ret = rtl8365mb_get_chip_id_and_ver(priv->map, &chip_id, &chip_ver); if (ret) { - dev_err(smi->dev, "failed to read chip id and version: %d\n", + dev_err(priv->dev, "failed to read chip id and version: %d\n", ret); return ret; } switch (chip_id) { case RTL8365MB_CHIP_ID_8365MB_VC: - dev_info(smi->dev, - "found an RTL8365MB-VC switch (ver=0x%04x)\n", - chip_ver); + switch (chip_ver) { + case RTL8365MB_CHIP_VER_8365MB_VC: + dev_info(priv->dev, + "found an RTL8365MB-VC switch (ver=0x%04x)\n", + chip_ver); + break; + case RTL8365MB_CHIP_VER_8367RB: + dev_info(priv->dev, + "found an RTL8367RB-VB switch (ver=0x%04x)\n", + chip_ver); + break; + case RTL8365MB_CHIP_VER_8367S: + dev_info(priv->dev, + "found an RTL8367S switch (ver=0x%04x)\n", + chip_ver); + break; + default: + dev_err(priv->dev, "unrecognized switch version (ver=0x%04x)", + chip_ver); + return -ENODEV; + } - smi->cpu_port = RTL8365MB_CPU_PORT_NUM_8365MB_VC; - smi->num_ports = smi->cpu_port + 1; + priv->num_ports = RTL8365MB_MAX_NUM_PORTS; - mb->smi = smi; + mb->priv = priv; mb->chip_id = chip_id; mb->chip_ver = chip_ver; - mb->port_mask = BIT(smi->num_ports) - 1; - mb->learn_limit_max = RTL8365MB_LEARN_LIMIT_MAX_8365MB_VC; + mb->port_mask = GENMASK(priv->num_ports - 1, 0); + mb->learn_limit_max = RTL8365MB_LEARN_LIMIT_MAX; mb->jam_table = rtl8365mb_init_jam_8365mb_vc; mb->jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc); - mb->cpu.enable = 1; - mb->cpu.mask = BIT(smi->cpu_port); - mb->cpu.trap_port = smi->cpu_port; + mb->cpu.trap_port = RTL8365MB_MAX_NUM_PORTS; mb->cpu.insert = RTL8365MB_CPU_INSERT_TO_ALL; mb->cpu.position = RTL8365MB_CPU_POS_AFTER_SA; mb->cpu.rx_length = RTL8365MB_CPU_RXLEN_64BYTES; @@ -1943,7 +2042,7 @@ static int rtl8365mb_detect(struct realtek_smi *smi) break; default: - dev_err(smi->dev, + dev_err(priv->dev, "found an unknown Realtek switch (id=0x%04x, ver=0x%04x)\n", chip_id, chip_ver); return -ENODEV; @@ -1952,11 +2051,12 @@ static int rtl8365mb_detect(struct realtek_smi *smi) return 0; } -static const struct dsa_switch_ops rtl8365mb_switch_ops = { +static const struct dsa_switch_ops rtl8365mb_switch_ops_smi = { .get_tag_protocol = rtl8365mb_get_tag_protocol, + .change_tag_protocol = rtl8365mb_change_tag_protocol, .setup = rtl8365mb_setup, .teardown = rtl8365mb_teardown, - .phylink_validate = rtl8365mb_phylink_validate, + .phylink_get_caps = rtl8365mb_phylink_get_caps, .phylink_mac_config = rtl8365mb_phylink_mac_config, .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down, .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up, @@ -1970,18 +2070,44 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = { .get_stats64 = rtl8365mb_get_stats64, }; -static const struct realtek_smi_ops rtl8365mb_smi_ops = { +static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = { + .get_tag_protocol = rtl8365mb_get_tag_protocol, + .change_tag_protocol = rtl8365mb_change_tag_protocol, + .setup = rtl8365mb_setup, + .teardown = rtl8365mb_teardown, + .phylink_get_caps = rtl8365mb_phylink_get_caps, + .phylink_mac_config = rtl8365mb_phylink_mac_config, + .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down, + .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up, + .phy_read = rtl8365mb_dsa_phy_read, + .phy_write = rtl8365mb_dsa_phy_write, + .port_stp_state_set = rtl8365mb_port_stp_state_set, + .get_strings = rtl8365mb_get_strings, + .get_ethtool_stats = rtl8365mb_get_ethtool_stats, + .get_sset_count = rtl8365mb_get_sset_count, + .get_eth_phy_stats = rtl8365mb_get_phy_stats, + .get_eth_mac_stats = rtl8365mb_get_mac_stats, + .get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats, + .get_stats64 = rtl8365mb_get_stats64, +}; + +static const struct realtek_ops rtl8365mb_ops = { .detect = rtl8365mb_detect, .phy_read = rtl8365mb_phy_read, .phy_write = rtl8365mb_phy_write, }; -const struct realtek_smi_variant rtl8365mb_variant = { - .ds_ops = &rtl8365mb_switch_ops, - .ops = &rtl8365mb_smi_ops, +const struct realtek_variant rtl8365mb_variant = { + .ds_ops_smi = &rtl8365mb_switch_ops_smi, + .ds_ops_mdio = &rtl8365mb_switch_ops_mdio, + .ops = &rtl8365mb_ops, .clk_delay = 10, .cmd_read = 0xb9, .cmd_write = 0xb8, .chip_data_sz = sizeof(struct rtl8365mb), }; EXPORT_SYMBOL_GPL(rtl8365mb_variant); + +MODULE_AUTHOR("Alvin Å ipraga "); +MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/rtl8366.c b/drivers/net/dsa/realtek/rtl8366-core.c similarity index 61% rename from drivers/net/dsa/rtl8366.c rename to drivers/net/dsa/realtek/rtl8366-core.c index bdb8d8d34880..dc5f75be3017 100644 --- a/drivers/net/dsa/rtl8366.c +++ b/drivers/net/dsa/realtek/rtl8366-core.c @@ -11,18 +11,18 @@ #include #include -#include "realtek-smi-core.h" +#include "realtek.h" -int rtl8366_mc_is_used(struct realtek_smi *smi, int mc_index, int *used) +int rtl8366_mc_is_used(struct realtek_priv *priv, int mc_index, int *used) { int ret; int i; *used = 0; - for (i = 0; i < smi->num_ports; i++) { + for (i = 0; i < priv->num_ports; i++) { int index = 0; - ret = smi->ops->get_mc_index(smi, i, &index); + ret = priv->ops->get_mc_index(priv, i, &index); if (ret) return ret; @@ -38,13 +38,13 @@ EXPORT_SYMBOL_GPL(rtl8366_mc_is_used); /** * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration - * @smi: the Realtek SMI device instance + * @priv: the Realtek SMI device instance * @vid: the VLAN ID to look up or allocate * @vlanmc: the pointer will be assigned to a pointer to a valid member config * if successful * @return: index of a new member config or negative error number */ -static int rtl8366_obtain_mc(struct realtek_smi *smi, int vid, +static int rtl8366_obtain_mc(struct realtek_priv *priv, int vid, struct rtl8366_vlan_mc *vlanmc) { struct rtl8366_vlan_4k vlan4k; @@ -52,10 +52,10 @@ static int rtl8366_obtain_mc(struct realtek_smi *smi, int vid, int i; /* Try to find an existing member config entry for this VID */ - for (i = 0; i < smi->num_vlan_mc; i++) { - ret = smi->ops->get_vlan_mc(smi, i, vlanmc); + for (i = 0; i < priv->num_vlan_mc; i++) { + ret = priv->ops->get_vlan_mc(priv, i, vlanmc); if (ret) { - dev_err(smi->dev, "error searching for VLAN MC %d for VID %d\n", + dev_err(priv->dev, "error searching for VLAN MC %d for VID %d\n", i, vid); return ret; } @@ -65,19 +65,19 @@ static int rtl8366_obtain_mc(struct realtek_smi *smi, int vid, } /* We have no MC entry for this VID, try to find an empty one */ - for (i = 0; i < smi->num_vlan_mc; i++) { - ret = smi->ops->get_vlan_mc(smi, i, vlanmc); + for (i = 0; i < priv->num_vlan_mc; i++) { + ret = priv->ops->get_vlan_mc(priv, i, vlanmc); if (ret) { - dev_err(smi->dev, "error searching for VLAN MC %d for VID %d\n", + dev_err(priv->dev, "error searching for VLAN MC %d for VID %d\n", i, vid); return ret; } if (vlanmc->vid == 0 && vlanmc->member == 0) { /* Update the entry from the 4K table */ - ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); + ret = priv->ops->get_vlan_4k(priv, vid, &vlan4k); if (ret) { - dev_err(smi->dev, "error looking for 4K VLAN MC %d for VID %d\n", + dev_err(priv->dev, "error looking for 4K VLAN MC %d for VID %d\n", i, vid); return ret; } @@ -86,30 +86,30 @@ static int rtl8366_obtain_mc(struct realtek_smi *smi, int vid, vlanmc->member = vlan4k.member; vlanmc->untag = vlan4k.untag; vlanmc->fid = vlan4k.fid; - ret = smi->ops->set_vlan_mc(smi, i, vlanmc); + ret = priv->ops->set_vlan_mc(priv, i, vlanmc); if (ret) { - dev_err(smi->dev, "unable to set/update VLAN MC %d for VID %d\n", + dev_err(priv->dev, "unable to set/update VLAN MC %d for VID %d\n", i, vid); return ret; } - dev_dbg(smi->dev, "created new MC at index %d for VID %d\n", + dev_dbg(priv->dev, "created new MC at index %d for VID %d\n", i, vid); return i; } } /* MC table is full, try to find an unused entry and replace it */ - for (i = 0; i < smi->num_vlan_mc; i++) { + for (i = 0; i < priv->num_vlan_mc; i++) { int used; - ret = rtl8366_mc_is_used(smi, i, &used); + ret = rtl8366_mc_is_used(priv, i, &used); if (ret) return ret; if (!used) { /* Update the entry from the 4K table */ - ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); + ret = priv->ops->get_vlan_4k(priv, vid, &vlan4k); if (ret) return ret; @@ -117,23 +117,23 @@ static int rtl8366_obtain_mc(struct realtek_smi *smi, int vid, vlanmc->member = vlan4k.member; vlanmc->untag = vlan4k.untag; vlanmc->fid = vlan4k.fid; - ret = smi->ops->set_vlan_mc(smi, i, vlanmc); + ret = priv->ops->set_vlan_mc(priv, i, vlanmc); if (ret) { - dev_err(smi->dev, "unable to set/update VLAN MC %d for VID %d\n", + dev_err(priv->dev, "unable to set/update VLAN MC %d for VID %d\n", i, vid); return ret; } - dev_dbg(smi->dev, "recycled MC at index %i for VID %d\n", + dev_dbg(priv->dev, "recycled MC at index %i for VID %d\n", i, vid); return i; } } - dev_err(smi->dev, "all VLAN member configurations are in use\n"); + dev_err(priv->dev, "all VLAN member configurations are in use\n"); return -ENOSPC; } -int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, +int rtl8366_set_vlan(struct realtek_priv *priv, int vid, u32 member, u32 untag, u32 fid) { struct rtl8366_vlan_mc vlanmc; @@ -141,31 +141,31 @@ int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, int mc; int ret; - if (!smi->ops->is_vlan_valid(smi, vid)) + if (!priv->ops->is_vlan_valid(priv, vid)) return -EINVAL; - dev_dbg(smi->dev, + dev_dbg(priv->dev, "setting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n", vid, member, untag); /* Update the 4K table */ - ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); + ret = priv->ops->get_vlan_4k(priv, vid, &vlan4k); if (ret) return ret; vlan4k.member |= member; vlan4k.untag |= untag; vlan4k.fid = fid; - ret = smi->ops->set_vlan_4k(smi, &vlan4k); + ret = priv->ops->set_vlan_4k(priv, &vlan4k); if (ret) return ret; - dev_dbg(smi->dev, + dev_dbg(priv->dev, "resulting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n", vid, vlan4k.member, vlan4k.untag); /* Find or allocate a member config for this VID */ - ret = rtl8366_obtain_mc(smi, vid, &vlanmc); + ret = rtl8366_obtain_mc(priv, vid, &vlanmc); if (ret < 0) return ret; mc = ret; @@ -176,12 +176,12 @@ int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, vlanmc.fid = fid; /* Commit updates to the MC entry */ - ret = smi->ops->set_vlan_mc(smi, mc, &vlanmc); + ret = priv->ops->set_vlan_mc(priv, mc, &vlanmc); if (ret) - dev_err(smi->dev, "failed to commit changes to VLAN MC index %d for VID %d\n", + dev_err(priv->dev, "failed to commit changes to VLAN MC index %d for VID %d\n", mc, vid); else - dev_dbg(smi->dev, + dev_dbg(priv->dev, "resulting VLAN%d MC members: 0x%02x, untagged: 0x%02x\n", vid, vlanmc.member, vlanmc.untag); @@ -189,37 +189,37 @@ int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, } EXPORT_SYMBOL_GPL(rtl8366_set_vlan); -int rtl8366_set_pvid(struct realtek_smi *smi, unsigned int port, +int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port, unsigned int vid) { struct rtl8366_vlan_mc vlanmc; int mc; int ret; - if (!smi->ops->is_vlan_valid(smi, vid)) + if (!priv->ops->is_vlan_valid(priv, vid)) return -EINVAL; /* Find or allocate a member config for this VID */ - ret = rtl8366_obtain_mc(smi, vid, &vlanmc); + ret = rtl8366_obtain_mc(priv, vid, &vlanmc); if (ret < 0) return ret; mc = ret; - ret = smi->ops->set_mc_index(smi, port, mc); + ret = priv->ops->set_mc_index(priv, port, mc); if (ret) { - dev_err(smi->dev, "set PVID: failed to set MC index %d for port %d\n", + dev_err(priv->dev, "set PVID: failed to set MC index %d for port %d\n", mc, port); return ret; } - dev_dbg(smi->dev, "set PVID: the PVID for port %d set to %d using existing MC index %d\n", + dev_dbg(priv->dev, "set PVID: the PVID for port %d set to %d using existing MC index %d\n", port, vid, mc); return 0; } EXPORT_SYMBOL_GPL(rtl8366_set_pvid); -int rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable) +int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable) { int ret; @@ -229,52 +229,52 @@ int rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable) */ if (enable) { /* Make sure VLAN is ON */ - ret = smi->ops->enable_vlan(smi, true); + ret = priv->ops->enable_vlan(priv, true); if (ret) return ret; - smi->vlan_enabled = true; + priv->vlan_enabled = true; } - ret = smi->ops->enable_vlan4k(smi, enable); + ret = priv->ops->enable_vlan4k(priv, enable); if (ret) return ret; - smi->vlan4k_enabled = enable; + priv->vlan4k_enabled = enable; return 0; } EXPORT_SYMBOL_GPL(rtl8366_enable_vlan4k); -int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable) +int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable) { int ret; - ret = smi->ops->enable_vlan(smi, enable); + ret = priv->ops->enable_vlan(priv, enable); if (ret) return ret; - smi->vlan_enabled = enable; + priv->vlan_enabled = enable; /* If we turn VLAN off, make sure that we turn off * 4k VLAN as well, if that happened to be on. */ if (!enable) { - smi->vlan4k_enabled = false; - ret = smi->ops->enable_vlan4k(smi, false); + priv->vlan4k_enabled = false; + ret = priv->ops->enable_vlan4k(priv, false); } return ret; } EXPORT_SYMBOL_GPL(rtl8366_enable_vlan); -int rtl8366_reset_vlan(struct realtek_smi *smi) +int rtl8366_reset_vlan(struct realtek_priv *priv) { struct rtl8366_vlan_mc vlanmc; int ret; int i; - rtl8366_enable_vlan(smi, false); - rtl8366_enable_vlan4k(smi, false); + rtl8366_enable_vlan(priv, false); + rtl8366_enable_vlan4k(priv, false); /* Clear the 16 VLAN member configurations */ vlanmc.vid = 0; @@ -282,8 +282,8 @@ int rtl8366_reset_vlan(struct realtek_smi *smi) vlanmc.member = 0; vlanmc.untag = 0; vlanmc.fid = 0; - for (i = 0; i < smi->num_vlan_mc; i++) { - ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); + for (i = 0; i < priv->num_vlan_mc; i++) { + ret = priv->ops->set_vlan_mc(priv, i, &vlanmc); if (ret) return ret; } @@ -298,12 +298,12 @@ int rtl8366_vlan_add(struct dsa_switch *ds, int port, { bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID); - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; u32 member = 0; u32 untag = 0; int ret; - if (!smi->ops->is_vlan_valid(smi, vlan->vid)) { + if (!priv->ops->is_vlan_valid(priv, vlan->vid)) { NL_SET_ERR_MSG_MOD(extack, "VLAN ID not valid"); return -EINVAL; } @@ -312,13 +312,13 @@ int rtl8366_vlan_add(struct dsa_switch *ds, int port, * FIXME: what's with this 4k business? * Just rtl8366_enable_vlan() seems inconclusive. */ - ret = rtl8366_enable_vlan4k(smi, true); + ret = rtl8366_enable_vlan4k(priv, true); if (ret) { NL_SET_ERR_MSG_MOD(extack, "Failed to enable VLAN 4K"); return ret; } - dev_dbg(smi->dev, "add VLAN %d on port %d, %s, %s\n", + dev_dbg(priv->dev, "add VLAN %d on port %d, %s, %s\n", vlan->vid, port, untagged ? "untagged" : "tagged", pvid ? "PVID" : "no PVID"); @@ -327,18 +327,18 @@ int rtl8366_vlan_add(struct dsa_switch *ds, int port, if (untagged) untag |= BIT(port); - ret = rtl8366_set_vlan(smi, vlan->vid, member, untag, 0); + ret = rtl8366_set_vlan(priv, vlan->vid, member, untag, 0); if (ret) { - dev_err(smi->dev, "failed to set up VLAN %04x", vlan->vid); + dev_err(priv->dev, "failed to set up VLAN %04x", vlan->vid); return ret; } if (!pvid) return 0; - ret = rtl8366_set_pvid(smi, port, vlan->vid); + ret = rtl8366_set_pvid(priv, port, vlan->vid); if (ret) { - dev_err(smi->dev, "failed to set PVID on port %d to VLAN %04x", + dev_err(priv->dev, "failed to set PVID on port %d to VLAN %04x", port, vlan->vid); return ret; } @@ -350,15 +350,15 @@ EXPORT_SYMBOL_GPL(rtl8366_vlan_add); int rtl8366_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret, i; - dev_dbg(smi->dev, "del VLAN %d on port %d\n", vlan->vid, port); + dev_dbg(priv->dev, "del VLAN %d on port %d\n", vlan->vid, port); - for (i = 0; i < smi->num_vlan_mc; i++) { + for (i = 0; i < priv->num_vlan_mc; i++) { struct rtl8366_vlan_mc vlanmc; - ret = smi->ops->get_vlan_mc(smi, i, &vlanmc); + ret = priv->ops->get_vlan_mc(priv, i, &vlanmc); if (ret) return ret; @@ -376,9 +376,9 @@ int rtl8366_vlan_del(struct dsa_switch *ds, int port, vlanmc.priority = 0; vlanmc.fid = 0; } - ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); + ret = priv->ops->set_vlan_mc(priv, i, &vlanmc); if (ret) { - dev_err(smi->dev, + dev_err(priv->dev, "failed to remove VLAN %04x\n", vlan->vid); return ret; @@ -394,15 +394,15 @@ EXPORT_SYMBOL_GPL(rtl8366_vlan_del); void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8366_mib_counter *mib; int i; - if (port >= smi->num_ports) + if (port >= priv->num_ports) return; - for (i = 0; i < smi->num_mib_counters; i++) { - mib = &smi->mib_counters[i]; + for (i = 0; i < priv->num_mib_counters; i++) { + mib = &priv->mib_counters[i]; strncpy(data + i * ETH_GSTRING_LEN, mib->name, ETH_GSTRING_LEN); } @@ -411,35 +411,35 @@ EXPORT_SYMBOL_GPL(rtl8366_get_strings); int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; /* We only support SS_STATS */ if (sset != ETH_SS_STATS) return 0; - if (port >= smi->num_ports) + if (port >= priv->num_ports) return -EINVAL; - return smi->num_mib_counters; + return priv->num_mib_counters; } EXPORT_SYMBOL_GPL(rtl8366_get_sset_count); void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int i; int ret; - if (port >= smi->num_ports) + if (port >= priv->num_ports) return; - for (i = 0; i < smi->num_mib_counters; i++) { + for (i = 0; i < priv->num_mib_counters; i++) { struct rtl8366_mib_counter *mib; u64 mibvalue = 0; - mib = &smi->mib_counters[i]; - ret = smi->ops->get_mib_counter(smi, port, mib, &mibvalue); + mib = &priv->mib_counters[i]; + ret = priv->ops->get_mib_counter(priv, port, mib, &mibvalue); if (ret) { - dev_err(smi->dev, "error reading MIB counter %s\n", + dev_err(priv->dev, "error reading MIB counter %s\n", mib->name); } data[i] = mibvalue; diff --git a/drivers/net/dsa/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c similarity index 78% rename from drivers/net/dsa/rtl8366rb.c rename to drivers/net/dsa/realtek/rtl8366rb.c index ecc19bd5115f..1a3406b9e64c 100644 --- a/drivers/net/dsa/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -21,7 +21,7 @@ #include #include -#include "realtek-smi-core.h" +#include "realtek.h" #define RTL8366RB_PORT_NUM_CPU 5 #define RTL8366RB_NUM_PORTS 6 @@ -396,7 +396,7 @@ static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = { { 0, 70, 2, "IfOutBroadcastPkts" }, }; -static int rtl8366rb_get_mib_counter(struct realtek_smi *smi, +static int rtl8366rb_get_mib_counter(struct realtek_priv *priv, int port, struct rtl8366_mib_counter *mib, u64 *mibvalue) @@ -412,12 +412,12 @@ static int rtl8366rb_get_mib_counter(struct realtek_smi *smi, /* Writing access counter address first * then ASIC will prepare 64bits counter wait for being retrived */ - ret = regmap_write(smi->map, addr, 0); /* Write whatever */ + ret = regmap_write(priv->map, addr, 0); /* Write whatever */ if (ret) return ret; /* Read MIB control register */ - ret = regmap_read(smi->map, RTL8366RB_MIB_CTRL_REG, &val); + ret = regmap_read(priv->map, RTL8366RB_MIB_CTRL_REG, &val); if (ret) return -EIO; @@ -430,7 +430,7 @@ static int rtl8366rb_get_mib_counter(struct realtek_smi *smi, /* Read each individual MIB 16 bits at the time */ *mibvalue = 0; for (i = mib->length; i > 0; i--) { - ret = regmap_read(smi->map, addr + (i - 1), &val); + ret = regmap_read(priv->map, addr + (i - 1), &val); if (ret) return ret; *mibvalue = (*mibvalue << 16) | (val & 0xFFFF); @@ -455,38 +455,38 @@ static u32 rtl8366rb_get_irqmask(struct irq_data *d) static void rtl8366rb_mask_irq(struct irq_data *d) { - struct realtek_smi *smi = irq_data_get_irq_chip_data(d); + struct realtek_priv *priv = irq_data_get_irq_chip_data(d); int ret; - ret = regmap_update_bits(smi->map, RTL8366RB_INTERRUPT_MASK_REG, + ret = regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_MASK_REG, rtl8366rb_get_irqmask(d), 0); if (ret) - dev_err(smi->dev, "could not mask IRQ\n"); + dev_err(priv->dev, "could not mask IRQ\n"); } static void rtl8366rb_unmask_irq(struct irq_data *d) { - struct realtek_smi *smi = irq_data_get_irq_chip_data(d); + struct realtek_priv *priv = irq_data_get_irq_chip_data(d); int ret; - ret = regmap_update_bits(smi->map, RTL8366RB_INTERRUPT_MASK_REG, + ret = regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_MASK_REG, rtl8366rb_get_irqmask(d), rtl8366rb_get_irqmask(d)); if (ret) - dev_err(smi->dev, "could not unmask IRQ\n"); + dev_err(priv->dev, "could not unmask IRQ\n"); } static irqreturn_t rtl8366rb_irq(int irq, void *data) { - struct realtek_smi *smi = data; + struct realtek_priv *priv = data; u32 stat; int ret; /* This clears the IRQ status register */ - ret = regmap_read(smi->map, RTL8366RB_INTERRUPT_STATUS_REG, + ret = regmap_read(priv->map, RTL8366RB_INTERRUPT_STATUS_REG, &stat); if (ret) { - dev_err(smi->dev, "can't read interrupt status\n"); + dev_err(priv->dev, "can't read interrupt status\n"); return IRQ_NONE; } stat &= RTL8366RB_INTERRUPT_VALID; @@ -502,7 +502,7 @@ static irqreturn_t rtl8366rb_irq(int irq, void *data) */ if (line < 12 && line > 5) line -= 5; - child_irq = irq_find_mapping(smi->irqdomain, line); + child_irq = irq_find_mapping(priv->irqdomain, line); handle_nested_irq(child_irq); } return IRQ_HANDLED; @@ -538,7 +538,7 @@ static const struct irq_domain_ops rtl8366rb_irqdomain_ops = { .xlate = irq_domain_xlate_onecell, }; -static int rtl8366rb_setup_cascaded_irq(struct realtek_smi *smi) +static int rtl8366rb_setup_cascaded_irq(struct realtek_priv *priv) { struct device_node *intc; unsigned long irq_trig; @@ -547,24 +547,24 @@ static int rtl8366rb_setup_cascaded_irq(struct realtek_smi *smi) u32 val; int i; - intc = of_get_child_by_name(smi->dev->of_node, "interrupt-controller"); + intc = of_get_child_by_name(priv->dev->of_node, "interrupt-controller"); if (!intc) { - dev_err(smi->dev, "missing child interrupt-controller node\n"); + dev_err(priv->dev, "missing child interrupt-controller node\n"); return -EINVAL; } /* RB8366RB IRQs cascade off this one */ irq = of_irq_get(intc, 0); if (irq <= 0) { - dev_err(smi->dev, "failed to get parent IRQ\n"); + dev_err(priv->dev, "failed to get parent IRQ\n"); ret = irq ? irq : -EINVAL; goto out_put_node; } /* This clears the IRQ status register */ - ret = regmap_read(smi->map, RTL8366RB_INTERRUPT_STATUS_REG, + ret = regmap_read(priv->map, RTL8366RB_INTERRUPT_STATUS_REG, &val); if (ret) { - dev_err(smi->dev, "can't read interrupt status\n"); + dev_err(priv->dev, "can't read interrupt status\n"); goto out_put_node; } @@ -573,48 +573,48 @@ static int rtl8366rb_setup_cascaded_irq(struct realtek_smi *smi) switch (irq_trig) { case IRQF_TRIGGER_RISING: case IRQF_TRIGGER_HIGH: - dev_info(smi->dev, "active high/rising IRQ\n"); + dev_info(priv->dev, "active high/rising IRQ\n"); val = 0; break; case IRQF_TRIGGER_FALLING: case IRQF_TRIGGER_LOW: - dev_info(smi->dev, "active low/falling IRQ\n"); + dev_info(priv->dev, "active low/falling IRQ\n"); val = RTL8366RB_INTERRUPT_POLARITY; break; } - ret = regmap_update_bits(smi->map, RTL8366RB_INTERRUPT_CONTROL_REG, + ret = regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_CONTROL_REG, RTL8366RB_INTERRUPT_POLARITY, val); if (ret) { - dev_err(smi->dev, "could not configure IRQ polarity\n"); + dev_err(priv->dev, "could not configure IRQ polarity\n"); goto out_put_node; } - ret = devm_request_threaded_irq(smi->dev, irq, NULL, + ret = devm_request_threaded_irq(priv->dev, irq, NULL, rtl8366rb_irq, IRQF_ONESHOT, - "RTL8366RB", smi); + "RTL8366RB", priv); if (ret) { - dev_err(smi->dev, "unable to request irq: %d\n", ret); + dev_err(priv->dev, "unable to request irq: %d\n", ret); goto out_put_node; } - smi->irqdomain = irq_domain_add_linear(intc, - RTL8366RB_NUM_INTERRUPT, - &rtl8366rb_irqdomain_ops, - smi); - if (!smi->irqdomain) { - dev_err(smi->dev, "failed to create IRQ domain\n"); + priv->irqdomain = irq_domain_add_linear(intc, + RTL8366RB_NUM_INTERRUPT, + &rtl8366rb_irqdomain_ops, + priv); + if (!priv->irqdomain) { + dev_err(priv->dev, "failed to create IRQ domain\n"); ret = -EINVAL; goto out_put_node; } - for (i = 0; i < smi->num_ports; i++) - irq_set_parent(irq_create_mapping(smi->irqdomain, i), irq); + for (i = 0; i < priv->num_ports; i++) + irq_set_parent(irq_create_mapping(priv->irqdomain, i), irq); out_put_node: of_node_put(intc); return ret; } -static int rtl8366rb_set_addr(struct realtek_smi *smi) +static int rtl8366rb_set_addr(struct realtek_priv *priv) { u8 addr[ETH_ALEN]; u16 val; @@ -622,18 +622,18 @@ static int rtl8366rb_set_addr(struct realtek_smi *smi) eth_random_addr(addr); - dev_info(smi->dev, "set MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", + dev_info(priv->dev, "set MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); val = addr[0] << 8 | addr[1]; - ret = regmap_write(smi->map, RTL8366RB_SMAR0, val); + ret = regmap_write(priv->map, RTL8366RB_SMAR0, val); if (ret) return ret; val = addr[2] << 8 | addr[3]; - ret = regmap_write(smi->map, RTL8366RB_SMAR1, val); + ret = regmap_write(priv->map, RTL8366RB_SMAR1, val); if (ret) return ret; val = addr[4] << 8 | addr[5]; - ret = regmap_write(smi->map, RTL8366RB_SMAR2, val); + ret = regmap_write(priv->map, RTL8366RB_SMAR2, val); if (ret) return ret; @@ -765,7 +765,7 @@ static const struct rtl8366rb_jam_tbl_entry rtl8366rb_green_jam[] = { /* Function that jams the tables in the proper registers */ static int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table, - int jam_size, struct realtek_smi *smi, + int jam_size, struct realtek_priv *priv, bool write_dbg) { u32 val; @@ -774,24 +774,24 @@ static int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table, for (i = 0; i < jam_size; i++) { if ((jam_table[i].reg & 0xBE00) == 0xBE00) { - ret = regmap_read(smi->map, + ret = regmap_read(priv->map, RTL8366RB_PHY_ACCESS_BUSY_REG, &val); if (ret) return ret; if (!(val & RTL8366RB_PHY_INT_BUSY)) { - ret = regmap_write(smi->map, - RTL8366RB_PHY_ACCESS_CTRL_REG, - RTL8366RB_PHY_CTRL_WRITE); + ret = regmap_write(priv->map, + RTL8366RB_PHY_ACCESS_CTRL_REG, + RTL8366RB_PHY_CTRL_WRITE); if (ret) return ret; } } if (write_dbg) - dev_dbg(smi->dev, "jam %04x into register %04x\n", + dev_dbg(priv->dev, "jam %04x into register %04x\n", jam_table[i].val, jam_table[i].reg); - ret = regmap_write(smi->map, + ret = regmap_write(priv->map, jam_table[i].reg, jam_table[i].val); if (ret) @@ -802,7 +802,7 @@ static int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table, static int rtl8366rb_setup(struct dsa_switch *ds) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; const struct rtl8366rb_jam_tbl_entry *jam_table; struct rtl8366rb *rb; u32 chip_ver = 0; @@ -812,11 +812,11 @@ static int rtl8366rb_setup(struct dsa_switch *ds) int ret; int i; - rb = smi->chip_data; + rb = priv->chip_data; - ret = regmap_read(smi->map, RTL8366RB_CHIP_ID_REG, &chip_id); + ret = regmap_read(priv->map, RTL8366RB_CHIP_ID_REG, &chip_id); if (ret) { - dev_err(smi->dev, "unable to read chip id\n"); + dev_err(priv->dev, "unable to read chip id\n"); return ret; } @@ -824,18 +824,18 @@ static int rtl8366rb_setup(struct dsa_switch *ds) case RTL8366RB_CHIP_ID_8366: break; default: - dev_err(smi->dev, "unknown chip id (%04x)\n", chip_id); + dev_err(priv->dev, "unknown chip id (%04x)\n", chip_id); return -ENODEV; } - ret = regmap_read(smi->map, RTL8366RB_CHIP_VERSION_CTRL_REG, + ret = regmap_read(priv->map, RTL8366RB_CHIP_VERSION_CTRL_REG, &chip_ver); if (ret) { - dev_err(smi->dev, "unable to read chip version\n"); + dev_err(priv->dev, "unable to read chip version\n"); return ret; } - dev_info(smi->dev, "RTL%04x ver %u chip found\n", + dev_info(priv->dev, "RTL%04x ver %u chip found\n", chip_id, chip_ver & RTL8366RB_CHIP_VERSION_MASK); /* Do the init dance using the right jam table */ @@ -872,20 +872,20 @@ static int rtl8366rb_setup(struct dsa_switch *ds) jam_size = ARRAY_SIZE(rtl8366rb_init_jam_dgn3500); } - ret = rtl8366rb_jam_table(jam_table, jam_size, smi, true); + ret = rtl8366rb_jam_table(jam_table, jam_size, priv, true); if (ret) return ret; /* Isolate all user ports so they can only send packets to itself and the CPU port */ for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) { - ret = regmap_write(smi->map, RTL8366RB_PORT_ISO(i), + ret = regmap_write(priv->map, RTL8366RB_PORT_ISO(i), RTL8366RB_PORT_ISO_PORTS(BIT(RTL8366RB_PORT_NUM_CPU)) | RTL8366RB_PORT_ISO_EN); if (ret) return ret; } /* CPU port can send packets to all ports */ - ret = regmap_write(smi->map, RTL8366RB_PORT_ISO(RTL8366RB_PORT_NUM_CPU), + ret = regmap_write(priv->map, RTL8366RB_PORT_ISO(RTL8366RB_PORT_NUM_CPU), RTL8366RB_PORT_ISO_PORTS(dsa_user_ports(ds)) | RTL8366RB_PORT_ISO_EN); if (ret) @@ -893,26 +893,26 @@ static int rtl8366rb_setup(struct dsa_switch *ds) /* Set up the "green ethernet" feature */ ret = rtl8366rb_jam_table(rtl8366rb_green_jam, - ARRAY_SIZE(rtl8366rb_green_jam), smi, false); + ARRAY_SIZE(rtl8366rb_green_jam), priv, false); if (ret) return ret; - ret = regmap_write(smi->map, + ret = regmap_write(priv->map, RTL8366RB_GREEN_FEATURE_REG, (chip_ver == 1) ? 0x0007 : 0x0003); if (ret) return ret; /* Vendor driver sets 0x240 in registers 0xc and 0xd (undocumented) */ - ret = regmap_write(smi->map, 0x0c, 0x240); + ret = regmap_write(priv->map, 0x0c, 0x240); if (ret) return ret; - ret = regmap_write(smi->map, 0x0d, 0x240); + ret = regmap_write(priv->map, 0x0d, 0x240); if (ret) return ret; /* Set some random MAC address */ - ret = rtl8366rb_set_addr(smi); + ret = rtl8366rb_set_addr(priv); if (ret) return ret; @@ -921,21 +921,21 @@ static int rtl8366rb_setup(struct dsa_switch *ds) * If you set RTL8368RB_CPU_NO_TAG (bit 15) in this registers * the custom tag is turned off. */ - ret = regmap_update_bits(smi->map, RTL8368RB_CPU_CTRL_REG, + ret = regmap_update_bits(priv->map, RTL8368RB_CPU_CTRL_REG, 0xFFFF, - BIT(smi->cpu_port)); + BIT(priv->cpu_port)); if (ret) return ret; /* Make sure we default-enable the fixed CPU port */ - ret = regmap_update_bits(smi->map, RTL8366RB_PECR, - BIT(smi->cpu_port), + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, + BIT(priv->cpu_port), 0); if (ret) return ret; /* Set maximum packet length to 1536 bytes */ - ret = regmap_update_bits(smi->map, RTL8366RB_SGCR, + ret = regmap_update_bits(priv->map, RTL8366RB_SGCR, RTL8366RB_SGCR_MAX_LENGTH_MASK, RTL8366RB_SGCR_MAX_LENGTH_1536); if (ret) @@ -945,13 +945,13 @@ static int rtl8366rb_setup(struct dsa_switch *ds) rb->max_mtu[i] = 1532; /* Disable learning for all ports */ - ret = regmap_write(smi->map, RTL8366RB_PORT_LEARNDIS_CTRL, + ret = regmap_write(priv->map, RTL8366RB_PORT_LEARNDIS_CTRL, RTL8366RB_PORT_ALL); if (ret) return ret; /* Enable auto ageing for all ports */ - ret = regmap_write(smi->map, RTL8366RB_SECURITY_CTRL, 0); + ret = regmap_write(priv->map, RTL8366RB_SECURITY_CTRL, 0); if (ret) return ret; @@ -962,30 +962,30 @@ static int rtl8366rb_setup(struct dsa_switch *ds) * connected to something exotic such as fiber, then this might * be worth experimenting with. */ - ret = regmap_update_bits(smi->map, RTL8366RB_PMC0, + ret = regmap_update_bits(priv->map, RTL8366RB_PMC0, RTL8366RB_PMC0_P4_IOMODE_MASK, 0 << RTL8366RB_PMC0_P4_IOMODE_SHIFT); if (ret) return ret; /* Accept all packets by default, we enable filtering on-demand */ - ret = regmap_write(smi->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, + ret = regmap_write(priv->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, 0); if (ret) return ret; - ret = regmap_write(smi->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, + ret = regmap_write(priv->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, 0); if (ret) return ret; /* Don't drop packets whose DA has not been learned */ - ret = regmap_update_bits(smi->map, RTL8366RB_SSCR2, + ret = regmap_update_bits(priv->map, RTL8366RB_SSCR2, RTL8366RB_SSCR2_DROP_UNKNOWN_DA, 0); if (ret) return ret; /* Set blinking, TODO: make this configurable */ - ret = regmap_update_bits(smi->map, RTL8366RB_LED_BLINKRATE_REG, + ret = regmap_update_bits(priv->map, RTL8366RB_LED_BLINKRATE_REG, RTL8366RB_LED_BLINKRATE_MASK, RTL8366RB_LED_BLINKRATE_56MS); if (ret) @@ -996,15 +996,15 @@ static int rtl8366rb_setup(struct dsa_switch *ds) * behaviour (no individual config) but we can set up each * LED separately. */ - if (smi->leds_disabled) { + if (priv->leds_disabled) { /* Turn everything off */ - regmap_update_bits(smi->map, + regmap_update_bits(priv->map, RTL8366RB_LED_0_1_CTRL_REG, 0x0FFF, 0); - regmap_update_bits(smi->map, + regmap_update_bits(priv->map, RTL8366RB_LED_2_3_CTRL_REG, 0x0FFF, 0); - regmap_update_bits(smi->map, + regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_CONTROL_REG, RTL8366RB_P4_RGMII_LED, 0); @@ -1014,7 +1014,7 @@ static int rtl8366rb_setup(struct dsa_switch *ds) val = RTL8366RB_LED_FORCE; } for (i = 0; i < 4; i++) { - ret = regmap_update_bits(smi->map, + ret = regmap_update_bits(priv->map, RTL8366RB_LED_CTRL_REG, 0xf << (i * 4), val << (i * 4)); @@ -1022,18 +1022,20 @@ static int rtl8366rb_setup(struct dsa_switch *ds) return ret; } - ret = rtl8366_reset_vlan(smi); + ret = rtl8366_reset_vlan(priv); if (ret) return ret; - ret = rtl8366rb_setup_cascaded_irq(smi); + ret = rtl8366rb_setup_cascaded_irq(priv); if (ret) - dev_info(smi->dev, "no interrupt support\n"); + dev_info(priv->dev, "no interrupt support\n"); - ret = realtek_smi_setup_mdio(smi); - if (ret) { - dev_info(smi->dev, "could not set up MDIO bus\n"); - return -ENODEV; + if (priv->setup_interface) { + ret = priv->setup_interface(ds); + if (ret) { + dev_err(priv->dev, "could not set up MDIO bus\n"); + return -ENODEV; + } } return 0; @@ -1052,35 +1054,35 @@ rtl8366rb_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, struct phy_device *phydev, int speed, int duplex, bool tx_pause, bool rx_pause) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret; - if (port != smi->cpu_port) + if (port != priv->cpu_port) return; - dev_dbg(smi->dev, "MAC link up on CPU port (%d)\n", port); + dev_dbg(priv->dev, "MAC link up on CPU port (%d)\n", port); /* Force the fixed CPU port into 1Gbit mode, no autonegotiation */ - ret = regmap_update_bits(smi->map, RTL8366RB_MAC_FORCE_CTRL_REG, + ret = regmap_update_bits(priv->map, RTL8366RB_MAC_FORCE_CTRL_REG, BIT(port), BIT(port)); if (ret) { - dev_err(smi->dev, "failed to force 1Gbit on CPU port\n"); + dev_err(priv->dev, "failed to force 1Gbit on CPU port\n"); return; } - ret = regmap_update_bits(smi->map, RTL8366RB_PAACR2, + ret = regmap_update_bits(priv->map, RTL8366RB_PAACR2, 0xFF00U, RTL8366RB_PAACR_CPU_PORT << 8); if (ret) { - dev_err(smi->dev, "failed to set PAACR on CPU port\n"); + dev_err(priv->dev, "failed to set PAACR on CPU port\n"); return; } /* Enable the CPU port */ - ret = regmap_update_bits(smi->map, RTL8366RB_PECR, BIT(port), + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), 0); if (ret) { - dev_err(smi->dev, "failed to enable the CPU port\n"); + dev_err(priv->dev, "failed to enable the CPU port\n"); return; } } @@ -1089,107 +1091,108 @@ static void rtl8366rb_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret; - if (port != smi->cpu_port) + if (port != priv->cpu_port) return; - dev_dbg(smi->dev, "MAC link down on CPU port (%d)\n", port); + dev_dbg(priv->dev, "MAC link down on CPU port (%d)\n", port); /* Disable the CPU port */ - ret = regmap_update_bits(smi->map, RTL8366RB_PECR, BIT(port), + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), BIT(port)); if (ret) { - dev_err(smi->dev, "failed to disable the CPU port\n"); + dev_err(priv->dev, "failed to disable the CPU port\n"); return; } } -static void rb8366rb_set_port_led(struct realtek_smi *smi, +static void rb8366rb_set_port_led(struct realtek_priv *priv, int port, bool enable) { u16 val = enable ? 0x3f : 0; int ret; - if (smi->leds_disabled) + if (priv->leds_disabled) return; switch (port) { case 0: - ret = regmap_update_bits(smi->map, + ret = regmap_update_bits(priv->map, RTL8366RB_LED_0_1_CTRL_REG, 0x3F, val); break; case 1: - ret = regmap_update_bits(smi->map, + ret = regmap_update_bits(priv->map, RTL8366RB_LED_0_1_CTRL_REG, 0x3F << RTL8366RB_LED_1_OFFSET, val << RTL8366RB_LED_1_OFFSET); break; case 2: - ret = regmap_update_bits(smi->map, + ret = regmap_update_bits(priv->map, RTL8366RB_LED_2_3_CTRL_REG, 0x3F, val); break; case 3: - ret = regmap_update_bits(smi->map, + ret = regmap_update_bits(priv->map, RTL8366RB_LED_2_3_CTRL_REG, 0x3F << RTL8366RB_LED_3_OFFSET, val << RTL8366RB_LED_3_OFFSET); break; case 4: - ret = regmap_update_bits(smi->map, + ret = regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_CONTROL_REG, RTL8366RB_P4_RGMII_LED, enable ? RTL8366RB_P4_RGMII_LED : 0); break; default: - dev_err(smi->dev, "no LED for port %d\n", port); + dev_err(priv->dev, "no LED for port %d\n", port); return; } if (ret) - dev_err(smi->dev, "error updating LED on port %d\n", port); + dev_err(priv->dev, "error updating LED on port %d\n", port); } static int rtl8366rb_port_enable(struct dsa_switch *ds, int port, struct phy_device *phy) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret; - dev_dbg(smi->dev, "enable port %d\n", port); - ret = regmap_update_bits(smi->map, RTL8366RB_PECR, BIT(port), + dev_dbg(priv->dev, "enable port %d\n", port); + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), 0); if (ret) return ret; - rb8366rb_set_port_led(smi, port, true); + rb8366rb_set_port_led(priv, port, true); return 0; } static void rtl8366rb_port_disable(struct dsa_switch *ds, int port) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret; - dev_dbg(smi->dev, "disable port %d\n", port); - ret = regmap_update_bits(smi->map, RTL8366RB_PECR, BIT(port), + dev_dbg(priv->dev, "disable port %d\n", port); + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), BIT(port)); if (ret) return; - rb8366rb_set_port_led(smi, port, false); + rb8366rb_set_port_led(priv, port, false); } static int rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, - bool *tx_fwd_offload) + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; unsigned int port_bitmap = 0; int ret, i; @@ -1202,17 +1205,17 @@ rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port, if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) continue; /* Join this port to each other port on the bridge */ - ret = regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(i), + ret = regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(i), RTL8366RB_PORT_ISO_PORTS(BIT(port)), RTL8366RB_PORT_ISO_PORTS(BIT(port))); if (ret) - dev_err(smi->dev, "failed to join port %d\n", port); + dev_err(priv->dev, "failed to join port %d\n", port); port_bitmap |= BIT(i); } /* Set the bits for the ports we can access */ - return regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(port), + return regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(port), RTL8366RB_PORT_ISO_PORTS(port_bitmap), RTL8366RB_PORT_ISO_PORTS(port_bitmap)); } @@ -1221,7 +1224,7 @@ static void rtl8366rb_port_bridge_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; unsigned int port_bitmap = 0; int ret, i; @@ -1234,28 +1237,30 @@ rtl8366rb_port_bridge_leave(struct dsa_switch *ds, int port, if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) continue; /* Remove this port from any other port on the bridge */ - ret = regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(i), + ret = regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(i), RTL8366RB_PORT_ISO_PORTS(BIT(port)), 0); if (ret) - dev_err(smi->dev, "failed to leave port %d\n", port); + dev_err(priv->dev, "failed to leave port %d\n", port); port_bitmap |= BIT(i); } /* Clear the bits for the ports we can not access, leave ourselves */ - regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(port), + regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(port), RTL8366RB_PORT_ISO_PORTS(port_bitmap), 0); } /** * rtl8366rb_drop_untagged() - make the switch drop untagged and C-tagged frames - * @smi: SMI state container + * @priv: SMI state container * @port: the port to drop untagged and C-tagged frames on * @drop: whether to drop or pass untagged and C-tagged frames + * + * Return: zero for success, a negative number on error. */ -static int rtl8366rb_drop_untagged(struct realtek_smi *smi, int port, bool drop) +static int rtl8366rb_drop_untagged(struct realtek_priv *priv, int port, bool drop) { - return regmap_update_bits(smi->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, + return regmap_update_bits(priv->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port), drop ? RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port) : 0); } @@ -1264,17 +1269,17 @@ static int rtl8366rb_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, struct netlink_ext_ack *extack) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8366rb *rb; int ret; - rb = smi->chip_data; + rb = priv->chip_data; - dev_dbg(smi->dev, "port %d: %s VLAN filtering\n", port, + dev_dbg(priv->dev, "port %d: %s VLAN filtering\n", port, vlan_filtering ? "enable" : "disable"); /* If the port is not in the member set, the frame will be dropped */ - ret = regmap_update_bits(smi->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, + ret = regmap_update_bits(priv->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, BIT(port), vlan_filtering ? BIT(port) : 0); if (ret) return ret; @@ -1284,9 +1289,9 @@ static int rtl8366rb_vlan_filtering(struct dsa_switch *ds, int port, * filtering on a port, we need to accept any frames. */ if (vlan_filtering) - ret = rtl8366rb_drop_untagged(smi, port, !rb->pvid_enabled[port]); + ret = rtl8366rb_drop_untagged(priv, port, !rb->pvid_enabled[port]); else - ret = rtl8366rb_drop_untagged(smi, port, false); + ret = rtl8366rb_drop_untagged(priv, port, false); return ret; } @@ -1308,11 +1313,11 @@ rtl8366rb_port_bridge_flags(struct dsa_switch *ds, int port, struct switchdev_brport_flags flags, struct netlink_ext_ack *extack) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret; if (flags.mask & BR_LEARNING) { - ret = regmap_update_bits(smi->map, RTL8366RB_PORT_LEARNDIS_CTRL, + ret = regmap_update_bits(priv->map, RTL8366RB_PORT_LEARNDIS_CTRL, BIT(port), (flags.val & BR_LEARNING) ? 0 : BIT(port)); if (ret) @@ -1325,7 +1330,7 @@ rtl8366rb_port_bridge_flags(struct dsa_switch *ds, int port, static void rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; u32 val; int i; @@ -1344,13 +1349,13 @@ rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) val = RTL8366RB_STP_STATE_FORWARDING; break; default: - dev_err(smi->dev, "unknown bridge state requested\n"); + dev_err(priv->dev, "unknown bridge state requested\n"); return; } /* Set the same status for the port on all the FIDs */ for (i = 0; i < RTL8366RB_NUM_FIDS; i++) { - regmap_update_bits(smi->map, RTL8366RB_STP_STATE_BASE + i, + regmap_update_bits(priv->map, RTL8366RB_STP_STATE_BASE + i, RTL8366RB_STP_STATE_MASK(port), RTL8366RB_STP_STATE(port, val)); } @@ -1359,26 +1364,26 @@ rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) static void rtl8366rb_port_fast_age(struct dsa_switch *ds, int port) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; /* This will age out any learned L2 entries */ - regmap_update_bits(smi->map, RTL8366RB_SECURITY_CTRL, + regmap_update_bits(priv->map, RTL8366RB_SECURITY_CTRL, BIT(port), BIT(port)); /* Restore the normal state of things */ - regmap_update_bits(smi->map, RTL8366RB_SECURITY_CTRL, + regmap_update_bits(priv->map, RTL8366RB_SECURITY_CTRL, BIT(port), 0); } static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8366rb *rb; unsigned int max_mtu; u32 len; int i; /* Cache the per-port MTU setting */ - rb = smi->chip_data; + rb = priv->chip_data; rb->max_mtu[port] = new_mtu; /* Roof out the MTU for the entire switch to the greatest @@ -1406,7 +1411,7 @@ static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu) else len = RTL8366RB_SGCR_MAX_LENGTH_16000; - return regmap_update_bits(smi->map, RTL8366RB_SGCR, + return regmap_update_bits(priv->map, RTL8366RB_SGCR, RTL8366RB_SGCR_MAX_LENGTH_MASK, len); } @@ -1419,7 +1424,7 @@ static int rtl8366rb_max_mtu(struct dsa_switch *ds, int port) return 15996; } -static int rtl8366rb_get_vlan_4k(struct realtek_smi *smi, u32 vid, +static int rtl8366rb_get_vlan_4k(struct realtek_priv *priv, u32 vid, struct rtl8366_vlan_4k *vlan4k) { u32 data[3]; @@ -1432,19 +1437,19 @@ static int rtl8366rb_get_vlan_4k(struct realtek_smi *smi, u32 vid, return -EINVAL; /* write VID */ - ret = regmap_write(smi->map, RTL8366RB_VLAN_TABLE_WRITE_BASE, + ret = regmap_write(priv->map, RTL8366RB_VLAN_TABLE_WRITE_BASE, vid & RTL8366RB_VLAN_VID_MASK); if (ret) return ret; /* write table access control word */ - ret = regmap_write(smi->map, RTL8366RB_TABLE_ACCESS_CTRL_REG, + ret = regmap_write(priv->map, RTL8366RB_TABLE_ACCESS_CTRL_REG, RTL8366RB_TABLE_VLAN_READ_CTRL); if (ret) return ret; for (i = 0; i < 3; i++) { - ret = regmap_read(smi->map, + ret = regmap_read(priv->map, RTL8366RB_VLAN_TABLE_READ_BASE + i, &data[i]); if (ret) @@ -1460,7 +1465,7 @@ static int rtl8366rb_get_vlan_4k(struct realtek_smi *smi, u32 vid, return 0; } -static int rtl8366rb_set_vlan_4k(struct realtek_smi *smi, +static int rtl8366rb_set_vlan_4k(struct realtek_priv *priv, const struct rtl8366_vlan_4k *vlan4k) { u32 data[3]; @@ -1480,7 +1485,7 @@ static int rtl8366rb_set_vlan_4k(struct realtek_smi *smi, data[2] = vlan4k->fid & RTL8366RB_VLAN_FID_MASK; for (i = 0; i < 3; i++) { - ret = regmap_write(smi->map, + ret = regmap_write(priv->map, RTL8366RB_VLAN_TABLE_WRITE_BASE + i, data[i]); if (ret) @@ -1488,13 +1493,13 @@ static int rtl8366rb_set_vlan_4k(struct realtek_smi *smi, } /* write table access control word */ - ret = regmap_write(smi->map, RTL8366RB_TABLE_ACCESS_CTRL_REG, + ret = regmap_write(priv->map, RTL8366RB_TABLE_ACCESS_CTRL_REG, RTL8366RB_TABLE_VLAN_WRITE_CTRL); return ret; } -static int rtl8366rb_get_vlan_mc(struct realtek_smi *smi, u32 index, +static int rtl8366rb_get_vlan_mc(struct realtek_priv *priv, u32 index, struct rtl8366_vlan_mc *vlanmc) { u32 data[3]; @@ -1507,7 +1512,7 @@ static int rtl8366rb_get_vlan_mc(struct realtek_smi *smi, u32 index, return -EINVAL; for (i = 0; i < 3; i++) { - ret = regmap_read(smi->map, + ret = regmap_read(priv->map, RTL8366RB_VLAN_MC_BASE(index) + i, &data[i]); if (ret) @@ -1525,7 +1530,7 @@ static int rtl8366rb_get_vlan_mc(struct realtek_smi *smi, u32 index, return 0; } -static int rtl8366rb_set_vlan_mc(struct realtek_smi *smi, u32 index, +static int rtl8366rb_set_vlan_mc(struct realtek_priv *priv, u32 index, const struct rtl8366_vlan_mc *vlanmc) { u32 data[3]; @@ -1549,7 +1554,7 @@ static int rtl8366rb_set_vlan_mc(struct realtek_smi *smi, u32 index, data[2] = vlanmc->fid & RTL8366RB_VLAN_FID_MASK; for (i = 0; i < 3; i++) { - ret = regmap_write(smi->map, + ret = regmap_write(priv->map, RTL8366RB_VLAN_MC_BASE(index) + i, data[i]); if (ret) @@ -1559,15 +1564,15 @@ static int rtl8366rb_set_vlan_mc(struct realtek_smi *smi, u32 index, return 0; } -static int rtl8366rb_get_mc_index(struct realtek_smi *smi, int port, int *val) +static int rtl8366rb_get_mc_index(struct realtek_priv *priv, int port, int *val) { u32 data; int ret; - if (port >= smi->num_ports) + if (port >= priv->num_ports) return -EINVAL; - ret = regmap_read(smi->map, RTL8366RB_PORT_VLAN_CTRL_REG(port), + ret = regmap_read(priv->map, RTL8366RB_PORT_VLAN_CTRL_REG(port), &data); if (ret) return ret; @@ -1578,22 +1583,22 @@ static int rtl8366rb_get_mc_index(struct realtek_smi *smi, int port, int *val) return 0; } -static int rtl8366rb_set_mc_index(struct realtek_smi *smi, int port, int index) +static int rtl8366rb_set_mc_index(struct realtek_priv *priv, int port, int index) { struct rtl8366rb *rb; bool pvid_enabled; int ret; - rb = smi->chip_data; + rb = priv->chip_data; pvid_enabled = !!index; - if (port >= smi->num_ports || index >= RTL8366RB_NUM_VLANS) + if (port >= priv->num_ports || index >= RTL8366RB_NUM_VLANS) return -EINVAL; - ret = regmap_update_bits(smi->map, RTL8366RB_PORT_VLAN_CTRL_REG(port), - RTL8366RB_PORT_VLAN_CTRL_MASK << + ret = regmap_update_bits(priv->map, RTL8366RB_PORT_VLAN_CTRL_REG(port), + RTL8366RB_PORT_VLAN_CTRL_MASK << RTL8366RB_PORT_VLAN_CTRL_SHIFT(port), - (index & RTL8366RB_PORT_VLAN_CTRL_MASK) << + (index & RTL8366RB_PORT_VLAN_CTRL_MASK) << RTL8366RB_PORT_VLAN_CTRL_SHIFT(port)); if (ret) return ret; @@ -1604,17 +1609,17 @@ static int rtl8366rb_set_mc_index(struct realtek_smi *smi, int port, int index) * not drop any untagged or C-tagged frames. Make sure to update the * filtering setting. */ - if (dsa_port_is_vlan_filtering(dsa_to_port(smi->ds, port))) - ret = rtl8366rb_drop_untagged(smi, port, !pvid_enabled); + if (dsa_port_is_vlan_filtering(dsa_to_port(priv->ds, port))) + ret = rtl8366rb_drop_untagged(priv, port, !pvid_enabled); return ret; } -static bool rtl8366rb_is_vlan_valid(struct realtek_smi *smi, unsigned int vlan) +static bool rtl8366rb_is_vlan_valid(struct realtek_priv *priv, unsigned int vlan) { unsigned int max = RTL8366RB_NUM_VLANS - 1; - if (smi->vlan4k_enabled) + if (priv->vlan4k_enabled) max = RTL8366RB_NUM_VIDS - 1; if (vlan > max) @@ -1623,23 +1628,23 @@ static bool rtl8366rb_is_vlan_valid(struct realtek_smi *smi, unsigned int vlan) return true; } -static int rtl8366rb_enable_vlan(struct realtek_smi *smi, bool enable) +static int rtl8366rb_enable_vlan(struct realtek_priv *priv, bool enable) { - dev_dbg(smi->dev, "%s VLAN\n", enable ? "enable" : "disable"); - return regmap_update_bits(smi->map, + dev_dbg(priv->dev, "%s VLAN\n", enable ? "enable" : "disable"); + return regmap_update_bits(priv->map, RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN, enable ? RTL8366RB_SGCR_EN_VLAN : 0); } -static int rtl8366rb_enable_vlan4k(struct realtek_smi *smi, bool enable) +static int rtl8366rb_enable_vlan4k(struct realtek_priv *priv, bool enable) { - dev_dbg(smi->dev, "%s VLAN 4k\n", enable ? "enable" : "disable"); - return regmap_update_bits(smi->map, RTL8366RB_SGCR, + dev_dbg(priv->dev, "%s VLAN 4k\n", enable ? "enable" : "disable"); + return regmap_update_bits(priv->map, RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN_4KTB, enable ? RTL8366RB_SGCR_EN_VLAN_4KTB : 0); } -static int rtl8366rb_phy_read(struct realtek_smi *smi, int phy, int regnum) +static int rtl8366rb_phy_read(struct realtek_priv *priv, int phy, int regnum) { u32 val; u32 reg; @@ -1648,32 +1653,32 @@ static int rtl8366rb_phy_read(struct realtek_smi *smi, int phy, int regnum) if (phy > RTL8366RB_PHY_NO_MAX) return -EINVAL; - ret = regmap_write(smi->map, RTL8366RB_PHY_ACCESS_CTRL_REG, + ret = regmap_write(priv->map, RTL8366RB_PHY_ACCESS_CTRL_REG, RTL8366RB_PHY_CTRL_READ); if (ret) return ret; reg = 0x8000 | (1 << (phy + RTL8366RB_PHY_NO_OFFSET)) | regnum; - ret = regmap_write(smi->map, reg, 0); + ret = regmap_write(priv->map, reg, 0); if (ret) { - dev_err(smi->dev, + dev_err(priv->dev, "failed to write PHY%d reg %04x @ %04x, ret %d\n", phy, regnum, reg, ret); return ret; } - ret = regmap_read(smi->map, RTL8366RB_PHY_ACCESS_DATA_REG, &val); + ret = regmap_read(priv->map, RTL8366RB_PHY_ACCESS_DATA_REG, &val); if (ret) return ret; - dev_dbg(smi->dev, "read PHY%d register 0x%04x @ %08x, val <- %04x\n", + dev_dbg(priv->dev, "read PHY%d register 0x%04x @ %08x, val <- %04x\n", phy, regnum, reg, val); return val; } -static int rtl8366rb_phy_write(struct realtek_smi *smi, int phy, int regnum, +static int rtl8366rb_phy_write(struct realtek_priv *priv, int phy, int regnum, u16 val) { u32 reg; @@ -1682,34 +1687,45 @@ static int rtl8366rb_phy_write(struct realtek_smi *smi, int phy, int regnum, if (phy > RTL8366RB_PHY_NO_MAX) return -EINVAL; - ret = regmap_write(smi->map, RTL8366RB_PHY_ACCESS_CTRL_REG, + ret = regmap_write(priv->map, RTL8366RB_PHY_ACCESS_CTRL_REG, RTL8366RB_PHY_CTRL_WRITE); if (ret) return ret; reg = 0x8000 | (1 << (phy + RTL8366RB_PHY_NO_OFFSET)) | regnum; - dev_dbg(smi->dev, "write PHY%d register 0x%04x @ %04x, val -> %04x\n", + dev_dbg(priv->dev, "write PHY%d register 0x%04x @ %04x, val -> %04x\n", phy, regnum, reg, val); - ret = regmap_write(smi->map, reg, val); + ret = regmap_write(priv->map, reg, val); if (ret) return ret; return 0; } -static int rtl8366rb_reset_chip(struct realtek_smi *smi) +static int rtl8366rb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) +{ + return rtl8366rb_phy_read(ds->priv, phy, regnum); +} + +static int rtl8366rb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, + u16 val) +{ + return rtl8366rb_phy_write(ds->priv, phy, regnum, val); +} + +static int rtl8366rb_reset_chip(struct realtek_priv *priv) { int timeout = 10; u32 val; int ret; - realtek_smi_write_reg_noack(smi, RTL8366RB_RESET_CTRL_REG, - RTL8366RB_CHIP_CTRL_RESET_HW); + priv->write_reg_noack(priv, RTL8366RB_RESET_CTRL_REG, + RTL8366RB_CHIP_CTRL_RESET_HW); do { usleep_range(20000, 25000); - ret = regmap_read(smi->map, RTL8366RB_RESET_CTRL_REG, &val); + ret = regmap_read(priv->map, RTL8366RB_RESET_CTRL_REG, &val); if (ret) return ret; @@ -1718,21 +1734,21 @@ static int rtl8366rb_reset_chip(struct realtek_smi *smi) } while (--timeout); if (!timeout) { - dev_err(smi->dev, "timeout waiting for the switch to reset\n"); + dev_err(priv->dev, "timeout waiting for the switch to reset\n"); return -EIO; } return 0; } -static int rtl8366rb_detect(struct realtek_smi *smi) +static int rtl8366rb_detect(struct realtek_priv *priv) { - struct device *dev = smi->dev; + struct device *dev = priv->dev; int ret; u32 val; /* Detect device */ - ret = regmap_read(smi->map, 0x5c, &val); + ret = regmap_read(priv->map, 0x5c, &val); if (ret) { dev_err(dev, "can't get chip ID (%d)\n", ret); return ret; @@ -1745,11 +1761,11 @@ static int rtl8366rb_detect(struct realtek_smi *smi) return -ENODEV; case 0x5937: dev_info(dev, "found an RTL8366RB switch\n"); - smi->cpu_port = RTL8366RB_PORT_NUM_CPU; - smi->num_ports = RTL8366RB_NUM_PORTS; - smi->num_vlan_mc = RTL8366RB_NUM_VLANS; - smi->mib_counters = rtl8366rb_mib_counters; - smi->num_mib_counters = ARRAY_SIZE(rtl8366rb_mib_counters); + priv->cpu_port = RTL8366RB_PORT_NUM_CPU; + priv->num_ports = RTL8366RB_NUM_PORTS; + priv->num_vlan_mc = RTL8366RB_NUM_VLANS; + priv->mib_counters = rtl8366rb_mib_counters; + priv->num_mib_counters = ARRAY_SIZE(rtl8366rb_mib_counters); break; default: dev_info(dev, "found an Unknown Realtek switch (id=0x%04x)\n", @@ -1757,14 +1773,14 @@ static int rtl8366rb_detect(struct realtek_smi *smi) break; } - ret = rtl8366rb_reset_chip(smi); + ret = rtl8366rb_reset_chip(priv); if (ret) return ret; return 0; } -static const struct dsa_switch_ops rtl8366rb_switch_ops = { +static const struct dsa_switch_ops rtl8366rb_switch_ops_smi = { .get_tag_protocol = rtl8366_get_tag_protocol, .setup = rtl8366rb_setup, .phylink_mac_link_up = rtl8366rb_mac_link_up, @@ -1787,7 +1803,32 @@ static const struct dsa_switch_ops rtl8366rb_switch_ops = { .port_max_mtu = rtl8366rb_max_mtu, }; -static const struct realtek_smi_ops rtl8366rb_smi_ops = { +static const struct dsa_switch_ops rtl8366rb_switch_ops_mdio = { + .get_tag_protocol = rtl8366_get_tag_protocol, + .setup = rtl8366rb_setup, + .phy_read = rtl8366rb_dsa_phy_read, + .phy_write = rtl8366rb_dsa_phy_write, + .phylink_mac_link_up = rtl8366rb_mac_link_up, + .phylink_mac_link_down = rtl8366rb_mac_link_down, + .get_strings = rtl8366_get_strings, + .get_ethtool_stats = rtl8366_get_ethtool_stats, + .get_sset_count = rtl8366_get_sset_count, + .port_bridge_join = rtl8366rb_port_bridge_join, + .port_bridge_leave = rtl8366rb_port_bridge_leave, + .port_vlan_filtering = rtl8366rb_vlan_filtering, + .port_vlan_add = rtl8366_vlan_add, + .port_vlan_del = rtl8366_vlan_del, + .port_enable = rtl8366rb_port_enable, + .port_disable = rtl8366rb_port_disable, + .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags, + .port_bridge_flags = rtl8366rb_port_bridge_flags, + .port_stp_state_set = rtl8366rb_port_stp_state_set, + .port_fast_age = rtl8366rb_port_fast_age, + .port_change_mtu = rtl8366rb_change_mtu, + .port_max_mtu = rtl8366rb_max_mtu, +}; + +static const struct realtek_ops rtl8366rb_ops = { .detect = rtl8366rb_detect, .get_vlan_mc = rtl8366rb_get_vlan_mc, .set_vlan_mc = rtl8366rb_set_vlan_mc, @@ -1803,12 +1844,17 @@ static const struct realtek_smi_ops rtl8366rb_smi_ops = { .phy_write = rtl8366rb_phy_write, }; -const struct realtek_smi_variant rtl8366rb_variant = { - .ds_ops = &rtl8366rb_switch_ops, - .ops = &rtl8366rb_smi_ops, +const struct realtek_variant rtl8366rb_variant = { + .ds_ops_smi = &rtl8366rb_switch_ops_smi, + .ds_ops_mdio = &rtl8366rb_switch_ops_mdio, + .ops = &rtl8366rb_ops, .clk_delay = 10, .cmd_read = 0xa9, .cmd_write = 0xa8, .chip_data_sz = sizeof(struct rtl8366rb), }; EXPORT_SYMBOL_GPL(rtl8366rb_variant); + +MODULE_AUTHOR("Linus Walleij "); +MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/sja1105/sja1105_flower.c b/drivers/net/dsa/sja1105/sja1105_flower.c index 7dcdd784aea4..fad5afe3819c 100644 --- a/drivers/net/dsa/sja1105/sja1105_flower.c +++ b/drivers/net/dsa/sja1105/sja1105_flower.c @@ -300,6 +300,46 @@ static int sja1105_flower_parse_key(struct sja1105_private *priv, return -EOPNOTSUPP; } +static int sja1105_policer_validate(const struct flow_action *action, + const struct flow_action_entry *act, + struct netlink_ext_ack *extack) +{ + if (act->police.exceed.act_id != FLOW_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when exceed action is not drop"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && + act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not pipe or ok"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && + !flow_action_is_last_entry(action, act)) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is ok, but action is not last"); + return -EOPNOTSUPP; + } + + if (act->police.peakrate_bytes_ps || + act->police.avrate || act->police.overhead) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when peakrate/avrate/overhead is configured"); + return -EOPNOTSUPP; + } + + if (act->police.rate_pkt_ps) { + NL_SET_ERR_MSG_MOD(extack, + "QoS offload not support packets per second"); + return -EOPNOTSUPP; + } + + return 0; +} + int sja1105_cls_flower_add(struct dsa_switch *ds, int port, struct flow_cls_offload *cls, bool ingress) { @@ -321,12 +361,9 @@ int sja1105_cls_flower_add(struct dsa_switch *ds, int port, flow_action_for_each(i, act, &rule->action) { switch (act->id) { case FLOW_ACTION_POLICE: - if (act->police.rate_pkt_ps) { - NL_SET_ERR_MSG_MOD(extack, - "QoS offload not support packets per second"); - rc = -EOPNOTSUPP; + rc = sja1105_policer_validate(&rule->action, act, extack); + if (rc) goto out; - } rc = sja1105_flower_policer(priv, port, extack, cookie, &key, diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index c2a47c6693b8..b33841c6507a 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -393,10 +393,8 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv) .start_dynspc = 0, /* 2^8 + 2^5 + 2^3 + 2^2 + 2^1 + 1 in Koopman notation */ .poly = 0x97, - /* This selects between Independent VLAN Learning (IVL) and - * Shared VLAN Learning (SVL) - */ - .shared_learn = true, + /* Always use Independent VLAN Learning (IVL) */ + .shared_learn = false, /* Don't discard management traffic based on ENFPORT - * we don't perform SMAC port enforcement anyway, so * what we are setting here doesn't matter. @@ -1358,37 +1356,16 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, return sja1105_clocking_setup_port(priv, port); } -/* The SJA1105 MAC programming model is through the static config (the xMII - * Mode table cannot be dynamically reconfigured), and we have to program - * that early (earlier than PHYLINK calls us, anyway). - * So just error out in case the connected PHY attempts to change the initial - * system interface MII protocol from what is defined in the DT, at least for - * now. - */ -static bool sja1105_phy_mode_mismatch(struct sja1105_private *priv, int port, - phy_interface_t interface) +static struct phylink_pcs * +sja1105_mac_select_pcs(struct dsa_switch *ds, int port, phy_interface_t iface) { - return priv->phy_mode[port] != interface; -} - -static void sja1105_mac_config(struct dsa_switch *ds, int port, - unsigned int mode, - const struct phylink_link_state *state) -{ - struct dsa_port *dp = dsa_to_port(ds, port); struct sja1105_private *priv = ds->priv; - struct dw_xpcs *xpcs; - - if (sja1105_phy_mode_mismatch(priv, port, state->interface)) { - dev_err(ds->dev, "Changing PHY mode to %s not supported!\n", - phy_modes(state->interface)); - return; - } - - xpcs = priv->xpcs[port]; + struct dw_xpcs *xpcs = priv->xpcs[port]; if (xpcs) - phylink_set_pcs(dp->pl, &xpcs->pcs); + return &xpcs->pcs; + + return NULL; } static void sja1105_mac_link_down(struct dsa_switch *ds, int port, @@ -1412,48 +1389,53 @@ static void sja1105_mac_link_up(struct dsa_switch *ds, int port, sja1105_inhibit_tx(priv, BIT(port), false); } -static void sja1105_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void sja1105_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - /* Construct a new mask which exhaustively contains all link features - * supported by the MAC, and then apply that (logical AND) to what will - * be sent to the PHY for "marketing". - */ - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; struct sja1105_private *priv = ds->priv; struct sja1105_xmii_params_entry *mii; + phy_interface_t phy_mode; - mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; - - /* include/linux/phylink.h says: - * When @state->interface is %PHY_INTERFACE_MODE_NA, phylink - * expects the MAC driver to return all supported link modes. + /* This driver does not make use of the speed, duplex, pause or the + * advertisement in its mac_config, so it is safe to mark this driver + * as non-legacy. */ - if (state->interface != PHY_INTERFACE_MODE_NA && - sja1105_phy_mode_mismatch(priv, port, state->interface)) { - linkmode_zero(supported); - return; + config->legacy_pre_march2020 = false; + + phy_mode = priv->phy_mode[port]; + if (phy_mode == PHY_INTERFACE_MODE_SGMII || + phy_mode == PHY_INTERFACE_MODE_2500BASEX) { + /* Changing the PHY mode on SERDES ports is possible and makes + * sense, because that is done through the XPCS. We allow + * changes between SGMII and 2500base-X. + */ + if (priv->info->supports_sgmii[port]) + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); + + if (priv->info->supports_2500basex[port]) + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + config->supported_interfaces); + } else { + /* The SJA1105 MAC programming model is through the static + * config (the xMII Mode table cannot be dynamically + * reconfigured), and we have to program that early. + */ + __set_bit(phy_mode, config->supported_interfaces); } /* The MAC does not support pause frames, and also doesn't * support half-duplex traffic modes. */ - phylink_set(mask, Autoneg); - phylink_set(mask, MII); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Full); - phylink_set(mask, 100baseT1_Full); + config->mac_capabilities = MAC_10FD | MAC_100FD; + + mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; if (mii->xmii_mode[port] == XMII_MODE_RGMII || mii->xmii_mode[port] == XMII_MODE_SGMII) - phylink_set(mask, 1000baseT_Full); - if (priv->info->supports_2500basex[port]) { - phylink_set(mask, 2500baseT_Full); - phylink_set(mask, 2500baseX_Full); - } + config->mac_capabilities |= MAC_1000FD; - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); + if (priv->info->supports_2500basex[port]) + config->mac_capabilities |= MAC_2500FD; } static int @@ -1819,25 +1801,52 @@ int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port, } static int sja1105_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct sja1105_private *priv = ds->priv; + if (!vid) { + switch (db.type) { + case DSA_DB_PORT: + vid = dsa_tag_8021q_standalone_vid(db.dp); + break; + case DSA_DB_BRIDGE: + vid = dsa_tag_8021q_bridge_vid(db.bridge.num); + break; + default: + return -EOPNOTSUPP; + } + } + return priv->info->fdb_add_cmd(ds, port, addr, vid); } static int sja1105_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct sja1105_private *priv = ds->priv; + if (!vid) { + switch (db.type) { + case DSA_DB_PORT: + vid = dsa_tag_8021q_standalone_vid(db.dp); + break; + case DSA_DB_BRIDGE: + vid = dsa_tag_8021q_bridge_vid(db.bridge.num); + break; + default: + return -EOPNOTSUPP; + } + } + return priv->info->fdb_del_cmd(ds, port, addr, vid); } static int sja1105_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data) { - struct dsa_port *dp = dsa_to_port(ds, port); struct sja1105_private *priv = ds->priv; struct device *dev = ds->dev; int i; @@ -1874,7 +1883,7 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, u64_to_ether_addr(l2_lookup.macaddr, macaddr); /* We need to hide the dsa_8021q VLANs from the user. */ - if (!dsa_port_is_vlan_filtering(dp)) + if (vid_is_dsa_8021q(l2_lookup.vlanid)) l2_lookup.vlanid = 0; rc = cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data); if (rc) @@ -1885,7 +1894,15 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, static void sja1105_fast_age(struct dsa_switch *ds, int port) { + struct dsa_port *dp = dsa_to_port(ds, port); struct sja1105_private *priv = ds->priv; + struct dsa_db db = { + .type = DSA_DB_BRIDGE, + .bridge = { + .dev = dsa_port_bridge_dev_get(dp), + .num = dsa_port_bridge_num_get(dp), + }, + }; int i; for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { @@ -1913,7 +1930,7 @@ static void sja1105_fast_age(struct dsa_switch *ds, int port) u64_to_ether_addr(l2_lookup.macaddr, macaddr); - rc = sja1105_fdb_del(ds, port, macaddr, l2_lookup.vlanid); + rc = sja1105_fdb_del(ds, port, macaddr, l2_lookup.vlanid, db); if (rc) { dev_err(ds->dev, "Failed to delete FDB entry %pM vid %lld: %pe\n", @@ -1924,15 +1941,17 @@ static void sja1105_fast_age(struct dsa_switch *ds, int port) } static int sja1105_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { - return sja1105_fdb_add(ds, port, mdb->addr, mdb->vid); + return sja1105_fdb_add(ds, port, mdb->addr, mdb->vid, db); } static int sja1105_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { - return sja1105_fdb_del(ds, port, mdb->addr, mdb->vid); + return sja1105_fdb_del(ds, port, mdb->addr, mdb->vid, db); } /* Common function for unicast and broadcast flood configuration. @@ -2075,7 +2094,8 @@ static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port, static int sja1105_bridge_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, - bool *tx_fwd_offload) + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { int rc; @@ -2083,7 +2103,7 @@ static int sja1105_bridge_join(struct dsa_switch *ds, int port, if (rc) return rc; - rc = dsa_tag_8021q_bridge_tx_fwd_offload(ds, port, bridge); + rc = dsa_tag_8021q_bridge_join(ds, port, bridge); if (rc) { sja1105_bridge_member(ds, port, bridge, false); return rc; @@ -2097,7 +2117,7 @@ static int sja1105_bridge_join(struct dsa_switch *ds, int port, static void sja1105_bridge_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge) { - dsa_tag_8021q_bridge_tx_fwd_unoffload(ds, port, bridge); + dsa_tag_8021q_bridge_leave(ds, port, bridge); sja1105_bridge_member(ds, port, bridge, false); } @@ -2357,7 +2377,6 @@ sja1105_get_tag_protocol(struct dsa_switch *ds, int port, int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, struct netlink_ext_ack *extack) { - struct sja1105_l2_lookup_params_entry *l2_lookup_params; struct sja1105_general_params_entry *general_params; struct sja1105_private *priv = ds->priv; struct sja1105_table *table; @@ -2395,28 +2414,6 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, general_params->incl_srcpt1 = enabled; general_params->incl_srcpt0 = enabled; - /* VLAN filtering => independent VLAN learning. - * No VLAN filtering (or best effort) => shared VLAN learning. - * - * In shared VLAN learning mode, untagged traffic still gets - * pvid-tagged, and the FDB table gets populated with entries - * containing the "real" (pvid or from VLAN tag) VLAN ID. - * However the switch performs a masked L2 lookup in the FDB, - * effectively only looking up a frame's DMAC (and not VID) for the - * forwarding decision. - * - * This is extremely convenient for us, because in modes with - * vlan_filtering=0, dsa_8021q actually installs unique pvid's into - * each front panel port. This is good for identification but breaks - * learning badly - the VID of the learnt FDB entry is unique, aka - * no frames coming from any other port are going to have it. So - * for forwarding purposes, this is as though learning was broken - * (all frames get flooded). - */ - table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS]; - l2_lookup_params = table->entries; - l2_lookup_params->shared_learn = !enabled; - for (port = 0; port < ds->num_ports; port++) { if (dsa_is_unused_port(ds, port)) continue; @@ -2525,7 +2522,7 @@ static int sja1105_bridge_vlan_add(struct dsa_switch *ds, int port, */ if (vid_is_dsa_8021q(vlan->vid)) { NL_SET_ERR_MSG_MOD(extack, - "Range 1024-3071 reserved for dsa_8021q operation"); + "Range 3072-4095 reserved for dsa_8021q operation"); return -EBUSY; } @@ -2850,7 +2847,7 @@ static int sja1105_mirror_apply(struct sja1105_private *priv, int from, int to, static int sja1105_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, - bool ingress) + bool ingress, struct netlink_ext_ack *extack) { return sja1105_mirror_apply(ds->priv, port, mirror->to_local_port, ingress, true); @@ -3102,6 +3099,7 @@ static int sja1105_setup(struct dsa_switch *ds) */ ds->vlan_filtering_is_global = true; ds->untag_bridge_pvid = true; + ds->fdb_isolation = true; /* tag_8021q has 3 bits for the VBID, and the value 0 is reserved */ ds->max_num_bridges = 7; @@ -3152,8 +3150,8 @@ static const struct dsa_switch_ops sja1105_switch_ops = { .set_ageing_time = sja1105_set_ageing_time, .port_change_mtu = sja1105_change_mtu, .port_max_mtu = sja1105_get_max_mtu, - .phylink_validate = sja1105_phylink_validate, - .phylink_mac_config = sja1105_mac_config, + .phylink_get_caps = sja1105_phylink_get_caps, + .phylink_mac_select_pcs = sja1105_mac_select_pcs, .phylink_mac_link_up = sja1105_mac_link_up, .phylink_mac_link_down = sja1105_mac_link_down, .get_strings = sja1105_get_strings, diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index be3068a935af..30fb2cc40164 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -399,7 +399,7 @@ static long sja1105_rxtstamp_work(struct ptp_clock_info *ptp) ts = sja1105_tstamp_reconstruct(ds, ticks, ts); shwt->hwtstamp = ns_to_ktime(sja1105_ticks_to_ns(ts)); - netif_rx_ni(skb); + netif_rx(skb); } if (ptp_data->extts_enabled) diff --git a/drivers/net/dsa/sja1105/sja1105_vl.c b/drivers/net/dsa/sja1105/sja1105_vl.c index f5dca6a9b0f9..b7e95d60a6e4 100644 --- a/drivers/net/dsa/sja1105/sja1105_vl.c +++ b/drivers/net/dsa/sja1105/sja1105_vl.c @@ -296,6 +296,19 @@ static bool sja1105_vl_key_lower(struct sja1105_vl_lookup_entry *a, return false; } +/* FIXME: this should change when the bridge upper of the port changes. */ +static u16 sja1105_port_get_tag_8021q_vid(struct dsa_port *dp) +{ + unsigned long bridge_num; + + if (!dp->bridge) + return dsa_tag_8021q_standalone_vid(dp); + + bridge_num = dsa_port_bridge_num_get(dp); + + return dsa_tag_8021q_bridge_vid(bridge_num); +} + static int sja1105_init_virtual_links(struct sja1105_private *priv, struct netlink_ext_ack *extack) { @@ -394,8 +407,9 @@ static int sja1105_init_virtual_links(struct sja1105_private *priv, vl_lookup[k].vlanid = rule->key.vl.vid; vl_lookup[k].vlanprior = rule->key.vl.pcp; } else { + /* FIXME */ struct dsa_port *dp = dsa_to_port(priv->ds, port); - u16 vid = dsa_tag_8021q_rx_vid(dp); + u16 vid = sja1105_port_get_tag_8021q_vid(dp); vl_lookup[k].vlanid = vid; vl_lookup[k].vlanprior = 0; diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c index 0730352cdd57..3887ed33c5fe 100644 --- a/drivers/net/dsa/xrs700x/xrs700x.c +++ b/drivers/net/dsa/xrs700x/xrs700x.c @@ -442,34 +442,27 @@ static void xrs700x_teardown(struct dsa_switch *ds) cancel_delayed_work_sync(&priv->mib_work); } -static void xrs700x_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void xrs700x_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - switch (port) { case 0: + __set_bit(PHY_INTERFACE_MODE_RMII, + config->supported_interfaces); + config->mac_capabilities = MAC_10FD | MAC_100FD; break; + case 1: case 2: case 3: - phylink_set(mask, 1000baseT_Full); + phy_interface_set_rgmii(config->supported_interfaces); + config->mac_capabilities = MAC_10FD | MAC_100FD | MAC_1000FD; break; + default: - linkmode_zero(supported); dev_err(ds->dev, "Unsupported port: %i\n", port); - return; + break; } - - phylink_set_port_modes(mask); - - /* The switch only supports full duplex. */ - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Full); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); } static void xrs700x_mac_link_up(struct dsa_switch *ds, int port, @@ -541,7 +534,8 @@ static int xrs700x_bridge_common(struct dsa_switch *ds, int port, } static int xrs700x_bridge_join(struct dsa_switch *ds, int port, - struct dsa_bridge bridge, bool *tx_fwd_offload) + struct dsa_bridge bridge, bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { return xrs700x_bridge_common(ds, port, bridge, true); } @@ -703,7 +697,7 @@ static const struct dsa_switch_ops xrs700x_ops = { .setup = xrs700x_setup, .teardown = xrs700x_teardown, .port_stp_state_set = xrs700x_port_stp_state_set, - .phylink_validate = xrs700x_phylink_validate, + .phylink_get_caps = xrs700x_phylink_get_caps, .phylink_mac_link_up = xrs700x_mac_link_up, .get_strings = xrs700x_get_strings, .get_sset_count = xrs700x_get_sset_count, diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index 8aec5d9fbfef..ad57209007e1 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -138,11 +138,6 @@ MODULE_PARM_DESC(use_mmio, "Use MMIO (1) or PIO(0) to access the NIC. " module_param(rx_copybreak, int, 0); module_param(use_mmio, int, 0); -#if defined(NETIF_F_TSO) && MAX_SKB_FRAGS > 32 -#warning Typhoon only supports 32 entries in its SG list for TSO, disabling TSO -#undef NETIF_F_TSO -#endif - #if TXLO_ENTRIES <= (2 * MAX_SKB_FRAGS) #error TX ring too small! #endif @@ -2261,9 +2256,28 @@ typhoon_test_mmio(struct pci_dev *pdev) return mode; } +#if MAX_SKB_FRAGS > 32 + +#include + +static netdev_features_t typhoon_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + if (skb_shinfo(skb)->nr_frags > 32 && skb_is_gso(skb)) + features &= ~NETIF_F_GSO_MASK; + + features = vlan_features_check(skb, features); + return vxlan_features_check(skb, features); +} +#endif + static const struct net_device_ops typhoon_netdev_ops = { .ndo_open = typhoon_open, .ndo_stop = typhoon_close, +#if MAX_SKB_FRAGS > 32 + .ndo_features_check = typhoon_features_check, +#endif .ndo_start_xmit = typhoon_start_tx, .ndo_set_rx_mode = typhoon_set_rx_mode, .ndo_tx_timeout = typhoon_tx_timeout, diff --git a/drivers/net/ethernet/8390/mcf8390.c b/drivers/net/ethernet/8390/mcf8390.c index 90cd7bdf06f5..21047ae1bc3d 100644 --- a/drivers/net/ethernet/8390/mcf8390.c +++ b/drivers/net/ethernet/8390/mcf8390.c @@ -410,10 +410,8 @@ static int mcf8390_probe(struct platform_device *pdev) int ret, irq; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no IRQ specified?\n"); + if (irq < 0) return -ENXIO; - } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (mem == NULL) { diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index db3ec4768159..bd4cb9d7c35d 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -78,6 +78,7 @@ source "drivers/net/ethernet/ezchip/Kconfig" source "drivers/net/ethernet/faraday/Kconfig" source "drivers/net/ethernet/freescale/Kconfig" source "drivers/net/ethernet/fujitsu/Kconfig" +source "drivers/net/ethernet/fungible/Kconfig" source "drivers/net/ethernet/google/Kconfig" source "drivers/net/ethernet/hisilicon/Kconfig" source "drivers/net/ethernet/huawei/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 8a87c1083d1d..8ef43e0c33c0 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_NET_VENDOR_EZCHIP) += ezchip/ obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/ obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/ obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/ +obj-$(CONFIG_NET_VENDOR_FUNGIBLE) += fungible/ obj-$(CONFIG_NET_VENDOR_GOOGLE) += google/ obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/ obj-$(CONFIG_NET_VENDOR_HUAWEI) += huawei/ diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c index 537e6a85e18d..fbf4588994ac 100644 --- a/drivers/net/ethernet/agere/et131x.c +++ b/drivers/net/ethernet/agere/et131x.c @@ -2413,11 +2413,13 @@ static void et131x_tx_dma_memory_free(struct et131x_adapter *adapter) kfree(tx_ring->tcb_ring); } +#define MAX_TX_DESC_PER_PKT 24 + /* nic_send_packet - NIC specific send handler for version B silicon. */ static int nic_send_packet(struct et131x_adapter *adapter, struct tcb *tcb) { u32 i; - struct tx_desc desc[24]; + struct tx_desc desc[MAX_TX_DESC_PER_PKT]; u32 frag = 0; u32 thiscopy, remainder; struct sk_buff *skb = tcb->skb; @@ -2432,9 +2434,6 @@ static int nic_send_packet(struct et131x_adapter *adapter, struct tcb *tcb) * more than 5 fragments. */ - /* nr_frags should be no more than 18. */ - BUILD_BUG_ON(MAX_SKB_FRAGS + 1 > 23); - memset(desc, 0, sizeof(struct tx_desc) * (nr_frags + 1)); for (i = 0; i < nr_frags; i++) { @@ -3762,6 +3761,13 @@ static netdev_tx_t et131x_tx(struct sk_buff *skb, struct net_device *netdev) struct et131x_adapter *adapter = netdev_priv(netdev); struct tx_ring *tx_ring = &adapter->tx_ring; + /* This driver does not support TSO, it is very unlikely + * this condition is true. + */ + if (unlikely(skb_shinfo(skb)->nr_frags > MAX_TX_DESC_PER_PKT - 2)) { + if (skb_linearize(skb)) + goto drop_err; + } /* stop the queue if it's getting full */ if (tx_ring->used >= NUM_TCB - 1 && !netif_queue_stopped(netdev)) netif_stop_queue(netdev); diff --git a/drivers/net/ethernet/altera/altera_sgdma.c b/drivers/net/ethernet/altera/altera_sgdma.c index db97170da8c7..7f247ccbe6ba 100644 --- a/drivers/net/ethernet/altera/altera_sgdma.c +++ b/drivers/net/ethernet/altera/altera_sgdma.c @@ -513,7 +513,7 @@ static int sgdma_txbusy(struct altera_tse_private *priv) { int delay = 0; - /* if DMA is busy, wait for current transactino to finish */ + /* if DMA is busy, wait for current transaction to finish */ while ((csrrd32(priv->tx_dma_csr, sgdma_csroffs(status)) & SGDMA_STSREG_BUSY) && (delay++ < 100)) udelay(1); diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index 993b2fb42961..a3816264c35c 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -72,7 +72,7 @@ MODULE_PARM_DESC(dma_tx_num, "Number of descriptors in the TX list"); */ #define ALTERA_RXDMABUFFER_SIZE 2048 -/* Allow network stack to resume queueing packets after we've +/* Allow network stack to resume queuing packets after we've * finished transmitting at least 1/4 of the packets in the queue. */ #define TSE_TX_THRESH(x) (x->tx_ring_size / 4) @@ -390,7 +390,7 @@ static int tse_rx(struct altera_tse_private *priv, int limit) "RCV pktstatus %08X pktlength %08X\n", pktstatus, pktlength); - /* DMA trasfer from TSE starts with 2 aditional bytes for + /* DMA transfer from TSE starts with 2 additional bytes for * IP payload alignment. Status returned by get_rx_status() * contains DMA transfer length. Packet is 2 bytes shorter. */ @@ -1044,7 +1044,7 @@ static void altera_tse_set_mcfilterall(struct net_device *dev) csrwr32(1, priv->mac_dev, tse_csroffs(hash_table) + i * 4); } -/* Set or clear the multicast filter for this adaptor +/* Set or clear the multicast filter for this adapter */ static void tse_set_rx_mode_hashfilter(struct net_device *dev) { @@ -1064,7 +1064,7 @@ static void tse_set_rx_mode_hashfilter(struct net_device *dev) spin_unlock(&priv->mac_cfg_lock); } -/* Set or clear the multicast filter for this adaptor +/* Set or clear the multicast filter for this adapter */ static void tse_set_rx_mode(struct net_device *dev) { diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index 53080fd143dc..07444aead3fd 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -1400,10 +1400,9 @@ static struct sk_buff *ena_alloc_skb(struct ena_ring *rx_ring, void *first_frag) struct sk_buff *skb; if (!first_frag) - skb = netdev_alloc_skb_ip_align(rx_ring->netdev, - rx_ring->rx_copybreak); + skb = napi_alloc_skb(rx_ring->napi, rx_ring->rx_copybreak); else - skb = build_skb(first_frag, ENA_PAGE_SIZE); + skb = napi_build_skb(first_frag, ENA_PAGE_SIZE); if (unlikely(!skb)) { ena_increase_stat(&rx_ring->rx_stats.skb_alloc_fail, 1, diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index ff2d099aab21..53dc8d5fede8 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -696,6 +696,12 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, buf_pool->rx_skb[skb_index] = NULL; datalen = xgene_enet_get_data_len(le64_to_cpu(raw_desc->m1)); + + /* strip off CRC as HW isn't doing this */ + nv = GET_VAL(NV, le64_to_cpu(raw_desc->m0)); + if (!nv) + datalen -= 4; + skb_put(skb, datalen); prefetch(skb->data - NET_IP_ALIGN); skb->protocol = eth_type_trans(skb, ndev); @@ -717,12 +723,8 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, } } - nv = GET_VAL(NV, le64_to_cpu(raw_desc->m0)); - if (!nv) { - /* strip off CRC as HW isn't doing this */ - datalen -= 4; + if (!nv) goto skip_jumbo; - } slots = page_pool->slots - 1; head = page_pool->head; diff --git a/drivers/net/ethernet/asix/ax88796c_main.c b/drivers/net/ethernet/asix/ax88796c_main.c index bf70481bb1ca..6ba5b024a7be 100644 --- a/drivers/net/ethernet/asix/ax88796c_main.c +++ b/drivers/net/ethernet/asix/ax88796c_main.c @@ -433,7 +433,7 @@ ax88796c_skb_return(struct ax88796c_device *ax_local, netif_info(ax_local, rx_status, ndev, "< rx, len %zu, type 0x%x\n", skb->len + sizeof(struct ethhdr), skb->protocol); - status = netif_rx_ni(skb); + status = netif_rx(skb); if (status != NET_RX_SUCCESS && net_ratelimit()) netif_info(ax_local, rx_err, ndev, "netif_rx status %d\n", status); diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c index f50604f3e541..49459397993e 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -1051,7 +1051,7 @@ static int atl1c_setup_ring_resources(struct atl1c_adapter *adapter) * each ring/block may need up to 8 bytes for alignment, hence the * additional bytes tacked onto the end. */ - ring_header->size = size = + ring_header->size = sizeof(struct atl1c_tpd_desc) * tpd_ring->count * tqc + sizeof(struct atl1c_rx_free_desc) * rfd_ring->count * rqc + sizeof(struct atl1c_recv_ret_status) * rfd_ring->count * rqc + diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index b04e423c446a..c1b97e8c55ef 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -1716,17 +1716,17 @@ static int bcm_enet_probe(struct platform_device *pdev) struct bcm_enet_priv *priv; struct net_device *dev; struct bcm63xx_enet_platform_data *pd; - struct resource *res_irq, *res_irq_rx, *res_irq_tx; + int irq, irq_rx, irq_tx; struct mii_bus *bus; int i, ret; if (!bcm_enet_shared_base[0]) return -EPROBE_DEFER; - res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - res_irq_rx = platform_get_resource(pdev, IORESOURCE_IRQ, 1); - res_irq_tx = platform_get_resource(pdev, IORESOURCE_IRQ, 2); - if (!res_irq || !res_irq_rx || !res_irq_tx) + irq = platform_get_irq(pdev, 0); + irq_rx = platform_get_irq(pdev, 1); + irq_tx = platform_get_irq(pdev, 2); + if (irq < 0 || irq_rx < 0 || irq_tx < 0) return -ENODEV; dev = alloc_etherdev(sizeof(*priv)); @@ -1748,9 +1748,9 @@ static int bcm_enet_probe(struct platform_device *pdev) goto out; } - dev->irq = priv->irq = res_irq->start; - priv->irq_rx = res_irq_rx->start; - priv->irq_tx = res_irq_tx->start; + dev->irq = priv->irq = irq; + priv->irq_rx = irq_rx; + priv->irq_tx = irq_tx; priv->mac_clk = devm_clk_get(&pdev->dev, "enet"); if (IS_ERR(priv->mac_clk)) { diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 2209d99b3404..dd5945c4bfec 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1271,7 +1271,7 @@ struct bnx2x_fw_stats_data { struct per_port_stats port; struct per_pf_stats pf; struct fcoe_statistics_params fcoe; - struct per_queue_stats queue_stats[1]; + struct per_queue_stats queue_stats[]; }; /* Public slow path states */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 4e85e7dbc2be..7071604f9984 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -6178,7 +6178,8 @@ static int bnx2x_format_ver(u32 num, u8 *str, u16 *len) return -EINVAL; } - ret = scnprintf(str, *len, "%hx.%hx", num >> 16, num); + ret = scnprintf(str, *len, "%x.%x", (num >> 16) & 0xFFFF, + num & 0xFFFF); *len -= ret; return 0; } @@ -6193,7 +6194,8 @@ static int bnx2x_3_seq_format_ver(u32 num, u8 *str, u16 *len) return -EINVAL; } - ret = scnprintf(str, *len, "%hhx.%hhx.%hhx", num >> 16, num >> 8, num); + ret = scnprintf(str, *len, "%x.%x.%x", (num >> 16) & 0xFF, + (num >> 8) & 0xFF, num & 0xFF); *len -= ret; return 0; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index b1c98d1408b8..1c28495875cf 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -233,6 +233,7 @@ static const u16 bnxt_async_events_arr[] = { ASYNC_EVENT_CMPL_EVENT_ID_ECHO_REQUEST, ASYNC_EVENT_CMPL_EVENT_ID_PPS_TIMESTAMP, ASYNC_EVENT_CMPL_EVENT_ID_ERROR_REPORT, + ASYNC_EVENT_CMPL_EVENT_ID_PHC_UPDATE, }; static struct workqueue_struct *bnxt_pf_wq; @@ -369,7 +370,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) i = skb_get_queue_mapping(skb); if (unlikely(i >= bp->tx_nr_rings)) { dev_kfree_skb_any(skb); - atomic_long_inc(&dev->tx_dropped); + dev_core_stats_tx_dropped_inc(dev); return NETDEV_TX_OK; } @@ -645,7 +646,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) if (txr->kick_pending) bnxt_txr_db_kick(bp, txr, txr->tx_prod); txr->tx_buf_ring[txr->tx_prod].skb = NULL; - atomic_long_inc(&dev->tx_dropped); + dev_core_stats_tx_dropped_inc(dev); return NETDEV_TX_OK; } @@ -2079,6 +2080,16 @@ static void bnxt_event_error_report(struct bnxt *bp, u32 data1, u32 data2) (BNXT_EVENT_RING_TYPE(data2) == \ ASYNC_EVENT_CMPL_RING_MONITOR_MSG_EVENT_DATA2_DISABLE_RING_TYPE_RX) +#define BNXT_EVENT_PHC_EVENT_TYPE(data1) \ + (((data1) & ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_FLAGS_MASK) >>\ + ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_FLAGS_SFT) + +#define BNXT_EVENT_PHC_RTC_UPDATE(data1) \ + (((data1) & ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_PHC_TIME_MSB_MASK) >>\ + ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_PHC_TIME_MSB_SFT) + +#define BNXT_PHC_BITS 48 + static int bnxt_async_event_process(struct bnxt *bp, struct hwrm_async_event_cmpl *cmpl) { @@ -2258,6 +2269,24 @@ static int bnxt_async_event_process(struct bnxt *bp, bnxt_event_error_report(bp, data1, data2); goto async_event_process_exit; } + case ASYNC_EVENT_CMPL_EVENT_ID_PHC_UPDATE: { + switch (BNXT_EVENT_PHC_EVENT_TYPE(data1)) { + case ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_FLAGS_PHC_RTC_UPDATE: + if (bp->fw_cap & BNXT_FW_CAP_PTP_RTC) { + struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + u64 ns; + + spin_lock_bh(&ptp->ptp_lock); + bnxt_ptp_update_current_time(bp); + ns = (((u64)BNXT_EVENT_PHC_RTC_UPDATE(data1) << + BNXT_PHC_BITS) | ptp->current_time); + bnxt_ptp_rtc_timecounter_init(ptp, ns); + spin_unlock_bh(&ptp->ptp_lock); + } + break; + } + goto async_event_process_exit; + } case ASYNC_EVENT_CMPL_EVENT_ID_DEFERRED_RESPONSE: { u16 seq_id = le32_to_cpu(cmpl->event_data2) & 0xffff; @@ -7416,6 +7445,7 @@ static int __bnxt_hwrm_ptp_qcfg(struct bnxt *bp) struct hwrm_port_mac_ptp_qcfg_output *resp; struct hwrm_port_mac_ptp_qcfg_input *req; struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + bool phc_cfg; u8 flags; int rc; @@ -7458,7 +7488,8 @@ static int __bnxt_hwrm_ptp_qcfg(struct bnxt *bp) rc = -ENODEV; goto exit; } - rc = bnxt_ptp_init(bp); + phc_cfg = (flags & PORT_MAC_PTP_QCFG_RESP_FLAGS_RTC_CONFIGURED) != 0; + rc = bnxt_ptp_init(bp, phc_cfg); if (rc) netdev_warn(bp->dev, "PTP initialization failed.\n"); exit: @@ -7516,6 +7547,8 @@ static int __bnxt_hwrm_func_qcaps(struct bnxt *bp) bp->fw_cap |= BNXT_FW_CAP_EXT_HW_STATS_SUPPORTED; if (BNXT_PF(bp) && (flags_ext & FUNC_QCAPS_RESP_FLAGS_EXT_PTP_PPS_SUPPORTED)) bp->fw_cap |= BNXT_FW_CAP_PTP_PPS; + if (flags_ext & FUNC_QCAPS_RESP_FLAGS_EXT_PTP_64BIT_RTC_SUPPORTED) + bp->fw_cap |= BNXT_FW_CAP_PTP_RTC; if (BNXT_PF(bp) && (flags_ext & FUNC_QCAPS_RESP_FLAGS_EXT_HOT_RESET_IF_SUPPORT)) bp->fw_cap |= BNXT_FW_CAP_HOT_RESET_IF; if (BNXT_PF(bp) && (flags_ext & FUNC_QCAPS_RESP_FLAGS_EXT_FW_LIVEPATCH_SUPPORTED)) @@ -9267,7 +9300,7 @@ void bnxt_tx_enable(struct bnxt *bp) /* Make sure napi polls see @dev_state change */ synchronize_net(); netif_tx_wake_all_queues(bp->dev); - if (bp->link_info.link_up) + if (BNXT_LINK_IS_UP(bp)) netif_carrier_on(bp->dev); } @@ -9297,7 +9330,7 @@ static char *bnxt_report_fec(struct bnxt_link_info *link_info) void bnxt_report_link(struct bnxt *bp) { - if (bp->link_info.link_up) { + if (BNXT_LINK_IS_UP(bp)) { const char *signal = ""; const char *flow_ctrl; const char *duplex; @@ -9383,7 +9416,7 @@ static int bnxt_hwrm_phy_qcaps(struct bnxt *bp) if (rc) goto hwrm_phy_qcaps_exit; - bp->phy_flags = resp->flags; + bp->phy_flags = resp->flags | (le16_to_cpu(resp->flags2) << 8); if (resp->flags & PORT_PHY_QCAPS_RESP_FLAGS_EEE_SUPPORTED) { struct ethtool_eee *eee = &bp->eee; u16 fw_speeds = le16_to_cpu(resp->supported_speeds_eee_mode); @@ -9433,7 +9466,7 @@ int bnxt_update_link(struct bnxt *bp, bool chng_link_state) struct bnxt_link_info *link_info = &bp->link_info; struct hwrm_port_phy_qcfg_output *resp; struct hwrm_port_phy_qcfg_input *req; - u8 link_up = link_info->link_up; + u8 link_state = link_info->link_state; bool support_changed = false; int rc; @@ -9534,14 +9567,14 @@ int bnxt_update_link(struct bnxt *bp, bool chng_link_state) /* TODO: need to add more logic to report VF link */ if (chng_link_state) { if (link_info->phy_link_status == BNXT_LINK_LINK) - link_info->link_up = 1; + link_info->link_state = BNXT_LINK_STATE_UP; else - link_info->link_up = 0; - if (link_up != link_info->link_up) + link_info->link_state = BNXT_LINK_STATE_DOWN; + if (link_state != link_info->link_state) bnxt_report_link(bp); } else { - /* alwasy link down if not require to update link state */ - link_info->link_up = 0; + /* always link down if not require to update link state */ + link_info->link_state = BNXT_LINK_STATE_DOWN; } hwrm_req_drop(bp, req); @@ -9741,7 +9774,18 @@ static int bnxt_hwrm_shutdown_link(struct bnxt *bp) return rc; req->flags = cpu_to_le32(PORT_PHY_CFG_REQ_FLAGS_FORCE_LINK_DWN); - return hwrm_req_send(bp, req); + rc = hwrm_req_send(bp, req); + if (!rc) { + mutex_lock(&bp->link_lock); + /* Device is not obliged link down in certain scenarios, even + * when forced. Setting the state unknown is consistent with + * driver startup and will force link state to be reported + * during subsequent open based on PORT_PHY_QCFG. + */ + bp->link_info.link_state = BNXT_LINK_STATE_UNKNOWN; + mutex_unlock(&bp->link_lock); + } + return rc; } static int bnxt_fw_reset_via_optee(struct bnxt *bp) @@ -10172,7 +10216,7 @@ static int bnxt_update_phy_setting(struct bnxt *bp) /* The last close may have shutdown the link, so need to call * PHY_CFG to bring it back up. */ - if (!bp->link_info.link_up) + if (!BNXT_LINK_IS_UP(bp)) update_link = true; if (!bnxt_eee_config_ok(bp)) @@ -10307,6 +10351,7 @@ static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) /* VF-reps may need to be re-opened after the PF is re-opened */ if (BNXT_PF(bp)) bnxt_vf_reps_open(bp); + bnxt_ptp_init_rtc(bp, true); return 0; open_err_irq: @@ -11403,7 +11448,7 @@ static void bnxt_timer(struct timer_list *t) if (bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY) bnxt_fw_health_check(bp); - if (bp->link_info.link_up && bp->stats_coal_ticks) { + if (BNXT_LINK_IS_UP(bp) && bp->stats_coal_ticks) { set_bit(BNXT_PERIODIC_STATS_SP_EVENT, &bp->sp_event); bnxt_queue_sp_work(bp); } @@ -12104,11 +12149,6 @@ int bnxt_fw_init_one(struct bnxt *bp) if (rc) return rc; - /* In case fw capabilities have changed, destroy the unneeded - * reporters and create newly capable ones. - */ - bnxt_dl_fw_reporters_destroy(bp, false); - bnxt_dl_fw_reporters_create(bp); bnxt_fw_init_one_p3(bp); return 0; } @@ -12937,7 +12977,7 @@ static void bnxt_remove_one(struct pci_dev *pdev) cancel_delayed_work_sync(&bp->fw_reset_task); bp->sp_event = 0; - bnxt_dl_fw_reporters_destroy(bp, true); + bnxt_dl_fw_reporters_destroy(bp); bnxt_dl_unregister(bp); bnxt_shutdown_tc(bp); @@ -13430,7 +13470,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) #ifdef CONFIG_BNXT_SRIOV init_waitqueue_head(&bp->sriov_cfg_wait); - mutex_init(&bp->sriov_lock); #endif if (BNXT_SUPPORTS_TPA(bp)) { bp->gro_func = bnxt_gro_func_5730x; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 666fc1e7a7d2..61aa3e8c5952 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1175,7 +1175,11 @@ struct bnxt_link_info { #define BNXT_PHY_STATE_ENABLED 0 #define BNXT_PHY_STATE_DISABLED 1 - u8 link_up; + u8 link_state; +#define BNXT_LINK_STATE_UNKNOWN 0 +#define BNXT_LINK_STATE_DOWN 1 +#define BNXT_LINK_STATE_UP 2 +#define BNXT_LINK_IS_UP(bp) ((bp)->link_info.link_state == BNXT_LINK_STATE_UP) u8 duplex; #define BNXT_LINK_DUPLEX_HALF PORT_PHY_QCFG_RESP_DUPLEX_STATE_HALF #define BNXT_LINK_DUPLEX_FULL PORT_PHY_QCFG_RESP_DUPLEX_STATE_FULL @@ -1958,6 +1962,7 @@ struct bnxt { #define BNXT_FW_CAP_EXT_STATS_SUPPORTED 0x00040000 #define BNXT_FW_CAP_ERR_RECOVER_RELOAD 0x00100000 #define BNXT_FW_CAP_HOT_RESET 0x00200000 + #define BNXT_FW_CAP_PTP_RTC 0x00400000 #define BNXT_FW_CAP_VLAN_RX_STRIP 0x01000000 #define BNXT_FW_CAP_VLAN_TX_INSERT 0x02000000 #define BNXT_FW_CAP_EXT_HW_STATS_SUPPORTED 0x04000000 @@ -2067,12 +2072,6 @@ struct bnxt { wait_queue_head_t sriov_cfg_wait; bool sriov_cfg; #define BNXT_SRIOV_CFG_WAIT_TMO msecs_to_jiffies(10000) - - /* lock to protect VF-rep creation/cleanup via - * multiple paths such as ->sriov_configure() and - * devlink ->eswitch_mode_set() - */ - struct mutex sriov_lock; #endif #if BITS_PER_LONG == 32 @@ -2099,8 +2098,8 @@ struct bnxt { u32 lpi_tmr_lo; u32 lpi_tmr_hi; - /* copied from flags in hwrm_port_phy_qcaps_output */ - u8 phy_flags; + /* copied from flags and flags2 in hwrm_port_phy_qcaps_output */ + u32 phy_flags; #define BNXT_PHY_FL_EEE_CAP PORT_PHY_QCAPS_RESP_FLAGS_EEE_SUPPORTED #define BNXT_PHY_FL_EXT_LPBK PORT_PHY_QCAPS_RESP_FLAGS_EXTERNAL_LPBK_SUPPORTED #define BNXT_PHY_FL_AN_PHY_LPBK PORT_PHY_QCAPS_RESP_FLAGS_AUTONEG_LPBK_SUPPORTED @@ -2109,6 +2108,8 @@ struct bnxt { #define BNXT_PHY_FL_NO_PHY_LPBK PORT_PHY_QCAPS_RESP_FLAGS_LOCAL_LPBK_NOT_SUPPORTED #define BNXT_PHY_FL_FW_MANAGED_LKDN PORT_PHY_QCAPS_RESP_FLAGS_FW_MANAGED_LINK_DOWN #define BNXT_PHY_FL_NO_FCS PORT_PHY_QCAPS_RESP_FLAGS_NO_FCS +#define BNXT_PHY_FL_NO_PAUSE (PORT_PHY_QCAPS_RESP_FLAGS2_PAUSE_UNSUPPORTED << 8) +#define BNXT_PHY_FL_NO_PFC (PORT_PHY_QCAPS_RESP_FLAGS2_PFC_UNSUPPORTED << 8) u8 num_tests; struct bnxt_test_info *test_info; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c index 217ff597cdf2..caab3d626a2a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c @@ -627,7 +627,8 @@ static int bnxt_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc) int rc; if (!(bp->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) || - !(bp->dcbx_cap & DCB_CAP_DCBX_HOST)) + !(bp->dcbx_cap & DCB_CAP_DCBX_HOST) || + (bp->phy_flags & BNXT_PHY_FL_NO_PAUSE)) return -EINVAL; if (!my_pfc) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index f6e21fac0e69..0c17f90d44a2 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -241,37 +241,37 @@ static const struct devlink_health_reporter_ops bnxt_dl_fw_reporter_ops = { .recover = bnxt_fw_recover, }; -void bnxt_dl_fw_reporters_create(struct bnxt *bp) +static struct devlink_health_reporter * +__bnxt_dl_reporter_create(struct bnxt *bp, + const struct devlink_health_reporter_ops *ops) { - struct bnxt_fw_health *health = bp->fw_health; + struct devlink_health_reporter *reporter; - if (!health || health->fw_reporter) - return; - - health->fw_reporter = - devlink_health_reporter_create(bp->dl, &bnxt_dl_fw_reporter_ops, - 0, bp); - if (IS_ERR(health->fw_reporter)) { - netdev_warn(bp->dev, "Failed to create FW health reporter, rc = %ld\n", - PTR_ERR(health->fw_reporter)); - health->fw_reporter = NULL; - bp->fw_cap &= ~BNXT_FW_CAP_ERROR_RECOVERY; + reporter = devlink_health_reporter_create(bp->dl, ops, 0, bp); + if (IS_ERR(reporter)) { + netdev_warn(bp->dev, "Failed to create %s health reporter, rc = %ld\n", + ops->name, PTR_ERR(reporter)); + return NULL; } + + return reporter; } -void bnxt_dl_fw_reporters_destroy(struct bnxt *bp, bool all) +void bnxt_dl_fw_reporters_create(struct bnxt *bp) { - struct bnxt_fw_health *health = bp->fw_health; + struct bnxt_fw_health *fw_health = bp->fw_health; - if (!health) - return; + if (fw_health && !fw_health->fw_reporter) + fw_health->fw_reporter = __bnxt_dl_reporter_create(bp, &bnxt_dl_fw_reporter_ops); +} - if ((bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY) && !all) - return; +void bnxt_dl_fw_reporters_destroy(struct bnxt *bp) +{ + struct bnxt_fw_health *fw_health = bp->fw_health; - if (health->fw_reporter) { - devlink_health_reporter_destroy(health->fw_reporter); - health->fw_reporter = NULL; + if (fw_health && fw_health->fw_reporter) { + devlink_health_reporter_destroy(fw_health->fw_reporter); + fw_health->fw_reporter = NULL; } } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.h index a715458abc30..b8105065367b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.h @@ -75,7 +75,7 @@ void bnxt_devlink_health_fw_report(struct bnxt *bp); void bnxt_dl_health_fw_status_update(struct bnxt *bp, bool healthy); void bnxt_dl_health_fw_recovery_done(struct bnxt *bp); void bnxt_dl_fw_reporters_create(struct bnxt *bp); -void bnxt_dl_fw_reporters_destroy(struct bnxt *bp, bool all); +void bnxt_dl_fw_reporters_destroy(struct bnxt *bp); int bnxt_dl_register(struct bnxt *bp); void bnxt_dl_unregister(struct bnxt *bp); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 8aaa2335f848..22e965e18fbc 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -803,9 +804,11 @@ static void bnxt_get_ringparam(struct net_device *dev, if (bp->flags & BNXT_FLAG_AGG_RINGS) { ering->rx_max_pending = BNXT_MAX_RX_DESC_CNT_JUM_ENA; ering->rx_jumbo_max_pending = BNXT_MAX_RX_JUM_DESC_CNT; + kernel_ering->tcp_data_split = ETHTOOL_TCP_DATA_SPLIT_ENABLED; } else { ering->rx_max_pending = BNXT_MAX_RX_DESC_CNT; ering->rx_jumbo_max_pending = 0; + kernel_ering->tcp_data_split = ETHTOOL_TCP_DATA_SPLIT_DISABLED; } ering->tx_max_pending = BNXT_MAX_TX_DESC_CNT; @@ -1659,15 +1662,19 @@ static void bnxt_fw_to_ethtool_support_fec(struct bnxt_link_info *link_info, static void bnxt_fw_to_ethtool_support_spds(struct bnxt_link_info *link_info, struct ethtool_link_ksettings *lk_ksettings) { + struct bnxt *bp = container_of(link_info, struct bnxt, link_info); u16 fw_speeds = link_info->support_speeds; BNXT_FW_TO_ETHTOOL_SPDS(fw_speeds, 0, lk_ksettings, supported); fw_speeds = link_info->support_pam4_speeds; BNXT_FW_TO_ETHTOOL_PAM4_SPDS(fw_speeds, lk_ksettings, supported); - ethtool_link_ksettings_add_link_mode(lk_ksettings, supported, Pause); - ethtool_link_ksettings_add_link_mode(lk_ksettings, supported, - Asym_Pause); + if (!(bp->phy_flags & BNXT_PHY_FL_NO_PAUSE)) { + ethtool_link_ksettings_add_link_mode(lk_ksettings, supported, + Pause); + ethtool_link_ksettings_add_link_mode(lk_ksettings, supported, + Asym_Pause); + } if (link_info->support_auto_speeds || link_info->support_pam4_auto_speeds) @@ -1898,7 +1905,8 @@ static int bnxt_set_link_ksettings(struct net_device *dev, /* any change to autoneg will cause link change, therefore the * driver should put back the original pause setting in autoneg */ - set_pause = true; + if (!(bp->phy_flags & BNXT_PHY_FL_NO_PAUSE)) + set_pause = true; } else { u8 phy_type = link_info->phy_type; @@ -2090,7 +2098,7 @@ static int bnxt_set_pauseparam(struct net_device *dev, struct bnxt *bp = netdev_priv(dev); struct bnxt_link_info *link_info = &bp->link_info; - if (!BNXT_PHY_CFG_ABLE(bp)) + if (!BNXT_PHY_CFG_ABLE(bp) || (bp->phy_flags & BNXT_PHY_FL_NO_PAUSE)) return -EOPNOTSUPP; mutex_lock(&bp->link_lock); @@ -2101,9 +2109,7 @@ static int bnxt_set_pauseparam(struct net_device *dev, } link_info->autoneg |= BNXT_AUTONEG_FLOW_CTRL; - if (bp->hwrm_spec_code >= 0x10201) - link_info->req_flow_ctrl = - PORT_PHY_CFG_REQ_AUTO_PAUSE_AUTONEG_PAUSE; + link_info->req_flow_ctrl = 0; } else { /* when transition from auto pause to force pause, * force a link change @@ -2132,7 +2138,7 @@ static u32 bnxt_get_link(struct net_device *dev) struct bnxt *bp = netdev_priv(dev); /* TODO: handle MF, VF, driver close case */ - return bp->link_info.link_up; + return BNXT_LINK_IS_UP(bp); } int bnxt_hwrm_nvm_get_dev_info(struct bnxt *bp, @@ -2509,6 +2515,7 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware u8 *kmem = NULL; u32 modify_len; u32 item_len; + u8 cmd_err; u16 index; int rc; @@ -2592,6 +2599,8 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware } rc = hwrm_req_send_silent(bp, install); + if (!rc) + break; if (defrag_attempted) { /* We have tried to defragment already in the previous @@ -2600,15 +2609,24 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware break; } - if (rc && ((struct hwrm_err_output *)resp)->cmd_err == - NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR) { + cmd_err = ((struct hwrm_err_output *)resp)->cmd_err; + + switch (cmd_err) { + case NVM_INSTALL_UPDATE_CMD_ERR_CODE_ANTI_ROLLBACK: + netdev_err(dev, "HWRM_NVM_INSTALL_UPDATE failure Anti-rollback detected\n"); + rc = -EALREADY; + break; + case NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR: install->flags = cpu_to_le16(NVM_INSTALL_UPDATE_REQ_FLAGS_ALLOWED_TO_DEFRAG); rc = hwrm_req_send_silent(bp, install); + if (!rc) + break; - if (rc && ((struct hwrm_err_output *)resp)->cmd_err == - NVM_INSTALL_UPDATE_CMD_ERR_CODE_NO_SPACE) { + cmd_err = ((struct hwrm_err_output *)resp)->cmd_err; + + if (cmd_err == NVM_INSTALL_UPDATE_CMD_ERR_CODE_NO_SPACE) { /* FW has cleared NVM area, driver will create * UPDATE directory and try the flash again */ @@ -2618,11 +2636,13 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware BNX_DIR_TYPE_UPDATE, BNX_DIR_ORDINAL_FIRST, 0, 0, item_len, NULL, 0); - } else if (rc) { - netdev_err(dev, "HWRM_NVM_INSTALL_UPDATE failure rc :%x\n", rc); + if (!rc) + break; } - } else if (rc) { - netdev_err(dev, "HWRM_NVM_INSTALL_UPDATE failure rc :%x\n", rc); + fallthrough; + default: + netdev_err(dev, "HWRM_NVM_INSTALL_UPDATE failure rc :%x cmd_err :%x\n", + rc, cmd_err); } } while (defrag_attempted && !rc); @@ -3324,7 +3344,7 @@ static int bnxt_disable_an_for_lpbk(struct bnxt *bp, return rc; fw_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_1GB; - if (bp->link_info.link_up) + if (BNXT_LINK_IS_UP(bp)) fw_speed = bp->link_info.link_speed; else if (fw_advertising & BNXT_LINK_SPEED_MSK_10GB) fw_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_10GB; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index ea86c54247c7..b7100edbd6dd 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -369,6 +369,12 @@ struct cmd_nums { #define HWRM_FUNC_PTP_EXT_CFG 0x1a0UL #define HWRM_FUNC_PTP_EXT_QCFG 0x1a1UL #define HWRM_FUNC_KEY_CTX_ALLOC 0x1a2UL + #define HWRM_FUNC_BACKING_STORE_CFG_V2 0x1a3UL + #define HWRM_FUNC_BACKING_STORE_QCFG_V2 0x1a4UL + #define HWRM_FUNC_DBR_PACING_CFG 0x1a5UL + #define HWRM_FUNC_DBR_PACING_QCFG 0x1a6UL + #define HWRM_FUNC_DBR_PACING_BROADCAST_EVENT 0x1a7UL + #define HWRM_FUNC_BACKING_STORE_QCAPS_V2 0x1a8UL #define HWRM_SELFTEST_QLIST 0x200UL #define HWRM_SELFTEST_EXEC 0x201UL #define HWRM_SELFTEST_IRQ 0x202UL @@ -390,6 +396,9 @@ struct cmd_nums { #define HWRM_MFG_PRVSN_IMPORT_CERT 0x212UL #define HWRM_MFG_PRVSN_GET_STATE 0x213UL #define HWRM_MFG_GET_NVM_MEASUREMENT 0x214UL + #define HWRM_MFG_PSOC_QSTATUS 0x215UL + #define HWRM_MFG_SELFTEST_QLIST 0x216UL + #define HWRM_MFG_SELFTEST_EXEC 0x217UL #define HWRM_TF 0x2bcUL #define HWRM_TF_VERSION_GET 0x2bdUL #define HWRM_TF_SESSION_OPEN 0x2c6UL @@ -532,8 +541,8 @@ struct hwrm_err_output { #define HWRM_VERSION_MAJOR 1 #define HWRM_VERSION_MINOR 10 #define HWRM_VERSION_UPDATE 2 -#define HWRM_VERSION_RSVD 63 -#define HWRM_VERSION_STR "1.10.2.63" +#define HWRM_VERSION_RSVD 73 +#define HWRM_VERSION_STR "1.10.2.73" /* hwrm_ver_get_input (size:192b/24B) */ struct hwrm_ver_get_input { @@ -757,10 +766,11 @@ struct hwrm_async_event_cmpl { #define ASYNC_EVENT_CMPL_EVENT_ID_DEFERRED_RESPONSE 0x40UL #define ASYNC_EVENT_CMPL_EVENT_ID_PFC_WATCHDOG_CFG_CHANGE 0x41UL #define ASYNC_EVENT_CMPL_EVENT_ID_ECHO_REQUEST 0x42UL - #define ASYNC_EVENT_CMPL_EVENT_ID_PHC_MASTER 0x43UL + #define ASYNC_EVENT_CMPL_EVENT_ID_PHC_UPDATE 0x43UL #define ASYNC_EVENT_CMPL_EVENT_ID_PPS_TIMESTAMP 0x44UL #define ASYNC_EVENT_CMPL_EVENT_ID_ERROR_REPORT 0x45UL - #define ASYNC_EVENT_CMPL_EVENT_ID_MAX_RGTR_EVENT_ID 0x46UL + #define ASYNC_EVENT_CMPL_EVENT_ID_DOORBELL_PACING_THRESHOLD 0x46UL + #define ASYNC_EVENT_CMPL_EVENT_ID_MAX_RGTR_EVENT_ID 0x47UL #define ASYNC_EVENT_CMPL_EVENT_ID_FW_TRACE_MSG 0xfeUL #define ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR 0xffUL #define ASYNC_EVENT_CMPL_EVENT_ID_LAST ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR @@ -1112,34 +1122,37 @@ struct hwrm_async_event_cmpl_echo_request { __le32 event_data1; }; -/* hwrm_async_event_cmpl_phc_master (size:128b/16B) */ -struct hwrm_async_event_cmpl_phc_master { +/* hwrm_async_event_cmpl_phc_update (size:128b/16B) */ +struct hwrm_async_event_cmpl_phc_update { __le16 type; - #define ASYNC_EVENT_CMPL_PHC_MASTER_TYPE_MASK 0x3fUL - #define ASYNC_EVENT_CMPL_PHC_MASTER_TYPE_SFT 0 - #define ASYNC_EVENT_CMPL_PHC_MASTER_TYPE_HWRM_ASYNC_EVENT 0x2eUL - #define ASYNC_EVENT_CMPL_PHC_MASTER_TYPE_LAST ASYNC_EVENT_CMPL_PHC_MASTER_TYPE_HWRM_ASYNC_EVENT + #define ASYNC_EVENT_CMPL_PHC_UPDATE_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_PHC_UPDATE_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_TYPE_LAST ASYNC_EVENT_CMPL_PHC_UPDATE_TYPE_HWRM_ASYNC_EVENT __le16 event_id; - #define ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_ID_PHC_MASTER 0x43UL - #define ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_ID_LAST ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_ID_PHC_MASTER + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_ID_PHC_UPDATE 0x43UL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_ID_LAST ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_ID_PHC_UPDATE __le32 event_data2; - #define ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_DATA2_PHC_MASTER_FID_MASK 0xffffUL - #define ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_DATA2_PHC_MASTER_FID_SFT 0 - #define ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_DATA2_PHC_SEC_FID_MASK 0xffff0000UL - #define ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_DATA2_PHC_SEC_FID_SFT 16 + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA2_PHC_MASTER_FID_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA2_PHC_MASTER_FID_SFT 0 + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA2_PHC_SEC_FID_MASK 0xffff0000UL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA2_PHC_SEC_FID_SFT 16 u8 opaque_v; - #define ASYNC_EVENT_CMPL_PHC_MASTER_V 0x1UL - #define ASYNC_EVENT_CMPL_PHC_MASTER_OPAQUE_MASK 0xfeUL - #define ASYNC_EVENT_CMPL_PHC_MASTER_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_PHC_UPDATE_V 0x1UL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_DATA1_FLAGS_MASK 0xfUL - #define ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_DATA1_FLAGS_SFT 0 - #define ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_DATA1_FLAGS_PHC_MASTER 0x1UL - #define ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_DATA1_FLAGS_PHC_SECONDARY 0x2UL - #define ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_DATA1_FLAGS_PHC_FAILOVER 0x3UL - #define ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_DATA1_FLAGS_LAST ASYNC_EVENT_CMPL_PHC_MASTER_EVENT_DATA1_FLAGS_PHC_FAILOVER + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_FLAGS_MASK 0xfUL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_FLAGS_SFT 0 + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_FLAGS_PHC_MASTER 0x1UL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_FLAGS_PHC_SECONDARY 0x2UL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_FLAGS_PHC_FAILOVER 0x3UL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_FLAGS_PHC_RTC_UPDATE 0x4UL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_FLAGS_LAST ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_FLAGS_PHC_RTC_UPDATE + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_PHC_TIME_MSB_MASK 0xffff0UL + #define ASYNC_EVENT_CMPL_PHC_UPDATE_EVENT_DATA1_PHC_TIME_MSB_SFT 4 }; /* hwrm_async_event_cmpl_pps_timestamp (size:128b/16B) */ @@ -1330,6 +1343,30 @@ struct hwrm_async_event_cmpl_error_report_nvm { #define ASYNC_EVENT_CMPL_ERROR_REPORT_NVM_EVENT_DATA1_NVM_ERR_TYPE_LAST ASYNC_EVENT_CMPL_ERROR_REPORT_NVM_EVENT_DATA1_NVM_ERR_TYPE_ERASE }; +/* hwrm_async_event_cmpl_error_report_doorbell_drop_threshold (size:128b/16B) */ +struct hwrm_async_event_cmpl_error_report_doorbell_drop_threshold { + __le16 type; + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_TYPE_LAST ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_TYPE_HWRM_ASYNC_EVENT + __le16 event_id; + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_EVENT_ID_ERROR_REPORT 0x45UL + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_EVENT_ID_LAST ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_EVENT_ID_ERROR_REPORT + __le32 event_data2; + u8 opaque_v; + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_V 0x1UL + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_OPAQUE_SFT 1 + u8 timestamp_lo; + __le16 timestamp_hi; + __le32 event_data1; + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_EVENT_DATA1_ERROR_TYPE_MASK 0xffUL + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_EVENT_DATA1_ERROR_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_EVENT_DATA1_ERROR_TYPE_DOORBELL_DROP_THRESHOLD 0x4UL + #define ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_EVENT_DATA1_ERROR_TYPE_LAST ASYNC_EVENT_CMPL_ERROR_REPORT_DOORBELL_DROP_THRESHOLD_EVENT_DATA1_ERROR_TYPE_DOORBELL_DROP_THRESHOLD +}; + /* hwrm_func_reset_input (size:192b/24B) */ struct hwrm_func_reset_input { __le16 req_type; @@ -1589,6 +1626,10 @@ struct hwrm_func_qcaps_output { #define FUNC_QCAPS_RESP_FLAGS_EXT_EP_RATE_CONTROL 0x800000UL #define FUNC_QCAPS_RESP_FLAGS_EXT_MIN_BW_SUPPORTED 0x1000000UL #define FUNC_QCAPS_RESP_FLAGS_EXT_TX_COAL_CMPL_CAP 0x2000000UL + #define FUNC_QCAPS_RESP_FLAGS_EXT_BS_V2_SUPPORTED 0x4000000UL + #define FUNC_QCAPS_RESP_FLAGS_EXT_BS_V2_REQUIRED 0x8000000UL + #define FUNC_QCAPS_RESP_FLAGS_EXT_PTP_64BIT_RTC_SUPPORTED 0x10000000UL + #define FUNC_QCAPS_RESP_FLAGS_EXT_DBR_PACING_SUPPORTED 0x20000000UL u8 max_schqs; u8 mpc_chnls_cap; #define FUNC_QCAPS_RESP_MPC_CHNLS_CAP_TCE 0x1UL @@ -2455,7 +2496,7 @@ struct hwrm_func_backing_store_qcaps_output { __le16 rkc_entry_size; __le32 tkc_max_entries; __le32 rkc_max_entries; - u8 rsvd[7]; + u8 rsvd1[7]; u8 valid; }; @@ -3164,7 +3205,7 @@ struct hwrm_func_ptp_pin_cfg_output { u8 valid; }; -/* hwrm_func_ptp_cfg_input (size:320b/40B) */ +/* hwrm_func_ptp_cfg_input (size:384b/48B) */ struct hwrm_func_ptp_cfg_input { __le16 req_type; __le16 cmpl_ring; @@ -3178,6 +3219,7 @@ struct hwrm_func_ptp_cfg_input { #define FUNC_PTP_CFG_REQ_ENABLES_PTP_FREQ_ADJ_EXT_PERIOD 0x8UL #define FUNC_PTP_CFG_REQ_ENABLES_PTP_FREQ_ADJ_EXT_UP 0x10UL #define FUNC_PTP_CFG_REQ_ENABLES_PTP_FREQ_ADJ_EXT_PHASE 0x20UL + #define FUNC_PTP_CFG_REQ_ENABLES_PTP_SET_TIME 0x40UL u8 ptp_pps_event; #define FUNC_PTP_CFG_REQ_PTP_PPS_EVENT_INTERNAL 0x1UL #define FUNC_PTP_CFG_REQ_PTP_PPS_EVENT_EXTERNAL 0x2UL @@ -3204,6 +3246,7 @@ struct hwrm_func_ptp_cfg_input { __le32 ptp_freq_adj_ext_up; __le32 ptp_freq_adj_ext_phase_lower; __le32 ptp_freq_adj_ext_phase_upper; + __le64 ptp_set_time; }; /* hwrm_func_ptp_cfg_output (size:128b/16B) */ @@ -3243,6 +3286,308 @@ struct hwrm_func_ptp_ts_query_output { u8 valid; }; +/* hwrm_func_ptp_ext_cfg_input (size:256b/32B) */ +struct hwrm_func_ptp_ext_cfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 enables; + #define FUNC_PTP_EXT_CFG_REQ_ENABLES_PHC_MASTER_FID 0x1UL + #define FUNC_PTP_EXT_CFG_REQ_ENABLES_PHC_SEC_FID 0x2UL + #define FUNC_PTP_EXT_CFG_REQ_ENABLES_PHC_SEC_MODE 0x4UL + #define FUNC_PTP_EXT_CFG_REQ_ENABLES_FAILOVER_TIMER 0x8UL + __le16 phc_master_fid; + __le16 phc_sec_fid; + u8 phc_sec_mode; + #define FUNC_PTP_EXT_CFG_REQ_PHC_SEC_MODE_SWITCH 0x0UL + #define FUNC_PTP_EXT_CFG_REQ_PHC_SEC_MODE_ALL 0x1UL + #define FUNC_PTP_EXT_CFG_REQ_PHC_SEC_MODE_PF_ONLY 0x2UL + #define FUNC_PTP_EXT_CFG_REQ_PHC_SEC_MODE_LAST FUNC_PTP_EXT_CFG_REQ_PHC_SEC_MODE_PF_ONLY + u8 unused_0; + __le32 failover_timer; + u8 unused_1[4]; +}; + +/* hwrm_func_ptp_ext_cfg_output (size:128b/16B) */ +struct hwrm_func_ptp_ext_cfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 unused_0[7]; + u8 valid; +}; + +/* hwrm_func_ptp_ext_qcfg_input (size:192b/24B) */ +struct hwrm_func_ptp_ext_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + u8 unused_0[8]; +}; + +/* hwrm_func_ptp_ext_qcfg_output (size:256b/32B) */ +struct hwrm_func_ptp_ext_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 phc_master_fid; + __le16 phc_sec_fid; + __le16 phc_active_fid0; + __le16 phc_active_fid1; + __le32 last_failover_event; + __le16 from_fid; + __le16 to_fid; + u8 unused_0[7]; + u8 valid; +}; + +/* hwrm_func_backing_store_cfg_v2_input (size:448b/56B) */ +struct hwrm_func_backing_store_cfg_v2_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 type; + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_QP 0x0UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_SRQ 0x1UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_CQ 0x2UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_VNIC 0x3UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_STAT 0x4UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_SP_TQM_RING 0x5UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_FP_TQM_RING 0x6UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_MRAV 0xeUL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_TIM 0xfUL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_TKC 0x13UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_RKC 0x14UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_MP_TQM_RING 0x15UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_INVALID 0xffffUL + #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_LAST FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_INVALID + __le16 instance; + __le32 flags; + #define FUNC_BACKING_STORE_CFG_V2_REQ_FLAGS_PREBOOT_MODE 0x1UL + __le64 page_dir; + __le32 num_entries; + __le16 entry_size; + u8 page_size_pbl_level; + #define FUNC_BACKING_STORE_CFG_V2_REQ_PBL_LEVEL_MASK 0xfUL + #define FUNC_BACKING_STORE_CFG_V2_REQ_PBL_LEVEL_SFT 0 + #define FUNC_BACKING_STORE_CFG_V2_REQ_PBL_LEVEL_LVL_0 0x0UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_PBL_LEVEL_LVL_1 0x1UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_PBL_LEVEL_LVL_2 0x2UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_PBL_LEVEL_LAST FUNC_BACKING_STORE_CFG_V2_REQ_PBL_LEVEL_LVL_2 + #define FUNC_BACKING_STORE_CFG_V2_REQ_PAGE_SIZE_MASK 0xf0UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_PAGE_SIZE_SFT 4 + #define FUNC_BACKING_STORE_CFG_V2_REQ_PAGE_SIZE_PG_4K (0x0UL << 4) + #define FUNC_BACKING_STORE_CFG_V2_REQ_PAGE_SIZE_PG_8K (0x1UL << 4) + #define FUNC_BACKING_STORE_CFG_V2_REQ_PAGE_SIZE_PG_64K (0x2UL << 4) + #define FUNC_BACKING_STORE_CFG_V2_REQ_PAGE_SIZE_PG_2M (0x3UL << 4) + #define FUNC_BACKING_STORE_CFG_V2_REQ_PAGE_SIZE_PG_8M (0x4UL << 4) + #define FUNC_BACKING_STORE_CFG_V2_REQ_PAGE_SIZE_PG_1G (0x5UL << 4) + #define FUNC_BACKING_STORE_CFG_V2_REQ_PAGE_SIZE_LAST FUNC_BACKING_STORE_CFG_V2_REQ_PAGE_SIZE_PG_1G + u8 subtype_valid_cnt; + __le32 split_entry_0; + __le32 split_entry_1; + __le32 split_entry_2; + __le32 split_entry_3; +}; + +/* hwrm_func_backing_store_cfg_v2_output (size:128b/16B) */ +struct hwrm_func_backing_store_cfg_v2_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 rsvd0[7]; + u8 valid; +}; + +/* hwrm_func_backing_store_qcfg_v2_input (size:192b/24B) */ +struct hwrm_func_backing_store_qcfg_v2_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 type; + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_QP 0x0UL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_SRQ 0x1UL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_CQ 0x2UL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_VNIC 0x3UL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_STAT 0x4UL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_SP_TQM_RING 0x5UL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_FP_TQM_RING 0x6UL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_MRAV 0xeUL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_TIM 0xfUL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_TKC 0x13UL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_RKC 0x14UL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_MP_TQM_RING 0x15UL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_INVALID 0xffffUL + #define FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_LAST FUNC_BACKING_STORE_QCFG_V2_REQ_TYPE_INVALID + __le16 instance; + u8 rsvd[4]; +}; + +/* hwrm_func_backing_store_qcfg_v2_output (size:448b/56B) */ +struct hwrm_func_backing_store_qcfg_v2_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 type; + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_QP 0x0UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_SRQ 0x1UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_CQ 0x2UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_VNIC 0x3UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_STAT 0x4UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_SP_TQM_RING 0x5UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_FP_TQM_RING 0x6UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_MRAV 0xeUL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_TIM 0xfUL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_TKC 0x13UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_RKC 0x14UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_MP_TQM_RING 0x15UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_INVALID 0xffffUL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_LAST FUNC_BACKING_STORE_QCFG_V2_RESP_TYPE_INVALID + __le16 instance; + __le32 flags; + __le64 page_dir; + __le32 num_entries; + u8 page_size_pbl_level; + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PBL_LEVEL_MASK 0xfUL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PBL_LEVEL_SFT 0 + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PBL_LEVEL_LVL_0 0x0UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PBL_LEVEL_LVL_1 0x1UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PBL_LEVEL_LVL_2 0x2UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PBL_LEVEL_LAST FUNC_BACKING_STORE_QCFG_V2_RESP_PBL_LEVEL_LVL_2 + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PAGE_SIZE_MASK 0xf0UL + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PAGE_SIZE_SFT 4 + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PAGE_SIZE_PG_4K (0x0UL << 4) + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PAGE_SIZE_PG_8K (0x1UL << 4) + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PAGE_SIZE_PG_64K (0x2UL << 4) + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PAGE_SIZE_PG_2M (0x3UL << 4) + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PAGE_SIZE_PG_8M (0x4UL << 4) + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PAGE_SIZE_PG_1G (0x5UL << 4) + #define FUNC_BACKING_STORE_QCFG_V2_RESP_PAGE_SIZE_LAST FUNC_BACKING_STORE_QCFG_V2_RESP_PAGE_SIZE_PG_1G + u8 subtype_valid_cnt; + u8 rsvd[2]; + __le32 split_entry_0; + __le32 split_entry_1; + __le32 split_entry_2; + __le32 split_entry_3; + u8 rsvd2[7]; + u8 valid; +}; + +/* qpc_split_entries (size:128b/16B) */ +struct qpc_split_entries { + __le32 qp_num_l2_entries; + __le32 qp_num_qp1_entries; + __le32 rsvd[2]; +}; + +/* srq_split_entries (size:128b/16B) */ +struct srq_split_entries { + __le32 srq_num_l2_entries; + __le32 rsvd; + __le32 rsvd2[2]; +}; + +/* cq_split_entries (size:128b/16B) */ +struct cq_split_entries { + __le32 cq_num_l2_entries; + __le32 rsvd; + __le32 rsvd2[2]; +}; + +/* vnic_split_entries (size:128b/16B) */ +struct vnic_split_entries { + __le32 vnic_num_vnic_entries; + __le32 rsvd; + __le32 rsvd2[2]; +}; + +/* mrav_split_entries (size:128b/16B) */ +struct mrav_split_entries { + __le32 mrav_num_av_entries; + __le32 rsvd; + __le32 rsvd2[2]; +}; + +/* hwrm_func_backing_store_qcaps_v2_input (size:192b/24B) */ +struct hwrm_func_backing_store_qcaps_v2_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 type; + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_QP 0x0UL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_SRQ 0x1UL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_CQ 0x2UL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_VNIC 0x3UL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_STAT 0x4UL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_SP_TQM_RING 0x5UL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_FP_TQM_RING 0x6UL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_MRAV 0xeUL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_TIM 0xfUL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_TKC 0x13UL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_RKC 0x14UL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_MP_TQM_RING 0x15UL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_INVALID 0xffffUL + #define FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_LAST FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_INVALID + u8 rsvd[6]; +}; + +/* hwrm_func_backing_store_qcaps_v2_output (size:448b/56B) */ +struct hwrm_func_backing_store_qcaps_v2_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 type; + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_QP 0x0UL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_SRQ 0x1UL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_CQ 0x2UL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_VNIC 0x3UL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_STAT 0x4UL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_SP_TQM_RING 0x5UL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_FP_TQM_RING 0x6UL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_MRAV 0xeUL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_TIM 0xfUL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_TKC 0x13UL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_RKC 0x14UL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_MP_TQM_RING 0x15UL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_INVALID 0xffffUL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_LAST FUNC_BACKING_STORE_QCAPS_V2_RESP_TYPE_INVALID + __le16 entry_size; + __le32 flags; + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_FLAGS_ENABLE_CTX_KIND_INIT 0x1UL + #define FUNC_BACKING_STORE_QCAPS_V2_RESP_FLAGS_TYPE_VALID 0x2UL + __le32 instance_bit_map; + u8 ctx_init_value; + u8 ctx_init_offset; + u8 entry_multiple; + u8 rsvd; + __le32 max_num_entries; + __le32 min_num_entries; + __le16 next_valid_type; + u8 subtype_valid_cnt; + u8 rsvd2; + __le32 split_entry_0; + __le32 split_entry_1; + __le32 split_entry_2; + __le32 split_entry_3; + u8 rsvd3[3]; + u8 valid; +}; + /* hwrm_func_drv_if_change_input (size:192b/24B) */ struct hwrm_func_drv_if_change_input { __le16 req_type; @@ -3741,7 +4086,7 @@ struct hwrm_port_phy_qcfg_output { u8 valid; }; -/* hwrm_port_mac_cfg_input (size:384b/48B) */ +/* hwrm_port_mac_cfg_input (size:448b/56B) */ struct hwrm_port_mac_cfg_input { __le16 req_type; __le16 cmpl_ring; @@ -3807,7 +4152,8 @@ struct hwrm_port_mac_cfg_input { #define PORT_MAC_CFG_REQ_COS_FIELD_CFG_DEFAULT_COS_SFT 5 u8 unused_0[3]; __le32 ptp_freq_adj_ppb; - __le32 ptp_adj_phase; + u8 unused_1[4]; + __le64 ptp_adj_phase; }; /* hwrm_port_mac_cfg_output (size:128b/16B) */ @@ -3850,6 +4196,7 @@ struct hwrm_port_mac_ptp_qcfg_output { #define PORT_MAC_PTP_QCFG_RESP_FLAGS_ONE_STEP_TX_TS 0x4UL #define PORT_MAC_PTP_QCFG_RESP_FLAGS_HWRM_ACCESS 0x8UL #define PORT_MAC_PTP_QCFG_RESP_FLAGS_PARTIAL_DIRECT_ACCESS_REF_CLOCK 0x10UL + #define PORT_MAC_PTP_QCFG_RESP_FLAGS_RTC_CONFIGURED 0x20UL u8 unused_0[3]; __le32 rx_ts_reg_off_lower; __le32 rx_ts_reg_off_upper; @@ -4339,7 +4686,8 @@ struct hwrm_port_phy_qcaps_output { #define PORT_PHY_QCAPS_RESP_PORT_CNT_2 0x2UL #define PORT_PHY_QCAPS_RESP_PORT_CNT_3 0x3UL #define PORT_PHY_QCAPS_RESP_PORT_CNT_4 0x4UL - #define PORT_PHY_QCAPS_RESP_PORT_CNT_LAST PORT_PHY_QCAPS_RESP_PORT_CNT_4 + #define PORT_PHY_QCAPS_RESP_PORT_CNT_12 0xcUL + #define PORT_PHY_QCAPS_RESP_PORT_CNT_LAST PORT_PHY_QCAPS_RESP_PORT_CNT_12 __le16 supported_speeds_force_mode; #define PORT_PHY_QCAPS_RESP_SUPPORTED_SPEEDS_FORCE_MODE_100MBHD 0x1UL #define PORT_PHY_QCAPS_RESP_SUPPORTED_SPEEDS_FORCE_MODE_100MB 0x2UL @@ -4399,7 +4747,7 @@ struct hwrm_port_phy_qcaps_output { __le16 flags2; #define PORT_PHY_QCAPS_RESP_FLAGS2_PAUSE_UNSUPPORTED 0x1UL #define PORT_PHY_QCAPS_RESP_FLAGS2_PFC_UNSUPPORTED 0x2UL - u8 unused_0[1]; + u8 internal_port_cnt; u8 valid; }; @@ -6221,12 +6569,13 @@ struct hwrm_vnic_rss_cfg_input { __le16 target_id; __le64 resp_addr; __le32 hash_type; - #define VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4 0x1UL - #define VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4 0x2UL - #define VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4 0x4UL - #define VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6 0x8UL - #define VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6 0x10UL - #define VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6 0x20UL + #define VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4 0x1UL + #define VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4 0x2UL + #define VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4 0x4UL + #define VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6 0x8UL + #define VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6 0x10UL + #define VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6 0x20UL + #define VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6_FLOW_LABEL 0x40UL __le16 vnic_id; u8 ring_table_pair_index; u8 hash_mode_flags; @@ -7898,6 +8247,7 @@ struct hwrm_cfa_adv_flow_mgnt_qcaps_output { u8 valid; }; +/* hwrm_tunnel_dst_port_query_input (size:192b/24B) */ struct hwrm_tunnel_dst_port_query_input { __le16 req_type; __le16 cmpl_ring; @@ -8909,6 +9259,50 @@ struct hwrm_dbg_qcfg_output { u8 valid; }; +/* hwrm_dbg_crashdump_medium_cfg_input (size:320b/40B) */ +struct hwrm_dbg_crashdump_medium_cfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 output_dest_flags; + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_TYPE_DDR 0x1UL + __le16 pg_size_lvl; + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_LVL_MASK 0x3UL + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_LVL_SFT 0 + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_LVL_LVL_0 0x0UL + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_LVL_LVL_1 0x1UL + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_LVL_LVL_2 0x2UL + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_LVL_LAST DBG_CRASHDUMP_MEDIUM_CFG_REQ_LVL_LVL_2 + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_PG_SIZE_MASK 0x1cUL + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_PG_SIZE_SFT 2 + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_PG_SIZE_PG_4K (0x0UL << 2) + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_PG_SIZE_PG_8K (0x1UL << 2) + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_PG_SIZE_PG_64K (0x2UL << 2) + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_PG_SIZE_PG_2M (0x3UL << 2) + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_PG_SIZE_PG_8M (0x4UL << 2) + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_PG_SIZE_PG_1G (0x5UL << 2) + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_PG_SIZE_LAST DBG_CRASHDUMP_MEDIUM_CFG_REQ_PG_SIZE_PG_1G + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_UNUSED11_MASK 0xffe0UL + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_UNUSED11_SFT 5 + __le32 size; + __le32 coredump_component_disable_flags; + #define DBG_CRASHDUMP_MEDIUM_CFG_REQ_NVRAM 0x1UL + __le32 unused_0; + __le64 pbl; +}; + +/* hwrm_dbg_crashdump_medium_cfg_output (size:128b/16B) */ +struct hwrm_dbg_crashdump_medium_cfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 unused_1[7]; + u8 valid; +}; + /* coredump_segment_record (size:128b/16B) */ struct coredump_segment_record { __le16 component_id; @@ -9372,8 +9766,35 @@ struct hwrm_nvm_install_update_output { __le16 resp_len; __le64 installed_items; u8 result; - #define NVM_INSTALL_UPDATE_RESP_RESULT_SUCCESS 0x0UL - #define NVM_INSTALL_UPDATE_RESP_RESULT_LAST NVM_INSTALL_UPDATE_RESP_RESULT_SUCCESS + #define NVM_INSTALL_UPDATE_RESP_RESULT_SUCCESS 0x0UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_FAILURE 0xffUL + #define NVM_INSTALL_UPDATE_RESP_RESULT_MALLOC_FAILURE 0xfdUL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_INDEX_PARAMETER 0xfbUL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_TYPE_PARAMETER 0xf3UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_PREREQUISITE 0xf2UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_FILE_HEADER 0xecUL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_SIGNATURE 0xebUL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_PROP_STREAM 0xeaUL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_PROP_LENGTH 0xe9UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_MANIFEST 0xe8UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_TRAILER 0xe7UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_CHECKSUM 0xe6UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_ITEM_CHECKSUM 0xe5UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_DATA_LENGTH 0xe4UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INVALID_DIRECTIVE 0xe1UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_UNSUPPORTED_CHIP_REV 0xceUL + #define NVM_INSTALL_UPDATE_RESP_RESULT_UNSUPPORTED_DEVICE_ID 0xcdUL + #define NVM_INSTALL_UPDATE_RESP_RESULT_UNSUPPORTED_SUBSYS_VENDOR 0xccUL + #define NVM_INSTALL_UPDATE_RESP_RESULT_UNSUPPORTED_SUBSYS_ID 0xcbUL + #define NVM_INSTALL_UPDATE_RESP_RESULT_UNSUPPORTED_PLATFORM 0xc5UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_DUPLICATE_ITEM 0xc4UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_ZERO_LENGTH_ITEM 0xc3UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INSTALL_CHECKSUM_ERROR 0xb9UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INSTALL_DATA_ERROR 0xb8UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_INSTALL_AUTHENTICATION_ERROR 0xb7UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_ITEM_NOT_FOUND 0xb0UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_ITEM_LOCKED 0xa7UL + #define NVM_INSTALL_UPDATE_RESP_RESULT_LAST NVM_INSTALL_UPDATE_RESP_RESULT_ITEM_LOCKED u8 problem_item; #define NVM_INSTALL_UPDATE_RESP_PROBLEM_ITEM_NONE 0x0UL #define NVM_INSTALL_UPDATE_RESP_PROBLEM_ITEM_PACKAGE 0xffUL diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c index 48520967746f..a0b321a19361 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c @@ -19,6 +19,20 @@ #include "bnxt_hwrm.h" #include "bnxt_ptp.h" +static int bnxt_ptp_cfg_settime(struct bnxt *bp, u64 time) +{ + struct hwrm_func_ptp_cfg_input *req; + int rc; + + rc = hwrm_req_init(bp, req, HWRM_FUNC_PTP_CFG); + if (rc) + return rc; + + req->enables = cpu_to_le16(FUNC_PTP_CFG_REQ_ENABLES_PTP_SET_TIME); + req->ptp_set_time = cpu_to_le64(time); + return hwrm_req_send(bp, req); +} + int bnxt_ptp_parse(struct sk_buff *skb, u16 *seq_id, u16 *hdr_off) { unsigned int ptp_class; @@ -48,6 +62,9 @@ static int bnxt_ptp_settime(struct ptp_clock_info *ptp_info, ptp_info); u64 ns = timespec64_to_ns(ts); + if (ptp->bp->fw_cap & BNXT_FW_CAP_PTP_RTC) + return bnxt_ptp_cfg_settime(ptp->bp, ns); + spin_lock_bh(&ptp->ptp_lock); timecounter_init(&ptp->tc, &ptp->cc, ns); spin_unlock_bh(&ptp->ptp_lock); @@ -131,11 +148,47 @@ static int bnxt_ptp_gettimex(struct ptp_clock_info *ptp_info, return 0; } +/* Caller holds ptp_lock */ +void bnxt_ptp_update_current_time(struct bnxt *bp) +{ + struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + + bnxt_refclk_read(ptp->bp, NULL, &ptp->current_time); + WRITE_ONCE(ptp->old_time, ptp->current_time); +} + +static int bnxt_ptp_adjphc(struct bnxt_ptp_cfg *ptp, s64 delta) +{ + struct hwrm_port_mac_cfg_input *req; + int rc; + + rc = hwrm_req_init(ptp->bp, req, HWRM_PORT_MAC_CFG); + if (rc) + return rc; + + req->enables = cpu_to_le32(PORT_MAC_CFG_REQ_ENABLES_PTP_ADJ_PHASE); + req->ptp_adj_phase = cpu_to_le64(delta); + + rc = hwrm_req_send(ptp->bp, req); + if (rc) { + netdev_err(ptp->bp->dev, "ptp adjphc failed. rc = %x\n", rc); + } else { + spin_lock_bh(&ptp->ptp_lock); + bnxt_ptp_update_current_time(ptp->bp); + spin_unlock_bh(&ptp->ptp_lock); + } + + return rc; +} + static int bnxt_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta) { struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, ptp_info); + if (ptp->bp->fw_cap & BNXT_FW_CAP_PTP_RTC) + return bnxt_ptp_adjphc(ptp, delta); + spin_lock_bh(&ptp->ptp_lock); timecounter_adjtime(&ptp->tc, delta); spin_unlock_bh(&ptp->ptp_lock); @@ -714,7 +767,70 @@ static bool bnxt_pps_config_ok(struct bnxt *bp) return !(bp->fw_cap & BNXT_FW_CAP_PTP_PPS) == !ptp->ptp_info.pin_config; } -int bnxt_ptp_init(struct bnxt *bp) +static void bnxt_ptp_timecounter_init(struct bnxt *bp, bool init_tc) +{ + struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + + if (!ptp->ptp_clock) { + memset(&ptp->cc, 0, sizeof(ptp->cc)); + ptp->cc.read = bnxt_cc_read; + ptp->cc.mask = CYCLECOUNTER_MASK(48); + ptp->cc.shift = 0; + ptp->cc.mult = 1; + ptp->next_overflow_check = jiffies + BNXT_PHC_OVERFLOW_PERIOD; + } + if (init_tc) + timecounter_init(&ptp->tc, &ptp->cc, ktime_to_ns(ktime_get_real())); +} + +/* Caller holds ptp_lock */ +void bnxt_ptp_rtc_timecounter_init(struct bnxt_ptp_cfg *ptp, u64 ns) +{ + timecounter_init(&ptp->tc, &ptp->cc, ns); + /* For RTC, cycle_last must be in sync with the timecounter value. */ + ptp->tc.cycle_last = ns & ptp->cc.mask; +} + +int bnxt_ptp_init_rtc(struct bnxt *bp, bool phc_cfg) +{ + struct timespec64 tsp; + u64 ns; + int rc; + + if (!bp->ptp_cfg || !(bp->fw_cap & BNXT_FW_CAP_PTP_RTC)) + return -ENODEV; + + if (!phc_cfg) { + ktime_get_real_ts64(&tsp); + ns = timespec64_to_ns(&tsp); + rc = bnxt_ptp_cfg_settime(bp, ns); + if (rc) + return rc; + } else { + rc = bnxt_hwrm_port_ts_query(bp, PORT_TS_QUERY_REQ_FLAGS_CURRENT_TIME, &ns); + if (rc) + return rc; + } + spin_lock_bh(&bp->ptp_cfg->ptp_lock); + bnxt_ptp_rtc_timecounter_init(bp->ptp_cfg, ns); + spin_unlock_bh(&bp->ptp_cfg->ptp_lock); + + return 0; +} + +static void bnxt_ptp_free(struct bnxt *bp) +{ + struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + + if (ptp->ptp_clock) { + ptp_clock_unregister(ptp->ptp_clock); + ptp->ptp_clock = NULL; + kfree(ptp->ptp_info.pin_config); + ptp->ptp_info.pin_config = NULL; + } +} + +int bnxt_ptp_init(struct bnxt *bp, bool phc_cfg) { struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; int rc; @@ -726,26 +842,23 @@ int bnxt_ptp_init(struct bnxt *bp) if (rc) return rc; + if (bp->fw_cap & BNXT_FW_CAP_PTP_RTC) { + bnxt_ptp_timecounter_init(bp, false); + rc = bnxt_ptp_init_rtc(bp, phc_cfg); + if (rc) + goto out; + } + if (ptp->ptp_clock && bnxt_pps_config_ok(bp)) return 0; - if (ptp->ptp_clock) { - ptp_clock_unregister(ptp->ptp_clock); - ptp->ptp_clock = NULL; - kfree(ptp->ptp_info.pin_config); - ptp->ptp_info.pin_config = NULL; - } + bnxt_ptp_free(bp); + atomic_set(&ptp->tx_avail, BNXT_MAX_TX_TS); spin_lock_init(&ptp->ptp_lock); - memset(&ptp->cc, 0, sizeof(ptp->cc)); - ptp->cc.read = bnxt_cc_read; - ptp->cc.mask = CYCLECOUNTER_MASK(48); - ptp->cc.shift = 0; - ptp->cc.mult = 1; - - ptp->next_overflow_check = jiffies + BNXT_PHC_OVERFLOW_PERIOD; - timecounter_init(&ptp->tc, &ptp->cc, ktime_to_ns(ktime_get_real())); + if (!(bp->fw_cap & BNXT_FW_CAP_PTP_RTC)) + bnxt_ptp_timecounter_init(bp, true); ptp->ptp_info = bnxt_ptp_caps; if ((bp->fw_cap & BNXT_FW_CAP_PTP_PPS)) { @@ -757,8 +870,8 @@ int bnxt_ptp_init(struct bnxt *bp) int err = PTR_ERR(ptp->ptp_clock); ptp->ptp_clock = NULL; - bnxt_unmap_ptp_regs(bp); - return err; + rc = err; + goto out; } if (bp->flags & BNXT_FLAG_CHIP_P5) { spin_lock_bh(&ptp->ptp_lock); @@ -768,6 +881,11 @@ int bnxt_ptp_init(struct bnxt *bp) ptp_schedule_worker(ptp->ptp_clock, 0); } return 0; + +out: + bnxt_ptp_free(bp); + bnxt_unmap_ptp_regs(bp); + return rc; } void bnxt_ptp_clear(struct bnxt *bp) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h index 7c528e1f8713..373baf45884b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h @@ -131,12 +131,15 @@ do { \ #endif int bnxt_ptp_parse(struct sk_buff *skb, u16 *seq_id, u16 *hdr_off); +void bnxt_ptp_update_current_time(struct bnxt *bp); void bnxt_ptp_pps_event(struct bnxt *bp, u32 data1, u32 data2); void bnxt_ptp_reapply_pps(struct bnxt *bp); int bnxt_hwtstamp_set(struct net_device *dev, struct ifreq *ifr); int bnxt_hwtstamp_get(struct net_device *dev, struct ifreq *ifr); int bnxt_get_tx_ts_p5(struct bnxt *bp, struct sk_buff *skb); int bnxt_get_rx_ts_p5(struct bnxt *bp, u64 *ts, u32 pkt_ts); -int bnxt_ptp_init(struct bnxt *bp); +void bnxt_ptp_rtc_timecounter_init(struct bnxt_ptp_cfg *ptp, u64 ns); +int bnxt_ptp_init_rtc(struct bnxt *bp, bool phc_cfg); +int bnxt_ptp_init(struct bnxt *bp, bool phc_cfg); void bnxt_ptp_clear(struct bnxt *bp); #endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index 1d177fed44a6..ddf2f3963abe 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -846,7 +846,7 @@ void bnxt_sriov_disable(struct bnxt *bp) return; /* synchronize VF and VF-rep create and destroy */ - mutex_lock(&bp->sriov_lock); + devl_lock(bp->dl); bnxt_vf_reps_destroy(bp); if (pci_vfs_assigned(bp->pdev)) { @@ -859,7 +859,7 @@ void bnxt_sriov_disable(struct bnxt *bp) /* Free the HW resources reserved for various VF's */ bnxt_hwrm_func_vf_resource_free(bp, num_vfs); } - mutex_unlock(&bp->sriov_lock); + devl_unlock(bp->dl); bnxt_free_vf_resources(bp); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c index 8eb28e088582..eb4803b11c0e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c @@ -559,44 +559,34 @@ int bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode, struct netlink_ext_ack *extack) { struct bnxt *bp = bnxt_get_bp_from_dl(devlink); - int rc = 0; - mutex_lock(&bp->sriov_lock); if (bp->eswitch_mode == mode) { netdev_info(bp->dev, "already in %s eswitch mode\n", mode == DEVLINK_ESWITCH_MODE_LEGACY ? "legacy" : "switchdev"); - rc = -EINVAL; - goto done; + return -EINVAL; } switch (mode) { case DEVLINK_ESWITCH_MODE_LEGACY: bnxt_vf_reps_destroy(bp); - break; + return 0; case DEVLINK_ESWITCH_MODE_SWITCHDEV: if (bp->hwrm_spec_code < 0x10803) { netdev_warn(bp->dev, "FW does not support SRIOV E-Switch SWITCHDEV mode\n"); - rc = -ENOTSUPP; - goto done; + return -ENOTSUPP; } if (pci_num_vf(bp->pdev) == 0) { netdev_info(bp->dev, "Enable VFs before setting switchdev mode\n"); - rc = -EPERM; - goto done; + return -EPERM; } - rc = bnxt_vf_reps_create(bp); - break; + return bnxt_vf_reps_create(bp); default: - rc = -EINVAL; - goto done; + return -EINVAL; } -done: - mutex_unlock(&bp->sriov_lock); - return rc; } #endif diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 2da804f84b48..2dd79af9411b 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -76,7 +76,7 @@ static inline void bcmgenet_writel(u32 value, void __iomem *offset) if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) __raw_writel(value, offset); else - writel_relaxed(value, offset); + writel(value, offset); } static inline u32 bcmgenet_readl(void __iomem *offset) @@ -84,7 +84,7 @@ static inline u32 bcmgenet_readl(void __iomem *offset) if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) return __raw_readl(offset); else - return readl_relaxed(offset); + return readl(offset); } static inline void dmadesc_set_length_status(struct bcmgenet_priv *priv, @@ -1368,7 +1368,7 @@ static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e) if (!p->eee_enabled) { bcmgenet_eee_enable_set(dev, false); } else { - ret = phy_init_eee(dev->phydev, 0); + ret = phy_init_eee(dev->phydev, false); if (ret) { netif_err(priv, hw, dev, "EEE initialization failed\n"); return ret; diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 9ddbee7de72b..f0a7d8396a4a 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -12,6 +12,7 @@ #include #include #include +#include #if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) || defined(CONFIG_MACB_USE_HWSTAMP) #define MACB_EXT_DESC @@ -1291,6 +1292,9 @@ struct macb { u32 wol; struct macb_ptp_info *ptp_info; /* macb-ptp interface */ + + struct phy *sgmii_phy; /* for ZynqMP SGMII mode */ + #ifdef MACB_EXT_DESC uint8_t hw_dma_cap; #endif diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index d13f06cf0308..800d5ced5800 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -34,7 +34,9 @@ #include #include #include +#include #include +#include #include "macb.h" /* This structure is only used for MACB on SiFive FU540 devices */ @@ -2762,10 +2764,14 @@ static int macb_open(struct net_device *dev) macb_init_hw(bp); - err = macb_phylink_connect(bp); + err = phy_power_on(bp->sgmii_phy); if (err) goto reset_hw; + err = macb_phylink_connect(bp); + if (err) + goto phy_off; + netif_tx_start_all_queues(dev); if (bp->ptp_info) @@ -2773,6 +2779,9 @@ static int macb_open(struct net_device *dev) return 0; +phy_off: + phy_power_off(bp->sgmii_phy); + reset_hw: macb_reset_hw(bp); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) @@ -2798,6 +2807,8 @@ static int macb_close(struct net_device *dev) phylink_stop(bp->phylink); phylink_disconnect_phy(bp->phylink); + phy_power_off(bp->sgmii_phy); + spin_lock_irqsave(&bp->lock, flags); macb_reset_hw(bp); netif_carrier_off(dev); @@ -4567,13 +4578,55 @@ static const struct macb_config np4_config = { .usrio = &macb_default_usrio, }; +static int zynqmp_init(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct macb *bp = netdev_priv(dev); + int ret; + + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { + /* Ensure PS-GTR PHY device used in SGMII mode is ready */ + bp->sgmii_phy = devm_phy_get(&pdev->dev, "sgmii-phy"); + + if (IS_ERR(bp->sgmii_phy)) { + ret = PTR_ERR(bp->sgmii_phy); + dev_err_probe(&pdev->dev, ret, + "failed to get PS-GTR PHY\n"); + return ret; + } + + ret = phy_init(bp->sgmii_phy); + if (ret) { + dev_err(&pdev->dev, "failed to init PS-GTR PHY: %d\n", + ret); + return ret; + } + } + + /* Fully reset GEM controller at hardware level using zynqmp-reset driver, + * if mapped in device tree. + */ + ret = device_reset_optional(&pdev->dev); + if (ret) { + dev_err_probe(&pdev->dev, ret, "failed to reset controller"); + phy_exit(bp->sgmii_phy); + return ret; + } + + ret = macb_init(pdev); + if (ret) + phy_exit(bp->sgmii_phy); + + return ret; +} + static const struct macb_config zynqmp_config = { .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO | MACB_CAPS_GEM_HAS_PTP | MACB_CAPS_BD_RD_PREFETCH, .dma_burst_length = 16, .clk_init = macb_clk_init, - .init = macb_init, + .init = zynqmp_init, .jumbo_max_len = 10240, .usrio = &macb_default_usrio, }; @@ -4790,7 +4843,7 @@ static int macb_probe(struct platform_device *pdev) err = macb_mii_init(bp); if (err) - goto err_out_free_netdev; + goto err_out_phy_exit; netif_carrier_off(dev); @@ -4815,6 +4868,9 @@ static int macb_probe(struct platform_device *pdev) mdiobus_unregister(bp->mii_bus); mdiobus_free(bp->mii_bus); +err_out_phy_exit: + phy_exit(bp->sgmii_phy); + err_out_free_netdev: free_netdev(dev); @@ -4836,6 +4892,7 @@ static int macb_remove(struct platform_device *pdev) if (dev) { bp = netdev_priv(dev); + phy_exit(bp->sgmii_phy); mdiobus_unregister(bp->mii_bus); mdiobus_free(bp->mii_bus); diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c index 574a32f23f96..2f6484dc186a 100644 --- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c +++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c @@ -1409,7 +1409,8 @@ static acpi_status bgx_acpi_register_phy(acpi_handle handle, struct device *dev = &bgx->pdev->dev; struct acpi_device *adev; - if (acpi_bus_get_device(handle, &adev)) + adev = acpi_fetch_acpi_dev(handle); + if (!adev) goto out; acpi_get_mac_address(dev, adev, bgx->lmac[bgx->acpi_lmac_idx].mac); diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 63521312cb90..174b1e156669 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -3349,6 +3349,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } if (!adapter->registered_device_map) { dev_err(&pdev->dev, "could not register any net devices\n"); + err = -ENODEV; goto out_free_dev; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c index 28fd2de9e4cf..1672d3afe5be 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c @@ -8,6 +8,46 @@ #include "cxgb4_filter.h" #include "cxgb4_tc_flower.h" +static int cxgb4_policer_validate(const struct flow_action *action, + const struct flow_action_entry *act, + struct netlink_ext_ack *extack) +{ + if (act->police.exceed.act_id != FLOW_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when exceed action is not drop"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && + act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not pipe or ok"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && + !flow_action_is_last_entry(action, act)) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is ok, but action is not last"); + return -EOPNOTSUPP; + } + + if (act->police.peakrate_bytes_ps || + act->police.avrate || act->police.overhead) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when peakrate/avrate/overhead is configured"); + return -EOPNOTSUPP; + } + + if (act->police.rate_pkt_ps) { + NL_SET_ERR_MSG_MOD(extack, + "QoS offload not support packets per second"); + return -EOPNOTSUPP; + } + + return 0; +} + static int cxgb4_matchall_egress_validate(struct net_device *dev, struct tc_cls_matchall_offload *cls) { @@ -48,11 +88,10 @@ static int cxgb4_matchall_egress_validate(struct net_device *dev, flow_action_for_each(i, entry, actions) { switch (entry->id) { case FLOW_ACTION_POLICE: - if (entry->police.rate_pkt_ps) { - NL_SET_ERR_MSG_MOD(extack, - "QoS offload not support packets per second"); - return -EOPNOTSUPP; - } + ret = cxgb4_policer_validate(actions, entry, extack); + if (ret) + return ret; + /* Convert bytes per second to bits per second */ if (entry->police.rate_bytes_ps * 8 > max_link_rate) { NL_SET_ERR_MSG_MOD(extack, @@ -150,11 +189,11 @@ static int cxgb4_matchall_alloc_tc(struct net_device *dev, flow_action_for_each(i, entry, &cls->rule->action) if (entry->id == FLOW_ACTION_POLICE) break; - if (entry->police.rate_pkt_ps) { - NL_SET_ERR_MSG_MOD(extack, - "QoS offload not support packets per second"); - return -EOPNOTSUPP; - } + + ret = cxgb4_policer_validate(&cls->rule->action, entry, extack); + if (ret) + return ret; + /* Convert from bytes per second to Kbps */ p.u.params.maxrate = div_u64(entry->police.rate_bytes_ps * 8, 1000); p.u.params.channel = pi->tx_chan; diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index c78b99a497df..8014eb33937c 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -2363,11 +2363,13 @@ static void gemini_port_save_mac_addr(struct gemini_ethernet_port *port) static int gemini_ethernet_port_probe(struct platform_device *pdev) { char *port_names[2] = { "ethernet0", "ethernet1" }; + struct device_node *np = pdev->dev.of_node; struct gemini_ethernet_port *port; struct device *dev = &pdev->dev; struct gemini_ethernet *geth; struct net_device *netdev; struct device *parent; + u8 mac[ETH_ALEN]; unsigned int id; int irq; int ret; @@ -2473,6 +2475,12 @@ static int gemini_ethernet_port_probe(struct platform_device *pdev) netif_napi_add(netdev, &port->napi, gmac_napi_poll, DEFAULT_NAPI_WEIGHT); + ret = of_get_mac_address(np, mac); + if (!ret) { + dev_info(dev, "Setting macaddr from DT %pM\n", mac); + memcpy(port->mac_addr, mac, ETH_ALEN); + } + if (is_valid_ether_addr((void *)port->mac_addr)) { eth_hw_addr_set(netdev, (u8 *)port->mac_addr); } else { diff --git a/drivers/net/ethernet/davicom/Kconfig b/drivers/net/ethernet/davicom/Kconfig index 7af86b6d4150..02e0caff98e3 100644 --- a/drivers/net/ethernet/davicom/Kconfig +++ b/drivers/net/ethernet/davicom/Kconfig @@ -3,6 +3,19 @@ # Davicom device configuration # +config NET_VENDOR_DAVICOM + bool "Davicom devices" + default y + help + If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Davicom devices. If you say Y, you will be asked + for your specific card in the following selections. + +if NET_VENDOR_DAVICOM + config DM9000 tristate "DM9000 support" depends on ARM || MIPS || COLDFIRE || NIOS2 || COMPILE_TEST @@ -22,3 +35,21 @@ config DM9000_FORCE_SIMPLE_PHY_POLL bit to determine if the link is up or down instead of the more costly MII PHY reads. Note, this will not work if the chip is operating with an external PHY. + +config DM9051 + tristate "DM9051 SPI support" + depends on SPI + select CRC32 + select MDIO + select PHYLIB + select REGMAP_SPI + help + Support for DM9051 SPI chipset. + + To compile this driver as a module, choose M here. The module + will be called dm9051. + + The SPI mode for the host's SPI master to access DM9051 is mode + 0 on the SPI bus. + +endif # NET_VENDOR_DAVICOM diff --git a/drivers/net/ethernet/davicom/Makefile b/drivers/net/ethernet/davicom/Makefile index 173c87d21076..225f85bc1f53 100644 --- a/drivers/net/ethernet/davicom/Makefile +++ b/drivers/net/ethernet/davicom/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_DM9000) += dm9000.o +obj-$(CONFIG_DM9051) += dm9051.o diff --git a/drivers/net/ethernet/davicom/dm9051.c b/drivers/net/ethernet/davicom/dm9051.c new file mode 100644 index 000000000000..a523ddda7609 --- /dev/null +++ b/drivers/net/ethernet/davicom/dm9051.c @@ -0,0 +1,1260 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Davicom Semiconductor,Inc. + * Davicom DM9051 SPI Fast Ethernet Linux driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm9051.h" + +#define DRVNAME_9051 "dm9051" + +/** + * struct rx_ctl_mach - rx activities record + * @status_err_counter: rx status error counter + * @large_err_counter: rx get large packet length error counter + * @rx_err_counter: receive packet error counter + * @tx_err_counter: transmit packet error counter + * @fifo_rst_counter: reset operation counter + * + * To keep track for the driver operation statistics + */ +struct rx_ctl_mach { + u16 status_err_counter; + u16 large_err_counter; + u16 rx_err_counter; + u16 tx_err_counter; + u16 fifo_rst_counter; +}; + +/** + * struct dm9051_rxctrl - dm9051 driver rx control + * @hash_table: Multicast hash-table data + * @rcr_all: KS_RXCR1 register setting + * + * The settings needs to control the receive filtering + * such as the multicast hash-filter and the receive register settings + */ +struct dm9051_rxctrl { + u16 hash_table[4]; + u8 rcr_all; +}; + +/** + * struct dm9051_rxhdr - rx packet data header + * @headbyte: lead byte equal to 0x01 notifies a valid packet + * @status: status bits for the received packet + * @rxlen: packet length + * + * The Rx packed, entered into the FIFO memory, start with these + * four bytes which is the Rx header, followed by the ethernet + * packet data and ends with an appended 4-byte CRC data. + * Both Rx packet and CRC data are for check purpose and finally + * are dropped by this driver + */ +struct dm9051_rxhdr { + u8 headbyte; + u8 status; + __le16 rxlen; +}; + +/** + * struct board_info - maintain the saved data + * @spidev: spi device structure + * @ndev: net device structure + * @mdiobus: mii bus structure + * @phydev: phy device structure + * @txq: tx queue structure + * @regmap_dm: regmap for register read/write + * @regmap_dmbulk: extra regmap for bulk read/write + * @rxctrl_work: Work queue for updating RX mode and multicast lists + * @tx_work: Work queue for tx packets + * @pause: ethtool pause parameter structure + * @spi_lockm: between threads lock structure + * @reg_mutex: regmap access lock structure + * @bc: rx control statistics structure + * @rxhdr: rx header structure + * @rctl: rx control setting structure + * @msg_enable: message level value + * @imr_all: to store operating imr value for register DM9051_IMR + * @lcr_all: to store operating rcr value for register DM9051_LMCR + * + * The saved data variables, keep up to date for retrieval back to use + */ +struct board_info { + u32 msg_enable; + struct spi_device *spidev; + struct net_device *ndev; + struct mii_bus *mdiobus; + struct phy_device *phydev; + struct sk_buff_head txq; + struct regmap *regmap_dm; + struct regmap *regmap_dmbulk; + struct work_struct rxctrl_work; + struct work_struct tx_work; + struct ethtool_pauseparam pause; + struct mutex spi_lockm; + struct mutex reg_mutex; + struct rx_ctl_mach bc; + struct dm9051_rxhdr rxhdr; + struct dm9051_rxctrl rctl; + u8 imr_all; + u8 lcr_all; +}; + +static int dm9051_set_reg(struct board_info *db, unsigned int reg, unsigned int val) +{ + int ret; + + ret = regmap_write(db->regmap_dm, reg, val); + if (ret < 0) + netif_err(db, drv, db->ndev, "%s: error %d set reg %02x\n", + __func__, ret, reg); + return ret; +} + +static int dm9051_update_bits(struct board_info *db, unsigned int reg, unsigned int mask, + unsigned int val) +{ + int ret; + + ret = regmap_update_bits(db->regmap_dm, reg, mask, val); + if (ret < 0) + netif_err(db, drv, db->ndev, "%s: error %d update bits reg %02x\n", + __func__, ret, reg); + return ret; +} + +/* skb buffer exhausted, just discard the received data + */ +static int dm9051_dumpblk(struct board_info *db, u8 reg, size_t count) +{ + struct net_device *ndev = db->ndev; + unsigned int rb; + int ret; + + /* no skb buffer, + * both reg and &rb must be noinc, + * read once one byte via regmap_read + */ + do { + ret = regmap_read(db->regmap_dm, reg, &rb); + if (ret < 0) { + netif_err(db, drv, ndev, "%s: error %d dumping read reg %02x\n", + __func__, ret, reg); + break; + } + } while (--count); + + return ret; +} + +static int dm9051_set_regs(struct board_info *db, unsigned int reg, const void *val, + size_t val_count) +{ + int ret; + + ret = regmap_bulk_write(db->regmap_dmbulk, reg, val, val_count); + if (ret < 0) + netif_err(db, drv, db->ndev, "%s: error %d bulk writing regs %02x\n", + __func__, ret, reg); + return ret; +} + +static int dm9051_get_regs(struct board_info *db, unsigned int reg, void *val, + size_t val_count) +{ + int ret; + + ret = regmap_bulk_read(db->regmap_dmbulk, reg, val, val_count); + if (ret < 0) + netif_err(db, drv, db->ndev, "%s: error %d bulk reading regs %02x\n", + __func__, ret, reg); + return ret; +} + +static int dm9051_write_mem(struct board_info *db, unsigned int reg, const void *buff, + size_t len) +{ + int ret; + + ret = regmap_noinc_write(db->regmap_dm, reg, buff, len); + if (ret < 0) + netif_err(db, drv, db->ndev, "%s: error %d noinc writing regs %02x\n", + __func__, ret, reg); + return ret; +} + +static int dm9051_read_mem(struct board_info *db, unsigned int reg, void *buff, + size_t len) +{ + int ret; + + ret = regmap_noinc_read(db->regmap_dm, reg, buff, len); + if (ret < 0) + netif_err(db, drv, db->ndev, "%s: error %d noinc reading regs %02x\n", + __func__, ret, reg); + return ret; +} + +/* waiting tx-end rather than tx-req + * got faster + */ +static int dm9051_nsr_poll(struct board_info *db) +{ + unsigned int mval; + int ret; + + ret = regmap_read_poll_timeout(db->regmap_dm, DM9051_NSR, mval, + mval & (NSR_TX2END | NSR_TX1END), 1, 20); + if (ret == -ETIMEDOUT) + netdev_err(db->ndev, "timeout in checking for tx end\n"); + return ret; +} + +static int dm9051_epcr_poll(struct board_info *db) +{ + unsigned int mval; + int ret; + + ret = regmap_read_poll_timeout(db->regmap_dm, DM9051_EPCR, mval, + !(mval & EPCR_ERRE), 100, 10000); + if (ret == -ETIMEDOUT) + netdev_err(db->ndev, "eeprom/phy in processing get timeout\n"); + return ret; +} + +static int dm9051_irq_flag(struct board_info *db) +{ + struct spi_device *spi = db->spidev; + int irq_type = irq_get_trigger_type(spi->irq); + + if (irq_type) + return irq_type; + + return IRQF_TRIGGER_LOW; +} + +static unsigned int dm9051_intcr_value(struct board_info *db) +{ + return (dm9051_irq_flag(db) == IRQF_TRIGGER_LOW) ? + INTCR_POL_LOW : INTCR_POL_HIGH; +} + +static int dm9051_set_fcr(struct board_info *db) +{ + u8 fcr = 0; + + if (db->pause.rx_pause) + fcr |= FCR_BKPM | FCR_FLCE; + if (db->pause.tx_pause) + fcr |= FCR_TXPEN; + + return dm9051_set_reg(db, DM9051_FCR, fcr); +} + +static int dm9051_set_recv(struct board_info *db) +{ + int ret; + + ret = dm9051_set_regs(db, DM9051_MAR, db->rctl.hash_table, sizeof(db->rctl.hash_table)); + if (ret) + return ret; + + return dm9051_set_reg(db, DM9051_RCR, db->rctl.rcr_all); /* enable rx */ +} + +static int dm9051_core_reset(struct board_info *db) +{ + int ret; + + db->bc.fifo_rst_counter++; + + ret = regmap_write(db->regmap_dm, DM9051_NCR, NCR_RST); /* NCR reset */ + if (ret) + return ret; + ret = regmap_write(db->regmap_dm, DM9051_MBNDRY, MBNDRY_BYTE); /* MemBound */ + if (ret) + return ret; + ret = regmap_write(db->regmap_dm, DM9051_PPCR, PPCR_PAUSE_COUNT); /* Pause Count */ + if (ret) + return ret; + ret = regmap_write(db->regmap_dm, DM9051_LMCR, db->lcr_all); /* LEDMode1 */ + if (ret) + return ret; + + return dm9051_set_reg(db, DM9051_INTCR, dm9051_intcr_value(db)); +} + +static int dm9051_update_fcr(struct board_info *db) +{ + u8 fcr = 0; + + if (db->pause.rx_pause) + fcr |= FCR_BKPM | FCR_FLCE; + if (db->pause.tx_pause) + fcr |= FCR_TXPEN; + + return dm9051_update_bits(db, DM9051_FCR, FCR_RXTX_BITS, fcr); +} + +static int dm9051_disable_interrupt(struct board_info *db) +{ + return dm9051_set_reg(db, DM9051_IMR, IMR_PAR); /* disable int */ +} + +static int dm9051_enable_interrupt(struct board_info *db) +{ + return dm9051_set_reg(db, DM9051_IMR, db->imr_all); /* enable int */ +} + +static int dm9051_stop_mrcmd(struct board_info *db) +{ + return dm9051_set_reg(db, DM9051_ISR, ISR_STOP_MRCMD); /* to stop mrcmd */ +} + +static int dm9051_clear_interrupt(struct board_info *db) +{ + return dm9051_update_bits(db, DM9051_ISR, ISR_CLR_INT, ISR_CLR_INT); +} + +static int dm9051_eeprom_read(struct board_info *db, int offset, u8 *to) +{ + int ret; + + ret = regmap_write(db->regmap_dm, DM9051_EPAR, offset); + if (ret) + return ret; + + ret = regmap_write(db->regmap_dm, DM9051_EPCR, EPCR_ERPRR); + if (ret) + return ret; + + ret = dm9051_epcr_poll(db); + if (ret) + return ret; + + ret = regmap_write(db->regmap_dm, DM9051_EPCR, 0); + if (ret) + return ret; + + return regmap_bulk_read(db->regmap_dmbulk, DM9051_EPDRL, to, 2); +} + +static int dm9051_eeprom_write(struct board_info *db, int offset, u8 *data) +{ + int ret; + + ret = regmap_write(db->regmap_dm, DM9051_EPAR, offset); + if (ret) + return ret; + + ret = regmap_bulk_write(db->regmap_dmbulk, DM9051_EPDRL, data, 2); + if (ret < 0) + return ret; + + ret = regmap_write(db->regmap_dm, DM9051_EPCR, EPCR_WEP | EPCR_ERPRW); + if (ret) + return ret; + + ret = dm9051_epcr_poll(db); + if (ret) + return ret; + + return regmap_write(db->regmap_dm, DM9051_EPCR, 0); +} + +static int dm9051_phyread(void *context, unsigned int reg, unsigned int *val) +{ + struct board_info *db = context; + int ret; + + ret = regmap_write(db->regmap_dm, DM9051_EPAR, DM9051_PHY | reg); + if (ret) + return ret; + + ret = regmap_write(db->regmap_dm, DM9051_EPCR, EPCR_ERPRR | EPCR_EPOS); + if (ret) + return ret; + + ret = dm9051_epcr_poll(db); + if (ret) + return ret; + + ret = regmap_write(db->regmap_dm, DM9051_EPCR, 0); + if (ret) + return ret; + + /* this is a 4 bytes data, clear to zero since following regmap_bulk_read + * only fill lower 2 bytes + */ + *val = 0; + return regmap_bulk_read(db->regmap_dmbulk, DM9051_EPDRL, val, 2); +} + +static int dm9051_phywrite(void *context, unsigned int reg, unsigned int val) +{ + struct board_info *db = context; + int ret; + + ret = regmap_write(db->regmap_dm, DM9051_EPAR, DM9051_PHY | reg); + if (ret) + return ret; + + ret = regmap_bulk_write(db->regmap_dmbulk, DM9051_EPDRL, &val, 2); + if (ret < 0) + return ret; + + ret = regmap_write(db->regmap_dm, DM9051_EPCR, EPCR_EPOS | EPCR_ERPRW); + if (ret) + return ret; + + ret = dm9051_epcr_poll(db); + if (ret) + return ret; + + return regmap_write(db->regmap_dm, DM9051_EPCR, 0); +} + +static int dm9051_mdio_read(struct mii_bus *bus, int addr, int regnum) +{ + struct board_info *db = bus->priv; + unsigned int val = 0xffff; + int ret; + + if (addr == DM9051_PHY_ADDR) { + ret = dm9051_phyread(db, regnum, &val); + if (ret) + return ret; + } + + return val; +} + +static int dm9051_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) +{ + struct board_info *db = bus->priv; + + if (addr == DM9051_PHY_ADDR) + return dm9051_phywrite(db, regnum, val); + + return -ENODEV; +} + +static void dm9051_reg_lock_mutex(void *dbcontext) +{ + struct board_info *db = dbcontext; + + mutex_lock(&db->reg_mutex); +} + +static void dm9051_reg_unlock_mutex(void *dbcontext) +{ + struct board_info *db = dbcontext; + + mutex_unlock(&db->reg_mutex); +} + +static struct regmap_config regconfigdm = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + .reg_stride = 1, + .cache_type = REGCACHE_NONE, + .read_flag_mask = 0, + .write_flag_mask = DM_SPI_WR, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .lock = dm9051_reg_lock_mutex, + .unlock = dm9051_reg_unlock_mutex, +}; + +static struct regmap_config regconfigdmbulk = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + .reg_stride = 1, + .cache_type = REGCACHE_NONE, + .read_flag_mask = 0, + .write_flag_mask = DM_SPI_WR, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .lock = dm9051_reg_lock_mutex, + .unlock = dm9051_reg_unlock_mutex, + .use_single_read = true, + .use_single_write = true, +}; + +static int dm9051_map_init(struct spi_device *spi, struct board_info *db) +{ + /* create two regmap instances, + * split read/write and bulk_read/bulk_write to individual regmap + * to resolve regmap execution confliction problem + */ + regconfigdm.lock_arg = db; + db->regmap_dm = devm_regmap_init_spi(db->spidev, ®configdm); + if (IS_ERR(db->regmap_dm)) + return PTR_ERR(db->regmap_dm); + + regconfigdmbulk.lock_arg = db; + db->regmap_dmbulk = devm_regmap_init_spi(db->spidev, ®configdmbulk); + if (IS_ERR(db->regmap_dmbulk)) + return PTR_ERR(db->regmap_dmbulk); + + return 0; +} + +static int dm9051_map_chipid(struct board_info *db) +{ + struct device *dev = &db->spidev->dev; + unsigned short wid; + u8 buff[6]; + int ret; + + ret = dm9051_get_regs(db, DM9051_VIDL, buff, sizeof(buff)); + if (ret < 0) + return ret; + + wid = get_unaligned_le16(buff + 2); + if (wid != DM9051_ID) { + dev_err(dev, "chipid error as %04x !\n", wid); + return -ENODEV; + } + + dev_info(dev, "chip %04x found\n", wid); + return 0; +} + +/* Read DM9051_PAR registers which is the mac address loaded from EEPROM while power-on + */ +static int dm9051_map_etherdev_par(struct net_device *ndev, struct board_info *db) +{ + u8 addr[ETH_ALEN]; + int ret; + + ret = dm9051_get_regs(db, DM9051_PAR, addr, sizeof(addr)); + if (ret < 0) + return ret; + + if (!is_valid_ether_addr(addr)) { + eth_hw_addr_random(ndev); + + ret = dm9051_set_regs(db, DM9051_PAR, ndev->dev_addr, sizeof(ndev->dev_addr)); + if (ret < 0) + return ret; + + dev_dbg(&db->spidev->dev, "Use random MAC address\n"); + return 0; + } + + eth_hw_addr_set(ndev, addr); + return 0; +} + +/* ethtool-ops + */ +static void dm9051_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strscpy(info->driver, DRVNAME_9051, sizeof(info->driver)); +} + +static void dm9051_set_msglevel(struct net_device *ndev, u32 value) +{ + struct board_info *db = to_dm9051_board(ndev); + + db->msg_enable = value; +} + +static u32 dm9051_get_msglevel(struct net_device *ndev) +{ + struct board_info *db = to_dm9051_board(ndev); + + return db->msg_enable; +} + +static int dm9051_get_eeprom_len(struct net_device *dev) +{ + return 128; +} + +static int dm9051_get_eeprom(struct net_device *ndev, + struct ethtool_eeprom *ee, u8 *data) +{ + struct board_info *db = to_dm9051_board(ndev); + int offset = ee->offset; + int len = ee->len; + int i, ret; + + if ((len | offset) & 1) + return -EINVAL; + + ee->magic = DM_EEPROM_MAGIC; + + for (i = 0; i < len; i += 2) { + ret = dm9051_eeprom_read(db, (offset + i) / 2, data + i); + if (ret) + break; + } + return ret; +} + +static int dm9051_set_eeprom(struct net_device *ndev, + struct ethtool_eeprom *ee, u8 *data) +{ + struct board_info *db = to_dm9051_board(ndev); + int offset = ee->offset; + int len = ee->len; + int i, ret; + + if ((len | offset) & 1) + return -EINVAL; + + if (ee->magic != DM_EEPROM_MAGIC) + return -EINVAL; + + for (i = 0; i < len; i += 2) { + ret = dm9051_eeprom_write(db, (offset + i) / 2, data + i); + if (ret) + break; + } + return ret; +} + +static void dm9051_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct board_info *db = to_dm9051_board(ndev); + + *pause = db->pause; +} + +static int dm9051_set_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct board_info *db = to_dm9051_board(ndev); + + db->pause = *pause; + + if (pause->autoneg == AUTONEG_DISABLE) + return dm9051_update_fcr(db); + + phy_set_sym_pause(db->phydev, pause->rx_pause, pause->tx_pause, + pause->autoneg); + phy_start_aneg(db->phydev); + return 0; +} + +static const struct ethtool_ops dm9051_ethtool_ops = { + .get_drvinfo = dm9051_get_drvinfo, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_msglevel = dm9051_get_msglevel, + .set_msglevel = dm9051_set_msglevel, + .nway_reset = phy_ethtool_nway_reset, + .get_link = ethtool_op_get_link, + .get_eeprom_len = dm9051_get_eeprom_len, + .get_eeprom = dm9051_get_eeprom, + .set_eeprom = dm9051_set_eeprom, + .get_pauseparam = dm9051_get_pauseparam, + .set_pauseparam = dm9051_set_pauseparam, +}; + +static int dm9051_all_start(struct board_info *db) +{ + int ret; + + /* GPR power on of the internal phy + */ + ret = dm9051_set_reg(db, DM9051_GPR, 0); + if (ret) + return ret; + + /* dm9051 chip registers could not be accessed within 1 ms + * after GPR power on, delay 1 ms is essential + */ + msleep(1); + + ret = dm9051_core_reset(db); + if (ret) + return ret; + + return dm9051_enable_interrupt(db); +} + +static int dm9051_all_stop(struct board_info *db) +{ + int ret; + + /* GPR power off of the internal phy, + * The internal phy still could be accessed after this GPR power off control + */ + ret = dm9051_set_reg(db, DM9051_GPR, GPR_PHY_OFF); + if (ret) + return ret; + + return dm9051_set_reg(db, DM9051_RCR, RCR_RX_DISABLE); +} + +/* fifo reset while rx error found + */ +static int dm9051_all_restart(struct board_info *db) +{ + struct net_device *ndev = db->ndev; + int ret; + + ret = dm9051_core_reset(db); + if (ret) + return ret; + + ret = dm9051_enable_interrupt(db); + if (ret) + return ret; + + netdev_dbg(ndev, " rxstatus_Er & rxlen_Er %d, RST_c %d\n", + db->bc.status_err_counter + db->bc.large_err_counter, + db->bc.fifo_rst_counter); + + ret = dm9051_set_recv(db); + if (ret) + return ret; + + return dm9051_set_fcr(db); +} + +/* read packets from the fifo memory + * return value, + * > 0 - read packet number, caller can repeat the rx operation + * 0 - no error, caller need stop further rx operation + * -EBUSY - read data error, caller escape from rx operation + */ +static int dm9051_loop_rx(struct board_info *db) +{ + struct net_device *ndev = db->ndev; + unsigned int rxbyte; + int ret, rxlen; + struct sk_buff *skb; + u8 *rdptr; + int scanrr = 0; + + do { + ret = dm9051_read_mem(db, DM_SPI_MRCMDX, &rxbyte, 2); + if (ret) + return ret; + + if ((rxbyte & GENMASK(7, 0)) != DM9051_PKT_RDY) + break; /* exhaust-empty */ + + ret = dm9051_read_mem(db, DM_SPI_MRCMD, &db->rxhdr, DM_RXHDR_SIZE); + if (ret) + return ret; + + ret = dm9051_stop_mrcmd(db); + if (ret) + return ret; + + rxlen = le16_to_cpu(db->rxhdr.rxlen); + if (db->rxhdr.status & RSR_ERR_BITS || rxlen > DM9051_PKT_MAX) { + netdev_dbg(ndev, "rxhdr-byte (%02x)\n", + db->rxhdr.headbyte); + + if (db->rxhdr.status & RSR_ERR_BITS) { + db->bc.status_err_counter++; + netdev_dbg(ndev, "check rxstatus-error (%02x)\n", + db->rxhdr.status); + } else { + db->bc.large_err_counter++; + netdev_dbg(ndev, "check rxlen large-error (%d > %d)\n", + rxlen, DM9051_PKT_MAX); + } + return dm9051_all_restart(db); + } + + skb = dev_alloc_skb(rxlen); + if (!skb) { + ret = dm9051_dumpblk(db, DM_SPI_MRCMD, rxlen); + if (ret) + return ret; + return scanrr; + } + + rdptr = skb_put(skb, rxlen - 4); + ret = dm9051_read_mem(db, DM_SPI_MRCMD, rdptr, rxlen); + if (ret) { + db->bc.rx_err_counter++; + dev_kfree_skb(skb); + return ret; + } + + ret = dm9051_stop_mrcmd(db); + if (ret) + return ret; + + skb->protocol = eth_type_trans(skb, db->ndev); + if (db->ndev->features & NETIF_F_RXCSUM) + skb_checksum_none_assert(skb); + netif_rx(skb); + db->ndev->stats.rx_bytes += rxlen; + db->ndev->stats.rx_packets++; + scanrr++; + } while (!ret); + + return scanrr; +} + +/* transmit a packet, + * return value, + * 0 - succeed + * -ETIMEDOUT - timeout error + */ +static int dm9051_single_tx(struct board_info *db, u8 *buff, unsigned int len) +{ + int ret; + + ret = dm9051_nsr_poll(db); + if (ret) + return ret; + + ret = dm9051_write_mem(db, DM_SPI_MWCMD, buff, len); + if (ret) + return ret; + + ret = dm9051_set_regs(db, DM9051_TXPLL, &len, 2); + if (ret < 0) + return ret; + + return dm9051_set_reg(db, DM9051_TCR, TCR_TXREQ); +} + +static int dm9051_loop_tx(struct board_info *db) +{ + struct net_device *ndev = db->ndev; + int ntx = 0; + int ret; + + while (!skb_queue_empty(&db->txq)) { + struct sk_buff *skb; + unsigned int len; + + skb = skb_dequeue(&db->txq); + if (skb) { + ntx++; + ret = dm9051_single_tx(db, skb->data, skb->len); + len = skb->len; + dev_kfree_skb(skb); + if (ret < 0) { + db->bc.tx_err_counter++; + return 0; + } + ndev->stats.tx_bytes += len; + ndev->stats.tx_packets++; + } + + if (netif_queue_stopped(ndev) && + (skb_queue_len(&db->txq) < DM9051_TX_QUE_LO_WATER)) + netif_wake_queue(ndev); + } + + return ntx; +} + +static irqreturn_t dm9051_rx_threaded_irq(int irq, void *pw) +{ + struct board_info *db = pw; + int result, result_tx; + + mutex_lock(&db->spi_lockm); + + result = dm9051_disable_interrupt(db); + if (result) + goto out_unlock; + + result = dm9051_clear_interrupt(db); + if (result) + goto out_unlock; + + do { + result = dm9051_loop_rx(db); /* threaded irq rx */ + if (result < 0) + goto out_unlock; + result_tx = dm9051_loop_tx(db); /* more tx better performance */ + if (result_tx < 0) + goto out_unlock; + } while (result > 0); + + dm9051_enable_interrupt(db); + + /* To exit and has mutex unlock while rx or tx error + */ +out_unlock: + mutex_unlock(&db->spi_lockm); + + return IRQ_HANDLED; +} + +static void dm9051_tx_delay(struct work_struct *work) +{ + struct board_info *db = container_of(work, struct board_info, tx_work); + int result; + + mutex_lock(&db->spi_lockm); + + result = dm9051_loop_tx(db); + if (result < 0) + netdev_err(db->ndev, "transmit packet error\n"); + + mutex_unlock(&db->spi_lockm); +} + +static void dm9051_rxctl_delay(struct work_struct *work) +{ + struct board_info *db = container_of(work, struct board_info, rxctrl_work); + struct net_device *ndev = db->ndev; + int result; + + mutex_lock(&db->spi_lockm); + + result = dm9051_set_regs(db, DM9051_PAR, ndev->dev_addr, sizeof(ndev->dev_addr)); + if (result < 0) + goto out_unlock; + + dm9051_set_recv(db); + + /* To has mutex unlock and return from this function if regmap function fail + */ +out_unlock: + mutex_unlock(&db->spi_lockm); +} + +/* Open network device + * Called when the network device is marked active, such as a user executing + * 'ifconfig up' on the device + */ +static int dm9051_open(struct net_device *ndev) +{ + struct board_info *db = to_dm9051_board(ndev); + struct spi_device *spi = db->spidev; + int ret; + + db->imr_all = IMR_PAR | IMR_PRM; + db->lcr_all = LMCR_MODE1; + db->rctl.rcr_all = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN; + memset(db->rctl.hash_table, 0, sizeof(db->rctl.hash_table)); + + ndev->irq = spi->irq; /* by dts */ + ret = request_threaded_irq(spi->irq, NULL, dm9051_rx_threaded_irq, + dm9051_irq_flag(db) | IRQF_ONESHOT, + ndev->name, db); + if (ret < 0) { + netdev_err(ndev, "failed to get irq\n"); + return ret; + } + + phy_support_sym_pause(db->phydev); + phy_start(db->phydev); + + /* flow control parameters init */ + db->pause.rx_pause = true; + db->pause.tx_pause = true; + db->pause.autoneg = AUTONEG_DISABLE; + + if (db->phydev->autoneg) + db->pause.autoneg = AUTONEG_ENABLE; + + ret = dm9051_all_start(db); + if (ret) { + phy_stop(db->phydev); + free_irq(spi->irq, db); + return ret; + } + + netif_wake_queue(ndev); + + return 0; +} + +/* Close network device + * Called to close down a network device which has been active. Cancel any + * work, shutdown the RX and TX process and then place the chip into a low + * power state while it is not being used + */ +static int dm9051_stop(struct net_device *ndev) +{ + struct board_info *db = to_dm9051_board(ndev); + int ret; + + ret = dm9051_all_stop(db); + if (ret) + return ret; + + flush_work(&db->tx_work); + flush_work(&db->rxctrl_work); + + phy_stop(db->phydev); + + free_irq(db->spidev->irq, db); + + netif_stop_queue(ndev); + + skb_queue_purge(&db->txq); + + return 0; +} + +/* event: play a schedule starter in condition + */ +static netdev_tx_t dm9051_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct board_info *db = to_dm9051_board(ndev); + + skb_queue_tail(&db->txq, skb); + if (skb_queue_len(&db->txq) > DM9051_TX_QUE_HI_WATER) + netif_stop_queue(ndev); /* enforce limit queue size */ + + schedule_work(&db->tx_work); + + return NETDEV_TX_OK; +} + +/* event: play with a schedule starter + */ +static void dm9051_set_rx_mode(struct net_device *ndev) +{ + struct board_info *db = to_dm9051_board(ndev); + struct dm9051_rxctrl rxctrl; + struct netdev_hw_addr *ha; + u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN; + u32 hash_val; + + memset(&rxctrl, 0, sizeof(rxctrl)); + + /* rx control */ + if (ndev->flags & IFF_PROMISC) { + rcr |= RCR_PRMSC; + netdev_dbg(ndev, "set_multicast rcr |= RCR_PRMSC, rcr= %02x\n", rcr); + } + + if (ndev->flags & IFF_ALLMULTI) { + rcr |= RCR_ALL; + netdev_dbg(ndev, "set_multicast rcr |= RCR_ALLMULTI, rcr= %02x\n", rcr); + } + + rxctrl.rcr_all = rcr; + + /* broadcast address */ + rxctrl.hash_table[0] = 0; + rxctrl.hash_table[1] = 0; + rxctrl.hash_table[2] = 0; + rxctrl.hash_table[3] = 0x8000; + + /* the multicast address in Hash Table : 64 bits */ + netdev_for_each_mc_addr(ha, ndev) { + hash_val = ether_crc_le(ETH_ALEN, ha->addr) & GENMASK(5, 0); + rxctrl.hash_table[hash_val / 16] |= BIT(0) << (hash_val % 16); + } + + /* schedule work to do the actual set of the data if needed */ + + if (memcmp(&db->rctl, &rxctrl, sizeof(rxctrl))) { + memcpy(&db->rctl, &rxctrl, sizeof(rxctrl)); + schedule_work(&db->rxctrl_work); + } +} + +/* event: write into the mac registers and eeprom directly + */ +static int dm9051_set_mac_address(struct net_device *ndev, void *p) +{ + struct board_info *db = to_dm9051_board(ndev); + int ret; + + ret = eth_prepare_mac_addr_change(ndev, p); + if (ret < 0) + return ret; + + eth_commit_mac_addr_change(ndev, p); + return dm9051_set_regs(db, DM9051_PAR, ndev->dev_addr, sizeof(ndev->dev_addr)); +} + +static const struct net_device_ops dm9051_netdev_ops = { + .ndo_open = dm9051_open, + .ndo_stop = dm9051_stop, + .ndo_start_xmit = dm9051_start_xmit, + .ndo_set_rx_mode = dm9051_set_rx_mode, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = dm9051_set_mac_address, +}; + +static void dm9051_operation_clear(struct board_info *db) +{ + db->bc.status_err_counter = 0; + db->bc.large_err_counter = 0; + db->bc.rx_err_counter = 0; + db->bc.tx_err_counter = 0; + db->bc.fifo_rst_counter = 0; +} + +static int dm9051_mdio_register(struct board_info *db) +{ + struct spi_device *spi = db->spidev; + int ret; + + db->mdiobus = devm_mdiobus_alloc(&spi->dev); + if (!db->mdiobus) + return -ENOMEM; + + db->mdiobus->priv = db; + db->mdiobus->read = dm9051_mdio_read; + db->mdiobus->write = dm9051_mdio_write; + db->mdiobus->name = "dm9051-mdiobus"; + db->mdiobus->phy_mask = (u32)~BIT(1); + db->mdiobus->parent = &spi->dev; + snprintf(db->mdiobus->id, MII_BUS_ID_SIZE, + "dm9051-%s.%u", dev_name(&spi->dev), spi->chip_select); + + ret = devm_mdiobus_register(&spi->dev, db->mdiobus); + if (ret) + dev_err(&spi->dev, "Could not register MDIO bus\n"); + + return ret; +} + +static void dm9051_handle_link_change(struct net_device *ndev) +{ + struct board_info *db = to_dm9051_board(ndev); + + phy_print_status(db->phydev); + + /* only write pause settings to mac. since mac and phy are integrated + * together, such as link state, speed and duplex are sync already + */ + if (db->phydev->link) { + if (db->phydev->pause) { + db->pause.rx_pause = true; + db->pause.tx_pause = true; + } + dm9051_update_fcr(db); + } +} + +/* phy connect as poll mode + */ +static int dm9051_phy_connect(struct board_info *db) +{ + char phy_id[MII_BUS_ID_SIZE + 3]; + + snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, + db->mdiobus->id, DM9051_PHY_ADDR); + + db->phydev = phy_connect(db->ndev, phy_id, dm9051_handle_link_change, + PHY_INTERFACE_MODE_MII); + if (IS_ERR(db->phydev)) + return PTR_ERR_OR_ZERO(db->phydev); + return 0; +} + +static int dm9051_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct net_device *ndev; + struct board_info *db; + int ret; + + ndev = devm_alloc_etherdev(dev, sizeof(struct board_info)); + if (!ndev) + return -ENOMEM; + + SET_NETDEV_DEV(ndev, dev); + dev_set_drvdata(dev, ndev); + + db = netdev_priv(ndev); + + db->msg_enable = 0; + db->spidev = spi; + db->ndev = ndev; + + ndev->netdev_ops = &dm9051_netdev_ops; + ndev->ethtool_ops = &dm9051_ethtool_ops; + + mutex_init(&db->spi_lockm); + mutex_init(&db->reg_mutex); + + INIT_WORK(&db->rxctrl_work, dm9051_rxctl_delay); + INIT_WORK(&db->tx_work, dm9051_tx_delay); + + ret = dm9051_map_init(spi, db); + if (ret) + return ret; + + ret = dm9051_map_chipid(db); + if (ret) + return ret; + + ret = dm9051_map_etherdev_par(ndev, db); + if (ret < 0) + return ret; + + ret = dm9051_mdio_register(db); + if (ret) + return ret; + + ret = dm9051_phy_connect(db); + if (ret) + return ret; + + dm9051_operation_clear(db); + skb_queue_head_init(&db->txq); + + ret = devm_register_netdev(dev, ndev); + if (ret) { + phy_disconnect(db->phydev); + return dev_err_probe(dev, ret, "device register failed"); + } + + return 0; +} + +static void dm9051_drv_remove(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct net_device *ndev = dev_get_drvdata(dev); + struct board_info *db = to_dm9051_board(ndev); + + phy_disconnect(db->phydev); +} + +static const struct of_device_id dm9051_match_table[] = { + { .compatible = "davicom,dm9051" }, + {} +}; + +static const struct spi_device_id dm9051_id_table[] = { + { "dm9051", 0 }, + {} +}; + +static struct spi_driver dm9051_driver = { + .driver = { + .name = DRVNAME_9051, + .of_match_table = dm9051_match_table, + }, + .probe = dm9051_probe, + .remove = dm9051_drv_remove, + .id_table = dm9051_id_table, +}; +module_spi_driver(dm9051_driver); + +MODULE_AUTHOR("Joseph CHANG "); +MODULE_DESCRIPTION("Davicom DM9051 network SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/davicom/dm9051.h b/drivers/net/ethernet/davicom/dm9051.h new file mode 100644 index 000000000000..fef3120edd7c --- /dev/null +++ b/drivers/net/ethernet/davicom/dm9051.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 Davicom Semiconductor,Inc. + * Davicom DM9051 SPI Fast Ethernet Linux driver + */ + +#ifndef _DM9051_H_ +#define _DM9051_H_ + +#include +#include +#include + +#define DM9051_ID 0x9051 + +#define DM9051_NCR 0x00 +#define DM9051_NSR 0x01 +#define DM9051_TCR 0x02 +#define DM9051_RCR 0x05 +#define DM9051_BPTR 0x08 +#define DM9051_FCR 0x0A +#define DM9051_EPCR 0x0B +#define DM9051_EPAR 0x0C +#define DM9051_EPDRL 0x0D +#define DM9051_EPDRH 0x0E +#define DM9051_PAR 0x10 +#define DM9051_MAR 0x16 +#define DM9051_GPCR 0x1E +#define DM9051_GPR 0x1F + +#define DM9051_VIDL 0x28 +#define DM9051_VIDH 0x29 +#define DM9051_PIDL 0x2A +#define DM9051_PIDH 0x2B +#define DM9051_SMCR 0x2F +#define DM9051_ATCR 0x30 +#define DM9051_SPIBCR 0x38 +#define DM9051_INTCR 0x39 +#define DM9051_PPCR 0x3D + +#define DM9051_MPCR 0x55 +#define DM9051_LMCR 0x57 +#define DM9051_MBNDRY 0x5E + +#define DM9051_MRRL 0x74 +#define DM9051_MRRH 0x75 +#define DM9051_MWRL 0x7A +#define DM9051_MWRH 0x7B +#define DM9051_TXPLL 0x7C +#define DM9051_TXPLH 0x7D +#define DM9051_ISR 0x7E +#define DM9051_IMR 0x7F + +#define DM_SPI_MRCMDX 0x70 +#define DM_SPI_MRCMD 0x72 +#define DM_SPI_MWCMD 0x78 + +#define DM_SPI_WR 0x80 + +/* dm9051 Ethernet controller registers bits + */ +/* 0x00 */ +#define NCR_WAKEEN BIT(6) +#define NCR_FDX BIT(3) +#define NCR_RST BIT(0) +/* 0x01 */ +#define NSR_SPEED BIT(7) +#define NSR_LINKST BIT(6) +#define NSR_WAKEST BIT(5) +#define NSR_TX2END BIT(3) +#define NSR_TX1END BIT(2) +/* 0x02 */ +#define TCR_DIS_JABBER_TIMER BIT(6) /* for Jabber Packet support */ +#define TCR_TXREQ BIT(0) +/* 0x05 */ +#define RCR_DIS_WATCHDOG_TIMER BIT(6) /* for Jabber Packet support */ +#define RCR_DIS_LONG BIT(5) +#define RCR_DIS_CRC BIT(4) +#define RCR_ALL BIT(3) +#define RCR_PRMSC BIT(1) +#define RCR_RXEN BIT(0) +#define RCR_RX_DISABLE (RCR_DIS_LONG | RCR_DIS_CRC) +/* 0x06 */ +#define RSR_RF BIT(7) +#define RSR_MF BIT(6) +#define RSR_LCS BIT(5) +#define RSR_RWTO BIT(4) +#define RSR_PLE BIT(3) +#define RSR_AE BIT(2) +#define RSR_CE BIT(1) +#define RSR_FOE BIT(0) +#define RSR_ERR_BITS (RSR_RF | RSR_LCS | RSR_RWTO | RSR_PLE | \ + RSR_AE | RSR_CE | RSR_FOE) +/* 0x0A */ +#define FCR_TXPEN BIT(5) +#define FCR_BKPM BIT(3) +#define FCR_FLCE BIT(0) +#define FCR_RXTX_BITS (FCR_TXPEN | FCR_BKPM | FCR_FLCE) +/* 0x0B */ +#define EPCR_WEP BIT(4) +#define EPCR_EPOS BIT(3) +#define EPCR_ERPRR BIT(2) +#define EPCR_ERPRW BIT(1) +#define EPCR_ERRE BIT(0) +/* 0x1E */ +#define GPCR_GEP_CNTL BIT(0) +/* 0x1F */ +#define GPR_PHY_OFF BIT(0) +/* 0x30 */ +#define ATCR_AUTO_TX BIT(7) +/* 0x39 */ +#define INTCR_POL_LOW (1 << 0) +#define INTCR_POL_HIGH (0 << 0) +/* 0x3D */ +/* Pause Packet Control Register - default = 1 */ +#define PPCR_PAUSE_COUNT 0x08 +/* 0x55 */ +#define MPCR_RSTTX BIT(1) +#define MPCR_RSTRX BIT(0) +/* 0x57 */ +/* LEDMode Control Register - LEDMode1 */ +/* Value 0x81 : bit[7] = 1, bit[2] = 0, bit[1:0] = 01b */ +#define LMCR_NEWMOD BIT(7) +#define LMCR_TYPED1 BIT(1) +#define LMCR_TYPED0 BIT(0) +#define LMCR_MODE1 (LMCR_NEWMOD | LMCR_TYPED0) +/* 0x5E */ +#define MBNDRY_BYTE BIT(7) +/* 0xFE */ +#define ISR_MBS BIT(7) +#define ISR_LNKCHG BIT(5) +#define ISR_ROOS BIT(3) +#define ISR_ROS BIT(2) +#define ISR_PTS BIT(1) +#define ISR_PRS BIT(0) +#define ISR_CLR_INT (ISR_LNKCHG | ISR_ROOS | ISR_ROS | \ + ISR_PTS | ISR_PRS) +#define ISR_STOP_MRCMD (ISR_MBS) +/* 0xFF */ +#define IMR_PAR BIT(7) +#define IMR_LNKCHGI BIT(5) +#define IMR_PTM BIT(1) +#define IMR_PRM BIT(0) + +/* Const + */ +#define DM9051_PHY_ADDR 1 /* PHY id */ +#define DM9051_PHY 0x40 /* PHY address 0x01 */ +#define DM9051_PKT_RDY 0x01 /* Packet ready to receive */ +#define DM9051_PKT_MAX 1536 /* Received packet max size */ +#define DM9051_TX_QUE_HI_WATER 50 +#define DM9051_TX_QUE_LO_WATER 25 +#define DM_EEPROM_MAGIC 0x9051 + +#define DM_RXHDR_SIZE sizeof(struct dm9051_rxhdr) + +static inline struct board_info *to_dm9051_board(struct net_device *ndev) +{ + return netdev_priv(ndev); +} + +#endif /* _DM9051_H_ */ diff --git a/drivers/net/ethernet/dec/tulip/pnic.c b/drivers/net/ethernet/dec/tulip/pnic.c index 3fb39e32e1b4..653bde48ef44 100644 --- a/drivers/net/ethernet/dec/tulip/pnic.c +++ b/drivers/net/ethernet/dec/tulip/pnic.c @@ -21,7 +21,7 @@ void pnic_do_nway(struct net_device *dev) struct tulip_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->base_addr; u32 phy_reg = ioread32(ioaddr + 0xB8); - u32 new_csr6 = tp->csr6 & ~0x40C40200; + u32 new_csr6; if (phy_reg & 0x78000000) { /* Ignore baseT4 */ if (phy_reg & 0x20000000) dev->if_port = 5; diff --git a/drivers/net/ethernet/dlink/sundance.c b/drivers/net/ethernet/dlink/sundance.c index c710dc17be90..8dd7bf9014ec 100644 --- a/drivers/net/ethernet/dlink/sundance.c +++ b/drivers/net/ethernet/dlink/sundance.c @@ -340,7 +340,7 @@ enum wake_event_bits { struct netdev_desc { __le32 next_desc; __le32 status; - struct desc_frag { __le32 addr, length; } frag[1]; + struct desc_frag { __le32 addr, length; } frag; }; /* Bits in netdev_desc.status */ @@ -980,8 +980,8 @@ static void tx_timeout(struct net_device *dev, unsigned int txqueue) le32_to_cpu(np->tx_ring[i].next_desc), le32_to_cpu(np->tx_ring[i].status), (le32_to_cpu(np->tx_ring[i].status) >> 2) & 0xff, - le32_to_cpu(np->tx_ring[i].frag[0].addr), - le32_to_cpu(np->tx_ring[i].frag[0].length)); + le32_to_cpu(np->tx_ring[i].frag.addr), + le32_to_cpu(np->tx_ring[i].frag.length)); } printk(KERN_DEBUG "TxListPtr=%08x netif_queue_stopped=%d\n", ioread32(np->base + TxListPtr), @@ -1027,7 +1027,7 @@ static void init_ring(struct net_device *dev) np->rx_ring[i].next_desc = cpu_to_le32(np->rx_ring_dma + ((i+1)%RX_RING_SIZE)*sizeof(*np->rx_ring)); np->rx_ring[i].status = 0; - np->rx_ring[i].frag[0].length = 0; + np->rx_ring[i].frag.length = 0; np->rx_skbuff[i] = NULL; } @@ -1039,16 +1039,16 @@ static void init_ring(struct net_device *dev) if (skb == NULL) break; skb_reserve(skb, 2); /* 16 byte align the IP header. */ - np->rx_ring[i].frag[0].addr = cpu_to_le32( + np->rx_ring[i].frag.addr = cpu_to_le32( dma_map_single(&np->pci_dev->dev, skb->data, np->rx_buf_sz, DMA_FROM_DEVICE)); if (dma_mapping_error(&np->pci_dev->dev, - np->rx_ring[i].frag[0].addr)) { + np->rx_ring[i].frag.addr)) { dev_kfree_skb(skb); np->rx_skbuff[i] = NULL; break; } - np->rx_ring[i].frag[0].length = cpu_to_le32(np->rx_buf_sz | LastFrag); + np->rx_ring[i].frag.length = cpu_to_le32(np->rx_buf_sz | LastFrag); } np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); @@ -1097,12 +1097,12 @@ start_tx (struct sk_buff *skb, struct net_device *dev) txdesc->next_desc = 0; txdesc->status = cpu_to_le32 ((entry << 2) | DisableAlign); - txdesc->frag[0].addr = cpu_to_le32(dma_map_single(&np->pci_dev->dev, + txdesc->frag.addr = cpu_to_le32(dma_map_single(&np->pci_dev->dev, skb->data, skb->len, DMA_TO_DEVICE)); if (dma_mapping_error(&np->pci_dev->dev, - txdesc->frag[0].addr)) + txdesc->frag.addr)) goto drop_frame; - txdesc->frag[0].length = cpu_to_le32 (skb->len | LastFrag); + txdesc->frag.length = cpu_to_le32 (skb->len | LastFrag); /* Increment cur_tx before tasklet_schedule() */ np->cur_tx++; @@ -1151,7 +1151,7 @@ reset_tx (struct net_device *dev) skb = np->tx_skbuff[i]; if (skb) { dma_unmap_single(&np->pci_dev->dev, - le32_to_cpu(np->tx_ring[i].frag[0].addr), + le32_to_cpu(np->tx_ring[i].frag.addr), skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); np->tx_skbuff[i] = NULL; @@ -1271,12 +1271,12 @@ static irqreturn_t intr_handler(int irq, void *dev_instance) skb = np->tx_skbuff[entry]; /* Free the original skb. */ dma_unmap_single(&np->pci_dev->dev, - le32_to_cpu(np->tx_ring[entry].frag[0].addr), + le32_to_cpu(np->tx_ring[entry].frag.addr), skb->len, DMA_TO_DEVICE); dev_consume_skb_irq(np->tx_skbuff[entry]); np->tx_skbuff[entry] = NULL; - np->tx_ring[entry].frag[0].addr = 0; - np->tx_ring[entry].frag[0].length = 0; + np->tx_ring[entry].frag.addr = 0; + np->tx_ring[entry].frag.length = 0; } spin_unlock(&np->lock); } else { @@ -1290,12 +1290,12 @@ static irqreturn_t intr_handler(int irq, void *dev_instance) skb = np->tx_skbuff[entry]; /* Free the original skb. */ dma_unmap_single(&np->pci_dev->dev, - le32_to_cpu(np->tx_ring[entry].frag[0].addr), + le32_to_cpu(np->tx_ring[entry].frag.addr), skb->len, DMA_TO_DEVICE); dev_consume_skb_irq(np->tx_skbuff[entry]); np->tx_skbuff[entry] = NULL; - np->tx_ring[entry].frag[0].addr = 0; - np->tx_ring[entry].frag[0].length = 0; + np->tx_ring[entry].frag.addr = 0; + np->tx_ring[entry].frag.length = 0; } spin_unlock(&np->lock); } @@ -1372,16 +1372,16 @@ static void rx_poll(struct tasklet_struct *t) (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) { skb_reserve(skb, 2); /* 16 byte align the IP header */ dma_sync_single_for_cpu(&np->pci_dev->dev, - le32_to_cpu(desc->frag[0].addr), + le32_to_cpu(desc->frag.addr), np->rx_buf_sz, DMA_FROM_DEVICE); skb_copy_to_linear_data(skb, np->rx_skbuff[entry]->data, pkt_len); dma_sync_single_for_device(&np->pci_dev->dev, - le32_to_cpu(desc->frag[0].addr), + le32_to_cpu(desc->frag.addr), np->rx_buf_sz, DMA_FROM_DEVICE); skb_put(skb, pkt_len); } else { dma_unmap_single(&np->pci_dev->dev, - le32_to_cpu(desc->frag[0].addr), + le32_to_cpu(desc->frag.addr), np->rx_buf_sz, DMA_FROM_DEVICE); skb_put(skb = np->rx_skbuff[entry], pkt_len); np->rx_skbuff[entry] = NULL; @@ -1427,18 +1427,18 @@ static void refill_rx (struct net_device *dev) if (skb == NULL) break; /* Better luck next round. */ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - np->rx_ring[entry].frag[0].addr = cpu_to_le32( + np->rx_ring[entry].frag.addr = cpu_to_le32( dma_map_single(&np->pci_dev->dev, skb->data, np->rx_buf_sz, DMA_FROM_DEVICE)); if (dma_mapping_error(&np->pci_dev->dev, - np->rx_ring[entry].frag[0].addr)) { + np->rx_ring[entry].frag.addr)) { dev_kfree_skb_irq(skb); np->rx_skbuff[entry] = NULL; break; } } /* Perhaps we need not reset this field. */ - np->rx_ring[entry].frag[0].length = + np->rx_ring[entry].frag.length = cpu_to_le32(np->rx_buf_sz | LastFrag); np->rx_ring[entry].status = 0; cnt++; @@ -1870,14 +1870,14 @@ static int netdev_close(struct net_device *dev) (int)(np->tx_ring_dma)); for (i = 0; i < TX_RING_SIZE; i++) printk(KERN_DEBUG " #%d desc. %4.4x %8.8x %8.8x.\n", - i, np->tx_ring[i].status, np->tx_ring[i].frag[0].addr, - np->tx_ring[i].frag[0].length); + i, np->tx_ring[i].status, np->tx_ring[i].frag.addr, + np->tx_ring[i].frag.length); printk(KERN_DEBUG " Rx ring %8.8x:\n", (int)(np->rx_ring_dma)); for (i = 0; i < /*RX_RING_SIZE*/4 ; i++) { printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", - i, np->rx_ring[i].status, np->rx_ring[i].frag[0].addr, - np->rx_ring[i].frag[0].length); + i, np->rx_ring[i].status, np->rx_ring[i].frag.addr, + np->rx_ring[i].frag.length); } } #endif /* __i386__ debugging only */ @@ -1892,19 +1892,19 @@ static int netdev_close(struct net_device *dev) skb = np->rx_skbuff[i]; if (skb) { dma_unmap_single(&np->pci_dev->dev, - le32_to_cpu(np->rx_ring[i].frag[0].addr), + le32_to_cpu(np->rx_ring[i].frag.addr), np->rx_buf_sz, DMA_FROM_DEVICE); dev_kfree_skb(skb); np->rx_skbuff[i] = NULL; } - np->rx_ring[i].frag[0].addr = cpu_to_le32(0xBADF00D0); /* poison */ + np->rx_ring[i].frag.addr = cpu_to_le32(0xBADF00D0); /* poison */ } for (i = 0; i < TX_RING_SIZE; i++) { np->tx_ring[i].next_desc = 0; skb = np->tx_skbuff[i]; if (skb) { dma_unmap_single(&np->pci_dev->dev, - le32_to_cpu(np->tx_ring[i].frag[0].addr), + le32_to_cpu(np->tx_ring[i].frag.addr), skb->len, DMA_TO_DEVICE); dev_kfree_skb(skb); np->tx_skbuff[i] = NULL; diff --git a/drivers/net/ethernet/ezchip/nps_enet.c b/drivers/net/ethernet/ezchip/nps_enet.c index 323340826dab..69dbf950d451 100644 --- a/drivers/net/ethernet/ezchip/nps_enet.c +++ b/drivers/net/ethernet/ezchip/nps_enet.c @@ -608,7 +608,6 @@ static s32 nps_enet_probe(struct platform_device *pdev) /* Get IRQ number */ priv->irq = platform_get_irq(pdev, 0); if (priv->irq < 0) { - dev_err(dev, "failed to retrieve value from device tree\n"); err = -ENODEV; goto out_netdev; } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 0f90d2d5bb60..4b047255d928 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "dpaa2-eth.h" @@ -34,6 +35,75 @@ MODULE_DESCRIPTION("Freescale DPAA2 Ethernet Driver"); struct ptp_qoriq *dpaa2_ptp; EXPORT_SYMBOL(dpaa2_ptp); +static void dpaa2_eth_detect_features(struct dpaa2_eth_priv *priv) +{ + priv->features = 0; + + if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_PTP_ONESTEP_VER_MAJOR, + DPNI_PTP_ONESTEP_VER_MINOR) >= 0) + priv->features |= DPAA2_ETH_FEATURE_ONESTEP_CFG_DIRECT; +} + +static void dpaa2_update_ptp_onestep_indirect(struct dpaa2_eth_priv *priv, + u32 offset, u8 udp) +{ + struct dpni_single_step_cfg cfg; + + cfg.en = 1; + cfg.ch_update = udp; + cfg.offset = offset; + cfg.peer_delay = 0; + + if (dpni_set_single_step_cfg(priv->mc_io, 0, priv->mc_token, &cfg)) + WARN_ONCE(1, "Failed to set single step register"); +} + +static void dpaa2_update_ptp_onestep_direct(struct dpaa2_eth_priv *priv, + u32 offset, u8 udp) +{ + u32 val = 0; + + val = DPAA2_PTP_SINGLE_STEP_ENABLE | + DPAA2_PTP_SINGLE_CORRECTION_OFF(offset); + + if (udp) + val |= DPAA2_PTP_SINGLE_STEP_CH; + + if (priv->onestep_reg_base) + writel(val, priv->onestep_reg_base); +} + +static void dpaa2_ptp_onestep_reg_update_method(struct dpaa2_eth_priv *priv) +{ + struct device *dev = priv->net_dev->dev.parent; + struct dpni_single_step_cfg ptp_cfg; + + priv->dpaa2_set_onestep_params_cb = dpaa2_update_ptp_onestep_indirect; + + if (!(priv->features & DPAA2_ETH_FEATURE_ONESTEP_CFG_DIRECT)) + return; + + if (dpni_get_single_step_cfg(priv->mc_io, 0, + priv->mc_token, &ptp_cfg)) { + dev_err(dev, "dpni_get_single_step_cfg cannot retrieve onestep reg, falling back to indirect update\n"); + return; + } + + if (!ptp_cfg.ptp_onestep_reg_base) { + dev_err(dev, "1588 onestep reg not available, falling back to indirect update\n"); + return; + } + + priv->onestep_reg_base = ioremap(ptp_cfg.ptp_onestep_reg_base, + sizeof(u32)); + if (!priv->onestep_reg_base) { + dev_err(dev, "1588 onestep reg cannot be mapped, falling back to indirect update\n"); + return; + } + + priv->dpaa2_set_onestep_params_cb = dpaa2_update_ptp_onestep_direct; +} + static void *dpaa2_iova_to_virt(struct iommu_domain *domain, dma_addr_t iova_addr) { @@ -695,7 +765,6 @@ static void dpaa2_eth_enable_tx_tstamp(struct dpaa2_eth_priv *priv, struct sk_buff *skb) { struct ptp_tstamp origin_timestamp; - struct dpni_single_step_cfg cfg; u8 msgtype, twostep, udp; struct dpaa2_faead *faead; struct dpaa2_fas *fas; @@ -749,17 +818,48 @@ static void dpaa2_eth_enable_tx_tstamp(struct dpaa2_eth_priv *priv, htonl(origin_timestamp.sec_lsb); *(__be32 *)(data + offset2 + 6) = htonl(origin_timestamp.nsec); - cfg.en = 1; - cfg.ch_update = udp; - cfg.offset = offset1; - cfg.peer_delay = 0; + if (priv->ptp_correction_off == offset1) + return; + + priv->dpaa2_set_onestep_params_cb(priv, offset1, udp); + priv->ptp_correction_off = offset1; - if (dpni_set_single_step_cfg(priv->mc_io, 0, priv->mc_token, - &cfg)) - WARN_ONCE(1, "Failed to set single step register"); } } +static void *dpaa2_eth_sgt_get(struct dpaa2_eth_priv *priv) +{ + struct dpaa2_eth_sgt_cache *sgt_cache; + void *sgt_buf = NULL; + int sgt_buf_size; + + sgt_cache = this_cpu_ptr(priv->sgt_cache); + sgt_buf_size = priv->tx_data_offset + + DPAA2_ETH_SG_ENTRIES_MAX * sizeof(struct dpaa2_sg_entry); + + if (sgt_cache->count == 0) + sgt_buf = napi_alloc_frag_align(sgt_buf_size, DPAA2_ETH_TX_BUF_ALIGN); + else + sgt_buf = sgt_cache->buf[--sgt_cache->count]; + if (!sgt_buf) + return NULL; + + memset(sgt_buf, 0, sgt_buf_size); + + return sgt_buf; +} + +static void dpaa2_eth_sgt_recycle(struct dpaa2_eth_priv *priv, void *sgt_buf) +{ + struct dpaa2_eth_sgt_cache *sgt_cache; + + sgt_cache = this_cpu_ptr(priv->sgt_cache); + if (sgt_cache->count >= DPAA2_ETH_SGT_CACHE_SIZE) + skb_free_frag(sgt_buf); + else + sgt_cache->buf[sgt_cache->count++] = sgt_buf; +} + /* Create a frame descriptor based on a fragmented skb */ static int dpaa2_eth_build_sg_fd(struct dpaa2_eth_priv *priv, struct sk_buff *skb, @@ -805,12 +905,11 @@ static int dpaa2_eth_build_sg_fd(struct dpaa2_eth_priv *priv, /* Prepare the HW SGT structure */ sgt_buf_size = priv->tx_data_offset + sizeof(struct dpaa2_sg_entry) * num_dma_bufs; - sgt_buf = napi_alloc_frag_align(sgt_buf_size, DPAA2_ETH_TX_BUF_ALIGN); + sgt_buf = dpaa2_eth_sgt_get(priv); if (unlikely(!sgt_buf)) { err = -ENOMEM; goto sgt_buf_alloc_failed; } - memset(sgt_buf, 0, sgt_buf_size); sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); @@ -846,6 +945,7 @@ static int dpaa2_eth_build_sg_fd(struct dpaa2_eth_priv *priv, err = -ENOMEM; goto dma_map_single_failed; } + memset(fd, 0, sizeof(struct dpaa2_fd)); dpaa2_fd_set_offset(fd, priv->tx_data_offset); dpaa2_fd_set_format(fd, dpaa2_fd_sg); dpaa2_fd_set_addr(fd, addr); @@ -855,7 +955,7 @@ static int dpaa2_eth_build_sg_fd(struct dpaa2_eth_priv *priv, return 0; dma_map_single_failed: - skb_free_frag(sgt_buf); + dpaa2_eth_sgt_recycle(priv, sgt_buf); sgt_buf_alloc_failed: dma_unmap_sg(dev, scl, num_sg, DMA_BIDIRECTIONAL); dma_map_sg_failed: @@ -875,7 +975,6 @@ static int dpaa2_eth_build_sg_fd_single_buf(struct dpaa2_eth_priv *priv, void **swa_addr) { struct device *dev = priv->net_dev->dev.parent; - struct dpaa2_eth_sgt_cache *sgt_cache; struct dpaa2_sg_entry *sgt; struct dpaa2_eth_swa *swa; dma_addr_t addr, sgt_addr; @@ -884,18 +983,10 @@ static int dpaa2_eth_build_sg_fd_single_buf(struct dpaa2_eth_priv *priv, int err; /* Prepare the HW SGT structure */ - sgt_cache = this_cpu_ptr(priv->sgt_cache); sgt_buf_size = priv->tx_data_offset + sizeof(struct dpaa2_sg_entry); - - if (sgt_cache->count == 0) - sgt_buf = kzalloc(sgt_buf_size + DPAA2_ETH_TX_BUF_ALIGN, - GFP_ATOMIC); - else - sgt_buf = sgt_cache->buf[--sgt_cache->count]; + sgt_buf = dpaa2_eth_sgt_get(priv); if (unlikely(!sgt_buf)) return -ENOMEM; - - sgt_buf = PTR_ALIGN(sgt_buf, DPAA2_ETH_TX_BUF_ALIGN); sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); addr = dma_map_single(dev, skb->data, skb->len, DMA_BIDIRECTIONAL); @@ -923,6 +1014,7 @@ static int dpaa2_eth_build_sg_fd_single_buf(struct dpaa2_eth_priv *priv, goto sgt_map_failed; } + memset(fd, 0, sizeof(struct dpaa2_fd)); dpaa2_fd_set_offset(fd, priv->tx_data_offset); dpaa2_fd_set_format(fd, dpaa2_fd_sg); dpaa2_fd_set_addr(fd, sgt_addr); @@ -934,10 +1026,7 @@ static int dpaa2_eth_build_sg_fd_single_buf(struct dpaa2_eth_priv *priv, sgt_map_failed: dma_unmap_single(dev, addr, skb->len, DMA_BIDIRECTIONAL); data_map_failed: - if (sgt_cache->count >= DPAA2_ETH_SGT_CACHE_SIZE) - kfree(sgt_buf); - else - sgt_cache->buf[sgt_cache->count++] = sgt_buf; + dpaa2_eth_sgt_recycle(priv, sgt_buf); return err; } @@ -978,6 +1067,7 @@ static int dpaa2_eth_build_single_fd(struct dpaa2_eth_priv *priv, if (unlikely(dma_mapping_error(dev, addr))) return -ENOMEM; + memset(fd, 0, sizeof(struct dpaa2_fd)); dpaa2_fd_set_addr(fd, addr); dpaa2_fd_set_offset(fd, (u16)(skb->data - buffer_start)); dpaa2_fd_set_len(fd, skb->len); @@ -1005,9 +1095,9 @@ static void dpaa2_eth_free_tx_fd(struct dpaa2_eth_priv *priv, struct dpaa2_eth_swa *swa; u8 fd_format = dpaa2_fd_get_format(fd); u32 fd_len = dpaa2_fd_get_len(fd); - - struct dpaa2_eth_sgt_cache *sgt_cache; struct dpaa2_sg_entry *sgt; + int should_free_skb = 1; + int i; fd_addr = dpaa2_fd_get_addr(fd); buffer_start = dpaa2_iova_to_virt(priv->iommu_domain, fd_addr); @@ -1039,6 +1129,28 @@ static void dpaa2_eth_free_tx_fd(struct dpaa2_eth_priv *priv, /* Unmap the SGT buffer */ dma_unmap_single(dev, fd_addr, swa->sg.sgt_size, DMA_BIDIRECTIONAL); + } else if (swa->type == DPAA2_ETH_SWA_SW_TSO) { + skb = swa->tso.skb; + + sgt = (struct dpaa2_sg_entry *)(buffer_start + + priv->tx_data_offset); + + /* Unmap and free the header */ + dma_unmap_single(dev, dpaa2_sg_get_addr(sgt), TSO_HEADER_SIZE, + DMA_TO_DEVICE); + kfree(dpaa2_iova_to_virt(priv->iommu_domain, dpaa2_sg_get_addr(sgt))); + + /* Unmap the other SG entries for the data */ + for (i = 1; i < swa->tso.num_sg; i++) + dma_unmap_single(dev, dpaa2_sg_get_addr(&sgt[i]), + dpaa2_sg_get_len(&sgt[i]), DMA_TO_DEVICE); + + /* Unmap the SGT buffer */ + dma_unmap_single(dev, fd_addr, swa->sg.sgt_size, + DMA_BIDIRECTIONAL); + + if (!swa->tso.is_last_fd) + should_free_skb = 0; } else { skb = swa->single.skb; @@ -1067,55 +1179,195 @@ static void dpaa2_eth_free_tx_fd(struct dpaa2_eth_priv *priv, } /* Get the timestamp value */ - if (skb->cb[0] == TX_TSTAMP) { - struct skb_shared_hwtstamps shhwtstamps; - __le64 *ts = dpaa2_get_ts(buffer_start, true); - u64 ns; + if (swa->type != DPAA2_ETH_SWA_SW_TSO) { + if (skb->cb[0] == TX_TSTAMP) { + struct skb_shared_hwtstamps shhwtstamps; + __le64 *ts = dpaa2_get_ts(buffer_start, true); + u64 ns; - memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); - ns = DPAA2_PTP_CLK_PERIOD_NS * le64_to_cpup(ts); - shhwtstamps.hwtstamp = ns_to_ktime(ns); - skb_tstamp_tx(skb, &shhwtstamps); - } else if (skb->cb[0] == TX_TSTAMP_ONESTEP_SYNC) { - mutex_unlock(&priv->onestep_tstamp_lock); - } - - /* Free SGT buffer allocated on tx */ - if (fd_format != dpaa2_fd_single) { - sgt_cache = this_cpu_ptr(priv->sgt_cache); - if (swa->type == DPAA2_ETH_SWA_SG) { - skb_free_frag(buffer_start); - } else { - if (sgt_cache->count >= DPAA2_ETH_SGT_CACHE_SIZE) - kfree(buffer_start); - else - sgt_cache->buf[sgt_cache->count++] = buffer_start; + ns = DPAA2_PTP_CLK_PERIOD_NS * le64_to_cpup(ts); + shhwtstamps.hwtstamp = ns_to_ktime(ns); + skb_tstamp_tx(skb, &shhwtstamps); + } else if (skb->cb[0] == TX_TSTAMP_ONESTEP_SYNC) { + mutex_unlock(&priv->onestep_tstamp_lock); } } - /* Move on with skb release */ - napi_consume_skb(skb, in_napi); + /* Free SGT buffer allocated on tx */ + if (fd_format != dpaa2_fd_single) + dpaa2_eth_sgt_recycle(priv, buffer_start); + + /* Move on with skb release. If we are just confirming multiple FDs + * from the same TSO skb then only the last one will need to free the + * skb. + */ + if (should_free_skb) + napi_consume_skb(skb, in_napi); +} + +static int dpaa2_eth_build_gso_fd(struct dpaa2_eth_priv *priv, + struct sk_buff *skb, struct dpaa2_fd *fd, + int *num_fds, u32 *total_fds_len) +{ + struct device *dev = priv->net_dev->dev.parent; + int hdr_len, total_len, data_left, fd_len; + int num_sge, err, i, sgt_buf_size; + struct dpaa2_fd *fd_start = fd; + struct dpaa2_sg_entry *sgt; + struct dpaa2_eth_swa *swa; + dma_addr_t sgt_addr, addr; + dma_addr_t tso_hdr_dma; + unsigned int index = 0; + struct tso_t tso; + char *tso_hdr; + void *sgt_buf; + + /* Initialize the TSO handler, and prepare the first payload */ + hdr_len = tso_start(skb, &tso); + *total_fds_len = 0; + + total_len = skb->len - hdr_len; + while (total_len > 0) { + /* Prepare the HW SGT structure for this frame */ + sgt_buf = dpaa2_eth_sgt_get(priv); + if (unlikely(!sgt_buf)) { + netdev_err(priv->net_dev, "dpaa2_eth_sgt_get() failed\n"); + err = -ENOMEM; + goto err_sgt_get; + } + sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); + + /* Determine the data length of this frame */ + data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); + total_len -= data_left; + fd_len = data_left + hdr_len; + + /* Prepare packet headers: MAC + IP + TCP */ + tso_hdr = kmalloc(TSO_HEADER_SIZE, GFP_ATOMIC); + if (!tso_hdr) { + err = -ENOMEM; + goto err_alloc_tso_hdr; + } + + tso_build_hdr(skb, tso_hdr, &tso, data_left, total_len == 0); + tso_hdr_dma = dma_map_single(dev, tso_hdr, TSO_HEADER_SIZE, DMA_TO_DEVICE); + if (dma_mapping_error(dev, tso_hdr_dma)) { + netdev_err(priv->net_dev, "dma_map_single(tso_hdr) failed\n"); + err = -ENOMEM; + goto err_map_tso_hdr; + } + + /* Setup the SG entry for the header */ + dpaa2_sg_set_addr(sgt, tso_hdr_dma); + dpaa2_sg_set_len(sgt, hdr_len); + dpaa2_sg_set_final(sgt, data_left <= 0); + + /* Compose the SG entries for each fragment of data */ + num_sge = 1; + while (data_left > 0) { + int size; + + /* Move to the next SG entry */ + sgt++; + size = min_t(int, tso.size, data_left); + + addr = dma_map_single(dev, tso.data, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev, addr)) { + netdev_err(priv->net_dev, "dma_map_single(tso.data) failed\n"); + err = -ENOMEM; + goto err_map_data; + } + dpaa2_sg_set_addr(sgt, addr); + dpaa2_sg_set_len(sgt, size); + dpaa2_sg_set_final(sgt, size == data_left); + + num_sge++; + + /* Build the data for the __next__ fragment */ + data_left -= size; + tso_build_data(skb, &tso, size); + } + + /* Store the skb backpointer in the SGT buffer */ + sgt_buf_size = priv->tx_data_offset + num_sge * sizeof(struct dpaa2_sg_entry); + swa = (struct dpaa2_eth_swa *)sgt_buf; + swa->type = DPAA2_ETH_SWA_SW_TSO; + swa->tso.skb = skb; + swa->tso.num_sg = num_sge; + swa->tso.sgt_size = sgt_buf_size; + swa->tso.is_last_fd = total_len == 0 ? 1 : 0; + + /* Separately map the SGT buffer */ + sgt_addr = dma_map_single(dev, sgt_buf, sgt_buf_size, DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(dev, sgt_addr))) { + netdev_err(priv->net_dev, "dma_map_single(sgt_buf) failed\n"); + err = -ENOMEM; + goto err_map_sgt; + } + + /* Setup the frame descriptor */ + memset(fd, 0, sizeof(struct dpaa2_fd)); + dpaa2_fd_set_offset(fd, priv->tx_data_offset); + dpaa2_fd_set_format(fd, dpaa2_fd_sg); + dpaa2_fd_set_addr(fd, sgt_addr); + dpaa2_fd_set_len(fd, fd_len); + dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA); + + *total_fds_len += fd_len; + /* Advance to the next frame descriptor */ + fd++; + index++; + } + + *num_fds = index; + + return 0; + +err_map_sgt: +err_map_data: + /* Unmap all the data S/G entries for the current FD */ + sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); + for (i = 1; i < num_sge; i++) + dma_unmap_single(dev, dpaa2_sg_get_addr(&sgt[i]), + dpaa2_sg_get_len(&sgt[i]), DMA_TO_DEVICE); + + /* Unmap the header entry */ + dma_unmap_single(dev, tso_hdr_dma, TSO_HEADER_SIZE, DMA_TO_DEVICE); +err_map_tso_hdr: + kfree(tso_hdr); +err_alloc_tso_hdr: + dpaa2_eth_sgt_recycle(priv, sgt_buf); +err_sgt_get: + /* Free all the other FDs that were already fully created */ + for (i = 0; i < index; i++) + dpaa2_eth_free_tx_fd(priv, NULL, &fd_start[i], false); + + return err; } static netdev_tx_t __dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev) { struct dpaa2_eth_priv *priv = netdev_priv(net_dev); - struct dpaa2_fd fd; - struct rtnl_link_stats64 *percpu_stats; + int total_enqueued = 0, retries = 0, enqueued; struct dpaa2_eth_drv_stats *percpu_extras; + struct rtnl_link_stats64 *percpu_stats; + unsigned int needed_headroom; + int num_fds = 1, max_retries; struct dpaa2_eth_fq *fq; struct netdev_queue *nq; + struct dpaa2_fd *fd; u16 queue_mapping; - unsigned int needed_headroom; - u32 fd_len; + void *swa = NULL; u8 prio = 0; int err, i; - void *swa; + u32 fd_len; percpu_stats = this_cpu_ptr(priv->percpu_stats); percpu_extras = this_cpu_ptr(priv->percpu_extras); + fd = (this_cpu_ptr(priv->fd))->array; needed_headroom = dpaa2_eth_needed_headroom(skb); @@ -1130,20 +1382,28 @@ static netdev_tx_t __dpaa2_eth_tx(struct sk_buff *skb, } /* Setup the FD fields */ - memset(&fd, 0, sizeof(fd)); - if (skb_is_nonlinear(skb)) { - err = dpaa2_eth_build_sg_fd(priv, skb, &fd, &swa); + if (skb_is_gso(skb)) { + err = dpaa2_eth_build_gso_fd(priv, skb, fd, &num_fds, &fd_len); + percpu_extras->tx_sg_frames += num_fds; + percpu_extras->tx_sg_bytes += fd_len; + percpu_extras->tx_tso_frames += num_fds; + percpu_extras->tx_tso_bytes += fd_len; + } else if (skb_is_nonlinear(skb)) { + err = dpaa2_eth_build_sg_fd(priv, skb, fd, &swa); percpu_extras->tx_sg_frames++; percpu_extras->tx_sg_bytes += skb->len; + fd_len = dpaa2_fd_get_len(fd); } else if (skb_headroom(skb) < needed_headroom) { - err = dpaa2_eth_build_sg_fd_single_buf(priv, skb, &fd, &swa); + err = dpaa2_eth_build_sg_fd_single_buf(priv, skb, fd, &swa); percpu_extras->tx_sg_frames++; percpu_extras->tx_sg_bytes += skb->len; percpu_extras->tx_converted_sg_frames++; percpu_extras->tx_converted_sg_bytes += skb->len; + fd_len = dpaa2_fd_get_len(fd); } else { - err = dpaa2_eth_build_single_fd(priv, skb, &fd, &swa); + err = dpaa2_eth_build_single_fd(priv, skb, fd, &swa); + fd_len = dpaa2_fd_get_len(fd); } if (unlikely(err)) { @@ -1151,11 +1411,12 @@ static netdev_tx_t __dpaa2_eth_tx(struct sk_buff *skb, goto err_build_fd; } - if (skb->cb[0]) - dpaa2_eth_enable_tx_tstamp(priv, &fd, swa, skb); + if (swa && skb->cb[0]) + dpaa2_eth_enable_tx_tstamp(priv, fd, swa, skb); /* Tracing point */ - trace_dpaa2_tx_fd(net_dev, &fd); + for (i = 0; i < num_fds; i++) + trace_dpaa2_tx_fd(net_dev, &fd[i]); /* TxConf FQ selection relies on queue id from the stack. * In case of a forwarded frame from another DPNI interface, we choose @@ -1175,27 +1436,32 @@ static netdev_tx_t __dpaa2_eth_tx(struct sk_buff *skb, queue_mapping %= dpaa2_eth_queue_count(priv); } fq = &priv->fq[queue_mapping]; - - fd_len = dpaa2_fd_get_len(&fd); nq = netdev_get_tx_queue(net_dev, queue_mapping); netdev_tx_sent_queue(nq, fd_len); /* Everything that happens after this enqueues might race with * the Tx confirmation callback for this frame */ - for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) { - err = priv->enqueue(priv, fq, &fd, prio, 1, NULL); - if (err != -EBUSY) - break; + max_retries = num_fds * DPAA2_ETH_ENQUEUE_RETRIES; + while (total_enqueued < num_fds && retries < max_retries) { + err = priv->enqueue(priv, fq, &fd[total_enqueued], + prio, num_fds - total_enqueued, &enqueued); + if (err == -EBUSY) { + retries++; + continue; + } + + total_enqueued += enqueued; } - percpu_extras->tx_portal_busy += i; + percpu_extras->tx_portal_busy += retries; + if (unlikely(err < 0)) { percpu_stats->tx_errors++; /* Clean up everything, including freeing the skb */ - dpaa2_eth_free_tx_fd(priv, fq, &fd, false); + dpaa2_eth_free_tx_fd(priv, fq, fd, false); netdev_tx_completed_queue(nq, 1, fd_len); } else { - percpu_stats->tx_packets++; + percpu_stats->tx_packets += total_enqueued; percpu_stats->tx_bytes += fd_len; } @@ -1523,7 +1789,7 @@ static void dpaa2_eth_sgt_cache_drain(struct dpaa2_eth_priv *priv) count = sgt_cache->count; for (i = 0; i < count; i++) - kfree(sgt_cache->buf[i]); + skb_free_frag(sgt_cache->buf[i]); sgt_cache->count = 0; } } @@ -1811,8 +2077,10 @@ static int dpaa2_eth_open(struct net_device *net_dev) goto enable_err; } - if (dpaa2_eth_is_type_phy(priv)) + if (dpaa2_eth_is_type_phy(priv)) { + dpaa2_mac_start(priv->mac); phylink_start(priv->mac->phylink); + } return 0; @@ -1887,6 +2155,7 @@ static int dpaa2_eth_stop(struct net_device *net_dev) if (dpaa2_eth_is_type_phy(priv)) { phylink_stop(priv->mac->phylink); + dpaa2_mac_stop(priv->mac); } else { netif_tx_stop_all_queues(net_dev); netif_carrier_off(net_dev); @@ -2207,6 +2476,9 @@ static int dpaa2_eth_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) config.rx_filter = HWTSTAMP_FILTER_ALL; } + if (priv->tx_tstamp_type == HWTSTAMP_TX_ONESTEP_SYNC) + dpaa2_ptp_onestep_reg_update_method(priv); + return copy_to_user(rq->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; } @@ -4100,6 +4372,8 @@ static int dpaa2_eth_netdev_init(struct net_device *net_dev) return err; } + dpaa2_eth_detect_features(priv); + /* Capabilities listing */ supported |= IFF_LIVE_ADDR_CHANGE; @@ -4115,7 +4389,8 @@ static int dpaa2_eth_netdev_init(struct net_device *net_dev) net_dev->features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA | - NETIF_F_LLTX | NETIF_F_HW_TC; + NETIF_F_LLTX | NETIF_F_HW_TC | NETIF_F_TSO; + net_dev->gso_max_segs = DPAA2_ETH_ENQUEUE_MAX_FDS; net_dev->hw_features = net_dev->features; if (priv->dpni_attrs.vlan_filter_entries) @@ -4397,6 +4672,13 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) goto err_alloc_sgt_cache; } + priv->fd = alloc_percpu(*priv->fd); + if (!priv->fd) { + dev_err(dev, "alloc_percpu(fds) failed\n"); + err = -ENOMEM; + goto err_alloc_fds; + } + err = dpaa2_eth_netdev_init(net_dev); if (err) goto err_netdev_init; @@ -4484,6 +4766,8 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) err_alloc_rings: err_csum: err_netdev_init: + free_percpu(priv->fd); +err_alloc_fds: free_percpu(priv->sgt_cache); err_alloc_sgt_cache: free_percpu(priv->percpu_extras); @@ -4539,6 +4823,7 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev) fsl_mc_free_irqs(ls_dev); dpaa2_eth_free_rings(priv); + free_percpu(priv->fd); free_percpu(priv->sgt_cache); free_percpu(priv->percpu_stats); free_percpu(priv->percpu_extras); @@ -4547,6 +4832,8 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev) dpaa2_eth_free_dpbp(priv); dpaa2_eth_free_dpio(priv); dpaa2_eth_free_dpni(priv); + if (priv->onestep_reg_base) + iounmap(priv->onestep_reg_base); fsl_mc_portal_free(priv->mc_io); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h index e54e70ebdd05..447718483ef4 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h @@ -122,6 +122,7 @@ enum dpaa2_eth_swa_type { DPAA2_ETH_SWA_SINGLE, DPAA2_ETH_SWA_SG, DPAA2_ETH_SWA_XDP, + DPAA2_ETH_SWA_SW_TSO, }; /* Must keep this struct smaller than DPAA2_ETH_SWA_SIZE */ @@ -142,6 +143,12 @@ struct dpaa2_eth_swa { int dma_size; struct xdp_frame *xdpf; } xdp; + struct { + struct sk_buff *skb; + int num_sg; + int sgt_size; + int is_last_fd; + } tso; }; }; @@ -354,6 +361,8 @@ struct dpaa2_eth_drv_stats { __u64 tx_conf_bytes; __u64 tx_sg_frames; __u64 tx_sg_bytes; + __u64 tx_tso_frames; + __u64 tx_tso_bytes; __u64 rx_sg_frames; __u64 rx_sg_bytes; /* Linear skbs sent as a S/G FD due to insufficient headroom */ @@ -493,8 +502,15 @@ struct dpaa2_eth_trap_data { struct dpaa2_eth_priv *priv; }; +#define DPAA2_ETH_SG_ENTRIES_MAX (PAGE_SIZE / sizeof(struct scatterlist)) + #define DPAA2_ETH_DEFAULT_COPYBREAK 512 +#define DPAA2_ETH_ENQUEUE_MAX_FDS 200 +struct dpaa2_eth_fds { + struct dpaa2_fd array[DPAA2_ETH_ENQUEUE_MAX_FDS]; +}; + /* Driver private data */ struct dpaa2_eth_priv { struct net_device *net_dev; @@ -510,12 +526,15 @@ struct dpaa2_eth_priv { u8 num_channels; struct dpaa2_eth_channel *channel[DPAA2_ETH_MAX_DPCONS]; struct dpaa2_eth_sgt_cache __percpu *sgt_cache; - + unsigned long features; struct dpni_attr dpni_attrs; u16 dpni_ver_major; u16 dpni_ver_minor; u16 tx_data_offset; - + void __iomem *onestep_reg_base; + u8 ptp_correction_off; + void (*dpaa2_set_onestep_params_cb)(struct dpaa2_eth_priv *priv, + u32 offset, u8 udp); struct fsl_mc_device *dpbp_dev; u16 rx_buf_size; u16 bpid; @@ -577,6 +596,8 @@ struct dpaa2_eth_priv { struct devlink_port devlink_port; u32 rx_copybreak; + + struct dpaa2_eth_fds __percpu *fd; }; struct dpaa2_eth_devlink_priv { @@ -655,6 +676,13 @@ enum dpaa2_eth_rx_dist { #define DPAA2_ETH_DIST_L4DST BIT(8) #define DPAA2_ETH_DIST_ALL (~0ULL) +#define DPNI_PTP_ONESTEP_VER_MAJOR 8 +#define DPNI_PTP_ONESTEP_VER_MINOR 2 +#define DPAA2_ETH_FEATURE_ONESTEP_CFG_DIRECT BIT(0) +#define DPAA2_PTP_SINGLE_STEP_ENABLE BIT(31) +#define DPAA2_PTP_SINGLE_STEP_CH BIT(7) +#define DPAA2_PTP_SINGLE_CORRECTION_OFF(v) ((v) << 8) + #define DPNI_PAUSE_VER_MAJOR 7 #define DPNI_PAUSE_VER_MINOR 13 #define dpaa2_eth_has_pause_support(priv) \ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c index 3fdbf87dccb1..eea7d7a07c00 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c @@ -44,6 +44,8 @@ static char dpaa2_ethtool_extras[][ETH_GSTRING_LEN] = { "[drv] tx conf bytes", "[drv] tx sg frames", "[drv] tx sg bytes", + "[drv] tx tso frames", + "[drv] tx tso bytes", "[drv] rx sg frames", "[drv] rx sg bytes", "[drv] tx converted sg frames", diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index 623d113b6581..c48811d3bcd5 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -3,6 +3,7 @@ #include #include +#include #include #include "dpaa2-eth.h" @@ -11,6 +12,28 @@ #define phylink_to_dpaa2_mac(config) \ container_of((config), struct dpaa2_mac, phylink_config) +#define DPMAC_PROTOCOL_CHANGE_VER_MAJOR 4 +#define DPMAC_PROTOCOL_CHANGE_VER_MINOR 8 + +#define DPAA2_MAC_FEATURE_PROTOCOL_CHANGE BIT(0) + +static int dpaa2_mac_cmp_ver(struct dpaa2_mac *mac, + u16 ver_major, u16 ver_minor) +{ + if (mac->ver_major == ver_major) + return mac->ver_minor - ver_minor; + return mac->ver_major - ver_major; +} + +static void dpaa2_mac_detect_features(struct dpaa2_mac *mac) +{ + mac->features = 0; + + if (dpaa2_mac_cmp_ver(mac, DPMAC_PROTOCOL_CHANGE_VER_MAJOR, + DPMAC_PROTOCOL_CHANGE_VER_MINOR) >= 0) + mac->features |= DPAA2_MAC_FEATURE_PROTOCOL_CHANGE; +} + static int phy_mode(enum dpmac_eth_if eth_if, phy_interface_t *if_mode) { *if_mode = PHY_INTERFACE_MODE_NA; @@ -38,6 +61,29 @@ static int phy_mode(enum dpmac_eth_if eth_if, phy_interface_t *if_mode) return 0; } +static enum dpmac_eth_if dpmac_eth_if_mode(phy_interface_t if_mode) +{ + switch (if_mode) { + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + return DPMAC_ETH_IF_RGMII; + case PHY_INTERFACE_MODE_USXGMII: + return DPMAC_ETH_IF_USXGMII; + case PHY_INTERFACE_MODE_QSGMII: + return DPMAC_ETH_IF_QSGMII; + case PHY_INTERFACE_MODE_SGMII: + return DPMAC_ETH_IF_SGMII; + case PHY_INTERFACE_MODE_10GBASER: + return DPMAC_ETH_IF_XFI; + case PHY_INTERFACE_MODE_1000BASEX: + return DPMAC_ETH_IF_1000BASEX; + default: + return DPMAC_ETH_IF_MII; + } +} + static struct fwnode_handle *dpaa2_mac_get_node(struct device *dev, u16 dpmac_id) { @@ -100,6 +146,14 @@ static int dpaa2_mac_get_if_mode(struct fwnode_handle *dpmac_node, return err; } +static struct phylink_pcs *dpaa2_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config); + + return mac->pcs; +} + static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { @@ -117,6 +171,19 @@ static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode, if (err) netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n", __func__, err); + + if (!mac->serdes_phy) + return; + + /* This happens only if we support changing of protocol at runtime */ + err = dpmac_set_protocol(mac->mc_io, 0, mac->mc_dev->mc_handle, + dpmac_eth_if_mode(state->interface)); + if (err) + netdev_err(mac->net_dev, "dpmac_set_protocol() = %d\n", err); + + err = phy_set_mode_ext(mac->serdes_phy, PHY_MODE_ETHERNET, state->interface); + if (err) + netdev_err(mac->net_dev, "phy_set_mode_ext() = %d\n", err); } static void dpaa2_mac_link_up(struct phylink_config *config, @@ -172,6 +239,7 @@ static void dpaa2_mac_link_down(struct phylink_config *config, static const struct phylink_mac_ops dpaa2_mac_phylink_ops = { .validate = phylink_generic_validate, + .mac_select_pcs = dpaa2_mac_select_pcs, .mac_config = dpaa2_mac_config, .mac_link_up = dpaa2_mac_link_up, .mac_link_down = dpaa2_mac_link_down, @@ -226,10 +294,66 @@ static void dpaa2_pcs_destroy(struct dpaa2_mac *mac) } } +static void dpaa2_mac_set_supported_interfaces(struct dpaa2_mac *mac) +{ + int intf, err; + + /* We support the current interface mode, and if we have a PCS + * similar interface modes that do not require the SerDes lane to be + * reconfigured. + */ + __set_bit(mac->if_mode, mac->phylink_config.supported_interfaces); + if (mac->pcs) { + switch (mac->if_mode) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_SGMII: + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + mac->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, + mac->phylink_config.supported_interfaces); + break; + + default: + break; + } + } + + if (!mac->serdes_phy) + return; + + /* In case we have access to the SerDes phy/lane, then ask the SerDes + * driver what interfaces are supported based on the current PLL + * configuration. + */ + for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) { + if (intf == PHY_INTERFACE_MODE_NA) + continue; + + err = phy_validate(mac->serdes_phy, PHY_MODE_ETHERNET, intf, NULL); + if (err) + continue; + + __set_bit(intf, mac->phylink_config.supported_interfaces); + } +} + +void dpaa2_mac_start(struct dpaa2_mac *mac) +{ + if (mac->serdes_phy) + phy_power_on(mac->serdes_phy); +} + +void dpaa2_mac_stop(struct dpaa2_mac *mac) +{ + if (mac->serdes_phy) + phy_power_off(mac->serdes_phy); +} + int dpaa2_mac_connect(struct dpaa2_mac *mac) { struct net_device *net_dev = mac->net_dev; struct fwnode_handle *dpmac_node; + struct phy *serdes_phy = NULL; struct phylink *phylink; int err; @@ -246,6 +370,20 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) return -EINVAL; mac->if_mode = err; + if (mac->features & DPAA2_MAC_FEATURE_PROTOCOL_CHANGE && + !phy_interface_mode_is_rgmii(mac->if_mode) && + is_of_node(dpmac_node)) { + serdes_phy = of_phy_get(to_of_node(dpmac_node), NULL); + + if (serdes_phy == ERR_PTR(-ENODEV)) + serdes_phy = NULL; + else if (IS_ERR(serdes_phy)) + return PTR_ERR(serdes_phy); + else + phy_init(serdes_phy); + } + mac->serdes_phy = serdes_phy; + /* The MAC does not have the capability to add RGMII delays so * error out if the interface mode requests them and there is no PHY * to act upon them @@ -274,25 +412,7 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) MAC_10FD | MAC_100FD | MAC_1000FD | MAC_2500FD | MAC_5000FD | MAC_10000FD; - /* We support the current interface mode, and if we have a PCS - * similar interface modes that do not require the PLLs to be - * reconfigured. - */ - __set_bit(mac->if_mode, mac->phylink_config.supported_interfaces); - if (mac->pcs) { - switch (mac->if_mode) { - case PHY_INTERFACE_MODE_1000BASEX: - case PHY_INTERFACE_MODE_SGMII: - __set_bit(PHY_INTERFACE_MODE_1000BASEX, - mac->phylink_config.supported_interfaces); - __set_bit(PHY_INTERFACE_MODE_SGMII, - mac->phylink_config.supported_interfaces); - break; - - default: - break; - } - } + dpaa2_mac_set_supported_interfaces(mac); phylink = phylink_create(&mac->phylink_config, dpmac_node, mac->if_mode, @@ -303,9 +423,6 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) } mac->phylink = phylink; - if (mac->pcs) - phylink_set_pcs(mac->phylink, mac->pcs); - err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0); if (err) { netdev_err(net_dev, "phylink_fwnode_phy_connect() = %d\n", err); @@ -330,6 +447,8 @@ void dpaa2_mac_disconnect(struct dpaa2_mac *mac) phylink_disconnect_phy(mac->phylink); phylink_destroy(mac->phylink); dpaa2_pcs_destroy(mac); + of_phy_put(mac->serdes_phy); + mac->serdes_phy = NULL; } int dpaa2_mac_open(struct dpaa2_mac *mac) @@ -353,6 +472,14 @@ int dpaa2_mac_open(struct dpaa2_mac *mac) goto err_close_dpmac; } + err = dpmac_get_api_version(mac->mc_io, 0, &mac->ver_major, &mac->ver_minor); + if (err) { + netdev_err(net_dev, "dpmac_get_api_version() = %d\n", err); + goto err_close_dpmac; + } + + dpaa2_mac_detect_features(mac); + /* Find the device node representing the MAC device and link the device * behind the associated netdev to it. */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h index 1331a8477fe4..a58cab188a99 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h @@ -17,6 +17,8 @@ struct dpaa2_mac { struct net_device *net_dev; struct fsl_mc_io *mc_io; struct dpmac_attr attr; + u16 ver_major, ver_minor; + unsigned long features; struct phylink_config phylink_config; struct phylink *phylink; @@ -24,6 +26,8 @@ struct dpaa2_mac { enum dpmac_link_type if_link_type; struct phylink_pcs *pcs; struct fwnode_handle *fw_node; + + struct phy *serdes_phy; }; bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev, @@ -43,4 +47,8 @@ void dpaa2_mac_get_strings(u8 *data); void dpaa2_mac_get_ethtool_stats(struct dpaa2_mac *mac, u64 *data); +void dpaa2_mac_start(struct dpaa2_mac *mac); + +void dpaa2_mac_stop(struct dpaa2_mac *mac); + #endif /* DPAA2_MAC_H */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 9a561072aa4a..e507e9065214 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -703,8 +703,10 @@ static int dpaa2_switch_port_open(struct net_device *netdev) dpaa2_switch_enable_ctrl_if_napi(ethsw); - if (dpaa2_switch_port_is_type_phy(port_priv)) + if (dpaa2_switch_port_is_type_phy(port_priv)) { + dpaa2_mac_start(port_priv->mac); phylink_start(port_priv->mac->phylink); + } return 0; } @@ -717,6 +719,7 @@ static int dpaa2_switch_port_stop(struct net_device *netdev) if (dpaa2_switch_port_is_type_phy(port_priv)) { phylink_stop(port_priv->mac->phylink); + dpaa2_mac_stop(port_priv->mac); } else { netif_tx_stop_all_queues(netdev); netif_carrier_off(netdev); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpmac-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpmac-cmd.h index a24b20f76938..e9ac2ecef3be 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpmac-cmd.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpmac-cmd.h @@ -19,11 +19,15 @@ #define DPMAC_CMDID_CLOSE DPMAC_CMD(0x800) #define DPMAC_CMDID_OPEN DPMAC_CMD(0x80c) +#define DPMAC_CMDID_GET_API_VERSION DPMAC_CMD(0xa0c) + #define DPMAC_CMDID_GET_ATTR DPMAC_CMD(0x004) #define DPMAC_CMDID_SET_LINK_STATE DPMAC_CMD_V2(0x0c3) #define DPMAC_CMDID_GET_COUNTER DPMAC_CMD(0x0c4) +#define DPMAC_CMDID_SET_PROTOCOL DPMAC_CMD(0x0c7) + /* Macros for accessing command fields smaller than 1byte */ #define DPMAC_MASK(field) \ GENMASK(DPMAC_##field##_SHIFT + DPMAC_##field##_SIZE - 1, \ @@ -70,4 +74,12 @@ struct dpmac_rsp_get_counter { __le64 counter; }; +struct dpmac_rsp_get_api_version { + __le16 major; + __le16 minor; +}; + +struct dpmac_cmd_set_protocol { + u8 eth_if; +}; #endif /* _FSL_DPMAC_CMD_H */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpmac.c b/drivers/net/ethernet/freescale/dpaa2/dpmac.c index d5997b654562..f440a4c3b70c 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpmac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpmac.c @@ -181,3 +181,57 @@ int dpmac_get_counter(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, return 0; } + +/** + * dpmac_get_api_version() - Get Data Path MAC version + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @major_ver: Major version of data path mac API + * @minor_ver: Minor version of data path mac API + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_get_api_version(struct fsl_mc_io *mc_io, u32 cmd_flags, + u16 *major_ver, u16 *minor_ver) +{ + struct dpmac_rsp_get_api_version *rsp_params; + struct fsl_mc_command cmd = { 0 }; + int err; + + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_API_VERSION, + cmd_flags, + 0); + + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + rsp_params = (struct dpmac_rsp_get_api_version *)cmd.params; + *major_ver = le16_to_cpu(rsp_params->major); + *minor_ver = le16_to_cpu(rsp_params->minor); + + return 0; +} + +/** + * dpmac_set_protocol() - Reconfigure the DPMAC protocol + * @mc_io: Pointer to opaque I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @protocol: New protocol for the DPMAC to be reconfigured in. + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_set_protocol(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, + enum dpmac_eth_if protocol) +{ + struct dpmac_cmd_set_protocol *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_PROTOCOL, + cmd_flags, token); + cmd_params = (struct dpmac_cmd_set_protocol *)cmd.params; + cmd_params->eth_if = protocol; + + return mc_send_command(mc_io, &cmd); +} diff --git a/drivers/net/ethernet/freescale/dpaa2/dpmac.h b/drivers/net/ethernet/freescale/dpaa2/dpmac.h index 8f7ceb731282..17488819ef68 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpmac.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpmac.h @@ -205,4 +205,9 @@ enum dpmac_counter_id { int dpmac_get_counter(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, enum dpmac_counter_id id, u64 *value); +int dpmac_get_api_version(struct fsl_mc_io *mc_io, u32 cmd_flags, + u16 *major_ver, u16 *minor_ver); + +int dpmac_set_protocol(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, + enum dpmac_eth_if protocol); #endif /* __FSL_DPMAC_H */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h index 9f80bdfeedec..828f538097af 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h @@ -98,7 +98,7 @@ #define DPNI_CMDID_GET_LINK_CFG DPNI_CMD(0x278) #define DPNI_CMDID_SET_SINGLE_STEP_CFG DPNI_CMD(0x279) -#define DPNI_CMDID_GET_SINGLE_STEP_CFG DPNI_CMD(0x27a) +#define DPNI_CMDID_GET_SINGLE_STEP_CFG DPNI_CMD_V2(0x27a) /* Macros for accessing command fields smaller than 1byte */ #define DPNI_MASK(field) \ @@ -658,12 +658,16 @@ struct dpni_cmd_single_step_cfg { __le16 flags; __le16 offset; __le32 peer_delay; + __le32 ptp_onestep_reg_base; + __le32 pad0; }; struct dpni_rsp_single_step_cfg { __le16 flags; __le16 offset; __le32 peer_delay; + __le32 ptp_onestep_reg_base; + __le32 pad0; }; struct dpni_cmd_enable_vlan_filter { diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.c b/drivers/net/ethernet/freescale/dpaa2/dpni.c index d6afada99fb6..6c3b36f20fb8 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpni.c @@ -2136,6 +2136,8 @@ int dpni_get_single_step_cfg(struct fsl_mc_io *mc_io, ptp_cfg->ch_update = dpni_get_field(le16_to_cpu(rsp_params->flags), PTP_CH_UPDATE) ? 1 : 0; ptp_cfg->peer_delay = le32_to_cpu(rsp_params->peer_delay); + ptp_cfg->ptp_onestep_reg_base = + le32_to_cpu(rsp_params->ptp_onestep_reg_base); return err; } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.h b/drivers/net/ethernet/freescale/dpaa2/dpni.h index 7de0562bbf59..6fffd519aa00 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpni.h @@ -1074,12 +1074,18 @@ int dpni_set_tx_shaping(struct fsl_mc_io *mc_io, * @peer_delay: For peer-to-peer transparent clocks add this value to the * correction field in addition to the transient time update. * The value expresses nanoseconds. + * @ptp_onestep_reg_base: 1588 SINGLE_STEP register base address. This address + * is used to update directly the register contents. + * User has to create an address mapping for it. + * + * */ struct dpni_single_step_cfg { u8 en; u8 ch_update; u16 offset; u32 peer_delay; + u32 ptp_onestep_reg_base; }; int dpni_set_single_step_cfg(struct fsl_mc_io *mc_io, diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h index fb39e406b7fc..68d806dc3701 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.h +++ b/drivers/net/ethernet/freescale/enetc/enetc.h @@ -18,6 +18,8 @@ #define ENETC_MAX_MTU (ENETC_MAC_MAXFRM_SIZE - \ (ETH_FCS_LEN + ETH_HLEN + VLAN_HLEN)) +#define ENETC_CBD_DATA_MEM_ALIGN 64 + struct enetc_tx_swbd { union { struct sk_buff *skb; @@ -415,6 +417,42 @@ int enetc_get_rss_table(struct enetc_si *si, u32 *table, int count); int enetc_set_rss_table(struct enetc_si *si, const u32 *table, int count); int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd); +static inline void *enetc_cbd_alloc_data_mem(struct enetc_si *si, + struct enetc_cbd *cbd, + int size, dma_addr_t *dma, + void **data_align) +{ + struct enetc_cbdr *ring = &si->cbd_ring; + dma_addr_t dma_align; + void *data; + + data = dma_alloc_coherent(ring->dma_dev, + size + ENETC_CBD_DATA_MEM_ALIGN, + dma, GFP_KERNEL); + if (!data) { + dev_err(ring->dma_dev, "CBD alloc data memory failed!\n"); + return NULL; + } + + dma_align = ALIGN(*dma, ENETC_CBD_DATA_MEM_ALIGN); + *data_align = PTR_ALIGN(data, ENETC_CBD_DATA_MEM_ALIGN); + + cbd->addr[0] = cpu_to_le32(lower_32_bits(dma_align)); + cbd->addr[1] = cpu_to_le32(upper_32_bits(dma_align)); + cbd->length = cpu_to_le16(size); + + return data; +} + +static inline void enetc_cbd_free_data_mem(struct enetc_si *si, int size, + void *data, dma_addr_t *dma) +{ + struct enetc_cbdr *ring = &si->cbd_ring; + + dma_free_coherent(ring->dma_dev, size + ENETC_CBD_DATA_MEM_ALIGN, + data, *dma); +} + #ifdef CONFIG_FSL_ENETC_QOS int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data); void enetc_sched_speed_set(struct enetc_ndev_priv *priv, int speed); diff --git a/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c b/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c index 073e56dcca4e..af68dc46a795 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c @@ -166,70 +166,55 @@ int enetc_set_mac_flt_entry(struct enetc_si *si, int index, return enetc_send_cmd(si, &cbd); } -#define RFSE_ALIGN 64 /* Set entry in RFS table */ int enetc_set_fs_entry(struct enetc_si *si, struct enetc_cmd_rfse *rfse, int index) { struct enetc_cbdr *ring = &si->cbd_ring; struct enetc_cbd cbd = {.cmd = 0}; - dma_addr_t dma, dma_align; void *tmp, *tmp_align; + dma_addr_t dma; int err; /* fill up the "set" descriptor */ cbd.cmd = 0; cbd.cls = 4; cbd.index = cpu_to_le16(index); - cbd.length = cpu_to_le16(sizeof(*rfse)); cbd.opt[3] = cpu_to_le32(0); /* SI */ - tmp = dma_alloc_coherent(ring->dma_dev, sizeof(*rfse) + RFSE_ALIGN, - &dma, GFP_KERNEL); - if (!tmp) { - dev_err(ring->dma_dev, "DMA mapping of RFS entry failed!\n"); + tmp = enetc_cbd_alloc_data_mem(si, &cbd, sizeof(*rfse), + &dma, &tmp_align); + if (!tmp) return -ENOMEM; - } - dma_align = ALIGN(dma, RFSE_ALIGN); - tmp_align = PTR_ALIGN(tmp, RFSE_ALIGN); memcpy(tmp_align, rfse, sizeof(*rfse)); - cbd.addr[0] = cpu_to_le32(lower_32_bits(dma_align)); - cbd.addr[1] = cpu_to_le32(upper_32_bits(dma_align)); - err = enetc_send_cmd(si, &cbd); if (err) dev_err(ring->dma_dev, "FS entry add failed (%d)!", err); - dma_free_coherent(ring->dma_dev, sizeof(*rfse) + RFSE_ALIGN, - tmp, dma); + enetc_cbd_free_data_mem(si, sizeof(*rfse), tmp, &dma); return err; } -#define RSSE_ALIGN 64 static int enetc_cmd_rss_table(struct enetc_si *si, u32 *table, int count, bool read) { struct enetc_cbdr *ring = &si->cbd_ring; struct enetc_cbd cbd = {.cmd = 0}; - dma_addr_t dma, dma_align; u8 *tmp, *tmp_align; + dma_addr_t dma; int err, i; - if (count < RSSE_ALIGN) + if (count < ENETC_CBD_DATA_MEM_ALIGN) /* HW only takes in a full 64 entry table */ return -EINVAL; - tmp = dma_alloc_coherent(ring->dma_dev, count + RSSE_ALIGN, - &dma, GFP_KERNEL); - if (!tmp) { - dev_err(ring->dma_dev, "DMA mapping of RSS table failed!\n"); + tmp = enetc_cbd_alloc_data_mem(si, &cbd, count, + &dma, (void *)&tmp_align); + if (!tmp) return -ENOMEM; - } - dma_align = ALIGN(dma, RSSE_ALIGN); - tmp_align = PTR_ALIGN(tmp, RSSE_ALIGN); if (!read) for (i = 0; i < count; i++) @@ -238,10 +223,6 @@ static int enetc_cmd_rss_table(struct enetc_si *si, u32 *table, int count, /* fill up the descriptor */ cbd.cmd = read ? 2 : 1; cbd.cls = 3; - cbd.length = cpu_to_le16(count); - - cbd.addr[0] = cpu_to_le32(lower_32_bits(dma_align)); - cbd.addr[1] = cpu_to_le32(upper_32_bits(dma_align)); err = enetc_send_cmd(si, &cbd); if (err) @@ -251,7 +232,7 @@ static int enetc_cmd_rss_table(struct enetc_si *si, u32 *table, int count, for (i = 0; i < count; i++) table[i] = tmp_align[i]; - dma_free_coherent(ring->dma_dev, count + RSSE_ALIGN, tmp, dma); + enetc_cbd_free_data_mem(si, count, tmp, &dma); return err; } diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mdio.c b/drivers/net/ethernet/freescale/enetc/enetc_mdio.c index 70e6d97b380f..1c8f5cc6dec4 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_mdio.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_mdio.c @@ -147,7 +147,7 @@ int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum) /* return all Fs if nothing was there */ if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) { dev_dbg(&bus->dev, - "Error while reading PHY%d reg at %d.%hhu\n", + "Error while reading PHY%d reg at %d.%d\n", phy_id, dev_addr, regnum); return 0xffff; } diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c index ed16a5ac9ad0..a0c75c717073 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c @@ -934,18 +934,21 @@ static void enetc_mdiobus_destroy(struct enetc_pf *pf) enetc_imdio_remove(pf); } +static struct phylink_pcs * +enetc_pl_mac_select_pcs(struct phylink_config *config, phy_interface_t iface) +{ + struct enetc_pf *pf = phylink_to_enetc_pf(config); + + return pf->pcs; +} + static void enetc_pl_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { struct enetc_pf *pf = phylink_to_enetc_pf(config); - struct enetc_ndev_priv *priv; enetc_mac_config(&pf->si->hw, state->interface); - - priv = netdev_priv(pf->si->ndev); - if (pf->pcs) - phylink_set_pcs(priv->phylink, pf->pcs); } static void enetc_force_rgmii_mac(struct enetc_hw *hw, int speed, int duplex) @@ -1062,6 +1065,7 @@ static void enetc_pl_mac_link_down(struct phylink_config *config, static const struct phylink_mac_ops enetc_mac_phylink_ops = { .validate = phylink_generic_validate, + .mac_select_pcs = enetc_pl_mac_select_pcs, .mac_config = enetc_pl_mac_config, .mac_link_up = enetc_pl_mac_link_up, .mac_link_down = enetc_pl_mac_link_down, diff --git a/drivers/net/ethernet/freescale/enetc/enetc_qos.c b/drivers/net/ethernet/freescale/enetc/enetc_qos.c index 3555c12edb45..79afb1d7289b 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_qos.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c @@ -52,10 +52,11 @@ static int enetc_setup_taprio(struct net_device *ndev, struct enetc_cbd cbd = {.cmd = 0}; struct tgs_gcl_conf *gcl_config; struct tgs_gcl_data *gcl_data; - struct gce *gce; dma_addr_t dma; + struct gce *gce; u16 data_size; u16 gcl_len; + void *tmp; u32 tge; int err; int i; @@ -82,8 +83,9 @@ static int enetc_setup_taprio(struct net_device *ndev, gcl_config = &cbd.gcl_conf; data_size = struct_size(gcl_data, entry, gcl_len); - gcl_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); - if (!gcl_data) + tmp = enetc_cbd_alloc_data_mem(priv->si, &cbd, data_size, + &dma, (void *)&gcl_data); + if (!tmp) return -ENOMEM; gce = (struct gce *)(gcl_data + 1); @@ -107,19 +109,8 @@ static int enetc_setup_taprio(struct net_device *ndev, temp_gce->period = cpu_to_le32(temp_entry->interval); } - cbd.length = cpu_to_le16(data_size); cbd.status_flags = 0; - dma = dma_map_single(&priv->si->pdev->dev, gcl_data, - data_size, DMA_TO_DEVICE); - if (dma_mapping_error(&priv->si->pdev->dev, dma)) { - netdev_err(priv->si->ndev, "DMA mapping failed!\n"); - kfree(gcl_data); - return -ENOMEM; - } - - cbd.addr[0] = cpu_to_le32(lower_32_bits(dma)); - cbd.addr[1] = cpu_to_le32(upper_32_bits(dma)); cbd.cls = BDCR_CMD_PORT_GCL; cbd.status_flags = 0; @@ -132,8 +123,7 @@ static int enetc_setup_taprio(struct net_device *ndev, ENETC_QBV_PTGCR_OFFSET, tge & (~ENETC_QBV_TGE)); - dma_unmap_single(&priv->si->pdev->dev, dma, data_size, DMA_TO_DEVICE); - kfree(gcl_data); + enetc_cbd_free_data_mem(priv->si, data_size, tmp, &dma); return err; } @@ -450,6 +440,7 @@ static struct actions_fwd enetc_act_fwd[] = { }; static struct enetc_psfp epsfp = { + .dev_bitmap = 0, .psfp_sfi_bitmap = NULL, }; @@ -463,8 +454,9 @@ static int enetc_streamid_hw_set(struct enetc_ndev_priv *priv, struct enetc_cbd cbd = {.cmd = 0}; struct streamid_data *si_data; struct streamid_conf *si_conf; - u16 data_size; dma_addr_t dma; + u16 data_size; + void *tmp; int port; int err; @@ -485,21 +477,11 @@ static int enetc_streamid_hw_set(struct enetc_ndev_priv *priv, cbd.status_flags = 0; data_size = sizeof(struct streamid_data); - si_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); - if (!si_data) + tmp = enetc_cbd_alloc_data_mem(priv->si, &cbd, data_size, + &dma, (void *)&si_data); + if (!tmp) return -ENOMEM; - cbd.length = cpu_to_le16(data_size); - dma = dma_map_single(&priv->si->pdev->dev, si_data, - data_size, DMA_FROM_DEVICE); - if (dma_mapping_error(&priv->si->pdev->dev, dma)) { - netdev_err(priv->si->ndev, "DMA mapping failed!\n"); - err = -ENOMEM; - goto out; - } - - cbd.addr[0] = cpu_to_le32(lower_32_bits(dma)); - cbd.addr[1] = cpu_to_le32(upper_32_bits(dma)); eth_broadcast_addr(si_data->dmac); si_data->vid_vidm_tg = (ENETC_CBDR_SID_VID_MASK + ((0x3 << 14) | ENETC_CBDR_SID_VIDM)); @@ -520,11 +502,6 @@ static int enetc_streamid_hw_set(struct enetc_ndev_priv *priv, goto out; /* Enable the entry overwrite again incase space flushed by hardware */ - memset(&cbd, 0, sizeof(cbd)); - - cbd.index = cpu_to_le16((u16)sid->index); - cbd.cmd = 0; - cbd.cls = BDCR_CMD_STREAM_IDENTIFY; cbd.status_flags = 0; si_conf->en = 0x80; @@ -537,11 +514,6 @@ static int enetc_streamid_hw_set(struct enetc_ndev_priv *priv, memset(si_data, 0, data_size); - cbd.length = cpu_to_le16(data_size); - - cbd.addr[0] = cpu_to_le32(lower_32_bits(dma)); - cbd.addr[1] = cpu_to_le32(upper_32_bits(dma)); - /* VIDM default to be 1. * VID Match. If set (b1) then the VID must match, otherwise * any VID is considered a match. VIDM setting is only used @@ -561,10 +533,7 @@ static int enetc_streamid_hw_set(struct enetc_ndev_priv *priv, err = enetc_send_cmd(priv->si, &cbd); out: - if (!dma_mapping_error(&priv->si->pdev->dev, dma)) - dma_unmap_single(&priv->si->pdev->dev, dma, data_size, DMA_FROM_DEVICE); - - kfree(si_data); + enetc_cbd_free_data_mem(priv->si, data_size, tmp, &dma); return err; } @@ -635,6 +604,7 @@ static int enetc_streamcounter_hw_get(struct enetc_ndev_priv *priv, struct sfi_counter_data *data_buf; dma_addr_t dma; u16 data_size; + void *tmp; int err; cbd.index = cpu_to_le16((u16)index); @@ -643,22 +613,12 @@ static int enetc_streamcounter_hw_get(struct enetc_ndev_priv *priv, cbd.status_flags = 0; data_size = sizeof(struct sfi_counter_data); - data_buf = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); - if (!data_buf) + + tmp = enetc_cbd_alloc_data_mem(priv->si, &cbd, data_size, + &dma, (void *)&data_buf); + if (!tmp) return -ENOMEM; - dma = dma_map_single(&priv->si->pdev->dev, data_buf, - data_size, DMA_FROM_DEVICE); - if (dma_mapping_error(&priv->si->pdev->dev, dma)) { - netdev_err(priv->si->ndev, "DMA mapping failed!\n"); - err = -ENOMEM; - goto exit; - } - cbd.addr[0] = cpu_to_le32(lower_32_bits(dma)); - cbd.addr[1] = cpu_to_le32(upper_32_bits(dma)); - - cbd.length = cpu_to_le16(data_size); - err = enetc_send_cmd(priv->si, &cbd); if (err) goto exit; @@ -684,7 +644,8 @@ static int enetc_streamcounter_hw_get(struct enetc_ndev_priv *priv, data_buf->flow_meter_dropl; exit: - kfree(data_buf); + enetc_cbd_free_data_mem(priv->si, data_size, tmp, &dma); + return err; } @@ -726,6 +687,7 @@ static int enetc_streamgate_hw_set(struct enetc_ndev_priv *priv, dma_addr_t dma; u16 data_size; int err, i; + void *tmp; u64 now; cbd.index = cpu_to_le16(sgi->index); @@ -772,25 +734,11 @@ static int enetc_streamgate_hw_set(struct enetc_ndev_priv *priv, sgcl_config->acl_len = (sgi->num_entries - 1) & 0x3; data_size = struct_size(sgcl_data, sgcl, sgi->num_entries); - - sgcl_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); - if (!sgcl_data) + tmp = enetc_cbd_alloc_data_mem(priv->si, &cbd, data_size, + &dma, (void *)&sgcl_data); + if (!tmp) return -ENOMEM; - cbd.length = cpu_to_le16(data_size); - - dma = dma_map_single(&priv->si->pdev->dev, - sgcl_data, data_size, - DMA_FROM_DEVICE); - if (dma_mapping_error(&priv->si->pdev->dev, dma)) { - netdev_err(priv->si->ndev, "DMA mapping failed!\n"); - kfree(sgcl_data); - return -ENOMEM; - } - - cbd.addr[0] = cpu_to_le32(lower_32_bits(dma)); - cbd.addr[1] = cpu_to_le32(upper_32_bits(dma)); - sgce = &sgcl_data->sgcl[0]; sgcl_config->agtst = 0x80; @@ -844,8 +792,7 @@ static int enetc_streamgate_hw_set(struct enetc_ndev_priv *priv, err = enetc_send_cmd(priv->si, &cbd); exit: - kfree(sgcl_data); - + enetc_cbd_free_data_mem(priv->si, data_size, tmp, &dma); return err; } @@ -1074,6 +1021,46 @@ static struct actions_fwd *enetc_check_flow_actions(u64 acts, return NULL; } +static int enetc_psfp_policer_validate(const struct flow_action *action, + const struct flow_action_entry *act, + struct netlink_ext_ack *extack) +{ + if (act->police.exceed.act_id != FLOW_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when exceed action is not drop"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && + act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not pipe or ok"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && + !flow_action_is_last_entry(action, act)) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is ok, but action is not last"); + return -EOPNOTSUPP; + } + + if (act->police.peakrate_bytes_ps || + act->police.avrate || act->police.overhead) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when peakrate/avrate/overhead is configured"); + return -EOPNOTSUPP; + } + + if (act->police.rate_pkt_ps) { + NL_SET_ERR_MSG_MOD(extack, + "QoS offload not support packets per second"); + return -EOPNOTSUPP; + } + + return 0; +} + static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, struct flow_cls_offload *f) { @@ -1230,11 +1217,10 @@ static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, /* Flow meter and max frame size */ if (entryp) { - if (entryp->police.rate_pkt_ps) { - NL_SET_ERR_MSG_MOD(extack, "QoS offload not support packets per second"); - err = -EOPNOTSUPP; + err = enetc_psfp_policer_validate(&rule->action, entryp, extack); + if (err) goto free_sfi; - } + if (entryp->police.burst) { fmi = kzalloc(sizeof(*fmi), GFP_KERNEL); if (!fmi) { diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 796133de527e..11227f51404c 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2797,7 +2797,7 @@ static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable) int ret = 0; if (enable) { - ret = phy_init_eee(ndev->phydev, 0); + ret = phy_init_eee(ndev->phydev, false); if (ret) return ret; diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index af99017a5453..7d49c28215f3 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -101,7 +101,6 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) u32 val, tempval; struct timespec64 ts; u64 ns; - val = 0; if (fep->pps_enable == enable) return 0; diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c index 266e562bd67a..ec90da1de030 100644 --- a/drivers/net/ethernet/freescale/xgmac_mdio.c +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -36,9 +37,10 @@ struct tgec_mdio_controller { } __packed; #define MDIO_STAT_ENC BIT(6) -#define MDIO_STAT_CLKDIV(x) (((x>>1) & 0xff) << 8) +#define MDIO_STAT_CLKDIV(x) (((x) & 0x1ff) << 7) #define MDIO_STAT_BSY BIT(0) #define MDIO_STAT_RD_ER BIT(1) +#define MDIO_STAT_PRE_DIS BIT(5) #define MDIO_CTL_DEV_ADDR(x) (x & 0x1f) #define MDIO_CTL_PORT_ADDR(x) ((x & 0x1f) << 5) #define MDIO_CTL_PRE_DIS BIT(10) @@ -50,6 +52,8 @@ struct tgec_mdio_controller { struct mdio_fsl_priv { struct tgec_mdio_controller __iomem *mdio_base; + struct clk *enet_clk; + u32 mdc_freq; bool is_little_endian; bool has_a009885; bool has_a011043; @@ -239,7 +243,7 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) if ((xgmac_read32(®s->mdio_stat, endian) & MDIO_STAT_RD_ER) && !priv->has_a011043) { dev_dbg(&bus->dev, - "Error while reading PHY%d reg at %d.%hhu\n", + "Error while reading PHY%d reg at %d.%d\n", phy_id, dev_addr, regnum); ret = 0xffff; } else { @@ -254,6 +258,50 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) return ret; } +static int xgmac_mdio_set_mdc_freq(struct mii_bus *bus) +{ + struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv; + struct tgec_mdio_controller __iomem *regs = priv->mdio_base; + struct device *dev = bus->parent; + u32 mdio_stat, div; + + if (device_property_read_u32(dev, "clock-frequency", &priv->mdc_freq)) + return 0; + + priv->enet_clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->enet_clk)) { + dev_err(dev, "Input clock unknown, not changing MDC frequency"); + return PTR_ERR(priv->enet_clk); + } + + div = ((clk_get_rate(priv->enet_clk) / priv->mdc_freq) - 1) / 2; + if (div < 5 || div > 0x1ff) { + dev_err(dev, "Requested MDC frequency is out of range, ignoring"); + return -EINVAL; + } + + mdio_stat = xgmac_read32(®s->mdio_stat, priv->is_little_endian); + mdio_stat &= ~MDIO_STAT_CLKDIV(0x1ff); + mdio_stat |= MDIO_STAT_CLKDIV(div); + xgmac_write32(mdio_stat, ®s->mdio_stat, priv->is_little_endian); + return 0; +} + +static void xgmac_mdio_set_suppress_preamble(struct mii_bus *bus) +{ + struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv; + struct tgec_mdio_controller __iomem *regs = priv->mdio_base; + struct device *dev = bus->parent; + u32 mdio_stat; + + if (!device_property_read_bool(dev, "suppress-preamble")) + return; + + mdio_stat = xgmac_read32(®s->mdio_stat, priv->is_little_endian); + mdio_stat |= MDIO_STAT_PRE_DIS; + xgmac_write32(mdio_stat, ®s->mdio_stat, priv->is_little_endian); +} + static int xgmac_mdio_probe(struct platform_device *pdev) { struct fwnode_handle *fwnode; @@ -273,7 +321,7 @@ static int xgmac_mdio_probe(struct platform_device *pdev) return -EINVAL; } - bus = mdiobus_alloc_size(sizeof(struct mdio_fsl_priv)); + bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(struct mdio_fsl_priv)); if (!bus) return -ENOMEM; @@ -284,13 +332,11 @@ static int xgmac_mdio_probe(struct platform_device *pdev) bus->probe_capabilities = MDIOBUS_C22_C45; snprintf(bus->id, MII_BUS_ID_SIZE, "%pa", &res->start); - /* Set the PHY base address */ priv = bus->priv; - priv->mdio_base = ioremap(res->start, resource_size(res)); - if (!priv->mdio_base) { - ret = -ENOMEM; - goto err_ioremap; - } + priv->mdio_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!priv->mdio_base) + return -ENOMEM; /* For both ACPI and DT cases, endianness of MDIO controller * needs to be specified using "little-endian" property. @@ -303,6 +349,12 @@ static int xgmac_mdio_probe(struct platform_device *pdev) priv->has_a011043 = device_property_read_bool(&pdev->dev, "fsl,erratum-a011043"); + xgmac_mdio_set_suppress_preamble(bus); + + ret = xgmac_mdio_set_mdc_freq(bus); + if (ret) + return ret; + fwnode = pdev->dev.fwnode; if (is_of_node(fwnode)) ret = of_mdiobus_register(bus, to_of_node(fwnode)); @@ -312,31 +364,11 @@ static int xgmac_mdio_probe(struct platform_device *pdev) ret = -EINVAL; if (ret) { dev_err(&pdev->dev, "cannot register MDIO bus\n"); - goto err_registration; + return ret; } platform_set_drvdata(pdev, bus); - return 0; - -err_registration: - iounmap(priv->mdio_base); - -err_ioremap: - mdiobus_free(bus); - - return ret; -} - -static int xgmac_mdio_remove(struct platform_device *pdev) -{ - struct mii_bus *bus = platform_get_drvdata(pdev); - struct mdio_fsl_priv *priv = bus->priv; - - mdiobus_unregister(bus); - iounmap(priv->mdio_base); - mdiobus_free(bus); - return 0; } @@ -364,7 +396,6 @@ static struct platform_driver xgmac_mdio_driver = { .acpi_match_table = xgmac_acpi_match, }, .probe = xgmac_mdio_probe, - .remove = xgmac_mdio_remove, }; module_platform_driver(xgmac_mdio_driver); diff --git a/drivers/net/ethernet/fungible/Kconfig b/drivers/net/ethernet/fungible/Kconfig new file mode 100644 index 000000000000..1ecedecc0f6c --- /dev/null +++ b/drivers/net/ethernet/fungible/Kconfig @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Fungible network driver configuration +# + +config NET_VENDOR_FUNGIBLE + bool "Fungible devices" + default y + help + If you have a Fungible network device, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Fungible cards. If you say Y, you will be asked + for your specific card in the following questions. + +if NET_VENDOR_FUNGIBLE + +config FUN_CORE + tristate + select SBITMAP + help + A service module offering basic common services to Fungible + device drivers. + +source "drivers/net/ethernet/fungible/funeth/Kconfig" + +endif # NET_VENDOR_FUNGIBLE diff --git a/drivers/net/ethernet/fungible/Makefile b/drivers/net/ethernet/fungible/Makefile new file mode 100644 index 000000000000..df759f1585a1 --- /dev/null +++ b/drivers/net/ethernet/fungible/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +# +# Makefile for the Fungible network device drivers. +# + +obj-$(CONFIG_FUN_CORE) += funcore/ +obj-$(CONFIG_FUN_ETH) += funeth/ diff --git a/drivers/net/ethernet/fungible/funcore/Makefile b/drivers/net/ethernet/fungible/funcore/Makefile new file mode 100644 index 000000000000..bc16b264b53e --- /dev/null +++ b/drivers/net/ethernet/fungible/funcore/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +obj-$(CONFIG_FUN_CORE) += funcore.o + +funcore-y := fun_dev.o fun_queue.o diff --git a/drivers/net/ethernet/fungible/funcore/fun_dev.c b/drivers/net/ethernet/fungible/funcore/fun_dev.c new file mode 100644 index 000000000000..5d7aef73df61 --- /dev/null +++ b/drivers/net/ethernet/fungible/funcore/fun_dev.c @@ -0,0 +1,843 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fun_queue.h" +#include "fun_dev.h" + +#define FUN_ADMIN_CMD_TO_MS 3000 + +enum { + AQA_ASQS_SHIFT = 0, + AQA_ACQS_SHIFT = 16, + AQA_MIN_QUEUE_SIZE = 2, + AQA_MAX_QUEUE_SIZE = 4096 +}; + +/* context for admin commands */ +struct fun_cmd_ctx { + fun_admin_callback_t cb; /* callback to invoke on completion */ + void *cb_data; /* user data provided to callback */ + int cpu; /* CPU where the cmd's tag was allocated */ +}; + +/* Context for synchronous admin commands. */ +struct fun_sync_cmd_ctx { + struct completion compl; + u8 *rsp_buf; /* caller provided response buffer */ + unsigned int rsp_len; /* response buffer size */ + u8 rsp_status; /* command response status */ +}; + +/* Wait for the CSTS.RDY bit to match @enabled. */ +static int fun_wait_ready(struct fun_dev *fdev, bool enabled) +{ + unsigned int cap_to = NVME_CAP_TIMEOUT(fdev->cap_reg); + u32 bit = enabled ? NVME_CSTS_RDY : 0; + unsigned long deadline; + + deadline = ((cap_to + 1) * HZ / 2) + jiffies; /* CAP.TO is in 500ms */ + + for (;;) { + u32 csts = readl(fdev->bar + NVME_REG_CSTS); + + if (csts == ~0) { + dev_err(fdev->dev, "CSTS register read %#x\n", csts); + return -EIO; + } + + if ((csts & NVME_CSTS_RDY) == bit) + return 0; + + if (time_is_before_jiffies(deadline)) + break; + + msleep(100); + } + + dev_err(fdev->dev, + "Timed out waiting for device to indicate RDY %u; aborting %s\n", + enabled, enabled ? "initialization" : "reset"); + return -ETIMEDOUT; +} + +/* Check CSTS and return an error if it is unreadable or has unexpected + * RDY value. + */ +static int fun_check_csts_rdy(struct fun_dev *fdev, unsigned int expected_rdy) +{ + u32 csts = readl(fdev->bar + NVME_REG_CSTS); + u32 actual_rdy = csts & NVME_CSTS_RDY; + + if (csts == ~0) { + dev_err(fdev->dev, "CSTS register read %#x\n", csts); + return -EIO; + } + if (actual_rdy != expected_rdy) { + dev_err(fdev->dev, "Unexpected CSTS RDY %u\n", actual_rdy); + return -EINVAL; + } + return 0; +} + +/* Check that CSTS RDY has the expected value. Then write a new value to the CC + * register and wait for CSTS RDY to match the new CC ENABLE state. + */ +static int fun_update_cc_enable(struct fun_dev *fdev, unsigned int initial_rdy) +{ + int rc = fun_check_csts_rdy(fdev, initial_rdy); + + if (rc) + return rc; + writel(fdev->cc_reg, fdev->bar + NVME_REG_CC); + return fun_wait_ready(fdev, !!(fdev->cc_reg & NVME_CC_ENABLE)); +} + +static int fun_disable_ctrl(struct fun_dev *fdev) +{ + fdev->cc_reg &= ~(NVME_CC_SHN_MASK | NVME_CC_ENABLE); + return fun_update_cc_enable(fdev, 1); +} + +static int fun_enable_ctrl(struct fun_dev *fdev, u32 admin_cqesz_log2, + u32 admin_sqesz_log2) +{ + fdev->cc_reg = (admin_cqesz_log2 << NVME_CC_IOCQES_SHIFT) | + (admin_sqesz_log2 << NVME_CC_IOSQES_SHIFT) | + ((PAGE_SHIFT - 12) << NVME_CC_MPS_SHIFT) | + NVME_CC_ENABLE; + + return fun_update_cc_enable(fdev, 0); +} + +static int fun_map_bars(struct fun_dev *fdev, const char *name) +{ + struct pci_dev *pdev = to_pci_dev(fdev->dev); + int err; + + err = pci_request_mem_regions(pdev, name); + if (err) { + dev_err(&pdev->dev, + "Couldn't get PCI memory resources, err %d\n", err); + return err; + } + + fdev->bar = pci_ioremap_bar(pdev, 0); + if (!fdev->bar) { + dev_err(&pdev->dev, "Couldn't map BAR 0\n"); + pci_release_mem_regions(pdev); + return -ENOMEM; + } + + return 0; +} + +static void fun_unmap_bars(struct fun_dev *fdev) +{ + struct pci_dev *pdev = to_pci_dev(fdev->dev); + + if (fdev->bar) { + iounmap(fdev->bar); + fdev->bar = NULL; + pci_release_mem_regions(pdev); + } +} + +static int fun_set_dma_masks(struct device *dev) +{ + int err; + + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (err) + dev_err(dev, "DMA mask configuration failed, err %d\n", err); + return err; +} + +static irqreturn_t fun_admin_irq(int irq, void *data) +{ + struct fun_queue *funq = data; + + return fun_process_cq(funq, 0) ? IRQ_HANDLED : IRQ_NONE; +} + +static void fun_complete_admin_cmd(struct fun_queue *funq, void *data, + void *entry, const struct fun_cqe_info *info) +{ + const struct fun_admin_rsp_common *rsp_common = entry; + struct fun_dev *fdev = funq->fdev; + struct fun_cmd_ctx *cmd_ctx; + int cpu; + u16 cid; + + if (info->sqhd == cpu_to_be16(0xffff)) { + dev_dbg(fdev->dev, "adminq event"); + if (fdev->adminq_cb) + fdev->adminq_cb(fdev, entry); + return; + } + + cid = be16_to_cpu(rsp_common->cid); + dev_dbg(fdev->dev, "admin CQE cid %u, op %u, ret %u\n", cid, + rsp_common->op, rsp_common->ret); + + cmd_ctx = &fdev->cmd_ctx[cid]; + if (cmd_ctx->cpu < 0) { + dev_err(fdev->dev, + "admin CQE with CID=%u, op=%u does not match a pending command\n", + cid, rsp_common->op); + return; + } + + if (cmd_ctx->cb) + cmd_ctx->cb(fdev, entry, xchg(&cmd_ctx->cb_data, NULL)); + + cpu = cmd_ctx->cpu; + cmd_ctx->cpu = -1; + sbitmap_queue_clear(&fdev->admin_sbq, cid, cpu); +} + +static int fun_init_cmd_ctx(struct fun_dev *fdev, unsigned int ntags) +{ + unsigned int i; + + fdev->cmd_ctx = kvcalloc(ntags, sizeof(*fdev->cmd_ctx), GFP_KERNEL); + if (!fdev->cmd_ctx) + return -ENOMEM; + + for (i = 0; i < ntags; i++) + fdev->cmd_ctx[i].cpu = -1; + + return 0; +} + +/* Allocate and enable an admin queue and assign it the first IRQ vector. */ +static int fun_enable_admin_queue(struct fun_dev *fdev, + const struct fun_dev_params *areq) +{ + struct fun_queue_alloc_req qreq = { + .cqe_size_log2 = areq->cqe_size_log2, + .sqe_size_log2 = areq->sqe_size_log2, + .cq_depth = areq->cq_depth, + .sq_depth = areq->sq_depth, + .rq_depth = areq->rq_depth, + }; + unsigned int ntags = areq->sq_depth - 1; + struct fun_queue *funq; + int rc; + + if (fdev->admin_q) + return -EEXIST; + + if (areq->sq_depth < AQA_MIN_QUEUE_SIZE || + areq->sq_depth > AQA_MAX_QUEUE_SIZE || + areq->cq_depth < AQA_MIN_QUEUE_SIZE || + areq->cq_depth > AQA_MAX_QUEUE_SIZE) + return -EINVAL; + + fdev->admin_q = fun_alloc_queue(fdev, 0, &qreq); + if (!fdev->admin_q) + return -ENOMEM; + + rc = fun_init_cmd_ctx(fdev, ntags); + if (rc) + goto free_q; + + rc = sbitmap_queue_init_node(&fdev->admin_sbq, ntags, -1, false, + GFP_KERNEL, dev_to_node(fdev->dev)); + if (rc) + goto free_cmd_ctx; + + funq = fdev->admin_q; + funq->cq_vector = 0; + rc = fun_request_irq(funq, dev_name(fdev->dev), fun_admin_irq, funq); + if (rc) + goto free_sbq; + + fun_set_cq_callback(funq, fun_complete_admin_cmd, NULL); + fdev->adminq_cb = areq->event_cb; + + writel((funq->sq_depth - 1) << AQA_ASQS_SHIFT | + (funq->cq_depth - 1) << AQA_ACQS_SHIFT, + fdev->bar + NVME_REG_AQA); + + writeq(funq->sq_dma_addr, fdev->bar + NVME_REG_ASQ); + writeq(funq->cq_dma_addr, fdev->bar + NVME_REG_ACQ); + + rc = fun_enable_ctrl(fdev, areq->cqe_size_log2, areq->sqe_size_log2); + if (rc) + goto free_irq; + + if (areq->rq_depth) { + rc = fun_create_rq(funq); + if (rc) + goto disable_ctrl; + + funq_rq_post(funq); + } + + return 0; + +disable_ctrl: + fun_disable_ctrl(fdev); +free_irq: + fun_free_irq(funq); +free_sbq: + sbitmap_queue_free(&fdev->admin_sbq); +free_cmd_ctx: + kvfree(fdev->cmd_ctx); + fdev->cmd_ctx = NULL; +free_q: + fun_free_queue(fdev->admin_q); + fdev->admin_q = NULL; + return rc; +} + +static void fun_disable_admin_queue(struct fun_dev *fdev) +{ + struct fun_queue *admq = fdev->admin_q; + + if (!admq) + return; + + fun_disable_ctrl(fdev); + + fun_free_irq(admq); + __fun_process_cq(admq, 0); + + sbitmap_queue_free(&fdev->admin_sbq); + + kvfree(fdev->cmd_ctx); + fdev->cmd_ctx = NULL; + + fun_free_queue(admq); + fdev->admin_q = NULL; +} + +/* Return %true if the admin queue has stopped servicing commands as can be + * detected through registers. This isn't exhaustive and may provide false + * negatives. + */ +static bool fun_adminq_stopped(struct fun_dev *fdev) +{ + u32 csts = readl(fdev->bar + NVME_REG_CSTS); + + return (csts & (NVME_CSTS_CFS | NVME_CSTS_RDY)) != NVME_CSTS_RDY; +} + +static int fun_wait_for_tag(struct fun_dev *fdev, int *cpup) +{ + struct sbitmap_queue *sbq = &fdev->admin_sbq; + struct sbq_wait_state *ws = &sbq->ws[0]; + DEFINE_SBQ_WAIT(wait); + int tag; + + for (;;) { + sbitmap_prepare_to_wait(sbq, ws, &wait, TASK_UNINTERRUPTIBLE); + if (fdev->suppress_cmds) { + tag = -ESHUTDOWN; + break; + } + tag = sbitmap_queue_get(sbq, cpup); + if (tag >= 0) + break; + schedule(); + } + + sbitmap_finish_wait(sbq, ws, &wait); + return tag; +} + +/* Submit an asynchronous admin command. Caller is responsible for implementing + * any waiting or timeout. Upon command completion the callback @cb is called. + */ +int fun_submit_admin_cmd(struct fun_dev *fdev, struct fun_admin_req_common *cmd, + fun_admin_callback_t cb, void *cb_data, bool wait_ok) +{ + struct fun_queue *funq = fdev->admin_q; + unsigned int cmdsize = cmd->len8 * 8; + struct fun_cmd_ctx *cmd_ctx; + int tag, cpu, rc = 0; + + if (WARN_ON(cmdsize > (1 << funq->sqe_size_log2))) + return -EMSGSIZE; + + tag = sbitmap_queue_get(&fdev->admin_sbq, &cpu); + if (tag < 0) { + if (!wait_ok) + return -EAGAIN; + tag = fun_wait_for_tag(fdev, &cpu); + if (tag < 0) + return tag; + } + + cmd->cid = cpu_to_be16(tag); + + cmd_ctx = &fdev->cmd_ctx[tag]; + cmd_ctx->cb = cb; + cmd_ctx->cb_data = cb_data; + + spin_lock(&funq->sq_lock); + + if (unlikely(fdev->suppress_cmds)) { + rc = -ESHUTDOWN; + sbitmap_queue_clear(&fdev->admin_sbq, tag, cpu); + } else { + cmd_ctx->cpu = cpu; + memcpy(fun_sqe_at(funq, funq->sq_tail), cmd, cmdsize); + + dev_dbg(fdev->dev, "admin cmd @ %u: %8ph\n", funq->sq_tail, + cmd); + + if (++funq->sq_tail == funq->sq_depth) + funq->sq_tail = 0; + writel(funq->sq_tail, funq->sq_db); + } + spin_unlock(&funq->sq_lock); + return rc; +} + +/* Abandon a pending admin command by clearing the issuer's callback data. + * Failure indicates that the command either has already completed or its + * completion is racing with this call. + */ +static bool fun_abandon_admin_cmd(struct fun_dev *fd, + const struct fun_admin_req_common *cmd, + void *cb_data) +{ + u16 cid = be16_to_cpu(cmd->cid); + struct fun_cmd_ctx *cmd_ctx = &fd->cmd_ctx[cid]; + + return cmpxchg(&cmd_ctx->cb_data, cb_data, NULL) == cb_data; +} + +/* Stop submission of new admin commands and wake up any processes waiting for + * tags. Already submitted commands are left to complete or time out. + */ +static void fun_admin_stop(struct fun_dev *fdev) +{ + spin_lock(&fdev->admin_q->sq_lock); + fdev->suppress_cmds = true; + spin_unlock(&fdev->admin_q->sq_lock); + sbitmap_queue_wake_all(&fdev->admin_sbq); +} + +/* The callback for synchronous execution of admin commands. It copies the + * command response to the caller's buffer and signals completion. + */ +static void fun_admin_cmd_sync_cb(struct fun_dev *fd, void *rsp, void *cb_data) +{ + const struct fun_admin_rsp_common *rsp_common = rsp; + struct fun_sync_cmd_ctx *ctx = cb_data; + + if (!ctx) + return; /* command issuer timed out and left */ + if (ctx->rsp_buf) { + unsigned int rsp_len = rsp_common->len8 * 8; + + if (unlikely(rsp_len > ctx->rsp_len)) { + dev_err(fd->dev, + "response for op %u is %uB > response buffer %uB\n", + rsp_common->op, rsp_len, ctx->rsp_len); + rsp_len = ctx->rsp_len; + } + memcpy(ctx->rsp_buf, rsp, rsp_len); + } + ctx->rsp_status = rsp_common->ret; + complete(&ctx->compl); +} + +/* Submit a synchronous admin command. */ +int fun_submit_admin_sync_cmd(struct fun_dev *fdev, + struct fun_admin_req_common *cmd, void *rsp, + size_t rspsize, unsigned int timeout) +{ + struct fun_sync_cmd_ctx ctx = { + .compl = COMPLETION_INITIALIZER_ONSTACK(ctx.compl), + .rsp_buf = rsp, + .rsp_len = rspsize, + }; + unsigned int cmdlen = cmd->len8 * 8; + unsigned long jiffies_left; + int ret; + + ret = fun_submit_admin_cmd(fdev, cmd, fun_admin_cmd_sync_cb, &ctx, + true); + if (ret) + return ret; + + if (!timeout) + timeout = FUN_ADMIN_CMD_TO_MS; + + jiffies_left = wait_for_completion_timeout(&ctx.compl, + msecs_to_jiffies(timeout)); + if (!jiffies_left) { + /* The command timed out. Attempt to cancel it so we can return. + * But if the command is in the process of completing we'll + * wait for it. + */ + if (fun_abandon_admin_cmd(fdev, cmd, &ctx)) { + dev_err(fdev->dev, "admin command timed out: %*ph\n", + cmdlen, cmd); + fun_admin_stop(fdev); + /* see if the timeout was due to a queue failure */ + if (fun_adminq_stopped(fdev)) + dev_err(fdev->dev, + "device does not accept admin commands\n"); + + return -ETIMEDOUT; + } + wait_for_completion(&ctx.compl); + } + + if (ctx.rsp_status) { + dev_err(fdev->dev, "admin command failed, err %d: %*ph\n", + ctx.rsp_status, cmdlen, cmd); + } + + return -ctx.rsp_status; +} +EXPORT_SYMBOL_GPL(fun_submit_admin_sync_cmd); + +/* Return the number of device resources of the requested type. */ +int fun_get_res_count(struct fun_dev *fdev, enum fun_admin_op res) +{ + union { + struct fun_admin_res_count_req req; + struct fun_admin_res_count_rsp rsp; + } cmd; + int rc; + + cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(res, sizeof(cmd.req)); + cmd.req.count = FUN_ADMIN_SIMPLE_SUBOP_INIT(FUN_ADMIN_SUBOP_RES_COUNT, + 0, 0); + + rc = fun_submit_admin_sync_cmd(fdev, &cmd.req.common, &cmd.rsp, + sizeof(cmd), 0); + return rc ? rc : be32_to_cpu(cmd.rsp.count.data); +} +EXPORT_SYMBOL_GPL(fun_get_res_count); + +/* Request that the instance of resource @res with the given id be deleted. */ +int fun_res_destroy(struct fun_dev *fdev, enum fun_admin_op res, + unsigned int flags, u32 id) +{ + struct fun_admin_generic_destroy_req req = { + .common = FUN_ADMIN_REQ_COMMON_INIT2(res, sizeof(req)), + .destroy = FUN_ADMIN_SIMPLE_SUBOP_INIT(FUN_ADMIN_SUBOP_DESTROY, + flags, id) + }; + + return fun_submit_admin_sync_cmd(fdev, &req.common, NULL, 0, 0); +} +EXPORT_SYMBOL_GPL(fun_res_destroy); + +/* Bind two entities of the given types and IDs. */ +int fun_bind(struct fun_dev *fdev, enum fun_admin_bind_type type0, + unsigned int id0, enum fun_admin_bind_type type1, + unsigned int id1) +{ + struct { + struct fun_admin_bind_req req; + struct fun_admin_bind_entry entry[2]; + } cmd = { + .req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, + sizeof(cmd)), + .entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0), + .entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1), + }; + + return fun_submit_admin_sync_cmd(fdev, &cmd.req.common, NULL, 0, 0); +} +EXPORT_SYMBOL_GPL(fun_bind); + +static int fun_get_dev_limits(struct fun_dev *fdev) +{ + struct pci_dev *pdev = to_pci_dev(fdev->dev); + unsigned int cq_count, sq_count, num_dbs; + int rc; + + rc = fun_get_res_count(fdev, FUN_ADMIN_OP_EPCQ); + if (rc < 0) + return rc; + cq_count = rc; + + rc = fun_get_res_count(fdev, FUN_ADMIN_OP_EPSQ); + if (rc < 0) + return rc; + sq_count = rc; + + /* The admin queue consumes 1 CQ and at least 1 SQ. To be usable the + * device must provide additional queues. + */ + if (cq_count < 2 || sq_count < 2 + !!fdev->admin_q->rq_depth) + return -EINVAL; + + /* Calculate the max QID based on SQ/CQ/doorbell counts. + * SQ/CQ doorbells alternate. + */ + num_dbs = (pci_resource_len(pdev, 0) - NVME_REG_DBS) / + (fdev->db_stride * 4); + fdev->max_qid = min3(cq_count, sq_count, num_dbs / 2) - 1; + fdev->kern_end_qid = fdev->max_qid + 1; + return 0; +} + +/* Allocate all MSI-X vectors available on a function and at least @min_vecs. */ +static int fun_alloc_irqs(struct pci_dev *pdev, unsigned int min_vecs) +{ + int vecs, num_msix = pci_msix_vec_count(pdev); + + if (num_msix < 0) + return num_msix; + if (min_vecs > num_msix) + return -ERANGE; + + vecs = pci_alloc_irq_vectors(pdev, min_vecs, num_msix, PCI_IRQ_MSIX); + if (vecs > 0) { + dev_info(&pdev->dev, + "Allocated %d IRQ vectors of %d requested\n", + vecs, num_msix); + } else { + dev_err(&pdev->dev, + "Unable to allocate at least %u IRQ vectors\n", + min_vecs); + } + return vecs; +} + +/* Allocate and initialize the IRQ manager state. */ +static int fun_alloc_irq_mgr(struct fun_dev *fdev) +{ + fdev->irq_map = bitmap_zalloc(fdev->num_irqs, GFP_KERNEL); + if (!fdev->irq_map) + return -ENOMEM; + + spin_lock_init(&fdev->irqmgr_lock); + /* mark IRQ 0 allocated, it is used by the admin queue */ + __set_bit(0, fdev->irq_map); + fdev->irqs_avail = fdev->num_irqs - 1; + return 0; +} + +/* Reserve @nirqs of the currently available IRQs and return their indices. */ +int fun_reserve_irqs(struct fun_dev *fdev, unsigned int nirqs, u16 *irq_indices) +{ + unsigned int b, n = 0; + int err = -ENOSPC; + + if (!nirqs) + return 0; + + spin_lock(&fdev->irqmgr_lock); + if (nirqs > fdev->irqs_avail) + goto unlock; + + for_each_clear_bit(b, fdev->irq_map, fdev->num_irqs) { + __set_bit(b, fdev->irq_map); + irq_indices[n++] = b; + if (n >= nirqs) + break; + } + + WARN_ON(n < nirqs); + fdev->irqs_avail -= n; + err = n; +unlock: + spin_unlock(&fdev->irqmgr_lock); + return err; +} +EXPORT_SYMBOL(fun_reserve_irqs); + +/* Release @nirqs previously allocated IRQS with the supplied indices. */ +void fun_release_irqs(struct fun_dev *fdev, unsigned int nirqs, + u16 *irq_indices) +{ + unsigned int i; + + spin_lock(&fdev->irqmgr_lock); + for (i = 0; i < nirqs; i++) + __clear_bit(irq_indices[i], fdev->irq_map); + fdev->irqs_avail += nirqs; + spin_unlock(&fdev->irqmgr_lock); +} +EXPORT_SYMBOL(fun_release_irqs); + +static void fun_serv_handler(struct work_struct *work) +{ + struct fun_dev *fd = container_of(work, struct fun_dev, service_task); + + if (test_bit(FUN_SERV_DISABLED, &fd->service_flags)) + return; + if (fd->serv_cb) + fd->serv_cb(fd); +} + +void fun_serv_stop(struct fun_dev *fd) +{ + set_bit(FUN_SERV_DISABLED, &fd->service_flags); + cancel_work_sync(&fd->service_task); +} +EXPORT_SYMBOL_GPL(fun_serv_stop); + +void fun_serv_restart(struct fun_dev *fd) +{ + clear_bit(FUN_SERV_DISABLED, &fd->service_flags); + if (fd->service_flags) + schedule_work(&fd->service_task); +} +EXPORT_SYMBOL_GPL(fun_serv_restart); + +void fun_serv_sched(struct fun_dev *fd) +{ + if (!test_bit(FUN_SERV_DISABLED, &fd->service_flags)) + schedule_work(&fd->service_task); +} +EXPORT_SYMBOL_GPL(fun_serv_sched); + +/* Check and try to get the device into a proper state for initialization, + * i.e., CSTS.RDY = CC.EN = 0. + */ +static int sanitize_dev(struct fun_dev *fdev) +{ + int rc; + + fdev->cap_reg = readq(fdev->bar + NVME_REG_CAP); + fdev->cc_reg = readl(fdev->bar + NVME_REG_CC); + + /* First get RDY to agree with the current EN. Give RDY the opportunity + * to complete a potential recent EN change. + */ + rc = fun_wait_ready(fdev, fdev->cc_reg & NVME_CC_ENABLE); + if (rc) + return rc; + + /* Next, reset the device if EN is currently 1. */ + if (fdev->cc_reg & NVME_CC_ENABLE) + rc = fun_disable_ctrl(fdev); + + return rc; +} + +/* Undo the device initialization of fun_dev_enable(). */ +void fun_dev_disable(struct fun_dev *fdev) +{ + struct pci_dev *pdev = to_pci_dev(fdev->dev); + + pci_set_drvdata(pdev, NULL); + + if (fdev->fw_handle != FUN_HCI_ID_INVALID) { + fun_res_destroy(fdev, FUN_ADMIN_OP_SWUPGRADE, 0, + fdev->fw_handle); + fdev->fw_handle = FUN_HCI_ID_INVALID; + } + + fun_disable_admin_queue(fdev); + + bitmap_free(fdev->irq_map); + pci_free_irq_vectors(pdev); + + pci_clear_master(pdev); + pci_disable_pcie_error_reporting(pdev); + pci_disable_device(pdev); + + fun_unmap_bars(fdev); +} +EXPORT_SYMBOL(fun_dev_disable); + +/* Perform basic initialization of a device, including + * - PCI config space setup and BAR0 mapping + * - interrupt management initialization + * - 1 admin queue setup + * - determination of some device limits, such as number of queues. + */ +int fun_dev_enable(struct fun_dev *fdev, struct pci_dev *pdev, + const struct fun_dev_params *areq, const char *name) +{ + int rc; + + fdev->dev = &pdev->dev; + rc = fun_map_bars(fdev, name); + if (rc) + return rc; + + rc = fun_set_dma_masks(fdev->dev); + if (rc) + goto unmap; + + rc = pci_enable_device_mem(pdev); + if (rc) { + dev_err(&pdev->dev, "Couldn't enable device, err %d\n", rc); + goto unmap; + } + + pci_enable_pcie_error_reporting(pdev); + + rc = sanitize_dev(fdev); + if (rc) + goto disable_dev; + + fdev->fw_handle = FUN_HCI_ID_INVALID; + fdev->q_depth = NVME_CAP_MQES(fdev->cap_reg) + 1; + fdev->db_stride = 1 << NVME_CAP_STRIDE(fdev->cap_reg); + fdev->dbs = fdev->bar + NVME_REG_DBS; + + INIT_WORK(&fdev->service_task, fun_serv_handler); + fdev->service_flags = FUN_SERV_DISABLED; + fdev->serv_cb = areq->serv_cb; + + rc = fun_alloc_irqs(pdev, areq->min_msix + 1); /* +1 for admin CQ */ + if (rc < 0) + goto disable_dev; + fdev->num_irqs = rc; + + rc = fun_alloc_irq_mgr(fdev); + if (rc) + goto free_irqs; + + pci_set_master(pdev); + rc = fun_enable_admin_queue(fdev, areq); + if (rc) + goto free_irq_mgr; + + rc = fun_get_dev_limits(fdev); + if (rc < 0) + goto disable_admin; + + pci_save_state(pdev); + pci_set_drvdata(pdev, fdev); + pcie_print_link_status(pdev); + dev_dbg(fdev->dev, "q_depth %u, db_stride %u, max qid %d kern_end_qid %d\n", + fdev->q_depth, fdev->db_stride, fdev->max_qid, + fdev->kern_end_qid); + return 0; + +disable_admin: + fun_disable_admin_queue(fdev); +free_irq_mgr: + pci_clear_master(pdev); + bitmap_free(fdev->irq_map); +free_irqs: + pci_free_irq_vectors(pdev); +disable_dev: + pci_disable_pcie_error_reporting(pdev); + pci_disable_device(pdev); +unmap: + fun_unmap_bars(fdev); + return rc; +} +EXPORT_SYMBOL(fun_dev_enable); + +MODULE_AUTHOR("Dimitris Michailidis "); +MODULE_DESCRIPTION("Core services driver for Fungible devices"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/ethernet/fungible/funcore/fun_dev.h b/drivers/net/ethernet/fungible/funcore/fun_dev.h new file mode 100644 index 000000000000..9e8c17ce8887 --- /dev/null +++ b/drivers/net/ethernet/fungible/funcore/fun_dev.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +#ifndef _FUNDEV_H +#define _FUNDEV_H + +#include +#include +#include +#include "fun_hci.h" + +struct pci_dev; +struct fun_dev; +struct fun_queue; +struct fun_cmd_ctx; +struct fun_queue_alloc_req; + +/* doorbell fields */ +enum { + FUN_DB_QIDX_S = 0, + FUN_DB_INTCOAL_ENTRIES_S = 16, + FUN_DB_INTCOAL_ENTRIES_M = 0x7f, + FUN_DB_INTCOAL_USEC_S = 23, + FUN_DB_INTCOAL_USEC_M = 0x7f, + FUN_DB_IRQ_S = 30, + FUN_DB_IRQ_F = 1 << FUN_DB_IRQ_S, + FUN_DB_IRQ_ARM_S = 31, + FUN_DB_IRQ_ARM_F = 1U << FUN_DB_IRQ_ARM_S +}; + +/* Callback for asynchronous admin commands. + * Invoked on reception of command response. + */ +typedef void (*fun_admin_callback_t)(struct fun_dev *fdev, void *rsp, + void *cb_data); + +/* Callback for events/notifications received by an admin queue. */ +typedef void (*fun_admin_event_cb)(struct fun_dev *fdev, void *cqe); + +/* Callback for pending work handled by the service task. */ +typedef void (*fun_serv_cb)(struct fun_dev *fd); + +/* service task flags */ +enum { + FUN_SERV_DISABLED, /* service task is disabled */ + FUN_SERV_FIRST_AVAIL +}; + +/* Driver state associated with a PCI function. */ +struct fun_dev { + struct device *dev; + + void __iomem *bar; /* start of BAR0 mapping */ + u32 __iomem *dbs; /* start of doorbells in BAR0 mapping */ + + /* admin queue */ + struct fun_queue *admin_q; + struct sbitmap_queue admin_sbq; + struct fun_cmd_ctx *cmd_ctx; + fun_admin_event_cb adminq_cb; + bool suppress_cmds; /* if set don't write commands to SQ */ + + /* address increment between consecutive doorbells, in 4B units */ + unsigned int db_stride; + + /* SW versions of device registers */ + u32 cc_reg; /* CC register */ + u64 cap_reg; /* CAPability register */ + + unsigned int q_depth; /* max queue depth supported by device */ + unsigned int max_qid; /* = #queues - 1, separately for SQs and CQs */ + unsigned int kern_end_qid; /* last qid in the kernel range + 1 */ + + unsigned int fw_handle; + + /* IRQ manager */ + unsigned int num_irqs; + unsigned int irqs_avail; + spinlock_t irqmgr_lock; + unsigned long *irq_map; + + /* The service task handles work that needs a process context */ + struct work_struct service_task; + unsigned long service_flags; + fun_serv_cb serv_cb; +}; + +struct fun_dev_params { + u8 cqe_size_log2; /* admin q CQE size */ + u8 sqe_size_log2; /* admin q SQE size */ + + /* admin q depths */ + u16 cq_depth; + u16 sq_depth; + u16 rq_depth; + + u16 min_msix; /* min vectors needed by requesting driver */ + + fun_admin_event_cb event_cb; + fun_serv_cb serv_cb; +}; + +/* Return the BAR address of a doorbell. */ +static inline u32 __iomem *fun_db_addr(const struct fun_dev *fdev, + unsigned int db_index) +{ + return &fdev->dbs[db_index * fdev->db_stride]; +} + +/* Return the BAR address of an SQ doorbell. SQ and CQ DBs alternate, + * SQs have even DB indices. + */ +static inline u32 __iomem *fun_sq_db_addr(const struct fun_dev *fdev, + unsigned int sqid) +{ + return fun_db_addr(fdev, sqid * 2); +} + +static inline u32 __iomem *fun_cq_db_addr(const struct fun_dev *fdev, + unsigned int cqid) +{ + return fun_db_addr(fdev, cqid * 2 + 1); +} + +int fun_get_res_count(struct fun_dev *fdev, enum fun_admin_op res); +int fun_res_destroy(struct fun_dev *fdev, enum fun_admin_op res, + unsigned int flags, u32 id); +int fun_bind(struct fun_dev *fdev, enum fun_admin_bind_type type0, + unsigned int id0, enum fun_admin_bind_type type1, + unsigned int id1); + +int fun_submit_admin_cmd(struct fun_dev *fdev, struct fun_admin_req_common *cmd, + fun_admin_callback_t cb, void *cb_data, bool wait_ok); +int fun_submit_admin_sync_cmd(struct fun_dev *fdev, + struct fun_admin_req_common *cmd, void *rsp, + size_t rspsize, unsigned int timeout); + +int fun_dev_enable(struct fun_dev *fdev, struct pci_dev *pdev, + const struct fun_dev_params *areq, const char *name); +void fun_dev_disable(struct fun_dev *fdev); + +int fun_reserve_irqs(struct fun_dev *fdev, unsigned int nirqs, + u16 *irq_indices); +void fun_release_irqs(struct fun_dev *fdev, unsigned int nirqs, + u16 *irq_indices); + +void fun_serv_stop(struct fun_dev *fd); +void fun_serv_restart(struct fun_dev *fd); +void fun_serv_sched(struct fun_dev *fd); + +#endif /* _FUNDEV_H */ diff --git a/drivers/net/ethernet/fungible/funcore/fun_hci.h b/drivers/net/ethernet/fungible/funcore/fun_hci.h new file mode 100644 index 000000000000..257203e94b68 --- /dev/null +++ b/drivers/net/ethernet/fungible/funcore/fun_hci.h @@ -0,0 +1,1202 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +#ifndef __FUN_HCI_H +#define __FUN_HCI_H + +enum { + FUN_HCI_ID_INVALID = 0xffffffff, +}; + +enum fun_admin_op { + FUN_ADMIN_OP_BIND = 0x1, + FUN_ADMIN_OP_EPCQ = 0x11, + FUN_ADMIN_OP_EPSQ = 0x12, + FUN_ADMIN_OP_PORT = 0x13, + FUN_ADMIN_OP_ETH = 0x14, + FUN_ADMIN_OP_VI = 0x15, + FUN_ADMIN_OP_SWUPGRADE = 0x1f, + FUN_ADMIN_OP_RSS = 0x21, + FUN_ADMIN_OP_ADI = 0x25, + FUN_ADMIN_OP_KTLS = 0x26, +}; + +enum { + FUN_REQ_COMMON_FLAG_RSP = 0x1, + FUN_REQ_COMMON_FLAG_HEAD_WB = 0x2, + FUN_REQ_COMMON_FLAG_INT = 0x4, + FUN_REQ_COMMON_FLAG_CQE_IN_RQBUF = 0x8, +}; + +struct fun_admin_req_common { + __u8 op; + __u8 len8; + __be16 flags; + __u8 suboff8; + __u8 rsvd0; + __be16 cid; +}; + +#define FUN_ADMIN_REQ_COMMON_INIT(_op, _len8, _flags, _suboff8, _cid) \ + (struct fun_admin_req_common) { \ + .op = (_op), .len8 = (_len8), .flags = cpu_to_be16(_flags), \ + .suboff8 = (_suboff8), .cid = cpu_to_be16(_cid), \ + } + +#define FUN_ADMIN_REQ_COMMON_INIT2(_op, _len) \ + (struct fun_admin_req_common) { \ + .op = (_op), .len8 = (_len) / 8, \ + } + +struct fun_admin_rsp_common { + __u8 op; + __u8 len8; + __be16 flags; + __u8 suboff8; + __u8 ret; + __be16 cid; +}; + +struct fun_admin_write48_req { + __be64 key_to_data; +}; + +#define FUN_ADMIN_WRITE48_REQ_KEY_S 56U +#define FUN_ADMIN_WRITE48_REQ_KEY_M 0xff +#define FUN_ADMIN_WRITE48_REQ_KEY_P_NOSWAP(x) \ + (((__u64)x) << FUN_ADMIN_WRITE48_REQ_KEY_S) + +#define FUN_ADMIN_WRITE48_REQ_DATA_S 0U +#define FUN_ADMIN_WRITE48_REQ_DATA_M 0xffffffffffff +#define FUN_ADMIN_WRITE48_REQ_DATA_P_NOSWAP(x) \ + (((__u64)x) << FUN_ADMIN_WRITE48_REQ_DATA_S) + +#define FUN_ADMIN_WRITE48_REQ_INIT(key, data) \ + (struct fun_admin_write48_req) { \ + .key_to_data = cpu_to_be64( \ + FUN_ADMIN_WRITE48_REQ_KEY_P_NOSWAP(key) | \ + FUN_ADMIN_WRITE48_REQ_DATA_P_NOSWAP(data)), \ + } + +struct fun_admin_write48_rsp { + __be64 key_to_data; +}; + +struct fun_admin_read48_req { + __be64 key_pack; +}; + +#define FUN_ADMIN_READ48_REQ_KEY_S 56U +#define FUN_ADMIN_READ48_REQ_KEY_M 0xff +#define FUN_ADMIN_READ48_REQ_KEY_P_NOSWAP(x) \ + (((__u64)x) << FUN_ADMIN_READ48_REQ_KEY_S) + +#define FUN_ADMIN_READ48_REQ_INIT(key) \ + (struct fun_admin_read48_req) { \ + .key_pack = \ + cpu_to_be64(FUN_ADMIN_READ48_REQ_KEY_P_NOSWAP(key)), \ + } + +struct fun_admin_read48_rsp { + __be64 key_to_data; +}; + +#define FUN_ADMIN_READ48_RSP_KEY_S 56U +#define FUN_ADMIN_READ48_RSP_KEY_M 0xff +#define FUN_ADMIN_READ48_RSP_KEY_G(x) \ + ((be64_to_cpu(x) >> FUN_ADMIN_READ48_RSP_KEY_S) & \ + FUN_ADMIN_READ48_RSP_KEY_M) + +#define FUN_ADMIN_READ48_RSP_RET_S 48U +#define FUN_ADMIN_READ48_RSP_RET_M 0xff +#define FUN_ADMIN_READ48_RSP_RET_G(x) \ + ((be64_to_cpu(x) >> FUN_ADMIN_READ48_RSP_RET_S) & \ + FUN_ADMIN_READ48_RSP_RET_M) + +#define FUN_ADMIN_READ48_RSP_DATA_S 0U +#define FUN_ADMIN_READ48_RSP_DATA_M 0xffffffffffff +#define FUN_ADMIN_READ48_RSP_DATA_G(x) \ + ((be64_to_cpu(x) >> FUN_ADMIN_READ48_RSP_DATA_S) & \ + FUN_ADMIN_READ48_RSP_DATA_M) + +enum fun_admin_bind_type { + FUN_ADMIN_BIND_TYPE_EPCQ = 0x1, + FUN_ADMIN_BIND_TYPE_EPSQ = 0x2, + FUN_ADMIN_BIND_TYPE_PORT = 0x3, + FUN_ADMIN_BIND_TYPE_RSS = 0x4, + FUN_ADMIN_BIND_TYPE_VI = 0x5, + FUN_ADMIN_BIND_TYPE_ETH = 0x6, +}; + +struct fun_admin_bind_entry { + __u8 type; + __u8 rsvd0[3]; + __be32 id; +}; + +#define FUN_ADMIN_BIND_ENTRY_INIT(_type, _id) \ + (struct fun_admin_bind_entry) { \ + .type = (_type), .id = cpu_to_be32(_id), \ + } + +struct fun_admin_bind_req { + struct fun_admin_req_common common; + struct fun_admin_bind_entry entry[]; +}; + +struct fun_admin_bind_rsp { + struct fun_admin_rsp_common bind_rsp_common; +}; + +struct fun_admin_simple_subop { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 data; +}; + +#define FUN_ADMIN_SIMPLE_SUBOP_INIT(_subop, _flags, _data) \ + (struct fun_admin_simple_subop) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .data = cpu_to_be32(_data), \ + } + +enum fun_admin_subop { + FUN_ADMIN_SUBOP_CREATE = 0x10, + FUN_ADMIN_SUBOP_DESTROY = 0x11, + FUN_ADMIN_SUBOP_MODIFY = 0x12, + FUN_ADMIN_SUBOP_RES_COUNT = 0x14, + FUN_ADMIN_SUBOP_READ = 0x15, + FUN_ADMIN_SUBOP_WRITE = 0x16, + FUN_ADMIN_SUBOP_NOTIFY = 0x17, +}; + +enum { + FUN_ADMIN_RES_CREATE_FLAG_ALLOCATOR = 0x1, +}; + +struct fun_admin_generic_destroy_req { + struct fun_admin_req_common common; + struct fun_admin_simple_subop destroy; +}; + +struct fun_admin_generic_create_rsp { + struct fun_admin_rsp_common common; + + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; +}; + +struct fun_admin_res_count_req { + struct fun_admin_req_common common; + struct fun_admin_simple_subop count; +}; + +struct fun_admin_res_count_rsp { + struct fun_admin_rsp_common common; + struct fun_admin_simple_subop count; +}; + +enum { + FUN_ADMIN_EPCQ_CREATE_FLAG_INT_EPCQ = 0x2, + FUN_ADMIN_EPCQ_CREATE_FLAG_ENTRY_WR_TPH = 0x4, + FUN_ADMIN_EPCQ_CREATE_FLAG_SL_WR_TPH = 0x8, + FUN_ADMIN_EPCQ_CREATE_FLAG_RQ = 0x80, + FUN_ADMIN_EPCQ_CREATE_FLAG_INT_IQ = 0x100, + FUN_ADMIN_EPCQ_CREATE_FLAG_INT_NOARM = 0x200, + FUN_ADMIN_EPCQ_CREATE_FLAG_DROP_ON_OVERFLOW = 0x400, +}; + +struct fun_admin_epcq_req { + struct fun_admin_req_common common; + union epcq_req_subop { + struct fun_admin_epcq_create_req { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + + __be32 epsqid; + __u8 rsvd1; + __u8 entry_size_log2; + __be16 nentries; + + __be64 address; + + __be16 tailroom; /* per packet tailroom in bytes */ + __u8 headroom; /* per packet headroom in 2B units */ + __u8 intcoal_kbytes; + __u8 intcoal_holdoff_nentries; + __u8 intcoal_holdoff_usecs; + __be16 intid; + + __be32 scan_start_id; + __be32 scan_end_id; + + __be16 tph_cpuid; + __u8 rsvd3[6]; + } create; + + struct fun_admin_epcq_modify_req { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + + __be16 headroom; /* headroom in bytes */ + __u8 rsvd1[6]; + } modify; + } u; +}; + +#define FUN_ADMIN_EPCQ_CREATE_REQ_INIT( \ + _subop, _flags, _id, _epsqid, _entry_size_log2, _nentries, _address, \ + _tailroom, _headroom, _intcoal_kbytes, _intcoal_holdoff_nentries, \ + _intcoal_holdoff_usecs, _intid, _scan_start_id, _scan_end_id, \ + _tph_cpuid) \ + (struct fun_admin_epcq_create_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), .epsqid = cpu_to_be32(_epsqid), \ + .entry_size_log2 = _entry_size_log2, \ + .nentries = cpu_to_be16(_nentries), \ + .address = cpu_to_be64(_address), \ + .tailroom = cpu_to_be16(_tailroom), .headroom = _headroom, \ + .intcoal_kbytes = _intcoal_kbytes, \ + .intcoal_holdoff_nentries = _intcoal_holdoff_nentries, \ + .intcoal_holdoff_usecs = _intcoal_holdoff_usecs, \ + .intid = cpu_to_be16(_intid), \ + .scan_start_id = cpu_to_be32(_scan_start_id), \ + .scan_end_id = cpu_to_be32(_scan_end_id), \ + .tph_cpuid = cpu_to_be16(_tph_cpuid), \ + } + +#define FUN_ADMIN_EPCQ_MODIFY_REQ_INIT(_subop, _flags, _id, _headroom) \ + (struct fun_admin_epcq_modify_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), .headroom = cpu_to_be16(_headroom), \ + } + +enum { + FUN_ADMIN_EPSQ_CREATE_FLAG_INT_EPSQ = 0x2, + FUN_ADMIN_EPSQ_CREATE_FLAG_ENTRY_RD_TPH = 0x4, + FUN_ADMIN_EPSQ_CREATE_FLAG_GL_RD_TPH = 0x8, + FUN_ADMIN_EPSQ_CREATE_FLAG_HEAD_WB_ADDRESS = 0x10, + FUN_ADMIN_EPSQ_CREATE_FLAG_HEAD_WB_ADDRESS_TPH = 0x20, + FUN_ADMIN_EPSQ_CREATE_FLAG_HEAD_WB_EPCQ = 0x40, + FUN_ADMIN_EPSQ_CREATE_FLAG_RQ = 0x80, + FUN_ADMIN_EPSQ_CREATE_FLAG_INT_IQ = 0x100, + FUN_ADMIN_EPSQ_CREATE_FLAG_NO_CMPL = 0x200, +}; + +struct fun_admin_epsq_req { + struct fun_admin_req_common common; + + union epsq_req_subop { + struct fun_admin_epsq_create_req { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + + __be32 epcqid; + __u8 rsvd1; + __u8 entry_size_log2; + __be16 nentries; + + __be64 address; /* DMA address of epsq */ + + __u8 rsvd2[3]; + __u8 intcoal_kbytes; + __u8 intcoal_holdoff_nentries; + __u8 intcoal_holdoff_usecs; + __be16 intid; + + __be32 scan_start_id; + __be32 scan_end_id; + + __u8 rsvd3[4]; + __be16 tph_cpuid; + __u8 buf_size_log2; /* log2 of RQ buffer size */ + __u8 head_wb_size_log2; /* log2 of head write back size */ + + __be64 head_wb_address; /* DMA address for head writeback */ + } create; + } u; +}; + +#define FUN_ADMIN_EPSQ_CREATE_REQ_INIT( \ + _subop, _flags, _id, _epcqid, _entry_size_log2, _nentries, _address, \ + _intcoal_kbytes, _intcoal_holdoff_nentries, _intcoal_holdoff_usecs, \ + _intid, _scan_start_id, _scan_end_id, _tph_cpuid, _buf_size_log2, \ + _head_wb_size_log2, _head_wb_address) \ + (struct fun_admin_epsq_create_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), .epcqid = cpu_to_be32(_epcqid), \ + .entry_size_log2 = _entry_size_log2, \ + .nentries = cpu_to_be16(_nentries), \ + .address = cpu_to_be64(_address), \ + .intcoal_kbytes = _intcoal_kbytes, \ + .intcoal_holdoff_nentries = _intcoal_holdoff_nentries, \ + .intcoal_holdoff_usecs = _intcoal_holdoff_usecs, \ + .intid = cpu_to_be16(_intid), \ + .scan_start_id = cpu_to_be32(_scan_start_id), \ + .scan_end_id = cpu_to_be32(_scan_end_id), \ + .tph_cpuid = cpu_to_be16(_tph_cpuid), \ + .buf_size_log2 = _buf_size_log2, \ + .head_wb_size_log2 = _head_wb_size_log2, \ + .head_wb_address = cpu_to_be64(_head_wb_address), \ + } + +enum { + FUN_PORT_CAP_OFFLOADS = 0x1, + FUN_PORT_CAP_STATS = 0x2, + FUN_PORT_CAP_LOOPBACK = 0x4, + FUN_PORT_CAP_VPORT = 0x8, + FUN_PORT_CAP_TX_PAUSE = 0x10, + FUN_PORT_CAP_RX_PAUSE = 0x20, + FUN_PORT_CAP_AUTONEG = 0x40, + FUN_PORT_CAP_RSS = 0x80, + FUN_PORT_CAP_VLAN_OFFLOADS = 0x100, + FUN_PORT_CAP_ENCAP_OFFLOADS = 0x200, + FUN_PORT_CAP_1000_X = 0x1000, + FUN_PORT_CAP_10G_R = 0x2000, + FUN_PORT_CAP_40G_R4 = 0x4000, + FUN_PORT_CAP_25G_R = 0x8000, + FUN_PORT_CAP_50G_R2 = 0x10000, + FUN_PORT_CAP_50G_R = 0x20000, + FUN_PORT_CAP_100G_R4 = 0x40000, + FUN_PORT_CAP_100G_R2 = 0x80000, + FUN_PORT_CAP_200G_R4 = 0x100000, + FUN_PORT_CAP_FEC_NONE = 0x10000000, + FUN_PORT_CAP_FEC_FC = 0x20000000, + FUN_PORT_CAP_FEC_RS = 0x40000000, +}; + +enum fun_port_brkout_mode { + FUN_PORT_BRKMODE_NA = 0x0, + FUN_PORT_BRKMODE_NONE = 0x1, + FUN_PORT_BRKMODE_2X = 0x2, + FUN_PORT_BRKMODE_4X = 0x3, +}; + +enum { + FUN_PORT_SPEED_AUTO = 0x0, + FUN_PORT_SPEED_10M = 0x1, + FUN_PORT_SPEED_100M = 0x2, + FUN_PORT_SPEED_1G = 0x4, + FUN_PORT_SPEED_10G = 0x8, + FUN_PORT_SPEED_25G = 0x10, + FUN_PORT_SPEED_40G = 0x20, + FUN_PORT_SPEED_50G = 0x40, + FUN_PORT_SPEED_100G = 0x80, + FUN_PORT_SPEED_200G = 0x100, +}; + +enum fun_port_duplex_mode { + FUN_PORT_FULL_DUPLEX = 0x0, + FUN_PORT_HALF_DUPLEX = 0x1, +}; + +enum { + FUN_PORT_FEC_NA = 0x0, + FUN_PORT_FEC_OFF = 0x1, + FUN_PORT_FEC_RS = 0x2, + FUN_PORT_FEC_FC = 0x4, + FUN_PORT_FEC_AUTO = 0x8, +}; + +enum fun_port_link_status { + FUN_PORT_LINK_UP = 0x0, + FUN_PORT_LINK_UP_WITH_ERR = 0x1, + FUN_PORT_LINK_DOWN = 0x2, +}; + +enum fun_port_led_type { + FUN_PORT_LED_OFF = 0x0, + FUN_PORT_LED_AMBER = 0x1, + FUN_PORT_LED_GREEN = 0x2, + FUN_PORT_LED_BEACON_ON = 0x3, + FUN_PORT_LED_BEACON_OFF = 0x4, +}; + +enum { + FUN_PORT_FLAG_MAC_DOWN = 0x1, + FUN_PORT_FLAG_MAC_UP = 0x2, + FUN_PORT_FLAG_NH_DOWN = 0x4, + FUN_PORT_FLAG_NH_UP = 0x8, +}; + +enum { + FUN_PORT_FLAG_ENABLE_NOTIFY = 0x1, +}; + +enum fun_port_lane_attr { + FUN_PORT_LANE_1 = 0x1, + FUN_PORT_LANE_2 = 0x2, + FUN_PORT_LANE_4 = 0x4, + FUN_PORT_LANE_SPEED_10G = 0x100, + FUN_PORT_LANE_SPEED_25G = 0x200, + FUN_PORT_LANE_SPEED_50G = 0x400, + FUN_PORT_LANE_SPLIT = 0x8000, +}; + +enum fun_admin_port_subop { + FUN_ADMIN_PORT_SUBOP_INETADDR_EVENT = 0x24, +}; + +enum fun_admin_port_key { + FUN_ADMIN_PORT_KEY_ILLEGAL = 0x0, + FUN_ADMIN_PORT_KEY_MTU = 0x1, + FUN_ADMIN_PORT_KEY_FEC = 0x2, + FUN_ADMIN_PORT_KEY_SPEED = 0x3, + FUN_ADMIN_PORT_KEY_DEBOUNCE = 0x4, + FUN_ADMIN_PORT_KEY_DUPLEX = 0x5, + FUN_ADMIN_PORT_KEY_MACADDR = 0x6, + FUN_ADMIN_PORT_KEY_LINKMODE = 0x7, + FUN_ADMIN_PORT_KEY_BREAKOUT = 0x8, + FUN_ADMIN_PORT_KEY_ENABLE = 0x9, + FUN_ADMIN_PORT_KEY_DISABLE = 0xa, + FUN_ADMIN_PORT_KEY_ERR_DISABLE = 0xb, + FUN_ADMIN_PORT_KEY_CAPABILITIES = 0xc, + FUN_ADMIN_PORT_KEY_LP_CAPABILITIES = 0xd, + FUN_ADMIN_PORT_KEY_STATS_DMA_LOW = 0xe, + FUN_ADMIN_PORT_KEY_STATS_DMA_HIGH = 0xf, + FUN_ADMIN_PORT_KEY_LANE_ATTRS = 0x10, + FUN_ADMIN_PORT_KEY_LED = 0x11, + FUN_ADMIN_PORT_KEY_ADVERT = 0x12, +}; + +struct fun_subop_imm { + __u8 subop; /* see fun_data_subop enum */ + __u8 flags; + __u8 nsgl; + __u8 rsvd0; + __be32 len; + + __u8 data[]; +}; + +enum fun_subop_sgl_flags { + FUN_SUBOP_SGL_USE_OFF8 = 0x1, + FUN_SUBOP_FLAG_FREE_BUF = 0x2, + FUN_SUBOP_FLAG_IS_REFBUF = 0x4, + FUN_SUBOP_SGL_FLAG_LOCAL = 0x8, +}; + +enum fun_data_op { + FUN_DATAOP_INVALID = 0x0, + FUN_DATAOP_SL = 0x1, /* scatter */ + FUN_DATAOP_GL = 0x2, /* gather */ + FUN_DATAOP_SGL = 0x3, /* scatter-gather */ + FUN_DATAOP_IMM = 0x4, /* immediate data */ + FUN_DATAOP_RQBUF = 0x8, /* rq buffer */ +}; + +struct fun_dataop_gl { + __u8 subop; + __u8 flags; + __be16 sgl_off; + __be32 sgl_len; + + __be64 sgl_data; +}; + +static inline void fun_dataop_gl_init(struct fun_dataop_gl *s, u8 flags, + u16 sgl_off, u32 sgl_len, u64 sgl_data) +{ + s->subop = FUN_DATAOP_GL; + s->flags = flags; + s->sgl_off = cpu_to_be16(sgl_off); + s->sgl_len = cpu_to_be32(sgl_len); + s->sgl_data = cpu_to_be64(sgl_data); +} + +struct fun_dataop_imm { + __u8 subop; + __u8 flags; + __be16 rsvd0; + __be32 sgl_len; +}; + +struct fun_subop_sgl { + __u8 subop; + __u8 flags; + __u8 nsgl; + __u8 rsvd0; + __be32 sgl_len; + + __be64 sgl_data; +}; + +#define FUN_SUBOP_SGL_INIT(_subop, _flags, _nsgl, _sgl_len, _sgl_data) \ + (struct fun_subop_sgl) { \ + .subop = (_subop), .flags = (_flags), .nsgl = (_nsgl), \ + .sgl_len = cpu_to_be32(_sgl_len), \ + .sgl_data = cpu_to_be64(_sgl_data), \ + } + +struct fun_dataop_rqbuf { + __u8 subop; + __u8 rsvd0; + __be16 cid; + __be32 bufoff; +}; + +struct fun_dataop_hdr { + __u8 nsgl; + __u8 flags; + __u8 ngather; + __u8 nscatter; + __be32 total_len; + + struct fun_dataop_imm imm[]; +}; + +#define FUN_DATAOP_HDR_INIT(_nsgl, _flags, _ngather, _nscatter, _total_len) \ + (struct fun_dataop_hdr) { \ + .nsgl = _nsgl, .flags = _flags, .ngather = _ngather, \ + .nscatter = _nscatter, .total_len = cpu_to_be32(_total_len), \ + } + +enum fun_port_inetaddr_event_type { + FUN_PORT_INETADDR_ADD = 0x1, + FUN_PORT_INETADDR_DEL = 0x2, +}; + +enum fun_port_inetaddr_addr_family { + FUN_PORT_INETADDR_IPV4 = 0x1, + FUN_PORT_INETADDR_IPV6 = 0x2, +}; + +struct fun_admin_port_req { + struct fun_admin_req_common common; + + union port_req_subop { + struct fun_admin_port_create_req { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + } create; + struct fun_admin_port_write_req { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; /* portid */ + + struct fun_admin_write48_req write48[]; + } write; + struct fun_admin_port_read_req { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; /* portid */ + + struct fun_admin_read48_req read48[]; + } read; + struct fun_admin_port_inetaddr_event_req { + __u8 subop; + __u8 rsvd0; + __u8 event_type; + __u8 addr_family; + __be32 id; + + __u8 addr[]; + } inetaddr_event; + } u; +}; + +#define FUN_ADMIN_PORT_CREATE_REQ_INIT(_subop, _flags, _id) \ + (struct fun_admin_port_create_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), \ + } + +#define FUN_ADMIN_PORT_WRITE_REQ_INIT(_subop, _flags, _id) \ + (struct fun_admin_port_write_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), \ + } + +#define FUN_ADMIN_PORT_READ_REQ_INIT(_subop, _flags, _id) \ + (struct fun_admin_port_read_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), \ + } + +struct fun_admin_port_rsp { + struct fun_admin_rsp_common common; + + union port_rsp_subop { + struct fun_admin_port_create_rsp { + __u8 subop; + __u8 rsvd0[3]; + __be32 id; + + __be16 lport; + __u8 rsvd1[6]; + } create; + struct fun_admin_port_write_rsp { + __u8 subop; + __u8 rsvd0[3]; + __be32 id; /* portid */ + + struct fun_admin_write48_rsp write48[]; + } write; + struct fun_admin_port_read_rsp { + __u8 subop; + __u8 rsvd0[3]; + __be32 id; /* portid */ + + struct fun_admin_read48_rsp read48[]; + } read; + struct fun_admin_port_inetaddr_event_rsp { + __u8 subop; + __u8 rsvd0[3]; + __be32 id; /* portid */ + } inetaddr_event; + } u; +}; + +enum fun_xcvr_type { + FUN_XCVR_BASET = 0x0, + FUN_XCVR_CU = 0x1, + FUN_XCVR_SMF = 0x2, + FUN_XCVR_MMF = 0x3, + FUN_XCVR_AOC = 0x4, + FUN_XCVR_SFPP = 0x10, /* SFP+ or later */ + FUN_XCVR_QSFPP = 0x11, /* QSFP+ or later */ + FUN_XCVR_QSFPDD = 0x12, /* QSFP-DD */ +}; + +struct fun_admin_port_notif { + struct fun_admin_rsp_common common; + + __u8 subop; + __u8 rsvd0; + __be16 id; + __be32 speed; /* in 10 Mbps units */ + + __u8 link_state; + __u8 missed_events; + __u8 link_down_reason; + __u8 xcvr_type; + __u8 flow_ctrl; + __u8 fec; + __u8 active_lanes; + __u8 rsvd1; + + __be64 advertising; + + __be64 lp_advertising; +}; + +enum fun_eth_rss_const { + FUN_ETH_RSS_MAX_KEY_SIZE = 0x28, + FUN_ETH_RSS_MAX_INDIR_ENT = 0x40, +}; + +enum fun_eth_hash_alg { + FUN_ETH_RSS_ALG_INVALID = 0x0, + FUN_ETH_RSS_ALG_TOEPLITZ = 0x1, + FUN_ETH_RSS_ALG_CRC32 = 0x2, +}; + +struct fun_admin_rss_req { + struct fun_admin_req_common common; + + union rss_req_subop { + struct fun_admin_rss_create_req { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + + __be32 rsvd1; + __be32 viid; /* VI flow id */ + + __be64 metadata[1]; + + __u8 alg; + __u8 keylen; + __u8 indir_nent; + __u8 rsvd2; + __be16 key_off; + __be16 indir_off; + + struct fun_dataop_hdr dataop; + } create; + } u; +}; + +#define FUN_ADMIN_RSS_CREATE_REQ_INIT(_subop, _flags, _id, _viid, _alg, \ + _keylen, _indir_nent, _key_off, \ + _indir_off) \ + (struct fun_admin_rss_create_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), .viid = cpu_to_be32(_viid), \ + .alg = _alg, .keylen = _keylen, .indir_nent = _indir_nent, \ + .key_off = cpu_to_be16(_key_off), \ + .indir_off = cpu_to_be16(_indir_off), \ + } + +struct fun_admin_vi_req { + struct fun_admin_req_common common; + + union vi_req_subop { + struct fun_admin_vi_create_req { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + + __be32 rsvd1; + __be32 portid; /* port flow id */ + } create; + } u; +}; + +#define FUN_ADMIN_VI_CREATE_REQ_INIT(_subop, _flags, _id, _portid) \ + (struct fun_admin_vi_create_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), .portid = cpu_to_be32(_portid), \ + } + +struct fun_admin_eth_req { + struct fun_admin_req_common common; + + union eth_req_subop { + struct fun_admin_eth_create_req { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + + __be32 rsvd1; + __be32 portid; /* port flow id */ + } create; + } u; +}; + +#define FUN_ADMIN_ETH_CREATE_REQ_INIT(_subop, _flags, _id, _portid) \ + (struct fun_admin_eth_create_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), .portid = cpu_to_be32(_portid), \ + } + +enum { + FUN_ADMIN_SWU_UPGRADE_FLAG_INIT = 0x10, + FUN_ADMIN_SWU_UPGRADE_FLAG_COMPLETE = 0x20, + FUN_ADMIN_SWU_UPGRADE_FLAG_DOWNGRADE = 0x40, + FUN_ADMIN_SWU_UPGRADE_FLAG_ACTIVE_IMAGE = 0x80, + FUN_ADMIN_SWU_UPGRADE_FLAG_ASYNC = 0x1, +}; + +enum fun_admin_swu_subop { + FUN_ADMIN_SWU_SUBOP_GET_VERSION = 0x20, + FUN_ADMIN_SWU_SUBOP_UPGRADE = 0x21, + FUN_ADMIN_SWU_SUBOP_UPGRADE_DATA = 0x22, + FUN_ADMIN_SWU_SUBOP_GET_ALL_VERSIONS = 0x23, +}; + +struct fun_admin_swu_req { + struct fun_admin_req_common common; + + union swu_req_subop { + struct fun_admin_swu_create_req { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + } create; + struct fun_admin_swu_upgrade_req { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + + __be32 fourcc; + __be32 rsvd1; + + __be64 image_size; /* upgrade image length */ + } upgrade; + struct fun_admin_swu_upgrade_data_req { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + + __be32 offset; /* offset of data in this command */ + __be32 size; /* total size of data in this command */ + } upgrade_data; + } u; + + struct fun_subop_sgl sgl[]; /* in, out buffers through sgl */ +}; + +#define FUN_ADMIN_SWU_CREATE_REQ_INIT(_subop, _flags, _id) \ + (struct fun_admin_swu_create_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), \ + } + +#define FUN_ADMIN_SWU_UPGRADE_REQ_INIT(_subop, _flags, _id, _fourcc, \ + _image_size) \ + (struct fun_admin_swu_upgrade_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), .fourcc = cpu_to_be32(_fourcc), \ + .image_size = cpu_to_be64(_image_size), \ + } + +#define FUN_ADMIN_SWU_UPGRADE_DATA_REQ_INIT(_subop, _flags, _id, _offset, \ + _size) \ + (struct fun_admin_swu_upgrade_data_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), .offset = cpu_to_be32(_offset), \ + .size = cpu_to_be32(_size), \ + } + +struct fun_admin_swu_rsp { + struct fun_admin_rsp_common common; + + union swu_rsp_subop { + struct fun_admin_swu_create_rsp { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + } create; + struct fun_admin_swu_upgrade_rsp { + __u8 subop; + __u8 rsvd0[3]; + __be32 id; + + __be32 fourcc; + __be32 status; + + __be32 progress; + __be32 unused; + } upgrade; + struct fun_admin_swu_upgrade_data_rsp { + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + + __be32 offset; + __be32 size; + } upgrade_data; + } u; +}; + +enum fun_ktls_version { + FUN_KTLS_TLSV2 = 0x20, + FUN_KTLS_TLSV3 = 0x30, +}; + +enum fun_ktls_cipher { + FUN_KTLS_CIPHER_AES_GCM_128 = 0x33, + FUN_KTLS_CIPHER_AES_GCM_256 = 0x34, + FUN_KTLS_CIPHER_AES_CCM_128 = 0x35, + FUN_KTLS_CIPHER_CHACHA20_POLY1305 = 0x36, +}; + +enum fun_ktls_modify_flags { + FUN_KTLS_MODIFY_REMOVE = 0x1, +}; + +struct fun_admin_ktls_create_req { + struct fun_admin_req_common common; + + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; +}; + +#define FUN_ADMIN_KTLS_CREATE_REQ_INIT(_subop, _flags, _id) \ + (struct fun_admin_ktls_create_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), \ + } + +struct fun_admin_ktls_create_rsp { + struct fun_admin_rsp_common common; + + __u8 subop; + __u8 rsvd0[3]; + __be32 id; +}; + +struct fun_admin_ktls_modify_req { + struct fun_admin_req_common common; + + __u8 subop; + __u8 rsvd0; + __be16 flags; + __be32 id; + + __be64 tlsid; + + __be32 tcp_seq; + __u8 version; + __u8 cipher; + __u8 rsvd1[2]; + + __u8 record_seq[8]; + + __u8 key[32]; + + __u8 iv[16]; + + __u8 salt[8]; +}; + +#define FUN_ADMIN_KTLS_MODIFY_REQ_INIT(_subop, _flags, _id, _tlsid, _tcp_seq, \ + _version, _cipher) \ + (struct fun_admin_ktls_modify_req) { \ + .subop = (_subop), .flags = cpu_to_be16(_flags), \ + .id = cpu_to_be32(_id), .tlsid = cpu_to_be64(_tlsid), \ + .tcp_seq = cpu_to_be32(_tcp_seq), .version = _version, \ + .cipher = _cipher, \ + } + +struct fun_admin_ktls_modify_rsp { + struct fun_admin_rsp_common common; + + __u8 subop; + __u8 rsvd0[3]; + __be32 id; + + __be64 tlsid; +}; + +struct fun_req_common { + __u8 op; + __u8 len8; + __be16 flags; + __u8 suboff8; + __u8 rsvd0; + __be16 cid; +}; + +struct fun_rsp_common { + __u8 op; + __u8 len8; + __be16 flags; + __u8 suboff8; + __u8 ret; + __be16 cid; +}; + +struct fun_cqe_info { + __be16 sqhd; + __be16 sqid; + __be16 cid; + __be16 sf_p; +}; + +enum fun_eprq_def { + FUN_EPRQ_PKT_ALIGN = 0x80, +}; + +struct fun_eprq_rqbuf { + __be64 bufaddr; +}; + +#define FUN_EPRQ_RQBUF_INIT(_bufaddr) \ + (struct fun_eprq_rqbuf) { \ + .bufaddr = cpu_to_be64(_bufaddr), \ + } + +enum fun_eth_op { + FUN_ETH_OP_TX = 0x1, + FUN_ETH_OP_RX = 0x2, +}; + +enum { + FUN_ETH_OFFLOAD_EN = 0x8000, + FUN_ETH_OUTER_EN = 0x4000, + FUN_ETH_INNER_LSO = 0x2000, + FUN_ETH_INNER_TSO = 0x1000, + FUN_ETH_OUTER_IPV6 = 0x800, + FUN_ETH_OUTER_UDP = 0x400, + FUN_ETH_INNER_IPV6 = 0x200, + FUN_ETH_INNER_UDP = 0x100, + FUN_ETH_UPDATE_OUTER_L3_LEN = 0x80, + FUN_ETH_UPDATE_OUTER_L3_CKSUM = 0x40, + FUN_ETH_UPDATE_OUTER_L4_LEN = 0x20, + FUN_ETH_UPDATE_OUTER_L4_CKSUM = 0x10, + FUN_ETH_UPDATE_INNER_L3_LEN = 0x8, + FUN_ETH_UPDATE_INNER_L3_CKSUM = 0x4, + FUN_ETH_UPDATE_INNER_L4_LEN = 0x2, + FUN_ETH_UPDATE_INNER_L4_CKSUM = 0x1, +}; + +struct fun_eth_offload { + __be16 flags; /* combination of above flags */ + __be16 mss; /* TSO max seg size */ + __be16 tcp_doff_flags; /* TCP data offset + flags 16b word */ + __be16 vlan; + + __be16 inner_l3_off; /* Inner L3 header offset */ + __be16 inner_l4_off; /* Inner L4 header offset */ + __be16 outer_l3_off; /* Outer L3 header offset */ + __be16 outer_l4_off; /* Outer L4 header offset */ +}; + +static inline void fun_eth_offload_init(struct fun_eth_offload *s, u16 flags, + u16 mss, __be16 tcp_doff_flags, + __be16 vlan, u16 inner_l3_off, + u16 inner_l4_off, u16 outer_l3_off, + u16 outer_l4_off) +{ + s->flags = cpu_to_be16(flags); + s->mss = cpu_to_be16(mss); + s->tcp_doff_flags = tcp_doff_flags; + s->vlan = vlan; + s->inner_l3_off = cpu_to_be16(inner_l3_off); + s->inner_l4_off = cpu_to_be16(inner_l4_off); + s->outer_l3_off = cpu_to_be16(outer_l3_off); + s->outer_l4_off = cpu_to_be16(outer_l4_off); +} + +struct fun_eth_tls { + __be64 tlsid; +}; + +enum { + FUN_ETH_TX_TLS = 0x8000, +}; + +struct fun_eth_tx_req { + __u8 op; + __u8 len8; + __be16 flags; + __u8 suboff8; + __u8 repr_idn; + __be16 encap_proto; + + struct fun_eth_offload offload; + + struct fun_dataop_hdr dataop; +}; + +struct fun_eth_rx_cv { + __be16 il4_prot_to_l2_type; +}; + +#define FUN_ETH_RX_CV_IL4_PROT_S 13U +#define FUN_ETH_RX_CV_IL4_PROT_M 0x3 + +#define FUN_ETH_RX_CV_IL3_PROT_S 11U +#define FUN_ETH_RX_CV_IL3_PROT_M 0x3 + +#define FUN_ETH_RX_CV_OL4_PROT_S 8U +#define FUN_ETH_RX_CV_OL4_PROT_M 0x7 + +#define FUN_ETH_RX_CV_ENCAP_TYPE_S 6U +#define FUN_ETH_RX_CV_ENCAP_TYPE_M 0x3 + +#define FUN_ETH_RX_CV_OL3_PROT_S 4U +#define FUN_ETH_RX_CV_OL3_PROT_M 0x3 + +#define FUN_ETH_RX_CV_VLAN_TYPE_S 3U +#define FUN_ETH_RX_CV_VLAN_TYPE_M 0x1 + +#define FUN_ETH_RX_CV_L2_TYPE_S 2U +#define FUN_ETH_RX_CV_L2_TYPE_M 0x1 + +enum fun_rx_cv { + FUN_RX_CV_NONE = 0x0, + FUN_RX_CV_IP = 0x2, + FUN_RX_CV_IP6 = 0x3, + FUN_RX_CV_TCP = 0x2, + FUN_RX_CV_UDP = 0x3, + FUN_RX_CV_VXLAN = 0x2, + FUN_RX_CV_MPLS = 0x3, +}; + +struct fun_eth_cqe { + __u8 op; + __u8 len8; + __u8 nsgl; + __u8 repr_idn; + __be32 pkt_len; + + __be64 timestamp; + + __be16 pkt_cv; + __be16 rsvd0; + __be32 hash; + + __be16 encap_proto; + __be16 vlan; + __be32 rsvd1; + + __be32 buf_offset; + __be16 headroom; + __be16 csum; +}; + +enum fun_admin_adi_attr { + FUN_ADMIN_ADI_ATTR_MACADDR = 0x1, + FUN_ADMIN_ADI_ATTR_VLAN = 0x2, + FUN_ADMIN_ADI_ATTR_RATE = 0x3, +}; + +struct fun_adi_param { + union adi_param { + struct fun_adi_mac { + __be64 addr; + } mac; + struct fun_adi_vlan { + __be32 rsvd; + __be16 eth_type; + __be16 tci; + } vlan; + struct fun_adi_rate { + __be32 rsvd; + __be32 tx_mbps; + } rate; + } u; +}; + +#define FUN_ADI_MAC_INIT(_addr) \ + (struct fun_adi_mac) { \ + .addr = cpu_to_be64(_addr), \ + } + +#define FUN_ADI_VLAN_INIT(_eth_type, _tci) \ + (struct fun_adi_vlan) { \ + .eth_type = cpu_to_be16(_eth_type), .tci = cpu_to_be16(_tci), \ + } + +#define FUN_ADI_RATE_INIT(_tx_mbps) \ + (struct fun_adi_rate) { \ + .tx_mbps = cpu_to_be32(_tx_mbps), \ + } + +struct fun_admin_adi_req { + struct fun_admin_req_common common; + + union adi_req_subop { + struct fun_admin_adi_write_req { + __u8 subop; + __u8 attribute; + __be16 rsvd; + __be32 id; + + struct fun_adi_param param; + } write; + } u; +}; + +#define FUN_ADMIN_ADI_WRITE_REQ_INIT(_subop, _attribute, _id) \ + (struct fun_admin_adi_write_req) { \ + .subop = (_subop), .attribute = (_attribute), \ + .id = cpu_to_be32(_id), \ + } + +#endif /* __FUN_HCI_H */ diff --git a/drivers/net/ethernet/fungible/funcore/fun_queue.c b/drivers/net/ethernet/fungible/funcore/fun_queue.c new file mode 100644 index 000000000000..8ab9f68434f5 --- /dev/null +++ b/drivers/net/ethernet/fungible/funcore/fun_queue.c @@ -0,0 +1,601 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +#include +#include +#include +#include +#include +#include +#include + +#include "fun_dev.h" +#include "fun_queue.h" + +/* Allocate memory for a queue. This includes the memory for the HW descriptor + * ring, an optional 64b HW write-back area, and an optional SW state ring. + * Returns the virtual and DMA addresses of the HW ring, the VA of the SW ring, + * and the VA of the write-back area. + */ +void *fun_alloc_ring_mem(struct device *dma_dev, size_t depth, + size_t hw_desc_sz, size_t sw_desc_sz, bool wb, + int numa_node, dma_addr_t *dma_addr, void **sw_va, + volatile __be64 **wb_va) +{ + int dev_node = dev_to_node(dma_dev); + size_t dma_sz; + void *va; + + if (numa_node == NUMA_NO_NODE) + numa_node = dev_node; + + /* Place optional write-back area at end of descriptor ring. */ + dma_sz = hw_desc_sz * depth; + if (wb) + dma_sz += sizeof(u64); + + set_dev_node(dma_dev, numa_node); + va = dma_alloc_coherent(dma_dev, dma_sz, dma_addr, GFP_KERNEL); + set_dev_node(dma_dev, dev_node); + if (!va) + return NULL; + + if (sw_desc_sz) { + *sw_va = kvzalloc_node(sw_desc_sz * depth, GFP_KERNEL, + numa_node); + if (!*sw_va) { + dma_free_coherent(dma_dev, dma_sz, va, *dma_addr); + return NULL; + } + } + + if (wb) + *wb_va = va + dma_sz - sizeof(u64); + return va; +} +EXPORT_SYMBOL_GPL(fun_alloc_ring_mem); + +void fun_free_ring_mem(struct device *dma_dev, size_t depth, size_t hw_desc_sz, + bool wb, void *hw_va, dma_addr_t dma_addr, void *sw_va) +{ + if (hw_va) { + size_t sz = depth * hw_desc_sz; + + if (wb) + sz += sizeof(u64); + dma_free_coherent(dma_dev, sz, hw_va, dma_addr); + } + kvfree(sw_va); +} +EXPORT_SYMBOL_GPL(fun_free_ring_mem); + +/* Prepare and issue an admin command to create an SQ on the device with the + * provided parameters. If the queue ID is auto-allocated by the device it is + * returned in *sqidp. + */ +int fun_sq_create(struct fun_dev *fdev, u16 flags, u32 sqid, u32 cqid, + u8 sqe_size_log2, u32 sq_depth, dma_addr_t dma_addr, + u8 coal_nentries, u8 coal_usec, u32 irq_num, + u32 scan_start_id, u32 scan_end_id, + u32 rq_buf_size_log2, u32 *sqidp, u32 __iomem **dbp) +{ + union { + struct fun_admin_epsq_req req; + struct fun_admin_generic_create_rsp rsp; + } cmd; + dma_addr_t wb_addr; + u32 hw_qid; + int rc; + + if (sq_depth > fdev->q_depth) + return -EINVAL; + if (flags & FUN_ADMIN_EPSQ_CREATE_FLAG_RQ) + sqe_size_log2 = ilog2(sizeof(struct fun_eprq_rqbuf)); + + wb_addr = dma_addr + (sq_depth << sqe_size_log2); + + cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_EPSQ, + sizeof(cmd.req)); + cmd.req.u.create = + FUN_ADMIN_EPSQ_CREATE_REQ_INIT(FUN_ADMIN_SUBOP_CREATE, flags, + sqid, cqid, sqe_size_log2, + sq_depth - 1, dma_addr, 0, + coal_nentries, coal_usec, + irq_num, scan_start_id, + scan_end_id, 0, + rq_buf_size_log2, + ilog2(sizeof(u64)), wb_addr); + + rc = fun_submit_admin_sync_cmd(fdev, &cmd.req.common, + &cmd.rsp, sizeof(cmd.rsp), 0); + if (rc) + return rc; + + hw_qid = be32_to_cpu(cmd.rsp.id); + *dbp = fun_sq_db_addr(fdev, hw_qid); + if (flags & FUN_ADMIN_RES_CREATE_FLAG_ALLOCATOR) + *sqidp = hw_qid; + return rc; +} +EXPORT_SYMBOL_GPL(fun_sq_create); + +/* Prepare and issue an admin command to create a CQ on the device with the + * provided parameters. If the queue ID is auto-allocated by the device it is + * returned in *cqidp. + */ +int fun_cq_create(struct fun_dev *fdev, u16 flags, u32 cqid, u32 rqid, + u8 cqe_size_log2, u32 cq_depth, dma_addr_t dma_addr, + u16 headroom, u16 tailroom, u8 coal_nentries, u8 coal_usec, + u32 irq_num, u32 scan_start_id, u32 scan_end_id, u32 *cqidp, + u32 __iomem **dbp) +{ + union { + struct fun_admin_epcq_req req; + struct fun_admin_generic_create_rsp rsp; + } cmd; + u32 hw_qid; + int rc; + + if (cq_depth > fdev->q_depth) + return -EINVAL; + + cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_EPCQ, + sizeof(cmd.req)); + cmd.req.u.create = + FUN_ADMIN_EPCQ_CREATE_REQ_INIT(FUN_ADMIN_SUBOP_CREATE, flags, + cqid, rqid, cqe_size_log2, + cq_depth - 1, dma_addr, tailroom, + headroom / 2, 0, coal_nentries, + coal_usec, irq_num, + scan_start_id, scan_end_id, 0); + + rc = fun_submit_admin_sync_cmd(fdev, &cmd.req.common, + &cmd.rsp, sizeof(cmd.rsp), 0); + if (rc) + return rc; + + hw_qid = be32_to_cpu(cmd.rsp.id); + *dbp = fun_cq_db_addr(fdev, hw_qid); + if (flags & FUN_ADMIN_RES_CREATE_FLAG_ALLOCATOR) + *cqidp = hw_qid; + return rc; +} +EXPORT_SYMBOL_GPL(fun_cq_create); + +static bool fun_sq_is_head_wb(const struct fun_queue *funq) +{ + return funq->sq_flags & FUN_ADMIN_EPSQ_CREATE_FLAG_HEAD_WB_ADDRESS; +} + +static void fun_clean_rq(struct fun_queue *funq) +{ + struct fun_dev *fdev = funq->fdev; + struct fun_rq_info *rqinfo; + unsigned int i; + + for (i = 0; i < funq->rq_depth; i++) { + rqinfo = &funq->rq_info[i]; + if (rqinfo->page) { + dma_unmap_page(fdev->dev, rqinfo->dma, PAGE_SIZE, + DMA_FROM_DEVICE); + put_page(rqinfo->page); + rqinfo->page = NULL; + } + } +} + +static int fun_fill_rq(struct fun_queue *funq) +{ + struct device *dev = funq->fdev->dev; + int i, node = dev_to_node(dev); + struct fun_rq_info *rqinfo; + + for (i = 0; i < funq->rq_depth; i++) { + rqinfo = &funq->rq_info[i]; + rqinfo->page = alloc_pages_node(node, GFP_KERNEL, 0); + if (unlikely(!rqinfo->page)) + return -ENOMEM; + + rqinfo->dma = dma_map_page(dev, rqinfo->page, 0, + PAGE_SIZE, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dev, rqinfo->dma))) { + put_page(rqinfo->page); + rqinfo->page = NULL; + return -ENOMEM; + } + + funq->rqes[i] = FUN_EPRQ_RQBUF_INIT(rqinfo->dma); + } + + funq->rq_tail = funq->rq_depth - 1; + return 0; +} + +static void fun_rq_update_pos(struct fun_queue *funq, int buf_offset) +{ + if (buf_offset <= funq->rq_buf_offset) { + struct fun_rq_info *rqinfo = &funq->rq_info[funq->rq_buf_idx]; + struct device *dev = funq->fdev->dev; + + dma_sync_single_for_device(dev, rqinfo->dma, PAGE_SIZE, + DMA_FROM_DEVICE); + funq->num_rqe_to_fill++; + if (++funq->rq_buf_idx == funq->rq_depth) + funq->rq_buf_idx = 0; + } + funq->rq_buf_offset = buf_offset; +} + +/* Given a command response with data scattered across >= 1 RQ buffers return + * a pointer to a contiguous buffer containing all the data. If the data is in + * one RQ buffer the start address within that buffer is returned, otherwise a + * new buffer is allocated and the data is gathered into it. + */ +static void *fun_data_from_rq(struct fun_queue *funq, + const struct fun_rsp_common *rsp, bool *need_free) +{ + u32 bufoff, total_len, remaining, fragsize, dataoff; + struct device *dma_dev = funq->fdev->dev; + const struct fun_dataop_rqbuf *databuf; + const struct fun_dataop_hdr *dataop; + const struct fun_rq_info *rqinfo; + void *data; + + dataop = (void *)rsp + rsp->suboff8 * 8; + total_len = be32_to_cpu(dataop->total_len); + + if (likely(dataop->nsgl == 1)) { + databuf = (struct fun_dataop_rqbuf *)dataop->imm; + bufoff = be32_to_cpu(databuf->bufoff); + fun_rq_update_pos(funq, bufoff); + rqinfo = &funq->rq_info[funq->rq_buf_idx]; + dma_sync_single_for_cpu(dma_dev, rqinfo->dma + bufoff, + total_len, DMA_FROM_DEVICE); + *need_free = false; + return page_address(rqinfo->page) + bufoff; + } + + /* For scattered completions gather the fragments into one buffer. */ + + data = kmalloc(total_len, GFP_ATOMIC); + /* NULL is OK here. In case of failure we still need to consume the data + * for proper buffer accounting but indicate an error in the response. + */ + if (likely(data)) + *need_free = true; + + dataoff = 0; + for (remaining = total_len; remaining; remaining -= fragsize) { + fun_rq_update_pos(funq, 0); + fragsize = min_t(unsigned int, PAGE_SIZE, remaining); + if (data) { + rqinfo = &funq->rq_info[funq->rq_buf_idx]; + dma_sync_single_for_cpu(dma_dev, rqinfo->dma, fragsize, + DMA_FROM_DEVICE); + memcpy(data + dataoff, page_address(rqinfo->page), + fragsize); + dataoff += fragsize; + } + } + return data; +} + +unsigned int __fun_process_cq(struct fun_queue *funq, unsigned int max) +{ + const struct fun_cqe_info *info; + struct fun_rsp_common *rsp; + unsigned int new_cqes; + u16 sf_p, flags; + bool need_free; + void *cqe; + + if (!max) + max = funq->cq_depth - 1; + + for (new_cqes = 0; new_cqes < max; new_cqes++) { + cqe = funq->cqes + (funq->cq_head << funq->cqe_size_log2); + info = funq_cqe_info(funq, cqe); + sf_p = be16_to_cpu(info->sf_p); + + if ((sf_p & 1) != funq->cq_phase) + break; + + /* ensure the phase tag is read before other CQE fields */ + dma_rmb(); + + if (++funq->cq_head == funq->cq_depth) { + funq->cq_head = 0; + funq->cq_phase = !funq->cq_phase; + } + + rsp = cqe; + flags = be16_to_cpu(rsp->flags); + + need_free = false; + if (unlikely(flags & FUN_REQ_COMMON_FLAG_CQE_IN_RQBUF)) { + rsp = fun_data_from_rq(funq, rsp, &need_free); + if (!rsp) { + rsp = cqe; + rsp->len8 = 1; + if (rsp->ret == 0) + rsp->ret = ENOMEM; + } + } + + if (funq->cq_cb) + funq->cq_cb(funq, funq->cb_data, rsp, info); + if (need_free) + kfree(rsp); + } + + dev_dbg(funq->fdev->dev, "CQ %u, new CQEs %u/%u, head %u, phase %u\n", + funq->cqid, new_cqes, max, funq->cq_head, funq->cq_phase); + return new_cqes; +} + +unsigned int fun_process_cq(struct fun_queue *funq, unsigned int max) +{ + unsigned int processed; + u32 db; + + processed = __fun_process_cq(funq, max); + + if (funq->num_rqe_to_fill) { + funq->rq_tail = (funq->rq_tail + funq->num_rqe_to_fill) % + funq->rq_depth; + funq->num_rqe_to_fill = 0; + writel(funq->rq_tail, funq->rq_db); + } + + db = funq->cq_head | FUN_DB_IRQ_ARM_F; + writel(db, funq->cq_db); + return processed; +} + +static int fun_alloc_sqes(struct fun_queue *funq) +{ + funq->sq_cmds = fun_alloc_ring_mem(funq->fdev->dev, funq->sq_depth, + 1 << funq->sqe_size_log2, 0, + fun_sq_is_head_wb(funq), + NUMA_NO_NODE, &funq->sq_dma_addr, + NULL, &funq->sq_head); + return funq->sq_cmds ? 0 : -ENOMEM; +} + +static int fun_alloc_cqes(struct fun_queue *funq) +{ + funq->cqes = fun_alloc_ring_mem(funq->fdev->dev, funq->cq_depth, + 1 << funq->cqe_size_log2, 0, false, + NUMA_NO_NODE, &funq->cq_dma_addr, NULL, + NULL); + return funq->cqes ? 0 : -ENOMEM; +} + +static int fun_alloc_rqes(struct fun_queue *funq) +{ + funq->rqes = fun_alloc_ring_mem(funq->fdev->dev, funq->rq_depth, + sizeof(*funq->rqes), + sizeof(*funq->rq_info), false, + NUMA_NO_NODE, &funq->rq_dma_addr, + (void **)&funq->rq_info, NULL); + return funq->rqes ? 0 : -ENOMEM; +} + +/* Free a queue's structures. */ +void fun_free_queue(struct fun_queue *funq) +{ + struct device *dev = funq->fdev->dev; + + fun_free_ring_mem(dev, funq->cq_depth, 1 << funq->cqe_size_log2, false, + funq->cqes, funq->cq_dma_addr, NULL); + fun_free_ring_mem(dev, funq->sq_depth, 1 << funq->sqe_size_log2, + fun_sq_is_head_wb(funq), funq->sq_cmds, + funq->sq_dma_addr, NULL); + + if (funq->rqes) { + fun_clean_rq(funq); + fun_free_ring_mem(dev, funq->rq_depth, sizeof(*funq->rqes), + false, funq->rqes, funq->rq_dma_addr, + funq->rq_info); + } + + kfree(funq); +} + +/* Allocate and initialize a funq's structures. */ +struct fun_queue *fun_alloc_queue(struct fun_dev *fdev, int qid, + const struct fun_queue_alloc_req *req) +{ + struct fun_queue *funq = kzalloc(sizeof(*funq), GFP_KERNEL); + + if (!funq) + return NULL; + + funq->fdev = fdev; + spin_lock_init(&funq->sq_lock); + + funq->qid = qid; + + /* Initial CQ/SQ/RQ ids */ + if (req->rq_depth) { + funq->cqid = 2 * qid; + if (funq->qid) { + /* I/O Q: use rqid = cqid, sqid = +1 */ + funq->rqid = funq->cqid; + funq->sqid = funq->rqid + 1; + } else { + /* Admin Q: sqid is always 0, use ID 1 for RQ */ + funq->sqid = 0; + funq->rqid = 1; + } + } else { + funq->cqid = qid; + funq->sqid = qid; + } + + funq->cq_flags = req->cq_flags; + funq->sq_flags = req->sq_flags; + + funq->cqe_size_log2 = req->cqe_size_log2; + funq->sqe_size_log2 = req->sqe_size_log2; + + funq->cq_depth = req->cq_depth; + funq->sq_depth = req->sq_depth; + + funq->cq_intcoal_nentries = req->cq_intcoal_nentries; + funq->cq_intcoal_usec = req->cq_intcoal_usec; + + funq->sq_intcoal_nentries = req->sq_intcoal_nentries; + funq->sq_intcoal_usec = req->sq_intcoal_usec; + + if (fun_alloc_cqes(funq)) + goto free_funq; + + funq->cq_phase = 1; + + if (fun_alloc_sqes(funq)) + goto free_funq; + + if (req->rq_depth) { + funq->rq_flags = req->rq_flags | FUN_ADMIN_EPSQ_CREATE_FLAG_RQ; + funq->rq_depth = req->rq_depth; + funq->rq_buf_offset = -1; + + if (fun_alloc_rqes(funq) || fun_fill_rq(funq)) + goto free_funq; + } + + funq->cq_vector = -1; + funq->cqe_info_offset = (1 << funq->cqe_size_log2) - sizeof(struct fun_cqe_info); + + /* SQ/CQ 0 are implicitly created, assign their doorbells now. + * Other queues are assigned doorbells at their explicit creation. + */ + if (funq->sqid == 0) + funq->sq_db = fun_sq_db_addr(fdev, 0); + if (funq->cqid == 0) + funq->cq_db = fun_cq_db_addr(fdev, 0); + + return funq; + +free_funq: + fun_free_queue(funq); + return NULL; +} + +/* Create a funq's CQ on the device. */ +static int fun_create_cq(struct fun_queue *funq) +{ + struct fun_dev *fdev = funq->fdev; + unsigned int rqid; + int rc; + + rqid = funq->cq_flags & FUN_ADMIN_EPCQ_CREATE_FLAG_RQ ? + funq->rqid : FUN_HCI_ID_INVALID; + rc = fun_cq_create(fdev, funq->cq_flags, funq->cqid, rqid, + funq->cqe_size_log2, funq->cq_depth, + funq->cq_dma_addr, 0, 0, funq->cq_intcoal_nentries, + funq->cq_intcoal_usec, funq->cq_vector, 0, 0, + &funq->cqid, &funq->cq_db); + if (!rc) + dev_dbg(fdev->dev, "created CQ %u\n", funq->cqid); + + return rc; +} + +/* Create a funq's SQ on the device. */ +static int fun_create_sq(struct fun_queue *funq) +{ + struct fun_dev *fdev = funq->fdev; + int rc; + + rc = fun_sq_create(fdev, funq->sq_flags, funq->sqid, funq->cqid, + funq->sqe_size_log2, funq->sq_depth, + funq->sq_dma_addr, funq->sq_intcoal_nentries, + funq->sq_intcoal_usec, funq->cq_vector, 0, 0, + 0, &funq->sqid, &funq->sq_db); + if (!rc) + dev_dbg(fdev->dev, "created SQ %u\n", funq->sqid); + + return rc; +} + +/* Create a funq's RQ on the device. */ +int fun_create_rq(struct fun_queue *funq) +{ + struct fun_dev *fdev = funq->fdev; + int rc; + + rc = fun_sq_create(fdev, funq->rq_flags, funq->rqid, funq->cqid, 0, + funq->rq_depth, funq->rq_dma_addr, 0, 0, + funq->cq_vector, 0, 0, PAGE_SHIFT, &funq->rqid, + &funq->rq_db); + if (!rc) + dev_dbg(fdev->dev, "created RQ %u\n", funq->rqid); + + return rc; +} + +static unsigned int funq_irq(struct fun_queue *funq) +{ + return pci_irq_vector(to_pci_dev(funq->fdev->dev), funq->cq_vector); +} + +int fun_request_irq(struct fun_queue *funq, const char *devname, + irq_handler_t handler, void *data) +{ + int rc; + + if (funq->cq_vector < 0) + return -EINVAL; + + funq->irq_handler = handler; + funq->irq_data = data; + + snprintf(funq->irqname, sizeof(funq->irqname), + funq->qid ? "%s-q[%d]" : "%s-adminq", devname, funq->qid); + + rc = request_irq(funq_irq(funq), handler, 0, funq->irqname, data); + if (rc) + funq->irq_handler = NULL; + + return rc; +} + +/* Create all component queues of a funq on the device. */ +int fun_create_queue(struct fun_queue *funq) +{ + int rc; + + rc = fun_create_cq(funq); + if (rc) + return rc; + + if (funq->rq_depth) { + rc = fun_create_rq(funq); + if (rc) + goto release_cq; + } + + rc = fun_create_sq(funq); + if (rc) + goto release_rq; + + return 0; + +release_rq: + fun_destroy_sq(funq->fdev, funq->rqid); +release_cq: + fun_destroy_cq(funq->fdev, funq->cqid); + return rc; +} + +void fun_free_irq(struct fun_queue *funq) +{ + if (funq->irq_handler) { + unsigned int vector = funq_irq(funq); + + free_irq(vector, funq->irq_data); + funq->irq_handler = NULL; + funq->irq_data = NULL; + } +} diff --git a/drivers/net/ethernet/fungible/funcore/fun_queue.h b/drivers/net/ethernet/fungible/funcore/fun_queue.h new file mode 100644 index 000000000000..7fb53d0ae8b0 --- /dev/null +++ b/drivers/net/ethernet/fungible/funcore/fun_queue.h @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +#ifndef _FUN_QEUEUE_H +#define _FUN_QEUEUE_H + +#include +#include + +struct device; +struct fun_dev; +struct fun_queue; +struct fun_cqe_info; +struct fun_rsp_common; + +typedef void (*cq_callback_t)(struct fun_queue *funq, void *data, void *msg, + const struct fun_cqe_info *info); + +struct fun_rq_info { + dma_addr_t dma; + struct page *page; +}; + +/* A queue group consisting of an SQ, a CQ, and an optional RQ. */ +struct fun_queue { + struct fun_dev *fdev; + spinlock_t sq_lock; + + dma_addr_t cq_dma_addr; + dma_addr_t sq_dma_addr; + dma_addr_t rq_dma_addr; + + u32 __iomem *cq_db; + u32 __iomem *sq_db; + u32 __iomem *rq_db; + + void *cqes; + void *sq_cmds; + struct fun_eprq_rqbuf *rqes; + struct fun_rq_info *rq_info; + + u32 cqid; + u32 sqid; + u32 rqid; + + u32 cq_depth; + u32 sq_depth; + u32 rq_depth; + + u16 cq_head; + u16 sq_tail; + u16 rq_tail; + + u8 cqe_size_log2; + u8 sqe_size_log2; + + u16 cqe_info_offset; + + u16 rq_buf_idx; + int rq_buf_offset; + u16 num_rqe_to_fill; + + u8 cq_intcoal_usec; + u8 cq_intcoal_nentries; + u8 sq_intcoal_usec; + u8 sq_intcoal_nentries; + + u16 cq_flags; + u16 sq_flags; + u16 rq_flags; + + /* SQ head writeback */ + u16 sq_comp; + + volatile __be64 *sq_head; + + cq_callback_t cq_cb; + void *cb_data; + + irq_handler_t irq_handler; + void *irq_data; + s16 cq_vector; + u8 cq_phase; + + /* I/O q index */ + u16 qid; + + char irqname[24]; +}; + +static inline void *fun_sqe_at(const struct fun_queue *funq, unsigned int pos) +{ + return funq->sq_cmds + (pos << funq->sqe_size_log2); +} + +static inline void funq_sq_post_tail(struct fun_queue *funq, u16 tail) +{ + if (++tail == funq->sq_depth) + tail = 0; + funq->sq_tail = tail; + writel(tail, funq->sq_db); +} + +static inline struct fun_cqe_info *funq_cqe_info(const struct fun_queue *funq, + void *cqe) +{ + return cqe + funq->cqe_info_offset; +} + +static inline void funq_rq_post(struct fun_queue *funq) +{ + writel(funq->rq_tail, funq->rq_db); +} + +struct fun_queue_alloc_req { + u8 cqe_size_log2; + u8 sqe_size_log2; + + u16 cq_flags; + u16 sq_flags; + u16 rq_flags; + + u32 cq_depth; + u32 sq_depth; + u32 rq_depth; + + u8 cq_intcoal_usec; + u8 cq_intcoal_nentries; + u8 sq_intcoal_usec; + u8 sq_intcoal_nentries; +}; + +int fun_sq_create(struct fun_dev *fdev, u16 flags, u32 sqid, u32 cqid, + u8 sqe_size_log2, u32 sq_depth, dma_addr_t dma_addr, + u8 coal_nentries, u8 coal_usec, u32 irq_num, + u32 scan_start_id, u32 scan_end_id, + u32 rq_buf_size_log2, u32 *sqidp, u32 __iomem **dbp); +int fun_cq_create(struct fun_dev *fdev, u16 flags, u32 cqid, u32 rqid, + u8 cqe_size_log2, u32 cq_depth, dma_addr_t dma_addr, + u16 headroom, u16 tailroom, u8 coal_nentries, u8 coal_usec, + u32 irq_num, u32 scan_start_id, u32 scan_end_id, + u32 *cqidp, u32 __iomem **dbp); +void *fun_alloc_ring_mem(struct device *dma_dev, size_t depth, + size_t hw_desc_sz, size_t sw_desc_size, bool wb, + int numa_node, dma_addr_t *dma_addr, void **sw_va, + volatile __be64 **wb_va); +void fun_free_ring_mem(struct device *dma_dev, size_t depth, size_t hw_desc_sz, + bool wb, void *hw_va, dma_addr_t dma_addr, void *sw_va); + +#define fun_destroy_sq(fdev, sqid) \ + fun_res_destroy((fdev), FUN_ADMIN_OP_EPSQ, 0, (sqid)) +#define fun_destroy_cq(fdev, cqid) \ + fun_res_destroy((fdev), FUN_ADMIN_OP_EPCQ, 0, (cqid)) + +struct fun_queue *fun_alloc_queue(struct fun_dev *fdev, int qid, + const struct fun_queue_alloc_req *req); +void fun_free_queue(struct fun_queue *funq); + +static inline void fun_set_cq_callback(struct fun_queue *funq, cq_callback_t cb, + void *cb_data) +{ + funq->cq_cb = cb; + funq->cb_data = cb_data; +} + +int fun_create_rq(struct fun_queue *funq); +int fun_create_queue(struct fun_queue *funq); + +void fun_free_irq(struct fun_queue *funq); +int fun_request_irq(struct fun_queue *funq, const char *devname, + irq_handler_t handler, void *data); + +unsigned int __fun_process_cq(struct fun_queue *funq, unsigned int max); +unsigned int fun_process_cq(struct fun_queue *funq, unsigned int max); + +#endif /* _FUN_QEUEUE_H */ diff --git a/drivers/net/ethernet/fungible/funeth/Kconfig b/drivers/net/ethernet/fungible/funeth/Kconfig new file mode 100644 index 000000000000..c72ad9386400 --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Fungible Ethernet driver configuration +# + +config FUN_ETH + tristate "Fungible Ethernet device driver" + depends on PCI && PCI_MSI + depends on TLS && TLS_DEVICE || TLS_DEVICE=n + select NET_DEVLINK + select FUN_CORE + help + This driver supports the Ethernet functionality of Fungible adapters. + It works with both physical and virtual functions. + + To compile this driver as a module, choose M here. The module + will be called funeth. diff --git a/drivers/net/ethernet/fungible/funeth/Makefile b/drivers/net/ethernet/fungible/funeth/Makefile new file mode 100644 index 000000000000..646d69595b4f --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +ccflags-y += -I$(srctree)/$(src)/../funcore -I$(srctree)/$(src) + +obj-$(CONFIG_FUN_ETH) += funeth.o + +funeth-y := funeth_main.o funeth_rx.o funeth_tx.o funeth_devlink.o \ + funeth_ethtool.o + +funeth-$(CONFIG_TLS_DEVICE) += funeth_ktls.o diff --git a/drivers/net/ethernet/fungible/funeth/fun_port.h b/drivers/net/ethernet/fungible/funeth/fun_port.h new file mode 100644 index 000000000000..0f9da44e3786 --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/fun_port.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +#ifndef _FUN_PORT_H +#define _FUN_PORT_H + +enum port_mac_rx_stats { + PORT_MAC_RX_etherStatsOctets = 0x0, + PORT_MAC_RX_OctetsReceivedOK = 0x1, + PORT_MAC_RX_aAlignmentErrors = 0x2, + PORT_MAC_RX_aPAUSEMACCtrlFramesReceived = 0x3, + PORT_MAC_RX_aFrameTooLongErrors = 0x4, + PORT_MAC_RX_aInRangeLengthErrors = 0x5, + PORT_MAC_RX_aFramesReceivedOK = 0x6, + PORT_MAC_RX_aFrameCheckSequenceErrors = 0x7, + PORT_MAC_RX_VLANReceivedOK = 0x8, + PORT_MAC_RX_ifInErrors = 0x9, + PORT_MAC_RX_ifInUcastPkts = 0xa, + PORT_MAC_RX_ifInMulticastPkts = 0xb, + PORT_MAC_RX_ifInBroadcastPkts = 0xc, + PORT_MAC_RX_etherStatsDropEvents = 0xd, + PORT_MAC_RX_etherStatsPkts = 0xe, + PORT_MAC_RX_etherStatsUndersizePkts = 0xf, + PORT_MAC_RX_etherStatsPkts64Octets = 0x10, + PORT_MAC_RX_etherStatsPkts65to127Octets = 0x11, + PORT_MAC_RX_etherStatsPkts128to255Octets = 0x12, + PORT_MAC_RX_etherStatsPkts256to511Octets = 0x13, + PORT_MAC_RX_etherStatsPkts512to1023Octets = 0x14, + PORT_MAC_RX_etherStatsPkts1024to1518Octets = 0x15, + PORT_MAC_RX_etherStatsPkts1519toMaxOctets = 0x16, + PORT_MAC_RX_etherStatsOversizePkts = 0x17, + PORT_MAC_RX_etherStatsJabbers = 0x18, + PORT_MAC_RX_etherStatsFragments = 0x19, + PORT_MAC_RX_CBFCPAUSEFramesReceived_0 = 0x1a, + PORT_MAC_RX_CBFCPAUSEFramesReceived_1 = 0x1b, + PORT_MAC_RX_CBFCPAUSEFramesReceived_2 = 0x1c, + PORT_MAC_RX_CBFCPAUSEFramesReceived_3 = 0x1d, + PORT_MAC_RX_CBFCPAUSEFramesReceived_4 = 0x1e, + PORT_MAC_RX_CBFCPAUSEFramesReceived_5 = 0x1f, + PORT_MAC_RX_CBFCPAUSEFramesReceived_6 = 0x20, + PORT_MAC_RX_CBFCPAUSEFramesReceived_7 = 0x21, + PORT_MAC_RX_CBFCPAUSEFramesReceived_8 = 0x22, + PORT_MAC_RX_CBFCPAUSEFramesReceived_9 = 0x23, + PORT_MAC_RX_CBFCPAUSEFramesReceived_10 = 0x24, + PORT_MAC_RX_CBFCPAUSEFramesReceived_11 = 0x25, + PORT_MAC_RX_CBFCPAUSEFramesReceived_12 = 0x26, + PORT_MAC_RX_CBFCPAUSEFramesReceived_13 = 0x27, + PORT_MAC_RX_CBFCPAUSEFramesReceived_14 = 0x28, + PORT_MAC_RX_CBFCPAUSEFramesReceived_15 = 0x29, + PORT_MAC_RX_MACControlFramesReceived = 0x2a, + PORT_MAC_RX_STATS_MAX = 0x2b, +}; + +enum port_mac_tx_stats { + PORT_MAC_TX_etherStatsOctets = 0x0, + PORT_MAC_TX_OctetsTransmittedOK = 0x1, + PORT_MAC_TX_aPAUSEMACCtrlFramesTransmitted = 0x2, + PORT_MAC_TX_aFramesTransmittedOK = 0x3, + PORT_MAC_TX_VLANTransmittedOK = 0x4, + PORT_MAC_TX_ifOutErrors = 0x5, + PORT_MAC_TX_ifOutUcastPkts = 0x6, + PORT_MAC_TX_ifOutMulticastPkts = 0x7, + PORT_MAC_TX_ifOutBroadcastPkts = 0x8, + PORT_MAC_TX_etherStatsPkts64Octets = 0x9, + PORT_MAC_TX_etherStatsPkts65to127Octets = 0xa, + PORT_MAC_TX_etherStatsPkts128to255Octets = 0xb, + PORT_MAC_TX_etherStatsPkts256to511Octets = 0xc, + PORT_MAC_TX_etherStatsPkts512to1023Octets = 0xd, + PORT_MAC_TX_etherStatsPkts1024to1518Octets = 0xe, + PORT_MAC_TX_etherStatsPkts1519toMaxOctets = 0xf, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_0 = 0x10, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_1 = 0x11, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_2 = 0x12, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_3 = 0x13, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_4 = 0x14, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_5 = 0x15, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_6 = 0x16, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_7 = 0x17, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_8 = 0x18, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_9 = 0x19, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_10 = 0x1a, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_11 = 0x1b, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_12 = 0x1c, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_13 = 0x1d, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_14 = 0x1e, + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_15 = 0x1f, + PORT_MAC_TX_MACControlFramesTransmitted = 0x20, + PORT_MAC_TX_etherStatsPkts = 0x21, + PORT_MAC_TX_STATS_MAX = 0x22, +}; + +enum port_mac_fec_stats { + PORT_MAC_FEC_Correctable = 0x0, + PORT_MAC_FEC_Uncorrectable = 0x1, + PORT_MAC_FEC_STATS_MAX = 0x2, +}; + +#endif /* _FUN_PORT_H */ diff --git a/drivers/net/ethernet/fungible/funeth/funeth.h b/drivers/net/ethernet/fungible/funeth/funeth.h new file mode 100644 index 000000000000..1250e10d21db --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/funeth.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +#ifndef _FUNETH_H +#define _FUNETH_H + +#include +#include +#include +#include +#include +#include +#include "fun_dev.h" + +#define ADMIN_SQE_SIZE SZ_128 +#define ADMIN_CQE_SIZE SZ_64 +#define ADMIN_RSP_MAX_LEN (ADMIN_CQE_SIZE - sizeof(struct fun_cqe_info)) + +#define FUN_MAX_MTU 9024 + +#define SQ_DEPTH 512U +#define CQ_DEPTH 1024U +#define RQ_DEPTH (512U / (PAGE_SIZE / 4096)) + +#define CQ_INTCOAL_USEC 10 +#define CQ_INTCOAL_NPKT 16 +#define SQ_INTCOAL_USEC 10 +#define SQ_INTCOAL_NPKT 16 + +#define INVALID_LPORT 0xffff + +#define FUN_PORT_CAP_PAUSE_MASK (FUN_PORT_CAP_TX_PAUSE | FUN_PORT_CAP_RX_PAUSE) + +struct fun_vport_info { + u8 mac[ETH_ALEN]; + u16 vlan; + __be16 vlan_proto; + u8 qos; + u8 spoofchk:1; + u8 trusted:1; + unsigned int max_rate; +}; + +/* "subclass" of fun_dev for Ethernet functions */ +struct fun_ethdev { + struct fun_dev fdev; + + /* the function's network ports */ + struct net_device **netdevs; + unsigned int num_ports; + + /* configuration for the function's virtual ports */ + unsigned int num_vports; + struct fun_vport_info *vport_info; + + struct mutex state_mutex; /* nests inside RTNL if both taken */ + + unsigned int nsqs_per_port; +}; + +static inline struct fun_ethdev *to_fun_ethdev(struct fun_dev *p) +{ + return container_of(p, struct fun_ethdev, fdev); +} + +struct fun_qset { + struct funeth_rxq **rxqs; + struct funeth_txq **txqs; + struct funeth_txq **xdpqs; + unsigned int nrxqs; + unsigned int ntxqs; + unsigned int nxdpqs; + unsigned int rxq_start; + unsigned int txq_start; + unsigned int xdpq_start; + unsigned int cq_depth; + unsigned int rq_depth; + unsigned int sq_depth; + int state; +}; + +/* Per netdevice driver state, i.e., netdev_priv. */ +struct funeth_priv { + struct fun_dev *fdev; + struct pci_dev *pdev; + struct net_device *netdev; + + struct funeth_rxq * __rcu *rxqs; + struct funeth_txq **txqs; + struct funeth_txq * __rcu *xdpqs; + + struct xarray irqs; + unsigned int num_tx_irqs; + unsigned int num_rx_irqs; + unsigned int rx_irq_ofst; + + unsigned int lane_attrs; + u16 lport; + + /* link settings */ + u64 port_caps; + u64 advertising; + u64 lp_advertising; + unsigned int link_speed; + u8 xcvr_type; + u8 active_fc; + u8 active_fec; + u8 link_down_reason; + seqcount_t link_seq; + + u32 msg_enable; + + unsigned int num_xdpqs; + + /* ethtool, etc. config parameters */ + unsigned int sq_depth; + unsigned int rq_depth; + unsigned int cq_depth; + unsigned int cq_irq_db; + u8 tx_coal_usec; + u8 tx_coal_count; + u8 rx_coal_usec; + u8 rx_coal_count; + + struct hwtstamp_config hwtstamp_cfg; + + /* cumulative queue stats from earlier queue instances */ + u64 tx_packets; + u64 tx_bytes; + u64 tx_dropped; + u64 rx_packets; + u64 rx_bytes; + u64 rx_dropped; + + /* RSS */ + unsigned int rss_hw_id; + enum fun_eth_hash_alg hash_algo; + u8 rss_key[FUN_ETH_RSS_MAX_KEY_SIZE]; + unsigned int indir_table_nentries; + u32 indir_table[FUN_ETH_RSS_MAX_INDIR_ENT]; + dma_addr_t rss_dma_addr; + void *rss_cfg; + + /* DMA area for port stats */ + dma_addr_t stats_dma_addr; + __be64 *stats; + + struct bpf_prog *xdp_prog; + + struct devlink_port dl_port; + + /* kTLS state */ + unsigned int ktls_id; + atomic64_t tx_tls_add; + atomic64_t tx_tls_del; + atomic64_t tx_tls_resync; +}; + +void fun_set_ethtool_ops(struct net_device *netdev); +int fun_port_write_cmd(struct funeth_priv *fp, int key, u64 data); +int fun_port_read_cmd(struct funeth_priv *fp, int key, u64 *data); +int fun_create_and_bind_tx(struct funeth_priv *fp, u32 sqid); +int fun_replace_queues(struct net_device *dev, struct fun_qset *newqs, + struct netlink_ext_ack *extack); +int fun_change_num_queues(struct net_device *dev, unsigned int ntx, + unsigned int nrx); +void fun_set_ring_count(struct net_device *netdev, unsigned int ntx, + unsigned int nrx); +int fun_config_rss(struct net_device *dev, int algo, const u8 *key, + const u32 *qtable, u8 op); + +#endif /* _FUNETH_H */ diff --git a/drivers/net/ethernet/fungible/funeth/funeth_devlink.c b/drivers/net/ethernet/fungible/funeth/funeth_devlink.c new file mode 100644 index 000000000000..a849b3c6b01f --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/funeth_devlink.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +#include "funeth.h" +#include "funeth_devlink.h" + +static int fun_dl_info_get(struct devlink *dl, struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + int err; + + err = devlink_info_driver_name_put(req, KBUILD_MODNAME); + if (err) + return err; + + return 0; +} + +static const struct devlink_ops fun_dl_ops = { + .info_get = fun_dl_info_get, +}; + +struct devlink *fun_devlink_alloc(struct device *dev) +{ + return devlink_alloc(&fun_dl_ops, sizeof(struct fun_ethdev), dev); +} + +void fun_devlink_free(struct devlink *devlink) +{ + devlink_free(devlink); +} + +void fun_devlink_register(struct devlink *devlink) +{ + devlink_register(devlink); +} + +void fun_devlink_unregister(struct devlink *devlink) +{ + devlink_unregister(devlink); +} diff --git a/drivers/net/ethernet/fungible/funeth/funeth_devlink.h b/drivers/net/ethernet/fungible/funeth/funeth_devlink.h new file mode 100644 index 000000000000..e40464d57ff4 --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/funeth_devlink.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +#ifndef __FUNETH_DEVLINK_H +#define __FUNETH_DEVLINK_H + +#include + +struct devlink *fun_devlink_alloc(struct device *dev); +void fun_devlink_free(struct devlink *devlink); +void fun_devlink_register(struct devlink *devlink); +void fun_devlink_unregister(struct devlink *devlink); + +#endif /* __FUNETH_DEVLINK_H */ diff --git a/drivers/net/ethernet/fungible/funeth/funeth_ethtool.c b/drivers/net/ethernet/fungible/funeth/funeth_ethtool.c new file mode 100644 index 000000000000..d081168c95fa --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/funeth_ethtool.c @@ -0,0 +1,1162 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +#include +#include +#include +#include +#include +#include +#include +#include +#include "funeth.h" +#include "fun_port.h" +#include "funeth_txrx.h" + +/* Min queue depth. The smallest power-of-2 supporting jumbo frames with 4K + * pages is 8. Require it for all types of queues though some could work with + * fewer entries. + */ +#define FUNETH_MIN_QDEPTH 8 + +static const char mac_tx_stat_names[][ETH_GSTRING_LEN] = { + "mac_tx_octets_total", + "mac_tx_frames_total", + "mac_tx_vlan_frames_ok", + "mac_tx_unicast_frames", + "mac_tx_multicast_frames", + "mac_tx_broadcast_frames", + "mac_tx_errors", + "mac_tx_CBFCPAUSE0", + "mac_tx_CBFCPAUSE1", + "mac_tx_CBFCPAUSE2", + "mac_tx_CBFCPAUSE3", + "mac_tx_CBFCPAUSE4", + "mac_tx_CBFCPAUSE5", + "mac_tx_CBFCPAUSE6", + "mac_tx_CBFCPAUSE7", + "mac_tx_CBFCPAUSE8", + "mac_tx_CBFCPAUSE9", + "mac_tx_CBFCPAUSE10", + "mac_tx_CBFCPAUSE11", + "mac_tx_CBFCPAUSE12", + "mac_tx_CBFCPAUSE13", + "mac_tx_CBFCPAUSE14", + "mac_tx_CBFCPAUSE15", +}; + +static const char mac_rx_stat_names[][ETH_GSTRING_LEN] = { + "mac_rx_octets_total", + "mac_rx_frames_total", + "mac_rx_VLAN_frames_ok", + "mac_rx_unicast_frames", + "mac_rx_multicast_frames", + "mac_rx_broadcast_frames", + "mac_rx_drop_events", + "mac_rx_errors", + "mac_rx_alignment_errors", + "mac_rx_CBFCPAUSE0", + "mac_rx_CBFCPAUSE1", + "mac_rx_CBFCPAUSE2", + "mac_rx_CBFCPAUSE3", + "mac_rx_CBFCPAUSE4", + "mac_rx_CBFCPAUSE5", + "mac_rx_CBFCPAUSE6", + "mac_rx_CBFCPAUSE7", + "mac_rx_CBFCPAUSE8", + "mac_rx_CBFCPAUSE9", + "mac_rx_CBFCPAUSE10", + "mac_rx_CBFCPAUSE11", + "mac_rx_CBFCPAUSE12", + "mac_rx_CBFCPAUSE13", + "mac_rx_CBFCPAUSE14", + "mac_rx_CBFCPAUSE15", +}; + +static const char * const txq_stat_names[] = { + "tx_pkts", + "tx_bytes", + "tx_cso", + "tx_tso", + "tx_encapsulated_tso", + "tx_more", + "tx_queue_stops", + "tx_queue_restarts", + "tx_mapping_errors", + "tx_tls_encrypted_packets", + "tx_tls_encrypted_bytes", + "tx_tls_ooo", + "tx_tls_drop_no_sync_data", +}; + +static const char * const xdpq_stat_names[] = { + "tx_xdp_pkts", + "tx_xdp_bytes", + "tx_xdp_full", + "tx_xdp_mapping_errors", +}; + +static const char * const rxq_stat_names[] = { + "rx_pkts", + "rx_bytes", + "rx_cso", + "gro_pkts", + "gro_merged", + "rx_xdp_tx", + "rx_xdp_redir", + "rx_xdp_drops", + "rx_buffers", + "rx_page_allocs", + "rx_drops", + "rx_budget_exhausted", + "rx_mapping_errors", +}; + +static const char * const tls_stat_names[] = { + "tx_tls_ctx", + "tx_tls_del", + "tx_tls_resync", +}; + +static void fun_link_modes_to_ethtool(u64 modes, + unsigned long *ethtool_modes_map) +{ +#define ADD_LINK_MODE(mode) \ + __set_bit(ETHTOOL_LINK_MODE_ ## mode ## _BIT, ethtool_modes_map) + + if (modes & FUN_PORT_CAP_AUTONEG) + ADD_LINK_MODE(Autoneg); + if (modes & FUN_PORT_CAP_1000_X) + ADD_LINK_MODE(1000baseX_Full); + if (modes & FUN_PORT_CAP_10G_R) { + ADD_LINK_MODE(10000baseCR_Full); + ADD_LINK_MODE(10000baseSR_Full); + ADD_LINK_MODE(10000baseLR_Full); + ADD_LINK_MODE(10000baseER_Full); + } + if (modes & FUN_PORT_CAP_25G_R) { + ADD_LINK_MODE(25000baseCR_Full); + ADD_LINK_MODE(25000baseSR_Full); + } + if (modes & FUN_PORT_CAP_40G_R4) { + ADD_LINK_MODE(40000baseCR4_Full); + ADD_LINK_MODE(40000baseSR4_Full); + ADD_LINK_MODE(40000baseLR4_Full); + } + if (modes & FUN_PORT_CAP_50G_R2) { + ADD_LINK_MODE(50000baseCR2_Full); + ADD_LINK_MODE(50000baseSR2_Full); + } + if (modes & FUN_PORT_CAP_50G_R) { + ADD_LINK_MODE(50000baseCR_Full); + ADD_LINK_MODE(50000baseSR_Full); + ADD_LINK_MODE(50000baseLR_ER_FR_Full); + } + if (modes & FUN_PORT_CAP_100G_R4) { + ADD_LINK_MODE(100000baseCR4_Full); + ADD_LINK_MODE(100000baseSR4_Full); + ADD_LINK_MODE(100000baseLR4_ER4_Full); + } + if (modes & FUN_PORT_CAP_100G_R2) { + ADD_LINK_MODE(100000baseCR2_Full); + ADD_LINK_MODE(100000baseSR2_Full); + ADD_LINK_MODE(100000baseLR2_ER2_FR2_Full); + } + if (modes & FUN_PORT_CAP_FEC_NONE) + ADD_LINK_MODE(FEC_NONE); + if (modes & FUN_PORT_CAP_FEC_FC) + ADD_LINK_MODE(FEC_BASER); + if (modes & FUN_PORT_CAP_FEC_RS) + ADD_LINK_MODE(FEC_RS); + if (modes & FUN_PORT_CAP_RX_PAUSE) + ADD_LINK_MODE(Pause); + +#undef ADD_LINK_MODE +} + +static void set_asym_pause(u64 advertising, struct ethtool_link_ksettings *ks) +{ + bool rx_pause, tx_pause; + + rx_pause = advertising & FUN_PORT_CAP_RX_PAUSE; + tx_pause = advertising & FUN_PORT_CAP_TX_PAUSE; + if (tx_pause ^ rx_pause) + ethtool_link_ksettings_add_link_mode(ks, advertising, + Asym_Pause); +} + +static unsigned int fun_port_type(unsigned int xcvr) +{ + if (!xcvr) + return PORT_NONE; + + switch (xcvr & 7) { + case FUN_XCVR_BASET: + return PORT_TP; + case FUN_XCVR_CU: + return PORT_DA; + default: + return PORT_FIBRE; + } +} + +static int fun_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *ks) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + unsigned int seq, speed, xcvr; + u64 lp_advertising; + bool link_up; + + ethtool_link_ksettings_zero_link_mode(ks, supported); + ethtool_link_ksettings_zero_link_mode(ks, advertising); + ethtool_link_ksettings_zero_link_mode(ks, lp_advertising); + + /* Link settings change asynchronously, take a consistent snapshot */ + do { + seq = read_seqcount_begin(&fp->link_seq); + link_up = netif_carrier_ok(netdev); + speed = fp->link_speed; + xcvr = fp->xcvr_type; + lp_advertising = fp->lp_advertising; + } while (read_seqcount_retry(&fp->link_seq, seq)); + + if (link_up) { + ks->base.speed = speed; + ks->base.duplex = DUPLEX_FULL; + fun_link_modes_to_ethtool(lp_advertising, + ks->link_modes.lp_advertising); + } else { + ks->base.speed = SPEED_UNKNOWN; + ks->base.duplex = DUPLEX_UNKNOWN; + } + + ks->base.autoneg = (fp->advertising & FUN_PORT_CAP_AUTONEG) ? + AUTONEG_ENABLE : AUTONEG_DISABLE; + ks->base.port = fun_port_type(xcvr); + + fun_link_modes_to_ethtool(fp->port_caps, ks->link_modes.supported); + if (fp->port_caps & (FUN_PORT_CAP_RX_PAUSE | FUN_PORT_CAP_TX_PAUSE)) + ethtool_link_ksettings_add_link_mode(ks, supported, Asym_Pause); + + fun_link_modes_to_ethtool(fp->advertising, ks->link_modes.advertising); + set_asym_pause(fp->advertising, ks); + return 0; +} + +static u64 fun_advert_modes(const struct ethtool_link_ksettings *ks) +{ + u64 modes = 0; + +#define HAS_MODE(mode) \ + ethtool_link_ksettings_test_link_mode(ks, advertising, mode) + + if (HAS_MODE(1000baseX_Full)) + modes |= FUN_PORT_CAP_1000_X; + if (HAS_MODE(10000baseCR_Full) || HAS_MODE(10000baseSR_Full) || + HAS_MODE(10000baseLR_Full) || HAS_MODE(10000baseER_Full)) + modes |= FUN_PORT_CAP_10G_R; + if (HAS_MODE(25000baseCR_Full) || HAS_MODE(25000baseSR_Full)) + modes |= FUN_PORT_CAP_25G_R; + if (HAS_MODE(40000baseCR4_Full) || HAS_MODE(40000baseSR4_Full) || + HAS_MODE(40000baseLR4_Full)) + modes |= FUN_PORT_CAP_40G_R4; + if (HAS_MODE(50000baseCR2_Full) || HAS_MODE(50000baseSR2_Full)) + modes |= FUN_PORT_CAP_50G_R2; + if (HAS_MODE(50000baseCR_Full) || HAS_MODE(50000baseSR_Full) || + HAS_MODE(50000baseLR_ER_FR_Full)) + modes |= FUN_PORT_CAP_50G_R; + if (HAS_MODE(100000baseCR4_Full) || HAS_MODE(100000baseSR4_Full) || + HAS_MODE(100000baseLR4_ER4_Full)) + modes |= FUN_PORT_CAP_100G_R4; + if (HAS_MODE(100000baseCR2_Full) || HAS_MODE(100000baseSR2_Full) || + HAS_MODE(100000baseLR2_ER2_FR2_Full)) + modes |= FUN_PORT_CAP_100G_R2; + + return modes; +#undef HAS_MODE +} + +static u64 fun_speed_to_link_mode(unsigned int speed) +{ + switch (speed) { + case SPEED_100000: + return FUN_PORT_CAP_100G_R4 | FUN_PORT_CAP_100G_R2; + case SPEED_50000: + return FUN_PORT_CAP_50G_R | FUN_PORT_CAP_50G_R2; + case SPEED_40000: + return FUN_PORT_CAP_40G_R4; + case SPEED_25000: + return FUN_PORT_CAP_25G_R; + case SPEED_10000: + return FUN_PORT_CAP_10G_R; + case SPEED_1000: + return FUN_PORT_CAP_1000_X; + default: + return 0; + } +} + +static int fun_change_advert(struct funeth_priv *fp, u64 new_advert) +{ + int err; + + if (new_advert == fp->advertising) + return 0; + + err = fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_ADVERT, new_advert); + if (!err) + fp->advertising = new_advert; + return err; +} + +#define FUN_PORT_CAP_FEC_MASK \ + (FUN_PORT_CAP_FEC_NONE | FUN_PORT_CAP_FEC_FC | FUN_PORT_CAP_FEC_RS) + +static int fun_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *ks) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = {}; + struct funeth_priv *fp = netdev_priv(netdev); + u64 new_advert; + + /* eswitch ports don't support mode changes */ + if (fp->port_caps & FUN_PORT_CAP_VPORT) + return -EOPNOTSUPP; + + if (ks->base.duplex == DUPLEX_HALF) + return -EINVAL; + if (ks->base.autoneg == AUTONEG_ENABLE && + !(fp->port_caps & FUN_PORT_CAP_AUTONEG)) + return -EINVAL; + + if (ks->base.autoneg == AUTONEG_ENABLE) { + if (linkmode_empty(ks->link_modes.advertising)) + return -EINVAL; + + fun_link_modes_to_ethtool(fp->port_caps, supported); + if (!linkmode_subset(ks->link_modes.advertising, supported)) + return -EINVAL; + + new_advert = fun_advert_modes(ks) | FUN_PORT_CAP_AUTONEG; + } else { + new_advert = fun_speed_to_link_mode(ks->base.speed); + new_advert &= fp->port_caps; + if (!new_advert) + return -EINVAL; + } + new_advert |= fp->advertising & + (FUN_PORT_CAP_PAUSE_MASK | FUN_PORT_CAP_FEC_MASK); + + return fun_change_advert(fp, new_advert); +} + +static void fun_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + u8 active_pause = fp->active_fc; + + pause->rx_pause = !!(active_pause & FUN_PORT_CAP_RX_PAUSE); + pause->tx_pause = !!(active_pause & FUN_PORT_CAP_TX_PAUSE); + pause->autoneg = !!(fp->advertising & FUN_PORT_CAP_AUTONEG); +} + +static int fun_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct funeth_priv *fp = netdev_priv(netdev); + u64 new_advert; + + if (fp->port_caps & FUN_PORT_CAP_VPORT) + return -EOPNOTSUPP; + /* Forcing PAUSE settings with AN enabled is unsupported. */ + if (!pause->autoneg && (fp->advertising & FUN_PORT_CAP_AUTONEG)) + return -EOPNOTSUPP; + if (pause->autoneg && !(fp->advertising & FUN_PORT_CAP_AUTONEG)) + return -EINVAL; + if (pause->tx_pause && !(fp->port_caps & FUN_PORT_CAP_TX_PAUSE)) + return -EINVAL; + if (pause->rx_pause && !(fp->port_caps & FUN_PORT_CAP_RX_PAUSE)) + return -EINVAL; + + new_advert = fp->advertising & ~FUN_PORT_CAP_PAUSE_MASK; + if (pause->tx_pause) + new_advert |= FUN_PORT_CAP_TX_PAUSE; + if (pause->rx_pause) + new_advert |= FUN_PORT_CAP_RX_PAUSE; + + return fun_change_advert(fp, new_advert); +} + +static int fun_restart_an(struct net_device *netdev) +{ + struct funeth_priv *fp = netdev_priv(netdev); + + if (!(fp->advertising & FUN_PORT_CAP_AUTONEG)) + return -EOPNOTSUPP; + + return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_ADVERT, + FUN_PORT_CAP_AUTONEG); +} + +static int fun_set_phys_id(struct net_device *netdev, + enum ethtool_phys_id_state state) +{ + struct funeth_priv *fp = netdev_priv(netdev); + unsigned int beacon; + + if (fp->port_caps & FUN_PORT_CAP_VPORT) + return -EOPNOTSUPP; + if (state != ETHTOOL_ID_ACTIVE && state != ETHTOOL_ID_INACTIVE) + return -EOPNOTSUPP; + + beacon = state == ETHTOOL_ID_ACTIVE ? FUN_PORT_LED_BEACON_ON : + FUN_PORT_LED_BEACON_OFF; + return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_LED, beacon); +} + +static void fun_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + + strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(fp->pdev), sizeof(info->bus_info)); +} + +static u32 fun_get_msglevel(struct net_device *netdev) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + + return fp->msg_enable; +} + +static void fun_set_msglevel(struct net_device *netdev, u32 value) +{ + struct funeth_priv *fp = netdev_priv(netdev); + + fp->msg_enable = value; +} + +static int fun_get_regs_len(struct net_device *dev) +{ + return NVME_REG_ACQ + sizeof(u64); +} + +static void fun_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *buf) +{ + const struct funeth_priv *fp = netdev_priv(dev); + void __iomem *bar = fp->fdev->bar; + + regs->version = 0; + *(u64 *)(buf + NVME_REG_CAP) = readq(bar + NVME_REG_CAP); + *(u32 *)(buf + NVME_REG_VS) = readl(bar + NVME_REG_VS); + *(u32 *)(buf + NVME_REG_INTMS) = readl(bar + NVME_REG_INTMS); + *(u32 *)(buf + NVME_REG_INTMC) = readl(bar + NVME_REG_INTMC); + *(u32 *)(buf + NVME_REG_CC) = readl(bar + NVME_REG_CC); + *(u32 *)(buf + NVME_REG_CSTS) = readl(bar + NVME_REG_CSTS); + *(u32 *)(buf + NVME_REG_AQA) = readl(bar + NVME_REG_AQA); + *(u64 *)(buf + NVME_REG_ASQ) = readq(bar + NVME_REG_ASQ); + *(u64 *)(buf + NVME_REG_ACQ) = readq(bar + NVME_REG_ACQ); +} + +static int fun_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kcoal, + struct netlink_ext_ack *ext_ack) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + + coal->rx_coalesce_usecs = fp->rx_coal_usec; + coal->rx_max_coalesced_frames = fp->rx_coal_count; + coal->use_adaptive_rx_coalesce = !fp->cq_irq_db; + coal->tx_coalesce_usecs = fp->tx_coal_usec; + coal->tx_max_coalesced_frames = fp->tx_coal_count; + return 0; +} + +static int fun_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kcoal, + struct netlink_ext_ack *ext_ack) +{ + struct funeth_priv *fp = netdev_priv(netdev); + struct funeth_rxq **rxqs; + unsigned int i, db_val; + + if (coal->rx_coalesce_usecs > FUN_DB_INTCOAL_USEC_M || + coal->rx_max_coalesced_frames > FUN_DB_INTCOAL_ENTRIES_M || + (coal->rx_coalesce_usecs | coal->rx_max_coalesced_frames) == 0 || + coal->tx_coalesce_usecs > FUN_DB_INTCOAL_USEC_M || + coal->tx_max_coalesced_frames > FUN_DB_INTCOAL_ENTRIES_M || + (coal->tx_coalesce_usecs | coal->tx_max_coalesced_frames) == 0) + return -EINVAL; + + /* a timer is required if there's any coalescing */ + if ((coal->rx_max_coalesced_frames > 1 && !coal->rx_coalesce_usecs) || + (coal->tx_max_coalesced_frames > 1 && !coal->tx_coalesce_usecs)) + return -EINVAL; + + fp->rx_coal_usec = coal->rx_coalesce_usecs; + fp->rx_coal_count = coal->rx_max_coalesced_frames; + fp->tx_coal_usec = coal->tx_coalesce_usecs; + fp->tx_coal_count = coal->tx_max_coalesced_frames; + + db_val = FUN_IRQ_CQ_DB(fp->rx_coal_usec, fp->rx_coal_count); + WRITE_ONCE(fp->cq_irq_db, db_val); + + rxqs = rtnl_dereference(fp->rxqs); + if (!rxqs) + return 0; + + for (i = 0; i < netdev->real_num_rx_queues; i++) + WRITE_ONCE(rxqs[i]->irq_db_val, db_val); + + db_val = FUN_IRQ_SQ_DB(fp->tx_coal_usec, fp->tx_coal_count); + for (i = 0; i < netdev->real_num_tx_queues; i++) + WRITE_ONCE(fp->txqs[i]->irq_db_val, db_val); + + return 0; +} + +static void fun_get_channels(struct net_device *netdev, + struct ethtool_channels *chan) +{ + chan->max_rx = netdev->num_rx_queues; + chan->rx_count = netdev->real_num_rx_queues; + + chan->max_tx = netdev->num_tx_queues; + chan->tx_count = netdev->real_num_tx_queues; +} + +static int fun_set_channels(struct net_device *netdev, + struct ethtool_channels *chan) +{ + if (!chan->tx_count || !chan->rx_count) + return -EINVAL; + + if (chan->tx_count == netdev->real_num_tx_queues && + chan->rx_count == netdev->real_num_rx_queues) + return 0; + + if (netif_running(netdev)) + return fun_change_num_queues(netdev, chan->tx_count, + chan->rx_count); + + fun_set_ring_count(netdev, chan->tx_count, chan->rx_count); + return 0; +} + +static void fun_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kring, + struct netlink_ext_ack *extack) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + unsigned int max_depth = fp->fdev->q_depth; + + /* We size CQs to be twice the RQ depth so max RQ depth is half the + * max queue depth. + */ + ring->rx_max_pending = max_depth / 2; + ring->tx_max_pending = max_depth; + + ring->rx_pending = fp->rq_depth; + ring->tx_pending = fp->sq_depth; + + kring->rx_buf_len = PAGE_SIZE; + kring->cqe_size = FUNETH_CQE_SIZE; +} + +static int fun_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kring, + struct netlink_ext_ack *extack) +{ + struct funeth_priv *fp = netdev_priv(netdev); + int rc; + + if (ring->rx_mini_pending || ring->rx_jumbo_pending) + return -EINVAL; + + /* queue depths must be powers-of-2 */ + if (!is_power_of_2(ring->rx_pending) || + !is_power_of_2(ring->tx_pending)) + return -EINVAL; + + if (ring->rx_pending < FUNETH_MIN_QDEPTH || + ring->tx_pending < FUNETH_MIN_QDEPTH) + return -EINVAL; + + if (fp->sq_depth == ring->tx_pending && + fp->rq_depth == ring->rx_pending) + return 0; + + if (netif_running(netdev)) { + struct fun_qset req = { + .cq_depth = 2 * ring->rx_pending, + .rq_depth = ring->rx_pending, + .sq_depth = ring->tx_pending + }; + + rc = fun_replace_queues(netdev, &req, extack); + if (rc) + return rc; + } + + fp->sq_depth = ring->tx_pending; + fp->rq_depth = ring->rx_pending; + fp->cq_depth = 2 * fp->rq_depth; + return 0; +} + +static int fun_get_sset_count(struct net_device *dev, int sset) +{ + const struct funeth_priv *fp = netdev_priv(dev); + int n; + + switch (sset) { + case ETH_SS_STATS: + n = (dev->real_num_tx_queues + 1) * ARRAY_SIZE(txq_stat_names) + + (dev->real_num_rx_queues + 1) * ARRAY_SIZE(rxq_stat_names) + + (fp->num_xdpqs + 1) * ARRAY_SIZE(xdpq_stat_names) + + ARRAY_SIZE(tls_stat_names); + if (fp->port_caps & FUN_PORT_CAP_STATS) { + n += ARRAY_SIZE(mac_tx_stat_names) + + ARRAY_SIZE(mac_rx_stat_names); + } + return n; + default: + break; + } + return 0; +} + +static void fun_get_strings(struct net_device *netdev, u32 sset, u8 *data) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + unsigned int i, j; + u8 *p = data; + + switch (sset) { + case ETH_SS_STATS: + if (fp->port_caps & FUN_PORT_CAP_STATS) { + memcpy(p, mac_tx_stat_names, sizeof(mac_tx_stat_names)); + p += sizeof(mac_tx_stat_names); + memcpy(p, mac_rx_stat_names, sizeof(mac_rx_stat_names)); + p += sizeof(mac_rx_stat_names); + } + + for (i = 0; i < netdev->real_num_tx_queues; i++) { + for (j = 0; j < ARRAY_SIZE(txq_stat_names); j++) + ethtool_sprintf(&p, "%s[%u]", txq_stat_names[j], + i); + } + for (j = 0; j < ARRAY_SIZE(txq_stat_names); j++) + ethtool_sprintf(&p, txq_stat_names[j]); + + for (i = 0; i < fp->num_xdpqs; i++) { + for (j = 0; j < ARRAY_SIZE(xdpq_stat_names); j++) + ethtool_sprintf(&p, "%s[%u]", + xdpq_stat_names[j], i); + } + for (j = 0; j < ARRAY_SIZE(xdpq_stat_names); j++) + ethtool_sprintf(&p, xdpq_stat_names[j]); + + for (i = 0; i < netdev->real_num_rx_queues; i++) { + for (j = 0; j < ARRAY_SIZE(rxq_stat_names); j++) + ethtool_sprintf(&p, "%s[%u]", rxq_stat_names[j], + i); + } + for (j = 0; j < ARRAY_SIZE(rxq_stat_names); j++) + ethtool_sprintf(&p, rxq_stat_names[j]); + + for (j = 0; j < ARRAY_SIZE(tls_stat_names); j++) + ethtool_sprintf(&p, tls_stat_names[j]); + break; + default: + break; + } +} + +static u64 *get_mac_stats(const struct funeth_priv *fp, u64 *data) +{ +#define TX_STAT(s) \ + *data++ = be64_to_cpu(fp->stats[PORT_MAC_RX_STATS_MAX + PORT_MAC_TX_##s]) + + TX_STAT(etherStatsOctets); + TX_STAT(etherStatsPkts); + TX_STAT(VLANTransmittedOK); + TX_STAT(ifOutUcastPkts); + TX_STAT(ifOutMulticastPkts); + TX_STAT(ifOutBroadcastPkts); + TX_STAT(ifOutErrors); + TX_STAT(CBFCPAUSEFramesTransmitted_0); + TX_STAT(CBFCPAUSEFramesTransmitted_1); + TX_STAT(CBFCPAUSEFramesTransmitted_2); + TX_STAT(CBFCPAUSEFramesTransmitted_3); + TX_STAT(CBFCPAUSEFramesTransmitted_4); + TX_STAT(CBFCPAUSEFramesTransmitted_5); + TX_STAT(CBFCPAUSEFramesTransmitted_6); + TX_STAT(CBFCPAUSEFramesTransmitted_7); + TX_STAT(CBFCPAUSEFramesTransmitted_8); + TX_STAT(CBFCPAUSEFramesTransmitted_9); + TX_STAT(CBFCPAUSEFramesTransmitted_10); + TX_STAT(CBFCPAUSEFramesTransmitted_11); + TX_STAT(CBFCPAUSEFramesTransmitted_12); + TX_STAT(CBFCPAUSEFramesTransmitted_13); + TX_STAT(CBFCPAUSEFramesTransmitted_14); + TX_STAT(CBFCPAUSEFramesTransmitted_15); + +#define RX_STAT(s) *data++ = be64_to_cpu(fp->stats[PORT_MAC_RX_##s]) + + RX_STAT(etherStatsOctets); + RX_STAT(etherStatsPkts); + RX_STAT(VLANReceivedOK); + RX_STAT(ifInUcastPkts); + RX_STAT(ifInMulticastPkts); + RX_STAT(ifInBroadcastPkts); + RX_STAT(etherStatsDropEvents); + RX_STAT(ifInErrors); + RX_STAT(aAlignmentErrors); + RX_STAT(CBFCPAUSEFramesReceived_0); + RX_STAT(CBFCPAUSEFramesReceived_1); + RX_STAT(CBFCPAUSEFramesReceived_2); + RX_STAT(CBFCPAUSEFramesReceived_3); + RX_STAT(CBFCPAUSEFramesReceived_4); + RX_STAT(CBFCPAUSEFramesReceived_5); + RX_STAT(CBFCPAUSEFramesReceived_6); + RX_STAT(CBFCPAUSEFramesReceived_7); + RX_STAT(CBFCPAUSEFramesReceived_8); + RX_STAT(CBFCPAUSEFramesReceived_9); + RX_STAT(CBFCPAUSEFramesReceived_10); + RX_STAT(CBFCPAUSEFramesReceived_11); + RX_STAT(CBFCPAUSEFramesReceived_12); + RX_STAT(CBFCPAUSEFramesReceived_13); + RX_STAT(CBFCPAUSEFramesReceived_14); + RX_STAT(CBFCPAUSEFramesReceived_15); + + return data; + +#undef TX_STAT +#undef RX_STAT +} + +static void fun_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + struct funeth_txq_stats txs; + struct funeth_rxq_stats rxs; + struct funeth_txq **xdpqs; + struct funeth_rxq **rxqs; + unsigned int i, start; + u64 *totals, *tot; + + if (fp->port_caps & FUN_PORT_CAP_STATS) + data = get_mac_stats(fp, data); + + rxqs = rtnl_dereference(fp->rxqs); + if (!rxqs) + return; + +#define ADD_STAT(cnt) do { \ + *data = (cnt); *tot++ += *data++; \ +} while (0) + + /* Tx queues */ + totals = data + netdev->real_num_tx_queues * ARRAY_SIZE(txq_stat_names); + + for (i = 0; i < netdev->real_num_tx_queues; i++) { + tot = totals; + + FUN_QSTAT_READ(fp->txqs[i], start, txs); + + ADD_STAT(txs.tx_pkts); + ADD_STAT(txs.tx_bytes); + ADD_STAT(txs.tx_cso); + ADD_STAT(txs.tx_tso); + ADD_STAT(txs.tx_encap_tso); + ADD_STAT(txs.tx_more); + ADD_STAT(txs.tx_nstops); + ADD_STAT(txs.tx_nrestarts); + ADD_STAT(txs.tx_map_err); + ADD_STAT(txs.tx_tls_pkts); + ADD_STAT(txs.tx_tls_bytes); + ADD_STAT(txs.tx_tls_fallback); + ADD_STAT(txs.tx_tls_drops); + } + data += ARRAY_SIZE(txq_stat_names); + + /* XDP Tx queues */ + xdpqs = rtnl_dereference(fp->xdpqs); + totals = data + fp->num_xdpqs * ARRAY_SIZE(xdpq_stat_names); + + for (i = 0; i < fp->num_xdpqs; i++) { + tot = totals; + + FUN_QSTAT_READ(xdpqs[i], start, txs); + + ADD_STAT(txs.tx_pkts); + ADD_STAT(txs.tx_bytes); + ADD_STAT(txs.tx_xdp_full); + ADD_STAT(txs.tx_map_err); + } + data += ARRAY_SIZE(xdpq_stat_names); + + /* Rx queues */ + totals = data + netdev->real_num_rx_queues * ARRAY_SIZE(rxq_stat_names); + + for (i = 0; i < netdev->real_num_rx_queues; i++) { + tot = totals; + + FUN_QSTAT_READ(rxqs[i], start, rxs); + + ADD_STAT(rxs.rx_pkts); + ADD_STAT(rxs.rx_bytes); + ADD_STAT(rxs.rx_cso); + ADD_STAT(rxs.gro_pkts); + ADD_STAT(rxs.gro_merged); + ADD_STAT(rxs.xdp_tx); + ADD_STAT(rxs.xdp_redir); + ADD_STAT(rxs.xdp_drops); + ADD_STAT(rxs.rx_bufs); + ADD_STAT(rxs.rx_page_alloc); + ADD_STAT(rxs.rx_mem_drops + rxs.xdp_err); + ADD_STAT(rxs.rx_budget); + ADD_STAT(rxs.rx_map_err); + } + data += ARRAY_SIZE(rxq_stat_names); +#undef ADD_STAT + + *data++ = atomic64_read(&fp->tx_tls_add); + *data++ = atomic64_read(&fp->tx_tls_del); + *data++ = atomic64_read(&fp->tx_tls_resync); +} + +#define RX_STAT(fp, s) be64_to_cpu((fp)->stats[PORT_MAC_RX_##s]) +#define TX_STAT(fp, s) \ + be64_to_cpu((fp)->stats[PORT_MAC_RX_STATS_MAX + PORT_MAC_TX_##s]) +#define FEC_STAT(fp, s) \ + be64_to_cpu((fp)->stats[PORT_MAC_RX_STATS_MAX + \ + PORT_MAC_TX_STATS_MAX + PORT_MAC_FEC_##s]) + +static void fun_get_pause_stats(struct net_device *netdev, + struct ethtool_pause_stats *stats) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + + if (!(fp->port_caps & FUN_PORT_CAP_STATS)) + return; + + stats->tx_pause_frames = TX_STAT(fp, aPAUSEMACCtrlFramesTransmitted); + stats->rx_pause_frames = RX_STAT(fp, aPAUSEMACCtrlFramesReceived); +} + +static void fun_get_802_3_stats(struct net_device *netdev, + struct ethtool_eth_mac_stats *stats) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + + if (!(fp->port_caps & FUN_PORT_CAP_STATS)) + return; + + stats->FramesTransmittedOK = TX_STAT(fp, aFramesTransmittedOK); + stats->FramesReceivedOK = RX_STAT(fp, aFramesReceivedOK); + stats->FrameCheckSequenceErrors = RX_STAT(fp, aFrameCheckSequenceErrors); + stats->OctetsTransmittedOK = TX_STAT(fp, OctetsTransmittedOK); + stats->OctetsReceivedOK = RX_STAT(fp, OctetsReceivedOK); + stats->InRangeLengthErrors = RX_STAT(fp, aInRangeLengthErrors); + stats->FrameTooLongErrors = RX_STAT(fp, aFrameTooLongErrors); +} + +static void fun_get_802_3_ctrl_stats(struct net_device *netdev, + struct ethtool_eth_ctrl_stats *stats) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + + if (!(fp->port_caps & FUN_PORT_CAP_STATS)) + return; + + stats->MACControlFramesTransmitted = TX_STAT(fp, MACControlFramesTransmitted); + stats->MACControlFramesReceived = RX_STAT(fp, MACControlFramesReceived); +} + +static void fun_get_rmon_stats(struct net_device *netdev, + struct ethtool_rmon_stats *stats, + const struct ethtool_rmon_hist_range **ranges) +{ + static const struct ethtool_rmon_hist_range rmon_ranges[] = { + { 64, 64 }, + { 65, 127 }, + { 128, 255 }, + { 256, 511 }, + { 512, 1023 }, + { 1024, 1518 }, + { 1519, 32767 }, + {} + }; + + const struct funeth_priv *fp = netdev_priv(netdev); + + if (!(fp->port_caps & FUN_PORT_CAP_STATS)) + return; + + stats->undersize_pkts = RX_STAT(fp, etherStatsUndersizePkts); + stats->oversize_pkts = RX_STAT(fp, etherStatsOversizePkts); + stats->fragments = RX_STAT(fp, etherStatsFragments); + stats->jabbers = RX_STAT(fp, etherStatsJabbers); + + stats->hist[0] = RX_STAT(fp, etherStatsPkts64Octets); + stats->hist[1] = RX_STAT(fp, etherStatsPkts65to127Octets); + stats->hist[2] = RX_STAT(fp, etherStatsPkts128to255Octets); + stats->hist[3] = RX_STAT(fp, etherStatsPkts256to511Octets); + stats->hist[4] = RX_STAT(fp, etherStatsPkts512to1023Octets); + stats->hist[5] = RX_STAT(fp, etherStatsPkts1024to1518Octets); + stats->hist[6] = RX_STAT(fp, etherStatsPkts1519toMaxOctets); + + stats->hist_tx[0] = TX_STAT(fp, etherStatsPkts64Octets); + stats->hist_tx[1] = TX_STAT(fp, etherStatsPkts65to127Octets); + stats->hist_tx[2] = TX_STAT(fp, etherStatsPkts128to255Octets); + stats->hist_tx[3] = TX_STAT(fp, etherStatsPkts256to511Octets); + stats->hist_tx[4] = TX_STAT(fp, etherStatsPkts512to1023Octets); + stats->hist_tx[5] = TX_STAT(fp, etherStatsPkts1024to1518Octets); + stats->hist_tx[6] = TX_STAT(fp, etherStatsPkts1519toMaxOctets); + + *ranges = rmon_ranges; +} + +static void fun_get_fec_stats(struct net_device *netdev, + struct ethtool_fec_stats *stats) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + + if (!(fp->port_caps & FUN_PORT_CAP_STATS)) + return; + + stats->corrected_blocks.total = FEC_STAT(fp, Correctable); + stats->uncorrectable_blocks.total = FEC_STAT(fp, Uncorrectable); +} + +#undef RX_STAT +#undef TX_STAT +#undef FEC_STAT + +static int fun_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = netdev->real_num_rx_queues; + return 0; + default: + break; + } + return -EOPNOTSUPP; +} + +static int fun_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info) +{ + return 0; +} + +static u32 fun_get_rxfh_indir_size(struct net_device *netdev) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + + return fp->indir_table_nentries; +} + +static u32 fun_get_rxfh_key_size(struct net_device *netdev) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + + return sizeof(fp->rss_key); +} + +static int fun_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, + u8 *hfunc) +{ + const struct funeth_priv *fp = netdev_priv(netdev); + + if (!fp->rss_cfg) + return -EOPNOTSUPP; + + if (indir) + memcpy(indir, fp->indir_table, + sizeof(u32) * fp->indir_table_nentries); + + if (key) + memcpy(key, fp->rss_key, sizeof(fp->rss_key)); + + if (hfunc) + *hfunc = fp->hash_algo == FUN_ETH_RSS_ALG_TOEPLITZ ? + ETH_RSS_HASH_TOP : ETH_RSS_HASH_CRC32; + + return 0; +} + +static int fun_set_rxfh(struct net_device *netdev, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct funeth_priv *fp = netdev_priv(netdev); + const u32 *rss_indir = indir ? indir : fp->indir_table; + const u8 *rss_key = key ? key : fp->rss_key; + enum fun_eth_hash_alg algo; + + if (!fp->rss_cfg) + return -EOPNOTSUPP; + + if (hfunc == ETH_RSS_HASH_NO_CHANGE) + algo = fp->hash_algo; + else if (hfunc == ETH_RSS_HASH_CRC32) + algo = FUN_ETH_RSS_ALG_CRC32; + else if (hfunc == ETH_RSS_HASH_TOP) + algo = FUN_ETH_RSS_ALG_TOEPLITZ; + else + return -EINVAL; + + /* If the port is enabled try to reconfigure RSS and keep the new + * settings if successful. If it is down we update the RSS settings + * and apply them at the next UP time. + */ + if (netif_running(netdev)) { + int rc = fun_config_rss(netdev, algo, rss_key, rss_indir, + FUN_ADMIN_SUBOP_MODIFY); + if (rc) + return rc; + } + + fp->hash_algo = algo; + if (key) + memcpy(fp->rss_key, key, sizeof(fp->rss_key)); + if (indir) + memcpy(fp->indir_table, indir, + sizeof(u32) * fp->indir_table_nentries); + return 0; +} + +static int fun_get_ts_info(struct net_device *netdev, + struct ethtool_ts_info *info) +{ + info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->phc_index = -1; + info->tx_types = BIT(HWTSTAMP_TX_OFF); + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL); + return 0; +} + +static unsigned int to_ethtool_fec(unsigned int fun_fec) +{ + unsigned int fec = 0; + + if (fun_fec == FUN_PORT_FEC_NA) + fec |= ETHTOOL_FEC_NONE; + if (fun_fec & FUN_PORT_FEC_OFF) + fec |= ETHTOOL_FEC_OFF; + if (fun_fec & FUN_PORT_FEC_RS) + fec |= ETHTOOL_FEC_RS; + if (fun_fec & FUN_PORT_FEC_FC) + fec |= ETHTOOL_FEC_BASER; + if (fun_fec & FUN_PORT_FEC_AUTO) + fec |= ETHTOOL_FEC_AUTO; + return fec; +} + +static int fun_get_fecparam(struct net_device *netdev, + struct ethtool_fecparam *fec) +{ + struct funeth_priv *fp = netdev_priv(netdev); + u64 fec_data; + int rc; + + rc = fun_port_read_cmd(fp, FUN_ADMIN_PORT_KEY_FEC, &fec_data); + if (rc) + return rc; + + fec->active_fec = to_ethtool_fec(fec_data & 0xff); + fec->fec = to_ethtool_fec(fec_data >> 8); + return 0; +} + +static int fun_set_fecparam(struct net_device *netdev, + struct ethtool_fecparam *fec) +{ + struct funeth_priv *fp = netdev_priv(netdev); + u64 fec_mode; + + switch (fec->fec) { + case ETHTOOL_FEC_AUTO: + fec_mode = FUN_PORT_FEC_AUTO; + break; + case ETHTOOL_FEC_OFF: + if (!(fp->port_caps & FUN_PORT_CAP_FEC_NONE)) + return -EINVAL; + fec_mode = FUN_PORT_FEC_OFF; + break; + case ETHTOOL_FEC_BASER: + if (!(fp->port_caps & FUN_PORT_CAP_FEC_FC)) + return -EINVAL; + fec_mode = FUN_PORT_FEC_FC; + break; + case ETHTOOL_FEC_RS: + if (!(fp->port_caps & FUN_PORT_CAP_FEC_RS)) + return -EINVAL; + fec_mode = FUN_PORT_FEC_RS; + break; + default: + return -EINVAL; + } + + return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_FEC, fec_mode); +} + +static const struct ethtool_ops fun_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | + ETHTOOL_COALESCE_MAX_FRAMES, + .get_link_ksettings = fun_get_link_ksettings, + .set_link_ksettings = fun_set_link_ksettings, + .set_phys_id = fun_set_phys_id, + .get_drvinfo = fun_get_drvinfo, + .get_msglevel = fun_get_msglevel, + .set_msglevel = fun_set_msglevel, + .get_regs_len = fun_get_regs_len, + .get_regs = fun_get_regs, + .get_link = ethtool_op_get_link, + .get_coalesce = fun_get_coalesce, + .set_coalesce = fun_set_coalesce, + .get_ts_info = fun_get_ts_info, + .get_ringparam = fun_get_ringparam, + .set_ringparam = fun_set_ringparam, + .get_sset_count = fun_get_sset_count, + .get_strings = fun_get_strings, + .get_ethtool_stats = fun_get_ethtool_stats, + .get_rxnfc = fun_get_rxnfc, + .set_rxnfc = fun_set_rxnfc, + .get_rxfh_indir_size = fun_get_rxfh_indir_size, + .get_rxfh_key_size = fun_get_rxfh_key_size, + .get_rxfh = fun_get_rxfh, + .set_rxfh = fun_set_rxfh, + .get_channels = fun_get_channels, + .set_channels = fun_set_channels, + .get_fecparam = fun_get_fecparam, + .set_fecparam = fun_set_fecparam, + .get_pauseparam = fun_get_pauseparam, + .set_pauseparam = fun_set_pauseparam, + .nway_reset = fun_restart_an, + .get_pause_stats = fun_get_pause_stats, + .get_fec_stats = fun_get_fec_stats, + .get_eth_mac_stats = fun_get_802_3_stats, + .get_eth_ctrl_stats = fun_get_802_3_ctrl_stats, + .get_rmon_stats = fun_get_rmon_stats, +}; + +void fun_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &fun_ethtool_ops; +} diff --git a/drivers/net/ethernet/fungible/funeth/funeth_ktls.c b/drivers/net/ethernet/fungible/funeth/funeth_ktls.c new file mode 100644 index 000000000000..f871def70d70 --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/funeth_ktls.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +#include "funeth.h" +#include "funeth_ktls.h" + +static int fun_admin_ktls_create(struct funeth_priv *fp, unsigned int id) +{ + struct fun_admin_ktls_create_req req = { + .common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_KTLS, + sizeof(req)), + .subop = FUN_ADMIN_SUBOP_CREATE, + .id = cpu_to_be32(id), + }; + + return fun_submit_admin_sync_cmd(fp->fdev, &req.common, NULL, 0, 0); +} + +static int fun_ktls_add(struct net_device *netdev, struct sock *sk, + enum tls_offload_ctx_dir direction, + struct tls_crypto_info *crypto_info, + u32 start_offload_tcp_sn) +{ + struct funeth_priv *fp = netdev_priv(netdev); + struct fun_admin_ktls_modify_req req = { + .common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_KTLS, + sizeof(req)), + .subop = FUN_ADMIN_SUBOP_MODIFY, + .id = cpu_to_be32(fp->ktls_id), + .tcp_seq = cpu_to_be32(start_offload_tcp_sn), + }; + struct fun_admin_ktls_modify_rsp rsp; + struct fun_ktls_tx_ctx *tx_ctx; + int rc; + + if (direction != TLS_OFFLOAD_CTX_DIR_TX) + return -EOPNOTSUPP; + + if (crypto_info->version == TLS_1_2_VERSION) + req.version = FUN_KTLS_TLSV2; + else + return -EOPNOTSUPP; + + switch (crypto_info->cipher_type) { + case TLS_CIPHER_AES_GCM_128: { + struct tls12_crypto_info_aes_gcm_128 *c = (void *)crypto_info; + + req.cipher = FUN_KTLS_CIPHER_AES_GCM_128; + memcpy(req.key, c->key, sizeof(c->key)); + memcpy(req.iv, c->iv, sizeof(c->iv)); + memcpy(req.salt, c->salt, sizeof(c->salt)); + memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq)); + break; + } + default: + return -EOPNOTSUPP; + } + + rc = fun_submit_admin_sync_cmd(fp->fdev, &req.common, &rsp, + sizeof(rsp), 0); + memzero_explicit(&req, sizeof(req)); + if (rc) + return rc; + + tx_ctx = tls_driver_ctx(sk, direction); + tx_ctx->tlsid = rsp.tlsid; + tx_ctx->next_seq = start_offload_tcp_sn; + atomic64_inc(&fp->tx_tls_add); + return 0; +} + +static void fun_ktls_del(struct net_device *netdev, + struct tls_context *tls_ctx, + enum tls_offload_ctx_dir direction) +{ + struct funeth_priv *fp = netdev_priv(netdev); + struct fun_admin_ktls_modify_req req; + struct fun_ktls_tx_ctx *tx_ctx; + + if (direction != TLS_OFFLOAD_CTX_DIR_TX) + return; + + tx_ctx = __tls_driver_ctx(tls_ctx, direction); + + req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_KTLS, + offsetof(struct fun_admin_ktls_modify_req, tcp_seq)); + req.subop = FUN_ADMIN_SUBOP_MODIFY; + req.flags = cpu_to_be16(FUN_KTLS_MODIFY_REMOVE); + req.id = cpu_to_be32(fp->ktls_id); + req.tlsid = tx_ctx->tlsid; + + fun_submit_admin_sync_cmd(fp->fdev, &req.common, NULL, 0, 0); + atomic64_inc(&fp->tx_tls_del); +} + +static int fun_ktls_resync(struct net_device *netdev, struct sock *sk, u32 seq, + u8 *rcd_sn, enum tls_offload_ctx_dir direction) +{ + struct funeth_priv *fp = netdev_priv(netdev); + struct fun_admin_ktls_modify_req req; + struct fun_ktls_tx_ctx *tx_ctx; + int rc; + + if (direction != TLS_OFFLOAD_CTX_DIR_TX) + return -EOPNOTSUPP; + + tx_ctx = tls_driver_ctx(sk, direction); + + req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_KTLS, + offsetof(struct fun_admin_ktls_modify_req, key)); + req.subop = FUN_ADMIN_SUBOP_MODIFY; + req.flags = 0; + req.id = cpu_to_be32(fp->ktls_id); + req.tlsid = tx_ctx->tlsid; + req.tcp_seq = cpu_to_be32(seq); + req.version = 0; + req.cipher = 0; + memcpy(req.record_seq, rcd_sn, sizeof(req.record_seq)); + + atomic64_inc(&fp->tx_tls_resync); + rc = fun_submit_admin_sync_cmd(fp->fdev, &req.common, NULL, 0, 0); + if (!rc) + tx_ctx->next_seq = seq; + return rc; +} + +static const struct tlsdev_ops fun_ktls_ops = { + .tls_dev_add = fun_ktls_add, + .tls_dev_del = fun_ktls_del, + .tls_dev_resync = fun_ktls_resync, +}; + +int fun_ktls_init(struct net_device *netdev) +{ + struct funeth_priv *fp = netdev_priv(netdev); + int rc; + + rc = fun_admin_ktls_create(fp, netdev->dev_port); + if (rc) + return rc; + + fp->ktls_id = netdev->dev_port; + netdev->tlsdev_ops = &fun_ktls_ops; + netdev->hw_features |= NETIF_F_HW_TLS_TX; + netdev->features |= NETIF_F_HW_TLS_TX; + return 0; +} + +void fun_ktls_cleanup(struct funeth_priv *fp) +{ + if (fp->ktls_id == FUN_HCI_ID_INVALID) + return; + + fun_res_destroy(fp->fdev, FUN_ADMIN_OP_KTLS, 0, fp->ktls_id); + fp->ktls_id = FUN_HCI_ID_INVALID; +} diff --git a/drivers/net/ethernet/fungible/funeth/funeth_ktls.h b/drivers/net/ethernet/fungible/funeth/funeth_ktls.h new file mode 100644 index 000000000000..9d6f2141a959 --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/funeth_ktls.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +#ifndef _FUN_KTLS_H +#define _FUN_KTLS_H + +#include + +struct funeth_priv; + +struct fun_ktls_tx_ctx { + __be64 tlsid; + u32 next_seq; +}; + +#if IS_ENABLED(CONFIG_TLS_DEVICE) +int fun_ktls_init(struct net_device *netdev); +void fun_ktls_cleanup(struct funeth_priv *fp); + +#else + +static inline void fun_ktls_init(struct net_device *netdev) +{ +} + +static inline void fun_ktls_cleanup(struct funeth_priv *fp) +{ +} +#endif + +#endif /* _FUN_KTLS_H */ diff --git a/drivers/net/ethernet/fungible/funeth/funeth_main.c b/drivers/net/ethernet/fungible/funeth/funeth_main.c new file mode 100644 index 000000000000..67dd02ed1fa3 --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/funeth_main.c @@ -0,0 +1,2091 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "funeth.h" +#include "funeth_devlink.h" +#include "funeth_ktls.h" +#include "fun_port.h" +#include "fun_queue.h" +#include "funeth_txrx.h" + +#define ADMIN_SQ_DEPTH 32 +#define ADMIN_CQ_DEPTH 64 +#define ADMIN_RQ_DEPTH 16 + +/* Default number of Tx/Rx queues. */ +#define FUN_DFLT_QUEUES 16U + +enum { + FUN_SERV_RES_CHANGE = FUN_SERV_FIRST_AVAIL, + FUN_SERV_DEL_PORTS, +}; + +static const struct pci_device_id funeth_id_table[] = { + { PCI_VDEVICE(FUNGIBLE, 0x0101) }, + { PCI_VDEVICE(FUNGIBLE, 0x0181) }, + { 0, } +}; + +/* Issue a port write admin command with @n key/value pairs. */ +static int fun_port_write_cmds(struct funeth_priv *fp, unsigned int n, + const int *keys, const u64 *data) +{ + unsigned int cmd_size, i; + union { + struct fun_admin_port_req req; + struct fun_admin_port_rsp rsp; + u8 v[ADMIN_SQE_SIZE]; + } cmd; + + cmd_size = offsetof(struct fun_admin_port_req, u.write.write48) + + n * sizeof(struct fun_admin_write48_req); + if (cmd_size > sizeof(cmd) || cmd_size > ADMIN_RSP_MAX_LEN) + return -EINVAL; + + cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_PORT, + cmd_size); + cmd.req.u.write = + FUN_ADMIN_PORT_WRITE_REQ_INIT(FUN_ADMIN_SUBOP_WRITE, 0, + fp->netdev->dev_port); + for (i = 0; i < n; i++) + cmd.req.u.write.write48[i] = + FUN_ADMIN_WRITE48_REQ_INIT(keys[i], data[i]); + + return fun_submit_admin_sync_cmd(fp->fdev, &cmd.req.common, + &cmd.rsp, cmd_size, 0); +} + +int fun_port_write_cmd(struct funeth_priv *fp, int key, u64 data) +{ + return fun_port_write_cmds(fp, 1, &key, &data); +} + +/* Issue a port read admin command with @n key/value pairs. */ +static int fun_port_read_cmds(struct funeth_priv *fp, unsigned int n, + const int *keys, u64 *data) +{ + const struct fun_admin_read48_rsp *r48rsp; + unsigned int cmd_size, i; + int rc; + union { + struct fun_admin_port_req req; + struct fun_admin_port_rsp rsp; + u8 v[ADMIN_SQE_SIZE]; + } cmd; + + cmd_size = offsetof(struct fun_admin_port_req, u.read.read48) + + n * sizeof(struct fun_admin_read48_req); + if (cmd_size > sizeof(cmd) || cmd_size > ADMIN_RSP_MAX_LEN) + return -EINVAL; + + cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_PORT, + cmd_size); + cmd.req.u.read = + FUN_ADMIN_PORT_READ_REQ_INIT(FUN_ADMIN_SUBOP_READ, 0, + fp->netdev->dev_port); + for (i = 0; i < n; i++) + cmd.req.u.read.read48[i] = FUN_ADMIN_READ48_REQ_INIT(keys[i]); + + rc = fun_submit_admin_sync_cmd(fp->fdev, &cmd.req.common, + &cmd.rsp, cmd_size, 0); + if (rc) + return rc; + + for (r48rsp = cmd.rsp.u.read.read48, i = 0; i < n; i++, r48rsp++) { + data[i] = FUN_ADMIN_READ48_RSP_DATA_G(r48rsp->key_to_data); + dev_dbg(fp->fdev->dev, + "port_read_rsp lport=%u (key_to_data=0x%llx) key=%d data:%lld retval:%lld", + fp->lport, r48rsp->key_to_data, keys[i], data[i], + FUN_ADMIN_READ48_RSP_RET_G(r48rsp->key_to_data)); + } + return 0; +} + +int fun_port_read_cmd(struct funeth_priv *fp, int key, u64 *data) +{ + return fun_port_read_cmds(fp, 1, &key, data); +} + +static void fun_report_link(struct net_device *netdev) +{ + if (netif_carrier_ok(netdev)) { + const struct funeth_priv *fp = netdev_priv(netdev); + const char *fec = "", *pause = ""; + int speed = fp->link_speed; + char unit = 'M'; + + if (fp->link_speed >= SPEED_1000) { + speed /= 1000; + unit = 'G'; + } + + if (fp->active_fec & FUN_PORT_FEC_RS) + fec = ", RS-FEC"; + else if (fp->active_fec & FUN_PORT_FEC_FC) + fec = ", BASER-FEC"; + + if ((fp->active_fc & FUN_PORT_CAP_PAUSE_MASK) == FUN_PORT_CAP_PAUSE_MASK) + pause = ", Tx/Rx PAUSE"; + else if (fp->active_fc & FUN_PORT_CAP_RX_PAUSE) + pause = ", Rx PAUSE"; + else if (fp->active_fc & FUN_PORT_CAP_TX_PAUSE) + pause = ", Tx PAUSE"; + + netdev_info(netdev, "Link up at %d %cb/s full-duplex%s%s\n", + speed, unit, pause, fec); + } else { + netdev_info(netdev, "Link down\n"); + } +} + +static int fun_adi_write(struct fun_dev *fdev, enum fun_admin_adi_attr attr, + unsigned int adi_id, const struct fun_adi_param *param) +{ + struct fun_admin_adi_req req = { + .common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_ADI, + sizeof(req)), + .u.write.subop = FUN_ADMIN_SUBOP_WRITE, + .u.write.attribute = attr, + .u.write.id = cpu_to_be32(adi_id), + .u.write.param = *param + }; + + return fun_submit_admin_sync_cmd(fdev, &req.common, NULL, 0, 0); +} + +/* Configure RSS for the given port. @op determines whether a new RSS context + * is to be created or whether an existing one should be reconfigured. The + * remaining parameters specify the hashing algorithm, key, and indirection + * table. + * + * This initiates packet delivery to the Rx queues set in the indirection + * table. + */ +int fun_config_rss(struct net_device *dev, int algo, const u8 *key, + const u32 *qtable, u8 op) +{ + struct funeth_priv *fp = netdev_priv(dev); + unsigned int table_len = fp->indir_table_nentries; + unsigned int len = FUN_ETH_RSS_MAX_KEY_SIZE + sizeof(u32) * table_len; + struct funeth_rxq **rxqs = rtnl_dereference(fp->rxqs); + union { + struct { + struct fun_admin_rss_req req; + struct fun_dataop_gl gl; + }; + struct fun_admin_generic_create_rsp rsp; + } cmd; + __be32 *indir_tab; + u16 flags; + int rc; + + if (op != FUN_ADMIN_SUBOP_CREATE && fp->rss_hw_id == FUN_HCI_ID_INVALID) + return -EINVAL; + + flags = op == FUN_ADMIN_SUBOP_CREATE ? + FUN_ADMIN_RES_CREATE_FLAG_ALLOCATOR : 0; + cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_RSS, + sizeof(cmd)); + cmd.req.u.create = + FUN_ADMIN_RSS_CREATE_REQ_INIT(op, flags, fp->rss_hw_id, + dev->dev_port, algo, + FUN_ETH_RSS_MAX_KEY_SIZE, + table_len, 0, + FUN_ETH_RSS_MAX_KEY_SIZE); + cmd.req.u.create.dataop = FUN_DATAOP_HDR_INIT(1, 0, 1, 0, len); + fun_dataop_gl_init(&cmd.gl, 0, 0, len, fp->rss_dma_addr); + + /* write the key and indirection table into the RSS DMA area */ + memcpy(fp->rss_cfg, key, FUN_ETH_RSS_MAX_KEY_SIZE); + indir_tab = fp->rss_cfg + FUN_ETH_RSS_MAX_KEY_SIZE; + for (rc = 0; rc < table_len; rc++) + *indir_tab++ = cpu_to_be32(rxqs[*qtable++]->hw_cqid); + + rc = fun_submit_admin_sync_cmd(fp->fdev, &cmd.req.common, + &cmd.rsp, sizeof(cmd.rsp), 0); + if (!rc && op == FUN_ADMIN_SUBOP_CREATE) + fp->rss_hw_id = be32_to_cpu(cmd.rsp.id); + return rc; +} + +/* Destroy the HW RSS conntext associated with the given port. This also stops + * all packet delivery to our Rx queues. + */ +static void fun_destroy_rss(struct funeth_priv *fp) +{ + if (fp->rss_hw_id != FUN_HCI_ID_INVALID) { + fun_res_destroy(fp->fdev, FUN_ADMIN_OP_RSS, 0, fp->rss_hw_id); + fp->rss_hw_id = FUN_HCI_ID_INVALID; + } +} + +static void fun_irq_aff_notify(struct irq_affinity_notify *notify, + const cpumask_t *mask) +{ + struct fun_irq *p = container_of(notify, struct fun_irq, aff_notify); + + cpumask_copy(&p->affinity_mask, mask); +} + +static void fun_irq_aff_release(struct kref __always_unused *ref) +{ +} + +/* Allocate an IRQ structure, assign an MSI-X index and initial affinity to it, + * and add it to the IRQ XArray. + */ +static struct fun_irq *fun_alloc_qirq(struct funeth_priv *fp, unsigned int idx, + int node, unsigned int xa_idx_offset) +{ + struct fun_irq *irq; + int cpu, res; + + cpu = cpumask_local_spread(idx, node); + node = cpu_to_mem(cpu); + + irq = kzalloc_node(sizeof(*irq), GFP_KERNEL, node); + if (!irq) + return ERR_PTR(-ENOMEM); + + res = fun_reserve_irqs(fp->fdev, 1, &irq->irq_idx); + if (res != 1) + goto free_irq; + + res = xa_insert(&fp->irqs, idx + xa_idx_offset, irq, GFP_KERNEL); + if (res) + goto release_irq; + + irq->irq = pci_irq_vector(fp->pdev, irq->irq_idx); + cpumask_set_cpu(cpu, &irq->affinity_mask); + irq->aff_notify.notify = fun_irq_aff_notify; + irq->aff_notify.release = fun_irq_aff_release; + irq->state = FUN_IRQ_INIT; + return irq; + +release_irq: + fun_release_irqs(fp->fdev, 1, &irq->irq_idx); +free_irq: + kfree(irq); + return ERR_PTR(res); +} + +static void fun_free_qirq(struct funeth_priv *fp, struct fun_irq *irq) +{ + netif_napi_del(&irq->napi); + fun_release_irqs(fp->fdev, 1, &irq->irq_idx); + kfree(irq); +} + +/* Release the IRQs reserved for Tx/Rx queues that aren't being used. */ +static void fun_prune_queue_irqs(struct net_device *dev) +{ + struct funeth_priv *fp = netdev_priv(dev); + unsigned int nreleased = 0; + struct fun_irq *irq; + unsigned long idx; + + xa_for_each(&fp->irqs, idx, irq) { + if (irq->txq || irq->rxq) /* skip those in use */ + continue; + + xa_erase(&fp->irqs, idx); + fun_free_qirq(fp, irq); + nreleased++; + if (idx < fp->rx_irq_ofst) + fp->num_tx_irqs--; + else + fp->num_rx_irqs--; + } + netif_info(fp, intr, dev, "Released %u queue IRQs\n", nreleased); +} + +/* Reserve IRQs, one per queue, to acommodate the requested queue numbers @ntx + * and @nrx. IRQs are added incrementally to those we already have. + * We hold on to allocated IRQs until garbage collection of unused IRQs is + * separately requested. + */ +static int fun_alloc_queue_irqs(struct net_device *dev, unsigned int ntx, + unsigned int nrx) +{ + struct funeth_priv *fp = netdev_priv(dev); + int node = dev_to_node(&fp->pdev->dev); + struct fun_irq *irq; + unsigned int i; + + for (i = fp->num_tx_irqs; i < ntx; i++) { + irq = fun_alloc_qirq(fp, i, node, 0); + if (IS_ERR(irq)) + return PTR_ERR(irq); + + fp->num_tx_irqs++; + netif_tx_napi_add(dev, &irq->napi, fun_txq_napi_poll, + NAPI_POLL_WEIGHT); + } + + for (i = fp->num_rx_irqs; i < nrx; i++) { + irq = fun_alloc_qirq(fp, i, node, fp->rx_irq_ofst); + if (IS_ERR(irq)) + return PTR_ERR(irq); + + fp->num_rx_irqs++; + netif_napi_add(dev, &irq->napi, fun_rxq_napi_poll, + NAPI_POLL_WEIGHT); + } + + netif_info(fp, intr, dev, "Reserved %u/%u IRQs for Tx/Rx queues\n", + ntx, nrx); + return 0; +} + +static void free_txqs(struct funeth_txq **txqs, unsigned int nqs, + unsigned int start, int state) +{ + unsigned int i; + + for (i = start; i < nqs && txqs[i]; i++) + txqs[i] = funeth_txq_free(txqs[i], state); +} + +static int alloc_txqs(struct net_device *dev, struct funeth_txq **txqs, + unsigned int nqs, unsigned int depth, unsigned int start, + int state) +{ + struct funeth_priv *fp = netdev_priv(dev); + unsigned int i; + int err; + + for (i = start; i < nqs; i++) { + err = funeth_txq_create(dev, i, depth, xa_load(&fp->irqs, i), + state, &txqs[i]); + if (err) { + free_txqs(txqs, nqs, start, FUN_QSTATE_DESTROYED); + return err; + } + } + return 0; +} + +static void free_rxqs(struct funeth_rxq **rxqs, unsigned int nqs, + unsigned int start, int state) +{ + unsigned int i; + + for (i = start; i < nqs && rxqs[i]; i++) + rxqs[i] = funeth_rxq_free(rxqs[i], state); +} + +static int alloc_rxqs(struct net_device *dev, struct funeth_rxq **rxqs, + unsigned int nqs, unsigned int ncqe, unsigned int nrqe, + unsigned int start, int state) +{ + struct funeth_priv *fp = netdev_priv(dev); + unsigned int i; + int err; + + for (i = start; i < nqs; i++) { + err = funeth_rxq_create(dev, i, ncqe, nrqe, + xa_load(&fp->irqs, i + fp->rx_irq_ofst), + state, &rxqs[i]); + if (err) { + free_rxqs(rxqs, nqs, start, FUN_QSTATE_DESTROYED); + return err; + } + } + return 0; +} + +static void free_xdpqs(struct funeth_txq **xdpqs, unsigned int nqs, + unsigned int start, int state) +{ + unsigned int i; + + for (i = start; i < nqs && xdpqs[i]; i++) + xdpqs[i] = funeth_txq_free(xdpqs[i], state); + + if (state == FUN_QSTATE_DESTROYED) + kfree(xdpqs); +} + +static struct funeth_txq **alloc_xdpqs(struct net_device *dev, unsigned int nqs, + unsigned int depth, unsigned int start, + int state) +{ + struct funeth_txq **xdpqs; + unsigned int i; + int err; + + xdpqs = kcalloc(nqs, sizeof(*xdpqs), GFP_KERNEL); + if (!xdpqs) + return ERR_PTR(-ENOMEM); + + for (i = start; i < nqs; i++) { + err = funeth_txq_create(dev, i, depth, NULL, state, &xdpqs[i]); + if (err) { + free_xdpqs(xdpqs, nqs, start, FUN_QSTATE_DESTROYED); + return ERR_PTR(err); + } + } + return xdpqs; +} + +static void fun_free_rings(struct net_device *netdev, struct fun_qset *qset) +{ + struct funeth_priv *fp = netdev_priv(netdev); + struct funeth_txq **xdpqs = qset->xdpqs; + struct funeth_rxq **rxqs = qset->rxqs; + + /* qset may not specify any queues to operate on. In that case the + * currently installed queues are implied. + */ + if (!rxqs) { + rxqs = rtnl_dereference(fp->rxqs); + xdpqs = rtnl_dereference(fp->xdpqs); + qset->txqs = fp->txqs; + qset->nrxqs = netdev->real_num_rx_queues; + qset->ntxqs = netdev->real_num_tx_queues; + qset->nxdpqs = fp->num_xdpqs; + } + if (!rxqs) + return; + + if (rxqs == rtnl_dereference(fp->rxqs)) { + rcu_assign_pointer(fp->rxqs, NULL); + rcu_assign_pointer(fp->xdpqs, NULL); + synchronize_net(); + fp->txqs = NULL; + } + + free_rxqs(rxqs, qset->nrxqs, qset->rxq_start, qset->state); + free_txqs(qset->txqs, qset->ntxqs, qset->txq_start, qset->state); + free_xdpqs(xdpqs, qset->nxdpqs, qset->xdpq_start, qset->state); + if (qset->state == FUN_QSTATE_DESTROYED) + kfree(rxqs); + + /* Tell the caller which queues were operated on. */ + qset->rxqs = rxqs; + qset->xdpqs = xdpqs; +} + +static int fun_alloc_rings(struct net_device *netdev, struct fun_qset *qset) +{ + struct funeth_txq **xdpqs = NULL, **txqs; + struct funeth_rxq **rxqs; + int err; + + err = fun_alloc_queue_irqs(netdev, qset->ntxqs, qset->nrxqs); + if (err) + return err; + + rxqs = kcalloc(qset->ntxqs + qset->nrxqs, sizeof(*rxqs), GFP_KERNEL); + if (!rxqs) + return -ENOMEM; + + if (qset->nxdpqs) { + xdpqs = alloc_xdpqs(netdev, qset->nxdpqs, qset->sq_depth, + qset->xdpq_start, qset->state); + if (IS_ERR(xdpqs)) { + err = PTR_ERR(xdpqs); + goto free_qvec; + } + } + + txqs = (struct funeth_txq **)&rxqs[qset->nrxqs]; + err = alloc_txqs(netdev, txqs, qset->ntxqs, qset->sq_depth, + qset->txq_start, qset->state); + if (err) + goto free_xdpqs; + + err = alloc_rxqs(netdev, rxqs, qset->nrxqs, qset->cq_depth, + qset->rq_depth, qset->rxq_start, qset->state); + if (err) + goto free_txqs; + + qset->rxqs = rxqs; + qset->txqs = txqs; + qset->xdpqs = xdpqs; + return 0; + +free_txqs: + free_txqs(txqs, qset->ntxqs, qset->txq_start, FUN_QSTATE_DESTROYED); +free_xdpqs: + free_xdpqs(xdpqs, qset->nxdpqs, qset->xdpq_start, FUN_QSTATE_DESTROYED); +free_qvec: + kfree(rxqs); + return err; +} + +/* Take queues to the next level. Presently this means creating them on the + * device. + */ +static int fun_advance_ring_state(struct net_device *dev, struct fun_qset *qset) +{ + struct funeth_priv *fp = netdev_priv(dev); + int i, err; + + for (i = 0; i < qset->nrxqs; i++) { + err = fun_rxq_create_dev(qset->rxqs[i], + xa_load(&fp->irqs, + i + fp->rx_irq_ofst)); + if (err) + goto out; + } + + for (i = 0; i < qset->ntxqs; i++) { + err = fun_txq_create_dev(qset->txqs[i], xa_load(&fp->irqs, i)); + if (err) + goto out; + } + + for (i = 0; i < qset->nxdpqs; i++) { + err = fun_txq_create_dev(qset->xdpqs[i], NULL); + if (err) + goto out; + } + + return 0; + +out: + fun_free_rings(dev, qset); + return err; +} + +static int fun_port_create(struct net_device *netdev) +{ + struct funeth_priv *fp = netdev_priv(netdev); + union { + struct fun_admin_port_req req; + struct fun_admin_port_rsp rsp; + } cmd; + int rc; + + if (fp->lport != INVALID_LPORT) + return 0; + + cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_PORT, + sizeof(cmd.req)); + cmd.req.u.create = + FUN_ADMIN_PORT_CREATE_REQ_INIT(FUN_ADMIN_SUBOP_CREATE, 0, + netdev->dev_port); + + rc = fun_submit_admin_sync_cmd(fp->fdev, &cmd.req.common, &cmd.rsp, + sizeof(cmd.rsp), 0); + + if (!rc) + fp->lport = be16_to_cpu(cmd.rsp.u.create.lport); + return rc; +} + +static int fun_port_destroy(struct net_device *netdev) +{ + struct funeth_priv *fp = netdev_priv(netdev); + + if (fp->lport == INVALID_LPORT) + return 0; + + fp->lport = INVALID_LPORT; + return fun_res_destroy(fp->fdev, FUN_ADMIN_OP_PORT, 0, + netdev->dev_port); +} + +static int fun_eth_create(struct funeth_priv *fp) +{ + union { + struct fun_admin_eth_req req; + struct fun_admin_generic_create_rsp rsp; + } cmd; + int rc; + + cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_ETH, + sizeof(cmd.req)); + cmd.req.u.create = FUN_ADMIN_ETH_CREATE_REQ_INIT( + FUN_ADMIN_SUBOP_CREATE, + FUN_ADMIN_RES_CREATE_FLAG_ALLOCATOR, + 0, fp->netdev->dev_port); + + rc = fun_submit_admin_sync_cmd(fp->fdev, &cmd.req.common, &cmd.rsp, + sizeof(cmd.rsp), 0); + return rc ? rc : be32_to_cpu(cmd.rsp.id); +} + +static int fun_vi_create(struct funeth_priv *fp) +{ + struct fun_admin_vi_req req = { + .common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_VI, + sizeof(req)), + .u.create = FUN_ADMIN_VI_CREATE_REQ_INIT(FUN_ADMIN_SUBOP_CREATE, + 0, + fp->netdev->dev_port, + fp->netdev->dev_port) + }; + + return fun_submit_admin_sync_cmd(fp->fdev, &req.common, NULL, 0, 0); +} + +/* Helper to create an ETH flow and bind an SQ to it. + * Returns the ETH id (>= 0) on success or a negative error. + */ +int fun_create_and_bind_tx(struct funeth_priv *fp, u32 sqid) +{ + int rc, ethid; + + ethid = fun_eth_create(fp); + if (ethid >= 0) { + rc = fun_bind(fp->fdev, FUN_ADMIN_BIND_TYPE_EPSQ, sqid, + FUN_ADMIN_BIND_TYPE_ETH, ethid); + if (rc) { + fun_res_destroy(fp->fdev, FUN_ADMIN_OP_ETH, 0, ethid); + ethid = rc; + } + } + return ethid; +} + +static irqreturn_t fun_queue_irq_handler(int irq, void *data) +{ + struct fun_irq *p = data; + + if (p->rxq) { + prefetch(p->rxq->next_cqe_info); + p->rxq->irq_cnt++; + } + napi_schedule_irqoff(&p->napi); + return IRQ_HANDLED; +} + +static int fun_enable_irqs(struct net_device *dev) +{ + struct funeth_priv *fp = netdev_priv(dev); + unsigned long idx, last; + unsigned int qidx; + struct fun_irq *p; + const char *qtype; + int err; + + xa_for_each(&fp->irqs, idx, p) { + if (p->txq) { + qtype = "tx"; + qidx = p->txq->qidx; + } else if (p->rxq) { + qtype = "rx"; + qidx = p->rxq->qidx; + } else { + continue; + } + + if (p->state != FUN_IRQ_INIT) + continue; + + snprintf(p->name, sizeof(p->name) - 1, "%s-%s-%u", dev->name, + qtype, qidx); + err = request_irq(p->irq, fun_queue_irq_handler, 0, p->name, p); + if (err) { + netdev_err(dev, "Failed to allocate IRQ %u, err %d\n", + p->irq, err); + goto unroll; + } + p->state = FUN_IRQ_REQUESTED; + } + + xa_for_each(&fp->irqs, idx, p) { + if (p->state != FUN_IRQ_REQUESTED) + continue; + irq_set_affinity_notifier(p->irq, &p->aff_notify); + irq_set_affinity_and_hint(p->irq, &p->affinity_mask); + napi_enable(&p->napi); + p->state = FUN_IRQ_ENABLED; + } + + return 0; + +unroll: + last = idx - 1; + xa_for_each_range(&fp->irqs, idx, p, 0, last) + if (p->state == FUN_IRQ_REQUESTED) { + free_irq(p->irq, p); + p->state = FUN_IRQ_INIT; + } + + return err; +} + +static void fun_disable_one_irq(struct fun_irq *irq) +{ + napi_disable(&irq->napi); + irq_set_affinity_notifier(irq->irq, NULL); + irq_update_affinity_hint(irq->irq, NULL); + free_irq(irq->irq, irq); + irq->state = FUN_IRQ_INIT; +} + +static void fun_disable_irqs(struct net_device *dev) +{ + struct funeth_priv *fp = netdev_priv(dev); + struct fun_irq *p; + unsigned long idx; + + xa_for_each(&fp->irqs, idx, p) + if (p->state == FUN_IRQ_ENABLED) + fun_disable_one_irq(p); +} + +static void fun_down(struct net_device *dev, struct fun_qset *qset) +{ + struct funeth_priv *fp = netdev_priv(dev); + + /* If we don't have queues the data path is already down. + * Note netif_running(dev) may be true. + */ + if (!rcu_access_pointer(fp->rxqs)) + return; + + /* It is also down if the queues aren't on the device. */ + if (fp->txqs[0]->init_state >= FUN_QSTATE_INIT_FULL) { + netif_info(fp, ifdown, dev, + "Tearing down data path on device\n"); + fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_DISABLE, 0); + + netif_carrier_off(dev); + netif_tx_disable(dev); + + fun_destroy_rss(fp); + fun_res_destroy(fp->fdev, FUN_ADMIN_OP_VI, 0, dev->dev_port); + fun_disable_irqs(dev); + } + + fun_free_rings(dev, qset); +} + +static int fun_up(struct net_device *dev, struct fun_qset *qset) +{ + static const int port_keys[] = { + FUN_ADMIN_PORT_KEY_STATS_DMA_LOW, + FUN_ADMIN_PORT_KEY_STATS_DMA_HIGH, + FUN_ADMIN_PORT_KEY_ENABLE + }; + + struct funeth_priv *fp = netdev_priv(dev); + u64 vals[] = { + lower_32_bits(fp->stats_dma_addr), + upper_32_bits(fp->stats_dma_addr), + FUN_PORT_FLAG_ENABLE_NOTIFY + }; + int err; + + netif_info(fp, ifup, dev, "Setting up data path on device\n"); + + if (qset->rxqs[0]->init_state < FUN_QSTATE_INIT_FULL) { + err = fun_advance_ring_state(dev, qset); + if (err) + return err; + } + + err = fun_vi_create(fp); + if (err) + goto free_queues; + + fp->txqs = qset->txqs; + rcu_assign_pointer(fp->rxqs, qset->rxqs); + rcu_assign_pointer(fp->xdpqs, qset->xdpqs); + + err = fun_enable_irqs(dev); + if (err) + goto destroy_vi; + + if (fp->rss_cfg) { + err = fun_config_rss(dev, fp->hash_algo, fp->rss_key, + fp->indir_table, FUN_ADMIN_SUBOP_CREATE); + } else { + /* The non-RSS case has only 1 queue. */ + err = fun_bind(fp->fdev, FUN_ADMIN_BIND_TYPE_VI, dev->dev_port, + FUN_ADMIN_BIND_TYPE_EPCQ, + qset->rxqs[0]->hw_cqid); + } + if (err) + goto disable_irqs; + + err = fun_port_write_cmds(fp, 3, port_keys, vals); + if (err) + goto free_rss; + + netif_tx_start_all_queues(dev); + return 0; + +free_rss: + fun_destroy_rss(fp); +disable_irqs: + fun_disable_irqs(dev); +destroy_vi: + fun_res_destroy(fp->fdev, FUN_ADMIN_OP_VI, 0, dev->dev_port); +free_queues: + fun_free_rings(dev, qset); + return err; +} + +static int funeth_open(struct net_device *netdev) +{ + struct funeth_priv *fp = netdev_priv(netdev); + struct fun_qset qset = { + .nrxqs = netdev->real_num_rx_queues, + .ntxqs = netdev->real_num_tx_queues, + .nxdpqs = fp->num_xdpqs, + .cq_depth = fp->cq_depth, + .rq_depth = fp->rq_depth, + .sq_depth = fp->sq_depth, + .state = FUN_QSTATE_INIT_FULL, + }; + int rc; + + rc = fun_alloc_rings(netdev, &qset); + if (rc) + return rc; + + rc = fun_up(netdev, &qset); + if (rc) { + qset.state = FUN_QSTATE_DESTROYED; + fun_free_rings(netdev, &qset); + } + + return rc; +} + +static int funeth_close(struct net_device *netdev) +{ + struct fun_qset qset = { .state = FUN_QSTATE_DESTROYED }; + + fun_down(netdev, &qset); + return 0; +} + +static void fun_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) +{ + struct funeth_priv *fp = netdev_priv(netdev); + struct funeth_txq **xdpqs; + struct funeth_rxq **rxqs; + unsigned int i, start; + + stats->tx_packets = fp->tx_packets; + stats->tx_bytes = fp->tx_bytes; + stats->tx_dropped = fp->tx_dropped; + + stats->rx_packets = fp->rx_packets; + stats->rx_bytes = fp->rx_bytes; + stats->rx_dropped = fp->rx_dropped; + + rcu_read_lock(); + rxqs = rcu_dereference(fp->rxqs); + if (!rxqs) + goto unlock; + + for (i = 0; i < netdev->real_num_tx_queues; i++) { + struct funeth_txq_stats txs; + + FUN_QSTAT_READ(fp->txqs[i], start, txs); + stats->tx_packets += txs.tx_pkts; + stats->tx_bytes += txs.tx_bytes; + stats->tx_dropped += txs.tx_map_err; + } + + for (i = 0; i < netdev->real_num_rx_queues; i++) { + struct funeth_rxq_stats rxs; + + FUN_QSTAT_READ(rxqs[i], start, rxs); + stats->rx_packets += rxs.rx_pkts; + stats->rx_bytes += rxs.rx_bytes; + stats->rx_dropped += rxs.rx_map_err + rxs.rx_mem_drops; + } + + xdpqs = rcu_dereference(fp->xdpqs); + if (!xdpqs) + goto unlock; + + for (i = 0; i < fp->num_xdpqs; i++) { + struct funeth_txq_stats txs; + + FUN_QSTAT_READ(xdpqs[i], start, txs); + stats->tx_packets += txs.tx_pkts; + stats->tx_bytes += txs.tx_bytes; + } +unlock: + rcu_read_unlock(); +} + +static int fun_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct funeth_priv *fp = netdev_priv(netdev); + int rc; + + rc = fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_MTU, new_mtu); + if (!rc) + netdev->mtu = new_mtu; + return rc; +} + +static int fun_set_macaddr(struct net_device *netdev, void *addr) +{ + struct funeth_priv *fp = netdev_priv(netdev); + struct sockaddr *saddr = addr; + int rc; + + if (!is_valid_ether_addr(saddr->sa_data)) + return -EADDRNOTAVAIL; + + if (ether_addr_equal(netdev->dev_addr, saddr->sa_data)) + return 0; + + rc = fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_MACADDR, + ether_addr_to_u64(saddr->sa_data)); + if (!rc) + eth_hw_addr_set(netdev, saddr->sa_data); + return rc; +} + +static int fun_get_port_attributes(struct net_device *netdev) +{ + static const int keys[] = { + FUN_ADMIN_PORT_KEY_MACADDR, FUN_ADMIN_PORT_KEY_CAPABILITIES, + FUN_ADMIN_PORT_KEY_ADVERT, FUN_ADMIN_PORT_KEY_MTU + }; + static const int phys_keys[] = { + FUN_ADMIN_PORT_KEY_LANE_ATTRS, + }; + + struct funeth_priv *fp = netdev_priv(netdev); + u64 data[ARRAY_SIZE(keys)]; + u8 mac[ETH_ALEN]; + int i, rc; + + rc = fun_port_read_cmds(fp, ARRAY_SIZE(keys), keys, data); + if (rc) + return rc; + + for (i = 0; i < ARRAY_SIZE(keys); i++) { + switch (keys[i]) { + case FUN_ADMIN_PORT_KEY_MACADDR: + u64_to_ether_addr(data[i], mac); + if (is_zero_ether_addr(mac)) { + eth_hw_addr_random(netdev); + } else if (is_valid_ether_addr(mac)) { + eth_hw_addr_set(netdev, mac); + } else { + netdev_err(netdev, + "device provided a bad MAC address %pM\n", + mac); + return -EINVAL; + } + break; + + case FUN_ADMIN_PORT_KEY_CAPABILITIES: + fp->port_caps = data[i]; + break; + + case FUN_ADMIN_PORT_KEY_ADVERT: + fp->advertising = data[i]; + break; + + case FUN_ADMIN_PORT_KEY_MTU: + netdev->mtu = data[i]; + break; + } + } + + if (!(fp->port_caps & FUN_PORT_CAP_VPORT)) { + rc = fun_port_read_cmds(fp, ARRAY_SIZE(phys_keys), phys_keys, + data); + if (rc) + return rc; + + fp->lane_attrs = data[0]; + } + + if (netdev->addr_assign_type == NET_ADDR_RANDOM) + return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_MACADDR, + ether_addr_to_u64(netdev->dev_addr)); + return 0; +} + +static int fun_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) +{ + const struct funeth_priv *fp = netdev_priv(dev); + + return copy_to_user(ifr->ifr_data, &fp->hwtstamp_cfg, + sizeof(fp->hwtstamp_cfg)) ? -EFAULT : 0; +} + +static int fun_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) +{ + struct funeth_priv *fp = netdev_priv(dev); + struct hwtstamp_config cfg; + + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + /* no TX HW timestamps */ + cfg.tx_type = HWTSTAMP_TX_OFF; + + switch (cfg.rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_SOME: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_NTP_ALL: + cfg.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + return -ERANGE; + } + + fp->hwtstamp_cfg = cfg; + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; +} + +static int fun_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCSHWTSTAMP: + return fun_hwtstamp_set(dev, ifr); + case SIOCGHWTSTAMP: + return fun_hwtstamp_get(dev, ifr); + default: + return -EOPNOTSUPP; + } +} + +/* Prepare the queues for XDP. */ +static int fun_enter_xdp(struct net_device *dev, struct bpf_prog *prog) +{ + struct funeth_priv *fp = netdev_priv(dev); + unsigned int i, nqs = num_online_cpus(); + struct funeth_txq **xdpqs; + struct funeth_rxq **rxqs; + int err; + + xdpqs = alloc_xdpqs(dev, nqs, fp->sq_depth, 0, FUN_QSTATE_INIT_FULL); + if (IS_ERR(xdpqs)) + return PTR_ERR(xdpqs); + + rxqs = rtnl_dereference(fp->rxqs); + for (i = 0; i < dev->real_num_rx_queues; i++) { + err = fun_rxq_set_bpf(rxqs[i], prog); + if (err) + goto out; + } + + fp->num_xdpqs = nqs; + rcu_assign_pointer(fp->xdpqs, xdpqs); + return 0; +out: + while (i--) + fun_rxq_set_bpf(rxqs[i], NULL); + + free_xdpqs(xdpqs, nqs, 0, FUN_QSTATE_DESTROYED); + return err; +} + +/* Set the queues for non-XDP operation. */ +static void fun_end_xdp(struct net_device *dev) +{ + struct funeth_priv *fp = netdev_priv(dev); + struct funeth_txq **xdpqs; + struct funeth_rxq **rxqs; + unsigned int i; + + xdpqs = rtnl_dereference(fp->xdpqs); + rcu_assign_pointer(fp->xdpqs, NULL); + synchronize_net(); + /* at this point both Rx and Tx XDP processing has ended */ + + free_xdpqs(xdpqs, fp->num_xdpqs, 0, FUN_QSTATE_DESTROYED); + fp->num_xdpqs = 0; + + rxqs = rtnl_dereference(fp->rxqs); + for (i = 0; i < dev->real_num_rx_queues; i++) + fun_rxq_set_bpf(rxqs[i], NULL); +} + +#define XDP_MAX_MTU \ + (PAGE_SIZE - FUN_XDP_HEADROOM - VLAN_ETH_HLEN - FUN_RX_TAILROOM) + +static int fun_xdp_setup(struct net_device *dev, struct netdev_bpf *xdp) +{ + struct bpf_prog *old_prog, *prog = xdp->prog; + struct funeth_priv *fp = netdev_priv(dev); + int i, err; + + /* XDP uses at most one buffer */ + if (prog && dev->mtu > XDP_MAX_MTU) { + netdev_err(dev, "device MTU %u too large for XDP\n", dev->mtu); + NL_SET_ERR_MSG_MOD(xdp->extack, + "Device MTU too large for XDP"); + return -EINVAL; + } + + if (!netif_running(dev)) { + fp->num_xdpqs = prog ? num_online_cpus() : 0; + } else if (prog && !fp->xdp_prog) { + err = fun_enter_xdp(dev, prog); + if (err) { + NL_SET_ERR_MSG_MOD(xdp->extack, + "Failed to set queues for XDP."); + return err; + } + } else if (!prog && fp->xdp_prog) { + fun_end_xdp(dev); + } else { + struct funeth_rxq **rxqs = rtnl_dereference(fp->rxqs); + + for (i = 0; i < dev->real_num_rx_queues; i++) + WRITE_ONCE(rxqs[i]->xdp_prog, prog); + } + + dev->max_mtu = prog ? XDP_MAX_MTU : FUN_MAX_MTU; + old_prog = xchg(&fp->xdp_prog, prog); + if (old_prog) + bpf_prog_put(old_prog); + + return 0; +} + +static int fun_xdp(struct net_device *dev, struct netdev_bpf *xdp) +{ + switch (xdp->command) { + case XDP_SETUP_PROG: + return fun_xdp_setup(dev, xdp); + default: + return -EINVAL; + } +} + +static struct devlink_port *fun_get_devlink_port(struct net_device *netdev) +{ + struct funeth_priv *fp = netdev_priv(netdev); + + return &fp->dl_port; +} + +static int fun_init_vports(struct fun_ethdev *ed, unsigned int n) +{ + if (ed->num_vports) + return -EINVAL; + + ed->vport_info = kvcalloc(n, sizeof(*ed->vport_info), GFP_KERNEL); + if (!ed->vport_info) + return -ENOMEM; + ed->num_vports = n; + return 0; +} + +static void fun_free_vports(struct fun_ethdev *ed) +{ + kvfree(ed->vport_info); + ed->vport_info = NULL; + ed->num_vports = 0; +} + +static struct fun_vport_info *fun_get_vport(struct fun_ethdev *ed, + unsigned int vport) +{ + if (!ed->vport_info || vport >= ed->num_vports) + return NULL; + + return ed->vport_info + vport; +} + +static int fun_set_vf_mac(struct net_device *dev, int vf, u8 *mac) +{ + struct funeth_priv *fp = netdev_priv(dev); + struct fun_adi_param mac_param = {}; + struct fun_dev *fdev = fp->fdev; + struct fun_ethdev *ed = to_fun_ethdev(fdev); + struct fun_vport_info *vi; + int rc = -EINVAL; + + if (is_multicast_ether_addr(mac)) + return -EINVAL; + + mutex_lock(&ed->state_mutex); + vi = fun_get_vport(ed, vf); + if (!vi) + goto unlock; + + mac_param.u.mac = FUN_ADI_MAC_INIT(ether_addr_to_u64(mac)); + rc = fun_adi_write(fdev, FUN_ADMIN_ADI_ATTR_MACADDR, vf + 1, + &mac_param); + if (!rc) + ether_addr_copy(vi->mac, mac); +unlock: + mutex_unlock(&ed->state_mutex); + return rc; +} + +static int fun_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos, + __be16 vlan_proto) +{ + struct funeth_priv *fp = netdev_priv(dev); + struct fun_adi_param vlan_param = {}; + struct fun_dev *fdev = fp->fdev; + struct fun_ethdev *ed = to_fun_ethdev(fdev); + struct fun_vport_info *vi; + int rc = -EINVAL; + + if (vlan > 4095 || qos > 7) + return -EINVAL; + if (vlan_proto && vlan_proto != htons(ETH_P_8021Q) && + vlan_proto != htons(ETH_P_8021AD)) + return -EINVAL; + + mutex_lock(&ed->state_mutex); + vi = fun_get_vport(ed, vf); + if (!vi) + goto unlock; + + vlan_param.u.vlan = FUN_ADI_VLAN_INIT(be16_to_cpu(vlan_proto), + ((u16)qos << VLAN_PRIO_SHIFT) | vlan); + rc = fun_adi_write(fdev, FUN_ADMIN_ADI_ATTR_VLAN, vf + 1, &vlan_param); + if (!rc) { + vi->vlan = vlan; + vi->qos = qos; + vi->vlan_proto = vlan_proto; + } +unlock: + mutex_unlock(&ed->state_mutex); + return rc; +} + +static int fun_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, + int max_tx_rate) +{ + struct funeth_priv *fp = netdev_priv(dev); + struct fun_adi_param rate_param = {}; + struct fun_dev *fdev = fp->fdev; + struct fun_ethdev *ed = to_fun_ethdev(fdev); + struct fun_vport_info *vi; + int rc = -EINVAL; + + if (min_tx_rate) + return -EINVAL; + + mutex_lock(&ed->state_mutex); + vi = fun_get_vport(ed, vf); + if (!vi) + goto unlock; + + rate_param.u.rate = FUN_ADI_RATE_INIT(max_tx_rate); + rc = fun_adi_write(fdev, FUN_ADMIN_ADI_ATTR_RATE, vf + 1, &rate_param); + if (!rc) + vi->max_rate = max_tx_rate; +unlock: + mutex_unlock(&ed->state_mutex); + return rc; +} + +static int fun_get_vf_config(struct net_device *dev, int vf, + struct ifla_vf_info *ivi) +{ + struct funeth_priv *fp = netdev_priv(dev); + struct fun_ethdev *ed = to_fun_ethdev(fp->fdev); + const struct fun_vport_info *vi; + + mutex_lock(&ed->state_mutex); + vi = fun_get_vport(ed, vf); + if (!vi) + goto unlock; + + memset(ivi, 0, sizeof(*ivi)); + ivi->vf = vf; + ether_addr_copy(ivi->mac, vi->mac); + ivi->vlan = vi->vlan; + ivi->qos = vi->qos; + ivi->vlan_proto = vi->vlan_proto; + ivi->max_tx_rate = vi->max_rate; + ivi->spoofchk = vi->spoofchk; +unlock: + mutex_unlock(&ed->state_mutex); + return vi ? 0 : -EINVAL; +} + +static void fun_uninit(struct net_device *dev) +{ + struct funeth_priv *fp = netdev_priv(dev); + + fun_prune_queue_irqs(dev); + xa_destroy(&fp->irqs); +} + +static const struct net_device_ops fun_netdev_ops = { + .ndo_open = funeth_open, + .ndo_stop = funeth_close, + .ndo_start_xmit = fun_start_xmit, + .ndo_get_stats64 = fun_get_stats64, + .ndo_change_mtu = fun_change_mtu, + .ndo_set_mac_address = fun_set_macaddr, + .ndo_validate_addr = eth_validate_addr, + .ndo_eth_ioctl = fun_ioctl, + .ndo_uninit = fun_uninit, + .ndo_bpf = fun_xdp, + .ndo_xdp_xmit = fun_xdp_xmit_frames, + .ndo_set_vf_mac = fun_set_vf_mac, + .ndo_set_vf_vlan = fun_set_vf_vlan, + .ndo_set_vf_rate = fun_set_vf_rate, + .ndo_get_vf_config = fun_get_vf_config, + .ndo_get_devlink_port = fun_get_devlink_port, +}; + +#define GSO_ENCAP_FLAGS (NETIF_F_GSO_GRE | NETIF_F_GSO_IPXIP4 | \ + NETIF_F_GSO_IPXIP6 | NETIF_F_GSO_UDP_TUNNEL | \ + NETIF_F_GSO_UDP_TUNNEL_CSUM) +#define TSO_FLAGS (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN) +#define VLAN_FEAT (NETIF_F_SG | NETIF_F_HW_CSUM | TSO_FLAGS | \ + GSO_ENCAP_FLAGS | NETIF_F_HIGHDMA) + +static void fun_dflt_rss_indir(struct funeth_priv *fp, unsigned int nrx) +{ + unsigned int i; + + for (i = 0; i < fp->indir_table_nentries; i++) + fp->indir_table[i] = ethtool_rxfh_indir_default(i, nrx); +} + +/* Reset the RSS indirection table to equal distribution across the current + * number of Rx queues. Called at init time and whenever the number of Rx + * queues changes subsequently. Note that this may also resize the indirection + * table. + */ +static void fun_reset_rss_indir(struct net_device *dev, unsigned int nrx) +{ + struct funeth_priv *fp = netdev_priv(dev); + + if (!fp->rss_cfg) + return; + + /* Set the table size to the max possible that allows an equal number + * of occurrences of each CQ. + */ + fp->indir_table_nentries = rounddown(FUN_ETH_RSS_MAX_INDIR_ENT, nrx); + fun_dflt_rss_indir(fp, nrx); +} + +/* Update the RSS LUT to contain only queues in [0, nrx). Normally this will + * update the LUT to an equal distribution among nrx queues, If @only_if_needed + * is set the LUT is left unchanged if it already does not reference any queues + * >= nrx. + */ +static int fun_rss_set_qnum(struct net_device *dev, unsigned int nrx, + bool only_if_needed) +{ + struct funeth_priv *fp = netdev_priv(dev); + u32 old_lut[FUN_ETH_RSS_MAX_INDIR_ENT]; + unsigned int i, oldsz; + int err; + + if (!fp->rss_cfg) + return 0; + + if (only_if_needed) { + for (i = 0; i < fp->indir_table_nentries; i++) + if (fp->indir_table[i] >= nrx) + break; + + if (i >= fp->indir_table_nentries) + return 0; + } + + memcpy(old_lut, fp->indir_table, sizeof(old_lut)); + oldsz = fp->indir_table_nentries; + fun_reset_rss_indir(dev, nrx); + + err = fun_config_rss(dev, fp->hash_algo, fp->rss_key, + fp->indir_table, FUN_ADMIN_SUBOP_MODIFY); + if (!err) + return 0; + + memcpy(fp->indir_table, old_lut, sizeof(old_lut)); + fp->indir_table_nentries = oldsz; + return err; +} + +/* Allocate the DMA area for the RSS configuration commands to the device, and + * initialize the hash, hash key, indirection table size and its entries to + * their defaults. The indirection table defaults to equal distribution across + * the Rx queues. + */ +static int fun_init_rss(struct net_device *dev) +{ + struct funeth_priv *fp = netdev_priv(dev); + size_t size = sizeof(fp->rss_key) + sizeof(fp->indir_table); + + fp->rss_hw_id = FUN_HCI_ID_INVALID; + if (!(fp->port_caps & FUN_PORT_CAP_OFFLOADS)) + return 0; + + fp->rss_cfg = dma_alloc_coherent(&fp->pdev->dev, size, + &fp->rss_dma_addr, GFP_KERNEL); + if (!fp->rss_cfg) + return -ENOMEM; + + fp->hash_algo = FUN_ETH_RSS_ALG_TOEPLITZ; + netdev_rss_key_fill(fp->rss_key, sizeof(fp->rss_key)); + fun_reset_rss_indir(dev, dev->real_num_rx_queues); + return 0; +} + +static void fun_free_rss(struct funeth_priv *fp) +{ + if (fp->rss_cfg) { + dma_free_coherent(&fp->pdev->dev, + sizeof(fp->rss_key) + sizeof(fp->indir_table), + fp->rss_cfg, fp->rss_dma_addr); + fp->rss_cfg = NULL; + } +} + +void fun_set_ring_count(struct net_device *netdev, unsigned int ntx, + unsigned int nrx) +{ + netif_set_real_num_tx_queues(netdev, ntx); + if (nrx != netdev->real_num_rx_queues) { + netif_set_real_num_rx_queues(netdev, nrx); + fun_reset_rss_indir(netdev, nrx); + } +} + +static int fun_init_stats_area(struct funeth_priv *fp) +{ + unsigned int nstats; + + if (!(fp->port_caps & FUN_PORT_CAP_STATS)) + return 0; + + nstats = PORT_MAC_RX_STATS_MAX + PORT_MAC_TX_STATS_MAX + + PORT_MAC_FEC_STATS_MAX; + + fp->stats = dma_alloc_coherent(&fp->pdev->dev, nstats * sizeof(u64), + &fp->stats_dma_addr, GFP_KERNEL); + if (!fp->stats) + return -ENOMEM; + return 0; +} + +static void fun_free_stats_area(struct funeth_priv *fp) +{ + unsigned int nstats; + + if (fp->stats) { + nstats = PORT_MAC_RX_STATS_MAX + PORT_MAC_TX_STATS_MAX; + dma_free_coherent(&fp->pdev->dev, nstats * sizeof(u64), + fp->stats, fp->stats_dma_addr); + fp->stats = NULL; + } +} + +static int fun_dl_port_register(struct net_device *netdev) +{ + struct funeth_priv *fp = netdev_priv(netdev); + struct devlink *dl = priv_to_devlink(fp->fdev); + struct devlink_port_attrs attrs = {}; + unsigned int idx; + + if (fp->port_caps & FUN_PORT_CAP_VPORT) { + attrs.flavour = DEVLINK_PORT_FLAVOUR_VIRTUAL; + idx = fp->lport; + } else { + idx = netdev->dev_port; + attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; + attrs.lanes = fp->lane_attrs & 7; + if (fp->lane_attrs & FUN_PORT_LANE_SPLIT) { + attrs.split = 1; + attrs.phys.port_number = fp->lport & ~3; + attrs.phys.split_subport_number = fp->lport & 3; + } else { + attrs.phys.port_number = fp->lport; + } + } + + devlink_port_attrs_set(&fp->dl_port, &attrs); + + return devlink_port_register(dl, &fp->dl_port, idx); +} + +/* Determine the max Tx/Rx queues for a port. */ +static int fun_max_qs(struct fun_ethdev *ed, unsigned int *ntx, + unsigned int *nrx) +{ + int neth; + + if (ed->num_ports > 1 || is_kdump_kernel()) { + *ntx = 1; + *nrx = 1; + return 0; + } + + neth = fun_get_res_count(&ed->fdev, FUN_ADMIN_OP_ETH); + if (neth < 0) + return neth; + + /* We determine the max number of queues based on the CPU + * cores, device interrupts and queues, RSS size, and device Tx flows. + * + * - At least 1 Rx and 1 Tx queues. + * - At most 1 Rx/Tx queue per core. + * - Each Rx/Tx queue needs 1 SQ. + */ + *ntx = min(ed->nsqs_per_port - 1, num_online_cpus()); + *nrx = *ntx; + if (*ntx > neth) + *ntx = neth; + if (*nrx > FUN_ETH_RSS_MAX_INDIR_ENT) + *nrx = FUN_ETH_RSS_MAX_INDIR_ENT; + return 0; +} + +static void fun_queue_defaults(struct net_device *dev, unsigned int nsqs) +{ + unsigned int ntx, nrx; + + ntx = min(dev->num_tx_queues, FUN_DFLT_QUEUES); + nrx = min(dev->num_rx_queues, FUN_DFLT_QUEUES); + if (ntx <= nrx) { + ntx = min(ntx, nsqs / 2); + nrx = min(nrx, nsqs - ntx); + } else { + nrx = min(nrx, nsqs / 2); + ntx = min(ntx, nsqs - nrx); + } + + netif_set_real_num_tx_queues(dev, ntx); + netif_set_real_num_rx_queues(dev, nrx); +} + +/* Replace the existing Rx/Tx/XDP queues with equal number of queues with + * different settings, e.g. depth. This is a disruptive replacement that + * temporarily shuts down the data path and should be limited to changes that + * can't be applied to live queues. The old queues are always discarded. + */ +int fun_replace_queues(struct net_device *dev, struct fun_qset *newqs, + struct netlink_ext_ack *extack) +{ + struct fun_qset oldqs = { .state = FUN_QSTATE_DESTROYED }; + struct funeth_priv *fp = netdev_priv(dev); + int err; + + newqs->nrxqs = dev->real_num_rx_queues; + newqs->ntxqs = dev->real_num_tx_queues; + newqs->nxdpqs = fp->num_xdpqs; + newqs->state = FUN_QSTATE_INIT_SW; + err = fun_alloc_rings(dev, newqs); + if (err) { + NL_SET_ERR_MSG_MOD(extack, + "Unable to allocate memory for new queues, keeping current settings"); + return err; + } + + fun_down(dev, &oldqs); + + err = fun_up(dev, newqs); + if (!err) + return 0; + + /* The new queues couldn't be installed. We do not retry the old queues + * as they are the same to the device as the new queues and would + * similarly fail. + */ + newqs->state = FUN_QSTATE_DESTROYED; + fun_free_rings(dev, newqs); + NL_SET_ERR_MSG_MOD(extack, "Unable to restore the data path with the new queues."); + return err; +} + +/* Change the number of Rx/Tx queues of a device while it is up. This is done + * by incrementally adding/removing queues to meet the new requirements while + * handling ongoing traffic. + */ +int fun_change_num_queues(struct net_device *dev, unsigned int ntx, + unsigned int nrx) +{ + unsigned int keep_tx = min(dev->real_num_tx_queues, ntx); + unsigned int keep_rx = min(dev->real_num_rx_queues, nrx); + struct funeth_priv *fp = netdev_priv(dev); + struct fun_qset oldqs = { + .rxqs = rtnl_dereference(fp->rxqs), + .txqs = fp->txqs, + .nrxqs = dev->real_num_rx_queues, + .ntxqs = dev->real_num_tx_queues, + .rxq_start = keep_rx, + .txq_start = keep_tx, + .state = FUN_QSTATE_DESTROYED + }; + struct fun_qset newqs = { + .nrxqs = nrx, + .ntxqs = ntx, + .rxq_start = keep_rx, + .txq_start = keep_tx, + .cq_depth = fp->cq_depth, + .rq_depth = fp->rq_depth, + .sq_depth = fp->sq_depth, + .state = FUN_QSTATE_INIT_FULL + }; + int i, err; + + err = fun_alloc_rings(dev, &newqs); + if (err) + goto free_irqs; + + err = fun_enable_irqs(dev); /* of any newly added queues */ + if (err) + goto free_rings; + + /* copy the queues we are keeping to the new set */ + memcpy(newqs.rxqs, oldqs.rxqs, keep_rx * sizeof(*oldqs.rxqs)); + memcpy(newqs.txqs, fp->txqs, keep_tx * sizeof(*fp->txqs)); + + if (nrx < dev->real_num_rx_queues) { + err = fun_rss_set_qnum(dev, nrx, true); + if (err) + goto disable_tx_irqs; + + for (i = nrx; i < dev->real_num_rx_queues; i++) + fun_disable_one_irq(container_of(oldqs.rxqs[i]->napi, + struct fun_irq, napi)); + + netif_set_real_num_rx_queues(dev, nrx); + } + + if (ntx < dev->real_num_tx_queues) + netif_set_real_num_tx_queues(dev, ntx); + + rcu_assign_pointer(fp->rxqs, newqs.rxqs); + fp->txqs = newqs.txqs; + synchronize_net(); + + if (ntx > dev->real_num_tx_queues) + netif_set_real_num_tx_queues(dev, ntx); + + if (nrx > dev->real_num_rx_queues) { + netif_set_real_num_rx_queues(dev, nrx); + fun_rss_set_qnum(dev, nrx, false); + } + + /* disable interrupts of any excess Tx queues */ + for (i = keep_tx; i < oldqs.ntxqs; i++) + fun_disable_one_irq(oldqs.txqs[i]->irq); + + fun_free_rings(dev, &oldqs); + fun_prune_queue_irqs(dev); + return 0; + +disable_tx_irqs: + for (i = oldqs.ntxqs; i < ntx; i++) + fun_disable_one_irq(newqs.txqs[i]->irq); +free_rings: + newqs.state = FUN_QSTATE_DESTROYED; + fun_free_rings(dev, &newqs); +free_irqs: + fun_prune_queue_irqs(dev); + return err; +} + +static int fun_create_netdev(struct fun_ethdev *ed, unsigned int portid) +{ + struct fun_dev *fdev = &ed->fdev; + struct net_device *netdev; + struct funeth_priv *fp; + unsigned int ntx, nrx; + int rc; + + rc = fun_max_qs(ed, &ntx, &nrx); + if (rc) + return rc; + + netdev = alloc_etherdev_mqs(sizeof(*fp), ntx, nrx); + if (!netdev) { + rc = -ENOMEM; + goto done; + } + + netdev->dev_port = portid; + fun_queue_defaults(netdev, ed->nsqs_per_port); + + fp = netdev_priv(netdev); + fp->fdev = fdev; + fp->pdev = to_pci_dev(fdev->dev); + fp->netdev = netdev; + xa_init(&fp->irqs); + fp->rx_irq_ofst = ntx; + seqcount_init(&fp->link_seq); + + fp->lport = INVALID_LPORT; + rc = fun_port_create(netdev); + if (rc) + goto free_netdev; + + /* bind port to admin CQ for async events */ + rc = fun_bind(fdev, FUN_ADMIN_BIND_TYPE_PORT, portid, + FUN_ADMIN_BIND_TYPE_EPCQ, 0); + if (rc) + goto destroy_port; + + rc = fun_get_port_attributes(netdev); + if (rc) + goto destroy_port; + + rc = fun_init_rss(netdev); + if (rc) + goto destroy_port; + + rc = fun_init_stats_area(fp); + if (rc) + goto free_rss; + + SET_NETDEV_DEV(netdev, fdev->dev); + netdev->netdev_ops = &fun_netdev_ops; + + netdev->hw_features = NETIF_F_SG | NETIF_F_RXHASH | NETIF_F_RXCSUM; + if (fp->port_caps & FUN_PORT_CAP_OFFLOADS) + netdev->hw_features |= NETIF_F_HW_CSUM | TSO_FLAGS; + if (fp->port_caps & FUN_PORT_CAP_ENCAP_OFFLOADS) + netdev->hw_features |= GSO_ENCAP_FLAGS; + + netdev->features |= netdev->hw_features | NETIF_F_HIGHDMA; + netdev->vlan_features = netdev->features & VLAN_FEAT; + netdev->mpls_features = netdev->vlan_features; + netdev->hw_enc_features = netdev->hw_features; + + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = FUN_MAX_MTU; + + fun_set_ethtool_ops(netdev); + + /* configurable parameters */ + fp->sq_depth = min(SQ_DEPTH, fdev->q_depth); + fp->cq_depth = min(CQ_DEPTH, fdev->q_depth); + fp->rq_depth = min_t(unsigned int, RQ_DEPTH, fdev->q_depth); + fp->rx_coal_usec = CQ_INTCOAL_USEC; + fp->rx_coal_count = CQ_INTCOAL_NPKT; + fp->tx_coal_usec = SQ_INTCOAL_USEC; + fp->tx_coal_count = SQ_INTCOAL_NPKT; + fp->cq_irq_db = FUN_IRQ_CQ_DB(fp->rx_coal_usec, fp->rx_coal_count); + + rc = fun_dl_port_register(netdev); + if (rc) + goto free_stats; + + fp->ktls_id = FUN_HCI_ID_INVALID; + fun_ktls_init(netdev); /* optional, failure OK */ + + netif_carrier_off(netdev); + ed->netdevs[portid] = netdev; + rc = register_netdev(netdev); + if (rc) + goto unreg_devlink; + + if (fp->dl_port.devlink) + devlink_port_type_eth_set(&fp->dl_port, netdev); + + return 0; + +unreg_devlink: + ed->netdevs[portid] = NULL; + fun_ktls_cleanup(fp); + if (fp->dl_port.devlink) + devlink_port_unregister(&fp->dl_port); +free_stats: + fun_free_stats_area(fp); +free_rss: + fun_free_rss(fp); +destroy_port: + fun_port_destroy(netdev); +free_netdev: + free_netdev(netdev); +done: + dev_err(fdev->dev, "couldn't allocate port %u, error %d", portid, rc); + return rc; +} + +static void fun_destroy_netdev(struct net_device *netdev) +{ + struct funeth_priv *fp; + + fp = netdev_priv(netdev); + if (fp->dl_port.devlink) { + devlink_port_type_clear(&fp->dl_port); + devlink_port_unregister(&fp->dl_port); + } + unregister_netdev(netdev); + fun_ktls_cleanup(fp); + fun_free_stats_area(fp); + fun_free_rss(fp); + fun_port_destroy(netdev); + free_netdev(netdev); +} + +static int fun_create_ports(struct fun_ethdev *ed, unsigned int nports) +{ + struct fun_dev *fd = &ed->fdev; + int i, rc; + + /* The admin queue takes 1 IRQ and 2 SQs. */ + ed->nsqs_per_port = min(fd->num_irqs - 1, + fd->kern_end_qid - 2) / nports; + if (ed->nsqs_per_port < 2) { + dev_err(fd->dev, "Too few SQs for %u ports", nports); + return -EINVAL; + } + + ed->netdevs = kcalloc(nports, sizeof(*ed->netdevs), GFP_KERNEL); + if (!ed->netdevs) + return -ENOMEM; + + ed->num_ports = nports; + for (i = 0; i < nports; i++) { + rc = fun_create_netdev(ed, i); + if (rc) + goto free_netdevs; + } + + return 0; + +free_netdevs: + while (i) + fun_destroy_netdev(ed->netdevs[--i]); + kfree(ed->netdevs); + ed->netdevs = NULL; + ed->num_ports = 0; + return rc; +} + +static void fun_destroy_ports(struct fun_ethdev *ed) +{ + unsigned int i; + + for (i = 0; i < ed->num_ports; i++) + fun_destroy_netdev(ed->netdevs[i]); + + kfree(ed->netdevs); + ed->netdevs = NULL; + ed->num_ports = 0; +} + +static void fun_update_link_state(const struct fun_ethdev *ed, + const struct fun_admin_port_notif *notif) +{ + unsigned int port_idx = be16_to_cpu(notif->id); + struct net_device *netdev; + struct funeth_priv *fp; + + if (port_idx >= ed->num_ports) + return; + + netdev = ed->netdevs[port_idx]; + fp = netdev_priv(netdev); + + write_seqcount_begin(&fp->link_seq); + fp->link_speed = be32_to_cpu(notif->speed) * 10; /* 10 Mbps->Mbps */ + fp->active_fc = notif->flow_ctrl; + fp->active_fec = notif->fec; + fp->xcvr_type = notif->xcvr_type; + fp->link_down_reason = notif->link_down_reason; + fp->lp_advertising = be64_to_cpu(notif->lp_advertising); + + if ((notif->link_state | notif->missed_events) & FUN_PORT_FLAG_MAC_DOWN) + netif_carrier_off(netdev); + if (notif->link_state & FUN_PORT_FLAG_MAC_UP) + netif_carrier_on(netdev); + + write_seqcount_end(&fp->link_seq); + fun_report_link(netdev); +} + +/* handler for async events delivered through the admin CQ */ +static void fun_event_cb(struct fun_dev *fdev, void *entry) +{ + u8 op = ((struct fun_admin_rsp_common *)entry)->op; + + if (op == FUN_ADMIN_OP_PORT) { + const struct fun_admin_port_notif *rsp = entry; + + if (rsp->subop == FUN_ADMIN_SUBOP_NOTIFY) { + fun_update_link_state(to_fun_ethdev(fdev), rsp); + } else if (rsp->subop == FUN_ADMIN_SUBOP_RES_COUNT) { + const struct fun_admin_res_count_rsp *r = entry; + + if (r->count.data) + set_bit(FUN_SERV_RES_CHANGE, &fdev->service_flags); + else + set_bit(FUN_SERV_DEL_PORTS, &fdev->service_flags); + fun_serv_sched(fdev); + } else { + dev_info(fdev->dev, "adminq event unexpected op %u subop %u", + op, rsp->subop); + } + } else { + dev_info(fdev->dev, "adminq event unexpected op %u", op); + } +} + +/* handler for pending work managed by the service task */ +static void fun_service_cb(struct fun_dev *fdev) +{ + struct fun_ethdev *ed = to_fun_ethdev(fdev); + int rc; + + if (test_and_clear_bit(FUN_SERV_DEL_PORTS, &fdev->service_flags)) + fun_destroy_ports(ed); + + if (!test_and_clear_bit(FUN_SERV_RES_CHANGE, &fdev->service_flags)) + return; + + rc = fun_get_res_count(fdev, FUN_ADMIN_OP_PORT); + if (rc < 0 || rc == ed->num_ports) + return; + + if (ed->num_ports) + fun_destroy_ports(ed); + if (rc) + fun_create_ports(ed, rc); +} + +static int funeth_sriov_configure(struct pci_dev *pdev, int nvfs) +{ + struct fun_dev *fdev = pci_get_drvdata(pdev); + struct fun_ethdev *ed = to_fun_ethdev(fdev); + int rc; + + if (nvfs == 0) { + if (pci_vfs_assigned(pdev)) { + dev_warn(&pdev->dev, + "Cannot disable SR-IOV while VFs are assigned\n"); + return -EPERM; + } + + mutex_lock(&ed->state_mutex); + fun_free_vports(ed); + mutex_unlock(&ed->state_mutex); + pci_disable_sriov(pdev); + return 0; + } + + rc = pci_enable_sriov(pdev, nvfs); + if (rc) + return rc; + + mutex_lock(&ed->state_mutex); + rc = fun_init_vports(ed, nvfs); + mutex_unlock(&ed->state_mutex); + if (rc) { + pci_disable_sriov(pdev); + return rc; + } + + return nvfs; +} + +static int funeth_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct fun_dev_params aqreq = { + .cqe_size_log2 = ilog2(ADMIN_CQE_SIZE), + .sqe_size_log2 = ilog2(ADMIN_SQE_SIZE), + .cq_depth = ADMIN_CQ_DEPTH, + .sq_depth = ADMIN_SQ_DEPTH, + .rq_depth = ADMIN_RQ_DEPTH, + .min_msix = 2, /* 1 Rx + 1 Tx */ + .event_cb = fun_event_cb, + .serv_cb = fun_service_cb, + }; + struct devlink *devlink; + struct fun_ethdev *ed; + struct fun_dev *fdev; + int rc; + + devlink = fun_devlink_alloc(&pdev->dev); + if (!devlink) { + dev_err(&pdev->dev, "devlink alloc failed\n"); + return -ENOMEM; + } + + ed = devlink_priv(devlink); + mutex_init(&ed->state_mutex); + + fdev = &ed->fdev; + rc = fun_dev_enable(fdev, pdev, &aqreq, KBUILD_MODNAME); + if (rc) + goto free_devlink; + + rc = fun_get_res_count(fdev, FUN_ADMIN_OP_PORT); + if (rc > 0) + rc = fun_create_ports(ed, rc); + if (rc < 0) + goto disable_dev; + + fun_serv_restart(fdev); + fun_devlink_register(devlink); + return 0; + +disable_dev: + fun_dev_disable(fdev); +free_devlink: + mutex_destroy(&ed->state_mutex); + fun_devlink_free(devlink); + return rc; +} + +static void funeth_remove(struct pci_dev *pdev) +{ + struct fun_dev *fdev = pci_get_drvdata(pdev); + struct devlink *devlink; + struct fun_ethdev *ed; + + ed = to_fun_ethdev(fdev); + devlink = priv_to_devlink(ed); + fun_devlink_unregister(devlink); + +#ifdef CONFIG_PCI_IOV + funeth_sriov_configure(pdev, 0); +#endif + + fun_serv_stop(fdev); + fun_destroy_ports(ed); + fun_dev_disable(fdev); + mutex_destroy(&ed->state_mutex); + + fun_devlink_free(devlink); +} + +static struct pci_driver funeth_driver = { + .name = KBUILD_MODNAME, + .id_table = funeth_id_table, + .probe = funeth_probe, + .remove = funeth_remove, + .shutdown = funeth_remove, + .sriov_configure = funeth_sriov_configure, +}; + +module_pci_driver(funeth_driver); + +MODULE_AUTHOR("Dimitris Michailidis "); +MODULE_DESCRIPTION("Fungible Ethernet Network Driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DEVICE_TABLE(pci, funeth_id_table); diff --git a/drivers/net/ethernet/fungible/funeth/funeth_rx.c b/drivers/net/ethernet/fungible/funeth/funeth_rx.c new file mode 100644 index 000000000000..0f6a549b9f67 --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/funeth_rx.c @@ -0,0 +1,826 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +#include +#include +#include +#include +#include +#include +#include +#include "funeth_txrx.h" +#include "funeth.h" +#include "fun_queue.h" + +#define CREATE_TRACE_POINTS +#include "funeth_trace.h" + +/* Given the device's max supported MTU and pages of at least 4KB a packet can + * be scattered into at most 4 buffers. + */ +#define RX_MAX_FRAGS 4 + +/* Per packet headroom in non-XDP mode. Present only for 1-frag packets. */ +#define FUN_RX_HEADROOM (NET_SKB_PAD + NET_IP_ALIGN) + +/* We try to reuse pages for our buffers. To avoid frequent page ref writes we + * take EXTRA_PAGE_REFS references at once and then hand them out one per packet + * occupying the buffer. + */ +#define EXTRA_PAGE_REFS 1000000 +#define MIN_PAGE_REFS 1000 + +enum { + FUN_XDP_FLUSH_REDIR = 1, + FUN_XDP_FLUSH_TX = 2, +}; + +/* See if a page is running low on refs we are holding and if so take more. */ +static void refresh_refs(struct funeth_rxbuf *buf) +{ + if (unlikely(buf->pg_refs < MIN_PAGE_REFS)) { + buf->pg_refs += EXTRA_PAGE_REFS; + page_ref_add(buf->page, EXTRA_PAGE_REFS); + } +} + +/* Offer a buffer to the Rx buffer cache. The cache will hold the buffer if its + * page is worth retaining and there's room for it. Otherwise the page is + * unmapped and our references released. + */ +static void cache_offer(struct funeth_rxq *q, const struct funeth_rxbuf *buf) +{ + struct funeth_rx_cache *c = &q->cache; + + if (c->prod_cnt - c->cons_cnt <= c->mask && buf->node == numa_mem_id()) { + c->bufs[c->prod_cnt & c->mask] = *buf; + c->prod_cnt++; + } else { + dma_unmap_page_attrs(q->dma_dev, buf->dma_addr, PAGE_SIZE, + DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + __page_frag_cache_drain(buf->page, buf->pg_refs); + } +} + +/* Get a page from the Rx buffer cache. We only consider the next available + * page and return it if we own all its references. + */ +static bool cache_get(struct funeth_rxq *q, struct funeth_rxbuf *rb) +{ + struct funeth_rx_cache *c = &q->cache; + struct funeth_rxbuf *buf; + + if (c->prod_cnt == c->cons_cnt) + return false; /* empty cache */ + + buf = &c->bufs[c->cons_cnt & c->mask]; + if (page_ref_count(buf->page) == buf->pg_refs) { + dma_sync_single_for_device(q->dma_dev, buf->dma_addr, + PAGE_SIZE, DMA_FROM_DEVICE); + *rb = *buf; + buf->page = NULL; + refresh_refs(rb); + c->cons_cnt++; + return true; + } + + /* Page can't be reused. If the cache is full drop this page. */ + if (c->prod_cnt - c->cons_cnt > c->mask) { + dma_unmap_page_attrs(q->dma_dev, buf->dma_addr, PAGE_SIZE, + DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + __page_frag_cache_drain(buf->page, buf->pg_refs); + buf->page = NULL; + c->cons_cnt++; + } + return false; +} + +/* Allocate and DMA-map a page for receive. */ +static int funeth_alloc_page(struct funeth_rxq *q, struct funeth_rxbuf *rb, + int node, gfp_t gfp) +{ + struct page *p; + + if (cache_get(q, rb)) + return 0; + + p = __alloc_pages_node(node, gfp | __GFP_NOWARN, 0); + if (unlikely(!p)) + return -ENOMEM; + + rb->dma_addr = dma_map_page(q->dma_dev, p, 0, PAGE_SIZE, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(q->dma_dev, rb->dma_addr))) { + FUN_QSTAT_INC(q, rx_map_err); + __free_page(p); + return -ENOMEM; + } + + FUN_QSTAT_INC(q, rx_page_alloc); + + rb->page = p; + rb->pg_refs = 1; + refresh_refs(rb); + rb->node = page_is_pfmemalloc(p) ? -1 : page_to_nid(p); + return 0; +} + +static void funeth_free_page(struct funeth_rxq *q, struct funeth_rxbuf *rb) +{ + if (rb->page) { + dma_unmap_page(q->dma_dev, rb->dma_addr, PAGE_SIZE, + DMA_FROM_DEVICE); + __page_frag_cache_drain(rb->page, rb->pg_refs); + rb->page = NULL; + } +} + +/* Run the XDP program assigned to an Rx queue. + * Return %NULL if the buffer is consumed, or the virtual address of the packet + * to turn into an skb. + */ +static void *fun_run_xdp(struct funeth_rxq *q, skb_frag_t *frags, void *buf_va, + int ref_ok, struct funeth_txq *xdp_q) +{ + struct bpf_prog *xdp_prog; + struct xdp_buff xdp; + u32 act; + + /* VA includes the headroom, frag size includes headroom + tailroom */ + xdp_init_buff(&xdp, ALIGN(skb_frag_size(frags), FUN_EPRQ_PKT_ALIGN), + &q->xdp_rxq); + xdp_prepare_buff(&xdp, buf_va, FUN_XDP_HEADROOM, skb_frag_size(frags) - + (FUN_RX_TAILROOM + FUN_XDP_HEADROOM), false); + + xdp_prog = READ_ONCE(q->xdp_prog); + act = bpf_prog_run_xdp(xdp_prog, &xdp); + + switch (act) { + case XDP_PASS: + /* remove headroom, which may not be FUN_XDP_HEADROOM now */ + skb_frag_size_set(frags, xdp.data_end - xdp.data); + skb_frag_off_add(frags, xdp.data - xdp.data_hard_start); + goto pass; + case XDP_TX: + if (unlikely(!ref_ok)) + goto pass; + if (!fun_xdp_tx(xdp_q, xdp.data, xdp.data_end - xdp.data)) + goto xdp_error; + FUN_QSTAT_INC(q, xdp_tx); + q->xdp_flush |= FUN_XDP_FLUSH_TX; + break; + case XDP_REDIRECT: + if (unlikely(!ref_ok)) + goto pass; + if (unlikely(xdp_do_redirect(q->netdev, &xdp, xdp_prog))) + goto xdp_error; + FUN_QSTAT_INC(q, xdp_redir); + q->xdp_flush |= FUN_XDP_FLUSH_REDIR; + break; + default: + bpf_warn_invalid_xdp_action(q->netdev, xdp_prog, act); + fallthrough; + case XDP_ABORTED: + trace_xdp_exception(q->netdev, xdp_prog, act); +xdp_error: + q->cur_buf->pg_refs++; /* return frags' page reference */ + FUN_QSTAT_INC(q, xdp_err); + break; + case XDP_DROP: + q->cur_buf->pg_refs++; + FUN_QSTAT_INC(q, xdp_drops); + break; + } + return NULL; + +pass: + return xdp.data; +} + +/* A CQE contains a fixed completion structure along with optional metadata and + * even packet data. Given the start address of a CQE return the start of the + * contained fixed structure, which lies at the end. + */ +static const void *cqe_to_info(const void *cqe) +{ + return cqe + FUNETH_CQE_INFO_OFFSET; +} + +/* The inverse of cqe_to_info(). */ +static const void *info_to_cqe(const void *cqe_info) +{ + return cqe_info - FUNETH_CQE_INFO_OFFSET; +} + +/* Return the type of hash provided by the device based on the L3 and L4 + * protocols it parsed for the packet. + */ +static enum pkt_hash_types cqe_to_pkt_hash_type(u16 pkt_parse) +{ + static const enum pkt_hash_types htype_map[] = { + PKT_HASH_TYPE_NONE, PKT_HASH_TYPE_L3, + PKT_HASH_TYPE_NONE, PKT_HASH_TYPE_L4, + PKT_HASH_TYPE_NONE, PKT_HASH_TYPE_L3, + PKT_HASH_TYPE_NONE, PKT_HASH_TYPE_L3 + }; + u16 key; + + /* Build the key from the TCP/UDP and IP/IPv6 bits */ + key = ((pkt_parse >> FUN_ETH_RX_CV_OL4_PROT_S) & 6) | + ((pkt_parse >> (FUN_ETH_RX_CV_OL3_PROT_S + 1)) & 1); + + return htype_map[key]; +} + +/* Each received packet can be scattered across several Rx buffers or can + * share a buffer with previously received packets depending on the buffer + * and packet sizes and the room available in the most recently used buffer. + * + * The rules are: + * - If the buffer at the head of an RQ has not been used it gets (part of) the + * next incoming packet. + * - Otherwise, if the packet fully fits in the buffer's remaining space the + * packet is written there. + * - Otherwise, the packet goes into the next Rx buffer. + * + * This function returns the Rx buffer for a packet or fragment thereof of the + * given length. If it isn't @buf it either recycles or frees that buffer + * before advancing the queue to the next buffer. + * + * If called repeatedly with the remaining length of a packet it will walk + * through all the buffers containing the packet. + */ +static struct funeth_rxbuf * +get_buf(struct funeth_rxq *q, struct funeth_rxbuf *buf, unsigned int len) +{ + if (q->buf_offset + len <= PAGE_SIZE || !q->buf_offset) + return buf; /* @buf holds (part of) the packet */ + + /* The packet occupies part of the next buffer. Move there after + * replenishing the current buffer slot either with the spare page or + * by reusing the slot's existing page. Note that if a spare page isn't + * available and the current packet occupies @buf it is a multi-frag + * packet that will be dropped leaving @buf available for reuse. + */ + if ((page_ref_count(buf->page) == buf->pg_refs && + buf->node == numa_mem_id()) || !q->spare_buf.page) { + dma_sync_single_for_device(q->dma_dev, buf->dma_addr, + PAGE_SIZE, DMA_FROM_DEVICE); + refresh_refs(buf); + } else { + cache_offer(q, buf); + *buf = q->spare_buf; + q->spare_buf.page = NULL; + q->rqes[q->rq_cons & q->rq_mask] = + FUN_EPRQ_RQBUF_INIT(buf->dma_addr); + } + q->buf_offset = 0; + q->rq_cons++; + return &q->bufs[q->rq_cons & q->rq_mask]; +} + +/* Gather the page fragments making up the first Rx packet on @q. Its total + * length @tot_len includes optional head- and tail-rooms. + * + * Return 0 if the device retains ownership of at least some of the pages. + * In this case the caller may only copy the packet. + * + * A non-zero return value gives the caller permission to use references to the + * pages, e.g., attach them to skbs. Additionally, if the value is <0 at least + * one of the pages is PF_MEMALLOC. + * + * Regardless of outcome the caller is granted a reference to each of the pages. + */ +static int fun_gather_pkt(struct funeth_rxq *q, unsigned int tot_len, + skb_frag_t *frags) +{ + struct funeth_rxbuf *buf = q->cur_buf; + unsigned int frag_len; + int ref_ok = 1; + + for (;;) { + buf = get_buf(q, buf, tot_len); + + /* We always keep the RQ full of buffers so before we can give + * one of our pages to the stack we require that we can obtain + * a replacement page. If we can't the packet will either be + * copied or dropped so we can retain ownership of the page and + * reuse it. + */ + if (!q->spare_buf.page && + funeth_alloc_page(q, &q->spare_buf, numa_mem_id(), + GFP_ATOMIC | __GFP_MEMALLOC)) + ref_ok = 0; + + frag_len = min_t(unsigned int, tot_len, + PAGE_SIZE - q->buf_offset); + dma_sync_single_for_cpu(q->dma_dev, + buf->dma_addr + q->buf_offset, + frag_len, DMA_FROM_DEVICE); + buf->pg_refs--; + if (ref_ok) + ref_ok |= buf->node; + + __skb_frag_set_page(frags, buf->page); + skb_frag_off_set(frags, q->buf_offset); + skb_frag_size_set(frags++, frag_len); + + tot_len -= frag_len; + if (!tot_len) + break; + + q->buf_offset = PAGE_SIZE; + } + q->buf_offset = ALIGN(q->buf_offset + frag_len, FUN_EPRQ_PKT_ALIGN); + q->cur_buf = buf; + return ref_ok; +} + +static bool rx_hwtstamp_enabled(const struct net_device *dev) +{ + const struct funeth_priv *d = netdev_priv(dev); + + return d->hwtstamp_cfg.rx_filter == HWTSTAMP_FILTER_ALL; +} + +/* Advance the CQ pointers and phase tag to the next CQE. */ +static void advance_cq(struct funeth_rxq *q) +{ + if (unlikely(q->cq_head == q->cq_mask)) { + q->cq_head = 0; + q->phase ^= 1; + q->next_cqe_info = cqe_to_info(q->cqes); + } else { + q->cq_head++; + q->next_cqe_info += FUNETH_CQE_SIZE; + } + prefetch(q->next_cqe_info); +} + +/* Process the packet represented by the head CQE of @q. Gather the packet's + * fragments, run it through the optional XDP program, and if needed construct + * an skb and pass it to the stack. + */ +static void fun_handle_cqe_pkt(struct funeth_rxq *q, struct funeth_txq *xdp_q) +{ + const struct fun_eth_cqe *rxreq = info_to_cqe(q->next_cqe_info); + unsigned int i, tot_len, pkt_len = be32_to_cpu(rxreq->pkt_len); + struct net_device *ndev = q->netdev; + skb_frag_t frags[RX_MAX_FRAGS]; + struct skb_shared_info *si; + unsigned int headroom; + gro_result_t gro_res; + struct sk_buff *skb; + int ref_ok; + void *va; + u16 cv; + + u64_stats_update_begin(&q->syncp); + q->stats.rx_pkts++; + q->stats.rx_bytes += pkt_len; + u64_stats_update_end(&q->syncp); + + advance_cq(q); + + /* account for head- and tail-room, present only for 1-buffer packets */ + tot_len = pkt_len; + headroom = be16_to_cpu(rxreq->headroom); + if (likely(headroom)) + tot_len += FUN_RX_TAILROOM + headroom; + + ref_ok = fun_gather_pkt(q, tot_len, frags); + va = skb_frag_address(frags); + if (xdp_q && headroom == FUN_XDP_HEADROOM) { + va = fun_run_xdp(q, frags, va, ref_ok, xdp_q); + if (!va) + return; + headroom = 0; /* XDP_PASS trims it */ + } + if (unlikely(!ref_ok)) + goto no_mem; + + if (likely(headroom)) { + /* headroom is either FUN_RX_HEADROOM or FUN_XDP_HEADROOM */ + prefetch(va + headroom); + skb = napi_build_skb(va, ALIGN(tot_len, FUN_EPRQ_PKT_ALIGN)); + if (unlikely(!skb)) + goto no_mem; + + skb_reserve(skb, headroom); + __skb_put(skb, pkt_len); + skb->protocol = eth_type_trans(skb, ndev); + } else { + prefetch(va); + skb = napi_get_frags(q->napi); + if (unlikely(!skb)) + goto no_mem; + + if (ref_ok < 0) + skb->pfmemalloc = 1; + + si = skb_shinfo(skb); + si->nr_frags = rxreq->nsgl; + for (i = 0; i < si->nr_frags; i++) + si->frags[i] = frags[i]; + + skb->len = pkt_len; + skb->data_len = pkt_len; + skb->truesize += round_up(pkt_len, FUN_EPRQ_PKT_ALIGN); + } + + skb_record_rx_queue(skb, q->qidx); + cv = be16_to_cpu(rxreq->pkt_cv); + if (likely((q->netdev->features & NETIF_F_RXHASH) && rxreq->hash)) + skb_set_hash(skb, be32_to_cpu(rxreq->hash), + cqe_to_pkt_hash_type(cv)); + if (likely((q->netdev->features & NETIF_F_RXCSUM) && rxreq->csum)) { + FUN_QSTAT_INC(q, rx_cso); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->csum_level = be16_to_cpu(rxreq->csum) - 1; + } + if (unlikely(rx_hwtstamp_enabled(q->netdev))) + skb_hwtstamps(skb)->hwtstamp = be64_to_cpu(rxreq->timestamp); + + trace_funeth_rx(q, rxreq->nsgl, pkt_len, skb->hash, cv); + + gro_res = skb->data_len ? napi_gro_frags(q->napi) : + napi_gro_receive(q->napi, skb); + if (gro_res == GRO_MERGED || gro_res == GRO_MERGED_FREE) + FUN_QSTAT_INC(q, gro_merged); + else if (gro_res == GRO_HELD) + FUN_QSTAT_INC(q, gro_pkts); + return; + +no_mem: + FUN_QSTAT_INC(q, rx_mem_drops); + + /* Release the references we've been granted for the frag pages. + * We return the ref of the last frag and free the rest. + */ + q->cur_buf->pg_refs++; + for (i = 0; i < rxreq->nsgl - 1; i++) + __free_page(skb_frag_page(frags + i)); +} + +/* Return 0 if the phase tag of the CQE at the CQ's head matches expectations + * indicating the CQE is new. + */ +static u16 cqe_phase_mismatch(const struct fun_cqe_info *ci, u16 phase) +{ + u16 sf_p = be16_to_cpu(ci->sf_p); + + return (sf_p & 1) ^ phase; +} + +/* Walk through a CQ identifying and processing fresh CQEs up to the given + * budget. Return the remaining budget. + */ +static int fun_process_cqes(struct funeth_rxq *q, int budget) +{ + struct funeth_priv *fp = netdev_priv(q->netdev); + struct funeth_txq **xdpqs, *xdp_q = NULL; + + xdpqs = rcu_dereference_bh(fp->xdpqs); + if (xdpqs) + xdp_q = xdpqs[smp_processor_id()]; + + while (budget && !cqe_phase_mismatch(q->next_cqe_info, q->phase)) { + /* access other descriptor fields after the phase check */ + dma_rmb(); + + fun_handle_cqe_pkt(q, xdp_q); + budget--; + } + + if (unlikely(q->xdp_flush)) { + if (q->xdp_flush & FUN_XDP_FLUSH_TX) + fun_txq_wr_db(xdp_q); + if (q->xdp_flush & FUN_XDP_FLUSH_REDIR) + xdp_do_flush(); + q->xdp_flush = 0; + } + + return budget; +} + +/* NAPI handler for Rx queues. Calls the CQE processing loop and writes RQ/CQ + * doorbells as needed. + */ +int fun_rxq_napi_poll(struct napi_struct *napi, int budget) +{ + struct fun_irq *irq = container_of(napi, struct fun_irq, napi); + struct funeth_rxq *q = irq->rxq; + int work_done = budget - fun_process_cqes(q, budget); + u32 cq_db_val = q->cq_head; + + if (unlikely(work_done >= budget)) + FUN_QSTAT_INC(q, rx_budget); + else if (napi_complete_done(napi, work_done)) + cq_db_val |= q->irq_db_val; + + /* check whether to post new Rx buffers */ + if (q->rq_cons - q->rq_cons_db >= q->rq_db_thres) { + u64_stats_update_begin(&q->syncp); + q->stats.rx_bufs += q->rq_cons - q->rq_cons_db; + u64_stats_update_end(&q->syncp); + q->rq_cons_db = q->rq_cons; + writel((q->rq_cons - 1) & q->rq_mask, q->rq_db); + } + + writel(cq_db_val, q->cq_db); + return work_done; +} + +/* Free the Rx buffers of an Rx queue. */ +static void fun_rxq_free_bufs(struct funeth_rxq *q) +{ + struct funeth_rxbuf *b = q->bufs; + unsigned int i; + + for (i = 0; i <= q->rq_mask; i++, b++) + funeth_free_page(q, b); + + funeth_free_page(q, &q->spare_buf); + q->cur_buf = NULL; +} + +/* Initially provision an Rx queue with Rx buffers. */ +static int fun_rxq_alloc_bufs(struct funeth_rxq *q, int node) +{ + struct funeth_rxbuf *b = q->bufs; + unsigned int i; + + for (i = 0; i <= q->rq_mask; i++, b++) { + if (funeth_alloc_page(q, b, node, GFP_KERNEL)) { + fun_rxq_free_bufs(q); + return -ENOMEM; + } + q->rqes[i] = FUN_EPRQ_RQBUF_INIT(b->dma_addr); + } + q->cur_buf = q->bufs; + return 0; +} + +/* Initialize a used-buffer cache of the given depth. */ +static int fun_rxq_init_cache(struct funeth_rx_cache *c, unsigned int depth, + int node) +{ + c->mask = depth - 1; + c->bufs = kvzalloc_node(depth * sizeof(*c->bufs), GFP_KERNEL, node); + return c->bufs ? 0 : -ENOMEM; +} + +/* Deallocate an Rx queue's used-buffer cache and its contents. */ +static void fun_rxq_free_cache(struct funeth_rxq *q) +{ + struct funeth_rxbuf *b = q->cache.bufs; + unsigned int i; + + for (i = 0; i <= q->cache.mask; i++, b++) + funeth_free_page(q, b); + + kvfree(q->cache.bufs); + q->cache.bufs = NULL; +} + +int fun_rxq_set_bpf(struct funeth_rxq *q, struct bpf_prog *prog) +{ + struct funeth_priv *fp = netdev_priv(q->netdev); + struct fun_admin_epcq_req cmd; + u16 headroom; + int err; + + headroom = prog ? FUN_XDP_HEADROOM : FUN_RX_HEADROOM; + if (headroom != q->headroom) { + cmd.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_EPCQ, + sizeof(cmd)); + cmd.u.modify = + FUN_ADMIN_EPCQ_MODIFY_REQ_INIT(FUN_ADMIN_SUBOP_MODIFY, + 0, q->hw_cqid, headroom); + err = fun_submit_admin_sync_cmd(fp->fdev, &cmd.common, NULL, 0, + 0); + if (err) + return err; + q->headroom = headroom; + } + + WRITE_ONCE(q->xdp_prog, prog); + return 0; +} + +/* Create an Rx queue, allocating the host memory it needs. */ +static struct funeth_rxq *fun_rxq_create_sw(struct net_device *dev, + unsigned int qidx, + unsigned int ncqe, + unsigned int nrqe, + struct fun_irq *irq) +{ + struct funeth_priv *fp = netdev_priv(dev); + struct funeth_rxq *q; + int err = -ENOMEM; + int numa_node; + + numa_node = fun_irq_node(irq); + q = kzalloc_node(sizeof(*q), GFP_KERNEL, numa_node); + if (!q) + goto err; + + q->qidx = qidx; + q->netdev = dev; + q->cq_mask = ncqe - 1; + q->rq_mask = nrqe - 1; + q->numa_node = numa_node; + q->rq_db_thres = nrqe / 4; + u64_stats_init(&q->syncp); + q->dma_dev = &fp->pdev->dev; + + q->rqes = fun_alloc_ring_mem(q->dma_dev, nrqe, sizeof(*q->rqes), + sizeof(*q->bufs), false, numa_node, + &q->rq_dma_addr, (void **)&q->bufs, NULL); + if (!q->rqes) + goto free_q; + + q->cqes = fun_alloc_ring_mem(q->dma_dev, ncqe, FUNETH_CQE_SIZE, 0, + false, numa_node, &q->cq_dma_addr, NULL, + NULL); + if (!q->cqes) + goto free_rqes; + + err = fun_rxq_init_cache(&q->cache, nrqe, numa_node); + if (err) + goto free_cqes; + + err = fun_rxq_alloc_bufs(q, numa_node); + if (err) + goto free_cache; + + q->stats.rx_bufs = q->rq_mask; + q->init_state = FUN_QSTATE_INIT_SW; + return q; + +free_cache: + fun_rxq_free_cache(q); +free_cqes: + dma_free_coherent(q->dma_dev, ncqe * FUNETH_CQE_SIZE, q->cqes, + q->cq_dma_addr); +free_rqes: + fun_free_ring_mem(q->dma_dev, nrqe, sizeof(*q->rqes), false, q->rqes, + q->rq_dma_addr, q->bufs); +free_q: + kfree(q); +err: + netdev_err(dev, "Unable to allocate memory for Rx queue %u\n", qidx); + return ERR_PTR(err); +} + +static void fun_rxq_free_sw(struct funeth_rxq *q) +{ + struct funeth_priv *fp = netdev_priv(q->netdev); + + fun_rxq_free_cache(q); + fun_rxq_free_bufs(q); + fun_free_ring_mem(q->dma_dev, q->rq_mask + 1, sizeof(*q->rqes), false, + q->rqes, q->rq_dma_addr, q->bufs); + dma_free_coherent(q->dma_dev, (q->cq_mask + 1) * FUNETH_CQE_SIZE, + q->cqes, q->cq_dma_addr); + + /* Before freeing the queue transfer key counters to the device. */ + fp->rx_packets += q->stats.rx_pkts; + fp->rx_bytes += q->stats.rx_bytes; + fp->rx_dropped += q->stats.rx_map_err + q->stats.rx_mem_drops; + + kfree(q); +} + +/* Create an Rx queue's resources on the device. */ +int fun_rxq_create_dev(struct funeth_rxq *q, struct fun_irq *irq) +{ + struct funeth_priv *fp = netdev_priv(q->netdev); + unsigned int ncqe = q->cq_mask + 1; + unsigned int nrqe = q->rq_mask + 1; + int err; + + err = xdp_rxq_info_reg(&q->xdp_rxq, q->netdev, q->qidx, + irq->napi.napi_id); + if (err) + goto out; + + err = xdp_rxq_info_reg_mem_model(&q->xdp_rxq, MEM_TYPE_PAGE_SHARED, + NULL); + if (err) + goto xdp_unreg; + + q->phase = 1; + q->irq_cnt = 0; + q->cq_head = 0; + q->rq_cons = 0; + q->rq_cons_db = 0; + q->buf_offset = 0; + q->napi = &irq->napi; + q->irq_db_val = fp->cq_irq_db; + q->next_cqe_info = cqe_to_info(q->cqes); + + q->xdp_prog = fp->xdp_prog; + q->headroom = fp->xdp_prog ? FUN_XDP_HEADROOM : FUN_RX_HEADROOM; + + err = fun_sq_create(fp->fdev, FUN_ADMIN_RES_CREATE_FLAG_ALLOCATOR | + FUN_ADMIN_EPSQ_CREATE_FLAG_RQ, 0, + FUN_HCI_ID_INVALID, 0, nrqe, q->rq_dma_addr, 0, 0, + 0, 0, fp->fdev->kern_end_qid, PAGE_SHIFT, + &q->hw_sqid, &q->rq_db); + if (err) + goto xdp_unreg; + + err = fun_cq_create(fp->fdev, FUN_ADMIN_RES_CREATE_FLAG_ALLOCATOR | + FUN_ADMIN_EPCQ_CREATE_FLAG_RQ, 0, + q->hw_sqid, ilog2(FUNETH_CQE_SIZE), ncqe, + q->cq_dma_addr, q->headroom, FUN_RX_TAILROOM, 0, 0, + irq->irq_idx, 0, fp->fdev->kern_end_qid, + &q->hw_cqid, &q->cq_db); + if (err) + goto free_rq; + + irq->rxq = q; + writel(q->rq_mask, q->rq_db); + q->init_state = FUN_QSTATE_INIT_FULL; + + netif_info(fp, ifup, q->netdev, + "Rx queue %u, depth %u/%u, HW qid %u/%u, IRQ idx %u, node %d, headroom %u\n", + q->qidx, ncqe, nrqe, q->hw_cqid, q->hw_sqid, irq->irq_idx, + q->numa_node, q->headroom); + return 0; + +free_rq: + fun_destroy_sq(fp->fdev, q->hw_sqid); +xdp_unreg: + xdp_rxq_info_unreg(&q->xdp_rxq); +out: + netdev_err(q->netdev, + "Failed to create Rx queue %u on device, error %d\n", + q->qidx, err); + return err; +} + +static void fun_rxq_free_dev(struct funeth_rxq *q) +{ + struct funeth_priv *fp = netdev_priv(q->netdev); + struct fun_irq *irq; + + if (q->init_state < FUN_QSTATE_INIT_FULL) + return; + + irq = container_of(q->napi, struct fun_irq, napi); + netif_info(fp, ifdown, q->netdev, + "Freeing Rx queue %u (id %u/%u), IRQ %u\n", + q->qidx, q->hw_cqid, q->hw_sqid, irq->irq_idx); + + irq->rxq = NULL; + xdp_rxq_info_unreg(&q->xdp_rxq); + fun_destroy_sq(fp->fdev, q->hw_sqid); + fun_destroy_cq(fp->fdev, q->hw_cqid); + q->init_state = FUN_QSTATE_INIT_SW; +} + +/* Create or advance an Rx queue, allocating all the host and device resources + * needed to reach the target state. + */ +int funeth_rxq_create(struct net_device *dev, unsigned int qidx, + unsigned int ncqe, unsigned int nrqe, struct fun_irq *irq, + int state, struct funeth_rxq **qp) +{ + struct funeth_rxq *q = *qp; + int err; + + if (!q) { + q = fun_rxq_create_sw(dev, qidx, ncqe, nrqe, irq); + if (IS_ERR(q)) + return PTR_ERR(q); + } + + if (q->init_state >= state) + goto out; + + err = fun_rxq_create_dev(q, irq); + if (err) { + if (!*qp) + fun_rxq_free_sw(q); + return err; + } + +out: + *qp = q; + return 0; +} + +/* Free Rx queue resources until it reaches the target state. */ +struct funeth_rxq *funeth_rxq_free(struct funeth_rxq *q, int state) +{ + if (state < FUN_QSTATE_INIT_FULL) + fun_rxq_free_dev(q); + + if (state == FUN_QSTATE_DESTROYED) { + fun_rxq_free_sw(q); + q = NULL; + } + + return q; +} diff --git a/drivers/net/ethernet/fungible/funeth/funeth_trace.h b/drivers/net/ethernet/fungible/funeth/funeth_trace.h new file mode 100644 index 000000000000..9e58dfec19d5 --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/funeth_trace.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM funeth + +#if !defined(_TRACE_FUNETH_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_FUNETH_H + +#include + +#include "funeth_txrx.h" + +TRACE_EVENT(funeth_tx, + + TP_PROTO(const struct funeth_txq *txq, + u32 len, + u32 sqe_idx, + u32 ngle), + + TP_ARGS(txq, len, sqe_idx, ngle), + + TP_STRUCT__entry( + __field(u32, qidx) + __field(u32, len) + __field(u32, sqe_idx) + __field(u32, ngle) + __string(devname, txq->netdev->name) + ), + + TP_fast_assign( + __entry->qidx = txq->qidx; + __entry->len = len; + __entry->sqe_idx = sqe_idx; + __entry->ngle = ngle; + __assign_str(devname, txq->netdev->name); + ), + + TP_printk("%s: Txq %u, SQE idx %u, len %u, num GLEs %u", + __get_str(devname), __entry->qidx, __entry->sqe_idx, + __entry->len, __entry->ngle) +); + +TRACE_EVENT(funeth_tx_free, + + TP_PROTO(const struct funeth_txq *txq, + u32 sqe_idx, + u32 num_sqes, + u32 hw_head), + + TP_ARGS(txq, sqe_idx, num_sqes, hw_head), + + TP_STRUCT__entry( + __field(u32, qidx) + __field(u32, sqe_idx) + __field(u32, num_sqes) + __field(u32, hw_head) + __string(devname, txq->netdev->name) + ), + + TP_fast_assign( + __entry->qidx = txq->qidx; + __entry->sqe_idx = sqe_idx; + __entry->num_sqes = num_sqes; + __entry->hw_head = hw_head; + __assign_str(devname, txq->netdev->name); + ), + + TP_printk("%s: Txq %u, SQE idx %u, SQEs %u, HW head %u", + __get_str(devname), __entry->qidx, __entry->sqe_idx, + __entry->num_sqes, __entry->hw_head) +); + +TRACE_EVENT(funeth_rx, + + TP_PROTO(const struct funeth_rxq *rxq, + u32 num_rqes, + u32 pkt_len, + u32 hash, + u32 cls_vec), + + TP_ARGS(rxq, num_rqes, pkt_len, hash, cls_vec), + + TP_STRUCT__entry( + __field(u32, qidx) + __field(u32, cq_head) + __field(u32, num_rqes) + __field(u32, len) + __field(u32, hash) + __field(u32, cls_vec) + __string(devname, rxq->netdev->name) + ), + + TP_fast_assign( + __entry->qidx = rxq->qidx; + __entry->cq_head = rxq->cq_head; + __entry->num_rqes = num_rqes; + __entry->len = pkt_len; + __entry->hash = hash; + __entry->cls_vec = cls_vec; + __assign_str(devname, rxq->netdev->name); + ), + + TP_printk("%s: Rxq %u, CQ head %u, RQEs %u, len %u, hash %u, CV %#x", + __get_str(devname), __entry->qidx, __entry->cq_head, + __entry->num_rqes, __entry->len, __entry->hash, + __entry->cls_vec) +); + +#endif /* _TRACE_FUNETH_H */ + +/* Below must be outside protection. */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE funeth_trace + +#include diff --git a/drivers/net/ethernet/fungible/funeth/funeth_tx.c b/drivers/net/ethernet/fungible/funeth/funeth_tx.c new file mode 100644 index 000000000000..ff6e29237253 --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/funeth_tx.c @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +#include +#include +#include +#include +#include +#include +#include "funeth.h" +#include "funeth_ktls.h" +#include "funeth_txrx.h" +#include "funeth_trace.h" +#include "fun_queue.h" + +#define FUN_XDP_CLEAN_THRES 32 +#define FUN_XDP_CLEAN_BATCH 16 + +/* DMA-map a packet and return the (length, DMA_address) pairs for its + * segments. If a mapping error occurs -ENOMEM is returned. + */ +static int map_skb(const struct sk_buff *skb, struct device *dev, + dma_addr_t *addr, unsigned int *len) +{ + const struct skb_shared_info *si; + const skb_frag_t *fp, *end; + + *len = skb_headlen(skb); + *addr = dma_map_single(dev, skb->data, *len, DMA_TO_DEVICE); + if (dma_mapping_error(dev, *addr)) + return -ENOMEM; + + si = skb_shinfo(skb); + end = &si->frags[si->nr_frags]; + + for (fp = si->frags; fp < end; fp++) { + *++len = skb_frag_size(fp); + *++addr = skb_frag_dma_map(dev, fp, 0, *len, DMA_TO_DEVICE); + if (dma_mapping_error(dev, *addr)) + goto unwind; + } + return 0; + +unwind: + while (fp-- > si->frags) + dma_unmap_page(dev, *--addr, skb_frag_size(fp), DMA_TO_DEVICE); + + dma_unmap_single(dev, addr[-1], skb_headlen(skb), DMA_TO_DEVICE); + return -ENOMEM; +} + +/* Return the address just past the end of a Tx queue's descriptor ring. + * It exploits the fact that the HW writeback area is just after the end + * of the descriptor ring. + */ +static void *txq_end(const struct funeth_txq *q) +{ + return (void *)q->hw_wb; +} + +/* Return the amount of space within a Tx ring from the given address to the + * end. + */ +static unsigned int txq_to_end(const struct funeth_txq *q, void *p) +{ + return txq_end(q) - p; +} + +/* Return the number of Tx descriptors occupied by a Tx request. */ +static unsigned int tx_req_ndesc(const struct fun_eth_tx_req *req) +{ + return DIV_ROUND_UP(req->len8, FUNETH_SQE_SIZE / 8); +} + +static __be16 tcp_hdr_doff_flags(const struct tcphdr *th) +{ + return *(__be16 *)&tcp_flag_word(th); +} + +static struct sk_buff *fun_tls_tx(struct sk_buff *skb, struct funeth_txq *q, + unsigned int *tls_len) +{ +#if IS_ENABLED(CONFIG_TLS_DEVICE) + const struct fun_ktls_tx_ctx *tls_ctx; + u32 datalen, seq; + + datalen = skb->len - (skb_transport_offset(skb) + tcp_hdrlen(skb)); + if (!datalen) + return skb; + + if (likely(!tls_offload_tx_resync_pending(skb->sk))) { + seq = ntohl(tcp_hdr(skb)->seq); + tls_ctx = tls_driver_ctx(skb->sk, TLS_OFFLOAD_CTX_DIR_TX); + + if (likely(tls_ctx->next_seq == seq)) { + *tls_len = datalen; + return skb; + } + if (seq - tls_ctx->next_seq < U32_MAX / 4) { + tls_offload_tx_resync_request(skb->sk, seq, + tls_ctx->next_seq); + } + } + + FUN_QSTAT_INC(q, tx_tls_fallback); + skb = tls_encrypt_skb(skb); + if (!skb) + FUN_QSTAT_INC(q, tx_tls_drops); + + return skb; +#else + return NULL; +#endif +} + +/* Write as many descriptors as needed for the supplied skb starting at the + * current producer location. The caller has made certain enough descriptors + * are available. + * + * Returns the number of descriptors written, 0 on error. + */ +static unsigned int write_pkt_desc(struct sk_buff *skb, struct funeth_txq *q, + unsigned int tls_len) +{ + unsigned int extra_bytes = 0, extra_pkts = 0; + unsigned int idx = q->prod_cnt & q->mask; + const struct skb_shared_info *shinfo; + unsigned int lens[MAX_SKB_FRAGS + 1]; + dma_addr_t addrs[MAX_SKB_FRAGS + 1]; + struct fun_eth_tx_req *req; + struct fun_dataop_gl *gle; + const struct tcphdr *th; + unsigned int ngle, i; + u16 flags; + + if (unlikely(map_skb(skb, q->dma_dev, addrs, lens))) { + FUN_QSTAT_INC(q, tx_map_err); + return 0; + } + + req = fun_tx_desc_addr(q, idx); + req->op = FUN_ETH_OP_TX; + req->len8 = 0; + req->flags = 0; + req->suboff8 = offsetof(struct fun_eth_tx_req, dataop); + req->repr_idn = 0; + req->encap_proto = 0; + + shinfo = skb_shinfo(skb); + if (likely(shinfo->gso_size)) { + if (skb->encapsulation) { + u16 ol4_ofst; + + flags = FUN_ETH_OUTER_EN | FUN_ETH_INNER_LSO | + FUN_ETH_UPDATE_INNER_L4_CKSUM | + FUN_ETH_UPDATE_OUTER_L3_LEN; + if (shinfo->gso_type & (SKB_GSO_UDP_TUNNEL | + SKB_GSO_UDP_TUNNEL_CSUM)) { + flags |= FUN_ETH_UPDATE_OUTER_L4_LEN | + FUN_ETH_OUTER_UDP; + if (shinfo->gso_type & SKB_GSO_UDP_TUNNEL_CSUM) + flags |= FUN_ETH_UPDATE_OUTER_L4_CKSUM; + ol4_ofst = skb_transport_offset(skb); + } else { + ol4_ofst = skb_inner_network_offset(skb); + } + + if (ip_hdr(skb)->version == 4) + flags |= FUN_ETH_UPDATE_OUTER_L3_CKSUM; + else + flags |= FUN_ETH_OUTER_IPV6; + + if (skb->inner_network_header) { + if (inner_ip_hdr(skb)->version == 4) + flags |= FUN_ETH_UPDATE_INNER_L3_CKSUM | + FUN_ETH_UPDATE_INNER_L3_LEN; + else + flags |= FUN_ETH_INNER_IPV6 | + FUN_ETH_UPDATE_INNER_L3_LEN; + } + th = inner_tcp_hdr(skb); + fun_eth_offload_init(&req->offload, flags, + shinfo->gso_size, + tcp_hdr_doff_flags(th), 0, + skb_inner_network_offset(skb), + skb_inner_transport_offset(skb), + skb_network_offset(skb), ol4_ofst); + FUN_QSTAT_INC(q, tx_encap_tso); + } else { + /* HW considers one set of headers as inner */ + flags = FUN_ETH_INNER_LSO | + FUN_ETH_UPDATE_INNER_L4_CKSUM | + FUN_ETH_UPDATE_INNER_L3_LEN; + if (shinfo->gso_type & SKB_GSO_TCPV6) + flags |= FUN_ETH_INNER_IPV6; + else + flags |= FUN_ETH_UPDATE_INNER_L3_CKSUM; + th = tcp_hdr(skb); + fun_eth_offload_init(&req->offload, flags, + shinfo->gso_size, + tcp_hdr_doff_flags(th), 0, + skb_network_offset(skb), + skb_transport_offset(skb), 0, 0); + FUN_QSTAT_INC(q, tx_tso); + } + + u64_stats_update_begin(&q->syncp); + q->stats.tx_cso += shinfo->gso_segs; + u64_stats_update_end(&q->syncp); + + extra_pkts = shinfo->gso_segs - 1; + extra_bytes = (be16_to_cpu(req->offload.inner_l4_off) + + __tcp_hdrlen(th)) * extra_pkts; + } else if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { + flags = FUN_ETH_UPDATE_INNER_L4_CKSUM; + if (skb->csum_offset == offsetof(struct udphdr, check)) + flags |= FUN_ETH_INNER_UDP; + fun_eth_offload_init(&req->offload, flags, 0, 0, 0, 0, + skb_checksum_start_offset(skb), 0, 0); + FUN_QSTAT_INC(q, tx_cso); + } else { + fun_eth_offload_init(&req->offload, 0, 0, 0, 0, 0, 0, 0, 0); + } + + ngle = shinfo->nr_frags + 1; + req->len8 = (sizeof(*req) + ngle * sizeof(*gle)) / 8; + req->dataop = FUN_DATAOP_HDR_INIT(ngle, 0, ngle, 0, skb->len); + + for (i = 0, gle = (struct fun_dataop_gl *)req->dataop.imm; + i < ngle && txq_to_end(q, gle); i++, gle++) + fun_dataop_gl_init(gle, 0, 0, lens[i], addrs[i]); + + if (txq_to_end(q, gle) == 0) { + gle = (struct fun_dataop_gl *)q->desc; + for ( ; i < ngle; i++, gle++) + fun_dataop_gl_init(gle, 0, 0, lens[i], addrs[i]); + } + + if (IS_ENABLED(CONFIG_TLS_DEVICE) && unlikely(tls_len)) { + struct fun_eth_tls *tls = (struct fun_eth_tls *)gle; + struct fun_ktls_tx_ctx *tls_ctx; + + req->len8 += FUNETH_TLS_SZ / 8; + req->flags = cpu_to_be16(FUN_ETH_TX_TLS); + + tls_ctx = tls_driver_ctx(skb->sk, TLS_OFFLOAD_CTX_DIR_TX); + tls->tlsid = tls_ctx->tlsid; + tls_ctx->next_seq += tls_len; + + u64_stats_update_begin(&q->syncp); + q->stats.tx_tls_bytes += tls_len; + q->stats.tx_tls_pkts += 1 + extra_pkts; + u64_stats_update_end(&q->syncp); + } + + u64_stats_update_begin(&q->syncp); + q->stats.tx_bytes += skb->len + extra_bytes; + q->stats.tx_pkts += 1 + extra_pkts; + u64_stats_update_end(&q->syncp); + + q->info[idx].skb = skb; + + trace_funeth_tx(q, skb->len, idx, req->dataop.ngather); + return tx_req_ndesc(req); +} + +/* Return the number of available descriptors of a Tx queue. + * HW assumes head==tail means the ring is empty so we need to keep one + * descriptor unused. + */ +static unsigned int fun_txq_avail(const struct funeth_txq *q) +{ + return q->mask - q->prod_cnt + q->cons_cnt; +} + +/* Stop a queue if it can't handle another worst-case packet. */ +static void fun_tx_check_stop(struct funeth_txq *q) +{ + if (likely(fun_txq_avail(q) >= FUNETH_MAX_PKT_DESC)) + return; + + netif_tx_stop_queue(q->ndq); + + /* NAPI reclaim is freeing packets in parallel with us and we may race. + * We have stopped the queue but check again after synchronizing with + * reclaim. + */ + smp_mb(); + if (likely(fun_txq_avail(q) < FUNETH_MAX_PKT_DESC)) + FUN_QSTAT_INC(q, tx_nstops); + else + netif_tx_start_queue(q->ndq); +} + +/* Return true if a queue has enough space to restart. Current condition is + * that the queue must be >= 1/4 empty. + */ +static bool fun_txq_may_restart(struct funeth_txq *q) +{ + return fun_txq_avail(q) >= q->mask / 4; +} + +netdev_tx_t fun_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct funeth_priv *fp = netdev_priv(netdev); + unsigned int qid = skb_get_queue_mapping(skb); + struct funeth_txq *q = fp->txqs[qid]; + unsigned int tls_len = 0; + unsigned int ndesc; + + if (IS_ENABLED(CONFIG_TLS_DEVICE) && skb->sk && + tls_is_sk_tx_device_offloaded(skb->sk)) { + skb = fun_tls_tx(skb, q, &tls_len); + if (unlikely(!skb)) + goto dropped; + } + + ndesc = write_pkt_desc(skb, q, tls_len); + if (unlikely(!ndesc)) { + dev_kfree_skb_any(skb); + goto dropped; + } + + q->prod_cnt += ndesc; + fun_tx_check_stop(q); + + skb_tx_timestamp(skb); + + if (__netdev_tx_sent_queue(q->ndq, skb->len, netdev_xmit_more())) + fun_txq_wr_db(q); + else + FUN_QSTAT_INC(q, tx_more); + + return NETDEV_TX_OK; + +dropped: + /* A dropped packet may be the last one in a xmit_more train, + * ring the doorbell just in case. + */ + if (!netdev_xmit_more()) + fun_txq_wr_db(q); + return NETDEV_TX_OK; +} + +/* Return a Tx queue's HW head index written back to host memory. */ +static u16 txq_hw_head(const struct funeth_txq *q) +{ + return (u16)be64_to_cpu(*q->hw_wb); +} + +/* Unmap the Tx packet starting at the given descriptor index and + * return the number of Tx descriptors it occupied. + */ +static unsigned int unmap_skb(const struct funeth_txq *q, unsigned int idx) +{ + const struct fun_eth_tx_req *req = fun_tx_desc_addr(q, idx); + unsigned int ngle = req->dataop.ngather; + struct fun_dataop_gl *gle; + + if (ngle) { + gle = (struct fun_dataop_gl *)req->dataop.imm; + dma_unmap_single(q->dma_dev, be64_to_cpu(gle->sgl_data), + be32_to_cpu(gle->sgl_len), DMA_TO_DEVICE); + + for (gle++; --ngle && txq_to_end(q, gle); gle++) + dma_unmap_page(q->dma_dev, be64_to_cpu(gle->sgl_data), + be32_to_cpu(gle->sgl_len), + DMA_TO_DEVICE); + + for (gle = (struct fun_dataop_gl *)q->desc; ngle; ngle--, gle++) + dma_unmap_page(q->dma_dev, be64_to_cpu(gle->sgl_data), + be32_to_cpu(gle->sgl_len), + DMA_TO_DEVICE); + } + + return tx_req_ndesc(req); +} + +/* Reclaim completed Tx descriptors and free their packets. Restart a stopped + * queue if we freed enough descriptors. + * + * Return true if we exhausted the budget while there is more work to be done. + */ +static bool fun_txq_reclaim(struct funeth_txq *q, int budget) +{ + unsigned int npkts = 0, nbytes = 0, ndesc = 0; + unsigned int head, limit, reclaim_idx; + + /* budget may be 0, e.g., netpoll */ + limit = budget ? budget : UINT_MAX; + + for (head = txq_hw_head(q), reclaim_idx = q->cons_cnt & q->mask; + head != reclaim_idx && npkts < limit; head = txq_hw_head(q)) { + /* The HW head is continually updated, ensure we don't read + * descriptor state before the head tells us to reclaim it. + * On the enqueue side the doorbell is an implicit write + * barrier. + */ + rmb(); + + do { + unsigned int pkt_desc = unmap_skb(q, reclaim_idx); + struct sk_buff *skb = q->info[reclaim_idx].skb; + + trace_funeth_tx_free(q, reclaim_idx, pkt_desc, head); + + nbytes += skb->len; + napi_consume_skb(skb, budget); + ndesc += pkt_desc; + reclaim_idx = (reclaim_idx + pkt_desc) & q->mask; + npkts++; + } while (reclaim_idx != head && npkts < limit); + } + + q->cons_cnt += ndesc; + netdev_tx_completed_queue(q->ndq, npkts, nbytes); + smp_mb(); /* pairs with the one in fun_tx_check_stop() */ + + if (unlikely(netif_tx_queue_stopped(q->ndq) && + fun_txq_may_restart(q))) { + netif_tx_wake_queue(q->ndq); + FUN_QSTAT_INC(q, tx_nrestarts); + } + + return reclaim_idx != head; +} + +/* The NAPI handler for Tx queues. */ +int fun_txq_napi_poll(struct napi_struct *napi, int budget) +{ + struct fun_irq *irq = container_of(napi, struct fun_irq, napi); + struct funeth_txq *q = irq->txq; + unsigned int db_val; + + if (fun_txq_reclaim(q, budget)) + return budget; /* exhausted budget */ + + napi_complete(napi); /* exhausted pending work */ + db_val = READ_ONCE(q->irq_db_val) | (q->cons_cnt & q->mask); + writel(db_val, q->db); + return 0; +} + +static void fun_xdp_unmap(const struct funeth_txq *q, unsigned int idx) +{ + const struct fun_eth_tx_req *req = fun_tx_desc_addr(q, idx); + const struct fun_dataop_gl *gle; + + gle = (const struct fun_dataop_gl *)req->dataop.imm; + dma_unmap_single(q->dma_dev, be64_to_cpu(gle->sgl_data), + be32_to_cpu(gle->sgl_len), DMA_TO_DEVICE); +} + +/* Reclaim up to @budget completed Tx descriptors from a TX XDP queue. */ +static unsigned int fun_xdpq_clean(struct funeth_txq *q, unsigned int budget) +{ + unsigned int npkts = 0, head, reclaim_idx; + + for (head = txq_hw_head(q), reclaim_idx = q->cons_cnt & q->mask; + head != reclaim_idx && npkts < budget; head = txq_hw_head(q)) { + /* The HW head is continually updated, ensure we don't read + * descriptor state before the head tells us to reclaim it. + * On the enqueue side the doorbell is an implicit write + * barrier. + */ + rmb(); + + do { + fun_xdp_unmap(q, reclaim_idx); + page_frag_free(q->info[reclaim_idx].vaddr); + + trace_funeth_tx_free(q, reclaim_idx, 1, head); + + reclaim_idx = (reclaim_idx + 1) & q->mask; + npkts++; + } while (reclaim_idx != head && npkts < budget); + } + + q->cons_cnt += npkts; + return npkts; +} + +bool fun_xdp_tx(struct funeth_txq *q, void *data, unsigned int len) +{ + struct fun_eth_tx_req *req; + struct fun_dataop_gl *gle; + unsigned int idx; + dma_addr_t dma; + + if (fun_txq_avail(q) < FUN_XDP_CLEAN_THRES) + fun_xdpq_clean(q, FUN_XDP_CLEAN_BATCH); + + if (!unlikely(fun_txq_avail(q))) { + FUN_QSTAT_INC(q, tx_xdp_full); + return false; + } + + dma = dma_map_single(q->dma_dev, data, len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(q->dma_dev, dma))) { + FUN_QSTAT_INC(q, tx_map_err); + return false; + } + + idx = q->prod_cnt & q->mask; + req = fun_tx_desc_addr(q, idx); + req->op = FUN_ETH_OP_TX; + req->len8 = (sizeof(*req) + sizeof(*gle)) / 8; + req->flags = 0; + req->suboff8 = offsetof(struct fun_eth_tx_req, dataop); + req->repr_idn = 0; + req->encap_proto = 0; + fun_eth_offload_init(&req->offload, 0, 0, 0, 0, 0, 0, 0, 0); + req->dataop = FUN_DATAOP_HDR_INIT(1, 0, 1, 0, len); + + gle = (struct fun_dataop_gl *)req->dataop.imm; + fun_dataop_gl_init(gle, 0, 0, len, dma); + + q->info[idx].vaddr = data; + + u64_stats_update_begin(&q->syncp); + q->stats.tx_bytes += len; + q->stats.tx_pkts++; + u64_stats_update_end(&q->syncp); + + trace_funeth_tx(q, len, idx, 1); + q->prod_cnt++; + + return true; +} + +int fun_xdp_xmit_frames(struct net_device *dev, int n, + struct xdp_frame **frames, u32 flags) +{ + struct funeth_priv *fp = netdev_priv(dev); + struct funeth_txq *q, **xdpqs; + int i, q_idx; + + if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) + return -EINVAL; + + xdpqs = rcu_dereference_bh(fp->xdpqs); + if (unlikely(!xdpqs)) + return -ENETDOWN; + + q_idx = smp_processor_id(); + if (unlikely(q_idx >= fp->num_xdpqs)) + return -ENXIO; + + for (q = xdpqs[q_idx], i = 0; i < n; i++) { + const struct xdp_frame *xdpf = frames[i]; + + if (!fun_xdp_tx(q, xdpf->data, xdpf->len)) + break; + } + + if (unlikely(flags & XDP_XMIT_FLUSH)) + fun_txq_wr_db(q); + return i; +} + +/* Purge a Tx queue of any queued packets. Should be called once HW access + * to the packets has been revoked, e.g., after the queue has been disabled. + */ +static void fun_txq_purge(struct funeth_txq *q) +{ + while (q->cons_cnt != q->prod_cnt) { + unsigned int idx = q->cons_cnt & q->mask; + + q->cons_cnt += unmap_skb(q, idx); + dev_kfree_skb_any(q->info[idx].skb); + } + netdev_tx_reset_queue(q->ndq); +} + +static void fun_xdpq_purge(struct funeth_txq *q) +{ + while (q->cons_cnt != q->prod_cnt) { + unsigned int idx = q->cons_cnt & q->mask; + + fun_xdp_unmap(q, idx); + page_frag_free(q->info[idx].vaddr); + q->cons_cnt++; + } +} + +/* Create a Tx queue, allocating all the host resources needed. */ +static struct funeth_txq *fun_txq_create_sw(struct net_device *dev, + unsigned int qidx, + unsigned int ndesc, + struct fun_irq *irq) +{ + struct funeth_priv *fp = netdev_priv(dev); + struct funeth_txq *q; + int numa_node; + + if (irq) + numa_node = fun_irq_node(irq); /* skb Tx queue */ + else + numa_node = cpu_to_node(qidx); /* XDP Tx queue */ + + q = kzalloc_node(sizeof(*q), GFP_KERNEL, numa_node); + if (!q) + goto err; + + q->dma_dev = &fp->pdev->dev; + q->desc = fun_alloc_ring_mem(q->dma_dev, ndesc, FUNETH_SQE_SIZE, + sizeof(*q->info), true, numa_node, + &q->dma_addr, (void **)&q->info, + &q->hw_wb); + if (!q->desc) + goto free_q; + + q->netdev = dev; + q->mask = ndesc - 1; + q->qidx = qidx; + q->numa_node = numa_node; + u64_stats_init(&q->syncp); + q->init_state = FUN_QSTATE_INIT_SW; + return q; + +free_q: + kfree(q); +err: + netdev_err(dev, "Can't allocate memory for %s queue %u\n", + irq ? "Tx" : "XDP", qidx); + return NULL; +} + +static void fun_txq_free_sw(struct funeth_txq *q) +{ + struct funeth_priv *fp = netdev_priv(q->netdev); + + fun_free_ring_mem(q->dma_dev, q->mask + 1, FUNETH_SQE_SIZE, true, + q->desc, q->dma_addr, q->info); + + fp->tx_packets += q->stats.tx_pkts; + fp->tx_bytes += q->stats.tx_bytes; + fp->tx_dropped += q->stats.tx_map_err; + + kfree(q); +} + +/* Allocate the device portion of a Tx queue. */ +int fun_txq_create_dev(struct funeth_txq *q, struct fun_irq *irq) +{ + struct funeth_priv *fp = netdev_priv(q->netdev); + unsigned int irq_idx, ndesc = q->mask + 1; + int err; + + q->irq = irq; + *q->hw_wb = 0; + q->prod_cnt = 0; + q->cons_cnt = 0; + irq_idx = irq ? irq->irq_idx : 0; + + err = fun_sq_create(fp->fdev, + FUN_ADMIN_EPSQ_CREATE_FLAG_HEAD_WB_ADDRESS | + FUN_ADMIN_RES_CREATE_FLAG_ALLOCATOR, 0, + FUN_HCI_ID_INVALID, ilog2(FUNETH_SQE_SIZE), ndesc, + q->dma_addr, fp->tx_coal_count, fp->tx_coal_usec, + irq_idx, 0, fp->fdev->kern_end_qid, 0, + &q->hw_qid, &q->db); + if (err) + goto out; + + err = fun_create_and_bind_tx(fp, q->hw_qid); + if (err < 0) + goto free_devq; + q->ethid = err; + + if (irq) { + irq->txq = q; + q->ndq = netdev_get_tx_queue(q->netdev, q->qidx); + q->irq_db_val = FUN_IRQ_SQ_DB(fp->tx_coal_usec, + fp->tx_coal_count); + writel(q->irq_db_val, q->db); + } + + q->init_state = FUN_QSTATE_INIT_FULL; + netif_info(fp, ifup, q->netdev, + "%s queue %u, depth %u, HW qid %u, IRQ idx %u, eth id %u, node %d\n", + irq ? "Tx" : "XDP", q->qidx, ndesc, q->hw_qid, irq_idx, + q->ethid, q->numa_node); + return 0; + +free_devq: + fun_destroy_sq(fp->fdev, q->hw_qid); +out: + netdev_err(q->netdev, + "Failed to create %s queue %u on device, error %d\n", + irq ? "Tx" : "XDP", q->qidx, err); + return err; +} + +static void fun_txq_free_dev(struct funeth_txq *q) +{ + struct funeth_priv *fp = netdev_priv(q->netdev); + + if (q->init_state < FUN_QSTATE_INIT_FULL) + return; + + netif_info(fp, ifdown, q->netdev, + "Freeing %s queue %u (id %u), IRQ %u, ethid %u\n", + q->irq ? "Tx" : "XDP", q->qidx, q->hw_qid, + q->irq ? q->irq->irq_idx : 0, q->ethid); + + fun_destroy_sq(fp->fdev, q->hw_qid); + fun_res_destroy(fp->fdev, FUN_ADMIN_OP_ETH, 0, q->ethid); + + if (q->irq) { + q->irq->txq = NULL; + fun_txq_purge(q); + } else { + fun_xdpq_purge(q); + } + + q->init_state = FUN_QSTATE_INIT_SW; +} + +/* Create or advance a Tx queue, allocating all the host and device resources + * needed to reach the target state. + */ +int funeth_txq_create(struct net_device *dev, unsigned int qidx, + unsigned int ndesc, struct fun_irq *irq, int state, + struct funeth_txq **qp) +{ + struct funeth_txq *q = *qp; + int err; + + if (!q) + q = fun_txq_create_sw(dev, qidx, ndesc, irq); + if (!q) + return -ENOMEM; + + if (q->init_state >= state) + goto out; + + err = fun_txq_create_dev(q, irq); + if (err) { + if (!*qp) + fun_txq_free_sw(q); + return err; + } + +out: + *qp = q; + return 0; +} + +/* Free Tx queue resources until it reaches the target state. + * The queue must be already disconnected from the stack. + */ +struct funeth_txq *funeth_txq_free(struct funeth_txq *q, int state) +{ + if (state < FUN_QSTATE_INIT_FULL) + fun_txq_free_dev(q); + + if (state == FUN_QSTATE_DESTROYED) { + fun_txq_free_sw(q); + q = NULL; + } + + return q; +} diff --git a/drivers/net/ethernet/fungible/funeth/funeth_txrx.h b/drivers/net/ethernet/fungible/funeth/funeth_txrx.h new file mode 100644 index 000000000000..04c9f91b7489 --- /dev/null +++ b/drivers/net/ethernet/fungible/funeth/funeth_txrx.h @@ -0,0 +1,264 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +#ifndef _FUNETH_TXRX_H +#define _FUNETH_TXRX_H + +#include +#include + +/* Tx descriptor size */ +#define FUNETH_SQE_SIZE 64U + +/* Size of device headers per Tx packet */ +#define FUNETH_FUNOS_HDR_SZ (sizeof(struct fun_eth_tx_req)) + +/* Number of gather list entries per Tx descriptor */ +#define FUNETH_GLE_PER_DESC (FUNETH_SQE_SIZE / sizeof(struct fun_dataop_gl)) + +/* Max gather list size in bytes for an sk_buff. */ +#define FUNETH_MAX_GL_SZ ((MAX_SKB_FRAGS + 1) * sizeof(struct fun_dataop_gl)) + +#if IS_ENABLED(CONFIG_TLS_DEVICE) +# define FUNETH_TLS_SZ sizeof(struct fun_eth_tls) +#else +# define FUNETH_TLS_SZ 0 +#endif + +/* Max number of Tx descriptors for an sk_buff using a gather list. */ +#define FUNETH_MAX_GL_DESC \ + DIV_ROUND_UP((FUNETH_FUNOS_HDR_SZ + FUNETH_MAX_GL_SZ + FUNETH_TLS_SZ), \ + FUNETH_SQE_SIZE) + +/* Max number of Tx descriptors for any packet. */ +#define FUNETH_MAX_PKT_DESC FUNETH_MAX_GL_DESC + +/* Rx CQ descriptor size. */ +#define FUNETH_CQE_SIZE 64U + +/* Offset of cqe_info within a CQE. */ +#define FUNETH_CQE_INFO_OFFSET (FUNETH_CQE_SIZE - sizeof(struct fun_cqe_info)) + +/* Construct the IRQ portion of a CQ doorbell. The resulting value arms the + * interrupt with the supplied time delay and packet count moderation settings. + */ +#define FUN_IRQ_CQ_DB(usec, pkts) \ + (FUN_DB_IRQ_ARM_F | ((usec) << FUN_DB_INTCOAL_USEC_S) | \ + ((pkts) << FUN_DB_INTCOAL_ENTRIES_S)) + +/* As above for SQ doorbells. */ +#define FUN_IRQ_SQ_DB(usec, pkts) \ + (FUN_DB_IRQ_ARM_F | \ + ((usec) << FUN_DB_INTCOAL_USEC_S) | \ + ((pkts) << FUN_DB_INTCOAL_ENTRIES_S)) + +/* Per packet tailroom. Present only for 1-frag packets. */ +#define FUN_RX_TAILROOM SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + +/* Per packet headroom for XDP. Preferred over XDP_PACKET_HEADROOM to + * accommodate two packets per buffer for 4K pages and 1500B MTUs. + */ +#define FUN_XDP_HEADROOM 192 + +/* Initialization state of a queue. */ +enum { + FUN_QSTATE_DESTROYED, /* what queue? */ + FUN_QSTATE_INIT_SW, /* exists in SW, not on the device */ + FUN_QSTATE_INIT_FULL, /* exists both in SW and on device */ +}; + +/* Initialization state of an interrupt. */ +enum { + FUN_IRQ_INIT, /* initialized and in the XArray but inactive */ + FUN_IRQ_REQUESTED, /* request_irq() done */ + FUN_IRQ_ENABLED, /* processing enabled */ + FUN_IRQ_DISABLED, /* processing disabled */ +}; + +struct bpf_prog; + +struct funeth_txq_stats { /* per Tx queue SW counters */ + u64 tx_pkts; /* # of Tx packets */ + u64 tx_bytes; /* total bytes of Tx packets */ + u64 tx_cso; /* # of packets with checksum offload */ + u64 tx_tso; /* # of non-encapsulated TSO super-packets */ + u64 tx_encap_tso; /* # of encapsulated TSO super-packets */ + u64 tx_more; /* # of DBs elided due to xmit_more */ + u64 tx_nstops; /* # of times the queue has stopped */ + u64 tx_nrestarts; /* # of times the queue has restarted */ + u64 tx_map_err; /* # of packets dropped due to DMA mapping errors */ + u64 tx_xdp_full; /* # of XDP packets that could not be enqueued */ + u64 tx_tls_pkts; /* # of Tx TLS packets offloaded to HW */ + u64 tx_tls_bytes; /* Tx bytes of HW-handled TLS payload */ + u64 tx_tls_fallback; /* attempted Tx TLS offloads punted to SW */ + u64 tx_tls_drops; /* attempted Tx TLS offloads dropped */ +}; + +struct funeth_tx_info { /* per Tx descriptor state */ + union { + struct sk_buff *skb; /* associated packet */ + void *vaddr; /* start address for XDP */ + }; +}; + +struct funeth_txq { + /* RO cacheline of frequently accessed data */ + u32 mask; /* queue depth - 1 */ + u32 hw_qid; /* device ID of the queue */ + void *desc; /* base address of descriptor ring */ + struct funeth_tx_info *info; + struct device *dma_dev; /* device for DMA mappings */ + volatile __be64 *hw_wb; /* HW write-back location */ + u32 __iomem *db; /* SQ doorbell register address */ + struct netdev_queue *ndq; + dma_addr_t dma_addr; /* DMA address of descriptor ring */ + /* producer R/W cacheline */ + u16 qidx; /* queue index within net_device */ + u16 ethid; + u32 prod_cnt; /* producer counter */ + struct funeth_txq_stats stats; + /* shared R/W cacheline, primarily accessed by consumer */ + u32 irq_db_val; /* value written to IRQ doorbell */ + u32 cons_cnt; /* consumer (cleanup) counter */ + struct net_device *netdev; + struct fun_irq *irq; + int numa_node; + u8 init_state; /* queue initialization state */ + struct u64_stats_sync syncp; +}; + +struct funeth_rxq_stats { /* per Rx queue SW counters */ + u64 rx_pkts; /* # of received packets, including SW drops */ + u64 rx_bytes; /* total size of received packets */ + u64 rx_cso; /* # of packets with checksum offload */ + u64 rx_bufs; /* total # of Rx buffers provided to device */ + u64 gro_pkts; /* # of GRO superpackets */ + u64 gro_merged; /* # of pkts merged into existing GRO superpackets */ + u64 rx_page_alloc; /* # of page allocations for Rx buffers */ + u64 rx_budget; /* NAPI iterations that exhausted their budget */ + u64 rx_mem_drops; /* # of packets dropped due to memory shortage */ + u64 rx_map_err; /* # of page DMA mapping errors */ + u64 xdp_drops; /* XDP_DROPped packets */ + u64 xdp_tx; /* successful XDP transmits */ + u64 xdp_redir; /* successful XDP redirects */ + u64 xdp_err; /* packets dropped due to XDP errors */ +}; + +struct funeth_rxbuf { /* per Rx buffer state */ + struct page *page; /* associated page */ + dma_addr_t dma_addr; /* DMA address of page start */ + int pg_refs; /* page refs held by driver */ + int node; /* page node, or -1 if it is PF_MEMALLOC */ +}; + +struct funeth_rx_cache { /* cache of DMA-mapped previously used buffers */ + struct funeth_rxbuf *bufs; /* base of Rx buffer state ring */ + unsigned int prod_cnt; /* producer counter */ + unsigned int cons_cnt; /* consumer counter */ + unsigned int mask; /* depth - 1 */ +}; + +/* An Rx queue consists of a CQ and an SQ used to provide Rx buffers. */ +struct funeth_rxq { + struct net_device *netdev; + struct napi_struct *napi; + struct device *dma_dev; /* device for DMA mappings */ + void *cqes; /* base of CQ descriptor ring */ + const void *next_cqe_info; /* fun_cqe_info of next CQE */ + u32 __iomem *cq_db; /* CQ doorbell register address */ + unsigned int cq_head; /* CQ head index */ + unsigned int cq_mask; /* CQ depth - 1 */ + u16 phase; /* CQ phase tag */ + u16 qidx; /* queue index within net_device */ + unsigned int irq_db_val; /* IRQ info for CQ doorbell */ + struct fun_eprq_rqbuf *rqes; /* base of RQ descriptor ring */ + struct funeth_rxbuf *bufs; /* base of Rx buffer state ring */ + struct funeth_rxbuf *cur_buf; /* currently active buffer */ + u32 __iomem *rq_db; /* RQ doorbell register address */ + unsigned int rq_cons; /* RQ consumer counter */ + unsigned int rq_mask; /* RQ depth - 1 */ + unsigned int buf_offset; /* offset of next pkt in head buffer */ + u8 xdp_flush; /* XDP flush types needed at NAPI end */ + u8 init_state; /* queue initialization state */ + u16 headroom; /* per packet headroom */ + unsigned int rq_cons_db; /* value of rq_cons at last RQ db */ + unsigned int rq_db_thres; /* # of new buffers needed to write RQ db */ + struct funeth_rxbuf spare_buf; /* spare for next buffer replacement */ + struct funeth_rx_cache cache; /* used buffer cache */ + struct bpf_prog *xdp_prog; /* optional XDP BPF program */ + struct funeth_rxq_stats stats; + dma_addr_t cq_dma_addr; /* DMA address of CQE ring */ + dma_addr_t rq_dma_addr; /* DMA address of RQE ring */ + u16 irq_cnt; + u32 hw_cqid; /* device ID of the queue's CQ */ + u32 hw_sqid; /* device ID of the queue's SQ */ + int numa_node; + struct u64_stats_sync syncp; + struct xdp_rxq_info xdp_rxq; +}; + +#define FUN_QSTAT_INC(q, counter) \ + do { \ + u64_stats_update_begin(&(q)->syncp); \ + (q)->stats.counter++; \ + u64_stats_update_end(&(q)->syncp); \ + } while (0) + +#define FUN_QSTAT_READ(q, seq, stats_copy) \ + do { \ + seq = u64_stats_fetch_begin(&(q)->syncp); \ + stats_copy = (q)->stats; \ + } while (u64_stats_fetch_retry(&(q)->syncp, (seq))) + +#define FUN_INT_NAME_LEN (IFNAMSIZ + 16) + +struct fun_irq { + struct napi_struct napi; + struct funeth_txq *txq; + struct funeth_rxq *rxq; + u8 state; + u16 irq_idx; /* index of MSI-X interrupt */ + int irq; /* Linux IRQ vector */ + cpumask_t affinity_mask; /* IRQ affinity */ + struct irq_affinity_notify aff_notify; + char name[FUN_INT_NAME_LEN]; +} ____cacheline_internodealigned_in_smp; + +/* Return the start address of the idx-th Tx descriptor. */ +static inline void *fun_tx_desc_addr(const struct funeth_txq *q, + unsigned int idx) +{ + return q->desc + idx * FUNETH_SQE_SIZE; +} + +static inline void fun_txq_wr_db(const struct funeth_txq *q) +{ + unsigned int tail = q->prod_cnt & q->mask; + + writel(tail, q->db); +} + +static inline int fun_irq_node(const struct fun_irq *p) +{ + return cpu_to_mem(cpumask_first(&p->affinity_mask)); +} + +int fun_rxq_napi_poll(struct napi_struct *napi, int budget); +int fun_txq_napi_poll(struct napi_struct *napi, int budget); +netdev_tx_t fun_start_xmit(struct sk_buff *skb, struct net_device *netdev); +bool fun_xdp_tx(struct funeth_txq *q, void *data, unsigned int len); +int fun_xdp_xmit_frames(struct net_device *dev, int n, + struct xdp_frame **frames, u32 flags); + +int funeth_txq_create(struct net_device *dev, unsigned int qidx, + unsigned int ndesc, struct fun_irq *irq, int state, + struct funeth_txq **qp); +int fun_txq_create_dev(struct funeth_txq *q, struct fun_irq *irq); +struct funeth_txq *funeth_txq_free(struct funeth_txq *q, int state); +int funeth_rxq_create(struct net_device *dev, unsigned int qidx, + unsigned int ncqe, unsigned int nrqe, struct fun_irq *irq, + int state, struct funeth_rxq **qp); +int fun_rxq_create_dev(struct funeth_rxq *q, struct fun_irq *irq); +struct funeth_rxq *funeth_rxq_free(struct funeth_rxq *q, int state); +int fun_rxq_set_bpf(struct funeth_rxq *q, struct bpf_prog *prog); + +#endif /* _FUNETH_TXRX_H */ diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 54e51c8221b8..6cafee55efc3 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -857,8 +857,7 @@ static int gve_alloc_qpls(struct gve_priv *priv) int i, j; int err; - /* Raw addressing means no QPLs */ - if (priv->queue_format == GVE_GQI_RDA_FORMAT) + if (num_qpls == 0) return 0; priv->qpls = kvcalloc(num_qpls, sizeof(*priv->qpls), GFP_KERNEL); @@ -901,8 +900,7 @@ static void gve_free_qpls(struct gve_priv *priv) int num_qpls = gve_num_tx_qpls(priv) + gve_num_rx_qpls(priv); int i; - /* Raw addressing means no QPLs */ - if (priv->queue_format == GVE_GQI_RDA_FORMAT) + if (num_qpls == 0) return; kvfree(priv->qpl_cfg.qpl_id_map); diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index e4e98aa7745f..021bbf308d68 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -439,7 +439,7 @@ static bool gve_rx_ctx_init(struct gve_rx_ctx *ctx, struct gve_rx_ring *rx) if (frag_size > rx->packet_buffer_size) { packet_size_error = true; netdev_warn(priv->dev, - "RX fragment error: packet_buffer_size=%d, frag_size=%d, droping packet.", + "RX fragment error: packet_buffer_size=%d, frag_size=%d, dropping packet.", rx->packet_buffer_size, be16_to_cpu(desc->len)); } page_info = &rx->data.page_info[idx]; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index d7a27c244d48..54faf0f2d1d8 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -887,8 +887,8 @@ static void hns_get_ethtool_stats(struct net_device *netdev, p[21] = net_stats->rx_compressed; p[22] = net_stats->tx_compressed; - p[23] = netdev->rx_dropped.counter; - p[24] = netdev->tx_dropped.counter; + p[23] = 0; /* was netdev->rx_dropped.counter */ + p[24] = 0; /* was netdev->tx_dropped.counter */ p[25] = priv->tx_timeout_count; diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h index 9298fbecb31a..6f18c9a03231 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h @@ -167,6 +167,7 @@ struct hnae3_handle; struct hnae3_queue { void __iomem *io_base; + void __iomem *mem_base; struct hnae3_ae_algo *ae_algo; struct hnae3_handle *handle; int tqp_index; /* index in a handle */ diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index babc5d7a3b52..0b8a73c40b12 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -2028,9 +2028,73 @@ static int hns3_fill_skb_to_desc(struct hns3_enet_ring *ring, return bd_num; } +static void hns3_tx_push_bd(struct hns3_enet_ring *ring, int num) +{ +#define HNS3_BYTES_PER_64BIT 8 + + struct hns3_desc desc[HNS3_MAX_PUSH_BD_NUM] = {}; + int offset = 0; + + /* make sure everything is visible to device before + * excuting tx push or updating doorbell + */ + dma_wmb(); + + do { + int idx = (ring->next_to_use - num + ring->desc_num) % + ring->desc_num; + + u64_stats_update_begin(&ring->syncp); + ring->stats.tx_push++; + u64_stats_update_end(&ring->syncp); + memcpy(&desc[offset], &ring->desc[idx], + sizeof(struct hns3_desc)); + offset++; + } while (--num); + + __iowrite64_copy(ring->tqp->mem_base, desc, + (sizeof(struct hns3_desc) * HNS3_MAX_PUSH_BD_NUM) / + HNS3_BYTES_PER_64BIT); + + io_stop_wc(); +} + +static void hns3_tx_mem_doorbell(struct hns3_enet_ring *ring) +{ +#define HNS3_MEM_DOORBELL_OFFSET 64 + + __le64 bd_num = cpu_to_le64((u64)ring->pending_buf); + + /* make sure everything is visible to device before + * excuting tx push or updating doorbell + */ + dma_wmb(); + + __iowrite64_copy(ring->tqp->mem_base + HNS3_MEM_DOORBELL_OFFSET, + &bd_num, 1); + u64_stats_update_begin(&ring->syncp); + ring->stats.tx_mem_doorbell += ring->pending_buf; + u64_stats_update_end(&ring->syncp); + + io_stop_wc(); +} + static void hns3_tx_doorbell(struct hns3_enet_ring *ring, int num, bool doorbell) { + struct net_device *netdev = ring_to_netdev(ring); + struct hns3_nic_priv *priv = netdev_priv(netdev); + + /* when tx push is enabled, the packet whose number of BD below + * HNS3_MAX_PUSH_BD_NUM can be pushed directly. + */ + if (test_bit(HNS3_NIC_STATE_TX_PUSH_ENABLE, &priv->state) && num && + !ring->pending_buf && num <= HNS3_MAX_PUSH_BD_NUM && doorbell) { + hns3_tx_push_bd(ring, num); + WRITE_ONCE(ring->last_to_use, ring->next_to_use); + return; + } + ring->pending_buf += num; if (!doorbell) { @@ -2038,11 +2102,12 @@ static void hns3_tx_doorbell(struct hns3_enet_ring *ring, int num, return; } - if (!ring->pending_buf) - return; + if (ring->tqp->mem_base) + hns3_tx_mem_doorbell(ring); + else + writel(ring->pending_buf, + ring->tqp->io_base + HNS3_RING_TX_RING_TAIL_REG); - writel(ring->pending_buf, - ring->tqp->io_base + HNS3_RING_TX_RING_TAIL_REG); ring->pending_buf = 0; WRITE_ONCE(ring->last_to_use, ring->next_to_use); } @@ -2732,6 +2797,9 @@ static void hns3_dump_queue_stats(struct net_device *ndev, "seg_pkt_cnt: %llu, tx_more: %llu, restart_queue: %llu, tx_busy: %llu\n", tx_ring->stats.seg_pkt_cnt, tx_ring->stats.tx_more, tx_ring->stats.restart_queue, tx_ring->stats.tx_busy); + + netdev_info(ndev, "tx_push: %llu, tx_mem_doorbell: %llu\n", + tx_ring->stats.tx_push, tx_ring->stats.tx_mem_doorbell); } static void hns3_dump_queue_reg(struct net_device *ndev, @@ -5094,6 +5162,9 @@ static void hns3_state_init(struct hnae3_handle *handle) set_bit(HNS3_NIC_STATE_INITED, &priv->state); + if (test_bit(HNAE3_DEV_SUPPORT_TX_PUSH_B, ae_dev->caps)) + set_bit(HNS3_NIC_STATE_TX_PUSH_ENABLE, &priv->state); + if (ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3) set_bit(HNAE3_PFLAG_LIMIT_PROMISC, &handle->supported_pflags); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h index a05a0c7423ce..4a3253692dcc 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "hnae3.h" @@ -25,9 +26,12 @@ enum hns3_nic_state { HNS3_NIC_STATE2_RESET_REQUESTED, HNS3_NIC_STATE_HW_TX_CSUM_ENABLE, HNS3_NIC_STATE_RXD_ADV_LAYOUT_ENABLE, + HNS3_NIC_STATE_TX_PUSH_ENABLE, HNS3_NIC_STATE_MAX }; +#define HNS3_MAX_PUSH_BD_NUM 2 + #define HNS3_RING_RX_RING_BASEADDR_L_REG 0x00000 #define HNS3_RING_RX_RING_BASEADDR_H_REG 0x00004 #define HNS3_RING_RX_RING_BD_NUM_REG 0x00008 @@ -410,6 +414,8 @@ struct ring_stats { u64 tx_pkts; u64 tx_bytes; u64 tx_more; + u64 tx_push; + u64 tx_mem_doorbell; u64 restart_queue; u64 tx_busy; u64 tx_copy; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c index c06c39ece80d..6469238ae090 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c @@ -23,6 +23,8 @@ static const struct hns3_stats hns3_txq_stats[] = { HNS3_TQP_STAT("packets", tx_pkts), HNS3_TQP_STAT("bytes", tx_bytes), HNS3_TQP_STAT("more", tx_more), + HNS3_TQP_STAT("push", tx_push), + HNS3_TQP_STAT("mem_doorbell", tx_mem_doorbell), HNS3_TQP_STAT("wake", restart_queue), HNS3_TQP_STAT("busy", tx_busy), HNS3_TQP_STAT("copy", tx_copy), diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 24f7afacae02..78d0498bdabc 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -1643,6 +1643,7 @@ static int hclge_config_gro(struct hclge_dev *hdev) static int hclge_alloc_tqps(struct hclge_dev *hdev) { + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev); struct hclge_comm_tqp *tqp; int i; @@ -1676,6 +1677,14 @@ static int hclge_alloc_tqps(struct hclge_dev *hdev) (i - HCLGE_TQP_MAX_SIZE_DEV_V2) * HCLGE_TQP_REG_SIZE; + /* when device supports tx push and has device memory, + * the queue can execute push mode or doorbell mode on + * device memory. + */ + if (test_bit(HNAE3_DEV_SUPPORT_TX_PUSH_B, ae_dev->caps)) + tqp->q.mem_base = hdev->hw.hw.mem_base + + HCLGE_TQP_MEM_OFFSET(hdev, i); + tqp++; } @@ -11008,8 +11017,6 @@ static void hclge_uninit_client_instance(struct hnae3_client *client, static int hclge_dev_mem_map(struct hclge_dev *hdev) { -#define HCLGE_MEM_BAR 4 - struct pci_dev *pdev = hdev->pdev; struct hclge_hw *hw = &hdev->hw; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index adfb26e79262..fc92ae385e30 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -169,6 +169,14 @@ enum HLCGE_PORT_TYPE { #define HCLGE_VECTOR0_ALL_MSIX_ERR_B 6U #define HCLGE_TRIGGER_IMP_RESET_B 7U +#define HCLGE_TQP_MEM_SIZE 0x10000 +#define HCLGE_MEM_BAR 4 +/* in the bar4, the first half is for roce, and the second half is for nic */ +#define HCLGE_NIC_MEM_OFFSET(hdev) \ + (pci_resource_len((hdev)->pdev, HCLGE_MEM_BAR) >> 1) +#define HCLGE_TQP_MEM_OFFSET(hdev, i) \ + (HCLGE_NIC_MEM_OFFSET(hdev) + HCLGE_TQP_MEM_SIZE * (i)) + #define HCLGE_MAC_DEFAULT_FRAME \ (ETH_HLEN + ETH_FCS_LEN + 2 * VLAN_HLEN + ETH_DATA_LEN) #define HCLGE_MAC_MIN_FRAME 64 @@ -1060,11 +1068,6 @@ static inline int hclge_get_queue_id(struct hnae3_queue *queue) return tqp->index; } -static inline bool hclge_is_reset_pending(struct hclge_dev *hdev) -{ - return !!hdev->reset_pending; -} - int hclge_inform_reset_assert_to_vf(struct hclge_vport *vport); int hclge_cfg_mac_speed_dup(struct hclge_dev *hdev, int speed, u8 duplex); int hclge_set_vlan_filter(struct hnae3_handle *handle, __be16 proto, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c index 089f4444b7e3..1f87a8a3fe32 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c @@ -1225,7 +1225,7 @@ static int hclge_tm_pri_dwrr_cfg(struct hclge_dev *hdev) ret = hclge_tm_ets_tc_dwrr_cfg(hdev); if (ret == -EOPNOTSUPP) { dev_warn(&hdev->pdev->dev, - "fw %08x does't support ets tc weight cmd\n", + "fw %08x doesn't support ets tc weight cmd\n", hdev->fw_version); ret = 0; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 21442a9bb996..93389bec8d89 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -321,6 +321,7 @@ static int hclgevf_get_pf_media_type(struct hclgevf_dev *hdev) static int hclgevf_alloc_tqps(struct hclgevf_dev *hdev) { + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev); struct hclge_comm_tqp *tqp; int i; @@ -354,6 +355,14 @@ static int hclgevf_alloc_tqps(struct hclgevf_dev *hdev) (i - HCLGEVF_TQP_MAX_SIZE_DEV_V2) * HCLGEVF_TQP_REG_SIZE; + /* when device supports tx push and has device memory, + * the queue can execute push mode or doorbell mode on + * device memory. + */ + if (test_bit(HNAE3_DEV_SUPPORT_TX_PUSH_B, ae_dev->caps)) + tqp->q.mem_base = hdev->hw.hw.mem_base + + HCLGEVF_TQP_MEM_OFFSET(hdev, i); + tqp++; } @@ -2546,8 +2555,6 @@ static void hclgevf_uninit_client_instance(struct hnae3_client *client, static int hclgevf_dev_mem_map(struct hclgevf_dev *hdev) { -#define HCLGEVF_MEM_BAR 4 - struct pci_dev *pdev = hdev->pdev; struct hclgevf_hw *hw = &hdev->hw; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h index 502ca1ce1a90..4b00fd44f118 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h @@ -96,6 +96,14 @@ #define HCLGEVF_RSS_IND_TBL_SIZE 512 +#define HCLGEVF_TQP_MEM_SIZE 0x10000 +#define HCLGEVF_MEM_BAR 4 +/* in the bar4, the first half is for roce, and the second half is for nic */ +#define HCLGEVF_NIC_MEM_OFFSET(hdev) \ + (pci_resource_len((hdev)->pdev, HCLGEVF_MEM_BAR) >> 1) +#define HCLGEVF_TQP_MEM_OFFSET(hdev, i) \ + (HCLGEVF_NIC_MEM_OFFSET(hdev) + HCLGEVF_TQP_MEM_SIZE * (i)) + #define HCLGEVF_MAC_MAX_FRAME 9728 #define HCLGEVF_STATS_TIMER_INTERVAL 36U diff --git a/drivers/net/ethernet/i825xx/sun3_82586.h b/drivers/net/ethernet/i825xx/sun3_82586.h index 451cb3d26cb5..d82eca563266 100644 --- a/drivers/net/ethernet/i825xx/sun3_82586.h +++ b/drivers/net/ethernet/i825xx/sun3_82586.h @@ -150,7 +150,7 @@ struct rfd_struct #define RFD_ERR_RNR 0x02 /* status: receiver out of resources */ #define RFD_ERR_OVR 0x01 /* DMA Overrun! */ -#define RFD_ERR_FTS 0x0080 /* Frame to short */ +#define RFD_ERR_FTS 0x0080 /* Frame too short */ #define RFD_ERR_NEOP 0x0040 /* No EOP flag (for bitstuffing only) */ #define RFD_ERR_TRUN 0x0020 /* (82596 only/SF mode) indicates truncated frame */ #define RFD_MATCHADD 0x0002 /* status: Destinationaddress !matches IA (only 82596) */ diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index b423e94956f1..77683909ca3d 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -1429,6 +1430,15 @@ static int __ibmvnic_open(struct net_device *netdev) return rc; } + adapter->tx_queues_active = true; + + /* Since queues were stopped until now, there shouldn't be any + * one in ibmvnic_complete_tx() or ibmvnic_xmit() so maybe we + * don't need the synchronize_rcu()? Leaving it for consistency + * with setting ->tx_queues_active = false. + */ + synchronize_rcu(); + netif_tx_start_all_queues(netdev); if (prev_state == VNIC_CLOSED) { @@ -1603,6 +1613,14 @@ static void ibmvnic_cleanup(struct net_device *netdev) struct ibmvnic_adapter *adapter = netdev_priv(netdev); /* ensure that transmissions are stopped if called by do_reset */ + + adapter->tx_queues_active = false; + + /* Ensure complete_tx() and ibmvnic_xmit() see ->tx_queues_active + * update so they don't restart a queue after we stop it below. + */ + synchronize_rcu(); + if (test_bit(0, &adapter->resetting)) netif_tx_disable(netdev); else @@ -1842,14 +1860,21 @@ static void ibmvnic_tx_scrq_clean_buffer(struct ibmvnic_adapter *adapter, tx_buff->skb = NULL; adapter->netdev->stats.tx_dropped++; } + ind_bufp->index = 0; + if (atomic_sub_return(entries, &tx_scrq->used) <= (adapter->req_tx_entries_per_subcrq / 2) && - __netif_subqueue_stopped(adapter->netdev, queue_num) && - !test_bit(0, &adapter->resetting)) { - netif_wake_subqueue(adapter->netdev, queue_num); - netdev_dbg(adapter->netdev, "Started queue %d\n", - queue_num); + __netif_subqueue_stopped(adapter->netdev, queue_num)) { + rcu_read_lock(); + + if (adapter->tx_queues_active) { + netif_wake_subqueue(adapter->netdev, queue_num); + netdev_dbg(adapter->netdev, "Started queue %d\n", + queue_num); + } + + rcu_read_unlock(); } } @@ -1904,11 +1929,12 @@ static netdev_tx_t ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) int index = 0; u8 proto = 0; - tx_scrq = adapter->tx_scrq[queue_num]; - txq = netdev_get_tx_queue(netdev, queue_num); - ind_bufp = &tx_scrq->ind_buf; - - if (test_bit(0, &adapter->resetting)) { + /* If a reset is in progress, drop the packet since + * the scrqs may get torn down. Otherwise use the + * rcu to ensure reset waits for us to complete. + */ + rcu_read_lock(); + if (!adapter->tx_queues_active) { dev_kfree_skb_any(skb); tx_send_failed++; @@ -1917,6 +1943,10 @@ static netdev_tx_t ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) goto out; } + tx_scrq = adapter->tx_scrq[queue_num]; + txq = netdev_get_tx_queue(netdev, queue_num); + ind_bufp = &tx_scrq->ind_buf; + if (ibmvnic_xmit_workarounds(skb, netdev)) { tx_dropped++; tx_send_failed++; @@ -1924,6 +1954,7 @@ static netdev_tx_t ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) ibmvnic_tx_scrq_flush(adapter, tx_scrq); goto out; } + if (skb_is_gso(skb)) tx_pool = &adapter->tso_pool[queue_num]; else @@ -2078,6 +2109,7 @@ static netdev_tx_t ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) netif_carrier_off(netdev); } out: + rcu_read_unlock(); netdev->stats.tx_dropped += tx_dropped; netdev->stats.tx_bytes += tx_bytes; netdev->stats.tx_packets += tx_packets; @@ -3640,6 +3672,30 @@ static int disable_scrq_irq(struct ibmvnic_adapter *adapter, return rc; } +/* We can not use the IRQ chip EOI handler because that has the + * unintended effect of changing the interrupt priority. + */ +static void ibmvnic_xics_eoi(struct device *dev, struct ibmvnic_sub_crq_queue *scrq) +{ + u64 val = 0xff000000 | scrq->hw_irq; + unsigned long rc; + + rc = plpar_hcall_norets(H_EOI, val); + if (rc) + dev_err(dev, "H_EOI FAILED irq 0x%llx. rc=%ld\n", val, rc); +} + +/* Due to a firmware bug, the hypervisor can send an interrupt to a + * transmit or receive queue just prior to a partition migration. + * Force an EOI after migration. + */ +static void ibmvnic_clear_pending_interrupt(struct device *dev, + struct ibmvnic_sub_crq_queue *scrq) +{ + if (!xive_enabled()) + ibmvnic_xics_eoi(dev, scrq); +} + static int enable_scrq_irq(struct ibmvnic_adapter *adapter, struct ibmvnic_sub_crq_queue *scrq) { @@ -3653,15 +3709,7 @@ static int enable_scrq_irq(struct ibmvnic_adapter *adapter, if (test_bit(0, &adapter->resetting) && adapter->reset_reason == VNIC_RESET_MOBILITY) { - u64 val = (0xff000000) | scrq->hw_irq; - - rc = plpar_hcall_norets(H_EOI, val); - /* H_EOI would fail with rc = H_FUNCTION when running - * in XIVE mode which is expected, but not an error. - */ - if (rc && (rc != H_FUNCTION)) - dev_err(dev, "H_EOI FAILED irq 0x%llx. rc=%ld\n", - val, rc); + ibmvnic_clear_pending_interrupt(dev, scrq); } rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address, @@ -3732,9 +3780,15 @@ static int ibmvnic_complete_tx(struct ibmvnic_adapter *adapter, (adapter->req_tx_entries_per_subcrq / 2) && __netif_subqueue_stopped(adapter->netdev, scrq->pool_index)) { - netif_wake_subqueue(adapter->netdev, scrq->pool_index); - netdev_dbg(adapter->netdev, "Started queue %d\n", - scrq->pool_index); + rcu_read_lock(); + if (adapter->tx_queues_active) { + netif_wake_subqueue(adapter->netdev, + scrq->pool_index); + netdev_dbg(adapter->netdev, + "Started queue %d\n", + scrq->pool_index); + } + rcu_read_unlock(); } } diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index fa2d607a7b1b..8f5cefb932dd 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -1006,11 +1006,14 @@ struct ibmvnic_adapter { struct work_struct ibmvnic_reset; struct delayed_work ibmvnic_delayed_reset; unsigned long resetting; - bool napi_enabled, from_passive_init; - bool login_pending; /* last device reset time */ unsigned long last_reset_time; + bool napi_enabled; + bool from_passive_init; + bool login_pending; + /* protected by rcu */ + bool tx_queues_active; bool failover_pending; bool force_reset_recovery; diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index c5bdef3ffe26..fa06f68c8c80 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -7414,9 +7414,9 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) resource_size_t flash_start, flash_len; static int cards_found; u16 aspm_disable_flag = 0; - int bars, i, err, pci_using_dac; u16 eeprom_data = 0; u16 eeprom_apme_mask = E1000_EEPROM_APME; + int bars, i, err; s32 ret_val = 0; if (ei->flags2 & FLAG2_DISABLE_ASPM_L0S) @@ -7430,17 +7430,11 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) return err; - pci_using_dac = 0; err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); - if (!err) { - pci_using_dac = 1; - } else { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, - "No usable DMA configuration, aborting\n"); - goto err_dma; - } + if (err) { + dev_err(&pdev->dev, + "No usable DMA configuration, aborting\n"); + goto err_dma; } bars = pci_select_bars(pdev, IORESOURCE_MEM); @@ -7576,10 +7570,8 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->priv_flags |= IFF_UNICAST_FLT; - if (pci_using_dac) { - netdev->features |= NETIF_F_HIGHDMA; - netdev->vlan_features |= NETIF_F_HIGHDMA; - } + netdev->features |= NETIF_F_HIGHDMA; + netdev->vlan_features |= NETIF_F_HIGHDMA; /* MTU range: 68 - max_hw_frame_size */ netdev->min_mtu = ETH_MIN_MTU; diff --git a/drivers/net/ethernet/intel/e1000e/phy.c b/drivers/net/ethernet/intel/e1000e/phy.c index 0f0efee5fc8e..fd07c3679bb1 100644 --- a/drivers/net/ethernet/intel/e1000e/phy.c +++ b/drivers/net/ethernet/intel/e1000e/phy.c @@ -146,11 +146,11 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) break; } if (!(mdic & E1000_MDIC_READY)) { - e_dbg("MDI Read did not complete\n"); + e_dbg("MDI Read PHY Reg Address %d did not complete\n", offset); return -E1000_ERR_PHY; } if (mdic & E1000_MDIC_ERROR) { - e_dbg("MDI Error\n"); + e_dbg("MDI Read PHY Reg Address %d Error\n", offset); return -E1000_ERR_PHY; } if (((mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT) != offset) { @@ -210,11 +210,11 @@ s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) break; } if (!(mdic & E1000_MDIC_READY)) { - e_dbg("MDI Write did not complete\n"); + e_dbg("MDI Write PHY Reg Address %d did not complete\n", offset); return -E1000_ERR_PHY; } if (mdic & E1000_MDIC_ERROR) { - e_dbg("MDI Error\n"); + e_dbg("MDI Write PHY Red Address %d Error\n", offset); return -E1000_ERR_PHY; } if (((mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT) != offset) { diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 80c5cecaf2b5..55c6bce5da61 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -854,6 +854,10 @@ struct i40e_vsi { u64 tx_force_wb; u64 rx_buf_failed; u64 rx_page_failed; + u64 rx_page_reuse; + u64 rx_page_alloc; + u64 rx_page_waive; + u64 rx_page_busy; /* These are containers of ring pointers, allocated at run-time */ struct i40e_ring **rx_rings; diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index 7abef88801fb..42439f725aa4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -769,7 +769,7 @@ static bool i40e_asq_done(struct i40e_hw *hw) } /** - * i40e_asq_send_command_atomic - send command to Admin Queue + * i40e_asq_send_command_atomic_exec - send command to Admin Queue * @hw: pointer to the hw struct * @desc: prefilled descriptor describing the command (non DMA mem) * @buff: buffer to use for indirect commands @@ -780,11 +780,13 @@ static bool i40e_asq_done(struct i40e_hw *hw) * This is the main send command driver routine for the Admin Queue send * queue. It runs the queue, cleans the queue, etc **/ -i40e_status -i40e_asq_send_command_atomic(struct i40e_hw *hw, struct i40e_aq_desc *desc, - void *buff, /* can be NULL */ u16 buff_size, - struct i40e_asq_cmd_details *cmd_details, - bool is_atomic_context) +static i40e_status +i40e_asq_send_command_atomic_exec(struct i40e_hw *hw, + struct i40e_aq_desc *desc, + void *buff, /* can be NULL */ + u16 buff_size, + struct i40e_asq_cmd_details *cmd_details, + bool is_atomic_context) { i40e_status status = 0; struct i40e_dma_mem *dma_buff = NULL; @@ -794,8 +796,6 @@ i40e_asq_send_command_atomic(struct i40e_hw *hw, struct i40e_aq_desc *desc, u16 retval = 0; u32 val = 0; - mutex_lock(&hw->aq.asq_mutex); - if (hw->aq.asq.count == 0) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQTX: Admin queue not initialized.\n"); @@ -969,6 +969,36 @@ i40e_asq_send_command_atomic(struct i40e_hw *hw, struct i40e_aq_desc *desc, } asq_send_command_error: + return status; +} + +/** + * i40e_asq_send_command_atomic - send command to Admin Queue + * @hw: pointer to the hw struct + * @desc: prefilled descriptor describing the command (non DMA mem) + * @buff: buffer to use for indirect commands + * @buff_size: size of buffer for indirect commands + * @cmd_details: pointer to command details structure + * @is_atomic_context: is the function called in an atomic context? + * + * Acquires the lock and calls the main send command execution + * routine. + **/ +i40e_status +i40e_asq_send_command_atomic(struct i40e_hw *hw, + struct i40e_aq_desc *desc, + void *buff, /* can be NULL */ + u16 buff_size, + struct i40e_asq_cmd_details *cmd_details, + bool is_atomic_context) +{ + i40e_status status; + + mutex_lock(&hw->aq.asq_mutex); + status = i40e_asq_send_command_atomic_exec(hw, desc, buff, buff_size, + cmd_details, + is_atomic_context); + mutex_unlock(&hw->aq.asq_mutex); return status; } @@ -982,6 +1012,52 @@ i40e_asq_send_command(struct i40e_hw *hw, struct i40e_aq_desc *desc, cmd_details, false); } +/** + * i40e_asq_send_command_atomic_v2 - send command to Admin Queue + * @hw: pointer to the hw struct + * @desc: prefilled descriptor describing the command (non DMA mem) + * @buff: buffer to use for indirect commands + * @buff_size: size of buffer for indirect commands + * @cmd_details: pointer to command details structure + * @is_atomic_context: is the function called in an atomic context? + * @aq_status: pointer to Admin Queue status return value + * + * Acquires the lock and calls the main send command execution + * routine. Returns the last Admin Queue status in aq_status + * to avoid race conditions in access to hw->aq.asq_last_status. + **/ +i40e_status +i40e_asq_send_command_atomic_v2(struct i40e_hw *hw, + struct i40e_aq_desc *desc, + void *buff, /* can be NULL */ + u16 buff_size, + struct i40e_asq_cmd_details *cmd_details, + bool is_atomic_context, + enum i40e_admin_queue_err *aq_status) +{ + i40e_status status; + + mutex_lock(&hw->aq.asq_mutex); + status = i40e_asq_send_command_atomic_exec(hw, desc, buff, + buff_size, + cmd_details, + is_atomic_context); + if (aq_status) + *aq_status = hw->aq.asq_last_status; + mutex_unlock(&hw->aq.asq_mutex); + return status; +} + +i40e_status +i40e_asq_send_command_v2(struct i40e_hw *hw, struct i40e_aq_desc *desc, + void *buff, /* can be NULL */ u16 buff_size, + struct i40e_asq_cmd_details *cmd_details, + enum i40e_admin_queue_err *aq_status) +{ + return i40e_asq_send_command_atomic_v2(hw, desc, buff, buff_size, + cmd_details, true, aq_status); +} + /** * i40e_fill_default_direct_cmd_desc - AQ descriptor helper function * @desc: pointer to the temp descriptor (non DMA mem) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 9ddeb015eb7e..6aefffd83615 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1899,8 +1899,9 @@ i40e_status i40e_aq_add_vsi(struct i40e_hw *hw, desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); - status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info, - sizeof(vsi_ctx->info), cmd_details); + status = i40e_asq_send_command_atomic(hw, &desc, &vsi_ctx->info, + sizeof(vsi_ctx->info), + cmd_details, true); if (status) goto aq_add_vsi_exit; @@ -2287,8 +2288,9 @@ i40e_status i40e_aq_update_vsi_params(struct i40e_hw *hw, desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); - status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info, - sizeof(vsi_ctx->info), cmd_details); + status = i40e_asq_send_command_atomic(hw, &desc, &vsi_ctx->info, + sizeof(vsi_ctx->info), + cmd_details, true); vsi_ctx->vsis_allocated = le16_to_cpu(resp->vsi_used); vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free); @@ -2632,33 +2634,28 @@ i40e_status i40e_aq_get_veb_parameters(struct i40e_hw *hw, } /** - * i40e_aq_add_macvlan - * @hw: pointer to the hw struct - * @seid: VSI for the mac address + * i40e_prepare_add_macvlan * @mv_list: list of macvlans to be added + * @desc: pointer to AQ descriptor structure * @count: length of the list - * @cmd_details: pointer to command details structure or NULL + * @seid: VSI for the mac address * - * Add MAC/VLAN addresses to the HW filtering + * Internal helper function that prepares the add macvlan request + * and returns the buffer size. **/ -i40e_status i40e_aq_add_macvlan(struct i40e_hw *hw, u16 seid, - struct i40e_aqc_add_macvlan_element_data *mv_list, - u16 count, struct i40e_asq_cmd_details *cmd_details) +static u16 +i40e_prepare_add_macvlan(struct i40e_aqc_add_macvlan_element_data *mv_list, + struct i40e_aq_desc *desc, u16 count, u16 seid) { - struct i40e_aq_desc desc; struct i40e_aqc_macvlan *cmd = - (struct i40e_aqc_macvlan *)&desc.params.raw; - i40e_status status; + (struct i40e_aqc_macvlan *)&desc->params.raw; u16 buf_size; int i; - if (count == 0 || !mv_list || !hw) - return I40E_ERR_PARAM; - buf_size = count * sizeof(*mv_list); /* prep the rest of the request */ - i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_add_macvlan); + i40e_fill_default_direct_cmd_desc(desc, i40e_aqc_opc_add_macvlan); cmd->num_addresses = cpu_to_le16(count); cmd->seid[0] = cpu_to_le16(I40E_AQC_MACVLAN_CMD_SEID_VALID | seid); cmd->seid[1] = 0; @@ -2669,14 +2666,71 @@ i40e_status i40e_aq_add_macvlan(struct i40e_hw *hw, u16 seid, mv_list[i].flags |= cpu_to_le16(I40E_AQC_MACVLAN_ADD_USE_SHARED_MAC); - desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + desc->flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); if (buf_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc->flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); - status = i40e_asq_send_command(hw, &desc, mv_list, buf_size, - cmd_details); + return buf_size; +} - return status; +/** + * i40e_aq_add_macvlan + * @hw: pointer to the hw struct + * @seid: VSI for the mac address + * @mv_list: list of macvlans to be added + * @count: length of the list + * @cmd_details: pointer to command details structure or NULL + * + * Add MAC/VLAN addresses to the HW filtering + **/ +i40e_status +i40e_aq_add_macvlan(struct i40e_hw *hw, u16 seid, + struct i40e_aqc_add_macvlan_element_data *mv_list, + u16 count, struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + u16 buf_size; + + if (count == 0 || !mv_list || !hw) + return I40E_ERR_PARAM; + + buf_size = i40e_prepare_add_macvlan(mv_list, &desc, count, seid); + + return i40e_asq_send_command_atomic(hw, &desc, mv_list, buf_size, + cmd_details, true); +} + +/** + * i40e_aq_add_macvlan_v2 + * @hw: pointer to the hw struct + * @seid: VSI for the mac address + * @mv_list: list of macvlans to be added + * @count: length of the list + * @cmd_details: pointer to command details structure or NULL + * @aq_status: pointer to Admin Queue status return value + * + * Add MAC/VLAN addresses to the HW filtering. + * The _v2 version returns the last Admin Queue status in aq_status + * to avoid race conditions in access to hw->aq.asq_last_status. + * It also calls _v2 versions of asq_send_command functions to + * get the aq_status on the stack. + **/ +i40e_status +i40e_aq_add_macvlan_v2(struct i40e_hw *hw, u16 seid, + struct i40e_aqc_add_macvlan_element_data *mv_list, + u16 count, struct i40e_asq_cmd_details *cmd_details, + enum i40e_admin_queue_err *aq_status) +{ + struct i40e_aq_desc desc; + u16 buf_size; + + if (count == 0 || !mv_list || !hw) + return I40E_ERR_PARAM; + + buf_size = i40e_prepare_add_macvlan(mv_list, &desc, count, seid); + + return i40e_asq_send_command_atomic_v2(hw, &desc, mv_list, buf_size, + cmd_details, true, aq_status); } /** @@ -2715,12 +2769,58 @@ i40e_status i40e_aq_remove_macvlan(struct i40e_hw *hw, u16 seid, if (buf_size > I40E_AQ_LARGE_BUF) desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); - status = i40e_asq_send_command(hw, &desc, mv_list, buf_size, - cmd_details); + status = i40e_asq_send_command_atomic(hw, &desc, mv_list, buf_size, + cmd_details, true); return status; } +/** + * i40e_aq_remove_macvlan_v2 + * @hw: pointer to the hw struct + * @seid: VSI for the mac address + * @mv_list: list of macvlans to be removed + * @count: length of the list + * @cmd_details: pointer to command details structure or NULL + * @aq_status: pointer to Admin Queue status return value + * + * Remove MAC/VLAN addresses from the HW filtering. + * The _v2 version returns the last Admin Queue status in aq_status + * to avoid race conditions in access to hw->aq.asq_last_status. + * It also calls _v2 versions of asq_send_command functions to + * get the aq_status on the stack. + **/ +i40e_status +i40e_aq_remove_macvlan_v2(struct i40e_hw *hw, u16 seid, + struct i40e_aqc_remove_macvlan_element_data *mv_list, + u16 count, struct i40e_asq_cmd_details *cmd_details, + enum i40e_admin_queue_err *aq_status) +{ + struct i40e_aqc_macvlan *cmd; + struct i40e_aq_desc desc; + u16 buf_size; + + if (count == 0 || !mv_list || !hw) + return I40E_ERR_PARAM; + + buf_size = count * sizeof(*mv_list); + + /* prep the rest of the request */ + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_remove_macvlan); + cmd = (struct i40e_aqc_macvlan *)&desc.params.raw; + cmd->num_addresses = cpu_to_le16(count); + cmd->seid[0] = cpu_to_le16(I40E_AQC_MACVLAN_CMD_SEID_VALID | seid); + cmd->seid[1] = 0; + cmd->seid[2] = 0; + + desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + if (buf_size > I40E_AQ_LARGE_BUF) + desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + + return i40e_asq_send_command_atomic_v2(hw, &desc, mv_list, buf_size, + cmd_details, true, aq_status); +} + /** * i40e_mirrorrule_op - Internal helper function to add/delete mirror rule * @hw: pointer to the hw struct @@ -3868,7 +3968,8 @@ i40e_status i40e_aq_delete_element(struct i40e_hw *hw, u16 seid, cmd->seid = cpu_to_le16(seid); - status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + status = i40e_asq_send_command_atomic(hw, &desc, NULL, 0, + cmd_details, true); return status; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 9db5001297c7..be7c6f34d45c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -275,9 +275,8 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) rx_ring->rx_stats.alloc_page_failed, rx_ring->rx_stats.alloc_buff_failed); dev_info(&pf->pdev->dev, - " rx_rings[%i]: rx_stats: realloc_count = %lld, page_reuse_count = %lld\n", + " rx_rings[%i]: rx_stats: realloc_count = 0, page_reuse_count = %lld\n", i, - rx_ring->rx_stats.realloc_count, rx_ring->rx_stats.page_reuse_count); dev_info(&pf->pdev->dev, " rx_rings[%i]: size = %i\n", diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 091f36adbbe1..e48499624d22 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -295,6 +295,10 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = { I40E_VSI_STAT("tx_busy", tx_busy), I40E_VSI_STAT("rx_alloc_fail", rx_buf_failed), I40E_VSI_STAT("rx_pg_alloc_fail", rx_page_failed), + I40E_VSI_STAT("rx_cache_reuse", rx_page_reuse), + I40E_VSI_STAT("rx_cache_alloc", rx_page_alloc), + I40E_VSI_STAT("rx_cache_waive", rx_page_waive), + I40E_VSI_STAT("rx_cache_busy", rx_page_busy), }; /* These PF_STATs might look like duplicates of some NETDEV_STATs, diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 31b03fe78d3b..6778df2177a1 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -773,6 +773,7 @@ void i40e_update_veb_stats(struct i40e_veb *veb) **/ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) { + u64 rx_page, rx_buf, rx_reuse, rx_alloc, rx_waive, rx_busy; struct i40e_pf *pf = vsi->back; struct rtnl_link_stats64 *ons; struct rtnl_link_stats64 *ns; /* netdev stats */ @@ -780,7 +781,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) struct i40e_eth_stats *es; /* device's eth stats */ u64 tx_restart, tx_busy; struct i40e_ring *p; - u64 rx_page, rx_buf; u64 bytes, packets; unsigned int start; u64 tx_linearize; @@ -806,6 +806,10 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) tx_restart = tx_busy = tx_linearize = tx_force_wb = 0; rx_page = 0; rx_buf = 0; + rx_reuse = 0; + rx_alloc = 0; + rx_waive = 0; + rx_busy = 0; rcu_read_lock(); for (q = 0; q < vsi->num_queue_pairs; q++) { /* locate Tx ring */ @@ -839,6 +843,10 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) rx_p += packets; rx_buf += p->rx_stats.alloc_buff_failed; rx_page += p->rx_stats.alloc_page_failed; + rx_reuse += p->rx_stats.page_reuse_count; + rx_alloc += p->rx_stats.page_alloc_count; + rx_waive += p->rx_stats.page_waive_count; + rx_busy += p->rx_stats.page_busy_count; if (i40e_enabled_xdp_vsi(vsi)) { /* locate XDP ring */ @@ -866,6 +874,10 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) vsi->tx_force_wb = tx_force_wb; vsi->rx_page_failed = rx_page; vsi->rx_buf_failed = rx_buf; + vsi->rx_page_reuse = rx_reuse; + vsi->rx_page_alloc = rx_alloc; + vsi->rx_page_waive = rx_waive; + vsi->rx_page_busy = rx_busy; ns->rx_packets = rx_p; ns->rx_bytes = rx_b; @@ -2143,19 +2155,19 @@ void i40e_aqc_del_filters(struct i40e_vsi *vsi, const char *vsi_name, int num_del, int *retval) { struct i40e_hw *hw = &vsi->back->hw; + enum i40e_admin_queue_err aq_status; i40e_status aq_ret; - int aq_err; - aq_ret = i40e_aq_remove_macvlan(hw, vsi->seid, list, num_del, NULL); - aq_err = hw->aq.asq_last_status; + aq_ret = i40e_aq_remove_macvlan_v2(hw, vsi->seid, list, num_del, NULL, + &aq_status); /* Explicitly ignore and do not report when firmware returns ENOENT */ - if (aq_ret && !(aq_err == I40E_AQ_RC_ENOENT)) { + if (aq_ret && !(aq_status == I40E_AQ_RC_ENOENT)) { *retval = -EIO; dev_info(&vsi->back->pdev->dev, "ignoring delete macvlan error on %s, err %s, aq_err %s\n", vsi_name, i40e_stat_str(hw, aq_ret), - i40e_aq_str(hw, aq_err)); + i40e_aq_str(hw, aq_status)); } } @@ -2178,10 +2190,10 @@ void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name, int num_add) { struct i40e_hw *hw = &vsi->back->hw; - int aq_err, fcnt; + enum i40e_admin_queue_err aq_status; + int fcnt; - i40e_aq_add_macvlan(hw, vsi->seid, list, num_add, NULL); - aq_err = hw->aq.asq_last_status; + i40e_aq_add_macvlan_v2(hw, vsi->seid, list, num_add, NULL, &aq_status); fcnt = i40e_update_filter_state(num_add, list, add_head); if (fcnt != num_add) { @@ -2189,17 +2201,19 @@ void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name, set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); dev_warn(&vsi->back->pdev->dev, "Error %s adding RX filters on %s, promiscuous mode forced on\n", - i40e_aq_str(hw, aq_err), vsi_name); + i40e_aq_str(hw, aq_status), vsi_name); } else if (vsi->type == I40E_VSI_SRIOV || vsi->type == I40E_VSI_VMDQ1 || vsi->type == I40E_VSI_VMDQ2) { dev_warn(&vsi->back->pdev->dev, "Error %s adding RX filters on %s, please set promiscuous on manually for %s\n", - i40e_aq_str(hw, aq_err), vsi_name, vsi_name); + i40e_aq_str(hw, aq_status), vsi_name, + vsi_name); } else { dev_warn(&vsi->back->pdev->dev, "Error %s adding RX filters on %s, incorrect VSI type: %i.\n", - i40e_aq_str(hw, aq_err), vsi_name, vsi->type); + i40e_aq_str(hw, aq_status), vsi_name, + vsi->type); } } } @@ -12712,7 +12726,8 @@ static int i40e_set_features(struct net_device *netdev, else i40e_vlan_stripping_disable(vsi); - if (!(features & NETIF_F_HW_TC) && pf->num_cloud_filters) { + if (!(features & NETIF_F_HW_TC) && + (netdev->features & NETIF_F_HW_TC) && pf->num_cloud_filters) { dev_err(&pf->pdev->dev, "Offloaded tc filters active, can't turn hw_tc_offload off"); return -EINVAL; @@ -13468,6 +13483,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) netdev->features |= hw_features | NETIF_F_HW_VLAN_CTAG_FILTER; netdev->hw_enc_features |= NETIF_F_TSO_MANGLEID; + netdev->features &= ~NETIF_F_HW_TC; + if (vsi->type == I40E_VSI_MAIN) { SET_NETDEV_DEV(netdev, &pf->pdev->dev); ether_addr_copy(mac_addr, hw->mac.perm_addr); @@ -15331,12 +15348,9 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* set up for high or low dma */ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (err) { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, - "DMA configuration failed: 0x%x\n", err); - goto err_dma; - } + dev_err(&pdev->dev, + "DMA configuration failed: 0x%x\n", err); + goto err_dma; } /* set up pci connections */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index fe6dca846028..3a38bf8bcde7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -682,10 +682,11 @@ i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw) __le16 le_sum; ret_code = i40e_calc_nvm_checksum(hw, &checksum); - le_sum = cpu_to_le16(checksum); - if (!ret_code) + if (!ret_code) { + le_sum = cpu_to_le16(checksum); ret_code = i40e_write_nvm_aq(hw, 0x00, I40E_SR_SW_CHECKSUM_WORD, 1, &le_sum, true); + } return ret_code; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index 9241b6005ad3..ebdcde6f1aeb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -27,10 +27,25 @@ i40e_asq_send_command(struct i40e_hw *hw, struct i40e_aq_desc *desc, void *buff, /* can be NULL */ u16 buff_size, struct i40e_asq_cmd_details *cmd_details); i40e_status +i40e_asq_send_command_v2(struct i40e_hw *hw, + struct i40e_aq_desc *desc, + void *buff, /* can be NULL */ + u16 buff_size, + struct i40e_asq_cmd_details *cmd_details, + enum i40e_admin_queue_err *aq_status); +i40e_status i40e_asq_send_command_atomic(struct i40e_hw *hw, struct i40e_aq_desc *desc, void *buff, /* can be NULL */ u16 buff_size, struct i40e_asq_cmd_details *cmd_details, bool is_atomic_context); +i40e_status +i40e_asq_send_command_atomic_v2(struct i40e_hw *hw, + struct i40e_aq_desc *desc, + void *buff, /* can be NULL */ + u16 buff_size, + struct i40e_asq_cmd_details *cmd_details, + bool is_atomic_context, + enum i40e_admin_queue_err *aq_status); /* debug function for adminq */ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, @@ -150,9 +165,19 @@ i40e_status i40e_aq_get_veb_parameters(struct i40e_hw *hw, i40e_status i40e_aq_add_macvlan(struct i40e_hw *hw, u16 vsi_id, struct i40e_aqc_add_macvlan_element_data *mv_list, u16 count, struct i40e_asq_cmd_details *cmd_details); +i40e_status +i40e_aq_add_macvlan_v2(struct i40e_hw *hw, u16 seid, + struct i40e_aqc_add_macvlan_element_data *mv_list, + u16 count, struct i40e_asq_cmd_details *cmd_details, + enum i40e_admin_queue_err *aq_status); i40e_status i40e_aq_remove_macvlan(struct i40e_hw *hw, u16 vsi_id, struct i40e_aqc_remove_macvlan_element_data *mv_list, u16 count, struct i40e_asq_cmd_details *cmd_details); +i40e_status +i40e_aq_remove_macvlan_v2(struct i40e_hw *hw, u16 seid, + struct i40e_aqc_remove_macvlan_element_data *mv_list, + u16 count, struct i40e_asq_cmd_details *cmd_details, + enum i40e_admin_queue_err *aq_status); i40e_status i40e_aq_add_mirrorrule(struct i40e_hw *hw, u16 sw_seid, u16 rule_type, u16 dest_vsi, u16 count, __le16 *mr_list, struct i40e_asq_cmd_details *cmd_details, diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 66cc79500c10..0eae5858f2fe 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -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, @@ -1382,8 +1380,6 @@ static void i40e_reuse_rx_page(struct i40e_ring *rx_ring, new_buff->page_offset = old_buff->page_offset; new_buff->pagecnt_bias = old_buff->pagecnt_bias; - rx_ring->rx_stats.page_reuse_count++; - /* clear contents of buffer_info */ old_buff->page = NULL; } @@ -1433,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 */ @@ -1463,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; @@ -1675,6 +1662,8 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, return false; } + rx_ring->rx_stats.page_alloc_count++; + /* map page for use */ dma = dma_map_page_attrs(rx_ring->dev, page, 0, i40e_rx_pg_size(rx_ring), @@ -1982,32 +1971,43 @@ static bool i40e_cleanup_headers(struct i40e_ring *rx_ring, struct sk_buff *skb, /** * i40e_can_reuse_rx_page - Determine if page can be reused for another Rx * @rx_buffer: buffer containing the page + * @rx_stats: rx stats structure for the rx ring * @rx_buffer_pgcnt: buffer page refcount pre xdp_do_redirect() call * * If page is reusable, we have a green light for calling i40e_reuse_rx_page, * which will assign the current buffer to the buffer that next_to_alloc is * pointing to; otherwise, the DMA mapping needs to be destroyed and - * page freed + * page freed. + * + * rx_stats will be updated to indicate whether the page was waived + * or busy if it could not be reused. */ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, + struct i40e_rx_queue_stats *rx_stats, int rx_buffer_pgcnt) { unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; struct page *page = rx_buffer->page; /* Is any reuse possible? */ - if (!dev_page_is_reusable(page)) + if (!dev_page_is_reusable(page)) { + rx_stats->page_waive_count++; return false; + } #if (PAGE_SIZE < 8192) /* if we are only owner of page we can reuse it */ - if (unlikely((rx_buffer_pgcnt - pagecnt_bias) > 1)) + if (unlikely((rx_buffer_pgcnt - pagecnt_bias) > 1)) { + rx_stats->page_busy_count++; return false; + } #else #define I40E_LAST_OFFSET \ (SKB_WITH_OVERHEAD(PAGE_SIZE) - I40E_RXBUFFER_2048) - if (rx_buffer->page_offset > I40E_LAST_OFFSET) + if (rx_buffer->page_offset > I40E_LAST_OFFSET) { + rx_stats->page_busy_count++; return false; + } #endif /* If we have drained the page fragment pool we need to update @@ -2237,7 +2237,7 @@ static void i40e_put_rx_buffer(struct i40e_ring *rx_ring, struct i40e_rx_buffer *rx_buffer, int rx_buffer_pgcnt) { - if (i40e_can_reuse_rx_page(rx_buffer, rx_buffer_pgcnt)) { + if (i40e_can_reuse_rx_page(rx_buffer, &rx_ring->rx_stats, rx_buffer_pgcnt)) { /* hand second half of page back to the ring */ i40e_reuse_rx_page(rx_ring, rx_buffer); } else { diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index bfc2845c99d1..c471c2da313c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -298,7 +298,9 @@ struct i40e_rx_queue_stats { u64 alloc_page_failed; u64 alloc_buff_failed; u64 page_reuse_count; - u64 realloc_count; + u64 page_alloc_count; + u64 page_waive_count; + u64 page_busy_count; }; enum i40e_ring_state_t { @@ -390,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) diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c b/drivers/net/ethernet/intel/i40e/i40e_xsk.c index 945b1bb9c6f4..c1d25b0b0ca2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c +++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c @@ -218,7 +218,6 @@ bool i40e_alloc_rx_buffers_zc(struct i40e_ring *rx_ring, u16 count) ntu += nb_buffs; if (ntu == rx_ring->count) { rx_desc = I40E_RX_DESC(rx_ring, 0); - xdp = i40e_rx_bi(rx_ring, 0); ntu = 0; } @@ -241,21 +240,25 @@ bool i40e_alloc_rx_buffers_zc(struct i40e_ring *rx_ring, u16 count) static struct sk_buff *i40e_construct_skb_zc(struct i40e_ring *rx_ring, struct xdp_buff *xdp) { + unsigned int totalsize = xdp->data_end - xdp->data_meta; unsigned int metasize = xdp->data - xdp->data_meta; - unsigned int datasize = xdp->data_end - xdp->data; struct sk_buff *skb; + net_prefetch(xdp->data_meta); + /* allocate a skb to store the frags */ - skb = __napi_alloc_skb(&rx_ring->q_vector->napi, - xdp->data_end - xdp->data_hard_start, + skb = __napi_alloc_skb(&rx_ring->q_vector->napi, totalsize, GFP_ATOMIC | __GFP_NOWARN); if (unlikely(!skb)) goto out; - skb_reserve(skb, xdp->data - xdp->data_hard_start); - memcpy(__skb_put(skb, datasize), xdp->data, datasize); - if (metasize) + memcpy(__skb_put(skb, totalsize), xdp->data_meta, + ALIGN(totalsize, sizeof(long))); + + if (metasize) { skb_metadata_set(skb, metasize); + __skb_pull(skb, metasize); + } out: xsk_buff_free(xdp); @@ -324,11 +327,11 @@ static void i40e_handle_xdp_result_zc(struct i40e_ring *rx_ring, int i40e_clean_rx_irq_zc(struct i40e_ring *rx_ring, int budget) { unsigned int total_rx_bytes = 0, total_rx_packets = 0; - u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); u16 next_to_clean = rx_ring->next_to_clean; u16 count_mask = rx_ring->count - 1; unsigned int xdp_res, xdp_xmit = 0; bool failure = false; + u16 cleaned_count; while (likely(total_rx_packets < (unsigned int)budget)) { union i40e_rx_desc *rx_desc; @@ -467,11 +470,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; diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h index 4babe4705a55..49aed3e506a6 100644 --- a/drivers/net/ethernet/intel/iavf/iavf.h +++ b/drivers/net/ethernet/intel/iavf/iavf.h @@ -44,6 +44,9 @@ #define DEFAULT_DEBUG_LEVEL_SHIFT 3 #define PFX "iavf: " +int iavf_status_to_errno(enum iavf_status status); +int virtchnl_status_to_errno(enum virtchnl_status_code v_status); + /* VSI state flags shared with common code */ enum iavf_vsi_state_t { __IAVF_VSI_DOWN, @@ -188,7 +191,7 @@ enum iavf_state_t { __IAVF_REMOVE, /* driver is being unloaded */ __IAVF_INIT_VERSION_CHECK, /* aq msg sent, awaiting reply */ __IAVF_INIT_GET_RESOURCES, /* aq msg sent, awaiting reply */ - __IAVF_INIT_GET_OFFLOAD_VLAN_V2_CAPS, + __IAVF_INIT_EXTENDED_CAPS, /* process extended caps which require aq msg exchange */ __IAVF_INIT_CONFIG_ADAPTER, __IAVF_INIT_SW, /* got resources, setting up structs */ __IAVF_INIT_FAILED, /* init failed, restarting procedure */ @@ -334,6 +337,21 @@ struct iavf_adapter { #define IAVF_FLAG_AQ_ENABLE_STAG_VLAN_INSERTION BIT_ULL(37) #define IAVF_FLAG_AQ_DISABLE_STAG_VLAN_INSERTION BIT_ULL(38) + /* flags for processing extended capability messages during + * __IAVF_INIT_EXTENDED_CAPS. Each capability exchange requires + * both a SEND and a RECV step, which must be processed in sequence. + * + * During the __IAVF_INIT_EXTENDED_CAPS state, the driver will + * process one flag at a time during each state loop. + */ + u64 extended_caps; +#define IAVF_EXTENDED_CAP_SEND_VLAN_V2 BIT_ULL(0) +#define IAVF_EXTENDED_CAP_RECV_VLAN_V2 BIT_ULL(1) + +#define IAVF_EXTENDED_CAPS \ + (IAVF_EXTENDED_CAP_SEND_VLAN_V2 | \ + IAVF_EXTENDED_CAP_RECV_VLAN_V2) + /* OS defined structs */ struct net_device *netdev; struct pci_dev *pdev; @@ -515,7 +533,7 @@ void iavf_add_vlans(struct iavf_adapter *adapter); void iavf_del_vlans(struct iavf_adapter *adapter); void iavf_set_promiscuous(struct iavf_adapter *adapter, int flags); void iavf_request_stats(struct iavf_adapter *adapter); -void iavf_request_reset(struct iavf_adapter *adapter); +int iavf_request_reset(struct iavf_adapter *adapter); void iavf_get_hena(struct iavf_adapter *adapter); void iavf_set_hena(struct iavf_adapter *adapter); void iavf_set_rss_key(struct iavf_adapter *adapter); diff --git a/drivers/net/ethernet/intel/iavf/iavf_common.c b/drivers/net/ethernet/intel/iavf/iavf_common.c index e9cc7f6ddc46..34e46a23894f 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_common.c +++ b/drivers/net/ethernet/intel/iavf/iavf_common.c @@ -131,8 +131,8 @@ const char *iavf_stat_str(struct iavf_hw *hw, enum iavf_status stat_err) return "IAVF_ERR_INVALID_MAC_ADDR"; case IAVF_ERR_DEVICE_NOT_SUPPORTED: return "IAVF_ERR_DEVICE_NOT_SUPPORTED"; - case IAVF_ERR_MASTER_REQUESTS_PENDING: - return "IAVF_ERR_MASTER_REQUESTS_PENDING"; + case IAVF_ERR_PRIMARY_REQUESTS_PENDING: + return "IAVF_ERR_PRIMARY_REQUESTS_PENDING"; case IAVF_ERR_INVALID_LINK_SETTINGS: return "IAVF_ERR_INVALID_LINK_SETTINGS"; case IAVF_ERR_AUTONEG_NOT_COMPLETE: diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c index 0e178a0a59c5..190590d32faf 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_main.c +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c @@ -51,6 +51,113 @@ MODULE_LICENSE("GPL v2"); static const struct net_device_ops iavf_netdev_ops; struct workqueue_struct *iavf_wq; +int iavf_status_to_errno(enum iavf_status status) +{ + switch (status) { + case IAVF_SUCCESS: + return 0; + case IAVF_ERR_PARAM: + case IAVF_ERR_MAC_TYPE: + case IAVF_ERR_INVALID_MAC_ADDR: + case IAVF_ERR_INVALID_LINK_SETTINGS: + case IAVF_ERR_INVALID_PD_ID: + case IAVF_ERR_INVALID_QP_ID: + case IAVF_ERR_INVALID_CQ_ID: + case IAVF_ERR_INVALID_CEQ_ID: + case IAVF_ERR_INVALID_AEQ_ID: + case IAVF_ERR_INVALID_SIZE: + case IAVF_ERR_INVALID_ARP_INDEX: + case IAVF_ERR_INVALID_FPM_FUNC_ID: + case IAVF_ERR_QP_INVALID_MSG_SIZE: + case IAVF_ERR_INVALID_FRAG_COUNT: + case IAVF_ERR_INVALID_ALIGNMENT: + case IAVF_ERR_INVALID_PUSH_PAGE_INDEX: + case IAVF_ERR_INVALID_IMM_DATA_SIZE: + case IAVF_ERR_INVALID_VF_ID: + case IAVF_ERR_INVALID_HMCFN_ID: + case IAVF_ERR_INVALID_PBLE_INDEX: + case IAVF_ERR_INVALID_SD_INDEX: + case IAVF_ERR_INVALID_PAGE_DESC_INDEX: + case IAVF_ERR_INVALID_SD_TYPE: + case IAVF_ERR_INVALID_HMC_OBJ_INDEX: + case IAVF_ERR_INVALID_HMC_OBJ_COUNT: + case IAVF_ERR_INVALID_SRQ_ARM_LIMIT: + return -EINVAL; + case IAVF_ERR_NVM: + case IAVF_ERR_NVM_CHECKSUM: + case IAVF_ERR_PHY: + case IAVF_ERR_CONFIG: + case IAVF_ERR_UNKNOWN_PHY: + case IAVF_ERR_LINK_SETUP: + case IAVF_ERR_ADAPTER_STOPPED: + case IAVF_ERR_PRIMARY_REQUESTS_PENDING: + case IAVF_ERR_AUTONEG_NOT_COMPLETE: + case IAVF_ERR_RESET_FAILED: + case IAVF_ERR_BAD_PTR: + case IAVF_ERR_SWFW_SYNC: + case IAVF_ERR_QP_TOOMANY_WRS_POSTED: + case IAVF_ERR_QUEUE_EMPTY: + case IAVF_ERR_FLUSHED_QUEUE: + case IAVF_ERR_OPCODE_MISMATCH: + case IAVF_ERR_CQP_COMPL_ERROR: + case IAVF_ERR_BACKING_PAGE_ERROR: + case IAVF_ERR_NO_PBLCHUNKS_AVAILABLE: + case IAVF_ERR_MEMCPY_FAILED: + case IAVF_ERR_SRQ_ENABLED: + case IAVF_ERR_ADMIN_QUEUE_ERROR: + case IAVF_ERR_ADMIN_QUEUE_FULL: + case IAVF_ERR_BAD_IWARP_CQE: + case IAVF_ERR_NVM_BLANK_MODE: + case IAVF_ERR_PE_DOORBELL_NOT_ENABLED: + case IAVF_ERR_DIAG_TEST_FAILED: + case IAVF_ERR_FIRMWARE_API_VERSION: + case IAVF_ERR_ADMIN_QUEUE_CRITICAL_ERROR: + return -EIO; + case IAVF_ERR_DEVICE_NOT_SUPPORTED: + return -ENODEV; + case IAVF_ERR_NO_AVAILABLE_VSI: + case IAVF_ERR_RING_FULL: + return -ENOSPC; + case IAVF_ERR_NO_MEMORY: + return -ENOMEM; + case IAVF_ERR_TIMEOUT: + case IAVF_ERR_ADMIN_QUEUE_TIMEOUT: + return -ETIMEDOUT; + case IAVF_ERR_NOT_IMPLEMENTED: + case IAVF_NOT_SUPPORTED: + return -EOPNOTSUPP; + case IAVF_ERR_ADMIN_QUEUE_NO_WORK: + return -EALREADY; + case IAVF_ERR_NOT_READY: + return -EBUSY; + case IAVF_ERR_BUF_TOO_SHORT: + return -EMSGSIZE; + } + + return -EIO; +} + +int virtchnl_status_to_errno(enum virtchnl_status_code v_status) +{ + switch (v_status) { + case VIRTCHNL_STATUS_SUCCESS: + return 0; + case VIRTCHNL_STATUS_ERR_PARAM: + case VIRTCHNL_STATUS_ERR_INVALID_VF_ID: + return -EINVAL; + case VIRTCHNL_STATUS_ERR_NO_MEMORY: + return -ENOMEM; + case VIRTCHNL_STATUS_ERR_OPCODE_MISMATCH: + case VIRTCHNL_STATUS_ERR_CQP_COMPL_ERROR: + case VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR: + return -EIO; + case VIRTCHNL_STATUS_ERR_NOT_SUPPORTED: + return -EOPNOTSUPP; + } + + return -EIO; +} + /** * iavf_pdev_to_adapter - go from pci_dev to adapter * @pdev: pci_dev pointer @@ -877,6 +984,7 @@ struct iavf_mac_filter *iavf_add_filter(struct iavf_adapter *adapter, list_add_tail(&f->list, &adapter->mac_filter_list); f->add = true; f->is_new_mac = true; + f->is_primary = false; adapter->aq_required |= IAVF_FLAG_AQ_ADD_MAC_FILTER; } else { f->remove = false; @@ -910,16 +1018,21 @@ static int iavf_set_mac(struct net_device *netdev, void *p) f = iavf_find_filter(adapter, hw->mac.addr); if (f) { f->remove = true; + f->is_primary = true; adapter->aq_required |= IAVF_FLAG_AQ_DEL_MAC_FILTER; } f = iavf_add_filter(adapter, addr->sa_data); + if (f) { + f->is_primary = true; + ether_addr_copy(hw->mac.addr, addr->sa_data); + } spin_unlock_bh(&adapter->mac_vlan_list_lock); - if (f) { - ether_addr_copy(hw->mac.addr, addr->sa_data); - } + /* schedule the watchdog task to immediately process the request */ + if (f) + queue_work(iavf_wq, &adapter->watchdog_task.work); return (f == NULL) ? -ENOMEM : 0; } @@ -1421,7 +1534,7 @@ static int iavf_config_rss_aq(struct iavf_adapter *adapter) struct iavf_aqc_get_set_rss_key_data *rss_key = (struct iavf_aqc_get_set_rss_key_data *)adapter->rss_key; struct iavf_hw *hw = &adapter->hw; - int ret = 0; + enum iavf_status status; if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ @@ -1430,24 +1543,25 @@ static int iavf_config_rss_aq(struct iavf_adapter *adapter) return -EBUSY; } - ret = iavf_aq_set_rss_key(hw, adapter->vsi.id, rss_key); - if (ret) { + status = iavf_aq_set_rss_key(hw, adapter->vsi.id, rss_key); + if (status) { dev_err(&adapter->pdev->dev, "Cannot set RSS key, err %s aq_err %s\n", - iavf_stat_str(hw, ret), + iavf_stat_str(hw, status), iavf_aq_str(hw, hw->aq.asq_last_status)); - return ret; + return iavf_status_to_errno(status); } - ret = iavf_aq_set_rss_lut(hw, adapter->vsi.id, false, - adapter->rss_lut, adapter->rss_lut_size); - if (ret) { + status = iavf_aq_set_rss_lut(hw, adapter->vsi.id, false, + adapter->rss_lut, adapter->rss_lut_size); + if (status) { dev_err(&adapter->pdev->dev, "Cannot set RSS lut, err %s aq_err %s\n", - iavf_stat_str(hw, ret), + iavf_stat_str(hw, status), iavf_aq_str(hw, hw->aq.asq_last_status)); + return iavf_status_to_errno(status); } - return ret; + return 0; } @@ -1517,7 +1631,6 @@ static void iavf_fill_rss_lut(struct iavf_adapter *adapter) static int iavf_init_rss(struct iavf_adapter *adapter) { struct iavf_hw *hw = &adapter->hw; - int ret; if (!RSS_PF(adapter)) { /* Enable PCTYPES for RSS, TCP/UDP with IPv4/IPv6 */ @@ -1533,9 +1646,8 @@ static int iavf_init_rss(struct iavf_adapter *adapter) iavf_fill_rss_lut(adapter); netdev_rss_key_fill((void *)adapter->rss_key, adapter->rss_key_size); - ret = iavf_config_rss(adapter); - return ret; + return iavf_config_rss(adapter); } /** @@ -2003,23 +2115,24 @@ static void iavf_startup(struct iavf_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; struct iavf_hw *hw = &adapter->hw; - int err; + enum iavf_status status; + int ret; WARN_ON(adapter->state != __IAVF_STARTUP); /* driver loaded, probe complete */ adapter->flags &= ~IAVF_FLAG_PF_COMMS_FAILED; adapter->flags &= ~IAVF_FLAG_RESET_PENDING; - err = iavf_set_mac_type(hw); - if (err) { - dev_err(&pdev->dev, "Failed to set MAC type (%d)\n", err); + status = iavf_set_mac_type(hw); + if (status) { + dev_err(&pdev->dev, "Failed to set MAC type (%d)\n", status); goto err; } - err = iavf_check_reset_complete(hw); - if (err) { + ret = iavf_check_reset_complete(hw); + if (ret) { dev_info(&pdev->dev, "Device is still in reset (%d), retrying\n", - err); + ret); goto err; } hw->aq.num_arq_entries = IAVF_AQ_LEN; @@ -2027,14 +2140,15 @@ static void iavf_startup(struct iavf_adapter *adapter) hw->aq.arq_buf_size = IAVF_MAX_AQ_BUF_SIZE; hw->aq.asq_buf_size = IAVF_MAX_AQ_BUF_SIZE; - err = iavf_init_adminq(hw); - if (err) { - dev_err(&pdev->dev, "Failed to init Admin Queue (%d)\n", err); + status = iavf_init_adminq(hw); + if (status) { + dev_err(&pdev->dev, "Failed to init Admin Queue (%d)\n", + status); goto err; } - err = iavf_send_api_ver(adapter); - if (err) { - dev_err(&pdev->dev, "Unable to send to PF (%d)\n", err); + ret = iavf_send_api_ver(adapter); + if (ret) { + dev_err(&pdev->dev, "Unable to send to PF (%d)\n", ret); iavf_shutdown_adminq(hw); goto err; } @@ -2070,7 +2184,7 @@ static void iavf_init_version_check(struct iavf_adapter *adapter) /* aq msg sent, awaiting reply */ err = iavf_verify_api_ver(adapter); if (err) { - if (err == IAVF_ERR_ADMIN_QUEUE_NO_WORK) + if (err == -EALREADY) err = iavf_send_api_ver(adapter); else dev_err(&pdev->dev, "Unsupported PF API version %d.%d, expected %d.%d\n", @@ -2171,11 +2285,11 @@ static void iavf_init_get_resources(struct iavf_adapter *adapter) } } err = iavf_get_vf_config(adapter); - if (err == IAVF_ERR_ADMIN_QUEUE_NO_WORK) { + if (err == -EALREADY) { err = iavf_send_vf_config_msg(adapter); goto err_alloc; - } else if (err == IAVF_ERR_PARAM) { - /* We only get ERR_PARAM if the device is in a very bad + } else if (err == -EINVAL) { + /* We only get -EINVAL if the device is in a very bad * state or if we've been disabled for previous bad * behavior. Either way, we're done now. */ @@ -2189,26 +2303,18 @@ static void iavf_init_get_resources(struct iavf_adapter *adapter) } err = iavf_parse_vf_resource_msg(adapter); - if (err) - goto err_alloc; - - err = iavf_send_vf_offload_vlan_v2_msg(adapter); - if (err == -EOPNOTSUPP) { - /* underlying PF doesn't support VIRTCHNL_VF_OFFLOAD_VLAN_V2, so - * go directly to finishing initialization - */ - iavf_change_state(adapter, __IAVF_INIT_CONFIG_ADAPTER); - return; - } else if (err) { - dev_err(&pdev->dev, "Unable to send offload vlan v2 request (%d)\n", + if (err) { + dev_err(&pdev->dev, "Failed to parse VF resource message from PF (%d)\n", err); goto err_alloc; } - - /* underlying PF supports VIRTCHNL_VF_OFFLOAD_VLAN_V2, so update the - * state accordingly + /* Some features require additional messages to negotiate extended + * capabilities. These are processed in sequence by the + * __IAVF_INIT_EXTENDED_CAPS driver state. */ - iavf_change_state(adapter, __IAVF_INIT_GET_OFFLOAD_VLAN_V2_CAPS); + adapter->extended_caps = IAVF_EXTENDED_CAPS; + + iavf_change_state(adapter, __IAVF_INIT_EXTENDED_CAPS); return; err_alloc: @@ -2219,34 +2325,92 @@ static void iavf_init_get_resources(struct iavf_adapter *adapter) } /** - * iavf_init_get_offload_vlan_v2_caps - part of driver startup + * iavf_init_send_offload_vlan_v2_caps - part of initializing VLAN V2 caps * @adapter: board private structure * - * Function processes __IAVF_INIT_GET_OFFLOAD_VLAN_V2_CAPS driver state if the - * VF negotiates VIRTCHNL_VF_OFFLOAD_VLAN_V2. If VIRTCHNL_VF_OFFLOAD_VLAN_V2 is - * not negotiated, then this state will never be entered. - **/ -static void iavf_init_get_offload_vlan_v2_caps(struct iavf_adapter *adapter) + * Function processes send of the extended VLAN V2 capability message to the + * PF. Must clear IAVF_EXTENDED_CAP_RECV_VLAN_V2 if the message is not sent, + * e.g. due to PF not negotiating VIRTCHNL_VF_OFFLOAD_VLAN_V2. + */ +static void iavf_init_send_offload_vlan_v2_caps(struct iavf_adapter *adapter) { int ret; - WARN_ON(adapter->state != __IAVF_INIT_GET_OFFLOAD_VLAN_V2_CAPS); + WARN_ON(!(adapter->extended_caps & IAVF_EXTENDED_CAP_SEND_VLAN_V2)); + + ret = iavf_send_vf_offload_vlan_v2_msg(adapter); + if (ret && ret == -EOPNOTSUPP) { + /* PF does not support VIRTCHNL_VF_OFFLOAD_V2. In this case, + * we did not send the capability exchange message and do not + * expect a response. + */ + adapter->extended_caps &= ~IAVF_EXTENDED_CAP_RECV_VLAN_V2; + } + + /* We sent the message, so move on to the next step */ + adapter->extended_caps &= ~IAVF_EXTENDED_CAP_SEND_VLAN_V2; +} + +/** + * iavf_init_recv_offload_vlan_v2_caps - part of initializing VLAN V2 caps + * @adapter: board private structure + * + * Function processes receipt of the extended VLAN V2 capability message from + * the PF. + **/ +static void iavf_init_recv_offload_vlan_v2_caps(struct iavf_adapter *adapter) +{ + int ret; + + WARN_ON(!(adapter->extended_caps & IAVF_EXTENDED_CAP_RECV_VLAN_V2)); memset(&adapter->vlan_v2_caps, 0, sizeof(adapter->vlan_v2_caps)); ret = iavf_get_vf_vlan_v2_caps(adapter); - if (ret) { - if (ret == IAVF_ERR_ADMIN_QUEUE_NO_WORK) - iavf_send_vf_offload_vlan_v2_msg(adapter); + if (ret) goto err; - } - iavf_change_state(adapter, __IAVF_INIT_CONFIG_ADAPTER); + /* We've processed receipt of the VLAN V2 caps message */ + adapter->extended_caps &= ~IAVF_EXTENDED_CAP_RECV_VLAN_V2; return; err: + /* We didn't receive a reply. Make sure we try sending again when + * __IAVF_INIT_FAILED attempts to recover. + */ + adapter->extended_caps |= IAVF_EXTENDED_CAP_SEND_VLAN_V2; iavf_change_state(adapter, __IAVF_INIT_FAILED); } +/** + * iavf_init_process_extended_caps - Part of driver startup + * @adapter: board private structure + * + * Function processes __IAVF_INIT_EXTENDED_CAPS driver state. This state + * handles negotiating capabilities for features which require an additional + * message. + * + * Once all extended capabilities exchanges are finished, the driver will + * transition into __IAVF_INIT_CONFIG_ADAPTER. + */ +static void iavf_init_process_extended_caps(struct iavf_adapter *adapter) +{ + WARN_ON(adapter->state != __IAVF_INIT_EXTENDED_CAPS); + + /* Process capability exchange for VLAN V2 */ + if (adapter->extended_caps & IAVF_EXTENDED_CAP_SEND_VLAN_V2) { + iavf_init_send_offload_vlan_v2_caps(adapter); + return; + } else if (adapter->extended_caps & IAVF_EXTENDED_CAP_RECV_VLAN_V2) { + iavf_init_recv_offload_vlan_v2_caps(adapter); + return; + } + + /* When we reach here, no further extended capabilities exchanges are + * necessary, so we finally transition into __IAVF_INIT_CONFIG_ADAPTER + */ + iavf_change_state(adapter, __IAVF_INIT_CONFIG_ADAPTER); +} + /** * iavf_init_config_adapter - last part of driver startup * @adapter: board private structure @@ -2411,8 +2575,8 @@ static void iavf_watchdog_task(struct work_struct *work) queue_delayed_work(iavf_wq, &adapter->watchdog_task, msecs_to_jiffies(1)); return; - case __IAVF_INIT_GET_OFFLOAD_VLAN_V2_CAPS: - iavf_init_get_offload_vlan_v2_caps(adapter); + case __IAVF_INIT_EXTENDED_CAPS: + iavf_init_process_extended_caps(adapter); mutex_unlock(&adapter->crit_lock); queue_delayed_work(iavf_wq, &adapter->watchdog_task, msecs_to_jiffies(1)); @@ -2626,6 +2790,7 @@ static void iavf_reset_task(struct work_struct *work) struct iavf_hw *hw = &adapter->hw; struct iavf_mac_filter *f, *ftmp; struct iavf_cloud_filter *cf; + enum iavf_status status; u32 reg_val; int i = 0, err; bool running; @@ -2727,10 +2892,12 @@ static void iavf_reset_task(struct work_struct *work) /* kill and reinit the admin queue */ iavf_shutdown_adminq(hw); adapter->current_op = VIRTCHNL_OP_UNKNOWN; - err = iavf_init_adminq(hw); - if (err) + status = iavf_init_adminq(hw); + if (status) { dev_info(&adapter->pdev->dev, "Failed to init adminq: %d\n", - err); + status); + goto reset_err; + } adapter->aq_required = 0; if ((adapter->flags & IAVF_FLAG_REINIT_MSIX_NEEDED) || @@ -4433,12 +4600,9 @@ static int iavf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (err) { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, - "DMA configuration failed: 0x%x\n", err); - goto err_dma; - } + dev_err(&pdev->dev, + "DMA configuration failed: 0x%x\n", err); + goto err_dma; } err = pci_request_regions(pdev, iavf_driver_name); @@ -4767,8 +4931,6 @@ static struct pci_driver iavf_driver = { **/ static int __init iavf_init_module(void) { - int ret; - pr_info("iavf: %s\n", iavf_driver_string); pr_info("%s\n", iavf_copyright); @@ -4779,8 +4941,7 @@ static int __init iavf_init_module(void) pr_err("%s: Failed to create workqueue\n", iavf_driver_name); return -ENOMEM; } - ret = pci_register_driver(&iavf_driver); - return ret; + return pci_register_driver(&iavf_driver); } module_init(iavf_init_module); diff --git a/drivers/net/ethernet/intel/iavf/iavf_status.h b/drivers/net/ethernet/intel/iavf/iavf_status.h index 46e3d1f6b604..2ea5c7c339bc 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_status.h +++ b/drivers/net/ethernet/intel/iavf/iavf_status.h @@ -18,7 +18,7 @@ enum iavf_status { IAVF_ERR_ADAPTER_STOPPED = -9, IAVF_ERR_INVALID_MAC_ADDR = -10, IAVF_ERR_DEVICE_NOT_SUPPORTED = -11, - IAVF_ERR_MASTER_REQUESTS_PENDING = -12, + IAVF_ERR_PRIMARY_REQUESTS_PENDING = -12, IAVF_ERR_INVALID_LINK_SETTINGS = -13, IAVF_ERR_AUTONEG_NOT_COMPLETE = -14, IAVF_ERR_RESET_FAILED = -15, diff --git a/drivers/net/ethernet/intel/iavf/iavf_txrx.c b/drivers/net/ethernet/intel/iavf/iavf_txrx.c index 8cbe7ad1347c..978f651c6b09 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_txrx.c +++ b/drivers/net/ethernet/intel/iavf/iavf_txrx.c @@ -374,29 +374,60 @@ static inline bool iavf_container_is_rx(struct iavf_q_vector *q_vector, return &q_vector->rx == rc; } -static inline unsigned int iavf_itr_divisor(struct iavf_q_vector *q_vector) -{ - unsigned int divisor; +#define IAVF_AIM_MULTIPLIER_100G 2560 +#define IAVF_AIM_MULTIPLIER_50G 1280 +#define IAVF_AIM_MULTIPLIER_40G 1024 +#define IAVF_AIM_MULTIPLIER_20G 512 +#define IAVF_AIM_MULTIPLIER_10G 256 +#define IAVF_AIM_MULTIPLIER_1G 32 - switch (q_vector->adapter->link_speed) { +static unsigned int iavf_mbps_itr_multiplier(u32 speed_mbps) +{ + switch (speed_mbps) { + case SPEED_100000: + return IAVF_AIM_MULTIPLIER_100G; + case SPEED_50000: + return IAVF_AIM_MULTIPLIER_50G; + case SPEED_40000: + return IAVF_AIM_MULTIPLIER_40G; + case SPEED_25000: + case SPEED_20000: + return IAVF_AIM_MULTIPLIER_20G; + case SPEED_10000: + default: + return IAVF_AIM_MULTIPLIER_10G; + case SPEED_1000: + case SPEED_100: + return IAVF_AIM_MULTIPLIER_1G; + } +} + +static unsigned int +iavf_virtchnl_itr_multiplier(enum virtchnl_link_speed speed_virtchnl) +{ + switch (speed_virtchnl) { case VIRTCHNL_LINK_SPEED_40GB: - divisor = IAVF_ITR_ADAPTIVE_MIN_INC * 1024; - break; + return IAVF_AIM_MULTIPLIER_40G; case VIRTCHNL_LINK_SPEED_25GB: case VIRTCHNL_LINK_SPEED_20GB: - divisor = IAVF_ITR_ADAPTIVE_MIN_INC * 512; - break; - default: + return IAVF_AIM_MULTIPLIER_20G; case VIRTCHNL_LINK_SPEED_10GB: - divisor = IAVF_ITR_ADAPTIVE_MIN_INC * 256; - break; + default: + return IAVF_AIM_MULTIPLIER_10G; case VIRTCHNL_LINK_SPEED_1GB: case VIRTCHNL_LINK_SPEED_100MB: - divisor = IAVF_ITR_ADAPTIVE_MIN_INC * 32; - break; + return IAVF_AIM_MULTIPLIER_1G; } +} - return divisor; +static unsigned int iavf_itr_divisor(struct iavf_adapter *adapter) +{ + if (ADV_LINK_SUPPORT(adapter)) + return IAVF_ITR_ADAPTIVE_MIN_INC * + iavf_mbps_itr_multiplier(adapter->link_speed_mbps); + else + return IAVF_ITR_ADAPTIVE_MIN_INC * + iavf_virtchnl_itr_multiplier(adapter->link_speed); } /** @@ -586,8 +617,9 @@ static void iavf_update_itr(struct iavf_q_vector *q_vector, * Use addition as we have already recorded the new latency flag * for the ITR value. */ - itr += DIV_ROUND_UP(avg_wire_size, iavf_itr_divisor(q_vector)) * - IAVF_ITR_ADAPTIVE_MIN_INC; + itr += DIV_ROUND_UP(avg_wire_size, + iavf_itr_divisor(q_vector->adapter)) * + IAVF_ITR_ADAPTIVE_MIN_INC; if ((itr & IAVF_ITR_MASK) > IAVF_ITR_ADAPTIVE_MAX_USECS) { itr &= IAVF_ITR_ADAPTIVE_LATENCY; diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c index 5263cefe46f5..782450d5c12f 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c +++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c @@ -22,17 +22,17 @@ static int iavf_send_pf_msg(struct iavf_adapter *adapter, enum virtchnl_ops op, u8 *msg, u16 len) { struct iavf_hw *hw = &adapter->hw; - enum iavf_status err; + enum iavf_status status; if (adapter->flags & IAVF_FLAG_PF_COMMS_FAILED) return 0; /* nothing to see here, move along */ - err = iavf_aq_send_msg_to_pf(hw, op, 0, msg, len, NULL); - if (err) - dev_dbg(&adapter->pdev->dev, "Unable to send opcode %d to PF, err %s, aq_err %s\n", - op, iavf_stat_str(hw, err), + status = iavf_aq_send_msg_to_pf(hw, op, 0, msg, len, NULL); + if (status) + dev_dbg(&adapter->pdev->dev, "Unable to send opcode %d to PF, status %s, aq_err %s\n", + op, iavf_stat_str(hw, status), iavf_aq_str(hw, hw->aq.asq_last_status)); - return err; + return iavf_status_to_errno(status); } /** @@ -54,6 +54,41 @@ int iavf_send_api_ver(struct iavf_adapter *adapter) sizeof(vvi)); } +/** + * iavf_poll_virtchnl_msg + * @hw: HW configuration structure + * @event: event to populate on success + * @op_to_poll: requested virtchnl op to poll for + * + * Initialize poll for virtchnl msg matching the requested_op. Returns 0 + * if a message of the correct opcode is in the queue or an error code + * if no message matching the op code is waiting and other failures. + */ +static int +iavf_poll_virtchnl_msg(struct iavf_hw *hw, struct iavf_arq_event_info *event, + enum virtchnl_ops op_to_poll) +{ + enum virtchnl_ops received_op; + enum iavf_status status; + u32 v_retval; + + while (1) { + /* When the AQ is empty, iavf_clean_arq_element will return + * nonzero and this loop will terminate. + */ + status = iavf_clean_arq_element(hw, event, NULL); + if (status != IAVF_SUCCESS) + return iavf_status_to_errno(status); + received_op = + (enum virtchnl_ops)le32_to_cpu(event->desc.cookie_high); + if (op_to_poll == received_op) + break; + } + + v_retval = le32_to_cpu(event->desc.cookie_low); + return virtchnl_status_to_errno((enum virtchnl_status_code)v_retval); +} + /** * iavf_verify_api_ver * @adapter: adapter structure @@ -65,55 +100,28 @@ int iavf_send_api_ver(struct iavf_adapter *adapter) **/ int iavf_verify_api_ver(struct iavf_adapter *adapter) { - struct virtchnl_version_info *pf_vvi; - struct iavf_hw *hw = &adapter->hw; struct iavf_arq_event_info event; - enum virtchnl_ops op; - enum iavf_status err; + int err; event.buf_len = IAVF_MAX_AQ_BUF_SIZE; - event.msg_buf = kzalloc(event.buf_len, GFP_KERNEL); - if (!event.msg_buf) { - err = -ENOMEM; - goto out; + event.msg_buf = kzalloc(IAVF_MAX_AQ_BUF_SIZE, GFP_KERNEL); + if (!event.msg_buf) + return -ENOMEM; + + err = iavf_poll_virtchnl_msg(&adapter->hw, &event, VIRTCHNL_OP_VERSION); + if (!err) { + struct virtchnl_version_info *pf_vvi = + (struct virtchnl_version_info *)event.msg_buf; + adapter->pf_version = *pf_vvi; + + if (pf_vvi->major > VIRTCHNL_VERSION_MAJOR || + (pf_vvi->major == VIRTCHNL_VERSION_MAJOR && + pf_vvi->minor > VIRTCHNL_VERSION_MINOR)) + err = -EIO; } - while (1) { - err = iavf_clean_arq_element(hw, &event, NULL); - /* When the AQ is empty, iavf_clean_arq_element will return - * nonzero and this loop will terminate. - */ - if (err) - goto out_alloc; - op = - (enum virtchnl_ops)le32_to_cpu(event.desc.cookie_high); - if (op == VIRTCHNL_OP_VERSION) - break; - } - - - err = (enum iavf_status)le32_to_cpu(event.desc.cookie_low); - if (err) - goto out_alloc; - - if (op != VIRTCHNL_OP_VERSION) { - dev_info(&adapter->pdev->dev, "Invalid reply type %d from PF\n", - op); - err = -EIO; - goto out_alloc; - } - - pf_vvi = (struct virtchnl_version_info *)event.msg_buf; - adapter->pf_version = *pf_vvi; - - if ((pf_vvi->major > VIRTCHNL_VERSION_MAJOR) || - ((pf_vvi->major == VIRTCHNL_VERSION_MAJOR) && - (pf_vvi->minor > VIRTCHNL_VERSION_MINOR))) - err = -EIO; - -out_alloc: kfree(event.msg_buf); -out: + return err; } @@ -208,33 +216,17 @@ int iavf_get_vf_config(struct iavf_adapter *adapter) { struct iavf_hw *hw = &adapter->hw; struct iavf_arq_event_info event; - enum virtchnl_ops op; - enum iavf_status err; u16 len; + int err; - len = sizeof(struct virtchnl_vf_resource) + + len = sizeof(struct virtchnl_vf_resource) + IAVF_MAX_VF_VSI * sizeof(struct virtchnl_vsi_resource); event.buf_len = len; - event.msg_buf = kzalloc(event.buf_len, GFP_KERNEL); - if (!event.msg_buf) { - err = -ENOMEM; - goto out; - } + event.msg_buf = kzalloc(len, GFP_KERNEL); + if (!event.msg_buf) + return -ENOMEM; - while (1) { - /* When the AQ is empty, iavf_clean_arq_element will return - * nonzero and this loop will terminate. - */ - err = iavf_clean_arq_element(hw, &event, NULL); - if (err) - goto out_alloc; - op = - (enum virtchnl_ops)le32_to_cpu(event.desc.cookie_high); - if (op == VIRTCHNL_OP_GET_VF_RESOURCES) - break; - } - - err = (enum iavf_status)le32_to_cpu(event.desc.cookie_low); + err = iavf_poll_virtchnl_msg(hw, &event, VIRTCHNL_OP_GET_VF_RESOURCES); memcpy(adapter->vf_res, event.msg_buf, min(event.msg_len, len)); /* some PFs send more queues than we should have so validate that @@ -243,48 +235,32 @@ int iavf_get_vf_config(struct iavf_adapter *adapter) if (!err) iavf_validate_num_queues(adapter); iavf_vf_parse_hw_config(hw, adapter->vf_res); -out_alloc: + kfree(event.msg_buf); -out: + return err; } int iavf_get_vf_vlan_v2_caps(struct iavf_adapter *adapter) { - struct iavf_hw *hw = &adapter->hw; struct iavf_arq_event_info event; - enum virtchnl_ops op; - enum iavf_status err; + int err; u16 len; - len = sizeof(struct virtchnl_vlan_caps); + len = sizeof(struct virtchnl_vlan_caps); event.buf_len = len; - event.msg_buf = kzalloc(event.buf_len, GFP_KERNEL); - if (!event.msg_buf) { - err = -ENOMEM; - goto out; - } + event.msg_buf = kzalloc(len, GFP_KERNEL); + if (!event.msg_buf) + return -ENOMEM; - while (1) { - /* When the AQ is empty, iavf_clean_arq_element will return - * nonzero and this loop will terminate. - */ - err = iavf_clean_arq_element(hw, &event, NULL); - if (err) - goto out_alloc; - op = (enum virtchnl_ops)le32_to_cpu(event.desc.cookie_high); - if (op == VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS) - break; - } + err = iavf_poll_virtchnl_msg(&adapter->hw, &event, + VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS); + if (!err) + memcpy(&adapter->vlan_v2_caps, event.msg_buf, + min(event.msg_len, len)); - err = (enum iavf_status)le32_to_cpu(event.desc.cookie_low); - if (err) - goto out_alloc; - - memcpy(&adapter->vlan_v2_caps, event.msg_buf, min(event.msg_len, len)); -out_alloc: kfree(event.msg_buf); -out: + return err; } @@ -453,6 +429,20 @@ void iavf_map_queues(struct iavf_adapter *adapter) kfree(vimi); } +/** + * iavf_set_mac_addr_type - Set the correct request type from the filter type + * @virtchnl_ether_addr: pointer to requested list element + * @filter: pointer to requested filter + **/ +static void +iavf_set_mac_addr_type(struct virtchnl_ether_addr *virtchnl_ether_addr, + const struct iavf_mac_filter *filter) +{ + virtchnl_ether_addr->type = filter->is_primary ? + VIRTCHNL_ETHER_ADDR_PRIMARY : + VIRTCHNL_ETHER_ADDR_EXTRA; +} + /** * iavf_add_ether_addrs * @adapter: adapter structure @@ -508,6 +498,7 @@ void iavf_add_ether_addrs(struct iavf_adapter *adapter) list_for_each_entry(f, &adapter->mac_filter_list, list) { if (f->add) { ether_addr_copy(veal->list[i].addr, f->macaddr); + iavf_set_mac_addr_type(&veal->list[i], f); i++; f->add = false; if (i == count) @@ -577,6 +568,7 @@ void iavf_del_ether_addrs(struct iavf_adapter *adapter) list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) { if (f->remove) { ether_addr_copy(veal->list[i].addr, f->macaddr); + iavf_set_mac_addr_type(&veal->list[i], f); i++; list_del(&f->list); kfree(f); @@ -1827,11 +1819,13 @@ void iavf_del_adv_rss_cfg(struct iavf_adapter *adapter) * * Request that the PF reset this VF. No response is expected. **/ -void iavf_request_reset(struct iavf_adapter *adapter) +int iavf_request_reset(struct iavf_adapter *adapter) { + int err; /* Don't check CURRENT_OP - this is always higher priority */ - iavf_send_pf_msg(adapter, VIRTCHNL_OP_RESET_VF, NULL, 0); + err = iavf_send_pf_msg(adapter, VIRTCHNL_OP_RESET_VF, NULL, 0); adapter->current_op = VIRTCHNL_OP_UNKNOWN; + return err; } /** diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile index c36faa7d1471..9183d480b70b 100644 --- a/drivers/net/ethernet/intel/ice/Makefile +++ b/drivers/net/ethernet/intel/ice/Makefile @@ -18,8 +18,12 @@ ice-y := ice_main.o \ ice_txrx_lib.o \ ice_txrx.o \ ice_fltr.o \ + ice_pf_vsi_vlan_ops.o \ + ice_vsi_vlan_ops.o \ + ice_vsi_vlan_lib.o \ ice_fdir.o \ ice_ethtool_fdir.o \ + ice_vlan_mode.o \ ice_flex_pipe.o \ ice_flow.o \ ice_idc.o \ @@ -29,9 +33,16 @@ ice-y := ice_main.o \ ice_ethtool.o \ ice_repr.o \ ice_tc_lib.o -ice-$(CONFIG_PCI_IOV) += ice_virtchnl_allowlist.o -ice-$(CONFIG_PCI_IOV) += ice_virtchnl_pf.o ice_sriov.o ice_virtchnl_fdir.o +ice-$(CONFIG_PCI_IOV) += \ + ice_sriov.o \ + ice_virtchnl.o \ + ice_virtchnl_allowlist.o \ + ice_virtchnl_fdir.o \ + ice_vf_mbx.o \ + ice_vf_vsi_vlan_ops.o \ + ice_vf_lib.o ice-$(CONFIG_PTP_1588_CLOCK) += ice_ptp.o ice_ptp_hw.o +ice-$(CONFIG_TTY) += ice_gnss.o ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index bea1d1e39fa2..b0b27bfcd7a2 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -51,9 +51,7 @@ #include #include #include -#if IS_ENABLED(CONFIG_DCB) -#include -#endif /* CONFIG_DCB */ +#include #include "ice_devids.h" #include "ice_type.h" #include "ice_txrx.h" @@ -63,8 +61,8 @@ #include "ice_flow.h" #include "ice_sched.h" #include "ice_idc_int.h" -#include "ice_virtchnl_pf.h" #include "ice_sriov.h" +#include "ice_vf_mbx.h" #include "ice_ptp.h" #include "ice_fdir.h" #include "ice_xsk.h" @@ -72,6 +70,8 @@ #include "ice_repr.h" #include "ice_eswitch.h" #include "ice_lag.h" +#include "ice_vsi_vlan_ops.h" +#include "ice_gnss.h" #define ICE_BAR0 0 #define ICE_REQ_DESC_MULTIPLE 32 @@ -107,7 +107,6 @@ /* All VF control VSIs share the same IRQ, so assign a unique ID for them */ #define ICE_RES_VF_CTRL_VEC_ID (ICE_RES_RDMA_VEC_ID - 1) #define ICE_INVAL_Q_INDEX 0xffff -#define ICE_INVAL_VFID 256 #define ICE_MAX_RXQS_PER_TC 256 /* Used when setting VSI context per TC Rx queues */ @@ -183,6 +182,7 @@ enum ice_feature { ICE_F_DSCP, ICE_F_SMA_CTRL, + ICE_F_GNSS, ICE_F_MAX }; @@ -290,6 +290,7 @@ enum ice_pf_state { ICE_LINK_DEFAULT_OVERRIDE_PENDING, ICE_PHY_INIT_COMPLETE, ICE_FD_VF_FLUSH_CTX, /* set at FD Rx IRQ or timeout */ + ICE_AUX_ERR_PENDING, ICE_STATE_NBITS /* must be last */ }; @@ -330,7 +331,7 @@ struct ice_vsi { u16 vsi_num; /* HW (absolute) index of this VSI */ u16 idx; /* software index in pf->vsi[] */ - s16 vf_id; /* VF ID for SR-IOV VSIs */ + struct ice_vf *vf; /* VF associated with this VSI */ u16 ethtype; /* Ethernet protocol for pause frame */ u16 num_gfltr; @@ -367,6 +368,8 @@ struct ice_vsi { u8 irqs_ready:1; u8 current_isup:1; /* Sync 'link up' logging */ u8 stat_offsets_loaded:1; + struct ice_vsi_vlan_ops inner_vlan_ops; + struct ice_vsi_vlan_ops outer_vlan_ops; u16 num_vlan; /* queue information */ @@ -467,7 +470,6 @@ enum ice_pf_flags { ICE_FLAG_FD_ENA, ICE_FLAG_PTP_SUPPORTED, /* PTP is supported by NVM */ ICE_FLAG_PTP, /* PTP is enabled by software */ - ICE_FLAG_AUX_ENA, ICE_FLAG_ADV_FEATURES, ICE_FLAG_TC_MQPRIO, /* support for Multi queue TC */ ICE_FLAG_CLS_FLOWER, @@ -481,9 +483,11 @@ enum ice_pf_flags { ICE_FLAG_LEGACY_RX, ICE_FLAG_VF_TRUE_PROMISC_ENA, ICE_FLAG_MDD_AUTO_RESET_VF, + ICE_FLAG_VF_VLAN_PRUNING, ICE_FLAG_LINK_LENIENT_MODE_ENA, ICE_FLAG_PLUG_AUX_DEV, ICE_FLAG_MTU_CHANGED, + ICE_FLAG_GNSS, /* GNSS successfully initialized */ ICE_PF_FLAGS_NBITS /* must be last */ }; @@ -524,15 +528,7 @@ struct ice_pf { struct ice_vsi **vsi; /* VSIs created by the driver */ struct ice_sw *first_sw; /* first switch created by firmware */ u16 eswitch_mode; /* current mode of eswitch */ - /* Virtchnl/SR-IOV config info */ - struct ice_vf *vf; - u16 num_alloc_vfs; /* actual number of VFs allocated */ - u16 num_vfs_supported; /* num VFs supported for this PF */ - u16 num_qps_per_vf; - u16 num_msix_per_vf; - /* used to ratelimit the MDD event logging */ - unsigned long last_printed_mdd_jiffies; - DECLARE_BITMAP(malvfs, ICE_MAX_VF_COUNT); + struct ice_vfs vfs; DECLARE_BITMAP(features, ICE_F_MAX); DECLARE_BITMAP(state, ICE_STATE_NBITS); DECLARE_BITMAP(flags, ICE_PF_FLAGS_NBITS); @@ -547,6 +543,9 @@ struct ice_pf { struct mutex tc_mutex; /* lock to protect TC changes */ u32 msg_enable; struct ice_ptp ptp; + struct tty_driver *ice_gnss_tty_driver; + struct tty_port gnss_tty_port; + struct gnss_serial *gnss_serial; u16 num_rdma_msix; /* Total MSIX vectors for RDMA driver */ u16 rdma_base_vector; @@ -559,6 +558,7 @@ struct ice_pf { wait_queue_head_t reset_wait_queue; u32 hw_csum_rx_error; + u32 oicr_err_reg; u16 oicr_idx; /* Other interrupt cause MSIX vector index */ u16 num_avail_sw_msix; /* remaining MSIX SW vectors left unclaimed */ u16 max_pf_txqs; /* Total Tx queues PF wide */ @@ -834,6 +834,9 @@ u16 ice_get_avail_rxq_count(struct ice_pf *pf); int ice_vsi_recfg_qs(struct ice_vsi *vsi, int new_rx, int new_tx); void ice_update_vsi_stats(struct ice_vsi *vsi); void ice_update_pf_stats(struct ice_pf *pf); +void +ice_fetch_u64_stats_per_ring(struct u64_stats_sync *syncp, + struct ice_q_stats stats, u64 *pkts, u64 *bytes); int ice_up(struct ice_vsi *vsi); int ice_down(struct ice_vsi *vsi); int ice_vsi_cfg(struct ice_vsi *vsi); @@ -887,7 +890,6 @@ static inline void ice_set_rdma_cap(struct ice_pf *pf) { if (pf->hw.func_caps.common_cap.rdma && pf->num_rdma_msix) { set_bit(ICE_FLAG_RDMA_ENA, pf->flags); - set_bit(ICE_FLAG_AUX_ENA, pf->flags); set_bit(ICE_FLAG_PLUG_AUX_DEV, pf->flags); } } @@ -909,6 +911,5 @@ static inline void ice_clear_rdma_cap(struct ice_pf *pf) ice_unplug_aux_dev(pf); clear_bit(ICE_FLAG_RDMA_ENA, pf->flags); - clear_bit(ICE_FLAG_AUX_ENA, pf->flags); } #endif /* _ICE_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h index ad1dcfa5ff65..b25e27c4d887 100644 --- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h +++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h @@ -226,6 +226,15 @@ struct ice_aqc_get_sw_cfg_resp_elem { #define ICE_AQC_GET_SW_CONF_RESP_IS_VF BIT(15) }; +/* Set Port parameters, (direct, 0x0203) */ +struct ice_aqc_set_port_params { + __le16 cmd_flags; +#define ICE_AQC_SET_P_PARAMS_DOUBLE_VLAN_ENA BIT(2) + __le16 bad_frame_vsi; + __le16 swid; + u8 reserved[10]; +}; + /* These resource type defines are used for all switch resource * commands where a resource type is required, such as: * Get Resource Allocation command (indirect 0x0204) @@ -283,6 +292,40 @@ struct ice_aqc_alloc_free_res_elem { struct ice_aqc_res_elem elem[]; }; +/* Request buffer for Set VLAN Mode AQ command (indirect 0x020C) */ +struct ice_aqc_set_vlan_mode { + u8 reserved; + u8 l2tag_prio_tagging; +#define ICE_AQ_VLAN_PRIO_TAG_S 0 +#define ICE_AQ_VLAN_PRIO_TAG_M (0x7 << ICE_AQ_VLAN_PRIO_TAG_S) +#define ICE_AQ_VLAN_PRIO_TAG_NOT_SUPPORTED 0x0 +#define ICE_AQ_VLAN_PRIO_TAG_STAG 0x1 +#define ICE_AQ_VLAN_PRIO_TAG_OUTER_CTAG 0x2 +#define ICE_AQ_VLAN_PRIO_TAG_OUTER_VLAN 0x3 +#define ICE_AQ_VLAN_PRIO_TAG_INNER_CTAG 0x4 +#define ICE_AQ_VLAN_PRIO_TAG_MAX 0x4 +#define ICE_AQ_VLAN_PRIO_TAG_ERROR 0x7 + u8 l2tag_reserved[64]; + u8 rdma_packet; +#define ICE_AQ_VLAN_RDMA_TAG_S 0 +#define ICE_AQ_VLAN_RDMA_TAG_M (0x3F << ICE_AQ_VLAN_RDMA_TAG_S) +#define ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING 0x10 +#define ICE_AQ_DVM_VLAN_RDMA_PKT_FLAG_SETTING 0x1A + u8 rdma_reserved[2]; + u8 mng_vlan_prot_id; +#define ICE_AQ_VLAN_MNG_PROTOCOL_ID_OUTER 0x10 +#define ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER 0x11 + u8 prot_id_reserved[30]; +}; + +/* Response buffer for Get VLAN Mode AQ command (indirect 0x020D) */ +struct ice_aqc_get_vlan_mode { + u8 vlan_mode; +#define ICE_AQ_VLAN_MODE_DVM_ENA BIT(0) + u8 l2tag_prio_tagging; + u8 reserved[98]; +}; + /* Add VSI (indirect 0x0210) * Update VSI (indirect 0x0211) * Get VSI (indirect 0x0212) @@ -343,108 +386,113 @@ struct ice_aqc_vsi_props { #define ICE_AQ_VSI_SW_FLAG_SRC_PRUNE BIT(7) u8 sw_flags2; #define ICE_AQ_VSI_SW_FLAG_RX_PRUNE_EN_S 0 -#define ICE_AQ_VSI_SW_FLAG_RX_PRUNE_EN_M \ - (0xF << ICE_AQ_VSI_SW_FLAG_RX_PRUNE_EN_S) +#define ICE_AQ_VSI_SW_FLAG_RX_PRUNE_EN_M (0xF << ICE_AQ_VSI_SW_FLAG_RX_PRUNE_EN_S) #define ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA BIT(0) #define ICE_AQ_VSI_SW_FLAG_LAN_ENA BIT(4) u8 veb_stat_id; #define ICE_AQ_VSI_SW_VEB_STAT_ID_S 0 -#define ICE_AQ_VSI_SW_VEB_STAT_ID_M (0x1F << ICE_AQ_VSI_SW_VEB_STAT_ID_S) +#define ICE_AQ_VSI_SW_VEB_STAT_ID_M (0x1F << ICE_AQ_VSI_SW_VEB_STAT_ID_S) #define ICE_AQ_VSI_SW_VEB_STAT_ID_VALID BIT(5) /* security section */ u8 sec_flags; #define ICE_AQ_VSI_SEC_FLAG_ALLOW_DEST_OVRD BIT(0) #define ICE_AQ_VSI_SEC_FLAG_ENA_MAC_ANTI_SPOOF BIT(2) -#define ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S 4 -#define ICE_AQ_VSI_SEC_TX_PRUNE_ENA_M (0xF << ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S) +#define ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S 4 +#define ICE_AQ_VSI_SEC_TX_PRUNE_ENA_M (0xF << ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S) #define ICE_AQ_VSI_SEC_TX_VLAN_PRUNE_ENA BIT(0) u8 sec_reserved; /* VLAN section */ - __le16 pvid; /* VLANS include priority bits */ - u8 pvlan_reserved[2]; - u8 vlan_flags; -#define ICE_AQ_VSI_VLAN_MODE_S 0 -#define ICE_AQ_VSI_VLAN_MODE_M (0x3 << ICE_AQ_VSI_VLAN_MODE_S) -#define ICE_AQ_VSI_VLAN_MODE_UNTAGGED 0x1 -#define ICE_AQ_VSI_VLAN_MODE_TAGGED 0x2 -#define ICE_AQ_VSI_VLAN_MODE_ALL 0x3 -#define ICE_AQ_VSI_PVLAN_INSERT_PVID BIT(2) -#define ICE_AQ_VSI_VLAN_EMOD_S 3 -#define ICE_AQ_VSI_VLAN_EMOD_M (0x3 << ICE_AQ_VSI_VLAN_EMOD_S) -#define ICE_AQ_VSI_VLAN_EMOD_STR_BOTH (0x0 << ICE_AQ_VSI_VLAN_EMOD_S) -#define ICE_AQ_VSI_VLAN_EMOD_STR_UP (0x1 << ICE_AQ_VSI_VLAN_EMOD_S) -#define ICE_AQ_VSI_VLAN_EMOD_STR (0x2 << ICE_AQ_VSI_VLAN_EMOD_S) -#define ICE_AQ_VSI_VLAN_EMOD_NOTHING (0x3 << ICE_AQ_VSI_VLAN_EMOD_S) - u8 pvlan_reserved2[3]; + __le16 port_based_inner_vlan; /* VLANS include priority bits */ + u8 inner_vlan_reserved[2]; + u8 inner_vlan_flags; +#define ICE_AQ_VSI_INNER_VLAN_TX_MODE_S 0 +#define ICE_AQ_VSI_INNER_VLAN_TX_MODE_M (0x3 << ICE_AQ_VSI_INNER_VLAN_TX_MODE_S) +#define ICE_AQ_VSI_INNER_VLAN_TX_MODE_ACCEPTUNTAGGED 0x1 +#define ICE_AQ_VSI_INNER_VLAN_TX_MODE_ACCEPTTAGGED 0x2 +#define ICE_AQ_VSI_INNER_VLAN_TX_MODE_ALL 0x3 +#define ICE_AQ_VSI_INNER_VLAN_INSERT_PVID BIT(2) +#define ICE_AQ_VSI_INNER_VLAN_EMODE_S 3 +#define ICE_AQ_VSI_INNER_VLAN_EMODE_M (0x3 << ICE_AQ_VSI_INNER_VLAN_EMODE_S) +#define ICE_AQ_VSI_INNER_VLAN_EMODE_STR_BOTH (0x0 << ICE_AQ_VSI_INNER_VLAN_EMODE_S) +#define ICE_AQ_VSI_INNER_VLAN_EMODE_STR_UP (0x1 << ICE_AQ_VSI_INNER_VLAN_EMODE_S) +#define ICE_AQ_VSI_INNER_VLAN_EMODE_STR (0x2 << ICE_AQ_VSI_INNER_VLAN_EMODE_S) +#define ICE_AQ_VSI_INNER_VLAN_EMODE_NOTHING (0x3 << ICE_AQ_VSI_INNER_VLAN_EMODE_S) + u8 inner_vlan_reserved2[3]; /* ingress egress up sections */ __le32 ingress_table; /* bitmap, 3 bits per up */ -#define ICE_AQ_VSI_UP_TABLE_UP0_S 0 -#define ICE_AQ_VSI_UP_TABLE_UP0_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP0_S) -#define ICE_AQ_VSI_UP_TABLE_UP1_S 3 -#define ICE_AQ_VSI_UP_TABLE_UP1_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP1_S) -#define ICE_AQ_VSI_UP_TABLE_UP2_S 6 -#define ICE_AQ_VSI_UP_TABLE_UP2_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP2_S) -#define ICE_AQ_VSI_UP_TABLE_UP3_S 9 -#define ICE_AQ_VSI_UP_TABLE_UP3_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP3_S) -#define ICE_AQ_VSI_UP_TABLE_UP4_S 12 -#define ICE_AQ_VSI_UP_TABLE_UP4_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP4_S) -#define ICE_AQ_VSI_UP_TABLE_UP5_S 15 -#define ICE_AQ_VSI_UP_TABLE_UP5_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP5_S) -#define ICE_AQ_VSI_UP_TABLE_UP6_S 18 -#define ICE_AQ_VSI_UP_TABLE_UP6_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP6_S) -#define ICE_AQ_VSI_UP_TABLE_UP7_S 21 -#define ICE_AQ_VSI_UP_TABLE_UP7_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP7_S) +#define ICE_AQ_VSI_UP_TABLE_UP0_S 0 +#define ICE_AQ_VSI_UP_TABLE_UP0_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP0_S) +#define ICE_AQ_VSI_UP_TABLE_UP1_S 3 +#define ICE_AQ_VSI_UP_TABLE_UP1_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP1_S) +#define ICE_AQ_VSI_UP_TABLE_UP2_S 6 +#define ICE_AQ_VSI_UP_TABLE_UP2_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP2_S) +#define ICE_AQ_VSI_UP_TABLE_UP3_S 9 +#define ICE_AQ_VSI_UP_TABLE_UP3_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP3_S) +#define ICE_AQ_VSI_UP_TABLE_UP4_S 12 +#define ICE_AQ_VSI_UP_TABLE_UP4_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP4_S) +#define ICE_AQ_VSI_UP_TABLE_UP5_S 15 +#define ICE_AQ_VSI_UP_TABLE_UP5_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP5_S) +#define ICE_AQ_VSI_UP_TABLE_UP6_S 18 +#define ICE_AQ_VSI_UP_TABLE_UP6_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP6_S) +#define ICE_AQ_VSI_UP_TABLE_UP7_S 21 +#define ICE_AQ_VSI_UP_TABLE_UP7_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP7_S) __le32 egress_table; /* same defines as for ingress table */ /* outer tags section */ - __le16 outer_tag; - u8 outer_tag_flags; -#define ICE_AQ_VSI_OUTER_TAG_MODE_S 0 -#define ICE_AQ_VSI_OUTER_TAG_MODE_M (0x3 << ICE_AQ_VSI_OUTER_TAG_MODE_S) -#define ICE_AQ_VSI_OUTER_TAG_NOTHING 0x0 -#define ICE_AQ_VSI_OUTER_TAG_REMOVE 0x1 -#define ICE_AQ_VSI_OUTER_TAG_COPY 0x2 -#define ICE_AQ_VSI_OUTER_TAG_TYPE_S 2 -#define ICE_AQ_VSI_OUTER_TAG_TYPE_M (0x3 << ICE_AQ_VSI_OUTER_TAG_TYPE_S) -#define ICE_AQ_VSI_OUTER_TAG_NONE 0x0 -#define ICE_AQ_VSI_OUTER_TAG_STAG 0x1 -#define ICE_AQ_VSI_OUTER_TAG_VLAN_8100 0x2 -#define ICE_AQ_VSI_OUTER_TAG_VLAN_9100 0x3 -#define ICE_AQ_VSI_OUTER_TAG_INSERT BIT(4) -#define ICE_AQ_VSI_OUTER_TAG_ACCEPT_HOST BIT(6) - u8 outer_tag_reserved; + __le16 port_based_outer_vlan; + u8 outer_vlan_flags; +#define ICE_AQ_VSI_OUTER_VLAN_EMODE_S 0 +#define ICE_AQ_VSI_OUTER_VLAN_EMODE_M (0x3 << ICE_AQ_VSI_OUTER_VLAN_EMODE_S) +#define ICE_AQ_VSI_OUTER_VLAN_EMODE_SHOW_BOTH 0x0 +#define ICE_AQ_VSI_OUTER_VLAN_EMODE_SHOW_UP 0x1 +#define ICE_AQ_VSI_OUTER_VLAN_EMODE_SHOW 0x2 +#define ICE_AQ_VSI_OUTER_VLAN_EMODE_NOTHING 0x3 +#define ICE_AQ_VSI_OUTER_TAG_TYPE_S 2 +#define ICE_AQ_VSI_OUTER_TAG_TYPE_M (0x3 << ICE_AQ_VSI_OUTER_TAG_TYPE_S) +#define ICE_AQ_VSI_OUTER_TAG_NONE 0x0 +#define ICE_AQ_VSI_OUTER_TAG_STAG 0x1 +#define ICE_AQ_VSI_OUTER_TAG_VLAN_8100 0x2 +#define ICE_AQ_VSI_OUTER_TAG_VLAN_9100 0x3 +#define ICE_AQ_VSI_OUTER_VLAN_PORT_BASED_INSERT BIT(4) +#define ICE_AQ_VSI_OUTER_VLAN_TX_MODE_S 5 +#define ICE_AQ_VSI_OUTER_VLAN_TX_MODE_M (0x3 << ICE_AQ_VSI_OUTER_VLAN_TX_MODE_S) +#define ICE_AQ_VSI_OUTER_VLAN_TX_MODE_ACCEPTUNTAGGED 0x1 +#define ICE_AQ_VSI_OUTER_VLAN_TX_MODE_ACCEPTTAGGED 0x2 +#define ICE_AQ_VSI_OUTER_VLAN_TX_MODE_ALL 0x3 +#define ICE_AQ_VSI_OUTER_VLAN_BLOCK_TX_DESC BIT(7) + u8 outer_vlan_reserved; /* queue mapping section */ __le16 mapping_flags; -#define ICE_AQ_VSI_Q_MAP_CONTIG 0x0 -#define ICE_AQ_VSI_Q_MAP_NONCONTIG BIT(0) +#define ICE_AQ_VSI_Q_MAP_CONTIG 0x0 +#define ICE_AQ_VSI_Q_MAP_NONCONTIG BIT(0) __le16 q_mapping[16]; -#define ICE_AQ_VSI_Q_S 0 -#define ICE_AQ_VSI_Q_M (0x7FF << ICE_AQ_VSI_Q_S) +#define ICE_AQ_VSI_Q_S 0 +#define ICE_AQ_VSI_Q_M (0x7FF << ICE_AQ_VSI_Q_S) __le16 tc_mapping[8]; -#define ICE_AQ_VSI_TC_Q_OFFSET_S 0 -#define ICE_AQ_VSI_TC_Q_OFFSET_M (0x7FF << ICE_AQ_VSI_TC_Q_OFFSET_S) -#define ICE_AQ_VSI_TC_Q_NUM_S 11 -#define ICE_AQ_VSI_TC_Q_NUM_M (0xF << ICE_AQ_VSI_TC_Q_NUM_S) +#define ICE_AQ_VSI_TC_Q_OFFSET_S 0 +#define ICE_AQ_VSI_TC_Q_OFFSET_M (0x7FF << ICE_AQ_VSI_TC_Q_OFFSET_S) +#define ICE_AQ_VSI_TC_Q_NUM_S 11 +#define ICE_AQ_VSI_TC_Q_NUM_M (0xF << ICE_AQ_VSI_TC_Q_NUM_S) /* queueing option section */ u8 q_opt_rss; -#define ICE_AQ_VSI_Q_OPT_RSS_LUT_S 0 -#define ICE_AQ_VSI_Q_OPT_RSS_LUT_M (0x3 << ICE_AQ_VSI_Q_OPT_RSS_LUT_S) -#define ICE_AQ_VSI_Q_OPT_RSS_LUT_VSI 0x0 -#define ICE_AQ_VSI_Q_OPT_RSS_LUT_PF 0x2 -#define ICE_AQ_VSI_Q_OPT_RSS_LUT_GBL 0x3 -#define ICE_AQ_VSI_Q_OPT_RSS_GBL_LUT_S 2 -#define ICE_AQ_VSI_Q_OPT_RSS_GBL_LUT_M (0xF << ICE_AQ_VSI_Q_OPT_RSS_GBL_LUT_S) -#define ICE_AQ_VSI_Q_OPT_RSS_HASH_S 6 -#define ICE_AQ_VSI_Q_OPT_RSS_HASH_M (0x3 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S) -#define ICE_AQ_VSI_Q_OPT_RSS_TPLZ (0x0 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S) -#define ICE_AQ_VSI_Q_OPT_RSS_SYM_TPLZ (0x1 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S) -#define ICE_AQ_VSI_Q_OPT_RSS_XOR (0x2 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S) -#define ICE_AQ_VSI_Q_OPT_RSS_JHASH (0x3 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S) +#define ICE_AQ_VSI_Q_OPT_RSS_LUT_S 0 +#define ICE_AQ_VSI_Q_OPT_RSS_LUT_M (0x3 << ICE_AQ_VSI_Q_OPT_RSS_LUT_S) +#define ICE_AQ_VSI_Q_OPT_RSS_LUT_VSI 0x0 +#define ICE_AQ_VSI_Q_OPT_RSS_LUT_PF 0x2 +#define ICE_AQ_VSI_Q_OPT_RSS_LUT_GBL 0x3 +#define ICE_AQ_VSI_Q_OPT_RSS_GBL_LUT_S 2 +#define ICE_AQ_VSI_Q_OPT_RSS_GBL_LUT_M (0xF << ICE_AQ_VSI_Q_OPT_RSS_GBL_LUT_S) +#define ICE_AQ_VSI_Q_OPT_RSS_HASH_S 6 +#define ICE_AQ_VSI_Q_OPT_RSS_HASH_M (0x3 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S) +#define ICE_AQ_VSI_Q_OPT_RSS_TPLZ (0x0 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S) +#define ICE_AQ_VSI_Q_OPT_RSS_SYM_TPLZ (0x1 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S) +#define ICE_AQ_VSI_Q_OPT_RSS_XOR (0x2 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S) +#define ICE_AQ_VSI_Q_OPT_RSS_JHASH (0x3 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S) u8 q_opt_tc; -#define ICE_AQ_VSI_Q_OPT_TC_OVR_S 0 -#define ICE_AQ_VSI_Q_OPT_TC_OVR_M (0x1F << ICE_AQ_VSI_Q_OPT_TC_OVR_S) -#define ICE_AQ_VSI_Q_OPT_PROF_TC_OVR BIT(7) +#define ICE_AQ_VSI_Q_OPT_TC_OVR_S 0 +#define ICE_AQ_VSI_Q_OPT_TC_OVR_M (0x1F << ICE_AQ_VSI_Q_OPT_TC_OVR_S) +#define ICE_AQ_VSI_Q_OPT_PROF_TC_OVR BIT(7) u8 q_opt_flags; -#define ICE_AQ_VSI_Q_OPT_PE_FLTR_EN BIT(0) +#define ICE_AQ_VSI_Q_OPT_PE_FLTR_EN BIT(0) u8 q_opt_reserved[3]; /* outer up section */ __le32 outer_up_table; /* same structure and defines as ingress tbl */ @@ -452,27 +500,27 @@ struct ice_aqc_vsi_props { __le16 sect_10_reserved; /* flow director section */ __le16 fd_options; -#define ICE_AQ_VSI_FD_ENABLE BIT(0) -#define ICE_AQ_VSI_FD_TX_AUTO_ENABLE BIT(1) -#define ICE_AQ_VSI_FD_PROG_ENABLE BIT(3) +#define ICE_AQ_VSI_FD_ENABLE BIT(0) +#define ICE_AQ_VSI_FD_TX_AUTO_ENABLE BIT(1) +#define ICE_AQ_VSI_FD_PROG_ENABLE BIT(3) __le16 max_fd_fltr_dedicated; __le16 max_fd_fltr_shared; __le16 fd_def_q; -#define ICE_AQ_VSI_FD_DEF_Q_S 0 -#define ICE_AQ_VSI_FD_DEF_Q_M (0x7FF << ICE_AQ_VSI_FD_DEF_Q_S) -#define ICE_AQ_VSI_FD_DEF_GRP_S 12 -#define ICE_AQ_VSI_FD_DEF_GRP_M (0x7 << ICE_AQ_VSI_FD_DEF_GRP_S) +#define ICE_AQ_VSI_FD_DEF_Q_S 0 +#define ICE_AQ_VSI_FD_DEF_Q_M (0x7FF << ICE_AQ_VSI_FD_DEF_Q_S) +#define ICE_AQ_VSI_FD_DEF_GRP_S 12 +#define ICE_AQ_VSI_FD_DEF_GRP_M (0x7 << ICE_AQ_VSI_FD_DEF_GRP_S) __le16 fd_report_opt; -#define ICE_AQ_VSI_FD_REPORT_Q_S 0 -#define ICE_AQ_VSI_FD_REPORT_Q_M (0x7FF << ICE_AQ_VSI_FD_REPORT_Q_S) -#define ICE_AQ_VSI_FD_DEF_PRIORITY_S 12 -#define ICE_AQ_VSI_FD_DEF_PRIORITY_M (0x7 << ICE_AQ_VSI_FD_DEF_PRIORITY_S) -#define ICE_AQ_VSI_FD_DEF_DROP BIT(15) +#define ICE_AQ_VSI_FD_REPORT_Q_S 0 +#define ICE_AQ_VSI_FD_REPORT_Q_M (0x7FF << ICE_AQ_VSI_FD_REPORT_Q_S) +#define ICE_AQ_VSI_FD_DEF_PRIORITY_S 12 +#define ICE_AQ_VSI_FD_DEF_PRIORITY_M (0x7 << ICE_AQ_VSI_FD_DEF_PRIORITY_S) +#define ICE_AQ_VSI_FD_DEF_DROP BIT(15) /* PASID section */ __le32 pasid_id; -#define ICE_AQ_VSI_PASID_ID_S 0 -#define ICE_AQ_VSI_PASID_ID_M (0xFFFFF << ICE_AQ_VSI_PASID_ID_S) -#define ICE_AQ_VSI_PASID_ID_VALID BIT(31) +#define ICE_AQ_VSI_PASID_ID_S 0 +#define ICE_AQ_VSI_PASID_ID_M (0xFFFFF << ICE_AQ_VSI_PASID_ID_S) +#define ICE_AQ_VSI_PASID_ID_VALID BIT(31) u8 reserved[24]; }; @@ -489,9 +537,13 @@ struct ice_aqc_add_get_recipe { struct ice_aqc_recipe_content { u8 rid; +#define ICE_AQ_RECIPE_ID_S 0 +#define ICE_AQ_RECIPE_ID_M (0x3F << ICE_AQ_RECIPE_ID_S) #define ICE_AQ_RECIPE_ID_IS_ROOT BIT(7) #define ICE_AQ_SW_ID_LKUP_IDX 0 u8 lkup_indx[5]; +#define ICE_AQ_RECIPE_LKUP_DATA_S 0 +#define ICE_AQ_RECIPE_LKUP_DATA_M (0x3F << ICE_AQ_RECIPE_LKUP_DATA_S) #define ICE_AQ_RECIPE_LKUP_IGNORE BIT(7) #define ICE_AQ_SW_ID_LKUP_MASK 0x00FF __le16 mask[5]; @@ -502,15 +554,25 @@ struct ice_aqc_recipe_content { u8 rsvd0[3]; u8 act_ctrl_join_priority; u8 act_ctrl_fwd_priority; +#define ICE_AQ_RECIPE_FWD_PRIORITY_S 0 +#define ICE_AQ_RECIPE_FWD_PRIORITY_M (0xF << ICE_AQ_RECIPE_FWD_PRIORITY_S) u8 act_ctrl; +#define ICE_AQ_RECIPE_ACT_NEED_PASS_L2 BIT(0) +#define ICE_AQ_RECIPE_ACT_ALLOW_PASS_L2 BIT(1) #define ICE_AQ_RECIPE_ACT_INV_ACT BIT(2) +#define ICE_AQ_RECIPE_ACT_PRUNE_INDX_S 4 +#define ICE_AQ_RECIPE_ACT_PRUNE_INDX_M (0x3 << ICE_AQ_RECIPE_ACT_PRUNE_INDX_S) u8 rsvd1; __le32 dflt_act; +#define ICE_AQ_RECIPE_DFLT_ACT_S 0 +#define ICE_AQ_RECIPE_DFLT_ACT_M (0x7FFFF << ICE_AQ_RECIPE_DFLT_ACT_S) +#define ICE_AQ_RECIPE_DFLT_ACT_VALID BIT(31) }; struct ice_aqc_recipe_data_elem { u8 recipe_indx; u8 resp_bits; +#define ICE_AQ_RECIPE_WAS_UPDATED BIT(0) u8 rsvd0[2]; u8 recipe_bitmap[8]; u8 rsvd1[4]; @@ -1339,6 +1401,24 @@ struct ice_aqc_get_link_topo { u8 rsvd[9]; }; +/* Read I2C (direct, 0x06E2) */ +struct ice_aqc_i2c { + struct ice_aqc_link_topo_addr topo_addr; + __le16 i2c_addr; + u8 i2c_params; +#define ICE_AQC_I2C_DATA_SIZE_M GENMASK(3, 0) +#define ICE_AQC_I2C_USE_REPEATED_START BIT(7) + + u8 rsvd; + __le16 i2c_bus_addr; + u8 rsvd2[4]; +}; + +/* Read I2C Response (direct, 0x06E2) */ +struct ice_aqc_read_i2c_resp { + u8 i2c_data[16]; +}; + /* Set Port Identification LED (direct, 0x06E9) */ struct ice_aqc_set_port_id_led { u8 lport_num; @@ -1883,7 +1963,7 @@ struct ice_aqc_get_clear_fw_log { }; /* Download Package (indirect 0x0C40) */ -/* Also used for Update Package (indirect 0x0C42) */ +/* Also used for Update Package (indirect 0x0C41 and 0x0C42) */ struct ice_aqc_download_pkg { u8 flags; #define ICE_AQC_DOWNLOAD_PKG_LAST_BUF 0x01 @@ -2009,6 +2089,7 @@ struct ice_aq_desc { struct ice_aqc_sff_eeprom read_write_sff_param; struct ice_aqc_set_port_id_led set_port_id_led; struct ice_aqc_get_sw_cfg get_sw_conf; + struct ice_aqc_set_port_params set_port_params; struct ice_aqc_sw_rules sw_rules; struct ice_aqc_add_get_recipe add_get_recipe; struct ice_aqc_recipe_to_profile recipe_to_profile; @@ -2049,6 +2130,8 @@ struct ice_aq_desc { struct ice_aqc_get_link_status get_link_status; struct ice_aqc_event_lan_overflow lan_overflow; struct ice_aqc_get_link_topo get_link_topo; + struct ice_aqc_i2c read_i2c; + struct ice_aqc_read_i2c_resp read_i2c_resp; } params; }; @@ -2110,10 +2193,13 @@ enum ice_adminq_opc { /* internal switch commands */ ice_aqc_opc_get_sw_cfg = 0x0200, + ice_aqc_opc_set_port_params = 0x0203, /* Alloc/Free/Get Resources */ ice_aqc_opc_alloc_res = 0x0208, ice_aqc_opc_free_res = 0x0209, + ice_aqc_opc_set_vlan_mode_parameters = 0x020C, + ice_aqc_opc_get_vlan_mode_parameters = 0x020D, /* VSI commands */ ice_aqc_opc_add_vsi = 0x0210, @@ -2160,6 +2246,7 @@ enum ice_adminq_opc { ice_aqc_opc_set_event_mask = 0x0613, ice_aqc_opc_set_mac_lb = 0x0620, ice_aqc_opc_get_link_topo = 0x06E0, + ice_aqc_opc_read_i2c = 0x06E2, ice_aqc_opc_set_port_id_led = 0x06E9, ice_aqc_opc_set_gpio = 0x06EC, ice_aqc_opc_get_gpio = 0x06ED, @@ -2204,6 +2291,7 @@ enum ice_adminq_opc { /* package commands */ ice_aqc_opc_download_pkg = 0x0C40, + ice_aqc_opc_upload_section = 0x0C41, ice_aqc_opc_update_pkg = 0x0C42, ice_aqc_opc_get_pkg_info_list = 0x0C43, diff --git a/drivers/net/ethernet/intel/ice/ice_arfs.h b/drivers/net/ethernet/intel/ice/ice_arfs.h index 80ed76f0cace..9669ad9bf7b5 100644 --- a/drivers/net/ethernet/intel/ice/ice_arfs.h +++ b/drivers/net/ethernet/intel/ice/ice_arfs.h @@ -3,6 +3,9 @@ #ifndef _ICE_ARFS_H_ #define _ICE_ARFS_H_ + +#include "ice_fdir.h" + enum ice_arfs_fltr_state { ICE_ARFS_INACTIVE, ICE_ARFS_ACTIVE, diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c index 1a5ece3bce79..136d7911adb4 100644 --- a/drivers/net/ethernet/intel/ice/ice_base.c +++ b/drivers/net/ethernet/intel/ice/ice_base.c @@ -5,6 +5,7 @@ #include "ice_base.h" #include "ice_lib.h" #include "ice_dcb_lib.h" +#include "ice_sriov.h" static bool ice_alloc_rx_buf_zc(struct ice_rx_ring *rx_ring) { @@ -322,7 +323,7 @@ ice_setup_tx_ctx(struct ice_tx_ring *ring, struct ice_tlan_ctx *tlan_ctx, u16 pf break; case ICE_VSI_VF: /* Firmware expects vmvf_num to be absolute VF ID */ - tlan_ctx->vmvf_num = hw->func_caps.vf_base_id + vsi->vf_id; + tlan_ctx->vmvf_num = hw->func_caps.vf_base_id + vsi->vf->vf_id; tlan_ctx->vmvf_type = ICE_TLAN_CTX_VMVF_TYPE_VF; break; case ICE_VSI_SWITCHDEV_CTRL: @@ -418,8 +419,22 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) */ rlan_ctx.crcstrip = 1; - /* L2TSEL flag defines the reported L2 Tags in the receive descriptor */ - rlan_ctx.l2tsel = 1; + /* L2TSEL flag defines the reported L2 Tags in the receive descriptor + * and it needs to remain 1 for non-DVM capable configurations to not + * break backward compatibility for VF drivers. Setting this field to 0 + * will cause the single/outer VLAN tag to be stripped to the L2TAG2_2ND + * field in the Rx descriptor. Setting it to 1 allows the VLAN tag to + * be stripped in L2TAG1 of the Rx descriptor, which is where VFs will + * check for the tag + */ + if (ice_is_dvm_ena(hw)) + if (vsi->type == ICE_VSI_VF && + ice_vf_is_port_vlan_ena(vsi->vf)) + rlan_ctx.l2tsel = 1; + else + rlan_ctx.l2tsel = 0; + else + rlan_ctx.l2tsel = 1; rlan_ctx.dtype = ICE_RX_DTYPE_NO_SPLIT; rlan_ctx.hsplit_0 = ICE_RLAN_RX_HSPLIT_0_NO_SPLIT; diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index e2af99a763ed..9619bdb9e49a 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -1518,16 +1518,27 @@ ice_aq_send_cmd(struct ice_hw *hw, struct ice_aq_desc *desc, void *buf, /* When a package download is in process (i.e. when the firmware's * Global Configuration Lock resource is held), only the Download - * Package, Get Version, Get Package Info List and Release Resource - * (with resource ID set to Global Config Lock) AdminQ commands are - * allowed; all others must block until the package download completes - * and the Global Config Lock is released. See also - * ice_acquire_global_cfg_lock(). + * Package, Get Version, Get Package Info List, Upload Section, + * Update Package, Set Port Parameters, Get/Set VLAN Mode Parameters, + * Add Recipe, Set Recipes to Profile Association, Get Recipe, and Get + * Recipes to Profile Association, and Release Resource (with resource + * ID set to Global Config Lock) AdminQ commands are allowed; all others + * must block until the package download completes and the Global Config + * Lock is released. See also ice_acquire_global_cfg_lock(). */ switch (le16_to_cpu(desc->opcode)) { case ice_aqc_opc_download_pkg: case ice_aqc_opc_get_pkg_info_list: case ice_aqc_opc_get_ver: + case ice_aqc_opc_upload_section: + case ice_aqc_opc_update_pkg: + case ice_aqc_opc_set_port_params: + case ice_aqc_opc_get_vlan_mode_parameters: + case ice_aqc_opc_set_vlan_mode_parameters: + case ice_aqc_opc_add_recipe: + case ice_aqc_opc_recipe_to_profile: + case ice_aqc_opc_get_recipe: + case ice_aqc_opc_get_recipe_to_profile: break; case ice_aqc_opc_release_res: if (le16_to_cpu(cmd->res_id) == ICE_AQC_RES_ID_GLBL_LOCK) @@ -2736,6 +2747,34 @@ void ice_clear_pxe_mode(struct ice_hw *hw) ice_aq_clear_pxe_mode(hw); } +/** + * ice_aq_set_port_params - set physical port parameters. + * @pi: pointer to the port info struct + * @double_vlan: if set double VLAN is enabled + * @cd: pointer to command details structure or NULL + * + * Set Physical port parameters (0x0203) + */ +int +ice_aq_set_port_params(struct ice_port_info *pi, bool double_vlan, + struct ice_sq_cd *cd) + +{ + struct ice_aqc_set_port_params *cmd; + struct ice_hw *hw = pi->hw; + struct ice_aq_desc desc; + u16 cmd_flags = 0; + + cmd = &desc.params.set_port_params; + + ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_port_params); + if (double_vlan) + cmd_flags |= ICE_AQC_SET_P_PARAMS_DOUBLE_VLAN_ENA; + cmd->cmd_flags = cpu_to_le16(cmd_flags); + + return ice_aq_send_cmd(hw, &desc, NULL, 0, cd); +} + /** * ice_get_link_speed_based_on_phy_type - returns link speed * @phy_type_low: lower part of phy_type @@ -4758,6 +4797,59 @@ ice_sched_query_elem(struct ice_hw *hw, u32 node_teid, return status; } +/** + * ice_aq_read_i2c + * @hw: pointer to the hw struct + * @topo_addr: topology address for a device to communicate with + * @bus_addr: 7-bit I2C bus address + * @addr: I2C memory address (I2C offset) with up to 16 bits + * @params: I2C parameters: bit [7] - Repeated start, + * bits [6:5] data offset size, + * bit [4] - I2C address type, + * bits [3:0] - data size to read (0-16 bytes) + * @data: pointer to data (0 to 16 bytes) to be read from the I2C device + * @cd: pointer to command details structure or NULL + * + * Read I2C (0x06E2) + */ +int +ice_aq_read_i2c(struct ice_hw *hw, struct ice_aqc_link_topo_addr topo_addr, + u16 bus_addr, __le16 addr, u8 params, u8 *data, + struct ice_sq_cd *cd) +{ + struct ice_aq_desc desc = { 0 }; + struct ice_aqc_i2c *cmd; + u8 data_size; + int status; + + ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_read_i2c); + cmd = &desc.params.read_i2c; + + if (!data) + return -EINVAL; + + data_size = FIELD_GET(ICE_AQC_I2C_DATA_SIZE_M, params); + + cmd->i2c_bus_addr = cpu_to_le16(bus_addr); + cmd->topo_addr = topo_addr; + cmd->i2c_params = params; + cmd->i2c_addr = addr; + + status = ice_aq_send_cmd(hw, &desc, NULL, 0, cd); + if (!status) { + struct ice_aqc_read_i2c_resp *resp; + u8 i; + + resp = &desc.params.read_i2c_resp; + for (i = 0; i < data_size; i++) { + *data = resp->i2c_data[i]; + data++; + } + } + + return status; +} + /** * ice_aq_set_driver_param - Set driver parameter to share via firmware * @hw: pointer to the HW struct diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h index 1c57097ddf0b..872ea7d2332d 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.h +++ b/drivers/net/ethernet/intel/ice/ice_common.h @@ -4,12 +4,14 @@ #ifndef _ICE_COMMON_H_ #define _ICE_COMMON_H_ -#include "ice.h" +#include + #include "ice_type.h" #include "ice_nvm.h" #include "ice_flex_pipe.h" -#include "ice_switch.h" #include +#include "ice_switch.h" +#include "ice_fdir.h" #define ICE_SQ_SEND_DELAY_TIME_MS 10 #define ICE_SQ_SEND_MAX_EXECUTE 3 @@ -85,6 +87,9 @@ int ice_aq_send_driver_ver(struct ice_hw *hw, struct ice_driver_ver *dv, struct ice_sq_cd *cd); int +ice_aq_set_port_params(struct ice_port_info *pi, bool double_vlan, + struct ice_sq_cd *cd); +int ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode, struct ice_aqc_get_phy_caps_data *caps, struct ice_sq_cd *cd); @@ -205,5 +210,9 @@ ice_aq_set_lldp_mib(struct ice_hw *hw, u8 mib_type, void *buf, u16 buf_size, bool ice_fw_supports_lldp_fltr_ctrl(struct ice_hw *hw); int ice_lldp_fltr_add_remove(struct ice_hw *hw, u16 vsi_num, bool add); +int +ice_aq_read_i2c(struct ice_hw *hw, struct ice_aqc_link_topo_addr topo_addr, + u16 bus_addr, __le16 addr, u8 params, u8 *data, + struct ice_sq_cd *cd); bool ice_fw_supports_report_dflt_cfg(struct ice_hw *hw); #endif /* _ICE_COMMON_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_dcb.h b/drivers/net/ethernet/intel/ice/ice_dcb.h index d73348f279f7..6abf28a14291 100644 --- a/drivers/net/ethernet/intel/ice/ice_dcb.h +++ b/drivers/net/ethernet/intel/ice/ice_dcb.h @@ -5,6 +5,7 @@ #define _ICE_DCB_H_ #include "ice_type.h" +#include #define ICE_DCBX_STATUS_NOT_STARTED 0 #define ICE_DCBX_STATUS_IN_PROGRESS 1 diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c index b94d8daeaa58..add90e75f05c 100644 --- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c @@ -916,7 +916,8 @@ ice_tx_prepare_vlan_flags_dcb(struct ice_tx_ring *tx_ring, return; /* Insert 802.1p priority into VLAN header */ - if ((first->tx_flags & ICE_TX_FLAGS_HW_VLAN) || + if ((first->tx_flags & ICE_TX_FLAGS_HW_VLAN || + first->tx_flags & ICE_TX_FLAGS_HW_OUTER_SINGLE_VLAN) || skb->priority != TC_PRIO_CONTROL) { first->tx_flags &= ~ICE_TX_FLAGS_VLAN_PR_M; /* Mask the lower 3 bits to set the 802.1p priority */ @@ -925,7 +926,10 @@ ice_tx_prepare_vlan_flags_dcb(struct ice_tx_ring *tx_ring, /* if this is not already set it means a VLAN 0 + priority needs * to be offloaded */ - first->tx_flags |= ICE_TX_FLAGS_HW_VLAN; + if (tx_ring->flags & ICE_TX_FLAGS_RING_VLAN_L2TAG2) + first->tx_flags |= ICE_TX_FLAGS_HW_OUTER_SINGLE_VLAN; + else + first->tx_flags |= ICE_TX_FLAGS_HW_VLAN; } } diff --git a/drivers/net/ethernet/intel/ice/ice_eswitch.c b/drivers/net/ethernet/intel/ice/ice_eswitch.c index 73edc24d81d5..9a84d746a6c4 100644 --- a/drivers/net/ethernet/intel/ice/ice_eswitch.c +++ b/drivers/net/ethernet/intel/ice/ice_eswitch.c @@ -116,9 +116,12 @@ static int ice_eswitch_setup_env(struct ice_pf *pf) struct ice_vsi *uplink_vsi = pf->switchdev.uplink_vsi; struct net_device *uplink_netdev = uplink_vsi->netdev; struct ice_vsi *ctrl_vsi = pf->switchdev.control_vsi; + struct ice_vsi_vlan_ops *vlan_ops; bool rule_added = false; - ice_vsi_manage_vlan_stripping(ctrl_vsi, false); + vlan_ops = ice_get_compat_vsi_vlan_ops(ctrl_vsi); + if (vlan_ops->dis_stripping(ctrl_vsi)) + return -ENODEV; ice_remove_vsi_fltr(&pf->hw, uplink_vsi->idx); @@ -127,7 +130,7 @@ static int ice_eswitch_setup_env(struct ice_pf *pf) __dev_mc_unsync(uplink_netdev, NULL); netif_addr_unlock_bh(uplink_netdev); - if (ice_vsi_add_vlan(uplink_vsi, 0, ICE_FWD_TO_VSI)) + if (ice_vsi_add_vlan_zero(uplink_vsi)) goto err_def_rx; if (!ice_is_dflt_vsi_in_use(uplink_vsi->vsw)) { @@ -173,10 +176,20 @@ static void ice_eswitch_remap_rings_to_vectors(struct ice_pf *pf) int q_id; ice_for_each_txq(vsi, q_id) { - struct ice_repr *repr = pf->vf[q_id].repr; - struct ice_q_vector *q_vector = repr->q_vector; - struct ice_tx_ring *tx_ring = vsi->tx_rings[q_id]; - struct ice_rx_ring *rx_ring = vsi->rx_rings[q_id]; + struct ice_q_vector *q_vector; + struct ice_tx_ring *tx_ring; + struct ice_rx_ring *rx_ring; + struct ice_repr *repr; + struct ice_vf *vf; + + vf = ice_get_vf_by_id(pf, q_id); + if (WARN_ON(!vf)) + continue; + + repr = vf->repr; + q_vector = repr->q_vector; + tx_ring = vsi->tx_rings[q_id]; + rx_ring = vsi->rx_rings[q_id]; q_vector->vsi = vsi; q_vector->reg_idx = vsi->q_vectors[0]->reg_idx; @@ -196,6 +209,38 @@ static void ice_eswitch_remap_rings_to_vectors(struct ice_pf *pf) rx_ring->q_vector = q_vector; rx_ring->next = NULL; rx_ring->netdev = repr->netdev; + + ice_put_vf(vf); + } +} + +/** + * ice_eswitch_release_reprs - clear PR VSIs configuration + * @pf: poiner to PF struct + * @ctrl_vsi: pointer to switchdev control VSI + */ +static void +ice_eswitch_release_reprs(struct ice_pf *pf, struct ice_vsi *ctrl_vsi) +{ + struct ice_vf *vf; + unsigned int bkt; + + lockdep_assert_held(&pf->vfs.table_lock); + + ice_for_each_vf(pf, bkt, vf) { + struct ice_vsi *vsi = vf->repr->src_vsi; + + /* Skip VFs that aren't configured */ + if (!vf->repr->dst) + continue; + + ice_vsi_update_security(vsi, ice_vsi_ctx_set_antispoof); + metadata_dst_free(vf->repr->dst); + vf->repr->dst = NULL; + ice_fltr_add_mac_and_broadcast(vsi, vf->hw_lan_addr.addr, + ICE_FWD_TO_VSI); + + netif_napi_del(&vf->repr->q_vector->napi); } } @@ -207,11 +252,13 @@ static int ice_eswitch_setup_reprs(struct ice_pf *pf) { struct ice_vsi *ctrl_vsi = pf->switchdev.control_vsi; int max_vsi_num = 0; - int i; + struct ice_vf *vf; + unsigned int bkt; - ice_for_each_vf(pf, i) { - struct ice_vsi *vsi = pf->vf[i].repr->src_vsi; - struct ice_vf *vf = &pf->vf[i]; + lockdep_assert_held(&pf->vfs.table_lock); + + ice_for_each_vf(pf, bkt, vf) { + struct ice_vsi *vsi = vf->repr->src_vsi; ice_remove_vsi_fltr(&pf->hw, vsi->idx); vf->repr->dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, @@ -228,14 +275,16 @@ static int ice_eswitch_setup_reprs(struct ice_pf *pf) vf->hw_lan_addr.addr, ICE_FWD_TO_VSI); metadata_dst_free(vf->repr->dst); + vf->repr->dst = NULL; goto err; } - if (ice_vsi_add_vlan(vsi, 0, ICE_FWD_TO_VSI)) { + if (ice_vsi_add_vlan_zero(vsi)) { ice_fltr_add_mac_and_broadcast(vsi, vf->hw_lan_addr.addr, ICE_FWD_TO_VSI); metadata_dst_free(vf->repr->dst); + vf->repr->dst = NULL; ice_vsi_update_security(vsi, ice_vsi_ctx_set_antispoof); goto err; } @@ -249,8 +298,8 @@ static int ice_eswitch_setup_reprs(struct ice_pf *pf) netif_keep_dst(vf->repr->netdev); } - ice_for_each_vf(pf, i) { - struct ice_repr *repr = pf->vf[i].repr; + ice_for_each_vf(pf, bkt, vf) { + struct ice_repr *repr = vf->repr; struct ice_vsi *vsi = repr->src_vsi; struct metadata_dst *dst; @@ -263,42 +312,11 @@ static int ice_eswitch_setup_reprs(struct ice_pf *pf) return 0; err: - for (i = i - 1; i >= 0; i--) { - struct ice_vsi *vsi = pf->vf[i].repr->src_vsi; - struct ice_vf *vf = &pf->vf[i]; - - ice_vsi_update_security(vsi, ice_vsi_ctx_set_antispoof); - metadata_dst_free(vf->repr->dst); - ice_fltr_add_mac_and_broadcast(vsi, vf->hw_lan_addr.addr, - ICE_FWD_TO_VSI); - } + ice_eswitch_release_reprs(pf, ctrl_vsi); return -ENODEV; } -/** - * ice_eswitch_release_reprs - clear PR VSIs configuration - * @pf: poiner to PF struct - * @ctrl_vsi: pointer to switchdev control VSI - */ -static void -ice_eswitch_release_reprs(struct ice_pf *pf, struct ice_vsi *ctrl_vsi) -{ - int i; - - ice_for_each_vf(pf, i) { - struct ice_vsi *vsi = pf->vf[i].repr->src_vsi; - struct ice_vf *vf = &pf->vf[i]; - - ice_vsi_update_security(vsi, ice_vsi_ctx_set_antispoof); - metadata_dst_free(vf->repr->dst); - ice_fltr_add_mac_and_broadcast(vsi, vf->hw_lan_addr.addr, - ICE_FWD_TO_VSI); - - netif_napi_del(&vf->repr->q_vector->napi); - } -} - /** * ice_eswitch_update_repr - reconfigure VF port representor * @vsi: VF VSI for which port representor is configured @@ -313,7 +331,7 @@ void ice_eswitch_update_repr(struct ice_vsi *vsi) if (!ice_is_switchdev_running(pf)) return; - vf = &pf->vf[vsi->vf_id]; + vf = vsi->vf; repr = vf->repr; repr->src_vsi = vsi; repr->dst->u.port_info.port_id = vsi->vsi_num; @@ -321,7 +339,8 @@ void ice_eswitch_update_repr(struct ice_vsi *vsi) ret = ice_vsi_update_security(vsi, ice_vsi_ctx_clear_antispoof); if (ret) { ice_fltr_add_mac_and_broadcast(vsi, vf->hw_lan_addr.addr, ICE_FWD_TO_VSI); - dev_err(ice_pf_to_dev(pf), "Failed to update VF %d port representor", vsi->vf_id); + dev_err(ice_pf_to_dev(pf), "Failed to update VF %d port representor", + vsi->vf->vf_id); } } @@ -405,7 +424,7 @@ static void ice_eswitch_release_env(struct ice_pf *pf) static struct ice_vsi * ice_eswitch_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi) { - return ice_vsi_setup(pf, pi, ICE_VSI_SWITCHDEV_CTRL, ICE_INVAL_VFID, NULL); + return ice_vsi_setup(pf, pi, ICE_VSI_SWITCHDEV_CTRL, NULL, NULL); } /** @@ -414,10 +433,13 @@ ice_eswitch_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi) */ static void ice_eswitch_napi_del(struct ice_pf *pf) { - int i; + struct ice_vf *vf; + unsigned int bkt; - ice_for_each_vf(pf, i) - netif_napi_del(&pf->vf[i].repr->q_vector->napi); + lockdep_assert_held(&pf->vfs.table_lock); + + ice_for_each_vf(pf, bkt, vf) + netif_napi_del(&vf->repr->q_vector->napi); } /** @@ -426,10 +448,13 @@ static void ice_eswitch_napi_del(struct ice_pf *pf) */ static void ice_eswitch_napi_enable(struct ice_pf *pf) { - int i; + struct ice_vf *vf; + unsigned int bkt; - ice_for_each_vf(pf, i) - napi_enable(&pf->vf[i].repr->q_vector->napi); + lockdep_assert_held(&pf->vfs.table_lock); + + ice_for_each_vf(pf, bkt, vf) + napi_enable(&vf->repr->q_vector->napi); } /** @@ -438,10 +463,13 @@ static void ice_eswitch_napi_enable(struct ice_pf *pf) */ static void ice_eswitch_napi_disable(struct ice_pf *pf) { - int i; + struct ice_vf *vf; + unsigned int bkt; - ice_for_each_vf(pf, i) - napi_disable(&pf->vf[i].repr->q_vector->napi); + lockdep_assert_held(&pf->vfs.table_lock); + + ice_for_each_vf(pf, bkt, vf) + napi_disable(&vf->repr->q_vector->napi); } /** @@ -519,7 +547,7 @@ ice_eswitch_mode_set(struct devlink *devlink, u16 mode, if (pf->eswitch_mode == mode) return 0; - if (pf->num_alloc_vfs) { + if (ice_has_vfs(pf)) { dev_info(ice_pf_to_dev(pf), "Changing eswitch mode is allowed only if there is no VFs created"); NL_SET_ERR_MSG_MOD(extack, "Changing eswitch mode is allowed only if there is no VFs created"); return -EOPNOTSUPP; @@ -610,16 +638,17 @@ int ice_eswitch_configure(struct ice_pf *pf) */ static void ice_eswitch_start_all_tx_queues(struct ice_pf *pf) { - struct ice_repr *repr; - int i; + struct ice_vf *vf; + unsigned int bkt; + + lockdep_assert_held(&pf->vfs.table_lock); if (test_bit(ICE_DOWN, pf->state)) return; - ice_for_each_vf(pf, i) { - repr = pf->vf[i].repr; - if (repr) - ice_repr_start_tx_queues(repr); + ice_for_each_vf(pf, bkt, vf) { + if (vf->repr) + ice_repr_start_tx_queues(vf->repr); } } @@ -629,16 +658,17 @@ static void ice_eswitch_start_all_tx_queues(struct ice_pf *pf) */ void ice_eswitch_stop_all_tx_queues(struct ice_pf *pf) { - struct ice_repr *repr; - int i; + struct ice_vf *vf; + unsigned int bkt; + + lockdep_assert_held(&pf->vfs.table_lock); if (test_bit(ICE_DOWN, pf->state)) return; - ice_for_each_vf(pf, i) { - repr = pf->vf[i].repr; - if (repr) - ice_repr_stop_tx_queues(repr); + ice_for_each_vf(pf, bkt, vf) { + if (vf->repr) + ice_repr_stop_tx_queues(vf->repr); } } diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index a5dc9824e255..24cda7e1f916 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -164,6 +164,7 @@ static const struct ice_priv_flag ice_gstrings_priv_flags[] = { ICE_PRIV_FLAG("vf-true-promisc-support", ICE_FLAG_VF_TRUE_PROMISC_ENA), ICE_PRIV_FLAG("mdd-auto-reset-vf", ICE_FLAG_MDD_AUTO_RESET_VF), + ICE_PRIV_FLAG("vf-vlan-pruning", ICE_FLAG_VF_VLAN_PRUNING), ICE_PRIV_FLAG("legacy-rx", ICE_FLAG_LEGACY_RX), }; @@ -315,16 +316,20 @@ ice_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, */ static bool ice_active_vfs(struct ice_pf *pf) { - unsigned int i; + bool active = false; + struct ice_vf *vf; + unsigned int bkt; - ice_for_each_vf(pf, i) { - struct ice_vf *vf = &pf->vf[i]; - - if (test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) - return true; + rcu_read_lock(); + ice_for_each_vf_rcu(pf, bkt, vf) { + if (test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { + active = true; + break; + } } + rcu_read_unlock(); - return false; + return active; } /** @@ -1295,6 +1300,14 @@ static int ice_set_priv_flags(struct net_device *netdev, u32 flags) change_bit(ICE_FLAG_VF_TRUE_PROMISC_ENA, pf->flags); ret = -EAGAIN; } + + if (test_bit(ICE_FLAG_VF_VLAN_PRUNING, change_flags) && + ice_has_vfs(pf)) { + dev_err(dev, "vf-vlan-pruning: VLAN pruning cannot be changed while VFs are active.\n"); + /* toggle bit back to previous state */ + change_bit(ICE_FLAG_VF_VLAN_PRUNING, pf->flags); + ret = -EOPNOTSUPP; + } ethtool_exit: clear_bit(ICE_FLAG_ETHTOOL_CTXT, pf->flags); return ret; @@ -2803,6 +2816,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]); diff --git a/drivers/net/ethernet/intel/ice/ice_flex_pipe.c b/drivers/net/ethernet/intel/ice/ice_flex_pipe.c index 4deb2c9446ec..c73cdab44f70 100644 --- a/drivers/net/ethernet/intel/ice/ice_flex_pipe.c +++ b/drivers/net/ethernet/intel/ice/ice_flex_pipe.c @@ -4,10 +4,19 @@ #include "ice_common.h" #include "ice_flex_pipe.h" #include "ice_flow.h" +#include "ice.h" + +/* For supporting double VLAN mode, it is necessary to enable or disable certain + * boost tcam entries. The metadata labels names that match the following + * prefixes will be saved to allow enabling double VLAN mode. + */ +#define ICE_DVM_PRE "BOOST_MAC_VLAN_DVM" /* enable these entries */ +#define ICE_SVM_PRE "BOOST_MAC_VLAN_SVM" /* disable these entries */ /* To support tunneling entries by PF, the package will append the PF number to * the label; for example TNL_VXLAN_PF0, TNL_VXLAN_PF1, TNL_VXLAN_PF2, etc. */ +#define ICE_TNL_PRE "TNL_" static const struct ice_tunnel_type_scan tnls[] = { { TNL_VXLAN, "TNL_VXLAN_PF" }, { TNL_GENEVE, "TNL_GENEVE_PF" }, @@ -522,6 +531,55 @@ ice_enum_labels(struct ice_seg *ice_seg, u32 type, struct ice_pkg_enum *state, return label->name; } +/** + * ice_add_tunnel_hint + * @hw: pointer to the HW structure + * @label_name: label text + * @val: value of the tunnel port boost entry + */ +static void ice_add_tunnel_hint(struct ice_hw *hw, char *label_name, u16 val) +{ + if (hw->tnl.count < ICE_TUNNEL_MAX_ENTRIES) { + u16 i; + + for (i = 0; tnls[i].type != TNL_LAST; i++) { + size_t len = strlen(tnls[i].label_prefix); + + /* Look for matching label start, before continuing */ + if (strncmp(label_name, tnls[i].label_prefix, len)) + continue; + + /* Make sure this label matches our PF. Note that the PF + * character ('0' - '7') will be located where our + * prefix string's null terminator is located. + */ + if ((label_name[len] - '0') == hw->pf_id) { + hw->tnl.tbl[hw->tnl.count].type = tnls[i].type; + hw->tnl.tbl[hw->tnl.count].valid = false; + hw->tnl.tbl[hw->tnl.count].boost_addr = val; + hw->tnl.tbl[hw->tnl.count].port = 0; + hw->tnl.count++; + break; + } + } + } +} + +/** + * ice_add_dvm_hint + * @hw: pointer to the HW structure + * @val: value of the boost entry + * @enable: true if entry needs to be enabled, or false if needs to be disabled + */ +static void ice_add_dvm_hint(struct ice_hw *hw, u16 val, bool enable) +{ + if (hw->dvm_upd.count < ICE_DVM_MAX_ENTRIES) { + hw->dvm_upd.tbl[hw->dvm_upd.count].boost_addr = val; + hw->dvm_upd.tbl[hw->dvm_upd.count].enable = enable; + hw->dvm_upd.count++; + } +} + /** * ice_init_pkg_hints * @hw: pointer to the HW structure @@ -548,32 +606,23 @@ static void ice_init_pkg_hints(struct ice_hw *hw, struct ice_seg *ice_seg) label_name = ice_enum_labels(ice_seg, ICE_SID_LBL_RXPARSER_TMEM, &state, &val); - while (label_name && hw->tnl.count < ICE_TUNNEL_MAX_ENTRIES) { - for (i = 0; tnls[i].type != TNL_LAST; i++) { - size_t len = strlen(tnls[i].label_prefix); + while (label_name) { + if (!strncmp(label_name, ICE_TNL_PRE, strlen(ICE_TNL_PRE))) + /* check for a tunnel entry */ + ice_add_tunnel_hint(hw, label_name, val); - /* Look for matching label start, before continuing */ - if (strncmp(label_name, tnls[i].label_prefix, len)) - continue; + /* check for a dvm mode entry */ + else if (!strncmp(label_name, ICE_DVM_PRE, strlen(ICE_DVM_PRE))) + ice_add_dvm_hint(hw, val, true); - /* Make sure this label matches our PF. Note that the PF - * character ('0' - '7') will be located where our - * prefix string's null terminator is located. - */ - if ((label_name[len] - '0') == hw->pf_id) { - hw->tnl.tbl[hw->tnl.count].type = tnls[i].type; - hw->tnl.tbl[hw->tnl.count].valid = false; - hw->tnl.tbl[hw->tnl.count].boost_addr = val; - hw->tnl.tbl[hw->tnl.count].port = 0; - hw->tnl.count++; - break; - } - } + /* check for a svm mode entry */ + else if (!strncmp(label_name, ICE_SVM_PRE, strlen(ICE_SVM_PRE))) + ice_add_dvm_hint(hw, val, false); label_name = ice_enum_labels(NULL, 0, &state, &val); } - /* Cache the appropriate boost TCAM entry pointers */ + /* Cache the appropriate boost TCAM entry pointers for tunnels */ for (i = 0; i < hw->tnl.count; i++) { ice_find_boost_entry(ice_seg, hw->tnl.tbl[i].boost_addr, &hw->tnl.tbl[i].boost_entry); @@ -583,6 +632,11 @@ static void ice_init_pkg_hints(struct ice_hw *hw, struct ice_seg *ice_seg) hw->tnl.valid_count[hw->tnl.tbl[i].type]++; } } + + /* Cache the appropriate boost TCAM entry pointers for DVM and SVM */ + for (i = 0; i < hw->dvm_upd.count; i++) + ice_find_boost_entry(ice_seg, hw->dvm_upd.tbl[i].boost_addr, + &hw->dvm_upd.tbl[i].boost_entry); } /* Key creation */ @@ -873,6 +927,27 @@ ice_aq_download_pkg(struct ice_hw *hw, struct ice_buf_hdr *pkg_buf, return status; } +/** + * ice_aq_upload_section + * @hw: pointer to the hardware structure + * @pkg_buf: the package buffer which will receive the section + * @buf_size: the size of the package buffer + * @cd: pointer to command details structure or NULL + * + * Upload Section (0x0C41) + */ +int +ice_aq_upload_section(struct ice_hw *hw, struct ice_buf_hdr *pkg_buf, + u16 buf_size, struct ice_sq_cd *cd) +{ + struct ice_aq_desc desc; + + ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_upload_section); + desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD); + + return ice_aq_send_cmd(hw, &desc, pkg_buf, buf_size, cd); +} + /** * ice_aq_update_pkg * @hw: pointer to the hardware structure @@ -957,25 +1032,21 @@ ice_find_seg_in_pkg(struct ice_hw *hw, u32 seg_type, } /** - * ice_update_pkg + * ice_update_pkg_no_lock * @hw: pointer to the hardware structure * @bufs: pointer to an array of buffers * @count: the number of buffers in the array - * - * Obtains change lock and updates package. */ -static int ice_update_pkg(struct ice_hw *hw, struct ice_buf *bufs, u32 count) +static int +ice_update_pkg_no_lock(struct ice_hw *hw, struct ice_buf *bufs, u32 count) { - u32 offset, info, i; - int status; - - status = ice_acquire_change_lock(hw, ICE_RES_WRITE); - if (status) - return status; + int status = 0; + u32 i; for (i = 0; i < count; i++) { struct ice_buf_hdr *bh = (struct ice_buf_hdr *)(bufs + i); bool last = ((i + 1) == count); + u32 offset, info; status = ice_aq_update_pkg(hw, bh, le16_to_cpu(bh->data_end), last, &offset, &info, NULL); @@ -987,6 +1058,27 @@ static int ice_update_pkg(struct ice_hw *hw, struct ice_buf *bufs, u32 count) } } + return status; +} + +/** + * ice_update_pkg + * @hw: pointer to the hardware structure + * @bufs: pointer to an array of buffers + * @count: the number of buffers in the array + * + * Obtains change lock and updates package. + */ +static int ice_update_pkg(struct ice_hw *hw, struct ice_buf *bufs, u32 count) +{ + int status; + + status = ice_acquire_change_lock(hw, ICE_RES_WRITE); + if (status) + return status; + + status = ice_update_pkg_no_lock(hw, bufs, count); + ice_release_change_lock(hw); return status; @@ -1080,6 +1172,13 @@ ice_dwnld_cfg_bufs(struct ice_hw *hw, struct ice_buf *bufs, u32 count) break; } + if (!status) { + status = ice_set_vlan_mode(hw); + if (status) + ice_debug(hw, ICE_DBG_PKG, "Failed to set VLAN mode: err %d\n", + status); + } + ice_release_global_cfg_lock(hw); return state; @@ -1117,6 +1216,7 @@ static enum ice_ddp_state ice_download_pkg(struct ice_hw *hw, struct ice_seg *ice_seg) { struct ice_buf_table *ice_buf_tbl; + int status; ice_debug(hw, ICE_DBG_PKG, "Segment format version: %d.%d.%d.%d\n", ice_seg->hdr.seg_format_ver.major, @@ -1133,8 +1233,12 @@ ice_download_pkg(struct ice_hw *hw, struct ice_seg *ice_seg) ice_debug(hw, ICE_DBG_PKG, "Seg buf count: %d\n", le32_to_cpu(ice_buf_tbl->buf_count)); - return ice_dwnld_cfg_bufs(hw, ice_buf_tbl->buf_array, - le32_to_cpu(ice_buf_tbl->buf_count)); + status = ice_dwnld_cfg_bufs(hw, ice_buf_tbl->buf_array, + le32_to_cpu(ice_buf_tbl->buf_count)); + + ice_post_pkg_dwnld_vlan_mode_cfg(hw); + + return status; } /** @@ -1701,16 +1805,43 @@ static struct ice_buf_build *ice_pkg_buf_alloc(struct ice_hw *hw) return bld; } +static bool ice_is_gtp_u_profile(u16 prof_idx) +{ + return (prof_idx >= ICE_PROFID_IPV6_GTPU_TEID && + prof_idx <= ICE_PROFID_IPV6_GTPU_IPV6_TCP_INNER) || + prof_idx == ICE_PROFID_IPV4_GTPU_TEID; +} + +static bool ice_is_gtp_c_profile(u16 prof_idx) +{ + switch (prof_idx) { + case ICE_PROFID_IPV4_GTPC_TEID: + case ICE_PROFID_IPV4_GTPC_NO_TEID: + case ICE_PROFID_IPV6_GTPC_TEID: + case ICE_PROFID_IPV6_GTPC_NO_TEID: + return true; + default: + return false; + } +} + /** * ice_get_sw_prof_type - determine switch profile type * @hw: pointer to the HW structure * @fv: pointer to the switch field vector + * @prof_idx: profile index to check */ static enum ice_prof_type -ice_get_sw_prof_type(struct ice_hw *hw, struct ice_fv *fv) +ice_get_sw_prof_type(struct ice_hw *hw, struct ice_fv *fv, u32 prof_idx) { u16 i; + if (ice_is_gtp_c_profile(prof_idx)) + return ICE_PROF_TUN_GTPC; + + if (ice_is_gtp_u_profile(prof_idx)) + return ICE_PROF_TUN_GTPU; + for (i = 0; i < hw->blk[ICE_BLK_SW].es.fvw; i++) { /* UDP tunnel will have UDP_OF protocol ID and VNI offset */ if (fv->ew[i].prot_id == (u8)ICE_PROT_UDP_OF && @@ -1757,7 +1888,7 @@ ice_get_sw_fv_bitmap(struct ice_hw *hw, enum ice_prof_type req_profs, if (fv) { /* Determine field vector type */ - prof_type = ice_get_sw_prof_type(hw, fv); + prof_type = ice_get_sw_prof_type(hw, fv, offset); if (req_profs & prof_type) set_bit((u16)offset, bm); @@ -1768,20 +1899,19 @@ ice_get_sw_fv_bitmap(struct ice_hw *hw, enum ice_prof_type req_profs, /** * ice_get_sw_fv_list * @hw: pointer to the HW structure - * @prot_ids: field vector to search for with a given protocol ID - * @ids_cnt: lookup/protocol count + * @lkups: list of protocol types * @bm: bitmap of field vectors to consider * @fv_list: Head of a list * * Finds all the field vector entries from switch block that contain - * a given protocol ID and returns a list of structures of type + * a given protocol ID and offset and returns a list of structures of type * "ice_sw_fv_list_entry". Every structure in the list has a field vector * definition and profile ID information * NOTE: The caller of the function is responsible for freeing the memory * allocated for every list entry. */ int -ice_get_sw_fv_list(struct ice_hw *hw, u8 *prot_ids, u16 ids_cnt, +ice_get_sw_fv_list(struct ice_hw *hw, struct ice_prot_lkup_ext *lkups, unsigned long *bm, struct list_head *fv_list) { struct ice_sw_fv_list_entry *fvl; @@ -1793,7 +1923,7 @@ ice_get_sw_fv_list(struct ice_hw *hw, u8 *prot_ids, u16 ids_cnt, memset(&state, 0, sizeof(state)); - if (!ids_cnt || !hw->seg) + if (!lkups->n_val_words || !hw->seg) return -EINVAL; ice_seg = hw->seg; @@ -1812,20 +1942,17 @@ ice_get_sw_fv_list(struct ice_hw *hw, u8 *prot_ids, u16 ids_cnt, if (!test_bit((u16)offset, bm)) continue; - for (i = 0; i < ids_cnt; i++) { + for (i = 0; i < lkups->n_val_words; i++) { int j; - /* This code assumes that if a switch field vector line - * has a matching protocol, then this line will contain - * the entries necessary to represent every field in - * that protocol header. - */ for (j = 0; j < hw->blk[ICE_BLK_SW].es.fvw; j++) - if (fv->ew[j].prot_id == prot_ids[i]) + if (fv->ew[j].prot_id == + lkups->fv_words[i].prot_id && + fv->ew[j].off == lkups->fv_words[i].off) break; if (j >= hw->blk[ICE_BLK_SW].es.fvw) break; - if (i + 1 == ids_cnt) { + if (i + 1 == lkups->n_val_words) { fvl = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*fvl), GFP_KERNEL); if (!fvl) @@ -1897,7 +2024,7 @@ void ice_init_prof_result_bm(struct ice_hw *hw) * * Frees a package buffer */ -static void ice_pkg_buf_free(struct ice_hw *hw, struct ice_buf_build *bld) +void ice_pkg_buf_free(struct ice_hw *hw, struct ice_buf_build *bld) { devm_kfree(ice_hw_to_dev(hw), bld); } @@ -1996,6 +2123,43 @@ ice_pkg_buf_alloc_section(struct ice_buf_build *bld, u32 type, u16 size) return NULL; } +/** + * ice_pkg_buf_alloc_single_section + * @hw: pointer to the HW structure + * @type: the section type value + * @size: the size of the section to reserve (in bytes) + * @section: returns pointer to the section + * + * Allocates a package buffer with a single section. + * Note: all package contents must be in Little Endian form. + */ +struct ice_buf_build * +ice_pkg_buf_alloc_single_section(struct ice_hw *hw, u32 type, u16 size, + void **section) +{ + struct ice_buf_build *buf; + + if (!section) + return NULL; + + buf = ice_pkg_buf_alloc(hw); + if (!buf) + return NULL; + + if (ice_pkg_buf_reserve_section(buf, 1)) + goto ice_pkg_buf_alloc_single_section_err; + + *section = ice_pkg_buf_alloc_section(buf, type, size); + if (!*section) + goto ice_pkg_buf_alloc_single_section_err; + + return buf; + +ice_pkg_buf_alloc_single_section_err: + ice_pkg_buf_free(hw, buf); + return NULL; +} + /** * ice_pkg_buf_get_active_sections * @bld: pointer to pkg build (allocated by ice_pkg_buf_alloc()) @@ -2023,7 +2187,7 @@ static u16 ice_pkg_buf_get_active_sections(struct ice_buf_build *bld) * * Return a pointer to the buffer's header */ -static struct ice_buf *ice_pkg_buf(struct ice_buf_build *bld) +struct ice_buf *ice_pkg_buf(struct ice_buf_build *bld) { if (!bld) return NULL; @@ -2059,6 +2223,89 @@ ice_get_open_tunnel_port(struct ice_hw *hw, u16 *port, return res; } +/** + * ice_upd_dvm_boost_entry + * @hw: pointer to the HW structure + * @entry: pointer to double vlan boost entry info + */ +static int +ice_upd_dvm_boost_entry(struct ice_hw *hw, struct ice_dvm_entry *entry) +{ + struct ice_boost_tcam_section *sect_rx, *sect_tx; + int status = -ENOSPC; + struct ice_buf_build *bld; + u8 val, dc, nm; + + bld = ice_pkg_buf_alloc(hw); + if (!bld) + return -ENOMEM; + + /* allocate 2 sections, one for Rx parser, one for Tx parser */ + if (ice_pkg_buf_reserve_section(bld, 2)) + goto ice_upd_dvm_boost_entry_err; + + sect_rx = ice_pkg_buf_alloc_section(bld, ICE_SID_RXPARSER_BOOST_TCAM, + struct_size(sect_rx, tcam, 1)); + if (!sect_rx) + goto ice_upd_dvm_boost_entry_err; + sect_rx->count = cpu_to_le16(1); + + sect_tx = ice_pkg_buf_alloc_section(bld, ICE_SID_TXPARSER_BOOST_TCAM, + struct_size(sect_tx, tcam, 1)); + if (!sect_tx) + goto ice_upd_dvm_boost_entry_err; + sect_tx->count = cpu_to_le16(1); + + /* copy original boost entry to update package buffer */ + memcpy(sect_rx->tcam, entry->boost_entry, sizeof(*sect_rx->tcam)); + + /* re-write the don't care and never match bits accordingly */ + if (entry->enable) { + /* all bits are don't care */ + val = 0x00; + dc = 0xFF; + nm = 0x00; + } else { + /* disable, one never match bit, the rest are don't care */ + val = 0x00; + dc = 0xF7; + nm = 0x08; + } + + ice_set_key((u8 *)§_rx->tcam[0].key, sizeof(sect_rx->tcam[0].key), + &val, NULL, &dc, &nm, 0, sizeof(u8)); + + /* exact copy of entry to Tx section entry */ + memcpy(sect_tx->tcam, sect_rx->tcam, sizeof(*sect_tx->tcam)); + + status = ice_update_pkg_no_lock(hw, ice_pkg_buf(bld), 1); + +ice_upd_dvm_boost_entry_err: + ice_pkg_buf_free(hw, bld); + + return status; +} + +/** + * ice_set_dvm_boost_entries + * @hw: pointer to the HW structure + * + * Enable double vlan by updating the appropriate boost tcam entries. + */ +int ice_set_dvm_boost_entries(struct ice_hw *hw) +{ + int status; + u16 i; + + for (i = 0; i < hw->dvm_upd.count; i++) { + status = ice_upd_dvm_boost_entry(hw, &hw->dvm_upd.tbl[i]); + if (status) + return status; + } + + return 0; +} + /** * ice_tunnel_idx_to_entry - convert linear index to the sparse one * @hw: pointer to the HW structure diff --git a/drivers/net/ethernet/intel/ice/ice_flex_pipe.h b/drivers/net/ethernet/intel/ice/ice_flex_pipe.h index 6cbc29bcb02f..9c530c86703e 100644 --- a/drivers/net/ethernet/intel/ice/ice_flex_pipe.h +++ b/drivers/net/ethernet/intel/ice/ice_flex_pipe.h @@ -87,8 +87,14 @@ ice_get_sw_fv_bitmap(struct ice_hw *hw, enum ice_prof_type type, void ice_init_prof_result_bm(struct ice_hw *hw); int -ice_get_sw_fv_list(struct ice_hw *hw, u8 *prot_ids, u16 ids_cnt, +ice_get_sw_fv_list(struct ice_hw *hw, struct ice_prot_lkup_ext *lkups, unsigned long *bm, struct list_head *fv_list); +int +ice_pkg_buf_unreserve_section(struct ice_buf_build *bld, u16 count); +u16 ice_pkg_buf_get_free_space(struct ice_buf_build *bld); +int +ice_aq_upload_section(struct ice_hw *hw, struct ice_buf_hdr *pkg_buf, + u16 buf_size, struct ice_sq_cd *cd); bool ice_get_open_tunnel_port(struct ice_hw *hw, u16 *port, enum ice_tunnel_type type); @@ -96,6 +102,7 @@ int ice_udp_tunnel_set_port(struct net_device *netdev, unsigned int table, unsigned int idx, struct udp_tunnel_info *ti); int ice_udp_tunnel_unset_port(struct net_device *netdev, unsigned int table, unsigned int idx, struct udp_tunnel_info *ti); +int ice_set_dvm_boost_entries(struct ice_hw *hw); /* Rx parser PTYPE functions */ bool ice_hw_ptype_ena(struct ice_hw *hw, u16 ptype); @@ -119,4 +126,10 @@ void ice_fill_blk_tbls(struct ice_hw *hw); void ice_clear_hw_tbls(struct ice_hw *hw); void ice_free_hw_tbls(struct ice_hw *hw); int ice_rem_prof(struct ice_hw *hw, enum ice_block blk, u64 id); +struct ice_buf_build * +ice_pkg_buf_alloc_single_section(struct ice_hw *hw, u32 type, u16 size, + void **section); +struct ice_buf *ice_pkg_buf(struct ice_buf_build *bld); +void ice_pkg_buf_free(struct ice_hw *hw, struct ice_buf_build *bld); + #endif /* _ICE_FLEX_PIPE_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_flex_type.h b/drivers/net/ethernet/intel/ice/ice_flex_type.h index fc087e0b5292..974d14a83b2e 100644 --- a/drivers/net/ethernet/intel/ice/ice_flex_type.h +++ b/drivers/net/ethernet/intel/ice/ice_flex_type.h @@ -162,6 +162,7 @@ struct ice_meta_sect { #define ICE_SID_RXPARSER_MARKER_PTYPE 55 #define ICE_SID_RXPARSER_BOOST_TCAM 56 +#define ICE_SID_RXPARSER_METADATA_INIT 58 #define ICE_SID_TXPARSER_BOOST_TCAM 66 #define ICE_SID_XLT0_PE 80 @@ -416,6 +417,8 @@ enum ice_tunnel_type { TNL_VXLAN = 0, TNL_GENEVE, TNL_GRETAP, + TNL_GTPC, + TNL_GTPU, __TNL_TYPE_CNT, TNL_LAST = 0xFF, TNL_ALL = 0xFF, @@ -442,6 +445,19 @@ struct ice_tunnel_table { u16 valid_count[__TNL_TYPE_CNT]; }; +struct ice_dvm_entry { + u16 boost_addr; + u16 enable; + struct ice_boost_tcam_entry *boost_entry; +}; + +#define ICE_DVM_MAX_ENTRIES 48 + +struct ice_dvm_table { + struct ice_dvm_entry tbl[ICE_DVM_MAX_ENTRIES]; + u16 count; +}; + struct ice_pkg_es { __le16 count; __le16 offset; @@ -659,7 +675,35 @@ enum ice_prof_type { ICE_PROF_NON_TUN = 0x1, ICE_PROF_TUN_UDP = 0x2, ICE_PROF_TUN_GRE = 0x4, - ICE_PROF_TUN_ALL = 0x6, + ICE_PROF_TUN_GTPU = 0x8, + ICE_PROF_TUN_GTPC = 0x10, + ICE_PROF_TUN_ALL = 0x1E, ICE_PROF_ALL = 0xFF, }; + +/* Number of bits/bytes contained in meta init entry. Note, this should be a + * multiple of 32 bits. + */ +#define ICE_META_INIT_BITS 192 +#define ICE_META_INIT_DW_CNT (ICE_META_INIT_BITS / (sizeof(__le32) * \ + BITS_PER_BYTE)) + +/* The meta init Flag field starts at this bit */ +#define ICE_META_FLAGS_ST 123 + +/* The entry and bit to check for Double VLAN Mode (DVM) support */ +#define ICE_META_VLAN_MODE_ENTRY 0 +#define ICE_META_FLAG_VLAN_MODE 60 +#define ICE_META_VLAN_MODE_BIT (ICE_META_FLAGS_ST + \ + ICE_META_FLAG_VLAN_MODE) + +struct ice_meta_init_entry { + __le32 bm[ICE_META_INIT_DW_CNT]; +}; + +struct ice_meta_init_section { + __le16 count; + __le16 offset; + struct ice_meta_init_entry entry; +}; #endif /* _ICE_FLEX_TYPE_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_flow.c b/drivers/net/ethernet/intel/ice/ice_flow.c index beed4838dcbe..ef103e47a8dc 100644 --- a/drivers/net/ethernet/intel/ice/ice_flow.c +++ b/drivers/net/ethernet/intel/ice/ice_flow.c @@ -3,6 +3,7 @@ #include "ice_common.h" #include "ice_flow.h" +#include /* Describe properties of a protocol header field */ struct ice_flow_field_info { diff --git a/drivers/net/ethernet/intel/ice/ice_flow.h b/drivers/net/ethernet/intel/ice/ice_flow.h index 84b6e4464a21..b465d27d9b80 100644 --- a/drivers/net/ethernet/intel/ice/ice_flow.h +++ b/drivers/net/ethernet/intel/ice/ice_flow.h @@ -4,6 +4,8 @@ #ifndef _ICE_FLOW_H_ #define _ICE_FLOW_H_ +#include "ice_flex_type.h" + #define ICE_FLOW_ENTRY_HANDLE_INVAL 0 #define ICE_FLOW_FLD_OFF_INVAL 0xffff diff --git a/drivers/net/ethernet/intel/ice/ice_fltr.c b/drivers/net/ethernet/intel/ice/ice_fltr.c index c29177c6bb9d..af57eb114966 100644 --- a/drivers/net/ethernet/intel/ice/ice_fltr.c +++ b/drivers/net/ethernet/intel/ice/ice_fltr.c @@ -203,21 +203,22 @@ ice_fltr_add_mac_to_list(struct ice_vsi *vsi, struct list_head *list, * ice_fltr_add_vlan_to_list - add VLAN filter info to exsisting list * @vsi: pointer to VSI struct * @list: list to add filter info to - * @vlan_id: VLAN ID to add - * @action: filter action + * @vlan: VLAN filter details */ static int ice_fltr_add_vlan_to_list(struct ice_vsi *vsi, struct list_head *list, - u16 vlan_id, enum ice_sw_fwd_act_type action) + struct ice_vlan *vlan) { struct ice_fltr_info info = { 0 }; info.flag = ICE_FLTR_TX; info.src_id = ICE_SRC_ID_VSI; info.lkup_type = ICE_SW_LKUP_VLAN; - info.fltr_act = action; + info.fltr_act = ICE_FWD_TO_VSI; info.vsi_handle = vsi->idx; - info.l_data.vlan.vlan_id = vlan_id; + info.l_data.vlan.vlan_id = vlan->vid; + info.l_data.vlan.tpid = vlan->tpid; + info.l_data.vlan.tpid_valid = true; return ice_fltr_add_entry_to_list(ice_pf_to_dev(vsi->back), &info, list); @@ -310,19 +311,17 @@ ice_fltr_prepare_mac_and_broadcast(struct ice_vsi *vsi, const u8 *mac, /** * ice_fltr_prepare_vlan - add or remove VLAN filter * @vsi: pointer to VSI struct - * @vlan_id: VLAN ID to add - * @action: action to be performed on filter match + * @vlan: VLAN filter details * @vlan_action: pointer to add or remove VLAN function */ static int -ice_fltr_prepare_vlan(struct ice_vsi *vsi, u16 vlan_id, - enum ice_sw_fwd_act_type action, +ice_fltr_prepare_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan, int (*vlan_action)(struct ice_vsi *, struct list_head *)) { LIST_HEAD(tmp_list); int result; - if (ice_fltr_add_vlan_to_list(vsi, &tmp_list, vlan_id, action)) + if (ice_fltr_add_vlan_to_list(vsi, &tmp_list, vlan)) return -ENOMEM; result = vlan_action(vsi, &tmp_list); @@ -395,27 +394,21 @@ int ice_fltr_remove_mac(struct ice_vsi *vsi, const u8 *mac, /** * ice_fltr_add_vlan - add single VLAN filter * @vsi: pointer to VSI struct - * @vlan_id: VLAN ID to add - * @action: action to be performed on filter match + * @vlan: VLAN filter details */ -int ice_fltr_add_vlan(struct ice_vsi *vsi, u16 vlan_id, - enum ice_sw_fwd_act_type action) +int ice_fltr_add_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan) { - return ice_fltr_prepare_vlan(vsi, vlan_id, action, - ice_fltr_add_vlan_list); + return ice_fltr_prepare_vlan(vsi, vlan, ice_fltr_add_vlan_list); } /** * ice_fltr_remove_vlan - remove VLAN filter * @vsi: pointer to VSI struct - * @vlan_id: filter VLAN to remove - * @action: action to remove + * @vlan: VLAN filter details */ -int ice_fltr_remove_vlan(struct ice_vsi *vsi, u16 vlan_id, - enum ice_sw_fwd_act_type action) +int ice_fltr_remove_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan) { - return ice_fltr_prepare_vlan(vsi, vlan_id, action, - ice_fltr_remove_vlan_list); + return ice_fltr_prepare_vlan(vsi, vlan, ice_fltr_remove_vlan_list); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_fltr.h b/drivers/net/ethernet/intel/ice/ice_fltr.h index 3eb42479175f..0f3dbc308eec 100644 --- a/drivers/net/ethernet/intel/ice/ice_fltr.h +++ b/drivers/net/ethernet/intel/ice/ice_fltr.h @@ -4,6 +4,8 @@ #ifndef _ICE_FLTR_H_ #define _ICE_FLTR_H_ +#include "ice_vlan.h" + void ice_fltr_free_list(struct device *dev, struct list_head *h); int ice_fltr_set_vlan_vsi_promisc(struct ice_hw *hw, struct ice_vsi *vsi, @@ -32,12 +34,8 @@ ice_fltr_remove_mac(struct ice_vsi *vsi, const u8 *mac, enum ice_sw_fwd_act_type action); int ice_fltr_remove_mac_list(struct ice_vsi *vsi, struct list_head *list); -int -ice_fltr_add_vlan(struct ice_vsi *vsi, u16 vid, - enum ice_sw_fwd_act_type action); -int -ice_fltr_remove_vlan(struct ice_vsi *vsi, u16 vid, - enum ice_sw_fwd_act_type action); +int ice_fltr_add_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan); +int ice_fltr_remove_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan); int ice_fltr_add_eth(struct ice_vsi *vsi, u16 ethertype, u16 flag, diff --git a/drivers/net/ethernet/intel/ice/ice_gnss.c b/drivers/net/ethernet/intel/ice/ice_gnss.c new file mode 100644 index 000000000000..35579cf4283f --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_gnss.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2018-2021, Intel Corporation. */ + +#include "ice.h" +#include "ice_lib.h" +#include + +/** + * ice_gnss_read - Read data from internal GNSS module + * @work: GNSS read work structure + * + * Read the data from internal GNSS receiver, number of bytes read will be + * returned in *read_data parameter. + */ +static void ice_gnss_read(struct kthread_work *work) +{ + struct gnss_serial *gnss = container_of(work, struct gnss_serial, + read_work.work); + struct ice_aqc_link_topo_addr link_topo; + u8 i2c_params, bytes_read; + struct tty_port *port; + struct ice_pf *pf; + struct ice_hw *hw; + __be16 data_len_b; + char *buf = NULL; + u16 i, data_len; + int err = 0; + + pf = gnss->back; + if (!pf || !gnss->tty || !gnss->tty->port) { + err = -EFAULT; + goto exit; + } + + hw = &pf->hw; + port = gnss->tty->port; + + buf = (char *)get_zeroed_page(GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto exit; + } + + memset(&link_topo, 0, sizeof(struct ice_aqc_link_topo_addr)); + link_topo.topo_params.index = ICE_E810T_GNSS_I2C_BUS; + link_topo.topo_params.node_type_ctx |= + FIELD_PREP(ICE_AQC_LINK_TOPO_NODE_CTX_M, + ICE_AQC_LINK_TOPO_NODE_CTX_OVERRIDE); + + i2c_params = ICE_GNSS_UBX_DATA_LEN_WIDTH | + ICE_AQC_I2C_USE_REPEATED_START; + + /* Read data length in a loop, when it's not 0 the data is ready */ + for (i = 0; i < ICE_MAX_UBX_READ_TRIES; i++) { + err = ice_aq_read_i2c(hw, link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR, + cpu_to_le16(ICE_GNSS_UBX_DATA_LEN_H), + i2c_params, (u8 *)&data_len_b, NULL); + if (err) + goto exit_buf; + + data_len = be16_to_cpu(data_len_b); + if (data_len != 0 && data_len != U16_MAX) + break; + + mdelay(10); + } + + data_len = min(data_len, (u16)PAGE_SIZE); + data_len = tty_buffer_request_room(port, data_len); + if (!data_len) { + err = -ENOMEM; + goto exit_buf; + } + + /* Read received data */ + for (i = 0; i < data_len; i += bytes_read) { + u16 bytes_left = data_len - i; + + bytes_read = bytes_left < ICE_MAX_I2C_DATA_SIZE ? bytes_left : + ICE_MAX_I2C_DATA_SIZE; + + err = ice_aq_read_i2c(hw, link_topo, ICE_GNSS_UBX_I2C_BUS_ADDR, + cpu_to_le16(ICE_GNSS_UBX_EMPTY_DATA), + bytes_read, &buf[i], NULL); + if (err) + goto exit_buf; + } + + /* Send the data to the tty layer for users to read. This doesn't + * actually push the data through unless tty->low_latency is set. + */ + tty_insert_flip_string(port, buf, i); + tty_flip_buffer_push(port); + +exit_buf: + free_page((unsigned long)buf); + kthread_queue_delayed_work(gnss->kworker, &gnss->read_work, + ICE_GNSS_TIMER_DELAY_TIME); +exit: + if (err) + dev_dbg(ice_pf_to_dev(pf), "GNSS failed to read err=%d\n", err); +} + +/** + * ice_gnss_struct_init - Initialize GNSS structure for the TTY + * @pf: Board private structure + */ +static struct gnss_serial *ice_gnss_struct_init(struct ice_pf *pf) +{ + struct device *dev = ice_pf_to_dev(pf); + struct kthread_worker *kworker; + struct gnss_serial *gnss; + + gnss = kzalloc(sizeof(*gnss), GFP_KERNEL); + if (!gnss) + return NULL; + + mutex_init(&gnss->gnss_mutex); + gnss->open_count = 0; + gnss->back = pf; + pf->gnss_serial = gnss; + + kthread_init_delayed_work(&gnss->read_work, ice_gnss_read); + /* Allocate a kworker for handling work required for the GNSS TTY + * writes. + */ + kworker = kthread_create_worker(0, "ice-gnss-%s", dev_name(dev)); + if (IS_ERR(kworker)) { + kfree(gnss); + return NULL; + } + + gnss->kworker = kworker; + + return gnss; +} + +/** + * ice_gnss_tty_open - Initialize GNSS structures on TTY device open + * @tty: pointer to the tty_struct + * @filp: pointer to the file + * + * This routine is mandatory. If this routine is not filled in, the attempted + * open will fail with ENODEV. + */ +static int ice_gnss_tty_open(struct tty_struct *tty, struct file *filp) +{ + struct gnss_serial *gnss; + struct ice_pf *pf; + + pf = (struct ice_pf *)tty->driver->driver_state; + if (!pf) + return -EFAULT; + + /* Clear the pointer in case something fails */ + tty->driver_data = NULL; + + /* Get the serial object associated with this tty pointer */ + gnss = pf->gnss_serial; + if (!gnss) { + /* Initialize GNSS struct on the first device open */ + gnss = ice_gnss_struct_init(pf); + if (!gnss) + return -ENOMEM; + } + + mutex_lock(&gnss->gnss_mutex); + + /* Save our structure within the tty structure */ + tty->driver_data = gnss; + gnss->tty = tty; + gnss->open_count++; + kthread_queue_delayed_work(gnss->kworker, &gnss->read_work, 0); + + mutex_unlock(&gnss->gnss_mutex); + + return 0; +} + +/** + * ice_gnss_tty_close - Cleanup GNSS structures on tty device close + * @tty: pointer to the tty_struct + * @filp: pointer to the file + */ +static void ice_gnss_tty_close(struct tty_struct *tty, struct file *filp) +{ + struct gnss_serial *gnss = tty->driver_data; + struct ice_pf *pf; + + if (!gnss) + return; + + pf = (struct ice_pf *)tty->driver->driver_state; + if (!pf) + return; + + mutex_lock(&gnss->gnss_mutex); + + if (!gnss->open_count) { + /* Port was never opened */ + dev_err(ice_pf_to_dev(pf), "GNSS port not opened\n"); + goto exit; + } + + gnss->open_count--; + if (gnss->open_count <= 0) { + /* Port is in shutdown state */ + kthread_cancel_delayed_work_sync(&gnss->read_work); + } +exit: + mutex_unlock(&gnss->gnss_mutex); +} + +/** + * ice_gnss_tty_write - Dummy TTY write function to avoid kernel panic + * @tty: pointer to the tty_struct + * @buf: pointer to the user data + * @cnt: the number of characters that was able to be sent to the hardware (or + * queued to be sent at a later time) + */ +static int +ice_gnss_tty_write(struct tty_struct *tty, const unsigned char *buf, int cnt) +{ + return 0; +} + +/** + * ice_gnss_tty_write_room - Dummy TTY write_room function to avoid kernel panic + * @tty: pointer to the tty_struct + */ +static unsigned int ice_gnss_tty_write_room(struct tty_struct *tty) +{ + return 0; +} + +static const struct tty_operations tty_gps_ops = { + .open = ice_gnss_tty_open, + .close = ice_gnss_tty_close, + .write = ice_gnss_tty_write, + .write_room = ice_gnss_tty_write_room, +}; + +/** + * ice_gnss_create_tty_driver - Create a TTY driver for GNSS + * @pf: Board private structure + */ +static struct tty_driver *ice_gnss_create_tty_driver(struct ice_pf *pf) +{ + struct device *dev = ice_pf_to_dev(pf); + const int ICE_TTYDRV_NAME_MAX = 14; + struct tty_driver *tty_driver; + char *ttydrv_name; + int err; + + tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW); + if (IS_ERR(tty_driver)) { + dev_err(ice_pf_to_dev(pf), "Failed to allocate memory for GNSS TTY\n"); + return NULL; + } + + ttydrv_name = kzalloc(ICE_TTYDRV_NAME_MAX, GFP_KERNEL); + if (!ttydrv_name) { + tty_driver_kref_put(tty_driver); + return NULL; + } + + snprintf(ttydrv_name, ICE_TTYDRV_NAME_MAX, "ttyGNSS_%02x%02x_", + (u8)pf->pdev->bus->number, (u8)PCI_SLOT(pf->pdev->devfn)); + + /* Initialize the tty driver*/ + tty_driver->owner = THIS_MODULE; + tty_driver->driver_name = dev_driver_string(dev); + tty_driver->name = (const char *)ttydrv_name; + tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + tty_driver->subtype = SERIAL_TYPE_NORMAL; + tty_driver->init_termios = tty_std_termios; + tty_driver->init_termios.c_iflag &= ~INLCR; + tty_driver->init_termios.c_iflag |= IGNCR; + tty_driver->init_termios.c_oflag &= ~OPOST; + tty_driver->init_termios.c_lflag &= ~ICANON; + tty_driver->init_termios.c_cflag &= ~(CSIZE | CBAUD | CBAUDEX); + /* baud rate 9600 */ + tty_termios_encode_baud_rate(&tty_driver->init_termios, 9600, 9600); + tty_driver->driver_state = pf; + tty_set_operations(tty_driver, &tty_gps_ops); + + pf->gnss_serial = NULL; + + tty_port_init(&pf->gnss_tty_port); + tty_port_link_device(&pf->gnss_tty_port, tty_driver, 0); + + err = tty_register_driver(tty_driver); + if (err) { + dev_err(ice_pf_to_dev(pf), "Failed to register TTY driver err=%d\n", + err); + + tty_port_destroy(&pf->gnss_tty_port); + kfree(ttydrv_name); + tty_driver_kref_put(pf->ice_gnss_tty_driver); + + return NULL; + } + + return tty_driver; +} + +/** + * ice_gnss_init - Initialize GNSS TTY support + * @pf: Board private structure + */ +void ice_gnss_init(struct ice_pf *pf) +{ + struct tty_driver *tty_driver; + + tty_driver = ice_gnss_create_tty_driver(pf); + if (!tty_driver) + return; + + pf->ice_gnss_tty_driver = tty_driver; + + set_bit(ICE_FLAG_GNSS, pf->flags); + dev_info(ice_pf_to_dev(pf), "GNSS TTY init successful\n"); +} + +/** + * ice_gnss_exit - Disable GNSS TTY support + * @pf: Board private structure + */ +void ice_gnss_exit(struct ice_pf *pf) +{ + if (!test_bit(ICE_FLAG_GNSS, pf->flags) || !pf->ice_gnss_tty_driver) + return; + + tty_port_destroy(&pf->gnss_tty_port); + + if (pf->gnss_serial) { + struct gnss_serial *gnss = pf->gnss_serial; + + kthread_cancel_delayed_work_sync(&gnss->read_work); + kfree(gnss); + pf->gnss_serial = NULL; + } + + tty_unregister_driver(pf->ice_gnss_tty_driver); + kfree(pf->ice_gnss_tty_driver->name); + tty_driver_kref_put(pf->ice_gnss_tty_driver); + pf->ice_gnss_tty_driver = NULL; +} + +/** + * ice_gnss_is_gps_present - Check if GPS HW is present + * @hw: pointer to HW struct + */ +bool ice_gnss_is_gps_present(struct ice_hw *hw) +{ + if (!hw->func_caps.ts_func_info.src_tmr_owned) + return false; + +#if IS_ENABLED(CONFIG_PTP_1588_CLOCK) + if (ice_is_e810t(hw)) { + int err; + u8 data; + + err = ice_read_pca9575_reg_e810t(hw, ICE_PCA9575_P0_IN, &data); + if (err || !!(data & ICE_E810T_P0_GNSS_PRSNT_N)) + return false; + } else { + return false; + } +#else + if (!ice_is_e810t(hw)) + return false; +#endif /* IS_ENABLED(CONFIG_PTP_1588_CLOCK) */ + + return true; +} diff --git a/drivers/net/ethernet/intel/ice/ice_gnss.h b/drivers/net/ethernet/intel/ice/ice_gnss.h new file mode 100644 index 000000000000..9211adb2372c --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_gnss.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018-2021, Intel Corporation. */ + +#ifndef _ICE_GNSS_H_ +#define _ICE_GNSS_H_ + +#include +#include + +#define ICE_E810T_GNSS_I2C_BUS 0x2 +#define ICE_GNSS_UBX_I2C_BUS_ADDR 0x42 +/* Data length register is big endian */ +#define ICE_GNSS_UBX_DATA_LEN_H 0xFD +#define ICE_GNSS_UBX_DATA_LEN_WIDTH 2 +#define ICE_GNSS_UBX_EMPTY_DATA 0xFF +#define ICE_GNSS_TIMER_DELAY_TIME (HZ / 10) /* 0.1 second per message */ +#define ICE_MAX_I2C_DATA_SIZE FIELD_MAX(ICE_AQC_I2C_DATA_SIZE_M) +#define ICE_MAX_UBX_READ_TRIES 255 + +/** + * struct gnss_serial - data used to initialize GNSS TTY port + * @back: back pointer to PF + * @tty: pointer to the tty for this device + * @open_count: number of times this port has been opened + * @gnss_mutex: gnss_mutex used to protect GNSS serial operations + * @kworker: kwork thread for handling periodic work + * @read_work: read_work function for handling GNSS reads + */ +struct gnss_serial { + struct ice_pf *back; + struct tty_struct *tty; + int open_count; + struct mutex gnss_mutex; /* protects GNSS serial structure */ + struct kthread_worker *kworker; + struct kthread_delayed_work read_work; +}; + +#if IS_ENABLED(CONFIG_TTY) +void ice_gnss_init(struct ice_pf *pf); +void ice_gnss_exit(struct ice_pf *pf); +bool ice_gnss_is_gps_present(struct ice_hw *hw); +#else +static inline void ice_gnss_init(struct ice_pf *pf) { } +static inline void ice_gnss_exit(struct ice_pf *pf) { } +static inline bool ice_gnss_is_gps_present(struct ice_hw *hw) +{ + return false; +} +#endif /* IS_ENABLED(CONFIG_TTY) */ +#endif /* _ICE_GNSS_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_idc.c b/drivers/net/ethernet/intel/ice/ice_idc.c index fc3580167e7b..25a436d342c2 100644 --- a/drivers/net/ethernet/intel/ice/ice_idc.c +++ b/drivers/net/ethernet/intel/ice/ice_idc.c @@ -34,6 +34,9 @@ void ice_send_event_to_aux(struct ice_pf *pf, struct iidc_event *event) { struct iidc_auxiliary_drv *iadrv; + if (WARN_ON_ONCE(!in_task())) + return; + if (!pf->adev) return; @@ -79,7 +82,7 @@ int ice_add_rdma_qset(struct ice_pf *pf, struct iidc_rdma_qset_params *qset) dev = ice_pf_to_dev(pf); - if (!test_bit(ICE_FLAG_RDMA_ENA, pf->flags)) + if (!ice_is_rdma_ena(pf)) return -EINVAL; vsi = ice_get_main_vsi(pf); @@ -227,6 +230,11 @@ void ice_get_qos_params(struct ice_pf *pf, struct iidc_qos_params *qos) for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) qos->tc_info[i].rel_bw = dcbx_cfg->etscfg.tcbwtable[i]; + + qos->pfc_mode = dcbx_cfg->pfc_mode; + if (qos->pfc_mode == IIDC_DSCP_PFC_MODE) + for (i = 0; i < IIDC_MAX_DSCP_MAPPING; i++) + qos->dscp_map[i] = dcbx_cfg->dscp_map[i]; } EXPORT_SYMBOL_GPL(ice_get_qos_params); @@ -236,7 +244,7 @@ EXPORT_SYMBOL_GPL(ice_get_qos_params); */ static int ice_reserve_rdma_qvector(struct ice_pf *pf) { - if (test_bit(ICE_FLAG_RDMA_ENA, pf->flags)) { + if (ice_is_rdma_ena(pf)) { int index; index = ice_get_res(pf, pf->irq_tracker, pf->num_rdma_msix, @@ -274,7 +282,7 @@ int ice_plug_aux_dev(struct ice_pf *pf) /* if this PF doesn't support a technology that requires auxiliary * devices, then gracefully exit */ - if (!ice_is_aux_ena(pf)) + if (!ice_is_rdma_ena(pf)) return 0; iadev = kzalloc(sizeof(*iadev), GFP_KERNEL); diff --git a/drivers/net/ethernet/intel/ice/ice_idc_int.h b/drivers/net/ethernet/intel/ice/ice_idc_int.h index b7796b8aecbd..4b0c86757df9 100644 --- a/drivers/net/ethernet/intel/ice/ice_idc_int.h +++ b/drivers/net/ethernet/intel/ice/ice_idc_int.h @@ -5,7 +5,6 @@ #define _ICE_IDC_INT_H_ #include -#include "ice.h" struct ice_pf; diff --git a/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h b/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h index 85a612838a89..b3baf7c3f910 100644 --- a/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h +++ b/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h @@ -424,6 +424,8 @@ enum ice_rx_flex_desc_status_error_0_bits { enum ice_rx_flex_desc_status_error_1_bits { /* Note: These are predefined bit offsets */ ICE_RX_FLEX_DESC_STATUS1_NAT_S = 4, + /* [10:5] reserved */ + ICE_RX_FLEX_DESC_STATUS1_L2TAG2P_S = 11, ICE_RX_FLEX_DESC_STATUS1_LAST /* this entry must be last!!! */ }; diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index 53256aca27c7..b897926f817d 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -8,6 +8,7 @@ #include "ice_fltr.h" #include "ice_dcb_lib.h" #include "ice_devlink.h" +#include "ice_vsi_vlan_ops.h" /** * ice_vsi_type_str - maps VSI type enum to string equivalents @@ -165,21 +166,19 @@ static void ice_vsi_set_num_desc(struct ice_vsi *vsi) /** * ice_vsi_set_num_qs - Set number of queues, descriptors and vectors for a VSI * @vsi: the VSI being configured - * @vf_id: ID of the VF being configured + * @vf: the VF associated with this VSI, if any * * Return 0 on success and a negative value on error */ -static void ice_vsi_set_num_qs(struct ice_vsi *vsi, u16 vf_id) +static void ice_vsi_set_num_qs(struct ice_vsi *vsi, struct ice_vf *vf) { + enum ice_vsi_type vsi_type = vsi->type; struct ice_pf *pf = vsi->back; - struct ice_vf *vf = NULL; - if (vsi->type == ICE_VSI_VF) - vsi->vf_id = vf_id; - else - vsi->vf_id = ICE_INVAL_VFID; + if (WARN_ON(vsi_type == ICE_VSI_VF && !vf)) + return; - switch (vsi->type) { + switch (vsi_type) { case ICE_VSI_PF: if (vsi->req_txq) { vsi->alloc_txq = vsi->req_txq; @@ -216,22 +215,21 @@ static void ice_vsi_set_num_qs(struct ice_vsi *vsi, u16 vf_id) /* The number of queues for ctrl VSI is equal to number of VFs. * Each ring is associated to the corresponding VF_PR netdev. */ - vsi->alloc_txq = pf->num_alloc_vfs; - vsi->alloc_rxq = pf->num_alloc_vfs; + vsi->alloc_txq = ice_get_num_vfs(pf); + vsi->alloc_rxq = vsi->alloc_txq; vsi->num_q_vectors = 1; break; case ICE_VSI_VF: - vf = &pf->vf[vsi->vf_id]; if (vf->num_req_qs) vf->num_vf_qs = vf->num_req_qs; vsi->alloc_txq = vf->num_vf_qs; vsi->alloc_rxq = vf->num_vf_qs; - /* pf->num_msix_per_vf includes (VF miscellaneous vector + + /* pf->vfs.num_msix_per includes (VF miscellaneous vector + * data queue interrupts). Since vsi->num_q_vectors is number * of queues vectors, subtract 1 (ICE_NONQ_VECS_VF) from the * original vector count */ - vsi->num_q_vectors = pf->num_msix_per_vf - ICE_NONQ_VECS_VF; + vsi->num_q_vectors = pf->vfs.num_msix_per - ICE_NONQ_VECS_VF; break; case ICE_VSI_CTRL: vsi->alloc_txq = 1; @@ -247,7 +245,7 @@ static void ice_vsi_set_num_qs(struct ice_vsi *vsi, u16 vf_id) vsi->alloc_rxq = 1; break; default: - dev_warn(ice_pf_to_dev(pf), "Unknown VSI type %d\n", vsi->type); + dev_warn(ice_pf_to_dev(pf), "Unknown VSI type %d\n", vsi_type); break; } @@ -298,7 +296,7 @@ void ice_vsi_delete(struct ice_vsi *vsi) return; if (vsi->type == ICE_VSI_VF) - ctxt->vf_num = vsi->vf_id; + ctxt->vf_num = vsi->vf->vf_id; ctxt->vsi_num = vsi->vsi_num; memcpy(&ctxt->info, &vsi->info, sizeof(ctxt->info)); @@ -383,8 +381,7 @@ int ice_vsi_clear(struct ice_vsi *vsi) pf->vsi[vsi->idx] = NULL; if (vsi->idx < pf->next_vsi && vsi->type != ICE_VSI_CTRL) pf->next_vsi = vsi->idx; - if (vsi->idx < pf->next_vsi && vsi->type == ICE_VSI_CTRL && - vsi->vf_id != ICE_INVAL_VFID) + if (vsi->idx < pf->next_vsi && vsi->type == ICE_VSI_CTRL && vsi->vf) pf->next_vsi = vsi->idx; ice_vsi_free_arrays(vsi); @@ -436,13 +433,16 @@ static irqreturn_t ice_eswitch_msix_clean_rings(int __always_unused irq, void *d { struct ice_q_vector *q_vector = (struct ice_q_vector *)data; struct ice_pf *pf = q_vector->vsi->back; - int i; + struct ice_vf *vf; + unsigned int bkt; if (!q_vector->tx.tx_ring && !q_vector->rx.rx_ring) return IRQ_HANDLED; - ice_for_each_vf(pf, i) - napi_schedule(&pf->vf[i].repr->q_vector->napi); + rcu_read_lock(); + ice_for_each_vf_rcu(pf, bkt, vf) + napi_schedule(&vf->repr->q_vector->napi); + rcu_read_unlock(); return IRQ_HANDLED; } @@ -452,17 +452,24 @@ static irqreturn_t ice_eswitch_msix_clean_rings(int __always_unused irq, void *d * @pf: board private structure * @vsi_type: type of VSI * @ch: ptr to channel - * @vf_id: ID of the VF being configured + * @vf: VF for ICE_VSI_VF and ICE_VSI_CTRL + * + * The VF pointer is used for ICE_VSI_VF and ICE_VSI_CTRL. For ICE_VSI_CTRL, + * it may be NULL in the case there is no association with a VF. For + * ICE_VSI_VF the VF pointer *must not* be NULL. * * returns a pointer to a VSI on success, NULL on failure. */ static struct ice_vsi * ice_vsi_alloc(struct ice_pf *pf, enum ice_vsi_type vsi_type, - struct ice_channel *ch, u16 vf_id) + struct ice_channel *ch, struct ice_vf *vf) { struct device *dev = ice_pf_to_dev(pf); struct ice_vsi *vsi = NULL; + if (WARN_ON(vsi_type == ICE_VSI_VF && !vf)) + return NULL; + /* Need to protect the allocation of the VSIs at the PF level */ mutex_lock(&pf->sw_mutex); @@ -484,9 +491,9 @@ ice_vsi_alloc(struct ice_pf *pf, enum ice_vsi_type vsi_type, set_bit(ICE_VSI_DOWN, vsi->state); if (vsi_type == ICE_VSI_VF) - ice_vsi_set_num_qs(vsi, vf_id); + ice_vsi_set_num_qs(vsi, vf); else if (vsi_type != ICE_VSI_CHNL) - ice_vsi_set_num_qs(vsi, ICE_INVAL_VFID); + ice_vsi_set_num_qs(vsi, NULL); switch (vsi->type) { case ICE_VSI_SWITCHDEV_CTRL: @@ -509,10 +516,16 @@ ice_vsi_alloc(struct ice_pf *pf, enum ice_vsi_type vsi_type, /* Setup ctrl VSI MSIX irq handler */ vsi->irq_handler = ice_msix_clean_ctrl_vsi; + + /* For the PF control VSI this is NULL, for the VF control VSI + * this will be the first VF to allocate it. + */ + vsi->vf = vf; break; case ICE_VSI_VF: if (ice_vsi_alloc_arrays(vsi)) goto err_rings; + vsi->vf = vf; break; case ICE_VSI_CHNL: if (!ch) @@ -530,7 +543,7 @@ ice_vsi_alloc(struct ice_pf *pf, enum ice_vsi_type vsi_type, goto unlock_pf; } - if (vsi->type == ICE_VSI_CTRL && vf_id == ICE_INVAL_VFID) { + if (vsi->type == ICE_VSI_CTRL && !vf) { /* Use the last VSI slot as the index for PF control VSI */ vsi->idx = pf->num_alloc_vsi - 1; pf->ctrl_vsi_idx = vsi->idx; @@ -545,8 +558,8 @@ ice_vsi_alloc(struct ice_pf *pf, enum ice_vsi_type vsi_type, pf->next_vsi); } - if (vsi->type == ICE_VSI_CTRL && vf_id != ICE_INVAL_VFID) - pf->vf[vf_id].ctrl_vsi_idx = vsi->idx; + if (vsi->type == ICE_VSI_CTRL && vf) + vf->ctrl_vsi_idx = vsi->idx; goto unlock_pf; err_rings: @@ -732,14 +745,14 @@ bool ice_is_safe_mode(struct ice_pf *pf) } /** - * ice_is_aux_ena + * ice_is_rdma_ena * @pf: pointer to the PF struct * - * returns true if AUX devices/drivers are supported, false otherwise + * returns true if RDMA is currently supported, false otherwise */ -bool ice_is_aux_ena(struct ice_pf *pf) +bool ice_is_rdma_ena(struct ice_pf *pf) { - return test_bit(ICE_FLAG_AUX_ENA, pf->flags); + return test_bit(ICE_FLAG_RDMA_ENA, pf->flags); } /** @@ -838,11 +851,12 @@ static void ice_vsi_set_rss_params(struct ice_vsi *vsi) /** * ice_set_dflt_vsi_ctx - Set default VSI context before adding a VSI + * @hw: HW structure used to determine the VLAN mode of the device * @ctxt: the VSI context being set * * This initializes a default VSI context for all sections except the Queues. */ -static void ice_set_dflt_vsi_ctx(struct ice_vsi_ctx *ctxt) +static void ice_set_dflt_vsi_ctx(struct ice_hw *hw, struct ice_vsi_ctx *ctxt) { u32 table = 0; @@ -853,13 +867,27 @@ static void ice_set_dflt_vsi_ctx(struct ice_vsi_ctx *ctxt) ctxt->info.sw_flags = ICE_AQ_VSI_SW_FLAG_SRC_PRUNE; /* Traffic from VSI can be sent to LAN */ ctxt->info.sw_flags2 = ICE_AQ_VSI_SW_FLAG_LAN_ENA; - /* By default bits 3 and 4 in vlan_flags are 0's which results in legacy - * behavior (show VLAN, DEI, and UP) in descriptor. Also, allow all - * packets untagged/tagged. + /* allow all untagged/tagged packets by default on Tx */ + ctxt->info.inner_vlan_flags = ((ICE_AQ_VSI_INNER_VLAN_TX_MODE_ALL & + ICE_AQ_VSI_INNER_VLAN_TX_MODE_M) >> + ICE_AQ_VSI_INNER_VLAN_TX_MODE_S); + /* SVM - by default bits 3 and 4 in inner_vlan_flags are 0's which + * results in legacy behavior (show VLAN, DEI, and UP) in descriptor. + * + * DVM - leave inner VLAN in packet by default */ - ctxt->info.vlan_flags = ((ICE_AQ_VSI_VLAN_MODE_ALL & - ICE_AQ_VSI_VLAN_MODE_M) >> - ICE_AQ_VSI_VLAN_MODE_S); + if (ice_is_dvm_ena(hw)) { + ctxt->info.inner_vlan_flags |= + ICE_AQ_VSI_INNER_VLAN_EMODE_NOTHING; + ctxt->info.outer_vlan_flags = + (ICE_AQ_VSI_OUTER_VLAN_TX_MODE_ALL << + ICE_AQ_VSI_OUTER_VLAN_TX_MODE_S) & + ICE_AQ_VSI_OUTER_VLAN_TX_MODE_M; + ctxt->info.outer_vlan_flags |= + (ICE_AQ_VSI_OUTER_TAG_VLAN_8100 << + ICE_AQ_VSI_OUTER_TAG_TYPE_S) & + ICE_AQ_VSI_OUTER_TAG_TYPE_M; + } /* Have 1:1 UP mapping for both ingress/egress tables */ table |= ICE_UP_TABLE_TRANSLATE(0, 0); table |= ICE_UP_TABLE_TRANSLATE(1, 1); @@ -1114,7 +1142,7 @@ static int ice_vsi_init(struct ice_vsi *vsi, bool init_vsi) case ICE_VSI_VF: ctxt->flags = ICE_AQ_VSI_TYPE_VF; /* VF number here is the absolute VF number (0-255) */ - ctxt->vf_num = vsi->vf_id + hw->func_caps.vf_base_id; + ctxt->vf_num = vsi->vf->vf_id + hw->func_caps.vf_base_id; break; default: ret = -ENODEV; @@ -1136,7 +1164,7 @@ static int ice_vsi_init(struct ice_vsi *vsi, bool init_vsi) ~ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA; } - ice_set_dflt_vsi_ctx(ctxt); + ice_set_dflt_vsi_ctx(hw, ctxt); if (test_bit(ICE_FLAG_FD_ENA, pf->flags)) ice_set_fd_vsi_ctx(ctxt, vsi); /* if the switch is in VEB mode, allow VSI loopback */ @@ -1168,25 +1196,6 @@ static int ice_vsi_init(struct ice_vsi *vsi, bool init_vsi) cpu_to_le16(ICE_AQ_VSI_PROP_RXQ_MAP_VALID); } - /* enable/disable MAC and VLAN anti-spoof when spoofchk is on/off - * respectively - */ - if (vsi->type == ICE_VSI_VF) { - ctxt->info.valid_sections |= - cpu_to_le16(ICE_AQ_VSI_PROP_SECURITY_VALID); - if (pf->vf[vsi->vf_id].spoofchk) { - ctxt->info.sec_flags |= - ICE_AQ_VSI_SEC_FLAG_ENA_MAC_ANTI_SPOOF | - (ICE_AQ_VSI_SEC_TX_VLAN_PRUNE_ENA << - ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S); - } else { - ctxt->info.sec_flags &= - ~(ICE_AQ_VSI_SEC_FLAG_ENA_MAC_ANTI_SPOOF | - (ICE_AQ_VSI_SEC_TX_VLAN_PRUNE_ENA << - ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S)); - } - } - /* Allow control frames out of main VSI */ if (vsi->type == ICE_VSI_PF) { ctxt->info.sec_flags |= ICE_AQ_VSI_SEC_FLAG_ALLOW_DEST_OVRD; @@ -1324,6 +1333,36 @@ ice_get_res(struct ice_pf *pf, struct ice_res_tracker *res, u16 needed, u16 id) return ice_search_res(res, needed, id); } +/** + * ice_get_vf_ctrl_res - Get VF control VSI resource + * @pf: pointer to the PF structure + * @vsi: the VSI to allocate a resource for + * + * Look up whether another VF has already allocated the control VSI resource. + * If so, re-use this resource so that we share it among all VFs. + * + * Otherwise, allocate the resource and return it. + */ +static int ice_get_vf_ctrl_res(struct ice_pf *pf, struct ice_vsi *vsi) +{ + struct ice_vf *vf; + unsigned int bkt; + int base; + + rcu_read_lock(); + ice_for_each_vf_rcu(pf, bkt, vf) { + if (vf != vsi->vf && vf->ctrl_vsi_idx != ICE_NO_VSI) { + base = pf->vsi[vf->ctrl_vsi_idx]->base_vector; + rcu_read_unlock(); + return base; + } + } + rcu_read_unlock(); + + return ice_get_res(pf, pf->irq_tracker, vsi->num_q_vectors, + ICE_RES_VF_CTRL_VEC_ID); +} + /** * ice_vsi_setup_vector_base - Set up the base vector for the given VSI * @vsi: ptr to the VSI @@ -1356,20 +1395,8 @@ static int ice_vsi_setup_vector_base(struct ice_vsi *vsi) num_q_vectors = vsi->num_q_vectors; /* reserve slots from OS requested IRQs */ - if (vsi->type == ICE_VSI_CTRL && vsi->vf_id != ICE_INVAL_VFID) { - int i; - - ice_for_each_vf(pf, i) { - struct ice_vf *vf = &pf->vf[i]; - - if (i != vsi->vf_id && vf->ctrl_vsi_idx != ICE_NO_VSI) { - base = pf->vsi[vf->ctrl_vsi_idx]->base_vector; - break; - } - } - if (i == pf->num_alloc_vfs) - base = ice_get_res(pf, pf->irq_tracker, num_q_vectors, - ICE_RES_VF_CTRL_VEC_ID); + if (vsi->type == ICE_VSI_CTRL && vsi->vf) { + base = ice_get_vf_ctrl_res(pf, vsi); } else { base = ice_get_res(pf, pf->irq_tracker, num_q_vectors, vsi->idx); @@ -1431,6 +1458,7 @@ static void ice_vsi_clear_rings(struct ice_vsi *vsi) */ static int ice_vsi_alloc_rings(struct ice_vsi *vsi) { + bool dvm_ena = ice_is_dvm_ena(&vsi->back->hw); struct ice_pf *pf = vsi->back; struct device *dev; u16 i; @@ -1452,6 +1480,10 @@ static int ice_vsi_alloc_rings(struct ice_vsi *vsi) ring->tx_tstamps = &pf->ptp.port.tx; ring->dev = dev; ring->count = vsi->num_tx_desc; + if (dvm_ena) + ring->flags |= ICE_TX_FLAGS_RING_VLAN_L2TAG2; + else + ring->flags |= ICE_TX_FLAGS_RING_VLAN_L2TAG1; WRITE_ONCE(vsi->tx_rings[i], ring); } @@ -1762,62 +1794,6 @@ void ice_update_eth_stats(struct ice_vsi *vsi) vsi->stat_offsets_loaded = true; } -/** - * ice_vsi_add_vlan - Add VSI membership for given VLAN - * @vsi: the VSI being configured - * @vid: VLAN ID to be added - * @action: filter action to be performed on match - */ -int -ice_vsi_add_vlan(struct ice_vsi *vsi, u16 vid, enum ice_sw_fwd_act_type action) -{ - struct ice_pf *pf = vsi->back; - struct device *dev; - int err = 0; - - dev = ice_pf_to_dev(pf); - - if (!ice_fltr_add_vlan(vsi, vid, action)) { - vsi->num_vlan++; - } else { - err = -ENODEV; - dev_err(dev, "Failure Adding VLAN %d on VSI %i\n", vid, - vsi->vsi_num); - } - - return err; -} - -/** - * ice_vsi_kill_vlan - Remove VSI membership for a given VLAN - * @vsi: the VSI being configured - * @vid: VLAN ID to be removed - * - * Returns 0 on success and negative on failure - */ -int ice_vsi_kill_vlan(struct ice_vsi *vsi, u16 vid) -{ - struct ice_pf *pf = vsi->back; - struct device *dev; - int err; - - dev = ice_pf_to_dev(pf); - - err = ice_fltr_remove_vlan(vsi, vid, ICE_FWD_TO_VSI); - if (!err) { - vsi->num_vlan--; - } else if (err == -ENOENT) { - dev_dbg(dev, "Failed to remove VLAN %d on VSI %i, it does not exist, error: %d\n", - vid, vsi->vsi_num, err); - err = 0; - } else { - dev_err(dev, "Error removing VLAN %d on vsi %i error: %d\n", - vid, vsi->vsi_num, err); - } - - return err; -} - /** * ice_vsi_cfg_frame_size - setup max frame size and Rx buffer length * @vsi: VSI @@ -2145,95 +2121,6 @@ void ice_vsi_cfg_msix(struct ice_vsi *vsi) } } -/** - * ice_vsi_manage_vlan_insertion - Manage VLAN insertion for the VSI for Tx - * @vsi: the VSI being changed - */ -int ice_vsi_manage_vlan_insertion(struct ice_vsi *vsi) -{ - struct ice_hw *hw = &vsi->back->hw; - struct ice_vsi_ctx *ctxt; - int ret; - - ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); - if (!ctxt) - return -ENOMEM; - - /* Here we are configuring the VSI to let the driver add VLAN tags by - * setting vlan_flags to ICE_AQ_VSI_VLAN_MODE_ALL. The actual VLAN tag - * insertion happens in the Tx hot path, in ice_tx_map. - */ - ctxt->info.vlan_flags = ICE_AQ_VSI_VLAN_MODE_ALL; - - /* Preserve existing VLAN strip setting */ - ctxt->info.vlan_flags |= (vsi->info.vlan_flags & - ICE_AQ_VSI_VLAN_EMOD_M); - - ctxt->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_VLAN_VALID); - - ret = ice_update_vsi(hw, vsi->idx, ctxt, NULL); - if (ret) { - dev_err(ice_pf_to_dev(vsi->back), "update VSI for VLAN insert failed, err %d aq_err %s\n", - ret, ice_aq_str(hw->adminq.sq_last_status)); - goto out; - } - - vsi->info.vlan_flags = ctxt->info.vlan_flags; -out: - kfree(ctxt); - return ret; -} - -/** - * ice_vsi_manage_vlan_stripping - Manage VLAN stripping for the VSI for Rx - * @vsi: the VSI being changed - * @ena: boolean value indicating if this is a enable or disable request - */ -int ice_vsi_manage_vlan_stripping(struct ice_vsi *vsi, bool ena) -{ - struct ice_hw *hw = &vsi->back->hw; - struct ice_vsi_ctx *ctxt; - int ret; - - /* do not allow modifying VLAN stripping when a port VLAN is configured - * on this VSI - */ - if (vsi->info.pvid) - return 0; - - ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); - if (!ctxt) - return -ENOMEM; - - /* Here we are configuring what the VSI should do with the VLAN tag in - * the Rx packet. We can either leave the tag in the packet or put it in - * the Rx descriptor. - */ - if (ena) - /* Strip VLAN tag from Rx packet and put it in the desc */ - ctxt->info.vlan_flags = ICE_AQ_VSI_VLAN_EMOD_STR_BOTH; - else - /* Disable stripping. Leave tag in packet */ - ctxt->info.vlan_flags = ICE_AQ_VSI_VLAN_EMOD_NOTHING; - - /* Allow all packets untagged/tagged */ - ctxt->info.vlan_flags |= ICE_AQ_VSI_VLAN_MODE_ALL; - - ctxt->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_VLAN_VALID); - - ret = ice_update_vsi(hw, vsi->idx, ctxt, NULL); - if (ret) { - dev_err(ice_pf_to_dev(vsi->back), "update VSI for VLAN strip failed, ena = %d err %d aq_err %s\n", - ena, ret, ice_aq_str(hw->adminq.sq_last_status)); - goto out; - } - - vsi->info.vlan_flags = ctxt->info.vlan_flags; -out: - kfree(ctxt); - return ret; -} - /** * ice_vsi_start_all_rx_rings - start/enable all of a VSI's Rx rings * @vsi: the VSI whose rings are to be enabled @@ -2327,61 +2214,6 @@ bool ice_vsi_is_vlan_pruning_ena(struct ice_vsi *vsi) return (vsi->info.sw_flags2 & ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA); } -/** - * ice_cfg_vlan_pruning - enable or disable VLAN pruning on the VSI - * @vsi: VSI to enable or disable VLAN pruning on - * @ena: set to true to enable VLAN pruning and false to disable it - * - * returns 0 if VSI is updated, negative otherwise - */ -int ice_cfg_vlan_pruning(struct ice_vsi *vsi, bool ena) -{ - struct ice_vsi_ctx *ctxt; - struct ice_pf *pf; - int status; - - if (!vsi) - return -EINVAL; - - /* Don't enable VLAN pruning if the netdev is currently in promiscuous - * mode. VLAN pruning will be enabled when the interface exits - * promiscuous mode if any VLAN filters are active. - */ - if (vsi->netdev && vsi->netdev->flags & IFF_PROMISC && ena) - return 0; - - pf = vsi->back; - ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); - if (!ctxt) - return -ENOMEM; - - ctxt->info = vsi->info; - - if (ena) - ctxt->info.sw_flags2 |= ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA; - else - ctxt->info.sw_flags2 &= ~ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA; - - ctxt->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_SW_VALID); - - status = ice_update_vsi(&pf->hw, vsi->idx, ctxt, NULL); - if (status) { - netdev_err(vsi->netdev, "%sabling VLAN pruning on VSI handle: %d, VSI HW ID: %d failed, err = %d, aq_err = %s\n", - ena ? "En" : "Dis", vsi->idx, vsi->vsi_num, - status, ice_aq_str(pf->hw.adminq.sq_last_status)); - goto err_out; - } - - vsi->info.sw_flags2 = ctxt->info.sw_flags2; - - kfree(ctxt); - return 0; - -err_out: - kfree(ctxt); - return -EIO; -} - static void ice_vsi_set_tc_cfg(struct ice_vsi *vsi) { if (!test_bit(ICE_FLAG_DCB_ENA, vsi->back->flags)) { @@ -2416,7 +2248,7 @@ ice_vsi_set_q_vectors_reg_idx(struct ice_vsi *vsi) } if (vsi->type == ICE_VSI_VF) { - struct ice_vf *vf = &vsi->back->vf[vsi->vf_id]; + struct ice_vf *vf = vsi->vf; q_vector->reg_idx = ice_calc_vf_reg_idx(vf, q_vector); } else { @@ -2601,9 +2433,8 @@ static void ice_set_agg_vsi(struct ice_vsi *vsi) * @pf: board private structure * @pi: pointer to the port_info instance * @vsi_type: VSI type - * @vf_id: defines VF ID to which this VSI connects. This field is meant to be - * used only for ICE_VSI_VF VSI type. For other VSI types, should - * fill-in ICE_INVAL_VFID as input. + * @vf: pointer to VF to which this VSI connects. This field is used primarily + * for the ICE_VSI_VF type. Other VSI types should pass NULL. * @ch: ptr to channel * * This allocates the sw VSI structure and its queue resources. @@ -2613,7 +2444,8 @@ static void ice_set_agg_vsi(struct ice_vsi *vsi) */ struct ice_vsi * ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi, - enum ice_vsi_type vsi_type, u16 vf_id, struct ice_channel *ch) + enum ice_vsi_type vsi_type, struct ice_vf *vf, + struct ice_channel *ch) { u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 }; struct device *dev = ice_pf_to_dev(pf); @@ -2621,11 +2453,11 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi, int ret, i; if (vsi_type == ICE_VSI_CHNL) - vsi = ice_vsi_alloc(pf, vsi_type, ch, ICE_INVAL_VFID); + vsi = ice_vsi_alloc(pf, vsi_type, ch, NULL); else if (vsi_type == ICE_VSI_VF || vsi_type == ICE_VSI_CTRL) - vsi = ice_vsi_alloc(pf, vsi_type, NULL, vf_id); + vsi = ice_vsi_alloc(pf, vsi_type, NULL, vf); else - vsi = ice_vsi_alloc(pf, vsi_type, NULL, ICE_INVAL_VFID); + vsi = ice_vsi_alloc(pf, vsi_type, NULL, NULL); if (!vsi) { dev_err(dev, "could not allocate VSI\n"); @@ -2637,9 +2469,6 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi, if (vsi->type == ICE_VSI_PF) vsi->ethtype = ETH_P_PAUSE; - if (vsi->type == ICE_VSI_VF || vsi->type == ICE_VSI_CTRL) - vsi->vf_id = vf_id; - ice_alloc_fd_res(vsi); if (vsi_type != ICE_VSI_CHNL) { @@ -2661,6 +2490,8 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi, if (ret) goto unroll_get_qs; + ice_vsi_init_vlan_ops(vsi); + switch (vsi->type) { case ICE_VSI_CTRL: case ICE_VSI_SWITCHDEV_CTRL: @@ -2681,17 +2512,6 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi, if (ret) goto unroll_vector_base; - /* Always add VLAN ID 0 switch rule by default. This is needed - * in order to allow all untagged and 0 tagged priority traffic - * if Rx VLAN pruning is enabled. Also there are cases where we - * don't get the call to add VLAN 0 via ice_vlan_rx_add_vid() - * so this handles those cases (i.e. adding the PF to a bridge - * without the 8021q module loaded). - */ - ret = ice_vsi_add_vlan(vsi, 0, ICE_FWD_TO_VSI); - if (ret) - goto unroll_clear_rings; - ice_vsi_map_rings_to_vectors(vsi); /* ICE_VSI_CTRL does not need RSS so skip RSS processing */ @@ -3068,6 +2888,37 @@ void ice_napi_del(struct ice_vsi *vsi) netif_napi_del(&vsi->q_vectors[v_idx]->napi); } +/** + * ice_free_vf_ctrl_res - Free the VF control VSI resource + * @pf: pointer to PF structure + * @vsi: the VSI to free resources for + * + * Check if the VF control VSI resource is still in use. If no VF is using it + * any more, release the VSI resource. Otherwise, leave it to be cleaned up + * once no other VF uses it. + */ +static void ice_free_vf_ctrl_res(struct ice_pf *pf, struct ice_vsi *vsi) +{ + struct ice_vf *vf; + unsigned int bkt; + + rcu_read_lock(); + ice_for_each_vf_rcu(pf, bkt, vf) { + if (vf != vsi->vf && vf->ctrl_vsi_idx != ICE_NO_VSI) { + rcu_read_unlock(); + return; + } + } + rcu_read_unlock(); + + /* No other VFs left that have control VSI. It is now safe to reclaim + * SW interrupts back to the common pool. + */ + ice_free_res(pf->irq_tracker, vsi->base_vector, + ICE_RES_VF_CTRL_VEC_ID); + pf->num_avail_sw_msix += vsi->num_q_vectors; +} + /** * ice_vsi_release - Delete a VSI and free its resources * @vsi: the VSI being removed @@ -3111,23 +2962,8 @@ int ice_vsi_release(struct ice_vsi *vsi) * many interrupts each VF needs. SR-IOV MSIX resources are also * cleared in the same manner. */ - if (vsi->type == ICE_VSI_CTRL && vsi->vf_id != ICE_INVAL_VFID) { - int i; - - ice_for_each_vf(pf, i) { - struct ice_vf *vf = &pf->vf[i]; - - if (i != vsi->vf_id && vf->ctrl_vsi_idx != ICE_NO_VSI) - break; - } - if (i == pf->num_alloc_vfs) { - /* No other VFs left that have control VSI, reclaim SW - * interrupts back to the common pool - */ - ice_free_res(pf->irq_tracker, vsi->base_vector, - ICE_RES_VF_CTRL_VEC_ID); - pf->num_avail_sw_msix += vsi->num_q_vectors; - } + if (vsi->type == ICE_VSI_CTRL && vsi->vf) { + ice_free_vf_ctrl_res(pf, vsi); } else if (vsi->type != ICE_VSI_VF) { /* reclaim SW interrupts back to the common pool */ ice_free_res(pf->irq_tracker, vsi->base_vector, vsi->idx); @@ -3311,7 +3147,6 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, bool init_vsi) u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 }; struct ice_coalesce_stored *coalesce; int prev_num_q_vectors = 0; - struct ice_vf *vf = NULL; enum ice_vsi_type vtype; struct ice_pf *pf; int ret, i; @@ -3321,8 +3156,10 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, bool init_vsi) pf = vsi->back; vtype = vsi->type; - if (vtype == ICE_VSI_VF) - vf = &pf->vf[vsi->vf_id]; + if (WARN_ON(vtype == ICE_VSI_VF) && !vsi->vf) + return -EINVAL; + + ice_vsi_init_vlan_ops(vsi); coalesce = kcalloc(vsi->num_q_vectors, sizeof(struct ice_coalesce_stored), GFP_KERNEL); @@ -3359,9 +3196,9 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, bool init_vsi) ice_vsi_clear_rings(vsi); ice_vsi_free_arrays(vsi); if (vtype == ICE_VSI_VF) - ice_vsi_set_num_qs(vsi, vf->vf_id); + ice_vsi_set_num_qs(vsi, vsi->vf); else - ice_vsi_set_num_qs(vsi, ICE_INVAL_VFID); + ice_vsi_set_num_qs(vsi, NULL); ret = ice_vsi_alloc_arrays(vsi); if (ret < 0) @@ -4123,9 +3960,9 @@ int ice_set_link(struct ice_vsi *vsi, bool ena) */ if (status == -EIO) { if (hw->adminq.sq_last_status == ICE_AQ_RC_EMODE) - dev_warn(dev, "can't set link to %s, err %d aq_err %s. not fatal, continuing\n", - (ena ? "ON" : "OFF"), status, - ice_aq_str(hw->adminq.sq_last_status)); + dev_dbg(dev, "can't set link to %s, err %d aq_err %s. not fatal, continuing\n", + (ena ? "ON" : "OFF"), status, + ice_aq_str(hw->adminq.sq_last_status)); } else if (status) { dev_err(dev, "can't set link to %s, err %d aq_err %s\n", (ena ? "ON" : "OFF"), status, @@ -4136,6 +3973,120 @@ int ice_set_link(struct ice_vsi *vsi, bool ena) return 0; } +/** + * ice_vsi_add_vlan_zero - add VLAN 0 filter(s) for this VSI + * @vsi: VSI used to add VLAN filters + * + * In Single VLAN Mode (SVM), single VLAN filters via ICE_SW_LKUP_VLAN are based + * on the inner VLAN ID, so the VLAN TPID (i.e. 0x8100 or 0x888a8) doesn't + * matter. In Double VLAN Mode (DVM), outer/single VLAN filters via + * ICE_SW_LKUP_VLAN are based on the outer/single VLAN ID + VLAN TPID. + * + * For both modes add a VLAN 0 + no VLAN TPID filter to handle untagged traffic + * when VLAN pruning is enabled. Also, this handles VLAN 0 priority tagged + * traffic in SVM, since the VLAN TPID isn't part of filtering. + * + * If DVM is enabled then an explicit VLAN 0 + VLAN TPID filter needs to be + * added to allow VLAN 0 priority tagged traffic in DVM, since the VLAN TPID is + * part of filtering. + */ +int ice_vsi_add_vlan_zero(struct ice_vsi *vsi) +{ + struct ice_vsi_vlan_ops *vlan_ops = ice_get_compat_vsi_vlan_ops(vsi); + struct ice_vlan vlan; + int err; + + vlan = ICE_VLAN(0, 0, 0); + err = vlan_ops->add_vlan(vsi, &vlan); + if (err && err != -EEXIST) + return err; + + /* in SVM both VLAN 0 filters are identical */ + if (!ice_is_dvm_ena(&vsi->back->hw)) + return 0; + + vlan = ICE_VLAN(ETH_P_8021Q, 0, 0); + err = vlan_ops->add_vlan(vsi, &vlan); + if (err && err != -EEXIST) + return err; + + return 0; +} + +/** + * ice_vsi_del_vlan_zero - delete VLAN 0 filter(s) for this VSI + * @vsi: VSI used to add VLAN filters + * + * Delete the VLAN 0 filters in the same manner that they were added in + * ice_vsi_add_vlan_zero. + */ +int ice_vsi_del_vlan_zero(struct ice_vsi *vsi) +{ + struct ice_vsi_vlan_ops *vlan_ops = ice_get_compat_vsi_vlan_ops(vsi); + struct ice_vlan vlan; + int err; + + vlan = ICE_VLAN(0, 0, 0); + err = vlan_ops->del_vlan(vsi, &vlan); + if (err && err != -EEXIST) + return err; + + /* in SVM both VLAN 0 filters are identical */ + if (!ice_is_dvm_ena(&vsi->back->hw)) + return 0; + + vlan = ICE_VLAN(ETH_P_8021Q, 0, 0); + err = vlan_ops->del_vlan(vsi, &vlan); + if (err && err != -EEXIST) + return err; + + return 0; +} + +/** + * ice_vsi_num_zero_vlans - get number of VLAN 0 filters based on VLAN mode + * @vsi: VSI used to get the VLAN mode + * + * If DVM is enabled then 2 VLAN 0 filters are added, else if SVM is enabled + * then 1 VLAN 0 filter is added. See ice_vsi_add_vlan_zero for more details. + */ +static u16 ice_vsi_num_zero_vlans(struct ice_vsi *vsi) +{ +#define ICE_DVM_NUM_ZERO_VLAN_FLTRS 2 +#define ICE_SVM_NUM_ZERO_VLAN_FLTRS 1 + /* no VLAN 0 filter is created when a port VLAN is active */ + if (vsi->type == ICE_VSI_VF) { + if (WARN_ON(!vsi->vf)) + return 0; + + if (ice_vf_is_port_vlan_ena(vsi->vf)) + return 0; + } + + if (ice_is_dvm_ena(&vsi->back->hw)) + return ICE_DVM_NUM_ZERO_VLAN_FLTRS; + else + return ICE_SVM_NUM_ZERO_VLAN_FLTRS; +} + +/** + * ice_vsi_has_non_zero_vlans - check if VSI has any non-zero VLANs + * @vsi: VSI used to determine if any non-zero VLANs have been added + */ +bool ice_vsi_has_non_zero_vlans(struct ice_vsi *vsi) +{ + return (vsi->num_vlan > ice_vsi_num_zero_vlans(vsi)); +} + +/** + * ice_vsi_num_non_zero_vlans - get the number of non-zero VLANs for this VSI + * @vsi: VSI used to get the number of non-zero VLANs added + */ +u16 ice_vsi_num_non_zero_vlans(struct ice_vsi *vsi) +{ + return (vsi->num_vlan - ice_vsi_num_zero_vlans(vsi)); +} + /** * ice_is_feature_supported * @pf: pointer to the struct ice_pf instance @@ -4190,8 +4141,11 @@ void ice_init_feature_support(struct ice_pf *pf) case ICE_DEV_ID_E810C_QSFP: case ICE_DEV_ID_E810C_SFP: ice_set_feature_support(pf, ICE_F_DSCP); - if (ice_is_e810t(&pf->hw)) + if (ice_is_e810t(&pf->hw)) { ice_set_feature_support(pf, ICE_F_SMA_CTRL); + if (ice_gnss_is_gps_present(&pf->hw)) + ice_set_feature_support(pf, ICE_F_GNSS); + } break; default: break; diff --git a/drivers/net/ethernet/intel/ice/ice_lib.h b/drivers/net/ethernet/intel/ice/ice_lib.h index b2ed189527d6..0095329949d4 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_lib.h @@ -5,6 +5,7 @@ #define _ICE_LIB_H_ #include "ice.h" +#include "ice_vlan.h" const char *ice_vsi_type_str(enum ice_vsi_type vsi_type); @@ -22,15 +23,6 @@ int ice_vsi_cfg_lan_txqs(struct ice_vsi *vsi); void ice_vsi_cfg_msix(struct ice_vsi *vsi); -int -ice_vsi_add_vlan(struct ice_vsi *vsi, u16 vid, enum ice_sw_fwd_act_type action); - -int ice_vsi_kill_vlan(struct ice_vsi *vsi, u16 vid); - -int ice_vsi_manage_vlan_insertion(struct ice_vsi *vsi); - -int ice_vsi_manage_vlan_stripping(struct ice_vsi *vsi, bool ena); - int ice_vsi_start_all_rx_rings(struct ice_vsi *vsi); int ice_vsi_stop_all_rx_rings(struct ice_vsi *vsi); @@ -45,8 +37,6 @@ int ice_vsi_stop_xdp_tx_rings(struct ice_vsi *vsi); bool ice_vsi_is_vlan_pruning_ena(struct ice_vsi *vsi); -int ice_cfg_vlan_pruning(struct ice_vsi *vsi, bool ena); - void ice_cfg_sw_lldp(struct ice_vsi *vsi, bool tx, bool create); int ice_set_link(struct ice_vsi *vsi, bool ena); @@ -62,7 +52,8 @@ void ice_vsi_cfg_netdev_tc(struct ice_vsi *vsi, u8 ena_tc); struct ice_vsi * ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi, - enum ice_vsi_type vsi_type, u16 vf_id, struct ice_channel *ch); + enum ice_vsi_type vsi_type, struct ice_vf *vf, + struct ice_channel *ch); void ice_napi_del(struct ice_vsi *vsi); @@ -110,7 +101,7 @@ void ice_set_q_vector_intrl(struct ice_q_vector *q_vector); int ice_vsi_cfg_mac_fltr(struct ice_vsi *vsi, const u8 *macaddr, bool set); bool ice_is_safe_mode(struct ice_pf *pf); -bool ice_is_aux_ena(struct ice_pf *pf); +bool ice_is_rdma_ena(struct ice_pf *pf); bool ice_is_dflt_vsi_in_use(struct ice_sw *sw); bool ice_is_vsi_dflt_vsi(struct ice_sw *sw, struct ice_vsi *vsi); @@ -132,7 +123,10 @@ void ice_vsi_ctx_clear_antispoof(struct ice_vsi_ctx *ctx); void ice_vsi_ctx_set_allow_override(struct ice_vsi_ctx *ctx); void ice_vsi_ctx_clear_allow_override(struct ice_vsi_ctx *ctx); - +int ice_vsi_add_vlan_zero(struct ice_vsi *vsi); +int ice_vsi_del_vlan_zero(struct ice_vsi *vsi); +bool ice_vsi_has_non_zero_vlans(struct ice_vsi *vsi); +u16 ice_vsi_num_non_zero_vlans(struct ice_vsi *vsi); bool ice_is_feature_supported(struct ice_pf *pf, enum ice_feature f); void ice_clear_feature_support(struct ice_pf *pf, enum ice_feature f); void ice_init_feature_support(struct ice_pf *pf); diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index b7e8744b0c0a..b588d7995631 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -21,6 +21,7 @@ #include "ice_trace.h" #include "ice_eswitch.h" #include "ice_tc_lib.h" +#include "ice_vsi_vlan_ops.h" #define DRV_SUMMARY "Intel(R) Ethernet Connection E800 Series Linux Driver" static const char ice_driver_string[] = DRV_SUMMARY; @@ -47,6 +48,21 @@ static DEFINE_IDA(ice_aux_ida); DEFINE_STATIC_KEY_FALSE(ice_xdp_locking_key); EXPORT_SYMBOL(ice_xdp_locking_key); +/** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure + * + * Used to access the device pointer from compilation units which can't easily + * include the definition of struct ice_pf without leading to circular header + * dependencies. + */ +struct device *ice_hw_to_dev(struct ice_hw *hw) +{ + struct ice_pf *pf = container_of(hw, struct ice_pf, hw); + + return &pf->pdev->dev; +} + static struct workqueue_struct *ice_wq; static const struct net_device_ops ice_netdev_safe_mode_ops; static const struct net_device_ops ice_netdev_ops; @@ -244,7 +260,7 @@ static int ice_set_promisc(struct ice_vsi *vsi, u8 promisc_m) if (vsi->type != ICE_VSI_PF) return 0; - if (vsi->num_vlan > 1) + if (ice_vsi_has_non_zero_vlans(vsi)) status = ice_fltr_set_vlan_vsi_promisc(&vsi->back->hw, vsi, promisc_m); else status = ice_fltr_set_vsi_promisc(&vsi->back->hw, vsi->idx, promisc_m, 0); @@ -264,7 +280,7 @@ static int ice_clear_promisc(struct ice_vsi *vsi, u8 promisc_m) if (vsi->type != ICE_VSI_PF) return 0; - if (vsi->num_vlan > 1) + if (ice_vsi_has_non_zero_vlans(vsi)) status = ice_fltr_clear_vlan_vsi_promisc(&vsi->back->hw, vsi, promisc_m); else status = ice_fltr_clear_vsi_promisc(&vsi->back->hw, vsi->idx, promisc_m, 0); @@ -279,6 +295,7 @@ static int ice_clear_promisc(struct ice_vsi *vsi, u8 promisc_m) */ static int ice_vsi_sync_fltr(struct ice_vsi *vsi) { + struct ice_vsi_vlan_ops *vlan_ops = ice_get_compat_vsi_vlan_ops(vsi); struct device *dev = ice_pf_to_dev(vsi->back); struct net_device *netdev = vsi->netdev; bool promisc_forced_on = false; @@ -352,7 +369,7 @@ static int ice_vsi_sync_fltr(struct ice_vsi *vsi) /* check for changes in promiscuous modes */ if (changed_flags & IFF_ALLMULTI) { if (vsi->current_netdev_flags & IFF_ALLMULTI) { - if (vsi->num_vlan > 1) + if (ice_vsi_has_non_zero_vlans(vsi)) promisc_m = ICE_MCAST_VLAN_PROMISC_BITS; else promisc_m = ICE_MCAST_PROMISC_BITS; @@ -366,7 +383,7 @@ static int ice_vsi_sync_fltr(struct ice_vsi *vsi) } } else { /* !(vsi->current_netdev_flags & IFF_ALLMULTI) */ - if (vsi->num_vlan > 1) + if (ice_vsi_has_non_zero_vlans(vsi)) promisc_m = ICE_MCAST_VLAN_PROMISC_BITS; else promisc_m = ICE_MCAST_PROMISC_BITS; @@ -396,7 +413,7 @@ static int ice_vsi_sync_fltr(struct ice_vsi *vsi) goto out_promisc; } err = 0; - ice_cfg_vlan_pruning(vsi, false); + vlan_ops->dis_rx_filtering(vsi); } } else { /* Clear Rx filter to remove traffic from wire */ @@ -409,8 +426,9 @@ static int ice_vsi_sync_fltr(struct ice_vsi *vsi) IFF_PROMISC; goto out_promisc; } - if (vsi->num_vlan > 1) - ice_cfg_vlan_pruning(vsi, true); + if (vsi->current_netdev_flags & + NETIF_F_HW_VLAN_CTAG_FILTER) + vlan_ops->ena_rx_filtering(vsi); } } } @@ -502,7 +520,8 @@ ice_prepare_for_reset(struct ice_pf *pf, enum ice_reset_req reset_type) { struct ice_hw *hw = &pf->hw; struct ice_vsi *vsi; - unsigned int i; + struct ice_vf *vf; + unsigned int bkt; dev_dbg(ice_pf_to_dev(pf), "reset_type=%d\n", reset_type); @@ -517,8 +536,10 @@ ice_prepare_for_reset(struct ice_pf *pf, enum ice_reset_req reset_type) ice_vc_notify_reset(pf); /* Disable VFs until reset is completed */ - ice_for_each_vf(pf, i) - ice_set_vf_state_qs_dis(&pf->vf[i]); + mutex_lock(&pf->vfs.table_lock); + ice_for_each_vf(pf, bkt, vf) + ice_set_vf_state_qs_dis(vf); + mutex_unlock(&pf->vfs.table_lock); if (ice_is_eswitch_mode_switchdev(pf)) { if (reset_type != ICE_RESET_PFR) @@ -565,6 +586,9 @@ ice_prepare_for_reset(struct ice_pf *pf, enum ice_reset_req reset_type) if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) ice_ptp_prepare_for_reset(pf); + if (ice_is_feature_supported(pf, ICE_F_GNSS)) + ice_gnss_exit(pf); + if (hw->port_info) ice_sched_clear_port(hw->port_info); @@ -610,7 +634,7 @@ static void ice_do_reset(struct ice_pf *pf, enum ice_reset_req reset_type) clear_bit(ICE_PREPARED_FOR_RESET, pf->state); clear_bit(ICE_PFR_REQ, pf->state); wake_up(&pf->reset_wait_queue); - ice_reset_all_vfs(pf, true); + ice_reset_all_vfs(pf); } } @@ -661,7 +685,7 @@ static void ice_reset_subtask(struct ice_pf *pf) clear_bit(ICE_CORER_REQ, pf->state); clear_bit(ICE_GLOBR_REQ, pf->state); wake_up(&pf->reset_wait_queue); - ice_reset_all_vfs(pf, true); + ice_reset_all_vfs(pf); } return; @@ -1660,7 +1684,8 @@ static void ice_handle_mdd_event(struct ice_pf *pf) { struct device *dev = ice_pf_to_dev(pf); struct ice_hw *hw = &pf->hw; - unsigned int i; + struct ice_vf *vf; + unsigned int bkt; u32 reg; if (!test_and_clear_bit(ICE_MDD_EVENT_PENDING, pf->state)) { @@ -1748,47 +1773,46 @@ static void ice_handle_mdd_event(struct ice_pf *pf) /* Check to see if one of the VFs caused an MDD event, and then * increment counters and set print pending */ - ice_for_each_vf(pf, i) { - struct ice_vf *vf = &pf->vf[i]; - - reg = rd32(hw, VP_MDET_TX_PQM(i)); + mutex_lock(&pf->vfs.table_lock); + ice_for_each_vf(pf, bkt, vf) { + reg = rd32(hw, VP_MDET_TX_PQM(vf->vf_id)); if (reg & VP_MDET_TX_PQM_VALID_M) { - wr32(hw, VP_MDET_TX_PQM(i), 0xFFFF); + wr32(hw, VP_MDET_TX_PQM(vf->vf_id), 0xFFFF); vf->mdd_tx_events.count++; set_bit(ICE_MDD_VF_PRINT_PENDING, pf->state); if (netif_msg_tx_err(pf)) dev_info(dev, "Malicious Driver Detection event TX_PQM detected on VF %d\n", - i); + vf->vf_id); } - reg = rd32(hw, VP_MDET_TX_TCLAN(i)); + reg = rd32(hw, VP_MDET_TX_TCLAN(vf->vf_id)); if (reg & VP_MDET_TX_TCLAN_VALID_M) { - wr32(hw, VP_MDET_TX_TCLAN(i), 0xFFFF); + wr32(hw, VP_MDET_TX_TCLAN(vf->vf_id), 0xFFFF); vf->mdd_tx_events.count++; set_bit(ICE_MDD_VF_PRINT_PENDING, pf->state); if (netif_msg_tx_err(pf)) dev_info(dev, "Malicious Driver Detection event TX_TCLAN detected on VF %d\n", - i); + vf->vf_id); } - reg = rd32(hw, VP_MDET_TX_TDPU(i)); + reg = rd32(hw, VP_MDET_TX_TDPU(vf->vf_id)); if (reg & VP_MDET_TX_TDPU_VALID_M) { - wr32(hw, VP_MDET_TX_TDPU(i), 0xFFFF); + wr32(hw, VP_MDET_TX_TDPU(vf->vf_id), 0xFFFF); vf->mdd_tx_events.count++; set_bit(ICE_MDD_VF_PRINT_PENDING, pf->state); if (netif_msg_tx_err(pf)) dev_info(dev, "Malicious Driver Detection event TX_TDPU detected on VF %d\n", - i); + vf->vf_id); } - reg = rd32(hw, VP_MDET_RX(i)); + reg = rd32(hw, VP_MDET_RX(vf->vf_id)); if (reg & VP_MDET_RX_VALID_M) { - wr32(hw, VP_MDET_RX(i), 0xFFFF); + wr32(hw, VP_MDET_RX(vf->vf_id), 0xFFFF); vf->mdd_rx_events.count++; set_bit(ICE_MDD_VF_PRINT_PENDING, pf->state); if (netif_msg_rx_err(pf)) dev_info(dev, "Malicious Driver Detection event RX detected on VF %d\n", - i); + vf->vf_id); /* Since the queue is disabled on VF Rx MDD events, the * PF can be configured to reset the VF through ethtool @@ -1799,12 +1823,11 @@ static void ice_handle_mdd_event(struct ice_pf *pf) * reset, so print the event prior to reset. */ ice_print_vf_rx_mdd_event(vf); - mutex_lock(&pf->vf[i].cfg_lock); - ice_reset_vf(&pf->vf[i], false); - mutex_unlock(&pf->vf[i].cfg_lock); + ice_reset_vf(vf, ICE_VF_RESET_LOCK); } } } + mutex_unlock(&pf->vfs.table_lock); ice_print_vfs_mdd_events(pf); } @@ -2255,6 +2278,19 @@ static void ice_service_task(struct work_struct *work) return; } + if (test_and_clear_bit(ICE_AUX_ERR_PENDING, pf->state)) { + struct iidc_event *event; + + event = kzalloc(sizeof(*event), GFP_KERNEL); + if (event) { + set_bit(IIDC_EVENT_CRIT_ERR, event->type); + /* report the entire OICR value to AUX driver */ + swap(event->reg, pf->oicr_err_reg); + ice_send_event_to_aux(pf, event); + kfree(event); + } + } + if (test_bit(ICE_FLAG_PLUG_AUX_DEV, pf->flags)) { /* Plug aux device per request */ ice_plug_aux_dev(pf); @@ -2454,7 +2490,7 @@ static int ice_vsi_req_irq_msix(struct ice_vsi *vsi, char *basename) /* skip this unused q_vector */ continue; } - if (vsi->type == ICE_VSI_CTRL && vsi->vf_id != ICE_INVAL_VFID) + if (vsi->type == ICE_VSI_CTRL && vsi->vf) err = devm_request_irq(dev, irq_num, vsi->irq_handler, IRQF_SHARED, q_vector->name, q_vector); @@ -2521,10 +2557,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; @@ -3041,17 +3077,9 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data) #define ICE_AUX_CRIT_ERR (PFINT_OICR_PE_CRITERR_M | PFINT_OICR_HMC_ERR_M | PFINT_OICR_PE_PUSH_M) if (oicr & ICE_AUX_CRIT_ERR) { - struct iidc_event *event; - + pf->oicr_err_reg |= oicr; + set_bit(ICE_AUX_ERR_PENDING, pf->state); ena_mask &= ~ICE_AUX_CRIT_ERR; - event = kzalloc(sizeof(*event), GFP_ATOMIC); - if (event) { - set_bit(IIDC_EVENT_CRIT_ERR, event->type); - /* report the entire OICR value to AUX driver */ - event->reg = oicr; - ice_send_event_to_aux(pf, event); - kfree(event); - } } /* Report any remaining unexpected interrupts */ @@ -3256,6 +3284,7 @@ static void ice_set_ops(struct net_device *netdev) static void ice_set_netdev_features(struct net_device *netdev) { struct ice_pf *pf = ice_netdev_to_pf(netdev); + bool is_dvm_ena = ice_is_dvm_ena(&pf->hw); netdev_features_t csumo_features; netdev_features_t vlano_features; netdev_features_t dflt_features; @@ -3282,6 +3311,10 @@ static void ice_set_netdev_features(struct net_device *netdev) NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; + /* Enable CTAG/STAG filtering by default in Double VLAN Mode (DVM) */ + if (is_dvm_ena) + vlano_features |= NETIF_F_HW_VLAN_STAG_FILTER; + tso_features = NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 | @@ -3313,6 +3346,15 @@ static void ice_set_netdev_features(struct net_device *netdev) tso_features; netdev->vlan_features |= dflt_features | csumo_features | tso_features; + + /* advertise support but don't enable by default since only one type of + * VLAN offload can be enabled at a time (i.e. CTAG or STAG). When one + * type turns on the other has to be turned off. This is enforced by the + * ice_fix_features() ndo callback. + */ + if (is_dvm_ena) + netdev->hw_features |= NETIF_F_HW_VLAN_STAG_RX | + NETIF_F_HW_VLAN_STAG_TX; } /** @@ -3387,14 +3429,14 @@ void ice_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size) static struct ice_vsi * ice_pf_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi) { - return ice_vsi_setup(pf, pi, ICE_VSI_PF, ICE_INVAL_VFID, NULL); + return ice_vsi_setup(pf, pi, ICE_VSI_PF, NULL, NULL); } static struct ice_vsi * ice_chnl_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi, struct ice_channel *ch) { - return ice_vsi_setup(pf, pi, ICE_VSI_CHNL, ICE_INVAL_VFID, ch); + return ice_vsi_setup(pf, pi, ICE_VSI_CHNL, NULL, ch); } /** @@ -3408,7 +3450,7 @@ ice_chnl_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi, static struct ice_vsi * ice_ctrl_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi) { - return ice_vsi_setup(pf, pi, ICE_VSI_CTRL, ICE_INVAL_VFID, NULL); + return ice_vsi_setup(pf, pi, ICE_VSI_CTRL, NULL, NULL); } /** @@ -3422,40 +3464,37 @@ ice_ctrl_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi) struct ice_vsi * ice_lb_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi) { - return ice_vsi_setup(pf, pi, ICE_VSI_LB, ICE_INVAL_VFID, NULL); + return ice_vsi_setup(pf, pi, ICE_VSI_LB, NULL, NULL); } /** * ice_vlan_rx_add_vid - Add a VLAN ID filter to HW offload * @netdev: network interface to be adjusted - * @proto: unused protocol + * @proto: VLAN TPID * @vid: VLAN ID to be added * * net_device_ops implementation for adding VLAN IDs */ static int -ice_vlan_rx_add_vid(struct net_device *netdev, __always_unused __be16 proto, - u16 vid) +ice_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid) { struct ice_netdev_priv *np = netdev_priv(netdev); + struct ice_vsi_vlan_ops *vlan_ops; struct ice_vsi *vsi = np->vsi; + struct ice_vlan vlan; int ret; /* VLAN 0 is added by default during load/reset */ if (!vid) return 0; - /* Enable VLAN pruning when a VLAN other than 0 is added */ - if (!ice_vsi_is_vlan_pruning_ena(vsi)) { - ret = ice_cfg_vlan_pruning(vsi, true); - if (ret) - return ret; - } + vlan_ops = ice_get_compat_vsi_vlan_ops(vsi); /* Add a switch rule for this VLAN ID so its corresponding VLAN tagged * packets aren't pruned by the device's internal switch on Rx */ - ret = ice_vsi_add_vlan(vsi, vid, ICE_FWD_TO_VSI); + vlan = ICE_VLAN(be16_to_cpu(proto), vid, 0); + ret = vlan_ops->add_vlan(vsi, &vlan); if (!ret) set_bit(ICE_VSI_VLAN_FLTR_CHANGED, vsi->state); @@ -3465,36 +3504,36 @@ ice_vlan_rx_add_vid(struct net_device *netdev, __always_unused __be16 proto, /** * ice_vlan_rx_kill_vid - Remove a VLAN ID filter from HW offload * @netdev: network interface to be adjusted - * @proto: unused protocol + * @proto: VLAN TPID * @vid: VLAN ID to be removed * * net_device_ops implementation for removing VLAN IDs */ static int -ice_vlan_rx_kill_vid(struct net_device *netdev, __always_unused __be16 proto, - u16 vid) +ice_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid) { struct ice_netdev_priv *np = netdev_priv(netdev); + struct ice_vsi_vlan_ops *vlan_ops; struct ice_vsi *vsi = np->vsi; + struct ice_vlan vlan; int ret; /* don't allow removal of VLAN 0 */ if (!vid) return 0; - /* Make sure ice_vsi_kill_vlan is successful before updating VLAN + vlan_ops = ice_get_compat_vsi_vlan_ops(vsi); + + /* Make sure VLAN delete is successful before updating VLAN * information */ - ret = ice_vsi_kill_vlan(vsi, vid); + vlan = ICE_VLAN(be16_to_cpu(proto), vid, 0); + ret = vlan_ops->del_vlan(vsi, &vlan); if (ret) return ret; - /* Disable pruning when VLAN 0 is the only VLAN rule */ - if (vsi->num_vlan == 1 && ice_vsi_is_vlan_pruning_ena(vsi)) - ret = ice_cfg_vlan_pruning(vsi, false); - set_bit(ICE_VSI_VLAN_FLTR_CHANGED, vsi->state); - return ret; + return 0; } /** @@ -3563,12 +3602,17 @@ static int ice_tc_indir_block_register(struct ice_vsi *vsi) static int ice_setup_pf_sw(struct ice_pf *pf) { struct device *dev = ice_pf_to_dev(pf); + bool dvm = ice_is_dvm_ena(&pf->hw); struct ice_vsi *vsi; int status; if (ice_is_reset_in_progress(pf->state)) return -EBUSY; + status = ice_aq_set_port_params(pf->hw.port_info, dvm, NULL); + if (status) + return -EIO; + vsi = ice_pf_vsi_setup(pf, pf->hw.port_info); if (!vsi) return -ENOMEM; @@ -3679,6 +3723,7 @@ static void ice_deinit_pf(struct ice_pf *pf) mutex_destroy(&pf->sw_mutex); mutex_destroy(&pf->tc_mutex); mutex_destroy(&pf->avail_q_mutex); + mutex_destroy(&pf->vfs.table_lock); if (pf->avail_txqs) { bitmap_free(pf->avail_txqs); @@ -3703,19 +3748,16 @@ static void ice_set_pf_caps(struct ice_pf *pf) struct ice_hw_func_caps *func_caps = &pf->hw.func_caps; clear_bit(ICE_FLAG_RDMA_ENA, pf->flags); - clear_bit(ICE_FLAG_AUX_ENA, pf->flags); - if (func_caps->common_cap.rdma) { + if (func_caps->common_cap.rdma) set_bit(ICE_FLAG_RDMA_ENA, pf->flags); - set_bit(ICE_FLAG_AUX_ENA, pf->flags); - } clear_bit(ICE_FLAG_DCB_CAPABLE, pf->flags); if (func_caps->common_cap.dcb) set_bit(ICE_FLAG_DCB_CAPABLE, pf->flags); clear_bit(ICE_FLAG_SRIOV_CAPABLE, pf->flags); if (func_caps->common_cap.sr_iov_1_1) { set_bit(ICE_FLAG_SRIOV_CAPABLE, pf->flags); - pf->num_vfs_supported = min_t(int, func_caps->num_allocd_vfs, - ICE_MAX_VF_COUNT); + pf->vfs.num_supported = min_t(int, func_caps->num_allocd_vfs, + ICE_MAX_SRIOV_VFS); } clear_bit(ICE_FLAG_RSS_ENA, pf->flags); if (func_caps->common_cap.rss_table_size) @@ -3781,6 +3823,9 @@ static int ice_init_pf(struct ice_pf *pf) return -ENOMEM; } + mutex_init(&pf->vfs.table_lock); + hash_init(pf->vfs.table); + return 0; } @@ -3835,7 +3880,7 @@ static int ice_ena_msix_range(struct ice_pf *pf) v_left -= needed; /* reserve vectors for RDMA auxiliary driver */ - if (test_bit(ICE_FLAG_RDMA_ENA, pf->flags)) { + if (ice_is_rdma_ena(pf)) { needed = num_cpus + ICE_RDMA_NUM_AEQ_MSIX; if (v_left < needed) goto no_hw_vecs_left_err; @@ -3876,7 +3921,7 @@ static int ice_ena_msix_range(struct ice_pf *pf) int v_remain = v_actual - v_other; int v_rdma = 0, v_min_rdma = 0; - if (test_bit(ICE_FLAG_RDMA_ENA, pf->flags)) { + if (ice_is_rdma_ena(pf)) { /* Need at least 1 interrupt in addition to * AEQ MSIX */ @@ -3910,7 +3955,7 @@ static int ice_ena_msix_range(struct ice_pf *pf) dev_notice(dev, "Enabled %d MSI-X vectors for LAN traffic.\n", pf->num_lan_msix); - if (test_bit(ICE_FLAG_RDMA_ENA, pf->flags)) + if (ice_is_rdma_ena(pf)) dev_notice(dev, "Enabled %d MSI-X vectors for RDMA.\n", pf->num_rdma_msix); } @@ -4090,8 +4135,8 @@ static void ice_set_safe_mode_vlan_cfg(struct ice_pf *pf) ctxt->info.sw_flags2 &= ~ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA; /* allow all VLANs on Tx and don't strip on Rx */ - ctxt->info.vlan_flags = ICE_AQ_VSI_VLAN_MODE_ALL | - ICE_AQ_VSI_VLAN_EMOD_NOTHING; + ctxt->info.inner_vlan_flags = ICE_AQ_VSI_INNER_VLAN_TX_MODE_ALL | + ICE_AQ_VSI_INNER_VLAN_EMODE_NOTHING; status = ice_update_vsi(hw, vsi->idx, ctxt, NULL); if (status) { @@ -4100,7 +4145,7 @@ static void ice_set_safe_mode_vlan_cfg(struct ice_pf *pf) } else { vsi->info.sec_flags = ctxt->info.sec_flags; vsi->info.sw_flags2 = ctxt->info.sw_flags2; - vsi->info.vlan_flags = ctxt->info.vlan_flags; + vsi->info.inner_vlan_flags = ctxt->info.inner_vlan_flags; } kfree(ctxt); @@ -4485,8 +4530,6 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) /* set up for high or low DMA */ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); - if (err) - err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (err) { dev_err(dev, "DMA configuration failed: 0x%x\n", err); return err; @@ -4709,6 +4752,9 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) ice_ptp_init(pf); + if (ice_is_feature_supported(pf, ICE_F_GNSS)) + ice_gnss_init(pf); + /* Note: Flow director init failure is non-fatal to load */ if (ice_init_fdir(pf)) dev_err(dev, "could not initialize flow director\n"); @@ -4738,7 +4784,7 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) /* ready to go, so clear down state bit */ clear_bit(ICE_DOWN, pf->state); - if (ice_is_aux_ena(pf)) { + if (ice_is_rdma_ena(pf)) { pf->aux_idx = ida_alloc(&ice_aux_ida, GFP_KERNEL); if (pf->aux_idx < 0) { dev_err(dev, "Failed to allocate device ID for AUX driver\n"); @@ -4883,6 +4929,8 @@ static void ice_remove(struct pci_dev *pdev) ice_deinit_lag(pf); if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) ice_ptp_release(pf); + if (ice_is_feature_supported(pf, ICE_F_GNSS)) + ice_gnss_exit(pf); if (!ice_is_safe_mode(pf)) ice_remove_arfs(pf); ice_setup_mc_magic_wake(pf); @@ -5596,6 +5644,194 @@ ice_fdb_del(struct ndmsg *ndm, __always_unused struct nlattr *tb[], return err; } +#define NETIF_VLAN_OFFLOAD_FEATURES (NETIF_F_HW_VLAN_CTAG_RX | \ + NETIF_F_HW_VLAN_CTAG_TX | \ + NETIF_F_HW_VLAN_STAG_RX | \ + NETIF_F_HW_VLAN_STAG_TX) + +#define NETIF_VLAN_FILTERING_FEATURES (NETIF_F_HW_VLAN_CTAG_FILTER | \ + NETIF_F_HW_VLAN_STAG_FILTER) + +/** + * ice_fix_features - fix the netdev features flags based on device limitations + * @netdev: ptr to the netdev that flags are being fixed on + * @features: features that need to be checked and possibly fixed + * + * Make sure any fixups are made to features in this callback. This enables the + * driver to not have to check unsupported configurations throughout the driver + * because that's the responsiblity of this callback. + * + * Single VLAN Mode (SVM) Supported Features: + * NETIF_F_HW_VLAN_CTAG_FILTER + * NETIF_F_HW_VLAN_CTAG_RX + * NETIF_F_HW_VLAN_CTAG_TX + * + * Double VLAN Mode (DVM) Supported Features: + * NETIF_F_HW_VLAN_CTAG_FILTER + * NETIF_F_HW_VLAN_CTAG_RX + * NETIF_F_HW_VLAN_CTAG_TX + * + * NETIF_F_HW_VLAN_STAG_FILTER + * NETIF_HW_VLAN_STAG_RX + * NETIF_HW_VLAN_STAG_TX + * + * Features that need fixing: + * Cannot simultaneously enable CTAG and STAG stripping and/or insertion. + * These are mutually exlusive as the VSI context cannot support multiple + * VLAN ethertypes simultaneously for stripping and/or insertion. If this + * is not done, then default to clearing the requested STAG offload + * settings. + * + * All supported filtering has to be enabled or disabled together. For + * example, in DVM, CTAG and STAG filtering have to be enabled and disabled + * together. If this is not done, then default to VLAN filtering disabled. + * These are mutually exclusive as there is currently no way to + * enable/disable VLAN filtering based on VLAN ethertype when using VLAN + * prune rules. + */ +static netdev_features_t +ice_fix_features(struct net_device *netdev, netdev_features_t features) +{ + struct ice_netdev_priv *np = netdev_priv(netdev); + netdev_features_t supported_vlan_filtering; + netdev_features_t requested_vlan_filtering; + struct ice_vsi *vsi = np->vsi; + + requested_vlan_filtering = features & NETIF_VLAN_FILTERING_FEATURES; + + /* make sure supported_vlan_filtering works for both SVM and DVM */ + supported_vlan_filtering = NETIF_F_HW_VLAN_CTAG_FILTER; + if (ice_is_dvm_ena(&vsi->back->hw)) + supported_vlan_filtering |= NETIF_F_HW_VLAN_STAG_FILTER; + + if (requested_vlan_filtering && + requested_vlan_filtering != supported_vlan_filtering) { + if (requested_vlan_filtering & NETIF_F_HW_VLAN_CTAG_FILTER) { + netdev_warn(netdev, "cannot support requested VLAN filtering settings, enabling all supported VLAN filtering settings\n"); + features |= supported_vlan_filtering; + } else { + netdev_warn(netdev, "cannot support requested VLAN filtering settings, clearing all supported VLAN filtering settings\n"); + features &= ~supported_vlan_filtering; + } + } + + if ((features & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX)) && + (features & (NETIF_F_HW_VLAN_STAG_RX | NETIF_F_HW_VLAN_STAG_TX))) { + netdev_warn(netdev, "cannot support CTAG and STAG VLAN stripping and/or insertion simultaneously since CTAG and STAG offloads are mutually exclusive, clearing STAG offload settings\n"); + features &= ~(NETIF_F_HW_VLAN_STAG_RX | + NETIF_F_HW_VLAN_STAG_TX); + } + + return features; +} + +/** + * ice_set_vlan_offload_features - set VLAN offload features for the PF VSI + * @vsi: PF's VSI + * @features: features used to determine VLAN offload settings + * + * First, determine the vlan_ethertype based on the VLAN offload bits in + * features. Then determine if stripping and insertion should be enabled or + * disabled. Finally enable or disable VLAN stripping and insertion. + */ +static int +ice_set_vlan_offload_features(struct ice_vsi *vsi, netdev_features_t features) +{ + bool enable_stripping = true, enable_insertion = true; + struct ice_vsi_vlan_ops *vlan_ops; + int strip_err = 0, insert_err = 0; + u16 vlan_ethertype = 0; + + vlan_ops = ice_get_compat_vsi_vlan_ops(vsi); + + if (features & (NETIF_F_HW_VLAN_STAG_RX | NETIF_F_HW_VLAN_STAG_TX)) + vlan_ethertype = ETH_P_8021AD; + else if (features & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX)) + vlan_ethertype = ETH_P_8021Q; + + if (!(features & (NETIF_F_HW_VLAN_STAG_RX | NETIF_F_HW_VLAN_CTAG_RX))) + enable_stripping = false; + if (!(features & (NETIF_F_HW_VLAN_STAG_TX | NETIF_F_HW_VLAN_CTAG_TX))) + enable_insertion = false; + + if (enable_stripping) + strip_err = vlan_ops->ena_stripping(vsi, vlan_ethertype); + else + strip_err = vlan_ops->dis_stripping(vsi); + + if (enable_insertion) + insert_err = vlan_ops->ena_insertion(vsi, vlan_ethertype); + else + insert_err = vlan_ops->dis_insertion(vsi); + + if (strip_err || insert_err) + return -EIO; + + return 0; +} + +/** + * ice_set_vlan_filtering_features - set VLAN filtering features for the PF VSI + * @vsi: PF's VSI + * @features: features used to determine VLAN filtering settings + * + * Enable or disable Rx VLAN filtering based on the VLAN filtering bits in the + * features. + */ +static int +ice_set_vlan_filtering_features(struct ice_vsi *vsi, netdev_features_t features) +{ + struct ice_vsi_vlan_ops *vlan_ops = ice_get_compat_vsi_vlan_ops(vsi); + int err = 0; + + /* support Single VLAN Mode (SVM) and Double VLAN Mode (DVM) by checking + * if either bit is set + */ + if (features & + (NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER)) + err = vlan_ops->ena_rx_filtering(vsi); + else + err = vlan_ops->dis_rx_filtering(vsi); + + return err; +} + +/** + * ice_set_vlan_features - set VLAN settings based on suggested feature set + * @netdev: ptr to the netdev being adjusted + * @features: the feature set that the stack is suggesting + * + * Only update VLAN settings if the requested_vlan_features are different than + * the current_vlan_features. + */ +static int +ice_set_vlan_features(struct net_device *netdev, netdev_features_t features) +{ + netdev_features_t current_vlan_features, requested_vlan_features; + struct ice_netdev_priv *np = netdev_priv(netdev); + struct ice_vsi *vsi = np->vsi; + int err; + + current_vlan_features = netdev->features & NETIF_VLAN_OFFLOAD_FEATURES; + requested_vlan_features = features & NETIF_VLAN_OFFLOAD_FEATURES; + if (current_vlan_features ^ requested_vlan_features) { + err = ice_set_vlan_offload_features(vsi, features); + if (err) + return err; + } + + current_vlan_features = netdev->features & + NETIF_VLAN_FILTERING_FEATURES; + requested_vlan_features = features & NETIF_VLAN_FILTERING_FEATURES; + if (current_vlan_features ^ requested_vlan_features) { + err = ice_set_vlan_filtering_features(vsi, features); + if (err) + return err; + } + + return 0; +} + /** * ice_set_features - set the netdev feature flags * @netdev: ptr to the netdev being adjusted @@ -5630,26 +5866,9 @@ ice_set_features(struct net_device *netdev, netdev_features_t features) netdev->features & NETIF_F_RXHASH) ice_vsi_manage_rss_lut(vsi, false); - if ((features & NETIF_F_HW_VLAN_CTAG_RX) && - !(netdev->features & NETIF_F_HW_VLAN_CTAG_RX)) - ret = ice_vsi_manage_vlan_stripping(vsi, true); - else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) && - (netdev->features & NETIF_F_HW_VLAN_CTAG_RX)) - ret = ice_vsi_manage_vlan_stripping(vsi, false); - - if ((features & NETIF_F_HW_VLAN_CTAG_TX) && - !(netdev->features & NETIF_F_HW_VLAN_CTAG_TX)) - ret = ice_vsi_manage_vlan_insertion(vsi); - else if (!(features & NETIF_F_HW_VLAN_CTAG_TX) && - (netdev->features & NETIF_F_HW_VLAN_CTAG_TX)) - ret = ice_vsi_manage_vlan_insertion(vsi); - - if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && - !(netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) - ret = ice_cfg_vlan_pruning(vsi, true); - else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) && - (netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) - ret = ice_cfg_vlan_pruning(vsi, false); + ret = ice_set_vlan_features(netdev, features); + if (ret) + return ret; if ((features & NETIF_F_NTUPLE) && !(netdev->features & NETIF_F_NTUPLE)) { @@ -5673,23 +5892,26 @@ ice_set_features(struct net_device *netdev, netdev_features_t features) else clear_bit(ICE_FLAG_CLS_FLOWER, pf->flags); - return ret; + return 0; } /** - * ice_vsi_vlan_setup - Setup VLAN offload properties on a VSI + * ice_vsi_vlan_setup - Setup VLAN offload properties on a PF VSI * @vsi: VSI to setup VLAN properties for */ static int ice_vsi_vlan_setup(struct ice_vsi *vsi) { - int ret = 0; + int err; - if (vsi->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) - ret = ice_vsi_manage_vlan_stripping(vsi, true); - if (vsi->netdev->features & NETIF_F_HW_VLAN_CTAG_TX) - ret = ice_vsi_manage_vlan_insertion(vsi); + err = ice_set_vlan_offload_features(vsi, vsi->netdev->features); + if (err) + return err; - return ret; + err = ice_set_vlan_filtering_features(vsi, vsi->netdev->features); + if (err) + return err; + + return ice_vsi_add_vlan_zero(vsi); } /** @@ -5930,9 +6152,9 @@ int ice_up(struct ice_vsi *vsi) * This function fetches stats from the ring considering the atomic operations * that needs to be performed to read u64 values in 32 bit machine. */ -static void -ice_fetch_u64_stats_per_ring(struct u64_stats_sync *syncp, struct ice_q_stats stats, - u64 *pkts, u64 *bytes) +void +ice_fetch_u64_stats_per_ring(struct u64_stats_sync *syncp, + struct ice_q_stats stats, u64 *pkts, u64 *bytes) { unsigned int start; @@ -6291,11 +6513,12 @@ static void ice_napi_disable_all(struct ice_vsi *vsi) */ int ice_down(struct ice_vsi *vsi) { - int i, tx_err, rx_err, link_err = 0; + int i, tx_err, rx_err, link_err = 0, vlan_err = 0; WARN_ON(!test_bit(ICE_VSI_DOWN, vsi->state)); if (vsi->netdev && vsi->type == ICE_VSI_PF) { + vlan_err = ice_vsi_del_vlan_zero(vsi); if (!ice_is_e810(&vsi->back->hw)) ice_ptp_link_change(vsi->back, vsi->back->hw.pf_id, false); netif_carrier_off(vsi->netdev); @@ -6337,7 +6560,7 @@ int ice_down(struct ice_vsi *vsi) ice_for_each_rxq(vsi, i) ice_clean_rx_ring(vsi->rx_rings[i]); - if (tx_err || rx_err || link_err) { + if (tx_err || rx_err || link_err || vlan_err) { netdev_err(vsi->netdev, "Failed to close VSI 0x%04X on switch 0x%04X\n", vsi->vsi_num, vsi->vsw->sw_id); return -EIO; @@ -6647,6 +6870,7 @@ static void ice_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type) { struct device *dev = ice_pf_to_dev(pf); struct ice_hw *hw = &pf->hw; + bool dvm; int err; if (test_bit(ICE_DOWN, pf->state)) @@ -6710,6 +6934,12 @@ static void ice_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type) goto err_init_ctrlq; } + dvm = ice_is_dvm_ena(hw); + + err = ice_aq_set_port_params(pf->hw.port_info, dvm, NULL); + if (err) + goto err_init_ctrlq; + err = ice_sched_init_port(hw->port_info); if (err) goto err_sched_init_port; @@ -6746,6 +6976,9 @@ static void ice_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type) if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) ice_ptp_reset(pf); + if (ice_is_feature_supported(pf, ICE_F_GNSS)) + ice_gnss_init(pf); + /* rebuild PF VSI */ err = ice_vsi_rebuild_by_type(pf, ICE_VSI_PF); if (err) { @@ -8606,6 +8839,7 @@ static const struct net_device_ops ice_netdev_ops = { .ndo_start_xmit = ice_start_xmit, .ndo_select_queue = ice_select_queue, .ndo_features_check = ice_features_check, + .ndo_fix_features = ice_fix_features, .ndo_set_rx_mode = ice_set_rx_mode, .ndo_set_mac_address = ice_set_mac_address, .ndo_validate_addr = eth_validate_addr, diff --git a/drivers/net/ethernet/intel/ice/ice_osdep.h b/drivers/net/ethernet/intel/ice/ice_osdep.h index f57c414bc0a9..82bc54fec7f3 100644 --- a/drivers/net/ethernet/intel/ice/ice_osdep.h +++ b/drivers/net/ethernet/intel/ice/ice_osdep.h @@ -5,10 +5,18 @@ #define _ICE_OSDEP_H_ #include +#include +#include #include +#include +#include +#include +#include +#include #ifndef CONFIG_64BIT #include #endif +#include #define wr32(a, reg, value) writel((value), ((a)->hw_addr + (reg))) #define rd32(a, reg) readl((a)->hw_addr + (reg)) @@ -24,8 +32,8 @@ struct ice_dma_mem { size_t size; }; -#define ice_hw_to_dev(ptr) \ - (&(container_of((ptr), struct ice_pf, hw))->pdev->dev) +struct ice_hw; +struct device *ice_hw_to_dev(struct ice_hw *hw); #ifdef CONFIG_DYNAMIC_DEBUG #define ice_debug(hw, type, fmt, args...) \ diff --git a/drivers/net/ethernet/intel/ice/ice_pf_vsi_vlan_ops.c b/drivers/net/ethernet/intel/ice/ice_pf_vsi_vlan_ops.c new file mode 100644 index 000000000000..976a03d3bdd5 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_pf_vsi_vlan_ops.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2019-2021, Intel Corporation. */ + +#include "ice_vsi_vlan_ops.h" +#include "ice_vsi_vlan_lib.h" +#include "ice_vlan_mode.h" +#include "ice.h" +#include "ice_pf_vsi_vlan_ops.h" + +void ice_pf_vsi_init_vlan_ops(struct ice_vsi *vsi) +{ + struct ice_vsi_vlan_ops *vlan_ops; + + if (ice_is_dvm_ena(&vsi->back->hw)) { + vlan_ops = &vsi->outer_vlan_ops; + + vlan_ops->add_vlan = ice_vsi_add_vlan; + vlan_ops->del_vlan = ice_vsi_del_vlan; + vlan_ops->ena_stripping = ice_vsi_ena_outer_stripping; + vlan_ops->dis_stripping = ice_vsi_dis_outer_stripping; + vlan_ops->ena_insertion = ice_vsi_ena_outer_insertion; + vlan_ops->dis_insertion = ice_vsi_dis_outer_insertion; + vlan_ops->ena_rx_filtering = ice_vsi_ena_rx_vlan_filtering; + vlan_ops->dis_rx_filtering = ice_vsi_dis_rx_vlan_filtering; + } else { + vlan_ops = &vsi->inner_vlan_ops; + + vlan_ops->add_vlan = ice_vsi_add_vlan; + vlan_ops->del_vlan = ice_vsi_del_vlan; + vlan_ops->ena_stripping = ice_vsi_ena_inner_stripping; + vlan_ops->dis_stripping = ice_vsi_dis_inner_stripping; + vlan_ops->ena_insertion = ice_vsi_ena_inner_insertion; + vlan_ops->dis_insertion = ice_vsi_dis_inner_insertion; + vlan_ops->ena_rx_filtering = ice_vsi_ena_rx_vlan_filtering; + vlan_ops->dis_rx_filtering = ice_vsi_dis_rx_vlan_filtering; + } +} + diff --git a/drivers/net/ethernet/intel/ice/ice_pf_vsi_vlan_ops.h b/drivers/net/ethernet/intel/ice/ice_pf_vsi_vlan_ops.h new file mode 100644 index 000000000000..6741ec8c5f6b --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_pf_vsi_vlan_ops.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019-2021, Intel Corporation. */ + +#ifndef _ICE_PF_VSI_VLAN_OPS_H_ +#define _ICE_PF_VSI_VLAN_OPS_H_ + +#include "ice_vsi_vlan_ops.h" + +struct ice_vsi; + +void ice_pf_vsi_init_vlan_ops(struct ice_vsi *vsi); + +#endif /* _ICE_PF_VSI_VLAN_OPS_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_protocol_type.h b/drivers/net/ethernet/intel/ice/ice_protocol_type.h index 695b6dd61dc2..3f64300b0e14 100644 --- a/drivers/net/ethernet/intel/ice/ice_protocol_type.h +++ b/drivers/net/ethernet/intel/ice/ice_protocol_type.h @@ -29,6 +29,7 @@ enum ice_protocol_type { ICE_MAC_OFOS = 0, ICE_MAC_IL, ICE_ETYPE_OL, + ICE_ETYPE_IL, ICE_VLAN_OFOS, ICE_IPV4_OFOS, ICE_IPV4_IL, @@ -40,6 +41,8 @@ enum ice_protocol_type { ICE_VXLAN, ICE_GENEVE, ICE_NVGRE, + ICE_GTP, + ICE_GTP_NO_PAY, ICE_VXLAN_GPE, ICE_SCTP_IL, ICE_PROTOCOL_LAST @@ -51,6 +54,8 @@ enum ice_sw_tunnel_type { ICE_SW_TUN_VXLAN, ICE_SW_TUN_GENEVE, ICE_SW_TUN_NVGRE, + ICE_SW_TUN_GTPU, + ICE_SW_TUN_GTPC, ICE_ALL_TUNNELS /* All tunnel types including NVGRE */ }; @@ -92,6 +97,7 @@ enum ice_prot_id { #define ICE_MAC_OFOS_HW 1 #define ICE_MAC_IL_HW 4 #define ICE_ETYPE_OL_HW 9 +#define ICE_ETYPE_IL_HW 10 #define ICE_VLAN_OF_HW 16 #define ICE_VLAN_OL_HW 17 #define ICE_IPV4_OFOS_HW 32 @@ -180,6 +186,20 @@ struct ice_udp_tnl_hdr { __be32 vni; /* only use lower 24-bits */ }; +struct ice_udp_gtp_hdr { + u8 flags; + u8 msg_type; + __be16 rsrvd_len; + __be32 teid; + __be16 rsrvd_seq_nbr; + u8 rsrvd_n_pdu_nbr; + u8 rsrvd_next_ext; + u8 rsvrd_ext_len; + u8 pdu_type; + u8 qfi; + u8 rsvrd; +}; + struct ice_nvgre_hdr { __be16 flags; __be16 protocol; @@ -196,6 +216,7 @@ union ice_prot_hdr { struct ice_sctp_hdr sctp_hdr; struct ice_udp_tnl_hdr tnl_hdr; struct ice_nvgre_hdr nvgre_hdr; + struct ice_udp_gtp_hdr gtp_hdr; }; /* This is mapping table entry that maps every word within a given protocol diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 000c39d163a2..a1cd33273ca4 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -3,6 +3,7 @@ #include "ice.h" #include "ice_lib.h" +#include "ice_trace.h" #define E810_OUT_PROP_DELAY_NS 1 @@ -2063,11 +2064,15 @@ static void ice_ptp_tx_tstamp_work(struct kthread_work *work) struct sk_buff *skb; int err; + ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx); + err = ice_read_phy_tstamp(hw, tx->quad, phy_idx, &raw_tstamp); if (err) continue; + ice_trace(tx_tstamp_fw_done, tx->tstamps[idx].skb, idx); + /* Check if the timestamp is invalid or stale */ if (!(raw_tstamp & ICE_PTP_TS_VALID) || raw_tstamp == tx->tstamps[idx].cached_tstamp) @@ -2093,6 +2098,8 @@ static void ice_ptp_tx_tstamp_work(struct kthread_work *work) tstamp = ice_ptp_extend_40b_ts(pf, raw_tstamp); shhwtstamps.hwtstamp = ns_to_ktime(tstamp); + ice_trace(tx_tstamp_complete, skb, idx); + skb_tstamp_tx(skb, &shhwtstamps); dev_kfree_skb_any(skb); } @@ -2131,6 +2138,7 @@ s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb) tx->tstamps[idx].start = jiffies; tx->tstamps[idx].skb = skb_get(skb); skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + ice_trace(tx_tstamp_request, skb, idx); } spin_unlock(&tx->lock); diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index ec8450f034e6..6dff97d53d81 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -3250,6 +3250,37 @@ int ice_write_sma_ctrl_e810t(struct ice_hw *hw, u8 data) return status; } +/** + * ice_read_pca9575_reg_e810t + * @hw: pointer to the hw struct + * @offset: GPIO controller register offset + * @data: pointer to data to be read from the GPIO controller + * + * Read the register from the GPIO controller + */ +int ice_read_pca9575_reg_e810t(struct ice_hw *hw, u8 offset, u8 *data) +{ + struct ice_aqc_link_topo_addr link_topo; + __le16 addr; + u16 handle; + int err; + + memset(&link_topo, 0, sizeof(link_topo)); + + err = ice_get_pca9575_handle(hw, &handle); + if (err) + return err; + + link_topo.handle = cpu_to_le16(handle); + link_topo.topo_params.node_type_ctx = + FIELD_PREP(ICE_AQC_LINK_TOPO_NODE_CTX_M, + ICE_AQC_LINK_TOPO_NODE_CTX_PROVIDED); + + addr = cpu_to_le16((u16)offset); + + return ice_aq_read_i2c(hw, link_topo, 0, addr, 1, data, NULL); +} + /** * ice_is_pca9575_present * @hw: pointer to the hw struct diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h index 519e75462e67..1246e4ee4b5d 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h @@ -191,6 +191,7 @@ int ice_phy_exit_bypass_e822(struct ice_hw *hw, u8 port); int ice_ptp_init_phy_e810(struct ice_hw *hw); int ice_read_sma_ctrl_e810t(struct ice_hw *hw, u8 *data); int ice_write_sma_ctrl_e810t(struct ice_hw *hw, u8 data); +int ice_read_pca9575_reg_e810t(struct ice_hw *hw, u8 offset, u8 *data); bool ice_is_pca9575_present(struct ice_hw *hw); #define PFTSYN_SEM_BYTES 4 @@ -443,4 +444,10 @@ bool ice_is_pca9575_present(struct ice_hw *hw); #define ICE_SMA_MAX_BIT_E810T 7 #define ICE_PCA9575_P1_OFFSET 8 +/* E810T PCA9575 IO controller registers */ +#define ICE_PCA9575_P0_IN 0x0 + +/* E810T PCA9575 IO controller pin control */ +#define ICE_E810T_P0_GNSS_PRSNT_N BIT(4) + #endif /* _ICE_PTP_HW_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_repr.c b/drivers/net/ethernet/intel/ice/ice_repr.c index dcc310e29300..848f2adea563 100644 --- a/drivers/net/ethernet/intel/ice/ice_repr.c +++ b/drivers/net/ethernet/intel/ice/ice_repr.c @@ -4,7 +4,7 @@ #include "ice.h" #include "ice_eswitch.h" #include "ice_devlink.h" -#include "ice_virtchnl_pf.h" +#include "ice_sriov.h" #include "ice_tc_lib.h" /** @@ -142,6 +142,59 @@ ice_repr_get_devlink_port(struct net_device *netdev) return &repr->vf->devlink_port; } +/** + * ice_repr_sp_stats64 - get slow path stats for port representor + * @dev: network interface device structure + * @stats: netlink stats structure + * + * RX/TX stats are being swapped here to be consistent with VF stats. In slow + * path, port representor receives data when the corresponding VF is sending it + * (and vice versa), TX and RX bytes/packets are effectively swapped on port + * representor. + */ +static int +ice_repr_sp_stats64(const struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct ice_netdev_priv *np = netdev_priv(dev); + int vf_id = np->repr->vf->vf_id; + struct ice_tx_ring *tx_ring; + struct ice_rx_ring *rx_ring; + u64 pkts, bytes; + + tx_ring = np->vsi->tx_rings[vf_id]; + ice_fetch_u64_stats_per_ring(&tx_ring->syncp, tx_ring->stats, + &pkts, &bytes); + stats->rx_packets = pkts; + stats->rx_bytes = bytes; + + rx_ring = np->vsi->rx_rings[vf_id]; + ice_fetch_u64_stats_per_ring(&rx_ring->syncp, rx_ring->stats, + &pkts, &bytes); + stats->tx_packets = pkts; + stats->tx_bytes = bytes; + stats->tx_dropped = rx_ring->rx_stats.alloc_page_failed + + rx_ring->rx_stats.alloc_buf_failed; + + return 0; +} + +static bool +ice_repr_ndo_has_offload_stats(const struct net_device *dev, int attr_id) +{ + return attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT; +} + +static int +ice_repr_ndo_get_offload_stats(int attr_id, const struct net_device *dev, + void *sp) +{ + if (attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT) + return ice_repr_sp_stats64(dev, (struct rtnl_link_stats64 *)sp); + + return -EINVAL; +} + static int ice_repr_setup_tc_cls_flower(struct ice_repr *repr, struct flow_cls_offload *flower) @@ -199,6 +252,8 @@ static const struct net_device_ops ice_repr_netdev_ops = { .ndo_start_xmit = ice_eswitch_port_start_xmit, .ndo_get_devlink_port = ice_repr_get_devlink_port, .ndo_setup_tc = ice_repr_setup_tc, + .ndo_has_offload_stats = ice_repr_ndo_has_offload_stats, + .ndo_get_offload_stats = ice_repr_ndo_get_offload_stats, }; /** @@ -284,6 +339,8 @@ static int ice_repr_add(struct ice_vf *vf) devlink_port_type_eth_set(&vf->devlink_port, repr->netdev); + ice_virtchnl_set_repr_ops(vf); + return 0; err_netdev: @@ -311,6 +368,9 @@ static int ice_repr_add(struct ice_vf *vf) */ static void ice_repr_rem(struct ice_vf *vf) { + if (!vf->repr) + return; + ice_devlink_destroy_vf_port(vf); kfree(vf->repr->q_vector); vf->repr->q_vector = NULL; @@ -323,38 +383,8 @@ static void ice_repr_rem(struct ice_vf *vf) #endif kfree(vf->repr); vf->repr = NULL; -} -/** - * ice_repr_add_for_all_vfs - add port representor for all VFs - * @pf: pointer to PF structure - */ -int ice_repr_add_for_all_vfs(struct ice_pf *pf) -{ - int err; - int i; - - ice_for_each_vf(pf, i) { - struct ice_vf *vf = &pf->vf[i]; - - err = ice_repr_add(vf); - if (err) - goto err; - - ice_vc_change_ops_to_repr(&vf->vc_ops); - } - - return 0; - -err: - for (i = i - 1; i >= 0; i--) { - struct ice_vf *vf = &pf->vf[i]; - - ice_repr_rem(vf); - ice_vc_set_dflt_vf_ops(&vf->vc_ops); - } - - return err; + ice_virtchnl_set_dflt_ops(vf); } /** @@ -363,14 +393,39 @@ int ice_repr_add_for_all_vfs(struct ice_pf *pf) */ void ice_repr_rem_from_all_vfs(struct ice_pf *pf) { - int i; + struct ice_vf *vf; + unsigned int bkt; - ice_for_each_vf(pf, i) { - struct ice_vf *vf = &pf->vf[i]; + lockdep_assert_held(&pf->vfs.table_lock); + ice_for_each_vf(pf, bkt, vf) ice_repr_rem(vf); - ice_vc_set_dflt_vf_ops(&vf->vc_ops); +} + +/** + * ice_repr_add_for_all_vfs - add port representor for all VFs + * @pf: pointer to PF structure + */ +int ice_repr_add_for_all_vfs(struct ice_pf *pf) +{ + struct ice_vf *vf; + unsigned int bkt; + int err; + + lockdep_assert_held(&pf->vfs.table_lock); + + ice_for_each_vf(pf, bkt, vf) { + err = ice_repr_add(vf); + if (err) + goto err; } + + return 0; + +err: + ice_repr_rem_from_all_vfs(pf); + + return err; } /** diff --git a/drivers/net/ethernet/intel/ice/ice_repr.h b/drivers/net/ethernet/intel/ice/ice_repr.h index 0c77ff050d15..378a45bfa256 100644 --- a/drivers/net/ethernet/intel/ice/ice_repr.h +++ b/drivers/net/ethernet/intel/ice/ice_repr.h @@ -5,7 +5,6 @@ #define _ICE_REPR_H_ #include -#include "ice.h" struct ice_repr { struct ice_vsi *src_vsi; diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 52c6bac41bf7..8915a9d39e36 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -1,532 +1,1919 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2018, Intel Corporation. */ -#include "ice_common.h" -#include "ice_sriov.h" +#include "ice.h" +#include "ice_vf_lib_private.h" +#include "ice_base.h" +#include "ice_lib.h" +#include "ice_fltr.h" +#include "ice_dcb_lib.h" +#include "ice_flow.h" +#include "ice_eswitch.h" +#include "ice_virtchnl_allowlist.h" +#include "ice_flex_pipe.h" +#include "ice_vf_vsi_vlan_ops.h" +#include "ice_vlan.h" /** - * ice_aq_send_msg_to_vf - * @hw: pointer to the hardware structure - * @vfid: VF ID to send msg - * @v_opcode: opcodes for VF-PF communication - * @v_retval: return error code - * @msg: pointer to the msg buffer - * @msglen: msg length - * @cd: pointer to command details + * ice_free_vf_entries - Free all VF entries from the hash table + * @pf: pointer to the PF structure * - * Send message to VF driver (0x0802) using mailbox - * queue and asynchronously sending message via - * ice_sq_send_cmd() function + * Iterate over the VF hash table, removing and releasing all VF entries. + * Called during VF teardown or as cleanup during failed VF initialization. */ -int -ice_aq_send_msg_to_vf(struct ice_hw *hw, u16 vfid, u32 v_opcode, u32 v_retval, - u8 *msg, u16 msglen, struct ice_sq_cd *cd) +static void ice_free_vf_entries(struct ice_pf *pf) { - struct ice_aqc_pf_vf_msg *cmd; - struct ice_aq_desc desc; + struct ice_vfs *vfs = &pf->vfs; + struct hlist_node *tmp; + struct ice_vf *vf; + unsigned int bkt; - ice_fill_dflt_direct_cmd_desc(&desc, ice_mbx_opc_send_msg_to_vf); - - cmd = &desc.params.virt; - cmd->id = cpu_to_le32(vfid); - - desc.cookie_high = cpu_to_le32(v_opcode); - desc.cookie_low = cpu_to_le32(v_retval); - - if (msglen) - desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD); - - return ice_sq_send_cmd(hw, &hw->mailboxq, &desc, msg, msglen, cd); -} - -/** - * ice_conv_link_speed_to_virtchnl - * @adv_link_support: determines the format of the returned link speed - * @link_speed: variable containing the link_speed to be converted - * - * Convert link speed supported by HW to link speed supported by virtchnl. - * If adv_link_support is true, then return link speed in Mbps. Else return - * link speed as a VIRTCHNL_LINK_SPEED_* casted to a u32. Note that the caller - * needs to cast back to an enum virtchnl_link_speed in the case where - * adv_link_support is false, but when adv_link_support is true the caller can - * expect the speed in Mbps. - */ -u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed) -{ - u32 speed; - - if (adv_link_support) - switch (link_speed) { - case ICE_AQ_LINK_SPEED_10MB: - speed = ICE_LINK_SPEED_10MBPS; - break; - case ICE_AQ_LINK_SPEED_100MB: - speed = ICE_LINK_SPEED_100MBPS; - break; - case ICE_AQ_LINK_SPEED_1000MB: - speed = ICE_LINK_SPEED_1000MBPS; - break; - case ICE_AQ_LINK_SPEED_2500MB: - speed = ICE_LINK_SPEED_2500MBPS; - break; - case ICE_AQ_LINK_SPEED_5GB: - speed = ICE_LINK_SPEED_5000MBPS; - break; - case ICE_AQ_LINK_SPEED_10GB: - speed = ICE_LINK_SPEED_10000MBPS; - break; - case ICE_AQ_LINK_SPEED_20GB: - speed = ICE_LINK_SPEED_20000MBPS; - break; - case ICE_AQ_LINK_SPEED_25GB: - speed = ICE_LINK_SPEED_25000MBPS; - break; - case ICE_AQ_LINK_SPEED_40GB: - speed = ICE_LINK_SPEED_40000MBPS; - break; - case ICE_AQ_LINK_SPEED_50GB: - speed = ICE_LINK_SPEED_50000MBPS; - break; - case ICE_AQ_LINK_SPEED_100GB: - speed = ICE_LINK_SPEED_100000MBPS; - break; - default: - speed = ICE_LINK_SPEED_UNKNOWN; - break; - } - else - /* Virtchnl speeds are not defined for every speed supported in - * the hardware. To maintain compatibility with older AVF - * drivers, while reporting the speed the new speed values are - * resolved to the closest known virtchnl speeds - */ - switch (link_speed) { - case ICE_AQ_LINK_SPEED_10MB: - case ICE_AQ_LINK_SPEED_100MB: - speed = (u32)VIRTCHNL_LINK_SPEED_100MB; - break; - case ICE_AQ_LINK_SPEED_1000MB: - case ICE_AQ_LINK_SPEED_2500MB: - case ICE_AQ_LINK_SPEED_5GB: - speed = (u32)VIRTCHNL_LINK_SPEED_1GB; - break; - case ICE_AQ_LINK_SPEED_10GB: - speed = (u32)VIRTCHNL_LINK_SPEED_10GB; - break; - case ICE_AQ_LINK_SPEED_20GB: - speed = (u32)VIRTCHNL_LINK_SPEED_20GB; - break; - case ICE_AQ_LINK_SPEED_25GB: - speed = (u32)VIRTCHNL_LINK_SPEED_25GB; - break; - case ICE_AQ_LINK_SPEED_40GB: - case ICE_AQ_LINK_SPEED_50GB: - case ICE_AQ_LINK_SPEED_100GB: - speed = (u32)VIRTCHNL_LINK_SPEED_40GB; - break; - default: - speed = (u32)VIRTCHNL_LINK_SPEED_UNKNOWN; - break; - } - - return speed; -} - -/* The mailbox overflow detection algorithm helps to check if there - * is a possibility of a malicious VF transmitting too many MBX messages to the - * PF. - * 1. The mailbox snapshot structure, ice_mbx_snapshot, is initialized during - * driver initialization in ice_init_hw() using ice_mbx_init_snapshot(). - * The struct ice_mbx_snapshot helps to track and traverse a static window of - * messages within the mailbox queue while looking for a malicious VF. - * - * 2. When the caller starts processing its mailbox queue in response to an - * interrupt, the structure ice_mbx_snapshot is expected to be cleared before - * the algorithm can be run for the first time for that interrupt. This can be - * done via ice_mbx_reset_snapshot(). - * - * 3. For every message read by the caller from the MBX Queue, the caller must - * call the detection algorithm's entry function ice_mbx_vf_state_handler(). - * Before every call to ice_mbx_vf_state_handler() the struct ice_mbx_data is - * filled as it is required to be passed to the algorithm. - * - * 4. Every time a message is read from the MBX queue, a VFId is received which - * is passed to the state handler. The boolean output is_malvf of the state - * handler ice_mbx_vf_state_handler() serves as an indicator to the caller - * whether this VF is malicious or not. - * - * 5. When a VF is identified to be malicious, the caller can send a message - * to the system administrator. The caller can invoke ice_mbx_report_malvf() - * to help determine if a malicious VF is to be reported or not. This function - * requires the caller to maintain a global bitmap to track all malicious VFs - * and pass that to ice_mbx_report_malvf() along with the VFID which was identified - * to be malicious by ice_mbx_vf_state_handler(). - * - * 6. The global bitmap maintained by PF can be cleared completely if PF is in - * reset or the bit corresponding to a VF can be cleared if that VF is in reset. - * When a VF is shut down and brought back up, we assume that the new VF - * brought up is not malicious and hence report it if found malicious. - * - * 7. The function ice_mbx_reset_snapshot() is called to reset the information - * in ice_mbx_snapshot for every new mailbox interrupt handled. - * - * 8. The memory allocated for variables in ice_mbx_snapshot is de-allocated - * when driver is unloaded. - */ -#define ICE_RQ_DATA_MASK(rq_data) ((rq_data) & PF_MBX_ARQH_ARQH_M) -/* Using the highest value for an unsigned 16-bit value 0xFFFF to indicate that - * the max messages check must be ignored in the algorithm - */ -#define ICE_IGNORE_MAX_MSG_CNT 0xFFFF - -/** - * ice_mbx_traverse - Pass through mailbox snapshot - * @hw: pointer to the HW struct - * @new_state: new algorithm state - * - * Traversing the mailbox static snapshot without checking - * for malicious VFs. - */ -static void -ice_mbx_traverse(struct ice_hw *hw, - enum ice_mbx_snapshot_state *new_state) -{ - struct ice_mbx_snap_buffer_data *snap_buf; - u32 num_iterations; - - snap_buf = &hw->mbx_snapshot.mbx_buf; - - /* As mailbox buffer is circular, applying a mask - * on the incremented iteration count. + /* Remove all VFs from the hash table and release their main + * reference. Once all references to the VF are dropped, ice_put_vf() + * will call ice_release_vf which will remove the VF memory. */ - num_iterations = ICE_RQ_DATA_MASK(++snap_buf->num_iterations); + lockdep_assert_held(&vfs->table_lock); - /* Checking either of the below conditions to exit snapshot traversal: - * Condition-1: If the number of iterations in the mailbox is equal to - * the mailbox head which would indicate that we have reached the end - * of the static snapshot. - * Condition-2: If the maximum messages serviced in the mailbox for a - * given interrupt is the highest possible value then there is no need - * to check if the number of messages processed is equal to it. If not - * check if the number of messages processed is greater than or equal - * to the maximum number of mailbox entries serviced in current work item. - */ - if (num_iterations == snap_buf->head || - (snap_buf->max_num_msgs_mbx < ICE_IGNORE_MAX_MSG_CNT && - ++snap_buf->num_msg_proc >= snap_buf->max_num_msgs_mbx)) - *new_state = ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT; + hash_for_each_safe(vfs->table, bkt, tmp, vf, entry) { + hash_del_rcu(&vf->entry); + ice_put_vf(vf); + } } /** - * ice_mbx_detect_malvf - Detect malicious VF in snapshot - * @hw: pointer to the HW struct - * @vf_id: relative virtual function ID - * @new_state: new algorithm state - * @is_malvf: boolean output to indicate if VF is malicious - * - * This function tracks the number of asynchronous messages - * sent per VF and marks the VF as malicious if it exceeds - * the permissible number of messages to send. + * ice_vf_vsi_release - invalidate the VF's VSI after freeing it + * @vf: invalidate this VF's VSI after freeing it */ -static int -ice_mbx_detect_malvf(struct ice_hw *hw, u16 vf_id, - enum ice_mbx_snapshot_state *new_state, - bool *is_malvf) +static void ice_vf_vsi_release(struct ice_vf *vf) { - struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; - - if (vf_id >= snap->mbx_vf.vfcntr_len) - return -EIO; - - /* increment the message count in the VF array */ - snap->mbx_vf.vf_cntr[vf_id]++; - - if (snap->mbx_vf.vf_cntr[vf_id] >= ICE_ASYNC_VF_MSG_THRESHOLD) - *is_malvf = true; - - /* continue to iterate through the mailbox snapshot */ - ice_mbx_traverse(hw, new_state); - - return 0; + ice_vsi_release(ice_get_vf_vsi(vf)); + ice_vf_invalidate_vsi(vf); } /** - * ice_mbx_reset_snapshot - Reset mailbox snapshot structure - * @snap: pointer to mailbox snapshot structure in the ice_hw struct - * - * Reset the mailbox snapshot structure and clear VF counter array. + * ice_free_vf_res - Free a VF's resources + * @vf: pointer to the VF info */ -static void ice_mbx_reset_snapshot(struct ice_mbx_snapshot *snap) +static void ice_free_vf_res(struct ice_vf *vf) { - u32 vfcntr_len; + struct ice_pf *pf = vf->pf; + int i, last_vector_idx; - if (!snap || !snap->mbx_vf.vf_cntr) - return; - - /* Clear VF counters. */ - vfcntr_len = snap->mbx_vf.vfcntr_len; - if (vfcntr_len) - memset(snap->mbx_vf.vf_cntr, 0, - (vfcntr_len * sizeof(*snap->mbx_vf.vf_cntr))); - - /* Reset mailbox snapshot for a new capture. */ - memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf)); - snap->mbx_buf.state = ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT; -} - -/** - * ice_mbx_vf_state_handler - Handle states of the overflow algorithm - * @hw: pointer to the HW struct - * @mbx_data: pointer to structure containing mailbox data - * @vf_id: relative virtual function (VF) ID - * @is_malvf: boolean output to indicate if VF is malicious - * - * The function serves as an entry point for the malicious VF - * detection algorithm by handling the different states and state - * transitions of the algorithm: - * New snapshot: This state is entered when creating a new static - * snapshot. The data from any previous mailbox snapshot is - * cleared and a new capture of the mailbox head and tail is - * logged. This will be the new static snapshot to detect - * asynchronous messages sent by VFs. On capturing the snapshot - * and depending on whether the number of pending messages in that - * snapshot exceed the watermark value, the state machine enters - * traverse or detect states. - * Traverse: If pending message count is below watermark then iterate - * through the snapshot without any action on VF. - * Detect: If pending message count exceeds watermark traverse - * the static snapshot and look for a malicious VF. - */ -int -ice_mbx_vf_state_handler(struct ice_hw *hw, - struct ice_mbx_data *mbx_data, u16 vf_id, - bool *is_malvf) -{ - struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; - struct ice_mbx_snap_buffer_data *snap_buf; - struct ice_ctl_q_info *cq = &hw->mailboxq; - enum ice_mbx_snapshot_state new_state; - int status = 0; - - if (!is_malvf || !mbx_data) - return -EINVAL; - - /* When entering the mailbox state machine assume that the VF - * is not malicious until detected. + /* First, disable VF's configuration API to prevent OS from + * accessing the VF's VSI after it's freed or invalidated. */ - *is_malvf = false; + clear_bit(ICE_VF_STATE_INIT, vf->vf_states); + ice_vf_fdir_exit(vf); + /* free VF control VSI */ + if (vf->ctrl_vsi_idx != ICE_NO_VSI) + ice_vf_ctrl_vsi_release(vf); - /* Checking if max messages allowed to be processed while servicing current - * interrupt is not less than the defined AVF message threshold. - */ - if (mbx_data->max_num_msgs_mbx <= ICE_ASYNC_VF_MSG_THRESHOLD) - return -EINVAL; - - /* The watermark value should not be lesser than the threshold limit - * set for the number of asynchronous messages a VF can send to mailbox - * nor should it be greater than the maximum number of messages in the - * mailbox serviced in current interrupt. - */ - if (mbx_data->async_watermark_val < ICE_ASYNC_VF_MSG_THRESHOLD || - mbx_data->async_watermark_val > mbx_data->max_num_msgs_mbx) - return -EINVAL; - - new_state = ICE_MAL_VF_DETECT_STATE_INVALID; - snap_buf = &snap->mbx_buf; - - switch (snap_buf->state) { - case ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT: - /* Clear any previously held data in mailbox snapshot structure. */ - ice_mbx_reset_snapshot(snap); - - /* Collect the pending ARQ count, number of messages processed and - * the maximum number of messages allowed to be processed from the - * Mailbox for current interrupt. - */ - snap_buf->num_pending_arq = mbx_data->num_pending_arq; - snap_buf->num_msg_proc = mbx_data->num_msg_proc; - snap_buf->max_num_msgs_mbx = mbx_data->max_num_msgs_mbx; - - /* Capture a new static snapshot of the mailbox by logging the - * head and tail of snapshot and set num_iterations to the tail - * value to mark the start of the iteration through the snapshot. - */ - snap_buf->head = ICE_RQ_DATA_MASK(cq->rq.next_to_clean + - mbx_data->num_pending_arq); - snap_buf->tail = ICE_RQ_DATA_MASK(cq->rq.next_to_clean - 1); - snap_buf->num_iterations = snap_buf->tail; - - /* Pending ARQ messages returned by ice_clean_rq_elem - * is the difference between the head and tail of the - * mailbox queue. Comparing this value against the watermark - * helps to check if we potentially have malicious VFs. - */ - if (snap_buf->num_pending_arq >= - mbx_data->async_watermark_val) { - new_state = ICE_MAL_VF_DETECT_STATE_DETECT; - status = ice_mbx_detect_malvf(hw, vf_id, &new_state, is_malvf); - } else { - new_state = ICE_MAL_VF_DETECT_STATE_TRAVERSE; - ice_mbx_traverse(hw, &new_state); - } - break; - - case ICE_MAL_VF_DETECT_STATE_TRAVERSE: - new_state = ICE_MAL_VF_DETECT_STATE_TRAVERSE; - ice_mbx_traverse(hw, &new_state); - break; - - case ICE_MAL_VF_DETECT_STATE_DETECT: - new_state = ICE_MAL_VF_DETECT_STATE_DETECT; - status = ice_mbx_detect_malvf(hw, vf_id, &new_state, is_malvf); - break; - - default: - new_state = ICE_MAL_VF_DETECT_STATE_INVALID; - status = -EIO; + /* free VSI and disconnect it from the parent uplink */ + if (vf->lan_vsi_idx != ICE_NO_VSI) { + ice_vf_vsi_release(vf); + vf->num_mac = 0; } - snap_buf->state = new_state; + last_vector_idx = vf->first_vector_idx + pf->vfs.num_msix_per - 1; - return status; + /* clear VF MDD event information */ + memset(&vf->mdd_tx_events, 0, sizeof(vf->mdd_tx_events)); + memset(&vf->mdd_rx_events, 0, sizeof(vf->mdd_rx_events)); + + /* Disable interrupts so that VF starts in a known state */ + for (i = vf->first_vector_idx; i <= last_vector_idx; i++) { + wr32(&pf->hw, GLINT_DYN_CTL(i), GLINT_DYN_CTL_CLEARPBA_M); + ice_flush(&pf->hw); + } + /* reset some of the state variables keeping track of the resources */ + clear_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states); + clear_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states); } /** - * ice_mbx_report_malvf - Track and note malicious VF - * @hw: pointer to the HW struct - * @all_malvfs: all malicious VFs tracked by PF - * @bitmap_len: length of bitmap in bits - * @vf_id: relative virtual function ID of the malicious VF - * @report_malvf: boolean to indicate if malicious VF must be reported - * - * This function will update a bitmap that keeps track of the malicious - * VFs attached to the PF. A malicious VF must be reported only once if - * discovered between VF resets or loading so the function checks - * the input vf_id against the bitmap to verify if the VF has been - * detected in any previous mailbox iterations. + * ice_dis_vf_mappings + * @vf: pointer to the VF structure */ -int -ice_mbx_report_malvf(struct ice_hw *hw, unsigned long *all_malvfs, - u16 bitmap_len, u16 vf_id, bool *report_malvf) +static void ice_dis_vf_mappings(struct ice_vf *vf) { - if (!all_malvfs || !report_malvf) + struct ice_pf *pf = vf->pf; + struct ice_vsi *vsi; + struct device *dev; + int first, last, v; + struct ice_hw *hw; + + hw = &pf->hw; + vsi = ice_get_vf_vsi(vf); + + dev = ice_pf_to_dev(pf); + wr32(hw, VPINT_ALLOC(vf->vf_id), 0); + wr32(hw, VPINT_ALLOC_PCI(vf->vf_id), 0); + + first = vf->first_vector_idx; + last = first + pf->vfs.num_msix_per - 1; + for (v = first; v <= last; v++) { + u32 reg; + + reg = (((1 << GLINT_VECT2FUNC_IS_PF_S) & + GLINT_VECT2FUNC_IS_PF_M) | + ((hw->pf_id << GLINT_VECT2FUNC_PF_NUM_S) & + GLINT_VECT2FUNC_PF_NUM_M)); + wr32(hw, GLINT_VECT2FUNC(v), reg); + } + + if (vsi->tx_mapping_mode == ICE_VSI_MAP_CONTIG) + wr32(hw, VPLAN_TX_QBASE(vf->vf_id), 0); + else + dev_err(dev, "Scattered mode for VF Tx queues is not yet implemented\n"); + + if (vsi->rx_mapping_mode == ICE_VSI_MAP_CONTIG) + wr32(hw, VPLAN_RX_QBASE(vf->vf_id), 0); + else + dev_err(dev, "Scattered mode for VF Rx queues is not yet implemented\n"); +} + +/** + * ice_sriov_free_msix_res - Reset/free any used MSIX resources + * @pf: pointer to the PF structure + * + * Since no MSIX entries are taken from the pf->irq_tracker then just clear + * the pf->sriov_base_vector. + * + * Returns 0 on success, and -EINVAL on error. + */ +static int ice_sriov_free_msix_res(struct ice_pf *pf) +{ + struct ice_res_tracker *res; + + if (!pf) return -EINVAL; - *report_malvf = false; - - if (bitmap_len < hw->mbx_snapshot.mbx_vf.vfcntr_len) + res = pf->irq_tracker; + if (!res) return -EINVAL; - if (vf_id >= bitmap_len) - return -EIO; + /* give back irq_tracker resources used */ + WARN_ON(pf->sriov_base_vector < res->num_entries); - /* If the vf_id is found in the bitmap set bit and boolean to true */ - if (!test_and_set_bit(vf_id, all_malvfs)) - *report_malvf = true; + pf->sriov_base_vector = 0; return 0; } /** - * ice_mbx_clear_malvf - Clear VF bitmap and counter for VF ID - * @snap: pointer to the mailbox snapshot structure - * @all_malvfs: all malicious VFs tracked by PF - * @bitmap_len: length of bitmap in bits - * @vf_id: relative virtual function ID of the malicious VF - * - * In case of a VF reset, this function can be called to clear - * the bit corresponding to the VF ID in the bitmap tracking all - * malicious VFs attached to the PF. The function also clears the - * VF counter array at the index of the VF ID. This is to ensure - * that the new VF loaded is not considered malicious before going - * through the overflow detection algorithm. + * ice_free_vfs - Free all VFs + * @pf: pointer to the PF structure */ -int -ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, unsigned long *all_malvfs, - u16 bitmap_len, u16 vf_id) +void ice_free_vfs(struct ice_pf *pf) { - if (!snap || !all_malvfs) - return -EINVAL; + struct device *dev = ice_pf_to_dev(pf); + struct ice_vfs *vfs = &pf->vfs; + struct ice_hw *hw = &pf->hw; + struct ice_vf *vf; + unsigned int bkt; - if (bitmap_len < snap->mbx_vf.vfcntr_len) - return -EINVAL; + if (!ice_has_vfs(pf)) + return; - /* Ensure VF ID value is not larger than bitmap or VF counter length */ - if (vf_id >= bitmap_len || vf_id >= snap->mbx_vf.vfcntr_len) - return -EIO; + while (test_and_set_bit(ICE_VF_DIS, pf->state)) + usleep_range(1000, 2000); - /* Clear VF ID bit in the bitmap tracking malicious VFs attached to PF */ - clear_bit(vf_id, all_malvfs); - - /* Clear the VF counter in the mailbox snapshot structure for that VF ID. - * This is to ensure that if a VF is unloaded and a new one brought back - * up with the same VF ID for a snapshot currently in traversal or detect - * state the counter for that VF ID does not increment on top of existing - * values in the mailbox overflow detection algorithm. + /* Disable IOV before freeing resources. This lets any VF drivers + * running in the host get themselves cleaned up before we yank + * the carpet out from underneath their feet. */ - snap->mbx_vf.vf_cntr[vf_id] = 0; + if (!pci_vfs_assigned(pf->pdev)) + pci_disable_sriov(pf->pdev); + else + dev_warn(dev, "VFs are assigned - not disabling SR-IOV\n"); + + mutex_lock(&vfs->table_lock); + + ice_eswitch_release(pf); + + ice_for_each_vf(pf, bkt, vf) { + mutex_lock(&vf->cfg_lock); + + ice_dis_vf_qs(vf); + + if (test_bit(ICE_VF_STATE_INIT, vf->vf_states)) { + /* disable VF qp mappings and set VF disable state */ + ice_dis_vf_mappings(vf); + set_bit(ICE_VF_STATE_DIS, vf->vf_states); + ice_free_vf_res(vf); + } + + if (!pci_vfs_assigned(pf->pdev)) { + u32 reg_idx, bit_idx; + + reg_idx = (hw->func_caps.vf_base_id + vf->vf_id) / 32; + bit_idx = (hw->func_caps.vf_base_id + vf->vf_id) % 32; + wr32(hw, GLGEN_VFLRSTAT(reg_idx), BIT(bit_idx)); + } + + /* clear malicious info since the VF is getting released */ + if (ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->vfs.malvfs, + ICE_MAX_SRIOV_VFS, vf->vf_id)) + dev_dbg(dev, "failed to clear malicious VF state for VF %u\n", + vf->vf_id); + + mutex_unlock(&vf->cfg_lock); + } + + if (ice_sriov_free_msix_res(pf)) + dev_err(dev, "Failed to free MSIX resources used by SR-IOV\n"); + + vfs->num_qps_per = 0; + ice_free_vf_entries(pf); + + mutex_unlock(&vfs->table_lock); + + clear_bit(ICE_VF_DIS, pf->state); + clear_bit(ICE_FLAG_SRIOV_ENA, pf->flags); +} + +/** + * ice_vf_vsi_setup - Set up a VF VSI + * @vf: VF to setup VSI for + * + * Returns pointer to the successfully allocated VSI struct on success, + * otherwise returns NULL on failure. + */ +static struct ice_vsi *ice_vf_vsi_setup(struct ice_vf *vf) +{ + struct ice_port_info *pi = ice_vf_get_port_info(vf); + struct ice_pf *pf = vf->pf; + struct ice_vsi *vsi; + + vsi = ice_vsi_setup(pf, pi, ICE_VSI_VF, vf, NULL); + + if (!vsi) { + dev_err(ice_pf_to_dev(pf), "Failed to create VF VSI\n"); + ice_vf_invalidate_vsi(vf); + return NULL; + } + + vf->lan_vsi_idx = vsi->idx; + vf->lan_vsi_num = vsi->vsi_num; + + return vsi; +} + +/** + * ice_calc_vf_first_vector_idx - Calculate MSIX vector index in the PF space + * @pf: pointer to PF structure + * @vf: pointer to VF that the first MSIX vector index is being calculated for + * + * This returns the first MSIX vector index in PF space that is used by this VF. + * This index is used when accessing PF relative registers such as + * GLINT_VECT2FUNC and GLINT_DYN_CTL. + * This will always be the OICR index in the AVF driver so any functionality + * using vf->first_vector_idx for queue configuration will have to increment by + * 1 to avoid meddling with the OICR index. + */ +static int ice_calc_vf_first_vector_idx(struct ice_pf *pf, struct ice_vf *vf) +{ + return pf->sriov_base_vector + vf->vf_id * pf->vfs.num_msix_per; +} + +/** + * ice_ena_vf_msix_mappings - enable VF MSIX mappings in hardware + * @vf: VF to enable MSIX mappings for + * + * Some of the registers need to be indexed/configured using hardware global + * device values and other registers need 0-based values, which represent PF + * based values. + */ +static void ice_ena_vf_msix_mappings(struct ice_vf *vf) +{ + int device_based_first_msix, device_based_last_msix; + int pf_based_first_msix, pf_based_last_msix, v; + struct ice_pf *pf = vf->pf; + int device_based_vf_id; + struct ice_hw *hw; + u32 reg; + + hw = &pf->hw; + pf_based_first_msix = vf->first_vector_idx; + pf_based_last_msix = (pf_based_first_msix + pf->vfs.num_msix_per) - 1; + + device_based_first_msix = pf_based_first_msix + + pf->hw.func_caps.common_cap.msix_vector_first_id; + device_based_last_msix = + (device_based_first_msix + pf->vfs.num_msix_per) - 1; + device_based_vf_id = vf->vf_id + hw->func_caps.vf_base_id; + + reg = (((device_based_first_msix << VPINT_ALLOC_FIRST_S) & + VPINT_ALLOC_FIRST_M) | + ((device_based_last_msix << VPINT_ALLOC_LAST_S) & + VPINT_ALLOC_LAST_M) | VPINT_ALLOC_VALID_M); + wr32(hw, VPINT_ALLOC(vf->vf_id), reg); + + reg = (((device_based_first_msix << VPINT_ALLOC_PCI_FIRST_S) + & VPINT_ALLOC_PCI_FIRST_M) | + ((device_based_last_msix << VPINT_ALLOC_PCI_LAST_S) & + VPINT_ALLOC_PCI_LAST_M) | VPINT_ALLOC_PCI_VALID_M); + wr32(hw, VPINT_ALLOC_PCI(vf->vf_id), reg); + + /* map the interrupts to its functions */ + for (v = pf_based_first_msix; v <= pf_based_last_msix; v++) { + reg = (((device_based_vf_id << GLINT_VECT2FUNC_VF_NUM_S) & + GLINT_VECT2FUNC_VF_NUM_M) | + ((hw->pf_id << GLINT_VECT2FUNC_PF_NUM_S) & + GLINT_VECT2FUNC_PF_NUM_M)); + wr32(hw, GLINT_VECT2FUNC(v), reg); + } + + /* Map mailbox interrupt to VF MSI-X vector 0 */ + wr32(hw, VPINT_MBX_CTL(device_based_vf_id), VPINT_MBX_CTL_CAUSE_ENA_M); +} + +/** + * ice_ena_vf_q_mappings - enable Rx/Tx queue mappings for a VF + * @vf: VF to enable the mappings for + * @max_txq: max Tx queues allowed on the VF's VSI + * @max_rxq: max Rx queues allowed on the VF's VSI + */ +static void ice_ena_vf_q_mappings(struct ice_vf *vf, u16 max_txq, u16 max_rxq) +{ + struct device *dev = ice_pf_to_dev(vf->pf); + struct ice_vsi *vsi = ice_get_vf_vsi(vf); + struct ice_hw *hw = &vf->pf->hw; + u32 reg; + + /* set regardless of mapping mode */ + wr32(hw, VPLAN_TXQ_MAPENA(vf->vf_id), VPLAN_TXQ_MAPENA_TX_ENA_M); + + /* VF Tx queues allocation */ + if (vsi->tx_mapping_mode == ICE_VSI_MAP_CONTIG) { + /* set the VF PF Tx queue range + * VFNUMQ value should be set to (number of queues - 1). A value + * of 0 means 1 queue and a value of 255 means 256 queues + */ + reg = (((vsi->txq_map[0] << VPLAN_TX_QBASE_VFFIRSTQ_S) & + VPLAN_TX_QBASE_VFFIRSTQ_M) | + (((max_txq - 1) << VPLAN_TX_QBASE_VFNUMQ_S) & + VPLAN_TX_QBASE_VFNUMQ_M)); + wr32(hw, VPLAN_TX_QBASE(vf->vf_id), reg); + } else { + dev_err(dev, "Scattered mode for VF Tx queues is not yet implemented\n"); + } + + /* set regardless of mapping mode */ + wr32(hw, VPLAN_RXQ_MAPENA(vf->vf_id), VPLAN_RXQ_MAPENA_RX_ENA_M); + + /* VF Rx queues allocation */ + if (vsi->rx_mapping_mode == ICE_VSI_MAP_CONTIG) { + /* set the VF PF Rx queue range + * VFNUMQ value should be set to (number of queues - 1). A value + * of 0 means 1 queue and a value of 255 means 256 queues + */ + reg = (((vsi->rxq_map[0] << VPLAN_RX_QBASE_VFFIRSTQ_S) & + VPLAN_RX_QBASE_VFFIRSTQ_M) | + (((max_rxq - 1) << VPLAN_RX_QBASE_VFNUMQ_S) & + VPLAN_RX_QBASE_VFNUMQ_M)); + wr32(hw, VPLAN_RX_QBASE(vf->vf_id), reg); + } else { + dev_err(dev, "Scattered mode for VF Rx queues is not yet implemented\n"); + } +} + +/** + * ice_ena_vf_mappings - enable VF MSIX and queue mapping + * @vf: pointer to the VF structure + */ +static void ice_ena_vf_mappings(struct ice_vf *vf) +{ + struct ice_vsi *vsi = ice_get_vf_vsi(vf); + + ice_ena_vf_msix_mappings(vf); + ice_ena_vf_q_mappings(vf, vsi->alloc_txq, vsi->alloc_rxq); +} + +/** + * ice_calc_vf_reg_idx - Calculate the VF's register index in the PF space + * @vf: VF to calculate the register index for + * @q_vector: a q_vector associated to the VF + */ +int ice_calc_vf_reg_idx(struct ice_vf *vf, struct ice_q_vector *q_vector) +{ + struct ice_pf *pf; + + if (!vf || !q_vector) + return -EINVAL; + + pf = vf->pf; + + /* always add one to account for the OICR being the first MSIX */ + return pf->sriov_base_vector + pf->vfs.num_msix_per * vf->vf_id + + q_vector->v_idx + 1; +} + +/** + * ice_get_max_valid_res_idx - Get the max valid resource index + * @res: pointer to the resource to find the max valid index for + * + * Start from the end of the ice_res_tracker and return right when we find the + * first res->list entry with the ICE_RES_VALID_BIT set. This function is only + * valid for SR-IOV because it is the only consumer that manipulates the + * res->end and this is always called when res->end is set to res->num_entries. + */ +static int ice_get_max_valid_res_idx(struct ice_res_tracker *res) +{ + int i; + + if (!res) + return -EINVAL; + + for (i = res->num_entries - 1; i >= 0; i--) + if (res->list[i] & ICE_RES_VALID_BIT) + return i; return 0; } /** - * ice_mbx_init_snapshot - Initialize mailbox snapshot structure - * @hw: pointer to the hardware structure - * @vf_count: number of VFs allocated on a PF + * ice_sriov_set_msix_res - Set any used MSIX resources + * @pf: pointer to PF structure + * @num_msix_needed: number of MSIX vectors needed for all SR-IOV VFs * - * Clear the mailbox snapshot structure and allocate memory - * for the VF counter array based on the number of VFs allocated - * on that PF. + * This function allows SR-IOV resources to be taken from the end of the PF's + * allowed HW MSIX vectors so that the irq_tracker will not be affected. We + * just set the pf->sriov_base_vector and return success. * - * Assumption: This function will assume ice_get_caps() has already been - * called to ensure that the vf_count can be compared against the number - * of VFs supported as defined in the functional capabilities of the device. + * If there are not enough resources available, return an error. This should + * always be caught by ice_set_per_vf_res(). + * + * Return 0 on success, and -EINVAL when there are not enough MSIX vectors + * in the PF's space available for SR-IOV. */ -int ice_mbx_init_snapshot(struct ice_hw *hw, u16 vf_count) +static int ice_sriov_set_msix_res(struct ice_pf *pf, u16 num_msix_needed) { - struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; + u16 total_vectors = pf->hw.func_caps.common_cap.num_msix_vectors; + int vectors_used = pf->irq_tracker->num_entries; + int sriov_base_vector; - /* Ensure that the number of VFs allocated is non-zero and - * is not greater than the number of supported VFs defined in - * the functional capabilities of the PF. + sriov_base_vector = total_vectors - num_msix_needed; + + /* make sure we only grab irq_tracker entries from the list end and + * that we have enough available MSIX vectors */ - if (!vf_count || vf_count > hw->func_caps.num_allocd_vfs) + if (sriov_base_vector < vectors_used) return -EINVAL; - snap->mbx_vf.vf_cntr = devm_kcalloc(ice_hw_to_dev(hw), vf_count, - sizeof(*snap->mbx_vf.vf_cntr), - GFP_KERNEL); - if (!snap->mbx_vf.vf_cntr) + pf->sriov_base_vector = sriov_base_vector; + + return 0; +} + +/** + * ice_set_per_vf_res - check if vectors and queues are available + * @pf: pointer to the PF structure + * @num_vfs: the number of SR-IOV VFs being configured + * + * First, determine HW interrupts from common pool. If we allocate fewer VFs, we + * get more vectors and can enable more queues per VF. Note that this does not + * grab any vectors from the SW pool already allocated. Also note, that all + * vector counts include one for each VF's miscellaneous interrupt vector + * (i.e. OICR). + * + * Minimum VFs - 2 vectors, 1 queue pair + * Small VFs - 5 vectors, 4 queue pairs + * Medium VFs - 17 vectors, 16 queue pairs + * + * Second, determine number of queue pairs per VF by starting with a pre-defined + * maximum each VF supports. If this is not possible, then we adjust based on + * queue pairs available on the device. + * + * Lastly, set queue and MSI-X VF variables tracked by the PF so it can be used + * by each VF during VF initialization and reset. + */ +static int ice_set_per_vf_res(struct ice_pf *pf, u16 num_vfs) +{ + int max_valid_res_idx = ice_get_max_valid_res_idx(pf->irq_tracker); + u16 num_msix_per_vf, num_txq, num_rxq, avail_qs; + int msix_avail_per_vf, msix_avail_for_sriov; + struct device *dev = ice_pf_to_dev(pf); + int err; + + lockdep_assert_held(&pf->vfs.table_lock); + + if (!num_vfs) + return -EINVAL; + + if (max_valid_res_idx < 0) + return -ENOSPC; + + /* determine MSI-X resources per VF */ + msix_avail_for_sriov = pf->hw.func_caps.common_cap.num_msix_vectors - + pf->irq_tracker->num_entries; + msix_avail_per_vf = msix_avail_for_sriov / num_vfs; + if (msix_avail_per_vf >= ICE_NUM_VF_MSIX_MED) { + num_msix_per_vf = ICE_NUM_VF_MSIX_MED; + } else if (msix_avail_per_vf >= ICE_NUM_VF_MSIX_SMALL) { + num_msix_per_vf = ICE_NUM_VF_MSIX_SMALL; + } else if (msix_avail_per_vf >= ICE_NUM_VF_MSIX_MULTIQ_MIN) { + num_msix_per_vf = ICE_NUM_VF_MSIX_MULTIQ_MIN; + } else if (msix_avail_per_vf >= ICE_MIN_INTR_PER_VF) { + num_msix_per_vf = ICE_MIN_INTR_PER_VF; + } else { + dev_err(dev, "Only %d MSI-X interrupts available for SR-IOV. Not enough to support minimum of %d MSI-X interrupts per VF for %d VFs\n", + msix_avail_for_sriov, ICE_MIN_INTR_PER_VF, + num_vfs); + return -ENOSPC; + } + + num_txq = min_t(u16, num_msix_per_vf - ICE_NONQ_VECS_VF, + ICE_MAX_RSS_QS_PER_VF); + avail_qs = ice_get_avail_txq_count(pf) / num_vfs; + if (!avail_qs) + num_txq = 0; + else if (num_txq > avail_qs) + num_txq = rounddown_pow_of_two(avail_qs); + + num_rxq = min_t(u16, num_msix_per_vf - ICE_NONQ_VECS_VF, + ICE_MAX_RSS_QS_PER_VF); + avail_qs = ice_get_avail_rxq_count(pf) / num_vfs; + if (!avail_qs) + num_rxq = 0; + else if (num_rxq > avail_qs) + num_rxq = rounddown_pow_of_two(avail_qs); + + if (num_txq < ICE_MIN_QS_PER_VF || num_rxq < ICE_MIN_QS_PER_VF) { + dev_err(dev, "Not enough queues to support minimum of %d queue pairs per VF for %d VFs\n", + ICE_MIN_QS_PER_VF, num_vfs); + return -ENOSPC; + } + + err = ice_sriov_set_msix_res(pf, num_msix_per_vf * num_vfs); + if (err) { + dev_err(dev, "Unable to set MSI-X resources for %d VFs, err %d\n", + num_vfs, err); + return err; + } + + /* only allow equal Tx/Rx queue count (i.e. queue pairs) */ + pf->vfs.num_qps_per = min_t(int, num_txq, num_rxq); + pf->vfs.num_msix_per = num_msix_per_vf; + dev_info(dev, "Enabling %d VFs with %d vectors and %d queues per VF\n", + num_vfs, pf->vfs.num_msix_per, pf->vfs.num_qps_per); + + return 0; +} + +/** + * ice_init_vf_vsi_res - initialize/setup VF VSI resources + * @vf: VF to initialize/setup the VSI for + * + * This function creates a VSI for the VF, adds a VLAN 0 filter, and sets up the + * VF VSI's broadcast filter and is only used during initial VF creation. + */ +static int ice_init_vf_vsi_res(struct ice_vf *vf) +{ + struct ice_vsi_vlan_ops *vlan_ops; + struct ice_pf *pf = vf->pf; + u8 broadcast[ETH_ALEN]; + struct ice_vsi *vsi; + struct device *dev; + int err; + + vf->first_vector_idx = ice_calc_vf_first_vector_idx(pf, vf); + + dev = ice_pf_to_dev(pf); + vsi = ice_vf_vsi_setup(vf); + if (!vsi) return -ENOMEM; - /* Setting the VF counter length to the number of allocated - * VFs for given PF's functional capabilities. - */ - snap->mbx_vf.vfcntr_len = vf_count; + err = ice_vsi_add_vlan_zero(vsi); + if (err) { + dev_warn(dev, "Failed to add VLAN 0 filter for VF %d\n", + vf->vf_id); + goto release_vsi; + } - /* Clear mbx_buf in the mailbox snaphot structure and setting the - * mailbox snapshot state to a new capture. + vlan_ops = ice_get_compat_vsi_vlan_ops(vsi); + err = vlan_ops->ena_rx_filtering(vsi); + if (err) { + dev_warn(dev, "Failed to enable Rx VLAN filtering for VF %d\n", + vf->vf_id); + goto release_vsi; + } + + eth_broadcast_addr(broadcast); + err = ice_fltr_add_mac(vsi, broadcast, ICE_FWD_TO_VSI); + if (err) { + dev_err(dev, "Failed to add broadcast MAC filter for VF %d, error %d\n", + vf->vf_id, err); + goto release_vsi; + } + + err = ice_vsi_apply_spoofchk(vsi, vf->spoofchk); + if (err) { + dev_warn(dev, "Failed to initialize spoofchk setting for VF %d\n", + vf->vf_id); + goto release_vsi; + } + + vf->num_mac = 1; + + return 0; + +release_vsi: + ice_vf_vsi_release(vf); + return err; +} + +/** + * ice_start_vfs - start VFs so they are ready to be used by SR-IOV + * @pf: PF the VFs are associated with + */ +static int ice_start_vfs(struct ice_pf *pf) +{ + struct ice_hw *hw = &pf->hw; + unsigned int bkt, it_cnt; + struct ice_vf *vf; + int retval; + + lockdep_assert_held(&pf->vfs.table_lock); + + it_cnt = 0; + ice_for_each_vf(pf, bkt, vf) { + vf->vf_ops->clear_reset_trigger(vf); + + retval = ice_init_vf_vsi_res(vf); + if (retval) { + dev_err(ice_pf_to_dev(pf), "Failed to initialize VSI resources for VF %d, error %d\n", + vf->vf_id, retval); + goto teardown; + } + + set_bit(ICE_VF_STATE_INIT, vf->vf_states); + ice_ena_vf_mappings(vf); + wr32(hw, VFGEN_RSTAT(vf->vf_id), VIRTCHNL_VFR_VFACTIVE); + it_cnt++; + } + + ice_flush(hw); + return 0; + +teardown: + ice_for_each_vf(pf, bkt, vf) { + if (it_cnt == 0) + break; + + ice_dis_vf_mappings(vf); + ice_vf_vsi_release(vf); + it_cnt--; + } + + return retval; +} + +/** + * ice_sriov_free_vf - Free VF memory after all references are dropped + * @vf: pointer to VF to free + * + * Called by ice_put_vf through ice_release_vf once the last reference to a VF + * structure has been dropped. + */ +static void ice_sriov_free_vf(struct ice_vf *vf) +{ + mutex_destroy(&vf->cfg_lock); + + kfree_rcu(vf, rcu); +} + +/** + * ice_sriov_clear_mbx_register - clears SRIOV VF's mailbox registers + * @vf: the vf to configure + */ +static void ice_sriov_clear_mbx_register(struct ice_vf *vf) +{ + struct ice_pf *pf = vf->pf; + + wr32(&pf->hw, VF_MBX_ARQLEN(vf->vf_id), 0); + wr32(&pf->hw, VF_MBX_ATQLEN(vf->vf_id), 0); +} + +/** + * ice_sriov_trigger_reset_register - trigger VF reset for SRIOV VF + * @vf: pointer to VF structure + * @is_vflr: true if reset occurred due to VFLR + * + * Trigger and cleanup after a VF reset for a SR-IOV VF. + */ +static void ice_sriov_trigger_reset_register(struct ice_vf *vf, bool is_vflr) +{ + struct ice_pf *pf = vf->pf; + u32 reg, reg_idx, bit_idx; + unsigned int vf_abs_id, i; + struct device *dev; + struct ice_hw *hw; + + dev = ice_pf_to_dev(pf); + hw = &pf->hw; + vf_abs_id = vf->vf_id + hw->func_caps.vf_base_id; + + /* In the case of a VFLR, HW has already reset the VF and we just need + * to clean up. Otherwise we must first trigger the reset using the + * VFRTRIG register. */ - memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf)); - snap->mbx_buf.state = ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT; + if (!is_vflr) { + reg = rd32(hw, VPGEN_VFRTRIG(vf->vf_id)); + reg |= VPGEN_VFRTRIG_VFSWR_M; + wr32(hw, VPGEN_VFRTRIG(vf->vf_id), reg); + } + + /* clear the VFLR bit in GLGEN_VFLRSTAT */ + reg_idx = (vf_abs_id) / 32; + bit_idx = (vf_abs_id) % 32; + wr32(hw, GLGEN_VFLRSTAT(reg_idx), BIT(bit_idx)); + ice_flush(hw); + + wr32(hw, PF_PCI_CIAA, + VF_DEVICE_STATUS | (vf_abs_id << PF_PCI_CIAA_VF_NUM_S)); + for (i = 0; i < ICE_PCI_CIAD_WAIT_COUNT; i++) { + reg = rd32(hw, PF_PCI_CIAD); + /* no transactions pending so stop polling */ + if ((reg & VF_TRANS_PENDING_M) == 0) + break; + + dev_err(dev, "VF %u PCI transactions stuck\n", vf->vf_id); + udelay(ICE_PCI_CIAD_WAIT_DELAY_US); + } +} + +/** + * ice_sriov_poll_reset_status - poll SRIOV VF reset status + * @vf: pointer to VF structure + * + * Returns true when reset is successful, else returns false + */ +static bool ice_sriov_poll_reset_status(struct ice_vf *vf) +{ + struct ice_pf *pf = vf->pf; + unsigned int i; + u32 reg; + + for (i = 0; i < 10; i++) { + /* VF reset requires driver to first reset the VF and then + * poll the status register to make sure that the reset + * completed successfully. + */ + reg = rd32(&pf->hw, VPGEN_VFRSTAT(vf->vf_id)); + if (reg & VPGEN_VFRSTAT_VFRD_M) + return true; + + /* only sleep if the reset is not done */ + usleep_range(10, 20); + } + return false; +} + +/** + * ice_sriov_clear_reset_trigger - enable VF to access hardware + * @vf: VF to enabled hardware access for + */ +static void ice_sriov_clear_reset_trigger(struct ice_vf *vf) +{ + struct ice_hw *hw = &vf->pf->hw; + u32 reg; + + reg = rd32(hw, VPGEN_VFRTRIG(vf->vf_id)); + reg &= ~VPGEN_VFRTRIG_VFSWR_M; + wr32(hw, VPGEN_VFRTRIG(vf->vf_id), reg); + ice_flush(hw); +} + +/** + * ice_sriov_vsi_rebuild - release and rebuild VF's VSI + * @vf: VF to release and setup the VSI for + * + * This is only called when a single VF is being reset (i.e. VFR, VFLR, host VF + * configuration change, etc.). + */ +static int ice_sriov_vsi_rebuild(struct ice_vf *vf) +{ + struct ice_pf *pf = vf->pf; + + ice_vf_vsi_release(vf); + if (!ice_vf_vsi_setup(vf)) { + dev_err(ice_pf_to_dev(pf), + "Failed to release and setup the VF%u's VSI\n", + vf->vf_id); + return -ENOMEM; + } return 0; } /** - * ice_mbx_deinit_snapshot - Free mailbox snapshot structure - * @hw: pointer to the hardware structure - * - * Clear the mailbox snapshot structure and free the VF counter array. + * ice_sriov_post_vsi_rebuild - tasks to do after the VF's VSI have been rebuilt + * @vf: VF to perform tasks on */ -void ice_mbx_deinit_snapshot(struct ice_hw *hw) +static void ice_sriov_post_vsi_rebuild(struct ice_vf *vf) { - struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; - - /* Free VF counter array and reset VF counter length */ - devm_kfree(ice_hw_to_dev(hw), snap->mbx_vf.vf_cntr); - snap->mbx_vf.vfcntr_len = 0; - - /* Clear mbx_buf in the mailbox snaphot structure */ - memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf)); + ice_vf_rebuild_host_cfg(vf); + ice_vf_set_initialized(vf); + ice_ena_vf_mappings(vf); + wr32(&vf->pf->hw, VFGEN_RSTAT(vf->vf_id), VIRTCHNL_VFR_VFACTIVE); +} + +static const struct ice_vf_ops ice_sriov_vf_ops = { + .reset_type = ICE_VF_RESET, + .free = ice_sriov_free_vf, + .clear_mbx_register = ice_sriov_clear_mbx_register, + .trigger_reset_register = ice_sriov_trigger_reset_register, + .poll_reset_status = ice_sriov_poll_reset_status, + .clear_reset_trigger = ice_sriov_clear_reset_trigger, + .vsi_rebuild = ice_sriov_vsi_rebuild, + .post_vsi_rebuild = ice_sriov_post_vsi_rebuild, +}; + +/** + * ice_create_vf_entries - Allocate and insert VF entries + * @pf: pointer to the PF structure + * @num_vfs: the number of VFs to allocate + * + * Allocate new VF entries and insert them into the hash table. Set some + * basic default fields for initializing the new VFs. + * + * After this function exits, the hash table will have num_vfs entries + * inserted. + * + * Returns 0 on success or an integer error code on failure. + */ +static int ice_create_vf_entries(struct ice_pf *pf, u16 num_vfs) +{ + struct ice_vfs *vfs = &pf->vfs; + struct ice_vf *vf; + u16 vf_id; + int err; + + lockdep_assert_held(&vfs->table_lock); + + for (vf_id = 0; vf_id < num_vfs; vf_id++) { + vf = kzalloc(sizeof(*vf), GFP_KERNEL); + if (!vf) { + err = -ENOMEM; + goto err_free_entries; + } + kref_init(&vf->refcnt); + + vf->pf = pf; + vf->vf_id = vf_id; + + /* set sriov vf ops for VFs created during SRIOV flow */ + vf->vf_ops = &ice_sriov_vf_ops; + + vf->vf_sw_id = pf->first_sw; + /* assign default capabilities */ + vf->spoofchk = true; + vf->num_vf_qs = pf->vfs.num_qps_per; + ice_vc_set_default_allowlist(vf); + + /* ctrl_vsi_idx will be set to a valid value only when VF + * creates its first fdir rule. + */ + ice_vf_ctrl_invalidate_vsi(vf); + ice_vf_fdir_init(vf); + + ice_virtchnl_set_dflt_ops(vf); + + mutex_init(&vf->cfg_lock); + + hash_add_rcu(vfs->table, &vf->entry, vf_id); + } + + return 0; + +err_free_entries: + ice_free_vf_entries(pf); + return err; +} + +/** + * ice_ena_vfs - enable VFs so they are ready to be used + * @pf: pointer to the PF structure + * @num_vfs: number of VFs to enable + */ +static int ice_ena_vfs(struct ice_pf *pf, u16 num_vfs) +{ + struct device *dev = ice_pf_to_dev(pf); + struct ice_hw *hw = &pf->hw; + int ret; + + /* Disable global interrupt 0 so we don't try to handle the VFLR. */ + wr32(hw, GLINT_DYN_CTL(pf->oicr_idx), + ICE_ITR_NONE << GLINT_DYN_CTL_ITR_INDX_S); + set_bit(ICE_OICR_INTR_DIS, pf->state); + ice_flush(hw); + + ret = pci_enable_sriov(pf->pdev, num_vfs); + if (ret) + goto err_unroll_intr; + + mutex_lock(&pf->vfs.table_lock); + + ret = ice_set_per_vf_res(pf, num_vfs); + if (ret) { + dev_err(dev, "Not enough resources for %d VFs, err %d. Try with fewer number of VFs\n", + num_vfs, ret); + goto err_unroll_sriov; + } + + ret = ice_create_vf_entries(pf, num_vfs); + if (ret) { + dev_err(dev, "Failed to allocate VF entries for %d VFs\n", + num_vfs); + goto err_unroll_sriov; + } + + ret = ice_start_vfs(pf); + if (ret) { + dev_err(dev, "Failed to start %d VFs, err %d\n", num_vfs, ret); + ret = -EAGAIN; + goto err_unroll_vf_entries; + } + + clear_bit(ICE_VF_DIS, pf->state); + + ret = ice_eswitch_configure(pf); + if (ret) { + dev_err(dev, "Failed to configure eswitch, err %d\n", ret); + goto err_unroll_sriov; + } + + /* rearm global interrupts */ + if (test_and_clear_bit(ICE_OICR_INTR_DIS, pf->state)) + ice_irq_dynamic_ena(hw, NULL, NULL); + + mutex_unlock(&pf->vfs.table_lock); + + return 0; + +err_unroll_vf_entries: + ice_free_vf_entries(pf); +err_unroll_sriov: + mutex_unlock(&pf->vfs.table_lock); + pci_disable_sriov(pf->pdev); +err_unroll_intr: + /* rearm interrupts here */ + ice_irq_dynamic_ena(hw, NULL, NULL); + clear_bit(ICE_OICR_INTR_DIS, pf->state); + return ret; +} + +/** + * ice_pci_sriov_ena - Enable or change number of VFs + * @pf: pointer to the PF structure + * @num_vfs: number of VFs to allocate + * + * Returns 0 on success and negative on failure + */ +static int ice_pci_sriov_ena(struct ice_pf *pf, int num_vfs) +{ + int pre_existing_vfs = pci_num_vf(pf->pdev); + struct device *dev = ice_pf_to_dev(pf); + int err; + + if (pre_existing_vfs && pre_existing_vfs != num_vfs) + ice_free_vfs(pf); + else if (pre_existing_vfs && pre_existing_vfs == num_vfs) + return 0; + + if (num_vfs > pf->vfs.num_supported) { + dev_err(dev, "Can't enable %d VFs, max VFs supported is %d\n", + num_vfs, pf->vfs.num_supported); + return -EOPNOTSUPP; + } + + dev_info(dev, "Enabling %d VFs\n", num_vfs); + err = ice_ena_vfs(pf, num_vfs); + if (err) { + dev_err(dev, "Failed to enable SR-IOV: %d\n", err); + return err; + } + + set_bit(ICE_FLAG_SRIOV_ENA, pf->flags); + return 0; +} + +/** + * ice_check_sriov_allowed - check if SR-IOV is allowed based on various checks + * @pf: PF to enabled SR-IOV on + */ +static int ice_check_sriov_allowed(struct ice_pf *pf) +{ + struct device *dev = ice_pf_to_dev(pf); + + if (!test_bit(ICE_FLAG_SRIOV_CAPABLE, pf->flags)) { + dev_err(dev, "This device is not capable of SR-IOV\n"); + return -EOPNOTSUPP; + } + + if (ice_is_safe_mode(pf)) { + dev_err(dev, "SR-IOV cannot be configured - Device is in Safe Mode\n"); + return -EOPNOTSUPP; + } + + if (!ice_pf_state_is_nominal(pf)) { + dev_err(dev, "Cannot enable SR-IOV, device not ready\n"); + return -EBUSY; + } + + return 0; +} + +/** + * ice_sriov_configure - Enable or change number of VFs via sysfs + * @pdev: pointer to a pci_dev structure + * @num_vfs: number of VFs to allocate or 0 to free VFs + * + * This function is called when the user updates the number of VFs in sysfs. On + * success return whatever num_vfs was set to by the caller. Return negative on + * failure. + */ +int ice_sriov_configure(struct pci_dev *pdev, int num_vfs) +{ + struct ice_pf *pf = pci_get_drvdata(pdev); + struct device *dev = ice_pf_to_dev(pf); + int err; + + err = ice_check_sriov_allowed(pf); + if (err) + return err; + + if (!num_vfs) { + if (!pci_vfs_assigned(pdev)) { + ice_mbx_deinit_snapshot(&pf->hw); + ice_free_vfs(pf); + if (pf->lag) + ice_enable_lag(pf->lag); + return 0; + } + + dev_err(dev, "can't free VFs because some are assigned to VMs.\n"); + return -EBUSY; + } + + err = ice_mbx_init_snapshot(&pf->hw, num_vfs); + if (err) + return err; + + err = ice_pci_sriov_ena(pf, num_vfs); + if (err) { + ice_mbx_deinit_snapshot(&pf->hw); + return err; + } + + if (pf->lag) + ice_disable_lag(pf->lag); + return num_vfs; +} + +/** + * ice_process_vflr_event - Free VF resources via IRQ calls + * @pf: pointer to the PF structure + * + * called from the VFLR IRQ handler to + * free up VF resources and state variables + */ +void ice_process_vflr_event(struct ice_pf *pf) +{ + struct ice_hw *hw = &pf->hw; + struct ice_vf *vf; + unsigned int bkt; + u32 reg; + + if (!test_and_clear_bit(ICE_VFLR_EVENT_PENDING, pf->state) || + !ice_has_vfs(pf)) + return; + + mutex_lock(&pf->vfs.table_lock); + ice_for_each_vf(pf, bkt, vf) { + u32 reg_idx, bit_idx; + + reg_idx = (hw->func_caps.vf_base_id + vf->vf_id) / 32; + bit_idx = (hw->func_caps.vf_base_id + vf->vf_id) % 32; + /* read GLGEN_VFLRSTAT register to find out the flr VFs */ + reg = rd32(hw, GLGEN_VFLRSTAT(reg_idx)); + if (reg & BIT(bit_idx)) + /* GLGEN_VFLRSTAT bit will be cleared in ice_reset_vf */ + ice_reset_vf(vf, ICE_VF_RESET_VFLR | ICE_VF_RESET_LOCK); + } + mutex_unlock(&pf->vfs.table_lock); +} + +/** + * ice_get_vf_from_pfq - get the VF who owns the PF space queue passed in + * @pf: PF used to index all VFs + * @pfq: queue index relative to the PF's function space + * + * If no VF is found who owns the pfq then return NULL, otherwise return a + * pointer to the VF who owns the pfq + * + * If this function returns non-NULL, it acquires a reference count of the VF + * structure. The caller is responsible for calling ice_put_vf() to drop this + * reference. + */ +static struct ice_vf *ice_get_vf_from_pfq(struct ice_pf *pf, u16 pfq) +{ + struct ice_vf *vf; + unsigned int bkt; + + rcu_read_lock(); + ice_for_each_vf_rcu(pf, bkt, vf) { + struct ice_vsi *vsi; + u16 rxq_idx; + + vsi = ice_get_vf_vsi(vf); + + ice_for_each_rxq(vsi, rxq_idx) + if (vsi->rxq_map[rxq_idx] == pfq) { + struct ice_vf *found; + + if (kref_get_unless_zero(&vf->refcnt)) + found = vf; + else + found = NULL; + rcu_read_unlock(); + return found; + } + } + rcu_read_unlock(); + + return NULL; +} + +/** + * ice_globalq_to_pfq - convert from global queue index to PF space queue index + * @pf: PF used for conversion + * @globalq: global queue index used to convert to PF space queue index + */ +static u32 ice_globalq_to_pfq(struct ice_pf *pf, u32 globalq) +{ + return globalq - pf->hw.func_caps.common_cap.rxq_first_id; +} + +/** + * ice_vf_lan_overflow_event - handle LAN overflow event for a VF + * @pf: PF that the LAN overflow event happened on + * @event: structure holding the event information for the LAN overflow event + * + * Determine if the LAN overflow event was caused by a VF queue. If it was not + * caused by a VF, do nothing. If a VF caused this LAN overflow event trigger a + * reset on the offending VF. + */ +void +ice_vf_lan_overflow_event(struct ice_pf *pf, struct ice_rq_event_info *event) +{ + u32 gldcb_rtctq, queue; + struct ice_vf *vf; + + gldcb_rtctq = le32_to_cpu(event->desc.params.lan_overflow.prtdcb_ruptq); + dev_dbg(ice_pf_to_dev(pf), "GLDCB_RTCTQ: 0x%08x\n", gldcb_rtctq); + + /* event returns device global Rx queue number */ + queue = (gldcb_rtctq & GLDCB_RTCTQ_RXQNUM_M) >> + GLDCB_RTCTQ_RXQNUM_S; + + vf = ice_get_vf_from_pfq(pf, ice_globalq_to_pfq(pf, queue)); + if (!vf) + return; + + ice_reset_vf(vf, ICE_VF_RESET_NOTIFY | ICE_VF_RESET_LOCK); + ice_put_vf(vf); +} + +/** + * ice_set_vf_spoofchk + * @netdev: network interface device structure + * @vf_id: VF identifier + * @ena: flag to enable or disable feature + * + * Enable or disable VF spoof checking + */ +int ice_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool ena) +{ + struct ice_netdev_priv *np = netdev_priv(netdev); + struct ice_pf *pf = np->vsi->back; + struct ice_vsi *vf_vsi; + struct device *dev; + struct ice_vf *vf; + int ret; + + dev = ice_pf_to_dev(pf); + + vf = ice_get_vf_by_id(pf, vf_id); + if (!vf) + return -EINVAL; + + ret = ice_check_vf_ready_for_cfg(vf); + if (ret) + goto out_put_vf; + + vf_vsi = ice_get_vf_vsi(vf); + if (!vf_vsi) { + netdev_err(netdev, "VSI %d for VF %d is null\n", + vf->lan_vsi_idx, vf->vf_id); + ret = -EINVAL; + goto out_put_vf; + } + + if (vf_vsi->type != ICE_VSI_VF) { + netdev_err(netdev, "Type %d of VSI %d for VF %d is no ICE_VSI_VF\n", + vf_vsi->type, vf_vsi->vsi_num, vf->vf_id); + ret = -ENODEV; + goto out_put_vf; + } + + if (ena == vf->spoofchk) { + dev_dbg(dev, "VF spoofchk already %s\n", ena ? "ON" : "OFF"); + ret = 0; + goto out_put_vf; + } + + ret = ice_vsi_apply_spoofchk(vf_vsi, ena); + if (ret) + dev_err(dev, "Failed to set spoofchk %s for VF %d VSI %d\n error %d\n", + ena ? "ON" : "OFF", vf->vf_id, vf_vsi->vsi_num, ret); + else + vf->spoofchk = ena; + +out_put_vf: + ice_put_vf(vf); + return ret; +} + +/** + * ice_get_vf_cfg + * @netdev: network interface device structure + * @vf_id: VF identifier + * @ivi: VF configuration structure + * + * return VF configuration + */ +int +ice_get_vf_cfg(struct net_device *netdev, int vf_id, struct ifla_vf_info *ivi) +{ + struct ice_pf *pf = ice_netdev_to_pf(netdev); + struct ice_vf *vf; + int ret; + + vf = ice_get_vf_by_id(pf, vf_id); + if (!vf) + return -EINVAL; + + ret = ice_check_vf_ready_for_cfg(vf); + if (ret) + goto out_put_vf; + + ivi->vf = vf_id; + ether_addr_copy(ivi->mac, vf->hw_lan_addr.addr); + + /* VF configuration for VLAN and applicable QoS */ + ivi->vlan = ice_vf_get_port_vlan_id(vf); + ivi->qos = ice_vf_get_port_vlan_prio(vf); + if (ice_vf_is_port_vlan_ena(vf)) + ivi->vlan_proto = cpu_to_be16(ice_vf_get_port_vlan_tpid(vf)); + + ivi->trusted = vf->trusted; + ivi->spoofchk = vf->spoofchk; + if (!vf->link_forced) + ivi->linkstate = IFLA_VF_LINK_STATE_AUTO; + else if (vf->link_up) + ivi->linkstate = IFLA_VF_LINK_STATE_ENABLE; + else + ivi->linkstate = IFLA_VF_LINK_STATE_DISABLE; + ivi->max_tx_rate = vf->max_tx_rate; + ivi->min_tx_rate = vf->min_tx_rate; + +out_put_vf: + ice_put_vf(vf); + return ret; +} + +/** + * ice_unicast_mac_exists - check if the unicast MAC exists on the PF's switch + * @pf: PF used to reference the switch's rules + * @umac: unicast MAC to compare against existing switch rules + * + * Return true on the first/any match, else return false + */ +static bool ice_unicast_mac_exists(struct ice_pf *pf, u8 *umac) +{ + struct ice_sw_recipe *mac_recipe_list = + &pf->hw.switch_info->recp_list[ICE_SW_LKUP_MAC]; + struct ice_fltr_mgmt_list_entry *list_itr; + struct list_head *rule_head; + struct mutex *rule_lock; /* protect MAC filter list access */ + + rule_head = &mac_recipe_list->filt_rules; + rule_lock = &mac_recipe_list->filt_rule_lock; + + mutex_lock(rule_lock); + list_for_each_entry(list_itr, rule_head, list_entry) { + u8 *existing_mac = &list_itr->fltr_info.l_data.mac.mac_addr[0]; + + if (ether_addr_equal(existing_mac, umac)) { + mutex_unlock(rule_lock); + return true; + } + } + + mutex_unlock(rule_lock); + + return false; +} + +/** + * ice_set_vf_mac + * @netdev: network interface device structure + * @vf_id: VF identifier + * @mac: MAC address + * + * program VF MAC address + */ +int ice_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) +{ + struct ice_pf *pf = ice_netdev_to_pf(netdev); + struct ice_vf *vf; + int ret; + + if (is_multicast_ether_addr(mac)) { + netdev_err(netdev, "%pM not a valid unicast address\n", mac); + return -EINVAL; + } + + vf = ice_get_vf_by_id(pf, vf_id); + if (!vf) + return -EINVAL; + + /* nothing left to do, unicast MAC already set */ + if (ether_addr_equal(vf->dev_lan_addr.addr, mac) && + ether_addr_equal(vf->hw_lan_addr.addr, mac)) { + ret = 0; + goto out_put_vf; + } + + ret = ice_check_vf_ready_for_cfg(vf); + if (ret) + goto out_put_vf; + + if (ice_unicast_mac_exists(pf, mac)) { + netdev_err(netdev, "Unicast MAC %pM already exists on this PF. Preventing setting VF %u unicast MAC address to %pM\n", + mac, vf_id, mac); + ret = -EINVAL; + goto out_put_vf; + } + + mutex_lock(&vf->cfg_lock); + + /* VF is notified of its new MAC via the PF's response to the + * VIRTCHNL_OP_GET_VF_RESOURCES message after the VF has been reset + */ + ether_addr_copy(vf->dev_lan_addr.addr, mac); + ether_addr_copy(vf->hw_lan_addr.addr, mac); + if (is_zero_ether_addr(mac)) { + /* VF will send VIRTCHNL_OP_ADD_ETH_ADDR message with its MAC */ + vf->pf_set_mac = false; + netdev_info(netdev, "Removing MAC on VF %d. VF driver will be reinitialized\n", + vf->vf_id); + } else { + /* PF will add MAC rule for the VF */ + vf->pf_set_mac = true; + netdev_info(netdev, "Setting MAC %pM on VF %d. VF driver will be reinitialized\n", + mac, vf_id); + } + + ice_reset_vf(vf, ICE_VF_RESET_NOTIFY); + mutex_unlock(&vf->cfg_lock); + +out_put_vf: + ice_put_vf(vf); + return ret; +} + +/** + * ice_set_vf_trust + * @netdev: network interface device structure + * @vf_id: VF identifier + * @trusted: Boolean value to enable/disable trusted VF + * + * Enable or disable a given VF as trusted + */ +int ice_set_vf_trust(struct net_device *netdev, int vf_id, bool trusted) +{ + struct ice_pf *pf = ice_netdev_to_pf(netdev); + struct ice_vf *vf; + int ret; + + if (ice_is_eswitch_mode_switchdev(pf)) { + dev_info(ice_pf_to_dev(pf), "Trusted VF is forbidden in switchdev mode\n"); + return -EOPNOTSUPP; + } + + vf = ice_get_vf_by_id(pf, vf_id); + if (!vf) + return -EINVAL; + + ret = ice_check_vf_ready_for_cfg(vf); + if (ret) + goto out_put_vf; + + /* Check if already trusted */ + if (trusted == vf->trusted) { + ret = 0; + goto out_put_vf; + } + + mutex_lock(&vf->cfg_lock); + + vf->trusted = trusted; + ice_reset_vf(vf, ICE_VF_RESET_NOTIFY); + dev_info(ice_pf_to_dev(pf), "VF %u is now %strusted\n", + vf_id, trusted ? "" : "un"); + + mutex_unlock(&vf->cfg_lock); + +out_put_vf: + ice_put_vf(vf); + return ret; +} + +/** + * ice_set_vf_link_state + * @netdev: network interface device structure + * @vf_id: VF identifier + * @link_state: required link state + * + * Set VF's link state, irrespective of physical link state status + */ +int ice_set_vf_link_state(struct net_device *netdev, int vf_id, int link_state) +{ + struct ice_pf *pf = ice_netdev_to_pf(netdev); + struct ice_vf *vf; + int ret; + + vf = ice_get_vf_by_id(pf, vf_id); + if (!vf) + return -EINVAL; + + ret = ice_check_vf_ready_for_cfg(vf); + if (ret) + goto out_put_vf; + + switch (link_state) { + case IFLA_VF_LINK_STATE_AUTO: + vf->link_forced = false; + break; + case IFLA_VF_LINK_STATE_ENABLE: + vf->link_forced = true; + vf->link_up = true; + break; + case IFLA_VF_LINK_STATE_DISABLE: + vf->link_forced = true; + vf->link_up = false; + break; + default: + ret = -EINVAL; + goto out_put_vf; + } + + ice_vc_notify_vf_link_state(vf); + +out_put_vf: + ice_put_vf(vf); + return ret; +} + +/** + * ice_calc_all_vfs_min_tx_rate - calculate cumulative min Tx rate on all VFs + * @pf: PF associated with VFs + */ +static int ice_calc_all_vfs_min_tx_rate(struct ice_pf *pf) +{ + struct ice_vf *vf; + unsigned int bkt; + int rate = 0; + + rcu_read_lock(); + ice_for_each_vf_rcu(pf, bkt, vf) + rate += vf->min_tx_rate; + rcu_read_unlock(); + + return rate; +} + +/** + * ice_min_tx_rate_oversubscribed - check if min Tx rate causes oversubscription + * @vf: VF trying to configure min_tx_rate + * @min_tx_rate: min Tx rate in Mbps + * + * Check if the min_tx_rate being passed in will cause oversubscription of total + * min_tx_rate based on the current link speed and all other VFs configured + * min_tx_rate + * + * Return true if the passed min_tx_rate would cause oversubscription, else + * return false + */ +static bool +ice_min_tx_rate_oversubscribed(struct ice_vf *vf, int min_tx_rate) +{ + int link_speed_mbps = ice_get_link_speed_mbps(ice_get_vf_vsi(vf)); + int all_vfs_min_tx_rate = ice_calc_all_vfs_min_tx_rate(vf->pf); + + /* this VF's previous rate is being overwritten */ + all_vfs_min_tx_rate -= vf->min_tx_rate; + + if (all_vfs_min_tx_rate + min_tx_rate > link_speed_mbps) { + dev_err(ice_pf_to_dev(vf->pf), "min_tx_rate of %d Mbps on VF %u would cause oversubscription of %d Mbps based on the current link speed %d Mbps\n", + min_tx_rate, vf->vf_id, + all_vfs_min_tx_rate + min_tx_rate - link_speed_mbps, + link_speed_mbps); + return true; + } + + return false; +} + +/** + * ice_set_vf_bw - set min/max VF bandwidth + * @netdev: network interface device structure + * @vf_id: VF identifier + * @min_tx_rate: Minimum Tx rate in Mbps + * @max_tx_rate: Maximum Tx rate in Mbps + */ +int +ice_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, + int max_tx_rate) +{ + struct ice_pf *pf = ice_netdev_to_pf(netdev); + struct ice_vsi *vsi; + struct device *dev; + struct ice_vf *vf; + int ret; + + dev = ice_pf_to_dev(pf); + + vf = ice_get_vf_by_id(pf, vf_id); + if (!vf) + return -EINVAL; + + ret = ice_check_vf_ready_for_cfg(vf); + if (ret) + goto out_put_vf; + + vsi = ice_get_vf_vsi(vf); + + /* when max_tx_rate is zero that means no max Tx rate limiting, so only + * check if max_tx_rate is non-zero + */ + if (max_tx_rate && min_tx_rate > max_tx_rate) { + dev_err(dev, "Cannot set min Tx rate %d Mbps greater than max Tx rate %d Mbps\n", + min_tx_rate, max_tx_rate); + ret = -EINVAL; + goto out_put_vf; + } + + if (min_tx_rate && ice_is_dcb_active(pf)) { + dev_err(dev, "DCB on PF is currently enabled. VF min Tx rate limiting not allowed on this PF.\n"); + ret = -EOPNOTSUPP; + goto out_put_vf; + } + + if (ice_min_tx_rate_oversubscribed(vf, min_tx_rate)) { + ret = -EINVAL; + goto out_put_vf; + } + + if (vf->min_tx_rate != (unsigned int)min_tx_rate) { + ret = ice_set_min_bw_limit(vsi, (u64)min_tx_rate * 1000); + if (ret) { + dev_err(dev, "Unable to set min-tx-rate for VF %d\n", + vf->vf_id); + goto out_put_vf; + } + + vf->min_tx_rate = min_tx_rate; + } + + if (vf->max_tx_rate != (unsigned int)max_tx_rate) { + ret = ice_set_max_bw_limit(vsi, (u64)max_tx_rate * 1000); + if (ret) { + dev_err(dev, "Unable to set max-tx-rate for VF %d\n", + vf->vf_id); + goto out_put_vf; + } + + vf->max_tx_rate = max_tx_rate; + } + +out_put_vf: + ice_put_vf(vf); + return ret; +} + +/** + * ice_get_vf_stats - populate some stats for the VF + * @netdev: the netdev of the PF + * @vf_id: the host OS identifier (0-255) + * @vf_stats: pointer to the OS memory to be initialized + */ +int ice_get_vf_stats(struct net_device *netdev, int vf_id, + struct ifla_vf_stats *vf_stats) +{ + struct ice_pf *pf = ice_netdev_to_pf(netdev); + struct ice_eth_stats *stats; + struct ice_vsi *vsi; + struct ice_vf *vf; + int ret; + + vf = ice_get_vf_by_id(pf, vf_id); + if (!vf) + return -EINVAL; + + ret = ice_check_vf_ready_for_cfg(vf); + if (ret) + goto out_put_vf; + + vsi = ice_get_vf_vsi(vf); + if (!vsi) { + ret = -EINVAL; + goto out_put_vf; + } + + ice_update_eth_stats(vsi); + stats = &vsi->eth_stats; + + memset(vf_stats, 0, sizeof(*vf_stats)); + + vf_stats->rx_packets = stats->rx_unicast + stats->rx_broadcast + + stats->rx_multicast; + vf_stats->tx_packets = stats->tx_unicast + stats->tx_broadcast + + stats->tx_multicast; + vf_stats->rx_bytes = stats->rx_bytes; + vf_stats->tx_bytes = stats->tx_bytes; + vf_stats->broadcast = stats->rx_broadcast; + vf_stats->multicast = stats->rx_multicast; + vf_stats->rx_dropped = stats->rx_discards; + vf_stats->tx_dropped = stats->tx_discards; + +out_put_vf: + ice_put_vf(vf); + return ret; +} + +/** + * ice_is_supported_port_vlan_proto - make sure the vlan_proto is supported + * @hw: hardware structure used to check the VLAN mode + * @vlan_proto: VLAN TPID being checked + * + * If the device is configured in Double VLAN Mode (DVM), then both ETH_P_8021Q + * and ETH_P_8021AD are supported. If the device is configured in Single VLAN + * Mode (SVM), then only ETH_P_8021Q is supported. + */ +static bool +ice_is_supported_port_vlan_proto(struct ice_hw *hw, u16 vlan_proto) +{ + bool is_supported = false; + + switch (vlan_proto) { + case ETH_P_8021Q: + is_supported = true; + break; + case ETH_P_8021AD: + if (ice_is_dvm_ena(hw)) + is_supported = true; + break; + } + + return is_supported; +} + +/** + * ice_set_vf_port_vlan + * @netdev: network interface device structure + * @vf_id: VF identifier + * @vlan_id: VLAN ID being set + * @qos: priority setting + * @vlan_proto: VLAN protocol + * + * program VF Port VLAN ID and/or QoS + */ +int +ice_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos, + __be16 vlan_proto) +{ + struct ice_pf *pf = ice_netdev_to_pf(netdev); + u16 local_vlan_proto = ntohs(vlan_proto); + struct device *dev; + struct ice_vf *vf; + int ret; + + dev = ice_pf_to_dev(pf); + + if (vlan_id >= VLAN_N_VID || qos > 7) { + dev_err(dev, "Invalid Port VLAN parameters for VF %d, ID %d, QoS %d\n", + vf_id, vlan_id, qos); + return -EINVAL; + } + + if (!ice_is_supported_port_vlan_proto(&pf->hw, local_vlan_proto)) { + dev_err(dev, "VF VLAN protocol 0x%04x is not supported\n", + local_vlan_proto); + return -EPROTONOSUPPORT; + } + + vf = ice_get_vf_by_id(pf, vf_id); + if (!vf) + return -EINVAL; + + ret = ice_check_vf_ready_for_cfg(vf); + if (ret) + goto out_put_vf; + + if (ice_vf_get_port_vlan_prio(vf) == qos && + ice_vf_get_port_vlan_tpid(vf) == local_vlan_proto && + ice_vf_get_port_vlan_id(vf) == vlan_id) { + /* duplicate request, so just return success */ + dev_dbg(dev, "Duplicate port VLAN %u, QoS %u, TPID 0x%04x request\n", + vlan_id, qos, local_vlan_proto); + ret = 0; + goto out_put_vf; + } + + mutex_lock(&vf->cfg_lock); + + vf->port_vlan_info = ICE_VLAN(local_vlan_proto, vlan_id, qos); + if (ice_vf_is_port_vlan_ena(vf)) + dev_info(dev, "Setting VLAN %u, QoS %u, TPID 0x%04x on VF %d\n", + vlan_id, qos, local_vlan_proto, vf_id); + else + dev_info(dev, "Clearing port VLAN on VF %d\n", vf_id); + + ice_reset_vf(vf, ICE_VF_RESET_NOTIFY); + mutex_unlock(&vf->cfg_lock); + +out_put_vf: + ice_put_vf(vf); + return ret; +} + +/** + * ice_print_vf_rx_mdd_event - print VF Rx malicious driver detect event + * @vf: pointer to the VF structure + */ +void ice_print_vf_rx_mdd_event(struct ice_vf *vf) +{ + struct ice_pf *pf = vf->pf; + struct device *dev; + + dev = ice_pf_to_dev(pf); + + dev_info(dev, "%d Rx Malicious Driver Detection events detected on PF %d VF %d MAC %pM. mdd-auto-reset-vfs=%s\n", + vf->mdd_rx_events.count, pf->hw.pf_id, vf->vf_id, + vf->dev_lan_addr.addr, + test_bit(ICE_FLAG_MDD_AUTO_RESET_VF, pf->flags) + ? "on" : "off"); +} + +/** + * ice_print_vfs_mdd_events - print VFs malicious driver detect event + * @pf: pointer to the PF structure + * + * Called from ice_handle_mdd_event to rate limit and print VFs MDD events. + */ +void ice_print_vfs_mdd_events(struct ice_pf *pf) +{ + struct device *dev = ice_pf_to_dev(pf); + struct ice_hw *hw = &pf->hw; + struct ice_vf *vf; + unsigned int bkt; + + /* check that there are pending MDD events to print */ + if (!test_and_clear_bit(ICE_MDD_VF_PRINT_PENDING, pf->state)) + return; + + /* VF MDD event logs are rate limited to one second intervals */ + if (time_is_after_jiffies(pf->vfs.last_printed_mdd_jiffies + HZ * 1)) + return; + + pf->vfs.last_printed_mdd_jiffies = jiffies; + + mutex_lock(&pf->vfs.table_lock); + ice_for_each_vf(pf, bkt, vf) { + /* only print Rx MDD event message if there are new events */ + if (vf->mdd_rx_events.count != vf->mdd_rx_events.last_printed) { + vf->mdd_rx_events.last_printed = + vf->mdd_rx_events.count; + ice_print_vf_rx_mdd_event(vf); + } + + /* only print Tx MDD event message if there are new events */ + if (vf->mdd_tx_events.count != vf->mdd_tx_events.last_printed) { + vf->mdd_tx_events.last_printed = + vf->mdd_tx_events.count; + + dev_info(dev, "%d Tx Malicious Driver Detection events detected on PF %d VF %d MAC %pM.\n", + vf->mdd_tx_events.count, hw->pf_id, vf->vf_id, + vf->dev_lan_addr.addr); + } + } + mutex_unlock(&pf->vfs.table_lock); +} + +/** + * ice_restore_all_vfs_msi_state - restore VF MSI state after PF FLR + * @pdev: pointer to a pci_dev structure + * + * Called when recovering from a PF FLR to restore interrupt capability to + * the VFs. + */ +void ice_restore_all_vfs_msi_state(struct pci_dev *pdev) +{ + u16 vf_id; + int pos; + + if (!pci_num_vf(pdev)) + return; + + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); + if (pos) { + struct pci_dev *vfdev; + + pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, + &vf_id); + vfdev = pci_get_device(pdev->vendor, vf_id, NULL); + while (vfdev) { + if (vfdev->is_virtfn && vfdev->physfn == pdev) + pci_restore_msi_state(vfdev); + vfdev = pci_get_device(pdev->vendor, vf_id, + vfdev); + } + } +} + +/** + * ice_is_malicious_vf - helper function to detect a malicious VF + * @pf: ptr to struct ice_pf + * @event: pointer to the AQ event + * @num_msg_proc: the number of messages processed so far + * @num_msg_pending: the number of messages peinding in admin queue + */ +bool +ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, + u16 num_msg_proc, u16 num_msg_pending) +{ + s16 vf_id = le16_to_cpu(event->desc.retval); + struct device *dev = ice_pf_to_dev(pf); + struct ice_mbx_data mbxdata; + bool malvf = false; + struct ice_vf *vf; + int status; + + vf = ice_get_vf_by_id(pf, vf_id); + if (!vf) + return false; + + if (test_bit(ICE_VF_STATE_DIS, vf->vf_states)) + goto out_put_vf; + + mbxdata.num_msg_proc = num_msg_proc; + mbxdata.num_pending_arq = num_msg_pending; + mbxdata.max_num_msgs_mbx = pf->hw.mailboxq.num_rq_entries; +#define ICE_MBX_OVERFLOW_WATERMARK 64 + mbxdata.async_watermark_val = ICE_MBX_OVERFLOW_WATERMARK; + + /* check to see if we have a malicious VF */ + status = ice_mbx_vf_state_handler(&pf->hw, &mbxdata, vf_id, &malvf); + if (status) + goto out_put_vf; + + if (malvf) { + bool report_vf = false; + + /* if the VF is malicious and we haven't let the user + * know about it, then let them know now + */ + status = ice_mbx_report_malvf(&pf->hw, pf->vfs.malvfs, + ICE_MAX_SRIOV_VFS, vf_id, + &report_vf); + if (status) + dev_dbg(dev, "Error reporting malicious VF\n"); + + if (report_vf) { + struct ice_vsi *pf_vsi = ice_get_main_vsi(pf); + + if (pf_vsi) + dev_warn(dev, "VF MAC %pM on PF MAC %pM is generating asynchronous messages and may be overflowing the PF message queue. Please see the Adapter User Guide for more information\n", + &vf->dev_lan_addr.addr[0], + pf_vsi->netdev->dev_addr); + } + } + +out_put_vf: + ice_put_vf(vf); + return malvf; } diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.h b/drivers/net/ethernet/intel/ice/ice_sriov.h index 68686a3fd7e8..955ab810a198 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.h +++ b/drivers/net/ethernet/intel/ice/ice_sriov.h @@ -3,50 +3,159 @@ #ifndef _ICE_SRIOV_H_ #define _ICE_SRIOV_H_ +#include "ice_virtchnl_fdir.h" +#include "ice_vf_lib.h" +#include "ice_virtchnl.h" -#include "ice_type.h" -#include "ice_controlq.h" +/* Static VF transaction/status register def */ +#define VF_DEVICE_STATUS 0xAA +#define VF_TRANS_PENDING_M 0x20 -/* Defining the mailbox message threshold as 63 asynchronous - * pending messages. Normal VF functionality does not require - * sending more than 63 asynchronous pending message. - */ -#define ICE_ASYNC_VF_MSG_THRESHOLD 63 +/* wait defines for polling PF_PCI_CIAD register status */ +#define ICE_PCI_CIAD_WAIT_COUNT 100 +#define ICE_PCI_CIAD_WAIT_DELAY_US 1 + +/* VF resource constraints */ +#define ICE_MIN_QS_PER_VF 1 +#define ICE_NONQ_VECS_VF 1 +#define ICE_NUM_VF_MSIX_MED 17 +#define ICE_NUM_VF_MSIX_SMALL 5 +#define ICE_NUM_VF_MSIX_MULTIQ_MIN 3 +#define ICE_MIN_INTR_PER_VF (ICE_MIN_QS_PER_VF + 1) +#define ICE_MAX_VF_RESET_TRIES 40 +#define ICE_MAX_VF_RESET_SLEEP_MS 20 #ifdef CONFIG_PCI_IOV +void ice_process_vflr_event(struct ice_pf *pf); +int ice_sriov_configure(struct pci_dev *pdev, int num_vfs); +int ice_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac); int -ice_aq_send_msg_to_vf(struct ice_hw *hw, u16 vfid, u32 v_opcode, u32 v_retval, - u8 *msg, u16 msglen, struct ice_sq_cd *cd); +ice_get_vf_cfg(struct net_device *netdev, int vf_id, struct ifla_vf_info *ivi); + +void ice_free_vfs(struct ice_pf *pf); +void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event); +void ice_restore_all_vfs_msi_state(struct pci_dev *pdev); +bool +ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, + u16 num_msg_proc, u16 num_msg_pending); -u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed); int -ice_mbx_vf_state_handler(struct ice_hw *hw, struct ice_mbx_data *mbx_data, - u16 vf_id, bool *is_mal_vf); +ice_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos, + __be16 vlan_proto); + int -ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, unsigned long *all_malvfs, - u16 bitmap_len, u16 vf_id); -int ice_mbx_init_snapshot(struct ice_hw *hw, u16 vf_count); -void ice_mbx_deinit_snapshot(struct ice_hw *hw); +ice_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, + int max_tx_rate); + +int ice_set_vf_trust(struct net_device *netdev, int vf_id, bool trusted); + +int ice_set_vf_link_state(struct net_device *netdev, int vf_id, int link_state); + +int ice_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool ena); + +int ice_calc_vf_reg_idx(struct ice_vf *vf, struct ice_q_vector *q_vector); + int -ice_mbx_report_malvf(struct ice_hw *hw, unsigned long *all_malvfs, - u16 bitmap_len, u16 vf_id, bool *report_malvf); +ice_get_vf_stats(struct net_device *netdev, int vf_id, + struct ifla_vf_stats *vf_stats); +void +ice_vf_lan_overflow_event(struct ice_pf *pf, struct ice_rq_event_info *event); +void ice_print_vfs_mdd_events(struct ice_pf *pf); +void ice_print_vf_rx_mdd_event(struct ice_vf *vf); +bool +ice_vc_validate_pattern(struct ice_vf *vf, struct virtchnl_proto_hdrs *proto); #else /* CONFIG_PCI_IOV */ +static inline void ice_process_vflr_event(struct ice_pf *pf) { } +static inline void ice_free_vfs(struct ice_pf *pf) { } +static inline +void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) { } +static inline +void ice_vf_lan_overflow_event(struct ice_pf *pf, struct ice_rq_event_info *event) { } +static inline void ice_print_vfs_mdd_events(struct ice_pf *pf) { } +static inline void ice_print_vf_rx_mdd_event(struct ice_vf *vf) { } +static inline void ice_restore_all_vfs_msi_state(struct pci_dev *pdev) { } + +static inline bool +ice_is_malicious_vf(struct ice_pf __always_unused *pf, + struct ice_rq_event_info __always_unused *event, + u16 __always_unused num_msg_proc, + u16 __always_unused num_msg_pending) +{ + return false; +} + static inline int -ice_aq_send_msg_to_vf(struct ice_hw __always_unused *hw, - u16 __always_unused vfid, u32 __always_unused v_opcode, - u32 __always_unused v_retval, u8 __always_unused *msg, - u16 __always_unused msglen, - struct ice_sq_cd __always_unused *cd) +ice_sriov_configure(struct pci_dev __always_unused *pdev, + int __always_unused num_vfs) +{ + return -EOPNOTSUPP; +} + +static inline int +ice_set_vf_mac(struct net_device __always_unused *netdev, + int __always_unused vf_id, u8 __always_unused *mac) +{ + return -EOPNOTSUPP; +} + +static inline int +ice_get_vf_cfg(struct net_device __always_unused *netdev, + int __always_unused vf_id, + struct ifla_vf_info __always_unused *ivi) +{ + return -EOPNOTSUPP; +} + +static inline int +ice_set_vf_trust(struct net_device __always_unused *netdev, + int __always_unused vf_id, bool __always_unused trusted) +{ + return -EOPNOTSUPP; +} + +static inline int +ice_set_vf_port_vlan(struct net_device __always_unused *netdev, + int __always_unused vf_id, u16 __always_unused vid, + u8 __always_unused qos, __be16 __always_unused v_proto) +{ + return -EOPNOTSUPP; +} + +static inline int +ice_set_vf_spoofchk(struct net_device __always_unused *netdev, + int __always_unused vf_id, bool __always_unused ena) +{ + return -EOPNOTSUPP; +} + +static inline int +ice_set_vf_link_state(struct net_device __always_unused *netdev, + int __always_unused vf_id, int __always_unused link_state) +{ + return -EOPNOTSUPP; +} + +static inline int +ice_set_vf_bw(struct net_device __always_unused *netdev, + int __always_unused vf_id, int __always_unused min_tx_rate, + int __always_unused max_tx_rate) +{ + return -EOPNOTSUPP; +} + +static inline int +ice_calc_vf_reg_idx(struct ice_vf __always_unused *vf, + struct ice_q_vector __always_unused *q_vector) { return 0; } -static inline u32 -ice_conv_link_speed_to_virtchnl(bool __always_unused adv_link_support, - u16 __always_unused link_speed) +static inline int +ice_get_vf_stats(struct net_device __always_unused *netdev, + int __always_unused vf_id, + struct ifla_vf_stats __always_unused *vf_stats) { - return 0; + return -EOPNOTSUPP; } - #endif /* CONFIG_PCI_IOV */ #endif /* _ICE_SRIOV_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_switch.c b/drivers/net/ethernet/intel/ice/ice_switch.c index 475ec2afa210..25b8f6f726eb 100644 --- a/drivers/net/ethernet/intel/ice/ice_switch.c +++ b/drivers/net/ethernet/intel/ice/ice_switch.c @@ -41,6 +41,7 @@ static const struct ice_dummy_pkt_offsets dummy_gre_tcp_packet_offsets[] = { { ICE_IPV4_OFOS, 14 }, { ICE_NVGRE, 34 }, { ICE_MAC_IL, 42 }, + { ICE_ETYPE_IL, 54 }, { ICE_IPV4_IL, 56 }, { ICE_TCP_IL, 76 }, { ICE_PROTOCOL_LAST, 0 }, @@ -65,7 +66,8 @@ static const u8 dummy_gre_tcp_packet[] = { 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_IL 42 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, + + 0x08, 0x00, /* ICE_ETYPE_IL 54 */ 0x45, 0x00, 0x00, 0x14, /* ICE_IPV4_IL 56 */ 0x00, 0x00, 0x00, 0x00, @@ -86,6 +88,7 @@ static const struct ice_dummy_pkt_offsets dummy_gre_udp_packet_offsets[] = { { ICE_IPV4_OFOS, 14 }, { ICE_NVGRE, 34 }, { ICE_MAC_IL, 42 }, + { ICE_ETYPE_IL, 54 }, { ICE_IPV4_IL, 56 }, { ICE_UDP_ILOS, 76 }, { ICE_PROTOCOL_LAST, 0 }, @@ -110,7 +113,8 @@ static const u8 dummy_gre_udp_packet[] = { 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_IL 42 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, + + 0x08, 0x00, /* ICE_ETYPE_IL 54 */ 0x45, 0x00, 0x00, 0x14, /* ICE_IPV4_IL 56 */ 0x00, 0x00, 0x00, 0x00, @@ -131,6 +135,7 @@ static const struct ice_dummy_pkt_offsets dummy_udp_tun_tcp_packet_offsets[] = { { ICE_GENEVE, 42 }, { ICE_VXLAN_GPE, 42 }, { ICE_MAC_IL, 50 }, + { ICE_ETYPE_IL, 62 }, { ICE_IPV4_IL, 64 }, { ICE_TCP_IL, 84 }, { ICE_PROTOCOL_LAST, 0 }, @@ -158,7 +163,8 @@ static const u8 dummy_udp_tun_tcp_packet[] = { 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_IL 50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, + + 0x08, 0x00, /* ICE_ETYPE_IL 62 */ 0x45, 0x00, 0x00, 0x28, /* ICE_IPV4_IL 64 */ 0x00, 0x01, 0x00, 0x00, @@ -182,6 +188,7 @@ static const struct ice_dummy_pkt_offsets dummy_udp_tun_udp_packet_offsets[] = { { ICE_GENEVE, 42 }, { ICE_VXLAN_GPE, 42 }, { ICE_MAC_IL, 50 }, + { ICE_ETYPE_IL, 62 }, { ICE_IPV4_IL, 64 }, { ICE_UDP_ILOS, 84 }, { ICE_PROTOCOL_LAST, 0 }, @@ -209,7 +216,8 @@ static const u8 dummy_udp_tun_udp_packet[] = { 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_IL 50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, + + 0x08, 0x00, /* ICE_ETYPE_IL 62 */ 0x45, 0x00, 0x00, 0x1c, /* ICE_IPV4_IL 64 */ 0x00, 0x01, 0x00, 0x00, @@ -221,6 +229,224 @@ static const u8 dummy_udp_tun_udp_packet[] = { 0x00, 0x08, 0x00, 0x00, }; +static const struct ice_dummy_pkt_offsets +dummy_gre_ipv6_tcp_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_ETYPE_OL, 12 }, + { ICE_IPV4_OFOS, 14 }, + { ICE_NVGRE, 34 }, + { ICE_MAC_IL, 42 }, + { ICE_ETYPE_IL, 54 }, + { ICE_IPV6_IL, 56 }, + { ICE_TCP_IL, 96 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_gre_ipv6_tcp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_OFOS 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x08, 0x00, /* ICE_ETYPE_OL 12 */ + + 0x45, 0x00, 0x00, 0x66, /* ICE_IPV4_OFOS 14 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2F, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x80, 0x00, 0x65, 0x58, /* ICE_NVGRE 34 */ + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_IL 42 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x86, 0xdd, /* ICE_ETYPE_IL 54 */ + + 0x60, 0x00, 0x00, 0x00, /* ICE_IPV6_IL 56 */ + 0x00, 0x08, 0x06, 0x40, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* ICE_TCP_IL 96 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x50, 0x02, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static const struct ice_dummy_pkt_offsets +dummy_gre_ipv6_udp_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_ETYPE_OL, 12 }, + { ICE_IPV4_OFOS, 14 }, + { ICE_NVGRE, 34 }, + { ICE_MAC_IL, 42 }, + { ICE_ETYPE_IL, 54 }, + { ICE_IPV6_IL, 56 }, + { ICE_UDP_ILOS, 96 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_gre_ipv6_udp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_OFOS 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x08, 0x00, /* ICE_ETYPE_OL 12 */ + + 0x45, 0x00, 0x00, 0x5a, /* ICE_IPV4_OFOS 14 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2F, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x80, 0x00, 0x65, 0x58, /* ICE_NVGRE 34 */ + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_IL 42 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x86, 0xdd, /* ICE_ETYPE_IL 54 */ + + 0x60, 0x00, 0x00, 0x00, /* ICE_IPV6_IL 56 */ + 0x00, 0x08, 0x11, 0x40, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* ICE_UDP_ILOS 96 */ + 0x00, 0x08, 0x00, 0x00, +}; + +static const struct ice_dummy_pkt_offsets +dummy_udp_tun_ipv6_tcp_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_ETYPE_OL, 12 }, + { ICE_IPV4_OFOS, 14 }, + { ICE_UDP_OF, 34 }, + { ICE_VXLAN, 42 }, + { ICE_GENEVE, 42 }, + { ICE_VXLAN_GPE, 42 }, + { ICE_MAC_IL, 50 }, + { ICE_ETYPE_IL, 62 }, + { ICE_IPV6_IL, 64 }, + { ICE_TCP_IL, 104 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_udp_tun_ipv6_tcp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_OFOS 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x08, 0x00, /* ICE_ETYPE_OL 12 */ + + 0x45, 0x00, 0x00, 0x6e, /* ICE_IPV4_OFOS 14 */ + 0x00, 0x01, 0x00, 0x00, + 0x40, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x12, 0xb5, /* ICE_UDP_OF 34 */ + 0x00, 0x5a, 0x00, 0x00, + + 0x00, 0x00, 0x65, 0x58, /* ICE_VXLAN 42 */ + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_IL 50 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x86, 0xdd, /* ICE_ETYPE_IL 62 */ + + 0x60, 0x00, 0x00, 0x00, /* ICE_IPV6_IL 64 */ + 0x00, 0x08, 0x06, 0x40, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* ICE_TCP_IL 104 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x50, 0x02, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static const struct ice_dummy_pkt_offsets +dummy_udp_tun_ipv6_udp_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_ETYPE_OL, 12 }, + { ICE_IPV4_OFOS, 14 }, + { ICE_UDP_OF, 34 }, + { ICE_VXLAN, 42 }, + { ICE_GENEVE, 42 }, + { ICE_VXLAN_GPE, 42 }, + { ICE_MAC_IL, 50 }, + { ICE_ETYPE_IL, 62 }, + { ICE_IPV6_IL, 64 }, + { ICE_UDP_ILOS, 104 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_udp_tun_ipv6_udp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_OFOS 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x08, 0x00, /* ICE_ETYPE_OL 12 */ + + 0x45, 0x00, 0x00, 0x62, /* ICE_IPV4_OFOS 14 */ + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x12, 0xb5, /* ICE_UDP_OF 34 */ + 0x00, 0x4e, 0x00, 0x00, + + 0x00, 0x00, 0x65, 0x58, /* ICE_VXLAN 42 */ + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_IL 50 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x86, 0xdd, /* ICE_ETYPE_IL 62 */ + + 0x60, 0x00, 0x00, 0x00, /* ICE_IPV6_IL 64 */ + 0x00, 0x08, 0x11, 0x40, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* ICE_UDP_ILOS 104 */ + 0x00, 0x08, 0x00, 0x00, +}; + /* offset info for MAC + IPv4 + UDP dummy packet */ static const struct ice_dummy_pkt_offsets dummy_udp_packet_offsets[] = { { ICE_MAC_OFOS, 0 }, @@ -500,6 +726,495 @@ static const u8 dummy_vlan_udp_ipv6_packet[] = { 0x00, 0x00, /* 2 bytes for 4 byte alignment */ }; +/* Outer IPv4 + Outer UDP + GTP + Inner IPv4 + Inner TCP */ +static const +struct ice_dummy_pkt_offsets dummy_ipv4_gtpu_ipv4_tcp_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_IPV4_OFOS, 14 }, + { ICE_UDP_OF, 34 }, + { ICE_GTP, 42 }, + { ICE_IPV4_IL, 62 }, + { ICE_TCP_IL, 82 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_ipv4_gtpu_ipv4_tcp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* Ethernet 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, + + 0x45, 0x00, 0x00, 0x58, /* IP 14 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x08, 0x68, /* UDP 34 */ + 0x00, 0x44, 0x00, 0x00, + + 0x34, 0xff, 0x00, 0x34, /* ICE_GTP Header 42 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x85, + + 0x02, 0x00, 0x00, 0x00, /* GTP_PDUSession_ExtensionHeader 54 */ + 0x00, 0x00, 0x00, 0x00, + + 0x45, 0x00, 0x00, 0x28, /* IP 62 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* TCP 82 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, /* 2 bytes for 4 byte alignment */ +}; + +/* Outer IPv4 + Outer UDP + GTP + Inner IPv4 + Inner UDP */ +static const +struct ice_dummy_pkt_offsets dummy_ipv4_gtpu_ipv4_udp_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_IPV4_OFOS, 14 }, + { ICE_UDP_OF, 34 }, + { ICE_GTP, 42 }, + { ICE_IPV4_IL, 62 }, + { ICE_UDP_ILOS, 82 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_ipv4_gtpu_ipv4_udp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* Ethernet 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, + + 0x45, 0x00, 0x00, 0x4c, /* IP 14 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x08, 0x68, /* UDP 34 */ + 0x00, 0x38, 0x00, 0x00, + + 0x34, 0xff, 0x00, 0x28, /* ICE_GTP Header 42 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x85, + + 0x02, 0x00, 0x00, 0x00, /* GTP_PDUSession_ExtensionHeader 54 */ + 0x00, 0x00, 0x00, 0x00, + + 0x45, 0x00, 0x00, 0x1c, /* IP 62 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* UDP 82 */ + 0x00, 0x08, 0x00, 0x00, + + 0x00, 0x00, /* 2 bytes for 4 byte alignment */ +}; + +/* Outer IPv6 + Outer UDP + GTP + Inner IPv4 + Inner TCP */ +static const +struct ice_dummy_pkt_offsets dummy_ipv4_gtpu_ipv6_tcp_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_IPV4_OFOS, 14 }, + { ICE_UDP_OF, 34 }, + { ICE_GTP, 42 }, + { ICE_IPV6_IL, 62 }, + { ICE_TCP_IL, 102 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_ipv4_gtpu_ipv6_tcp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* Ethernet 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, + + 0x45, 0x00, 0x00, 0x6c, /* IP 14 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x08, 0x68, /* UDP 34 */ + 0x00, 0x58, 0x00, 0x00, + + 0x34, 0xff, 0x00, 0x48, /* ICE_GTP Header 42 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x85, + + 0x02, 0x00, 0x00, 0x00, /* GTP_PDUSession_ExtensionHeader 54 */ + 0x00, 0x00, 0x00, 0x00, + + 0x60, 0x00, 0x00, 0x00, /* IPv6 62 */ + 0x00, 0x14, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* TCP 102 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, /* 2 bytes for 4 byte alignment */ +}; + +static const +struct ice_dummy_pkt_offsets dummy_ipv4_gtpu_ipv6_udp_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_IPV4_OFOS, 14 }, + { ICE_UDP_OF, 34 }, + { ICE_GTP, 42 }, + { ICE_IPV6_IL, 62 }, + { ICE_UDP_ILOS, 102 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_ipv4_gtpu_ipv6_udp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* Ethernet 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, + + 0x45, 0x00, 0x00, 0x60, /* IP 14 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x08, 0x68, /* UDP 34 */ + 0x00, 0x4c, 0x00, 0x00, + + 0x34, 0xff, 0x00, 0x3c, /* ICE_GTP Header 42 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x85, + + 0x02, 0x00, 0x00, 0x00, /* GTP_PDUSession_ExtensionHeader 54 */ + 0x00, 0x00, 0x00, 0x00, + + 0x60, 0x00, 0x00, 0x00, /* IPv6 62 */ + 0x00, 0x08, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* UDP 102 */ + 0x00, 0x08, 0x00, 0x00, + + 0x00, 0x00, /* 2 bytes for 4 byte alignment */ +}; + +static const +struct ice_dummy_pkt_offsets dummy_ipv6_gtpu_ipv4_tcp_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_IPV6_OFOS, 14 }, + { ICE_UDP_OF, 54 }, + { ICE_GTP, 62 }, + { ICE_IPV4_IL, 82 }, + { ICE_TCP_IL, 102 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_ipv6_gtpu_ipv4_tcp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* Ethernet 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x86, 0xdd, + + 0x60, 0x00, 0x00, 0x00, /* IPv6 14 */ + 0x00, 0x44, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x08, 0x68, /* UDP 54 */ + 0x00, 0x44, 0x00, 0x00, + + 0x34, 0xff, 0x00, 0x34, /* ICE_GTP Header 62 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x85, + + 0x02, 0x00, 0x00, 0x00, /* GTP_PDUSession_ExtensionHeader 74 */ + 0x00, 0x00, 0x00, 0x00, + + 0x45, 0x00, 0x00, 0x28, /* IP 82 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* TCP 102 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, /* 2 bytes for 4 byte alignment */ +}; + +static const +struct ice_dummy_pkt_offsets dummy_ipv6_gtpu_ipv4_udp_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_IPV6_OFOS, 14 }, + { ICE_UDP_OF, 54 }, + { ICE_GTP, 62 }, + { ICE_IPV4_IL, 82 }, + { ICE_UDP_ILOS, 102 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_ipv6_gtpu_ipv4_udp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* Ethernet 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x86, 0xdd, + + 0x60, 0x00, 0x00, 0x00, /* IPv6 14 */ + 0x00, 0x38, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x08, 0x68, /* UDP 54 */ + 0x00, 0x38, 0x00, 0x00, + + 0x34, 0xff, 0x00, 0x28, /* ICE_GTP Header 62 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x85, + + 0x02, 0x00, 0x00, 0x00, /* GTP_PDUSession_ExtensionHeader 74 */ + 0x00, 0x00, 0x00, 0x00, + + 0x45, 0x00, 0x00, 0x1c, /* IP 82 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* UDP 102 */ + 0x00, 0x08, 0x00, 0x00, + + 0x00, 0x00, /* 2 bytes for 4 byte alignment */ +}; + +static const +struct ice_dummy_pkt_offsets dummy_ipv6_gtpu_ipv6_tcp_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_IPV6_OFOS, 14 }, + { ICE_UDP_OF, 54 }, + { ICE_GTP, 62 }, + { ICE_IPV6_IL, 82 }, + { ICE_TCP_IL, 122 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_ipv6_gtpu_ipv6_tcp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* Ethernet 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x86, 0xdd, + + 0x60, 0x00, 0x00, 0x00, /* IPv6 14 */ + 0x00, 0x58, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x08, 0x68, /* UDP 54 */ + 0x00, 0x58, 0x00, 0x00, + + 0x34, 0xff, 0x00, 0x48, /* ICE_GTP Header 62 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x85, + + 0x02, 0x00, 0x00, 0x00, /* GTP_PDUSession_ExtensionHeader 74 */ + 0x00, 0x00, 0x00, 0x00, + + 0x60, 0x00, 0x00, 0x00, /* IPv6 82 */ + 0x00, 0x14, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* TCP 122 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, /* 2 bytes for 4 byte alignment */ +}; + +static const +struct ice_dummy_pkt_offsets dummy_ipv6_gtpu_ipv6_udp_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_IPV6_OFOS, 14 }, + { ICE_UDP_OF, 54 }, + { ICE_GTP, 62 }, + { ICE_IPV6_IL, 82 }, + { ICE_UDP_ILOS, 122 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_ipv6_gtpu_ipv6_udp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* Ethernet 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x86, 0xdd, + + 0x60, 0x00, 0x00, 0x00, /* IPv6 14 */ + 0x00, 0x4c, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x08, 0x68, /* UDP 54 */ + 0x00, 0x4c, 0x00, 0x00, + + 0x34, 0xff, 0x00, 0x3c, /* ICE_GTP Header 62 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x85, + + 0x02, 0x00, 0x00, 0x00, /* GTP_PDUSession_ExtensionHeader 74 */ + 0x00, 0x00, 0x00, 0x00, + + 0x60, 0x00, 0x00, 0x00, /* IPv6 82 */ + 0x00, 0x08, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* UDP 122 */ + 0x00, 0x08, 0x00, 0x00, + + 0x00, 0x00, /* 2 bytes for 4 byte alignment */ +}; + +static const u8 dummy_ipv4_gtpu_ipv4_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_OFOS 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, + + 0x45, 0x00, 0x00, 0x44, /* ICE_IPV4_OFOS 14 */ + 0x00, 0x00, 0x40, 0x00, + 0x40, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x08, 0x68, 0x08, 0x68, /* ICE_UDP_OF 34 */ + 0x00, 0x00, 0x00, 0x00, + + 0x34, 0xff, 0x00, 0x28, /* ICE_GTP 42 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x85, + + 0x02, 0x00, 0x00, 0x00, /* PDU Session extension header */ + 0x00, 0x00, 0x00, 0x00, + + 0x45, 0x00, 0x00, 0x14, /* ICE_IPV4_IL 62 */ + 0x00, 0x00, 0x40, 0x00, + 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +static const +struct ice_dummy_pkt_offsets dummy_ipv4_gtp_no_pay_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_IPV4_OFOS, 14 }, + { ICE_UDP_OF, 34 }, + { ICE_GTP_NO_PAY, 42 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const +struct ice_dummy_pkt_offsets dummy_ipv6_gtp_no_pay_packet_offsets[] = { + { ICE_MAC_OFOS, 0 }, + { ICE_IPV6_OFOS, 14 }, + { ICE_UDP_OF, 54 }, + { ICE_GTP_NO_PAY, 62 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +static const u8 dummy_ipv6_gtp_packet[] = { + 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_OFOS 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x86, 0xdd, + + 0x60, 0x00, 0x00, 0x00, /* ICE_IPV6_OFOS 14 */ + 0x00, 0x6c, 0x11, 0x00, /* Next header UDP*/ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x08, 0x68, 0x08, 0x68, /* ICE_UDP_OF 54 */ + 0x00, 0x00, 0x00, 0x00, + + 0x30, 0x00, 0x00, 0x28, /* ICE_GTP 62 */ + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, +}; + #define ICE_SW_RULE_RX_TX_ETH_HDR_SIZE \ (offsetof(struct ice_aqc_sw_rules_elem, pdata.lkup_tx_rx.hdr) + \ (DUMMY_ETH_HDR_LEN * \ @@ -1096,6 +1811,64 @@ ice_aq_get_recipe(struct ice_hw *hw, return status; } +/** + * ice_update_recipe_lkup_idx - update a default recipe based on the lkup_idx + * @hw: pointer to the HW struct + * @params: parameters used to update the default recipe + * + * This function only supports updating default recipes and it only supports + * updating a single recipe based on the lkup_idx at a time. + * + * This is done as a read-modify-write operation. First, get the current recipe + * contents based on the recipe's ID. Then modify the field vector index and + * mask if it's valid at the lkup_idx. Finally, use the add recipe AQ to update + * the pre-existing recipe with the modifications. + */ +int +ice_update_recipe_lkup_idx(struct ice_hw *hw, + struct ice_update_recipe_lkup_idx_params *params) +{ + struct ice_aqc_recipe_data_elem *rcp_list; + u16 num_recps = ICE_MAX_NUM_RECIPES; + int status; + + rcp_list = kcalloc(num_recps, sizeof(*rcp_list), GFP_KERNEL); + if (!rcp_list) + return -ENOMEM; + + /* read current recipe list from firmware */ + rcp_list->recipe_indx = params->rid; + status = ice_aq_get_recipe(hw, rcp_list, &num_recps, params->rid, NULL); + if (status) { + ice_debug(hw, ICE_DBG_SW, "Failed to get recipe %d, status %d\n", + params->rid, status); + goto error_out; + } + + /* only modify existing recipe's lkup_idx and mask if valid, while + * leaving all other fields the same, then update the recipe firmware + */ + rcp_list->content.lkup_indx[params->lkup_idx] = params->fv_idx; + if (params->mask_valid) + rcp_list->content.mask[params->lkup_idx] = + cpu_to_le16(params->mask); + + if (params->ignore_valid) + rcp_list->content.lkup_indx[params->lkup_idx] |= + ICE_AQ_RECIPE_LKUP_IGNORE; + + status = ice_aq_add_recipe(hw, &rcp_list[0], 1, NULL); + if (status) + ice_debug(hw, ICE_DBG_SW, "Failed to update recipe %d lkup_idx %d fv_idx %d mask %d mask_valid %s, status %d\n", + params->rid, params->lkup_idx, params->fv_idx, + params->mask, params->mask_valid ? "true" : "false", + status); + +error_out: + kfree(rcp_list); + return status; +} + /** * ice_aq_map_recipe_to_profile - Map recipe to packet profile * @hw: pointer to the HW struct @@ -1539,6 +2312,7 @@ ice_fill_sw_rule(struct ice_hw *hw, struct ice_fltr_info *f_info, struct ice_aqc_sw_rules_elem *s_rule, enum ice_adminq_opc opc) { u16 vlan_id = ICE_MAX_VLAN_ID + 1; + u16 vlan_tpid = ETH_P_8021Q; void *daddr = NULL; u16 eth_hdr_sz; u8 *eth_hdr; @@ -1611,6 +2385,8 @@ ice_fill_sw_rule(struct ice_hw *hw, struct ice_fltr_info *f_info, break; case ICE_SW_LKUP_VLAN: vlan_id = f_info->l_data.vlan.vlan_id; + if (f_info->l_data.vlan.tpid_valid) + vlan_tpid = f_info->l_data.vlan.tpid; if (f_info->fltr_act == ICE_FWD_TO_VSI || f_info->fltr_act == ICE_FWD_TO_VSI_LIST) { act |= ICE_SINGLE_ACT_PRUNE; @@ -1653,6 +2429,8 @@ ice_fill_sw_rule(struct ice_hw *hw, struct ice_fltr_info *f_info, if (!(vlan_id > ICE_MAX_VLAN_ID)) { off = (__force __be16 *)(eth_hdr + ICE_ETH_VLAN_TCI_OFFSET); *off = cpu_to_be16(vlan_id); + off = (__force __be16 *)(eth_hdr + ICE_ETH_ETHTYPE_OFFSET); + *off = cpu_to_be16(vlan_tpid); } /* Create the switch rule with the final dummy Ethernet header */ @@ -3755,6 +4533,7 @@ static const struct ice_prot_ext_tbl_entry ice_prot_ext[ICE_PROTOCOL_LAST] = { { ICE_MAC_OFOS, { 0, 2, 4, 6, 8, 10, 12 } }, { ICE_MAC_IL, { 0, 2, 4, 6, 8, 10, 12 } }, { ICE_ETYPE_OL, { 0 } }, + { ICE_ETYPE_IL, { 0 } }, { ICE_VLAN_OFOS, { 2, 0 } }, { ICE_IPV4_OFOS, { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 } }, { ICE_IPV4_IL, { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 } }, @@ -3767,13 +4546,16 @@ static const struct ice_prot_ext_tbl_entry ice_prot_ext[ICE_PROTOCOL_LAST] = { { ICE_UDP_ILOS, { 0, 2 } }, { ICE_VXLAN, { 8, 10, 12, 14 } }, { ICE_GENEVE, { 8, 10, 12, 14 } }, - { ICE_NVGRE, { 0, 2, 4, 6 } }, + { ICE_NVGRE, { 0, 2, 4, 6 } }, + { ICE_GTP, { 8, 10, 12, 14, 16, 18, 20, 22 } }, + { ICE_GTP_NO_PAY, { 8, 10, 12, 14 } }, }; static struct ice_protocol_entry ice_prot_id_tbl[ICE_PROTOCOL_LAST] = { { ICE_MAC_OFOS, ICE_MAC_OFOS_HW }, { ICE_MAC_IL, ICE_MAC_IL_HW }, { ICE_ETYPE_OL, ICE_ETYPE_OL_HW }, + { ICE_ETYPE_IL, ICE_ETYPE_IL_HW }, { ICE_VLAN_OFOS, ICE_VLAN_OL_HW }, { ICE_IPV4_OFOS, ICE_IPV4_OFOS_HW }, { ICE_IPV4_IL, ICE_IPV4_IL_HW }, @@ -3784,7 +4566,9 @@ static struct ice_protocol_entry ice_prot_id_tbl[ICE_PROTOCOL_LAST] = { { ICE_UDP_ILOS, ICE_UDP_ILOS_HW }, { ICE_VXLAN, ICE_UDP_OF_HW }, { ICE_GENEVE, ICE_UDP_OF_HW }, - { ICE_NVGRE, ICE_GRE_OF_HW }, + { ICE_NVGRE, ICE_GRE_OF_HW }, + { ICE_GTP, ICE_UDP_OF_HW }, + { ICE_GTP_NO_PAY, ICE_UDP_ILOS_HW }, }; /** @@ -3867,6 +4651,23 @@ ice_find_recp(struct ice_hw *hw, struct ice_prot_lkup_ext *lkup_exts, return ICE_MAX_NUM_RECIPES; } +/** + * ice_change_proto_id_to_dvm - change proto id in prot_id_tbl + * + * As protocol id for outer vlan is different in dvm and svm, if dvm is + * supported protocol array record for outer vlan has to be modified to + * reflect the value proper for DVM. + */ +void ice_change_proto_id_to_dvm(void) +{ + u8 i; + + for (i = 0; i < ARRAY_SIZE(ice_prot_id_tbl); i++) + if (ice_prot_id_tbl[i].type == ICE_VLAN_OFOS && + ice_prot_id_tbl[i].protocol_id != ICE_VLAN_OF_HW) + ice_prot_id_tbl[i].protocol_id = ICE_VLAN_OF_HW; +} + /** * ice_prot_type_to_id - get protocol ID from protocol type * @type: protocol type @@ -4426,41 +5227,6 @@ ice_create_recipe_group(struct ice_hw *hw, struct ice_sw_recipe *rm, return status; } -/** - * ice_get_fv - get field vectors/extraction sequences for spec. lookup types - * @hw: pointer to hardware structure - * @lkups: lookup elements or match criteria for the advanced recipe, one - * structure per protocol header - * @lkups_cnt: number of protocols - * @bm: bitmap of field vectors to consider - * @fv_list: pointer to a list that holds the returned field vectors - */ -static int -ice_get_fv(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups, u16 lkups_cnt, - unsigned long *bm, struct list_head *fv_list) -{ - u8 *prot_ids; - int status; - u16 i; - - prot_ids = kcalloc(lkups_cnt, sizeof(*prot_ids), GFP_KERNEL); - if (!prot_ids) - return -ENOMEM; - - for (i = 0; i < lkups_cnt; i++) - if (!ice_prot_type_to_id(lkups[i].type, &prot_ids[i])) { - status = -EIO; - goto free_mem; - } - - /* Find field vectors that include all specified protocol types */ - status = ice_get_sw_fv_list(hw, prot_ids, lkups_cnt, bm, fv_list); - -free_mem: - kfree(prot_ids); - return status; -} - /** * ice_tun_type_match_word - determine if tun type needs a match mask * @tun_type: tunnel type @@ -4472,6 +5238,8 @@ static bool ice_tun_type_match_word(enum ice_sw_tunnel_type tun_type, u16 *mask) case ICE_SW_TUN_GENEVE: case ICE_SW_TUN_VXLAN: case ICE_SW_TUN_NVGRE: + case ICE_SW_TUN_GTPU: + case ICE_SW_TUN_GTPC: *mask = ICE_TUN_FLAG_MASK; return true; @@ -4537,6 +5305,12 @@ ice_get_compat_fv_bitmap(struct ice_hw *hw, struct ice_adv_rule_info *rinfo, case ICE_SW_TUN_NVGRE: prof_type = ICE_PROF_TUN_GRE; break; + case ICE_SW_TUN_GTPU: + prof_type = ICE_PROF_TUN_GTPU; + break; + case ICE_SW_TUN_GTPC: + prof_type = ICE_PROF_TUN_GTPC; + break; case ICE_SW_TUN_AND_NON_TUN: default: prof_type = ICE_PROF_ALL; @@ -4609,11 +5383,11 @@ ice_add_adv_recipe(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups, /* Get bitmap of field vectors (profiles) that are compatible with the * rule request; only these will be searched in the subsequent call to - * ice_get_fv. + * ice_get_sw_fv_list. */ ice_get_compat_fv_bitmap(hw, rinfo, fv_bitmap); - status = ice_get_fv(hw, lkups, lkups_cnt, fv_bitmap, &rm->fv_list); + status = ice_get_sw_fv_list(hw, lkup_exts, fv_bitmap, &rm->fv_list); if (status) goto err_unroll; @@ -4737,34 +5511,126 @@ ice_find_dummy_packet(struct ice_adv_lkup_elem *lkups, u16 lkups_cnt, const u8 **pkt, u16 *pkt_len, const struct ice_dummy_pkt_offsets **offsets) { - bool tcp = false, udp = false, ipv6 = false, vlan = false; + bool inner_tcp = false, inner_udp = false, outer_ipv6 = false; + bool vlan = false, inner_ipv6 = false, gtp_no_pay = false; u16 i; for (i = 0; i < lkups_cnt; i++) { if (lkups[i].type == ICE_UDP_ILOS) - udp = true; + inner_udp = true; else if (lkups[i].type == ICE_TCP_IL) - tcp = true; + inner_tcp = true; else if (lkups[i].type == ICE_IPV6_OFOS) - ipv6 = true; + outer_ipv6 = true; else if (lkups[i].type == ICE_VLAN_OFOS) vlan = true; else if (lkups[i].type == ICE_ETYPE_OL && lkups[i].h_u.ethertype.ethtype_id == cpu_to_be16(ICE_IPV6_ETHER_ID) && lkups[i].m_u.ethertype.ethtype_id == - cpu_to_be16(0xFFFF)) - ipv6 = true; + cpu_to_be16(0xFFFF)) + outer_ipv6 = true; + else if (lkups[i].type == ICE_ETYPE_IL && + lkups[i].h_u.ethertype.ethtype_id == + cpu_to_be16(ICE_IPV6_ETHER_ID) && + lkups[i].m_u.ethertype.ethtype_id == + cpu_to_be16(0xFFFF)) + inner_ipv6 = true; + else if (lkups[i].type == ICE_IPV6_IL) + inner_ipv6 = true; + else if (lkups[i].type == ICE_GTP_NO_PAY) + gtp_no_pay = true; + } + + if (tun_type == ICE_SW_TUN_GTPU) { + if (outer_ipv6) { + if (gtp_no_pay) { + *pkt = dummy_ipv6_gtp_packet; + *pkt_len = sizeof(dummy_ipv6_gtp_packet); + *offsets = dummy_ipv6_gtp_no_pay_packet_offsets; + } else if (inner_ipv6) { + if (inner_udp) { + *pkt = dummy_ipv6_gtpu_ipv6_udp_packet; + *pkt_len = sizeof(dummy_ipv6_gtpu_ipv6_udp_packet); + *offsets = dummy_ipv6_gtpu_ipv6_udp_packet_offsets; + } else { + *pkt = dummy_ipv6_gtpu_ipv6_tcp_packet; + *pkt_len = sizeof(dummy_ipv6_gtpu_ipv6_tcp_packet); + *offsets = dummy_ipv6_gtpu_ipv6_tcp_packet_offsets; + } + } else { + if (inner_udp) { + *pkt = dummy_ipv6_gtpu_ipv4_udp_packet; + *pkt_len = sizeof(dummy_ipv6_gtpu_ipv4_udp_packet); + *offsets = dummy_ipv6_gtpu_ipv4_udp_packet_offsets; + } else { + *pkt = dummy_ipv6_gtpu_ipv4_tcp_packet; + *pkt_len = sizeof(dummy_ipv6_gtpu_ipv4_tcp_packet); + *offsets = dummy_ipv6_gtpu_ipv4_tcp_packet_offsets; + } + } + } else { + if (gtp_no_pay) { + *pkt = dummy_ipv4_gtpu_ipv4_packet; + *pkt_len = sizeof(dummy_ipv4_gtpu_ipv4_packet); + *offsets = dummy_ipv4_gtp_no_pay_packet_offsets; + } else if (inner_ipv6) { + if (inner_udp) { + *pkt = dummy_ipv4_gtpu_ipv6_udp_packet; + *pkt_len = sizeof(dummy_ipv4_gtpu_ipv6_udp_packet); + *offsets = dummy_ipv4_gtpu_ipv6_udp_packet_offsets; + } else { + *pkt = dummy_ipv4_gtpu_ipv6_tcp_packet; + *pkt_len = sizeof(dummy_ipv4_gtpu_ipv6_tcp_packet); + *offsets = dummy_ipv4_gtpu_ipv6_tcp_packet_offsets; + } + } else { + if (inner_udp) { + *pkt = dummy_ipv4_gtpu_ipv4_udp_packet; + *pkt_len = sizeof(dummy_ipv4_gtpu_ipv4_udp_packet); + *offsets = dummy_ipv4_gtpu_ipv4_udp_packet_offsets; + } else { + *pkt = dummy_ipv4_gtpu_ipv4_tcp_packet; + *pkt_len = sizeof(dummy_ipv4_gtpu_ipv4_tcp_packet); + *offsets = dummy_ipv4_gtpu_ipv4_tcp_packet_offsets; + } + } + } + return; + } + + if (tun_type == ICE_SW_TUN_GTPC) { + if (outer_ipv6) { + *pkt = dummy_ipv6_gtp_packet; + *pkt_len = sizeof(dummy_ipv6_gtp_packet); + *offsets = dummy_ipv6_gtp_no_pay_packet_offsets; + } else { + *pkt = dummy_ipv4_gtpu_ipv4_packet; + *pkt_len = sizeof(dummy_ipv4_gtpu_ipv4_packet); + *offsets = dummy_ipv4_gtp_no_pay_packet_offsets; + } + return; } if (tun_type == ICE_SW_TUN_NVGRE) { - if (tcp) { + if (inner_tcp && inner_ipv6) { + *pkt = dummy_gre_ipv6_tcp_packet; + *pkt_len = sizeof(dummy_gre_ipv6_tcp_packet); + *offsets = dummy_gre_ipv6_tcp_packet_offsets; + return; + } + if (inner_tcp) { *pkt = dummy_gre_tcp_packet; *pkt_len = sizeof(dummy_gre_tcp_packet); *offsets = dummy_gre_tcp_packet_offsets; return; } - + if (inner_ipv6) { + *pkt = dummy_gre_ipv6_udp_packet; + *pkt_len = sizeof(dummy_gre_ipv6_udp_packet); + *offsets = dummy_gre_ipv6_udp_packet_offsets; + return; + } *pkt = dummy_gre_udp_packet; *pkt_len = sizeof(dummy_gre_udp_packet); *offsets = dummy_gre_udp_packet_offsets; @@ -4773,20 +5639,31 @@ ice_find_dummy_packet(struct ice_adv_lkup_elem *lkups, u16 lkups_cnt, if (tun_type == ICE_SW_TUN_VXLAN || tun_type == ICE_SW_TUN_GENEVE) { - if (tcp) { + if (inner_tcp && inner_ipv6) { + *pkt = dummy_udp_tun_ipv6_tcp_packet; + *pkt_len = sizeof(dummy_udp_tun_ipv6_tcp_packet); + *offsets = dummy_udp_tun_ipv6_tcp_packet_offsets; + return; + } + if (inner_tcp) { *pkt = dummy_udp_tun_tcp_packet; *pkt_len = sizeof(dummy_udp_tun_tcp_packet); *offsets = dummy_udp_tun_tcp_packet_offsets; return; } - + if (inner_ipv6) { + *pkt = dummy_udp_tun_ipv6_udp_packet; + *pkt_len = sizeof(dummy_udp_tun_ipv6_udp_packet); + *offsets = dummy_udp_tun_ipv6_udp_packet_offsets; + return; + } *pkt = dummy_udp_tun_udp_packet; *pkt_len = sizeof(dummy_udp_tun_udp_packet); *offsets = dummy_udp_tun_udp_packet_offsets; return; } - if (udp && !ipv6) { + if (inner_udp && !outer_ipv6) { if (vlan) { *pkt = dummy_vlan_udp_packet; *pkt_len = sizeof(dummy_vlan_udp_packet); @@ -4797,7 +5674,7 @@ ice_find_dummy_packet(struct ice_adv_lkup_elem *lkups, u16 lkups_cnt, *pkt_len = sizeof(dummy_udp_packet); *offsets = dummy_udp_packet_offsets; return; - } else if (udp && ipv6) { + } else if (inner_udp && outer_ipv6) { if (vlan) { *pkt = dummy_vlan_udp_ipv6_packet; *pkt_len = sizeof(dummy_vlan_udp_ipv6_packet); @@ -4808,7 +5685,7 @@ ice_find_dummy_packet(struct ice_adv_lkup_elem *lkups, u16 lkups_cnt, *pkt_len = sizeof(dummy_udp_ipv6_packet); *offsets = dummy_udp_ipv6_packet_offsets; return; - } else if ((tcp && ipv6) || ipv6) { + } else if ((inner_tcp && outer_ipv6) || outer_ipv6) { if (vlan) { *pkt = dummy_vlan_tcp_ipv6_packet; *pkt_len = sizeof(dummy_vlan_tcp_ipv6_packet); @@ -4885,6 +5762,7 @@ ice_fill_adv_dummy_packet(struct ice_adv_lkup_elem *lkups, u16 lkups_cnt, len = sizeof(struct ice_ether_hdr); break; case ICE_ETYPE_OL: + case ICE_ETYPE_IL: len = sizeof(struct ice_ethtype_hdr); break; case ICE_VLAN_OFOS: @@ -4913,6 +5791,10 @@ ice_fill_adv_dummy_packet(struct ice_adv_lkup_elem *lkups, u16 lkups_cnt, case ICE_GENEVE: len = sizeof(struct ice_udp_tnl_hdr); break; + case ICE_GTP_NO_PAY: + case ICE_GTP: + len = sizeof(struct ice_udp_gtp_hdr); + break; default: return -EINVAL; } diff --git a/drivers/net/ethernet/intel/ice/ice_switch.h b/drivers/net/ethernet/intel/ice/ice_switch.h index d8334beaaa8a..ed3d1d03befa 100644 --- a/drivers/net/ethernet/intel/ice/ice_switch.h +++ b/drivers/net/ethernet/intel/ice/ice_switch.h @@ -14,6 +14,15 @@ #define ICE_VSI_INVAL_ID 0xffff #define ICE_INVAL_Q_HANDLE 0xFFFF +/* Switch Profile IDs for Profile related switch rules */ +#define ICE_PROFID_IPV4_GTPC_TEID 41 +#define ICE_PROFID_IPV4_GTPC_NO_TEID 42 +#define ICE_PROFID_IPV4_GTPU_TEID 43 +#define ICE_PROFID_IPV6_GTPC_TEID 44 +#define ICE_PROFID_IPV6_GTPC_NO_TEID 45 +#define ICE_PROFID_IPV6_GTPU_TEID 46 +#define ICE_PROFID_IPV6_GTPU_IPV6_TCP_INNER 70 + #define ICE_SW_RULE_RX_TX_NO_HDR_SIZE \ (offsetof(struct ice_aqc_sw_rules_elem, pdata.lkup_tx_rx.hdr)) @@ -33,15 +42,6 @@ struct ice_vsi_ctx { struct ice_q_ctx *rdma_q_ctx[ICE_MAX_TRAFFIC_CLASS]; }; -enum ice_sw_fwd_act_type { - ICE_FWD_TO_VSI = 0, - ICE_FWD_TO_VSI_LIST, /* Do not use this when adding filter */ - ICE_FWD_TO_Q, - ICE_FWD_TO_QGRP, - ICE_DROP_PACKET, - ICE_INVAL_ACT -}; - /* Switch recipe ID enum values are specific to hardware */ enum ice_sw_lkup_type { ICE_SW_LKUP_ETHERTYPE = 0, @@ -86,6 +86,8 @@ struct ice_fltr_info { } mac_vlan; struct { u16 vlan_id; + u16 tpid; + u8 tpid_valid; } vlan; /* Set lkup_type as ICE_SW_LKUP_ETHERTYPE * if just using ethertype as filter. Set lkup_type as @@ -125,6 +127,15 @@ struct ice_fltr_info { u8 lan_en; /* Indicate if packet can be forwarded to the uplink */ }; +struct ice_update_recipe_lkup_idx_params { + u16 rid; + u16 fv_idx; + bool ignore_valid; + u16 mask; + bool mask_valid; + u8 lkup_idx; +}; + struct ice_adv_lkup_elem { enum ice_protocol_type type; union ice_prot_hdr h_u; /* Header values */ @@ -367,4 +378,8 @@ void ice_rm_all_sw_replay_rule_info(struct ice_hw *hw); int ice_aq_sw_rules(struct ice_hw *hw, void *rule_list, u16 rule_list_sz, u8 num_rules, enum ice_adminq_opc opc, struct ice_sq_cd *cd); +int +ice_update_recipe_lkup_idx(struct ice_hw *hw, + struct ice_update_recipe_lkup_idx_params *params); +void ice_change_proto_id_to_dvm(void); #endif /* _ICE_SWITCH_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c b/drivers/net/ethernet/intel/ice/ice_tc_lib.c index 65cf32eb4046..3acd9f921c44 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c @@ -24,6 +24,12 @@ ice_tc_count_lkups(u32 flags, struct ice_tc_flower_lyr_2_4_hdrs *headers, if (flags & ICE_TC_FLWR_FIELD_TENANT_ID) lkups_cnt++; + if (flags & ICE_TC_FLWR_FIELD_ENC_DST_MAC) + lkups_cnt++; + + if (flags & ICE_TC_FLWR_FIELD_ENC_OPTS) + lkups_cnt++; + if (flags & (ICE_TC_FLWR_FIELD_ENC_SRC_IPV4 | ICE_TC_FLWR_FIELD_ENC_DEST_IPV4 | ICE_TC_FLWR_FIELD_ENC_SRC_IPV6 | @@ -33,9 +39,7 @@ ice_tc_count_lkups(u32 flags, struct ice_tc_flower_lyr_2_4_hdrs *headers, if (flags & ICE_TC_FLWR_FIELD_ENC_DEST_L4_PORT) lkups_cnt++; - /* currently inner etype filter isn't supported */ - if ((flags & ICE_TC_FLWR_FIELD_ETH_TYPE_ID) && - fltr->tunnel_type == TNL_LAST) + if (flags & ICE_TC_FLWR_FIELD_ETH_TYPE_ID) lkups_cnt++; /* are MAC fields specified? */ @@ -64,6 +68,11 @@ static enum ice_protocol_type ice_proto_type_from_mac(bool inner) return inner ? ICE_MAC_IL : ICE_MAC_OFOS; } +static enum ice_protocol_type ice_proto_type_from_etype(bool inner) +{ + return inner ? ICE_ETYPE_IL : ICE_ETYPE_OL; +} + static enum ice_protocol_type ice_proto_type_from_ipv4(bool inner) { return inner ? ICE_IPV4_IL : ICE_IPV4_OFOS; @@ -96,6 +105,11 @@ ice_proto_type_from_tunnel(enum ice_tunnel_type type) return ICE_GENEVE; case TNL_GRETAP: return ICE_NVGRE; + case TNL_GTPU: + /* NO_PAY profiles will not work with GTP-U */ + return ICE_GTP; + case TNL_GTPC: + return ICE_GTP_NO_PAY; default: return 0; } @@ -111,6 +125,10 @@ ice_sw_type_from_tunnel(enum ice_tunnel_type type) return ICE_SW_TUN_GENEVE; case TNL_GRETAP: return ICE_SW_TUN_NVGRE; + case TNL_GTPU: + return ICE_SW_TUN_GTPU; + case TNL_GTPC: + return ICE_SW_TUN_GTPC; default: return ICE_NON_TUN; } @@ -137,7 +155,15 @@ ice_tc_fill_tunnel_outer(u32 flags, struct ice_tc_flower_fltr *fltr, break; case TNL_GRETAP: list[i].h_u.nvgre_hdr.tni_flow = fltr->tenant_id; - memcpy(&list[i].m_u.nvgre_hdr.tni_flow, "\xff\xff\xff\xff", 4); + memcpy(&list[i].m_u.nvgre_hdr.tni_flow, + "\xff\xff\xff\xff", 4); + i++; + break; + case TNL_GTPC: + case TNL_GTPU: + list[i].h_u.gtp_hdr.teid = fltr->tenant_id; + memcpy(&list[i].m_u.gtp_hdr.teid, + "\xff\xff\xff\xff", 4); i++; break; default: @@ -145,6 +171,33 @@ ice_tc_fill_tunnel_outer(u32 flags, struct ice_tc_flower_fltr *fltr, } } + if (flags & ICE_TC_FLWR_FIELD_ENC_DST_MAC) { + list[i].type = ice_proto_type_from_mac(false); + ether_addr_copy(list[i].h_u.eth_hdr.dst_addr, + hdr->l2_key.dst_mac); + ether_addr_copy(list[i].m_u.eth_hdr.dst_addr, + hdr->l2_mask.dst_mac); + i++; + } + + if (flags & ICE_TC_FLWR_FIELD_ENC_OPTS && + (fltr->tunnel_type == TNL_GTPU || fltr->tunnel_type == TNL_GTPC)) { + list[i].type = ice_proto_type_from_tunnel(fltr->tunnel_type); + + if (fltr->gtp_pdu_info_masks.pdu_type) { + list[i].h_u.gtp_hdr.pdu_type = + fltr->gtp_pdu_info_keys.pdu_type << 4; + memcpy(&list[i].m_u.gtp_hdr.pdu_type, "\xf0", 1); + } + + if (fltr->gtp_pdu_info_masks.qfi) { + list[i].h_u.gtp_hdr.qfi = fltr->gtp_pdu_info_keys.qfi; + memcpy(&list[i].m_u.gtp_hdr.qfi, "\x3f", 1); + } + + i++; + } + if (flags & (ICE_TC_FLWR_FIELD_ENC_SRC_IPV4 | ICE_TC_FLWR_FIELD_ENC_DEST_IPV4)) { list[i].type = ice_proto_type_from_ipv4(false); @@ -224,8 +277,10 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags, headers = &tc_fltr->inner_headers; inner = true; - } else if (flags & ICE_TC_FLWR_FIELD_ETH_TYPE_ID) { - list[i].type = ICE_ETYPE_OL; + } + + if (flags & ICE_TC_FLWR_FIELD_ETH_TYPE_ID) { + list[i].type = ice_proto_type_from_etype(inner); list[i].h_u.ethertype.ethtype_id = headers->l2_key.n_proto; list[i].m_u.ethertype.ethtype_id = headers->l2_mask.n_proto; i++; @@ -344,6 +399,12 @@ static int ice_tc_tun_get_type(struct net_device *tunnel_dev) if (netif_is_gretap(tunnel_dev) || netif_is_ip6gretap(tunnel_dev)) return TNL_GRETAP; + + /* Assume GTP-U by default in case of GTP netdev. + * GTP-C may be selected later, based on enc_dst_port. + */ + if (netif_is_gtp(tunnel_dev)) + return TNL_GTPU; return TNL_LAST; } @@ -743,6 +804,40 @@ ice_get_tunnel_device(struct net_device *dev, struct flow_rule *rule) return NULL; } +/** + * ice_parse_gtp_type - Sets GTP tunnel type to GTP-U or GTP-C + * @match: Flow match structure + * @fltr: Pointer to filter structure + * + * GTP-C/GTP-U is selected based on destination port number (enc_dst_port). + * Before calling this funtcion, fltr->tunnel_type should be set to TNL_GTPU, + * therefore making GTP-U the default choice (when destination port number is + * not specified). + */ +static int +ice_parse_gtp_type(struct flow_match_ports match, + struct ice_tc_flower_fltr *fltr) +{ + u16 dst_port; + + if (match.key->dst) { + dst_port = be16_to_cpu(match.key->dst); + + switch (dst_port) { + case 2152: + break; + case 2123: + fltr->tunnel_type = TNL_GTPC; + break; + default: + NL_SET_ERR_MSG_MOD(fltr->extack, "Unsupported GTP port number"); + return -EINVAL; + } + } + + return 0; +} + static int ice_parse_tunnel_attr(struct net_device *dev, struct flow_rule *rule, struct ice_tc_flower_fltr *fltr) @@ -798,8 +893,28 @@ ice_parse_tunnel_attr(struct net_device *dev, struct flow_rule *rule, struct flow_match_ports match; flow_rule_match_enc_ports(rule, &match); - if (ice_tc_set_port(match, fltr, headers, true)) - return -EINVAL; + + if (fltr->tunnel_type != TNL_GTPU) { + if (ice_tc_set_port(match, fltr, headers, true)) + return -EINVAL; + } else { + if (ice_parse_gtp_type(match, fltr)) + return -EINVAL; + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_OPTS)) { + struct flow_match_enc_opts match; + + flow_rule_match_enc_opts(rule, &match); + + memcpy(&fltr->gtp_pdu_info_keys, &match.key->data[0], + sizeof(struct gtp_pdu_session_info)); + + memcpy(&fltr->gtp_pdu_info_masks, &match.mask->data[0], + sizeof(struct gtp_pdu_session_info)); + + fltr->flags |= ICE_TC_FLWR_FIELD_ENC_OPTS; } return 0; @@ -837,6 +952,7 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi, BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) | BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) | BIT(FLOW_DISSECTOR_KEY_ENC_PORTS) | + BIT(FLOW_DISSECTOR_KEY_ENC_OPTS) | BIT(FLOW_DISSECTOR_KEY_ENC_IP) | BIT(FLOW_DISSECTOR_KEY_PORTS))) { NL_SET_ERR_MSG_MOD(fltr->extack, "Unsupported key used"); @@ -1059,12 +1175,24 @@ ice_handle_tclass_action(struct ice_vsi *vsi, * this code won't do anything * 2. For non-tunnel, if user didn't specify MAC address, add implicit * dest MAC to be lower netdev's active unicast MAC address + * 3. For tunnel, as of now TC-filter through flower classifier doesn't + * have provision for user to specify outer DMAC, hence driver to + * implicitly add outer dest MAC to be lower netdev's active unicast + * MAC address. */ - if (!(fltr->flags & ICE_TC_FLWR_FIELD_DST_MAC)) { - ether_addr_copy(fltr->outer_headers.l2_key.dst_mac, - main_vsi->netdev->dev_addr); - eth_broadcast_addr(fltr->outer_headers.l2_mask.dst_mac); + if (fltr->tunnel_type != TNL_LAST && + !(fltr->flags & ICE_TC_FLWR_FIELD_ENC_DST_MAC)) + fltr->flags |= ICE_TC_FLWR_FIELD_ENC_DST_MAC; + + if (fltr->tunnel_type == TNL_LAST && + !(fltr->flags & ICE_TC_FLWR_FIELD_DST_MAC)) fltr->flags |= ICE_TC_FLWR_FIELD_DST_MAC; + + if (fltr->flags & (ICE_TC_FLWR_FIELD_DST_MAC | + ICE_TC_FLWR_FIELD_ENC_DST_MAC)) { + ether_addr_copy(fltr->outer_headers.l2_key.dst_mac, + vsi->netdev->dev_addr); + memset(fltr->outer_headers.l2_mask.dst_mac, 0xff, ETH_ALEN); } /* validate specified dest MAC address, make sure either it belongs to diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.h b/drivers/net/ethernet/intel/ice/ice_tc_lib.h index 319049477959..e25e958f4396 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.h @@ -22,6 +22,7 @@ #define ICE_TC_FLWR_FIELD_ENC_SRC_L4_PORT BIT(15) #define ICE_TC_FLWR_FIELD_ENC_DST_MAC BIT(16) #define ICE_TC_FLWR_FIELD_ETH_TYPE_ID BIT(17) +#define ICE_TC_FLWR_FIELD_ENC_OPTS BIT(18) #define ICE_TC_FLOWER_MASK_32 0xFFFFFFFF @@ -119,6 +120,8 @@ struct ice_tc_flower_fltr { struct ice_tc_flower_lyr_2_4_hdrs inner_headers; struct ice_vsi *src_vsi; __be32 tenant_id; + struct gtp_pdu_session_info gtp_pdu_info_keys; + struct gtp_pdu_session_info gtp_pdu_info_masks; u32 flags; u8 tunnel_type; struct ice_tc_flower_action action; diff --git a/drivers/net/ethernet/intel/ice/ice_trace.h b/drivers/net/ethernet/intel/ice/ice_trace.h index cf685247c07a..ae98d5a8ff60 100644 --- a/drivers/net/ethernet/intel/ice/ice_trace.h +++ b/drivers/net/ethernet/intel/ice/ice_trace.h @@ -216,6 +216,30 @@ DEFINE_EVENT(ice_xmit_template, name, \ DEFINE_XMIT_TEMPLATE_OP_EVENT(ice_xmit_frame_ring); DEFINE_XMIT_TEMPLATE_OP_EVENT(ice_xmit_frame_ring_drop); +DECLARE_EVENT_CLASS(ice_tx_tstamp_template, + TP_PROTO(struct sk_buff *skb, int idx), + + TP_ARGS(skb, idx), + + TP_STRUCT__entry(__field(void *, skb) + __field(int, idx)), + + TP_fast_assign(__entry->skb = skb; + __entry->idx = idx;), + + TP_printk("skb %pK idx %d", + __entry->skb, __entry->idx) +); +#define DEFINE_TX_TSTAMP_OP_EVENT(name) \ +DEFINE_EVENT(ice_tx_tstamp_template, name, \ + TP_PROTO(struct sk_buff *skb, int idx), \ + TP_ARGS(skb, idx)) + +DEFINE_TX_TSTAMP_OP_EVENT(ice_tx_tstamp_request); +DEFINE_TX_TSTAMP_OP_EVENT(ice_tx_tstamp_fw_req); +DEFINE_TX_TSTAMP_OP_EVENT(ice_tx_tstamp_fw_done); +DEFINE_TX_TSTAMP_OP_EVENT(ice_tx_tstamp_complete); + /* End tracepoints */ #endif /* _ICE_TRACE_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index 3e38695f1c9d..f9bf008471c9 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -173,6 +173,8 @@ void ice_clean_tx_ring(struct ice_tx_ring *tx_ring) 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; @@ -221,8 +223,7 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) struct ice_tx_buf *tx_buf; /* get the bql data ready */ - if (!ice_ring_is_xdp(tx_ring)) - netdev_txq_bql_complete_prefetchw(txring_txq(tx_ring)); + netdev_txq_bql_complete_prefetchw(txring_txq(tx_ring)); tx_buf = &tx_ring->tx_buf[i]; tx_desc = ICE_TX_DESC(tx_ring, i); @@ -311,10 +312,6 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) tx_ring->next_to_clean = i; ice_update_tx_ring_stats(tx_ring, total_pkts, total_bytes); - - if (ice_ring_is_xdp(tx_ring)) - return !!budget; - netdev_tx_completed_queue(txring_txq(tx_ring), total_pkts, total_bytes); #define TX_WAKE_THRESHOLD ((s16)(DESC_NEEDED * 2)) @@ -983,15 +980,17 @@ static struct sk_buff * ice_construct_skb(struct ice_rx_ring *rx_ring, struct ice_rx_buf *rx_buf, struct xdp_buff *xdp) { + unsigned int metasize = xdp->data - xdp->data_meta; unsigned int size = xdp->data_end - xdp->data; unsigned int headlen; struct sk_buff *skb; /* prefetch first cache line of first page */ - net_prefetch(xdp->data); + net_prefetch(xdp->data_meta); /* allocate a skb to store the frags */ - skb = __napi_alloc_skb(&rx_ring->q_vector->napi, ICE_RX_HDR_SIZE, + skb = __napi_alloc_skb(&rx_ring->q_vector->napi, + ICE_RX_HDR_SIZE + metasize, GFP_ATOMIC | __GFP_NOWARN); if (unlikely(!skb)) return NULL; @@ -1003,8 +1002,13 @@ ice_construct_skb(struct ice_rx_ring *rx_ring, struct ice_rx_buf *rx_buf, headlen = eth_get_headlen(skb->dev, xdp->data, ICE_RX_HDR_SIZE); /* align pull length to size of long to optimize memcpy performance */ - memcpy(__skb_put(skb, headlen), xdp->data, ALIGN(headlen, - sizeof(long))); + memcpy(__skb_put(skb, headlen + metasize), xdp->data_meta, + ALIGN(headlen + metasize, sizeof(long))); + + if (metasize) { + skb_metadata_set(skb, metasize); + __skb_pull(skb, metasize); + } /* if we exhaust the linear part then add what is left as a frag */ size -= headlen; @@ -1080,7 +1084,7 @@ ice_is_non_eop(struct ice_rx_ring *rx_ring, union ice_32b_rx_flex_desc *rx_desc) { /* if we are the last buffer then there is nothing else to do */ #define ICE_RXD_EOF BIT(ICE_RX_FLEX_DESC_STATUS0_EOF_S) - if (likely(ice_test_staterr(rx_desc, ICE_RXD_EOF))) + if (likely(ice_test_staterr(rx_desc->wb.status_error0, ICE_RXD_EOF))) return false; rx_ring->rx_stats.non_eop_descs++; @@ -1142,7 +1146,7 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) * hardware wrote DD then it will be non-zero */ stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_DD_S); - if (!ice_test_staterr(rx_desc, stat_err_bits)) + if (!ice_test_staterr(rx_desc->wb.status_error0, stat_err_bits)) break; /* This memory barrier is needed to keep us from reading @@ -1156,7 +1160,7 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) struct ice_vsi *ctrl_vsi = rx_ring->vsi; if (rx_desc->wb.rxdid == FDIR_DESC_RXDID && - ctrl_vsi->vf_id != ICE_INVAL_VFID) + ctrl_vsi->vf) ice_vc_fdir_irq_handler(ctrl_vsi, rx_desc); ice_put_rx_buf(rx_ring, NULL, 0); cleaned_count++; @@ -1228,14 +1232,13 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) continue; stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_RXE_S); - if (unlikely(ice_test_staterr(rx_desc, stat_err_bits))) { + if (unlikely(ice_test_staterr(rx_desc->wb.status_error0, + stat_err_bits))) { dev_kfree_skb_any(skb); continue; } - stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_L2TAG1P_S); - if (ice_test_staterr(rx_desc, stat_err_bits)) - vlan_tag = le16_to_cpu(rx_desc->wb.l2tag1); + vlan_tag = ice_get_vlan_tag_from_rx_desc(rx_desc); /* pad the skb if needed, to make a valid ethernet frame */ if (eth_skb_pad(skb)) { @@ -1460,7 +1463,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 @@ -1513,7 +1516,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 { @@ -1917,12 +1920,16 @@ ice_tx_prepare_vlan_flags(struct ice_tx_ring *tx_ring, struct ice_tx_buf *first) if (!skb_vlan_tag_present(skb) && eth_type_vlan(skb->protocol)) return; - /* currently, we always assume 802.1Q for VLAN insertion as VLAN - * insertion for 802.1AD is not supported + /* the VLAN ethertype/tpid is determined by VSI configuration and netdev + * feature flags, which the driver only allows either 802.1Q or 802.1ad + * VLAN offloads exclusively so we only care about the VLAN ID here */ if (skb_vlan_tag_present(skb)) { first->tx_flags |= skb_vlan_tag_get(skb) << ICE_TX_FLAGS_VLAN_S; - first->tx_flags |= ICE_TX_FLAGS_HW_VLAN; + if (tx_ring->flags & ICE_TX_FLAGS_RING_VLAN_L2TAG2) + first->tx_flags |= ICE_TX_FLAGS_HW_OUTER_SINGLE_VLAN; + else + first->tx_flags |= ICE_TX_FLAGS_HW_VLAN; } ice_tx_prepare_vlan_flags_dcb(tx_ring, first); @@ -2295,6 +2302,13 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) /* prepare the VLAN tagging flags for Tx */ ice_tx_prepare_vlan_flags(tx_ring, first); + if (first->tx_flags & ICE_TX_FLAGS_HW_OUTER_SINGLE_VLAN) { + offload.cd_qw1 |= (u64)(ICE_TX_DESC_DTYPE_CTX | + (ICE_TX_CTX_DESC_IL2TAG2 << + ICE_TXD_CTX_QW1_CMD_S)); + offload.cd_l2tag2 = (first->tx_flags & ICE_TX_FLAGS_VLAN_M) >> + ICE_TX_FLAGS_VLAN_S; + } /* set up TSO offload */ tso = ice_tso(first, &offload); diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.h b/drivers/net/ethernet/intel/ice/ice_txrx.h index b7b3bd4816f0..cead3eb149bd 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.h +++ b/drivers/net/ethernet/intel/ice/ice_txrx.h @@ -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) @@ -122,6 +123,7 @@ static inline int ice_skb_pad(void) #define ICE_TX_FLAGS_IPV4 BIT(5) #define ICE_TX_FLAGS_IPV6 BIT(6) #define ICE_TX_FLAGS_TUNNEL BIT(7) +#define ICE_TX_FLAGS_HW_OUTER_SINGLE_VLAN BIT(8) #define ICE_TX_FLAGS_VLAN_M 0xffff0000 #define ICE_TX_FLAGS_VLAN_PR_M 0xe0000000 #define ICE_TX_FLAGS_VLAN_PR_S 29 @@ -321,18 +323,21 @@ 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) +#define ICE_TX_FLAGS_RING_VLAN_L2TAG1 BIT(1) +#define ICE_TX_FLAGS_RING_VLAN_L2TAG2 BIT(2) u8 flags; u8 dcb_tc; /* Traffic class of ring */ u8 ptp_tx; diff --git a/drivers/net/ethernet/intel/ice/ice_txrx_lib.c b/drivers/net/ethernet/intel/ice/ice_txrx_lib.c index 0e87b98e0966..7ee38d02d1e5 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx_lib.c @@ -209,9 +209,14 @@ ice_process_skb_fields(struct ice_rx_ring *rx_ring, void ice_receive_skb(struct ice_rx_ring *rx_ring, struct sk_buff *skb, u16 vlan_tag) { - if ((rx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && - (vlan_tag & VLAN_VID_MASK)) + netdev_features_t features = rx_ring->netdev->features; + bool non_zero_vlan = !!(vlan_tag & VLAN_VID_MASK); + + if ((features & NETIF_F_HW_VLAN_CTAG_RX) && non_zero_vlan) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); + else if ((features & NETIF_F_HW_VLAN_STAG_RX) && non_zero_vlan) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021AD), vlan_tag); + napi_gro_receive(&rx_ring->q_vector->napi, skb); } @@ -222,6 +227,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 +239,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 +260,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 +275,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 +307,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 +322,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; diff --git a/drivers/net/ethernet/intel/ice/ice_txrx_lib.h b/drivers/net/ethernet/intel/ice/ice_txrx_lib.h index 11b6c1601986..c7d2954dc9ea 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_txrx_lib.h @@ -7,7 +7,7 @@ /** * ice_test_staterr - tests bits in Rx descriptor status and error fields - * @rx_desc: pointer to receive descriptor (in le64 format) + * @status_err_n: Rx descriptor status_error0 or status_error1 bits * @stat_err_bits: value to mask * * This function does some fast chicanery in order to return the @@ -16,9 +16,9 @@ * at offset zero. */ static inline bool -ice_test_staterr(union ice_32b_rx_flex_desc *rx_desc, const u16 stat_err_bits) +ice_test_staterr(__le16 status_err_n, const u16 stat_err_bits) { - return !!(rx_desc->wb.status_error0 & cpu_to_le16(stat_err_bits)); + return !!(status_err_n & cpu_to_le16(stat_err_bits)); } static inline __le64 @@ -31,6 +31,30 @@ ice_build_ctob(u64 td_cmd, u64 td_offset, unsigned int size, u64 td_tag) (td_tag << ICE_TXD_QW1_L2TAG1_S)); } +/** + * ice_get_vlan_tag_from_rx_desc - get VLAN from Rx flex descriptor + * @rx_desc: Rx 32b flex descriptor with RXDID=2 + * + * The OS and current PF implementation only support stripping a single VLAN tag + * at a time, so there should only ever be 0 or 1 tags in the l2tag* fields. If + * one is found return the tag, else return 0 to mean no VLAN tag was found. + */ +static inline u16 +ice_get_vlan_tag_from_rx_desc(union ice_32b_rx_flex_desc *rx_desc) +{ + u16 stat_err_bits; + + stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_L2TAG1P_S); + if (ice_test_staterr(rx_desc->wb.status_error0, stat_err_bits)) + return le16_to_cpu(rx_desc->wb.l2tag1); + + stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS1_L2TAG2P_S); + if (ice_test_staterr(rx_desc->wb.status_error1, stat_err_bits)) + return le16_to_cpu(rx_desc->wb.l2tag2_2nd); + + return 0; +} + /** * ice_xdp_ring_update_tail - Updates the XDP Tx ring tail register * @xdp_ring: XDP Tx ring diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index 546145dd1f02..f2a518a1fd94 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -9,12 +9,14 @@ #define ICE_CHNL_MAX_TC 16 #include "ice_hw_autogen.h" +#include "ice_devids.h" #include "ice_osdep.h" #include "ice_controlq.h" #include "ice_lan_tx_rx.h" #include "ice_flex_type.h" #include "ice_protocol_type.h" #include "ice_sbq_cmd.h" +#include "ice_vlan_mode.h" static inline bool ice_is_tc_ena(unsigned long bitmap, u8 tc) { @@ -54,6 +56,11 @@ static inline u32 ice_round_to_num(u32 N, u32 R) #define ICE_DBG_AQ_DESC BIT_ULL(25) #define ICE_DBG_AQ_DESC_BUF BIT_ULL(26) #define ICE_DBG_AQ_CMD BIT_ULL(27) +#define ICE_DBG_AQ (ICE_DBG_AQ_MSG | \ + ICE_DBG_AQ_DESC | \ + ICE_DBG_AQ_DESC_BUF | \ + ICE_DBG_AQ_CMD) + #define ICE_DBG_USER BIT_ULL(31) enum ice_aq_res_ids { @@ -920,6 +927,9 @@ struct ice_hw { struct udp_tunnel_nic_shared udp_tunnel_shared; struct udp_tunnel_nic_info udp_tunnel_nic; + /* dvm boost update information */ + struct ice_dvm_table dvm_upd; + /* HW block tables */ struct ice_blk_info blk[ICE_BLK_COUNT]; struct mutex fl_profs_locks[ICE_BLK_COUNT]; /* lock fltr profiles */ @@ -943,6 +953,7 @@ struct ice_hw { struct list_head rss_list_head; struct ice_mbx_snapshot mbx_snapshot; DECLARE_BITMAP(hw_ptype, ICE_FLOW_PTYPE_MAX); + u8 dvm_ena; u16 io_expander_handle; }; @@ -1008,6 +1019,15 @@ struct ice_hw_port_stats { u64 fd_sb_match; }; +enum ice_sw_fwd_act_type { + ICE_FWD_TO_VSI = 0, + ICE_FWD_TO_VSI_LIST, /* Do not use this when adding filter */ + ICE_FWD_TO_Q, + ICE_FWD_TO_QGRP, + ICE_DROP_PACKET, + ICE_INVAL_ACT +}; + struct ice_aq_get_set_rss_lut_params { u16 vsi_handle; /* software VSI handle */ u16 lut_size; /* size of the LUT buffer */ diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c new file mode 100644 index 000000000000..6578059d9479 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -0,0 +1,1029 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2022, Intel Corporation. */ + +#include "ice_vf_lib_private.h" +#include "ice.h" +#include "ice_lib.h" +#include "ice_fltr.h" +#include "ice_virtchnl_allowlist.h" + +/* Public functions which may be accessed by all driver files */ + +/** + * ice_get_vf_by_id - Get pointer to VF by ID + * @pf: the PF private structure + * @vf_id: the VF ID to locate + * + * Locate and return a pointer to the VF structure associated with a given ID. + * Returns NULL if the ID does not have a valid VF structure associated with + * it. + * + * This function takes a reference to the VF, which must be released by + * calling ice_put_vf() once the caller is finished accessing the VF structure + * returned. + */ +struct ice_vf *ice_get_vf_by_id(struct ice_pf *pf, u16 vf_id) +{ + struct ice_vf *vf; + + rcu_read_lock(); + hash_for_each_possible_rcu(pf->vfs.table, vf, entry, vf_id) { + if (vf->vf_id == vf_id) { + struct ice_vf *found; + + if (kref_get_unless_zero(&vf->refcnt)) + found = vf; + else + found = NULL; + + rcu_read_unlock(); + return found; + } + } + rcu_read_unlock(); + + return NULL; +} + +/** + * ice_release_vf - Release VF associated with a refcount + * @ref: the kref decremented to zero + * + * Callback function for kref_put to release a VF once its reference count has + * hit zero. + */ +static void ice_release_vf(struct kref *ref) +{ + struct ice_vf *vf = container_of(ref, struct ice_vf, refcnt); + + vf->vf_ops->free(vf); +} + +/** + * ice_put_vf - Release a reference to a VF + * @vf: the VF structure to decrease reference count on + * + * Decrease the reference count for a VF, and free the entry if it is no + * longer in use. + * + * This must be called after ice_get_vf_by_id() once the reference to the VF + * structure is no longer used. Otherwise, the VF structure will never be + * freed. + */ +void ice_put_vf(struct ice_vf *vf) +{ + kref_put(&vf->refcnt, ice_release_vf); +} + +/** + * ice_has_vfs - Return true if the PF has any associated VFs + * @pf: the PF private structure + * + * Return whether or not the PF has any allocated VFs. + * + * Note that this function only guarantees that there are no VFs at the point + * of calling it. It does not guarantee that no more VFs will be added. + */ +bool ice_has_vfs(struct ice_pf *pf) +{ + /* A simple check that the hash table is not empty does not require + * the mutex or rcu_read_lock. + */ + return !hash_empty(pf->vfs.table); +} + +/** + * ice_get_num_vfs - Get number of allocated VFs + * @pf: the PF private structure + * + * Return the total number of allocated VFs. NOTE: VF IDs are not guaranteed + * to be contiguous. Do not assume that a VF ID is guaranteed to be less than + * the output of this function. + */ +u16 ice_get_num_vfs(struct ice_pf *pf) +{ + struct ice_vf *vf; + unsigned int bkt; + u16 num_vfs = 0; + + rcu_read_lock(); + ice_for_each_vf_rcu(pf, bkt, vf) + num_vfs++; + rcu_read_unlock(); + + return num_vfs; +} + +/** + * ice_get_vf_vsi - get VF's VSI based on the stored index + * @vf: VF used to get VSI + */ +struct ice_vsi *ice_get_vf_vsi(struct ice_vf *vf) +{ + if (vf->lan_vsi_idx == ICE_NO_VSI) + return NULL; + + return vf->pf->vsi[vf->lan_vsi_idx]; +} + +/** + * ice_is_vf_disabled + * @vf: pointer to the VF info + * + * If the PF has been disabled, there is no need resetting VF until PF is + * active again. Similarly, if the VF has been disabled, this means something + * else is resetting the VF, so we shouldn't continue. + * + * Returns true if the caller should consider the VF as disabled whether + * because that single VF is explicitly disabled or because the PF is + * currently disabled. + */ +bool ice_is_vf_disabled(struct ice_vf *vf) +{ + struct ice_pf *pf = vf->pf; + + return (test_bit(ICE_VF_DIS, pf->state) || + test_bit(ICE_VF_STATE_DIS, vf->vf_states)); +} + +/** + * ice_wait_on_vf_reset - poll to make sure a given VF is ready after reset + * @vf: The VF being resseting + * + * The max poll time is about ~800ms, which is about the maximum time it takes + * for a VF to be reset and/or a VF driver to be removed. + */ +static void ice_wait_on_vf_reset(struct ice_vf *vf) +{ + int i; + + for (i = 0; i < ICE_MAX_VF_RESET_TRIES; i++) { + if (test_bit(ICE_VF_STATE_INIT, vf->vf_states)) + break; + msleep(ICE_MAX_VF_RESET_SLEEP_MS); + } +} + +/** + * ice_check_vf_ready_for_cfg - check if VF is ready to be configured/queried + * @vf: VF to check if it's ready to be configured/queried + * + * The purpose of this function is to make sure the VF is not in reset, not + * disabled, and initialized so it can be configured and/or queried by a host + * administrator. + */ +int ice_check_vf_ready_for_cfg(struct ice_vf *vf) +{ + ice_wait_on_vf_reset(vf); + + if (ice_is_vf_disabled(vf)) + return -EINVAL; + + if (ice_check_vf_init(vf)) + return -EBUSY; + + return 0; +} + +/** + * ice_trigger_vf_reset - Reset a VF on HW + * @vf: pointer to the VF structure + * @is_vflr: true if VFLR was issued, false if not + * @is_pfr: true if the reset was triggered due to a previous PFR + * + * Trigger hardware to start a reset for a particular VF. Expects the caller + * to wait the proper amount of time to allow hardware to reset the VF before + * it cleans up and restores VF functionality. + */ +static void ice_trigger_vf_reset(struct ice_vf *vf, bool is_vflr, bool is_pfr) +{ + /* Inform VF that it is no longer active, as a warning */ + clear_bit(ICE_VF_STATE_ACTIVE, vf->vf_states); + + /* Disable VF's configuration API during reset. The flag is re-enabled + * when it's safe again to access VF's VSI. + */ + clear_bit(ICE_VF_STATE_INIT, vf->vf_states); + + /* VF_MBX_ARQLEN and VF_MBX_ATQLEN are cleared by PFR, so the driver + * needs to clear them in the case of VFR/VFLR. If this is done for + * PFR, it can mess up VF resets because the VF driver may already + * have started cleanup by the time we get here. + */ + if (!is_pfr) + vf->vf_ops->clear_mbx_register(vf); + + vf->vf_ops->trigger_reset_register(vf, is_vflr); +} + +static void ice_vf_clear_counters(struct ice_vf *vf) +{ + struct ice_vsi *vsi = ice_get_vf_vsi(vf); + + vf->num_mac = 0; + vsi->num_vlan = 0; + memset(&vf->mdd_tx_events, 0, sizeof(vf->mdd_tx_events)); + memset(&vf->mdd_rx_events, 0, sizeof(vf->mdd_rx_events)); +} + +/** + * ice_vf_pre_vsi_rebuild - tasks to be done prior to VSI rebuild + * @vf: VF to perform pre VSI rebuild tasks + * + * These tasks are items that don't need to be amortized since they are most + * likely called in a for loop with all VF(s) in the reset_all_vfs() case. + */ +static void ice_vf_pre_vsi_rebuild(struct ice_vf *vf) +{ + ice_vf_clear_counters(vf); + vf->vf_ops->clear_reset_trigger(vf); +} + +/** + * ice_vf_rebuild_vsi - rebuild the VF's VSI + * @vf: VF to rebuild the VSI for + * + * This is only called when all VF(s) are being reset (i.e. PCIe Reset on the + * host, PFR, CORER, etc.). + */ +static int ice_vf_rebuild_vsi(struct ice_vf *vf) +{ + struct ice_vsi *vsi = ice_get_vf_vsi(vf); + struct ice_pf *pf = vf->pf; + + if (ice_vsi_rebuild(vsi, true)) { + dev_err(ice_pf_to_dev(pf), "failed to rebuild VF %d VSI\n", + vf->vf_id); + return -EIO; + } + /* vsi->idx will remain the same in this case so don't update + * vf->lan_vsi_idx + */ + vsi->vsi_num = ice_get_hw_vsi_num(&pf->hw, vsi->idx); + vf->lan_vsi_num = vsi->vsi_num; + + return 0; +} + +/** + * ice_is_any_vf_in_promisc - check if any VF(s) are in promiscuous mode + * @pf: PF structure for accessing VF(s) + * + * Return false if no VF(s) are in unicast and/or multicast promiscuous mode, + * else return true + */ +bool ice_is_any_vf_in_promisc(struct ice_pf *pf) +{ + bool is_vf_promisc = false; + struct ice_vf *vf; + unsigned int bkt; + + rcu_read_lock(); + ice_for_each_vf_rcu(pf, bkt, vf) { + /* found a VF that has promiscuous mode configured */ + if (test_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states) || + test_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states)) { + is_vf_promisc = true; + break; + } + } + rcu_read_unlock(); + + return is_vf_promisc; +} + +/** + * ice_vf_set_vsi_promisc - Enable promiscuous mode for a VF VSI + * @vf: the VF to configure + * @vsi: the VF's VSI + * @promisc_m: the promiscuous mode to enable + */ +int +ice_vf_set_vsi_promisc(struct ice_vf *vf, struct ice_vsi *vsi, u8 promisc_m) +{ + struct ice_hw *hw = &vsi->back->hw; + int status; + + if (ice_vf_is_port_vlan_ena(vf)) + status = ice_fltr_set_vsi_promisc(hw, vsi->idx, promisc_m, + ice_vf_get_port_vlan_id(vf)); + else if (ice_vsi_has_non_zero_vlans(vsi)) + status = ice_fltr_set_vlan_vsi_promisc(hw, vsi, promisc_m); + else + status = ice_fltr_set_vsi_promisc(hw, vsi->idx, promisc_m, 0); + + if (status && status != -EEXIST) { + dev_err(ice_pf_to_dev(vsi->back), "enable Tx/Rx filter promiscuous mode on VF-%u failed, error: %d\n", + vf->vf_id, status); + return status; + } + + return 0; +} + +/** + * ice_vf_clear_vsi_promisc - Disable promiscuous mode for a VF VSI + * @vf: the VF to configure + * @vsi: the VF's VSI + * @promisc_m: the promiscuous mode to disable + */ +int +ice_vf_clear_vsi_promisc(struct ice_vf *vf, struct ice_vsi *vsi, u8 promisc_m) +{ + struct ice_hw *hw = &vsi->back->hw; + int status; + + if (ice_vf_is_port_vlan_ena(vf)) + status = ice_fltr_clear_vsi_promisc(hw, vsi->idx, promisc_m, + ice_vf_get_port_vlan_id(vf)); + else if (ice_vsi_has_non_zero_vlans(vsi)) + status = ice_fltr_clear_vlan_vsi_promisc(hw, vsi, promisc_m); + else + status = ice_fltr_clear_vsi_promisc(hw, vsi->idx, promisc_m, 0); + + if (status && status != -ENOENT) { + dev_err(ice_pf_to_dev(vsi->back), "disable Tx/Rx filter promiscuous mode on VF-%u failed, error: %d\n", + vf->vf_id, status); + return status; + } + + return 0; +} + +/** + * ice_reset_all_vfs - reset all allocated VFs in one go + * @pf: pointer to the PF structure + * + * First, tell the hardware to reset each VF, then do all the waiting in one + * chunk, and finally finish restoring each VF after the wait. This is useful + * during PF routines which need to reset all VFs, as otherwise it must perform + * these resets in a serialized fashion. + * + * Returns true if any VFs were reset, and false otherwise. + */ +void ice_reset_all_vfs(struct ice_pf *pf) +{ + struct device *dev = ice_pf_to_dev(pf); + struct ice_hw *hw = &pf->hw; + struct ice_vf *vf; + unsigned int bkt; + + /* If we don't have any VFs, then there is nothing to reset */ + if (!ice_has_vfs(pf)) + return; + + mutex_lock(&pf->vfs.table_lock); + + /* clear all malicious info if the VFs are getting reset */ + ice_for_each_vf(pf, bkt, vf) + if (ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->vfs.malvfs, + ICE_MAX_SRIOV_VFS, vf->vf_id)) + dev_dbg(dev, "failed to clear malicious VF state for VF %u\n", + vf->vf_id); + + /* If VFs have been disabled, there is no need to reset */ + if (test_and_set_bit(ICE_VF_DIS, pf->state)) { + mutex_unlock(&pf->vfs.table_lock); + return; + } + + /* Begin reset on all VFs at once */ + ice_for_each_vf(pf, bkt, vf) + ice_trigger_vf_reset(vf, true, true); + + /* HW requires some time to make sure it can flush the FIFO for a VF + * when it resets it. Now that we've triggered all of the VFs, iterate + * the table again and wait for each VF to complete. + */ + ice_for_each_vf(pf, bkt, vf) { + if (!vf->vf_ops->poll_reset_status(vf)) { + /* Display a warning if at least one VF didn't manage + * to reset in time, but continue on with the + * operation. + */ + dev_warn(dev, "VF %u reset check timeout\n", vf->vf_id); + break; + } + } + + /* free VF resources to begin resetting the VSI state */ + ice_for_each_vf(pf, bkt, vf) { + mutex_lock(&vf->cfg_lock); + + vf->driver_caps = 0; + ice_vc_set_default_allowlist(vf); + + ice_vf_fdir_exit(vf); + ice_vf_fdir_init(vf); + /* clean VF control VSI when resetting VFs since it should be + * setup only when VF creates its first FDIR rule. + */ + if (vf->ctrl_vsi_idx != ICE_NO_VSI) + ice_vf_ctrl_invalidate_vsi(vf); + + ice_vf_pre_vsi_rebuild(vf); + ice_vf_rebuild_vsi(vf); + vf->vf_ops->post_vsi_rebuild(vf); + + mutex_unlock(&vf->cfg_lock); + } + + if (ice_is_eswitch_mode_switchdev(pf)) + if (ice_eswitch_rebuild(pf)) + dev_warn(dev, "eswitch rebuild failed\n"); + + ice_flush(hw); + clear_bit(ICE_VF_DIS, pf->state); + + mutex_unlock(&pf->vfs.table_lock); +} + +/** + * ice_notify_vf_reset - Notify VF of a reset event + * @vf: pointer to the VF structure + */ +static void ice_notify_vf_reset(struct ice_vf *vf) +{ + struct ice_hw *hw = &vf->pf->hw; + struct virtchnl_pf_event pfe; + + /* Bail out if VF is in disabled state, neither initialized, nor active + * state - otherwise proceed with notifications + */ + if ((!test_bit(ICE_VF_STATE_INIT, vf->vf_states) && + !test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) || + test_bit(ICE_VF_STATE_DIS, vf->vf_states)) + return; + + pfe.event = VIRTCHNL_EVENT_RESET_IMPENDING; + pfe.severity = PF_EVENT_SEVERITY_CERTAIN_DOOM; + ice_aq_send_msg_to_vf(hw, vf->vf_id, VIRTCHNL_OP_EVENT, + VIRTCHNL_STATUS_SUCCESS, (u8 *)&pfe, sizeof(pfe), + NULL); +} + +/** + * ice_reset_vf - Reset a particular VF + * @vf: pointer to the VF structure + * @flags: flags controlling behavior of the reset + * + * Flags: + * ICE_VF_RESET_VFLR - Indicates a reset is due to VFLR event + * ICE_VF_RESET_NOTIFY - Send VF a notification prior to reset + * ICE_VF_RESET_LOCK - Acquire VF cfg_lock before resetting + * + * Returns 0 if the VF is currently in reset, if the resets are disabled, or + * if the VF resets successfully. Returns an error code if the VF fails to + * rebuild. + */ +int ice_reset_vf(struct ice_vf *vf, u32 flags) +{ + struct ice_pf *pf = vf->pf; + struct ice_vsi *vsi; + struct device *dev; + struct ice_hw *hw; + u8 promisc_m; + int err = 0; + bool rsd; + + dev = ice_pf_to_dev(pf); + hw = &pf->hw; + + if (flags & ICE_VF_RESET_NOTIFY) + ice_notify_vf_reset(vf); + + if (test_bit(ICE_VF_RESETS_DISABLED, pf->state)) { + dev_dbg(dev, "Trying to reset VF %d, but all VF resets are disabled\n", + vf->vf_id); + return 0; + } + + if (ice_is_vf_disabled(vf)) { + dev_dbg(dev, "VF is already disabled, there is no need for resetting it, telling VM, all is fine %d\n", + vf->vf_id); + return 0; + } + + if (flags & ICE_VF_RESET_LOCK) + mutex_lock(&vf->cfg_lock); + else + lockdep_assert_held(&vf->cfg_lock); + + /* Set VF disable bit state here, before triggering reset */ + set_bit(ICE_VF_STATE_DIS, vf->vf_states); + ice_trigger_vf_reset(vf, flags & ICE_VF_RESET_VFLR, false); + + vsi = ice_get_vf_vsi(vf); + + ice_dis_vf_qs(vf); + + /* Call Disable LAN Tx queue AQ whether or not queues are + * enabled. This is needed for successful completion of VFR. + */ + ice_dis_vsi_txq(vsi->port_info, vsi->idx, 0, 0, NULL, NULL, + NULL, vf->vf_ops->reset_type, vf->vf_id, NULL); + + /* poll VPGEN_VFRSTAT reg to make sure + * that reset is complete + */ + rsd = vf->vf_ops->poll_reset_status(vf); + + /* Display a warning if VF didn't manage to reset in time, but need to + * continue on with the operation. + */ + if (!rsd) + dev_warn(dev, "VF reset check timeout on VF %d\n", vf->vf_id); + + vf->driver_caps = 0; + ice_vc_set_default_allowlist(vf); + + /* disable promiscuous modes in case they were enabled + * ignore any error if disabling process failed + */ + if (test_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states) || + test_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states)) { + if (ice_vf_is_port_vlan_ena(vf) || vsi->num_vlan) + promisc_m = ICE_UCAST_VLAN_PROMISC_BITS; + else + promisc_m = ICE_UCAST_PROMISC_BITS; + + if (ice_vf_clear_vsi_promisc(vf, vsi, promisc_m)) + dev_err(dev, "disabling promiscuous mode failed\n"); + } + + ice_eswitch_del_vf_mac_rule(vf); + + ice_vf_fdir_exit(vf); + ice_vf_fdir_init(vf); + /* clean VF control VSI when resetting VF since it should be setup + * only when VF creates its first FDIR rule. + */ + if (vf->ctrl_vsi_idx != ICE_NO_VSI) + ice_vf_ctrl_vsi_release(vf); + + ice_vf_pre_vsi_rebuild(vf); + + if (vf->vf_ops->vsi_rebuild(vf)) { + dev_err(dev, "Failed to release and setup the VF%u's VSI\n", + vf->vf_id); + err = -EFAULT; + goto out_unlock; + } + + vf->vf_ops->post_vsi_rebuild(vf); + vsi = ice_get_vf_vsi(vf); + ice_eswitch_update_repr(vsi); + ice_eswitch_replay_vf_mac_rule(vf); + + /* if the VF has been reset allow it to come up again */ + if (ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->vfs.malvfs, + ICE_MAX_SRIOV_VFS, vf->vf_id)) + dev_dbg(dev, "failed to clear malicious VF state for VF %u\n", + vf->vf_id); + +out_unlock: + if (flags & ICE_VF_RESET_LOCK) + mutex_unlock(&vf->cfg_lock); + + return err; +} + +/** + * ice_set_vf_state_qs_dis - Set VF queues state to disabled + * @vf: pointer to the VF structure + */ +void ice_set_vf_state_qs_dis(struct ice_vf *vf) +{ + /* Clear Rx/Tx enabled queues flag */ + bitmap_zero(vf->txq_ena, ICE_MAX_RSS_QS_PER_VF); + bitmap_zero(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF); + clear_bit(ICE_VF_STATE_QS_ENA, vf->vf_states); +} + +/* Private functions only accessed from other virtualization files */ + +/** + * ice_dis_vf_qs - Disable the VF queues + * @vf: pointer to the VF structure + */ +void ice_dis_vf_qs(struct ice_vf *vf) +{ + struct ice_vsi *vsi = ice_get_vf_vsi(vf); + + ice_vsi_stop_lan_tx_rings(vsi, ICE_NO_RESET, vf->vf_id); + ice_vsi_stop_all_rx_rings(vsi); + ice_set_vf_state_qs_dis(vf); +} + +/** + * ice_check_vf_init - helper to check if VF init complete + * @vf: the pointer to the VF to check + */ +int ice_check_vf_init(struct ice_vf *vf) +{ + struct ice_pf *pf = vf->pf; + + if (!test_bit(ICE_VF_STATE_INIT, vf->vf_states)) { + dev_err(ice_pf_to_dev(pf), "VF ID: %u in reset. Try again.\n", + vf->vf_id); + return -EBUSY; + } + return 0; +} + +/** + * ice_vf_get_port_info - Get the VF's port info structure + * @vf: VF used to get the port info structure for + */ +struct ice_port_info *ice_vf_get_port_info(struct ice_vf *vf) +{ + return vf->pf->hw.port_info; +} + +static int ice_cfg_mac_antispoof(struct ice_vsi *vsi, bool enable) +{ + struct ice_vsi_ctx *ctx; + int err; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->info.sec_flags = vsi->info.sec_flags; + ctx->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_SECURITY_VALID); + + if (enable) + ctx->info.sec_flags |= ICE_AQ_VSI_SEC_FLAG_ENA_MAC_ANTI_SPOOF; + else + ctx->info.sec_flags &= ~ICE_AQ_VSI_SEC_FLAG_ENA_MAC_ANTI_SPOOF; + + err = ice_update_vsi(&vsi->back->hw, vsi->idx, ctx, NULL); + if (err) + dev_err(ice_pf_to_dev(vsi->back), "Failed to configure Tx MAC anti-spoof %s for VSI %d, error %d\n", + enable ? "ON" : "OFF", vsi->vsi_num, err); + else + vsi->info.sec_flags = ctx->info.sec_flags; + + kfree(ctx); + + return err; +} + +/** + * ice_vsi_ena_spoofchk - enable Tx spoof checking for this VSI + * @vsi: VSI to enable Tx spoof checking for + */ +static int ice_vsi_ena_spoofchk(struct ice_vsi *vsi) +{ + struct ice_vsi_vlan_ops *vlan_ops; + int err; + + vlan_ops = ice_get_compat_vsi_vlan_ops(vsi); + + err = vlan_ops->ena_tx_filtering(vsi); + if (err) + return err; + + return ice_cfg_mac_antispoof(vsi, true); +} + +/** + * ice_vsi_dis_spoofchk - disable Tx spoof checking for this VSI + * @vsi: VSI to disable Tx spoof checking for + */ +static int ice_vsi_dis_spoofchk(struct ice_vsi *vsi) +{ + struct ice_vsi_vlan_ops *vlan_ops; + int err; + + vlan_ops = ice_get_compat_vsi_vlan_ops(vsi); + + err = vlan_ops->dis_tx_filtering(vsi); + if (err) + return err; + + return ice_cfg_mac_antispoof(vsi, false); +} + +/** + * ice_vsi_apply_spoofchk - Apply Tx spoof checking setting to a VSI + * @vsi: VSI associated to the VF + * @enable: whether to enable or disable the spoof checking + */ +int ice_vsi_apply_spoofchk(struct ice_vsi *vsi, bool enable) +{ + int err; + + if (enable) + err = ice_vsi_ena_spoofchk(vsi); + else + err = ice_vsi_dis_spoofchk(vsi); + + return err; +} + +/** + * ice_is_vf_trusted + * @vf: pointer to the VF info + */ +bool ice_is_vf_trusted(struct ice_vf *vf) +{ + return test_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps); +} + +/** + * ice_vf_has_no_qs_ena - check if the VF has any Rx or Tx queues enabled + * @vf: the VF to check + * + * Returns true if the VF has no Rx and no Tx queues enabled and returns false + * otherwise + */ +bool ice_vf_has_no_qs_ena(struct ice_vf *vf) +{ + return (!bitmap_weight(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF) && + !bitmap_weight(vf->txq_ena, ICE_MAX_RSS_QS_PER_VF)); +} + +/** + * ice_is_vf_link_up - check if the VF's link is up + * @vf: VF to check if link is up + */ +bool ice_is_vf_link_up(struct ice_vf *vf) +{ + struct ice_port_info *pi = ice_vf_get_port_info(vf); + + if (ice_check_vf_init(vf)) + return false; + + if (ice_vf_has_no_qs_ena(vf)) + return false; + else if (vf->link_forced) + return vf->link_up; + else + return pi->phy.link_info.link_info & + ICE_AQ_LINK_UP; +} + +/** + * ice_vf_set_host_trust_cfg - set trust setting based on pre-reset value + * @vf: VF to configure trust setting for + */ +static void ice_vf_set_host_trust_cfg(struct ice_vf *vf) +{ + if (vf->trusted) + set_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps); + else + clear_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps); +} + +/** + * ice_vf_rebuild_host_mac_cfg - add broadcast and the VF's perm_addr/LAA + * @vf: VF to add MAC filters for + * + * Called after a VF VSI has been re-added/rebuilt during reset. The PF driver + * always re-adds a broadcast filter and the VF's perm_addr/LAA after reset. + */ +static int ice_vf_rebuild_host_mac_cfg(struct ice_vf *vf) +{ + struct device *dev = ice_pf_to_dev(vf->pf); + struct ice_vsi *vsi = ice_get_vf_vsi(vf); + u8 broadcast[ETH_ALEN]; + int status; + + if (ice_is_eswitch_mode_switchdev(vf->pf)) + return 0; + + eth_broadcast_addr(broadcast); + status = ice_fltr_add_mac(vsi, broadcast, ICE_FWD_TO_VSI); + if (status) { + dev_err(dev, "failed to add broadcast MAC filter for VF %u, error %d\n", + vf->vf_id, status); + return status; + } + + vf->num_mac++; + + if (is_valid_ether_addr(vf->hw_lan_addr.addr)) { + status = ice_fltr_add_mac(vsi, vf->hw_lan_addr.addr, + ICE_FWD_TO_VSI); + if (status) { + dev_err(dev, "failed to add default unicast MAC filter %pM for VF %u, error %d\n", + &vf->hw_lan_addr.addr[0], vf->vf_id, + status); + return status; + } + vf->num_mac++; + + ether_addr_copy(vf->dev_lan_addr.addr, vf->hw_lan_addr.addr); + } + + return 0; +} + +/** + * ice_vf_rebuild_host_vlan_cfg - add VLAN 0 filter or rebuild the Port VLAN + * @vf: VF to add MAC filters for + * @vsi: Pointer to VSI + * + * Called after a VF VSI has been re-added/rebuilt during reset. The PF driver + * always re-adds either a VLAN 0 or port VLAN based filter after reset. + */ +static int ice_vf_rebuild_host_vlan_cfg(struct ice_vf *vf, struct ice_vsi *vsi) +{ + struct ice_vsi_vlan_ops *vlan_ops = ice_get_compat_vsi_vlan_ops(vsi); + struct device *dev = ice_pf_to_dev(vf->pf); + int err; + + if (ice_vf_is_port_vlan_ena(vf)) { + err = vlan_ops->set_port_vlan(vsi, &vf->port_vlan_info); + if (err) { + dev_err(dev, "failed to configure port VLAN via VSI parameters for VF %u, error %d\n", + vf->vf_id, err); + return err; + } + + err = vlan_ops->add_vlan(vsi, &vf->port_vlan_info); + } else { + err = ice_vsi_add_vlan_zero(vsi); + } + + if (err) { + dev_err(dev, "failed to add VLAN %u filter for VF %u during VF rebuild, error %d\n", + ice_vf_is_port_vlan_ena(vf) ? + ice_vf_get_port_vlan_id(vf) : 0, vf->vf_id, err); + return err; + } + + err = vlan_ops->ena_rx_filtering(vsi); + if (err) + dev_warn(dev, "failed to enable Rx VLAN filtering for VF %d VSI %d during VF rebuild, error %d\n", + vf->vf_id, vsi->idx, err); + + return 0; +} + +/** + * ice_vf_rebuild_host_tx_rate_cfg - re-apply the Tx rate limiting configuration + * @vf: VF to re-apply the configuration for + * + * Called after a VF VSI has been re-added/rebuild during reset. The PF driver + * needs to re-apply the host configured Tx rate limiting configuration. + */ +static int ice_vf_rebuild_host_tx_rate_cfg(struct ice_vf *vf) +{ + struct device *dev = ice_pf_to_dev(vf->pf); + struct ice_vsi *vsi = ice_get_vf_vsi(vf); + int err; + + if (vf->min_tx_rate) { + err = ice_set_min_bw_limit(vsi, (u64)vf->min_tx_rate * 1000); + if (err) { + dev_err(dev, "failed to set min Tx rate to %d Mbps for VF %u, error %d\n", + vf->min_tx_rate, vf->vf_id, err); + return err; + } + } + + if (vf->max_tx_rate) { + err = ice_set_max_bw_limit(vsi, (u64)vf->max_tx_rate * 1000); + if (err) { + dev_err(dev, "failed to set max Tx rate to %d Mbps for VF %u, error %d\n", + vf->max_tx_rate, vf->vf_id, err); + return err; + } + } + + return 0; +} + +/** + * ice_vf_rebuild_aggregator_node_cfg - rebuild aggregator node config + * @vsi: Pointer to VSI + * + * This function moves VSI into corresponding scheduler aggregator node + * based on cached value of "aggregator node info" per VSI + */ +static void ice_vf_rebuild_aggregator_node_cfg(struct ice_vsi *vsi) +{ + struct ice_pf *pf = vsi->back; + struct device *dev; + int status; + + if (!vsi->agg_node) + return; + + dev = ice_pf_to_dev(pf); + if (vsi->agg_node->num_vsis == ICE_MAX_VSIS_IN_AGG_NODE) { + dev_dbg(dev, + "agg_id %u already has reached max_num_vsis %u\n", + vsi->agg_node->agg_id, vsi->agg_node->num_vsis); + return; + } + + status = ice_move_vsi_to_agg(pf->hw.port_info, vsi->agg_node->agg_id, + vsi->idx, vsi->tc_cfg.ena_tc); + if (status) + dev_dbg(dev, "unable to move VSI idx %u into aggregator %u node", + vsi->idx, vsi->agg_node->agg_id); + else + vsi->agg_node->num_vsis++; +} + +/** + * ice_vf_rebuild_host_cfg - host admin configuration is persistent across reset + * @vf: VF to rebuild host configuration on + */ +void ice_vf_rebuild_host_cfg(struct ice_vf *vf) +{ + struct device *dev = ice_pf_to_dev(vf->pf); + struct ice_vsi *vsi = ice_get_vf_vsi(vf); + + ice_vf_set_host_trust_cfg(vf); + + if (ice_vf_rebuild_host_mac_cfg(vf)) + dev_err(dev, "failed to rebuild default MAC configuration for VF %d\n", + vf->vf_id); + + if (ice_vf_rebuild_host_vlan_cfg(vf, vsi)) + dev_err(dev, "failed to rebuild VLAN configuration for VF %u\n", + vf->vf_id); + + if (ice_vf_rebuild_host_tx_rate_cfg(vf)) + dev_err(dev, "failed to rebuild Tx rate limiting configuration for VF %u\n", + vf->vf_id); + + if (ice_vsi_apply_spoofchk(vsi, vf->spoofchk)) + dev_err(dev, "failed to rebuild spoofchk configuration for VF %d\n", + vf->vf_id); + + /* rebuild aggregator node config for main VF VSI */ + ice_vf_rebuild_aggregator_node_cfg(vsi); +} + +/** + * ice_vf_ctrl_invalidate_vsi - invalidate ctrl_vsi_idx to remove VSI access + * @vf: VF that control VSI is being invalidated on + */ +void ice_vf_ctrl_invalidate_vsi(struct ice_vf *vf) +{ + vf->ctrl_vsi_idx = ICE_NO_VSI; +} + +/** + * ice_vf_ctrl_vsi_release - invalidate the VF's control VSI after freeing it + * @vf: VF that control VSI is being released on + */ +void ice_vf_ctrl_vsi_release(struct ice_vf *vf) +{ + ice_vsi_release(vf->pf->vsi[vf->ctrl_vsi_idx]); + ice_vf_ctrl_invalidate_vsi(vf); +} + +/** + * ice_vf_ctrl_vsi_setup - Set up a VF control VSI + * @vf: VF to setup control VSI for + * + * Returns pointer to the successfully allocated VSI struct on success, + * otherwise returns NULL on failure. + */ +struct ice_vsi *ice_vf_ctrl_vsi_setup(struct ice_vf *vf) +{ + struct ice_port_info *pi = ice_vf_get_port_info(vf); + struct ice_pf *pf = vf->pf; + struct ice_vsi *vsi; + + vsi = ice_vsi_setup(pf, pi, ICE_VSI_CTRL, vf, NULL); + if (!vsi) { + dev_err(ice_pf_to_dev(pf), "Failed to create VF control VSI\n"); + ice_vf_ctrl_invalidate_vsi(vf); + } + + return vsi; +} + +/** + * ice_vf_invalidate_vsi - invalidate vsi_idx/vsi_num to remove VSI access + * @vf: VF to remove access to VSI for + */ +void ice_vf_invalidate_vsi(struct ice_vf *vf) +{ + vf->lan_vsi_idx = ICE_NO_VSI; + vf->lan_vsi_num = ICE_NO_VSI; +} + +/** + * ice_vf_set_initialized - VF is ready for VIRTCHNL communication + * @vf: VF to set in initialized state + * + * After this function the VF will be ready to receive/handle the + * VIRTCHNL_OP_GET_VF_RESOURCES message + */ +void ice_vf_set_initialized(struct ice_vf *vf) +{ + ice_set_vf_state_qs_dis(vf); + clear_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states); + clear_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states); + clear_bit(ICE_VF_STATE_DIS, vf->vf_states); + set_bit(ICE_VF_STATE_INIT, vf->vf_states); + memset(&vf->vlan_v2_caps, 0, sizeof(vf->vlan_v2_caps)); +} diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.h b/drivers/net/ethernet/intel/ice/ice_vf_lib.h new file mode 100644 index 000000000000..831b667dc5b2 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.h @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018-2021, Intel Corporation. */ + +#ifndef _ICE_VF_LIB_H_ +#define _ICE_VF_LIB_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "ice_type.h" +#include "ice_virtchnl_fdir.h" +#include "ice_vsi_vlan_ops.h" + +#define ICE_MAX_SRIOV_VFS 256 + +/* VF resource constraints */ +#define ICE_MAX_RSS_QS_PER_VF 16 + +struct ice_pf; +struct ice_vf; +struct ice_virtchnl_ops; + +/* VF capabilities */ +enum ice_virtchnl_cap { + ICE_VIRTCHNL_VF_CAP_PRIVILEGE = 0, +}; + +/* Specific VF states */ +enum ice_vf_states { + ICE_VF_STATE_INIT = 0, /* PF is initializing VF */ + ICE_VF_STATE_ACTIVE, /* VF resources are allocated for use */ + ICE_VF_STATE_QS_ENA, /* VF queue(s) enabled */ + ICE_VF_STATE_DIS, + ICE_VF_STATE_MC_PROMISC, + ICE_VF_STATE_UC_PROMISC, + ICE_VF_STATES_NBITS +}; + +struct ice_time_mac { + unsigned long time_modified; + u8 addr[ETH_ALEN]; +}; + +/* VF MDD events print structure */ +struct ice_mdd_vf_events { + u16 count; /* total count of Rx|Tx events */ + /* count number of the last printed event */ + u16 last_printed; +}; + +/* VF operations */ +struct ice_vf_ops { + enum ice_disq_rst_src reset_type; + void (*free)(struct ice_vf *vf); + void (*clear_mbx_register)(struct ice_vf *vf); + void (*trigger_reset_register)(struct ice_vf *vf, bool is_vflr); + bool (*poll_reset_status)(struct ice_vf *vf); + void (*clear_reset_trigger)(struct ice_vf *vf); + int (*vsi_rebuild)(struct ice_vf *vf); + void (*post_vsi_rebuild)(struct ice_vf *vf); +}; + +/* Virtchnl/SR-IOV config info */ +struct ice_vfs { + DECLARE_HASHTABLE(table, 8); /* table of VF entries */ + struct mutex table_lock; /* Lock for protecting the hash table */ + u16 num_supported; /* max supported VFs on this PF */ + u16 num_qps_per; /* number of queue pairs per VF */ + u16 num_msix_per; /* number of MSI-X vectors per VF */ + unsigned long last_printed_mdd_jiffies; /* MDD message rate limit */ + DECLARE_BITMAP(malvfs, ICE_MAX_SRIOV_VFS); /* malicious VF indicator */ +}; + +/* VF information structure */ +struct ice_vf { + struct hlist_node entry; + struct rcu_head rcu; + struct kref refcnt; + struct ice_pf *pf; + + /* Used during virtchnl message handling and NDO ops against the VF + * that will trigger a VFR + */ + struct mutex cfg_lock; + + u16 vf_id; /* VF ID in the PF space */ + u16 lan_vsi_idx; /* index into PF struct */ + u16 ctrl_vsi_idx; + struct ice_vf_fdir fdir; + /* first vector index of this VF in the PF space */ + int first_vector_idx; + struct ice_sw *vf_sw_id; /* switch ID the VF VSIs connect to */ + struct virtchnl_version_info vf_ver; + u32 driver_caps; /* reported by VF driver */ + struct virtchnl_ether_addr dev_lan_addr; + struct virtchnl_ether_addr hw_lan_addr; + struct ice_time_mac legacy_last_added_umac; + DECLARE_BITMAP(txq_ena, ICE_MAX_RSS_QS_PER_VF); + DECLARE_BITMAP(rxq_ena, ICE_MAX_RSS_QS_PER_VF); + struct ice_vlan port_vlan_info; /* Port VLAN ID, QoS, and TPID */ + struct virtchnl_vlan_caps vlan_v2_caps; + u8 pf_set_mac:1; /* VF MAC address set by VMM admin */ + u8 trusted:1; + u8 spoofchk:1; + u8 link_forced:1; + u8 link_up:1; /* only valid if VF link is forced */ + /* VSI indices - actual VSI pointers are maintained in the PF structure + * When assigned, these will be non-zero, because VSI 0 is always + * the main LAN VSI for the PF. + */ + u16 lan_vsi_num; /* ID as used by firmware */ + unsigned int min_tx_rate; /* Minimum Tx bandwidth limit in Mbps */ + unsigned int max_tx_rate; /* Maximum Tx bandwidth limit in Mbps */ + DECLARE_BITMAP(vf_states, ICE_VF_STATES_NBITS); /* VF runtime states */ + + unsigned long vf_caps; /* VF's adv. capabilities */ + u8 num_req_qs; /* num of queue pairs requested by VF */ + u16 num_mac; + u16 num_vf_qs; /* num of queue configured per VF */ + struct ice_mdd_vf_events mdd_rx_events; + struct ice_mdd_vf_events mdd_tx_events; + DECLARE_BITMAP(opcodes_allowlist, VIRTCHNL_OP_MAX); + + struct ice_repr *repr; + const struct ice_virtchnl_ops *virtchnl_ops; + const struct ice_vf_ops *vf_ops; + + /* devlink port data */ + struct devlink_port devlink_port; +}; + +/* Flags for controlling behavior of ice_reset_vf */ +enum ice_vf_reset_flags { + ICE_VF_RESET_VFLR = BIT(0), /* Indicate a VFLR reset */ + ICE_VF_RESET_NOTIFY = BIT(1), /* Notify VF prior to reset */ + ICE_VF_RESET_LOCK = BIT(2), /* Acquire the VF cfg_lock */ +}; + +static inline u16 ice_vf_get_port_vlan_id(struct ice_vf *vf) +{ + return vf->port_vlan_info.vid; +} + +static inline u8 ice_vf_get_port_vlan_prio(struct ice_vf *vf) +{ + return vf->port_vlan_info.prio; +} + +static inline bool ice_vf_is_port_vlan_ena(struct ice_vf *vf) +{ + return (ice_vf_get_port_vlan_id(vf) || ice_vf_get_port_vlan_prio(vf)); +} + +static inline u16 ice_vf_get_port_vlan_tpid(struct ice_vf *vf) +{ + return vf->port_vlan_info.tpid; +} + +/* VF Hash Table access functions + * + * These functions provide abstraction for interacting with the VF hash table. + * In general, direct access to the hash table should be avoided outside of + * these functions where possible. + * + * The VF entries in the hash table are protected by reference counting to + * track lifetime of accesses from the table. The ice_get_vf_by_id() function + * obtains a reference to the VF structure which must be dropped by using + * ice_put_vf(). + */ + +/** + * ice_for_each_vf - Iterate over each VF entry + * @pf: pointer to the PF private structure + * @bkt: bucket index used for iteration + * @vf: pointer to the VF entry currently being processed in the loop. + * + * The bkt variable is an unsigned integer iterator used to traverse the VF + * entries. It is *not* guaranteed to be the VF's vf_id. Do not assume it is. + * Use vf->vf_id to get the id number if needed. + * + * The caller is expected to be under the table_lock mutex for the entire + * loop. Use this iterator if your loop is long or if it might sleep. + */ +#define ice_for_each_vf(pf, bkt, vf) \ + hash_for_each((pf)->vfs.table, (bkt), (vf), entry) + +/** + * ice_for_each_vf_rcu - Iterate over each VF entry protected by RCU + * @pf: pointer to the PF private structure + * @bkt: bucket index used for iteration + * @vf: pointer to the VF entry currently being processed in the loop. + * + * The bkt variable is an unsigned integer iterator used to traverse the VF + * entries. It is *not* guaranteed to be the VF's vf_id. Do not assume it is. + * Use vf->vf_id to get the id number if needed. + * + * The caller is expected to be under rcu_read_lock() for the entire loop. + * Only use this iterator if your loop is short and you can guarantee it does + * not sleep. + */ +#define ice_for_each_vf_rcu(pf, bkt, vf) \ + hash_for_each_rcu((pf)->vfs.table, (bkt), (vf), entry) + +#ifdef CONFIG_PCI_IOV +struct ice_vf *ice_get_vf_by_id(struct ice_pf *pf, u16 vf_id); +void ice_put_vf(struct ice_vf *vf); +bool ice_has_vfs(struct ice_pf *pf); +u16 ice_get_num_vfs(struct ice_pf *pf); +struct ice_vsi *ice_get_vf_vsi(struct ice_vf *vf); +bool ice_is_vf_disabled(struct ice_vf *vf); +int ice_check_vf_ready_for_cfg(struct ice_vf *vf); +void ice_set_vf_state_qs_dis(struct ice_vf *vf); +bool ice_is_any_vf_in_promisc(struct ice_pf *pf); +int +ice_vf_set_vsi_promisc(struct ice_vf *vf, struct ice_vsi *vsi, u8 promisc_m); +int +ice_vf_clear_vsi_promisc(struct ice_vf *vf, struct ice_vsi *vsi, u8 promisc_m); +int ice_reset_vf(struct ice_vf *vf, u32 flags); +void ice_reset_all_vfs(struct ice_pf *pf); +#else /* CONFIG_PCI_IOV */ +static inline struct ice_vf *ice_get_vf_by_id(struct ice_pf *pf, u16 vf_id) +{ + return NULL; +} + +static inline void ice_put_vf(struct ice_vf *vf) +{ +} + +static inline bool ice_has_vfs(struct ice_pf *pf) +{ + return false; +} + +static inline u16 ice_get_num_vfs(struct ice_pf *pf) +{ + return 0; +} + +static inline struct ice_vsi *ice_get_vf_vsi(struct ice_vf *vf) +{ + return NULL; +} + +static inline bool ice_is_vf_disabled(struct ice_vf *vf) +{ + return true; +} + +static inline int ice_check_vf_ready_for_cfg(struct ice_vf *vf) +{ + return -EOPNOTSUPP; +} + +static inline void ice_set_vf_state_qs_dis(struct ice_vf *vf) +{ +} + +static inline bool ice_is_any_vf_in_promisc(struct ice_pf *pf) +{ + return false; +} + +static inline int +ice_vf_set_vsi_promisc(struct ice_vf *vf, struct ice_vsi *vsi, u8 promisc_m) +{ + return -EOPNOTSUPP; +} + +static inline int +ice_vf_clear_vsi_promisc(struct ice_vf *vf, struct ice_vsi *vsi, u8 promisc_m) +{ + return -EOPNOTSUPP; +} + +static inline int ice_reset_vf(struct ice_vf *vf, u32 flags) +{ + return 0; +} + +static inline void ice_reset_all_vfs(struct ice_pf *pf) +{ +} +#endif /* !CONFIG_PCI_IOV */ + +#endif /* _ICE_VF_LIB_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h b/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h new file mode 100644 index 000000000000..15887e772c76 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018-2021, Intel Corporation. */ + +#ifndef _ICE_VF_LIB_PRIVATE_H_ +#define _ICE_VF_LIB_PRIVATE_H_ + +#include "ice_vf_lib.h" + +/* This header file is for exposing functions in ice_vf_lib.c to other files + * which are also conditionally compiled depending on CONFIG_PCI_IOV. + * Functions which may be used by other files should be exposed as part of + * ice_vf_lib.h + * + * Functions in this file are exposed only when CONFIG_PCI_IOV is enabled, and + * thus this header must not be included by .c files which may be compiled + * with CONFIG_PCI_IOV disabled. + * + * To avoid this, only include this header file directly within .c files that + * are conditionally enabled in the "ice-$(CONFIG_PCI_IOV)" block. + */ + +#ifndef CONFIG_PCI_IOV +#warning "Only include ice_vf_lib_private.h in CONFIG_PCI_IOV virtualization files" +#endif + +void ice_dis_vf_qs(struct ice_vf *vf); +int ice_check_vf_init(struct ice_vf *vf); +struct ice_port_info *ice_vf_get_port_info(struct ice_vf *vf); +int ice_vsi_apply_spoofchk(struct ice_vsi *vsi, bool enable); +bool ice_is_vf_trusted(struct ice_vf *vf); +bool ice_vf_has_no_qs_ena(struct ice_vf *vf); +bool ice_is_vf_link_up(struct ice_vf *vf); +void ice_vf_rebuild_host_cfg(struct ice_vf *vf); +void ice_vf_ctrl_invalidate_vsi(struct ice_vf *vf); +void ice_vf_ctrl_vsi_release(struct ice_vf *vf); +struct ice_vsi *ice_vf_ctrl_vsi_setup(struct ice_vf *vf); +void ice_vf_invalidate_vsi(struct ice_vf *vf); +void ice_vf_set_initialized(struct ice_vf *vf); + +#endif /* _ICE_VF_LIB_PRIVATE_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c new file mode 100644 index 000000000000..fc8c93fa4455 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2018, Intel Corporation. */ + +#include "ice_common.h" +#include "ice_vf_mbx.h" + +/** + * ice_aq_send_msg_to_vf + * @hw: pointer to the hardware structure + * @vfid: VF ID to send msg + * @v_opcode: opcodes for VF-PF communication + * @v_retval: return error code + * @msg: pointer to the msg buffer + * @msglen: msg length + * @cd: pointer to command details + * + * Send message to VF driver (0x0802) using mailbox + * queue and asynchronously sending message via + * ice_sq_send_cmd() function + */ +int +ice_aq_send_msg_to_vf(struct ice_hw *hw, u16 vfid, u32 v_opcode, u32 v_retval, + u8 *msg, u16 msglen, struct ice_sq_cd *cd) +{ + struct ice_aqc_pf_vf_msg *cmd; + struct ice_aq_desc desc; + + ice_fill_dflt_direct_cmd_desc(&desc, ice_mbx_opc_send_msg_to_vf); + + cmd = &desc.params.virt; + cmd->id = cpu_to_le32(vfid); + + desc.cookie_high = cpu_to_le32(v_opcode); + desc.cookie_low = cpu_to_le32(v_retval); + + if (msglen) + desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD); + + return ice_sq_send_cmd(hw, &hw->mailboxq, &desc, msg, msglen, cd); +} + +/** + * ice_conv_link_speed_to_virtchnl + * @adv_link_support: determines the format of the returned link speed + * @link_speed: variable containing the link_speed to be converted + * + * Convert link speed supported by HW to link speed supported by virtchnl. + * If adv_link_support is true, then return link speed in Mbps. Else return + * link speed as a VIRTCHNL_LINK_SPEED_* casted to a u32. Note that the caller + * needs to cast back to an enum virtchnl_link_speed in the case where + * adv_link_support is false, but when adv_link_support is true the caller can + * expect the speed in Mbps. + */ +u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed) +{ + u32 speed; + + if (adv_link_support) + switch (link_speed) { + case ICE_AQ_LINK_SPEED_10MB: + speed = ICE_LINK_SPEED_10MBPS; + break; + case ICE_AQ_LINK_SPEED_100MB: + speed = ICE_LINK_SPEED_100MBPS; + break; + case ICE_AQ_LINK_SPEED_1000MB: + speed = ICE_LINK_SPEED_1000MBPS; + break; + case ICE_AQ_LINK_SPEED_2500MB: + speed = ICE_LINK_SPEED_2500MBPS; + break; + case ICE_AQ_LINK_SPEED_5GB: + speed = ICE_LINK_SPEED_5000MBPS; + break; + case ICE_AQ_LINK_SPEED_10GB: + speed = ICE_LINK_SPEED_10000MBPS; + break; + case ICE_AQ_LINK_SPEED_20GB: + speed = ICE_LINK_SPEED_20000MBPS; + break; + case ICE_AQ_LINK_SPEED_25GB: + speed = ICE_LINK_SPEED_25000MBPS; + break; + case ICE_AQ_LINK_SPEED_40GB: + speed = ICE_LINK_SPEED_40000MBPS; + break; + case ICE_AQ_LINK_SPEED_50GB: + speed = ICE_LINK_SPEED_50000MBPS; + break; + case ICE_AQ_LINK_SPEED_100GB: + speed = ICE_LINK_SPEED_100000MBPS; + break; + default: + speed = ICE_LINK_SPEED_UNKNOWN; + break; + } + else + /* Virtchnl speeds are not defined for every speed supported in + * the hardware. To maintain compatibility with older AVF + * drivers, while reporting the speed the new speed values are + * resolved to the closest known virtchnl speeds + */ + switch (link_speed) { + case ICE_AQ_LINK_SPEED_10MB: + case ICE_AQ_LINK_SPEED_100MB: + speed = (u32)VIRTCHNL_LINK_SPEED_100MB; + break; + case ICE_AQ_LINK_SPEED_1000MB: + case ICE_AQ_LINK_SPEED_2500MB: + case ICE_AQ_LINK_SPEED_5GB: + speed = (u32)VIRTCHNL_LINK_SPEED_1GB; + break; + case ICE_AQ_LINK_SPEED_10GB: + speed = (u32)VIRTCHNL_LINK_SPEED_10GB; + break; + case ICE_AQ_LINK_SPEED_20GB: + speed = (u32)VIRTCHNL_LINK_SPEED_20GB; + break; + case ICE_AQ_LINK_SPEED_25GB: + speed = (u32)VIRTCHNL_LINK_SPEED_25GB; + break; + case ICE_AQ_LINK_SPEED_40GB: + case ICE_AQ_LINK_SPEED_50GB: + case ICE_AQ_LINK_SPEED_100GB: + speed = (u32)VIRTCHNL_LINK_SPEED_40GB; + break; + default: + speed = (u32)VIRTCHNL_LINK_SPEED_UNKNOWN; + break; + } + + return speed; +} + +/* The mailbox overflow detection algorithm helps to check if there + * is a possibility of a malicious VF transmitting too many MBX messages to the + * PF. + * 1. The mailbox snapshot structure, ice_mbx_snapshot, is initialized during + * driver initialization in ice_init_hw() using ice_mbx_init_snapshot(). + * The struct ice_mbx_snapshot helps to track and traverse a static window of + * messages within the mailbox queue while looking for a malicious VF. + * + * 2. When the caller starts processing its mailbox queue in response to an + * interrupt, the structure ice_mbx_snapshot is expected to be cleared before + * the algorithm can be run for the first time for that interrupt. This can be + * done via ice_mbx_reset_snapshot(). + * + * 3. For every message read by the caller from the MBX Queue, the caller must + * call the detection algorithm's entry function ice_mbx_vf_state_handler(). + * Before every call to ice_mbx_vf_state_handler() the struct ice_mbx_data is + * filled as it is required to be passed to the algorithm. + * + * 4. Every time a message is read from the MBX queue, a VFId is received which + * is passed to the state handler. The boolean output is_malvf of the state + * handler ice_mbx_vf_state_handler() serves as an indicator to the caller + * whether this VF is malicious or not. + * + * 5. When a VF is identified to be malicious, the caller can send a message + * to the system administrator. The caller can invoke ice_mbx_report_malvf() + * to help determine if a malicious VF is to be reported or not. This function + * requires the caller to maintain a global bitmap to track all malicious VFs + * and pass that to ice_mbx_report_malvf() along with the VFID which was identified + * to be malicious by ice_mbx_vf_state_handler(). + * + * 6. The global bitmap maintained by PF can be cleared completely if PF is in + * reset or the bit corresponding to a VF can be cleared if that VF is in reset. + * When a VF is shut down and brought back up, we assume that the new VF + * brought up is not malicious and hence report it if found malicious. + * + * 7. The function ice_mbx_reset_snapshot() is called to reset the information + * in ice_mbx_snapshot for every new mailbox interrupt handled. + * + * 8. The memory allocated for variables in ice_mbx_snapshot is de-allocated + * when driver is unloaded. + */ +#define ICE_RQ_DATA_MASK(rq_data) ((rq_data) & PF_MBX_ARQH_ARQH_M) +/* Using the highest value for an unsigned 16-bit value 0xFFFF to indicate that + * the max messages check must be ignored in the algorithm + */ +#define ICE_IGNORE_MAX_MSG_CNT 0xFFFF + +/** + * ice_mbx_traverse - Pass through mailbox snapshot + * @hw: pointer to the HW struct + * @new_state: new algorithm state + * + * Traversing the mailbox static snapshot without checking + * for malicious VFs. + */ +static void +ice_mbx_traverse(struct ice_hw *hw, + enum ice_mbx_snapshot_state *new_state) +{ + struct ice_mbx_snap_buffer_data *snap_buf; + u32 num_iterations; + + snap_buf = &hw->mbx_snapshot.mbx_buf; + + /* As mailbox buffer is circular, applying a mask + * on the incremented iteration count. + */ + num_iterations = ICE_RQ_DATA_MASK(++snap_buf->num_iterations); + + /* Checking either of the below conditions to exit snapshot traversal: + * Condition-1: If the number of iterations in the mailbox is equal to + * the mailbox head which would indicate that we have reached the end + * of the static snapshot. + * Condition-2: If the maximum messages serviced in the mailbox for a + * given interrupt is the highest possible value then there is no need + * to check if the number of messages processed is equal to it. If not + * check if the number of messages processed is greater than or equal + * to the maximum number of mailbox entries serviced in current work item. + */ + if (num_iterations == snap_buf->head || + (snap_buf->max_num_msgs_mbx < ICE_IGNORE_MAX_MSG_CNT && + ++snap_buf->num_msg_proc >= snap_buf->max_num_msgs_mbx)) + *new_state = ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT; +} + +/** + * ice_mbx_detect_malvf - Detect malicious VF in snapshot + * @hw: pointer to the HW struct + * @vf_id: relative virtual function ID + * @new_state: new algorithm state + * @is_malvf: boolean output to indicate if VF is malicious + * + * This function tracks the number of asynchronous messages + * sent per VF and marks the VF as malicious if it exceeds + * the permissible number of messages to send. + */ +static int +ice_mbx_detect_malvf(struct ice_hw *hw, u16 vf_id, + enum ice_mbx_snapshot_state *new_state, + bool *is_malvf) +{ + struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; + + if (vf_id >= snap->mbx_vf.vfcntr_len) + return -EIO; + + /* increment the message count in the VF array */ + snap->mbx_vf.vf_cntr[vf_id]++; + + if (snap->mbx_vf.vf_cntr[vf_id] >= ICE_ASYNC_VF_MSG_THRESHOLD) + *is_malvf = true; + + /* continue to iterate through the mailbox snapshot */ + ice_mbx_traverse(hw, new_state); + + return 0; +} + +/** + * ice_mbx_reset_snapshot - Reset mailbox snapshot structure + * @snap: pointer to mailbox snapshot structure in the ice_hw struct + * + * Reset the mailbox snapshot structure and clear VF counter array. + */ +static void ice_mbx_reset_snapshot(struct ice_mbx_snapshot *snap) +{ + u32 vfcntr_len; + + if (!snap || !snap->mbx_vf.vf_cntr) + return; + + /* Clear VF counters. */ + vfcntr_len = snap->mbx_vf.vfcntr_len; + if (vfcntr_len) + memset(snap->mbx_vf.vf_cntr, 0, + (vfcntr_len * sizeof(*snap->mbx_vf.vf_cntr))); + + /* Reset mailbox snapshot for a new capture. */ + memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf)); + snap->mbx_buf.state = ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT; +} + +/** + * ice_mbx_vf_state_handler - Handle states of the overflow algorithm + * @hw: pointer to the HW struct + * @mbx_data: pointer to structure containing mailbox data + * @vf_id: relative virtual function (VF) ID + * @is_malvf: boolean output to indicate if VF is malicious + * + * The function serves as an entry point for the malicious VF + * detection algorithm by handling the different states and state + * transitions of the algorithm: + * New snapshot: This state is entered when creating a new static + * snapshot. The data from any previous mailbox snapshot is + * cleared and a new capture of the mailbox head and tail is + * logged. This will be the new static snapshot to detect + * asynchronous messages sent by VFs. On capturing the snapshot + * and depending on whether the number of pending messages in that + * snapshot exceed the watermark value, the state machine enters + * traverse or detect states. + * Traverse: If pending message count is below watermark then iterate + * through the snapshot without any action on VF. + * Detect: If pending message count exceeds watermark traverse + * the static snapshot and look for a malicious VF. + */ +int +ice_mbx_vf_state_handler(struct ice_hw *hw, + struct ice_mbx_data *mbx_data, u16 vf_id, + bool *is_malvf) +{ + struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; + struct ice_mbx_snap_buffer_data *snap_buf; + struct ice_ctl_q_info *cq = &hw->mailboxq; + enum ice_mbx_snapshot_state new_state; + int status = 0; + + if (!is_malvf || !mbx_data) + return -EINVAL; + + /* When entering the mailbox state machine assume that the VF + * is not malicious until detected. + */ + *is_malvf = false; + + /* Checking if max messages allowed to be processed while servicing current + * interrupt is not less than the defined AVF message threshold. + */ + if (mbx_data->max_num_msgs_mbx <= ICE_ASYNC_VF_MSG_THRESHOLD) + return -EINVAL; + + /* The watermark value should not be lesser than the threshold limit + * set for the number of asynchronous messages a VF can send to mailbox + * nor should it be greater than the maximum number of messages in the + * mailbox serviced in current interrupt. + */ + if (mbx_data->async_watermark_val < ICE_ASYNC_VF_MSG_THRESHOLD || + mbx_data->async_watermark_val > mbx_data->max_num_msgs_mbx) + return -EINVAL; + + new_state = ICE_MAL_VF_DETECT_STATE_INVALID; + snap_buf = &snap->mbx_buf; + + switch (snap_buf->state) { + case ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT: + /* Clear any previously held data in mailbox snapshot structure. */ + ice_mbx_reset_snapshot(snap); + + /* Collect the pending ARQ count, number of messages processed and + * the maximum number of messages allowed to be processed from the + * Mailbox for current interrupt. + */ + snap_buf->num_pending_arq = mbx_data->num_pending_arq; + snap_buf->num_msg_proc = mbx_data->num_msg_proc; + snap_buf->max_num_msgs_mbx = mbx_data->max_num_msgs_mbx; + + /* Capture a new static snapshot of the mailbox by logging the + * head and tail of snapshot and set num_iterations to the tail + * value to mark the start of the iteration through the snapshot. + */ + snap_buf->head = ICE_RQ_DATA_MASK(cq->rq.next_to_clean + + mbx_data->num_pending_arq); + snap_buf->tail = ICE_RQ_DATA_MASK(cq->rq.next_to_clean - 1); + snap_buf->num_iterations = snap_buf->tail; + + /* Pending ARQ messages returned by ice_clean_rq_elem + * is the difference between the head and tail of the + * mailbox queue. Comparing this value against the watermark + * helps to check if we potentially have malicious VFs. + */ + if (snap_buf->num_pending_arq >= + mbx_data->async_watermark_val) { + new_state = ICE_MAL_VF_DETECT_STATE_DETECT; + status = ice_mbx_detect_malvf(hw, vf_id, &new_state, is_malvf); + } else { + new_state = ICE_MAL_VF_DETECT_STATE_TRAVERSE; + ice_mbx_traverse(hw, &new_state); + } + break; + + case ICE_MAL_VF_DETECT_STATE_TRAVERSE: + new_state = ICE_MAL_VF_DETECT_STATE_TRAVERSE; + ice_mbx_traverse(hw, &new_state); + break; + + case ICE_MAL_VF_DETECT_STATE_DETECT: + new_state = ICE_MAL_VF_DETECT_STATE_DETECT; + status = ice_mbx_detect_malvf(hw, vf_id, &new_state, is_malvf); + break; + + default: + new_state = ICE_MAL_VF_DETECT_STATE_INVALID; + status = -EIO; + } + + snap_buf->state = new_state; + + return status; +} + +/** + * ice_mbx_report_malvf - Track and note malicious VF + * @hw: pointer to the HW struct + * @all_malvfs: all malicious VFs tracked by PF + * @bitmap_len: length of bitmap in bits + * @vf_id: relative virtual function ID of the malicious VF + * @report_malvf: boolean to indicate if malicious VF must be reported + * + * This function will update a bitmap that keeps track of the malicious + * VFs attached to the PF. A malicious VF must be reported only once if + * discovered between VF resets or loading so the function checks + * the input vf_id against the bitmap to verify if the VF has been + * detected in any previous mailbox iterations. + */ +int +ice_mbx_report_malvf(struct ice_hw *hw, unsigned long *all_malvfs, + u16 bitmap_len, u16 vf_id, bool *report_malvf) +{ + if (!all_malvfs || !report_malvf) + return -EINVAL; + + *report_malvf = false; + + if (bitmap_len < hw->mbx_snapshot.mbx_vf.vfcntr_len) + return -EINVAL; + + if (vf_id >= bitmap_len) + return -EIO; + + /* If the vf_id is found in the bitmap set bit and boolean to true */ + if (!test_and_set_bit(vf_id, all_malvfs)) + *report_malvf = true; + + return 0; +} + +/** + * ice_mbx_clear_malvf - Clear VF bitmap and counter for VF ID + * @snap: pointer to the mailbox snapshot structure + * @all_malvfs: all malicious VFs tracked by PF + * @bitmap_len: length of bitmap in bits + * @vf_id: relative virtual function ID of the malicious VF + * + * In case of a VF reset, this function can be called to clear + * the bit corresponding to the VF ID in the bitmap tracking all + * malicious VFs attached to the PF. The function also clears the + * VF counter array at the index of the VF ID. This is to ensure + * that the new VF loaded is not considered malicious before going + * through the overflow detection algorithm. + */ +int +ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, unsigned long *all_malvfs, + u16 bitmap_len, u16 vf_id) +{ + if (!snap || !all_malvfs) + return -EINVAL; + + if (bitmap_len < snap->mbx_vf.vfcntr_len) + return -EINVAL; + + /* Ensure VF ID value is not larger than bitmap or VF counter length */ + if (vf_id >= bitmap_len || vf_id >= snap->mbx_vf.vfcntr_len) + return -EIO; + + /* Clear VF ID bit in the bitmap tracking malicious VFs attached to PF */ + clear_bit(vf_id, all_malvfs); + + /* Clear the VF counter in the mailbox snapshot structure for that VF ID. + * This is to ensure that if a VF is unloaded and a new one brought back + * up with the same VF ID for a snapshot currently in traversal or detect + * state the counter for that VF ID does not increment on top of existing + * values in the mailbox overflow detection algorithm. + */ + snap->mbx_vf.vf_cntr[vf_id] = 0; + + return 0; +} + +/** + * ice_mbx_init_snapshot - Initialize mailbox snapshot structure + * @hw: pointer to the hardware structure + * @vf_count: number of VFs allocated on a PF + * + * Clear the mailbox snapshot structure and allocate memory + * for the VF counter array based on the number of VFs allocated + * on that PF. + * + * Assumption: This function will assume ice_get_caps() has already been + * called to ensure that the vf_count can be compared against the number + * of VFs supported as defined in the functional capabilities of the device. + */ +int ice_mbx_init_snapshot(struct ice_hw *hw, u16 vf_count) +{ + struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; + + /* Ensure that the number of VFs allocated is non-zero and + * is not greater than the number of supported VFs defined in + * the functional capabilities of the PF. + */ + if (!vf_count || vf_count > hw->func_caps.num_allocd_vfs) + return -EINVAL; + + snap->mbx_vf.vf_cntr = devm_kcalloc(ice_hw_to_dev(hw), vf_count, + sizeof(*snap->mbx_vf.vf_cntr), + GFP_KERNEL); + if (!snap->mbx_vf.vf_cntr) + return -ENOMEM; + + /* Setting the VF counter length to the number of allocated + * VFs for given PF's functional capabilities. + */ + snap->mbx_vf.vfcntr_len = vf_count; + + /* Clear mbx_buf in the mailbox snaphot structure and setting the + * mailbox snapshot state to a new capture. + */ + memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf)); + snap->mbx_buf.state = ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT; + + return 0; +} + +/** + * ice_mbx_deinit_snapshot - Free mailbox snapshot structure + * @hw: pointer to the hardware structure + * + * Clear the mailbox snapshot structure and free the VF counter array. + */ +void ice_mbx_deinit_snapshot(struct ice_hw *hw) +{ + struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; + + /* Free VF counter array and reset VF counter length */ + devm_kfree(ice_hw_to_dev(hw), snap->mbx_vf.vf_cntr); + snap->mbx_vf.vfcntr_len = 0; + + /* Clear mbx_buf in the mailbox snaphot structure */ + memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf)); +} diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h new file mode 100644 index 000000000000..582716e6d5f9 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2018, Intel Corporation. */ + +#ifndef _ICE_VF_MBX_H_ +#define _ICE_VF_MBX_H_ + +#include "ice_type.h" +#include "ice_controlq.h" + +/* Defining the mailbox message threshold as 63 asynchronous + * pending messages. Normal VF functionality does not require + * sending more than 63 asynchronous pending message. + */ +#define ICE_ASYNC_VF_MSG_THRESHOLD 63 + +#ifdef CONFIG_PCI_IOV +int +ice_aq_send_msg_to_vf(struct ice_hw *hw, u16 vfid, u32 v_opcode, u32 v_retval, + u8 *msg, u16 msglen, struct ice_sq_cd *cd); + +u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed); +int +ice_mbx_vf_state_handler(struct ice_hw *hw, struct ice_mbx_data *mbx_data, + u16 vf_id, bool *is_mal_vf); +int +ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, unsigned long *all_malvfs, + u16 bitmap_len, u16 vf_id); +int ice_mbx_init_snapshot(struct ice_hw *hw, u16 vf_count); +void ice_mbx_deinit_snapshot(struct ice_hw *hw); +int +ice_mbx_report_malvf(struct ice_hw *hw, unsigned long *all_malvfs, + u16 bitmap_len, u16 vf_id, bool *report_malvf); +#else /* CONFIG_PCI_IOV */ +static inline int +ice_aq_send_msg_to_vf(struct ice_hw __always_unused *hw, + u16 __always_unused vfid, u32 __always_unused v_opcode, + u32 __always_unused v_retval, u8 __always_unused *msg, + u16 __always_unused msglen, + struct ice_sq_cd __always_unused *cd) +{ + return 0; +} + +static inline u32 +ice_conv_link_speed_to_virtchnl(bool __always_unused adv_link_support, + u16 __always_unused link_speed) +{ + return 0; +} + +#endif /* CONFIG_PCI_IOV */ +#endif /* _ICE_VF_MBX_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.c b/drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.c new file mode 100644 index 000000000000..5ecc0ee9a78e --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2019-2021, Intel Corporation. */ + +#include "ice_vsi_vlan_ops.h" +#include "ice_vsi_vlan_lib.h" +#include "ice_vlan_mode.h" +#include "ice.h" +#include "ice_vf_vsi_vlan_ops.h" +#include "ice_sriov.h" + +static int +noop_vlan_arg(struct ice_vsi __always_unused *vsi, + struct ice_vlan __always_unused *vlan) +{ + return 0; +} + +static int +noop_vlan(struct ice_vsi __always_unused *vsi) +{ + return 0; +} + +/** + * ice_vf_vsi_init_vlan_ops - Initialize default VSI VLAN ops for VF VSI + * @vsi: VF's VSI being configured + * + * If Double VLAN Mode (DVM) is enabled, assume that the VF supports the new + * VIRTCHNL_VF_VLAN_OFFLOAD_V2 capability and set up the VLAN ops accordingly. + * If SVM is enabled maintain the same level of VLAN support previous to + * VIRTCHNL_VF_VLAN_OFFLOAD_V2. + */ +void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi) +{ + struct ice_vsi_vlan_ops *vlan_ops; + struct ice_pf *pf = vsi->back; + struct ice_vf *vf = vsi->vf; + + if (WARN_ON(!vf)) + return; + + if (ice_is_dvm_ena(&pf->hw)) { + vlan_ops = &vsi->outer_vlan_ops; + + /* outer VLAN ops regardless of port VLAN config */ + vlan_ops->add_vlan = ice_vsi_add_vlan; + vlan_ops->dis_rx_filtering = ice_vsi_dis_rx_vlan_filtering; + vlan_ops->ena_tx_filtering = ice_vsi_ena_tx_vlan_filtering; + vlan_ops->dis_tx_filtering = ice_vsi_dis_tx_vlan_filtering; + + if (ice_vf_is_port_vlan_ena(vf)) { + /* setup outer VLAN ops */ + vlan_ops->set_port_vlan = ice_vsi_set_outer_port_vlan; + vlan_ops->ena_rx_filtering = + ice_vsi_ena_rx_vlan_filtering; + + /* setup inner VLAN ops */ + vlan_ops = &vsi->inner_vlan_ops; + vlan_ops->add_vlan = noop_vlan_arg; + vlan_ops->del_vlan = noop_vlan_arg; + vlan_ops->ena_stripping = ice_vsi_ena_inner_stripping; + vlan_ops->dis_stripping = ice_vsi_dis_inner_stripping; + vlan_ops->ena_insertion = ice_vsi_ena_inner_insertion; + vlan_ops->dis_insertion = ice_vsi_dis_inner_insertion; + } else { + if (!test_bit(ICE_FLAG_VF_VLAN_PRUNING, pf->flags)) + vlan_ops->ena_rx_filtering = noop_vlan; + else + vlan_ops->ena_rx_filtering = + ice_vsi_ena_rx_vlan_filtering; + + vlan_ops->del_vlan = ice_vsi_del_vlan; + vlan_ops->ena_stripping = ice_vsi_ena_outer_stripping; + vlan_ops->dis_stripping = ice_vsi_dis_outer_stripping; + vlan_ops->ena_insertion = ice_vsi_ena_outer_insertion; + vlan_ops->dis_insertion = ice_vsi_dis_outer_insertion; + + /* setup inner VLAN ops */ + vlan_ops = &vsi->inner_vlan_ops; + + vlan_ops->ena_stripping = ice_vsi_ena_inner_stripping; + vlan_ops->dis_stripping = ice_vsi_dis_inner_stripping; + vlan_ops->ena_insertion = ice_vsi_ena_inner_insertion; + vlan_ops->dis_insertion = ice_vsi_dis_inner_insertion; + } + } else { + vlan_ops = &vsi->inner_vlan_ops; + + /* inner VLAN ops regardless of port VLAN config */ + vlan_ops->add_vlan = ice_vsi_add_vlan; + vlan_ops->dis_rx_filtering = ice_vsi_dis_rx_vlan_filtering; + vlan_ops->ena_tx_filtering = ice_vsi_ena_tx_vlan_filtering; + vlan_ops->dis_tx_filtering = ice_vsi_dis_tx_vlan_filtering; + + if (ice_vf_is_port_vlan_ena(vf)) { + vlan_ops->set_port_vlan = ice_vsi_set_inner_port_vlan; + vlan_ops->ena_rx_filtering = + ice_vsi_ena_rx_vlan_filtering; + } else { + if (!test_bit(ICE_FLAG_VF_VLAN_PRUNING, pf->flags)) + vlan_ops->ena_rx_filtering = noop_vlan; + else + vlan_ops->ena_rx_filtering = + ice_vsi_ena_rx_vlan_filtering; + + vlan_ops->del_vlan = ice_vsi_del_vlan; + vlan_ops->ena_stripping = ice_vsi_ena_inner_stripping; + vlan_ops->dis_stripping = ice_vsi_dis_inner_stripping; + vlan_ops->ena_insertion = ice_vsi_ena_inner_insertion; + vlan_ops->dis_insertion = ice_vsi_dis_inner_insertion; + } + } +} + +/** + * ice_vf_vsi_cfg_dvm_legacy_vlan_mode - Config VLAN mode for old VFs in DVM + * @vsi: VF's VSI being configured + * + * This should only be called when Double VLAN Mode (DVM) is enabled, there + * is not a port VLAN enabled on this VF, and the VF negotiates + * VIRTCHNL_VF_OFFLOAD_VLAN. + * + * This function sets up the VF VSI's inner and outer ice_vsi_vlan_ops and also + * initializes software only VLAN mode (i.e. allow all VLANs). Also, use no-op + * implementations for any functions that may be called during the lifetime of + * the VF so these methods do nothing and succeed. + */ +void ice_vf_vsi_cfg_dvm_legacy_vlan_mode(struct ice_vsi *vsi) +{ + struct ice_vsi_vlan_ops *vlan_ops; + struct ice_vf *vf = vsi->vf; + struct device *dev; + + if (WARN_ON(!vf)) + return; + + dev = ice_pf_to_dev(vf->pf); + + if (!ice_is_dvm_ena(&vsi->back->hw) || ice_vf_is_port_vlan_ena(vf)) + return; + + vlan_ops = &vsi->outer_vlan_ops; + + /* Rx VLAN filtering always disabled to allow software offloaded VLANs + * for VFs that only support VIRTCHNL_VF_OFFLOAD_VLAN and don't have a + * port VLAN configured + */ + vlan_ops->dis_rx_filtering = ice_vsi_dis_rx_vlan_filtering; + /* Don't fail when attempting to enable Rx VLAN filtering */ + vlan_ops->ena_rx_filtering = noop_vlan; + + /* Tx VLAN filtering always disabled to allow software offloaded VLANs + * for VFs that only support VIRTCHNL_VF_OFFLOAD_VLAN and don't have a + * port VLAN configured + */ + vlan_ops->dis_tx_filtering = ice_vsi_dis_tx_vlan_filtering; + /* Don't fail when attempting to enable Tx VLAN filtering */ + vlan_ops->ena_tx_filtering = noop_vlan; + + if (vlan_ops->dis_rx_filtering(vsi)) + dev_dbg(dev, "Failed to disable Rx VLAN filtering for old VF without VIRTCHNL_VF_OFFLOAD_VLAN_V2 support\n"); + if (vlan_ops->dis_tx_filtering(vsi)) + dev_dbg(dev, "Failed to disable Tx VLAN filtering for old VF without VIRTHCNL_VF_OFFLOAD_VLAN_V2 support\n"); + + /* All outer VLAN offloads must be disabled */ + vlan_ops->dis_stripping = ice_vsi_dis_outer_stripping; + vlan_ops->dis_insertion = ice_vsi_dis_outer_insertion; + + if (vlan_ops->dis_stripping(vsi)) + dev_dbg(dev, "Failed to disable outer VLAN stripping for old VF without VIRTCHNL_VF_OFFLOAD_VLAN_V2 support\n"); + + if (vlan_ops->dis_insertion(vsi)) + dev_dbg(dev, "Failed to disable outer VLAN insertion for old VF without VIRTCHNL_VF_OFFLOAD_VLAN_V2 support\n"); + + /* All inner VLAN offloads must be disabled */ + vlan_ops = &vsi->inner_vlan_ops; + + vlan_ops->dis_stripping = ice_vsi_dis_outer_stripping; + vlan_ops->dis_insertion = ice_vsi_dis_outer_insertion; + + if (vlan_ops->dis_stripping(vsi)) + dev_dbg(dev, "Failed to disable inner VLAN stripping for old VF without VIRTCHNL_VF_OFFLOAD_VLAN_V2 support\n"); + + if (vlan_ops->dis_insertion(vsi)) + dev_dbg(dev, "Failed to disable inner VLAN insertion for old VF without VIRTCHNL_VF_OFFLOAD_VLAN_V2 support\n"); +} + +/** + * ice_vf_vsi_cfg_svm_legacy_vlan_mode - Config VLAN mode for old VFs in SVM + * @vsi: VF's VSI being configured + * + * This should only be called when Single VLAN Mode (SVM) is enabled, there is + * not a port VLAN enabled on this VF, and the VF negotiates + * VIRTCHNL_VF_OFFLOAD_VLAN. + * + * All of the normal SVM VLAN ops are identical for this case. However, by + * default Rx VLAN filtering should be turned off by default in this case. + */ +void ice_vf_vsi_cfg_svm_legacy_vlan_mode(struct ice_vsi *vsi) +{ + struct ice_vf *vf = vsi->vf; + + if (WARN_ON(!vf)) + return; + + if (ice_is_dvm_ena(&vsi->back->hw) || ice_vf_is_port_vlan_ena(vf)) + return; + + if (vsi->inner_vlan_ops.dis_rx_filtering(vsi)) + dev_dbg(ice_pf_to_dev(vf->pf), "Failed to disable Rx VLAN filtering for old VF with VIRTCHNL_VF_OFFLOAD_VLAN support\n"); +} diff --git a/drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.h b/drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.h new file mode 100644 index 000000000000..875a4e615f39 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019-2021, Intel Corporation. */ + +#ifndef _ICE_VF_VSI_VLAN_OPS_H_ +#define _ICE_VF_VSI_VLAN_OPS_H_ + +#include "ice_vsi_vlan_ops.h" + +struct ice_vsi; + +void ice_vf_vsi_cfg_dvm_legacy_vlan_mode(struct ice_vsi *vsi); +void ice_vf_vsi_cfg_svm_legacy_vlan_mode(struct ice_vsi *vsi); + +#ifdef CONFIG_PCI_IOV +void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi); +#else +static inline void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi) { } +#endif /* CONFIG_PCI_IOV */ +#endif /* _ICE_PF_VSI_VLAN_OPS_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c b/drivers/net/ethernet/intel/ice/ice_virtchnl.c similarity index 50% rename from drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c rename to drivers/net/ethernet/intel/ice/ice_virtchnl.c index 1be3cd4b2bef..3f1a63815bac 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.c @@ -1,15 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2018, Intel Corporation. */ +/* Copyright (C) 2022, Intel Corporation. */ +#include "ice_virtchnl.h" +#include "ice_vf_lib_private.h" #include "ice.h" #include "ice_base.h" #include "ice_lib.h" #include "ice_fltr.h" -#include "ice_dcb_lib.h" -#include "ice_flow.h" -#include "ice_eswitch.h" #include "ice_virtchnl_allowlist.h" +#include "ice_vf_vsi_vlan_ops.h" +#include "ice_vlan.h" #include "ice_flex_pipe.h" +#include "ice_dcb_lib.h" #define FIELD_SELECTOR(proto_hdr_field) \ BIT((proto_hdr_field) & PROTO_HDR_FIELD_MASK) @@ -163,45 +165,6 @@ ice_vc_hash_field_match_type ice_vc_hash_field_list[] = { BIT_ULL(ICE_FLOW_FIELD_IDX_PFCP_SEID)}, }; -/** - * ice_get_vf_vsi - get VF's VSI based on the stored index - * @vf: VF used to get VSI - */ -struct ice_vsi *ice_get_vf_vsi(struct ice_vf *vf) -{ - return vf->pf->vsi[vf->lan_vsi_idx]; -} - -/** - * ice_validate_vf_id - helper to check if VF ID is valid - * @pf: pointer to the PF structure - * @vf_id: the ID of the VF to check - */ -static int ice_validate_vf_id(struct ice_pf *pf, u16 vf_id) -{ - /* vf_id range is only valid for 0-255, and should always be unsigned */ - if (vf_id >= pf->num_alloc_vfs) { - dev_err(ice_pf_to_dev(pf), "Invalid VF ID: %u\n", vf_id); - return -EINVAL; - } - return 0; -} - -/** - * ice_check_vf_init - helper to check if VF init complete - * @pf: pointer to the PF structure - * @vf: the pointer to the VF to check - */ -static int ice_check_vf_init(struct ice_pf *pf, struct ice_vf *vf) -{ - if (!test_bit(ICE_VF_STATE_INIT, vf->vf_states)) { - dev_err(ice_pf_to_dev(pf), "VF ID: %u in reset. Try again.\n", - vf->vf_id); - return -EBUSY; - } - return 0; -} - /** * ice_vc_vf_broadcast - Broadcast a message to all VFs on PF * @pf: pointer to the PF structure @@ -215,11 +178,11 @@ ice_vc_vf_broadcast(struct ice_pf *pf, enum virtchnl_ops v_opcode, enum virtchnl_status_code v_retval, u8 *msg, u16 msglen) { struct ice_hw *hw = &pf->hw; - unsigned int i; - - ice_for_each_vf(pf, i) { - struct ice_vf *vf = &pf->vf[i]; + struct ice_vf *vf; + unsigned int bkt; + mutex_lock(&pf->vfs.table_lock); + ice_for_each_vf(pf, bkt, vf) { /* Not all vfs are enabled so skip the ones that are not */ if (!test_bit(ICE_VF_STATE_INIT, vf->vf_states) && !test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) @@ -231,6 +194,7 @@ ice_vc_vf_broadcast(struct ice_pf *pf, enum virtchnl_ops v_opcode, ice_aq_send_msg_to_vf(hw, vf->vf_id, v_opcode, v_retval, msg, msglen, NULL); } + mutex_unlock(&pf->vfs.table_lock); } /** @@ -258,39 +222,6 @@ ice_set_pfe_link(struct ice_vf *vf, struct virtchnl_pf_event *pfe, } } -/** - * ice_vf_has_no_qs_ena - check if the VF has any Rx or Tx queues enabled - * @vf: the VF to check - * - * Returns true if the VF has no Rx and no Tx queues enabled and returns false - * otherwise - */ -static bool ice_vf_has_no_qs_ena(struct ice_vf *vf) -{ - return (!bitmap_weight(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF) && - !bitmap_weight(vf->txq_ena, ICE_MAX_RSS_QS_PER_VF)); -} - -/** - * ice_is_vf_link_up - check if the VF's link is up - * @vf: VF to check if link is up - */ -static bool ice_is_vf_link_up(struct ice_vf *vf) -{ - struct ice_pf *pf = vf->pf; - - if (ice_check_vf_init(pf, vf)) - return false; - - if (ice_vf_has_no_qs_ena(vf)) - return false; - else if (vf->link_forced) - return vf->link_up; - else - return pf->hw.port_info->phy.link_info.link_info & - ICE_AQ_LINK_UP; -} - /** * ice_vc_notify_vf_link_state - Inform a VF of link status * @vf: pointer to the VF structure @@ -316,1368 +247,19 @@ void ice_vc_notify_vf_link_state(struct ice_vf *vf) sizeof(pfe), NULL); } -/** - * ice_vf_invalidate_vsi - invalidate vsi_idx/vsi_num to remove VSI access - * @vf: VF to remove access to VSI for - */ -static void ice_vf_invalidate_vsi(struct ice_vf *vf) -{ - vf->lan_vsi_idx = ICE_NO_VSI; - vf->lan_vsi_num = ICE_NO_VSI; -} - -/** - * ice_vf_vsi_release - invalidate the VF's VSI after freeing it - * @vf: invalidate this VF's VSI after freeing it - */ -static void ice_vf_vsi_release(struct ice_vf *vf) -{ - ice_vsi_release(ice_get_vf_vsi(vf)); - ice_vf_invalidate_vsi(vf); -} - -/** - * ice_vf_ctrl_invalidate_vsi - invalidate ctrl_vsi_idx to remove VSI access - * @vf: VF that control VSI is being invalidated on - */ -static void ice_vf_ctrl_invalidate_vsi(struct ice_vf *vf) -{ - vf->ctrl_vsi_idx = ICE_NO_VSI; -} - -/** - * ice_vf_ctrl_vsi_release - invalidate the VF's control VSI after freeing it - * @vf: VF that control VSI is being released on - */ -static void ice_vf_ctrl_vsi_release(struct ice_vf *vf) -{ - ice_vsi_release(vf->pf->vsi[vf->ctrl_vsi_idx]); - ice_vf_ctrl_invalidate_vsi(vf); -} - -/** - * ice_free_vf_res - Free a VF's resources - * @vf: pointer to the VF info - */ -static void ice_free_vf_res(struct ice_vf *vf) -{ - struct ice_pf *pf = vf->pf; - int i, last_vector_idx; - - /* First, disable VF's configuration API to prevent OS from - * accessing the VF's VSI after it's freed or invalidated. - */ - clear_bit(ICE_VF_STATE_INIT, vf->vf_states); - ice_vf_fdir_exit(vf); - /* free VF control VSI */ - if (vf->ctrl_vsi_idx != ICE_NO_VSI) - ice_vf_ctrl_vsi_release(vf); - - /* free VSI and disconnect it from the parent uplink */ - if (vf->lan_vsi_idx != ICE_NO_VSI) { - ice_vf_vsi_release(vf); - vf->num_mac = 0; - } - - last_vector_idx = vf->first_vector_idx + pf->num_msix_per_vf - 1; - - /* clear VF MDD event information */ - memset(&vf->mdd_tx_events, 0, sizeof(vf->mdd_tx_events)); - memset(&vf->mdd_rx_events, 0, sizeof(vf->mdd_rx_events)); - - /* Disable interrupts so that VF starts in a known state */ - for (i = vf->first_vector_idx; i <= last_vector_idx; i++) { - wr32(&pf->hw, GLINT_DYN_CTL(i), GLINT_DYN_CTL_CLEARPBA_M); - ice_flush(&pf->hw); - } - /* reset some of the state variables keeping track of the resources */ - clear_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states); - clear_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states); -} - -/** - * ice_dis_vf_mappings - * @vf: pointer to the VF structure - */ -static void ice_dis_vf_mappings(struct ice_vf *vf) -{ - struct ice_pf *pf = vf->pf; - struct ice_vsi *vsi; - struct device *dev; - int first, last, v; - struct ice_hw *hw; - - hw = &pf->hw; - vsi = ice_get_vf_vsi(vf); - - dev = ice_pf_to_dev(pf); - wr32(hw, VPINT_ALLOC(vf->vf_id), 0); - wr32(hw, VPINT_ALLOC_PCI(vf->vf_id), 0); - - first = vf->first_vector_idx; - last = first + pf->num_msix_per_vf - 1; - for (v = first; v <= last; v++) { - u32 reg; - - reg = (((1 << GLINT_VECT2FUNC_IS_PF_S) & - GLINT_VECT2FUNC_IS_PF_M) | - ((hw->pf_id << GLINT_VECT2FUNC_PF_NUM_S) & - GLINT_VECT2FUNC_PF_NUM_M)); - wr32(hw, GLINT_VECT2FUNC(v), reg); - } - - if (vsi->tx_mapping_mode == ICE_VSI_MAP_CONTIG) - wr32(hw, VPLAN_TX_QBASE(vf->vf_id), 0); - else - dev_err(dev, "Scattered mode for VF Tx queues is not yet implemented\n"); - - if (vsi->rx_mapping_mode == ICE_VSI_MAP_CONTIG) - wr32(hw, VPLAN_RX_QBASE(vf->vf_id), 0); - else - dev_err(dev, "Scattered mode for VF Rx queues is not yet implemented\n"); -} - -/** - * ice_sriov_free_msix_res - Reset/free any used MSIX resources - * @pf: pointer to the PF structure - * - * Since no MSIX entries are taken from the pf->irq_tracker then just clear - * the pf->sriov_base_vector. - * - * Returns 0 on success, and -EINVAL on error. - */ -static int ice_sriov_free_msix_res(struct ice_pf *pf) -{ - struct ice_res_tracker *res; - - if (!pf) - return -EINVAL; - - res = pf->irq_tracker; - if (!res) - return -EINVAL; - - /* give back irq_tracker resources used */ - WARN_ON(pf->sriov_base_vector < res->num_entries); - - pf->sriov_base_vector = 0; - - return 0; -} - -/** - * ice_set_vf_state_qs_dis - Set VF queues state to disabled - * @vf: pointer to the VF structure - */ -void ice_set_vf_state_qs_dis(struct ice_vf *vf) -{ - /* Clear Rx/Tx enabled queues flag */ - bitmap_zero(vf->txq_ena, ICE_MAX_RSS_QS_PER_VF); - bitmap_zero(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF); - clear_bit(ICE_VF_STATE_QS_ENA, vf->vf_states); -} - -/** - * ice_dis_vf_qs - Disable the VF queues - * @vf: pointer to the VF structure - */ -static void ice_dis_vf_qs(struct ice_vf *vf) -{ - struct ice_vsi *vsi = ice_get_vf_vsi(vf); - - ice_vsi_stop_lan_tx_rings(vsi, ICE_NO_RESET, vf->vf_id); - ice_vsi_stop_all_rx_rings(vsi); - ice_set_vf_state_qs_dis(vf); -} - -/** - * ice_free_vfs - Free all VFs - * @pf: pointer to the PF structure - */ -void ice_free_vfs(struct ice_pf *pf) -{ - struct device *dev = ice_pf_to_dev(pf); - struct ice_hw *hw = &pf->hw; - unsigned int tmp, i; - - if (!pf->vf) - return; - - ice_eswitch_release(pf); - - while (test_and_set_bit(ICE_VF_DIS, pf->state)) - usleep_range(1000, 2000); - - /* Disable IOV before freeing resources. This lets any VF drivers - * running in the host get themselves cleaned up before we yank - * the carpet out from underneath their feet. - */ - if (!pci_vfs_assigned(pf->pdev)) - pci_disable_sriov(pf->pdev); - else - dev_warn(dev, "VFs are assigned - not disabling SR-IOV\n"); - - tmp = pf->num_alloc_vfs; - pf->num_qps_per_vf = 0; - pf->num_alloc_vfs = 0; - for (i = 0; i < tmp; i++) { - struct ice_vf *vf = &pf->vf[i]; - - mutex_lock(&vf->cfg_lock); - - ice_dis_vf_qs(vf); - - if (test_bit(ICE_VF_STATE_INIT, vf->vf_states)) { - /* disable VF qp mappings and set VF disable state */ - ice_dis_vf_mappings(vf); - set_bit(ICE_VF_STATE_DIS, vf->vf_states); - ice_free_vf_res(vf); - } - - mutex_unlock(&vf->cfg_lock); - - mutex_destroy(&vf->cfg_lock); - } - - if (ice_sriov_free_msix_res(pf)) - dev_err(dev, "Failed to free MSIX resources used by SR-IOV\n"); - - devm_kfree(dev, pf->vf); - pf->vf = NULL; - - /* This check is for when the driver is unloaded while VFs are - * assigned. Setting the number of VFs to 0 through sysfs is caught - * before this function ever gets called. - */ - if (!pci_vfs_assigned(pf->pdev)) { - unsigned int vf_id; - - /* Acknowledge VFLR for all VFs. Without this, VFs will fail to - * work correctly when SR-IOV gets re-enabled. - */ - for (vf_id = 0; vf_id < tmp; vf_id++) { - u32 reg_idx, bit_idx; - - reg_idx = (hw->func_caps.vf_base_id + vf_id) / 32; - bit_idx = (hw->func_caps.vf_base_id + vf_id) % 32; - wr32(hw, GLGEN_VFLRSTAT(reg_idx), BIT(bit_idx)); - } - } - - /* clear malicious info if the VFs are getting released */ - for (i = 0; i < tmp; i++) - if (ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->malvfs, - ICE_MAX_VF_COUNT, i)) - dev_dbg(dev, "failed to clear malicious VF state for VF %u\n", - i); - - clear_bit(ICE_VF_DIS, pf->state); - clear_bit(ICE_FLAG_SRIOV_ENA, pf->flags); -} - -/** - * ice_trigger_vf_reset - Reset a VF on HW - * @vf: pointer to the VF structure - * @is_vflr: true if VFLR was issued, false if not - * @is_pfr: true if the reset was triggered due to a previous PFR - * - * Trigger hardware to start a reset for a particular VF. Expects the caller - * to wait the proper amount of time to allow hardware to reset the VF before - * it cleans up and restores VF functionality. - */ -static void ice_trigger_vf_reset(struct ice_vf *vf, bool is_vflr, bool is_pfr) -{ - struct ice_pf *pf = vf->pf; - u32 reg, reg_idx, bit_idx; - unsigned int vf_abs_id, i; - struct device *dev; - struct ice_hw *hw; - - dev = ice_pf_to_dev(pf); - hw = &pf->hw; - vf_abs_id = vf->vf_id + hw->func_caps.vf_base_id; - - /* Inform VF that it is no longer active, as a warning */ - clear_bit(ICE_VF_STATE_ACTIVE, vf->vf_states); - - /* Disable VF's configuration API during reset. The flag is re-enabled - * when it's safe again to access VF's VSI. - */ - clear_bit(ICE_VF_STATE_INIT, vf->vf_states); - - /* VF_MBX_ARQLEN and VF_MBX_ATQLEN are cleared by PFR, so the driver - * needs to clear them in the case of VFR/VFLR. If this is done for - * PFR, it can mess up VF resets because the VF driver may already - * have started cleanup by the time we get here. - */ - if (!is_pfr) { - wr32(hw, VF_MBX_ARQLEN(vf->vf_id), 0); - wr32(hw, VF_MBX_ATQLEN(vf->vf_id), 0); - } - - /* In the case of a VFLR, the HW has already reset the VF and we - * just need to clean up, so don't hit the VFRTRIG register. - */ - if (!is_vflr) { - /* reset VF using VPGEN_VFRTRIG reg */ - reg = rd32(hw, VPGEN_VFRTRIG(vf->vf_id)); - reg |= VPGEN_VFRTRIG_VFSWR_M; - wr32(hw, VPGEN_VFRTRIG(vf->vf_id), reg); - } - /* clear the VFLR bit in GLGEN_VFLRSTAT */ - reg_idx = (vf_abs_id) / 32; - bit_idx = (vf_abs_id) % 32; - wr32(hw, GLGEN_VFLRSTAT(reg_idx), BIT(bit_idx)); - ice_flush(hw); - - wr32(hw, PF_PCI_CIAA, - VF_DEVICE_STATUS | (vf_abs_id << PF_PCI_CIAA_VF_NUM_S)); - for (i = 0; i < ICE_PCI_CIAD_WAIT_COUNT; i++) { - reg = rd32(hw, PF_PCI_CIAD); - /* no transactions pending so stop polling */ - if ((reg & VF_TRANS_PENDING_M) == 0) - break; - - dev_err(dev, "VF %u PCI transactions stuck\n", vf->vf_id); - udelay(ICE_PCI_CIAD_WAIT_DELAY_US); - } -} - -/** - * ice_vsi_manage_pvid - Enable or disable port VLAN for VSI - * @vsi: the VSI to update - * @pvid_info: VLAN ID and QoS used to set the PVID VSI context field - * @enable: true for enable PVID false for disable - */ -static int ice_vsi_manage_pvid(struct ice_vsi *vsi, u16 pvid_info, bool enable) -{ - struct ice_hw *hw = &vsi->back->hw; - struct ice_aqc_vsi_props *info; - struct ice_vsi_ctx *ctxt; - int ret; - - ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); - if (!ctxt) - return -ENOMEM; - - ctxt->info = vsi->info; - info = &ctxt->info; - if (enable) { - info->vlan_flags = ICE_AQ_VSI_VLAN_MODE_UNTAGGED | - ICE_AQ_VSI_PVLAN_INSERT_PVID | - ICE_AQ_VSI_VLAN_EMOD_STR; - info->sw_flags2 |= ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA; - } else { - info->vlan_flags = ICE_AQ_VSI_VLAN_EMOD_NOTHING | - ICE_AQ_VSI_VLAN_MODE_ALL; - info->sw_flags2 &= ~ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA; - } - - info->pvid = cpu_to_le16(pvid_info); - info->valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_VLAN_VALID | - ICE_AQ_VSI_PROP_SW_VALID); - - ret = ice_update_vsi(hw, vsi->idx, ctxt, NULL); - if (ret) { - dev_info(ice_hw_to_dev(hw), "update VSI for port VLAN failed, err %d aq_err %s\n", - ret, ice_aq_str(hw->adminq.sq_last_status)); - goto out; - } - - vsi->info.vlan_flags = info->vlan_flags; - vsi->info.sw_flags2 = info->sw_flags2; - vsi->info.pvid = info->pvid; -out: - kfree(ctxt); - return ret; -} - -/** - * ice_vf_get_port_info - Get the VF's port info structure - * @vf: VF used to get the port info structure for - */ -static struct ice_port_info *ice_vf_get_port_info(struct ice_vf *vf) -{ - return vf->pf->hw.port_info; -} - -/** - * ice_vf_vsi_setup - Set up a VF VSI - * @vf: VF to setup VSI for - * - * Returns pointer to the successfully allocated VSI struct on success, - * otherwise returns NULL on failure. - */ -static struct ice_vsi *ice_vf_vsi_setup(struct ice_vf *vf) -{ - struct ice_port_info *pi = ice_vf_get_port_info(vf); - struct ice_pf *pf = vf->pf; - struct ice_vsi *vsi; - - vsi = ice_vsi_setup(pf, pi, ICE_VSI_VF, vf->vf_id, NULL); - - if (!vsi) { - dev_err(ice_pf_to_dev(pf), "Failed to create VF VSI\n"); - ice_vf_invalidate_vsi(vf); - return NULL; - } - - vf->lan_vsi_idx = vsi->idx; - vf->lan_vsi_num = vsi->vsi_num; - - return vsi; -} - -/** - * ice_vf_ctrl_vsi_setup - Set up a VF control VSI - * @vf: VF to setup control VSI for - * - * Returns pointer to the successfully allocated VSI struct on success, - * otherwise returns NULL on failure. - */ -struct ice_vsi *ice_vf_ctrl_vsi_setup(struct ice_vf *vf) -{ - struct ice_port_info *pi = ice_vf_get_port_info(vf); - struct ice_pf *pf = vf->pf; - struct ice_vsi *vsi; - - vsi = ice_vsi_setup(pf, pi, ICE_VSI_CTRL, vf->vf_id, NULL); - if (!vsi) { - dev_err(ice_pf_to_dev(pf), "Failed to create VF control VSI\n"); - ice_vf_ctrl_invalidate_vsi(vf); - } - - return vsi; -} - -/** - * ice_calc_vf_first_vector_idx - Calculate MSIX vector index in the PF space - * @pf: pointer to PF structure - * @vf: pointer to VF that the first MSIX vector index is being calculated for - * - * This returns the first MSIX vector index in PF space that is used by this VF. - * This index is used when accessing PF relative registers such as - * GLINT_VECT2FUNC and GLINT_DYN_CTL. - * This will always be the OICR index in the AVF driver so any functionality - * using vf->first_vector_idx for queue configuration will have to increment by - * 1 to avoid meddling with the OICR index. - */ -static int ice_calc_vf_first_vector_idx(struct ice_pf *pf, struct ice_vf *vf) -{ - return pf->sriov_base_vector + vf->vf_id * pf->num_msix_per_vf; -} - -/** - * ice_vf_rebuild_host_tx_rate_cfg - re-apply the Tx rate limiting configuration - * @vf: VF to re-apply the configuration for - * - * Called after a VF VSI has been re-added/rebuild during reset. The PF driver - * needs to re-apply the host configured Tx rate limiting configuration. - */ -static int ice_vf_rebuild_host_tx_rate_cfg(struct ice_vf *vf) -{ - struct device *dev = ice_pf_to_dev(vf->pf); - struct ice_vsi *vsi = ice_get_vf_vsi(vf); - int err; - - if (vf->min_tx_rate) { - err = ice_set_min_bw_limit(vsi, (u64)vf->min_tx_rate * 1000); - if (err) { - dev_err(dev, "failed to set min Tx rate to %d Mbps for VF %u, error %d\n", - vf->min_tx_rate, vf->vf_id, err); - return err; - } - } - - if (vf->max_tx_rate) { - err = ice_set_max_bw_limit(vsi, (u64)vf->max_tx_rate * 1000); - if (err) { - dev_err(dev, "failed to set max Tx rate to %d Mbps for VF %u, error %d\n", - vf->max_tx_rate, vf->vf_id, err); - return err; - } - } - - return 0; -} - -/** - * ice_vf_rebuild_host_vlan_cfg - add VLAN 0 filter or rebuild the Port VLAN - * @vf: VF to add MAC filters for - * - * Called after a VF VSI has been re-added/rebuilt during reset. The PF driver - * always re-adds either a VLAN 0 or port VLAN based filter after reset. - */ -static int ice_vf_rebuild_host_vlan_cfg(struct ice_vf *vf) -{ - struct device *dev = ice_pf_to_dev(vf->pf); - struct ice_vsi *vsi = ice_get_vf_vsi(vf); - u16 vlan_id = 0; - int err; - - if (vf->port_vlan_info) { - err = ice_vsi_manage_pvid(vsi, vf->port_vlan_info, true); - if (err) { - dev_err(dev, "failed to configure port VLAN via VSI parameters for VF %u, error %d\n", - vf->vf_id, err); - return err; - } - - vlan_id = vf->port_vlan_info & VLAN_VID_MASK; - } - - /* vlan_id will either be 0 or the port VLAN number */ - err = ice_vsi_add_vlan(vsi, vlan_id, ICE_FWD_TO_VSI); - if (err) { - dev_err(dev, "failed to add %s VLAN %u filter for VF %u, error %d\n", - vf->port_vlan_info ? "port" : "", vlan_id, vf->vf_id, - err); - return err; - } - - return 0; -} - -/** - * ice_vf_rebuild_host_mac_cfg - add broadcast and the VF's perm_addr/LAA - * @vf: VF to add MAC filters for - * - * Called after a VF VSI has been re-added/rebuilt during reset. The PF driver - * always re-adds a broadcast filter and the VF's perm_addr/LAA after reset. - */ -static int ice_vf_rebuild_host_mac_cfg(struct ice_vf *vf) -{ - struct device *dev = ice_pf_to_dev(vf->pf); - struct ice_vsi *vsi = ice_get_vf_vsi(vf); - u8 broadcast[ETH_ALEN]; - int status; - - if (ice_is_eswitch_mode_switchdev(vf->pf)) - return 0; - - eth_broadcast_addr(broadcast); - status = ice_fltr_add_mac(vsi, broadcast, ICE_FWD_TO_VSI); - if (status) { - dev_err(dev, "failed to add broadcast MAC filter for VF %u, error %d\n", - vf->vf_id, status); - return status; - } - - vf->num_mac++; - - if (is_valid_ether_addr(vf->hw_lan_addr.addr)) { - status = ice_fltr_add_mac(vsi, vf->hw_lan_addr.addr, - ICE_FWD_TO_VSI); - if (status) { - dev_err(dev, "failed to add default unicast MAC filter %pM for VF %u, error %d\n", - &vf->hw_lan_addr.addr[0], vf->vf_id, - status); - return status; - } - vf->num_mac++; - - ether_addr_copy(vf->dev_lan_addr.addr, vf->hw_lan_addr.addr); - } - - return 0; -} - -/** - * ice_vf_set_host_trust_cfg - set trust setting based on pre-reset value - * @vf: VF to configure trust setting for - */ -static void ice_vf_set_host_trust_cfg(struct ice_vf *vf) -{ - if (vf->trusted) - set_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps); - else - clear_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps); -} - -/** - * ice_ena_vf_msix_mappings - enable VF MSIX mappings in hardware - * @vf: VF to enable MSIX mappings for - * - * Some of the registers need to be indexed/configured using hardware global - * device values and other registers need 0-based values, which represent PF - * based values. - */ -static void ice_ena_vf_msix_mappings(struct ice_vf *vf) -{ - int device_based_first_msix, device_based_last_msix; - int pf_based_first_msix, pf_based_last_msix, v; - struct ice_pf *pf = vf->pf; - int device_based_vf_id; - struct ice_hw *hw; - u32 reg; - - hw = &pf->hw; - pf_based_first_msix = vf->first_vector_idx; - pf_based_last_msix = (pf_based_first_msix + pf->num_msix_per_vf) - 1; - - device_based_first_msix = pf_based_first_msix + - pf->hw.func_caps.common_cap.msix_vector_first_id; - device_based_last_msix = - (device_based_first_msix + pf->num_msix_per_vf) - 1; - device_based_vf_id = vf->vf_id + hw->func_caps.vf_base_id; - - reg = (((device_based_first_msix << VPINT_ALLOC_FIRST_S) & - VPINT_ALLOC_FIRST_M) | - ((device_based_last_msix << VPINT_ALLOC_LAST_S) & - VPINT_ALLOC_LAST_M) | VPINT_ALLOC_VALID_M); - wr32(hw, VPINT_ALLOC(vf->vf_id), reg); - - reg = (((device_based_first_msix << VPINT_ALLOC_PCI_FIRST_S) - & VPINT_ALLOC_PCI_FIRST_M) | - ((device_based_last_msix << VPINT_ALLOC_PCI_LAST_S) & - VPINT_ALLOC_PCI_LAST_M) | VPINT_ALLOC_PCI_VALID_M); - wr32(hw, VPINT_ALLOC_PCI(vf->vf_id), reg); - - /* map the interrupts to its functions */ - for (v = pf_based_first_msix; v <= pf_based_last_msix; v++) { - reg = (((device_based_vf_id << GLINT_VECT2FUNC_VF_NUM_S) & - GLINT_VECT2FUNC_VF_NUM_M) | - ((hw->pf_id << GLINT_VECT2FUNC_PF_NUM_S) & - GLINT_VECT2FUNC_PF_NUM_M)); - wr32(hw, GLINT_VECT2FUNC(v), reg); - } - - /* Map mailbox interrupt to VF MSI-X vector 0 */ - wr32(hw, VPINT_MBX_CTL(device_based_vf_id), VPINT_MBX_CTL_CAUSE_ENA_M); -} - -/** - * ice_ena_vf_q_mappings - enable Rx/Tx queue mappings for a VF - * @vf: VF to enable the mappings for - * @max_txq: max Tx queues allowed on the VF's VSI - * @max_rxq: max Rx queues allowed on the VF's VSI - */ -static void ice_ena_vf_q_mappings(struct ice_vf *vf, u16 max_txq, u16 max_rxq) -{ - struct device *dev = ice_pf_to_dev(vf->pf); - struct ice_vsi *vsi = ice_get_vf_vsi(vf); - struct ice_hw *hw = &vf->pf->hw; - u32 reg; - - /* set regardless of mapping mode */ - wr32(hw, VPLAN_TXQ_MAPENA(vf->vf_id), VPLAN_TXQ_MAPENA_TX_ENA_M); - - /* VF Tx queues allocation */ - if (vsi->tx_mapping_mode == ICE_VSI_MAP_CONTIG) { - /* set the VF PF Tx queue range - * VFNUMQ value should be set to (number of queues - 1). A value - * of 0 means 1 queue and a value of 255 means 256 queues - */ - reg = (((vsi->txq_map[0] << VPLAN_TX_QBASE_VFFIRSTQ_S) & - VPLAN_TX_QBASE_VFFIRSTQ_M) | - (((max_txq - 1) << VPLAN_TX_QBASE_VFNUMQ_S) & - VPLAN_TX_QBASE_VFNUMQ_M)); - wr32(hw, VPLAN_TX_QBASE(vf->vf_id), reg); - } else { - dev_err(dev, "Scattered mode for VF Tx queues is not yet implemented\n"); - } - - /* set regardless of mapping mode */ - wr32(hw, VPLAN_RXQ_MAPENA(vf->vf_id), VPLAN_RXQ_MAPENA_RX_ENA_M); - - /* VF Rx queues allocation */ - if (vsi->rx_mapping_mode == ICE_VSI_MAP_CONTIG) { - /* set the VF PF Rx queue range - * VFNUMQ value should be set to (number of queues - 1). A value - * of 0 means 1 queue and a value of 255 means 256 queues - */ - reg = (((vsi->rxq_map[0] << VPLAN_RX_QBASE_VFFIRSTQ_S) & - VPLAN_RX_QBASE_VFFIRSTQ_M) | - (((max_rxq - 1) << VPLAN_RX_QBASE_VFNUMQ_S) & - VPLAN_RX_QBASE_VFNUMQ_M)); - wr32(hw, VPLAN_RX_QBASE(vf->vf_id), reg); - } else { - dev_err(dev, "Scattered mode for VF Rx queues is not yet implemented\n"); - } -} - -/** - * ice_ena_vf_mappings - enable VF MSIX and queue mapping - * @vf: pointer to the VF structure - */ -static void ice_ena_vf_mappings(struct ice_vf *vf) -{ - struct ice_vsi *vsi = ice_get_vf_vsi(vf); - - ice_ena_vf_msix_mappings(vf); - ice_ena_vf_q_mappings(vf, vsi->alloc_txq, vsi->alloc_rxq); -} - -/** - * ice_determine_res - * @pf: pointer to the PF structure - * @avail_res: available resources in the PF structure - * @max_res: maximum resources that can be given per VF - * @min_res: minimum resources that can be given per VF - * - * Returns non-zero value if resources (queues/vectors) are available or - * returns zero if PF cannot accommodate for all num_alloc_vfs. - */ -static int -ice_determine_res(struct ice_pf *pf, u16 avail_res, u16 max_res, u16 min_res) -{ - bool checked_min_res = false; - int res; - - /* start by checking if PF can assign max number of resources for - * all num_alloc_vfs. - * if yes, return number per VF - * If no, divide by 2 and roundup, check again - * repeat the loop till we reach a point where even minimum resources - * are not available, in that case return 0 - */ - res = max_res; - while ((res >= min_res) && !checked_min_res) { - int num_all_res; - - num_all_res = pf->num_alloc_vfs * res; - if (num_all_res <= avail_res) - return res; - - if (res == min_res) - checked_min_res = true; - - res = DIV_ROUND_UP(res, 2); - } - return 0; -} - -/** - * ice_calc_vf_reg_idx - Calculate the VF's register index in the PF space - * @vf: VF to calculate the register index for - * @q_vector: a q_vector associated to the VF - */ -int ice_calc_vf_reg_idx(struct ice_vf *vf, struct ice_q_vector *q_vector) -{ - struct ice_pf *pf; - - if (!vf || !q_vector) - return -EINVAL; - - pf = vf->pf; - - /* always add one to account for the OICR being the first MSIX */ - return pf->sriov_base_vector + pf->num_msix_per_vf * vf->vf_id + - q_vector->v_idx + 1; -} - -/** - * ice_get_max_valid_res_idx - Get the max valid resource index - * @res: pointer to the resource to find the max valid index for - * - * Start from the end of the ice_res_tracker and return right when we find the - * first res->list entry with the ICE_RES_VALID_BIT set. This function is only - * valid for SR-IOV because it is the only consumer that manipulates the - * res->end and this is always called when res->end is set to res->num_entries. - */ -static int ice_get_max_valid_res_idx(struct ice_res_tracker *res) -{ - int i; - - if (!res) - return -EINVAL; - - for (i = res->num_entries - 1; i >= 0; i--) - if (res->list[i] & ICE_RES_VALID_BIT) - return i; - - return 0; -} - -/** - * ice_sriov_set_msix_res - Set any used MSIX resources - * @pf: pointer to PF structure - * @num_msix_needed: number of MSIX vectors needed for all SR-IOV VFs - * - * This function allows SR-IOV resources to be taken from the end of the PF's - * allowed HW MSIX vectors so that the irq_tracker will not be affected. We - * just set the pf->sriov_base_vector and return success. - * - * If there are not enough resources available, return an error. This should - * always be caught by ice_set_per_vf_res(). - * - * Return 0 on success, and -EINVAL when there are not enough MSIX vectors - * in the PF's space available for SR-IOV. - */ -static int ice_sriov_set_msix_res(struct ice_pf *pf, u16 num_msix_needed) -{ - u16 total_vectors = pf->hw.func_caps.common_cap.num_msix_vectors; - int vectors_used = pf->irq_tracker->num_entries; - int sriov_base_vector; - - sriov_base_vector = total_vectors - num_msix_needed; - - /* make sure we only grab irq_tracker entries from the list end and - * that we have enough available MSIX vectors - */ - if (sriov_base_vector < vectors_used) - return -EINVAL; - - pf->sriov_base_vector = sriov_base_vector; - - return 0; -} - -/** - * ice_set_per_vf_res - check if vectors and queues are available - * @pf: pointer to the PF structure - * - * First, determine HW interrupts from common pool. If we allocate fewer VFs, we - * get more vectors and can enable more queues per VF. Note that this does not - * grab any vectors from the SW pool already allocated. Also note, that all - * vector counts include one for each VF's miscellaneous interrupt vector - * (i.e. OICR). - * - * Minimum VFs - 2 vectors, 1 queue pair - * Small VFs - 5 vectors, 4 queue pairs - * Medium VFs - 17 vectors, 16 queue pairs - * - * Second, determine number of queue pairs per VF by starting with a pre-defined - * maximum each VF supports. If this is not possible, then we adjust based on - * queue pairs available on the device. - * - * Lastly, set queue and MSI-X VF variables tracked by the PF so it can be used - * by each VF during VF initialization and reset. - */ -static int ice_set_per_vf_res(struct ice_pf *pf) -{ - int max_valid_res_idx = ice_get_max_valid_res_idx(pf->irq_tracker); - int msix_avail_per_vf, msix_avail_for_sriov; - struct device *dev = ice_pf_to_dev(pf); - u16 num_msix_per_vf, num_txq, num_rxq; - - if (!pf->num_alloc_vfs || max_valid_res_idx < 0) - return -EINVAL; - - /* determine MSI-X resources per VF */ - msix_avail_for_sriov = pf->hw.func_caps.common_cap.num_msix_vectors - - pf->irq_tracker->num_entries; - msix_avail_per_vf = msix_avail_for_sriov / pf->num_alloc_vfs; - if (msix_avail_per_vf >= ICE_NUM_VF_MSIX_MED) { - num_msix_per_vf = ICE_NUM_VF_MSIX_MED; - } else if (msix_avail_per_vf >= ICE_NUM_VF_MSIX_SMALL) { - num_msix_per_vf = ICE_NUM_VF_MSIX_SMALL; - } else if (msix_avail_per_vf >= ICE_NUM_VF_MSIX_MULTIQ_MIN) { - num_msix_per_vf = ICE_NUM_VF_MSIX_MULTIQ_MIN; - } else if (msix_avail_per_vf >= ICE_MIN_INTR_PER_VF) { - num_msix_per_vf = ICE_MIN_INTR_PER_VF; - } else { - dev_err(dev, "Only %d MSI-X interrupts available for SR-IOV. Not enough to support minimum of %d MSI-X interrupts per VF for %d VFs\n", - msix_avail_for_sriov, ICE_MIN_INTR_PER_VF, - pf->num_alloc_vfs); - return -EIO; - } - - /* determine queue resources per VF */ - num_txq = ice_determine_res(pf, ice_get_avail_txq_count(pf), - min_t(u16, - num_msix_per_vf - ICE_NONQ_VECS_VF, - ICE_MAX_RSS_QS_PER_VF), - ICE_MIN_QS_PER_VF); - - num_rxq = ice_determine_res(pf, ice_get_avail_rxq_count(pf), - min_t(u16, - num_msix_per_vf - ICE_NONQ_VECS_VF, - ICE_MAX_RSS_QS_PER_VF), - ICE_MIN_QS_PER_VF); - - if (!num_txq || !num_rxq) { - dev_err(dev, "Not enough queues to support minimum of %d queue pairs per VF for %d VFs\n", - ICE_MIN_QS_PER_VF, pf->num_alloc_vfs); - return -EIO; - } - - if (ice_sriov_set_msix_res(pf, num_msix_per_vf * pf->num_alloc_vfs)) { - dev_err(dev, "Unable to set MSI-X resources for %d VFs\n", - pf->num_alloc_vfs); - return -EINVAL; - } - - /* only allow equal Tx/Rx queue count (i.e. queue pairs) */ - pf->num_qps_per_vf = min_t(int, num_txq, num_rxq); - pf->num_msix_per_vf = num_msix_per_vf; - dev_info(dev, "Enabling %d VFs with %d vectors and %d queues per VF\n", - pf->num_alloc_vfs, pf->num_msix_per_vf, pf->num_qps_per_vf); - - return 0; -} - -/** - * ice_clear_vf_reset_trigger - enable VF to access hardware - * @vf: VF to enabled hardware access for - */ -static void ice_clear_vf_reset_trigger(struct ice_vf *vf) -{ - struct ice_hw *hw = &vf->pf->hw; - u32 reg; - - reg = rd32(hw, VPGEN_VFRTRIG(vf->vf_id)); - reg &= ~VPGEN_VFRTRIG_VFSWR_M; - wr32(hw, VPGEN_VFRTRIG(vf->vf_id), reg); - ice_flush(hw); -} - -static int -ice_vf_set_vsi_promisc(struct ice_vf *vf, struct ice_vsi *vsi, u8 promisc_m) -{ - struct ice_hw *hw = &vsi->back->hw; - int status; - - if (vf->port_vlan_info) - status = ice_fltr_set_vsi_promisc(hw, vsi->idx, promisc_m, - vf->port_vlan_info & VLAN_VID_MASK); - else if (vsi->num_vlan > 1) - status = ice_fltr_set_vlan_vsi_promisc(hw, vsi, promisc_m); - else - status = ice_fltr_set_vsi_promisc(hw, vsi->idx, promisc_m, 0); - - if (status && status != -EEXIST) { - dev_err(ice_pf_to_dev(vsi->back), "enable Tx/Rx filter promiscuous mode on VF-%u failed, error: %d\n", - vf->vf_id, status); - return status; - } - - return 0; -} - -static int -ice_vf_clear_vsi_promisc(struct ice_vf *vf, struct ice_vsi *vsi, u8 promisc_m) -{ - struct ice_hw *hw = &vsi->back->hw; - int status; - - if (vf->port_vlan_info) - status = ice_fltr_clear_vsi_promisc(hw, vsi->idx, promisc_m, - vf->port_vlan_info & VLAN_VID_MASK); - else if (vsi->num_vlan > 1) - status = ice_fltr_clear_vlan_vsi_promisc(hw, vsi, promisc_m); - else - status = ice_fltr_clear_vsi_promisc(hw, vsi->idx, promisc_m, 0); - - if (status && status != -ENOENT) { - dev_err(ice_pf_to_dev(vsi->back), "disable Tx/Rx filter promiscuous mode on VF-%u failed, error: %d\n", - vf->vf_id, status); - return status; - } - - return 0; -} - -static void ice_vf_clear_counters(struct ice_vf *vf) -{ - struct ice_vsi *vsi = ice_get_vf_vsi(vf); - - vf->num_mac = 0; - vsi->num_vlan = 0; - memset(&vf->mdd_tx_events, 0, sizeof(vf->mdd_tx_events)); - memset(&vf->mdd_rx_events, 0, sizeof(vf->mdd_rx_events)); -} - -/** - * ice_vf_pre_vsi_rebuild - tasks to be done prior to VSI rebuild - * @vf: VF to perform pre VSI rebuild tasks - * - * These tasks are items that don't need to be amortized since they are most - * likely called in a for loop with all VF(s) in the reset_all_vfs() case. - */ -static void ice_vf_pre_vsi_rebuild(struct ice_vf *vf) -{ - ice_vf_clear_counters(vf); - ice_clear_vf_reset_trigger(vf); -} - -/** - * ice_vf_rebuild_aggregator_node_cfg - rebuild aggregator node config - * @vsi: Pointer to VSI - * - * This function moves VSI into corresponding scheduler aggregator node - * based on cached value of "aggregator node info" per VSI - */ -static void ice_vf_rebuild_aggregator_node_cfg(struct ice_vsi *vsi) -{ - struct ice_pf *pf = vsi->back; - struct device *dev; - int status; - - if (!vsi->agg_node) - return; - - dev = ice_pf_to_dev(pf); - if (vsi->agg_node->num_vsis == ICE_MAX_VSIS_IN_AGG_NODE) { - dev_dbg(dev, - "agg_id %u already has reached max_num_vsis %u\n", - vsi->agg_node->agg_id, vsi->agg_node->num_vsis); - return; - } - - status = ice_move_vsi_to_agg(pf->hw.port_info, vsi->agg_node->agg_id, - vsi->idx, vsi->tc_cfg.ena_tc); - if (status) - dev_dbg(dev, "unable to move VSI idx %u into aggregator %u node", - vsi->idx, vsi->agg_node->agg_id); - else - vsi->agg_node->num_vsis++; -} - -/** - * ice_vf_rebuild_host_cfg - host admin configuration is persistent across reset - * @vf: VF to rebuild host configuration on - */ -static void ice_vf_rebuild_host_cfg(struct ice_vf *vf) -{ - struct device *dev = ice_pf_to_dev(vf->pf); - struct ice_vsi *vsi = ice_get_vf_vsi(vf); - - ice_vf_set_host_trust_cfg(vf); - - if (ice_vf_rebuild_host_mac_cfg(vf)) - dev_err(dev, "failed to rebuild default MAC configuration for VF %d\n", - vf->vf_id); - - if (ice_vf_rebuild_host_vlan_cfg(vf)) - dev_err(dev, "failed to rebuild VLAN configuration for VF %u\n", - vf->vf_id); - - if (ice_vf_rebuild_host_tx_rate_cfg(vf)) - dev_err(dev, "failed to rebuild Tx rate limiting configuration for VF %u\n", - vf->vf_id); - - /* rebuild aggregator node config for main VF VSI */ - ice_vf_rebuild_aggregator_node_cfg(vsi); -} - -/** - * ice_vf_rebuild_vsi_with_release - release and setup the VF's VSI - * @vf: VF to release and setup the VSI for - * - * This is only called when a single VF is being reset (i.e. VFR, VFLR, host VF - * configuration change, etc.). - */ -static int ice_vf_rebuild_vsi_with_release(struct ice_vf *vf) -{ - ice_vf_vsi_release(vf); - if (!ice_vf_vsi_setup(vf)) - return -ENOMEM; - - return 0; -} - -/** - * ice_vf_rebuild_vsi - rebuild the VF's VSI - * @vf: VF to rebuild the VSI for - * - * This is only called when all VF(s) are being reset (i.e. PCIe Reset on the - * host, PFR, CORER, etc.). - */ -static int ice_vf_rebuild_vsi(struct ice_vf *vf) -{ - struct ice_vsi *vsi = ice_get_vf_vsi(vf); - struct ice_pf *pf = vf->pf; - - if (ice_vsi_rebuild(vsi, true)) { - dev_err(ice_pf_to_dev(pf), "failed to rebuild VF %d VSI\n", - vf->vf_id); - return -EIO; - } - /* vsi->idx will remain the same in this case so don't update - * vf->lan_vsi_idx - */ - vsi->vsi_num = ice_get_hw_vsi_num(&pf->hw, vsi->idx); - vf->lan_vsi_num = vsi->vsi_num; - - return 0; -} - -/** - * ice_vf_set_initialized - VF is ready for VIRTCHNL communication - * @vf: VF to set in initialized state - * - * After this function the VF will be ready to receive/handle the - * VIRTCHNL_OP_GET_VF_RESOURCES message - */ -static void ice_vf_set_initialized(struct ice_vf *vf) -{ - ice_set_vf_state_qs_dis(vf); - clear_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states); - clear_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states); - clear_bit(ICE_VF_STATE_DIS, vf->vf_states); - set_bit(ICE_VF_STATE_INIT, vf->vf_states); -} - -/** - * ice_vf_post_vsi_rebuild - tasks to do after the VF's VSI have been rebuilt - * @vf: VF to perform tasks on - */ -static void ice_vf_post_vsi_rebuild(struct ice_vf *vf) -{ - struct ice_pf *pf = vf->pf; - struct ice_hw *hw; - - hw = &pf->hw; - - ice_vf_rebuild_host_cfg(vf); - - ice_vf_set_initialized(vf); - ice_ena_vf_mappings(vf); - wr32(hw, VFGEN_RSTAT(vf->vf_id), VIRTCHNL_VFR_VFACTIVE); -} - -/** - * ice_reset_all_vfs - reset all allocated VFs in one go - * @pf: pointer to the PF structure - * @is_vflr: true if VFLR was issued, false if not - * - * First, tell the hardware to reset each VF, then do all the waiting in one - * chunk, and finally finish restoring each VF after the wait. This is useful - * during PF routines which need to reset all VFs, as otherwise it must perform - * these resets in a serialized fashion. - * - * Returns true if any VFs were reset, and false otherwise. - */ -bool ice_reset_all_vfs(struct ice_pf *pf, bool is_vflr) -{ - struct device *dev = ice_pf_to_dev(pf); - struct ice_hw *hw = &pf->hw; - struct ice_vf *vf; - int v, i; - - /* If we don't have any VFs, then there is nothing to reset */ - if (!pf->num_alloc_vfs) - return false; - - /* clear all malicious info if the VFs are getting reset */ - ice_for_each_vf(pf, i) - if (ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->malvfs, ICE_MAX_VF_COUNT, i)) - dev_dbg(dev, "failed to clear malicious VF state for VF %u\n", i); - - /* If VFs have been disabled, there is no need to reset */ - if (test_and_set_bit(ICE_VF_DIS, pf->state)) - return false; - - /* Begin reset on all VFs at once */ - ice_for_each_vf(pf, v) - ice_trigger_vf_reset(&pf->vf[v], is_vflr, true); - - /* HW requires some time to make sure it can flush the FIFO for a VF - * when it resets it. Poll the VPGEN_VFRSTAT register for each VF in - * sequence to make sure that it has completed. We'll keep track of - * the VFs using a simple iterator that increments once that VF has - * finished resetting. - */ - for (i = 0, v = 0; i < 10 && v < pf->num_alloc_vfs; i++) { - /* Check each VF in sequence */ - while (v < pf->num_alloc_vfs) { - u32 reg; - - vf = &pf->vf[v]; - reg = rd32(hw, VPGEN_VFRSTAT(vf->vf_id)); - if (!(reg & VPGEN_VFRSTAT_VFRD_M)) { - /* only delay if the check failed */ - usleep_range(10, 20); - break; - } - - /* If the current VF has finished resetting, move on - * to the next VF in sequence. - */ - v++; - } - } - - /* Display a warning if at least one VF didn't manage to reset in - * time, but continue on with the operation. - */ - if (v < pf->num_alloc_vfs) - dev_warn(dev, "VF reset check timeout\n"); - - /* free VF resources to begin resetting the VSI state */ - ice_for_each_vf(pf, v) { - vf = &pf->vf[v]; - - mutex_lock(&vf->cfg_lock); - - vf->driver_caps = 0; - ice_vc_set_default_allowlist(vf); - - ice_vf_fdir_exit(vf); - ice_vf_fdir_init(vf); - /* clean VF control VSI when resetting VFs since it should be - * setup only when VF creates its first FDIR rule. - */ - if (vf->ctrl_vsi_idx != ICE_NO_VSI) - ice_vf_ctrl_invalidate_vsi(vf); - - ice_vf_pre_vsi_rebuild(vf); - ice_vf_rebuild_vsi(vf); - ice_vf_post_vsi_rebuild(vf); - - mutex_unlock(&vf->cfg_lock); - } - - if (ice_is_eswitch_mode_switchdev(pf)) - if (ice_eswitch_rebuild(pf)) - dev_warn(dev, "eswitch rebuild failed\n"); - - ice_flush(hw); - clear_bit(ICE_VF_DIS, pf->state); - - return true; -} - -/** - * ice_is_vf_disabled - * @vf: pointer to the VF info - * - * Returns true if the PF or VF is disabled, false otherwise. - */ -bool ice_is_vf_disabled(struct ice_vf *vf) -{ - struct ice_pf *pf = vf->pf; - - /* If the PF has been disabled, there is no need resetting VF until - * PF is active again. Similarly, if the VF has been disabled, this - * means something else is resetting the VF, so we shouldn't continue. - * Otherwise, set disable VF state bit for actual reset, and continue. - */ - return (test_bit(ICE_VF_DIS, pf->state) || - test_bit(ICE_VF_STATE_DIS, vf->vf_states)); -} - -/** - * ice_reset_vf - Reset a particular VF - * @vf: pointer to the VF structure - * @is_vflr: true if VFLR was issued, false if not - * - * Returns true if the VF is currently in reset, resets successfully, or resets - * are disabled and false otherwise. - */ -bool ice_reset_vf(struct ice_vf *vf, bool is_vflr) -{ - struct ice_pf *pf = vf->pf; - struct ice_vsi *vsi; - struct device *dev; - struct ice_hw *hw; - bool rsd = false; - u8 promisc_m; - u32 reg; - int i; - - lockdep_assert_held(&vf->cfg_lock); - - dev = ice_pf_to_dev(pf); - - if (test_bit(ICE_VF_RESETS_DISABLED, pf->state)) { - dev_dbg(dev, "Trying to reset VF %d, but all VF resets are disabled\n", - vf->vf_id); - return true; - } - - if (ice_is_vf_disabled(vf)) { - dev_dbg(dev, "VF is already disabled, there is no need for resetting it, telling VM, all is fine %d\n", - vf->vf_id); - return true; - } - - /* Set VF disable bit state here, before triggering reset */ - set_bit(ICE_VF_STATE_DIS, vf->vf_states); - ice_trigger_vf_reset(vf, is_vflr, false); - - vsi = ice_get_vf_vsi(vf); - - ice_dis_vf_qs(vf); - - /* Call Disable LAN Tx queue AQ whether or not queues are - * enabled. This is needed for successful completion of VFR. - */ - ice_dis_vsi_txq(vsi->port_info, vsi->idx, 0, 0, NULL, NULL, - NULL, ICE_VF_RESET, vf->vf_id, NULL); - - hw = &pf->hw; - /* poll VPGEN_VFRSTAT reg to make sure - * that reset is complete - */ - for (i = 0; i < 10; i++) { - /* VF reset requires driver to first reset the VF and then - * poll the status register to make sure that the reset - * completed successfully. - */ - reg = rd32(hw, VPGEN_VFRSTAT(vf->vf_id)); - if (reg & VPGEN_VFRSTAT_VFRD_M) { - rsd = true; - break; - } - - /* only sleep if the reset is not done */ - usleep_range(10, 20); - } - - vf->driver_caps = 0; - ice_vc_set_default_allowlist(vf); - - /* Display a warning if VF didn't manage to reset in time, but need to - * continue on with the operation. - */ - if (!rsd) - dev_warn(dev, "VF reset check timeout on VF %d\n", vf->vf_id); - - /* disable promiscuous modes in case they were enabled - * ignore any error if disabling process failed - */ - if (test_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states) || - test_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states)) { - if (vf->port_vlan_info || vsi->num_vlan) - promisc_m = ICE_UCAST_VLAN_PROMISC_BITS; - else - promisc_m = ICE_UCAST_PROMISC_BITS; - - if (ice_vf_clear_vsi_promisc(vf, vsi, promisc_m)) - dev_err(dev, "disabling promiscuous mode failed\n"); - } - - ice_eswitch_del_vf_mac_rule(vf); - - ice_vf_fdir_exit(vf); - ice_vf_fdir_init(vf); - /* clean VF control VSI when resetting VF since it should be setup - * only when VF creates its first FDIR rule. - */ - if (vf->ctrl_vsi_idx != ICE_NO_VSI) - ice_vf_ctrl_vsi_release(vf); - - ice_vf_pre_vsi_rebuild(vf); - - if (ice_vf_rebuild_vsi_with_release(vf)) { - dev_err(dev, "Failed to release and setup the VF%u's VSI\n", vf->vf_id); - return false; - } - - ice_vf_post_vsi_rebuild(vf); - vsi = ice_get_vf_vsi(vf); - ice_eswitch_update_repr(vsi); - ice_eswitch_replay_vf_mac_rule(vf); - - /* if the VF has been reset allow it to come up again */ - if (ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->malvfs, ICE_MAX_VF_COUNT, vf->vf_id)) - dev_dbg(dev, "failed to clear malicious VF state for VF %u\n", i); - - return true; -} - /** * ice_vc_notify_link_state - Inform all VFs on a PF of link status * @pf: pointer to the PF structure */ void ice_vc_notify_link_state(struct ice_pf *pf) { - int i; + struct ice_vf *vf; + unsigned int bkt; - ice_for_each_vf(pf, i) - ice_vc_notify_vf_link_state(&pf->vf[i]); + mutex_lock(&pf->vfs.table_lock); + ice_for_each_vf(pf, bkt, vf) + ice_vc_notify_vf_link_state(vf); + mutex_unlock(&pf->vfs.table_lock); } /** @@ -1690,7 +272,7 @@ void ice_vc_notify_reset(struct ice_pf *pf) { struct virtchnl_pf_event pfe; - if (!pf->num_alloc_vfs) + if (!ice_has_vfs(pf)) return; pfe.event = VIRTCHNL_EVENT_RESET_IMPENDING; @@ -1699,462 +281,6 @@ void ice_vc_notify_reset(struct ice_pf *pf) (u8 *)&pfe, sizeof(struct virtchnl_pf_event)); } -/** - * ice_vc_notify_vf_reset - Notify VF of a reset event - * @vf: pointer to the VF structure - */ -static void ice_vc_notify_vf_reset(struct ice_vf *vf) -{ - struct virtchnl_pf_event pfe; - struct ice_pf *pf; - - if (!vf) - return; - - pf = vf->pf; - if (ice_validate_vf_id(pf, vf->vf_id)) - return; - - /* Bail out if VF is in disabled state, neither initialized, nor active - * state - otherwise proceed with notifications - */ - if ((!test_bit(ICE_VF_STATE_INIT, vf->vf_states) && - !test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) || - test_bit(ICE_VF_STATE_DIS, vf->vf_states)) - return; - - pfe.event = VIRTCHNL_EVENT_RESET_IMPENDING; - pfe.severity = PF_EVENT_SEVERITY_CERTAIN_DOOM; - ice_aq_send_msg_to_vf(&pf->hw, vf->vf_id, VIRTCHNL_OP_EVENT, - VIRTCHNL_STATUS_SUCCESS, (u8 *)&pfe, sizeof(pfe), - NULL); -} - -/** - * ice_init_vf_vsi_res - initialize/setup VF VSI resources - * @vf: VF to initialize/setup the VSI for - * - * This function creates a VSI for the VF, adds a VLAN 0 filter, and sets up the - * VF VSI's broadcast filter and is only used during initial VF creation. - */ -static int ice_init_vf_vsi_res(struct ice_vf *vf) -{ - struct ice_pf *pf = vf->pf; - u8 broadcast[ETH_ALEN]; - struct ice_vsi *vsi; - struct device *dev; - int err; - - vf->first_vector_idx = ice_calc_vf_first_vector_idx(pf, vf); - - dev = ice_pf_to_dev(pf); - vsi = ice_vf_vsi_setup(vf); - if (!vsi) - return -ENOMEM; - - err = ice_vsi_add_vlan(vsi, 0, ICE_FWD_TO_VSI); - if (err) { - dev_warn(dev, "Failed to add VLAN 0 filter for VF %d\n", - vf->vf_id); - goto release_vsi; - } - - eth_broadcast_addr(broadcast); - err = ice_fltr_add_mac(vsi, broadcast, ICE_FWD_TO_VSI); - if (err) { - dev_err(dev, "Failed to add broadcast MAC filter for VF %d, error %d\n", - vf->vf_id, err); - goto release_vsi; - } - - vf->num_mac = 1; - - return 0; - -release_vsi: - ice_vf_vsi_release(vf); - return err; -} - -/** - * ice_start_vfs - start VFs so they are ready to be used by SR-IOV - * @pf: PF the VFs are associated with - */ -static int ice_start_vfs(struct ice_pf *pf) -{ - struct ice_hw *hw = &pf->hw; - int retval, i; - - ice_for_each_vf(pf, i) { - struct ice_vf *vf = &pf->vf[i]; - - ice_clear_vf_reset_trigger(vf); - - retval = ice_init_vf_vsi_res(vf); - if (retval) { - dev_err(ice_pf_to_dev(pf), "Failed to initialize VSI resources for VF %d, error %d\n", - vf->vf_id, retval); - goto teardown; - } - - set_bit(ICE_VF_STATE_INIT, vf->vf_states); - ice_ena_vf_mappings(vf); - wr32(hw, VFGEN_RSTAT(vf->vf_id), VIRTCHNL_VFR_VFACTIVE); - } - - ice_flush(hw); - return 0; - -teardown: - for (i = i - 1; i >= 0; i--) { - struct ice_vf *vf = &pf->vf[i]; - - ice_dis_vf_mappings(vf); - ice_vf_vsi_release(vf); - } - - return retval; -} - -/** - * ice_set_dflt_settings_vfs - set VF defaults during initialization/creation - * @pf: PF holding reference to all VFs for default configuration - */ -static void ice_set_dflt_settings_vfs(struct ice_pf *pf) -{ - int i; - - ice_for_each_vf(pf, i) { - struct ice_vf *vf = &pf->vf[i]; - - vf->pf = pf; - vf->vf_id = i; - vf->vf_sw_id = pf->first_sw; - /* assign default capabilities */ - set_bit(ICE_VIRTCHNL_VF_CAP_L2, &vf->vf_caps); - vf->spoofchk = true; - vf->num_vf_qs = pf->num_qps_per_vf; - ice_vc_set_default_allowlist(vf); - - /* ctrl_vsi_idx will be set to a valid value only when VF - * creates its first fdir rule. - */ - ice_vf_ctrl_invalidate_vsi(vf); - ice_vf_fdir_init(vf); - - ice_vc_set_dflt_vf_ops(&vf->vc_ops); - - mutex_init(&vf->cfg_lock); - } -} - -/** - * ice_alloc_vfs - allocate num_vfs in the PF structure - * @pf: PF to store the allocated VFs in - * @num_vfs: number of VFs to allocate - */ -static int ice_alloc_vfs(struct ice_pf *pf, int num_vfs) -{ - struct ice_vf *vfs; - - vfs = devm_kcalloc(ice_pf_to_dev(pf), num_vfs, sizeof(*vfs), - GFP_KERNEL); - if (!vfs) - return -ENOMEM; - - pf->vf = vfs; - pf->num_alloc_vfs = num_vfs; - - return 0; -} - -/** - * ice_ena_vfs - enable VFs so they are ready to be used - * @pf: pointer to the PF structure - * @num_vfs: number of VFs to enable - */ -static int ice_ena_vfs(struct ice_pf *pf, u16 num_vfs) -{ - struct device *dev = ice_pf_to_dev(pf); - struct ice_hw *hw = &pf->hw; - int ret; - - /* Disable global interrupt 0 so we don't try to handle the VFLR. */ - wr32(hw, GLINT_DYN_CTL(pf->oicr_idx), - ICE_ITR_NONE << GLINT_DYN_CTL_ITR_INDX_S); - set_bit(ICE_OICR_INTR_DIS, pf->state); - ice_flush(hw); - - ret = pci_enable_sriov(pf->pdev, num_vfs); - if (ret) { - pf->num_alloc_vfs = 0; - goto err_unroll_intr; - } - - ret = ice_alloc_vfs(pf, num_vfs); - if (ret) - goto err_pci_disable_sriov; - - if (ice_set_per_vf_res(pf)) { - dev_err(dev, "Not enough resources for %d VFs, try with fewer number of VFs\n", - num_vfs); - ret = -ENOSPC; - goto err_unroll_sriov; - } - - ice_set_dflt_settings_vfs(pf); - - if (ice_start_vfs(pf)) { - dev_err(dev, "Failed to start VF(s)\n"); - ret = -EAGAIN; - goto err_unroll_sriov; - } - - clear_bit(ICE_VF_DIS, pf->state); - - ret = ice_eswitch_configure(pf); - if (ret) - goto err_unroll_sriov; - - /* rearm global interrupts */ - if (test_and_clear_bit(ICE_OICR_INTR_DIS, pf->state)) - ice_irq_dynamic_ena(hw, NULL, NULL); - - return 0; - -err_unroll_sriov: - devm_kfree(dev, pf->vf); - pf->vf = NULL; - pf->num_alloc_vfs = 0; -err_pci_disable_sriov: - pci_disable_sriov(pf->pdev); -err_unroll_intr: - /* rearm interrupts here */ - ice_irq_dynamic_ena(hw, NULL, NULL); - clear_bit(ICE_OICR_INTR_DIS, pf->state); - return ret; -} - -/** - * ice_pci_sriov_ena - Enable or change number of VFs - * @pf: pointer to the PF structure - * @num_vfs: number of VFs to allocate - * - * Returns 0 on success and negative on failure - */ -static int ice_pci_sriov_ena(struct ice_pf *pf, int num_vfs) -{ - int pre_existing_vfs = pci_num_vf(pf->pdev); - struct device *dev = ice_pf_to_dev(pf); - int err; - - if (pre_existing_vfs && pre_existing_vfs != num_vfs) - ice_free_vfs(pf); - else if (pre_existing_vfs && pre_existing_vfs == num_vfs) - return 0; - - if (num_vfs > pf->num_vfs_supported) { - dev_err(dev, "Can't enable %d VFs, max VFs supported is %d\n", - num_vfs, pf->num_vfs_supported); - return -EOPNOTSUPP; - } - - dev_info(dev, "Enabling %d VFs\n", num_vfs); - err = ice_ena_vfs(pf, num_vfs); - if (err) { - dev_err(dev, "Failed to enable SR-IOV: %d\n", err); - return err; - } - - set_bit(ICE_FLAG_SRIOV_ENA, pf->flags); - return 0; -} - -/** - * ice_check_sriov_allowed - check if SR-IOV is allowed based on various checks - * @pf: PF to enabled SR-IOV on - */ -static int ice_check_sriov_allowed(struct ice_pf *pf) -{ - struct device *dev = ice_pf_to_dev(pf); - - if (!test_bit(ICE_FLAG_SRIOV_CAPABLE, pf->flags)) { - dev_err(dev, "This device is not capable of SR-IOV\n"); - return -EOPNOTSUPP; - } - - if (ice_is_safe_mode(pf)) { - dev_err(dev, "SR-IOV cannot be configured - Device is in Safe Mode\n"); - return -EOPNOTSUPP; - } - - if (!ice_pf_state_is_nominal(pf)) { - dev_err(dev, "Cannot enable SR-IOV, device not ready\n"); - return -EBUSY; - } - - return 0; -} - -/** - * ice_sriov_configure - Enable or change number of VFs via sysfs - * @pdev: pointer to a pci_dev structure - * @num_vfs: number of VFs to allocate or 0 to free VFs - * - * This function is called when the user updates the number of VFs in sysfs. On - * success return whatever num_vfs was set to by the caller. Return negative on - * failure. - */ -int ice_sriov_configure(struct pci_dev *pdev, int num_vfs) -{ - struct ice_pf *pf = pci_get_drvdata(pdev); - struct device *dev = ice_pf_to_dev(pf); - int err; - - err = ice_check_sriov_allowed(pf); - if (err) - return err; - - if (!num_vfs) { - if (!pci_vfs_assigned(pdev)) { - ice_mbx_deinit_snapshot(&pf->hw); - ice_free_vfs(pf); - if (pf->lag) - ice_enable_lag(pf->lag); - return 0; - } - - dev_err(dev, "can't free VFs because some are assigned to VMs.\n"); - return -EBUSY; - } - - err = ice_mbx_init_snapshot(&pf->hw, num_vfs); - if (err) - return err; - - err = ice_pci_sriov_ena(pf, num_vfs); - if (err) { - ice_mbx_deinit_snapshot(&pf->hw); - return err; - } - - if (pf->lag) - ice_disable_lag(pf->lag); - return num_vfs; -} - -/** - * ice_process_vflr_event - Free VF resources via IRQ calls - * @pf: pointer to the PF structure - * - * called from the VFLR IRQ handler to - * free up VF resources and state variables - */ -void ice_process_vflr_event(struct ice_pf *pf) -{ - struct ice_hw *hw = &pf->hw; - unsigned int vf_id; - u32 reg; - - if (!test_and_clear_bit(ICE_VFLR_EVENT_PENDING, pf->state) || - !pf->num_alloc_vfs) - return; - - ice_for_each_vf(pf, vf_id) { - struct ice_vf *vf = &pf->vf[vf_id]; - u32 reg_idx, bit_idx; - - reg_idx = (hw->func_caps.vf_base_id + vf_id) / 32; - bit_idx = (hw->func_caps.vf_base_id + vf_id) % 32; - /* read GLGEN_VFLRSTAT register to find out the flr VFs */ - reg = rd32(hw, GLGEN_VFLRSTAT(reg_idx)); - if (reg & BIT(bit_idx)) { - /* GLGEN_VFLRSTAT bit will be cleared in ice_reset_vf */ - mutex_lock(&vf->cfg_lock); - ice_reset_vf(vf, true); - mutex_unlock(&vf->cfg_lock); - } - } -} - -/** - * ice_vc_reset_vf - Perform software reset on the VF after informing the AVF - * @vf: pointer to the VF info - */ -static void ice_vc_reset_vf(struct ice_vf *vf) -{ - ice_vc_notify_vf_reset(vf); - ice_reset_vf(vf, false); -} - -/** - * ice_get_vf_from_pfq - get the VF who owns the PF space queue passed in - * @pf: PF used to index all VFs - * @pfq: queue index relative to the PF's function space - * - * If no VF is found who owns the pfq then return NULL, otherwise return a - * pointer to the VF who owns the pfq - */ -static struct ice_vf *ice_get_vf_from_pfq(struct ice_pf *pf, u16 pfq) -{ - unsigned int vf_id; - - ice_for_each_vf(pf, vf_id) { - struct ice_vf *vf = &pf->vf[vf_id]; - struct ice_vsi *vsi; - u16 rxq_idx; - - vsi = ice_get_vf_vsi(vf); - - ice_for_each_rxq(vsi, rxq_idx) - if (vsi->rxq_map[rxq_idx] == pfq) - return vf; - } - - return NULL; -} - -/** - * ice_globalq_to_pfq - convert from global queue index to PF space queue index - * @pf: PF used for conversion - * @globalq: global queue index used to convert to PF space queue index - */ -static u32 ice_globalq_to_pfq(struct ice_pf *pf, u32 globalq) -{ - return globalq - pf->hw.func_caps.common_cap.rxq_first_id; -} - -/** - * ice_vf_lan_overflow_event - handle LAN overflow event for a VF - * @pf: PF that the LAN overflow event happened on - * @event: structure holding the event information for the LAN overflow event - * - * Determine if the LAN overflow event was caused by a VF queue. If it was not - * caused by a VF, do nothing. If a VF caused this LAN overflow event trigger a - * reset on the offending VF. - */ -void -ice_vf_lan_overflow_event(struct ice_pf *pf, struct ice_rq_event_info *event) -{ - u32 gldcb_rtctq, queue; - struct ice_vf *vf; - - gldcb_rtctq = le32_to_cpu(event->desc.params.lan_overflow.prtdcb_ruptq); - dev_dbg(ice_pf_to_dev(pf), "GLDCB_RTCTQ: 0x%08x\n", gldcb_rtctq); - - /* event returns device global Rx queue number */ - queue = (gldcb_rtctq & GLDCB_RTCTQ_RXQNUM_M) >> - GLDCB_RTCTQ_RXQNUM_S; - - vf = ice_get_vf_from_pfq(pf, ice_globalq_to_pfq(pf, queue)); - if (!vf) - return; - - mutex_lock(&vf->cfg_lock); - ice_vc_reset_vf(vf); - mutex_unlock(&vf->cfg_lock); -} - /** * ice_vc_send_msg_to_vf - Send message to VF * @vf: pointer to the VF info @@ -2173,13 +299,7 @@ ice_vc_send_msg_to_vf(struct ice_vf *vf, u32 v_opcode, struct ice_pf *pf; int aq_ret; - if (!vf) - return -EINVAL; - pf = vf->pf; - if (ice_validate_vf_id(pf, vf->vf_id)) - return -EINVAL; - dev = ice_pf_to_dev(pf); aq_ret = ice_aq_send_msg_to_vf(&pf->hw, vf->vf_id, v_opcode, v_retval, @@ -2233,7 +353,7 @@ static u16 ice_vc_get_max_frame_size(struct ice_vf *vf) max_frame_size = pi->phy.link_info.max_frame_size; - if (vf->port_vlan_info) + if (ice_vf_is_port_vlan_ena(vf)) max_frame_size -= VLAN_HLEN; return max_frame_size; @@ -2250,12 +370,12 @@ static int ice_vc_get_vf_res_msg(struct ice_vf *vf, u8 *msg) { enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; struct virtchnl_vf_resource *vfres = NULL; - struct ice_pf *pf = vf->pf; + struct ice_hw *hw = &vf->pf->hw; struct ice_vsi *vsi; int len = 0; int ret; - if (ice_check_vf_init(pf, vf)) { + if (ice_check_vf_init(vf)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto err; } @@ -2282,8 +402,33 @@ static int ice_vc_get_vf_res_msg(struct ice_vf *vf, u8 *msg) goto err; } - if (!vsi->info.pvid) - vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_VLAN; + if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_VLAN_V2) { + /* VLAN offloads based on current device configuration */ + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_VLAN_V2; + } else if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_VLAN) { + /* allow VF to negotiate VIRTCHNL_VF_OFFLOAD explicitly for + * these two conditions, which amounts to guest VLAN filtering + * and offloads being based on the inner VLAN or the + * inner/single VLAN respectively and don't allow VF to + * negotiate VIRTCHNL_VF_OFFLOAD in any other cases + */ + if (ice_is_dvm_ena(hw) && ice_vf_is_port_vlan_ena(vf)) { + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_VLAN; + } else if (!ice_is_dvm_ena(hw) && + !ice_vf_is_port_vlan_ena(vf)) { + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_VLAN; + /* configure backward compatible support for VFs that + * only support VIRTCHNL_VF_OFFLOAD_VLAN, the PF is + * configured in SVM, and no port VLAN is configured + */ + ice_vf_vsi_cfg_svm_legacy_vlan_mode(vsi); + } else if (ice_is_dvm_ena(hw)) { + /* configure software offloaded VLAN support when DVM + * is enabled, but no port VLAN is enabled + */ + ice_vf_vsi_cfg_dvm_legacy_vlan_mode(vsi); + } + } if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PF) { vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PF; @@ -2327,7 +472,7 @@ static int ice_vc_get_vf_res_msg(struct ice_vf *vf, u8 *msg) vfres->num_vsis = 1; /* Tx and Rx queue are equal for VF */ vfres->num_queue_pairs = vsi->num_txq; - vfres->max_vectors = pf->num_msix_per_vf; + vfres->max_vectors = vf->pf->vfs.num_msix_per; vfres->rss_key_size = ICE_VSIQF_HKEY_ARRAY_SIZE; vfres->rss_lut_size = ICE_VSIQF_HLUT_ARRAY_SIZE; vfres->max_mtu = ice_vc_get_max_frame_size(vf); @@ -2366,7 +511,7 @@ static int ice_vc_get_vf_res_msg(struct ice_vf *vf, u8 *msg) static void ice_vc_reset_vf_msg(struct ice_vf *vf) { if (test_bit(ICE_VF_STATE_INIT, vf->vf_states)) - ice_reset_vf(vf, false); + ice_reset_vf(vf, 0); } /** @@ -2401,7 +546,7 @@ bool ice_vc_isvalid_vsi_id(struct ice_vf *vf, u16 vsi_id) vsi = ice_find_vsi_from_id(pf, vsi_id); - return (vsi && (vsi->vf_id == vf->vf_id)); + return (vsi && (vsi->vf == vf)); } /** @@ -2832,150 +977,6 @@ static int ice_vc_config_rss_lut(struct ice_vf *vf, u8 *msg) NULL, 0); } -/** - * ice_wait_on_vf_reset - poll to make sure a given VF is ready after reset - * @vf: The VF being resseting - * - * The max poll time is about ~800ms, which is about the maximum time it takes - * for a VF to be reset and/or a VF driver to be removed. - */ -static void ice_wait_on_vf_reset(struct ice_vf *vf) -{ - int i; - - for (i = 0; i < ICE_MAX_VF_RESET_TRIES; i++) { - if (test_bit(ICE_VF_STATE_INIT, vf->vf_states)) - break; - msleep(ICE_MAX_VF_RESET_SLEEP_MS); - } -} - -/** - * ice_check_vf_ready_for_cfg - check if VF is ready to be configured/queried - * @vf: VF to check if it's ready to be configured/queried - * - * The purpose of this function is to make sure the VF is not in reset, not - * disabled, and initialized so it can be configured and/or queried by a host - * administrator. - */ -int ice_check_vf_ready_for_cfg(struct ice_vf *vf) -{ - struct ice_pf *pf; - - ice_wait_on_vf_reset(vf); - - if (ice_is_vf_disabled(vf)) - return -EINVAL; - - pf = vf->pf; - if (ice_check_vf_init(pf, vf)) - return -EBUSY; - - return 0; -} - -/** - * ice_set_vf_spoofchk - * @netdev: network interface device structure - * @vf_id: VF identifier - * @ena: flag to enable or disable feature - * - * Enable or disable VF spoof checking - */ -int ice_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool ena) -{ - struct ice_netdev_priv *np = netdev_priv(netdev); - struct ice_pf *pf = np->vsi->back; - struct ice_vsi_ctx *ctx; - struct ice_vsi *vf_vsi; - struct device *dev; - struct ice_vf *vf; - int ret; - - dev = ice_pf_to_dev(pf); - if (ice_validate_vf_id(pf, vf_id)) - return -EINVAL; - - vf = &pf->vf[vf_id]; - ret = ice_check_vf_ready_for_cfg(vf); - if (ret) - return ret; - - vf_vsi = ice_get_vf_vsi(vf); - if (!vf_vsi) { - netdev_err(netdev, "VSI %d for VF %d is null\n", - vf->lan_vsi_idx, vf->vf_id); - return -EINVAL; - } - - if (vf_vsi->type != ICE_VSI_VF) { - netdev_err(netdev, "Type %d of VSI %d for VF %d is no ICE_VSI_VF\n", - vf_vsi->type, vf_vsi->vsi_num, vf->vf_id); - return -ENODEV; - } - - if (ena == vf->spoofchk) { - dev_dbg(dev, "VF spoofchk already %s\n", ena ? "ON" : "OFF"); - return 0; - } - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->info.sec_flags = vf_vsi->info.sec_flags; - ctx->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_SECURITY_VALID); - if (ena) { - ctx->info.sec_flags |= - ICE_AQ_VSI_SEC_FLAG_ENA_MAC_ANTI_SPOOF | - (ICE_AQ_VSI_SEC_TX_VLAN_PRUNE_ENA << - ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S); - } else { - ctx->info.sec_flags &= - ~(ICE_AQ_VSI_SEC_FLAG_ENA_MAC_ANTI_SPOOF | - (ICE_AQ_VSI_SEC_TX_VLAN_PRUNE_ENA << - ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S)); - } - - ret = ice_update_vsi(&pf->hw, vf_vsi->idx, ctx, NULL); - if (ret) { - dev_err(dev, "Failed to %sable spoofchk on VF %d VSI %d\n error %d\n", - ena ? "en" : "dis", vf->vf_id, vf_vsi->vsi_num, ret); - goto out; - } - - /* only update spoofchk state and VSI context on success */ - vf_vsi->info.sec_flags = ctx->info.sec_flags; - vf->spoofchk = ena; - -out: - kfree(ctx); - return ret; -} - -/** - * ice_is_any_vf_in_promisc - check if any VF(s) are in promiscuous mode - * @pf: PF structure for accessing VF(s) - * - * Return false if no VF(s) are in unicast and/or multicast promiscuous mode, - * else return true - */ -bool ice_is_any_vf_in_promisc(struct ice_pf *pf) -{ - int vf_idx; - - ice_for_each_vf(pf, vf_idx) { - struct ice_vf *vf = &pf->vf[vf_idx]; - - /* found a VF that has promiscuous mode configured */ - if (test_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states) || - test_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states)) - return true; - } - - return false; -} - /** * ice_vc_cfg_promiscuous_mode_msg * @vf: pointer to the VF info @@ -2989,6 +990,7 @@ static int ice_vc_cfg_promiscuous_mode_msg(struct ice_vf *vf, u8 *msg) bool rm_promisc, alluni = false, allmulti = false; struct virtchnl_promisc_info *info = (struct virtchnl_promisc_info *)msg; + struct ice_vsi_vlan_ops *vlan_ops; int mcast_err = 0, ucast_err = 0; struct ice_pf *pf = vf->pf; struct ice_vsi *vsi; @@ -3012,7 +1014,7 @@ static int ice_vc_cfg_promiscuous_mode_msg(struct ice_vf *vf, u8 *msg) } dev = ice_pf_to_dev(pf); - if (!test_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) { + if (!ice_is_vf_trusted(vf)) { dev_err(dev, "Unprivileged VF %d is attempting to configure promiscuous mode\n", vf->vf_id); /* Leave v_ret alone, lie to the VF on purpose. */ @@ -3027,16 +1029,15 @@ static int ice_vc_cfg_promiscuous_mode_msg(struct ice_vf *vf, u8 *msg) rm_promisc = !allmulti && !alluni; - if (vsi->num_vlan || vf->port_vlan_info) { - if (rm_promisc) - ret = ice_cfg_vlan_pruning(vsi, true); - else - ret = ice_cfg_vlan_pruning(vsi, false); - if (ret) { - dev_err(dev, "Failed to configure VLAN pruning in promiscuous mode\n"); - v_ret = VIRTCHNL_STATUS_ERR_PARAM; - goto error_param; - } + vlan_ops = ice_get_compat_vsi_vlan_ops(vsi); + if (rm_promisc) + ret = vlan_ops->ena_rx_filtering(vsi); + else + ret = vlan_ops->dis_rx_filtering(vsi); + if (ret) { + dev_err(dev, "Failed to configure VLAN pruning in promiscuous mode\n"); + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto error_param; } if (!test_bit(ICE_FLAG_VF_TRUE_PROMISC_ENA, pf->flags)) { @@ -3063,7 +1064,8 @@ static int ice_vc_cfg_promiscuous_mode_msg(struct ice_vf *vf, u8 *msg) } else { u8 mcast_m, ucast_m; - if (vf->port_vlan_info || vsi->num_vlan > 1) { + if (ice_vf_is_port_vlan_ena(vf) || + ice_vsi_has_non_zero_vlans(vsi)) { mcast_m = ICE_MCAST_VLAN_PROMISC_BITS; ucast_m = ICE_UCAST_VLAN_PROMISC_BITS; } else { @@ -3090,16 +1092,21 @@ static int ice_vc_cfg_promiscuous_mode_msg(struct ice_vf *vf, u8 *msg) !test_and_set_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states)) dev_info(dev, "VF %u successfully set multicast promiscuous mode\n", vf->vf_id); - else if (!allmulti && test_and_clear_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states)) + else if (!allmulti && + test_and_clear_bit(ICE_VF_STATE_MC_PROMISC, + vf->vf_states)) dev_info(dev, "VF %u successfully unset multicast promiscuous mode\n", vf->vf_id); } if (!ucast_err) { - if (alluni && !test_and_set_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states)) + if (alluni && + !test_and_set_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states)) dev_info(dev, "VF %u successfully set unicast promiscuous mode\n", vf->vf_id); - else if (!alluni && test_and_clear_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states)) + else if (!alluni && + test_and_clear_bit(ICE_VF_STATE_UC_PROMISC, + vf->vf_states)) dev_info(dev, "VF %u successfully unset unicast promiscuous mode\n", vf->vf_id); } @@ -3492,7 +1499,7 @@ static int ice_vc_cfg_irq_map_msg(struct ice_vf *vf, u8 *msg) * there is actually at least a single VF queue vector mapped */ if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states) || - pf->num_msix_per_vf < num_q_vectors_mapped || + pf->vfs.num_msix_per < num_q_vectors_mapped || !num_q_vectors_mapped) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; @@ -3514,7 +1521,7 @@ static int ice_vc_cfg_irq_map_msg(struct ice_vf *vf, u8 *msg) /* vector_id is always 0-based for each VF, and can never be * larger than or equal to the max allowed interrupts per VF */ - if (!(vector_id < pf->num_msix_per_vf) || + if (!(vector_id < pf->vfs.num_msix_per) || !ice_vc_isvalid_vsi_id(vf, vsi_id) || (!vector_id && (map->rxq_map || map->txq_map))) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; @@ -3643,10 +1650,11 @@ static int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg) } vsi->max_frame = qpi->rxq.max_pkt_size; - /* add space for the port VLAN since the VF driver is not - * expected to account for it in the MTU calculation + /* add space for the port VLAN since the VF driver is + * not expected to account for it in the MTU + * calculation */ - if (vf->port_vlan_info) + if (ice_vf_is_port_vlan_ena(vf)) vsi->max_frame += VLAN_HLEN; if (ice_vsi_cfg_single_rxq(vsi, q_idx)) { @@ -3662,15 +1670,6 @@ static int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg) NULL, 0); } -/** - * ice_is_vf_trusted - * @vf: pointer to the VF info - */ -static bool ice_is_vf_trusted(struct ice_vf *vf) -{ - return test_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps); -} - /** * ice_can_vf_change_mac * @vf: pointer to the VF info @@ -4045,7 +2044,7 @@ static int ice_vc_request_qs_msg(struct ice_vf *vf, u8 *msg) } else { /* request is successful, then reset VF */ vf->num_req_qs = req_queues; - ice_vc_reset_vf(vf); + ice_reset_vf(vf, ICE_VF_RESET_NOTIFY); dev_info(dev, "VF %d granted request of %u queues.\n", vf->vf_id, req_queues); return 0; @@ -4057,70 +2056,6 @@ static int ice_vc_request_qs_msg(struct ice_vf *vf, u8 *msg) v_ret, (u8 *)vfres, sizeof(*vfres)); } -/** - * ice_set_vf_port_vlan - * @netdev: network interface device structure - * @vf_id: VF identifier - * @vlan_id: VLAN ID being set - * @qos: priority setting - * @vlan_proto: VLAN protocol - * - * program VF Port VLAN ID and/or QoS - */ -int -ice_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos, - __be16 vlan_proto) -{ - struct ice_pf *pf = ice_netdev_to_pf(netdev); - struct device *dev; - struct ice_vf *vf; - u16 vlanprio; - int ret; - - dev = ice_pf_to_dev(pf); - if (ice_validate_vf_id(pf, vf_id)) - return -EINVAL; - - if (vlan_id >= VLAN_N_VID || qos > 7) { - dev_err(dev, "Invalid Port VLAN parameters for VF %d, ID %d, QoS %d\n", - vf_id, vlan_id, qos); - return -EINVAL; - } - - if (vlan_proto != htons(ETH_P_8021Q)) { - dev_err(dev, "VF VLAN protocol is not supported\n"); - return -EPROTONOSUPPORT; - } - - vf = &pf->vf[vf_id]; - ret = ice_check_vf_ready_for_cfg(vf); - if (ret) - return ret; - - vlanprio = vlan_id | (qos << VLAN_PRIO_SHIFT); - - if (vf->port_vlan_info == vlanprio) { - /* duplicate request, so just return success */ - dev_dbg(dev, "Duplicate pvid %d request\n", vlanprio); - return 0; - } - - mutex_lock(&vf->cfg_lock); - - vf->port_vlan_info = vlanprio; - - if (vf->port_vlan_info) - dev_info(dev, "Setting VLAN %d, QoS 0x%x on VF %d\n", - vlan_id, qos, vf_id); - else - dev_info(dev, "Clearing port VLAN on VF %d\n", vf_id); - - ice_vc_reset_vf(vf); - mutex_unlock(&vf->cfg_lock); - - return 0; -} - /** * ice_vf_vlan_offload_ena - determine if capabilities support VLAN offloads * @caps: VF driver negotiated capabilities @@ -4132,6 +2067,83 @@ static bool ice_vf_vlan_offload_ena(u32 caps) return !!(caps & VIRTCHNL_VF_OFFLOAD_VLAN); } +/** + * ice_is_vlan_promisc_allowed - check if VLAN promiscuous config is allowed + * @vf: VF used to determine if VLAN promiscuous config is allowed + */ +static bool ice_is_vlan_promisc_allowed(struct ice_vf *vf) +{ + if ((test_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states) || + test_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states)) && + test_bit(ICE_FLAG_VF_TRUE_PROMISC_ENA, vf->pf->flags)) + return true; + + return false; +} + +/** + * ice_vf_ena_vlan_promisc - Enable Tx/Rx VLAN promiscuous for the VLAN + * @vsi: VF's VSI used to enable VLAN promiscuous mode + * @vlan: VLAN used to enable VLAN promiscuous + * + * This function should only be called if VLAN promiscuous mode is allowed, + * which can be determined via ice_is_vlan_promisc_allowed(). + */ +static int ice_vf_ena_vlan_promisc(struct ice_vsi *vsi, struct ice_vlan *vlan) +{ + u8 promisc_m = ICE_PROMISC_VLAN_TX | ICE_PROMISC_VLAN_RX; + int status; + + status = ice_fltr_set_vsi_promisc(&vsi->back->hw, vsi->idx, promisc_m, + vlan->vid); + if (status && status != -EEXIST) + return status; + + return 0; +} + +/** + * ice_vf_dis_vlan_promisc - Disable Tx/Rx VLAN promiscuous for the VLAN + * @vsi: VF's VSI used to disable VLAN promiscuous mode for + * @vlan: VLAN used to disable VLAN promiscuous + * + * This function should only be called if VLAN promiscuous mode is allowed, + * which can be determined via ice_is_vlan_promisc_allowed(). + */ +static int ice_vf_dis_vlan_promisc(struct ice_vsi *vsi, struct ice_vlan *vlan) +{ + u8 promisc_m = ICE_PROMISC_VLAN_TX | ICE_PROMISC_VLAN_RX; + int status; + + status = ice_fltr_clear_vsi_promisc(&vsi->back->hw, vsi->idx, promisc_m, + vlan->vid); + if (status && status != -ENOENT) + return status; + + return 0; +} + +/** + * ice_vf_has_max_vlans - check if VF already has the max allowed VLAN filters + * @vf: VF to check against + * @vsi: VF's VSI + * + * If the VF is trusted then the VF is allowed to add as many VLANs as it + * wants to, so return false. + * + * When the VF is untrusted compare the number of non-zero VLANs + 1 to the max + * allowed VLANs for an untrusted VF. Return the result of this comparison. + */ +static bool ice_vf_has_max_vlans(struct ice_vf *vf, struct ice_vsi *vsi) +{ + if (ice_is_vf_trusted(vf)) + return false; + +#define ICE_VF_ADDED_VLAN_ZERO_FLTRS 1 + return ((ice_vsi_num_non_zero_vlans(vsi) + + ICE_VF_ADDED_VLAN_ZERO_FLTRS) >= ICE_MAX_VLAN_PER_VF); +} + /** * ice_vc_process_vlan_msg * @vf: pointer to the VF info @@ -4149,9 +2161,7 @@ static int ice_vc_process_vlan_msg(struct ice_vf *vf, u8 *msg, bool add_v) bool vlan_promisc = false; struct ice_vsi *vsi; struct device *dev; - struct ice_hw *hw; int status = 0; - u8 promisc_m; int i; dev = ice_pf_to_dev(pf); @@ -4179,15 +2189,13 @@ static int ice_vc_process_vlan_msg(struct ice_vf *vf, u8 *msg, bool add_v) } } - hw = &pf->hw; vsi = ice_get_vf_vsi(vf); if (!vsi) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } - if (add_v && !ice_is_vf_trusted(vf) && - vsi->num_vlan >= ICE_MAX_VLAN_PER_VF) { + if (add_v && ice_vf_has_max_vlans(vf, vsi)) { dev_info(dev, "VF-%d is not trusted, switch the VF to trusted mode, in order to add more VLAN addresses\n", vf->vf_id); /* There is no need to let VF know about being not trusted, @@ -4196,22 +2204,28 @@ static int ice_vc_process_vlan_msg(struct ice_vf *vf, u8 *msg, bool add_v) goto error_param; } - if (vsi->info.pvid) { + /* in DVM a VF can add/delete inner VLAN filters when + * VIRTCHNL_VF_OFFLOAD_VLAN is negotiated, so only reject in SVM + */ + if (ice_vf_is_port_vlan_ena(vf) && !ice_is_dvm_ena(&pf->hw)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } - if ((test_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states) || - test_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states)) && - test_bit(ICE_FLAG_VF_TRUE_PROMISC_ENA, pf->flags)) - vlan_promisc = true; + /* in DVM VLAN promiscuous is based on the outer VLAN, which would be + * the port VLAN if VIRTCHNL_VF_OFFLOAD_VLAN was negotiated, so only + * allow vlan_promisc = true in SVM and if no port VLAN is configured + */ + vlan_promisc = ice_is_vlan_promisc_allowed(vf) && + !ice_is_dvm_ena(&pf->hw) && + !ice_vf_is_port_vlan_ena(vf); if (add_v) { for (i = 0; i < vfl->num_elements; i++) { u16 vid = vfl->vlan_id[i]; + struct ice_vlan vlan; - if (!ice_is_vf_trusted(vf) && - vsi->num_vlan >= ICE_MAX_VLAN_PER_VF) { + if (ice_vf_has_max_vlans(vf, vsi)) { dev_info(dev, "VF-%d is not trusted, switch the VF to trusted mode, in order to add more VLAN addresses\n", vf->vf_id); /* There is no need to let VF know about being @@ -4228,29 +2242,23 @@ static int ice_vc_process_vlan_msg(struct ice_vf *vf, u8 *msg, bool add_v) if (!vid) continue; - status = ice_vsi_add_vlan(vsi, vid, ICE_FWD_TO_VSI); + vlan = ICE_VLAN(ETH_P_8021Q, vid, 0); + status = vsi->inner_vlan_ops.add_vlan(vsi, &vlan); if (status) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } - /* Enable VLAN pruning when non-zero VLAN is added */ - if (!vlan_promisc && vid && - !ice_vsi_is_vlan_pruning_ena(vsi)) { - status = ice_cfg_vlan_pruning(vsi, true); - if (status) { + /* Enable VLAN filtering on first non-zero VLAN */ + if (!vlan_promisc && vid && !ice_is_dvm_ena(&pf->hw)) { + if (vsi->inner_vlan_ops.ena_rx_filtering(vsi)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; dev_err(dev, "Enable VLAN pruning on VLAN ID: %d failed error-%d\n", vid, status); goto error_param; } } else if (vlan_promisc) { - /* Enable Ucast/Mcast VLAN promiscuous mode */ - promisc_m = ICE_PROMISC_VLAN_TX | - ICE_PROMISC_VLAN_RX; - - status = ice_set_vsi_promisc(hw, vsi->idx, - promisc_m, vid); + status = ice_vf_ena_vlan_promisc(vsi, &vlan); if (status) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; dev_err(dev, "Enable Unicast/multicast promiscuous mode on VLAN ID:%d failed error-%d\n", @@ -4271,6 +2279,7 @@ static int ice_vc_process_vlan_msg(struct ice_vf *vf, u8 *msg, bool add_v) num_vf_vlan = vsi->num_vlan; for (i = 0; i < vfl->num_elements && i < num_vf_vlan; i++) { u16 vid = vfl->vlan_id[i]; + struct ice_vlan vlan; /* we add VLAN 0 by default for each VF so we can enable * Tx VLAN anti-spoof without triggering MDD events so @@ -4279,28 +2288,19 @@ static int ice_vc_process_vlan_msg(struct ice_vf *vf, u8 *msg, bool add_v) if (!vid) continue; - /* Make sure ice_vsi_kill_vlan is successful before - * updating VLAN information - */ - status = ice_vsi_kill_vlan(vsi, vid); + vlan = ICE_VLAN(ETH_P_8021Q, vid, 0); + status = vsi->inner_vlan_ops.del_vlan(vsi, &vlan); if (status) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } - /* Disable VLAN pruning when only VLAN 0 is left */ - if (vsi->num_vlan == 1 && - ice_vsi_is_vlan_pruning_ena(vsi)) - ice_cfg_vlan_pruning(vsi, false); + /* Disable VLAN filtering when only VLAN 0 is left */ + if (!ice_vsi_has_non_zero_vlans(vsi)) + vsi->inner_vlan_ops.dis_rx_filtering(vsi); - /* Disable Unicast/Multicast VLAN promiscuous mode */ - if (vlan_promisc) { - promisc_m = ICE_PROMISC_VLAN_TX | - ICE_PROMISC_VLAN_RX; - - ice_clear_vsi_promisc(hw, vsi->idx, - promisc_m, vid); - } + if (vlan_promisc) + ice_vf_dis_vlan_promisc(vsi, &vlan); } } @@ -4360,7 +2360,7 @@ static int ice_vc_ena_vlan_stripping(struct ice_vf *vf) } vsi = ice_get_vf_vsi(vf); - if (ice_vsi_manage_vlan_stripping(vsi, true)) + if (vsi->inner_vlan_ops.ena_stripping(vsi, ETH_P_8021Q)) v_ret = VIRTCHNL_STATUS_ERR_PARAM; error_param: @@ -4395,7 +2395,7 @@ static int ice_vc_dis_vlan_stripping(struct ice_vf *vf) goto error_param; } - if (ice_vsi_manage_vlan_stripping(vsi, false)) + if (vsi->inner_vlan_ops.dis_stripping(vsi)) v_ret = VIRTCHNL_STATUS_ERR_PARAM; error_param: @@ -4407,11 +2407,8 @@ static int ice_vc_dis_vlan_stripping(struct ice_vf *vf) * ice_vf_init_vlan_stripping - enable/disable VLAN stripping on initialization * @vf: VF to enable/disable VLAN stripping for on initialization * - * If the VIRTCHNL_VF_OFFLOAD_VLAN flag is set enable VLAN stripping, else if - * the flag is cleared then we want to disable stripping. For example, the flag - * will be cleared when port VLANs are configured by the administrator before - * passing the VF to the guest or if the AVF driver doesn't support VLAN - * offloads. + * Set the default for VLAN stripping based on whether a port VLAN is configured + * and the current VLAN mode of the device. */ static int ice_vf_init_vlan_stripping(struct ice_vf *vf) { @@ -4420,17 +2417,977 @@ static int ice_vf_init_vlan_stripping(struct ice_vf *vf) if (!vsi) return -EINVAL; - /* don't modify stripping if port VLAN is configured */ - if (vsi->info.pvid) + /* don't modify stripping if port VLAN is configured in SVM since the + * port VLAN is based on the inner/single VLAN in SVM + */ + if (ice_vf_is_port_vlan_ena(vf) && !ice_is_dvm_ena(&vsi->back->hw)) return 0; if (ice_vf_vlan_offload_ena(vf->driver_caps)) - return ice_vsi_manage_vlan_stripping(vsi, true); + return vsi->inner_vlan_ops.ena_stripping(vsi, ETH_P_8021Q); else - return ice_vsi_manage_vlan_stripping(vsi, false); + return vsi->inner_vlan_ops.dis_stripping(vsi); } -static struct ice_vc_vf_ops ice_vc_vf_dflt_ops = { +static u16 ice_vc_get_max_vlan_fltrs(struct ice_vf *vf) +{ + if (vf->trusted) + return VLAN_N_VID; + else + return ICE_MAX_VLAN_PER_VF; +} + +/** + * ice_vf_outer_vlan_not_allowed - check if outer VLAN can be used + * @vf: VF that being checked for + * + * When the device is in double VLAN mode, check whether or not the outer VLAN + * is allowed. + */ +static bool ice_vf_outer_vlan_not_allowed(struct ice_vf *vf) +{ + if (ice_vf_is_port_vlan_ena(vf)) + return true; + + return false; +} + +/** + * ice_vc_set_dvm_caps - set VLAN capabilities when the device is in DVM + * @vf: VF that capabilities are being set for + * @caps: VLAN capabilities to populate + * + * Determine VLAN capabilities support based on whether a port VLAN is + * configured. If a port VLAN is configured then the VF should use the inner + * filtering/offload capabilities since the port VLAN is using the outer VLAN + * capabilies. + */ +static void +ice_vc_set_dvm_caps(struct ice_vf *vf, struct virtchnl_vlan_caps *caps) +{ + struct virtchnl_vlan_supported_caps *supported_caps; + + if (ice_vf_outer_vlan_not_allowed(vf)) { + /* until support for inner VLAN filtering is added when a port + * VLAN is configured, only support software offloaded inner + * VLANs when a port VLAN is confgured in DVM + */ + supported_caps = &caps->filtering.filtering_support; + supported_caps->inner = VIRTCHNL_VLAN_UNSUPPORTED; + + supported_caps = &caps->offloads.stripping_support; + supported_caps->inner = VIRTCHNL_VLAN_ETHERTYPE_8100 | + VIRTCHNL_VLAN_TOGGLE | + VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1; + supported_caps->outer = VIRTCHNL_VLAN_UNSUPPORTED; + + supported_caps = &caps->offloads.insertion_support; + supported_caps->inner = VIRTCHNL_VLAN_ETHERTYPE_8100 | + VIRTCHNL_VLAN_TOGGLE | + VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1; + supported_caps->outer = VIRTCHNL_VLAN_UNSUPPORTED; + + caps->offloads.ethertype_init = VIRTCHNL_VLAN_ETHERTYPE_8100; + caps->offloads.ethertype_match = + VIRTCHNL_ETHERTYPE_STRIPPING_MATCHES_INSERTION; + } else { + supported_caps = &caps->filtering.filtering_support; + supported_caps->inner = VIRTCHNL_VLAN_UNSUPPORTED; + supported_caps->outer = VIRTCHNL_VLAN_ETHERTYPE_8100 | + VIRTCHNL_VLAN_ETHERTYPE_88A8 | + VIRTCHNL_VLAN_ETHERTYPE_9100 | + VIRTCHNL_VLAN_ETHERTYPE_AND; + caps->filtering.ethertype_init = VIRTCHNL_VLAN_ETHERTYPE_8100 | + VIRTCHNL_VLAN_ETHERTYPE_88A8 | + VIRTCHNL_VLAN_ETHERTYPE_9100; + + supported_caps = &caps->offloads.stripping_support; + supported_caps->inner = VIRTCHNL_VLAN_TOGGLE | + VIRTCHNL_VLAN_ETHERTYPE_8100 | + VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1; + supported_caps->outer = VIRTCHNL_VLAN_TOGGLE | + VIRTCHNL_VLAN_ETHERTYPE_8100 | + VIRTCHNL_VLAN_ETHERTYPE_88A8 | + VIRTCHNL_VLAN_ETHERTYPE_9100 | + VIRTCHNL_VLAN_ETHERTYPE_XOR | + VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2_2; + + supported_caps = &caps->offloads.insertion_support; + supported_caps->inner = VIRTCHNL_VLAN_TOGGLE | + VIRTCHNL_VLAN_ETHERTYPE_8100 | + VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1; + supported_caps->outer = VIRTCHNL_VLAN_TOGGLE | + VIRTCHNL_VLAN_ETHERTYPE_8100 | + VIRTCHNL_VLAN_ETHERTYPE_88A8 | + VIRTCHNL_VLAN_ETHERTYPE_9100 | + VIRTCHNL_VLAN_ETHERTYPE_XOR | + VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2; + + caps->offloads.ethertype_init = VIRTCHNL_VLAN_ETHERTYPE_8100; + + caps->offloads.ethertype_match = + VIRTCHNL_ETHERTYPE_STRIPPING_MATCHES_INSERTION; + } + + caps->filtering.max_filters = ice_vc_get_max_vlan_fltrs(vf); +} + +/** + * ice_vc_set_svm_caps - set VLAN capabilities when the device is in SVM + * @vf: VF that capabilities are being set for + * @caps: VLAN capabilities to populate + * + * Determine VLAN capabilities support based on whether a port VLAN is + * configured. If a port VLAN is configured then the VF does not have any VLAN + * filtering or offload capabilities since the port VLAN is using the inner VLAN + * capabilities in single VLAN mode (SVM). Otherwise allow the VF to use inner + * VLAN fitlering and offload capabilities. + */ +static void +ice_vc_set_svm_caps(struct ice_vf *vf, struct virtchnl_vlan_caps *caps) +{ + struct virtchnl_vlan_supported_caps *supported_caps; + + if (ice_vf_is_port_vlan_ena(vf)) { + supported_caps = &caps->filtering.filtering_support; + supported_caps->inner = VIRTCHNL_VLAN_UNSUPPORTED; + supported_caps->outer = VIRTCHNL_VLAN_UNSUPPORTED; + + supported_caps = &caps->offloads.stripping_support; + supported_caps->inner = VIRTCHNL_VLAN_UNSUPPORTED; + supported_caps->outer = VIRTCHNL_VLAN_UNSUPPORTED; + + supported_caps = &caps->offloads.insertion_support; + supported_caps->inner = VIRTCHNL_VLAN_UNSUPPORTED; + supported_caps->outer = VIRTCHNL_VLAN_UNSUPPORTED; + + caps->offloads.ethertype_init = VIRTCHNL_VLAN_UNSUPPORTED; + caps->offloads.ethertype_match = VIRTCHNL_VLAN_UNSUPPORTED; + caps->filtering.max_filters = 0; + } else { + supported_caps = &caps->filtering.filtering_support; + supported_caps->inner = VIRTCHNL_VLAN_ETHERTYPE_8100; + supported_caps->outer = VIRTCHNL_VLAN_UNSUPPORTED; + caps->filtering.ethertype_init = VIRTCHNL_VLAN_ETHERTYPE_8100; + + supported_caps = &caps->offloads.stripping_support; + supported_caps->inner = VIRTCHNL_VLAN_ETHERTYPE_8100 | + VIRTCHNL_VLAN_TOGGLE | + VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1; + supported_caps->outer = VIRTCHNL_VLAN_UNSUPPORTED; + + supported_caps = &caps->offloads.insertion_support; + supported_caps->inner = VIRTCHNL_VLAN_ETHERTYPE_8100 | + VIRTCHNL_VLAN_TOGGLE | + VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1; + supported_caps->outer = VIRTCHNL_VLAN_UNSUPPORTED; + + caps->offloads.ethertype_init = VIRTCHNL_VLAN_ETHERTYPE_8100; + caps->offloads.ethertype_match = + VIRTCHNL_ETHERTYPE_STRIPPING_MATCHES_INSERTION; + caps->filtering.max_filters = ice_vc_get_max_vlan_fltrs(vf); + } +} + +/** + * ice_vc_get_offload_vlan_v2_caps - determine VF's VLAN capabilities + * @vf: VF to determine VLAN capabilities for + * + * This will only be called if the VF and PF successfully negotiated + * VIRTCHNL_VF_OFFLOAD_VLAN_V2. + * + * Set VLAN capabilities based on the current VLAN mode and whether a port VLAN + * is configured or not. + */ +static int ice_vc_get_offload_vlan_v2_caps(struct ice_vf *vf) +{ + enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; + struct virtchnl_vlan_caps *caps = NULL; + int err, len = 0; + + if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + caps = kzalloc(sizeof(*caps), GFP_KERNEL); + if (!caps) { + v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY; + goto out; + } + len = sizeof(*caps); + + if (ice_is_dvm_ena(&vf->pf->hw)) + ice_vc_set_dvm_caps(vf, caps); + else + ice_vc_set_svm_caps(vf, caps); + + /* store negotiated caps to prevent invalid VF messages */ + memcpy(&vf->vlan_v2_caps, caps, sizeof(*caps)); + +out: + err = ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS, + v_ret, (u8 *)caps, len); + kfree(caps); + return err; +} + +/** + * ice_vc_validate_vlan_tpid - validate VLAN TPID + * @filtering_caps: negotiated/supported VLAN filtering capabilities + * @tpid: VLAN TPID used for validation + * + * Convert the VLAN TPID to a VIRTCHNL_VLAN_ETHERTYPE_* and then compare against + * the negotiated/supported filtering caps to see if the VLAN TPID is valid. + */ +static bool ice_vc_validate_vlan_tpid(u16 filtering_caps, u16 tpid) +{ + enum virtchnl_vlan_support vlan_ethertype = VIRTCHNL_VLAN_UNSUPPORTED; + + switch (tpid) { + case ETH_P_8021Q: + vlan_ethertype = VIRTCHNL_VLAN_ETHERTYPE_8100; + break; + case ETH_P_8021AD: + vlan_ethertype = VIRTCHNL_VLAN_ETHERTYPE_88A8; + break; + case ETH_P_QINQ1: + vlan_ethertype = VIRTCHNL_VLAN_ETHERTYPE_9100; + break; + } + + if (!(filtering_caps & vlan_ethertype)) + return false; + + return true; +} + +/** + * ice_vc_is_valid_vlan - validate the virtchnl_vlan + * @vc_vlan: virtchnl_vlan to validate + * + * If the VLAN TCI and VLAN TPID are 0, then this filter is invalid, so return + * false. Otherwise return true. + */ +static bool ice_vc_is_valid_vlan(struct virtchnl_vlan *vc_vlan) +{ + if (!vc_vlan->tci || !vc_vlan->tpid) + return false; + + return true; +} + +/** + * ice_vc_validate_vlan_filter_list - validate the filter list from the VF + * @vfc: negotiated/supported VLAN filtering capabilities + * @vfl: VLAN filter list from VF to validate + * + * Validate all of the filters in the VLAN filter list from the VF. If any of + * the checks fail then return false. Otherwise return true. + */ +static bool +ice_vc_validate_vlan_filter_list(struct virtchnl_vlan_filtering_caps *vfc, + struct virtchnl_vlan_filter_list_v2 *vfl) +{ + u16 i; + + if (!vfl->num_elements) + return false; + + for (i = 0; i < vfl->num_elements; i++) { + struct virtchnl_vlan_supported_caps *filtering_support = + &vfc->filtering_support; + struct virtchnl_vlan_filter *vlan_fltr = &vfl->filters[i]; + struct virtchnl_vlan *outer = &vlan_fltr->outer; + struct virtchnl_vlan *inner = &vlan_fltr->inner; + + if ((ice_vc_is_valid_vlan(outer) && + filtering_support->outer == VIRTCHNL_VLAN_UNSUPPORTED) || + (ice_vc_is_valid_vlan(inner) && + filtering_support->inner == VIRTCHNL_VLAN_UNSUPPORTED)) + return false; + + if ((outer->tci_mask && + !(filtering_support->outer & VIRTCHNL_VLAN_FILTER_MASK)) || + (inner->tci_mask && + !(filtering_support->inner & VIRTCHNL_VLAN_FILTER_MASK))) + return false; + + if (((outer->tci & VLAN_PRIO_MASK) && + !(filtering_support->outer & VIRTCHNL_VLAN_PRIO)) || + ((inner->tci & VLAN_PRIO_MASK) && + !(filtering_support->inner & VIRTCHNL_VLAN_PRIO))) + return false; + + if ((ice_vc_is_valid_vlan(outer) && + !ice_vc_validate_vlan_tpid(filtering_support->outer, + outer->tpid)) || + (ice_vc_is_valid_vlan(inner) && + !ice_vc_validate_vlan_tpid(filtering_support->inner, + inner->tpid))) + return false; + } + + return true; +} + +/** + * ice_vc_to_vlan - transform from struct virtchnl_vlan to struct ice_vlan + * @vc_vlan: struct virtchnl_vlan to transform + */ +static struct ice_vlan ice_vc_to_vlan(struct virtchnl_vlan *vc_vlan) +{ + struct ice_vlan vlan = { 0 }; + + vlan.prio = (vc_vlan->tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; + vlan.vid = vc_vlan->tci & VLAN_VID_MASK; + vlan.tpid = vc_vlan->tpid; + + return vlan; +} + +/** + * ice_vc_vlan_action - action to perform on the virthcnl_vlan + * @vsi: VF's VSI used to perform the action + * @vlan_action: function to perform the action with (i.e. add/del) + * @vlan: VLAN filter to perform the action with + */ +static int +ice_vc_vlan_action(struct ice_vsi *vsi, + int (*vlan_action)(struct ice_vsi *, struct ice_vlan *), + struct ice_vlan *vlan) +{ + int err; + + err = vlan_action(vsi, vlan); + if (err) + return err; + + return 0; +} + +/** + * ice_vc_del_vlans - delete VLAN(s) from the virtchnl filter list + * @vf: VF used to delete the VLAN(s) + * @vsi: VF's VSI used to delete the VLAN(s) + * @vfl: virthchnl filter list used to delete the filters + */ +static int +ice_vc_del_vlans(struct ice_vf *vf, struct ice_vsi *vsi, + struct virtchnl_vlan_filter_list_v2 *vfl) +{ + bool vlan_promisc = ice_is_vlan_promisc_allowed(vf); + int err; + u16 i; + + for (i = 0; i < vfl->num_elements; i++) { + struct virtchnl_vlan_filter *vlan_fltr = &vfl->filters[i]; + struct virtchnl_vlan *vc_vlan; + + vc_vlan = &vlan_fltr->outer; + if (ice_vc_is_valid_vlan(vc_vlan)) { + struct ice_vlan vlan = ice_vc_to_vlan(vc_vlan); + + err = ice_vc_vlan_action(vsi, + vsi->outer_vlan_ops.del_vlan, + &vlan); + if (err) + return err; + + if (vlan_promisc) + ice_vf_dis_vlan_promisc(vsi, &vlan); + } + + vc_vlan = &vlan_fltr->inner; + if (ice_vc_is_valid_vlan(vc_vlan)) { + struct ice_vlan vlan = ice_vc_to_vlan(vc_vlan); + + err = ice_vc_vlan_action(vsi, + vsi->inner_vlan_ops.del_vlan, + &vlan); + if (err) + return err; + + /* no support for VLAN promiscuous on inner VLAN unless + * we are in Single VLAN Mode (SVM) + */ + if (!ice_is_dvm_ena(&vsi->back->hw) && vlan_promisc) + ice_vf_dis_vlan_promisc(vsi, &vlan); + } + } + + return 0; +} + +/** + * ice_vc_remove_vlan_v2_msg - virtchnl handler for VIRTCHNL_OP_DEL_VLAN_V2 + * @vf: VF the message was received from + * @msg: message received from the VF + */ +static int ice_vc_remove_vlan_v2_msg(struct ice_vf *vf, u8 *msg) +{ + struct virtchnl_vlan_filter_list_v2 *vfl = + (struct virtchnl_vlan_filter_list_v2 *)msg; + enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; + struct ice_vsi *vsi; + + if (!ice_vc_validate_vlan_filter_list(&vf->vlan_v2_caps.filtering, + vfl)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + if (!ice_vc_isvalid_vsi_id(vf, vfl->vport_id)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + vsi = ice_get_vf_vsi(vf); + if (!vsi) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + if (ice_vc_del_vlans(vf, vsi, vfl)) + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + +out: + return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_DEL_VLAN_V2, v_ret, NULL, + 0); +} + +/** + * ice_vc_add_vlans - add VLAN(s) from the virtchnl filter list + * @vf: VF used to add the VLAN(s) + * @vsi: VF's VSI used to add the VLAN(s) + * @vfl: virthchnl filter list used to add the filters + */ +static int +ice_vc_add_vlans(struct ice_vf *vf, struct ice_vsi *vsi, + struct virtchnl_vlan_filter_list_v2 *vfl) +{ + bool vlan_promisc = ice_is_vlan_promisc_allowed(vf); + int err; + u16 i; + + for (i = 0; i < vfl->num_elements; i++) { + struct virtchnl_vlan_filter *vlan_fltr = &vfl->filters[i]; + struct virtchnl_vlan *vc_vlan; + + vc_vlan = &vlan_fltr->outer; + if (ice_vc_is_valid_vlan(vc_vlan)) { + struct ice_vlan vlan = ice_vc_to_vlan(vc_vlan); + + err = ice_vc_vlan_action(vsi, + vsi->outer_vlan_ops.add_vlan, + &vlan); + if (err) + return err; + + if (vlan_promisc) { + err = ice_vf_ena_vlan_promisc(vsi, &vlan); + if (err) + return err; + } + } + + vc_vlan = &vlan_fltr->inner; + if (ice_vc_is_valid_vlan(vc_vlan)) { + struct ice_vlan vlan = ice_vc_to_vlan(vc_vlan); + + err = ice_vc_vlan_action(vsi, + vsi->inner_vlan_ops.add_vlan, + &vlan); + if (err) + return err; + + /* no support for VLAN promiscuous on inner VLAN unless + * we are in Single VLAN Mode (SVM) + */ + if (!ice_is_dvm_ena(&vsi->back->hw) && vlan_promisc) { + err = ice_vf_ena_vlan_promisc(vsi, &vlan); + if (err) + return err; + } + } + } + + return 0; +} + +/** + * ice_vc_validate_add_vlan_filter_list - validate add filter list from the VF + * @vsi: VF VSI used to get number of existing VLAN filters + * @vfc: negotiated/supported VLAN filtering capabilities + * @vfl: VLAN filter list from VF to validate + * + * Validate all of the filters in the VLAN filter list from the VF during the + * VIRTCHNL_OP_ADD_VLAN_V2 opcode. If any of the checks fail then return false. + * Otherwise return true. + */ +static bool +ice_vc_validate_add_vlan_filter_list(struct ice_vsi *vsi, + struct virtchnl_vlan_filtering_caps *vfc, + struct virtchnl_vlan_filter_list_v2 *vfl) +{ + u16 num_requested_filters = vsi->num_vlan + vfl->num_elements; + + if (num_requested_filters > vfc->max_filters) + return false; + + return ice_vc_validate_vlan_filter_list(vfc, vfl); +} + +/** + * ice_vc_add_vlan_v2_msg - virtchnl handler for VIRTCHNL_OP_ADD_VLAN_V2 + * @vf: VF the message was received from + * @msg: message received from the VF + */ +static int ice_vc_add_vlan_v2_msg(struct ice_vf *vf, u8 *msg) +{ + enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; + struct virtchnl_vlan_filter_list_v2 *vfl = + (struct virtchnl_vlan_filter_list_v2 *)msg; + struct ice_vsi *vsi; + + if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + if (!ice_vc_isvalid_vsi_id(vf, vfl->vport_id)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + vsi = ice_get_vf_vsi(vf); + if (!vsi) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + if (!ice_vc_validate_add_vlan_filter_list(vsi, + &vf->vlan_v2_caps.filtering, + vfl)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + if (ice_vc_add_vlans(vf, vsi, vfl)) + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + +out: + return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_ADD_VLAN_V2, v_ret, NULL, + 0); +} + +/** + * ice_vc_valid_vlan_setting - validate VLAN setting + * @negotiated_settings: negotiated VLAN settings during VF init + * @ethertype_setting: ethertype(s) requested for the VLAN setting + */ +static bool +ice_vc_valid_vlan_setting(u32 negotiated_settings, u32 ethertype_setting) +{ + if (ethertype_setting && !(negotiated_settings & ethertype_setting)) + return false; + + /* only allow a single VIRTCHNL_VLAN_ETHERTYPE if + * VIRTHCNL_VLAN_ETHERTYPE_AND is not negotiated/supported + */ + if (!(negotiated_settings & VIRTCHNL_VLAN_ETHERTYPE_AND) && + hweight32(ethertype_setting) > 1) + return false; + + /* ability to modify the VLAN setting was not negotiated */ + if (!(negotiated_settings & VIRTCHNL_VLAN_TOGGLE)) + return false; + + return true; +} + +/** + * ice_vc_valid_vlan_setting_msg - validate the VLAN setting message + * @caps: negotiated VLAN settings during VF init + * @msg: message to validate + * + * Used to validate any VLAN virtchnl message sent as a + * virtchnl_vlan_setting structure. Validates the message against the + * negotiated/supported caps during VF driver init. + */ +static bool +ice_vc_valid_vlan_setting_msg(struct virtchnl_vlan_supported_caps *caps, + struct virtchnl_vlan_setting *msg) +{ + if ((!msg->outer_ethertype_setting && + !msg->inner_ethertype_setting) || + (!caps->outer && !caps->inner)) + return false; + + if (msg->outer_ethertype_setting && + !ice_vc_valid_vlan_setting(caps->outer, + msg->outer_ethertype_setting)) + return false; + + if (msg->inner_ethertype_setting && + !ice_vc_valid_vlan_setting(caps->inner, + msg->inner_ethertype_setting)) + return false; + + return true; +} + +/** + * ice_vc_get_tpid - transform from VIRTCHNL_VLAN_ETHERTYPE_* to VLAN TPID + * @ethertype_setting: VIRTCHNL_VLAN_ETHERTYPE_* used to get VLAN TPID + * @tpid: VLAN TPID to populate + */ +static int ice_vc_get_tpid(u32 ethertype_setting, u16 *tpid) +{ + switch (ethertype_setting) { + case VIRTCHNL_VLAN_ETHERTYPE_8100: + *tpid = ETH_P_8021Q; + break; + case VIRTCHNL_VLAN_ETHERTYPE_88A8: + *tpid = ETH_P_8021AD; + break; + case VIRTCHNL_VLAN_ETHERTYPE_9100: + *tpid = ETH_P_QINQ1; + break; + default: + *tpid = 0; + return -EINVAL; + } + + return 0; +} + +/** + * ice_vc_ena_vlan_offload - enable VLAN offload based on the ethertype_setting + * @vsi: VF's VSI used to enable the VLAN offload + * @ena_offload: function used to enable the VLAN offload + * @ethertype_setting: VIRTCHNL_VLAN_ETHERTYPE_* to enable offloads for + */ +static int +ice_vc_ena_vlan_offload(struct ice_vsi *vsi, + int (*ena_offload)(struct ice_vsi *vsi, u16 tpid), + u32 ethertype_setting) +{ + u16 tpid; + int err; + + err = ice_vc_get_tpid(ethertype_setting, &tpid); + if (err) + return err; + + err = ena_offload(vsi, tpid); + if (err) + return err; + + return 0; +} + +#define ICE_L2TSEL_QRX_CONTEXT_REG_IDX 3 +#define ICE_L2TSEL_BIT_OFFSET 23 +enum ice_l2tsel { + ICE_L2TSEL_EXTRACT_FIRST_TAG_L2TAG2_2ND, + ICE_L2TSEL_EXTRACT_FIRST_TAG_L2TAG1, +}; + +/** + * ice_vsi_update_l2tsel - update l2tsel field for all Rx rings on this VSI + * @vsi: VSI used to update l2tsel on + * @l2tsel: l2tsel setting requested + * + * Use the l2tsel setting to update all of the Rx queue context bits for l2tsel. + * This will modify which descriptor field the first offloaded VLAN will be + * stripped into. + */ +static void ice_vsi_update_l2tsel(struct ice_vsi *vsi, enum ice_l2tsel l2tsel) +{ + struct ice_hw *hw = &vsi->back->hw; + u32 l2tsel_bit; + int i; + + if (l2tsel == ICE_L2TSEL_EXTRACT_FIRST_TAG_L2TAG2_2ND) + l2tsel_bit = 0; + else + l2tsel_bit = BIT(ICE_L2TSEL_BIT_OFFSET); + + for (i = 0; i < vsi->alloc_rxq; i++) { + u16 pfq = vsi->rxq_map[i]; + u32 qrx_context_offset; + u32 regval; + + qrx_context_offset = + QRX_CONTEXT(ICE_L2TSEL_QRX_CONTEXT_REG_IDX, pfq); + + regval = rd32(hw, qrx_context_offset); + regval &= ~BIT(ICE_L2TSEL_BIT_OFFSET); + regval |= l2tsel_bit; + wr32(hw, qrx_context_offset, regval); + } +} + +/** + * ice_vc_ena_vlan_stripping_v2_msg + * @vf: VF the message was received from + * @msg: message received from the VF + * + * virthcnl handler for VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2 + */ +static int ice_vc_ena_vlan_stripping_v2_msg(struct ice_vf *vf, u8 *msg) +{ + enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; + struct virtchnl_vlan_supported_caps *stripping_support; + struct virtchnl_vlan_setting *strip_msg = + (struct virtchnl_vlan_setting *)msg; + u32 ethertype_setting; + struct ice_vsi *vsi; + + if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + if (!ice_vc_isvalid_vsi_id(vf, strip_msg->vport_id)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + vsi = ice_get_vf_vsi(vf); + if (!vsi) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + stripping_support = &vf->vlan_v2_caps.offloads.stripping_support; + if (!ice_vc_valid_vlan_setting_msg(stripping_support, strip_msg)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + ethertype_setting = strip_msg->outer_ethertype_setting; + if (ethertype_setting) { + if (ice_vc_ena_vlan_offload(vsi, + vsi->outer_vlan_ops.ena_stripping, + ethertype_setting)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } else { + enum ice_l2tsel l2tsel = + ICE_L2TSEL_EXTRACT_FIRST_TAG_L2TAG2_2ND; + + /* PF tells the VF that the outer VLAN tag is always + * extracted to VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2_2 and + * inner is always extracted to + * VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1. This is needed to + * support outer stripping so the first tag always ends + * up in L2TAG2_2ND and the second/inner tag, if + * enabled, is extracted in L2TAG1. + */ + ice_vsi_update_l2tsel(vsi, l2tsel); + } + } + + ethertype_setting = strip_msg->inner_ethertype_setting; + if (ethertype_setting && + ice_vc_ena_vlan_offload(vsi, vsi->inner_vlan_ops.ena_stripping, + ethertype_setting)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + +out: + return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2, + v_ret, NULL, 0); +} + +/** + * ice_vc_dis_vlan_stripping_v2_msg + * @vf: VF the message was received from + * @msg: message received from the VF + * + * virthcnl handler for VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2 + */ +static int ice_vc_dis_vlan_stripping_v2_msg(struct ice_vf *vf, u8 *msg) +{ + enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; + struct virtchnl_vlan_supported_caps *stripping_support; + struct virtchnl_vlan_setting *strip_msg = + (struct virtchnl_vlan_setting *)msg; + u32 ethertype_setting; + struct ice_vsi *vsi; + + if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + if (!ice_vc_isvalid_vsi_id(vf, strip_msg->vport_id)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + vsi = ice_get_vf_vsi(vf); + if (!vsi) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + stripping_support = &vf->vlan_v2_caps.offloads.stripping_support; + if (!ice_vc_valid_vlan_setting_msg(stripping_support, strip_msg)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + ethertype_setting = strip_msg->outer_ethertype_setting; + if (ethertype_setting) { + if (vsi->outer_vlan_ops.dis_stripping(vsi)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } else { + enum ice_l2tsel l2tsel = + ICE_L2TSEL_EXTRACT_FIRST_TAG_L2TAG1; + + /* PF tells the VF that the outer VLAN tag is always + * extracted to VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2_2 and + * inner is always extracted to + * VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1. This is needed to + * support inner stripping while outer stripping is + * disabled so that the first and only tag is extracted + * in L2TAG1. + */ + ice_vsi_update_l2tsel(vsi, l2tsel); + } + } + + ethertype_setting = strip_msg->inner_ethertype_setting; + if (ethertype_setting && vsi->inner_vlan_ops.dis_stripping(vsi)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + +out: + return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2, + v_ret, NULL, 0); +} + +/** + * ice_vc_ena_vlan_insertion_v2_msg + * @vf: VF the message was received from + * @msg: message received from the VF + * + * virthcnl handler for VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2 + */ +static int ice_vc_ena_vlan_insertion_v2_msg(struct ice_vf *vf, u8 *msg) +{ + enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; + struct virtchnl_vlan_supported_caps *insertion_support; + struct virtchnl_vlan_setting *insertion_msg = + (struct virtchnl_vlan_setting *)msg; + u32 ethertype_setting; + struct ice_vsi *vsi; + + if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + if (!ice_vc_isvalid_vsi_id(vf, insertion_msg->vport_id)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + vsi = ice_get_vf_vsi(vf); + if (!vsi) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + insertion_support = &vf->vlan_v2_caps.offloads.insertion_support; + if (!ice_vc_valid_vlan_setting_msg(insertion_support, insertion_msg)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + ethertype_setting = insertion_msg->outer_ethertype_setting; + if (ethertype_setting && + ice_vc_ena_vlan_offload(vsi, vsi->outer_vlan_ops.ena_insertion, + ethertype_setting)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + ethertype_setting = insertion_msg->inner_ethertype_setting; + if (ethertype_setting && + ice_vc_ena_vlan_offload(vsi, vsi->inner_vlan_ops.ena_insertion, + ethertype_setting)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + +out: + return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2, + v_ret, NULL, 0); +} + +/** + * ice_vc_dis_vlan_insertion_v2_msg + * @vf: VF the message was received from + * @msg: message received from the VF + * + * virthcnl handler for VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2 + */ +static int ice_vc_dis_vlan_insertion_v2_msg(struct ice_vf *vf, u8 *msg) +{ + enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; + struct virtchnl_vlan_supported_caps *insertion_support; + struct virtchnl_vlan_setting *insertion_msg = + (struct virtchnl_vlan_setting *)msg; + u32 ethertype_setting; + struct ice_vsi *vsi; + + if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + if (!ice_vc_isvalid_vsi_id(vf, insertion_msg->vport_id)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + vsi = ice_get_vf_vsi(vf); + if (!vsi) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + insertion_support = &vf->vlan_v2_caps.offloads.insertion_support; + if (!ice_vc_valid_vlan_setting_msg(insertion_support, insertion_msg)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + ethertype_setting = insertion_msg->outer_ethertype_setting; + if (ethertype_setting && vsi->outer_vlan_ops.dis_insertion(vsi)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + + ethertype_setting = insertion_msg->inner_ethertype_setting; + if (ethertype_setting && vsi->inner_vlan_ops.dis_insertion(vsi)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto out; + } + +out: + return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2, + v_ret, NULL, 0); +} + +static const struct ice_virtchnl_ops ice_virtchnl_dflt_ops = { .get_ver_msg = ice_vc_get_ver_msg, .get_vf_res_msg = ice_vc_get_vf_res_msg, .reset_vf = ice_vc_reset_vf_msg, @@ -4452,11 +3409,22 @@ static struct ice_vc_vf_ops ice_vc_vf_dflt_ops = { .handle_rss_cfg_msg = ice_vc_handle_rss_cfg, .add_fdir_fltr_msg = ice_vc_add_fdir_fltr, .del_fdir_fltr_msg = ice_vc_del_fdir_fltr, + .get_offload_vlan_v2_caps = ice_vc_get_offload_vlan_v2_caps, + .add_vlan_v2_msg = ice_vc_add_vlan_v2_msg, + .remove_vlan_v2_msg = ice_vc_remove_vlan_v2_msg, + .ena_vlan_stripping_v2_msg = ice_vc_ena_vlan_stripping_v2_msg, + .dis_vlan_stripping_v2_msg = ice_vc_dis_vlan_stripping_v2_msg, + .ena_vlan_insertion_v2_msg = ice_vc_ena_vlan_insertion_v2_msg, + .dis_vlan_insertion_v2_msg = ice_vc_dis_vlan_insertion_v2_msg, }; -void ice_vc_set_dflt_vf_ops(struct ice_vc_vf_ops *ops) +/** + * ice_virtchnl_set_dflt_ops - Switch to default virtchnl ops + * @vf: the VF to switch ops + */ +void ice_virtchnl_set_dflt_ops(struct ice_vf *vf) { - *ops = ice_vc_vf_dflt_ops; + vf->virtchnl_ops = &ice_virtchnl_dflt_ops; } /** @@ -4589,15 +3557,44 @@ ice_vc_repr_cfg_promiscuous_mode(struct ice_vf *vf, u8 __always_unused *msg) NULL, 0); } -void ice_vc_change_ops_to_repr(struct ice_vc_vf_ops *ops) +static const struct ice_virtchnl_ops ice_virtchnl_repr_ops = { + .get_ver_msg = ice_vc_get_ver_msg, + .get_vf_res_msg = ice_vc_get_vf_res_msg, + .reset_vf = ice_vc_reset_vf_msg, + .add_mac_addr_msg = ice_vc_repr_add_mac, + .del_mac_addr_msg = ice_vc_repr_del_mac, + .cfg_qs_msg = ice_vc_cfg_qs_msg, + .ena_qs_msg = ice_vc_ena_qs_msg, + .dis_qs_msg = ice_vc_dis_qs_msg, + .request_qs_msg = ice_vc_request_qs_msg, + .cfg_irq_map_msg = ice_vc_cfg_irq_map_msg, + .config_rss_key = ice_vc_config_rss_key, + .config_rss_lut = ice_vc_config_rss_lut, + .get_stats_msg = ice_vc_get_stats_msg, + .cfg_promiscuous_mode_msg = ice_vc_repr_cfg_promiscuous_mode, + .add_vlan_msg = ice_vc_repr_add_vlan, + .remove_vlan_msg = ice_vc_repr_del_vlan, + .ena_vlan_stripping = ice_vc_repr_ena_vlan_stripping, + .dis_vlan_stripping = ice_vc_repr_dis_vlan_stripping, + .handle_rss_cfg_msg = ice_vc_handle_rss_cfg, + .add_fdir_fltr_msg = ice_vc_add_fdir_fltr, + .del_fdir_fltr_msg = ice_vc_del_fdir_fltr, + .get_offload_vlan_v2_caps = ice_vc_get_offload_vlan_v2_caps, + .add_vlan_v2_msg = ice_vc_add_vlan_v2_msg, + .remove_vlan_v2_msg = ice_vc_remove_vlan_v2_msg, + .ena_vlan_stripping_v2_msg = ice_vc_ena_vlan_stripping_v2_msg, + .dis_vlan_stripping_v2_msg = ice_vc_dis_vlan_stripping_v2_msg, + .ena_vlan_insertion_v2_msg = ice_vc_ena_vlan_insertion_v2_msg, + .dis_vlan_insertion_v2_msg = ice_vc_dis_vlan_insertion_v2_msg, +}; + +/** + * ice_virtchnl_set_repr_ops - Switch to representor virtchnl ops + * @vf: the VF to switch ops + */ +void ice_virtchnl_set_repr_ops(struct ice_vf *vf) { - ops->add_mac_addr_msg = ice_vc_repr_add_mac; - ops->del_mac_addr_msg = ice_vc_repr_del_mac; - ops->add_vlan_msg = ice_vc_repr_add_vlan; - ops->remove_vlan_msg = ice_vc_repr_del_vlan; - ops->ena_vlan_stripping = ice_vc_repr_ena_vlan_stripping; - ops->dis_vlan_stripping = ice_vc_repr_dis_vlan_stripping; - ops->cfg_promiscuous_mode_msg = ice_vc_repr_cfg_promiscuous_mode; + vf->virtchnl_ops = &ice_virtchnl_repr_ops; } /** @@ -4612,20 +3609,21 @@ void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) { u32 v_opcode = le32_to_cpu(event->desc.cookie_high); s16 vf_id = le16_to_cpu(event->desc.retval); + const struct ice_virtchnl_ops *ops; u16 msglen = event->msg_len; - struct ice_vc_vf_ops *ops; u8 *msg = event->msg_buf; struct ice_vf *vf = NULL; struct device *dev; int err = 0; dev = ice_pf_to_dev(pf); - if (ice_validate_vf_id(pf, vf_id)) { - err = -EINVAL; - goto error_handler; - } - vf = &pf->vf[vf_id]; + vf = ice_get_vf_by_id(pf, vf_id); + if (!vf) { + dev_err(dev, "Unable to locate VF for message from VF ID %d, opcode %d, len %d\n", + vf_id, v_opcode, msglen); + return; + } /* Check if VF is disabled. */ if (test_bit(ICE_VF_STATE_DIS, vf->vf_states)) { @@ -4633,7 +3631,7 @@ void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) goto error_handler; } - ops = &vf->vc_ops; + ops = vf->virtchnl_ops; /* Perform basic checks on the msg */ err = virtchnl_vc_validate_vf_msg(&vf->vf_ver, v_opcode, msg, msglen); @@ -4648,6 +3646,7 @@ void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) ice_vc_send_msg_to_vf(vf, v_opcode, VIRTCHNL_STATUS_ERR_NOT_SUPPORTED, NULL, 0); + ice_put_vf(vf); return; } @@ -4657,6 +3656,7 @@ void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) NULL, 0); dev_err(dev, "Invalid message from VF %d, opcode %d, len %d, error %d\n", vf_id, v_opcode, msglen, err); + ice_put_vf(vf); return; } @@ -4666,6 +3666,7 @@ void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) if (!mutex_trylock(&vf->cfg_lock)) { dev_info(dev, "VF %u is being configured in another context that will trigger a VFR, so there is no need to handle this message\n", vf->vf_id); + ice_put_vf(vf); return; } @@ -4676,7 +3677,7 @@ void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) case VIRTCHNL_OP_GET_VF_RESOURCES: err = ops->get_vf_res_msg(vf, msg); if (ice_vf_init_vlan_stripping(vf)) - dev_err(dev, "Failed to initialize VLAN stripping for VF %d\n", + dev_dbg(dev, "Failed to initialize VLAN stripping for VF %d\n", vf->vf_id); ice_vc_notify_vf_link_state(vf); break; @@ -4741,6 +3742,27 @@ void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) case VIRTCHNL_OP_DEL_RSS_CFG: err = ops->handle_rss_cfg_msg(vf, msg, false); break; + case VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS: + err = ops->get_offload_vlan_v2_caps(vf); + break; + case VIRTCHNL_OP_ADD_VLAN_V2: + err = ops->add_vlan_v2_msg(vf, msg); + break; + case VIRTCHNL_OP_DEL_VLAN_V2: + err = ops->remove_vlan_v2_msg(vf, msg); + break; + case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2: + err = ops->ena_vlan_stripping_v2_msg(vf, msg); + break; + case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2: + err = ops->dis_vlan_stripping_v2_msg(vf, msg); + break; + case VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2: + err = ops->ena_vlan_insertion_v2_msg(vf, msg); + break; + case VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2: + err = ops->dis_vlan_insertion_v2_msg(vf, msg); + break; case VIRTCHNL_OP_UNKNOWN: default: dev_err(dev, "Unsupported opcode %d from VF %d\n", v_opcode, @@ -4759,549 +3781,5 @@ void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) } mutex_unlock(&vf->cfg_lock); -} - -/** - * ice_get_vf_cfg - * @netdev: network interface device structure - * @vf_id: VF identifier - * @ivi: VF configuration structure - * - * return VF configuration - */ -int -ice_get_vf_cfg(struct net_device *netdev, int vf_id, struct ifla_vf_info *ivi) -{ - struct ice_pf *pf = ice_netdev_to_pf(netdev); - struct ice_vf *vf; - - if (ice_validate_vf_id(pf, vf_id)) - return -EINVAL; - - vf = &pf->vf[vf_id]; - - if (ice_check_vf_init(pf, vf)) - return -EBUSY; - - ivi->vf = vf_id; - ether_addr_copy(ivi->mac, vf->hw_lan_addr.addr); - - /* VF configuration for VLAN and applicable QoS */ - ivi->vlan = vf->port_vlan_info & VLAN_VID_MASK; - ivi->qos = (vf->port_vlan_info & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; - - ivi->trusted = vf->trusted; - ivi->spoofchk = vf->spoofchk; - if (!vf->link_forced) - ivi->linkstate = IFLA_VF_LINK_STATE_AUTO; - else if (vf->link_up) - ivi->linkstate = IFLA_VF_LINK_STATE_ENABLE; - else - ivi->linkstate = IFLA_VF_LINK_STATE_DISABLE; - ivi->max_tx_rate = vf->max_tx_rate; - ivi->min_tx_rate = vf->min_tx_rate; - return 0; -} - -/** - * ice_unicast_mac_exists - check if the unicast MAC exists on the PF's switch - * @pf: PF used to reference the switch's rules - * @umac: unicast MAC to compare against existing switch rules - * - * Return true on the first/any match, else return false - */ -static bool ice_unicast_mac_exists(struct ice_pf *pf, u8 *umac) -{ - struct ice_sw_recipe *mac_recipe_list = - &pf->hw.switch_info->recp_list[ICE_SW_LKUP_MAC]; - struct ice_fltr_mgmt_list_entry *list_itr; - struct list_head *rule_head; - struct mutex *rule_lock; /* protect MAC filter list access */ - - rule_head = &mac_recipe_list->filt_rules; - rule_lock = &mac_recipe_list->filt_rule_lock; - - mutex_lock(rule_lock); - list_for_each_entry(list_itr, rule_head, list_entry) { - u8 *existing_mac = &list_itr->fltr_info.l_data.mac.mac_addr[0]; - - if (ether_addr_equal(existing_mac, umac)) { - mutex_unlock(rule_lock); - return true; - } - } - - mutex_unlock(rule_lock); - - return false; -} - -/** - * ice_set_vf_mac - * @netdev: network interface device structure - * @vf_id: VF identifier - * @mac: MAC address - * - * program VF MAC address - */ -int ice_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) -{ - struct ice_pf *pf = ice_netdev_to_pf(netdev); - struct ice_vf *vf; - int ret; - - if (ice_validate_vf_id(pf, vf_id)) - return -EINVAL; - - if (is_multicast_ether_addr(mac)) { - netdev_err(netdev, "%pM not a valid unicast address\n", mac); - return -EINVAL; - } - - vf = &pf->vf[vf_id]; - /* nothing left to do, unicast MAC already set */ - if (ether_addr_equal(vf->dev_lan_addr.addr, mac) && - ether_addr_equal(vf->hw_lan_addr.addr, mac)) - return 0; - - ret = ice_check_vf_ready_for_cfg(vf); - if (ret) - return ret; - - if (ice_unicast_mac_exists(pf, mac)) { - netdev_err(netdev, "Unicast MAC %pM already exists on this PF. Preventing setting VF %u unicast MAC address to %pM\n", - mac, vf_id, mac); - return -EINVAL; - } - - mutex_lock(&vf->cfg_lock); - - /* VF is notified of its new MAC via the PF's response to the - * VIRTCHNL_OP_GET_VF_RESOURCES message after the VF has been reset - */ - ether_addr_copy(vf->dev_lan_addr.addr, mac); - ether_addr_copy(vf->hw_lan_addr.addr, mac); - if (is_zero_ether_addr(mac)) { - /* VF will send VIRTCHNL_OP_ADD_ETH_ADDR message with its MAC */ - vf->pf_set_mac = false; - netdev_info(netdev, "Removing MAC on VF %d. VF driver will be reinitialized\n", - vf->vf_id); - } else { - /* PF will add MAC rule for the VF */ - vf->pf_set_mac = true; - netdev_info(netdev, "Setting MAC %pM on VF %d. VF driver will be reinitialized\n", - mac, vf_id); - } - - ice_vc_reset_vf(vf); - mutex_unlock(&vf->cfg_lock); - return 0; -} - -/** - * ice_set_vf_trust - * @netdev: network interface device structure - * @vf_id: VF identifier - * @trusted: Boolean value to enable/disable trusted VF - * - * Enable or disable a given VF as trusted - */ -int ice_set_vf_trust(struct net_device *netdev, int vf_id, bool trusted) -{ - struct ice_pf *pf = ice_netdev_to_pf(netdev); - struct ice_vf *vf; - int ret; - - if (ice_is_eswitch_mode_switchdev(pf)) { - dev_info(ice_pf_to_dev(pf), "Trusted VF is forbidden in switchdev mode\n"); - return -EOPNOTSUPP; - } - - if (ice_validate_vf_id(pf, vf_id)) - return -EINVAL; - - vf = &pf->vf[vf_id]; - ret = ice_check_vf_ready_for_cfg(vf); - if (ret) - return ret; - - /* Check if already trusted */ - if (trusted == vf->trusted) - return 0; - - mutex_lock(&vf->cfg_lock); - - vf->trusted = trusted; - ice_vc_reset_vf(vf); - dev_info(ice_pf_to_dev(pf), "VF %u is now %strusted\n", - vf_id, trusted ? "" : "un"); - - mutex_unlock(&vf->cfg_lock); - - return 0; -} - -/** - * ice_set_vf_link_state - * @netdev: network interface device structure - * @vf_id: VF identifier - * @link_state: required link state - * - * Set VF's link state, irrespective of physical link state status - */ -int ice_set_vf_link_state(struct net_device *netdev, int vf_id, int link_state) -{ - struct ice_pf *pf = ice_netdev_to_pf(netdev); - struct ice_vf *vf; - int ret; - - if (ice_validate_vf_id(pf, vf_id)) - return -EINVAL; - - vf = &pf->vf[vf_id]; - ret = ice_check_vf_ready_for_cfg(vf); - if (ret) - return ret; - - switch (link_state) { - case IFLA_VF_LINK_STATE_AUTO: - vf->link_forced = false; - break; - case IFLA_VF_LINK_STATE_ENABLE: - vf->link_forced = true; - vf->link_up = true; - break; - case IFLA_VF_LINK_STATE_DISABLE: - vf->link_forced = true; - vf->link_up = false; - break; - default: - return -EINVAL; - } - - ice_vc_notify_vf_link_state(vf); - - return 0; -} - -/** - * ice_calc_all_vfs_min_tx_rate - calculate cumulative min Tx rate on all VFs - * @pf: PF associated with VFs - */ -static int ice_calc_all_vfs_min_tx_rate(struct ice_pf *pf) -{ - int rate = 0, i; - - ice_for_each_vf(pf, i) - rate += pf->vf[i].min_tx_rate; - - return rate; -} - -/** - * ice_min_tx_rate_oversubscribed - check if min Tx rate causes oversubscription - * @vf: VF trying to configure min_tx_rate - * @min_tx_rate: min Tx rate in Mbps - * - * Check if the min_tx_rate being passed in will cause oversubscription of total - * min_tx_rate based on the current link speed and all other VFs configured - * min_tx_rate - * - * Return true if the passed min_tx_rate would cause oversubscription, else - * return false - */ -static bool -ice_min_tx_rate_oversubscribed(struct ice_vf *vf, int min_tx_rate) -{ - int link_speed_mbps = ice_get_link_speed_mbps(ice_get_vf_vsi(vf)); - int all_vfs_min_tx_rate = ice_calc_all_vfs_min_tx_rate(vf->pf); - - /* this VF's previous rate is being overwritten */ - all_vfs_min_tx_rate -= vf->min_tx_rate; - - if (all_vfs_min_tx_rate + min_tx_rate > link_speed_mbps) { - dev_err(ice_pf_to_dev(vf->pf), "min_tx_rate of %d Mbps on VF %u would cause oversubscription of %d Mbps based on the current link speed %d Mbps\n", - min_tx_rate, vf->vf_id, - all_vfs_min_tx_rate + min_tx_rate - link_speed_mbps, - link_speed_mbps); - return true; - } - - return false; -} - -/** - * ice_set_vf_bw - set min/max VF bandwidth - * @netdev: network interface device structure - * @vf_id: VF identifier - * @min_tx_rate: Minimum Tx rate in Mbps - * @max_tx_rate: Maximum Tx rate in Mbps - */ -int -ice_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, - int max_tx_rate) -{ - struct ice_pf *pf = ice_netdev_to_pf(netdev); - struct ice_vsi *vsi; - struct device *dev; - struct ice_vf *vf; - int ret; - - dev = ice_pf_to_dev(pf); - if (ice_validate_vf_id(pf, vf_id)) - return -EINVAL; - - vf = &pf->vf[vf_id]; - ret = ice_check_vf_ready_for_cfg(vf); - if (ret) - return ret; - - vsi = ice_get_vf_vsi(vf); - - /* when max_tx_rate is zero that means no max Tx rate limiting, so only - * check if max_tx_rate is non-zero - */ - if (max_tx_rate && min_tx_rate > max_tx_rate) { - dev_err(dev, "Cannot set min Tx rate %d Mbps greater than max Tx rate %d Mbps\n", - min_tx_rate, max_tx_rate); - return -EINVAL; - } - - if (min_tx_rate && ice_is_dcb_active(pf)) { - dev_err(dev, "DCB on PF is currently enabled. VF min Tx rate limiting not allowed on this PF.\n"); - return -EOPNOTSUPP; - } - - if (ice_min_tx_rate_oversubscribed(vf, min_tx_rate)) - return -EINVAL; - - if (vf->min_tx_rate != (unsigned int)min_tx_rate) { - ret = ice_set_min_bw_limit(vsi, (u64)min_tx_rate * 1000); - if (ret) { - dev_err(dev, "Unable to set min-tx-rate for VF %d\n", - vf->vf_id); - return ret; - } - - vf->min_tx_rate = min_tx_rate; - } - - if (vf->max_tx_rate != (unsigned int)max_tx_rate) { - ret = ice_set_max_bw_limit(vsi, (u64)max_tx_rate * 1000); - if (ret) { - dev_err(dev, "Unable to set max-tx-rate for VF %d\n", - vf->vf_id); - return ret; - } - - vf->max_tx_rate = max_tx_rate; - } - - return 0; -} - -/** - * ice_get_vf_stats - populate some stats for the VF - * @netdev: the netdev of the PF - * @vf_id: the host OS identifier (0-255) - * @vf_stats: pointer to the OS memory to be initialized - */ -int ice_get_vf_stats(struct net_device *netdev, int vf_id, - struct ifla_vf_stats *vf_stats) -{ - struct ice_pf *pf = ice_netdev_to_pf(netdev); - struct ice_eth_stats *stats; - struct ice_vsi *vsi; - struct ice_vf *vf; - int ret; - - if (ice_validate_vf_id(pf, vf_id)) - return -EINVAL; - - vf = &pf->vf[vf_id]; - ret = ice_check_vf_ready_for_cfg(vf); - if (ret) - return ret; - - vsi = ice_get_vf_vsi(vf); - if (!vsi) - return -EINVAL; - - ice_update_eth_stats(vsi); - stats = &vsi->eth_stats; - - memset(vf_stats, 0, sizeof(*vf_stats)); - - vf_stats->rx_packets = stats->rx_unicast + stats->rx_broadcast + - stats->rx_multicast; - vf_stats->tx_packets = stats->tx_unicast + stats->tx_broadcast + - stats->tx_multicast; - vf_stats->rx_bytes = stats->rx_bytes; - vf_stats->tx_bytes = stats->tx_bytes; - vf_stats->broadcast = stats->rx_broadcast; - vf_stats->multicast = stats->rx_multicast; - vf_stats->rx_dropped = stats->rx_discards; - vf_stats->tx_dropped = stats->tx_discards; - - return 0; -} - -/** - * ice_print_vf_rx_mdd_event - print VF Rx malicious driver detect event - * @vf: pointer to the VF structure - */ -void ice_print_vf_rx_mdd_event(struct ice_vf *vf) -{ - struct ice_pf *pf = vf->pf; - struct device *dev; - - dev = ice_pf_to_dev(pf); - - dev_info(dev, "%d Rx Malicious Driver Detection events detected on PF %d VF %d MAC %pM. mdd-auto-reset-vfs=%s\n", - vf->mdd_rx_events.count, pf->hw.pf_id, vf->vf_id, - vf->dev_lan_addr.addr, - test_bit(ICE_FLAG_MDD_AUTO_RESET_VF, pf->flags) - ? "on" : "off"); -} - -/** - * ice_print_vfs_mdd_events - print VFs malicious driver detect event - * @pf: pointer to the PF structure - * - * Called from ice_handle_mdd_event to rate limit and print VFs MDD events. - */ -void ice_print_vfs_mdd_events(struct ice_pf *pf) -{ - struct device *dev = ice_pf_to_dev(pf); - struct ice_hw *hw = &pf->hw; - int i; - - /* check that there are pending MDD events to print */ - if (!test_and_clear_bit(ICE_MDD_VF_PRINT_PENDING, pf->state)) - return; - - /* VF MDD event logs are rate limited to one second intervals */ - if (time_is_after_jiffies(pf->last_printed_mdd_jiffies + HZ * 1)) - return; - - pf->last_printed_mdd_jiffies = jiffies; - - ice_for_each_vf(pf, i) { - struct ice_vf *vf = &pf->vf[i]; - - /* only print Rx MDD event message if there are new events */ - if (vf->mdd_rx_events.count != vf->mdd_rx_events.last_printed) { - vf->mdd_rx_events.last_printed = - vf->mdd_rx_events.count; - ice_print_vf_rx_mdd_event(vf); - } - - /* only print Tx MDD event message if there are new events */ - if (vf->mdd_tx_events.count != vf->mdd_tx_events.last_printed) { - vf->mdd_tx_events.last_printed = - vf->mdd_tx_events.count; - - dev_info(dev, "%d Tx Malicious Driver Detection events detected on PF %d VF %d MAC %pM.\n", - vf->mdd_tx_events.count, hw->pf_id, i, - vf->dev_lan_addr.addr); - } - } -} - -/** - * ice_restore_all_vfs_msi_state - restore VF MSI state after PF FLR - * @pdev: pointer to a pci_dev structure - * - * Called when recovering from a PF FLR to restore interrupt capability to - * the VFs. - */ -void ice_restore_all_vfs_msi_state(struct pci_dev *pdev) -{ - u16 vf_id; - int pos; - - if (!pci_num_vf(pdev)) - return; - - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); - if (pos) { - struct pci_dev *vfdev; - - pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, - &vf_id); - vfdev = pci_get_device(pdev->vendor, vf_id, NULL); - while (vfdev) { - if (vfdev->is_virtfn && vfdev->physfn == pdev) - pci_restore_msi_state(vfdev); - vfdev = pci_get_device(pdev->vendor, vf_id, - vfdev); - } - } -} - -/** - * ice_is_malicious_vf - helper function to detect a malicious VF - * @pf: ptr to struct ice_pf - * @event: pointer to the AQ event - * @num_msg_proc: the number of messages processed so far - * @num_msg_pending: the number of messages peinding in admin queue - */ -bool -ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, - u16 num_msg_proc, u16 num_msg_pending) -{ - s16 vf_id = le16_to_cpu(event->desc.retval); - struct device *dev = ice_pf_to_dev(pf); - struct ice_mbx_data mbxdata; - bool malvf = false; - struct ice_vf *vf; - int status; - - if (ice_validate_vf_id(pf, vf_id)) - return false; - - vf = &pf->vf[vf_id]; - /* Check if VF is disabled. */ - if (test_bit(ICE_VF_STATE_DIS, vf->vf_states)) - return false; - - mbxdata.num_msg_proc = num_msg_proc; - mbxdata.num_pending_arq = num_msg_pending; - mbxdata.max_num_msgs_mbx = pf->hw.mailboxq.num_rq_entries; -#define ICE_MBX_OVERFLOW_WATERMARK 64 - mbxdata.async_watermark_val = ICE_MBX_OVERFLOW_WATERMARK; - - /* check to see if we have a malicious VF */ - status = ice_mbx_vf_state_handler(&pf->hw, &mbxdata, vf_id, &malvf); - if (status) - return false; - - if (malvf) { - bool report_vf = false; - - /* if the VF is malicious and we haven't let the user - * know about it, then let them know now - */ - status = ice_mbx_report_malvf(&pf->hw, pf->malvfs, - ICE_MAX_VF_COUNT, vf_id, - &report_vf); - if (status) - dev_dbg(dev, "Error reporting malicious VF\n"); - - if (report_vf) { - struct ice_vsi *pf_vsi = ice_get_main_vsi(pf); - - if (pf_vsi) - dev_warn(dev, "VF MAC %pM on PF MAC %pM is generating asynchronous messages and may be overflowing the PF message queue. Please see the Adapter User Guide for more information\n", - &vf->dev_lan_addr.addr[0], - pf_vsi->netdev->dev_addr); - } - - return true; - } - - /* if there was an error in detection or the VF is not malicious then - * return false - */ - return false; + ice_put_vf(vf); } diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.h b/drivers/net/ethernet/intel/ice/ice_virtchnl.h new file mode 100644 index 000000000000..b5a3fd8adbb4 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2022, Intel Corporation. */ + +#ifndef _ICE_VIRTCHNL_H_ +#define _ICE_VIRTCHNL_H_ + +#include +#include +#include +#include +#include "ice_vf_lib.h" + +/* Restrict number of MAC Addr and VLAN that non-trusted VF can programmed */ +#define ICE_MAX_VLAN_PER_VF 8 + +/* MAC filters: 1 is reserved for the VF's default/perm_addr/LAA MAC, 1 for + * broadcast, and 16 for additional unicast/multicast filters + */ +#define ICE_MAX_MACADDR_PER_VF 18 + +struct ice_virtchnl_ops { + int (*get_ver_msg)(struct ice_vf *vf, u8 *msg); + int (*get_vf_res_msg)(struct ice_vf *vf, u8 *msg); + void (*reset_vf)(struct ice_vf *vf); + int (*add_mac_addr_msg)(struct ice_vf *vf, u8 *msg); + int (*del_mac_addr_msg)(struct ice_vf *vf, u8 *msg); + int (*cfg_qs_msg)(struct ice_vf *vf, u8 *msg); + int (*ena_qs_msg)(struct ice_vf *vf, u8 *msg); + int (*dis_qs_msg)(struct ice_vf *vf, u8 *msg); + int (*request_qs_msg)(struct ice_vf *vf, u8 *msg); + int (*cfg_irq_map_msg)(struct ice_vf *vf, u8 *msg); + int (*config_rss_key)(struct ice_vf *vf, u8 *msg); + int (*config_rss_lut)(struct ice_vf *vf, u8 *msg); + int (*get_stats_msg)(struct ice_vf *vf, u8 *msg); + int (*cfg_promiscuous_mode_msg)(struct ice_vf *vf, u8 *msg); + int (*add_vlan_msg)(struct ice_vf *vf, u8 *msg); + int (*remove_vlan_msg)(struct ice_vf *vf, u8 *msg); + int (*ena_vlan_stripping)(struct ice_vf *vf); + int (*dis_vlan_stripping)(struct ice_vf *vf); + int (*handle_rss_cfg_msg)(struct ice_vf *vf, u8 *msg, bool add); + int (*add_fdir_fltr_msg)(struct ice_vf *vf, u8 *msg); + int (*del_fdir_fltr_msg)(struct ice_vf *vf, u8 *msg); + int (*get_offload_vlan_v2_caps)(struct ice_vf *vf); + int (*add_vlan_v2_msg)(struct ice_vf *vf, u8 *msg); + int (*remove_vlan_v2_msg)(struct ice_vf *vf, u8 *msg); + int (*ena_vlan_stripping_v2_msg)(struct ice_vf *vf, u8 *msg); + int (*dis_vlan_stripping_v2_msg)(struct ice_vf *vf, u8 *msg); + int (*ena_vlan_insertion_v2_msg)(struct ice_vf *vf, u8 *msg); + int (*dis_vlan_insertion_v2_msg)(struct ice_vf *vf, u8 *msg); +}; + +#ifdef CONFIG_PCI_IOV +void ice_virtchnl_set_dflt_ops(struct ice_vf *vf); +void ice_virtchnl_set_repr_ops(struct ice_vf *vf); +void ice_vc_notify_vf_link_state(struct ice_vf *vf); +void ice_vc_notify_link_state(struct ice_pf *pf); +void ice_vc_notify_reset(struct ice_pf *pf); +int +ice_vc_send_msg_to_vf(struct ice_vf *vf, u32 v_opcode, + enum virtchnl_status_code v_retval, u8 *msg, u16 msglen); +bool ice_vc_isvalid_vsi_id(struct ice_vf *vf, u16 vsi_id); +#else /* CONFIG_PCI_IOV */ +static inline void ice_virtchnl_set_dflt_ops(struct ice_vf *vf) { } +static inline void ice_virtchnl_set_repr_ops(struct ice_vf *vf) { } +static inline void ice_vc_notify_vf_link_state(struct ice_vf *vf) { } +static inline void ice_vc_notify_link_state(struct ice_pf *pf) { } +static inline void ice_vc_notify_reset(struct ice_pf *pf) { } + +static inline int +ice_vc_send_msg_to_vf(struct ice_vf *vf, u32 v_opcode, + enum virtchnl_status_code v_retval, u8 *msg, u16 msglen) +{ + return -EOPNOTSUPP; +} + +static inline bool ice_vc_isvalid_vsi_id(struct ice_vf *vf, u16 vsi_id) +{ + return false; +} +#endif /* !CONFIG_PCI_IOV */ + +#endif /* _ICE_VIRTCHNL_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl_allowlist.c b/drivers/net/ethernet/intel/ice/ice_virtchnl_allowlist.c index 9feebe5f556c..5a82216e7d03 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl_allowlist.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl_allowlist.c @@ -55,6 +55,15 @@ static const u32 vlan_allowlist_opcodes[] = { VIRTCHNL_OP_ENABLE_VLAN_STRIPPING, VIRTCHNL_OP_DISABLE_VLAN_STRIPPING, }; +/* VIRTCHNL_VF_OFFLOAD_VLAN_V2 */ +static const u32 vlan_v2_allowlist_opcodes[] = { + VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS, VIRTCHNL_OP_ADD_VLAN_V2, + VIRTCHNL_OP_DEL_VLAN_V2, VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2, + VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2, + VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2, + VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2, +}; + /* VIRTCHNL_VF_OFFLOAD_RSS_PF */ static const u32 rss_pf_allowlist_opcodes[] = { VIRTCHNL_OP_CONFIG_RSS_KEY, VIRTCHNL_OP_CONFIG_RSS_LUT, @@ -89,6 +98,7 @@ static const struct allowlist_opcode_info allowlist_opcodes[] = { ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_RSS_PF, rss_pf_allowlist_opcodes), ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_ADV_RSS_PF, adv_rss_pf_allowlist_opcodes), ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_FDIR_PF, fdir_pf_allowlist_opcodes), + ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_VLAN_V2, vlan_v2_allowlist_opcodes), }; /** diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.c b/drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.c index d64df81d4893..8e38ee2faf58 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.c @@ -5,6 +5,7 @@ #include "ice_base.h" #include "ice_lib.h" #include "ice_flow.h" +#include "ice_vf_lib_private.h" #define to_fltr_conf_from_desc(p) \ container_of(p, struct virtchnl_fdir_fltr_conf, input) @@ -1288,15 +1289,16 @@ ice_vc_fdir_irq_handler(struct ice_vsi *ctrl_vsi, union ice_32b_rx_flex_desc *rx_desc) { struct ice_pf *pf = ctrl_vsi->back; + struct ice_vf *vf = ctrl_vsi->vf; struct ice_vf_fdir_ctx *ctx_done; struct ice_vf_fdir_ctx *ctx_irq; struct ice_vf_fdir *fdir; unsigned long flags; struct device *dev; - struct ice_vf *vf; int ret; - vf = &pf->vf[ctrl_vsi->vf_id]; + if (WARN_ON(!vf)) + return; fdir = &vf->fdir; ctx_done = &fdir->ctx_done; @@ -1571,15 +1573,16 @@ ice_vc_del_fdir_fltr_post(struct ice_vf *vf, struct ice_vf_fdir_ctx *ctx, */ void ice_flush_fdir_ctx(struct ice_pf *pf) { - int i; + struct ice_vf *vf; + unsigned int bkt; if (!test_and_clear_bit(ICE_FD_VF_FLUSH_CTX, pf->state)) return; - ice_for_each_vf(pf, i) { + mutex_lock(&pf->vfs.table_lock); + ice_for_each_vf(pf, bkt, vf) { struct device *dev = ice_pf_to_dev(pf); enum virtchnl_fdir_prgm_status status; - struct ice_vf *vf = &pf->vf[i]; struct ice_vf_fdir_ctx *ctx; unsigned long flags; int ret; @@ -1633,6 +1636,7 @@ void ice_flush_fdir_ctx(struct ice_pf *pf) ctx->flags &= ~ICE_VF_FDIR_CTX_VALID; spin_unlock_irqrestore(&vf->fdir.ctx_lock, flags); } + mutex_unlock(&pf->vfs.table_lock); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.h b/drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.h index f4e629f4c09b..c5bcc8d7481c 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.h +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.h @@ -6,6 +6,7 @@ struct ice_vf; struct ice_pf; +struct ice_vsi; enum ice_fdir_ctx_stat { ICE_FDIR_CTX_READY, diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl_pf.h b/drivers/net/ethernet/intel/ice/ice_virtchnl_pf.h deleted file mode 100644 index 8f27255cc0cc..000000000000 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl_pf.h +++ /dev/null @@ -1,343 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2018, Intel Corporation. */ - -#ifndef _ICE_VIRTCHNL_PF_H_ -#define _ICE_VIRTCHNL_PF_H_ -#include "ice.h" -#include "ice_virtchnl_fdir.h" - -/* Restrict number of MAC Addr and VLAN that non-trusted VF can programmed */ -#define ICE_MAX_VLAN_PER_VF 8 -/* MAC filters: 1 is reserved for the VF's default/perm_addr/LAA MAC, 1 for - * broadcast, and 16 for additional unicast/multicast filters - */ -#define ICE_MAX_MACADDR_PER_VF 18 - -/* Malicious Driver Detection */ -#define ICE_MDD_EVENTS_THRESHOLD 30 - -/* Static VF transaction/status register def */ -#define VF_DEVICE_STATUS 0xAA -#define VF_TRANS_PENDING_M 0x20 - -/* wait defines for polling PF_PCI_CIAD register status */ -#define ICE_PCI_CIAD_WAIT_COUNT 100 -#define ICE_PCI_CIAD_WAIT_DELAY_US 1 - -/* VF resource constraints */ -#define ICE_MAX_VF_COUNT 256 -#define ICE_MIN_QS_PER_VF 1 -#define ICE_NONQ_VECS_VF 1 -#define ICE_MAX_SCATTER_QS_PER_VF 16 -#define ICE_MAX_RSS_QS_PER_VF 16 -#define ICE_NUM_VF_MSIX_MED 17 -#define ICE_NUM_VF_MSIX_SMALL 5 -#define ICE_NUM_VF_MSIX_MULTIQ_MIN 3 -#define ICE_MIN_INTR_PER_VF (ICE_MIN_QS_PER_VF + 1) -#define ICE_MAX_VF_RESET_TRIES 40 -#define ICE_MAX_VF_RESET_SLEEP_MS 20 - -#define ice_for_each_vf(pf, i) \ - for ((i) = 0; (i) < (pf)->num_alloc_vfs; (i)++) - -/* Specific VF states */ -enum ice_vf_states { - ICE_VF_STATE_INIT = 0, /* PF is initializing VF */ - ICE_VF_STATE_ACTIVE, /* VF resources are allocated for use */ - ICE_VF_STATE_QS_ENA, /* VF queue(s) enabled */ - ICE_VF_STATE_DIS, - ICE_VF_STATE_MC_PROMISC, - ICE_VF_STATE_UC_PROMISC, - ICE_VF_STATES_NBITS -}; - -/* VF capabilities */ -enum ice_virtchnl_cap { - ICE_VIRTCHNL_VF_CAP_L2 = 0, - ICE_VIRTCHNL_VF_CAP_PRIVILEGE, -}; - -struct ice_time_mac { - unsigned long time_modified; - u8 addr[ETH_ALEN]; -}; - -/* VF MDD events print structure */ -struct ice_mdd_vf_events { - u16 count; /* total count of Rx|Tx events */ - /* count number of the last printed event */ - u16 last_printed; -}; - -struct ice_vf; - -struct ice_vc_vf_ops { - int (*get_ver_msg)(struct ice_vf *vf, u8 *msg); - int (*get_vf_res_msg)(struct ice_vf *vf, u8 *msg); - void (*reset_vf)(struct ice_vf *vf); - int (*add_mac_addr_msg)(struct ice_vf *vf, u8 *msg); - int (*del_mac_addr_msg)(struct ice_vf *vf, u8 *msg); - int (*cfg_qs_msg)(struct ice_vf *vf, u8 *msg); - int (*ena_qs_msg)(struct ice_vf *vf, u8 *msg); - int (*dis_qs_msg)(struct ice_vf *vf, u8 *msg); - int (*request_qs_msg)(struct ice_vf *vf, u8 *msg); - int (*cfg_irq_map_msg)(struct ice_vf *vf, u8 *msg); - int (*config_rss_key)(struct ice_vf *vf, u8 *msg); - int (*config_rss_lut)(struct ice_vf *vf, u8 *msg); - int (*get_stats_msg)(struct ice_vf *vf, u8 *msg); - int (*cfg_promiscuous_mode_msg)(struct ice_vf *vf, u8 *msg); - int (*add_vlan_msg)(struct ice_vf *vf, u8 *msg); - int (*remove_vlan_msg)(struct ice_vf *vf, u8 *msg); - int (*ena_vlan_stripping)(struct ice_vf *vf); - int (*dis_vlan_stripping)(struct ice_vf *vf); - int (*handle_rss_cfg_msg)(struct ice_vf *vf, u8 *msg, bool add); - int (*add_fdir_fltr_msg)(struct ice_vf *vf, u8 *msg); - int (*del_fdir_fltr_msg)(struct ice_vf *vf, u8 *msg); -}; - -/* VF information structure */ -struct ice_vf { - struct ice_pf *pf; - - /* Used during virtchnl message handling and NDO ops against the VF - * that will trigger a VFR - */ - struct mutex cfg_lock; - - u16 vf_id; /* VF ID in the PF space */ - u16 lan_vsi_idx; /* index into PF struct */ - u16 ctrl_vsi_idx; - struct ice_vf_fdir fdir; - /* first vector index of this VF in the PF space */ - int first_vector_idx; - struct ice_sw *vf_sw_id; /* switch ID the VF VSIs connect to */ - struct virtchnl_version_info vf_ver; - u32 driver_caps; /* reported by VF driver */ - struct virtchnl_ether_addr dev_lan_addr; - struct virtchnl_ether_addr hw_lan_addr; - struct ice_time_mac legacy_last_added_umac; - DECLARE_BITMAP(txq_ena, ICE_MAX_RSS_QS_PER_VF); - DECLARE_BITMAP(rxq_ena, ICE_MAX_RSS_QS_PER_VF); - u16 port_vlan_info; /* Port VLAN ID and QoS */ - u8 pf_set_mac:1; /* VF MAC address set by VMM admin */ - u8 trusted:1; - u8 spoofchk:1; - u8 link_forced:1; - u8 link_up:1; /* only valid if VF link is forced */ - /* VSI indices - actual VSI pointers are maintained in the PF structure - * When assigned, these will be non-zero, because VSI 0 is always - * the main LAN VSI for the PF. - */ - u16 lan_vsi_num; /* ID as used by firmware */ - unsigned int min_tx_rate; /* Minimum Tx bandwidth limit in Mbps */ - unsigned int max_tx_rate; /* Maximum Tx bandwidth limit in Mbps */ - DECLARE_BITMAP(vf_states, ICE_VF_STATES_NBITS); /* VF runtime states */ - - unsigned long vf_caps; /* VF's adv. capabilities */ - u8 num_req_qs; /* num of queue pairs requested by VF */ - u16 num_mac; - u16 num_vf_qs; /* num of queue configured per VF */ - struct ice_mdd_vf_events mdd_rx_events; - struct ice_mdd_vf_events mdd_tx_events; - DECLARE_BITMAP(opcodes_allowlist, VIRTCHNL_OP_MAX); - - struct ice_repr *repr; - - struct ice_vc_vf_ops vc_ops; - - /* devlink port data */ - struct devlink_port devlink_port; -}; - -#ifdef CONFIG_PCI_IOV -struct ice_vsi *ice_get_vf_vsi(struct ice_vf *vf); -void ice_process_vflr_event(struct ice_pf *pf); -int ice_sriov_configure(struct pci_dev *pdev, int num_vfs); -int ice_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac); -int -ice_get_vf_cfg(struct net_device *netdev, int vf_id, struct ifla_vf_info *ivi); - -void ice_free_vfs(struct ice_pf *pf); -void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event); -void ice_vc_notify_link_state(struct ice_pf *pf); -void ice_vc_notify_reset(struct ice_pf *pf); -void ice_vc_notify_vf_link_state(struct ice_vf *vf); -void ice_vc_change_ops_to_repr(struct ice_vc_vf_ops *ops); -void ice_vc_set_dflt_vf_ops(struct ice_vc_vf_ops *ops); -bool ice_reset_all_vfs(struct ice_pf *pf, bool is_vflr); -bool ice_reset_vf(struct ice_vf *vf, bool is_vflr); -void ice_restore_all_vfs_msi_state(struct pci_dev *pdev); -bool -ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, - u16 num_msg_proc, u16 num_msg_pending); - -int -ice_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos, - __be16 vlan_proto); - -int -ice_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, - int max_tx_rate); - -int ice_set_vf_trust(struct net_device *netdev, int vf_id, bool trusted); - -int ice_set_vf_link_state(struct net_device *netdev, int vf_id, int link_state); - -int ice_check_vf_ready_for_cfg(struct ice_vf *vf); - -bool ice_is_vf_disabled(struct ice_vf *vf); - -int ice_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool ena); - -int ice_calc_vf_reg_idx(struct ice_vf *vf, struct ice_q_vector *q_vector); - -void ice_set_vf_state_qs_dis(struct ice_vf *vf); -int -ice_get_vf_stats(struct net_device *netdev, int vf_id, - struct ifla_vf_stats *vf_stats); -bool ice_is_any_vf_in_promisc(struct ice_pf *pf); -void -ice_vf_lan_overflow_event(struct ice_pf *pf, struct ice_rq_event_info *event); -void ice_print_vfs_mdd_events(struct ice_pf *pf); -void ice_print_vf_rx_mdd_event(struct ice_vf *vf); -bool -ice_vc_validate_pattern(struct ice_vf *vf, struct virtchnl_proto_hdrs *proto); -struct ice_vsi *ice_vf_ctrl_vsi_setup(struct ice_vf *vf); -int -ice_vc_send_msg_to_vf(struct ice_vf *vf, u32 v_opcode, - enum virtchnl_status_code v_retval, u8 *msg, u16 msglen); -bool ice_vc_isvalid_vsi_id(struct ice_vf *vf, u16 vsi_id); -#else /* CONFIG_PCI_IOV */ -static inline void ice_process_vflr_event(struct ice_pf *pf) { } -static inline void ice_free_vfs(struct ice_pf *pf) { } -static inline -void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) { } -static inline void ice_vc_notify_link_state(struct ice_pf *pf) { } -static inline void ice_vc_notify_reset(struct ice_pf *pf) { } -static inline void ice_vc_notify_vf_link_state(struct ice_vf *vf) { } -static inline void ice_vc_change_ops_to_repr(struct ice_vc_vf_ops *ops) { } -static inline void ice_vc_set_dflt_vf_ops(struct ice_vc_vf_ops *ops) { } -static inline void ice_set_vf_state_qs_dis(struct ice_vf *vf) { } -static inline -void ice_vf_lan_overflow_event(struct ice_pf *pf, struct ice_rq_event_info *event) { } -static inline void ice_print_vfs_mdd_events(struct ice_pf *pf) { } -static inline void ice_print_vf_rx_mdd_event(struct ice_vf *vf) { } -static inline void ice_restore_all_vfs_msi_state(struct pci_dev *pdev) { } - -static inline int ice_check_vf_ready_for_cfg(struct ice_vf *vf) -{ - return -EOPNOTSUPP; -} - -static inline bool ice_is_vf_disabled(struct ice_vf *vf) -{ - return true; -} - -static inline struct ice_vsi *ice_get_vf_vsi(struct ice_vf *vf) -{ - return NULL; -} - -static inline bool -ice_is_malicious_vf(struct ice_pf __always_unused *pf, - struct ice_rq_event_info __always_unused *event, - u16 __always_unused num_msg_proc, - u16 __always_unused num_msg_pending) -{ - return false; -} - -static inline bool -ice_reset_all_vfs(struct ice_pf __always_unused *pf, - bool __always_unused is_vflr) -{ - return true; -} - -static inline bool -ice_reset_vf(struct ice_vf __always_unused *vf, bool __always_unused is_vflr) -{ - return true; -} - -static inline int -ice_sriov_configure(struct pci_dev __always_unused *pdev, - int __always_unused num_vfs) -{ - return -EOPNOTSUPP; -} - -static inline int -ice_set_vf_mac(struct net_device __always_unused *netdev, - int __always_unused vf_id, u8 __always_unused *mac) -{ - return -EOPNOTSUPP; -} - -static inline int -ice_get_vf_cfg(struct net_device __always_unused *netdev, - int __always_unused vf_id, - struct ifla_vf_info __always_unused *ivi) -{ - return -EOPNOTSUPP; -} - -static inline int -ice_set_vf_trust(struct net_device __always_unused *netdev, - int __always_unused vf_id, bool __always_unused trusted) -{ - return -EOPNOTSUPP; -} - -static inline int -ice_set_vf_port_vlan(struct net_device __always_unused *netdev, - int __always_unused vf_id, u16 __always_unused vid, - u8 __always_unused qos, __be16 __always_unused v_proto) -{ - return -EOPNOTSUPP; -} - -static inline int -ice_set_vf_spoofchk(struct net_device __always_unused *netdev, - int __always_unused vf_id, bool __always_unused ena) -{ - return -EOPNOTSUPP; -} - -static inline int -ice_set_vf_link_state(struct net_device __always_unused *netdev, - int __always_unused vf_id, int __always_unused link_state) -{ - return -EOPNOTSUPP; -} - -static inline int -ice_set_vf_bw(struct net_device __always_unused *netdev, - int __always_unused vf_id, int __always_unused min_tx_rate, - int __always_unused max_tx_rate) -{ - return -EOPNOTSUPP; -} - -static inline int -ice_calc_vf_reg_idx(struct ice_vf __always_unused *vf, - struct ice_q_vector __always_unused *q_vector) -{ - return 0; -} - -static inline int -ice_get_vf_stats(struct net_device __always_unused *netdev, - int __always_unused vf_id, - struct ifla_vf_stats __always_unused *vf_stats) -{ - return -EOPNOTSUPP; -} - -static inline bool ice_is_any_vf_in_promisc(struct ice_pf __always_unused *pf) -{ - return false; -} -#endif /* CONFIG_PCI_IOV */ -#endif /* _ICE_VIRTCHNL_PF_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_vlan.h b/drivers/net/ethernet/intel/ice/ice_vlan.h new file mode 100644 index 000000000000..bc4550a03173 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vlan.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019-2021, Intel Corporation. */ + +#ifndef _ICE_VLAN_H_ +#define _ICE_VLAN_H_ + +#include +#include "ice_type.h" + +struct ice_vlan { + u16 tpid; + u16 vid; + u8 prio; +}; + +#define ICE_VLAN(tpid, vid, prio) ((struct ice_vlan){ tpid, vid, prio }) + +#endif /* _ICE_VLAN_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_vlan_mode.c b/drivers/net/ethernet/intel/ice/ice_vlan_mode.c new file mode 100644 index 000000000000..1b618de592b7 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vlan_mode.c @@ -0,0 +1,439 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2019-2021, Intel Corporation. */ + +#include "ice_common.h" + +/** + * ice_pkg_get_supported_vlan_mode - determine if DDP supports Double VLAN mode + * @hw: pointer to the HW struct + * @dvm: output variable to determine if DDP supports DVM(true) or SVM(false) + */ +static int +ice_pkg_get_supported_vlan_mode(struct ice_hw *hw, bool *dvm) +{ + u16 meta_init_size = sizeof(struct ice_meta_init_section); + struct ice_meta_init_section *sect; + struct ice_buf_build *bld; + int status; + + /* if anything fails, we assume there is no DVM support */ + *dvm = false; + + bld = ice_pkg_buf_alloc_single_section(hw, + ICE_SID_RXPARSER_METADATA_INIT, + meta_init_size, (void **)§); + if (!bld) + return -ENOMEM; + + /* only need to read a single section */ + sect->count = cpu_to_le16(1); + sect->offset = cpu_to_le16(ICE_META_VLAN_MODE_ENTRY); + + status = ice_aq_upload_section(hw, + (struct ice_buf_hdr *)ice_pkg_buf(bld), + ICE_PKG_BUF_SIZE, NULL); + if (!status) { + DECLARE_BITMAP(entry, ICE_META_INIT_BITS); + u32 arr[ICE_META_INIT_DW_CNT]; + u16 i; + + /* convert to host bitmap format */ + for (i = 0; i < ICE_META_INIT_DW_CNT; i++) + arr[i] = le32_to_cpu(sect->entry.bm[i]); + + bitmap_from_arr32(entry, arr, (u16)ICE_META_INIT_BITS); + + /* check if DVM is supported */ + *dvm = test_bit(ICE_META_VLAN_MODE_BIT, entry); + } + + ice_pkg_buf_free(hw, bld); + + return status; +} + +/** + * ice_aq_get_vlan_mode - get the VLAN mode of the device + * @hw: pointer to the HW structure + * @get_params: structure FW fills in based on the current VLAN mode config + * + * Get VLAN Mode Parameters (0x020D) + */ +static int +ice_aq_get_vlan_mode(struct ice_hw *hw, + struct ice_aqc_get_vlan_mode *get_params) +{ + struct ice_aq_desc desc; + + if (!get_params) + return -EINVAL; + + ice_fill_dflt_direct_cmd_desc(&desc, + ice_aqc_opc_get_vlan_mode_parameters); + + return ice_aq_send_cmd(hw, &desc, get_params, sizeof(*get_params), + NULL); +} + +/** + * ice_aq_is_dvm_ena - query FW to check if double VLAN mode is enabled + * @hw: pointer to the HW structure + * + * Returns true if the hardware/firmware is configured in double VLAN mode, + * else return false signaling that the hardware/firmware is configured in + * single VLAN mode. + * + * Also, return false if this call fails for any reason (i.e. firmware doesn't + * support this AQ call). + */ +static bool ice_aq_is_dvm_ena(struct ice_hw *hw) +{ + struct ice_aqc_get_vlan_mode get_params = { 0 }; + int status; + + status = ice_aq_get_vlan_mode(hw, &get_params); + if (status) { + ice_debug(hw, ICE_DBG_AQ, "Failed to get VLAN mode, status %d\n", + status); + return false; + } + + return (get_params.vlan_mode & ICE_AQ_VLAN_MODE_DVM_ENA); +} + +/** + * ice_is_dvm_ena - check if double VLAN mode is enabled + * @hw: pointer to the HW structure + * + * The device is configured in single or double VLAN mode on initialization and + * this cannot be dynamically changed during runtime. Based on this there is no + * need to make an AQ call every time the driver needs to know the VLAN mode. + * Instead, use the cached VLAN mode. + */ +bool ice_is_dvm_ena(struct ice_hw *hw) +{ + return hw->dvm_ena; +} + +/** + * ice_cache_vlan_mode - cache VLAN mode after DDP is downloaded + * @hw: pointer to the HW structure + * + * This is only called after downloading the DDP and after the global + * configuration lock has been released because all ports on a device need to + * cache the VLAN mode. + */ +static void ice_cache_vlan_mode(struct ice_hw *hw) +{ + hw->dvm_ena = ice_aq_is_dvm_ena(hw) ? true : false; +} + +/** + * ice_pkg_supports_dvm - find out if DDP supports DVM + * @hw: pointer to the HW structure + */ +static bool ice_pkg_supports_dvm(struct ice_hw *hw) +{ + bool pkg_supports_dvm; + int status; + + status = ice_pkg_get_supported_vlan_mode(hw, &pkg_supports_dvm); + if (status) { + ice_debug(hw, ICE_DBG_PKG, "Failed to get supported VLAN mode, status %d\n", + status); + return false; + } + + return pkg_supports_dvm; +} + +/** + * ice_fw_supports_dvm - find out if FW supports DVM + * @hw: pointer to the HW structure + */ +static bool ice_fw_supports_dvm(struct ice_hw *hw) +{ + struct ice_aqc_get_vlan_mode get_vlan_mode = { 0 }; + int status; + + /* If firmware returns success, then it supports DVM, else it only + * supports SVM + */ + status = ice_aq_get_vlan_mode(hw, &get_vlan_mode); + if (status) { + ice_debug(hw, ICE_DBG_NVM, "Failed to get VLAN mode, status %d\n", + status); + return false; + } + + return true; +} + +/** + * ice_is_dvm_supported - check if Double VLAN Mode is supported + * @hw: pointer to the hardware structure + * + * Returns true if Double VLAN Mode (DVM) is supported and false if only Single + * VLAN Mode (SVM) is supported. In order for DVM to be supported the DDP and + * firmware must support it, otherwise only SVM is supported. This function + * should only be called while the global config lock is held and after the + * package has been successfully downloaded. + */ +static bool ice_is_dvm_supported(struct ice_hw *hw) +{ + if (!ice_pkg_supports_dvm(hw)) { + ice_debug(hw, ICE_DBG_PKG, "DDP doesn't support DVM\n"); + return false; + } + + if (!ice_fw_supports_dvm(hw)) { + ice_debug(hw, ICE_DBG_PKG, "FW doesn't support DVM\n"); + return false; + } + + return true; +} + +#define ICE_EXTERNAL_VLAN_ID_FV_IDX 11 +#define ICE_SW_LKUP_VLAN_LOC_LKUP_IDX 1 +#define ICE_SW_LKUP_VLAN_PKT_FLAGS_LKUP_IDX 2 +#define ICE_SW_LKUP_PROMISC_VLAN_LOC_LKUP_IDX 2 +#define ICE_PKT_FLAGS_0_TO_15_FV_IDX 1 +#define ICE_PKT_FLAGS_0_TO_15_VLAN_FLAGS_MASK 0xD000 +static struct ice_update_recipe_lkup_idx_params ice_dvm_dflt_recipes[] = { + { + /* Update recipe ICE_SW_LKUP_VLAN to filter based on the + * outer/single VLAN in DVM + */ + .rid = ICE_SW_LKUP_VLAN, + .fv_idx = ICE_EXTERNAL_VLAN_ID_FV_IDX, + .ignore_valid = true, + .mask = 0, + .mask_valid = false, /* use pre-existing mask */ + .lkup_idx = ICE_SW_LKUP_VLAN_LOC_LKUP_IDX, + }, + { + /* Update recipe ICE_SW_LKUP_VLAN to filter based on the VLAN + * packet flags to support VLAN filtering on multiple VLAN + * ethertypes (i.e. 0x8100 and 0x88a8) in DVM + */ + .rid = ICE_SW_LKUP_VLAN, + .fv_idx = ICE_PKT_FLAGS_0_TO_15_FV_IDX, + .ignore_valid = false, + .mask = ICE_PKT_FLAGS_0_TO_15_VLAN_FLAGS_MASK, + .mask_valid = true, + .lkup_idx = ICE_SW_LKUP_VLAN_PKT_FLAGS_LKUP_IDX, + }, + { + /* Update recipe ICE_SW_LKUP_PROMISC_VLAN to filter based on the + * outer/single VLAN in DVM + */ + .rid = ICE_SW_LKUP_PROMISC_VLAN, + .fv_idx = ICE_EXTERNAL_VLAN_ID_FV_IDX, + .ignore_valid = true, + .mask = 0, + .mask_valid = false, /* use pre-existing mask */ + .lkup_idx = ICE_SW_LKUP_PROMISC_VLAN_LOC_LKUP_IDX, + }, +}; + +/** + * ice_dvm_update_dflt_recipes - update default switch recipes in DVM + * @hw: hardware structure used to update the recipes + */ +static int ice_dvm_update_dflt_recipes(struct ice_hw *hw) +{ + unsigned long i; + + for (i = 0; i < ARRAY_SIZE(ice_dvm_dflt_recipes); i++) { + struct ice_update_recipe_lkup_idx_params *params; + int status; + + params = &ice_dvm_dflt_recipes[i]; + + status = ice_update_recipe_lkup_idx(hw, params); + if (status) { + ice_debug(hw, ICE_DBG_INIT, "Failed to update RID %d lkup_idx %d fv_idx %d mask_valid %s mask 0x%04x\n", + params->rid, params->lkup_idx, params->fv_idx, + params->mask_valid ? "true" : "false", + params->mask); + return status; + } + } + + return 0; +} + +/** + * ice_aq_set_vlan_mode - set the VLAN mode of the device + * @hw: pointer to the HW structure + * @set_params: requested VLAN mode configuration + * + * Set VLAN Mode Parameters (0x020C) + */ +static int +ice_aq_set_vlan_mode(struct ice_hw *hw, + struct ice_aqc_set_vlan_mode *set_params) +{ + u8 rdma_packet, mng_vlan_prot_id; + struct ice_aq_desc desc; + + if (!set_params) + return -EINVAL; + + if (set_params->l2tag_prio_tagging > ICE_AQ_VLAN_PRIO_TAG_MAX) + return -EINVAL; + + rdma_packet = set_params->rdma_packet; + if (rdma_packet != ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING && + rdma_packet != ICE_AQ_DVM_VLAN_RDMA_PKT_FLAG_SETTING) + return -EINVAL; + + mng_vlan_prot_id = set_params->mng_vlan_prot_id; + if (mng_vlan_prot_id != ICE_AQ_VLAN_MNG_PROTOCOL_ID_OUTER && + mng_vlan_prot_id != ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER) + return -EINVAL; + + ice_fill_dflt_direct_cmd_desc(&desc, + ice_aqc_opc_set_vlan_mode_parameters); + desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD); + + return ice_aq_send_cmd(hw, &desc, set_params, sizeof(*set_params), + NULL); +} + +/** + * ice_set_dvm - sets up software and hardware for double VLAN mode + * @hw: pointer to the hardware structure + */ +static int ice_set_dvm(struct ice_hw *hw) +{ + struct ice_aqc_set_vlan_mode params = { 0 }; + int status; + + params.l2tag_prio_tagging = ICE_AQ_VLAN_PRIO_TAG_OUTER_CTAG; + params.rdma_packet = ICE_AQ_DVM_VLAN_RDMA_PKT_FLAG_SETTING; + params.mng_vlan_prot_id = ICE_AQ_VLAN_MNG_PROTOCOL_ID_OUTER; + + status = ice_aq_set_vlan_mode(hw, ¶ms); + if (status) { + ice_debug(hw, ICE_DBG_INIT, "Failed to set double VLAN mode parameters, status %d\n", + status); + return status; + } + + status = ice_dvm_update_dflt_recipes(hw); + if (status) { + ice_debug(hw, ICE_DBG_INIT, "Failed to update default recipes for double VLAN mode, status %d\n", + status); + return status; + } + + status = ice_aq_set_port_params(hw->port_info, true, NULL); + if (status) { + ice_debug(hw, ICE_DBG_INIT, "Failed to set port in double VLAN mode, status %d\n", + status); + return status; + } + + status = ice_set_dvm_boost_entries(hw); + if (status) { + ice_debug(hw, ICE_DBG_INIT, "Failed to set boost TCAM entries for double VLAN mode, status %d\n", + status); + return status; + } + + return 0; +} + +/** + * ice_set_svm - set single VLAN mode + * @hw: pointer to the HW structure + */ +static int ice_set_svm(struct ice_hw *hw) +{ + struct ice_aqc_set_vlan_mode *set_params; + int status; + + status = ice_aq_set_port_params(hw->port_info, false, NULL); + if (status) { + ice_debug(hw, ICE_DBG_INIT, "Failed to set port parameters for single VLAN mode\n"); + return status; + } + + set_params = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*set_params), + GFP_KERNEL); + if (!set_params) + return -ENOMEM; + + /* default configuration for SVM configurations */ + set_params->l2tag_prio_tagging = ICE_AQ_VLAN_PRIO_TAG_INNER_CTAG; + set_params->rdma_packet = ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING; + set_params->mng_vlan_prot_id = ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER; + + status = ice_aq_set_vlan_mode(hw, set_params); + if (status) + ice_debug(hw, ICE_DBG_INIT, "Failed to configure port in single VLAN mode\n"); + + devm_kfree(ice_hw_to_dev(hw), set_params); + return status; +} + +/** + * ice_set_vlan_mode + * @hw: pointer to the HW structure + */ +int ice_set_vlan_mode(struct ice_hw *hw) +{ + if (!ice_is_dvm_supported(hw)) + return 0; + + if (!ice_set_dvm(hw)) + return 0; + + return ice_set_svm(hw); +} + +/** + * ice_print_dvm_not_supported - print if DDP and/or FW doesn't support DVM + * @hw: pointer to the HW structure + * + * The purpose of this function is to print that QinQ is not supported due to + * incompatibilty from the DDP and/or FW. This will give a hint to the user to + * update one and/or both components if they expect QinQ functionality. + */ +static void ice_print_dvm_not_supported(struct ice_hw *hw) +{ + bool pkg_supports_dvm = ice_pkg_supports_dvm(hw); + bool fw_supports_dvm = ice_fw_supports_dvm(hw); + + if (!fw_supports_dvm && !pkg_supports_dvm) + dev_info(ice_hw_to_dev(hw), "QinQ functionality cannot be enabled on this device. Update your DDP package and NVM to versions that support QinQ.\n"); + else if (!pkg_supports_dvm) + dev_info(ice_hw_to_dev(hw), "QinQ functionality cannot be enabled on this device. Update your DDP package to a version that supports QinQ.\n"); + else if (!fw_supports_dvm) + dev_info(ice_hw_to_dev(hw), "QinQ functionality cannot be enabled on this device. Update your NVM to a version that supports QinQ.\n"); +} + +/** + * ice_post_pkg_dwnld_vlan_mode_cfg - configure VLAN mode after DDP download + * @hw: pointer to the HW structure + * + * This function is meant to configure any VLAN mode specific functionality + * after the global configuration lock has been released and the DDP has been + * downloaded. + * + * Since only one PF downloads the DDP and configures the VLAN mode there needs + * to be a way to configure the other PFs after the DDP has been downloaded and + * the global configuration lock has been released. All such code should go in + * this function. + */ +void ice_post_pkg_dwnld_vlan_mode_cfg(struct ice_hw *hw) +{ + ice_cache_vlan_mode(hw); + + if (ice_is_dvm_ena(hw)) + ice_change_proto_id_to_dvm(); + else + ice_print_dvm_not_supported(hw); +} diff --git a/drivers/net/ethernet/intel/ice/ice_vlan_mode.h b/drivers/net/ethernet/intel/ice/ice_vlan_mode.h new file mode 100644 index 000000000000..a0fb743d08e2 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vlan_mode.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019-2021, Intel Corporation. */ + +#ifndef _ICE_VLAN_MODE_H_ +#define _ICE_VLAN_MODE_H_ + +struct ice_hw; + +bool ice_is_dvm_ena(struct ice_hw *hw); +int ice_set_vlan_mode(struct ice_hw *hw); +void ice_post_pkg_dwnld_vlan_mode_cfg(struct ice_hw *hw); + +#endif /* _ICE_VLAN_MODE_H */ diff --git a/drivers/net/ethernet/intel/ice/ice_vsi_vlan_lib.c b/drivers/net/ethernet/intel/ice/ice_vsi_vlan_lib.c new file mode 100644 index 000000000000..5b4a0abb4607 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vsi_vlan_lib.c @@ -0,0 +1,707 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2019-2021, Intel Corporation. */ + +#include "ice_vsi_vlan_lib.h" +#include "ice_lib.h" +#include "ice_fltr.h" +#include "ice.h" + +static void print_invalid_tpid(struct ice_vsi *vsi, u16 tpid) +{ + dev_err(ice_pf_to_dev(vsi->back), "%s %d specified invalid VLAN tpid 0x%04x\n", + ice_vsi_type_str(vsi->type), vsi->idx, tpid); +} + +/** + * validate_vlan - check if the ice_vlan passed in is valid + * @vsi: VSI used for printing error message + * @vlan: ice_vlan structure to validate + * + * Return true if the VLAN TPID is valid or if the VLAN TPID is 0 and the VLAN + * VID is 0, which allows for non-zero VLAN filters with the specified VLAN TPID + * and untagged VLAN 0 filters to be added to the prune list respectively. + */ +static bool validate_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan) +{ + if (vlan->tpid != ETH_P_8021Q && vlan->tpid != ETH_P_8021AD && + vlan->tpid != ETH_P_QINQ1 && (vlan->tpid || vlan->vid)) { + print_invalid_tpid(vsi, vlan->tpid); + return false; + } + + return true; +} + +/** + * ice_vsi_add_vlan - default add VLAN implementation for all VSI types + * @vsi: VSI being configured + * @vlan: VLAN filter to add + */ +int ice_vsi_add_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan) +{ + int err; + + if (!validate_vlan(vsi, vlan)) + return -EINVAL; + + err = ice_fltr_add_vlan(vsi, vlan); + if (err && err != -EEXIST) { + dev_err(ice_pf_to_dev(vsi->back), "Failure Adding VLAN %d on VSI %i, status %d\n", + vlan->vid, vsi->vsi_num, err); + return err; + } + + vsi->num_vlan++; + return 0; +} + +/** + * ice_vsi_del_vlan - default del VLAN implementation for all VSI types + * @vsi: VSI being configured + * @vlan: VLAN filter to delete + */ +int ice_vsi_del_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan) +{ + struct ice_pf *pf = vsi->back; + struct device *dev; + int err; + + if (!validate_vlan(vsi, vlan)) + return -EINVAL; + + dev = ice_pf_to_dev(pf); + + err = ice_fltr_remove_vlan(vsi, vlan); + if (!err) + vsi->num_vlan--; + else if (err == -ENOENT || err == -EBUSY) + err = 0; + else + dev_err(dev, "Error removing VLAN %d on VSI %i error: %d\n", + vlan->vid, vsi->vsi_num, err); + + return err; +} + +/** + * ice_vsi_manage_vlan_insertion - Manage VLAN insertion for the VSI for Tx + * @vsi: the VSI being changed + */ +static int ice_vsi_manage_vlan_insertion(struct ice_vsi *vsi) +{ + struct ice_hw *hw = &vsi->back->hw; + struct ice_vsi_ctx *ctxt; + int err; + + ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + return -ENOMEM; + + /* Here we are configuring the VSI to let the driver add VLAN tags by + * setting inner_vlan_flags to ICE_AQ_VSI_INNER_VLAN_TX_MODE_ALL. The actual VLAN tag + * insertion happens in the Tx hot path, in ice_tx_map. + */ + ctxt->info.inner_vlan_flags = ICE_AQ_VSI_INNER_VLAN_TX_MODE_ALL; + + /* Preserve existing VLAN strip setting */ + ctxt->info.inner_vlan_flags |= (vsi->info.inner_vlan_flags & + ICE_AQ_VSI_INNER_VLAN_EMODE_M); + + ctxt->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_VLAN_VALID); + + err = ice_update_vsi(hw, vsi->idx, ctxt, NULL); + if (err) { + dev_err(ice_pf_to_dev(vsi->back), "update VSI for VLAN insert failed, err %d aq_err %s\n", + err, ice_aq_str(hw->adminq.sq_last_status)); + goto out; + } + + vsi->info.inner_vlan_flags = ctxt->info.inner_vlan_flags; +out: + kfree(ctxt); + return err; +} + +/** + * ice_vsi_manage_vlan_stripping - Manage VLAN stripping for the VSI for Rx + * @vsi: the VSI being changed + * @ena: boolean value indicating if this is a enable or disable request + */ +static int ice_vsi_manage_vlan_stripping(struct ice_vsi *vsi, bool ena) +{ + struct ice_hw *hw = &vsi->back->hw; + struct ice_vsi_ctx *ctxt; + int err; + + /* do not allow modifying VLAN stripping when a port VLAN is configured + * on this VSI + */ + if (vsi->info.port_based_inner_vlan) + return 0; + + ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + return -ENOMEM; + + /* Here we are configuring what the VSI should do with the VLAN tag in + * the Rx packet. We can either leave the tag in the packet or put it in + * the Rx descriptor. + */ + if (ena) + /* Strip VLAN tag from Rx packet and put it in the desc */ + ctxt->info.inner_vlan_flags = ICE_AQ_VSI_INNER_VLAN_EMODE_STR_BOTH; + else + /* Disable stripping. Leave tag in packet */ + ctxt->info.inner_vlan_flags = ICE_AQ_VSI_INNER_VLAN_EMODE_NOTHING; + + /* Allow all packets untagged/tagged */ + ctxt->info.inner_vlan_flags |= ICE_AQ_VSI_INNER_VLAN_TX_MODE_ALL; + + ctxt->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_VLAN_VALID); + + err = ice_update_vsi(hw, vsi->idx, ctxt, NULL); + if (err) { + dev_err(ice_pf_to_dev(vsi->back), "update VSI for VLAN strip failed, ena = %d err %d aq_err %s\n", + ena, err, ice_aq_str(hw->adminq.sq_last_status)); + goto out; + } + + vsi->info.inner_vlan_flags = ctxt->info.inner_vlan_flags; +out: + kfree(ctxt); + return err; +} + +int ice_vsi_ena_inner_stripping(struct ice_vsi *vsi, const u16 tpid) +{ + if (tpid != ETH_P_8021Q) { + print_invalid_tpid(vsi, tpid); + return -EINVAL; + } + + return ice_vsi_manage_vlan_stripping(vsi, true); +} + +int ice_vsi_dis_inner_stripping(struct ice_vsi *vsi) +{ + return ice_vsi_manage_vlan_stripping(vsi, false); +} + +int ice_vsi_ena_inner_insertion(struct ice_vsi *vsi, const u16 tpid) +{ + if (tpid != ETH_P_8021Q) { + print_invalid_tpid(vsi, tpid); + return -EINVAL; + } + + return ice_vsi_manage_vlan_insertion(vsi); +} + +int ice_vsi_dis_inner_insertion(struct ice_vsi *vsi) +{ + return ice_vsi_manage_vlan_insertion(vsi); +} + +/** + * __ice_vsi_set_inner_port_vlan - set port VLAN VSI context settings to enable a port VLAN + * @vsi: the VSI to update + * @pvid_info: VLAN ID and QoS used to set the PVID VSI context field + */ +static int __ice_vsi_set_inner_port_vlan(struct ice_vsi *vsi, u16 pvid_info) +{ + struct ice_hw *hw = &vsi->back->hw; + struct ice_aqc_vsi_props *info; + struct ice_vsi_ctx *ctxt; + int ret; + + ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + return -ENOMEM; + + ctxt->info = vsi->info; + info = &ctxt->info; + info->inner_vlan_flags = ICE_AQ_VSI_INNER_VLAN_TX_MODE_ACCEPTUNTAGGED | + ICE_AQ_VSI_INNER_VLAN_INSERT_PVID | + ICE_AQ_VSI_INNER_VLAN_EMODE_STR; + info->sw_flags2 |= ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA; + + info->port_based_inner_vlan = cpu_to_le16(pvid_info); + info->valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_VLAN_VALID | + ICE_AQ_VSI_PROP_SW_VALID); + + ret = ice_update_vsi(hw, vsi->idx, ctxt, NULL); + if (ret) { + dev_info(ice_hw_to_dev(hw), "update VSI for port VLAN failed, err %d aq_err %s\n", + ret, ice_aq_str(hw->adminq.sq_last_status)); + goto out; + } + + vsi->info.inner_vlan_flags = info->inner_vlan_flags; + vsi->info.sw_flags2 = info->sw_flags2; + vsi->info.port_based_inner_vlan = info->port_based_inner_vlan; +out: + kfree(ctxt); + return ret; +} + +int ice_vsi_set_inner_port_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan) +{ + u16 port_vlan_info; + + if (vlan->tpid != ETH_P_8021Q) + return -EINVAL; + + if (vlan->prio > 7) + return -EINVAL; + + port_vlan_info = vlan->vid | (vlan->prio << VLAN_PRIO_SHIFT); + + return __ice_vsi_set_inner_port_vlan(vsi, port_vlan_info); +} + +/** + * ice_cfg_vlan_pruning - enable or disable VLAN pruning on the VSI + * @vsi: VSI to enable or disable VLAN pruning on + * @ena: set to true to enable VLAN pruning and false to disable it + * + * returns 0 if VSI is updated, negative otherwise + */ +static int ice_cfg_vlan_pruning(struct ice_vsi *vsi, bool ena) +{ + struct ice_vsi_ctx *ctxt; + struct ice_pf *pf; + int status; + + if (!vsi) + return -EINVAL; + + /* Don't enable VLAN pruning if the netdev is currently in promiscuous + * mode. VLAN pruning will be enabled when the interface exits + * promiscuous mode if any VLAN filters are active. + */ + if (vsi->netdev && vsi->netdev->flags & IFF_PROMISC && ena) + return 0; + + pf = vsi->back; + ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + return -ENOMEM; + + ctxt->info = vsi->info; + + if (ena) + ctxt->info.sw_flags2 |= ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA; + else + ctxt->info.sw_flags2 &= ~ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA; + + ctxt->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_SW_VALID); + + status = ice_update_vsi(&pf->hw, vsi->idx, ctxt, NULL); + if (status) { + netdev_err(vsi->netdev, "%sabling VLAN pruning on VSI handle: %d, VSI HW ID: %d failed, err = %d, aq_err = %s\n", + ena ? "En" : "Dis", vsi->idx, vsi->vsi_num, status, + ice_aq_str(pf->hw.adminq.sq_last_status)); + goto err_out; + } + + vsi->info.sw_flags2 = ctxt->info.sw_flags2; + + kfree(ctxt); + return 0; + +err_out: + kfree(ctxt); + return status; +} + +int ice_vsi_ena_rx_vlan_filtering(struct ice_vsi *vsi) +{ + return ice_cfg_vlan_pruning(vsi, true); +} + +int ice_vsi_dis_rx_vlan_filtering(struct ice_vsi *vsi) +{ + return ice_cfg_vlan_pruning(vsi, false); +} + +static int ice_cfg_vlan_antispoof(struct ice_vsi *vsi, bool enable) +{ + struct ice_vsi_ctx *ctx; + int err; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->info.sec_flags = vsi->info.sec_flags; + ctx->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_SECURITY_VALID); + + if (enable) + ctx->info.sec_flags |= ICE_AQ_VSI_SEC_TX_VLAN_PRUNE_ENA << + ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S; + else + ctx->info.sec_flags &= ~(ICE_AQ_VSI_SEC_TX_VLAN_PRUNE_ENA << + ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S); + + err = ice_update_vsi(&vsi->back->hw, vsi->idx, ctx, NULL); + if (err) + dev_err(ice_pf_to_dev(vsi->back), "Failed to configure Tx VLAN anti-spoof %s for VSI %d, error %d\n", + enable ? "ON" : "OFF", vsi->vsi_num, err); + else + vsi->info.sec_flags = ctx->info.sec_flags; + + kfree(ctx); + + return err; +} + +int ice_vsi_ena_tx_vlan_filtering(struct ice_vsi *vsi) +{ + return ice_cfg_vlan_antispoof(vsi, true); +} + +int ice_vsi_dis_tx_vlan_filtering(struct ice_vsi *vsi) +{ + return ice_cfg_vlan_antispoof(vsi, false); +} + +/** + * tpid_to_vsi_outer_vlan_type - convert from TPID to VSI context based tag_type + * @tpid: tpid used to translate into VSI context based tag_type + * @tag_type: output variable to hold the VSI context based tag type + */ +static int tpid_to_vsi_outer_vlan_type(u16 tpid, u8 *tag_type) +{ + switch (tpid) { + case ETH_P_8021Q: + *tag_type = ICE_AQ_VSI_OUTER_TAG_VLAN_8100; + break; + case ETH_P_8021AD: + *tag_type = ICE_AQ_VSI_OUTER_TAG_STAG; + break; + case ETH_P_QINQ1: + *tag_type = ICE_AQ_VSI_OUTER_TAG_VLAN_9100; + break; + default: + *tag_type = 0; + return -EINVAL; + } + + return 0; +} + +/** + * ice_vsi_ena_outer_stripping - enable outer VLAN stripping + * @vsi: VSI to configure + * @tpid: TPID to enable outer VLAN stripping for + * + * Enable outer VLAN stripping via VSI context. This function should only be + * used if DVM is supported. Also, this function should never be called directly + * as it should be part of ice_vsi_vlan_ops if it's needed. + * + * Since the VSI context only supports a single TPID for insertion and + * stripping, setting the TPID for stripping will affect the TPID for insertion. + * Callers need to be aware of this limitation. + * + * Only modify outer VLAN stripping settings and the VLAN TPID. Outer VLAN + * insertion settings are unmodified. + * + * This enables hardware to strip a VLAN tag with the specified TPID to be + * stripped from the packet and placed in the receive descriptor. + */ +int ice_vsi_ena_outer_stripping(struct ice_vsi *vsi, u16 tpid) +{ + struct ice_hw *hw = &vsi->back->hw; + struct ice_vsi_ctx *ctxt; + u8 tag_type; + int err; + + /* do not allow modifying VLAN stripping when a port VLAN is configured + * on this VSI + */ + if (vsi->info.port_based_outer_vlan) + return 0; + + if (tpid_to_vsi_outer_vlan_type(tpid, &tag_type)) + return -EINVAL; + + ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + return -ENOMEM; + + ctxt->info.valid_sections = + cpu_to_le16(ICE_AQ_VSI_PROP_OUTER_TAG_VALID); + /* clear current outer VLAN strip settings */ + ctxt->info.outer_vlan_flags = vsi->info.outer_vlan_flags & + ~(ICE_AQ_VSI_OUTER_VLAN_EMODE_M | ICE_AQ_VSI_OUTER_TAG_TYPE_M); + ctxt->info.outer_vlan_flags |= + ((ICE_AQ_VSI_OUTER_VLAN_EMODE_SHOW_BOTH << + ICE_AQ_VSI_OUTER_VLAN_EMODE_S) | + ((tag_type << ICE_AQ_VSI_OUTER_TAG_TYPE_S) & + ICE_AQ_VSI_OUTER_TAG_TYPE_M)); + + err = ice_update_vsi(hw, vsi->idx, ctxt, NULL); + if (err) + dev_err(ice_pf_to_dev(vsi->back), "update VSI for enabling outer VLAN stripping failed, err %d aq_err %s\n", + err, ice_aq_str(hw->adminq.sq_last_status)); + else + vsi->info.outer_vlan_flags = ctxt->info.outer_vlan_flags; + + kfree(ctxt); + return err; +} + +/** + * ice_vsi_dis_outer_stripping - disable outer VLAN stripping + * @vsi: VSI to configure + * + * Disable outer VLAN stripping via VSI context. This function should only be + * used if DVM is supported. Also, this function should never be called directly + * as it should be part of ice_vsi_vlan_ops if it's needed. + * + * Only modify the outer VLAN stripping settings. The VLAN TPID and outer VLAN + * insertion settings are unmodified. + * + * This tells the hardware to not strip any VLAN tagged packets, thus leaving + * them in the packet. This enables software offloaded VLAN stripping and + * disables hardware offloaded VLAN stripping. + */ +int ice_vsi_dis_outer_stripping(struct ice_vsi *vsi) +{ + struct ice_hw *hw = &vsi->back->hw; + struct ice_vsi_ctx *ctxt; + int err; + + if (vsi->info.port_based_outer_vlan) + return 0; + + ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + return -ENOMEM; + + ctxt->info.valid_sections = + cpu_to_le16(ICE_AQ_VSI_PROP_OUTER_TAG_VALID); + /* clear current outer VLAN strip settings */ + ctxt->info.outer_vlan_flags = vsi->info.outer_vlan_flags & + ~ICE_AQ_VSI_OUTER_VLAN_EMODE_M; + ctxt->info.outer_vlan_flags |= ICE_AQ_VSI_OUTER_VLAN_EMODE_NOTHING << + ICE_AQ_VSI_OUTER_VLAN_EMODE_S; + + err = ice_update_vsi(hw, vsi->idx, ctxt, NULL); + if (err) + dev_err(ice_pf_to_dev(vsi->back), "update VSI for disabling outer VLAN stripping failed, err %d aq_err %s\n", + err, ice_aq_str(hw->adminq.sq_last_status)); + else + vsi->info.outer_vlan_flags = ctxt->info.outer_vlan_flags; + + kfree(ctxt); + return err; +} + +/** + * ice_vsi_ena_outer_insertion - enable outer VLAN insertion + * @vsi: VSI to configure + * @tpid: TPID to enable outer VLAN insertion for + * + * Enable outer VLAN insertion via VSI context. This function should only be + * used if DVM is supported. Also, this function should never be called directly + * as it should be part of ice_vsi_vlan_ops if it's needed. + * + * Since the VSI context only supports a single TPID for insertion and + * stripping, setting the TPID for insertion will affect the TPID for stripping. + * Callers need to be aware of this limitation. + * + * Only modify outer VLAN insertion settings and the VLAN TPID. Outer VLAN + * stripping settings are unmodified. + * + * This allows a VLAN tag with the specified TPID to be inserted in the transmit + * descriptor. + */ +int ice_vsi_ena_outer_insertion(struct ice_vsi *vsi, u16 tpid) +{ + struct ice_hw *hw = &vsi->back->hw; + struct ice_vsi_ctx *ctxt; + u8 tag_type; + int err; + + if (vsi->info.port_based_outer_vlan) + return 0; + + if (tpid_to_vsi_outer_vlan_type(tpid, &tag_type)) + return -EINVAL; + + ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + return -ENOMEM; + + ctxt->info.valid_sections = + cpu_to_le16(ICE_AQ_VSI_PROP_OUTER_TAG_VALID); + /* clear current outer VLAN insertion settings */ + ctxt->info.outer_vlan_flags = vsi->info.outer_vlan_flags & + ~(ICE_AQ_VSI_OUTER_VLAN_PORT_BASED_INSERT | + ICE_AQ_VSI_OUTER_VLAN_BLOCK_TX_DESC | + ICE_AQ_VSI_OUTER_VLAN_TX_MODE_M | + ICE_AQ_VSI_OUTER_TAG_TYPE_M); + ctxt->info.outer_vlan_flags |= + ((ICE_AQ_VSI_OUTER_VLAN_TX_MODE_ALL << + ICE_AQ_VSI_OUTER_VLAN_TX_MODE_S) & + ICE_AQ_VSI_OUTER_VLAN_TX_MODE_M) | + ((tag_type << ICE_AQ_VSI_OUTER_TAG_TYPE_S) & + ICE_AQ_VSI_OUTER_TAG_TYPE_M); + + err = ice_update_vsi(hw, vsi->idx, ctxt, NULL); + if (err) + dev_err(ice_pf_to_dev(vsi->back), "update VSI for enabling outer VLAN insertion failed, err %d aq_err %s\n", + err, ice_aq_str(hw->adminq.sq_last_status)); + else + vsi->info.outer_vlan_flags = ctxt->info.outer_vlan_flags; + + kfree(ctxt); + return err; +} + +/** + * ice_vsi_dis_outer_insertion - disable outer VLAN insertion + * @vsi: VSI to configure + * + * Disable outer VLAN insertion via VSI context. This function should only be + * used if DVM is supported. Also, this function should never be called directly + * as it should be part of ice_vsi_vlan_ops if it's needed. + * + * Only modify the outer VLAN insertion settings. The VLAN TPID and outer VLAN + * settings are unmodified. + * + * This tells the hardware to not allow any VLAN tagged packets in the transmit + * descriptor. This enables software offloaded VLAN insertion and disables + * hardware offloaded VLAN insertion. + */ +int ice_vsi_dis_outer_insertion(struct ice_vsi *vsi) +{ + struct ice_hw *hw = &vsi->back->hw; + struct ice_vsi_ctx *ctxt; + int err; + + if (vsi->info.port_based_outer_vlan) + return 0; + + ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + return -ENOMEM; + + ctxt->info.valid_sections = + cpu_to_le16(ICE_AQ_VSI_PROP_OUTER_TAG_VALID); + /* clear current outer VLAN insertion settings */ + ctxt->info.outer_vlan_flags = vsi->info.outer_vlan_flags & + ~(ICE_AQ_VSI_OUTER_VLAN_PORT_BASED_INSERT | + ICE_AQ_VSI_OUTER_VLAN_TX_MODE_M); + ctxt->info.outer_vlan_flags |= + ICE_AQ_VSI_OUTER_VLAN_BLOCK_TX_DESC | + ((ICE_AQ_VSI_OUTER_VLAN_TX_MODE_ALL << + ICE_AQ_VSI_OUTER_VLAN_TX_MODE_S) & + ICE_AQ_VSI_OUTER_VLAN_TX_MODE_M); + + err = ice_update_vsi(hw, vsi->idx, ctxt, NULL); + if (err) + dev_err(ice_pf_to_dev(vsi->back), "update VSI for disabling outer VLAN insertion failed, err %d aq_err %s\n", + err, ice_aq_str(hw->adminq.sq_last_status)); + else + vsi->info.outer_vlan_flags = ctxt->info.outer_vlan_flags; + + kfree(ctxt); + return err; +} + +/** + * __ice_vsi_set_outer_port_vlan - set the outer port VLAN and related settings + * @vsi: VSI to configure + * @vlan_info: packed u16 that contains the VLAN prio and ID + * @tpid: TPID of the port VLAN + * + * Set the port VLAN prio, ID, and TPID. + * + * Enable VLAN pruning so the VSI doesn't receive any traffic that doesn't match + * a VLAN prune rule. The caller should take care to add a VLAN prune rule that + * matches the port VLAN ID and TPID. + * + * Tell hardware to strip outer VLAN tagged packets on receive and don't put + * them in the receive descriptor. VSI(s) in port VLANs should not be aware of + * the port VLAN ID or TPID they are assigned to. + * + * Tell hardware to prevent outer VLAN tag insertion on transmit and only allow + * untagged outer packets from the transmit descriptor. + * + * Also, tell the hardware to insert the port VLAN on transmit. + */ +static int +__ice_vsi_set_outer_port_vlan(struct ice_vsi *vsi, u16 vlan_info, u16 tpid) +{ + struct ice_hw *hw = &vsi->back->hw; + struct ice_vsi_ctx *ctxt; + u8 tag_type; + int err; + + if (tpid_to_vsi_outer_vlan_type(tpid, &tag_type)) + return -EINVAL; + + ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + return -ENOMEM; + + ctxt->info = vsi->info; + + ctxt->info.sw_flags2 |= ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA; + + ctxt->info.port_based_outer_vlan = cpu_to_le16(vlan_info); + ctxt->info.outer_vlan_flags = + (ICE_AQ_VSI_OUTER_VLAN_EMODE_SHOW << + ICE_AQ_VSI_OUTER_VLAN_EMODE_S) | + ((tag_type << ICE_AQ_VSI_OUTER_TAG_TYPE_S) & + ICE_AQ_VSI_OUTER_TAG_TYPE_M) | + ICE_AQ_VSI_OUTER_VLAN_BLOCK_TX_DESC | + (ICE_AQ_VSI_OUTER_VLAN_TX_MODE_ACCEPTUNTAGGED << + ICE_AQ_VSI_OUTER_VLAN_TX_MODE_S) | + ICE_AQ_VSI_OUTER_VLAN_PORT_BASED_INSERT; + + ctxt->info.valid_sections = + cpu_to_le16(ICE_AQ_VSI_PROP_OUTER_TAG_VALID | + ICE_AQ_VSI_PROP_SW_VALID); + + err = ice_update_vsi(hw, vsi->idx, ctxt, NULL); + if (err) { + dev_err(ice_pf_to_dev(vsi->back), "update VSI for setting outer port based VLAN failed, err %d aq_err %s\n", + err, ice_aq_str(hw->adminq.sq_last_status)); + } else { + vsi->info.port_based_outer_vlan = ctxt->info.port_based_outer_vlan; + vsi->info.outer_vlan_flags = ctxt->info.outer_vlan_flags; + vsi->info.sw_flags2 = ctxt->info.sw_flags2; + } + + kfree(ctxt); + return err; +} + +/** + * ice_vsi_set_outer_port_vlan - public version of __ice_vsi_set_outer_port_vlan + * @vsi: VSI to configure + * @vlan: ice_vlan structure used to set the port VLAN + * + * Set the outer port VLAN via VSI context. This function should only be + * used if DVM is supported. Also, this function should never be called directly + * as it should be part of ice_vsi_vlan_ops if it's needed. + * + * This function does not support clearing the port VLAN as there is currently + * no use case for this. + * + * Use the ice_vlan structure passed in to set this VSI in a port VLAN. + */ +int ice_vsi_set_outer_port_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan) +{ + u16 port_vlan_info; + + if (vlan->prio > (VLAN_PRIO_MASK >> VLAN_PRIO_SHIFT)) + return -EINVAL; + + port_vlan_info = vlan->vid | (vlan->prio << VLAN_PRIO_SHIFT); + + return __ice_vsi_set_outer_port_vlan(vsi, port_vlan_info, vlan->tpid); +} diff --git a/drivers/net/ethernet/intel/ice/ice_vsi_vlan_lib.h b/drivers/net/ethernet/intel/ice/ice_vsi_vlan_lib.h new file mode 100644 index 000000000000..f459909490ec --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vsi_vlan_lib.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019-2021, Intel Corporation. */ + +#ifndef _ICE_VSI_VLAN_LIB_H_ +#define _ICE_VSI_VLAN_LIB_H_ + +#include +#include "ice_vlan.h" + +struct ice_vsi; + +int ice_vsi_add_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan); +int ice_vsi_del_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan); + +int ice_vsi_ena_inner_stripping(struct ice_vsi *vsi, u16 tpid); +int ice_vsi_dis_inner_stripping(struct ice_vsi *vsi); +int ice_vsi_ena_inner_insertion(struct ice_vsi *vsi, u16 tpid); +int ice_vsi_dis_inner_insertion(struct ice_vsi *vsi); +int ice_vsi_set_inner_port_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan); + +int ice_vsi_ena_rx_vlan_filtering(struct ice_vsi *vsi); +int ice_vsi_dis_rx_vlan_filtering(struct ice_vsi *vsi); +int ice_vsi_ena_tx_vlan_filtering(struct ice_vsi *vsi); +int ice_vsi_dis_tx_vlan_filtering(struct ice_vsi *vsi); + +int ice_vsi_ena_outer_stripping(struct ice_vsi *vsi, u16 tpid); +int ice_vsi_dis_outer_stripping(struct ice_vsi *vsi); +int ice_vsi_ena_outer_insertion(struct ice_vsi *vsi, u16 tpid); +int ice_vsi_dis_outer_insertion(struct ice_vsi *vsi); +int ice_vsi_set_outer_port_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan); + +#endif /* _ICE_VSI_VLAN_LIB_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_vsi_vlan_ops.c b/drivers/net/ethernet/intel/ice/ice_vsi_vlan_ops.c new file mode 100644 index 000000000000..4a6c850d83ac --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vsi_vlan_ops.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2019-2021, Intel Corporation. */ + +#include "ice_pf_vsi_vlan_ops.h" +#include "ice_vf_vsi_vlan_ops.h" +#include "ice_lib.h" +#include "ice.h" + +static int +op_unsupported_vlan_arg(struct ice_vsi * __always_unused vsi, + struct ice_vlan * __always_unused vlan) +{ + return -EOPNOTSUPP; +} + +static int +op_unsupported_tpid_arg(struct ice_vsi *__always_unused vsi, + u16 __always_unused tpid) +{ + return -EOPNOTSUPP; +} + +static int op_unsupported(struct ice_vsi *__always_unused vsi) +{ + return -EOPNOTSUPP; +} + +/* If any new ops are added to the VSI VLAN ops interface then an unsupported + * implementation should be set here. + */ +static struct ice_vsi_vlan_ops ops_unsupported = { + .add_vlan = op_unsupported_vlan_arg, + .del_vlan = op_unsupported_vlan_arg, + .ena_stripping = op_unsupported_tpid_arg, + .dis_stripping = op_unsupported, + .ena_insertion = op_unsupported_tpid_arg, + .dis_insertion = op_unsupported, + .ena_rx_filtering = op_unsupported, + .dis_rx_filtering = op_unsupported, + .ena_tx_filtering = op_unsupported, + .dis_tx_filtering = op_unsupported, + .set_port_vlan = op_unsupported_vlan_arg, +}; + +/** + * ice_vsi_init_unsupported_vlan_ops - init all VSI VLAN ops to unsupported + * @vsi: VSI to initialize VSI VLAN ops to unsupported for + * + * By default all inner and outer VSI VLAN ops return -EOPNOTSUPP. This was done + * as oppsed to leaving the ops null to prevent unexpected crashes. Instead if + * an unsupported VSI VLAN op is called it will just return -EOPNOTSUPP. + * + */ +static void ice_vsi_init_unsupported_vlan_ops(struct ice_vsi *vsi) +{ + vsi->outer_vlan_ops = ops_unsupported; + vsi->inner_vlan_ops = ops_unsupported; +} + +/** + * ice_vsi_init_vlan_ops - initialize type specific VSI VLAN ops + * @vsi: VSI to initialize ops for + * + * If any VSI types are added and/or require different ops than the PF or VF VSI + * then they will have to add a case here to handle that. Also, VSI type + * specific files should be added in the same manner that was done for PF VSI. + */ +void ice_vsi_init_vlan_ops(struct ice_vsi *vsi) +{ + /* Initialize all VSI types to have unsupported VSI VLAN ops */ + ice_vsi_init_unsupported_vlan_ops(vsi); + + switch (vsi->type) { + case ICE_VSI_PF: + case ICE_VSI_SWITCHDEV_CTRL: + ice_pf_vsi_init_vlan_ops(vsi); + break; + case ICE_VSI_VF: + ice_vf_vsi_init_vlan_ops(vsi); + break; + default: + dev_dbg(ice_pf_to_dev(vsi->back), "%s does not support VLAN operations\n", + ice_vsi_type_str(vsi->type)); + break; + } +} + +/** + * ice_get_compat_vsi_vlan_ops - Get VSI VLAN ops based on VLAN mode + * @vsi: VSI used to get the VSI VLAN ops + * + * This function is meant to be used when the caller doesn't know which VLAN ops + * to use (i.e. inner or outer). This allows backward compatibility for VLANs + * since most of the Outer VSI VLAN functins are not supported when + * the device is configured in Single VLAN Mode (SVM). + */ +struct ice_vsi_vlan_ops *ice_get_compat_vsi_vlan_ops(struct ice_vsi *vsi) +{ + if (ice_is_dvm_ena(&vsi->back->hw)) + return &vsi->outer_vlan_ops; + else + return &vsi->inner_vlan_ops; +} diff --git a/drivers/net/ethernet/intel/ice/ice_vsi_vlan_ops.h b/drivers/net/ethernet/intel/ice/ice_vsi_vlan_ops.h new file mode 100644 index 000000000000..5b47568f6256 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_vsi_vlan_ops.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019-2021, Intel Corporation. */ + +#ifndef _ICE_VSI_VLAN_OPS_H_ +#define _ICE_VSI_VLAN_OPS_H_ + +#include "ice_type.h" +#include "ice_vsi_vlan_lib.h" + +struct ice_vsi; + +struct ice_vsi_vlan_ops { + int (*add_vlan)(struct ice_vsi *vsi, struct ice_vlan *vlan); + int (*del_vlan)(struct ice_vsi *vsi, struct ice_vlan *vlan); + int (*ena_stripping)(struct ice_vsi *vsi, const u16 tpid); + int (*dis_stripping)(struct ice_vsi *vsi); + int (*ena_insertion)(struct ice_vsi *vsi, const u16 tpid); + int (*dis_insertion)(struct ice_vsi *vsi); + int (*ena_rx_filtering)(struct ice_vsi *vsi); + int (*dis_rx_filtering)(struct ice_vsi *vsi); + int (*ena_tx_filtering)(struct ice_vsi *vsi); + int (*dis_tx_filtering)(struct ice_vsi *vsi); + int (*set_port_vlan)(struct ice_vsi *vsi, struct ice_vlan *vlan); +}; + +void ice_vsi_init_vlan_ops(struct ice_vsi *vsi); +struct ice_vsi_vlan_ops *ice_get_compat_vsi_vlan_ops(struct ice_vsi *vsi); + +#endif /* _ICE_VSI_VLAN_OPS_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.c b/drivers/net/ethernet/intel/ice/ice_xsk.c index 2388837d6d6c..88853a6ed931 100644 --- a/drivers/net/ethernet/intel/ice/ice_xsk.c +++ b/drivers/net/ethernet/intel/ice/ice_xsk.c @@ -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 @@ int ice_xsk_pool_setup(struct ice_vsi *vsi, struct xsk_buff_pool *pool, u16 qid) 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 @@ int ice_xsk_pool_setup(struct ice_vsi *vsi, struct xsk_buff_pool *pool, u16 qid) } /** - * 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); } /** @@ -428,20 +495,24 @@ static void ice_bump_ntc(struct ice_rx_ring *rx_ring) static struct sk_buff * ice_construct_skb_zc(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp) { - unsigned int datasize_hard = xdp->data_end - xdp->data_hard_start; + unsigned int totalsize = xdp->data_end - xdp->data_meta; unsigned int metasize = xdp->data - xdp->data_meta; - unsigned int datasize = xdp->data_end - xdp->data; struct sk_buff *skb; - skb = __napi_alloc_skb(&rx_ring->q_vector->napi, datasize_hard, + net_prefetch(xdp->data_meta); + + skb = __napi_alloc_skb(&rx_ring->q_vector->napi, totalsize, GFP_ATOMIC | __GFP_NOWARN); if (unlikely(!skb)) return NULL; - skb_reserve(skb, xdp->data - xdp->data_hard_start); - memcpy(__skb_put(skb, datasize), xdp->data, datasize); - if (metasize) + memcpy(__skb_put(skb, totalsize), xdp->data_meta, + ALIGN(totalsize, sizeof(long))); + + if (metasize) { skb_metadata_set(skb, metasize); + __skb_pull(skb, metasize); + } xsk_buff_free(xdp); return skb; @@ -528,7 +599,7 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget) rx_desc = ICE_RX_DESC(rx_ring, rx_ring->next_to_clean); stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_DD_S); - if (!ice_test_staterr(rx_desc, stat_err_bits)) + if (!ice_test_staterr(rx_desc->wb.status_error0, stat_err_bits)) break; /* This memory barrier is needed to keep us from reading @@ -583,9 +654,7 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget) total_rx_bytes += skb->len; total_rx_packets++; - stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_L2TAG1P_S); - if (ice_test_staterr(rx_desc, stat_err_bits)) - vlan_tag = le16_to_cpu(rx_desc->wb.l2tag1); + vlan_tag = ice_get_vlan_tag_from_rx_desc(rx_desc); rx_ptype = le16_to_cpu(rx_desc->wb.ptype_flex_flags0) & ICE_RX_FLEX_DESC_PTYPE_M; @@ -611,58 +680,6 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget) 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 @@ -672,74 +689,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; } /** diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.h b/drivers/net/ethernet/intel/ice/ice_xsk.h index 4c7bd8e9dfc4..21faec8e97db 100644 --- a/drivers/net/ethernet/intel/ice/ice_xsk.h +++ b/drivers/net/ethernet/intel/ice/ice_xsk.h @@ -4,7 +4,16 @@ #ifndef _ICE_XSK_H_ #define _ICE_XSK_H_ #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__ >= 8 +#define loop_unrolled_for _Pragma("GCC unroll 8") for +#else +#define loop_unrolled_for for +#endif struct ice_vsi; @@ -12,13 +21,21 @@ struct ice_vsi; 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 +51,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) diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 51a2dcaf553d..2a5782063f4c 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -965,10 +965,6 @@ static int igb_set_ringparam(struct net_device *netdev, memcpy(&temp_ring[i], adapter->rx_ring[i], sizeof(struct igb_ring)); - /* Clear copied XDP RX-queue info */ - memset(&temp_ring[i].xdp_rxq, 0, - sizeof(temp_ring[i].xdp_rxq)); - temp_ring[i].count = new_rx_count; err = igb_setup_rx_resources(&temp_ring[i]); if (err) { diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 38ba92022cd4..34b33b21e0dc 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3164,8 +3164,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) s32 ret_val; static int global_quad_port_a; /* global quad port a indication */ const struct e1000_info *ei = igb_info_tbl[ent->driver_data]; - int err, pci_using_dac; u8 part_str[E1000_PBANUM_LENGTH]; + int err; /* Catch broken hardware that put the wrong VF device ID in * the PCIe SR-IOV capability. @@ -3180,17 +3180,11 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) return err; - pci_using_dac = 0; err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); - if (!err) { - pci_using_dac = 1; - } else { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, - "No usable DMA configuration, aborting\n"); - goto err_dma; - } + if (err) { + dev_err(&pdev->dev, + "No usable DMA configuration, aborting\n"); + goto err_dma; } err = pci_request_mem_regions(pdev, igb_driver_name); @@ -3306,8 +3300,7 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (hw->mac.type >= e1000_i350) netdev->hw_features |= NETIF_F_NTUPLE; - if (pci_using_dac) - netdev->features |= NETIF_F_HIGHDMA; + netdev->features |= NETIF_F_HIGHDMA; netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID; netdev->mpls_features |= NETIF_F_HW_CSUM; @@ -4352,7 +4345,18 @@ int igb_setup_rx_resources(struct igb_ring *rx_ring) { struct igb_adapter *adapter = netdev_priv(rx_ring->netdev); struct device *dev = rx_ring->dev; - int size; + int size, res; + + /* XDP RX-queue info */ + if (xdp_rxq_info_is_reg(&rx_ring->xdp_rxq)) + xdp_rxq_info_unreg(&rx_ring->xdp_rxq); + res = xdp_rxq_info_reg(&rx_ring->xdp_rxq, rx_ring->netdev, + rx_ring->queue_index, 0); + if (res < 0) { + dev_err(dev, "Failed to register xdp_rxq index %u\n", + rx_ring->queue_index); + return res; + } size = sizeof(struct igb_rx_buffer) * rx_ring->count; @@ -4375,14 +4379,10 @@ int igb_setup_rx_resources(struct igb_ring *rx_ring) rx_ring->xdp_prog = adapter->xdp_prog; - /* XDP RX-queue info */ - if (xdp_rxq_info_reg(&rx_ring->xdp_rxq, rx_ring->netdev, - rx_ring->queue_index, 0) < 0) - goto err; - return 0; err: + xdp_rxq_info_unreg(&rx_ring->xdp_rxq); vfree(rx_ring->rx_buffer_info); rx_ring->rx_buffer_info = NULL; dev_err(dev, "Unable to allocate memory for the Rx descriptor ring\n"); diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 6580fcddb4be..02fec948ce64 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -165,23 +165,21 @@ static void igb_ptp_systim_to_hwtstamp(struct igb_adapter *adapter, unsigned long flags; u64 ns; + memset(hwtstamps, 0, sizeof(*hwtstamps)); + switch (adapter->hw.mac.type) { case e1000_82576: case e1000_82580: case e1000_i354: case e1000_i350: spin_lock_irqsave(&adapter->tmreg_lock, flags); - ns = timecounter_cyc2time(&adapter->tc, systim); - spin_unlock_irqrestore(&adapter->tmreg_lock, flags); - memset(hwtstamps, 0, sizeof(*hwtstamps)); hwtstamps->hwtstamp = ns_to_ktime(ns); break; case e1000_i210: case e1000_i211: - memset(hwtstamps, 0, sizeof(*hwtstamps)); /* Upper 32 bits contain s, lower 32 bits contain ns. */ hwtstamps->hwtstamp = ktime_set(systim >> 32, systim & 0xFFFFFFFF); diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index b78407289741..43ced78c3a2e 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -2684,25 +2684,18 @@ static int igbvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct igbvf_adapter *adapter; struct e1000_hw *hw; const struct igbvf_info *ei = igbvf_info_tbl[ent->driver_data]; - static int cards_found; - int err, pci_using_dac; + int err; err = pci_enable_device_mem(pdev); if (err) return err; - pci_using_dac = 0; err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); - if (!err) { - pci_using_dac = 1; - } else { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, - "No usable DMA configuration, aborting\n"); - goto err_dma; - } + if (err) { + dev_err(&pdev->dev, + "No usable DMA configuration, aborting\n"); + goto err_dma; } err = pci_request_regions(pdev, igbvf_driver_name); @@ -2783,10 +2776,7 @@ static int igbvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->hw_features |= NETIF_F_GSO_PARTIAL | IGBVF_GSO_PARTIAL_FEATURES; - netdev->features = netdev->hw_features; - - if (pci_using_dac) - netdev->features |= NETIF_F_HIGHDMA; + netdev->features = netdev->hw_features | NETIF_F_HIGHDMA; netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID; netdev->mpls_features |= NETIF_F_HW_CSUM; diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 2f17f36e94fd..74b2c590ed5d 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -505,6 +505,9 @@ int igc_setup_rx_resources(struct igc_ring *rx_ring) u8 index = rx_ring->queue_index; int size, desc_len, res; + /* XDP RX-queue info */ + if (xdp_rxq_info_is_reg(&rx_ring->xdp_rxq)) + xdp_rxq_info_unreg(&rx_ring->xdp_rxq); res = xdp_rxq_info_reg(&rx_ring->xdp_rxq, ndev, index, rx_ring->q_vector->napi.napi_id); if (res < 0) { @@ -2446,19 +2449,20 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget) static struct sk_buff *igc_construct_skb_zc(struct igc_ring *ring, struct xdp_buff *xdp) { + unsigned int totalsize = xdp->data_end - xdp->data_meta; unsigned int metasize = xdp->data - xdp->data_meta; - unsigned int datasize = xdp->data_end - xdp->data; - unsigned int totalsize = metasize + datasize; struct sk_buff *skb; - skb = __napi_alloc_skb(&ring->q_vector->napi, - xdp->data_end - xdp->data_hard_start, + net_prefetch(xdp->data_meta); + + skb = __napi_alloc_skb(&ring->q_vector->napi, totalsize, GFP_ATOMIC | __GFP_NOWARN); if (unlikely(!skb)) return NULL; - skb_reserve(skb, xdp->data_meta - xdp->data_hard_start); - memcpy(__skb_put(skb, totalsize), xdp->data_meta, totalsize); + memcpy(__skb_put(skb, totalsize), xdp->data_meta, + ALIGN(totalsize, sizeof(long))); + if (metasize) { skb_metadata_set(skb, metasize); __skb_pull(skb, metasize); @@ -6251,23 +6255,17 @@ static int igc_probe(struct pci_dev *pdev, struct net_device *netdev; struct igc_hw *hw; const struct igc_info *ei = igc_info_tbl[ent->driver_data]; - int err, pci_using_dac; + int err; err = pci_enable_device_mem(pdev); if (err) return err; - pci_using_dac = 0; err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); - if (!err) { - pci_using_dac = 1; - } else { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, - "No usable DMA configuration, aborting\n"); - goto err_dma; - } + if (err) { + dev_err(&pdev->dev, + "No usable DMA configuration, aborting\n"); + goto err_dma; } err = pci_request_mem_regions(pdev, igc_driver_name); @@ -6367,8 +6365,7 @@ static int igc_probe(struct pci_dev *pdev, netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; netdev->hw_features |= netdev->features; - if (pci_using_dac) - netdev->features |= NETIF_F_HIGHDMA; + netdev->features |= NETIF_F_HIGHDMA; netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID; netdev->mpls_features |= NETIF_F_HW_CSUM; diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c index 99d481904ce6..affdefcca7e3 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c @@ -361,7 +361,6 @@ ixgb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct net_device *netdev = NULL; struct ixgb_adapter *adapter; static int cards_found = 0; - int pci_using_dac; u8 addr[ETH_ALEN]; int i; int err; @@ -370,16 +369,10 @@ ixgb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) return err; - pci_using_dac = 0; err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); - if (!err) { - pci_using_dac = 1; - } else { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) { - pr_err("No usable DMA configuration, aborting\n"); - goto err_dma_mask; - } + if (err) { + pr_err("No usable DMA configuration, aborting\n"); + goto err_dma_mask; } err = pci_request_regions(pdev, ixgb_driver_name); @@ -444,10 +437,8 @@ ixgb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) NETIF_F_HW_VLAN_CTAG_FILTER; netdev->hw_features |= NETIF_F_RXCSUM; - if (pci_using_dac) { - netdev->features |= NETIF_F_HIGHDMA; - netdev->vlan_features |= NETIF_F_HIGHDMA; - } + netdev->features |= NETIF_F_HIGHDMA; + netdev->vlan_features |= NETIF_F_HIGHDMA; /* MTU range: 68 - 16114 */ netdev->min_mtu = ETH_MIN_MTU; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 4a69823e6abd..921a4d977d65 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -177,11 +177,14 @@ struct vf_data_storage { u16 pf_vlan; /* When set, guest VLAN config not allowed. */ u16 pf_qos; u16 tx_rate; + int link_enable; + int link_state; u8 spoofchk_enabled; bool rss_query_enabled; u8 trusted; int xcast_mode; unsigned int vf_api; + u8 primary_abort_count; }; enum ixgbevf_xcast_modes { @@ -556,6 +559,8 @@ struct ixgbe_mac_addr { #define IXGBE_TRY_LINK_TIMEOUT (4 * HZ) #define IXGBE_SFP_POLL_JIFFIES (2 * HZ) /* SFP poll every 2 seconds */ +#define IXGBE_PRIMARY_ABORT_LIMIT 5 + /* board specific private data structure */ struct ixgbe_adapter { unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; @@ -614,6 +619,7 @@ struct ixgbe_adapter { #define IXGBE_FLAG2_RX_LEGACY BIT(16) #define IXGBE_FLAG2_IPSEC_ENABLED BIT(17) #define IXGBE_FLAG2_VF_IPSEC_ENABLED BIT(18) +#define IXGBE_FLAG2_AUTO_DISABLE_VF BIT(19) /* Tx fast path data */ int num_tx_queues; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index e90b5047e695..4c26c4b92f07 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -30,7 +30,7 @@ static s32 ixgbe_write_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data); static s32 ixgbe_detect_eeprom_page_size_generic(struct ixgbe_hw *hw, u16 offset); -static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw); +static s32 ixgbe_disable_pcie_primary(struct ixgbe_hw *hw); /* Base table for registers values that change by MAC */ const u32 ixgbe_mvals_8259X[IXGBE_MVALS_IDX_LIMIT] = { @@ -746,10 +746,10 @@ s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw) usleep_range(1000, 2000); /* - * Prevent the PCI-E bus from from hanging by disabling PCI-E master + * Prevent the PCI-E bus from hanging by disabling PCI-E primary * access and verify no pending requests */ - return ixgbe_disable_pcie_master(hw); + return ixgbe_disable_pcie_primary(hw); } /** @@ -2506,15 +2506,15 @@ static u32 ixgbe_pcie_timeout_poll(struct ixgbe_hw *hw) } /** - * ixgbe_disable_pcie_master - Disable PCI-express master access + * ixgbe_disable_pcie_primary - Disable PCI-express primary access * @hw: pointer to hardware structure * - * Disables PCI-Express master access and verifies there are no pending - * requests. IXGBE_ERR_MASTER_REQUESTS_PENDING is returned if master disable - * bit hasn't caused the master requests to be disabled, else 0 - * is returned signifying master requests disabled. + * Disables PCI-Express primary access and verifies there are no pending + * requests. IXGBE_ERR_PRIMARY_REQUESTS_PENDING is returned if primary disable + * bit hasn't caused the primary requests to be disabled, else 0 + * is returned signifying primary requests disabled. **/ -static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw) +static s32 ixgbe_disable_pcie_primary(struct ixgbe_hw *hw) { u32 i, poll; u16 value; @@ -2523,23 +2523,23 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw) IXGBE_WRITE_REG(hw, IXGBE_CTRL, IXGBE_CTRL_GIO_DIS); /* Poll for bit to read as set */ - for (i = 0; i < IXGBE_PCI_MASTER_DISABLE_TIMEOUT; i++) { + for (i = 0; i < IXGBE_PCI_PRIMARY_DISABLE_TIMEOUT; i++) { if (IXGBE_READ_REG(hw, IXGBE_CTRL) & IXGBE_CTRL_GIO_DIS) break; usleep_range(100, 120); } - if (i >= IXGBE_PCI_MASTER_DISABLE_TIMEOUT) { + if (i >= IXGBE_PCI_PRIMARY_DISABLE_TIMEOUT) { hw_dbg(hw, "GIO disable did not set - requesting resets\n"); goto gio_disable_fail; } - /* Exit if master requests are blocked */ + /* Exit if primary requests are blocked */ if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO) || ixgbe_removed(hw->hw_addr)) return 0; - /* Poll for master request bit to clear */ - for (i = 0; i < IXGBE_PCI_MASTER_DISABLE_TIMEOUT; i++) { + /* Poll for primary request bit to clear */ + for (i = 0; i < IXGBE_PCI_PRIMARY_DISABLE_TIMEOUT; i++) { udelay(100); if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO)) return 0; @@ -2547,13 +2547,13 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw) /* * Two consecutive resets are required via CTRL.RST per datasheet - * 5.2.5.3.2 Master Disable. We set a flag to inform the reset routine - * of this need. The first reset prevents new master requests from + * 5.2.5.3.2 Primary Disable. We set a flag to inform the reset routine + * of this need. The first reset prevents new primary requests from * being issued by our device. We then must wait 1usec or more for any * remaining completions from the PCIe bus to trickle in, and then reset * again to clear out any effects they may have had on our device. */ - hw_dbg(hw, "GIO Master Disable bit didn't clear - requesting resets\n"); + hw_dbg(hw, "GIO Primary Disable bit didn't clear - requesting resets\n"); gio_disable_fail: hw->mac.flags |= IXGBE_FLAGS_DOUBLE_RESET_REQUIRED; @@ -2575,7 +2575,7 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw) } hw_dbg(hw, "PCIe transaction pending bit also did not clear.\n"); - return IXGBE_ERR_MASTER_REQUESTS_PENDING; + return IXGBE_ERR_PRIMARY_REQUESTS_PENDING; } /** diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index f70967c32116..628d0eb0599f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -138,6 +138,8 @@ static const char ixgbe_priv_flags_strings[][ETH_GSTRING_LEN] = { "legacy-rx", #define IXGBE_PRIV_FLAGS_VF_IPSEC_EN BIT(1) "vf-ipsec", +#define IXGBE_PRIV_FLAGS_AUTO_DISABLE_VF BIT(2) + "mdd-disable-vf", }; #define IXGBE_PRIV_FLAGS_STR_LEN ARRAY_SIZE(ixgbe_priv_flags_strings) @@ -3510,6 +3512,9 @@ static u32 ixgbe_get_priv_flags(struct net_device *netdev) if (adapter->flags2 & IXGBE_FLAG2_VF_IPSEC_ENABLED) priv_flags |= IXGBE_PRIV_FLAGS_VF_IPSEC_EN; + if (adapter->flags2 & IXGBE_FLAG2_AUTO_DISABLE_VF) + priv_flags |= IXGBE_PRIV_FLAGS_AUTO_DISABLE_VF; + return priv_flags; } @@ -3517,6 +3522,7 @@ static int ixgbe_set_priv_flags(struct net_device *netdev, u32 priv_flags) { struct ixgbe_adapter *adapter = netdev_priv(netdev); unsigned int flags2 = adapter->flags2; + unsigned int i; flags2 &= ~IXGBE_FLAG2_RX_LEGACY; if (priv_flags & IXGBE_PRIV_FLAGS_LEGACY_RX) @@ -3526,6 +3532,21 @@ static int ixgbe_set_priv_flags(struct net_device *netdev, u32 priv_flags) if (priv_flags & IXGBE_PRIV_FLAGS_VF_IPSEC_EN) flags2 |= IXGBE_FLAG2_VF_IPSEC_ENABLED; + flags2 &= ~IXGBE_FLAG2_AUTO_DISABLE_VF; + if (priv_flags & IXGBE_PRIV_FLAGS_AUTO_DISABLE_VF) { + if (adapter->hw.mac.type == ixgbe_mac_82599EB) { + /* Reset primary abort counter */ + for (i = 0; i < adapter->num_vfs; i++) + adapter->vfinfo[i].primary_abort_count = 0; + + flags2 |= IXGBE_FLAG2_AUTO_DISABLE_VF; + } else { + e_info(probe, + "Cannot set private flags: Operation not supported\n"); + return -EOPNOTSUPP; + } + } + if (flags2 != adapter->flags2) { adapter->flags2 = flags2; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 89b467006291..c4a4954aa317 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -5687,6 +5687,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) ctrl_ext = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT); ctrl_ext |= IXGBE_CTRL_EXT_PFRSTD; IXGBE_WRITE_REG(hw, IXGBE_CTRL_EXT, ctrl_ext); + + /* update setting rx tx for all active vfs */ + ixgbe_set_all_vfs(adapter); } void ixgbe_reinit_locked(struct ixgbe_adapter *adapter) @@ -5948,8 +5951,8 @@ void ixgbe_reset(struct ixgbe_adapter *adapter) case IXGBE_ERR_SFP_NOT_PRESENT: case IXGBE_ERR_SFP_NOT_SUPPORTED: break; - case IXGBE_ERR_MASTER_REQUESTS_PENDING: - e_dev_err("master disable timed out\n"); + case IXGBE_ERR_PRIMARY_REQUESTS_PENDING: + e_dev_err("primary disable timed out\n"); break; case IXGBE_ERR_EEPROM_VERSION: /* We are running on a pre-production device, log a warning */ @@ -6144,11 +6147,8 @@ void ixgbe_down(struct ixgbe_adapter *adapter) for (i = 0 ; i < adapter->num_vfs; i++) adapter->vfinfo[i].clear_to_send = false; - /* ping all the active vfs to let them know we are going down */ - ixgbe_ping_all_vfs(adapter); - - /* Disable all VFTE/VFRE TX/RX */ - ixgbe_disable_tx_rx(adapter); + /* update setting rx tx for all active vfs */ + ixgbe_set_all_vfs(adapter); } /* disable transmits in the hardware now that interrupts are off */ @@ -7613,6 +7613,27 @@ static void ixgbe_watchdog_flush_tx(struct ixgbe_adapter *adapter) } #ifdef CONFIG_PCI_IOV +static void ixgbe_bad_vf_abort(struct ixgbe_adapter *adapter, u32 vf) +{ + struct ixgbe_hw *hw = &adapter->hw; + + if (adapter->hw.mac.type == ixgbe_mac_82599EB && + adapter->flags2 & IXGBE_FLAG2_AUTO_DISABLE_VF) { + adapter->vfinfo[vf].primary_abort_count++; + if (adapter->vfinfo[vf].primary_abort_count == + IXGBE_PRIMARY_ABORT_LIMIT) { + ixgbe_set_vf_link_state(adapter, vf, + IFLA_VF_LINK_STATE_DISABLE); + adapter->vfinfo[vf].primary_abort_count = 0; + + e_info(drv, + "Malicious Driver Detection event detected on PF %d VF %d MAC: %pM mdd-disable-vf=on", + hw->bus.func, vf, + adapter->vfinfo[vf].vf_mac_addresses); + } + } +} + static void ixgbe_check_for_bad_vf(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; @@ -7644,8 +7665,10 @@ static void ixgbe_check_for_bad_vf(struct ixgbe_adapter *adapter) continue; pci_read_config_word(vfdev, PCI_STATUS, &status_reg); if (status_reg != IXGBE_FAILED_READ_CFG_WORD && - status_reg & PCI_STATUS_REC_MASTER_ABORT) + status_reg & PCI_STATUS_REC_MASTER_ABORT) { + ixgbe_bad_vf_abort(adapter, vf); pcie_flr(vfdev); + } } } @@ -10284,6 +10307,7 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_set_vf_vlan = ixgbe_ndo_set_vf_vlan, .ndo_set_vf_rate = ixgbe_ndo_set_vf_bw, .ndo_set_vf_spoofchk = ixgbe_ndo_set_vf_spoofchk, + .ndo_set_vf_link_state = ixgbe_ndo_set_vf_link_state, .ndo_set_vf_rss_query_en = ixgbe_ndo_set_vf_rss_query_en, .ndo_set_vf_trust = ixgbe_ndo_set_vf_trust, .ndo_get_vf_config = ixgbe_ndo_get_vf_config, @@ -10632,9 +10656,9 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct ixgbe_adapter *adapter = NULL; struct ixgbe_hw *hw; const struct ixgbe_info *ii = ixgbe_info_tbl[ent->driver_data]; - int i, err, pci_using_dac, expected_gts; unsigned int indices = MAX_TX_QUEUES; u8 part_str[IXGBE_PBANUM_LENGTH]; + int i, err, expected_gts; bool disable_dev = false; #ifdef IXGBE_FCOE u16 device_caps; @@ -10654,16 +10678,11 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) return err; - if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) { - pci_using_dac = 1; - } else { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, - "No usable DMA configuration, aborting\n"); - goto err_dma; - } - pci_using_dac = 0; + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (err) { + dev_err(&pdev->dev, + "No usable DMA configuration, aborting\n"); + goto err_dma; } err = pci_request_mem_regions(pdev, ixgbe_driver_name); @@ -10750,6 +10769,9 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto err_sw_init; + if (adapter->hw.mac.type == ixgbe_mac_82599EB) + adapter->flags2 |= IXGBE_FLAG2_AUTO_DISABLE_VF; + switch (adapter->hw.mac.type) { case ixgbe_mac_X550: case ixgbe_mac_X550EM_x: @@ -10861,8 +10883,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->hw_features |= NETIF_F_NTUPLE | NETIF_F_HW_TC; - if (pci_using_dac) - netdev->features |= NETIF_F_HIGHDMA; + netdev->features |= NETIF_F_HIGHDMA; netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID; netdev->hw_enc_features |= netdev->vlan_features; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h index a148534d7256..8f4316b19278 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h @@ -85,6 +85,8 @@ enum ixgbe_pfvf_api_rev { #define IXGBE_VF_IPSEC_ADD 0x0d #define IXGBE_VF_IPSEC_DEL 0x0e +#define IXGBE_VF_GET_LINK_STATE 0x10 /* get vf link state */ + /* length of permanent address message returned from PF */ #define IXGBE_VF_PERMADDR_MSG_LEN 4 /* word in permanent address message with the current multicast type */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 214a38de3f41..7f11c0a8e7a9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -96,6 +96,7 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter, for (i = 0; i < num_vfs; i++) { /* enable spoof checking for all VFs */ adapter->vfinfo[i].spoofchk_enabled = true; + adapter->vfinfo[i].link_enable = true; /* We support VF RSS querying only for 82599 and x540 * devices at the moment. These devices share RSS @@ -820,6 +821,57 @@ static inline void ixgbe_write_qde(struct ixgbe_adapter *adapter, u32 vf, } } +/** + * ixgbe_set_vf_rx_tx - Set VF rx tx + * @adapter: Pointer to adapter struct + * @vf: VF identifier + * + * Set or reset correct transmit and receive for vf + **/ +static void ixgbe_set_vf_rx_tx(struct ixgbe_adapter *adapter, int vf) +{ + u32 reg_cur_tx, reg_cur_rx, reg_req_tx, reg_req_rx; + struct ixgbe_hw *hw = &adapter->hw; + u32 reg_offset, vf_shift; + + vf_shift = vf % 32; + reg_offset = vf / 32; + + reg_cur_tx = IXGBE_READ_REG(hw, IXGBE_VFTE(reg_offset)); + reg_cur_rx = IXGBE_READ_REG(hw, IXGBE_VFRE(reg_offset)); + + if (adapter->vfinfo[vf].link_enable) { + reg_req_tx = reg_cur_tx | 1 << vf_shift; + reg_req_rx = reg_cur_rx | 1 << vf_shift; + } else { + reg_req_tx = reg_cur_tx & ~(1 << vf_shift); + reg_req_rx = reg_cur_rx & ~(1 << vf_shift); + } + + /* The 82599 cannot support a mix of jumbo and non-jumbo PF/VFs. + * For more info take a look at ixgbe_set_vf_lpe + */ + if (adapter->hw.mac.type == ixgbe_mac_82599EB) { + struct net_device *dev = adapter->netdev; + int pf_max_frame = dev->mtu + ETH_HLEN; + +#if IS_ENABLED(CONFIG_FCOE) + if (dev->features & NETIF_F_FCOE_MTU) + pf_max_frame = max_t(int, pf_max_frame, + IXGBE_FCOE_JUMBO_FRAME_SIZE); +#endif /* CONFIG_FCOE */ + + if (pf_max_frame > ETH_FRAME_LEN) + reg_req_rx = reg_cur_rx & ~(1 << vf_shift); + } + + /* Enable/Disable particular VF */ + if (reg_cur_tx != reg_req_tx) + IXGBE_WRITE_REG(hw, IXGBE_VFTE(reg_offset), reg_req_tx); + if (reg_cur_rx != reg_req_rx) + IXGBE_WRITE_REG(hw, IXGBE_VFRE(reg_offset), reg_req_rx); +} + static int ixgbe_vf_reset_msg(struct ixgbe_adapter *adapter, u32 vf) { struct ixgbe_ring_feature *vmdq = &adapter->ring_feature[RING_F_VMDQ]; @@ -845,11 +897,6 @@ static int ixgbe_vf_reset_msg(struct ixgbe_adapter *adapter, u32 vf) vf_shift = vf % 32; reg_offset = vf / 32; - /* enable transmit for vf */ - reg = IXGBE_READ_REG(hw, IXGBE_VFTE(reg_offset)); - reg |= BIT(vf_shift); - IXGBE_WRITE_REG(hw, IXGBE_VFTE(reg_offset), reg); - /* force drop enable for all VF Rx queues */ reg = IXGBE_QDE_ENABLE; if (adapter->vfinfo[vf].pf_vlan) @@ -857,27 +904,7 @@ static int ixgbe_vf_reset_msg(struct ixgbe_adapter *adapter, u32 vf) ixgbe_write_qde(adapter, vf, reg); - /* enable receive for vf */ - reg = IXGBE_READ_REG(hw, IXGBE_VFRE(reg_offset)); - reg |= BIT(vf_shift); - /* - * The 82599 cannot support a mix of jumbo and non-jumbo PF/VFs. - * For more info take a look at ixgbe_set_vf_lpe - */ - if (adapter->hw.mac.type == ixgbe_mac_82599EB) { - struct net_device *dev = adapter->netdev; - int pf_max_frame = dev->mtu + ETH_HLEN; - -#ifdef CONFIG_FCOE - if (dev->features & NETIF_F_FCOE_MTU) - pf_max_frame = max_t(int, pf_max_frame, - IXGBE_FCOE_JUMBO_FRAME_SIZE); - -#endif /* CONFIG_FCOE */ - if (pf_max_frame > ETH_FRAME_LEN) - reg &= ~BIT(vf_shift); - } - IXGBE_WRITE_REG(hw, IXGBE_VFRE(reg_offset), reg); + ixgbe_set_vf_rx_tx(adapter, vf); /* enable VF mailbox for further messages */ adapter->vfinfo[vf].clear_to_send = true; @@ -1202,6 +1229,26 @@ static int ixgbe_update_vf_xcast_mode(struct ixgbe_adapter *adapter, return 0; } +static int ixgbe_get_vf_link_state(struct ixgbe_adapter *adapter, + u32 *msgbuf, u32 vf) +{ + u32 *link_state = &msgbuf[1]; + + /* verify the PF is supporting the correct API */ + switch (adapter->vfinfo[vf].vf_api) { + case ixgbe_mbox_api_12: + case ixgbe_mbox_api_13: + case ixgbe_mbox_api_14: + break; + default: + return -EOPNOTSUPP; + } + + *link_state = adapter->vfinfo[vf].link_enable; + + return 0; +} + static int ixgbe_rcv_msg_from_vf(struct ixgbe_adapter *adapter, u32 vf) { u32 mbx_size = IXGBE_VFMAILBOX_SIZE; @@ -1267,6 +1314,9 @@ static int ixgbe_rcv_msg_from_vf(struct ixgbe_adapter *adapter, u32 vf) case IXGBE_VF_UPDATE_XCAST_MODE: retval = ixgbe_update_vf_xcast_mode(adapter, msgbuf, vf); break; + case IXGBE_VF_GET_LINK_STATE: + retval = ixgbe_get_vf_link_state(adapter, msgbuf, vf); + break; case IXGBE_VF_IPSEC_ADD: retval = ixgbe_ipsec_vf_add_sa(adapter, msgbuf, vf); break; @@ -1322,18 +1372,6 @@ void ixgbe_msg_task(struct ixgbe_adapter *adapter) } } -void ixgbe_disable_tx_rx(struct ixgbe_adapter *adapter) -{ - struct ixgbe_hw *hw = &adapter->hw; - - /* disable transmit and receive for all vfs */ - IXGBE_WRITE_REG(hw, IXGBE_VFTE(0), 0); - IXGBE_WRITE_REG(hw, IXGBE_VFTE(1), 0); - - IXGBE_WRITE_REG(hw, IXGBE_VFRE(0), 0); - IXGBE_WRITE_REG(hw, IXGBE_VFRE(1), 0); -} - static inline void ixgbe_ping_vf(struct ixgbe_adapter *adapter, int vf) { struct ixgbe_hw *hw = &adapter->hw; @@ -1359,6 +1397,21 @@ void ixgbe_ping_all_vfs(struct ixgbe_adapter *adapter) } } +/** + * ixgbe_set_all_vfs - update vfs queues + * @adapter: Pointer to adapter struct + * + * Update setting transmit and receive queues for all vfs + **/ +void ixgbe_set_all_vfs(struct ixgbe_adapter *adapter) +{ + int i; + + for (i = 0 ; i < adapter->num_vfs; i++) + ixgbe_set_vf_link_state(adapter, i, + adapter->vfinfo[i].link_state); +} + int ixgbe_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) { struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -1656,6 +1709,84 @@ int ixgbe_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting) return 0; } +/** + * ixgbe_set_vf_link_state - Set link state + * @adapter: Pointer to adapter struct + * @vf: VF identifier + * @state: required link state + * + * Set a link force state on/off a single vf + **/ +void ixgbe_set_vf_link_state(struct ixgbe_adapter *adapter, int vf, int state) +{ + adapter->vfinfo[vf].link_state = state; + + switch (state) { + case IFLA_VF_LINK_STATE_AUTO: + if (test_bit(__IXGBE_DOWN, &adapter->state)) + adapter->vfinfo[vf].link_enable = false; + else + adapter->vfinfo[vf].link_enable = true; + break; + case IFLA_VF_LINK_STATE_ENABLE: + adapter->vfinfo[vf].link_enable = true; + break; + case IFLA_VF_LINK_STATE_DISABLE: + adapter->vfinfo[vf].link_enable = false; + break; + } + + ixgbe_set_vf_rx_tx(adapter, vf); + + /* restart the VF */ + adapter->vfinfo[vf].clear_to_send = false; + ixgbe_ping_vf(adapter, vf); +} + +/** + * ixgbe_ndo_set_vf_link_state - Set link state + * @netdev: network interface device structure + * @vf: VF identifier + * @state: required link state + * + * Set the link state of a specified VF, regardless of physical link state + **/ +int ixgbe_ndo_set_vf_link_state(struct net_device *netdev, int vf, int state) +{ + struct ixgbe_adapter *adapter = netdev_priv(netdev); + int ret = 0; + + if (vf < 0 || vf >= adapter->num_vfs) { + dev_err(&adapter->pdev->dev, + "NDO set VF link - invalid VF identifier %d\n", vf); + return -EINVAL; + } + + switch (state) { + case IFLA_VF_LINK_STATE_ENABLE: + dev_info(&adapter->pdev->dev, + "NDO set VF %d link state %d - not supported\n", + vf, state); + break; + case IFLA_VF_LINK_STATE_DISABLE: + dev_info(&adapter->pdev->dev, + "NDO set VF %d link state disable\n", vf); + ixgbe_set_vf_link_state(adapter, vf, state); + break; + case IFLA_VF_LINK_STATE_AUTO: + dev_info(&adapter->pdev->dev, + "NDO set VF %d link state auto\n", vf); + ixgbe_set_vf_link_state(adapter, vf, state); + break; + default: + dev_err(&adapter->pdev->dev, + "NDO set VF %d - invalid link state %d\n", vf, state); + ret = -EINVAL; + } + + return ret; +} + int ixgbe_ndo_set_vf_rss_query_en(struct net_device *netdev, int vf, bool setting) { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h index 3ec21923c89c..0690ecb8dfa3 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h @@ -17,8 +17,8 @@ void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter); #endif void ixgbe_msg_task(struct ixgbe_adapter *adapter); int ixgbe_vf_configuration(struct pci_dev *pdev, unsigned int event_mask); -void ixgbe_disable_tx_rx(struct ixgbe_adapter *adapter); void ixgbe_ping_all_vfs(struct ixgbe_adapter *adapter); +void ixgbe_set_all_vfs(struct ixgbe_adapter *adapter); int ixgbe_ndo_set_vf_mac(struct net_device *netdev, int queue, u8 *mac); int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int queue, u16 vlan, u8 qos, __be16 vlan_proto); @@ -31,7 +31,9 @@ int ixgbe_ndo_set_vf_rss_query_en(struct net_device *netdev, int vf, int ixgbe_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting); int ixgbe_ndo_get_vf_config(struct net_device *netdev, int vf, struct ifla_vf_info *ivi); +int ixgbe_ndo_set_vf_link_state(struct net_device *netdev, int vf, int state); void ixgbe_check_vf_rate_limit(struct ixgbe_adapter *adapter); +void ixgbe_set_vf_link_state(struct ixgbe_adapter *adapter, int vf, int state); int ixgbe_disable_sriov(struct ixgbe_adapter *adapter); #ifdef CONFIG_PCI_IOV void ixgbe_enable_sriov(struct ixgbe_adapter *adapter, unsigned int max_vfs); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 2647937f7f4d..6da9880d766a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -1247,7 +1247,7 @@ struct ixgbe_nvm_version { #define IXGBE_PSRTYPE_RQPL_SHIFT 29 /* CTRL Bit Masks */ -#define IXGBE_CTRL_GIO_DIS 0x00000004 /* Global IO Master Disable bit */ +#define IXGBE_CTRL_GIO_DIS 0x00000004 /* Global IO Primary Disable bit */ #define IXGBE_CTRL_LNK_RST 0x00000008 /* Link Reset. Resets everything. */ #define IXGBE_CTRL_RST 0x04000000 /* Reset (SW) */ #define IXGBE_CTRL_RST_MASK (IXGBE_CTRL_LNK_RST | IXGBE_CTRL_RST) @@ -1811,7 +1811,7 @@ enum { /* STATUS Bit Masks */ #define IXGBE_STATUS_LAN_ID 0x0000000C /* LAN ID */ #define IXGBE_STATUS_LAN_ID_SHIFT 2 /* LAN ID Shift*/ -#define IXGBE_STATUS_GIO 0x00080000 /* GIO Master Enable Status */ +#define IXGBE_STATUS_GIO 0x00080000 /* GIO Primary Enable Status */ #define IXGBE_STATUS_LAN_ID_0 0x00000000 /* LAN ID 0 */ #define IXGBE_STATUS_LAN_ID_1 0x00000004 /* LAN ID 1 */ @@ -2193,8 +2193,8 @@ enum { #define IXGBE_PCIDEVCTRL2_4_8s 0xd #define IXGBE_PCIDEVCTRL2_17_34s 0xe -/* Number of 100 microseconds we wait for PCI Express master disable */ -#define IXGBE_PCI_MASTER_DISABLE_TIMEOUT 800 +/* Number of 100 microseconds we wait for PCI Express primary disable */ +#define IXGBE_PCI_PRIMARY_DISABLE_TIMEOUT 800 /* RAH */ #define IXGBE_RAH_VIND_MASK 0x003C0000 @@ -3671,7 +3671,7 @@ struct ixgbe_info { #define IXGBE_ERR_ADAPTER_STOPPED -9 #define IXGBE_ERR_INVALID_MAC_ADDR -10 #define IXGBE_ERR_DEVICE_NOT_SUPPORTED -11 -#define IXGBE_ERR_MASTER_REQUESTS_PENDING -12 +#define IXGBE_ERR_PRIMARY_REQUESTS_PENDING -12 #define IXGBE_ERR_INVALID_LINK_SETTINGS -13 #define IXGBE_ERR_AUTONEG_NOT_COMPLETE -14 #define IXGBE_ERR_RESET_FAILED -15 diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c index 6a5e9cf6b5da..dd7ff66d422f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c @@ -207,26 +207,28 @@ bool ixgbe_alloc_rx_buffers_zc(struct ixgbe_ring *rx_ring, u16 count) } static struct sk_buff *ixgbe_construct_skb_zc(struct ixgbe_ring *rx_ring, - struct ixgbe_rx_buffer *bi) + const struct xdp_buff *xdp) { - unsigned int metasize = bi->xdp->data - bi->xdp->data_meta; - unsigned int datasize = bi->xdp->data_end - bi->xdp->data; + unsigned int totalsize = xdp->data_end - xdp->data_meta; + unsigned int metasize = xdp->data - xdp->data_meta; struct sk_buff *skb; + net_prefetch(xdp->data_meta); + /* allocate a skb to store the frags */ - skb = __napi_alloc_skb(&rx_ring->q_vector->napi, - bi->xdp->data_end - bi->xdp->data_hard_start, + skb = __napi_alloc_skb(&rx_ring->q_vector->napi, totalsize, GFP_ATOMIC | __GFP_NOWARN); if (unlikely(!skb)) return NULL; - skb_reserve(skb, bi->xdp->data - bi->xdp->data_hard_start); - memcpy(__skb_put(skb, datasize), bi->xdp->data, datasize); - if (metasize) - skb_metadata_set(skb, metasize); + memcpy(__skb_put(skb, totalsize), xdp->data_meta, + ALIGN(totalsize, sizeof(long))); + + if (metasize) { + skb_metadata_set(skb, metasize); + __skb_pull(skb, metasize); + } - xsk_buff_free(bi->xdp); - bi->xdp = NULL; return skb; } @@ -317,12 +319,15 @@ int ixgbe_clean_rx_irq_zc(struct ixgbe_q_vector *q_vector, } /* XDP_PASS path */ - skb = ixgbe_construct_skb_zc(rx_ring, bi); + skb = ixgbe_construct_skb_zc(rx_ring, bi->xdp); if (!skb) { rx_ring->rx_stats.alloc_rx_buff_failed++; break; } + xsk_buff_free(bi->xdp); + bi->xdp = NULL; + cleaned_count++; ixgbe_inc_ntc(rx_ring); diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index e257390a4f6a..149c733fcc2b 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h @@ -387,6 +387,8 @@ struct ixgbevf_adapter { u32 *rss_key; u8 rss_indir_tbl[IXGBEVF_X550_VFRETA_SIZE]; u32 flags; + bool link_state; + #define IXGBEVF_FLAGS_LEGACY_RX BIT(1) #ifdef CONFIG_XFRM diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 0f293acd17e8..55b87bc3a938 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -2298,7 +2298,9 @@ static void ixgbevf_negotiate_api(struct ixgbevf_adapter *adapter) static void ixgbevf_up_complete(struct ixgbevf_adapter *adapter) { struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; struct ixgbe_hw *hw = &adapter->hw; + bool state; ixgbevf_configure_msix(adapter); @@ -2311,6 +2313,11 @@ static void ixgbevf_up_complete(struct ixgbevf_adapter *adapter) spin_unlock_bh(&adapter->mbx_lock); + state = adapter->link_state; + hw->mac.ops.get_link_state(hw, &adapter->link_state); + if (state && state != adapter->link_state) + dev_info(&pdev->dev, "VF is administratively disabled\n"); + smp_mb__before_atomic(); clear_bit(__IXGBEVF_DOWN, &adapter->state); ixgbevf_napi_enable_all(adapter); @@ -2753,7 +2760,7 @@ static int ixgbevf_alloc_q_vector(struct ixgbevf_adapter *adapter, int v_idx, ring->reg_idx = reg_idx; /* assign ring to adapter */ - adapter->tx_ring[txr_idx] = ring; + adapter->tx_ring[txr_idx] = ring; /* update count and index */ txr_count--; @@ -3081,6 +3088,8 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter) adapter->tx_ring_count = IXGBEVF_DEFAULT_TXD; adapter->rx_ring_count = IXGBEVF_DEFAULT_RXD; + adapter->link_state = true; + set_bit(__IXGBEVF_DOWN, &adapter->state); return 0; @@ -3313,7 +3322,7 @@ static void ixgbevf_watchdog_subtask(struct ixgbevf_adapter *adapter) ixgbevf_watchdog_update_link(adapter); - if (adapter->link_up) + if (adapter->link_up && adapter->link_state) ixgbevf_watchdog_link_is_up(adapter); else ixgbevf_watchdog_link_is_down(adapter); @@ -4512,22 +4521,17 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct ixgbevf_adapter *adapter = NULL; struct ixgbe_hw *hw = NULL; const struct ixgbevf_info *ii = ixgbevf_info_tbl[ent->driver_data]; - int err, pci_using_dac; bool disable_dev = false; + int err; err = pci_enable_device(pdev); if (err) return err; - if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) { - pci_using_dac = 1; - } else { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, "No usable DMA configuration, aborting\n"); - goto err_dma; - } - pci_using_dac = 0; + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (err) { + dev_err(&pdev->dev, "No usable DMA configuration, aborting\n"); + goto err_dma; } err = pci_request_regions(pdev, ixgbevf_driver_name); @@ -4607,10 +4611,7 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->hw_features |= NETIF_F_GSO_PARTIAL | IXGBEVF_GSO_PARTIAL_FEATURES; - netdev->features = netdev->hw_features; - - if (pci_using_dac) - netdev->features |= NETIF_F_HIGHDMA; + netdev->features = netdev->hw_features | NETIF_F_HIGHDMA; netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID; netdev->mpls_features |= NETIF_F_SG | diff --git a/drivers/net/ethernet/intel/ixgbevf/mbx.h b/drivers/net/ethernet/intel/ixgbevf/mbx.h index 7346ccf014a5..835bbcc5cc8e 100644 --- a/drivers/net/ethernet/intel/ixgbevf/mbx.h +++ b/drivers/net/ethernet/intel/ixgbevf/mbx.h @@ -100,6 +100,8 @@ enum ixgbe_pfvf_api_rev { #define IXGBE_VF_IPSEC_ADD 0x0d #define IXGBE_VF_IPSEC_DEL 0x0e +#define IXGBE_VF_GET_LINK_STATE 0x10 /* get vf link state */ + /* length of permanent address message returned from PF */ #define IXGBE_VF_PERMADDR_MSG_LEN 4 /* word in permanent address message with the current multicast type */ diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index 61d8970c6d1d..68fc32e36e88 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -584,6 +584,46 @@ static s32 ixgbevf_hv_update_xcast_mode(struct ixgbe_hw *hw, int xcast_mode) return -EOPNOTSUPP; } +/** + * ixgbevf_get_link_state_vf - Get VF link state from PF + * @hw: pointer to the HW structure + * @link_state: link state storage + * + * Returns state of the operation error or success. + */ +static s32 ixgbevf_get_link_state_vf(struct ixgbe_hw *hw, bool *link_state) +{ + u32 msgbuf[2]; + s32 ret_val; + s32 err; + + msgbuf[0] = IXGBE_VF_GET_LINK_STATE; + msgbuf[1] = 0x0; + + err = ixgbevf_write_msg_read_ack(hw, msgbuf, msgbuf, 2); + + if (err || (msgbuf[0] & IXGBE_VT_MSGTYPE_FAILURE)) { + ret_val = IXGBE_ERR_MBX; + } else { + ret_val = 0; + *link_state = msgbuf[1]; + } + + return ret_val; +} + +/** + * ixgbevf_hv_get_link_state_vf - * Hyper-V variant - just a stub. + * @hw: unused + * @link_state: unused + * + * Hyper-V variant; there is no mailbox communication. + */ +static s32 ixgbevf_hv_get_link_state_vf(struct ixgbe_hw *hw, bool *link_state) +{ + return -EOPNOTSUPP; +} + /** * ixgbevf_set_vfta_vf - Set/Unset VLAN filter table address * @hw: pointer to the HW structure @@ -968,6 +1008,7 @@ static const struct ixgbe_mac_operations ixgbevf_mac_ops = { .set_rar = ixgbevf_set_rar_vf, .update_mc_addr_list = ixgbevf_update_mc_addr_list_vf, .update_xcast_mode = ixgbevf_update_xcast_mode, + .get_link_state = ixgbevf_get_link_state_vf, .set_uc_addr = ixgbevf_set_uc_addr_vf, .set_vfta = ixgbevf_set_vfta_vf, .set_rlpml = ixgbevf_set_rlpml_vf, @@ -985,6 +1026,7 @@ static const struct ixgbe_mac_operations ixgbevf_hv_mac_ops = { .set_rar = ixgbevf_hv_set_rar_vf, .update_mc_addr_list = ixgbevf_hv_update_mc_addr_list_vf, .update_xcast_mode = ixgbevf_hv_update_xcast_mode, + .get_link_state = ixgbevf_hv_get_link_state_vf, .set_uc_addr = ixgbevf_hv_set_uc_addr_vf, .set_vfta = ixgbevf_hv_set_vfta_vf, .set_rlpml = ixgbevf_hv_set_rlpml_vf, diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.h b/drivers/net/ethernet/intel/ixgbevf/vf.h index 54158dac8707..b4eef5b6c172 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.h +++ b/drivers/net/ethernet/intel/ixgbevf/vf.h @@ -39,6 +39,7 @@ struct ixgbe_mac_operations { s32 (*init_rx_addrs)(struct ixgbe_hw *); s32 (*update_mc_addr_list)(struct ixgbe_hw *, struct net_device *); s32 (*update_xcast_mode)(struct ixgbe_hw *, int); + s32 (*get_link_state)(struct ixgbe_hw *hw, bool *link_state); s32 (*enable_mc)(struct ixgbe_hw *); s32 (*disable_mc)(struct ixgbe_hw *); s32 (*clear_vfta)(struct ixgbe_hw *); diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index 439674fc9765..b6c5122da995 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "jme.h" @@ -2179,7 +2180,7 @@ jme_stop_queue_if_full(struct jme_adapter *jme) } if (unlikely(txbi->start_xmit && - (jiffies - txbi->start_xmit) >= TX_TIMEOUT && + time_is_before_eq_jiffies(txbi->start_xmit + TX_TIMEOUT) && txbi->skb)) { netif_stop_queue(jme->dev); netif_info(jme, tx_queued, jme->dev, diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 143ca8be5eb5..5f9ab1842d49 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -1661,7 +1661,7 @@ mv643xx_eth_set_ringparam(struct net_device *dev, struct ethtool_ringparam *er, if (er->rx_mini_pending || er->rx_jumbo_pending) return -EINVAL; - mp->rx_ring_size = er->rx_pending < 4096 ? er->rx_pending : 4096; + mp->rx_ring_size = min(er->rx_pending, 4096U); mp->tx_ring_size = clamp_t(unsigned int, er->tx_pending, MV643XX_MAX_SKB_DESCS * 2, 4096); if (mp->tx_ring_size != er->tx_pending) @@ -3092,8 +3092,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev) struct mv643xx_eth_private *mp; struct net_device *dev; struct phy_device *phydev = NULL; - struct resource *res; - int err; + int err, irq; pd = dev_get_platdata(&pdev->dev); if (pd == NULL) { @@ -3189,9 +3188,12 @@ static int mv643xx_eth_probe(struct platform_device *pdev) timer_setup(&mp->rx_oom, oom_timer_wrapper, 0); - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - BUG_ON(!res); - dev->irq = res->start; + irq = platform_get_irq(pdev, 0); + if (WARN_ON(irq < 0)) { + err = irq; + goto out; + } + dev->irq = irq; dev->netdev_ops = &mv643xx_eth_netdev_ops; diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 83c8908f0cc7..934f6dd90992 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -76,6 +76,8 @@ #define MVNETA_WIN_SIZE(w) (0x2204 + ((w) << 3)) #define MVNETA_WIN_REMAP(w) (0x2280 + ((w) << 2)) #define MVNETA_BASE_ADDR_ENABLE 0x2290 +#define MVNETA_AC5_CNM_DDR_TARGET 0x2 +#define MVNETA_AC5_CNM_DDR_ATTR 0xb #define MVNETA_ACCESS_PROTECT_ENABLE 0x2294 #define MVNETA_PORT_CONFIG 0x2400 #define MVNETA_UNI_PROMISC_MODE BIT(0) @@ -544,6 +546,7 @@ struct mvneta_port { /* Flags for special SoC configurations */ bool neta_armada3700; + bool neta_ac5; u16 rx_offset_correction; const struct mbus_dram_target_info *dram_target_info; }; @@ -1884,8 +1887,8 @@ static void mvneta_txq_bufs_free(struct mvneta_port *pp, bytes_compl += buf->skb->len; pkts_compl++; dev_kfree_skb_any(buf->skb); - } else if (buf->type == MVNETA_TYPE_XDP_TX || - buf->type == MVNETA_TYPE_XDP_NDO) { + } else if ((buf->type == MVNETA_TYPE_XDP_TX || + buf->type == MVNETA_TYPE_XDP_NDO) && buf->xdpf) { if (napi && buf->type == MVNETA_TYPE_XDP_TX) xdp_return_frame_rx_napi(buf->xdpf); else @@ -2060,61 +2063,104 @@ int mvneta_rx_refill_queue(struct mvneta_port *pp, struct mvneta_rx_queue *rxq) static void mvneta_xdp_put_buff(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, - struct xdp_buff *xdp, struct skb_shared_info *sinfo, - int sync_len) + struct xdp_buff *xdp, int sync_len) { + struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); int i; + if (likely(!xdp_buff_has_frags(xdp))) + goto out; + for (i = 0; i < sinfo->nr_frags; i++) page_pool_put_full_page(rxq->page_pool, skb_frag_page(&sinfo->frags[i]), true); + +out: page_pool_put_page(rxq->page_pool, virt_to_head_page(xdp->data), sync_len, true); } static int mvneta_xdp_submit_frame(struct mvneta_port *pp, struct mvneta_tx_queue *txq, - struct xdp_frame *xdpf, bool dma_map) + struct xdp_frame *xdpf, int *nxmit_byte, bool dma_map) { + struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf); + struct device *dev = pp->dev->dev.parent; struct mvneta_tx_desc *tx_desc; - struct mvneta_tx_buf *buf; - dma_addr_t dma_addr; + int i, num_frames = 1; + struct page *page; - if (txq->count >= txq->tx_stop_threshold) + if (unlikely(xdp_frame_has_frags(xdpf))) + num_frames += sinfo->nr_frags; + + if (txq->count + num_frames >= txq->size) return MVNETA_XDP_DROPPED; - tx_desc = mvneta_txq_next_desc_get(txq); + for (i = 0; i < num_frames; i++) { + struct mvneta_tx_buf *buf = &txq->buf[txq->txq_put_index]; + skb_frag_t *frag = NULL; + int len = xdpf->len; + dma_addr_t dma_addr; - buf = &txq->buf[txq->txq_put_index]; - if (dma_map) { - /* ndo_xdp_xmit */ - dma_addr = dma_map_single(pp->dev->dev.parent, xdpf->data, - xdpf->len, DMA_TO_DEVICE); - if (dma_mapping_error(pp->dev->dev.parent, dma_addr)) { - mvneta_txq_desc_put(txq); - return MVNETA_XDP_DROPPED; + if (unlikely(i)) { /* paged area */ + frag = &sinfo->frags[i - 1]; + len = skb_frag_size(frag); } - buf->type = MVNETA_TYPE_XDP_NDO; - } else { - struct page *page = virt_to_page(xdpf->data); - dma_addr = page_pool_get_dma_addr(page) + - sizeof(*xdpf) + xdpf->headroom; - dma_sync_single_for_device(pp->dev->dev.parent, dma_addr, - xdpf->len, DMA_BIDIRECTIONAL); - buf->type = MVNETA_TYPE_XDP_TX; + tx_desc = mvneta_txq_next_desc_get(txq); + if (dma_map) { + /* ndo_xdp_xmit */ + void *data; + + data = unlikely(frag) ? skb_frag_address(frag) + : xdpf->data; + dma_addr = dma_map_single(dev, data, len, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma_addr)) { + mvneta_txq_desc_put(txq); + goto unmap; + } + + buf->type = MVNETA_TYPE_XDP_NDO; + } else { + page = unlikely(frag) ? skb_frag_page(frag) + : virt_to_page(xdpf->data); + dma_addr = page_pool_get_dma_addr(page); + if (unlikely(frag)) + dma_addr += skb_frag_off(frag); + else + dma_addr += sizeof(*xdpf) + xdpf->headroom; + dma_sync_single_for_device(dev, dma_addr, len, + DMA_BIDIRECTIONAL); + buf->type = MVNETA_TYPE_XDP_TX; + } + buf->xdpf = unlikely(i) ? NULL : xdpf; + + tx_desc->command = unlikely(i) ? 0 : MVNETA_TXD_F_DESC; + tx_desc->buf_phys_addr = dma_addr; + tx_desc->data_size = len; + *nxmit_byte += len; + + mvneta_txq_inc_put(txq); } - buf->xdpf = xdpf; + /*last descriptor */ + tx_desc->command |= MVNETA_TXD_L_DESC | MVNETA_TXD_Z_PAD; - tx_desc->command = MVNETA_TXD_FLZ_DESC; - tx_desc->buf_phys_addr = dma_addr; - tx_desc->data_size = xdpf->len; - - mvneta_txq_inc_put(txq); - txq->pending++; - txq->count++; + txq->pending += num_frames; + txq->count += num_frames; return MVNETA_XDP_TX; + +unmap: + for (i--; i >= 0; i--) { + mvneta_txq_desc_put(txq); + tx_desc = txq->descs + txq->next_desc_to_proc; + dma_unmap_single(dev, tx_desc->buf_phys_addr, + tx_desc->data_size, + DMA_TO_DEVICE); + } + + return MVNETA_XDP_DROPPED; } static int @@ -2123,8 +2169,8 @@ mvneta_xdp_xmit_back(struct mvneta_port *pp, struct xdp_buff *xdp) struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); struct mvneta_tx_queue *txq; struct netdev_queue *nq; + int cpu, nxmit_byte = 0; struct xdp_frame *xdpf; - int cpu; u32 ret; xdpf = xdp_convert_buff_to_frame(xdp); @@ -2136,10 +2182,10 @@ mvneta_xdp_xmit_back(struct mvneta_port *pp, struct xdp_buff *xdp) nq = netdev_get_tx_queue(pp->dev, txq->id); __netif_tx_lock(nq, cpu); - ret = mvneta_xdp_submit_frame(pp, txq, xdpf, false); + ret = mvneta_xdp_submit_frame(pp, txq, xdpf, &nxmit_byte, false); if (ret == MVNETA_XDP_TX) { u64_stats_update_begin(&stats->syncp); - stats->es.ps.tx_bytes += xdpf->len; + stats->es.ps.tx_bytes += nxmit_byte; stats->es.ps.tx_packets++; stats->es.ps.xdp_tx++; u64_stats_update_end(&stats->syncp); @@ -2178,11 +2224,11 @@ mvneta_xdp_xmit(struct net_device *dev, int num_frame, __netif_tx_lock(nq, cpu); for (i = 0; i < num_frame; i++) { - ret = mvneta_xdp_submit_frame(pp, txq, frames[i], true); + ret = mvneta_xdp_submit_frame(pp, txq, frames[i], &nxmit_byte, + true); if (ret != MVNETA_XDP_TX) break; - nxmit_byte += frames[i]->len; nxmit++; } @@ -2205,7 +2251,6 @@ mvneta_run_xdp(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, struct bpf_prog *prog, struct xdp_buff *xdp, u32 frame_sz, struct mvneta_stats *stats) { - struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); unsigned int len, data_len, sync; u32 ret, act; @@ -2226,7 +2271,7 @@ mvneta_run_xdp(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, err = xdp_do_redirect(pp->dev, xdp, prog); if (unlikely(err)) { - mvneta_xdp_put_buff(pp, rxq, xdp, sinfo, sync); + mvneta_xdp_put_buff(pp, rxq, xdp, sync); ret = MVNETA_XDP_DROPPED; } else { ret = MVNETA_XDP_REDIR; @@ -2237,7 +2282,7 @@ mvneta_run_xdp(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, case XDP_TX: ret = mvneta_xdp_xmit_back(pp, xdp); if (ret != MVNETA_XDP_TX) - mvneta_xdp_put_buff(pp, rxq, xdp, sinfo, sync); + mvneta_xdp_put_buff(pp, rxq, xdp, sync); break; default: bpf_warn_invalid_xdp_action(pp->dev, prog, act); @@ -2246,7 +2291,7 @@ mvneta_run_xdp(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, trace_xdp_exception(pp->dev, prog, act); fallthrough; case XDP_DROP: - mvneta_xdp_put_buff(pp, rxq, xdp, sinfo, sync); + mvneta_xdp_put_buff(pp, rxq, xdp, sync); ret = MVNETA_XDP_DROPPED; stats->xdp_drop++; break; @@ -2269,7 +2314,6 @@ mvneta_swbm_rx_frame(struct mvneta_port *pp, int data_len = -MVNETA_MH_SIZE, len; struct net_device *dev = pp->dev; enum dma_data_direction dma_dir; - struct skb_shared_info *sinfo; if (*size > MVNETA_MAX_RX_BUF_SIZE) { len = MVNETA_MAX_RX_BUF_SIZE; @@ -2289,11 +2333,9 @@ mvneta_swbm_rx_frame(struct mvneta_port *pp, /* Prefetch header */ prefetch(data); + xdp_buff_clear_frags_flag(xdp); xdp_prepare_buff(xdp, data, pp->rx_offset_correction + MVNETA_MH_SIZE, data_len, false); - - sinfo = xdp_get_shared_info_from_buff(xdp); - sinfo->nr_frags = 0; } static void @@ -2301,9 +2343,9 @@ mvneta_swbm_add_rx_fragment(struct mvneta_port *pp, struct mvneta_rx_desc *rx_desc, struct mvneta_rx_queue *rxq, struct xdp_buff *xdp, int *size, - struct skb_shared_info *xdp_sinfo, struct page *page) { + struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); struct net_device *dev = pp->dev; enum dma_data_direction dma_dir; int data_len, len; @@ -2321,25 +2363,25 @@ mvneta_swbm_add_rx_fragment(struct mvneta_port *pp, len, dma_dir); rx_desc->buf_phys_addr = 0; - if (data_len > 0 && xdp_sinfo->nr_frags < MAX_SKB_FRAGS) { - skb_frag_t *frag = &xdp_sinfo->frags[xdp_sinfo->nr_frags++]; + if (!xdp_buff_has_frags(xdp)) + sinfo->nr_frags = 0; + + if (data_len > 0 && sinfo->nr_frags < MAX_SKB_FRAGS) { + skb_frag_t *frag = &sinfo->frags[sinfo->nr_frags++]; skb_frag_off_set(frag, pp->rx_offset_correction); skb_frag_size_set(frag, data_len); __skb_frag_set_page(frag, page); + + if (!xdp_buff_has_frags(xdp)) { + sinfo->xdp_frags_size = *size; + xdp_buff_set_frags_flag(xdp); + } + if (page_is_pfmemalloc(page)) + xdp_buff_set_frag_pfmemalloc(xdp); } else { page_pool_put_full_page(rxq->page_pool, page, true); } - - /* last fragment */ - if (len == *size) { - struct skb_shared_info *sinfo; - - sinfo = xdp_get_shared_info_from_buff(xdp); - sinfo->nr_frags = xdp_sinfo->nr_frags; - memcpy(sinfo->frags, xdp_sinfo->frags, - sinfo->nr_frags * sizeof(skb_frag_t)); - } *size -= len; } @@ -2348,8 +2390,11 @@ mvneta_swbm_build_skb(struct mvneta_port *pp, struct page_pool *pool, struct xdp_buff *xdp, u32 desc_status) { struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); - int i, num_frags = sinfo->nr_frags; struct sk_buff *skb; + u8 num_frags; + + if (unlikely(xdp_buff_has_frags(xdp))) + num_frags = sinfo->nr_frags; skb = build_skb(xdp->data_hard_start, PAGE_SIZE); if (!skb) @@ -2361,13 +2406,11 @@ mvneta_swbm_build_skb(struct mvneta_port *pp, struct page_pool *pool, skb_put(skb, xdp->data_end - xdp->data); skb->ip_summed = mvneta_rx_csum(pp, desc_status); - for (i = 0; i < num_frags; i++) { - skb_frag_t *frag = &sinfo->frags[i]; - - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - skb_frag_page(frag), skb_frag_off(frag), - skb_frag_size(frag), PAGE_SIZE); - } + if (unlikely(xdp_buff_has_frags(xdp))) + xdp_update_skb_shared_info(skb, num_frags, + sinfo->xdp_frags_size, + num_frags * xdp->frame_sz, + xdp_buff_is_frag_pfmemalloc(xdp)); return skb; } @@ -2379,7 +2422,6 @@ static int mvneta_rx_swbm(struct napi_struct *napi, { int rx_proc = 0, rx_todo, refill, size = 0; struct net_device *dev = pp->dev; - struct skb_shared_info sinfo; struct mvneta_stats ps = {}; struct bpf_prog *xdp_prog; u32 desc_status, frame_sz; @@ -2388,8 +2430,6 @@ static int mvneta_rx_swbm(struct napi_struct *napi, xdp_init_buff(&xdp_buf, PAGE_SIZE, &rxq->xdp_rxq); xdp_buf.data_hard_start = NULL; - sinfo.nr_frags = 0; - /* Get number of received packets */ rx_todo = mvneta_rxq_busy_desc_num_get(pp, rxq); @@ -2431,7 +2471,7 @@ static int mvneta_rx_swbm(struct napi_struct *napi, } mvneta_swbm_add_rx_fragment(pp, rx_desc, rxq, &xdp_buf, - &size, &sinfo, page); + &size, page); } /* Middle or Last descriptor */ if (!(rx_status & MVNETA_RXD_LAST_DESC)) @@ -2439,7 +2479,7 @@ static int mvneta_rx_swbm(struct napi_struct *napi, continue; if (size) { - mvneta_xdp_put_buff(pp, rxq, &xdp_buf, &sinfo, -1); + mvneta_xdp_put_buff(pp, rxq, &xdp_buf, -1); goto next; } @@ -2451,7 +2491,7 @@ static int mvneta_rx_swbm(struct napi_struct *napi, if (IS_ERR(skb)) { struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); - mvneta_xdp_put_buff(pp, rxq, &xdp_buf, &sinfo, -1); + mvneta_xdp_put_buff(pp, rxq, &xdp_buf, -1); u64_stats_update_begin(&stats->syncp); stats->es.skb_alloc_error++; @@ -2468,11 +2508,10 @@ static int mvneta_rx_swbm(struct napi_struct *napi, napi_gro_receive(napi, skb); next: xdp_buf.data_hard_start = NULL; - sinfo.nr_frags = 0; } if (xdp_buf.data_hard_start) - mvneta_xdp_put_buff(pp, rxq, &xdp_buf, &sinfo, -1); + mvneta_xdp_put_buff(pp, rxq, &xdp_buf, -1); if (ps.xdp_redirect) xdp_do_flush_map(); @@ -3260,7 +3299,8 @@ static int mvneta_create_page_pool(struct mvneta_port *pp, return err; } - err = xdp_rxq_info_reg(&rxq->xdp_rxq, pp->dev, rxq->id, 0); + err = __xdp_rxq_info_reg(&rxq->xdp_rxq, pp->dev, rxq->id, 0, + PAGE_SIZE); if (err < 0) goto err_free_pp; @@ -3740,6 +3780,7 @@ static void mvneta_percpu_disable(void *arg) static int mvneta_change_mtu(struct net_device *dev, int mtu) { struct mvneta_port *pp = netdev_priv(dev); + struct bpf_prog *prog = pp->xdp_prog; int ret; if (!IS_ALIGNED(MVNETA_RX_PKT_SIZE(mtu), 8)) { @@ -3748,8 +3789,11 @@ static int mvneta_change_mtu(struct net_device *dev, int mtu) mtu = ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8); } - if (pp->xdp_prog && mtu > MVNETA_MAX_RX_BUF_SIZE) { - netdev_info(dev, "Illegal MTU value %d for XDP mode\n", mtu); + if (prog && !prog->aux->xdp_has_frags && + mtu > MVNETA_MAX_RX_BUF_SIZE) { + netdev_info(dev, "Illegal MTU %d for XDP prog without frags\n", + mtu); + return -EINVAL; } @@ -3969,6 +4013,15 @@ static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = { .pcs_an_restart = mvneta_pcs_an_restart, }; +static struct phylink_pcs *mvneta_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct mvneta_port *pp = netdev_priv(ndev); + + return &pp->phylink_pcs; +} + static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, phy_interface_t interface) { @@ -4169,13 +4222,14 @@ static void mvneta_mac_link_up(struct phylink_config *config, mvneta_port_up(pp); if (phy && pp->eee_enabled) { - pp->eee_active = phy_init_eee(phy, 0) >= 0; + pp->eee_active = phy_init_eee(phy, false) >= 0; mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled); } } static const struct phylink_mac_ops mvneta_phylink_ops = { .validate = phylink_generic_validate, + .mac_select_pcs = mvneta_mac_select_pcs, .mac_prepare = mvneta_mac_prepare, .mac_config = mvneta_mac_config, .mac_finish = mvneta_mac_finish, @@ -4490,8 +4544,9 @@ static int mvneta_xdp_setup(struct net_device *dev, struct bpf_prog *prog, struct mvneta_port *pp = netdev_priv(dev); struct bpf_prog *old_prog; - if (prog && dev->mtu > MVNETA_MAX_RX_BUF_SIZE) { - NL_SET_ERR_MSG_MOD(extack, "MTU too large for XDP"); + if (prog && !prog->aux->xdp_has_frags && + dev->mtu > MVNETA_MAX_RX_BUF_SIZE) { + NL_SET_ERR_MSG_MOD(extack, "prog does not support XDP frags"); return -EOPNOTSUPP; } @@ -5272,6 +5327,10 @@ static void mvneta_conf_mbus_windows(struct mvneta_port *pp, win_protect |= 3 << (2 * i); } } else { + if (pp->neta_ac5) + mvreg_write(pp, MVNETA_WIN_BASE(0), + (MVNETA_AC5_CNM_DDR_ATTR << 8) | + MVNETA_AC5_CNM_DDR_TARGET); /* For Armada3700 open default 4GB Mbus window, leaving * arbitration of target/attribute to a different layer * of configuration. @@ -5321,26 +5380,66 @@ static int mvneta_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; - dev->irq = irq_of_parse_and_map(dn, 0); - if (dev->irq == 0) - return -EINVAL; + dev->tx_queue_len = MVNETA_MAX_TXD; + dev->watchdog_timeo = 5 * HZ; + dev->netdev_ops = &mvneta_netdev_ops; + dev->ethtool_ops = &mvneta_eth_tool_ops; + + pp = netdev_priv(dev); + spin_lock_init(&pp->lock); + pp->dn = dn; + + pp->rxq_def = rxq_def; + pp->indir[0] = rxq_def; err = of_get_phy_mode(dn, &phy_mode); if (err) { dev_err(&pdev->dev, "incorrect phy-mode\n"); - goto err_free_irq; + return err; } + pp->phy_interface = phy_mode; + comphy = devm_of_phy_get(&pdev->dev, dn, NULL); - if (comphy == ERR_PTR(-EPROBE_DEFER)) { - err = -EPROBE_DEFER; - goto err_free_irq; - } else if (IS_ERR(comphy)) { + if (comphy == ERR_PTR(-EPROBE_DEFER)) + return -EPROBE_DEFER; + + if (IS_ERR(comphy)) comphy = NULL; + + pp->comphy = comphy; + + pp->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pp->base)) + return PTR_ERR(pp->base); + + /* Get special SoC configurations */ + if (of_device_is_compatible(dn, "marvell,armada-3700-neta")) + pp->neta_armada3700 = true; + if (of_device_is_compatible(dn, "marvell,armada-ac5-neta")) { + pp->neta_armada3700 = true; + pp->neta_ac5 = true; } - pp = netdev_priv(dev); - spin_lock_init(&pp->lock); + dev->irq = irq_of_parse_and_map(dn, 0); + if (dev->irq == 0) + return -EINVAL; + + pp->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(pp->clk)) + pp->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pp->clk)) { + err = PTR_ERR(pp->clk); + goto err_free_irq; + } + + clk_prepare_enable(pp->clk); + + pp->clk_bus = devm_clk_get(&pdev->dev, "bus"); + if (!IS_ERR(pp->clk_bus)) + clk_prepare_enable(pp->clk_bus); + + pp->phylink_pcs.ops = &mvneta_phylink_pcs_ops; pp->phylink_config.dev = &dev->dev; pp->phylink_config.type = PHYLINK_NETDEV; @@ -5377,55 +5476,16 @@ static int mvneta_probe(struct platform_device *pdev) phy_mode, &mvneta_phylink_ops); if (IS_ERR(phylink)) { err = PTR_ERR(phylink); - goto err_free_irq; - } - - dev->tx_queue_len = MVNETA_MAX_TXD; - dev->watchdog_timeo = 5 * HZ; - dev->netdev_ops = &mvneta_netdev_ops; - - dev->ethtool_ops = &mvneta_eth_tool_ops; - - pp->phylink = phylink; - pp->comphy = comphy; - pp->phy_interface = phy_mode; - pp->dn = dn; - - pp->rxq_def = rxq_def; - pp->indir[0] = rxq_def; - - /* Get special SoC configurations */ - if (of_device_is_compatible(dn, "marvell,armada-3700-neta")) - pp->neta_armada3700 = true; - - pp->clk = devm_clk_get(&pdev->dev, "core"); - if (IS_ERR(pp->clk)) - pp->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pp->clk)) { - err = PTR_ERR(pp->clk); - goto err_free_phylink; - } - - clk_prepare_enable(pp->clk); - - pp->clk_bus = devm_clk_get(&pdev->dev, "bus"); - if (!IS_ERR(pp->clk_bus)) - clk_prepare_enable(pp->clk_bus); - - pp->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(pp->base)) { - err = PTR_ERR(pp->base); goto err_clk; } - pp->phylink_pcs.ops = &mvneta_phylink_pcs_ops; - phylink_set_pcs(phylink, &pp->phylink_pcs); + pp->phylink = phylink; /* Alloc per-cpu port structure */ pp->ports = alloc_percpu(struct mvneta_pcpu_port); if (!pp->ports) { err = -ENOMEM; - goto err_clk; + goto err_free_phylink; } /* Alloc per-cpu stats */ @@ -5569,12 +5629,12 @@ static int mvneta_probe(struct platform_device *pdev) free_percpu(pp->stats); err_free_ports: free_percpu(pp->ports); -err_clk: - clk_disable_unprepare(pp->clk_bus); - clk_disable_unprepare(pp->clk); err_free_phylink: if (pp->phylink) phylink_destroy(pp->phylink); +err_clk: + clk_disable_unprepare(pp->clk_bus); + clk_disable_unprepare(pp->clk); err_free_irq: irq_dispose_mapping(dev->irq); return err; @@ -5720,6 +5780,7 @@ static const struct of_device_id mvneta_match[] = { { .compatible = "marvell,armada-370-neta" }, { .compatible = "marvell,armada-xp-neta" }, { .compatible = "marvell,armada-3700-neta" }, + { .compatible = "marvell,armada-ac5-neta" }, { } }; MODULE_DEVICE_TABLE(of, mvneta_match); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c index 3631d612aaca..25491edc35ce 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c @@ -578,31 +578,78 @@ void cgx_lmac_promisc_config(int cgx_id, int lmac_id, bool enable) } } +static int cgx_lmac_get_pause_frm_status(void *cgxd, int lmac_id, + u8 *tx_pause, u8 *rx_pause) +{ + struct cgx *cgx = cgxd; + u64 cfg; + + if (is_dev_rpm(cgx)) + return 0; + + if (!is_lmac_valid(cgx, lmac_id)) + return -ENODEV; + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); + *rx_pause = !!(cfg & CGX_SMUX_RX_FRM_CTL_CTL_BCK); + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); + *tx_pause = !!(cfg & CGX_SMUX_TX_CTL_L2P_BP_CONV); + return 0; +} + /* Enable or disable forwarding received pause frames to Tx block */ void cgx_lmac_enadis_rx_pause_fwding(void *cgxd, int lmac_id, bool enable) { struct cgx *cgx = cgxd; + u8 rx_pause, tx_pause; + bool is_pfc_enabled; + struct lmac *lmac; u64 cfg; if (!cgx) return; + lmac = lmac_pdata(lmac_id, cgx); + if (!lmac) + return; + + /* Pause frames are not enabled just return */ + if (!bitmap_weight(lmac->rx_fc_pfvf_bmap.bmap, lmac->rx_fc_pfvf_bmap.max)) + return; + + cgx_lmac_get_pause_frm_status(cgx, lmac_id, &rx_pause, &tx_pause); + is_pfc_enabled = rx_pause ? false : true; + if (enable) { - cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); - cfg |= CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; - cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); + if (!is_pfc_enabled) { + cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); + cfg |= CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); - cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); - cfg |= CGX_SMUX_RX_FRM_CTL_CTL_BCK; - cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); + cfg |= CGX_SMUX_RX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); + } else { + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL); + cfg |= CGXX_SMUX_CBFC_CTL_BCK_EN; + cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg); + } } else { - cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); - cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; - cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); - cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); - cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; - cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); + if (!is_pfc_enabled) { + cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); + cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); + cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); + } else { + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL); + cfg &= ~CGXX_SMUX_CBFC_CTL_BCK_EN; + cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg); + } } } @@ -722,26 +769,6 @@ int cgx_lmac_tx_enable(void *cgxd, int lmac_id, bool enable) return !!(last & DATA_PKT_TX_EN); } -static int cgx_lmac_get_pause_frm_status(void *cgxd, int lmac_id, - u8 *tx_pause, u8 *rx_pause) -{ - struct cgx *cgx = cgxd; - u64 cfg; - - if (is_dev_rpm(cgx)) - return 0; - - if (!is_lmac_valid(cgx, lmac_id)) - return -ENODEV; - - cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); - *rx_pause = !!(cfg & CGX_SMUX_RX_FRM_CTL_CTL_BCK); - - cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); - *tx_pause = !!(cfg & CGX_SMUX_TX_CTL_L2P_BP_CONV); - return 0; -} - static int cgx_lmac_enadis_pause_frm(void *cgxd, int lmac_id, u8 tx_pause, u8 rx_pause) { @@ -782,21 +809,8 @@ static void cgx_lmac_pause_frm_config(void *cgxd, int lmac_id, bool enable) if (!is_lmac_valid(cgx, lmac_id)) return; + if (enable) { - /* Enable receive pause frames */ - cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); - cfg |= CGX_SMUX_RX_FRM_CTL_CTL_BCK; - cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); - - cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); - cfg |= CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; - cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); - - /* Enable pause frames transmission */ - cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); - cfg |= CGX_SMUX_TX_CTL_L2P_BP_CONV; - cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg); - /* Set pause time and interval */ cgx_write(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_TIME, DEFAULT_PAUSE_TIME); @@ -813,21 +827,120 @@ static void cgx_lmac_pause_frm_config(void *cgxd, int lmac_id, bool enable) cfg &= ~0xFFFFULL; cgx_write(cgx, lmac_id, CGXX_GMP_GMI_TX_PAUSE_PKT_INTERVAL, cfg | (DEFAULT_PAUSE_TIME / 2)); - } else { - /* ALL pause frames received are completely ignored */ - cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); - cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; - cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); - - cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); - cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; - cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); - - /* Disable pause frames transmission */ - cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); - cfg &= ~CGX_SMUX_TX_CTL_L2P_BP_CONV; - cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg); } + + /* ALL pause frames received are completely ignored */ + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); + cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); + + cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); + cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; + cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); + + /* Disable pause frames transmission */ + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); + cfg &= ~CGX_SMUX_TX_CTL_L2P_BP_CONV; + cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg); + + cfg = cgx_read(cgx, 0, CGXX_CMR_RX_OVR_BP); + cfg |= CGX_CMR_RX_OVR_BP_EN(lmac_id); + cfg &= ~CGX_CMR_RX_OVR_BP_BP(lmac_id); + cgx_write(cgx, 0, CGXX_CMR_RX_OVR_BP, cfg); +} + +int verify_lmac_fc_cfg(void *cgxd, int lmac_id, u8 tx_pause, u8 rx_pause, + int pfvf_idx) +{ + struct cgx *cgx = cgxd; + struct lmac *lmac; + + lmac = lmac_pdata(lmac_id, cgx); + if (!lmac) + return -ENODEV; + + if (!rx_pause) + clear_bit(pfvf_idx, lmac->rx_fc_pfvf_bmap.bmap); + else + set_bit(pfvf_idx, lmac->rx_fc_pfvf_bmap.bmap); + + if (!tx_pause) + clear_bit(pfvf_idx, lmac->tx_fc_pfvf_bmap.bmap); + else + set_bit(pfvf_idx, lmac->tx_fc_pfvf_bmap.bmap); + + /* check if other pfvfs are using flow control */ + if (!rx_pause && bitmap_weight(lmac->rx_fc_pfvf_bmap.bmap, lmac->rx_fc_pfvf_bmap.max)) { + dev_warn(&cgx->pdev->dev, + "Receive Flow control disable not permitted as its used by other PFVFs\n"); + return -EPERM; + } + + if (!tx_pause && bitmap_weight(lmac->tx_fc_pfvf_bmap.bmap, lmac->tx_fc_pfvf_bmap.max)) { + dev_warn(&cgx->pdev->dev, + "Transmit Flow control disable not permitted as its used by other PFVFs\n"); + return -EPERM; + } + + return 0; +} + +int cgx_lmac_pfc_config(void *cgxd, int lmac_id, u8 tx_pause, + u8 rx_pause, u16 pfc_en) +{ + struct cgx *cgx = cgxd; + u64 cfg; + + if (!is_lmac_valid(cgx, lmac_id)) + return -ENODEV; + + /* Return as no traffic classes are requested */ + if (tx_pause && !pfc_en) + return 0; + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL); + + if (rx_pause) { + cfg |= (CGXX_SMUX_CBFC_CTL_RX_EN | + CGXX_SMUX_CBFC_CTL_BCK_EN | + CGXX_SMUX_CBFC_CTL_DRP_EN); + } else { + cfg &= ~(CGXX_SMUX_CBFC_CTL_RX_EN | + CGXX_SMUX_CBFC_CTL_BCK_EN | + CGXX_SMUX_CBFC_CTL_DRP_EN); + } + + if (tx_pause) + cfg |= CGXX_SMUX_CBFC_CTL_TX_EN; + else + cfg &= ~CGXX_SMUX_CBFC_CTL_TX_EN; + + cfg = FIELD_SET(CGX_PFC_CLASS_MASK, pfc_en, cfg); + + cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg); + + /* Write source MAC address which will be filled into PFC packet */ + cfg = cgx_lmac_addr_get(cgx->cgx_id, lmac_id); + cgx_write(cgx, lmac_id, CGXX_SMUX_SMAC, cfg); + + return 0; +} + +int cgx_lmac_get_pfc_frm_cfg(void *cgxd, int lmac_id, u8 *tx_pause, + u8 *rx_pause) +{ + struct cgx *cgx = cgxd; + u64 cfg; + + if (!is_lmac_valid(cgx, lmac_id)) + return -ENODEV; + + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL); + + *rx_pause = !!(cfg & CGXX_SMUX_CBFC_CTL_RX_EN); + *tx_pause = !!(cfg & CGXX_SMUX_CBFC_CTL_TX_EN); + + return 0; } void cgx_lmac_ptp_config(void *cgxd, int lmac_id, bool enable) @@ -1489,6 +1602,16 @@ static int cgx_lmac_init(struct cgx *cgx) /* Reserve first entry for default MAC address */ set_bit(0, lmac->mac_to_index_bmap.bmap); + lmac->rx_fc_pfvf_bmap.max = 128; + err = rvu_alloc_bitmap(&lmac->rx_fc_pfvf_bmap); + if (err) + goto err_dmac_bmap_free; + + lmac->tx_fc_pfvf_bmap.max = 128; + err = rvu_alloc_bitmap(&lmac->tx_fc_pfvf_bmap); + if (err) + goto err_rx_fc_bmap_free; + init_waitqueue_head(&lmac->wq_cmd_cmplt); mutex_init(&lmac->cmd_lock); spin_lock_init(&lmac->event_cb_lock); @@ -1505,6 +1628,10 @@ static int cgx_lmac_init(struct cgx *cgx) return cgx_lmac_verify_fwi_version(cgx); err_bitmap_free: + rvu_free_bitmap(&lmac->tx_fc_pfvf_bmap); +err_rx_fc_bmap_free: + rvu_free_bitmap(&lmac->rx_fc_pfvf_bmap); +err_dmac_bmap_free: rvu_free_bitmap(&lmac->mac_to_index_bmap); err_name_free: kfree(lmac->name); @@ -1572,6 +1699,8 @@ static struct mac_ops cgx_mac_ops = { .mac_enadis_ptp_config = cgx_lmac_ptp_config, .mac_rx_tx_enable = cgx_lmac_rx_tx_enable, .mac_tx_enable = cgx_lmac_tx_enable, + .pfc_config = cgx_lmac_pfc_config, + .mac_get_pfc_frm_cfg = cgx_lmac_get_pfc_frm_cfg, }; static int cgx_probe(struct pci_dev *pdev, const struct pci_device_id *id) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h index ab1e4abdea38..bd2f33a26eee 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h @@ -76,6 +76,13 @@ #define CGXX_SMUX_TX_CTL 0x20178 #define CGXX_SMUX_TX_PAUSE_PKT_TIME 0x20110 #define CGXX_SMUX_TX_PAUSE_PKT_INTERVAL 0x20120 +#define CGXX_SMUX_SMAC 0x20108 +#define CGXX_SMUX_CBFC_CTL 0x20218 +#define CGXX_SMUX_CBFC_CTL_RX_EN BIT_ULL(0) +#define CGXX_SMUX_CBFC_CTL_TX_EN BIT_ULL(1) +#define CGXX_SMUX_CBFC_CTL_DRP_EN BIT_ULL(2) +#define CGXX_SMUX_CBFC_CTL_BCK_EN BIT_ULL(3) +#define CGX_PFC_CLASS_MASK GENMASK_ULL(47, 32) #define CGXX_GMP_GMI_TX_PAUSE_PKT_TIME 0x38230 #define CGXX_GMP_GMI_TX_PAUSE_PKT_INTERVAL 0x38248 #define CGX_SMUX_TX_CTL_L2P_BP_CONV BIT_ULL(7) @@ -172,4 +179,10 @@ u64 cgx_lmac_read(int cgx_id, int lmac_id, u64 offset); int cgx_lmac_addr_update(u8 cgx_id, u8 lmac_id, u8 *mac_addr, u8 index); u64 cgx_read_dmac_ctrl(void *cgxd, int lmac_id); u64 cgx_read_dmac_entry(void *cgxd, int index); +int cgx_lmac_pfc_config(void *cgxd, int lmac_id, u8 tx_pause, u8 rx_pause, + u16 pfc_en); +int cgx_lmac_get_pfc_frm_cfg(void *cgxd, int lmac_id, u8 *tx_pause, + u8 *rx_pause); +int verify_lmac_fc_cfg(void *cgxd, int lmac_id, u8 tx_pause, u8 rx_pause, + int pfvf_idx); #endif /* CGX_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h index b33e7d1d0851..f30581bf0688 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h @@ -17,6 +17,8 @@ * @resp: command response * @link_info: link related information * @mac_to_index_bmap: Mac address to CGX table index mapping + * @rx_fc_pfvf_bmap: Receive flow control enabled netdev mapping + * @tx_fc_pfvf_bmap: Transmit flow control enabled netdev mapping * @event_cb: callback for linkchange events * @event_cb_lock: lock for serializing callback with unregister * @cgx: parent cgx port @@ -33,6 +35,8 @@ struct lmac { u64 resp; struct cgx_link_user_info link_info; struct rsrc_bmap mac_to_index_bmap; + struct rsrc_bmap rx_fc_pfvf_bmap; + struct rsrc_bmap tx_fc_pfvf_bmap; struct cgx_event_cb event_cb; /* lock for serializing callback with unregister */ spinlock_t event_cb_lock; @@ -110,6 +114,12 @@ struct mac_ops { int (*mac_rx_tx_enable)(void *cgxd, int lmac_id, bool enable); int (*mac_tx_enable)(void *cgxd, int lmac_id, bool enable); + int (*pfc_config)(void *cgxd, int lmac_id, + u8 tx_pause, u8 rx_pause, u16 pfc_en); + + int (*mac_get_pfc_frm_cfg)(void *cgxd, int lmac_id, + u8 *tx_pause, u8 *rx_pause); + }; struct cgx { diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h index 58e2aeebc14f..550cb11197bf 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -172,6 +172,8 @@ M(RPM_STATS, 0x21C, rpm_stats, msg_req, rpm_stats_rsp) \ M(CGX_MAC_ADDR_RESET, 0x21D, cgx_mac_addr_reset, msg_req, msg_rsp) \ M(CGX_MAC_ADDR_UPDATE, 0x21E, cgx_mac_addr_update, cgx_mac_addr_update_req, \ msg_rsp) \ +M(CGX_PRIO_FLOW_CTRL_CFG, 0x21F, cgx_prio_flow_ctrl_cfg, cgx_pfc_cfg, \ + cgx_pfc_rsp) \ /* NPA mbox IDs (range 0x400 - 0x5FF) */ \ M(NPA_LF_ALLOC, 0x400, npa_lf_alloc, \ npa_lf_alloc_req, npa_lf_alloc_rsp) \ @@ -609,6 +611,21 @@ struct rpm_stats_rsp { u64 tx_stats[RPM_TX_STATS_COUNT]; }; +struct cgx_pfc_cfg { + struct mbox_msghdr hdr; + u8 rx_pause; + u8 tx_pause; + u16 pfc_en; /* bitmap indicating pfc enabled traffic classes */ +}; + +struct cgx_pfc_rsp { + struct mbox_msghdr hdr; + u8 rx_pause; + u8 tx_pause; +}; + + /* NPA mbox message formats */ + struct npc_set_pkind { struct mbox_msghdr hdr; #define OTX2_PRIV_FLAGS_DEFAULT BIT_ULL(0) @@ -1603,6 +1620,8 @@ enum cgx_af_status { LMAC_AF_ERR_INVALID_PARAM = -1101, LMAC_AF_ERR_PF_NOT_MAPPED = -1102, LMAC_AF_ERR_PERM_DENIED = -1103, + LMAC_AF_ERR_PFC_ENADIS_PERM_DENIED = -1104, + LMAC_AF_ERR_8023PAUSE_ENADIS_PERM_DENIED = -1105, }; #endif /* MBOX_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/ptp.c b/drivers/net/ethernet/marvell/octeontx2/af/ptp.c index e682b7bfde64..67a6821d2dff 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/ptp.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/ptp.c @@ -25,6 +25,9 @@ #define PCI_SUBSYS_DEVID_OCTX2_95XXO_PTP 0xB600 #define PCI_DEVID_OCTEONTX2_RST 0xA085 #define PCI_DEVID_CN10K_PTP 0xA09E +#define PCI_SUBSYS_DEVID_CN10K_A_PTP 0xB900 +#define PCI_SUBSYS_DEVID_CNF10K_A_PTP 0xBA00 +#define PCI_SUBSYS_DEVID_CNF10K_B_PTP 0xBC00 #define PCI_PTP_BAR_NO 0 @@ -46,10 +49,105 @@ #define PTP_CLOCK_HI 0xF10ULL #define PTP_CLOCK_COMP 0xF18ULL #define PTP_TIMESTAMP 0xF20ULL +#define PTP_CLOCK_SEC 0xFD0ULL + +#define CYCLE_MULT 1000 static struct ptp *first_ptp_block; static const struct pci_device_id ptp_id_table[]; +static bool cn10k_ptp_errata(struct ptp *ptp) +{ + if (ptp->pdev->subsystem_device == PCI_SUBSYS_DEVID_CN10K_A_PTP || + ptp->pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_A_PTP) + return true; + return false; +} + +static bool is_ptp_tsfmt_sec_nsec(struct ptp *ptp) +{ + if (ptp->pdev->subsystem_device == PCI_SUBSYS_DEVID_CN10K_A_PTP || + ptp->pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_A_PTP) + return true; + return false; +} + +static u64 read_ptp_tstmp_sec_nsec(struct ptp *ptp) +{ + u64 sec, sec1, nsec; + unsigned long flags; + + spin_lock_irqsave(&ptp->ptp_lock, flags); + sec = readq(ptp->reg_base + PTP_CLOCK_SEC) & 0xFFFFFFFFUL; + nsec = readq(ptp->reg_base + PTP_CLOCK_HI); + sec1 = readq(ptp->reg_base + PTP_CLOCK_SEC) & 0xFFFFFFFFUL; + /* check nsec rollover */ + if (sec1 > sec) { + nsec = readq(ptp->reg_base + PTP_CLOCK_HI); + sec = sec1; + } + spin_unlock_irqrestore(&ptp->ptp_lock, flags); + + return sec * NSEC_PER_SEC + nsec; +} + +static u64 read_ptp_tstmp_nsec(struct ptp *ptp) +{ + return readq(ptp->reg_base + PTP_CLOCK_HI); +} + +static u64 ptp_calc_adjusted_comp(u64 ptp_clock_freq) +{ + u64 comp, adj = 0, cycles_per_sec, ns_drift = 0; + u32 ptp_clock_nsec, cycle_time; + int cycle; + + /* Errata: + * Issue #1: At the time of 1 sec rollover of the nano-second counter, + * the nano-second counter is set to 0. However, it should be set to + * (existing counter_value - 10^9). + * + * Issue #2: The nano-second counter rolls over at 0x3B9A_C9FF. + * It should roll over at 0x3B9A_CA00. + */ + + /* calculate ptp_clock_comp value */ + comp = ((u64)1000000000ULL << 32) / ptp_clock_freq; + /* use CYCLE_MULT to avoid accuracy loss due to integer arithmetic */ + cycle_time = NSEC_PER_SEC * CYCLE_MULT / ptp_clock_freq; + /* cycles per sec */ + cycles_per_sec = ptp_clock_freq; + + /* check whether ptp nanosecond counter rolls over early */ + cycle = cycles_per_sec - 1; + ptp_clock_nsec = (cycle * comp) >> 32; + while (ptp_clock_nsec < NSEC_PER_SEC) { + if (ptp_clock_nsec == 0x3B9AC9FF) + goto calc_adj_comp; + cycle++; + ptp_clock_nsec = (cycle * comp) >> 32; + } + /* compute nanoseconds lost per second when nsec counter rolls over */ + ns_drift = ptp_clock_nsec - NSEC_PER_SEC; + /* calculate ptp_clock_comp adjustment */ + if (ns_drift > 0) { + adj = comp * ns_drift; + adj = adj / 1000000000ULL; + } + /* speed up the ptp clock to account for nanoseconds lost */ + comp += adj; + return comp; + +calc_adj_comp: + /* slow down the ptp clock to not rollover early */ + adj = comp * cycle_time; + adj = adj / 1000000000ULL; + adj = adj / CYCLE_MULT; + comp -= adj; + + return comp; +} + struct ptp *ptp_get(void) { struct ptp *ptp = first_ptp_block; @@ -77,8 +175,8 @@ void ptp_put(struct ptp *ptp) static int ptp_adjfine(struct ptp *ptp, long scaled_ppm) { bool neg_adj = false; - u64 comp; - u64 adj; + u32 freq, freq_adj; + u64 comp, adj; s64 ppb; if (scaled_ppm < 0) { @@ -100,15 +198,22 @@ static int ptp_adjfine(struct ptp *ptp, long scaled_ppm) * where tbase is the basic compensation value calculated * initialy in the probe function. */ - comp = ((u64)1000000000ull << 32) / ptp->clock_rate; /* convert scaled_ppm to ppb */ ppb = 1 + scaled_ppm; ppb *= 125; ppb >>= 13; - adj = comp * ppb; - adj = div_u64(adj, 1000000000ull); - comp = neg_adj ? comp - adj : comp + adj; + if (cn10k_ptp_errata(ptp)) { + /* calculate the new frequency based on ppb */ + freq_adj = (ptp->clock_rate * ppb) / 1000000000ULL; + freq = neg_adj ? ptp->clock_rate + freq_adj : ptp->clock_rate - freq_adj; + comp = ptp_calc_adjusted_comp(freq); + } else { + comp = ((u64)1000000000ull << 32) / ptp->clock_rate; + adj = comp * ppb; + adj = div_u64(adj, 1000000000ull); + comp = neg_adj ? comp - adj : comp + adj; + } writeq(comp, ptp->reg_base + PTP_CLOCK_COMP); return 0; @@ -117,7 +222,7 @@ static int ptp_adjfine(struct ptp *ptp, long scaled_ppm) static int ptp_get_clock(struct ptp *ptp, u64 *clk) { /* Return the current PTP clock */ - *clk = readq(ptp->reg_base + PTP_CLOCK_HI); + *clk = ptp->read_ptp_tstmp(ptp); return 0; } @@ -166,7 +271,11 @@ void ptp_start(struct ptp *ptp, u64 sclk, u32 ext_clk_freq, u32 extts) writeq(0x1dcd650000000000, ptp->reg_base + PTP_PPS_HI_INCR); writeq(0x1dcd650000000000, ptp->reg_base + PTP_PPS_LO_INCR); - clock_comp = ((u64)1000000000ull << 32) / ptp->clock_rate; + if (cn10k_ptp_errata(ptp)) + clock_comp = ptp_calc_adjusted_comp(ptp->clock_rate); + else + clock_comp = ((u64)1000000000ull << 32) / ptp->clock_rate; + /* Initial compensation value to start the nanosecs counter */ writeq(clock_comp, ptp->reg_base + PTP_CLOCK_COMP); } @@ -214,6 +323,12 @@ static int ptp_probe(struct pci_dev *pdev, if (!first_ptp_block) first_ptp_block = ptp; + spin_lock_init(&ptp->ptp_lock); + if (is_ptp_tsfmt_sec_nsec(ptp)) + ptp->read_ptp_tstmp = &read_ptp_tstmp_sec_nsec; + else + ptp->read_ptp_tstmp = &read_ptp_tstmp_nsec; + return 0; error_free: diff --git a/drivers/net/ethernet/marvell/octeontx2/af/ptp.h b/drivers/net/ethernet/marvell/octeontx2/af/ptp.h index 1b81a0493cd3..95a955159f40 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/ptp.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/ptp.h @@ -15,6 +15,8 @@ struct ptp { struct pci_dev *pdev; void __iomem *reg_base; + u64 (*read_ptp_tstmp)(struct ptp *ptp); + spinlock_t ptp_lock; /* lock */ u32 clock_rate; }; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c index 9ea2f6ac38ec..47e83d7a5804 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c @@ -32,6 +32,8 @@ static struct mac_ops rpm_mac_ops = { .mac_enadis_ptp_config = rpm_lmac_ptp_config, .mac_rx_tx_enable = rpm_lmac_rx_tx_enable, .mac_tx_enable = rpm_lmac_tx_enable, + .pfc_config = rpm_lmac_pfc_config, + .mac_get_pfc_frm_cfg = rpm_lmac_get_pfc_frm_cfg, }; struct mac_ops *rpm_get_mac_ops(void) @@ -96,11 +98,20 @@ int rpm_lmac_rx_tx_enable(void *rpmd, int lmac_id, bool enable) void rpm_lmac_enadis_rx_pause_fwding(void *rpmd, int lmac_id, bool enable) { rpm_t *rpm = rpmd; + struct lmac *lmac; u64 cfg; if (!rpm) return; + lmac = lmac_pdata(lmac_id, rpm); + if (!lmac) + return; + + /* Pause frames are not enabled just return */ + if (!bitmap_weight(lmac->rx_fc_pfvf_bmap.bmap, lmac->rx_fc_pfvf_bmap.max)) + return; + if (enable) { cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); cfg &= ~RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE; @@ -122,13 +133,94 @@ int rpm_lmac_get_pause_frm_status(void *rpmd, int lmac_id, return -ENODEV; cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); - *rx_pause = !(cfg & RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE); + if (!(cfg & RPMX_MTI_MAC100X_COMMAND_CONFIG_PFC_MODE)) { + *rx_pause = !(cfg & RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE); + *tx_pause = !(cfg & RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE); + } - cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); - *tx_pause = !(cfg & RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE); return 0; } +static void rpm_cfg_pfc_quanta_thresh(rpm_t *rpm, int lmac_id, + unsigned long pfc_en, + bool enable) +{ + u64 quanta_offset = 0, quanta_thresh = 0, cfg; + int i, shift; + + /* Set pause time and interval */ + for_each_set_bit(i, &pfc_en, 16) { + switch (i) { + case 0: + case 1: + quanta_offset = RPMX_MTI_MAC100X_CL01_PAUSE_QUANTA; + quanta_thresh = RPMX_MTI_MAC100X_CL01_QUANTA_THRESH; + break; + case 2: + case 3: + quanta_offset = RPMX_MTI_MAC100X_CL23_PAUSE_QUANTA; + quanta_thresh = RPMX_MTI_MAC100X_CL23_QUANTA_THRESH; + break; + case 4: + case 5: + quanta_offset = RPMX_MTI_MAC100X_CL45_PAUSE_QUANTA; + quanta_thresh = RPMX_MTI_MAC100X_CL45_QUANTA_THRESH; + break; + case 6: + case 7: + quanta_offset = RPMX_MTI_MAC100X_CL67_PAUSE_QUANTA; + quanta_thresh = RPMX_MTI_MAC100X_CL67_QUANTA_THRESH; + break; + case 8: + case 9: + quanta_offset = RPMX_MTI_MAC100X_CL89_PAUSE_QUANTA; + quanta_thresh = RPMX_MTI_MAC100X_CL89_QUANTA_THRESH; + break; + case 10: + case 11: + quanta_offset = RPMX_MTI_MAC100X_CL1011_PAUSE_QUANTA; + quanta_thresh = RPMX_MTI_MAC100X_CL1011_QUANTA_THRESH; + break; + case 12: + case 13: + quanta_offset = RPMX_MTI_MAC100X_CL1213_PAUSE_QUANTA; + quanta_thresh = RPMX_MTI_MAC100X_CL1213_QUANTA_THRESH; + break; + case 14: + case 15: + quanta_offset = RPMX_MTI_MAC100X_CL1415_PAUSE_QUANTA; + quanta_thresh = RPMX_MTI_MAC100X_CL1415_QUANTA_THRESH; + break; + } + + if (!quanta_offset || !quanta_thresh) + continue; + + shift = (i % 2) ? 1 : 0; + cfg = rpm_read(rpm, lmac_id, quanta_offset); + if (enable) { + cfg |= ((u64)RPM_DEFAULT_PAUSE_TIME << shift * 16); + } else { + if (!shift) + cfg &= ~GENMASK_ULL(15, 0); + else + cfg &= ~GENMASK_ULL(31, 16); + } + rpm_write(rpm, lmac_id, quanta_offset, cfg); + + cfg = rpm_read(rpm, lmac_id, quanta_thresh); + if (enable) { + cfg |= ((u64)(RPM_DEFAULT_PAUSE_TIME / 2) << shift * 16); + } else { + if (!shift) + cfg &= ~GENMASK_ULL(15, 0); + else + cfg &= ~GENMASK_ULL(31, 16); + } + rpm_write(rpm, lmac_id, quanta_thresh, cfg); + } +} + int rpm_lmac_enadis_pause_frm(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause) { @@ -152,8 +244,12 @@ int rpm_lmac_enadis_pause_frm(void *rpmd, int lmac_id, u8 tx_pause, cfg = rpm_read(rpm, 0, RPMX_CMR_RX_OVR_BP); if (tx_pause) { + /* Configure CL0 Pause Quanta & threshold for 802.3X frames */ + rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, 1, true); cfg &= ~RPMX_CMR_RX_OVR_BP_EN(lmac_id); } else { + /* Disable all Pause Quanta & threshold values */ + rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, 0xffff, false); cfg |= RPMX_CMR_RX_OVR_BP_EN(lmac_id); cfg &= ~RPMX_CMR_RX_OVR_BP_BP(lmac_id); } @@ -166,56 +262,20 @@ void rpm_lmac_pause_frm_config(void *rpmd, int lmac_id, bool enable) rpm_t *rpm = rpmd; u64 cfg; - if (enable) { - /* Enable 802.3 pause frame mode */ - cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); - cfg &= ~RPMX_MTI_MAC100X_COMMAND_CONFIG_PFC_MODE; - rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); + /* ALL pause frames received are completely ignored */ + cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); + cfg |= RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE; + rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); - /* Enable receive pause frames */ - cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); - cfg &= ~RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE; - rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); + /* Disable forward pause to TX block */ + cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); + cfg |= RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE; + rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); - /* Enable forward pause to TX block */ - cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); - cfg &= ~RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE; - rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); - - /* Enable pause frames transmission */ - cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); - cfg &= ~RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE; - rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); - - /* Set pause time and interval */ - cfg = rpm_read(rpm, lmac_id, - RPMX_MTI_MAC100X_CL01_PAUSE_QUANTA); - cfg &= ~0xFFFFULL; - rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_CL01_PAUSE_QUANTA, - cfg | RPM_DEFAULT_PAUSE_TIME); - /* Set pause interval as the hardware default is too short */ - cfg = rpm_read(rpm, lmac_id, - RPMX_MTI_MAC100X_CL01_QUANTA_THRESH); - cfg &= ~0xFFFFULL; - rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_CL01_QUANTA_THRESH, - cfg | (RPM_DEFAULT_PAUSE_TIME / 2)); - - } else { - /* ALL pause frames received are completely ignored */ - cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); - cfg |= RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE; - rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); - - /* Disable forward pause to TX block */ - cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); - cfg |= RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE; - rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); - - /* Disable pause frames transmission */ - cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); - cfg |= RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE; - rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); - } + /* Disable pause frames transmission */ + cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); + cfg |= RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE; + rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); } int rpm_get_rx_stats(void *rpmd, int lmac_id, int idx, u64 *rx_stat) @@ -323,3 +383,65 @@ void rpm_lmac_ptp_config(void *rpmd, int lmac_id, bool enable) cfg &= ~RPMX_RX_TS_PREPEND; rpm_write(rpm, lmac_id, RPMX_CMRX_CFG, cfg); } + +int rpm_lmac_pfc_config(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause, u16 pfc_en) +{ + rpm_t *rpm = rpmd; + u64 cfg; + + if (!is_lmac_valid(rpm, lmac_id)) + return -ENODEV; + + /* reset PFC class quanta and threshold */ + rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, 0xffff, false); + + cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); + + if (rx_pause) { + cfg &= ~(RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE | + RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE | + RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_FWD); + } else { + cfg |= (RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE | + RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE | + RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_FWD); + } + + if (tx_pause) { + rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, pfc_en, true); + cfg &= ~RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE; + } else { + rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, 0xfff, false); + cfg |= RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE; + } + + if (!rx_pause && !tx_pause) + cfg &= ~RPMX_MTI_MAC100X_COMMAND_CONFIG_PFC_MODE; + else + cfg |= RPMX_MTI_MAC100X_COMMAND_CONFIG_PFC_MODE; + + rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); + + cfg = rpm_read(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL); + cfg = FIELD_SET(RPM_PFC_CLASS_MASK, pfc_en, cfg); + rpm_write(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL, cfg); + + return 0; +} + +int rpm_lmac_get_pfc_frm_cfg(void *rpmd, int lmac_id, u8 *tx_pause, u8 *rx_pause) +{ + rpm_t *rpm = rpmd; + u64 cfg; + + if (!is_lmac_valid(rpm, lmac_id)) + return -ENODEV; + + cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); + if (cfg & RPMX_MTI_MAC100X_COMMAND_CONFIG_PFC_MODE) { + *rx_pause = !(cfg & RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE); + *tx_pause = !(cfg & RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE); + } + + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.h b/drivers/net/ethernet/marvell/octeontx2/af/rpm.h index ff580311edd0..9ab8d49dd180 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.h @@ -33,7 +33,21 @@ #define RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE BIT_ULL(8) #define RPMX_MTI_MAC100X_COMMAND_CONFIG_PFC_MODE BIT_ULL(19) #define RPMX_MTI_MAC100X_CL01_PAUSE_QUANTA 0x80A8 +#define RPMX_MTI_MAC100X_CL23_PAUSE_QUANTA 0x80B0 +#define RPMX_MTI_MAC100X_CL45_PAUSE_QUANTA 0x80B8 +#define RPMX_MTI_MAC100X_CL67_PAUSE_QUANTA 0x80C0 #define RPMX_MTI_MAC100X_CL01_QUANTA_THRESH 0x80C8 +#define RPMX_MTI_MAC100X_CL23_QUANTA_THRESH 0x80D0 +#define RPMX_MTI_MAC100X_CL45_QUANTA_THRESH 0x80D8 +#define RPMX_MTI_MAC100X_CL67_QUANTA_THRESH 0x80E0 +#define RPMX_MTI_MAC100X_CL89_PAUSE_QUANTA 0x8108 +#define RPMX_MTI_MAC100X_CL1011_PAUSE_QUANTA 0x8110 +#define RPMX_MTI_MAC100X_CL1213_PAUSE_QUANTA 0x8118 +#define RPMX_MTI_MAC100X_CL1415_PAUSE_QUANTA 0x8120 +#define RPMX_MTI_MAC100X_CL89_QUANTA_THRESH 0x8128 +#define RPMX_MTI_MAC100X_CL1011_QUANTA_THRESH 0x8130 +#define RPMX_MTI_MAC100X_CL1213_QUANTA_THRESH 0x8138 +#define RPMX_MTI_MAC100X_CL1415_QUANTA_THRESH 0x8140 #define RPM_DEFAULT_PAUSE_TIME 0xFFFF #define RPMX_CMR_RX_OVR_BP 0x4120 #define RPMX_CMR_RX_OVR_BP_EN(x) BIT_ULL((x) + 8) @@ -45,6 +59,18 @@ #define RPM_LMAC_FWI 0xa #define RPM_TX_EN BIT_ULL(0) #define RPM_RX_EN BIT_ULL(1) +#define RPMX_CMRX_PRT_CBFC_CTL 0x5B08 +#define RPMX_CMRX_PRT_CBFC_CTL_LOGL_EN_RX_SHIFT 33 +#define RPMX_CMRX_PRT_CBFC_CTL_PHYS_BP_SHIFT 16 +#define RPMX_CMRX_PRT_CBFC_CTL_LOGL_EN_TX_SHIFT 0 +#define RPM_PFC_CLASS_MASK GENMASK_ULL(48, 33) +#define RPMX_MTI_MAC100X_CL89_QUANTA_THRESH 0x8128 +#define RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_PAD_EN BIT_ULL(11) +#define RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE BIT_ULL(8) +#define RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_FWD BIT_ULL(7) +#define RPMX_MTI_MAC100X_CL01_PAUSE_QUANTA 0x80A8 +#define RPMX_MTI_MAC100X_CL89_PAUSE_QUANTA 0x8108 +#define RPM_DEFAULT_PAUSE_TIME 0xFFFF /* Function Declarations */ int rpm_get_nr_lmacs(void *rpmd); @@ -61,4 +87,8 @@ int rpm_get_rx_stats(void *rpmd, int lmac_id, int idx, u64 *rx_stat); void rpm_lmac_ptp_config(void *rpmd, int lmac_id, bool enable); int rpm_lmac_rx_tx_enable(void *rpmd, int lmac_id, bool enable); int rpm_lmac_tx_enable(void *rpmd, int lmac_id, bool enable); +int rpm_lmac_pfc_config(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause, + u16 pfc_en); +int rpm_lmac_get_pfc_frm_cfg(void *rpmd, int lmac_id, u8 *tx_pause, + u8 *rx_pause); #endif /* RPM_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index 5ed94cfb47d2..513b43ecd5be 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -807,6 +807,9 @@ u32 rvu_cgx_get_fifolen(struct rvu *rvu); void *rvu_first_cgx_pdata(struct rvu *rvu); int cgxlmac_to_pf(struct rvu *rvu, int cgx_id, int lmac_id); int rvu_cgx_config_tx(void *cgxd, int lmac_id, bool enable); +int rvu_cgx_prio_flow_ctrl_cfg(struct rvu *rvu, u16 pcifunc, u8 tx_pause, u8 rx_pause, + u16 pfc_en); +int rvu_cgx_cfg_pause_frm(struct rvu *rvu, u16 pcifunc, u8 tx_pause, u8 rx_pause); int npc_get_nixlf_mcam_index(struct npc_mcam *mcam, u16 pcifunc, int nixlf, int type); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c index 8a7ac5a8b821..9ffe99830e34 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c @@ -863,6 +863,45 @@ int rvu_mbox_handler_cgx_intlbk_disable(struct rvu *rvu, struct msg_req *req, return 0; } +int rvu_cgx_cfg_pause_frm(struct rvu *rvu, u16 pcifunc, u8 tx_pause, u8 rx_pause) +{ + int pf = rvu_get_pf(pcifunc); + u8 rx_pfc = 0, tx_pfc = 0; + struct mac_ops *mac_ops; + u8 cgx_id, lmac_id; + void *cgxd; + + if (!is_mac_feature_supported(rvu, pf, RVU_LMAC_FEAT_FC)) + return 0; + + /* This msg is expected only from PF/VFs that are mapped to CGX LMACs, + * if received from other PF/VF simply ACK, nothing to do. + */ + if (!is_pf_cgxmapped(rvu, pf)) + return LMAC_AF_ERR_PF_NOT_MAPPED; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + cgxd = rvu_cgx_pdata(cgx_id, rvu); + mac_ops = get_mac_ops(cgxd); + + mac_ops->mac_get_pfc_frm_cfg(cgxd, lmac_id, &tx_pfc, &rx_pfc); + if (tx_pfc || rx_pfc) { + dev_warn(rvu->dev, + "Can not configure 802.3X flow control as PFC frames are enabled"); + return LMAC_AF_ERR_8023PAUSE_ENADIS_PERM_DENIED; + } + + mutex_lock(&rvu->rsrc_lock); + if (verify_lmac_fc_cfg(cgxd, lmac_id, tx_pause, rx_pause, + pcifunc & RVU_PFVF_FUNC_MASK)) { + mutex_unlock(&rvu->rsrc_lock); + return LMAC_AF_ERR_PERM_DENIED; + } + mutex_unlock(&rvu->rsrc_lock); + + return mac_ops->mac_enadis_pause_frm(cgxd, lmac_id, tx_pause, rx_pause); +} + int rvu_mbox_handler_cgx_cfg_pause_frm(struct rvu *rvu, struct cgx_pause_frm_cfg *req, struct cgx_pause_frm_cfg *rsp) @@ -870,11 +909,9 @@ int rvu_mbox_handler_cgx_cfg_pause_frm(struct rvu *rvu, int pf = rvu_get_pf(req->hdr.pcifunc); struct mac_ops *mac_ops; u8 cgx_id, lmac_id; + int err = 0; void *cgxd; - if (!is_mac_feature_supported(rvu, pf, RVU_LMAC_FEAT_FC)) - return 0; - /* This msg is expected only from PF/VFs that are mapped to CGX LMACs, * if received from other PF/VF simply ACK, nothing to do. */ @@ -886,13 +923,11 @@ int rvu_mbox_handler_cgx_cfg_pause_frm(struct rvu *rvu, mac_ops = get_mac_ops(cgxd); if (req->set) - mac_ops->mac_enadis_pause_frm(cgxd, lmac_id, - req->tx_pause, req->rx_pause); + err = rvu_cgx_cfg_pause_frm(rvu, req->hdr.pcifunc, req->tx_pause, req->rx_pause); else - mac_ops->mac_get_pause_frm_status(cgxd, lmac_id, - &rsp->tx_pause, - &rsp->rx_pause); - return 0; + mac_ops->mac_get_pause_frm_status(cgxd, lmac_id, &rsp->tx_pause, &rsp->rx_pause); + + return err; } int rvu_mbox_handler_cgx_get_phy_fec_stats(struct rvu *rvu, struct msg_req *req, @@ -1079,3 +1114,67 @@ int rvu_mbox_handler_cgx_mac_addr_update(struct rvu *rvu, rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); return cgx_lmac_addr_update(cgx_id, lmac_id, req->mac_addr, req->index); } + +int rvu_cgx_prio_flow_ctrl_cfg(struct rvu *rvu, u16 pcifunc, u8 tx_pause, + u8 rx_pause, u16 pfc_en) +{ + int pf = rvu_get_pf(pcifunc); + u8 rx_8023 = 0, tx_8023 = 0; + struct mac_ops *mac_ops; + u8 cgx_id, lmac_id; + void *cgxd; + + /* This msg is expected only from PF/VFs that are mapped to CGX LMACs, + * if received from other PF/VF simply ACK, nothing to do. + */ + if (!is_pf_cgxmapped(rvu, pf)) + return -ENODEV; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + cgxd = rvu_cgx_pdata(cgx_id, rvu); + mac_ops = get_mac_ops(cgxd); + + mac_ops->mac_get_pause_frm_status(cgxd, lmac_id, &tx_8023, &rx_8023); + if (tx_8023 || rx_8023) { + dev_warn(rvu->dev, + "Can not configure PFC as 802.3X pause frames are enabled"); + return LMAC_AF_ERR_PFC_ENADIS_PERM_DENIED; + } + + mutex_lock(&rvu->rsrc_lock); + if (verify_lmac_fc_cfg(cgxd, lmac_id, tx_pause, rx_pause, + pcifunc & RVU_PFVF_FUNC_MASK)) { + mutex_unlock(&rvu->rsrc_lock); + return LMAC_AF_ERR_PERM_DENIED; + } + mutex_unlock(&rvu->rsrc_lock); + + return mac_ops->pfc_config(cgxd, lmac_id, tx_pause, rx_pause, pfc_en); +} + +int rvu_mbox_handler_cgx_prio_flow_ctrl_cfg(struct rvu *rvu, + struct cgx_pfc_cfg *req, + struct cgx_pfc_rsp *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + struct mac_ops *mac_ops; + u8 cgx_id, lmac_id; + void *cgxd; + int err; + + /* This msg is expected only from PF/VFs that are mapped to CGX LMACs, + * if received from other PF/VF simply ACK, nothing to do. + */ + if (!is_pf_cgxmapped(rvu, pf)) + return -ENODEV; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + cgxd = rvu_cgx_pdata(cgx_id, rvu); + mac_ops = get_mac_ops(cgxd); + + err = rvu_cgx_prio_flow_ctrl_cfg(rvu, req->hdr.pcifunc, req->tx_pause, + req->rx_pause, req->pfc_en); + + mac_ops->mac_get_pfc_frm_cfg(cgxd, lmac_id, &rsp->tx_pause, &rsp->rx_pause); + return err; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 97fb61915379..0fa625e2528e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -296,7 +296,6 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf, struct rvu_hwinfo *hw = rvu->hw; struct sdp_node_info *sdp_info; int pkind, pf, vf, lbkid, vfid; - struct mac_ops *mac_ops; u8 cgx_id, lmac_id; bool from_vf; int err; @@ -326,13 +325,6 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf, cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, pkind); rvu_npc_set_pkind(rvu, pkind, pfvf); - mac_ops = get_mac_ops(rvu_cgx_pdata(cgx_id, rvu)); - - /* By default we enable pause frames */ - if ((pcifunc & RVU_PFVF_FUNC_MASK) == 0) - mac_ops->mac_enadis_pause_frm(rvu_cgx_pdata(cgx_id, - rvu), - lmac_id, true, true); break; case NIX_INTF_TYPE_LBK: vf = (pcifunc & RVU_PFVF_FUNC_MASK) - 1; @@ -533,7 +525,7 @@ static int rvu_nix_get_bpid(struct rvu *rvu, struct nix_bp_cfg_req *req, */ switch (type) { case NIX_INTF_TYPE_CGX: - if ((req->chan_base + req->chan_cnt) > 15) + if ((req->chan_base + req->chan_cnt) > 16) return -EINVAL; rvu_get_cgx_lmac_id(pfvf->cgx_lmac, &cgx_id, &lmac_id); /* Assign bpid based on cgx, lmac and chan id */ @@ -4578,6 +4570,12 @@ void rvu_nix_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int nixlf) pfvf->hw_rx_tstamp_en = false; } + /* reset priority flow control config */ + rvu_cgx_prio_flow_ctrl_cfg(rvu, pcifunc, 0, 0, 0); + + /* reset 802.3x flow control config */ + rvu_cgx_cfg_pause_frm(rvu, pcifunc, 0, 0); + nix_ctx_free(rvu, pfvf); nix_free_all_bandprof(rvu, pcifunc); @@ -5314,6 +5312,7 @@ int rvu_nix_setup_ratelimit_aggr(struct rvu *rvu, u16 pcifunc, aq_req.ctype = NIX_AQ_CTYPE_BANDPROF; aq_req.op = NIX_AQ_INSTOP_WRITE; memcpy(&aq_req.prof, &aq_rsp.prof, sizeof(struct nix_bandprof_s)); + memset((char *)&aq_req.prof_mask, 0xff, sizeof(struct nix_bandprof_s)); /* Clear higher layer enable bit in the mid profile, just in case */ aq_req.prof.hl_en = 0; aq_req.prof_mask.hl_en = 1; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile index 0048b5946712..d463dc72d80a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile @@ -11,4 +11,7 @@ rvu_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o \ otx2_devlink.o rvu_nicvf-y := otx2_vf.o otx2_devlink.o +rvu_nicpf-$(CONFIG_DCB) += otx2_dcbnl.o +rvu_nicvf-$(CONFIG_DCB) += otx2_dcbnl.o + ccflags-y += -I$(srctree)/drivers/net/ethernet/marvell/octeontx2/af diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 66da31f30d3e..b9d7601138ca 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -222,8 +222,11 @@ EXPORT_SYMBOL(otx2_set_mac_address); int otx2_hw_set_mtu(struct otx2_nic *pfvf, int mtu) { struct nix_frs_cfg *req; + u16 maxlen; int err; + maxlen = otx2_get_max_mtu(pfvf) + OTX2_ETH_HLEN + OTX2_HW_TIMESTAMP_LEN; + mutex_lock(&pfvf->mbox.lock); req = otx2_mbox_alloc_msg_nix_set_hw_frs(&pfvf->mbox); if (!req) { @@ -233,6 +236,10 @@ int otx2_hw_set_mtu(struct otx2_nic *pfvf, int mtu) req->maxlen = pfvf->netdev->mtu + OTX2_ETH_HLEN + OTX2_HW_TIMESTAMP_LEN; + /* Use max receive length supported by hardware for loopback devices */ + if (is_otx2_lbkvf(pfvf->pdev)) + req->maxlen = maxlen; + err = otx2_sync_mbox_msg(&pfvf->mbox); mutex_unlock(&pfvf->mbox.lock); return err; @@ -262,6 +269,7 @@ int otx2_config_pause_frm(struct otx2_nic *pfvf) mutex_unlock(&pfvf->mbox.lock); return err; } +EXPORT_SYMBOL(otx2_config_pause_frm); int otx2_set_flowkey_cfg(struct otx2_nic *pfvf) { @@ -931,7 +939,11 @@ static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) if (!is_otx2_lbkvf(pfvf->pdev)) { /* Enable receive CQ backpressure */ aq->cq.bp_ena = 1; +#ifdef CONFIG_DCB + aq->cq.bpid = pfvf->bpid[pfvf->queue_to_pfc_map[qidx]]; +#else aq->cq.bpid = pfvf->bpid[0]; +#endif /* Set backpressure level is same as cq pass level */ aq->cq.bp = RQ_PASS_LVL_CQ(pfvf->hw.rq_skid, qset->rqe_cnt); @@ -1036,7 +1048,7 @@ int otx2_config_nix(struct otx2_nic *pfvf) struct nix_lf_alloc_rsp *rsp; int err; - pfvf->qset.xqe_size = NIX_XQESZ_W16 ? 128 : 512; + pfvf->qset.xqe_size = pfvf->hw.xqe_size; /* Get memory to put this msg */ nixlf = otx2_mbox_alloc_msg_nix_lf_alloc(&pfvf->mbox); @@ -1049,7 +1061,7 @@ int otx2_config_nix(struct otx2_nic *pfvf) nixlf->cq_cnt = pfvf->qset.cq_cnt; nixlf->rss_sz = MAX_RSS_INDIR_TBL_SIZE; nixlf->rss_grps = MAX_RSS_GROUPS; - nixlf->xqe_sz = NIX_XQESZ_W16; + nixlf->xqe_sz = pfvf->hw.xqe_size == 128 ? NIX_XQESZ_W16 : NIX_XQESZ_W64; /* We don't know absolute NPA LF idx attached. * AF will replace 'RVU_DEFAULT_PF_FUNC' with * NPA LF attached to this RVU PF/VF. @@ -1211,7 +1223,11 @@ static int otx2_aura_init(struct otx2_nic *pfvf, int aura_id, */ if (pfvf->nix_blkaddr == BLKADDR_NIX1) aq->aura.bp_ena = 1; +#ifdef CONFIG_DCB + aq->aura.nix0_bpid = pfvf->bpid[pfvf->queue_to_pfc_map[aura_id]]; +#else aq->aura.nix0_bpid = pfvf->bpid[0]; +#endif /* Set backpressure level for RQ's Aura */ aq->aura.bp = RQ_BP_LVL_AURA; @@ -1538,11 +1554,18 @@ int otx2_nix_config_bp(struct otx2_nic *pfvf, bool enable) return -ENOMEM; req->chan_base = 0; - req->chan_cnt = 1; +#ifdef CONFIG_DCB + req->chan_cnt = pfvf->pfc_en ? IEEE_8021QAZ_MAX_TCS : 1; + req->bpid_per_chan = pfvf->pfc_en ? 1 : 0; +#else + req->chan_cnt = 1; req->bpid_per_chan = 0; +#endif + return otx2_sync_mbox_msg(&pfvf->mbox); } +EXPORT_SYMBOL(otx2_nix_config_bp); /* Mbox message handlers */ void mbox_handler_cgx_stats(struct otx2_nic *pfvf, @@ -1704,6 +1727,56 @@ u16 otx2_get_max_mtu(struct otx2_nic *pfvf) } EXPORT_SYMBOL(otx2_get_max_mtu); +int otx2_handle_ntuple_tc_features(struct net_device *netdev, netdev_features_t features) +{ + netdev_features_t changed = features ^ netdev->features; + struct otx2_nic *pfvf = netdev_priv(netdev); + bool ntuple = !!(features & NETIF_F_NTUPLE); + bool tc = !!(features & NETIF_F_HW_TC); + + if ((changed & NETIF_F_NTUPLE) && !ntuple) + otx2_destroy_ntuple_flows(pfvf); + + if ((changed & NETIF_F_NTUPLE) && ntuple) { + if (!pfvf->flow_cfg->max_flows) { + netdev_err(netdev, + "Can't enable NTUPLE, MCAM entries not allocated\n"); + return -EINVAL; + } + } + + if ((changed & NETIF_F_HW_TC) && tc) { + if (!pfvf->flow_cfg->max_flows) { + netdev_err(netdev, + "Can't enable TC, MCAM entries not allocated\n"); + return -EINVAL; + } + } + + if ((changed & NETIF_F_HW_TC) && !tc && + pfvf->flow_cfg && pfvf->flow_cfg->nr_flows) { + netdev_err(netdev, "Can't disable TC hardware offload while flows are active\n"); + return -EBUSY; + } + + if ((changed & NETIF_F_NTUPLE) && ntuple && + (netdev->features & NETIF_F_HW_TC) && !(changed & NETIF_F_HW_TC)) { + netdev_err(netdev, + "Can't enable NTUPLE when TC is active, disable TC and retry\n"); + return -EINVAL; + } + + if ((changed & NETIF_F_HW_TC) && tc && + (netdev->features & NETIF_F_NTUPLE) && !(changed & NETIF_F_NTUPLE)) { + netdev_err(netdev, + "Can't enable TC when NTUPLE is active, disable NTUPLE and retry\n"); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(otx2_handle_ntuple_tc_features); + #define M(_name, _id, _fn_name, _req_type, _rsp_type) \ int __weak \ otx2_mbox_up_handler_ ## _fn_name(struct otx2_nic *pfvf, \ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 14509fc64cce..c587c14ac2a3 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -178,6 +179,10 @@ struct otx2_hw { u16 rqpool_cnt; u16 sqpool_cnt; +#define OTX2_DEFAULT_RBUF_LEN 2048 + u16 rbuf_len; + u32 xqe_size; + /* NPA */ u32 stack_pg_ptrs; /* No of ptrs per stack page */ u32 stack_pg_bytes; /* Size of stack page */ @@ -272,6 +277,8 @@ struct otx2_ptp { u64 thresh; struct ptp_pin_desc extts_config; + u64 (*convert_rx_ptp_tstmp)(u64 timestamp); + u64 (*convert_tx_ptp_tstmp)(u64 timestamp); }; #define OTX2_HW_TIMESTAMP_LEN 8 @@ -396,6 +403,11 @@ struct otx2_nic { /* Devlink */ struct otx2_devlink *dl; +#ifdef CONFIG_DCB + /* PFC */ + u8 pfc_en; + u8 *queue_to_pfc_map; +#endif }; static inline bool is_otx2_lbkvf(struct pci_dev *pdev) @@ -863,6 +875,8 @@ int otx2_enable_rxvlan(struct otx2_nic *pf, bool enable); int otx2_install_rxvlan_offload_flow(struct otx2_nic *pfvf); bool otx2_xdp_sq_append_pkt(struct otx2_nic *pfvf, u64 iova, int len, u16 qidx); u16 otx2_get_max_mtu(struct otx2_nic *pfvf); +int otx2_handle_ntuple_tc_features(struct net_device *netdev, + netdev_features_t features); /* tc support */ int otx2_init_tc(struct otx2_nic *nic); void otx2_shutdown_tc(struct otx2_nic *nic); @@ -876,4 +890,11 @@ int otx2_dmacflt_remove(struct otx2_nic *pf, const u8 *mac, u8 bit_pos); int otx2_dmacflt_update(struct otx2_nic *pf, u8 *mac, u8 bit_pos); void otx2_dmacflt_reinstall_flows(struct otx2_nic *pf); void otx2_dmacflt_update_pfmac_flow(struct otx2_nic *pfvf); + +#ifdef CONFIG_DCB +/* DCB support*/ +void otx2_update_bpid_in_rqctx(struct otx2_nic *pfvf, int vlan_prio, int qidx, bool pfc_enable); +int otx2_config_priority_flow_ctrl(struct otx2_nic *pfvf); +int otx2_dcbnl_set_ops(struct net_device *dev); +#endif #endif /* OTX2_COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c new file mode 100644 index 000000000000..723d2506d309 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU Ethernet driver + * + * Copyright (C) 2021 Marvell. + * + */ + +#include "otx2_common.h" + +int otx2_config_priority_flow_ctrl(struct otx2_nic *pfvf) +{ + struct cgx_pfc_cfg *req; + struct cgx_pfc_rsp *rsp; + int err = 0; + + if (is_otx2_lbkvf(pfvf->pdev)) + return 0; + + mutex_lock(&pfvf->mbox.lock); + req = otx2_mbox_alloc_msg_cgx_prio_flow_ctrl_cfg(&pfvf->mbox); + if (!req) { + err = -ENOMEM; + goto unlock; + } + + if (pfvf->pfc_en) { + req->rx_pause = true; + req->tx_pause = true; + } else { + req->rx_pause = false; + req->tx_pause = false; + } + req->pfc_en = pfvf->pfc_en; + + if (!otx2_sync_mbox_msg(&pfvf->mbox)) { + rsp = (struct cgx_pfc_rsp *) + otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr); + if (req->rx_pause != rsp->rx_pause || req->tx_pause != rsp->tx_pause) { + dev_warn(pfvf->dev, + "Failed to config PFC\n"); + err = -EPERM; + } + } +unlock: + mutex_unlock(&pfvf->mbox.lock); + return err; +} + +void otx2_update_bpid_in_rqctx(struct otx2_nic *pfvf, int vlan_prio, int qidx, + bool pfc_enable) +{ + bool if_up = netif_running(pfvf->netdev); + struct npa_aq_enq_req *npa_aq; + struct nix_aq_enq_req *aq; + int err = 0; + + if (pfvf->queue_to_pfc_map[qidx] && pfc_enable) { + dev_warn(pfvf->dev, + "PFC enable not permitted as Priority %d already mapped to Queue %d\n", + pfvf->queue_to_pfc_map[qidx], qidx); + return; + } + + if (if_up) { + netif_tx_stop_all_queues(pfvf->netdev); + netif_carrier_off(pfvf->netdev); + } + + pfvf->queue_to_pfc_map[qidx] = vlan_prio; + + aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox); + if (!aq) { + err = -ENOMEM; + goto out; + } + + aq->cq.bpid = pfvf->bpid[vlan_prio]; + aq->cq_mask.bpid = GENMASK(8, 0); + + /* Fill AQ info */ + aq->qidx = qidx; + aq->ctype = NIX_AQ_CTYPE_CQ; + aq->op = NIX_AQ_INSTOP_WRITE; + + otx2_sync_mbox_msg(&pfvf->mbox); + + npa_aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); + if (!npa_aq) { + err = -ENOMEM; + goto out; + } + npa_aq->aura.nix0_bpid = pfvf->bpid[vlan_prio]; + npa_aq->aura_mask.nix0_bpid = GENMASK(8, 0); + + /* Fill NPA AQ info */ + npa_aq->aura_id = qidx; + npa_aq->ctype = NPA_AQ_CTYPE_AURA; + npa_aq->op = NPA_AQ_INSTOP_WRITE; + otx2_sync_mbox_msg(&pfvf->mbox); + +out: + if (if_up) { + netif_carrier_on(pfvf->netdev); + netif_tx_start_all_queues(pfvf->netdev); + } + + if (err) + dev_warn(pfvf->dev, + "Updating BPIDs in CQ and Aura contexts of RQ%d failed with err %d\n", + qidx, err); +} + +static int otx2_dcbnl_ieee_getpfc(struct net_device *dev, struct ieee_pfc *pfc) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + + pfc->pfc_cap = IEEE_8021QAZ_MAX_TCS; + pfc->pfc_en = pfvf->pfc_en; + + return 0; +} + +static int otx2_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + int err; + + /* Save PFC configuration to interface */ + pfvf->pfc_en = pfc->pfc_en; + + err = otx2_config_priority_flow_ctrl(pfvf); + if (err) + return err; + + /* Request Per channel Bpids */ + if (pfc->pfc_en) + otx2_nix_config_bp(pfvf, true); + + return 0; +} + +static u8 otx2_dcbnl_getdcbx(struct net_device __always_unused *dev) +{ + return DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE; +} + +static u8 otx2_dcbnl_setdcbx(struct net_device __always_unused *dev, u8 mode) +{ + return (mode != (DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE)) ? 1 : 0; +} + +static const struct dcbnl_rtnl_ops otx2_dcbnl_ops = { + .ieee_getpfc = otx2_dcbnl_ieee_getpfc, + .ieee_setpfc = otx2_dcbnl_ieee_setpfc, + .getdcbx = otx2_dcbnl_getdcbx, + .setdcbx = otx2_dcbnl_setdcbx, +}; + +int otx2_dcbnl_set_ops(struct net_device *dev) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + + pfvf->queue_to_pfc_map = devm_kzalloc(pfvf->dev, pfvf->hw.rx_queues, + GFP_KERNEL); + if (!pfvf->queue_to_pfc_map) + return -ENOMEM; + dev->dcbnl_ops = &otx2_dcbnl_ops; + + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c index d85db90632d6..fc328de5345e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c @@ -371,6 +371,8 @@ static void otx2_get_ringparam(struct net_device *netdev, ring->rx_pending = qs->rqe_cnt ? qs->rqe_cnt : Q_COUNT(Q_SIZE_256); ring->tx_max_pending = Q_COUNT(Q_SIZE_MAX); ring->tx_pending = qs->sqe_cnt ? qs->sqe_cnt : Q_COUNT(Q_SIZE_4K); + kernel_ring->rx_buf_len = pfvf->hw.rbuf_len; + kernel_ring->cqe_size = pfvf->hw.xqe_size; } static int otx2_set_ringparam(struct net_device *netdev, @@ -379,6 +381,9 @@ static int otx2_set_ringparam(struct net_device *netdev, struct netlink_ext_ack *extack) { struct otx2_nic *pfvf = netdev_priv(netdev); + u32 rx_buf_len = kernel_ring->rx_buf_len; + u32 old_rx_buf_len = pfvf->hw.rbuf_len; + u32 xqe_size = kernel_ring->cqe_size; bool if_up = netif_running(netdev); struct otx2_qset *qs = &pfvf->qset; u32 rx_count, tx_count; @@ -386,6 +391,21 @@ static int otx2_set_ringparam(struct net_device *netdev, if (ring->rx_mini_pending || ring->rx_jumbo_pending) return -EINVAL; + /* Hardware supports max size of 32k for a receive buffer + * and 1536 is typical ethernet frame size. + */ + if (rx_buf_len && (rx_buf_len < 1536 || rx_buf_len > 32768)) { + netdev_err(netdev, + "Receive buffer range is 1536 - 32768"); + return -EINVAL; + } + + if (xqe_size != 128 && xqe_size != 512) { + netdev_err(netdev, + "Completion event size must be 128 or 512"); + return -EINVAL; + } + /* Permitted lengths are 16 64 256 1K 4K 16K 64K 256K 1M */ rx_count = ring->rx_pending; /* On some silicon variants a skid or reserved CQEs are @@ -403,7 +423,8 @@ static int otx2_set_ringparam(struct net_device *netdev, Q_COUNT(Q_SIZE_4K), Q_COUNT(Q_SIZE_MAX)); tx_count = Q_COUNT(Q_SIZE(tx_count, 3)); - if (tx_count == qs->sqe_cnt && rx_count == qs->rqe_cnt) + if (tx_count == qs->sqe_cnt && rx_count == qs->rqe_cnt && + rx_buf_len == old_rx_buf_len && xqe_size == pfvf->hw.xqe_size) return 0; if (if_up) @@ -413,6 +434,9 @@ static int otx2_set_ringparam(struct net_device *netdev, qs->sqe_cnt = tx_count; qs->rqe_cnt = rx_count; + pfvf->hw.rbuf_len = rx_buf_len; + pfvf->hw.xqe_size = xqe_size; + if (if_up) return netdev->netdev_ops->ndo_open(netdev); @@ -1207,6 +1231,8 @@ static int otx2_set_link_ksettings(struct net_device *netdev, static const struct ethtool_ops otx2_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES, + .supported_ring_params = ETHTOOL_RING_USE_RX_BUF_LEN | + ETHTOOL_RING_USE_CQE_SIZE, .get_link = otx2_get_link, .get_drvinfo = otx2_get_drvinfo, .get_strings = otx2_get_strings, @@ -1326,6 +1352,8 @@ static int otx2vf_get_link_ksettings(struct net_device *netdev, static const struct ethtool_ops otx2vf_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES, + .supported_ring_params = ETHTOOL_RING_USE_RX_BUF_LEN | + ETHTOOL_RING_USE_CQE_SIZE, .get_link = otx2_get_link, .get_drvinfo = otx2vf_get_drvinfo, .get_strings = otx2vf_get_strings, diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c index 77a13fb555fb..54f235c216a9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c @@ -21,8 +21,10 @@ struct otx2_flow { u16 entry; bool is_vf; u8 rss_ctx_id; +#define DMAC_FILTER_RULE BIT(0) +#define PFC_FLOWCTRL_RULE BIT(1) + u16 rule_type; int vf; - bool dmac_filter; }; enum dmac_req { @@ -899,6 +901,9 @@ static int otx2_is_flow_rule_dmacfilter(struct otx2_nic *pfvf, static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow) { u64 ring_cookie = flow->flow_spec.ring_cookie; +#ifdef CONFIG_DCB + int vlan_prio, qidx, pfc_rule = 0; +#endif struct npc_install_flow_req *req; int err, vf = 0; @@ -940,6 +945,24 @@ static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow) mutex_unlock(&pfvf->mbox.lock); return -EINVAL; } + +#ifdef CONFIG_DCB + /* Identify PFC rule if PFC enabled and ntuple rule is vlan */ + if (!vf && (req->features & BIT_ULL(NPC_OUTER_VID)) && + pfvf->pfc_en && req->op != NIX_RX_ACTIONOP_RSS) { + vlan_prio = ntohs(req->packet.vlan_tci) & + ntohs(req->mask.vlan_tci); + + /* Get the priority */ + vlan_prio >>= 13; + flow->rule_type |= PFC_FLOWCTRL_RULE; + /* Check if PFC enabled for this priority */ + if (pfvf->pfc_en & BIT(vlan_prio)) { + pfc_rule = true; + qidx = req->index; + } + } +#endif } /* ethtool ring_cookie has (VF + 1) for VF */ @@ -951,6 +974,12 @@ static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow) /* Send message to AF */ err = otx2_sync_mbox_msg(&pfvf->mbox); + +#ifdef CONFIG_DCB + if (!err && pfc_rule) + otx2_update_bpid_in_rqctx(pfvf, vlan_prio, qidx, true); +#endif + mutex_unlock(&pfvf->mbox.lock); return err; } @@ -966,7 +995,7 @@ static int otx2_add_flow_with_pfmac(struct otx2_nic *pfvf, return -ENOMEM; pf_mac->entry = 0; - pf_mac->dmac_filter = true; + pf_mac->rule_type |= DMAC_FILTER_RULE; pf_mac->location = pfvf->flow_cfg->max_flows; memcpy(&pf_mac->flow_spec, &flow->flow_spec, sizeof(struct ethtool_rx_flow_spec)); @@ -1031,7 +1060,7 @@ int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc) eth_hdr = &flow->flow_spec.h_u.ether_spec; /* Sync dmac filter table with updated fields */ - if (flow->dmac_filter) + if (flow->rule_type & DMAC_FILTER_RULE) return otx2_dmacflt_update(pfvf, eth_hdr->h_dest, flow->entry); @@ -1052,7 +1081,7 @@ int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc) if (!test_bit(0, &flow_cfg->dmacflt_bmap)) otx2_add_flow_with_pfmac(pfvf, flow); - flow->dmac_filter = true; + flow->rule_type |= DMAC_FILTER_RULE; flow->entry = find_first_zero_bit(&flow_cfg->dmacflt_bmap, flow_cfg->dmacflt_max_flows); fsp->location = flow_cfg->max_flows + flow->entry; @@ -1120,7 +1149,7 @@ static void otx2_update_rem_pfmac(struct otx2_nic *pfvf, int req) bool found = false; list_for_each_entry(iter, &pfvf->flow_cfg->flow_list, list) { - if (iter->dmac_filter && iter->entry == 0) { + if ((iter->rule_type & DMAC_FILTER_RULE) && iter->entry == 0) { eth_hdr = &iter->flow_spec.h_u.ether_spec; if (req == DMAC_ADDR_DEL) { otx2_dmacflt_remove(pfvf, eth_hdr->h_dest, @@ -1156,7 +1185,7 @@ int otx2_remove_flow(struct otx2_nic *pfvf, u32 location) if (!flow) return -ENOENT; - if (flow->dmac_filter) { + if (flow->rule_type & DMAC_FILTER_RULE) { struct ethhdr *eth_hdr = &flow->flow_spec.h_u.ether_spec; /* user not allowed to remove dmac filter with interface mac */ @@ -1174,6 +1203,13 @@ int otx2_remove_flow(struct otx2_nic *pfvf, u32 location) flow_cfg->dmacflt_max_flows) == 1) otx2_update_rem_pfmac(pfvf, DMAC_ADDR_DEL); } else { +#ifdef CONFIG_DCB + if (flow->rule_type & PFC_FLOWCTRL_RULE) + otx2_update_bpid_in_rqctx(pfvf, 0, + flow->flow_spec.ring_cookie, + false); +#endif + err = otx2_remove_flow_msg(pfvf, flow->entry, false); } @@ -1383,7 +1419,7 @@ void otx2_dmacflt_reinstall_flows(struct otx2_nic *pf) struct ethhdr *eth_hdr; list_for_each_entry(iter, &pf->flow_cfg->flow_list, list) { - if (iter->dmac_filter) { + if (iter->rule_type & DMAC_FILTER_RULE) { eth_hdr = &iter->flow_spec.h_u.ether_spec; otx2_dmacflt_add(pf, eth_hdr->h_dest, iter->entry); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index d39341e4ab37..441aafc26a08 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1311,6 +1311,9 @@ static int otx2_get_rbuf_size(struct otx2_nic *pf, int mtu) int total_size; int rbuf_size; + if (pf->hw.rbuf_len) + return ALIGN(pf->hw.rbuf_len, OTX2_ALIGN) + OTX2_HEAD_ROOM; + /* The data transferred by NIX to memory consists of actual packet * plus additional data which has timestamp and/or EDSA/HIGIG2 * headers if interface is configured in corresponding modes. @@ -1694,9 +1697,6 @@ int otx2_open(struct net_device *netdev) if (pf->linfo.link_up && !(pf->pcifunc & RVU_PFVF_FUNC_MASK)) otx2_handle_link_event(pf); - /* Restore pause frame settings */ - otx2_config_pause_frm(pf); - /* Install DMAC Filters */ if (pf->flags & OTX2_FLAG_DMACFLTR_SUPPORT) otx2_dmacflt_reinstall_flows(pf); @@ -1863,9 +1863,7 @@ static int otx2_set_features(struct net_device *netdev, netdev_features_t features) { netdev_features_t changed = features ^ netdev->features; - bool ntuple = !!(features & NETIF_F_NTUPLE); struct otx2_nic *pf = netdev_priv(netdev); - bool tc = !!(features & NETIF_F_HW_TC); if ((changed & NETIF_F_LOOPBACK) && netif_running(netdev)) return otx2_cgx_config_loopback(pf, @@ -1875,46 +1873,7 @@ static int otx2_set_features(struct net_device *netdev, return otx2_enable_rxvlan(pf, features & NETIF_F_HW_VLAN_CTAG_RX); - if ((changed & NETIF_F_NTUPLE) && !ntuple) - otx2_destroy_ntuple_flows(pf); - - if ((changed & NETIF_F_NTUPLE) && ntuple) { - if (!pf->flow_cfg->max_flows) { - netdev_err(netdev, - "Can't enable NTUPLE, MCAM entries not allocated\n"); - return -EINVAL; - } - } - - if ((changed & NETIF_F_HW_TC) && tc) { - if (!pf->flow_cfg->max_flows) { - netdev_err(netdev, - "Can't enable TC, MCAM entries not allocated\n"); - return -EINVAL; - } - } - - if ((changed & NETIF_F_HW_TC) && !tc && - pf->flow_cfg && pf->flow_cfg->nr_flows) { - netdev_err(netdev, "Can't disable TC hardware offload while flows are active\n"); - return -EBUSY; - } - - if ((changed & NETIF_F_NTUPLE) && ntuple && - (netdev->features & NETIF_F_HW_TC) && !(changed & NETIF_F_HW_TC)) { - netdev_err(netdev, - "Can't enable NTUPLE when TC is active, disable TC and retry\n"); - return -EINVAL; - } - - if ((changed & NETIF_F_HW_TC) && tc && - (netdev->features & NETIF_F_NTUPLE) && !(changed & NETIF_F_NTUPLE)) { - netdev_err(netdev, - "Can't enable TC when NTUPLE is active, disable NTUPLE and retry\n"); - return -EINVAL; - } - - return 0; + return otx2_handle_ntuple_tc_features(netdev, features); } static void otx2_reset_task(struct work_struct *work) @@ -2625,6 +2584,9 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) hw->tx_queues = qcount; hw->tot_tx_queues = qcount; hw->max_queues = qcount; + hw->rbuf_len = OTX2_DEFAULT_RBUF_LEN; + /* Use CQE of 128 byte descriptor size by default */ + hw->xqe_size = 128; num_vec = pci_msix_vec_count(pdev); hw->irq_name = devm_kmalloc_array(&hw->pdev->dev, num_vec, NAME_SIZE, @@ -2778,9 +2740,11 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* Enable link notifications */ otx2_cgx_config_linkevents(pf, true); - /* Enable pause frames by default */ - pf->flags |= OTX2_FLAG_RX_PAUSE_ENABLED; - pf->flags |= OTX2_FLAG_TX_PAUSE_ENABLED; +#ifdef CONFIG_DCB + err = otx2_dcbnl_set_ops(netdev); + if (err) + goto err_pf_sriov_init; +#endif return 0; @@ -2925,6 +2889,21 @@ static void otx2_remove(struct pci_dev *pdev) if (pf->flags & OTX2_FLAG_RX_TSTAMP_ENABLED) otx2_config_hw_rx_tstamp(pf, false); + /* Disable 802.3x pause frames */ + if (pf->flags & OTX2_FLAG_RX_PAUSE_ENABLED || + (pf->flags & OTX2_FLAG_TX_PAUSE_ENABLED)) { + pf->flags &= ~OTX2_FLAG_RX_PAUSE_ENABLED; + pf->flags &= ~OTX2_FLAG_TX_PAUSE_ENABLED; + otx2_config_pause_frm(pf); + } + +#ifdef CONFIG_DCB + /* Disable PFC config */ + if (pf->pfc_en) { + pf->pfc_en = 0; + otx2_config_priority_flow_ctrl(pf); + } +#endif cancel_work_sync(&pf->reset_task); /* Disable link notifications */ otx2_cgx_config_linkevents(pf, false); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c index 61c20907315f..fdc2c9315b91 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c @@ -294,6 +294,14 @@ int otx2_ptp_init(struct otx2_nic *pfvf) goto error; } + if (is_dev_otx2(pfvf->pdev)) { + ptp_ptr->convert_rx_ptp_tstmp = &otx2_ptp_convert_rx_timestamp; + ptp_ptr->convert_tx_ptp_tstmp = &otx2_ptp_convert_tx_timestamp; + } else { + ptp_ptr->convert_rx_ptp_tstmp = &cn10k_ptp_convert_timestamp; + ptp_ptr->convert_tx_ptp_tstmp = &cn10k_ptp_convert_timestamp; + } + pfvf->ptp = ptp_ptr; error: diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.h index 6ff284211d7b..7ff41927ceaf 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.h @@ -8,6 +8,21 @@ #ifndef OTX2_PTP_H #define OTX2_PTP_H +static inline u64 otx2_ptp_convert_rx_timestamp(u64 timestamp) +{ + return be64_to_cpu(*(__be64 *)×tamp); +} + +static inline u64 otx2_ptp_convert_tx_timestamp(u64 timestamp) +{ + return timestamp; +} + +static inline u64 cn10k_ptp_convert_timestamp(u64 timestamp) +{ + return ((timestamp >> 32) * NSEC_PER_SEC) + (timestamp & 0xFFFFFFFFUL); +} + int otx2_ptp_init(struct otx2_nic *pfvf); void otx2_ptp_destroy(struct otx2_nic *pfvf); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c index 626961a41089..28b19945d716 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c @@ -58,7 +58,7 @@ int otx2_tc_alloc_ent_bitmap(struct otx2_nic *nic) { struct otx2_tc_info *tc = &nic->tc_info; - if (!nic->flow_cfg->max_flows || is_otx2_vf(nic->pcifunc)) + if (!nic->flow_cfg->max_flows) return 0; /* Max flows changed, free the existing bitmap */ @@ -190,6 +190,40 @@ static int otx2_tc_validate_flow(struct otx2_nic *nic, return 0; } +static int otx2_policer_validate(const struct flow_action *action, + const struct flow_action_entry *act, + struct netlink_ext_ack *extack) +{ + if (act->police.exceed.act_id != FLOW_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when exceed action is not drop"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && + act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not pipe or ok"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && + !flow_action_is_last_entry(action, act)) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is ok, but action is not last"); + return -EOPNOTSUPP; + } + + if (act->police.peakrate_bytes_ps || + act->police.avrate || act->police.overhead) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when peakrate/avrate/overhead is configured"); + return -EOPNOTSUPP; + } + + return 0; +} + static int otx2_tc_egress_matchall_install(struct otx2_nic *nic, struct tc_cls_matchall_offload *cls) { @@ -212,6 +246,10 @@ static int otx2_tc_egress_matchall_install(struct otx2_nic *nic, entry = &cls->rule->action.entries[0]; switch (entry->id) { case FLOW_ACTION_POLICE: + err = otx2_policer_validate(&cls->rule->action, entry, extack); + if (err) + return err; + if (entry->police.rate_pkt_ps) { NL_SET_ERR_MSG_MOD(extack, "QoS offload not support packets per second"); return -EOPNOTSUPP; @@ -315,6 +353,7 @@ static int otx2_tc_parse_actions(struct otx2_nic *nic, u8 nr_police = 0; bool pps = false; u64 rate; + int err; int i; if (!flow_action_has_entries(flow_action)) { @@ -355,6 +394,10 @@ static int otx2_tc_parse_actions(struct otx2_nic *nic, return -EOPNOTSUPP; } + err = otx2_policer_validate(flow_action, act, extack); + if (err) + return err; + if (act->police.rate_bytes_ps > 0) { rate = act->police.rate_bytes_ps * 8; burst = act->police.burst; @@ -1023,6 +1066,7 @@ int otx2_setup_tc(struct net_device *netdev, enum tc_setup_type type, return -EOPNOTSUPP; } } +EXPORT_SYMBOL(otx2_setup_tc); static const struct rhashtable_params tc_flow_ht_params = { .head_offset = offsetof(struct otx2_tc_flow, node), @@ -1052,6 +1096,7 @@ int otx2_init_tc(struct otx2_nic *nic) tc->flow_ht_params = tc_flow_ht_params; return rhashtable_init(&tc->flow_table, &tc->flow_ht_params); } +EXPORT_SYMBOL(otx2_init_tc); void otx2_shutdown_tc(struct otx2_nic *nic) { @@ -1060,3 +1105,4 @@ void otx2_shutdown_tc(struct otx2_nic *nic) kfree(tc->tc_entries_bitmap); rhashtable_destroy(&tc->flow_table); } +EXPORT_SYMBOL(otx2_shutdown_tc); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index 7c4068c5d1ac..c26de15b2ac3 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -148,6 +148,7 @@ static void otx2_snd_pkt_handler(struct otx2_nic *pfvf, if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) { timestamp = ((u64 *)sq->timestamps->base)[snd_comp->sqe_id]; if (timestamp != 1) { + timestamp = pfvf->ptp->convert_tx_ptp_tstmp(timestamp); err = otx2_ptp_tstamp2time(pfvf, timestamp, &tsns); if (!err) { memset(&ts, 0, sizeof(ts)); @@ -167,14 +168,15 @@ static void otx2_snd_pkt_handler(struct otx2_nic *pfvf, static void otx2_set_rxtstamp(struct otx2_nic *pfvf, struct sk_buff *skb, void *data) { - u64 tsns; + u64 timestamp, tsns; int err; if (!(pfvf->flags & OTX2_FLAG_RX_TSTAMP_ENABLED)) return; + timestamp = pfvf->ptp->convert_rx_ptp_tstmp(*(u64 *)data); /* The first 8 bytes is the timestamp */ - err = otx2_ptp_tstamp2time(pfvf, be64_to_cpu(*(__be64 *)data), &tsns); + err = otx2_ptp_tstamp2time(pfvf, timestamp, &tsns); if (err) return; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c index 925b74ebb8b0..9e87836ed8bf 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c @@ -472,23 +472,7 @@ static void otx2vf_reset_task(struct work_struct *work) static int otx2vf_set_features(struct net_device *netdev, netdev_features_t features) { - netdev_features_t changed = features ^ netdev->features; - bool ntuple_enabled = !!(features & NETIF_F_NTUPLE); - struct otx2_nic *vf = netdev_priv(netdev); - - if (changed & NETIF_F_NTUPLE) { - if (!ntuple_enabled) { - otx2_mcam_flow_del(vf); - return 0; - } - - if (!otx2_get_maxflows(vf->flow_cfg)) { - netdev_err(netdev, - "Can't enable NTUPLE, MCAM entries not allocated\n"); - return -EINVAL; - } - } - return 0; + return otx2_handle_ntuple_tc_features(netdev, features); } static const struct net_device_ops otx2vf_netdev_ops = { @@ -502,6 +486,7 @@ static const struct net_device_ops otx2vf_netdev_ops = { .ndo_get_stats64 = otx2_get_stats64, .ndo_tx_timeout = otx2_tx_timeout, .ndo_eth_ioctl = otx2_ioctl, + .ndo_setup_tc = otx2_setup_tc, }; static int otx2_wq_init(struct otx2_nic *vf) @@ -586,6 +571,9 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id) hw->tx_queues = qcount; hw->max_queues = qcount; hw->tot_tx_queues = qcount; + hw->rbuf_len = OTX2_DEFAULT_RBUF_LEN; + /* Use CQE of 128 byte descriptor size by default */ + hw->xqe_size = 128; hw->irq_name = devm_kmalloc_array(&hw->pdev->dev, num_vec, NAME_SIZE, GFP_KERNEL); @@ -662,6 +650,7 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id) netdev->hw_features |= NETIF_F_NTUPLE; netdev->hw_features |= NETIF_F_RXALL; + netdev->hw_features |= NETIF_F_HW_TC; netif_set_gso_max_segs(netdev, OTX2_MAX_GSO_SEGS); netdev->watchdog_timeo = OTX2_TX_TIMEOUT; @@ -697,16 +686,24 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto err_unreg_netdev; - err = otx2_register_dl(vf); + err = otx2_init_tc(vf); if (err) goto err_unreg_netdev; - /* Enable pause frames by default */ - vf->flags |= OTX2_FLAG_RX_PAUSE_ENABLED; - vf->flags |= OTX2_FLAG_TX_PAUSE_ENABLED; + err = otx2_register_dl(vf); + if (err) + goto err_shutdown_tc; + +#ifdef CONFIG_DCB + err = otx2_dcbnl_set_ops(netdev); + if (err) + goto err_shutdown_tc; +#endif return 0; +err_shutdown_tc: + otx2_shutdown_tc(vf); err_unreg_netdev: unregister_netdev(netdev); err_ptp_destroy: @@ -739,6 +736,22 @@ static void otx2vf_remove(struct pci_dev *pdev) vf = netdev_priv(netdev); + /* Disable 802.3x pause frames */ + if (vf->flags & OTX2_FLAG_RX_PAUSE_ENABLED || + (vf->flags & OTX2_FLAG_TX_PAUSE_ENABLED)) { + vf->flags &= ~OTX2_FLAG_RX_PAUSE_ENABLED; + vf->flags &= ~OTX2_FLAG_TX_PAUSE_ENABLED; + otx2_config_pause_frm(vf); + } + +#ifdef CONFIG_DCB + /* Disable PFC config */ + if (vf->pfc_en) { + vf->pfc_en = 0; + otx2_config_priority_flow_ctrl(vf); + } +#endif + cancel_work_sync(&vf->reset_task); otx2_unregister_dl(vf); unregister_netdev(netdev); diff --git a/drivers/net/ethernet/marvell/prestera/prestera.h b/drivers/net/ethernet/marvell/prestera/prestera.h index 2fd9ef2fe5d6..6f754ae2a584 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera.h +++ b/drivers/net/ethernet/marvell/prestera/prestera.h @@ -281,8 +281,11 @@ struct prestera_router { struct prestera_switch *sw; struct list_head vr_list; struct list_head rif_entry_list; + struct rhashtable fib_ht; + struct rhashtable kern_fib_cache_ht; struct notifier_block inetaddr_nb; struct notifier_block inetaddr_valid_nb; + struct notifier_block fib_nb; }; struct prestera_rxtx_params { @@ -325,6 +328,8 @@ int prestera_port_cfg_mac_write(struct prestera_port *port, struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev); +void prestera_queue_work(struct work_struct *work); + int prestera_port_pvid_set(struct prestera_port *port, u16 vid); bool prestera_netdev_check(const struct net_device *dev); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_acl.c b/drivers/net/ethernet/marvell/prestera/prestera_acl.c index f0d9f592173b..47c899c08951 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_acl.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_acl.c @@ -22,6 +22,7 @@ struct prestera_acl { struct prestera_acl_ruleset_ht_key { struct prestera_flow_block *block; + u32 chain_index; }; struct prestera_acl_rule_entry { @@ -33,6 +34,10 @@ struct prestera_acl_rule_entry { struct { u8 valid:1; } accept, drop, trap; + struct { + struct prestera_acl_action_jump i; + u8 valid:1; + } jump; struct { u32 id; struct prestera_counter_block *block; @@ -49,6 +54,7 @@ struct prestera_acl_ruleset { refcount_t refcount; void *keymask; u32 vtcam_id; + u32 index; u16 pcl_id; bool offload; }; @@ -83,20 +89,45 @@ static const struct rhashtable_params __prestera_acl_rule_entry_ht_params = { .automatic_shrinking = true, }; +int prestera_acl_chain_to_client(u32 chain_index, u32 *client) +{ + static const u32 client_map[] = { + PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0, + PRESTERA_HW_COUNTER_CLIENT_LOOKUP_1, + PRESTERA_HW_COUNTER_CLIENT_LOOKUP_2 + }; + + if (chain_index >= ARRAY_SIZE(client_map)) + return -EINVAL; + + *client = client_map[chain_index]; + return 0; +} + +static bool prestera_acl_chain_is_supported(u32 chain_index) +{ + return (chain_index & ~PRESTERA_ACL_CHAIN_MASK) == 0; +} + static struct prestera_acl_ruleset * prestera_acl_ruleset_create(struct prestera_acl *acl, - struct prestera_flow_block *block) + struct prestera_flow_block *block, + u32 chain_index) { struct prestera_acl_ruleset *ruleset; u32 uid = 0; int err; + if (!prestera_acl_chain_is_supported(chain_index)) + return ERR_PTR(-EINVAL); + ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL); if (!ruleset) return ERR_PTR(-ENOMEM); ruleset->acl = acl; ruleset->ht_key.block = block; + ruleset->ht_key.chain_index = chain_index; refcount_set(&ruleset->refcount, 1); err = rhashtable_init(&ruleset->rule_ht, &prestera_acl_rule_ht_params); @@ -108,7 +139,9 @@ prestera_acl_ruleset_create(struct prestera_acl *acl, goto err_ruleset_create; /* make pcl-id based on uid */ - ruleset->pcl_id = (u8)uid; + ruleset->pcl_id = PRESTERA_ACL_PCL_ID_MAKE((u8)uid, chain_index); + ruleset->index = uid; + err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node, prestera_acl_ruleset_ht_params); if (err) @@ -133,35 +166,64 @@ void prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset, int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset) { + struct prestera_acl_iface iface; u32 vtcam_id; int err; if (ruleset->offload) return -EEXIST; - err = prestera_acl_vtcam_id_get(ruleset->acl, 0, + err = prestera_acl_vtcam_id_get(ruleset->acl, + ruleset->ht_key.chain_index, ruleset->keymask, &vtcam_id); if (err) - return err; + goto err_vtcam_create; + + if (ruleset->ht_key.chain_index) { + /* for chain > 0, bind iface index to pcl-id to be able + * to jump from any other ruleset to this one using the index. + */ + iface.index = ruleset->index; + iface.type = PRESTERA_ACL_IFACE_TYPE_INDEX; + err = prestera_hw_vtcam_iface_bind(ruleset->acl->sw, &iface, + vtcam_id, ruleset->pcl_id); + if (err) + goto err_ruleset_bind; + } ruleset->vtcam_id = vtcam_id; ruleset->offload = true; return 0; + +err_ruleset_bind: + prestera_acl_vtcam_id_put(ruleset->acl, ruleset->vtcam_id); +err_vtcam_create: + return err; } static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset) { struct prestera_acl *acl = ruleset->acl; u8 uid = ruleset->pcl_id & PRESTERA_ACL_KEYMASK_PCL_ID_USER; + int err; rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node, prestera_acl_ruleset_ht_params); - if (ruleset->offload) + if (ruleset->offload) { + if (ruleset->ht_key.chain_index) { + struct prestera_acl_iface iface = { + .type = PRESTERA_ACL_IFACE_TYPE_INDEX, + .index = ruleset->index + }; + err = prestera_hw_vtcam_iface_unbind(acl->sw, &iface, + ruleset->vtcam_id); + WARN_ON(err); + } WARN_ON(prestera_acl_vtcam_id_put(acl, ruleset->vtcam_id)); + } idr_remove(&acl->uid, uid); - rhashtable_destroy(&ruleset->rule_ht); kfree(ruleset->keymask); kfree(ruleset); @@ -169,23 +231,26 @@ static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset) static struct prestera_acl_ruleset * __prestera_acl_ruleset_lookup(struct prestera_acl *acl, - struct prestera_flow_block *block) + struct prestera_flow_block *block, + u32 chain_index) { struct prestera_acl_ruleset_ht_key ht_key; memset(&ht_key, 0, sizeof(ht_key)); ht_key.block = block; + ht_key.chain_index = chain_index; return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key, prestera_acl_ruleset_ht_params); } struct prestera_acl_ruleset * prestera_acl_ruleset_lookup(struct prestera_acl *acl, - struct prestera_flow_block *block) + struct prestera_flow_block *block, + u32 chain_index) { struct prestera_acl_ruleset *ruleset; - ruleset = __prestera_acl_ruleset_lookup(acl, block); + ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index); if (!ruleset) return ERR_PTR(-ENOENT); @@ -195,17 +260,18 @@ prestera_acl_ruleset_lookup(struct prestera_acl *acl, struct prestera_acl_ruleset * prestera_acl_ruleset_get(struct prestera_acl *acl, - struct prestera_flow_block *block) + struct prestera_flow_block *block, + u32 chain_index) { struct prestera_acl_ruleset *ruleset; - ruleset = __prestera_acl_ruleset_lookup(acl, block); + ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index); if (ruleset) { refcount_inc(&ruleset->refcount); return ruleset; } - return prestera_acl_ruleset_create(acl, block); + return prestera_acl_ruleset_create(acl, block, chain_index); } void prestera_acl_ruleset_put(struct prestera_acl_ruleset *ruleset) @@ -293,6 +359,11 @@ prestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset, prestera_acl_rule_ht_params); } +u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset) +{ + return ruleset->index; +} + bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset) { return ruleset->offload; @@ -300,7 +371,7 @@ bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset) struct prestera_acl_rule * prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset, - unsigned long cookie) + unsigned long cookie, u32 chain_index) { struct prestera_acl_rule *rule; @@ -310,6 +381,7 @@ prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset, rule->ruleset = ruleset; rule->cookie = cookie; + rule->chain_index = chain_index; refcount_inc(&ruleset->refcount); @@ -324,6 +396,10 @@ void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule, void prestera_acl_rule_destroy(struct prestera_acl_rule *rule) { + if (rule->jump_ruleset) + /* release ruleset kept by jump action */ + prestera_acl_ruleset_put(rule->jump_ruleset); + prestera_acl_ruleset_put(rule->ruleset); kfree(rule); } @@ -347,7 +423,10 @@ int prestera_acl_rule_add(struct prestera_switch *sw, /* setup counter */ rule->re_arg.count.valid = true; - rule->re_arg.count.client = PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0; + err = prestera_acl_chain_to_client(ruleset->ht_key.chain_index, + &rule->re_arg.count.client); + if (err) + goto err_rule_add; rule->re = prestera_acl_rule_entry_find(sw->acl, &rule->re_key); err = WARN_ON(rule->re) ? -EEXIST : 0; @@ -360,8 +439,10 @@ int prestera_acl_rule_add(struct prestera_switch *sw, if (err) goto err_rule_add; - /* bind the block (all ports) to chain index 0 */ - if (!ruleset->rule_count) { + /* bind the block (all ports) to chain index 0, rest of + * the chains are bound to goto action + */ + if (!ruleset->ht_key.chain_index && !ruleset->rule_count) { err = prestera_acl_ruleset_block_bind(ruleset, block); if (err) goto err_acl_block_bind; @@ -395,7 +476,7 @@ void prestera_acl_rule_del(struct prestera_switch *sw, prestera_acl_rule_entry_destroy(sw->acl, rule->re); /* unbind block (all ports) */ - if (!ruleset->rule_count) + if (!ruleset->ht_key.chain_index && !ruleset->rule_count) prestera_acl_ruleset_block_unbind(ruleset, block); } @@ -459,6 +540,12 @@ static int __prestera_acl_rule_entry2hw_add(struct prestera_switch *sw, act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_TRAP; act_num++; } + /* jump */ + if (e->jump.valid) { + act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_JUMP; + act_hw[act_num].jump = e->jump.i; + act_num++; + } /* counter */ if (e->counter.block) { act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_COUNT; @@ -505,6 +592,9 @@ __prestera_acl_rule_entry_act_construct(struct prestera_switch *sw, e->drop.valid = arg->drop.valid; /* trap */ e->trap.valid = arg->trap.valid; + /* jump */ + e->jump.valid = arg->jump.valid; + e->jump.i = arg->jump.i; /* counter */ if (arg->count.valid) { int err; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_acl.h b/drivers/net/ethernet/marvell/prestera/prestera_acl.h index 40f6c1d961fa..6d2ad27682d1 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_acl.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_acl.h @@ -10,6 +10,14 @@ #define PRESTERA_ACL_KEYMASK_PCL_ID 0x3FF #define PRESTERA_ACL_KEYMASK_PCL_ID_USER \ (PRESTERA_ACL_KEYMASK_PCL_ID & 0x00FF) +#define PRESTERA_ACL_KEYMASK_PCL_ID_CHAIN \ + (PRESTERA_ACL_KEYMASK_PCL_ID & 0xFF00) +#define PRESTERA_ACL_CHAIN_MASK \ + (PRESTERA_ACL_KEYMASK_PCL_ID >> 8) + +#define PRESTERA_ACL_PCL_ID_MAKE(uid, chain_id) \ + (((uid) & PRESTERA_ACL_KEYMASK_PCL_ID_USER) | \ + (((chain_id) << 8) & PRESTERA_ACL_KEYMASK_PCL_ID_CHAIN)) #define rule_match_set_n(match_p, type, val_p, size) \ memcpy(&(match_p)[PRESTERA_ACL_RULE_MATCH_TYPE_##type], \ @@ -46,6 +54,7 @@ enum prestera_acl_rule_action { PRESTERA_ACL_RULE_ACTION_ACCEPT = 0, PRESTERA_ACL_RULE_ACTION_DROP = 1, PRESTERA_ACL_RULE_ACTION_TRAP = 2, + PRESTERA_ACL_RULE_ACTION_JUMP = 5, PRESTERA_ACL_RULE_ACTION_COUNT = 7, PRESTERA_ACL_RULE_ACTION_MAX @@ -61,6 +70,10 @@ struct prestera_acl_match { __be32 mask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX]; }; +struct prestera_acl_action_jump { + u32 index; +}; + struct prestera_acl_action_count { u32 id; }; @@ -74,6 +87,7 @@ struct prestera_acl_hw_action_info { enum prestera_acl_rule_action id; union { struct prestera_acl_action_count count; + struct prestera_acl_action_jump jump; }; }; @@ -87,6 +101,10 @@ struct prestera_acl_rule_entry_arg { struct { u8 valid:1; } accept, drop, trap; + struct { + struct prestera_acl_action_jump i; + u8 valid:1; + } jump; struct { u8 valid:1; u32 client; @@ -98,7 +116,9 @@ struct prestera_acl_rule { struct rhash_head ht_node; /* Member of acl HT */ struct list_head list; struct prestera_acl_ruleset *ruleset; + struct prestera_acl_ruleset *jump_ruleset; unsigned long cookie; + u32 chain_index; u32 priority; struct prestera_acl_rule_entry_key re_key; struct prestera_acl_rule_entry_arg re_arg; @@ -122,7 +142,7 @@ void prestera_acl_fini(struct prestera_switch *sw); struct prestera_acl_rule * prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset, - unsigned long cookie); + unsigned long cookie, u32 chain_index); void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule, u32 priority); void prestera_acl_rule_destroy(struct prestera_acl_rule *rule); @@ -147,10 +167,12 @@ prestera_acl_rule_entry_create(struct prestera_acl *acl, struct prestera_acl_rule_entry_arg *arg); struct prestera_acl_ruleset * prestera_acl_ruleset_get(struct prestera_acl *acl, - struct prestera_flow_block *block); + struct prestera_flow_block *block, + u32 chain_index); struct prestera_acl_ruleset * prestera_acl_ruleset_lookup(struct prestera_acl *acl, - struct prestera_flow_block *block); + struct prestera_flow_block *block, + u32 chain_index); void prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset, void *keymask); bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset); @@ -160,6 +182,7 @@ int prestera_acl_ruleset_bind(struct prestera_acl_ruleset *ruleset, struct prestera_port *port); int prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset, struct prestera_port *port); +u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset); void prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id); @@ -167,5 +190,6 @@ prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, void *keymask, u32 *vtcam_id); int prestera_acl_vtcam_id_put(struct prestera_acl *acl, u32 vtcam_id); +int prestera_acl_chain_to_client(u32 chain_index, u32 *client); #endif /* _PRESTERA_ACL_H_ */ diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flow.c b/drivers/net/ethernet/marvell/prestera/prestera_flow.c index d849f046ece7..05c3ad98eba9 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flow.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_flow.c @@ -29,9 +29,6 @@ static int prestera_flow_block_mall_cb(struct prestera_flow_block *block, static int prestera_flow_block_flower_cb(struct prestera_flow_block *block, struct flow_cls_offload *f) { - if (f->common.chain_index != 0) - return -EOPNOTSUPP; - switch (f->command) { case FLOW_CLS_REPLACE: return prestera_flower_replace(block, f); @@ -71,6 +68,7 @@ static void prestera_flow_block_destroy(void *cb_priv) prestera_flower_template_cleanup(block); + WARN_ON(!list_empty(&block->template_list)); WARN_ON(!list_empty(&block->binding_list)); kfree(block); @@ -86,6 +84,7 @@ prestera_flow_block_create(struct prestera_switch *sw, struct net *net) return NULL; INIT_LIST_HEAD(&block->binding_list); + INIT_LIST_HEAD(&block->template_list); block->net = net; block->sw = sw; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flow.h b/drivers/net/ethernet/marvell/prestera/prestera_flow.h index 1ea5b745bf72..6550278b166a 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flow.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_flow.h @@ -8,7 +8,6 @@ struct prestera_port; struct prestera_switch; -struct prestera_flower_template; struct prestera_flow_block_binding { struct list_head list; @@ -22,7 +21,7 @@ struct prestera_flow_block { struct net *net; struct prestera_acl_ruleset *ruleset_zero; struct flow_block_cb *block_cb; - struct prestera_flower_template *tmplt; + struct list_head template_list; unsigned int rule_count; }; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flower.c b/drivers/net/ethernet/marvell/prestera/prestera_flower.c index 19c1417fd05f..921959a980ee 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flower.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_flower.c @@ -8,26 +8,63 @@ struct prestera_flower_template { struct prestera_acl_ruleset *ruleset; + struct list_head list; + u32 chain_index; }; +static void +prestera_flower_template_free(struct prestera_flower_template *template) +{ + prestera_acl_ruleset_put(template->ruleset); + list_del(&template->list); + kfree(template); +} + void prestera_flower_template_cleanup(struct prestera_flow_block *block) { - if (block->tmplt) { - /* put the reference to the ruleset kept in create */ - prestera_acl_ruleset_put(block->tmplt->ruleset); - kfree(block->tmplt); - block->tmplt = NULL; - return; - } + struct prestera_flower_template *template, *tmp; + + /* put the reference to all rulesets kept in tmpl create */ + list_for_each_entry_safe(template, tmp, &block->template_list, list) + prestera_flower_template_free(template); +} + +static int +prestera_flower_parse_goto_action(struct prestera_flow_block *block, + struct prestera_acl_rule *rule, + u32 chain_index, + const struct flow_action_entry *act) +{ + struct prestera_acl_ruleset *ruleset; + + if (act->chain_index <= chain_index) + /* we can jump only forward */ + return -EINVAL; + + if (rule->re_arg.jump.valid) + return -EEXIST; + + ruleset = prestera_acl_ruleset_get(block->sw->acl, block, + act->chain_index); + if (IS_ERR(ruleset)) + return PTR_ERR(ruleset); + + rule->re_arg.jump.valid = 1; + rule->re_arg.jump.i.index = prestera_acl_ruleset_index_get(ruleset); + + rule->jump_ruleset = ruleset; + + return 0; } static int prestera_flower_parse_actions(struct prestera_flow_block *block, struct prestera_acl_rule *rule, struct flow_action *flow_action, + u32 chain_index, struct netlink_ext_ack *extack) { const struct flow_action_entry *act; - int i; + int err, i; /* whole struct (rule->re_arg) must be initialized with 0 */ if (!flow_action_has_entries(flow_action)) @@ -53,6 +90,13 @@ static int prestera_flower_parse_actions(struct prestera_flow_block *block, rule->re_arg.trap.valid = 1; break; + case FLOW_ACTION_GOTO: + err = prestera_flower_parse_goto_action(block, rule, + chain_index, + act); + if (err) + return err; + break; default: NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); pr_err("Unsupported action\n"); @@ -259,6 +303,7 @@ static int prestera_flower_parse(struct prestera_flow_block *block, } return prestera_flower_parse_actions(block, rule, &f->rule->action, + f->common.chain_index, f->common.extack); } @@ -270,12 +315,13 @@ int prestera_flower_replace(struct prestera_flow_block *block, struct prestera_acl_rule *rule; int err; - ruleset = prestera_acl_ruleset_get(acl, block); + ruleset = prestera_acl_ruleset_get(acl, block, f->common.chain_index); if (IS_ERR(ruleset)) return PTR_ERR(ruleset); /* increments the ruleset reference */ - rule = prestera_acl_rule_create(ruleset, f->cookie); + rule = prestera_acl_rule_create(ruleset, f->cookie, + f->common.chain_index); if (IS_ERR(rule)) { err = PTR_ERR(rule); goto err_rule_create; @@ -312,7 +358,8 @@ void prestera_flower_destroy(struct prestera_flow_block *block, struct prestera_acl_ruleset *ruleset; struct prestera_acl_rule *rule; - ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block); + ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block, + f->common.chain_index); if (IS_ERR(ruleset)) return; @@ -345,7 +392,8 @@ int prestera_flower_tmplt_create(struct prestera_flow_block *block, } prestera_acl_rule_keymask_pcl_id_set(&rule, 0); - ruleset = prestera_acl_ruleset_get(block->sw->acl, block); + ruleset = prestera_acl_ruleset_get(block->sw->acl, block, + f->common.chain_index); if (IS_ERR_OR_NULL(ruleset)) { err = -EINVAL; goto err_ruleset_get; @@ -364,7 +412,8 @@ int prestera_flower_tmplt_create(struct prestera_flow_block *block, /* keep the reference to the ruleset */ template->ruleset = ruleset; - block->tmplt = template; + template->chain_index = f->common.chain_index; + list_add_rcu(&template->list, &block->template_list); return 0; err_ruleset_get: @@ -377,7 +426,14 @@ int prestera_flower_tmplt_create(struct prestera_flow_block *block, void prestera_flower_tmplt_destroy(struct prestera_flow_block *block, struct flow_cls_offload *f) { - prestera_flower_template_cleanup(block); + struct prestera_flower_template *template, *tmp; + + list_for_each_entry_safe(template, tmp, &block->template_list, list) + if (template->chain_index == f->common.chain_index) { + /* put the reference to the ruleset kept in create */ + prestera_flower_template_free(template); + return; + } } int prestera_flower_stats(struct prestera_flow_block *block, @@ -390,7 +446,8 @@ int prestera_flower_stats(struct prestera_flow_block *block, u64 bytes; int err; - ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block); + ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block, + f->common.chain_index); if (IS_ERR(ruleset)) return PTR_ERR(ruleset); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flower.h b/drivers/net/ethernet/marvell/prestera/prestera_flower.h index dc3aa4280e9f..495f151e6fa9 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flower.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_flower.h @@ -6,7 +6,6 @@ #include -struct prestera_switch; struct prestera_flow_block; int prestera_flower_replace(struct prestera_flow_block *block, diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.c b/drivers/net/ethernet/marvell/prestera/prestera_hw.c index e6bfadc874c5..c66cc929c820 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_hw.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.c @@ -55,6 +55,8 @@ enum prestera_cmd_type_t { PRESTERA_CMD_TYPE_ROUTER_RIF_CREATE = 0x600, PRESTERA_CMD_TYPE_ROUTER_RIF_DELETE = 0x601, + PRESTERA_CMD_TYPE_ROUTER_LPM_ADD = 0x610, + PRESTERA_CMD_TYPE_ROUTER_LPM_DELETE = 0x611, PRESTERA_CMD_TYPE_ROUTER_VR_CREATE = 0x630, PRESTERA_CMD_TYPE_ROUTER_VR_DELETE = 0x631, @@ -423,6 +425,9 @@ struct prestera_msg_acl_action { __le32 id; __le32 __reserved; union { + struct { + __le32 index; + } jump; struct { __le32 id; } count; @@ -499,6 +504,15 @@ struct prestera_msg_iface { u8 __pad[3]; }; +struct prestera_msg_ip_addr { + union { + __be32 ipv4; + __be32 ipv6[4]; + } u; + u8 v; /* e.g. PRESTERA_IPV4 */ + u8 __pad[3]; +}; + struct prestera_msg_rif_req { struct prestera_msg_cmd cmd; struct prestera_msg_iface iif; @@ -515,6 +529,15 @@ struct prestera_msg_rif_resp { u8 __pad[2]; }; +struct prestera_msg_lpm_req { + struct prestera_msg_cmd cmd; + struct prestera_msg_ip_addr dst; + __le32 grp_id; + __le32 dst_len; + __le16 vr_id; + u8 __pad[2]; +}; + struct prestera_msg_vr_req { struct prestera_msg_cmd cmd; __le16 vr_id; @@ -598,9 +621,11 @@ static void prestera_hw_build_tests(void) BUILD_BUG_ON(sizeof(struct prestera_msg_counter_stats) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_rif_req) != 36); BUILD_BUG_ON(sizeof(struct prestera_msg_vr_req) != 8); + BUILD_BUG_ON(sizeof(struct prestera_msg_lpm_req) != 36); /* structure that are part of req/resp fw messages */ BUILD_BUG_ON(sizeof(struct prestera_msg_iface) != 16); + BUILD_BUG_ON(sizeof(struct prestera_msg_ip_addr) != 20); /* check responses */ BUILD_BUG_ON(sizeof(struct prestera_msg_common_resp) != 8); @@ -1164,6 +1189,9 @@ prestera_acl_rule_add_put_action(struct prestera_msg_acl_action *action, case PRESTERA_ACL_RULE_ACTION_TRAP: /* just rule action id, no specific data */ break; + case PRESTERA_ACL_RULE_ACTION_JUMP: + action->jump.index = __cpu_to_le32(info->jump.index); + break; case PRESTERA_ACL_RULE_ACTION_COUNT: action->count.id = __cpu_to_le32(info->count.id); break; @@ -1891,6 +1919,33 @@ int prestera_hw_vr_delete(struct prestera_switch *sw, u16 vr_id) sizeof(req)); } +int prestera_hw_lpm_add(struct prestera_switch *sw, u16 vr_id, + __be32 dst, u32 dst_len, u32 grp_id) +{ + struct prestera_msg_lpm_req req = { + .dst_len = __cpu_to_le32(dst_len), + .vr_id = __cpu_to_le16(vr_id), + .grp_id = __cpu_to_le32(grp_id), + .dst.u.ipv4 = dst + }; + + return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_LPM_ADD, &req.cmd, + sizeof(req)); +} + +int prestera_hw_lpm_del(struct prestera_switch *sw, u16 vr_id, + __be32 dst, u32 dst_len) +{ + struct prestera_msg_lpm_req req = { + .dst_len = __cpu_to_le32(dst_len), + .vr_id = __cpu_to_le16(vr_id), + .dst.u.ipv4 = dst + }; + + return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_LPM_DELETE, &req.cmd, + sizeof(req)); +} + int prestera_hw_rxtx_init(struct prestera_switch *sw, struct prestera_rxtx_params *params) { diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.h b/drivers/net/ethernet/marvell/prestera/prestera_hw.h index 3ff12bae5909..fd896a8838bb 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_hw.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.h @@ -249,6 +249,12 @@ int prestera_hw_rif_delete(struct prestera_switch *sw, u16 rif_id, int prestera_hw_vr_create(struct prestera_switch *sw, u16 *vr_id); int prestera_hw_vr_delete(struct prestera_switch *sw, u16 vr_id); +/* LPM PI */ +int prestera_hw_lpm_add(struct prestera_switch *sw, u16 vr_id, + __be32 dst, u32 dst_len, u32 grp_id); +int prestera_hw_lpm_del(struct prestera_switch *sw, u16 vr_id, + __be32 dst, u32 dst_len); + /* Event handlers */ int prestera_hw_event_handler_register(struct prestera_switch *sw, enum prestera_event_type type, diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c index 73cd0a4b7291..3952fdcc9240 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_main.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c @@ -28,6 +28,12 @@ #define PRESTERA_MAC_ADDR_NUM_MAX 255 static struct workqueue_struct *prestera_wq; +static struct workqueue_struct *prestera_owq; + +void prestera_queue_work(struct work_struct *work) +{ + queue_work(prestera_owq, work); +} int prestera_port_pvid_set(struct prestera_port *port, u16 vid) { @@ -1025,12 +1031,19 @@ static int __init prestera_module_init(void) if (!prestera_wq) return -ENOMEM; + prestera_owq = alloc_ordered_workqueue("prestera_ordered", 0); + if (!prestera_owq) { + destroy_workqueue(prestera_wq); + return -ENOMEM; + } + return 0; } static void __exit prestera_module_exit(void) { destroy_workqueue(prestera_wq); + destroy_workqueue(prestera_owq); } module_init(prestera_module_init); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c index 6ef4d32b8fdd..6c5618cf4f08 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_router.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_router.c @@ -5,10 +5,39 @@ #include #include #include +#include #include "prestera.h" #include "prestera_router_hw.h" +struct prestera_kern_fib_cache_key { + struct prestera_ip_addr addr; + u32 prefix_len; + u32 kern_tb_id; /* tb_id from kernel (not fixed) */ +}; + +/* Subscribing on neighbours in kernel */ +struct prestera_kern_fib_cache { + struct prestera_kern_fib_cache_key key; + struct { + struct prestera_fib_key fib_key; + enum prestera_fib_type fib_type; + } lpm_info; /* hold prepared lpm info */ + /* Indicate if route is not overlapped by another table */ + struct rhash_head ht_node; /* node of prestera_router */ + struct fib_info *fi; + u8 kern_tos; + u8 kern_type; + bool reachable; +}; + +static const struct rhashtable_params __prestera_kern_fib_cache_ht_params = { + .key_offset = offsetof(struct prestera_kern_fib_cache, key), + .head_offset = offsetof(struct prestera_kern_fib_cache, ht_node), + .key_len = sizeof(struct prestera_kern_fib_cache_key), + .automatic_shrinking = true, +}; + /* This util to be used, to convert kernel rules for default vr in hw_vr */ static u32 prestera_fix_tb_id(u32 tb_id) { @@ -20,6 +49,290 @@ static u32 prestera_fix_tb_id(u32 tb_id) return tb_id; } +static void +prestera_util_fen_info2fib_cache_key(struct fib_entry_notifier_info *fen_info, + struct prestera_kern_fib_cache_key *key) +{ + memset(key, 0, sizeof(*key)); + key->addr.u.ipv4 = cpu_to_be32(fen_info->dst); + key->prefix_len = fen_info->dst_len; + key->kern_tb_id = fen_info->tb_id; +} + +static struct prestera_kern_fib_cache * +prestera_kern_fib_cache_find(struct prestera_switch *sw, + struct prestera_kern_fib_cache_key *key) +{ + struct prestera_kern_fib_cache *fib_cache; + + fib_cache = + rhashtable_lookup_fast(&sw->router->kern_fib_cache_ht, key, + __prestera_kern_fib_cache_ht_params); + return fib_cache; +} + +static void +prestera_kern_fib_cache_destroy(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fib_cache) +{ + fib_info_put(fib_cache->fi); + rhashtable_remove_fast(&sw->router->kern_fib_cache_ht, + &fib_cache->ht_node, + __prestera_kern_fib_cache_ht_params); + kfree(fib_cache); +} + +/* Operations on fi (offload, etc) must be wrapped in utils. + * This function just create storage. + */ +static struct prestera_kern_fib_cache * +prestera_kern_fib_cache_create(struct prestera_switch *sw, + struct prestera_kern_fib_cache_key *key, + struct fib_info *fi, u8 tos, u8 type) +{ + struct prestera_kern_fib_cache *fib_cache; + int err; + + fib_cache = kzalloc(sizeof(*fib_cache), GFP_KERNEL); + if (!fib_cache) + goto err_kzalloc; + + memcpy(&fib_cache->key, key, sizeof(*key)); + fib_info_hold(fi); + fib_cache->fi = fi; + fib_cache->kern_tos = tos; + fib_cache->kern_type = type; + + err = rhashtable_insert_fast(&sw->router->kern_fib_cache_ht, + &fib_cache->ht_node, + __prestera_kern_fib_cache_ht_params); + if (err) + goto err_ht_insert; + + return fib_cache; + +err_ht_insert: + fib_info_put(fi); + kfree(fib_cache); +err_kzalloc: + return NULL; +} + +static void +__prestera_k_arb_fib_lpm_offload_set(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc, + bool fail, bool offload, bool trap) +{ + struct fib_rt_info fri; + + if (fc->key.addr.v != PRESTERA_IPV4) + return; + + fri.fi = fc->fi; + fri.tb_id = fc->key.kern_tb_id; + fri.dst = fc->key.addr.u.ipv4; + fri.dst_len = fc->key.prefix_len; + fri.tos = fc->kern_tos; + fri.type = fc->kern_type; + /* flags begin */ + fri.offload = offload; + fri.trap = trap; + fri.offload_failed = fail; + /* flags end */ + fib_alias_hw_flags_set(&init_net, &fri); +} + +static int +__prestera_pr_k_arb_fc_lpm_info_calc(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc) +{ + memset(&fc->lpm_info, 0, sizeof(fc->lpm_info)); + + switch (fc->fi->fib_type) { + case RTN_UNICAST: + fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_TRAP; + break; + /* Unsupported. Leave it for kernel: */ + case RTN_BROADCAST: + case RTN_MULTICAST: + /* Routes we must trap by design: */ + case RTN_LOCAL: + case RTN_UNREACHABLE: + case RTN_PROHIBIT: + fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_TRAP; + break; + case RTN_BLACKHOLE: + fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_DROP; + break; + default: + dev_err(sw->dev->dev, "Unsupported fib_type"); + return -EOPNOTSUPP; + } + + fc->lpm_info.fib_key.addr = fc->key.addr; + fc->lpm_info.fib_key.prefix_len = fc->key.prefix_len; + fc->lpm_info.fib_key.tb_id = prestera_fix_tb_id(fc->key.kern_tb_id); + + return 0; +} + +static int __prestera_k_arb_f_lpm_set(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc, + bool enabled) +{ + struct prestera_fib_node *fib_node; + + fib_node = prestera_fib_node_find(sw, &fc->lpm_info.fib_key); + if (fib_node) + prestera_fib_node_destroy(sw, fib_node); + + if (!enabled) + return 0; + + fib_node = prestera_fib_node_create(sw, &fc->lpm_info.fib_key, + fc->lpm_info.fib_type); + + if (!fib_node) { + dev_err(sw->dev->dev, "fib_node=NULL %pI4n/%d kern_tb_id = %d", + &fc->key.addr.u.ipv4, fc->key.prefix_len, + fc->key.kern_tb_id); + return -ENOENT; + } + + return 0; +} + +static int __prestera_k_arb_fc_apply(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc) +{ + int err; + + err = __prestera_pr_k_arb_fc_lpm_info_calc(sw, fc); + if (err) + return err; + + err = __prestera_k_arb_f_lpm_set(sw, fc, fc->reachable); + if (err) { + __prestera_k_arb_fib_lpm_offload_set(sw, fc, + true, false, false); + return err; + } + + switch (fc->lpm_info.fib_type) { + case PRESTERA_FIB_TYPE_TRAP: + __prestera_k_arb_fib_lpm_offload_set(sw, fc, false, + false, fc->reachable); + break; + case PRESTERA_FIB_TYPE_DROP: + __prestera_k_arb_fib_lpm_offload_set(sw, fc, false, true, + fc->reachable); + break; + case PRESTERA_FIB_TYPE_INVALID: + break; + } + + return 0; +} + +static struct prestera_kern_fib_cache * +__prestera_k_arb_util_fib_overlaps(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc) +{ + struct prestera_kern_fib_cache_key fc_key; + struct prestera_kern_fib_cache *rfc; + + /* TODO: parse kernel rules */ + rfc = NULL; + if (fc->key.kern_tb_id == RT_TABLE_LOCAL) { + memcpy(&fc_key, &fc->key, sizeof(fc_key)); + fc_key.kern_tb_id = RT_TABLE_MAIN; + rfc = prestera_kern_fib_cache_find(sw, &fc_key); + } + + return rfc; +} + +static struct prestera_kern_fib_cache * +__prestera_k_arb_util_fib_overlapped(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc) +{ + struct prestera_kern_fib_cache_key fc_key; + struct prestera_kern_fib_cache *rfc; + + /* TODO: parse kernel rules */ + rfc = NULL; + if (fc->key.kern_tb_id == RT_TABLE_MAIN) { + memcpy(&fc_key, &fc->key, sizeof(fc_key)); + fc_key.kern_tb_id = RT_TABLE_LOCAL; + rfc = prestera_kern_fib_cache_find(sw, &fc_key); + } + + return rfc; +} + +static int +prestera_k_arb_fib_evt(struct prestera_switch *sw, + bool replace, /* replace or del */ + struct fib_entry_notifier_info *fen_info) +{ + struct prestera_kern_fib_cache *tfib_cache, *bfib_cache; /* top/btm */ + struct prestera_kern_fib_cache_key fc_key; + struct prestera_kern_fib_cache *fib_cache; + int err; + + prestera_util_fen_info2fib_cache_key(fen_info, &fc_key); + fib_cache = prestera_kern_fib_cache_find(sw, &fc_key); + if (fib_cache) { + fib_cache->reachable = false; + err = __prestera_k_arb_fc_apply(sw, fib_cache); + if (err) + dev_err(sw->dev->dev, + "Applying destroyed fib_cache failed"); + + bfib_cache = __prestera_k_arb_util_fib_overlaps(sw, fib_cache); + tfib_cache = __prestera_k_arb_util_fib_overlapped(sw, fib_cache); + if (!tfib_cache && bfib_cache) { + bfib_cache->reachable = true; + err = __prestera_k_arb_fc_apply(sw, bfib_cache); + if (err) + dev_err(sw->dev->dev, + "Applying fib_cache btm failed"); + } + + prestera_kern_fib_cache_destroy(sw, fib_cache); + } + + if (replace) { + fib_cache = prestera_kern_fib_cache_create(sw, &fc_key, + fen_info->fi, + fen_info->tos, + fen_info->type); + if (!fib_cache) { + dev_err(sw->dev->dev, "fib_cache == NULL"); + return -ENOENT; + } + + bfib_cache = __prestera_k_arb_util_fib_overlaps(sw, fib_cache); + tfib_cache = __prestera_k_arb_util_fib_overlapped(sw, fib_cache); + if (!tfib_cache) + fib_cache->reachable = true; + + if (bfib_cache) { + bfib_cache->reachable = false; + err = __prestera_k_arb_fc_apply(sw, bfib_cache); + if (err) + dev_err(sw->dev->dev, + "Applying fib_cache btm failed"); + } + + err = __prestera_k_arb_fc_apply(sw, fib_cache); + if (err) + dev_err(sw->dev->dev, "Applying fib_cache failed"); + } + + return 0; +} + static int __prestera_inetaddr_port_event(struct net_device *port_dev, unsigned long event, struct netlink_ext_ack *extack) @@ -137,6 +450,89 @@ static int __prestera_inetaddr_valid_cb(struct notifier_block *nb, return notifier_from_errno(err); } +struct prestera_fib_event_work { + struct work_struct work; + struct prestera_switch *sw; + struct fib_entry_notifier_info fen_info; + unsigned long event; +}; + +static void __prestera_router_fib_event_work(struct work_struct *work) +{ + struct prestera_fib_event_work *fib_work = + container_of(work, struct prestera_fib_event_work, work); + struct prestera_switch *sw = fib_work->sw; + int err; + + rtnl_lock(); + + switch (fib_work->event) { + case FIB_EVENT_ENTRY_REPLACE: + err = prestera_k_arb_fib_evt(sw, true, &fib_work->fen_info); + if (err) + goto err_out; + + break; + case FIB_EVENT_ENTRY_DEL: + err = prestera_k_arb_fib_evt(sw, false, &fib_work->fen_info); + if (err) + goto err_out; + + break; + } + + goto out; + +err_out: + dev_err(sw->dev->dev, "Error when processing %pI4h/%d", + &fib_work->fen_info.dst, + fib_work->fen_info.dst_len); +out: + fib_info_put(fib_work->fen_info.fi); + rtnl_unlock(); + kfree(fib_work); +} + +/* Called with rcu_read_lock() */ +static int __prestera_router_fib_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct prestera_fib_event_work *fib_work; + struct fib_entry_notifier_info *fen_info; + struct fib_notifier_info *info = ptr; + struct prestera_router *router; + + if (info->family != AF_INET) + return NOTIFY_DONE; + + router = container_of(nb, struct prestera_router, fib_nb); + + switch (event) { + case FIB_EVENT_ENTRY_REPLACE: + case FIB_EVENT_ENTRY_DEL: + fen_info = container_of(info, struct fib_entry_notifier_info, + info); + if (!fen_info->fi) + return NOTIFY_DONE; + + fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); + if (WARN_ON(!fib_work)) + return NOTIFY_BAD; + + fib_info_hold(fen_info->fi); + fib_work->fen_info = *fen_info; + fib_work->event = event; + fib_work->sw = router->sw; + INIT_WORK(&fib_work->work, __prestera_router_fib_event_work); + prestera_queue_work(&fib_work->work); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_DONE; +} + int prestera_router_init(struct prestera_switch *sw) { struct prestera_router *router; @@ -153,6 +549,11 @@ int prestera_router_init(struct prestera_switch *sw) if (err) goto err_router_lib_init; + err = rhashtable_init(&router->kern_fib_cache_ht, + &__prestera_kern_fib_cache_ht_params); + if (err) + goto err_kern_fib_cache_ht_init; + router->inetaddr_valid_nb.notifier_call = __prestera_inetaddr_valid_cb; err = register_inetaddr_validator_notifier(&router->inetaddr_valid_nb); if (err) @@ -163,11 +564,21 @@ int prestera_router_init(struct prestera_switch *sw) if (err) goto err_register_inetaddr_notifier; + router->fib_nb.notifier_call = __prestera_router_fib_event; + err = register_fib_notifier(&init_net, &router->fib_nb, + /* TODO: flush fib entries */ NULL, NULL); + if (err) + goto err_register_fib_notifier; + return 0; +err_register_fib_notifier: + unregister_inetaddr_notifier(&router->inetaddr_nb); err_register_inetaddr_notifier: unregister_inetaddr_validator_notifier(&router->inetaddr_valid_nb); err_register_inetaddr_validator_notifier: + rhashtable_destroy(&router->kern_fib_cache_ht); +err_kern_fib_cache_ht_init: prestera_router_hw_fini(sw); err_router_lib_init: kfree(sw->router); @@ -178,6 +589,7 @@ void prestera_router_fini(struct prestera_switch *sw) { unregister_inetaddr_notifier(&sw->router->inetaddr_nb); unregister_inetaddr_validator_notifier(&sw->router->inetaddr_valid_nb); + rhashtable_destroy(&sw->router->kern_fib_cache_ht); prestera_router_hw_fini(sw); kfree(sw->router); sw->router = NULL; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router_hw.c b/drivers/net/ethernet/marvell/prestera/prestera_router_hw.c index e5592b69ad37..5b0cf3be9a9e 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_router_hw.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_router_hw.c @@ -9,23 +9,41 @@ #include "prestera_acl.h" /* +--+ - * +------->|vr| - * | +--+ - * | - * +-+-------+ - * |rif_entry| - * +---------+ - * Rif is + * +------->|vr|<-+ + * | +--+ | + * | | + * +-+-------+ +--+---+-+ + * |rif_entry| |fib_node| + * +---------+ +--------+ + * Rif is Fib - is exit point * used as * entry point * for vr in hw */ +#define PRESTERA_NHGR_UNUSED (0) +#define PRESTERA_NHGR_DROP (0xFFFFFFFF) + +static const struct rhashtable_params __prestera_fib_ht_params = { + .key_offset = offsetof(struct prestera_fib_node, key), + .head_offset = offsetof(struct prestera_fib_node, ht_node), + .key_len = sizeof(struct prestera_fib_key), + .automatic_shrinking = true, +}; + int prestera_router_hw_init(struct prestera_switch *sw) { + int err; + + err = rhashtable_init(&sw->router->fib_ht, + &__prestera_fib_ht_params); + if (err) + goto err_fib_ht_init; + INIT_LIST_HEAD(&sw->router->vr_list); INIT_LIST_HEAD(&sw->router->rif_entry_list); +err_fib_ht_init: return 0; } @@ -33,6 +51,7 @@ void prestera_router_hw_fini(struct prestera_switch *sw) { WARN_ON(!list_empty(&sw->router->vr_list)); WARN_ON(!list_empty(&sw->router->rif_entry_list)); + rhashtable_destroy(&sw->router->fib_ht); } static struct prestera_vr *__prestera_vr_find(struct prestera_switch *sw, @@ -212,3 +231,102 @@ prestera_rif_entry_create(struct prestera_switch *sw, err_kzalloc: return NULL; } + +struct prestera_fib_node * +prestera_fib_node_find(struct prestera_switch *sw, struct prestera_fib_key *key) +{ + struct prestera_fib_node *fib_node; + + fib_node = rhashtable_lookup_fast(&sw->router->fib_ht, key, + __prestera_fib_ht_params); + return fib_node; +} + +static void __prestera_fib_node_destruct(struct prestera_switch *sw, + struct prestera_fib_node *fib_node) +{ + struct prestera_vr *vr; + + vr = fib_node->info.vr; + prestera_hw_lpm_del(sw, vr->hw_vr_id, fib_node->key.addr.u.ipv4, + fib_node->key.prefix_len); + switch (fib_node->info.type) { + case PRESTERA_FIB_TYPE_TRAP: + break; + case PRESTERA_FIB_TYPE_DROP: + break; + default: + pr_err("Unknown fib_node->info.type = %d", + fib_node->info.type); + } + + prestera_vr_put(sw, vr); +} + +void prestera_fib_node_destroy(struct prestera_switch *sw, + struct prestera_fib_node *fib_node) +{ + __prestera_fib_node_destruct(sw, fib_node); + rhashtable_remove_fast(&sw->router->fib_ht, &fib_node->ht_node, + __prestera_fib_ht_params); + kfree(fib_node); +} + +struct prestera_fib_node * +prestera_fib_node_create(struct prestera_switch *sw, + struct prestera_fib_key *key, + enum prestera_fib_type fib_type) +{ + struct prestera_fib_node *fib_node; + u32 grp_id; + struct prestera_vr *vr; + int err; + + fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL); + if (!fib_node) + goto err_kzalloc; + + memcpy(&fib_node->key, key, sizeof(*key)); + fib_node->info.type = fib_type; + + vr = prestera_vr_get(sw, key->tb_id, NULL); + if (IS_ERR(vr)) + goto err_vr_get; + + fib_node->info.vr = vr; + + switch (fib_type) { + case PRESTERA_FIB_TYPE_TRAP: + grp_id = PRESTERA_NHGR_UNUSED; + break; + case PRESTERA_FIB_TYPE_DROP: + grp_id = PRESTERA_NHGR_DROP; + break; + default: + pr_err("Unsupported fib_type %d", fib_type); + goto err_nh_grp_get; + } + + err = prestera_hw_lpm_add(sw, vr->hw_vr_id, key->addr.u.ipv4, + key->prefix_len, grp_id); + if (err) + goto err_lpm_add; + + err = rhashtable_insert_fast(&sw->router->fib_ht, &fib_node->ht_node, + __prestera_fib_ht_params); + if (err) + goto err_ht_insert; + + return fib_node; + +err_ht_insert: + prestera_hw_lpm_del(sw, vr->hw_vr_id, key->addr.u.ipv4, + key->prefix_len); +err_lpm_add: +err_nh_grp_get: + prestera_vr_put(sw, vr); +err_vr_get: + kfree(fib_node); +err_kzalloc: + return NULL; +} diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router_hw.h b/drivers/net/ethernet/marvell/prestera/prestera_router_hw.h index b6b028551868..67dbb49c8bd4 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_router_hw.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_router_hw.h @@ -22,6 +22,42 @@ struct prestera_rif_entry { struct list_head router_node; /* ht */ }; +struct prestera_ip_addr { + union { + __be32 ipv4; + struct in6_addr ipv6; + } u; + enum { + PRESTERA_IPV4 = 0, + PRESTERA_IPV6 + } v; +}; + +struct prestera_fib_key { + struct prestera_ip_addr addr; + u32 prefix_len; + u32 tb_id; +}; + +struct prestera_fib_info { + struct prestera_vr *vr; + struct list_head vr_node; + enum prestera_fib_type { + PRESTERA_FIB_TYPE_INVALID = 0, + /* It can be connected route + * and will be overlapped with neighbours + */ + PRESTERA_FIB_TYPE_TRAP, + PRESTERA_FIB_TYPE_DROP + } type; +}; + +struct prestera_fib_node { + struct rhash_head ht_node; /* node of prestera_vr */ + struct prestera_fib_key key; + struct prestera_fib_info info; /* action related info */ +}; + struct prestera_rif_entry * prestera_rif_entry_find(const struct prestera_switch *sw, const struct prestera_rif_entry_key *k); @@ -31,6 +67,14 @@ struct prestera_rif_entry * prestera_rif_entry_create(struct prestera_switch *sw, struct prestera_rif_entry_key *k, u32 tb_id, const unsigned char *addr); +struct prestera_fib_node *prestera_fib_node_find(struct prestera_switch *sw, + struct prestera_fib_key *key); +void prestera_fib_node_destroy(struct prestera_switch *sw, + struct prestera_fib_node *fib_node); +struct prestera_fib_node * +prestera_fib_node_create(struct prestera_switch *sw, + struct prestera_fib_key *key, + enum prestera_fib_type fib_type); int prestera_router_hw_init(struct prestera_switch *sw); void prestera_router_hw_fini(struct prestera_switch *sw); diff --git a/drivers/net/ethernet/mediatek/mtk_star_emac.c b/drivers/net/ethernet/mediatek/mtk_star_emac.c index 89ca7960b225..4cd0747edaff 100644 --- a/drivers/net/ethernet/mediatek/mtk_star_emac.c +++ b/drivers/net/ethernet/mediatek/mtk_star_emac.c @@ -1556,6 +1556,7 @@ static int mtk_star_probe(struct platform_device *pdev) return devm_register_netdev(dev, ndev); } +#ifdef CONFIG_OF static const struct of_device_id mtk_star_of_match[] = { { .compatible = "mediatek,mt8516-eth", }, { .compatible = "mediatek,mt8518-eth", }, @@ -1563,6 +1564,7 @@ static const struct of_device_id mtk_star_of_match[] = { { } }; MODULE_DEVICE_TABLE(of, mtk_star_of_match); +#endif static SIMPLE_DEV_PM_OPS(mtk_star_pm_ops, mtk_star_suspend, mtk_star_resume); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 8cfc649f226b..8f762fc170b3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -1067,7 +1067,7 @@ static int mlx4_en_config_rss_qp(struct mlx4_en_priv *priv, int qpn, struct mlx4_qp_context *context; int err = 0; - context = kmalloc(sizeof(*context), GFP_KERNEL); + context = kzalloc(sizeof(*context), GFP_KERNEL); if (!context) return -ENOMEM; @@ -1078,7 +1078,6 @@ static int mlx4_en_config_rss_qp(struct mlx4_en_priv *priv, int qpn, } qp->event = mlx4_en_sqp_event; - memset(context, 0, sizeof(*context)); mlx4_en_fill_qp_context(priv, ring->actual_size, ring->stride, 0, 0, qpn, ring->cqn, -1, context); context->db_rec_addr = cpu_to_be64(ring->wqres.db.dma); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 817f4154b86d..f777151d226f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include "mlx4_en.h" diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index fcfd38fa9e6c..4bc666714a35 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -28,7 +28,7 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en/rqt.o en/tir.o en/rss.o en/rx_res.o \ en_selftest.o en/port.o en/monitor_stats.o en/health.o \ en/reporter_tx.o en/reporter_rx.o en/params.o en/xsk/pool.o \ en/xsk/setup.o en/xsk/rx.o en/xsk/tx.o en/devlink.o en/ptp.o \ - en/qos.o en/trap.o en/fs_tt_redirect.o + en/qos.o en/trap.o en/fs_tt_redirect.o en/selq.o # # Netdev extra @@ -55,7 +55,11 @@ mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en/tc/act/act.o en/tc/act/drop.o en/tc/a en/tc/act/ct.o en/tc/act/sample.o en/tc/act/ptype.o \ en/tc/act/redirect_ingress.o -mlx5_core-$(CONFIG_MLX5_TC_CT) += en/tc_ct.o +ifneq ($(CONFIG_MLX5_TC_CT),) + mlx5_core-y += en/tc_ct.o en/tc/ct_fs_dmfs.o + mlx5_core-$(CONFIG_MLX5_SW_STEERING) += en/tc/ct_fs_smfs.o +endif + mlx5_core-$(CONFIG_MLX5_TC_SAMPLE) += en/tc/sample.o # @@ -103,9 +107,10 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o steering/dr_icm_pool.o steering/dr_buddy.o \ steering/dr_ste.o steering/dr_send.o \ steering/dr_ste_v0.o steering/dr_ste_v1.o \ + steering/dr_ste_v2.o \ steering/dr_cmd.o steering/dr_fw.o \ steering/dr_action.o steering/fs_dr.o \ - steering/dr_dbg.o + steering/dr_dbg.o lib/smfs.o # # SF device # diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c index 291e427e9e4f..e52b0bac09da 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c @@ -71,53 +71,6 @@ static void *mlx5_dma_zalloc_coherent_node(struct mlx5_core_dev *dev, return cpu_handle; } -static int mlx5_buf_alloc_node(struct mlx5_core_dev *dev, int size, - struct mlx5_frag_buf *buf, int node) -{ - dma_addr_t t; - - buf->size = size; - buf->npages = 1; - buf->page_shift = (u8)get_order(size) + PAGE_SHIFT; - - buf->frags = kzalloc(sizeof(*buf->frags), GFP_KERNEL); - if (!buf->frags) - return -ENOMEM; - - buf->frags->buf = mlx5_dma_zalloc_coherent_node(dev, size, - &t, node); - if (!buf->frags->buf) - goto err_out; - - buf->frags->map = t; - - while (t & ((1 << buf->page_shift) - 1)) { - --buf->page_shift; - buf->npages *= 2; - } - - return 0; -err_out: - kfree(buf->frags); - return -ENOMEM; -} - -int mlx5_buf_alloc(struct mlx5_core_dev *dev, - int size, struct mlx5_frag_buf *buf) -{ - return mlx5_buf_alloc_node(dev, size, buf, dev->priv.numa_node); -} -EXPORT_SYMBOL(mlx5_buf_alloc); - -void mlx5_buf_free(struct mlx5_core_dev *dev, struct mlx5_frag_buf *buf) -{ - dma_free_coherent(mlx5_core_dma_dev(dev), buf->size, buf->frags->buf, - buf->frags->map); - - kfree(buf->frags); -} -EXPORT_SYMBOL_GPL(mlx5_buf_free); - int mlx5_frag_buf_alloc_node(struct mlx5_core_dev *dev, int size, struct mlx5_frag_buf *buf, int node) { @@ -183,11 +136,11 @@ static struct mlx5_db_pgdir *mlx5_alloc_db_pgdir(struct mlx5_core_dev *dev, u32 db_per_page = PAGE_SIZE / cache_line_size(); struct mlx5_db_pgdir *pgdir; - pgdir = kzalloc(sizeof(*pgdir), GFP_KERNEL); + pgdir = kzalloc_node(sizeof(*pgdir), GFP_KERNEL, node); if (!pgdir) return NULL; - pgdir->bitmap = bitmap_zalloc(db_per_page, GFP_KERNEL); + pgdir->bitmap = bitmap_zalloc_node(db_per_page, GFP_KERNEL, node); if (!pgdir->bitmap) { kfree(pgdir); return NULL; @@ -286,19 +239,6 @@ void mlx5_db_free(struct mlx5_core_dev *dev, struct mlx5_db *db) } EXPORT_SYMBOL_GPL(mlx5_db_free); -void mlx5_fill_page_array(struct mlx5_frag_buf *buf, __be64 *pas) -{ - u64 addr; - int i; - - for (i = 0; i < buf->npages; i++) { - addr = buf->frags->map + (i << buf->page_shift); - - pas[i] = cpu_to_be64(addr); - } -} -EXPORT_SYMBOL_GPL(mlx5_fill_page_array); - void mlx5_fill_page_frag_array_perm(struct mlx5_frag_buf *buf, __be64 *pas, u8 perm) { int i; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index fc19a0762af2..26ba94cb432e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -31,7 +31,6 @@ */ #include -#include #include #include #include @@ -191,10 +190,10 @@ static int verify_block_sig(struct mlx5_cmd_prot_block *block) int xor_len = sizeof(*block) - sizeof(block->data) - 1; if (xor8_buf(block, rsvd0_off, xor_len) != 0xff) - return -EINVAL; + return -EHWPOISON; if (xor8_buf(block, 0, sizeof(*block)) != 0xff) - return -EINVAL; + return -EHWPOISON; return 0; } @@ -260,12 +259,12 @@ static int verify_signature(struct mlx5_cmd_work_ent *ent) sig = xor8_buf(ent->lay, 0, sizeof(*ent->lay)); if (sig != 0xff) - return -EINVAL; + return -EHWPOISON; for (i = 0; i < n && next; i++) { err = verify_block_sig(next->buf); if (err) - return err; + return -EHWPOISON; next = next->next; } @@ -485,7 +484,7 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_LOAD_VHCA_STATE: *status = MLX5_DRIVER_STATUS_ABORTED; *synd = MLX5_DRIVER_SYND; - return -EIO; + return -ENOLINK; default: mlx5_core_err(dev, "Unknown FW command (%d)\n", op); return -EINVAL; @@ -771,45 +770,73 @@ struct mlx5_ifc_mbox_in_bits { u8 reserved_at_40[0x40]; }; -void mlx5_cmd_mbox_status(void *out, u8 *status, u32 *syndrome) +void mlx5_cmd_out_err(struct mlx5_core_dev *dev, u16 opcode, u16 op_mod, void *out) { - *status = MLX5_GET(mbox_out, out, status); - *syndrome = MLX5_GET(mbox_out, out, syndrome); -} + u32 syndrome = MLX5_GET(mbox_out, out, syndrome); + u8 status = MLX5_GET(mbox_out, out, status); -static int mlx5_cmd_check(struct mlx5_core_dev *dev, void *in, void *out) + mlx5_core_err_rl(dev, + "%s(0x%x) op_mod(0x%x) failed, status %s(0x%x), syndrome (0x%x), err(%d)\n", + mlx5_command_str(opcode), opcode, op_mod, + cmd_status_str(status), status, syndrome, cmd_status_to_err(status)); +} +EXPORT_SYMBOL(mlx5_cmd_out_err); + +static void cmd_status_print(struct mlx5_core_dev *dev, void *in, void *out) { + u16 opcode, op_mod; u32 syndrome; u8 status; - u16 opcode; - u16 op_mod; u16 uid; + int err; - mlx5_cmd_mbox_status(out, &status, &syndrome); - if (!status) - return 0; + syndrome = MLX5_GET(mbox_out, out, syndrome); + status = MLX5_GET(mbox_out, out, status); opcode = MLX5_GET(mbox_in, in, opcode); op_mod = MLX5_GET(mbox_in, in, op_mod); uid = MLX5_GET(mbox_in, in, uid); + err = cmd_status_to_err(status); + if (!uid && opcode != MLX5_CMD_OP_DESTROY_MKEY) - mlx5_core_err_rl(dev, - "%s(0x%x) op_mod(0x%x) failed, status %s(0x%x), syndrome (0x%x)\n", - mlx5_command_str(opcode), opcode, op_mod, - cmd_status_str(status), status, syndrome); + mlx5_cmd_out_err(dev, opcode, op_mod, out); else mlx5_core_dbg(dev, - "%s(0x%x) op_mod(0x%x) failed, status %s(0x%x), syndrome (0x%x)\n", - mlx5_command_str(opcode), - opcode, op_mod, - cmd_status_str(status), - status, - syndrome); - - return cmd_status_to_err(status); + "%s(0x%x) op_mod(0x%x) uid(%d) failed, status %s(0x%x), syndrome (0x%x), err(%d)\n", + mlx5_command_str(opcode), opcode, op_mod, uid, + cmd_status_str(status), status, syndrome, err); } +int mlx5_cmd_check(struct mlx5_core_dev *dev, int err, void *in, void *out) +{ + /* aborted due to PCI error or via reset flow mlx5_cmd_trigger_completions() */ + if (err == -ENXIO) { + u16 opcode = MLX5_GET(mbox_in, in, opcode); + u32 syndrome; + u8 status; + + /* PCI Error, emulate command return status, for smooth reset */ + err = mlx5_internal_err_ret_value(dev, opcode, &syndrome, &status); + MLX5_SET(mbox_out, out, status, status); + MLX5_SET(mbox_out, out, syndrome, syndrome); + if (!err) + return 0; + } + + /* driver or FW delivery error */ + if (err != -EREMOTEIO && err) + return err; + + /* check outbox status */ + err = cmd_status_to_err(MLX5_GET(mbox_out, out, status)); + if (err) + cmd_status_print(dev, in, out); + + return err; +} +EXPORT_SYMBOL(mlx5_cmd_check); + static void dump_command(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent, int input) { @@ -991,13 +1018,7 @@ static void cmd_work_handler(struct work_struct *work) /* Skip sending command to fw if internal error */ if (mlx5_cmd_is_down(dev) || !opcode_allowed(&dev->cmd, ent->op)) { - u8 status = 0; - u32 drv_synd; - - ent->ret = mlx5_internal_err_ret_value(dev, msg_to_opcode(ent->in), &drv_synd, &status); - MLX5_SET(mbox_out, ent->out, status, status); - MLX5_SET(mbox_out, ent->out, syndrome, drv_synd); - + ent->ret = -ENXIO; mlx5_cmd_comp_handler(dev, 1ULL << ent->idx, true); return; } @@ -1016,6 +1037,31 @@ static void cmd_work_handler(struct work_struct *work) } } +static int deliv_status_to_err(u8 status) +{ + switch (status) { + case MLX5_CMD_DELIVERY_STAT_OK: + case MLX5_DRIVER_STATUS_ABORTED: + return 0; + case MLX5_CMD_DELIVERY_STAT_SIGNAT_ERR: + case MLX5_CMD_DELIVERY_STAT_TOK_ERR: + return -EBADR; + case MLX5_CMD_DELIVERY_STAT_BAD_BLK_NUM_ERR: + case MLX5_CMD_DELIVERY_STAT_OUT_PTR_ALIGN_ERR: + case MLX5_CMD_DELIVERY_STAT_IN_PTR_ALIGN_ERR: + return -EFAULT; /* Bad address */ + case MLX5_CMD_DELIVERY_STAT_IN_LENGTH_ERR: + case MLX5_CMD_DELIVERY_STAT_OUT_LENGTH_ERR: + case MLX5_CMD_DELIVERY_STAT_CMD_DESCR_ERR: + case MLX5_CMD_DELIVERY_STAT_RES_FLD_NOT_CLR_ERR: + return -ENOMSG; + case MLX5_CMD_DELIVERY_STAT_FW_ERR: + return -EIO; + default: + return -EINVAL; + } +} + static const char *deliv_status_to_str(u8 status) { switch (status) { @@ -1112,16 +1158,27 @@ static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent) /* Notes: * 1. Callback functions may not sleep * 2. page queue commands do not support asynchrous completion + * + * return value in case (!callback): + * ret < 0 : Command execution couldn't be submitted by driver + * ret > 0 : Command execution couldn't be performed by firmware + * ret == 0: Command was executed by FW, Caller must check FW outbox status. + * + * return value in case (callback): + * ret < 0 : Command execution couldn't be submitted by driver + * ret == 0: Command will be submitted to FW for execution + * and the callback will be called for further status updates */ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, struct mlx5_cmd_msg *out, void *uout, int uout_size, mlx5_cmd_cbk_t callback, - void *context, int page_queue, u8 *status, + void *context, int page_queue, u8 token, bool force_polling) { struct mlx5_cmd *cmd = &dev->cmd; struct mlx5_cmd_work_ent *ent; struct mlx5_cmd_stats *stats; + u8 status = 0; int err = 0; s64 ds; u16 op; @@ -1152,12 +1209,12 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, cmd_work_handler(&ent->work); } else if (!queue_work(cmd->wq, &ent->work)) { mlx5_core_warn(dev, "failed to queue work\n"); - err = -ENOMEM; + err = -EALREADY; goto out_free; } if (callback) - goto out; /* mlx5_cmd_comp_handler() will put(ent) */ + return 0; /* mlx5_cmd_comp_handler() will put(ent) */ err = wait_func(dev, ent); if (err == -ETIMEDOUT || err == -ECANCELED) @@ -1175,12 +1232,11 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, mlx5_core_dbg_mask(dev, 1 << MLX5_CMD_TIME, "fw exec time for %s is %lld nsec\n", mlx5_command_str(op), ds); - *status = ent->status; out_free: + status = ent->status; cmd_ent_put(ent); -out: - return err; + return err ? : status; } static ssize_t dbg_write(struct file *filp, const char __user *buf, @@ -1497,7 +1553,7 @@ static void create_debugfs_files(struct mlx5_core_dev *dev) { struct mlx5_cmd_debug *dbg = &dev->cmd.dbg; - dbg->dbg_root = debugfs_create_dir("cmd", dev->priv.dbg_root); + dbg->dbg_root = debugfs_create_dir("cmd", mlx5_debugfs_get_dev_root(dev)); debugfs_create_file("in", 0400, dbg->dbg_root, dev, &dfops); debugfs_create_file("out", 0200, dbg->dbg_root, dev, &dfops); @@ -1623,15 +1679,15 @@ static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool force ent->ts2 = ktime_get_ns(); memcpy(ent->out->first.data, ent->lay->out, sizeof(ent->lay->out)); dump_command(dev, ent, 0); - if (!ent->ret) { + + if (vec & MLX5_TRIGGERED_CMD_COMP) + ent->ret = -ENXIO; + + if (!ent->ret) { /* Command completed by FW */ if (!cmd->checksum_disabled) ent->ret = verify_signature(ent); - else - ent->ret = 0; - if (vec & MLX5_TRIGGERED_CMD_COMP) - ent->status = MLX5_DRIVER_STATUS_ABORTED; - else - ent->status = ent->lay->status_own >> 1; + + ent->status = ent->lay->status_own >> 1; mlx5_core_dbg(dev, "command completed. ret 0x%x, delivery status %s(0x%x)\n", ent->ret, deliv_status_to_str(ent->status), ent->status); @@ -1649,21 +1705,18 @@ static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool force callback = ent->callback; context = ent->context; - err = ent->ret; - if (!err) { + err = ent->ret ? : ent->status; + if (err > 0) /* Failed in FW, command didn't execute */ + err = deliv_status_to_err(err); + + if (!err) err = mlx5_copy_from_msg(ent->uout, ent->out, ent->uout_size); - err = err ? err : mlx5_cmd_check(dev, - ent->in->first.data, - ent->uout); - } - mlx5_free_cmd_msg(dev, ent->out); free_msg(dev, ent->in); - err = err ? err : ent->status; /* final consumer is done, release ent */ cmd_ent_put(ent); callback(err, context); @@ -1677,7 +1730,7 @@ static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool force } } -void mlx5_cmd_trigger_completions(struct mlx5_core_dev *dev) +static void mlx5_cmd_trigger_completions(struct mlx5_core_dev *dev) { struct mlx5_cmd *cmd = &dev->cmd; unsigned long bitmask; @@ -1730,31 +1783,6 @@ void mlx5_cmd_flush(struct mlx5_core_dev *dev) up(&cmd->sem); } -static int status_to_err(u8 status) -{ - switch (status) { - case MLX5_CMD_DELIVERY_STAT_OK: - case MLX5_DRIVER_STATUS_ABORTED: - return 0; - case MLX5_CMD_DELIVERY_STAT_SIGNAT_ERR: - case MLX5_CMD_DELIVERY_STAT_TOK_ERR: - return -EBADR; - case MLX5_CMD_DELIVERY_STAT_BAD_BLK_NUM_ERR: - case MLX5_CMD_DELIVERY_STAT_OUT_PTR_ALIGN_ERR: - case MLX5_CMD_DELIVERY_STAT_IN_PTR_ALIGN_ERR: - return -EFAULT; /* Bad address */ - case MLX5_CMD_DELIVERY_STAT_IN_LENGTH_ERR: - case MLX5_CMD_DELIVERY_STAT_OUT_LENGTH_ERR: - case MLX5_CMD_DELIVERY_STAT_CMD_DESCR_ERR: - case MLX5_CMD_DELIVERY_STAT_RES_FLD_NOT_CLR_ERR: - return -ENOMSG; - case MLX5_CMD_DELIVERY_STAT_FW_ERR: - return -EIO; - default: - return -EINVAL; - } -} - static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size, gfp_t gfp) { @@ -1798,27 +1826,23 @@ static int is_manage_pages(void *in) return MLX5_GET(mbox_in, in, opcode) == MLX5_CMD_OP_MANAGE_PAGES; } +/* Notes: + * 1. Callback functions may not sleep + * 2. Page queue commands do not support asynchrous completion + */ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, int out_size, mlx5_cmd_cbk_t callback, void *context, bool force_polling) { - struct mlx5_cmd_msg *inb; - struct mlx5_cmd_msg *outb; + u16 opcode = MLX5_GET(mbox_in, in, opcode); + struct mlx5_cmd_msg *inb, *outb; int pages_queue; gfp_t gfp; - int err; - u8 status = 0; - u32 drv_synd; - u16 opcode; u8 token; + int err; - opcode = MLX5_GET(mbox_in, in, opcode); - if (mlx5_cmd_is_down(dev) || !opcode_allowed(&dev->cmd, opcode)) { - err = mlx5_internal_err_ret_value(dev, opcode, &drv_synd, &status); - MLX5_SET(mbox_out, out, status, status); - MLX5_SET(mbox_out, out, syndrome, drv_synd); - return err; - } + if (mlx5_cmd_is_down(dev) || !opcode_allowed(&dev->cmd, opcode)) + return -ENXIO; pages_queue = is_manage_pages(in); gfp = callback ? GFP_ATOMIC : GFP_KERNEL; @@ -1844,39 +1868,133 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, } err = mlx5_cmd_invoke(dev, inb, outb, out, out_size, callback, context, - pages_queue, &status, token, force_polling); + pages_queue, token, force_polling); + if (callback) + return err; + + if (err > 0) /* Failed in FW, command didn't execute */ + err = deliv_status_to_err(err); + if (err) goto out_out; - mlx5_core_dbg(dev, "err %d, status %d\n", err, status); - if (status) { - err = status_to_err(status); - goto out_out; - } - - if (!callback) - err = mlx5_copy_from_msg(out, outb, out_size); - + /* command completed by FW */ + err = mlx5_copy_from_msg(out, outb, out_size); out_out: - if (!callback) - mlx5_free_cmd_msg(dev, outb); - + mlx5_free_cmd_msg(dev, outb); out_in: - if (!callback) - free_msg(dev, inb); + free_msg(dev, inb); return err; } +static void cmd_status_log(struct mlx5_core_dev *dev, u16 opcode, u8 status, int err) +{ + struct mlx5_cmd_stats *stats; + + if (!err) + return; + + stats = &dev->cmd.stats[opcode]; + spin_lock_irq(&stats->lock); + stats->failed++; + if (err < 0) + stats->last_failed_errno = -err; + if (err == -EREMOTEIO) { + stats->failed_mbox_status++; + stats->last_failed_mbox_status = status; + } + spin_unlock_irq(&stats->lock); +} + +/* preserve -EREMOTEIO for outbox.status != OK, otherwise return err as is */ +static int cmd_status_err(struct mlx5_core_dev *dev, int err, u16 opcode, void *out) +{ + u8 status = MLX5_GET(mbox_out, out, status); + + if (err == -EREMOTEIO) /* -EREMOTEIO is preserved */ + err = -EIO; + + if (!err && status != MLX5_CMD_STAT_OK) + err = -EREMOTEIO; + + cmd_status_log(dev, opcode, status, err); + return err; +} + +/** + * mlx5_cmd_do - Executes a fw command, wait for completion. + * Unlike mlx5_cmd_exec, this function will not translate or intercept + * outbox.status and will return -EREMOTEIO when + * outbox.status != MLX5_CMD_STAT_OK + * + * @dev: mlx5 core device + * @in: inbox mlx5_ifc command buffer + * @in_size: inbox buffer size + * @out: outbox mlx5_ifc buffer + * @out_size: outbox size + * + * @return: + * -EREMOTEIO : Command executed by FW, outbox.status != MLX5_CMD_STAT_OK. + * Caller must check FW outbox status. + * 0 : Command execution successful, outbox.status == MLX5_CMD_STAT_OK. + * < 0 : Command execution couldn't be performed by firmware or driver + */ +int mlx5_cmd_do(struct mlx5_core_dev *dev, void *in, int in_size, void *out, int out_size) +{ + int err = cmd_exec(dev, in, in_size, out, out_size, NULL, NULL, false); + u16 opcode = MLX5_GET(mbox_in, in, opcode); + + err = cmd_status_err(dev, err, opcode, out); + return err; +} +EXPORT_SYMBOL(mlx5_cmd_do); + +/** + * mlx5_cmd_exec - Executes a fw command, wait for completion + * + * @dev: mlx5 core device + * @in: inbox mlx5_ifc command buffer + * @in_size: inbox buffer size + * @out: outbox mlx5_ifc buffer + * @out_size: outbox size + * + * @return: 0 if no error, FW command execution was successful + * and outbox status is ok. + */ int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, int out_size) { - int err; + int err = mlx5_cmd_do(dev, in, in_size, out, out_size); - err = cmd_exec(dev, in, in_size, out, out_size, NULL, NULL, false); - return err ? : mlx5_cmd_check(dev, in, out); + return mlx5_cmd_check(dev, err, in, out); } EXPORT_SYMBOL(mlx5_cmd_exec); +/** + * mlx5_cmd_exec_polling - Executes a fw command, poll for completion + * Needed for driver force teardown, when command completion EQ + * will not be available to complete the command + * + * @dev: mlx5 core device + * @in: inbox mlx5_ifc command buffer + * @in_size: inbox buffer size + * @out: outbox mlx5_ifc buffer + * @out_size: outbox size + * + * @return: 0 if no error, FW command execution was successful + * and outbox status is ok. + */ +int mlx5_cmd_exec_polling(struct mlx5_core_dev *dev, void *in, int in_size, + void *out, int out_size) +{ + int err = cmd_exec(dev, in, in_size, out, out_size, NULL, NULL, true); + u16 opcode = MLX5_GET(mbox_in, in, opcode); + + err = cmd_status_err(dev, err, opcode, out); + return mlx5_cmd_check(dev, err, in, out); +} +EXPORT_SYMBOL(mlx5_cmd_exec_polling); + void mlx5_cmd_init_async_ctx(struct mlx5_core_dev *dev, struct mlx5_async_ctx *ctx) { @@ -1905,8 +2023,10 @@ EXPORT_SYMBOL(mlx5_cmd_cleanup_async_ctx); static void mlx5_cmd_exec_cb_handler(int status, void *_work) { struct mlx5_async_work *work = _work; - struct mlx5_async_ctx *ctx = work->ctx; + struct mlx5_async_ctx *ctx; + ctx = work->ctx; + status = cmd_status_err(ctx->dev, status, work->opcode, work->out); work->user_callback(status, work); if (atomic_dec_and_test(&ctx->num_inflight)) wake_up(&ctx->wait); @@ -1920,6 +2040,8 @@ int mlx5_cmd_exec_cb(struct mlx5_async_ctx *ctx, void *in, int in_size, work->ctx = ctx; work->user_callback = callback; + work->opcode = MLX5_GET(mbox_in, in, opcode); + work->out = out; if (WARN_ON(!atomic_inc_not_zero(&ctx->num_inflight))) return -EIO; ret = cmd_exec(ctx->dev, in, in_size, out, out_size, @@ -1931,17 +2053,6 @@ int mlx5_cmd_exec_cb(struct mlx5_async_ctx *ctx, void *in, int in_size, } EXPORT_SYMBOL(mlx5_cmd_exec_cb); -int mlx5_cmd_exec_polling(struct mlx5_core_dev *dev, void *in, int in_size, - void *out, int out_size) -{ - int err; - - err = cmd_exec(dev, in, in_size, out, out_size, NULL, NULL, true); - - return err ? : mlx5_cmd_check(dev, in, out); -} -EXPORT_SYMBOL(mlx5_cmd_exec_polling); - static void destroy_msg_cache(struct mlx5_core_dev *dev) { struct cmd_msg_cache *ch; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cq.c b/drivers/net/ethernet/mellanox/mlx5/core/cq.c index 5371ad0a12eb..4caa1b6f40ba 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cq.c @@ -31,7 +31,6 @@ */ #include -#include #include #include #include @@ -86,8 +85,9 @@ static void mlx5_add_cq_to_tasklet(struct mlx5_core_cq *cq, spin_unlock_irqrestore(&tasklet_ctx->lock, flags); } -int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, - u32 *in, int inlen, u32 *out, int outlen) +/* Callers must verify outbox status in case of err */ +int mlx5_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, + u32 *in, int inlen, u32 *out, int outlen) { int eqn = MLX5_GET(cqc, MLX5_ADDR_OF(create_cq_in, in, cq_context), c_eqn_or_apu_element); @@ -101,7 +101,7 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, memset(out, 0, outlen); MLX5_SET(create_cq_in, in, opcode, MLX5_CMD_OP_CREATE_CQ); - err = mlx5_cmd_exec(dev, in, inlen, out, outlen); + err = mlx5_cmd_do(dev, in, inlen, out, outlen); if (err) return err; @@ -148,6 +148,16 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, mlx5_cmd_exec_in(dev, destroy_cq, din); return err; } +EXPORT_SYMBOL(mlx5_create_cq); + +/* oubox is checked and err val is normalized */ +int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, + u32 *in, int inlen, u32 *out, int outlen) +{ + int err = mlx5_create_cq(dev, cq, in, inlen, out, outlen); + + return mlx5_cmd_check(dev, err, in, out); +} EXPORT_SYMBOL(mlx5_core_create_cq); int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c index 10d195042ab5..3d3e55a5cb11 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c @@ -30,7 +30,6 @@ * SOFTWARE. */ -#include #include #include #include @@ -99,26 +98,32 @@ void mlx5_unregister_debugfs(void) debugfs_remove(mlx5_debugfs_root); } +struct dentry *mlx5_debugfs_get_dev_root(struct mlx5_core_dev *dev) +{ + return dev->priv.dbg.dbg_root; +} +EXPORT_SYMBOL(mlx5_debugfs_get_dev_root); + void mlx5_qp_debugfs_init(struct mlx5_core_dev *dev) { - dev->priv.qp_debugfs = debugfs_create_dir("QPs", dev->priv.dbg_root); + dev->priv.dbg.qp_debugfs = debugfs_create_dir("QPs", dev->priv.dbg.dbg_root); } EXPORT_SYMBOL(mlx5_qp_debugfs_init); void mlx5_qp_debugfs_cleanup(struct mlx5_core_dev *dev) { - debugfs_remove_recursive(dev->priv.qp_debugfs); + debugfs_remove_recursive(dev->priv.dbg.qp_debugfs); } EXPORT_SYMBOL(mlx5_qp_debugfs_cleanup); void mlx5_eq_debugfs_init(struct mlx5_core_dev *dev) { - dev->priv.eq_debugfs = debugfs_create_dir("EQs", dev->priv.dbg_root); + dev->priv.dbg.eq_debugfs = debugfs_create_dir("EQs", dev->priv.dbg.dbg_root); } void mlx5_eq_debugfs_cleanup(struct mlx5_core_dev *dev) { - debugfs_remove_recursive(dev->priv.eq_debugfs); + debugfs_remove_recursive(dev->priv.dbg.eq_debugfs); } static ssize_t average_read(struct file *filp, char __user *buf, size_t count, @@ -168,8 +173,8 @@ void mlx5_cmdif_debugfs_init(struct mlx5_core_dev *dev) const char *namep; int i; - cmd = &dev->priv.cmdif_debugfs; - *cmd = debugfs_create_dir("commands", dev->priv.dbg_root); + cmd = &dev->priv.dbg.cmdif_debugfs; + *cmd = debugfs_create_dir("commands", dev->priv.dbg.dbg_root); for (i = 0; i < MLX5_CMD_OP_MAX; i++) { stats = &dev->cmd.stats[i]; @@ -180,23 +185,51 @@ void mlx5_cmdif_debugfs_init(struct mlx5_core_dev *dev) debugfs_create_file("average", 0400, stats->root, stats, &stats_fops); debugfs_create_u64("n", 0400, stats->root, &stats->n); + debugfs_create_u64("failed", 0400, stats->root, &stats->failed); + debugfs_create_u64("failed_mbox_status", 0400, stats->root, + &stats->failed_mbox_status); + debugfs_create_u32("last_failed_errno", 0400, stats->root, + &stats->last_failed_errno); + debugfs_create_u8("last_failed_mbox_status", 0400, stats->root, + &stats->last_failed_mbox_status); } } } void mlx5_cmdif_debugfs_cleanup(struct mlx5_core_dev *dev) { - debugfs_remove_recursive(dev->priv.cmdif_debugfs); + debugfs_remove_recursive(dev->priv.dbg.cmdif_debugfs); } void mlx5_cq_debugfs_init(struct mlx5_core_dev *dev) { - dev->priv.cq_debugfs = debugfs_create_dir("CQs", dev->priv.dbg_root); + dev->priv.dbg.cq_debugfs = debugfs_create_dir("CQs", dev->priv.dbg.dbg_root); } void mlx5_cq_debugfs_cleanup(struct mlx5_core_dev *dev) { - debugfs_remove_recursive(dev->priv.cq_debugfs); + debugfs_remove_recursive(dev->priv.dbg.cq_debugfs); +} + +void mlx5_pages_debugfs_init(struct mlx5_core_dev *dev) +{ + struct dentry *pages; + + dev->priv.dbg.pages_debugfs = debugfs_create_dir("pages", dev->priv.dbg.dbg_root); + pages = dev->priv.dbg.pages_debugfs; + + debugfs_create_u32("fw_pages_total", 0400, pages, &dev->priv.fw_pages); + debugfs_create_u32("fw_pages_vfs", 0400, pages, &dev->priv.vfs_pages); + debugfs_create_u32("fw_pages_host_pf", 0400, pages, &dev->priv.host_pf_pages); + debugfs_create_u32("fw_pages_alloc_failed", 0400, pages, &dev->priv.fw_pages_alloc_failed); + debugfs_create_u32("fw_pages_give_dropped", 0400, pages, &dev->priv.give_pages_dropped); + debugfs_create_u32("fw_pages_reclaim_discard", 0400, pages, + &dev->priv.reclaim_pages_discard); +} + +void mlx5_pages_debugfs_cleanup(struct mlx5_core_dev *dev) +{ + debugfs_remove_recursive(dev->priv.dbg.pages_debugfs); } static u64 qp_read_field(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp, @@ -441,7 +474,7 @@ int mlx5_debug_qp_add(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp) if (!mlx5_debugfs_root) return 0; - err = add_res_tree(dev, MLX5_DBG_RSC_QP, dev->priv.qp_debugfs, + err = add_res_tree(dev, MLX5_DBG_RSC_QP, dev->priv.dbg.qp_debugfs, &qp->dbg, qp->qpn, qp_fields, ARRAY_SIZE(qp_fields), qp); if (err) @@ -468,7 +501,7 @@ int mlx5_debug_eq_add(struct mlx5_core_dev *dev, struct mlx5_eq *eq) if (!mlx5_debugfs_root) return 0; - err = add_res_tree(dev, MLX5_DBG_RSC_EQ, dev->priv.eq_debugfs, + err = add_res_tree(dev, MLX5_DBG_RSC_EQ, dev->priv.dbg.eq_debugfs, &eq->dbg, eq->eqn, eq_fields, ARRAY_SIZE(eq_fields), eq); if (err) @@ -493,7 +526,7 @@ int mlx5_debug_cq_add(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq) if (!mlx5_debugfs_root) return 0; - err = add_res_tree(dev, MLX5_DBG_RSC_CQ, dev->priv.cq_debugfs, + err = add_res_tree(dev, MLX5_DBG_RSC_CQ, dev->priv.dbg.cq_debugfs, &cq->dbg, cq->cqn, cq_fields, ARRAY_SIZE(cq_fields), cq); if (err) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index d1093bb2d436..057dde6f4417 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -100,15 +100,11 @@ static int mlx5_devlink_reload_fw_activate(struct devlink *devlink, struct netli } net_port_alive = !!(reset_type & MLX5_MFRL_REG_RESET_TYPE_NET_PORT_ALIVE); - err = mlx5_fw_reset_set_reset_sync(dev, net_port_alive); + err = mlx5_fw_reset_set_reset_sync(dev, net_port_alive, extack); if (err) - goto out; + return err; - err = mlx5_fw_reset_wait_reset_done(dev); -out: - if (err) - NL_SET_ERR_MSG_MOD(extack, "FW activate command failed"); - return err; + return mlx5_fw_reset_wait_reset_done(dev); } static int mlx5_devlink_trigger_fw_live_patch(struct devlink *devlink, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index c14e06ca64d8..8653ac0fd865 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -59,6 +59,7 @@ #include "lib/hv_vhca.h" #include "lib/clock.h" #include "en/rx_res.h" +#include "en/selq.h" extern const struct net_device_ops mlx5e_netdev_ops; struct page_pool; @@ -172,8 +173,9 @@ struct page_pool; #define MLX5E_KLM_ENTRIES_PER_WQE(wqe_size)\ ALIGN_DOWN(MLX5E_KLM_MAX_ENTRIES_PER_WQE(wqe_size), MLX5_UMR_KLM_ALIGNMENT) -#define MLX5E_MAX_KLM_PER_WQE \ - MLX5E_KLM_ENTRIES_PER_WQE(MLX5E_TX_MPW_MAX_NUM_DS << MLX5_MKEY_BSF_OCTO_SIZE) +#define MLX5E_MAX_KLM_PER_WQE(mdev) \ + MLX5E_KLM_ENTRIES_PER_WQE(mlx5e_get_sw_max_sq_mpw_wqebbs(mlx5e_get_max_sq_wqebbs(mdev)) \ + << MLX5_MKEY_BSF_OCTO_SIZE) #define MLX5E_MSG_LEVEL NETIF_MSG_LINK @@ -221,6 +223,32 @@ static inline int mlx5e_get_max_num_channels(struct mlx5_core_dev *mdev) min_t(int, mlx5_comp_vectors_count(mdev), MLX5E_MAX_NUM_CHANNELS); } +/* The maximum WQE size can be retrieved by max_wqe_sz_sq in + * bytes units. Driver hardens the limitation to 1KB (16 + * WQEBBs), unless firmware capability is stricter. + */ +static inline u16 mlx5e_get_max_sq_wqebbs(struct mlx5_core_dev *mdev) +{ + return min_t(u16, MLX5_SEND_WQE_MAX_WQEBBS, + MLX5_CAP_GEN(mdev, max_wqe_sz_sq) / MLX5_SEND_WQE_BB); +} + +static inline u16 mlx5e_get_sw_max_sq_mpw_wqebbs(u16 max_sq_wqebbs) +{ +/* The return value will be multiplied by MLX5_SEND_WQEBB_NUM_DS. + * Since max_sq_wqebbs may be up to MLX5_SEND_WQE_MAX_WQEBBS == 16, + * see mlx5e_get_max_sq_wqebbs(), the multiplication (16 * 4 == 64) + * overflows the 6-bit DS field of Ctrl Segment. Use a bound lower + * than MLX5_SEND_WQE_MAX_WQEBBS to let a full-session WQE be + * cache-aligned. + */ +#if L1_CACHE_BYTES < 128 + return min_t(u16, max_sq_wqebbs, MLX5_SEND_WQE_MAX_WQEBBS - 1); +#else + return min_t(u16, max_sq_wqebbs, MLX5_SEND_WQE_MAX_WQEBBS - 2); +#endif +} + struct mlx5e_tx_wqe { struct mlx5_wqe_ctrl_seg ctrl; struct mlx5_wqe_eth_seg eth; @@ -377,6 +405,7 @@ enum { MLX5E_SQ_STATE_VLAN_NEED_L2_INLINE, MLX5E_SQ_STATE_PENDING_XSK_TX, MLX5E_SQ_STATE_PENDING_TLS_RX_RESYNC, + MLX5E_SQ_STATE_XDP_MULTIBUF, }; struct mlx5e_tx_mpwqe { @@ -427,12 +456,12 @@ struct mlx5e_txqsq { struct netdev_queue *txq; u32 sqn; u16 stop_room; + u16 max_sq_mpw_wqebbs; u8 min_inline_mode; struct device *pdev; __be32 mkey_be; unsigned long state; unsigned int hw_mtu; - struct hwtstamp_config *tstamp; struct mlx5_clock *clock; struct net_device *netdev; struct mlx5_core_dev *mdev; @@ -446,6 +475,7 @@ struct mlx5e_txqsq { struct work_struct recover_work; struct mlx5e_ptpsq *ptpsq; cqe_ts_to_ns ptp_cyc2time; + u16 max_sq_wqebbs; } ____cacheline_aligned_in_smp; struct mlx5e_dma_info { @@ -486,7 +516,7 @@ struct mlx5e_xdp_info { } frame; struct { struct mlx5e_rq *rq; - struct mlx5e_dma_info di; + struct page *page; } page; }; }; @@ -508,7 +538,7 @@ struct mlx5e_xdpsq; typedef int (*mlx5e_fp_xmit_xdp_frame_check)(struct mlx5e_xdpsq *); typedef bool (*mlx5e_fp_xmit_xdp_frame)(struct mlx5e_xdpsq *, struct mlx5e_xmit_data *, - struct mlx5e_xdp_info *, + struct skb_shared_info *, int); struct mlx5e_xdpsq { @@ -540,6 +570,8 @@ struct mlx5e_xdpsq { u32 sqn; struct device *pdev; __be32 mkey_be; + u16 stop_room; + u16 max_sq_mpw_wqebbs; u8 min_inline_mode; unsigned long state; unsigned int hw_mtu; @@ -547,6 +579,7 @@ struct mlx5e_xdpsq { /* control path */ struct mlx5_wq_ctrl wq_ctrl; struct mlx5e_channel *channel; + u16 max_sq_wqebbs; } ____cacheline_aligned_in_smp; struct mlx5e_ktls_resync_resp; @@ -575,6 +608,7 @@ struct mlx5e_icosq { /* control path */ struct mlx5_wq_ctrl wq_ctrl; struct mlx5e_channel *channel; + u16 max_sq_wqebbs; struct work_struct recover_work; } ____cacheline_aligned_in_smp; @@ -681,6 +715,7 @@ struct mlx5e_rq { u8 umr_in_progress; u8 umr_last_bulk; u8 umr_completed; + u8 min_wqe_bulk; struct mlx5e_shampo_hd *shampo; } mpwqe; }; @@ -876,9 +911,8 @@ struct mlx5e_trap; struct mlx5e_priv { /* priv data path fields - start */ + struct mlx5e_selq selq; struct mlx5e_txqsq **txq2sq; - int **channel_tc2realtxq; - int port_ptp_tc2realtxq[MLX5E_MAX_NUM_TC]; #ifdef CONFIG_MLX5_CORE_EN_DCB struct mlx5e_dcbx_dp dcbx_dp; #endif @@ -921,7 +955,6 @@ struct mlx5e_priv { u16 drop_rq_q_counter; struct notifier_block events_nb; struct notifier_block blocking_events_nb; - int num_tc_x_num_ch; struct udp_tunnel_nic_info nic_info; #ifdef CONFIG_MLX5_CORE_EN_DCB diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c index 66180ffb4606..08fd1370a8b0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c @@ -178,16 +178,28 @@ u8 mlx5e_mpwqe_get_log_num_strides(struct mlx5_core_dev *mdev, mlx5e_mpwqe_get_log_stride_size(mdev, params, xsk); } +u8 mlx5e_mpwqe_get_min_wqe_bulk(unsigned int wq_sz) +{ +#define UMR_WQE_BULK (2) + return min_t(unsigned int, UMR_WQE_BULK, wq_sz / 2 - 1); +} + u16 mlx5e_get_rq_headroom(struct mlx5_core_dev *mdev, struct mlx5e_params *params, struct mlx5e_xsk_param *xsk) { - bool is_linear_skb = (params->rq_wq_type == MLX5_WQ_TYPE_CYCLIC) ? - mlx5e_rx_is_linear_skb(params, xsk) : - mlx5e_rx_mpwqe_is_linear_skb(mdev, params, xsk); + u16 linear_headroom = mlx5e_get_linear_rq_headroom(params, xsk); - return is_linear_skb || params->packet_merge.type == MLX5E_PACKET_MERGE_SHAMPO ? - mlx5e_get_linear_rq_headroom(params, xsk) : 0; + if (params->rq_wq_type == MLX5_WQ_TYPE_CYCLIC) + return linear_headroom; + + if (mlx5e_rx_mpwqe_is_linear_skb(mdev, params, xsk)) + return linear_headroom; + + if (params->packet_merge.type == MLX5E_PACKET_MERGE_SHAMPO) + return linear_headroom; + + return 0; } u16 mlx5e_calc_sq_stop_room(struct mlx5_core_dev *mdev, struct mlx5e_params *params) @@ -196,13 +208,13 @@ u16 mlx5e_calc_sq_stop_room(struct mlx5_core_dev *mdev, struct mlx5e_params *par u16 stop_room; stop_room = mlx5e_tls_get_stop_room(mdev, params); - stop_room += mlx5e_stop_room_for_wqe(MLX5_SEND_WQE_MAX_WQEBBS); + stop_room += mlx5e_stop_room_for_max_wqe(mdev); if (is_mpwqe) /* A MPWQE can take up to the maximum-sized WQE + all the normal * stop room can be taken if a new packet breaks the active * MPWQE session and allocates its WQEs right away. */ - stop_room += mlx5e_stop_room_for_wqe(MLX5_SEND_WQE_MAX_WQEBBS); + stop_room += mlx5e_stop_room_for_max_wqe(mdev); return stop_room; } @@ -359,12 +371,13 @@ void mlx5e_build_rq_params(struct mlx5_core_dev *mdev, { /* Prefer Striding RQ, unless any of the following holds: * - Striding RQ configuration is not possible/supported. - * - Slow PCI heuristic. + * - CQE compression is ON, and stride_index mini_cqe layout is not supported. * - Legacy RQ would use linear SKB while Striding RQ would use non-linear. * * No XSK params: checking the availability of striding RQ in general. */ - if (!slow_pci_heuristic(mdev) && + if ((!MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS) || + MLX5_CAP_GEN(mdev, mini_cqe_resp_stride_index)) && mlx5e_striding_rq_possible(mdev, params) && (mlx5e_rx_mpwqe_is_linear_skb(mdev, params, NULL) || !mlx5e_rx_is_linear_skb(params, NULL))) @@ -385,16 +398,29 @@ void mlx5e_build_create_cq_param(struct mlx5e_create_cq_param *ccp, struct mlx5e }; } +static int mlx5e_max_nonlinear_mtu(int first_frag_size, int frag_size, bool xdp) +{ + if (xdp) + /* XDP requires all fragments to be of the same size. */ + return first_frag_size + (MLX5E_MAX_RX_FRAGS - 1) * frag_size; + + /* Optimization for small packets: the last fragment is bigger than the others. */ + return first_frag_size + (MLX5E_MAX_RX_FRAGS - 2) * frag_size + PAGE_SIZE; +} + #define DEFAULT_FRAG_SIZE (2048) -static void mlx5e_build_rq_frags_info(struct mlx5_core_dev *mdev, - struct mlx5e_params *params, - struct mlx5e_xsk_param *xsk, - struct mlx5e_rq_frags_info *info) +static int mlx5e_build_rq_frags_info(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, + struct mlx5e_xsk_param *xsk, + struct mlx5e_rq_frags_info *info) { u32 byte_count = MLX5E_SW2HW_MTU(params, params->sw_mtu); int frag_size_max = DEFAULT_FRAG_SIZE; + int first_frag_size_max; u32 buf_size = 0; + u16 headroom; + int max_mtu; int i; if (mlx5_fpga_is_ipsec_device(mdev)) @@ -413,21 +439,48 @@ static void mlx5e_build_rq_frags_info(struct mlx5_core_dev *mdev, goto out; } - if (byte_count > PAGE_SIZE + - (MLX5E_MAX_RX_FRAGS - 1) * frag_size_max) + headroom = mlx5e_get_linear_rq_headroom(params, xsk); + first_frag_size_max = SKB_WITH_OVERHEAD(frag_size_max - headroom); + + max_mtu = mlx5e_max_nonlinear_mtu(first_frag_size_max, frag_size_max, + params->xdp_prog); + if (byte_count > max_mtu || params->xdp_prog) { frag_size_max = PAGE_SIZE; + first_frag_size_max = SKB_WITH_OVERHEAD(frag_size_max - headroom); + + max_mtu = mlx5e_max_nonlinear_mtu(first_frag_size_max, frag_size_max, + params->xdp_prog); + if (byte_count > max_mtu) { + mlx5_core_err(mdev, "MTU %u is too big for non-linear legacy RQ (max %d)\n", + params->sw_mtu, max_mtu); + return -EINVAL; + } + } i = 0; while (buf_size < byte_count) { int frag_size = byte_count - buf_size; - if (i < MLX5E_MAX_RX_FRAGS - 1) + if (i == 0) + frag_size = min(frag_size, first_frag_size_max); + else if (i < MLX5E_MAX_RX_FRAGS - 1) frag_size = min(frag_size, frag_size_max); info->arr[i].frag_size = frag_size; - info->arr[i].frag_stride = roundup_pow_of_two(frag_size); - buf_size += frag_size; + + if (params->xdp_prog) { + /* XDP multi buffer expects fragments of the same size. */ + info->arr[i].frag_stride = frag_size_max; + } else { + if (i == 0) { + /* Ensure that headroom and tailroom are included. */ + frag_size += headroom; + frag_size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + } + info->arr[i].frag_stride = roundup_pow_of_two(frag_size); + } + i++; } info->num_frags = i; @@ -437,6 +490,8 @@ static void mlx5e_build_rq_frags_info(struct mlx5_core_dev *mdev, out: info->wqe_bulk = max_t(u8, info->wqe_bulk, 8); info->log_num_frags = order_base_2(info->num_frags); + + return 0; } static u8 mlx5e_get_rqwq_log_stride(u8 wq_type, int ndsegs) @@ -533,6 +588,7 @@ int mlx5e_build_rq_param(struct mlx5_core_dev *mdev, void *rqc = param->rqc; void *wq = MLX5_ADDR_OF(rqc, rqc, wq); int ndsegs = 1; + int err; switch (params->rq_wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: { @@ -572,7 +628,9 @@ int mlx5e_build_rq_param(struct mlx5_core_dev *mdev, } default: /* MLX5_WQ_TYPE_CYCLIC */ MLX5_SET(wq, wq, log_wq_sz, params->log_rq_mtu_frames); - mlx5e_build_rq_frags_info(mdev, params, xsk, ¶m->frags_info); + err = mlx5e_build_rq_frags_info(mdev, params, xsk, ¶m->frags_info); + if (err) + return err; ndsegs = param->frags_info.num_frags; } @@ -717,7 +775,7 @@ static u32 mlx5e_shampo_icosq_sz(struct mlx5_core_dev *mdev, int wq_size = BIT(MLX5_GET(wq, wqc, log_wq_sz)); u32 wqebbs; - max_klm_per_umr = MLX5E_MAX_KLM_PER_WQE; + max_klm_per_umr = MLX5E_MAX_KLM_PER_WQE(mdev); max_hd_per_wqe = mlx5e_shampo_hd_per_wqe(mdev, params, rq_param); max_num_of_umr_per_wqe = max_hd_per_wqe / max_klm_per_umr; rest = max_hd_per_wqe % max_klm_per_umr; @@ -774,10 +832,10 @@ static void mlx5e_build_async_icosq_param(struct mlx5_core_dev *mdev, void *wq = MLX5_ADDR_OF(sqc, sqc, wq); mlx5e_build_sq_param_common(mdev, param); - param->stop_room = mlx5e_stop_room_for_wqe(1); /* for XSK NOP */ + param->stop_room = mlx5e_stop_room_for_wqe(mdev, 1); /* for XSK NOP */ param->is_tls = mlx5e_accel_is_ktls_rx(mdev); if (param->is_tls) - param->stop_room += mlx5e_stop_room_for_wqe(1); /* for TLS RX resync NOP */ + param->stop_room += mlx5e_stop_room_for_wqe(mdev, 1); /* for TLS RX resync NOP */ MLX5_SET(sqc, sqc, reg_umr, MLX5_CAP_ETH(mdev, reg_umr_sq)); MLX5_SET(wq, wq, log_wq_sz, log_wq_size); mlx5e_build_ico_cq_param(mdev, log_wq_size, ¶m->cqp); @@ -785,6 +843,7 @@ static void mlx5e_build_async_icosq_param(struct mlx5_core_dev *mdev, void mlx5e_build_xdpsq_param(struct mlx5_core_dev *mdev, struct mlx5e_params *params, + struct mlx5e_xsk_param *xsk, struct mlx5e_sq_param *param) { void *sqc = param->sqc; @@ -793,6 +852,7 @@ void mlx5e_build_xdpsq_param(struct mlx5_core_dev *mdev, mlx5e_build_sq_param_common(mdev, param); MLX5_SET(wq, wq, log_wq_sz, params->log_sq_size); param->is_mpw = MLX5E_GET_PFLAG(params, MLX5E_PFLAG_XDP_TX_MPWQE); + param->is_xdp_mb = !mlx5e_rx_is_linear_skb(params, xsk); mlx5e_build_tx_cq_param(mdev, params, ¶m->cqp); } @@ -812,7 +872,7 @@ int mlx5e_build_channel_param(struct mlx5_core_dev *mdev, async_icosq_log_wq_sz = mlx5e_build_async_icosq_log_wq_sz(mdev); mlx5e_build_sq_param(mdev, params, &cparam->txq_sq); - mlx5e_build_xdpsq_param(mdev, params, &cparam->xdp_sq); + mlx5e_build_xdpsq_param(mdev, params, NULL, &cparam->xdp_sq); mlx5e_build_icosq_param(mdev, icosq_log_wq_sz, &cparam->icosq); mlx5e_build_async_icosq_param(mdev, async_icosq_log_wq_sz, &cparam->async_icosq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.h b/drivers/net/ethernet/mellanox/mlx5/core/en/params.h index 433e6967692d..f5c46e78eebc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.h @@ -31,6 +31,7 @@ struct mlx5e_sq_param { struct mlx5_wq_param wq; bool is_mpw; bool is_tls; + bool is_xdp_mb; u16 stop_room; }; @@ -129,6 +130,7 @@ u8 mlx5e_mpwqe_get_log_stride_size(struct mlx5_core_dev *mdev, u8 mlx5e_mpwqe_get_log_num_strides(struct mlx5_core_dev *mdev, struct mlx5e_params *params, struct mlx5e_xsk_param *xsk); +u8 mlx5e_mpwqe_get_min_wqe_bulk(unsigned int wq_sz); u16 mlx5e_get_rq_headroom(struct mlx5_core_dev *mdev, struct mlx5e_params *params, struct mlx5e_xsk_param *xsk); @@ -154,6 +156,7 @@ void mlx5e_build_tx_cq_param(struct mlx5_core_dev *mdev, struct mlx5e_cq_param *param); void mlx5e_build_xdpsq_param(struct mlx5_core_dev *mdev, struct mlx5e_params *params, + struct mlx5e_xsk_param *xsk, struct mlx5e_sq_param *param); int mlx5e_build_channel_param(struct mlx5_core_dev *mdev, struct mlx5e_params *params, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c index 82baafd3c00c..335b20b6383b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c @@ -195,7 +195,6 @@ static int mlx5e_ptp_alloc_txqsq(struct mlx5e_ptp *c, int txq_ix, int node; sq->pdev = c->pdev; - sq->tstamp = c->tstamp; sq->clock = &mdev->clock; sq->mkey_be = c->mkey_be; sq->netdev = c->netdev; @@ -449,7 +448,7 @@ static void mlx5e_ptp_build_sq_param(struct mlx5_core_dev *mdev, wq = MLX5_ADDR_OF(sqc, sqc, wq); MLX5_SET(wq, wq, log_wq_sz, params->log_sq_size); - param->stop_room = mlx5e_stop_room_for_wqe(MLX5_SEND_WQE_MAX_WQEBBS); + param->stop_room = mlx5e_stop_room_for_max_wqe(mdev); mlx5e_build_tx_cq_param(mdev, params, ¶m->cqp); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c b/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c index c1e07496c89c..9db677e9ca9c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c @@ -50,7 +50,6 @@ static int mlx5e_find_unused_qos_qid(struct mlx5e_priv *priv) struct mlx5e_qos_node { struct hlist_node hnode; - struct rcu_head rcu; struct mlx5e_qos_node *parent; u64 rate; u32 bw_share; @@ -132,7 +131,11 @@ static void mlx5e_sw_node_delete(struct mlx5e_priv *priv, struct mlx5e_qos_node __clear_bit(node->qid, priv->htb.qos_used_qids); mlx5e_update_tx_netdev_queues(priv); } - kfree_rcu(node, rcu); + /* Make sure this qid is no longer selected by mlx5e_select_queue, so + * that mlx5e_reactivate_qos_sq can safely restart the netdev TX queue. + */ + synchronize_net(); + kfree(node); } /* TX datapath API */ @@ -273,10 +276,18 @@ static int mlx5e_open_qos_sq(struct mlx5e_priv *priv, struct mlx5e_channels *chs static void mlx5e_activate_qos_sq(struct mlx5e_priv *priv, struct mlx5e_qos_node *node) { struct mlx5e_txqsq *sq; + u16 qid; sq = mlx5e_get_qos_sq(priv, node->qid); - WRITE_ONCE(priv->txq2sq[mlx5e_qid_from_qos(&priv->channels, node->qid)], sq); + qid = mlx5e_qid_from_qos(&priv->channels, node->qid); + + /* If it's a new queue, it will be marked as started at this point. + * Stop it before updating txq2sq. + */ + mlx5e_tx_disable_queue(netdev_get_tx_queue(priv->netdev, qid)); + + priv->txq2sq[qid] = sq; /* Make the change to txq2sq visible before the queue is started. * As mlx5e_xmit runs under a spinlock, there is an implicit ACQUIRE, @@ -299,8 +310,13 @@ static void mlx5e_deactivate_qos_sq(struct mlx5e_priv *priv, u16 qid) qos_dbg(priv->mdev, "Deactivate QoS SQ qid %u\n", qid); mlx5e_deactivate_txqsq(sq); - /* The queue is disabled, no synchronization with datapath is needed. */ priv->txq2sq[mlx5e_qid_from_qos(&priv->channels, qid)] = NULL; + + /* Make the change to txq2sq visible before the queue is started again. + * As mlx5e_xmit runs under a spinlock, there is an implicit ACQUIRE, + * which pairs with this barrier. + */ + smp_wmb(); } static void mlx5e_close_qos_sq(struct mlx5e_priv *priv, u16 qid) @@ -485,9 +501,11 @@ int mlx5e_htb_root_add(struct mlx5e_priv *priv, u16 htb_maj_id, u16 htb_defcls, opened = test_bit(MLX5E_STATE_OPENED, &priv->state); if (opened) { + mlx5e_selq_prepare(&priv->selq, &priv->channels.params, true); + err = mlx5e_qos_alloc_queues(priv, &priv->channels); if (err) - return err; + goto err_cancel_selq; } root = mlx5e_sw_node_create_root(priv); @@ -508,6 +526,9 @@ int mlx5e_htb_root_add(struct mlx5e_priv *priv, u16 htb_maj_id, u16 htb_defcls, */ smp_store_release(&priv->htb.maj_id, htb_maj_id); + if (opened) + mlx5e_selq_apply(&priv->selq); + return 0; err_sw_node_delete: @@ -516,6 +537,8 @@ int mlx5e_htb_root_add(struct mlx5e_priv *priv, u16 htb_maj_id, u16 htb_defcls, err_free_queues: if (opened) mlx5e_qos_close_all_queues(&priv->channels); +err_cancel_selq: + mlx5e_selq_cancel(&priv->selq); return err; } @@ -526,8 +549,15 @@ int mlx5e_htb_root_del(struct mlx5e_priv *priv) qos_dbg(priv->mdev, "TC_HTB_DESTROY\n"); + /* Wait until real_num_tx_queues is updated for mlx5e_select_queue, + * so that we can safely switch to its non-HTB non-PTP fastpath. + */ + synchronize_net(); + + mlx5e_selq_prepare(&priv->selq, &priv->channels.params, false); + mlx5e_selq_apply(&priv->selq); + WRITE_ONCE(priv->htb.maj_id, 0); - synchronize_rcu(); /* Sync with mlx5e_select_htb_queue and TX data path. */ root = mlx5e_sw_node_find(priv, MLX5E_HTB_CLASSID_ROOT); if (!root) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/qos.h b/drivers/net/ethernet/mellanox/mlx5/core/en/qos.h index b7558907ba20..5d9bd91d86c2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/qos.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/qos.h @@ -18,7 +18,6 @@ int mlx5e_qos_cur_leaf_nodes(struct mlx5e_priv *priv); /* TX datapath API */ int mlx5e_get_txq_by_classid(struct mlx5e_priv *priv, u16 classid); -struct mlx5e_txqsq *mlx5e_get_sq(struct mlx5e_priv *priv, int qid); /* SQ lifecycle */ int mlx5e_qos_open_queues(struct mlx5e_priv *priv, struct mlx5e_channels *chs); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c index 0991345c4ae5..86fa0bdbee36 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c @@ -263,14 +263,14 @@ int mlx5e_rep_tc_init(struct mlx5e_rep_priv *rpriv) INIT_LIST_HEAD(&uplink_priv->unready_flows); /* init shared tc flow table */ - err = mlx5e_tc_esw_init(&uplink_priv->tc_ht); + err = mlx5e_tc_esw_init(uplink_priv); return err; } void mlx5e_rep_tc_cleanup(struct mlx5e_rep_priv *rpriv) { /* delete shared tc flow table */ - mlx5e_tc_esw_cleanup(&rpriv->uplink_priv.tc_ht); + mlx5e_tc_esw_cleanup(&rpriv->uplink_priv); mutex_destroy(&rpriv->uplink_priv.unready_flows_lock); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c index c1cdd8c2e37a..7f93426b88b3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c @@ -442,7 +442,7 @@ int mlx5e_rss_packet_merge_set_param(struct mlx5e_rss *rss, goto inner_tir; err = mlx5e_tir_modify(tir, builder); if (err) { - mlx5e_rss_warn(rss->mdev, "Failed to update LRO state of indirect TIR %#x for traffic type %d: err = %d\n", + mlx5e_rss_warn(rss->mdev, "Failed to update packet merge state of indirect TIR %#x for traffic type %d: err = %d\n", mlx5e_tir_get_tirn(tir), tt, err); if (!final_err) final_err = err; @@ -457,7 +457,7 @@ int mlx5e_rss_packet_merge_set_param(struct mlx5e_rss *rss, continue; err = mlx5e_tir_modify(tir, builder); if (err) { - mlx5e_rss_warn(rss->mdev, "Failed to update LRO state of inner indirect TIR %#x for traffic type %d: err = %d\n", + mlx5e_rss_warn(rss->mdev, "Failed to update packet merge state of inner indirect TIR %#x for traffic type %d: err = %d\n", mlx5e_tir_get_tirn(tir), tt, err); if (!final_err) final_err = err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/selq.c b/drivers/net/ethernet/mellanox/mlx5/core/en/selq.c new file mode 100644 index 000000000000..d98a277eb7f8 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/selq.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#include "selq.h" +#include +#include +#include +#include "en.h" +#include "en/ptp.h" + +struct mlx5e_selq_params { + unsigned int num_regular_queues; + unsigned int num_channels; + unsigned int num_tcs; + union { + u8 is_special_queues; + struct { + bool is_htb : 1; + bool is_ptp : 1; + }; + }; +}; + +int mlx5e_selq_init(struct mlx5e_selq *selq, struct mutex *state_lock) +{ + struct mlx5e_selq_params *init_params; + + selq->state_lock = state_lock; + + selq->standby = kvzalloc(sizeof(*selq->standby), GFP_KERNEL); + if (!selq->standby) + return -ENOMEM; + + init_params = kvzalloc(sizeof(*selq->active), GFP_KERNEL); + if (!init_params) { + kvfree(selq->standby); + selq->standby = NULL; + return -ENOMEM; + } + /* Assign dummy values, so that mlx5e_select_queue won't crash. */ + *init_params = (struct mlx5e_selq_params) { + .num_regular_queues = 1, + .num_channels = 1, + .num_tcs = 1, + .is_htb = false, + .is_ptp = false, + }; + rcu_assign_pointer(selq->active, init_params); + + return 0; +} + +void mlx5e_selq_cleanup(struct mlx5e_selq *selq) +{ + WARN_ON_ONCE(selq->is_prepared); + + kvfree(selq->standby); + selq->standby = NULL; + selq->is_prepared = true; + + mlx5e_selq_apply(selq); + + kvfree(selq->standby); + selq->standby = NULL; +} + +void mlx5e_selq_prepare(struct mlx5e_selq *selq, struct mlx5e_params *params, bool htb) +{ + lockdep_assert_held(selq->state_lock); + WARN_ON_ONCE(selq->is_prepared); + + selq->is_prepared = true; + + selq->standby->num_channels = params->num_channels; + selq->standby->num_tcs = mlx5e_get_dcb_num_tc(params); + selq->standby->num_regular_queues = + selq->standby->num_channels * selq->standby->num_tcs; + selq->standby->is_htb = htb; + selq->standby->is_ptp = MLX5E_GET_PFLAG(params, MLX5E_PFLAG_TX_PORT_TS); +} + +void mlx5e_selq_apply(struct mlx5e_selq *selq) +{ + struct mlx5e_selq_params *old_params; + + WARN_ON_ONCE(!selq->is_prepared); + + selq->is_prepared = false; + + old_params = rcu_replace_pointer(selq->active, selq->standby, + lockdep_is_held(selq->state_lock)); + synchronize_net(); /* Wait until ndo_select_queue starts emitting correct values. */ + selq->standby = old_params; +} + +void mlx5e_selq_cancel(struct mlx5e_selq *selq) +{ + lockdep_assert_held(selq->state_lock); + WARN_ON_ONCE(!selq->is_prepared); + + selq->is_prepared = false; +} + +#ifdef CONFIG_MLX5_CORE_EN_DCB +static int mlx5e_get_dscp_up(struct mlx5e_priv *priv, struct sk_buff *skb) +{ + int dscp_cp = 0; + + if (skb->protocol == htons(ETH_P_IP)) + dscp_cp = ipv4_get_dsfield(ip_hdr(skb)) >> 2; + else if (skb->protocol == htons(ETH_P_IPV6)) + dscp_cp = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2; + + return priv->dcbx_dp.dscp2prio[dscp_cp]; +} +#endif + +static int mlx5e_get_up(struct mlx5e_priv *priv, struct sk_buff *skb) +{ +#ifdef CONFIG_MLX5_CORE_EN_DCB + if (READ_ONCE(priv->dcbx_dp.trust_state) == MLX5_QPTS_TRUST_DSCP) + return mlx5e_get_dscp_up(priv, skb); +#endif + if (skb_vlan_tag_present(skb)) + return skb_vlan_tag_get_prio(skb); + return 0; +} + +static u16 mlx5e_select_ptpsq(struct net_device *dev, struct sk_buff *skb, + struct mlx5e_selq_params *selq) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + int up; + + up = selq->num_tcs > 1 ? mlx5e_get_up(priv, skb) : 0; + + return selq->num_regular_queues + up; +} + +static int mlx5e_select_htb_queue(struct mlx5e_priv *priv, struct sk_buff *skb) +{ + u16 classid; + + /* Order maj_id before defcls - pairs with mlx5e_htb_root_add. */ + if ((TC_H_MAJ(skb->priority) >> 16) == smp_load_acquire(&priv->htb.maj_id)) + classid = TC_H_MIN(skb->priority); + else + classid = READ_ONCE(priv->htb.defcls); + + if (!classid) + return 0; + + return mlx5e_get_txq_by_classid(priv, classid); +} + +u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5e_selq_params *selq; + int txq_ix, up; + + selq = rcu_dereference_bh(priv->selq.active); + + /* This is a workaround needed only for the mlx5e_netdev_change_profile + * flow that zeroes out the whole priv without unregistering the netdev + * and without preventing ndo_select_queue from being called. + */ + if (unlikely(!selq)) + return 0; + + if (likely(!selq->is_special_queues)) { + /* No special queues, netdev_pick_tx returns one of the regular ones. */ + + txq_ix = netdev_pick_tx(dev, skb, NULL); + + if (selq->num_tcs <= 1) + return txq_ix; + + up = mlx5e_get_up(priv, skb); + + /* Normalize any picked txq_ix to [0, num_channels), + * So we can return a txq_ix that matches the channel and + * packet UP. + */ + return mlx5e_txq_to_ch_ix(txq_ix, selq->num_channels) + + up * selq->num_channels; + } + + if (unlikely(selq->is_htb)) { + /* num_tcs == 1, shortcut for PTP */ + + txq_ix = mlx5e_select_htb_queue(priv, skb); + if (txq_ix > 0) + return txq_ix; + + if (unlikely(selq->is_ptp && mlx5e_use_ptpsq(skb))) + return selq->num_channels; + + txq_ix = netdev_pick_tx(dev, skb, NULL); + + /* Fix netdev_pick_tx() not to choose ptp_channel and HTB txqs. + * If they are selected, switch to regular queues. + * Driver to select these queues only at mlx5e_select_ptpsq() + * and mlx5e_select_htb_queue(). + */ + return mlx5e_txq_to_ch_ix_htb(txq_ix, selq->num_channels); + } + + /* PTP is enabled */ + + if (mlx5e_use_ptpsq(skb)) + return mlx5e_select_ptpsq(dev, skb, selq); + + txq_ix = netdev_pick_tx(dev, skb, NULL); + + /* Normalize any picked txq_ix to [0, num_channels). Queues in range + * [0, num_regular_queues) will be mapped to the corresponding channel + * index, so that we can apply the packet's UP (if num_tcs > 1). + * If netdev_pick_tx() picks ptp_channel, switch to a regular queue, + * because driver should select the PTP only at mlx5e_select_ptpsq(). + */ + txq_ix = mlx5e_txq_to_ch_ix(txq_ix, selq->num_channels); + + if (selq->num_tcs <= 1) + return txq_ix; + + up = mlx5e_get_up(priv, skb); + + return txq_ix + up * selq->num_channels; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/selq.h b/drivers/net/ethernet/mellanox/mlx5/core/en/selq.h new file mode 100644 index 000000000000..6c070141d8f1 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/selq.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#ifndef __MLX5_EN_SELQ_H__ +#define __MLX5_EN_SELQ_H__ + +#include + +struct mlx5e_selq_params; + +struct mlx5e_selq { + struct mlx5e_selq_params __rcu *active; + struct mlx5e_selq_params *standby; + struct mutex *state_lock; /* points to priv->state_lock */ + bool is_prepared; +}; + +struct mlx5e_params; +struct net_device; +struct sk_buff; + +int mlx5e_selq_init(struct mlx5e_selq *selq, struct mutex *state_lock); +void mlx5e_selq_cleanup(struct mlx5e_selq *selq); +void mlx5e_selq_prepare(struct mlx5e_selq *selq, struct mlx5e_params *params, bool htb); +void mlx5e_selq_apply(struct mlx5e_selq *selq); +void mlx5e_selq_cancel(struct mlx5e_selq *selq); + +static inline u16 mlx5e_txq_to_ch_ix(u16 txq, u16 num_channels) +{ + while (unlikely(txq >= num_channels)) + txq -= num_channels; + return txq; +} + +static inline u16 mlx5e_txq_to_ch_ix_htb(u16 txq, u16 num_channels) +{ + if (unlikely(txq >= num_channels)) { + if (unlikely(txq >= num_channels << 3)) + txq %= num_channels; + else + do + txq -= num_channels; + while (txq >= num_channels); + } + return txq; +} + +u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev); + +#endif /* __MLX5_EN_SELQ_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/accept.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/accept.c index b0de6b999675..21aab96357b5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/accept.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/accept.c @@ -7,7 +7,8 @@ static bool tc_act_can_offload_accept(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { return true; } @@ -18,9 +19,8 @@ tc_act_parse_accept(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_priv *priv, struct mlx5_flow_attr *attr) { - attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | - MLX5_FLOW_CONTEXT_ACTION_COUNT; - attr->flags |= MLX5_ESW_ATTR_FLAG_ACCEPT; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + attr->flags |= MLX5_ATTR_FLAG_ACCEPT; return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.c index e600924e30ea..af37a8d247a1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.c @@ -2,6 +2,7 @@ // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. #include "act.h" +#include "en/tc/post_act.h" #include "en/tc_priv.h" #include "mlx5_core.h" @@ -34,6 +35,13 @@ static struct mlx5e_tc_act *tc_acts_fdb[NUM_FLOW_ACTIONS] = { NULL, /* FLOW_ACTION_CT_METADATA, */ &mlx5e_tc_act_mpls_push, &mlx5e_tc_act_mpls_pop, + NULL, /* FLOW_ACTION_MPLS_MANGLE, */ + NULL, /* FLOW_ACTION_GATE, */ + NULL, /* FLOW_ACTION_PPPOE_PUSH, */ + NULL, /* FLOW_ACTION_JUMP, */ + NULL, /* FLOW_ACTION_PIPE, */ + &mlx5e_tc_act_vlan, + &mlx5e_tc_act_vlan, }; /* Must be aligned with enum flow_action_id. */ @@ -101,3 +109,75 @@ mlx5e_tc_act_init_parse_state(struct mlx5e_tc_act_parse_state *parse_state, parse_state->num_actions = flow_action->num_entries; parse_state->extack = extack; } + +void +mlx5e_tc_act_reorder_flow_actions(struct flow_action *flow_action, + struct mlx5e_tc_flow_action *flow_action_reorder) +{ + struct flow_action_entry *act; + int i, j = 0; + + flow_action_for_each(i, act, flow_action) { + /* Add CT action to be first. */ + if (act->id == FLOW_ACTION_CT) + flow_action_reorder->entries[j++] = act; + } + + flow_action_for_each(i, act, flow_action) { + if (act->id == FLOW_ACTION_CT) + continue; + flow_action_reorder->entries[j++] = act; + } +} + +int +mlx5e_tc_act_post_parse(struct mlx5e_tc_act_parse_state *parse_state, + struct flow_action *flow_action, + struct mlx5_flow_attr *attr, + enum mlx5_flow_namespace_type ns_type) +{ + struct flow_action_entry *act; + struct mlx5e_tc_act *tc_act; + struct mlx5e_priv *priv; + int err = 0, i; + + priv = parse_state->flow->priv; + + flow_action_for_each(i, act, flow_action) { + tc_act = mlx5e_tc_act_get(act->id, ns_type); + if (!tc_act || !tc_act->post_parse || + !tc_act->can_offload(parse_state, act, i, attr)) + continue; + + err = tc_act->post_parse(parse_state, priv, attr); + if (err) + goto out; + } + +out: + return err; +} + +int +mlx5e_tc_act_set_next_post_act(struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr, + struct mlx5_flow_attr *next_attr) +{ + struct mlx5_core_dev *mdev = flow->priv->mdev; + struct mlx5e_tc_mod_hdr_acts *mod_acts; + int err; + + mod_acts = &attr->parse_attr->mod_hdr_acts; + + /* Set handle on current post act rule to next post act rule. */ + err = mlx5e_tc_post_act_set_handle(mdev, next_attr->post_act_handle, mod_acts); + if (err) { + mlx5_core_warn(mdev, "Failed setting post action handle"); + return err; + } + + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | + MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; + + return 0; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h index 9cc844bd00f5..f34714c5ddd4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h @@ -16,14 +16,17 @@ struct mlx5e_tc_act_parse_state { unsigned int num_actions; struct mlx5e_tc_flow *flow; struct netlink_ext_ack *extack; + u32 actions; + bool ct; bool ct_clear; bool encap; bool decap; bool mpls_push; + bool eth_push; + bool eth_pop; bool ptype_host; const struct ip_tunnel_info *tun_info; struct mlx5e_mpls_info mpls_info; - struct pedit_headers_action hdrs[__PEDIT_CMD_MAX]; int ifindexes[MLX5_MAX_FLOW_FWD_VPORTS]; int if_count; struct mlx5_tc_ct_priv *ct_priv; @@ -32,7 +35,8 @@ struct mlx5e_tc_act_parse_state { struct mlx5e_tc_act { bool (*can_offload)(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index); + int act_index, + struct mlx5_flow_attr *attr); int (*parse_action)(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, @@ -42,6 +46,15 @@ struct mlx5e_tc_act { int (*post_parse)(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_priv *priv, struct mlx5_flow_attr *attr); + + bool (*is_multi_table_act)(struct mlx5e_priv *priv, + const struct flow_action_entry *act, + struct mlx5_flow_attr *attr); +}; + +struct mlx5e_tc_flow_action { + unsigned int num_entries; + struct flow_action_entry **entries; }; extern struct mlx5e_tc_act mlx5e_tc_act_drop; @@ -74,4 +87,19 @@ mlx5e_tc_act_init_parse_state(struct mlx5e_tc_act_parse_state *parse_state, struct flow_action *flow_action, struct netlink_ext_ack *extack); +void +mlx5e_tc_act_reorder_flow_actions(struct flow_action *flow_action, + struct mlx5e_tc_flow_action *flow_action_reorder); + +int +mlx5e_tc_act_post_parse(struct mlx5e_tc_act_parse_state *parse_state, + struct flow_action *flow_action, + struct mlx5_flow_attr *attr, + enum mlx5_flow_namespace_type ns_type); + +int +mlx5e_tc_act_set_next_post_act(struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr, + struct mlx5_flow_attr *next_attr); + #endif /* __MLX5_EN_TC_ACT_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/csum.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/csum.c index 29920ef0180a..c0f08ae6a57f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/csum.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/csum.c @@ -38,11 +38,12 @@ csum_offload_supported(struct mlx5e_priv *priv, static bool tc_act_can_offload_csum(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { struct mlx5e_tc_flow *flow = parse_state->flow; - return csum_offload_supported(flow->priv, flow->attr->action, + return csum_offload_supported(flow->priv, attr->action, act->csum_flags, parse_state->extack); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ct.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ct.c index 58cc33f1363d..b9d38fe807df 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ct.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ct.c @@ -8,13 +8,14 @@ static bool tc_act_can_offload_ct(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { + bool clear_action = act->ct.action & TCA_CT_ACT_CLEAR; struct netlink_ext_ack *extack = parse_state->extack; - if (flow_flag_test(parse_state->flow, SAMPLE)) { - NL_SET_ERR_MSG_MOD(extack, - "Sample action with connection tracking is not supported"); + if (parse_state->ct && !clear_action) { + NL_SET_ERR_MSG_MOD(extack, "Multiple CT actions are not supported"); return false; } @@ -40,18 +41,34 @@ tc_act_parse_ct(struct mlx5e_tc_act_parse_state *parse_state, if (err) return err; - flow_flag_set(parse_state->flow, CT); if (mlx5e_is_eswitch_flow(parse_state->flow)) attr->esw_attr->split_count = attr->esw_attr->out_count; + if (!clear_action) { + attr->flags |= MLX5_ATTR_FLAG_CT; + flow_flag_set(parse_state->flow, CT); + parse_state->ct = true; + } parse_state->ct_clear = clear_action; return 0; } +static bool +tc_act_is_multi_table_act_ct(struct mlx5e_priv *priv, + const struct flow_action_entry *act, + struct mlx5_flow_attr *attr) +{ + if (act->ct.action & TCA_CT_ACT_CLEAR) + return false; + + return true; +} + struct mlx5e_tc_act mlx5e_tc_act_ct = { .can_offload = tc_act_can_offload_ct, .parse_action = tc_act_parse_ct, + .is_multi_table_act = tc_act_is_multi_table_act_ct, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/drop.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/drop.c index 2e29a23bed12..dd025a95c439 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/drop.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/drop.c @@ -7,7 +7,8 @@ static bool tc_act_can_offload_drop(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { return true; } @@ -18,8 +19,7 @@ tc_act_parse_drop(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_priv *priv, struct mlx5_flow_attr *attr) { - attr->action |= MLX5_FLOW_CONTEXT_ACTION_DROP | - MLX5_FLOW_CONTEXT_ACTION_COUNT; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_DROP; return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c index f44515061228..4726bcb46eec 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c @@ -8,6 +8,7 @@ static int validate_goto_chain(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr, const struct flow_action_entry *act, struct netlink_ext_ack *extack) { @@ -32,7 +33,7 @@ validate_goto_chain(struct mlx5e_priv *priv, } if (!mlx5_chains_backwards_supported(chains) && - dest_chain <= flow->attr->chain) { + dest_chain <= attr->chain) { NL_SET_ERR_MSG_MOD(extack, "Goto lower numbered chain isn't supported"); return -EOPNOTSUPP; } @@ -43,8 +44,8 @@ validate_goto_chain(struct mlx5e_priv *priv, return -EOPNOTSUPP; } - if (flow->attr->action & (MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT | - MLX5_FLOW_CONTEXT_ACTION_DECAP) && + if (attr->action & (MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT | + MLX5_FLOW_CONTEXT_ACTION_DECAP) && !reformat_and_fwd) { NL_SET_ERR_MSG_MOD(extack, "Goto chain is not allowed if action has reformat or decap"); @@ -57,12 +58,13 @@ validate_goto_chain(struct mlx5e_priv *priv, static bool tc_act_can_offload_goto(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { struct netlink_ext_ack *extack = parse_state->extack; struct mlx5e_tc_flow *flow = parse_state->flow; - if (validate_goto_chain(flow->priv, flow, act, extack)) + if (validate_goto_chain(flow->priv, flow, attr, act, extack)) return false; return true; @@ -74,8 +76,7 @@ tc_act_parse_goto(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_priv *priv, struct mlx5_flow_attr *attr) { - attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | - MLX5_FLOW_CONTEXT_ACTION_COUNT; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; attr->dest_chain = act->chain_index; return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mark.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mark.c index d775c3d9edf3..e8d227595b3e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mark.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mark.c @@ -7,7 +7,8 @@ static bool tc_act_can_offload_mark(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { if (act->mark & ~MLX5E_TC_FLOW_ID_MASK) { NL_SET_ERR_MSG_MOD(parse_state->extack, "Bad flow mark, only 16 bit supported"); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c index 2e615e0ba972..2b002c6a2e73 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c @@ -99,7 +99,8 @@ get_fdb_out_dev(struct net_device *uplink_dev, struct net_device *out_dev) static bool tc_act_can_offload_mirred(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { struct netlink_ext_ack *extack = parse_state->extack; struct mlx5e_tc_flow *flow = parse_state->flow; @@ -108,8 +109,8 @@ tc_act_can_offload_mirred(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_priv *priv = flow->priv; struct mlx5_esw_flow_attr *esw_attr; - parse_attr = flow->attr->parse_attr; - esw_attr = flow->attr->esw_attr; + parse_attr = attr->parse_attr; + esw_attr = attr->esw_attr; if (!out_dev) { /* out_dev is NULL when filters with @@ -124,6 +125,16 @@ tc_act_can_offload_mirred(struct mlx5e_tc_act_parse_state *parse_state, return false; } + if (parse_state->eth_pop && !parse_state->mpls_push) { + NL_SET_ERR_MSG_MOD(extack, "vlan pop eth is supported only with mpls push"); + return false; + } + + if (flow_flag_test(parse_state->flow, L3_TO_L2_DECAP) && !parse_state->eth_push) { + NL_SET_ERR_MSG_MOD(extack, "mpls pop is only supported with vlan eth push"); + return false; + } + if (mlx5e_is_ft_flow(flow) && out_dev == priv->netdev) { /* Ignore forward to self rules generated * by adding both mlx5 devs to the flow table @@ -301,8 +312,7 @@ tc_act_parse_mirred(struct mlx5e_tc_act_parse_state *parse_state, if (err) return err; - attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | - MLX5_FLOW_CONTEXT_ACTION_COUNT; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred_nic.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred_nic.c index 2c74567b6d25..90b4c1b34776 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred_nic.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred_nic.c @@ -7,7 +7,8 @@ static bool tc_act_can_offload_mirred_nic(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { struct netlink_ext_ack *extack = parse_state->extack; struct mlx5e_tc_flow *flow = parse_state->flow; @@ -39,8 +40,7 @@ tc_act_parse_mirred_nic(struct mlx5e_tc_act_parse_state *parse_state, { attr->parse_attr->mirred_ifindex[0] = act->dev->ifindex; flow_flag_set(parse_state->flow, HAIRPIN); - attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | - MLX5_FLOW_CONTEXT_ACTION_COUNT; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mpls.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mpls.c index 89ca88c78840..f106190bf37c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mpls.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mpls.c @@ -8,7 +8,8 @@ static bool tc_act_can_offload_mpls_push(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { struct netlink_ext_ack *extack = parse_state->extack; struct mlx5e_priv *priv = parse_state->flow->priv; @@ -47,21 +48,22 @@ tc_act_parse_mpls_push(struct mlx5e_tc_act_parse_state *parse_state, static bool tc_act_can_offload_mpls_pop(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { struct netlink_ext_ack *extack = parse_state->extack; - struct mlx5e_tc_flow *flow = parse_state->flow; struct net_device *filter_dev; - filter_dev = flow->attr->parse_attr->filter_dev; + filter_dev = attr->parse_attr->filter_dev; /* we only support mpls pop if it is the first action + * or it is second action after tunnel key unset * and the filter net device is bareudp. Subsequent * actions can be pedit and the last can be mirred * egress redirect. */ - if (act_index) { - NL_SET_ERR_MSG_MOD(extack, "mpls pop supported only as first action"); + if ((act_index == 1 && !parse_state->decap) || act_index > 1) { + NL_SET_ERR_MSG_MOD(extack, "mpls pop supported only as first action or with decap"); return false; } @@ -79,7 +81,7 @@ tc_act_parse_mpls_pop(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_priv *priv, struct mlx5_flow_attr *attr) { - attr->parse_attr->eth.h_proto = act->mpls_pop.proto; + attr->esw_attr->eth.h_proto = act->mpls_pop.proto; attr->action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT; flow_flag_set(parse_state->flow, L3_TO_L2_DECAP); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.c index 79addbbef087..47597c524e59 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.c @@ -42,12 +42,11 @@ set_pedit_val(u8 hdr_type, u32 mask, u32 val, u32 offset, return -EOPNOTSUPP; } -static int -parse_pedit_to_modify_hdr(struct mlx5e_priv *priv, - const struct flow_action_entry *act, int namespace, - struct mlx5e_tc_flow_parse_attr *parse_attr, - struct pedit_headers_action *hdrs, - struct netlink_ext_ack *extack) +int +mlx5e_tc_act_pedit_parse_action(struct mlx5e_priv *priv, + const struct flow_action_entry *act, int namespace, + struct pedit_headers_action *hdrs, + struct netlink_ext_ack *extack) { u8 cmd = (act->id == FLOW_ACTION_MANGLE) ? 0 : 1; u8 htype = act->mangle.htype; @@ -79,51 +78,11 @@ parse_pedit_to_modify_hdr(struct mlx5e_priv *priv, return err; } -static int -parse_pedit_to_reformat(const struct flow_action_entry *act, - struct mlx5e_tc_flow_parse_attr *parse_attr, - struct netlink_ext_ack *extack) -{ - u32 mask, val, offset; - u32 *p; - - if (act->id != FLOW_ACTION_MANGLE) { - NL_SET_ERR_MSG_MOD(extack, "Unsupported action id"); - return -EOPNOTSUPP; - } - - if (act->mangle.htype != FLOW_ACT_MANGLE_HDR_TYPE_ETH) { - NL_SET_ERR_MSG_MOD(extack, "Only Ethernet modification is supported"); - return -EOPNOTSUPP; - } - - mask = ~act->mangle.mask; - val = act->mangle.val; - offset = act->mangle.offset; - p = (u32 *)&parse_attr->eth; - *(p + (offset >> 2)) |= (val & mask); - - return 0; -} - -int -mlx5e_tc_act_pedit_parse_action(struct mlx5e_priv *priv, - const struct flow_action_entry *act, int namespace, - struct mlx5e_tc_flow_parse_attr *parse_attr, - struct pedit_headers_action *hdrs, - struct mlx5e_tc_flow *flow, - struct netlink_ext_ack *extack) -{ - if (flow && flow_flag_test(flow, L3_TO_L2_DECAP)) - return parse_pedit_to_reformat(act, parse_attr, extack); - - return parse_pedit_to_modify_hdr(priv, act, namespace, parse_attr, hdrs, extack); -} - static bool tc_act_can_offload_pedit(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { return true; } @@ -141,21 +100,16 @@ tc_act_parse_pedit(struct mlx5e_tc_act_parse_state *parse_state, ns_type = mlx5e_get_flow_namespace(flow); - err = mlx5e_tc_act_pedit_parse_action(flow->priv, act, ns_type, - attr->parse_attr, parse_state->hdrs, - flow, parse_state->extack); + err = mlx5e_tc_act_pedit_parse_action(flow->priv, act, ns_type, attr->parse_attr->hdrs, + parse_state->extack); if (err) return err; - if (flow_flag_test(flow, L3_TO_L2_DECAP)) - goto out; - attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; if (ns_type == MLX5_FLOW_NAMESPACE_FDB) esw_attr->split_count = esw_attr->out_count; -out: return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.h index da8ab03af58f..434c8bd710a2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.h @@ -24,9 +24,7 @@ struct pedit_headers_action { int mlx5e_tc_act_pedit_parse_action(struct mlx5e_priv *priv, const struct flow_action_entry *act, int namespace, - struct mlx5e_tc_flow_parse_attr *parse_attr, struct pedit_headers_action *hdrs, - struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack); #endif /* __MLX5_EN_TC_ACT_PEDIT_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ptype.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ptype.c index 0819110193dc..6454b031ff7a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ptype.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ptype.c @@ -7,7 +7,8 @@ static bool tc_act_can_offload_ptype(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { return true; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/redirect_ingress.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/redirect_ingress.c index 1c32e24e528d..ad09a8a5f36e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/redirect_ingress.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/redirect_ingress.c @@ -7,16 +7,16 @@ static bool tc_act_can_offload_redirect_ingress(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { struct netlink_ext_ack *extack = parse_state->extack; - struct mlx5e_tc_flow *flow = parse_state->flow; struct mlx5e_tc_flow_parse_attr *parse_attr; struct net_device *out_dev = act->dev; struct mlx5_esw_flow_attr *esw_attr; - parse_attr = flow->attr->parse_attr; - esw_attr = flow->attr->esw_attr; + parse_attr = attr->parse_attr; + esw_attr = attr->esw_attr; if (!out_dev) return false; @@ -58,8 +58,7 @@ tc_act_parse_redirect_ingress(struct mlx5e_tc_act_parse_state *parse_state, struct net_device *out_dev = act->dev; int err; - attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | - MLX5_FLOW_CONTEXT_ACTION_COUNT; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; err = mlx5e_set_fwd_to_int_port_actions(priv, attr, out_dev->ifindex, MLX5E_TC_INT_PORT_INGRESS, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/sample.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/sample.c index 6699bdf5cf01..2c0196431302 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/sample.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/sample.c @@ -4,17 +4,21 @@ #include #include "act.h" #include "en/tc_priv.h" +#include "en/tc/act/sample.h" static bool tc_act_can_offload_sample(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { struct netlink_ext_ack *extack = parse_state->extack; + bool ct_nat; - if (flow_flag_test(parse_state->flow, CT)) { - NL_SET_ERR_MSG_MOD(extack, - "Sample action with connection tracking is not supported"); + ct_nat = attr->ct_attr.ct_action & TCA_CT_ACT_NAT; + + if (flow_flag_test(parse_state->flow, CT) && ct_nat) { + NL_SET_ERR_MSG_MOD(extack, "Sample action with CT NAT is not supported"); return false; } @@ -27,11 +31,7 @@ tc_act_parse_sample(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_priv *priv, struct mlx5_flow_attr *attr) { - struct mlx5e_sample_attr *sample_attr; - - sample_attr = kzalloc(sizeof(*attr->sample_attr), GFP_KERNEL); - if (!sample_attr) - return -ENOMEM; + struct mlx5e_sample_attr *sample_attr = &attr->sample_attr; sample_attr->rate = act->sample.rate; sample_attr->group_num = act->sample.psample_group->group_num; @@ -39,13 +39,33 @@ tc_act_parse_sample(struct mlx5e_tc_act_parse_state *parse_state, if (act->sample.truncate) sample_attr->trunc_size = act->sample.trunc_size; - attr->sample_attr = sample_attr; + attr->flags |= MLX5_ATTR_FLAG_SAMPLE; flow_flag_set(parse_state->flow, SAMPLE); return 0; } +bool +mlx5e_tc_act_sample_is_multi_table(struct mlx5_core_dev *mdev, + struct mlx5_flow_attr *attr) +{ + if (MLX5_CAP_GEN(mdev, reg_c_preserve) || + attr->action & MLX5_FLOW_CONTEXT_ACTION_DECAP) + return true; + + return false; +} + +static bool +tc_act_is_multi_table_act_sample(struct mlx5e_priv *priv, + const struct flow_action_entry *act, + struct mlx5_flow_attr *attr) +{ + return mlx5e_tc_act_sample_is_multi_table(priv->mdev, attr); +} + struct mlx5e_tc_act mlx5e_tc_act_sample = { .can_offload = tc_act_can_offload_sample, .parse_action = tc_act_parse_sample, + .is_multi_table_act = tc_act_is_multi_table_act_sample, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/sample.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/sample.h new file mode 100644 index 000000000000..3efb3a15c5d2 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/sample.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#ifndef __MLX5_EN_TC_ACT_SAMPLE_H__ +#define __MLX5_EN_TC_ACT_SAMPLE_H__ + +#include +#include "en/tc_priv.h" + +bool +mlx5e_tc_act_sample_is_multi_table(struct mlx5_core_dev *mdev, + struct mlx5_flow_attr *attr); + +#endif /* __MLX5_EN_TC_ACT_SAMPLE_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c index 046b64c2cec4..a7d9eab19e4a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c @@ -7,7 +7,8 @@ static bool tc_act_can_offload_trap(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { struct netlink_ext_ack *extack = parse_state->extack; @@ -25,9 +26,8 @@ tc_act_parse_trap(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_priv *priv, struct mlx5_flow_attr *attr) { - attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | - MLX5_FLOW_CONTEXT_ACTION_COUNT; - attr->flags |= MLX5_ESW_ATTR_FLAG_SLOW_PATH; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + attr->flags |= MLX5_ATTR_FLAG_SLOW_PATH; return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/tun.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/tun.c index 6f4a2cf46afd..b4fa2de9711d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/tun.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/tun.c @@ -8,7 +8,8 @@ static bool tc_act_can_offload_tun_encap(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { if (!act->tunnel) { NL_SET_ERR_MSG_MOD(parse_state->extack, @@ -34,7 +35,8 @@ tc_act_parse_tun_encap(struct mlx5e_tc_act_parse_state *parse_state, static bool tc_act_can_offload_tun_decap(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { return true; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.c index 70fc0c2d8813..b86ac604d0c2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.c @@ -9,7 +9,6 @@ static int add_vlan_prio_tag_rewrite_action(struct mlx5e_priv *priv, struct mlx5e_tc_flow_parse_attr *parse_attr, - struct pedit_headers_action *hdrs, u32 *action, struct netlink_ext_ack *extack) { const struct flow_action_entry prio_tag_act = { @@ -26,7 +25,7 @@ add_vlan_prio_tag_rewrite_action(struct mlx5e_priv *priv, }; return mlx5e_tc_act_vlan_add_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB, - &prio_tag_act, parse_attr, hdrs, action, + &prio_tag_act, parse_attr, action, extack); } @@ -35,7 +34,8 @@ parse_tc_vlan_action(struct mlx5e_priv *priv, const struct flow_action_entry *act, struct mlx5_esw_flow_attr *attr, u32 *action, - struct netlink_ext_ack *extack) + struct netlink_ext_ack *extack, + struct mlx5e_tc_act_parse_state *parse_state) { u8 vlan_idx = attr->total_vlan; @@ -85,6 +85,16 @@ parse_tc_vlan_action(struct mlx5e_priv *priv, *action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH; } break; + case FLOW_ACTION_VLAN_POP_ETH: + parse_state->eth_pop = true; + break; + case FLOW_ACTION_VLAN_PUSH_ETH: + if (!flow_flag_test(parse_state->flow, L3_TO_L2_DECAP)) + return -EOPNOTSUPP; + parse_state->eth_push = true; + memcpy(attr->eth.h_dest, act->vlan_push_eth.dst, ETH_ALEN); + memcpy(attr->eth.h_source, act->vlan_push_eth.src, ETH_ALEN); + break; default: NL_SET_ERR_MSG_MOD(extack, "Unexpected action id for VLAN"); return -EINVAL; @@ -110,7 +120,7 @@ mlx5e_tc_act_vlan_add_push_action(struct mlx5e_priv *priv, }; int err; - err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr, &attr->action, extack); + err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr, &attr->action, extack, NULL); if (err) return err; @@ -140,7 +150,7 @@ mlx5e_tc_act_vlan_add_pop_action(struct mlx5e_priv *priv, priv->netdev->lower_level; while (nest_level--) { err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr, &attr->action, - extack); + extack, NULL); if (err) return err; } @@ -151,7 +161,8 @@ mlx5e_tc_act_vlan_add_pop_action(struct mlx5e_priv *priv, static bool tc_act_can_offload_vlan(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { return true; } @@ -170,11 +181,11 @@ tc_act_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state, /* Replace vlan pop+push with vlan modify */ attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP; err = mlx5e_tc_act_vlan_add_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB, act, - attr->parse_attr, parse_state->hdrs, - &attr->action, parse_state->extack); + attr->parse_attr, &attr->action, + parse_state->extack); } else { err = parse_tc_vlan_action(priv, act, esw_attr, &attr->action, - parse_state->extack); + parse_state->extack, parse_state); } if (err) @@ -191,7 +202,6 @@ tc_act_post_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5_flow_attr *attr) { struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr; - struct pedit_headers_action *hdrs = parse_state->hdrs; struct netlink_ext_ack *extack = parse_state->extack; struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; int err; @@ -202,7 +212,7 @@ tc_act_post_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state, * tag rewrite. */ attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP; - err = add_vlan_prio_tag_rewrite_action(priv, parse_attr, hdrs, + err = add_vlan_prio_tag_rewrite_action(priv, parse_attr, &attr->action, extack); if (err) return err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.h index 3d62f13ab61f..2fa58c6f44eb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.h @@ -24,7 +24,6 @@ int mlx5e_tc_act_vlan_add_rewrite_action(struct mlx5e_priv *priv, int namespace, const struct flow_action_entry *act, struct mlx5e_tc_flow_parse_attr *parse_attr, - struct pedit_headers_action *hdrs, u32 *action, struct netlink_ext_ack *extack); #endif /* __MLX5_EN_TC_ACT_VLAN_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan_mangle.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan_mangle.c index 63e36e7f53e3..9a8a1a6bd99e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan_mangle.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan_mangle.c @@ -12,7 +12,6 @@ int mlx5e_tc_act_vlan_add_rewrite_action(struct mlx5e_priv *priv, int namespace, const struct flow_action_entry *act, struct mlx5e_tc_flow_parse_attr *parse_attr, - struct pedit_headers_action *hdrs, u32 *action, struct netlink_ext_ack *extack) { u16 mask16 = VLAN_VID_MASK; @@ -44,8 +43,8 @@ mlx5e_tc_act_vlan_add_rewrite_action(struct mlx5e_priv *priv, int namespace, return -EOPNOTSUPP; } - err = mlx5e_tc_act_pedit_parse_action(priv, &pedit_act, namespace, parse_attr, hdrs, - NULL, extack); + err = mlx5e_tc_act_pedit_parse_action(priv, &pedit_act, namespace, parse_attr->hdrs, + extack); *action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; return err; @@ -54,7 +53,8 @@ mlx5e_tc_act_vlan_add_rewrite_action(struct mlx5e_priv *priv, int namespace, static bool tc_act_can_offload_vlan_mangle(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, - int act_index) + int act_index, + struct mlx5_flow_attr *attr) { return true; } @@ -69,8 +69,7 @@ tc_act_parse_vlan_mangle(struct mlx5e_tc_act_parse_state *parse_state, int err; ns_type = mlx5e_get_flow_namespace(parse_state->flow); - err = mlx5e_tc_act_vlan_add_rewrite_action(priv, ns_type, act, - attr->parse_attr, parse_state->hdrs, + err = mlx5e_tc_act_vlan_add_rewrite_action(priv, ns_type, act, attr->parse_attr, &attr->action, parse_state->extack); if (err) return err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs.h new file mode 100644 index 000000000000..bb6b1a979ba1 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. */ + +#ifndef __MLX5_EN_TC_CT_FS_H__ +#define __MLX5_EN_TC_CT_FS_H__ + +struct mlx5_ct_fs { + const struct net_device *netdev; + struct mlx5_core_dev *dev; + + /* private data */ + void *priv_data[]; +}; + +struct mlx5_ct_fs_rule { +}; + +struct mlx5_ct_fs_ops { + int (*init)(struct mlx5_ct_fs *fs, struct mlx5_flow_table *ct, + struct mlx5_flow_table *ct_nat, struct mlx5_flow_table *post_ct); + void (*destroy)(struct mlx5_ct_fs *fs); + + struct mlx5_ct_fs_rule * (*ct_rule_add)(struct mlx5_ct_fs *fs, + struct mlx5_flow_spec *spec, + struct mlx5_flow_attr *attr, + struct flow_rule *flow_rule); + void (*ct_rule_del)(struct mlx5_ct_fs *fs, struct mlx5_ct_fs_rule *fs_rule); + + size_t priv_size; +}; + +static inline void *mlx5_ct_fs_priv(struct mlx5_ct_fs *fs) +{ + return &fs->priv_data; +} + +struct mlx5_ct_fs_ops *mlx5_ct_fs_dmfs_ops_get(void); + +#if IS_ENABLED(CONFIG_MLX5_SW_STEERING) +struct mlx5_ct_fs_ops *mlx5_ct_fs_smfs_ops_get(void); +#else +static inline struct mlx5_ct_fs_ops * +mlx5_ct_fs_smfs_ops_get(void) +{ + return NULL; +} +#endif /* IS_ENABLED(CONFIG_MLX5_SW_STEERING) */ + +#endif /* __MLX5_EN_TC_CT_FS_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_dmfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_dmfs.c new file mode 100644 index 000000000000..ae4f55be48ce --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_dmfs.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. */ + +#include "en_tc.h" +#include "en/tc_ct.h" +#include "en/tc/ct_fs.h" + +#define ct_dbg(fmt, args...)\ + netdev_dbg(fs->netdev, "ct_fs_dmfs debug: " fmt "\n", ##args) + +struct mlx5_ct_fs_dmfs_rule { + struct mlx5_ct_fs_rule fs_rule; + struct mlx5_flow_handle *rule; + struct mlx5_flow_attr *attr; +}; + +static int +mlx5_ct_fs_dmfs_init(struct mlx5_ct_fs *fs, struct mlx5_flow_table *ct, + struct mlx5_flow_table *ct_nat, struct mlx5_flow_table *post_ct) +{ + return 0; +} + +static void +mlx5_ct_fs_dmfs_destroy(struct mlx5_ct_fs *fs) +{ +} + +static struct mlx5_ct_fs_rule * +mlx5_ct_fs_dmfs_ct_rule_add(struct mlx5_ct_fs *fs, struct mlx5_flow_spec *spec, + struct mlx5_flow_attr *attr, struct flow_rule *flow_rule) +{ + struct mlx5e_priv *priv = netdev_priv(fs->netdev); + struct mlx5_ct_fs_dmfs_rule *dmfs_rule; + int err; + + dmfs_rule = kzalloc(sizeof(*dmfs_rule), GFP_KERNEL); + if (!dmfs_rule) + return ERR_PTR(-ENOMEM); + + dmfs_rule->rule = mlx5_tc_rule_insert(priv, spec, attr); + if (IS_ERR(dmfs_rule->rule)) { + err = PTR_ERR(dmfs_rule->rule); + ct_dbg("Failed to add ct entry fs rule"); + goto err_insert; + } + + dmfs_rule->attr = attr; + + return &dmfs_rule->fs_rule; + +err_insert: + kfree(dmfs_rule); + return ERR_PTR(err); +} + +static void +mlx5_ct_fs_dmfs_ct_rule_del(struct mlx5_ct_fs *fs, struct mlx5_ct_fs_rule *fs_rule) +{ + struct mlx5_ct_fs_dmfs_rule *dmfs_rule = container_of(fs_rule, + struct mlx5_ct_fs_dmfs_rule, + fs_rule); + + mlx5_tc_rule_delete(netdev_priv(fs->netdev), dmfs_rule->rule, dmfs_rule->attr); + kfree(dmfs_rule); +} + +static struct mlx5_ct_fs_ops dmfs_ops = { + .ct_rule_add = mlx5_ct_fs_dmfs_ct_rule_add, + .ct_rule_del = mlx5_ct_fs_dmfs_ct_rule_del, + + .init = mlx5_ct_fs_dmfs_init, + .destroy = mlx5_ct_fs_dmfs_destroy, +}; + +struct mlx5_ct_fs_ops *mlx5_ct_fs_dmfs_ops_get(void) +{ + return &dmfs_ops; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_smfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_smfs.c new file mode 100644 index 000000000000..59988e24b704 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_smfs.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. */ + +#include + +#include "en_tc.h" +#include "en/tc_priv.h" +#include "en/tc_ct.h" +#include "en/tc/ct_fs.h" + +#include "lib/smfs.h" + +#define INIT_ERR_PREFIX "ct_fs_smfs init failed" +#define ct_dbg(fmt, args...)\ + netdev_dbg(fs->netdev, "ct_fs_smfs debug: " fmt "\n", ##args) +#define MLX5_CT_TCP_FLAGS_MASK cpu_to_be16(be32_to_cpu(TCP_FLAG_RST | TCP_FLAG_FIN) >> 16) + +struct mlx5_ct_fs_smfs_matcher { + struct mlx5dr_matcher *dr_matcher; + struct list_head list; + int prio; + refcount_t ref; +}; + +struct mlx5_ct_fs_smfs_matchers { + struct mlx5_ct_fs_smfs_matcher smfs_matchers[4]; + struct list_head used; +}; + +struct mlx5_ct_fs_smfs { + struct mlx5dr_table *ct_tbl, *ct_nat_tbl; + struct mlx5_ct_fs_smfs_matchers matchers; + struct mlx5_ct_fs_smfs_matchers matchers_nat; + struct mlx5dr_action *fwd_action; + struct mlx5_flow_table *ct_nat; + struct mutex lock; /* Guards matchers */ +}; + +struct mlx5_ct_fs_smfs_rule { + struct mlx5_ct_fs_rule fs_rule; + struct mlx5dr_rule *rule; + struct mlx5dr_action *count_action; + struct mlx5_ct_fs_smfs_matcher *smfs_matcher; +}; + +static inline void +mlx5_ct_fs_smfs_fill_mask(struct mlx5_ct_fs *fs, struct mlx5_flow_spec *spec, bool ipv4, bool tcp) +{ + void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, outer_headers); + + if (likely(MLX5_CAP_FLOWTABLE_NIC_RX(fs->dev, ft_field_support.outer_ip_version))) + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_version); + else + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ethertype); + + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol); + if (likely(ipv4)) { + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, + src_ipv4_src_ipv6.ipv4_layout.ipv4); + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, + dst_ipv4_dst_ipv6.ipv4_layout.ipv4); + } else { + memset(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c, + dst_ipv4_dst_ipv6.ipv6_layout.ipv6), + 0xFF, + MLX5_FLD_SZ_BYTES(fte_match_set_lyr_2_4, + dst_ipv4_dst_ipv6.ipv6_layout.ipv6)); + memset(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c, + src_ipv4_src_ipv6.ipv6_layout.ipv6), + 0xFF, + MLX5_FLD_SZ_BYTES(fte_match_set_lyr_2_4, + src_ipv4_src_ipv6.ipv6_layout.ipv6)); + } + + if (likely(tcp)) { + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, tcp_sport); + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, tcp_dport); + MLX5_SET(fte_match_set_lyr_2_4, headers_c, tcp_flags, + ntohs(MLX5_CT_TCP_FLAGS_MASK)); + } else { + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, udp_sport); + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, udp_dport); + } + + mlx5e_tc_match_to_reg_match(spec, ZONE_TO_REG, 0, MLX5_CT_ZONE_MASK); +} + +static struct mlx5dr_matcher * +mlx5_ct_fs_smfs_matcher_create(struct mlx5_ct_fs *fs, struct mlx5dr_table *tbl, bool ipv4, + bool tcp, u32 priority) +{ + struct mlx5dr_matcher *dr_matcher; + struct mlx5_flow_spec *spec; + + spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return ERR_PTR(-ENOMEM); + + mlx5_ct_fs_smfs_fill_mask(fs, spec, ipv4, tcp); + spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS_2 | MLX5_MATCH_OUTER_HEADERS; + + dr_matcher = mlx5_smfs_matcher_create(tbl, priority, spec); + kfree(spec); + if (!dr_matcher) + return ERR_PTR(-EINVAL); + + return dr_matcher; +} + +static struct mlx5_ct_fs_smfs_matcher * +mlx5_ct_fs_smfs_matcher_get(struct mlx5_ct_fs *fs, bool nat, bool ipv4, bool tcp) +{ + struct mlx5_ct_fs_smfs *fs_smfs = mlx5_ct_fs_priv(fs); + struct mlx5_ct_fs_smfs_matcher *m, *smfs_matcher; + struct mlx5_ct_fs_smfs_matchers *matchers; + struct mlx5dr_matcher *dr_matcher; + struct mlx5dr_table *tbl; + struct list_head *prev; + int prio; + + matchers = nat ? &fs_smfs->matchers_nat : &fs_smfs->matchers; + smfs_matcher = &matchers->smfs_matchers[ipv4 * 2 + tcp]; + + if (refcount_inc_not_zero(&smfs_matcher->ref)) + return smfs_matcher; + + mutex_lock(&fs_smfs->lock); + + /* Retry with lock, as another thread might have already created the relevant matcher + * till we acquired the lock + */ + if (refcount_inc_not_zero(&smfs_matcher->ref)) + goto out_unlock; + + // Find next available priority in sorted used list + prio = 0; + prev = &matchers->used; + list_for_each_entry(m, &matchers->used, list) { + prev = &m->list; + + if (m->prio == prio) + prio = m->prio + 1; + else + break; + } + + tbl = nat ? fs_smfs->ct_nat_tbl : fs_smfs->ct_tbl; + dr_matcher = mlx5_ct_fs_smfs_matcher_create(fs, tbl, ipv4, tcp, prio); + if (IS_ERR(dr_matcher)) { + netdev_warn(fs->netdev, + "ct_fs_smfs: failed to create matcher (nat %d, ipv4 %d, tcp %d), err: %ld\n", + nat, ipv4, tcp, PTR_ERR(dr_matcher)); + + smfs_matcher = ERR_CAST(dr_matcher); + goto out_unlock; + } + + smfs_matcher->dr_matcher = dr_matcher; + smfs_matcher->prio = prio; + list_add(&smfs_matcher->list, prev); + refcount_set(&smfs_matcher->ref, 1); + +out_unlock: + mutex_unlock(&fs_smfs->lock); + return smfs_matcher; +} + +static void +mlx5_ct_fs_smfs_matcher_put(struct mlx5_ct_fs *fs, struct mlx5_ct_fs_smfs_matcher *smfs_matcher) +{ + struct mlx5_ct_fs_smfs *fs_smfs = mlx5_ct_fs_priv(fs); + + if (!refcount_dec_and_mutex_lock(&smfs_matcher->ref, &fs_smfs->lock)) + return; + + mlx5_smfs_matcher_destroy(smfs_matcher->dr_matcher); + list_del(&smfs_matcher->list); + mutex_unlock(&fs_smfs->lock); +} + +static int +mlx5_ct_fs_smfs_init(struct mlx5_ct_fs *fs, struct mlx5_flow_table *ct, + struct mlx5_flow_table *ct_nat, struct mlx5_flow_table *post_ct) +{ + struct mlx5dr_table *ct_tbl, *ct_nat_tbl, *post_ct_tbl; + struct mlx5_ct_fs_smfs *fs_smfs = mlx5_ct_fs_priv(fs); + + post_ct_tbl = mlx5_smfs_table_get_from_fs_ft(post_ct); + ct_nat_tbl = mlx5_smfs_table_get_from_fs_ft(ct_nat); + ct_tbl = mlx5_smfs_table_get_from_fs_ft(ct); + fs_smfs->ct_nat = ct_nat; + + if (!ct_tbl || !ct_nat_tbl || !post_ct_tbl) { + netdev_warn(fs->netdev, "ct_fs_smfs: failed to init, missing backing dr tables"); + return -EOPNOTSUPP; + } + + ct_dbg("using smfs steering"); + + fs_smfs->fwd_action = mlx5_smfs_action_create_dest_table(post_ct_tbl); + if (!fs_smfs->fwd_action) { + return -EINVAL; + } + + fs_smfs->ct_tbl = ct_tbl; + fs_smfs->ct_nat_tbl = ct_nat_tbl; + mutex_init(&fs_smfs->lock); + INIT_LIST_HEAD(&fs_smfs->matchers.used); + INIT_LIST_HEAD(&fs_smfs->matchers_nat.used); + + return 0; +} + +static void +mlx5_ct_fs_smfs_destroy(struct mlx5_ct_fs *fs) +{ + struct mlx5_ct_fs_smfs *fs_smfs = mlx5_ct_fs_priv(fs); + + mlx5_smfs_action_destroy(fs_smfs->fwd_action); +} + +static inline bool +mlx5_tc_ct_valid_used_dissector_keys(const u32 used_keys) +{ +#define DISSECTOR_BIT(name) BIT(FLOW_DISSECTOR_KEY_ ## name) + const u32 basic_keys = DISSECTOR_BIT(BASIC) | DISSECTOR_BIT(CONTROL) | + DISSECTOR_BIT(PORTS) | DISSECTOR_BIT(META); + const u32 ipv4_tcp = basic_keys | DISSECTOR_BIT(IPV4_ADDRS) | DISSECTOR_BIT(TCP); + const u32 ipv4_udp = basic_keys | DISSECTOR_BIT(IPV4_ADDRS); + const u32 ipv6_tcp = basic_keys | DISSECTOR_BIT(IPV6_ADDRS) | DISSECTOR_BIT(TCP); + const u32 ipv6_udp = basic_keys | DISSECTOR_BIT(IPV6_ADDRS); + + return (used_keys == ipv4_tcp || used_keys == ipv4_udp || used_keys == ipv6_tcp || + used_keys == ipv6_udp); +} + +static bool +mlx5_ct_fs_smfs_ct_validate_flow_rule(struct mlx5_ct_fs *fs, struct flow_rule *flow_rule) +{ + struct flow_match_ipv4_addrs ipv4_addrs; + struct flow_match_ipv6_addrs ipv6_addrs; + struct flow_match_control control; + struct flow_match_basic basic; + struct flow_match_ports ports; + struct flow_match_tcp tcp; + + if (!mlx5_tc_ct_valid_used_dissector_keys(flow_rule->match.dissector->used_keys)) { + ct_dbg("rule uses unexpected dissectors (0x%08x)", + flow_rule->match.dissector->used_keys); + return false; + } + + flow_rule_match_basic(flow_rule, &basic); + flow_rule_match_control(flow_rule, &control); + flow_rule_match_ipv4_addrs(flow_rule, &ipv4_addrs); + flow_rule_match_ipv6_addrs(flow_rule, &ipv6_addrs); + flow_rule_match_ports(flow_rule, &ports); + flow_rule_match_tcp(flow_rule, &tcp); + + if (basic.mask->n_proto != htons(0xFFFF) || + (basic.key->n_proto != htons(ETH_P_IP) && basic.key->n_proto != htons(ETH_P_IPV6)) || + basic.mask->ip_proto != 0xFF || + (basic.key->ip_proto != IPPROTO_UDP && basic.key->ip_proto != IPPROTO_TCP)) { + ct_dbg("rule uses unexpected basic match (n_proto 0x%04x/0x%04x, ip_proto 0x%02x/0x%02x)", + ntohs(basic.key->n_proto), ntohs(basic.mask->n_proto), + basic.key->ip_proto, basic.mask->ip_proto); + return false; + } + + if (ports.mask->src != htons(0xFFFF) || ports.mask->dst != htons(0xFFFF)) { + ct_dbg("rule uses ports match (src 0x%04x, dst 0x%04x)", + ports.mask->src, ports.mask->dst); + return false; + } + + if (basic.key->ip_proto == IPPROTO_TCP && tcp.mask->flags != MLX5_CT_TCP_FLAGS_MASK) { + ct_dbg("rule uses unexpected tcp match (flags 0x%02x)", tcp.mask->flags); + return false; + } + + return true; +} + +static struct mlx5_ct_fs_rule * +mlx5_ct_fs_smfs_ct_rule_add(struct mlx5_ct_fs *fs, struct mlx5_flow_spec *spec, + struct mlx5_flow_attr *attr, struct flow_rule *flow_rule) +{ + struct mlx5_ct_fs_smfs *fs_smfs = mlx5_ct_fs_priv(fs); + struct mlx5_ct_fs_smfs_matcher *smfs_matcher; + struct mlx5_ct_fs_smfs_rule *smfs_rule; + struct mlx5dr_action *actions[5]; + struct mlx5dr_rule *rule; + int num_actions = 0, err; + bool nat, tcp, ipv4; + + if (!mlx5_ct_fs_smfs_ct_validate_flow_rule(fs, flow_rule)) + return ERR_PTR(-EOPNOTSUPP); + + smfs_rule = kzalloc(sizeof(*smfs_rule), GFP_KERNEL); + if (!smfs_rule) + return ERR_PTR(-ENOMEM); + + smfs_rule->count_action = mlx5_smfs_action_create_flow_counter(mlx5_fc_id(attr->counter)); + if (!smfs_rule->count_action) { + err = -EINVAL; + goto err_count; + } + + actions[num_actions++] = smfs_rule->count_action; + actions[num_actions++] = attr->modify_hdr->action.dr_action; + actions[num_actions++] = fs_smfs->fwd_action; + + nat = (attr->ft == fs_smfs->ct_nat); + ipv4 = mlx5e_tc_get_ip_version(spec, true) == 4; + tcp = MLX5_GET(fte_match_param, spec->match_value, + outer_headers.ip_protocol) == IPPROTO_TCP; + + smfs_matcher = mlx5_ct_fs_smfs_matcher_get(fs, nat, ipv4, tcp); + if (IS_ERR(smfs_matcher)) { + err = PTR_ERR(smfs_matcher); + goto err_matcher; + } + + rule = mlx5_smfs_rule_create(smfs_matcher->dr_matcher, spec, num_actions, actions, + MLX5_FLOW_CONTEXT_FLOW_SOURCE_ANY_VPORT); + if (!rule) { + err = -EINVAL; + goto err_create; + } + + smfs_rule->rule = rule; + smfs_rule->smfs_matcher = smfs_matcher; + + return &smfs_rule->fs_rule; + +err_create: + mlx5_ct_fs_smfs_matcher_put(fs, smfs_matcher); +err_matcher: + mlx5_smfs_action_destroy(smfs_rule->count_action); +err_count: + kfree(smfs_rule); + return ERR_PTR(err); +} + +static void +mlx5_ct_fs_smfs_ct_rule_del(struct mlx5_ct_fs *fs, struct mlx5_ct_fs_rule *fs_rule) +{ + struct mlx5_ct_fs_smfs_rule *smfs_rule = container_of(fs_rule, + struct mlx5_ct_fs_smfs_rule, + fs_rule); + + mlx5_smfs_rule_destroy(smfs_rule->rule); + mlx5_ct_fs_smfs_matcher_put(fs, smfs_rule->smfs_matcher); + mlx5_smfs_action_destroy(smfs_rule->count_action); + kfree(smfs_rule); +} + +static struct mlx5_ct_fs_ops fs_smfs_ops = { + .ct_rule_add = mlx5_ct_fs_smfs_ct_rule_add, + .ct_rule_del = mlx5_ct_fs_smfs_ct_rule_del, + + .init = mlx5_ct_fs_smfs_init, + .destroy = mlx5_ct_fs_smfs_destroy, + + .priv_size = sizeof(struct mlx5_ct_fs_smfs), +}; + +struct mlx5_ct_fs_ops * +mlx5_ct_fs_smfs_ops_get(void) +{ + return &fs_smfs_ops; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_act.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_act.c index 31b4e39be2d3..dea137dd744b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_act.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_act.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +#include "en/tc_priv.h" #include "en_tc.h" #include "post_act.h" #include "mlx5_core.h" @@ -75,21 +76,47 @@ mlx5e_tc_post_act_destroy(struct mlx5e_post_act *post_act) kfree(post_act); } +int +mlx5e_tc_post_act_offload(struct mlx5e_post_act *post_act, + struct mlx5e_post_act_handle *handle) +{ + struct mlx5_flow_spec *spec; + int err; + + spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + + /* Post action rule matches on fte_id and executes original rule's tc rule action */ + mlx5e_tc_match_to_reg_match(spec, FTEID_TO_REG, handle->id, MLX5_POST_ACTION_MASK); + + handle->rule = mlx5e_tc_rule_offload(post_act->priv, spec, handle->attr); + if (IS_ERR(handle->rule)) { + err = PTR_ERR(handle->rule); + netdev_warn(post_act->priv->netdev, "Failed to add post action rule"); + goto err_rule; + } + + kvfree(spec); + return 0; + +err_rule: + kvfree(spec); + return err; +} + struct mlx5e_post_act_handle * mlx5e_tc_post_act_add(struct mlx5e_post_act *post_act, struct mlx5_flow_attr *attr) { u32 attr_sz = ns_to_attr_sz(post_act->ns_type); - struct mlx5e_post_act_handle *handle = NULL; - struct mlx5_flow_attr *post_attr = NULL; - struct mlx5_flow_spec *spec = NULL; + struct mlx5e_post_act_handle *handle; + struct mlx5_flow_attr *post_attr; int err; handle = kzalloc(sizeof(*handle), GFP_KERNEL); - spec = kvzalloc(sizeof(*spec), GFP_KERNEL); post_attr = mlx5_alloc_flow_attr(post_act->ns_type); - if (!handle || !spec || !post_attr) { + if (!handle || !post_attr) { kfree(post_attr); - kvfree(spec); kfree(handle); return ERR_PTR(-ENOMEM); } @@ -100,7 +127,7 @@ mlx5e_tc_post_act_add(struct mlx5e_post_act *post_act, struct mlx5_flow_attr *at post_attr->ft = post_act->ft; post_attr->inner_match_level = MLX5_MATCH_NONE; post_attr->outer_match_level = MLX5_MATCH_NONE; - post_attr->action &= ~(MLX5_FLOW_CONTEXT_ACTION_DECAP); + post_attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_DECAP; handle->ns_type = post_act->ns_type; /* Splits were handled before post action */ @@ -112,36 +139,29 @@ mlx5e_tc_post_act_add(struct mlx5e_post_act *post_act, struct mlx5_flow_attr *at if (err) goto err_xarray; - /* Post action rule matches on fte_id and executes original rule's - * tc rule action - */ - mlx5e_tc_match_to_reg_match(spec, FTEID_TO_REG, - handle->id, MLX5_POST_ACTION_MASK); - - handle->rule = mlx5_tc_rule_insert(post_act->priv, spec, post_attr); - if (IS_ERR(handle->rule)) { - err = PTR_ERR(handle->rule); - netdev_warn(post_act->priv->netdev, "Failed to add post action rule"); - goto err_rule; - } handle->attr = post_attr; - kvfree(spec); return handle; -err_rule: - xa_erase(&post_act->ids, handle->id); err_xarray: kfree(post_attr); - kvfree(spec); kfree(handle); return ERR_PTR(err); } +void +mlx5e_tc_post_act_unoffload(struct mlx5e_post_act *post_act, + struct mlx5e_post_act_handle *handle) +{ + mlx5e_tc_rule_unoffload(post_act->priv, handle->rule, handle->attr); + handle->rule = NULL; +} + void mlx5e_tc_post_act_del(struct mlx5e_post_act *post_act, struct mlx5e_post_act_handle *handle) { - mlx5_tc_rule_delete(post_act->priv, handle->rule, handle->attr); + if (!IS_ERR_OR_NULL(handle->rule)) + mlx5e_tc_post_act_unoffload(post_act, handle); xa_erase(&post_act->ids, handle->id); kfree(handle->attr); kfree(handle); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_act.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_act.h index b530ec1981a5..f476774c0b75 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_act.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_act.h @@ -24,6 +24,14 @@ mlx5e_tc_post_act_add(struct mlx5e_post_act *post_act, struct mlx5_flow_attr *at void mlx5e_tc_post_act_del(struct mlx5e_post_act *post_act, struct mlx5e_post_act_handle *handle); +int +mlx5e_tc_post_act_offload(struct mlx5e_post_act *post_act, + struct mlx5e_post_act_handle *handle); + +void +mlx5e_tc_post_act_unoffload(struct mlx5e_post_act *post_act, + struct mlx5e_post_act_handle *handle); + struct mlx5_flow_table * mlx5e_tc_post_act_get_ft(struct mlx5e_post_act *post_act); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c index ff4b4f8a5a9d..fd4504518578 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c @@ -5,6 +5,7 @@ #include #include "en/mapping.h" #include "en/tc/post_act.h" +#include "en/tc/act/sample.h" #include "en/mod_hdr.h" #include "sample.h" #include "eswitch.h" @@ -46,14 +47,12 @@ struct mlx5e_sample_flow { struct mlx5_flow_handle *pre_rule; struct mlx5_flow_attr *post_attr; struct mlx5_flow_handle *post_rule; - struct mlx5e_post_act_handle *post_act_handle; }; struct mlx5e_sample_restore { struct hlist_node hlist; struct mlx5_modify_hdr *modify_hdr; struct mlx5_flow_handle *rule; - struct mlx5e_post_act_handle *post_act_handle; u32 obj_id; int count; }; @@ -231,69 +230,46 @@ sampler_put(struct mlx5e_tc_psample *tc_psample, struct mlx5e_sampler *sampler) */ static struct mlx5_modify_hdr * sample_modify_hdr_get(struct mlx5_core_dev *mdev, u32 obj_id, - struct mlx5e_post_act_handle *handle) + struct mlx5e_tc_mod_hdr_acts *mod_acts) { - struct mlx5e_tc_mod_hdr_acts mod_acts = {}; struct mlx5_modify_hdr *modify_hdr; int err; - err = mlx5e_tc_match_to_reg_set(mdev, &mod_acts, MLX5_FLOW_NAMESPACE_FDB, + err = mlx5e_tc_match_to_reg_set(mdev, mod_acts, MLX5_FLOW_NAMESPACE_FDB, CHAIN_TO_REG, obj_id); if (err) goto err_set_regc0; - if (handle) { - err = mlx5e_tc_post_act_set_handle(mdev, handle, &mod_acts); - if (err) - goto err_post_act; - } - modify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_FDB, - mod_acts.num_actions, - mod_acts.actions); + mod_acts->num_actions, + mod_acts->actions); if (IS_ERR(modify_hdr)) { err = PTR_ERR(modify_hdr); goto err_modify_hdr; } - mlx5e_mod_hdr_dealloc(&mod_acts); + mlx5e_mod_hdr_dealloc(mod_acts); return modify_hdr; err_modify_hdr: -err_post_act: - mlx5e_mod_hdr_dealloc(&mod_acts); + mlx5e_mod_hdr_dealloc(mod_acts); err_set_regc0: return ERR_PTR(err); } -static u32 -restore_hash(u32 obj_id, struct mlx5e_post_act_handle *post_act_handle) -{ - return jhash_2words(obj_id, hash32_ptr(post_act_handle), 0); -} - -static bool -restore_equal(struct mlx5e_sample_restore *restore, u32 obj_id, - struct mlx5e_post_act_handle *post_act_handle) -{ - return restore->obj_id == obj_id && restore->post_act_handle == post_act_handle; -} - static struct mlx5e_sample_restore * sample_restore_get(struct mlx5e_tc_psample *tc_psample, u32 obj_id, - struct mlx5e_post_act_handle *post_act_handle) + struct mlx5e_tc_mod_hdr_acts *mod_acts) { struct mlx5_eswitch *esw = tc_psample->esw; struct mlx5_core_dev *mdev = esw->dev; struct mlx5e_sample_restore *restore; struct mlx5_modify_hdr *modify_hdr; - u32 hash_key; int err; mutex_lock(&tc_psample->restore_lock); - hash_key = restore_hash(obj_id, post_act_handle); - hash_for_each_possible(tc_psample->restore_hashtbl, restore, hlist, hash_key) - if (restore_equal(restore, obj_id, post_act_handle)) + hash_for_each_possible(tc_psample->restore_hashtbl, restore, hlist, obj_id) + if (restore->obj_id == obj_id) goto add_ref; restore = kzalloc(sizeof(*restore), GFP_KERNEL); @@ -302,9 +278,8 @@ sample_restore_get(struct mlx5e_tc_psample *tc_psample, u32 obj_id, goto err_alloc; } restore->obj_id = obj_id; - restore->post_act_handle = post_act_handle; - modify_hdr = sample_modify_hdr_get(mdev, obj_id, post_act_handle); + modify_hdr = sample_modify_hdr_get(mdev, obj_id, mod_acts); if (IS_ERR(modify_hdr)) { err = PTR_ERR(modify_hdr); goto err_modify_hdr; @@ -317,7 +292,7 @@ sample_restore_get(struct mlx5e_tc_psample *tc_psample, u32 obj_id, goto err_restore; } - hash_add(tc_psample->restore_hashtbl, &restore->hlist, hash_key); + hash_add(tc_psample->restore_hashtbl, &restore->hlist, obj_id); add_ref: restore->count++; mutex_unlock(&tc_psample->restore_lock); @@ -403,7 +378,7 @@ add_post_rule(struct mlx5_eswitch *esw, struct mlx5e_sample_flow *sample_flow, post_attr->chain = 0; post_attr->prio = 0; post_attr->ft = default_tbl; - post_attr->flags = MLX5_ESW_ATTR_FLAG_NO_IN_PORT; + post_attr->flags = MLX5_ATTR_FLAG_NO_IN_PORT; /* When offloading sample and encap action, if there is no valid * neigh data struct, a slow path rule is offloaded first. Source @@ -492,16 +467,16 @@ del_post_rule(struct mlx5_eswitch *esw, struct mlx5e_sample_flow *sample_flow, struct mlx5_flow_handle * mlx5e_tc_sample_offload(struct mlx5e_tc_psample *tc_psample, struct mlx5_flow_spec *spec, - struct mlx5_flow_attr *attr, - u32 tunnel_id) + struct mlx5_flow_attr *attr) { - struct mlx5e_post_act_handle *post_act_handle = NULL; struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr; struct mlx5_esw_flow_attr *pre_esw_attr; struct mlx5_mapped_obj restore_obj = {}; + struct mlx5e_tc_mod_hdr_acts *mod_acts; struct mlx5e_sample_flow *sample_flow; struct mlx5e_sample_attr *sample_attr; struct mlx5_flow_attr *pre_attr; + u32 tunnel_id = attr->tunnel_id; struct mlx5_eswitch *esw; u32 default_tbl_id; u32 obj_id; @@ -513,7 +488,7 @@ mlx5e_tc_sample_offload(struct mlx5e_tc_psample *tc_psample, sample_flow = kzalloc(sizeof(*sample_flow), GFP_KERNEL); if (!sample_flow) return ERR_PTR(-ENOMEM); - sample_attr = attr->sample_attr; + sample_attr = &attr->sample_attr; sample_attr->sample_flow = sample_flow; /* For NICs with reg_c_preserve support or decap action, use @@ -522,18 +497,11 @@ mlx5e_tc_sample_offload(struct mlx5e_tc_psample *tc_psample, * original flow table. */ esw = tc_psample->esw; - if (MLX5_CAP_GEN(esw->dev, reg_c_preserve) || - attr->action & MLX5_FLOW_CONTEXT_ACTION_DECAP) { + if (mlx5e_tc_act_sample_is_multi_table(esw->dev, attr)) { struct mlx5_flow_table *ft; ft = mlx5e_tc_post_act_get_ft(tc_psample->post_act); default_tbl_id = ft->id; - post_act_handle = mlx5e_tc_post_act_add(tc_psample->post_act, attr); - if (IS_ERR(post_act_handle)) { - err = PTR_ERR(post_act_handle); - goto err_post_act; - } - sample_flow->post_act_handle = post_act_handle; } else { err = add_post_rule(esw, sample_flow, spec, attr, &default_tbl_id); if (err) @@ -546,6 +514,7 @@ mlx5e_tc_sample_offload(struct mlx5e_tc_psample *tc_psample, err = PTR_ERR(sample_flow->sampler); goto err_sampler; } + sample_attr->sampler_id = sample_flow->sampler->sampler_id; /* Create an id mapping reg_c0 value to sample object. */ restore_obj.type = MLX5_MAPPED_OBJ_SAMPLE; @@ -559,7 +528,8 @@ mlx5e_tc_sample_offload(struct mlx5e_tc_psample *tc_psample, sample_attr->restore_obj_id = obj_id; /* Create sample restore context. */ - sample_flow->restore = sample_restore_get(tc_psample, obj_id, post_act_handle); + mod_acts = &attr->parse_attr->mod_hdr_acts; + sample_flow->restore = sample_restore_get(tc_psample, obj_id, mod_acts); if (IS_ERR(sample_flow->restore)) { err = PTR_ERR(sample_flow->restore); goto err_sample_restore; @@ -580,13 +550,13 @@ mlx5e_tc_sample_offload(struct mlx5e_tc_psample *tc_psample, if (tunnel_id) pre_attr->action |= MLX5_FLOW_CONTEXT_ACTION_DECAP; pre_attr->modify_hdr = sample_flow->restore->modify_hdr; - pre_attr->flags = MLX5_ESW_ATTR_FLAG_SAMPLE; + pre_attr->flags = MLX5_ATTR_FLAG_SAMPLE; pre_attr->inner_match_level = attr->inner_match_level; pre_attr->outer_match_level = attr->outer_match_level; pre_attr->chain = attr->chain; pre_attr->prio = attr->prio; - pre_attr->sample_attr = attr->sample_attr; - sample_attr->sampler_id = sample_flow->sampler->sampler_id; + pre_attr->ft = attr->ft; + pre_attr->sample_attr = *sample_attr; pre_esw_attr = pre_attr->esw_attr; pre_esw_attr->in_mdev = esw_attr->in_mdev; pre_esw_attr->in_rep = esw_attr->in_rep; @@ -611,9 +581,6 @@ mlx5e_tc_sample_offload(struct mlx5e_tc_psample *tc_psample, if (sample_flow->post_rule) del_post_rule(esw, sample_flow, attr); err_post_rule: - if (post_act_handle) - mlx5e_tc_post_act_del(tc_psample->post_act, post_act_handle); -err_post_act: kfree(sample_flow); return ERR_PTR(err); } @@ -633,15 +600,13 @@ mlx5e_tc_sample_unoffload(struct mlx5e_tc_psample *tc_psample, * will hit fw syndromes. */ esw = tc_psample->esw; - sample_flow = attr->sample_attr->sample_flow; + sample_flow = attr->sample_attr.sample_flow; mlx5_eswitch_del_offloaded_rule(esw, sample_flow->pre_rule, sample_flow->pre_attr); sample_restore_put(tc_psample, sample_flow->restore); - mapping_remove(esw->offloads.reg_c0_obj_pool, attr->sample_attr->restore_obj_id); + mapping_remove(esw->offloads.reg_c0_obj_pool, attr->sample_attr.restore_obj_id); sampler_put(tc_psample, sample_flow->sampler); - if (sample_flow->post_act_handle) - mlx5e_tc_post_act_del(tc_psample->post_act, sample_flow->post_act_handle); - else + if (sample_flow->post_rule) del_post_rule(esw, sample_flow, attr); kfree(sample_flow->pre_attr); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.h index 9ef8a49d7801..a569367eae4d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.h @@ -26,8 +26,7 @@ void mlx5e_tc_sample_skb(struct sk_buff *skb, struct mlx5_mapped_obj *mapped_obj struct mlx5_flow_handle * mlx5e_tc_sample_offload(struct mlx5e_tc_psample *sample_priv, struct mlx5_flow_spec *spec, - struct mlx5_flow_attr *attr, - u32 tunnel_id); + struct mlx5_flow_attr *attr); void mlx5e_tc_sample_unoffload(struct mlx5e_tc_psample *sample_priv, @@ -45,8 +44,7 @@ mlx5e_tc_sample_cleanup(struct mlx5e_tc_psample *tc_psample); static inline struct mlx5_flow_handle * mlx5e_tc_sample_offload(struct mlx5e_tc_psample *tc_psample, struct mlx5_flow_spec *spec, - struct mlx5_flow_attr *attr, - u32 tunnel_id) + struct mlx5_flow_attr *attr) { return ERR_PTR(-EOPNOTSUPP); } static inline void diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c index 4a0d38d219ed..e49f51124c74 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c @@ -18,15 +18,16 @@ #include "lib/fs_chains.h" #include "en/tc_ct.h" +#include "en/tc/ct_fs.h" +#include "en/tc_priv.h" #include "en/mod_hdr.h" #include "en/mapping.h" #include "en/tc/post_act.h" #include "en.h" #include "en_tc.h" #include "en_rep.h" +#include "fs_core.h" -#define MLX5_CT_ZONE_BITS (mlx5e_tc_attr_to_reg_mappings[ZONE_TO_REG].mlen) -#define MLX5_CT_ZONE_MASK GENMASK(MLX5_CT_ZONE_BITS - 1, 0) #define MLX5_CT_STATE_ESTABLISHED_BIT BIT(1) #define MLX5_CT_STATE_TRK_BIT BIT(2) #define MLX5_CT_STATE_NAT_BIT BIT(3) @@ -62,19 +63,20 @@ struct mlx5_tc_ct_priv { struct mapping_ctx *labels_mapping; enum mlx5_flow_namespace_type ns_type; struct mlx5_fs_chains *chains; + struct mlx5_ct_fs *fs; + struct mlx5_ct_fs_ops *fs_ops; spinlock_t ht_lock; /* protects ft entries */ }; struct mlx5_ct_flow { struct mlx5_flow_attr *pre_ct_attr; struct mlx5_flow_handle *pre_ct_rule; - struct mlx5e_post_act_handle *post_act_handle; struct mlx5_ct_ft *ft; u32 chain_mapping; }; struct mlx5_ct_zone_rule { - struct mlx5_flow_handle *rule; + struct mlx5_ct_fs_rule *rule; struct mlx5e_mod_hdr_handle *mh; struct mlx5_flow_attr *attr; bool nat; @@ -258,7 +260,8 @@ mlx5_tc_ct_rule_to_tuple(struct mlx5_ct_tuple *tuple, struct flow_rule *rule) return -EOPNOTSUPP; } } else { - return -EOPNOTSUPP; + if (tuple->ip_proto != IPPROTO_GRE) + return -EOPNOTSUPP; } return 0; @@ -505,7 +508,7 @@ mlx5_tc_ct_entry_del_rule(struct mlx5_tc_ct_priv *ct_priv, ct_dbg("Deleting ct entry rule in zone %d", entry->tuple.zone); - mlx5_tc_rule_delete(netdev_priv(ct_priv->netdev), zone_rule->rule, attr); + ct_priv->fs_ops->ct_rule_del(ct_priv->fs, zone_rule->rule); mlx5_tc_ct_entry_destroy_mod_hdr(ct_priv, zone_rule->attr, zone_rule->mh); mlx5_put_label_mapping(ct_priv, attr->ct_attr.ct_labels_id); kfree(attr); @@ -807,16 +810,20 @@ mlx5_tc_ct_entry_add_rule(struct mlx5_tc_ct_priv *ct_priv, attr->dest_chain = 0; attr->dest_ft = mlx5e_tc_post_act_get_ft(ct_priv->post_act); attr->ft = nat ? ct_priv->ct_nat : ct_priv->ct; - attr->outer_match_level = MLX5_MATCH_L4; + if (entry->tuple.ip_proto == IPPROTO_TCP || + entry->tuple.ip_proto == IPPROTO_UDP) + attr->outer_match_level = MLX5_MATCH_L4; + else + attr->outer_match_level = MLX5_MATCH_L3; attr->counter = entry->counter->counter; - attr->flags |= MLX5_ESW_ATTR_FLAG_NO_IN_PORT; + attr->flags |= MLX5_ATTR_FLAG_NO_IN_PORT; if (ct_priv->ns_type == MLX5_FLOW_NAMESPACE_FDB) attr->esw_attr->in_mdev = priv->mdev; mlx5_tc_ct_set_tuple_match(ct_priv, spec, flow_rule); mlx5e_tc_match_to_reg_match(spec, ZONE_TO_REG, entry->tuple.zone, MLX5_CT_ZONE_MASK); - zone_rule->rule = mlx5_tc_rule_insert(priv, spec, attr); + zone_rule->rule = ct_priv->fs_ops->ct_rule_add(ct_priv->fs, spec, attr, flow_rule); if (IS_ERR(zone_rule->rule)) { err = PTR_ERR(zone_rule->rule); ct_dbg("Failed to add ct entry rule, nat: %d", nat); @@ -1154,7 +1161,6 @@ mlx5_tc_ct_block_flow_offload_del(struct mlx5_ct_ft *ft, } rhashtable_remove_fast(&ft->ct_entries_ht, &entry->node, cts_ht_params); - mlx5_tc_ct_entry_remove_from_tuples(entry); spin_unlock_bh(&ct_priv->ht_lock); mlx5_tc_ct_entry_put(entry); @@ -1224,16 +1230,20 @@ mlx5_tc_ct_skb_to_tuple(struct sk_buff *skb, struct mlx5_ct_tuple *tuple, struct flow_keys flow_keys; skb_reset_network_header(skb); - skb_flow_dissect_flow_keys(skb, &flow_keys, 0); + skb_flow_dissect_flow_keys(skb, &flow_keys, FLOW_DISSECTOR_F_STOP_BEFORE_ENCAP); tuple->zone = zone; if (flow_keys.basic.ip_proto != IPPROTO_TCP && - flow_keys.basic.ip_proto != IPPROTO_UDP) + flow_keys.basic.ip_proto != IPPROTO_UDP && + flow_keys.basic.ip_proto != IPPROTO_GRE) return false; - tuple->port.src = flow_keys.ports.src; - tuple->port.dst = flow_keys.ports.dst; + if (flow_keys.basic.ip_proto == IPPROTO_TCP || + flow_keys.basic.ip_proto == IPPROTO_UDP) { + tuple->port.src = flow_keys.ports.src; + tuple->port.dst = flow_keys.ports.dst; + } tuple->n_proto = flow_keys.basic.n_proto; tuple->ip_proto = flow_keys.basic.ip_proto; @@ -1756,7 +1766,7 @@ mlx5_tc_ct_del_ft_cb(struct mlx5_tc_ct_priv *ct_priv, struct mlx5_ct_ft *ft) /* We translate the tc filter with CT action to the following HW model: * * +---------------------+ - * + ft prio (tc chain) + + * + ft prio (tc chain) + * + original match + * +---------------------+ * | set chain miss mapping @@ -1766,7 +1776,7 @@ mlx5_tc_ct_del_ft_cb(struct mlx5_tc_ct_priv *ct_priv, struct mlx5_ct_ft *ft) * v * +---------------------+ * + pre_ct/pre_ct_nat + if matches +-------------------------+ - * + zone+nat match +---------------->+ post_act (see below) + + * + zone+nat match +---------------->+ post_act (see below) + * +---------------------+ set zone +-------------------------+ * | set zone * v @@ -1781,21 +1791,19 @@ mlx5_tc_ct_del_ft_cb(struct mlx5_tc_ct_priv *ct_priv, struct mlx5_ct_ft *ft) * | do nat (if needed) * v * +--------------+ - * + post_act + original filter actions + * + post_act + original filter actions * + fte_id match +------------------------> * +--------------+ */ static struct mlx5_flow_handle * __mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *ct_priv, - struct mlx5e_tc_flow *flow, struct mlx5_flow_spec *orig_spec, struct mlx5_flow_attr *attr) { bool nat = attr->ct_attr.ct_action & TCA_CT_ACT_NAT; struct mlx5e_priv *priv = netdev_priv(ct_priv->netdev); - struct mlx5e_tc_mod_hdr_acts pre_mod_acts = {}; + struct mlx5e_tc_mod_hdr_acts *pre_mod_acts; u32 attr_sz = ns_to_attr_sz(ct_priv->ns_type); - struct mlx5e_post_act_handle *handle; struct mlx5_flow_attr *pre_ct_attr; struct mlx5_modify_hdr *mod_hdr; struct mlx5_ct_flow *ct_flow; @@ -1818,14 +1826,6 @@ __mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *ct_priv, } ct_flow->ft = ft; - handle = mlx5e_tc_post_act_add(ct_priv->post_act, attr); - if (IS_ERR(handle)) { - err = PTR_ERR(handle); - ct_dbg("Failed to allocate post action handle"); - goto err_post_act_handle; - } - ct_flow->post_act_handle = handle; - /* Base flow attributes of both rules on original rule attribute */ ct_flow->pre_ct_attr = mlx5_alloc_flow_attr(ct_priv->ns_type); if (!ct_flow->pre_ct_attr) { @@ -1835,6 +1835,7 @@ __mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *ct_priv, pre_ct_attr = ct_flow->pre_ct_attr; memcpy(pre_ct_attr, attr, attr_sz); + pre_mod_acts = &pre_ct_attr->parse_attr->mod_hdr_acts; /* Modify the original rule's action to fwd and modify, leave decap */ pre_ct_attr->action = attr->action & MLX5_FLOW_CONTEXT_ACTION_DECAP; @@ -1853,30 +1854,22 @@ __mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *ct_priv, } ct_flow->chain_mapping = chain_mapping; - err = mlx5e_tc_match_to_reg_set(priv->mdev, &pre_mod_acts, ct_priv->ns_type, + err = mlx5e_tc_match_to_reg_set(priv->mdev, pre_mod_acts, ct_priv->ns_type, CHAIN_TO_REG, chain_mapping); if (err) { ct_dbg("Failed to set chain register mapping"); goto err_mapping; } - err = mlx5e_tc_post_act_set_handle(priv->mdev, handle, &pre_mod_acts); - if (err) { - ct_dbg("Failed to set post action handle"); - goto err_mapping; - } - /* If original flow is decap, we do it before going into ct table * so add a rewrite for the tunnel match_id. */ if ((pre_ct_attr->action & MLX5_FLOW_CONTEXT_ACTION_DECAP) && attr->chain == 0) { - u32 tun_id = mlx5e_tc_get_flow_tun_id(flow); - - err = mlx5e_tc_match_to_reg_set(priv->mdev, &pre_mod_acts, + err = mlx5e_tc_match_to_reg_set(priv->mdev, pre_mod_acts, ct_priv->ns_type, TUNNEL_TO_REG, - tun_id); + attr->tunnel_id); if (err) { ct_dbg("Failed to set tunnel register mapping"); goto err_mapping; @@ -1884,8 +1877,8 @@ __mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *ct_priv, } mod_hdr = mlx5_modify_header_alloc(priv->mdev, ct_priv->ns_type, - pre_mod_acts.num_actions, - pre_mod_acts.actions); + pre_mod_acts->num_actions, + pre_mod_acts->actions); if (IS_ERR(mod_hdr)) { err = PTR_ERR(mod_hdr); ct_dbg("Failed to create pre ct mod hdr"); @@ -1905,20 +1898,18 @@ __mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *ct_priv, } attr->ct_attr.ct_flow = ct_flow; - mlx5e_mod_hdr_dealloc(&pre_mod_acts); + mlx5e_mod_hdr_dealloc(pre_mod_acts); return ct_flow->pre_ct_rule; err_insert_orig: mlx5_modify_header_dealloc(priv->mdev, pre_ct_attr->modify_hdr); err_mapping: - mlx5e_mod_hdr_dealloc(&pre_mod_acts); + mlx5e_mod_hdr_dealloc(pre_mod_acts); mlx5_chains_put_chain_mapping(ct_priv->chains, ct_flow->chain_mapping); err_get_chain: kfree(ct_flow->pre_ct_attr); err_alloc_pre: - mlx5e_tc_post_act_del(ct_priv->post_act, handle); -err_post_act_handle: mlx5_tc_ct_del_ft_cb(ct_priv, ft); err_ft: kfree(ct_flow); @@ -1926,87 +1917,19 @@ __mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *ct_priv, return ERR_PTR(err); } -static struct mlx5_flow_handle * -__mlx5_tc_ct_flow_offload_clear(struct mlx5_tc_ct_priv *ct_priv, - struct mlx5_flow_spec *orig_spec, - struct mlx5_flow_attr *attr, - struct mlx5e_tc_mod_hdr_acts *mod_acts) -{ - struct mlx5e_priv *priv = netdev_priv(ct_priv->netdev); - u32 attr_sz = ns_to_attr_sz(ct_priv->ns_type); - struct mlx5_flow_attr *pre_ct_attr; - struct mlx5_modify_hdr *mod_hdr; - struct mlx5_flow_handle *rule; - struct mlx5_ct_flow *ct_flow; - int err; - - ct_flow = kzalloc(sizeof(*ct_flow), GFP_KERNEL); - if (!ct_flow) - return ERR_PTR(-ENOMEM); - - /* Base esw attributes on original rule attribute */ - pre_ct_attr = mlx5_alloc_flow_attr(ct_priv->ns_type); - if (!pre_ct_attr) { - err = -ENOMEM; - goto err_attr; - } - - memcpy(pre_ct_attr, attr, attr_sz); - - mod_hdr = mlx5_modify_header_alloc(priv->mdev, ct_priv->ns_type, - mod_acts->num_actions, - mod_acts->actions); - if (IS_ERR(mod_hdr)) { - err = PTR_ERR(mod_hdr); - ct_dbg("Failed to add create ct clear mod hdr"); - goto err_mod_hdr; - } - - pre_ct_attr->modify_hdr = mod_hdr; - - rule = mlx5_tc_rule_insert(priv, orig_spec, pre_ct_attr); - if (IS_ERR(rule)) { - err = PTR_ERR(rule); - ct_dbg("Failed to add ct clear rule"); - goto err_insert; - } - - attr->ct_attr.ct_flow = ct_flow; - ct_flow->pre_ct_attr = pre_ct_attr; - ct_flow->pre_ct_rule = rule; - return rule; - -err_insert: - mlx5_modify_header_dealloc(priv->mdev, mod_hdr); -err_mod_hdr: - netdev_warn(priv->netdev, - "Failed to offload ct clear flow, err %d\n", err); - kfree(pre_ct_attr); -err_attr: - kfree(ct_flow); - - return ERR_PTR(err); -} - struct mlx5_flow_handle * mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *priv, - struct mlx5e_tc_flow *flow, struct mlx5_flow_spec *spec, struct mlx5_flow_attr *attr, struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts) { - bool clear_action = attr->ct_attr.ct_action & TCA_CT_ACT_CLEAR; struct mlx5_flow_handle *rule; if (!priv) return ERR_PTR(-EOPNOTSUPP); mutex_lock(&priv->control_lock); - - if (clear_action) - rule = __mlx5_tc_ct_flow_offload_clear(priv, spec, attr, mod_hdr_acts); - else - rule = __mlx5_tc_ct_flow_offload(priv, flow, spec, attr); + rule = __mlx5_tc_ct_flow_offload(priv, spec, attr); mutex_unlock(&priv->control_lock); return rule; @@ -2014,21 +1937,17 @@ mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *priv, static void __mlx5_tc_ct_delete_flow(struct mlx5_tc_ct_priv *ct_priv, - struct mlx5e_tc_flow *flow, - struct mlx5_ct_flow *ct_flow) + struct mlx5_ct_flow *ct_flow, + struct mlx5_flow_attr *attr) { struct mlx5_flow_attr *pre_ct_attr = ct_flow->pre_ct_attr; struct mlx5e_priv *priv = netdev_priv(ct_priv->netdev); - mlx5_tc_rule_delete(priv, ct_flow->pre_ct_rule, - pre_ct_attr); + mlx5_tc_rule_delete(priv, ct_flow->pre_ct_rule, pre_ct_attr); mlx5_modify_header_dealloc(priv->mdev, pre_ct_attr->modify_hdr); - if (ct_flow->post_act_handle) { - mlx5_chains_put_chain_mapping(ct_priv->chains, ct_flow->chain_mapping); - mlx5e_tc_post_act_del(ct_priv->post_act, ct_flow->post_act_handle); - mlx5_tc_ct_del_ft_cb(ct_priv, ct_flow->ft); - } + mlx5_chains_put_chain_mapping(ct_priv->chains, ct_flow->chain_mapping); + mlx5_tc_ct_del_ft_cb(ct_priv, ct_flow->ft); kfree(ct_flow->pre_ct_attr); kfree(ct_flow); @@ -2036,7 +1955,6 @@ __mlx5_tc_ct_delete_flow(struct mlx5_tc_ct_priv *ct_priv, void mlx5_tc_ct_delete_flow(struct mlx5_tc_ct_priv *priv, - struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr) { struct mlx5_ct_flow *ct_flow = attr->ct_attr.ct_flow; @@ -2048,10 +1966,42 @@ mlx5_tc_ct_delete_flow(struct mlx5_tc_ct_priv *priv, return; mutex_lock(&priv->control_lock); - __mlx5_tc_ct_delete_flow(priv, flow, ct_flow); + __mlx5_tc_ct_delete_flow(priv, ct_flow, attr); mutex_unlock(&priv->control_lock); } +static int +mlx5_tc_ct_fs_init(struct mlx5_tc_ct_priv *ct_priv) +{ + struct mlx5_flow_table *post_ct = mlx5e_tc_post_act_get_ft(ct_priv->post_act); + struct mlx5_ct_fs_ops *fs_ops = mlx5_ct_fs_dmfs_ops_get(); + int err; + + if (ct_priv->ns_type == MLX5_FLOW_NAMESPACE_FDB && + ct_priv->dev->priv.steering->mode == MLX5_FLOW_STEERING_MODE_SMFS) { + ct_dbg("Using SMFS ct flow steering provider"); + fs_ops = mlx5_ct_fs_smfs_ops_get(); + } + + ct_priv->fs = kzalloc(sizeof(*ct_priv->fs) + fs_ops->priv_size, GFP_KERNEL); + if (!ct_priv->fs) + return -ENOMEM; + + ct_priv->fs->netdev = ct_priv->netdev; + ct_priv->fs->dev = ct_priv->dev; + ct_priv->fs_ops = fs_ops; + + err = ct_priv->fs_ops->init(ct_priv->fs, ct_priv->ct, ct_priv->ct_nat, post_ct); + if (err) + goto err_init; + + return 0; + +err_init: + kfree(ct_priv->fs); + return err; +} + static int mlx5_tc_ct_init_check_esw_support(struct mlx5_eswitch *esw, const char **err_msg) @@ -2190,8 +2140,14 @@ mlx5_tc_ct_init(struct mlx5e_priv *priv, struct mlx5_fs_chains *chains, if (rhashtable_init(&ct_priv->ct_tuples_nat_ht, &tuples_nat_ht_params)) goto err_ct_tuples_nat_ht; + err = mlx5_tc_ct_fs_init(ct_priv); + if (err) + goto err_init_fs; + return ct_priv; +err_init_fs: + rhashtable_destroy(&ct_priv->ct_tuples_nat_ht); err_ct_tuples_nat_ht: rhashtable_destroy(&ct_priv->ct_tuples_ht); err_ct_tuples_ht: @@ -2222,6 +2178,9 @@ mlx5_tc_ct_clean(struct mlx5_tc_ct_priv *ct_priv) chains = ct_priv->chains; + ct_priv->fs_ops->destroy(ct_priv->fs); + kfree(ct_priv->fs); + mlx5_chains_destroy_global_table(chains, ct_priv->ct_nat); mlx5_chains_destroy_global_table(chains, ct_priv->ct); mapping_destroy(ct_priv->zone_mapping); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.h index 99662af1e41a..36d3652bf829 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.h @@ -86,6 +86,8 @@ struct mlx5_ct_attr { #define REG_MAPPING_MLEN(reg) (mlx5e_tc_attr_to_reg_mappings[reg].mlen) #define REG_MAPPING_MOFFSET(reg) (mlx5e_tc_attr_to_reg_mappings[reg].moffset) +#define MLX5_CT_ZONE_BITS (mlx5e_tc_attr_to_reg_mappings[ZONE_TO_REG].mlen) +#define MLX5_CT_ZONE_MASK GENMASK(MLX5_CT_ZONE_BITS - 1, 0) #if IS_ENABLED(CONFIG_MLX5_TC_CT) @@ -116,13 +118,11 @@ mlx5_tc_ct_parse_action(struct mlx5_tc_ct_priv *priv, struct mlx5_flow_handle * mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *priv, - struct mlx5e_tc_flow *flow, struct mlx5_flow_spec *spec, struct mlx5_flow_attr *attr, struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts); void mlx5_tc_ct_delete_flow(struct mlx5_tc_ct_priv *priv, - struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr); bool @@ -183,7 +183,6 @@ mlx5_tc_ct_parse_action(struct mlx5_tc_ct_priv *priv, static inline struct mlx5_flow_handle * mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *priv, - struct mlx5e_tc_flow *flow, struct mlx5_flow_spec *spec, struct mlx5_flow_attr *attr, struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts) @@ -193,7 +192,6 @@ mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *priv, static inline void mlx5_tc_ct_delete_flow(struct mlx5_tc_ct_priv *priv, - struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr) { } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h index 70b40ae384e4..3b74a6fd5c43 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h @@ -38,9 +38,9 @@ struct mlx5e_tc_flow_parse_attr { struct mlx5e_mpls_info mpls_info[MLX5_MAX_FLOW_FWD_VPORTS]; struct net_device *filter_dev; struct mlx5_flow_spec spec; + struct pedit_headers_action hdrs[__PEDIT_CMD_MAX]; struct mlx5e_tc_mod_hdr_acts mod_hdr_acts; int mirred_ifindex[MLX5_MAX_FLOW_FWD_VPORTS]; - struct ethhdr eth; struct mlx5e_tc_act_parse_state parse_state; }; @@ -108,10 +108,20 @@ struct mlx5e_tc_flow { struct rcu_head rcu_head; struct completion init_done; struct completion del_hw_done; - int tunnel_id; /* the mapped tunnel id of this flow */ struct mlx5_flow_attr *attr; + struct list_head attrs; }; +struct mlx5_flow_handle * +mlx5e_tc_rule_offload(struct mlx5e_priv *priv, + struct mlx5_flow_spec *spec, + struct mlx5_flow_attr *attr); + +void +mlx5e_tc_rule_unoffload(struct mlx5e_priv *priv, + struct mlx5_flow_handle *rule, + struct mlx5_flow_attr *attr); + u8 mlx5e_tc_get_ip_version(struct mlx5_flow_spec *spec, bool outer); struct mlx5_flow_handle * @@ -120,6 +130,12 @@ mlx5e_tc_offload_fdb_rules(struct mlx5_eswitch *esw, struct mlx5_flow_spec *spec, struct mlx5_flow_attr *attr); +struct mlx5_flow_attr * +mlx5e_tc_get_encap_attr(struct mlx5e_tc_flow *flow); + +void mlx5e_tc_unoffload_flow_post_acts(struct mlx5e_tc_flow *flow); +int mlx5e_tc_offload_flow_post_acts(struct mlx5e_tc_flow *flow); + bool mlx5e_is_eswitch_flow(struct mlx5e_tc_flow *flow); bool mlx5e_is_ft_flow(struct mlx5e_tc_flow *flow); bool mlx5e_is_offloaded_flow(struct mlx5e_tc_flow *flow); @@ -174,6 +190,7 @@ struct mlx5_flow_handle * mlx5e_tc_offload_to_slow_path(struct mlx5_eswitch *esw, struct mlx5e_tc_flow *flow, struct mlx5_flow_spec *spec); + void mlx5e_tc_unoffload_fdb_rules(struct mlx5_eswitch *esw, struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c index d39d0dae22fc..5aff97914367 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c @@ -173,19 +173,29 @@ void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv, list_for_each_entry(flow, flow_list, tmp_list) { if (!mlx5e_is_offloaded_flow(flow) || !flow_flag_test(flow, SLOW)) continue; - attr = flow->attr; - esw_attr = attr->esw_attr; - spec = &attr->parse_attr->spec; + spec = &flow->attr->parse_attr->spec; + + attr = mlx5e_tc_get_encap_attr(flow); + esw_attr = attr->esw_attr; esw_attr->dests[flow->tmp_entry_index].pkt_reformat = e->pkt_reformat; esw_attr->dests[flow->tmp_entry_index].flags |= MLX5_ESW_DEST_ENCAP_VALID; /* Do not offload flows with unresolved neighbors */ if (!mlx5e_tc_flow_all_encaps_valid(esw_attr)) continue; + + err = mlx5e_tc_offload_flow_post_acts(flow); + if (err) { + mlx5_core_warn(priv->mdev, "Failed to update flow post acts, %d\n", + err); + continue; + } + /* update from slow path rule to encap rule */ - rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, attr); + rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, flow->attr); if (IS_ERR(rule)) { + mlx5e_tc_unoffload_flow_post_acts(flow); err = PTR_ERR(rule); mlx5_core_warn(priv->mdev, "Failed to update cached encapsulation flow, %d\n", err); @@ -214,12 +224,13 @@ void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv, list_for_each_entry(flow, flow_list, tmp_list) { if (!mlx5e_is_offloaded_flow(flow) || flow_flag_test(flow, SLOW)) continue; - attr = flow->attr; - esw_attr = attr->esw_attr; - spec = &attr->parse_attr->spec; + spec = &flow->attr->parse_attr->spec; /* update from encap rule to slow path rule */ rule = mlx5e_tc_offload_to_slow_path(esw, flow, spec); + + attr = mlx5e_tc_get_encap_attr(flow); + esw_attr = attr->esw_attr; /* mark the flow's encap dest as non-valid */ esw_attr->dests[flow->tmp_entry_index].flags &= ~MLX5_ESW_DEST_ENCAP_VALID; @@ -230,7 +241,8 @@ void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv, continue; } - mlx5e_tc_unoffload_fdb_rules(esw, flow, attr); + mlx5e_tc_unoffload_fdb_rules(esw, flow, flow->attr); + mlx5e_tc_unoffload_flow_post_acts(flow); flow->rule[0] = rule; /* was unset when fast path rule removed */ flow_flag_set(flow, OFFLOADED); @@ -488,12 +500,17 @@ static void mlx5e_detach_encap_route(struct mlx5e_priv *priv, int out_index); void mlx5e_detach_encap(struct mlx5e_priv *priv, - struct mlx5e_tc_flow *flow, int out_index) + struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr, + int out_index) { struct mlx5e_encap_entry *e = flow->encaps[out_index].e; struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; - if (flow->attr->esw_attr->dests[out_index].flags & + if (!mlx5e_is_eswitch_flow(flow)) + return; + + if (attr->esw_attr->dests[out_index].flags & MLX5_ESW_DEST_CHAIN_WITH_SRC_PORT_CHANGE) mlx5e_detach_encap_route(priv, flow, out_index); @@ -733,6 +750,7 @@ static unsigned int mlx5e_route_tbl_get_last_update(struct mlx5e_priv *priv) static int mlx5e_attach_encap_route(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr, struct mlx5e_encap_entry *e, bool new_encap_entry, unsigned long tbl_time_before, @@ -740,6 +758,7 @@ static int mlx5e_attach_encap_route(struct mlx5e_priv *priv, int mlx5e_attach_encap(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr, struct net_device *mirred_dev, int out_index, struct netlink_ext_ack *extack, @@ -748,7 +767,6 @@ int mlx5e_attach_encap(struct mlx5e_priv *priv, { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5e_tc_flow_parse_attr *parse_attr; - struct mlx5_flow_attr *attr = flow->attr; const struct ip_tunnel_info *tun_info; const struct mlx5e_mpls_info *mpls_info; unsigned long tbl_time_before = 0; @@ -837,8 +855,8 @@ int mlx5e_attach_encap(struct mlx5e_priv *priv, e->compl_result = 1; attach_flow: - err = mlx5e_attach_encap_route(priv, flow, e, entry_created, tbl_time_before, - out_index); + err = mlx5e_attach_encap_route(priv, flow, attr, e, entry_created, + tbl_time_before, out_index); if (err) goto out_err; @@ -888,20 +906,18 @@ int mlx5e_attach_decap(struct mlx5e_priv *priv, struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5_esw_flow_attr *attr = flow->attr->esw_attr; struct mlx5_pkt_reformat_params reformat_params; - struct mlx5e_tc_flow_parse_attr *parse_attr; struct mlx5e_decap_entry *d; struct mlx5e_decap_key key; uintptr_t hash_key; int err = 0; - parse_attr = flow->attr->parse_attr; - if (sizeof(parse_attr->eth) > MLX5_CAP_ESW(priv->mdev, max_encap_header_size)) { + if (sizeof(attr->eth) > MLX5_CAP_ESW(priv->mdev, max_encap_header_size)) { NL_SET_ERR_MSG_MOD(extack, "encap header larger than max supported"); return -EOPNOTSUPP; } - key.key = parse_attr->eth; + key.key = attr->eth; hash_key = hash_decap_info(&key); mutex_lock(&esw->offloads.decap_tbl_lock); d = mlx5e_decap_get(priv, &key, hash_key); @@ -931,8 +947,8 @@ int mlx5e_attach_decap(struct mlx5e_priv *priv, memset(&reformat_params, 0, sizeof(reformat_params)); reformat_params.type = MLX5_REFORMAT_TYPE_L3_TUNNEL_TO_L2; - reformat_params.size = sizeof(parse_attr->eth); - reformat_params.data = &parse_attr->eth; + reformat_params.size = sizeof(attr->eth); + reformat_params.data = &attr->eth; d->pkt_reformat = mlx5_packet_reformat_alloc(priv->mdev, &reformat_params, MLX5_FLOW_NAMESPACE_FDB); @@ -1201,6 +1217,7 @@ int mlx5e_attach_decap_route(struct mlx5e_priv *priv, static int mlx5e_attach_encap_route(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr, struct mlx5e_encap_entry *e, bool new_encap_entry, unsigned long tbl_time_before, @@ -1209,7 +1226,6 @@ static int mlx5e_attach_encap_route(struct mlx5e_priv *priv, struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; unsigned long tbl_time_after = tbl_time_before; struct mlx5e_tc_flow_parse_attr *parse_attr; - struct mlx5_flow_attr *attr = flow->attr; const struct ip_tunnel_info *tun_info; struct mlx5_esw_flow_attr *esw_attr; struct mlx5e_route_entry *r; @@ -1360,17 +1376,19 @@ static void mlx5e_reoffload_encap(struct mlx5e_priv *priv, list_for_each_entry(flow, encap_flows, tmp_list) { struct mlx5e_tc_flow_parse_attr *parse_attr; - struct mlx5_flow_attr *attr = flow->attr; struct mlx5_esw_flow_attr *esw_attr; struct mlx5_flow_handle *rule; + struct mlx5_flow_attr *attr; struct mlx5_flow_spec *spec; if (flow_flag_test(flow, FAILED)) continue; + spec = &flow->attr->parse_attr->spec; + + attr = mlx5e_tc_get_encap_attr(flow); esw_attr = attr->esw_attr; parse_attr = attr->parse_attr; - spec = &parse_attr->spec; err = mlx5e_update_vf_tunnel(esw, esw_attr, &parse_attr->mod_hdr_acts, e->out_dev, e->route_dev_ifindex, @@ -1380,7 +1398,7 @@ static void mlx5e_reoffload_encap(struct mlx5e_priv *priv, continue; } - err = mlx5e_tc_add_flow_mod_hdr(priv, parse_attr, flow); + err = mlx5e_tc_add_flow_mod_hdr(priv, flow, attr); if (err) { mlx5_core_warn(priv->mdev, "Failed to update flow mod_hdr err=%d", err); @@ -1392,9 +1410,18 @@ static void mlx5e_reoffload_encap(struct mlx5e_priv *priv, esw_attr->dests[flow->tmp_entry_index].flags |= MLX5_ESW_DEST_ENCAP_VALID; if (!mlx5e_tc_flow_all_encaps_valid(esw_attr)) goto offload_to_slow_path; + + err = mlx5e_tc_offload_flow_post_acts(flow); + if (err) { + mlx5_core_warn(priv->mdev, "Failed to update flow post acts, %d\n", + err); + goto offload_to_slow_path; + } + /* update from slow path rule to encap rule */ - rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, attr); + rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, flow->attr); if (IS_ERR(rule)) { + mlx5e_tc_unoffload_flow_post_acts(flow); err = PTR_ERR(rule); mlx5_core_warn(priv->mdev, "Failed to update cached encapsulation flow, %d\n", err); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.h index 3391504d9a08..d542b8476491 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.h @@ -7,15 +7,19 @@ #include "tc_priv.h" void mlx5e_detach_encap(struct mlx5e_priv *priv, - struct mlx5e_tc_flow *flow, int out_index); + struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr, + int out_index); int mlx5e_attach_encap(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr, struct net_device *mirred_dev, int out_index, struct netlink_ext_ack *extack, struct net_device **encap_dev, bool *encap_valid); + int mlx5e_attach_decap(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h b/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h index b789af07829c..c208ea307bff 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h @@ -9,19 +9,6 @@ #define MLX5E_TX_WQE_EMPTY_DS_COUNT (sizeof(struct mlx5e_tx_wqe) / MLX5_SEND_WQE_DS) -/* The mult of MLX5_SEND_WQE_MAX_WQEBBS * MLX5_SEND_WQEBB_NUM_DS - * (16 * 4 == 64) does not fit in the 6-bit DS field of Ctrl Segment. - * We use a bound lower that MLX5_SEND_WQE_MAX_WQEBBS to let a - * full-session WQE be cache-aligned. - */ -#if L1_CACHE_BYTES < 128 -#define MLX5E_TX_MPW_MAX_WQEBBS (MLX5_SEND_WQE_MAX_WQEBBS - 1) -#else -#define MLX5E_TX_MPW_MAX_WQEBBS (MLX5_SEND_WQE_MAX_WQEBBS - 2) -#endif - -#define MLX5E_TX_MPW_MAX_NUM_DS (MLX5E_TX_MPW_MAX_WQEBBS * MLX5_SEND_WQEBB_NUM_DS) - #define INL_HDR_START_SZ (sizeof(((struct mlx5_wqe_eth_seg *)NULL)->inline_hdr.start)) #define MLX5E_RX_ERR_CQE(cqe) (get_cqe_opcode(cqe) != MLX5_CQE_RESP_SEND) @@ -57,10 +44,8 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget); int mlx5e_poll_ico_cq(struct mlx5e_cq *cq); /* RX */ -void mlx5e_page_dma_unmap(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info); -void mlx5e_page_release_dynamic(struct mlx5e_rq *rq, - struct mlx5e_dma_info *dma_info, - bool recycle); +void mlx5e_page_dma_unmap(struct mlx5e_rq *rq, struct page *page); +void mlx5e_page_release_dynamic(struct mlx5e_rq *rq, struct page *page, bool recycle); INDIRECT_CALLABLE_DECLARE(bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq)); INDIRECT_CALLABLE_DECLARE(bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq)); int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget); @@ -68,8 +53,6 @@ void mlx5e_free_rx_descs(struct mlx5e_rq *rq); void mlx5e_free_rx_in_progress_descs(struct mlx5e_rq *rq); /* TX */ -u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb, - struct net_device *sb_dev); netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev); bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget); void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq); @@ -308,9 +291,9 @@ mlx5e_tx_dma_unmap(struct device *pdev, struct mlx5e_sq_dma *dma) void mlx5e_sq_xmit_simple(struct mlx5e_txqsq *sq, struct sk_buff *skb, bool xmit_more); void mlx5e_tx_mpwqe_ensure_complete(struct mlx5e_txqsq *sq); -static inline bool mlx5e_tx_mpwqe_is_full(struct mlx5e_tx_mpwqe *session) +static inline bool mlx5e_tx_mpwqe_is_full(struct mlx5e_tx_mpwqe *session, u8 max_sq_mpw_wqebbs) { - return session->ds_count == MLX5E_TX_MPW_MAX_NUM_DS; + return session->ds_count == max_sq_mpw_wqebbs * MLX5_SEND_WQEBB_NUM_DS; } static inline void mlx5e_rqwq_reset(struct mlx5e_rq *rq) @@ -431,10 +414,10 @@ mlx5e_set_eseg_swp(struct sk_buff *skb, struct mlx5_wqe_eth_seg *eseg, } } -static inline u16 mlx5e_stop_room_for_wqe(u16 wqe_size) -{ - BUILD_BUG_ON(PAGE_SIZE / MLX5_SEND_WQE_BB < MLX5_SEND_WQE_MAX_WQEBBS); +#define MLX5E_STOP_ROOM(wqebbs) ((wqebbs) * 2 - 1) +static inline u16 mlx5e_stop_room_for_wqe(struct mlx5_core_dev *mdev, u16 wqe_size) +{ /* A WQE must not cross the page boundary, hence two conditions: * 1. Its size must not exceed the page size. * 2. If the WQE size is X, and the space remaining in a page is less @@ -443,18 +426,28 @@ static inline u16 mlx5e_stop_room_for_wqe(u16 wqe_size) * stop room of X-1 + X. * WQE size is also limited by the hardware limit. */ + WARN_ONCE(wqe_size > mlx5e_get_max_sq_wqebbs(mdev), + "wqe_size %u is greater than max SQ WQEBBs %u", + wqe_size, mlx5e_get_max_sq_wqebbs(mdev)); - if (__builtin_constant_p(wqe_size)) - BUILD_BUG_ON(wqe_size > MLX5_SEND_WQE_MAX_WQEBBS); - else - WARN_ON_ONCE(wqe_size > MLX5_SEND_WQE_MAX_WQEBBS); - return wqe_size * 2 - 1; + return MLX5E_STOP_ROOM(wqe_size); +} + +static inline u16 mlx5e_stop_room_for_max_wqe(struct mlx5_core_dev *mdev) +{ + return MLX5E_STOP_ROOM(mlx5e_get_max_sq_wqebbs(mdev)); } static inline bool mlx5e_icosq_can_post_wqe(struct mlx5e_icosq *sq, u16 wqe_size) { - u16 room = sq->reserved_room + mlx5e_stop_room_for_wqe(wqe_size); + u16 room = sq->reserved_room; + + WARN_ONCE(wqe_size > sq->max_sq_wqebbs, + "wqe_size %u is greater than max SQ WQEBBs %u", + wqe_size, sq->max_sq_wqebbs); + + room += MLX5E_STOP_ROOM(wqe_size); return mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, room); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c index 56e10c84a706..8f321a6c0809 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c @@ -57,12 +57,14 @@ int mlx5e_xdp_max_mtu(struct mlx5e_params *params, struct mlx5e_xsk_param *xsk) static inline bool mlx5e_xmit_xdp_buff(struct mlx5e_xdpsq *sq, struct mlx5e_rq *rq, - struct mlx5e_dma_info *di, struct xdp_buff *xdp) + struct page *page, struct xdp_buff *xdp) { + struct skb_shared_info *sinfo = NULL; struct mlx5e_xmit_data xdptxd; struct mlx5e_xdp_info xdpi; struct xdp_frame *xdpf; dma_addr_t dma_addr; + int i; xdpf = xdp_convert_buff_to_frame(xdp); if (unlikely(!xdpf)) @@ -96,46 +98,77 @@ mlx5e_xmit_xdp_buff(struct mlx5e_xdpsq *sq, struct mlx5e_rq *rq, xdptxd.dma_addr = dma_addr; xdpi.frame.xdpf = xdpf; xdpi.frame.dma_addr = dma_addr; - } else { - /* Driver assumes that xdp_convert_buff_to_frame returns - * an xdp_frame that points to the same memory region as - * the original xdp_buff. It allows to map the memory only - * once and to use the DMA_BIDIRECTIONAL mode. - */ - xdpi.mode = MLX5E_XDP_XMIT_MODE_PAGE; + if (unlikely(!INDIRECT_CALL_2(sq->xmit_xdp_frame, mlx5e_xmit_xdp_frame_mpwqe, + mlx5e_xmit_xdp_frame, sq, &xdptxd, NULL, 0))) + return false; - dma_addr = di->addr + (xdpf->data - (void *)xdpf); - dma_sync_single_for_device(sq->pdev, dma_addr, xdptxd.len, - DMA_TO_DEVICE); - - xdptxd.dma_addr = dma_addr; - xdpi.page.rq = rq; - xdpi.page.di = *di; + mlx5e_xdpi_fifo_push(&sq->db.xdpi_fifo, &xdpi); + return true; } - return INDIRECT_CALL_2(sq->xmit_xdp_frame, mlx5e_xmit_xdp_frame_mpwqe, - mlx5e_xmit_xdp_frame, sq, &xdptxd, &xdpi, 0); + /* Driver assumes that xdp_convert_buff_to_frame returns an xdp_frame + * that points to the same memory region as the original xdp_buff. It + * allows to map the memory only once and to use the DMA_BIDIRECTIONAL + * mode. + */ + + xdpi.mode = MLX5E_XDP_XMIT_MODE_PAGE; + xdpi.page.rq = rq; + + dma_addr = page_pool_get_dma_addr(page) + (xdpf->data - (void *)xdpf); + dma_sync_single_for_device(sq->pdev, dma_addr, xdptxd.len, DMA_TO_DEVICE); + + if (unlikely(xdp_frame_has_frags(xdpf))) { + sinfo = xdp_get_shared_info_from_frame(xdpf); + + for (i = 0; i < sinfo->nr_frags; i++) { + skb_frag_t *frag = &sinfo->frags[i]; + dma_addr_t addr; + u32 len; + + addr = page_pool_get_dma_addr(skb_frag_page(frag)) + + skb_frag_off(frag); + len = skb_frag_size(frag); + dma_sync_single_for_device(sq->pdev, addr, len, + DMA_TO_DEVICE); + } + } + + xdptxd.dma_addr = dma_addr; + + if (unlikely(!INDIRECT_CALL_2(sq->xmit_xdp_frame, mlx5e_xmit_xdp_frame_mpwqe, + mlx5e_xmit_xdp_frame, sq, &xdptxd, sinfo, 0))) + return false; + + xdpi.page.page = page; + mlx5e_xdpi_fifo_push(&sq->db.xdpi_fifo, &xdpi); + + if (unlikely(xdp_frame_has_frags(xdpf))) { + for (i = 0; i < sinfo->nr_frags; i++) { + skb_frag_t *frag = &sinfo->frags[i]; + + xdpi.page.page = skb_frag_page(frag); + mlx5e_xdpi_fifo_push(&sq->db.xdpi_fifo, &xdpi); + } + } + + return true; } /* returns true if packet was consumed by xdp */ -bool mlx5e_xdp_handle(struct mlx5e_rq *rq, struct mlx5e_dma_info *di, - u32 *len, struct xdp_buff *xdp) +bool mlx5e_xdp_handle(struct mlx5e_rq *rq, struct page *page, + struct bpf_prog *prog, struct xdp_buff *xdp) { - struct bpf_prog *prog = rcu_dereference(rq->xdp_prog); u32 act; int err; - if (!prog) - return false; - act = bpf_prog_run_xdp(prog, xdp); switch (act) { case XDP_PASS: - *len = xdp->data_end - xdp->data; return false; case XDP_TX: - if (unlikely(!mlx5e_xmit_xdp_buff(rq->xdpsq, rq, di, xdp))) + if (unlikely(!mlx5e_xmit_xdp_buff(rq->xdpsq, rq, page, xdp))) goto xdp_abort; __set_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags); /* non-atomic */ return true; @@ -147,7 +180,7 @@ bool mlx5e_xdp_handle(struct mlx5e_rq *rq, struct mlx5e_dma_info *di, __set_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags); __set_bit(MLX5E_RQ_FLAG_XDP_REDIRECT, rq->flags); if (xdp->rxq->mem.type != MEM_TYPE_XSK_BUFF_POOL) - mlx5e_page_dma_unmap(rq, di); + mlx5e_page_dma_unmap(rq, page); rq->stats->xdp_redirect++; return true; default: @@ -199,7 +232,7 @@ static void mlx5e_xdp_mpwqe_session_start(struct mlx5e_xdpsq *sq) struct mlx5e_tx_wqe *wqe; u16 pi; - pi = mlx5e_xdpsq_get_next_pi(sq, MLX5E_TX_MPW_MAX_WQEBBS); + pi = mlx5e_xdpsq_get_next_pi(sq, sq->max_sq_mpw_wqebbs); wqe = MLX5E_TX_FETCH_WQE(sq, pi); net_prefetchw(wqe->data); @@ -245,10 +278,8 @@ enum { INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check_mpwqe(struct mlx5e_xdpsq *sq) { if (unlikely(!sq->mpwqe.wqe)) { - const u16 stop_room = mlx5e_stop_room_for_wqe(MLX5_SEND_WQE_MAX_WQEBBS); - if (unlikely(!mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, - stop_room))) { + sq->stop_room))) { /* SQ is full, ring doorbell */ mlx5e_xmit_xdp_doorbell(sq); sq->stats->full++; @@ -261,13 +292,27 @@ INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check_mpwqe(struct mlx5e_xdpsq return MLX5E_XDP_CHECK_OK; } +INDIRECT_CALLABLE_SCOPE bool +mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, + struct skb_shared_info *sinfo, int check_result); + INDIRECT_CALLABLE_SCOPE bool mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, - struct mlx5e_xdp_info *xdpi, int check_result) + struct skb_shared_info *sinfo, int check_result) { struct mlx5e_tx_mpwqe *session = &sq->mpwqe; struct mlx5e_xdpsq_stats *stats = sq->stats; + if (unlikely(sinfo)) { + /* MPWQE is enabled, but a multi-buffer packet is queued for + * transmission. MPWQE can't send fragmented packets, so close + * the current session and fall back to a regular WQE. + */ + if (unlikely(sq->mpwqe.wqe)) + mlx5e_xdp_mpwqe_complete(sq); + return mlx5e_xmit_xdp_frame(sq, xdptxd, sinfo, 0); + } + if (unlikely(xdptxd->len > sq->hw_mtu)) { stats->err++; return false; @@ -288,17 +333,16 @@ mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptx mlx5e_xdp_mpwqe_add_dseg(sq, xdptxd, stats); - if (unlikely(mlx5e_xdp_mpqwe_is_full(session))) + if (unlikely(mlx5e_xdp_mpqwe_is_full(session, sq->max_sq_mpw_wqebbs))) mlx5e_xdp_mpwqe_complete(sq); - mlx5e_xdpi_fifo_push(&sq->db.xdpi_fifo, xdpi); stats->xmit++; return true; } -INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check(struct mlx5e_xdpsq *sq) +static int mlx5e_xmit_xdp_frame_check_stop_room(struct mlx5e_xdpsq *sq, int stop_room) { - if (unlikely(!mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, 1))) { + if (unlikely(!mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, stop_room))) { /* SQ is full, ring doorbell */ mlx5e_xmit_xdp_doorbell(sq); sq->stats->full++; @@ -308,45 +352,76 @@ INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check(struct mlx5e_xdpsq *sq) return MLX5E_XDP_CHECK_OK; } +INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check(struct mlx5e_xdpsq *sq) +{ + return mlx5e_xmit_xdp_frame_check_stop_room(sq, 1); +} + INDIRECT_CALLABLE_SCOPE bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, - struct mlx5e_xdp_info *xdpi, int check_result) + struct skb_shared_info *sinfo, int check_result) { struct mlx5_wq_cyc *wq = &sq->wq; - u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); - struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); - - struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; - struct mlx5_wqe_eth_seg *eseg = &wqe->eth; - struct mlx5_wqe_data_seg *dseg = wqe->data; + struct mlx5_wqe_ctrl_seg *cseg; + struct mlx5_wqe_data_seg *dseg; + struct mlx5_wqe_eth_seg *eseg; + struct mlx5e_tx_wqe *wqe; dma_addr_t dma_addr = xdptxd->dma_addr; u32 dma_len = xdptxd->len; + u16 ds_cnt, inline_hdr_sz; + u8 num_wqebbs = 1; + int num_frags = 0; + u16 pi; struct mlx5e_xdpsq_stats *stats = sq->stats; - net_prefetchw(wqe); - if (unlikely(dma_len < MLX5E_XDP_MIN_INLINE || sq->hw_mtu < dma_len)) { stats->err++; return false; } - if (!check_result) - check_result = mlx5e_xmit_xdp_frame_check(sq); + ds_cnt = MLX5E_TX_WQE_EMPTY_DS_COUNT + 1; + if (sq->min_inline_mode != MLX5_INLINE_MODE_NONE) + ds_cnt++; + + /* check_result must be 0 if sinfo is passed. */ + if (!check_result) { + int stop_room = 1; + + if (unlikely(sinfo)) { + ds_cnt += sinfo->nr_frags; + num_frags = sinfo->nr_frags; + num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS); + /* Assuming MLX5_CAP_GEN(mdev, max_wqe_sz_sq) is big + * enough to hold all fragments. + */ + stop_room = MLX5E_STOP_ROOM(num_wqebbs); + } + + check_result = mlx5e_xmit_xdp_frame_check_stop_room(sq, stop_room); + } if (unlikely(check_result < 0)) return false; - cseg->fm_ce_se = 0; + pi = mlx5e_xdpsq_get_next_pi(sq, num_wqebbs); + wqe = mlx5_wq_cyc_get_wqe(wq, pi); + net_prefetchw(wqe); + + cseg = &wqe->ctrl; + eseg = &wqe->eth; + dseg = wqe->data; + + inline_hdr_sz = 0; /* copy the inline part if required */ if (sq->min_inline_mode != MLX5_INLINE_MODE_NONE) { memcpy(eseg->inline_hdr.start, xdptxd->data, sizeof(eseg->inline_hdr.start)); - eseg->inline_hdr.sz = cpu_to_be16(MLX5E_XDP_MIN_INLINE); memcpy(dseg, xdptxd->data + sizeof(eseg->inline_hdr.start), MLX5E_XDP_MIN_INLINE - sizeof(eseg->inline_hdr.start)); dma_len -= MLX5E_XDP_MIN_INLINE; dma_addr += MLX5E_XDP_MIN_INLINE; + inline_hdr_sz = MLX5E_XDP_MIN_INLINE; dseg++; } @@ -356,11 +431,45 @@ mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | MLX5_OPCODE_SEND); - sq->pc++; + if (unlikely(test_bit(MLX5E_SQ_STATE_XDP_MULTIBUF, &sq->state))) { + u8 num_pkts = 1 + num_frags; + int i; + + memset(&cseg->trailer, 0, sizeof(cseg->trailer)); + memset(eseg, 0, sizeof(*eseg) - sizeof(eseg->trailer)); + + eseg->inline_hdr.sz = cpu_to_be16(inline_hdr_sz); + dseg->lkey = sq->mkey_be; + + for (i = 0; i < num_frags; i++) { + skb_frag_t *frag = &sinfo->frags[i]; + dma_addr_t addr; + + addr = page_pool_get_dma_addr(skb_frag_page(frag)) + + skb_frag_off(frag); + + dseg++; + dseg->addr = cpu_to_be64(addr); + dseg->byte_count = cpu_to_be32(skb_frag_size(frag)); + dseg->lkey = sq->mkey_be; + } + + cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt); + + sq->db.wqe_info[pi] = (struct mlx5e_xdp_wqe_info) { + .num_wqebbs = num_wqebbs, + .num_pkts = num_pkts, + }; + + sq->pc += num_wqebbs; + } else { + cseg->fm_ce_se = 0; + + sq->pc++; + } sq->doorbell_cseg = cseg; - mlx5e_xdpi_fifo_push(&sq->db.xdpi_fifo, xdpi); stats->xmit++; return true; } @@ -386,7 +495,7 @@ static void mlx5e_free_xdpsq_desc(struct mlx5e_xdpsq *sq, break; case MLX5E_XDP_XMIT_MODE_PAGE: /* XDP_TX from the regular RQ */ - mlx5e_page_release_dynamic(xdpi.page.rq, &xdpi.page.di, recycle); + mlx5e_page_release_dynamic(xdpi.page.rq, xdpi.page.page, recycle); break; case MLX5E_XDP_XMIT_MODE_XSK: /* AF_XDP send */ @@ -539,12 +648,13 @@ int mlx5e_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames, xdpi.frame.dma_addr = xdptxd.dma_addr; ret = INDIRECT_CALL_2(sq->xmit_xdp_frame, mlx5e_xmit_xdp_frame_mpwqe, - mlx5e_xmit_xdp_frame, sq, &xdptxd, &xdpi, 0); + mlx5e_xmit_xdp_frame, sq, &xdptxd, NULL, 0); if (unlikely(!ret)) { dma_unmap_single(sq->pdev, xdptxd.dma_addr, xdptxd.len, DMA_TO_DEVICE); break; } + mlx5e_xdpi_fifo_push(&sq->db.xdpi_fifo, &xdpi); nxmit++; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h index 8d991c3b7a50..287e17911251 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h @@ -38,7 +38,6 @@ #include "en/txrx.h" #define MLX5E_XDP_MIN_INLINE (ETH_HLEN + VLAN_HLEN) -#define MLX5E_XDP_TX_DS_COUNT (MLX5E_TX_WQE_EMPTY_DS_COUNT + 1 /* SG DS */) #define MLX5E_XDP_INLINE_WQE_MAX_DS_CNT 16 #define MLX5E_XDP_INLINE_WQE_SZ_THRSD \ @@ -47,8 +46,8 @@ struct mlx5e_xsk_param; int mlx5e_xdp_max_mtu(struct mlx5e_params *params, struct mlx5e_xsk_param *xsk); -bool mlx5e_xdp_handle(struct mlx5e_rq *rq, struct mlx5e_dma_info *di, - u32 *len, struct xdp_buff *xdp); +bool mlx5e_xdp_handle(struct mlx5e_rq *rq, struct page *page, + struct bpf_prog *prog, struct xdp_buff *xdp); void mlx5e_xdp_mpwqe_complete(struct mlx5e_xdpsq *sq); bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq); void mlx5e_free_xdpsq_descs(struct mlx5e_xdpsq *sq); @@ -59,11 +58,11 @@ int mlx5e_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames, INDIRECT_CALLABLE_DECLARE(bool mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, - struct mlx5e_xdp_info *xdpi, + struct skb_shared_info *sinfo, int check_result)); INDIRECT_CALLABLE_DECLARE(bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, - struct mlx5e_xdp_info *xdpi, + struct skb_shared_info *sinfo, int check_result)); INDIRECT_CALLABLE_DECLARE(int mlx5e_xmit_xdp_frame_check_mpwqe(struct mlx5e_xdpsq *sq)); INDIRECT_CALLABLE_DECLARE(int mlx5e_xmit_xdp_frame_check(struct mlx5e_xdpsq *sq)); @@ -123,12 +122,13 @@ static inline bool mlx5e_xdp_get_inline_state(struct mlx5e_xdpsq *sq, bool cur) return cur; } -static inline bool mlx5e_xdp_mpqwe_is_full(struct mlx5e_tx_mpwqe *session) +static inline bool mlx5e_xdp_mpqwe_is_full(struct mlx5e_tx_mpwqe *session, u8 max_sq_mpw_wqebbs) { if (session->inline_on) return session->ds_count + MLX5E_XDP_INLINE_WQE_MAX_DS_CNT > - MLX5E_TX_MPW_MAX_NUM_DS; - return mlx5e_tx_mpwqe_is_full(session); + max_sq_mpw_wqebbs * MLX5_SEND_WQEBB_NUM_DS; + + return mlx5e_tx_mpwqe_is_full(session, max_sq_mpw_wqebbs); } struct mlx5e_xdp_wqe_info { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c index 8e7b877d8a12..021da085e603 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c @@ -4,6 +4,7 @@ #include "rx.h" #include "en/xdp.h" #include +#include /* RX data path */ @@ -30,7 +31,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, u32 page_idx) { struct xdp_buff *xdp = wi->umr.dma_info[page_idx].xsk; - u32 cqe_bcnt32 = cqe_bcnt; + struct bpf_prog *prog; /* Check packet size. Note LRO doesn't use linear SKB */ if (unlikely(cqe_bcnt > rq->hw_mtu)) { @@ -45,7 +46,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, */ WARN_ON_ONCE(head_offset); - xdp->data_end = xdp->data + cqe_bcnt32; + xdp->data_end = xdp->data + cqe_bcnt; xdp_set_data_meta_invalid(xdp); xsk_buff_dma_sync_for_cpu(xdp, rq->xsk_pool); net_prefetch(xdp->data); @@ -65,7 +66,8 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, * allocated first from the Reuse Ring, so it has enough space. */ - if (likely(mlx5e_xdp_handle(rq, NULL, &cqe_bcnt32, xdp))) { + prog = rcu_dereference(rq->xdp_prog); + if (likely(prog && mlx5e_xdp_handle(rq, NULL, prog, xdp))) { if (likely(__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags))) __set_bit(page_idx, wi->xdp_xmit_bitmap); /* non-atomic */ return NULL; /* page/packet was consumed by XDP */ @@ -74,7 +76,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, /* XDP_PASS: copy the data from the UMEM to a new SKB and reuse the * frame. On SKB allocation failure, NULL is returned. */ - return mlx5e_xsk_construct_skb(rq, xdp->data, cqe_bcnt32); + return mlx5e_xsk_construct_skb(rq, xdp->data, xdp->data_end - xdp->data); } struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq, @@ -83,6 +85,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq, u32 cqe_bcnt) { struct xdp_buff *xdp = wi->di->xsk; + struct bpf_prog *prog; /* wi->offset is not used in this function, because xdp->data and the * DMA address point directly to the necessary place. Furthermore, the @@ -101,12 +104,13 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq, return NULL; } - if (likely(mlx5e_xdp_handle(rq, NULL, &cqe_bcnt, xdp))) + prog = rcu_dereference(rq->xdp_prog); + if (likely(prog && mlx5e_xdp_handle(rq, NULL, prog, xdp))) return NULL; /* page/packet was consumed by XDP */ /* XDP_PASS: copy the data from the UMEM to a new SKB. The frame reuse * will be handled by mlx5e_put_rx_frag. * On SKB allocation failure, NULL is returned. */ - return mlx5e_xsk_construct_skb(rq, xdp->data, cqe_bcnt); + return mlx5e_xsk_construct_skb(rq, xdp->data, xdp->data_end - xdp->data); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c index 25eac9e20342..3ad7f1301fa8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c @@ -43,7 +43,7 @@ static void mlx5e_build_xsk_cparam(struct mlx5_core_dev *mdev, struct mlx5e_channel_param *cparam) { mlx5e_build_rq_param(mdev, params, xsk, q_counter, &cparam->rq); - mlx5e_build_xdpsq_param(mdev, params, &cparam->xdp_sq); + mlx5e_build_xdpsq_param(mdev, params, xsk, &cparam->xdp_sq); } static int mlx5e_init_xsk_rq(struct mlx5e_channel *c, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c index 8e96260fce1d..3ec0c17db010 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c @@ -103,12 +103,15 @@ bool mlx5e_xsk_tx(struct mlx5e_xdpsq *sq, unsigned int budget) xsk_buff_raw_dma_sync_for_device(pool, xdptxd.dma_addr, xdptxd.len); ret = INDIRECT_CALL_2(sq->xmit_xdp_frame, mlx5e_xmit_xdp_frame_mpwqe, - mlx5e_xmit_xdp_frame, sq, &xdptxd, &xdpi, check_result); + mlx5e_xmit_xdp_frame, sq, &xdptxd, NULL, + check_result); if (unlikely(!ret)) { if (sq->mpwqe.wqe) mlx5e_xdp_mpwqe_complete(sq); mlx5e_xsk_tx_post_err(sq, &xdpi); + } else { + mlx5e_xdpi_fifo_push(&sq->db.xdpi_fifo, &xdpi); } flush = true; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h index d964665eaa63..62cde3e87c2e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h @@ -139,15 +139,6 @@ static inline bool mlx5e_accel_tx_begin(struct net_device *dev, return true; } -static inline bool mlx5e_accel_tx_is_ipsec_flow(struct mlx5e_accel_tx_state *state) -{ -#ifdef CONFIG_MLX5_EN_IPSEC - return mlx5e_ipsec_is_tx_flow(&state->ipsec); -#else - return false; -#endif -} - static inline unsigned int mlx5e_accel_tx_ids_len(struct mlx5e_txqsq *sq, struct mlx5e_accel_tx_state *state) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 7cab08a2f715..299e3f0fcb5c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -35,7 +35,6 @@ #include #include #include -#include #include "en.h" #include "en_accel/ipsec.h" diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c index 9ad3459fb63a..aaf11c66bf4c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c @@ -32,9 +32,9 @@ u16 mlx5e_ktls_get_stop_room(struct mlx5_core_dev *mdev, struct mlx5e_params *pa num_dumps = mlx5e_ktls_dumps_num_wqes(params, MAX_SKB_FRAGS, TLS_MAX_PAYLOAD_SIZE); - stop_room += mlx5e_stop_room_for_wqe(MLX5E_TLS_SET_STATIC_PARAMS_WQEBBS); - stop_room += mlx5e_stop_room_for_wqe(MLX5E_TLS_SET_PROGRESS_PARAMS_WQEBBS); - stop_room += num_dumps * mlx5e_stop_room_for_wqe(MLX5E_KTLS_DUMP_WQEBBS); + stop_room += mlx5e_stop_room_for_wqe(mdev, MLX5E_TLS_SET_STATIC_PARAMS_WQEBBS); + stop_room += mlx5e_stop_room_for_wqe(mdev, MLX5E_TLS_SET_PROGRESS_PARAMS_WQEBBS); + stop_room += num_dumps * mlx5e_stop_room_for_wqe(mdev, MLX5E_KTLS_DUMP_WQEBBS); return stop_room; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.c index 7a700f913582..a05580cea481 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.c @@ -386,5 +386,5 @@ u16 mlx5e_tls_get_stop_room(struct mlx5_core_dev *mdev, struct mlx5e_params *par /* FPGA */ /* Resync SKB. */ - return mlx5e_stop_room_for_wqe(MLX5_SEND_WQE_MAX_WQEBBS); + return mlx5e_stop_room_for_max_wqe(mdev); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c index a4c8d8d00d5a..d659fe07d464 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c @@ -1142,7 +1142,7 @@ static int mlx5e_update_trust_state_hw(struct mlx5e_priv *priv, void *context) err = mlx5_set_trust_state(priv->mdev, *trust_state); if (err) return err; - priv->dcbx_dp.trust_state = *trust_state; + WRITE_ONCE(priv->dcbx_dp.trust_state, *trust_state); return 0; } @@ -1187,16 +1187,18 @@ static int mlx5e_set_dscp2prio(struct mlx5e_priv *priv, u8 dscp, u8 prio) static int mlx5e_trust_initialize(struct mlx5e_priv *priv) { struct mlx5_core_dev *mdev = priv->mdev; + u8 trust_state; int err; - priv->dcbx_dp.trust_state = MLX5_QPTS_TRUST_PCP; - - if (!MLX5_DSCP_SUPPORTED(mdev)) + if (!MLX5_DSCP_SUPPORTED(mdev)) { + WRITE_ONCE(priv->dcbx_dp.trust_state, MLX5_QPTS_TRUST_PCP); return 0; + } - err = mlx5_query_trust_state(priv->mdev, &priv->dcbx_dp.trust_state); + err = mlx5_query_trust_state(priv->mdev, &trust_state); if (err) return err; + WRITE_ONCE(priv->dcbx_dp.trust_state, trust_state); mlx5e_params_calc_trust_tx_min_inline_mode(priv->mdev, &priv->channels.params, priv->dcbx_dp.trust_state); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 3667f5ef5990..2f1dedc721d1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -72,12 +72,13 @@ bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) { - bool striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && - MLX5_CAP_GEN(mdev, umr_ptr_rlky) && - MLX5_CAP_ETH(mdev, reg_umr_sq); - u16 max_wqe_sz_cap = MLX5_CAP_GEN(mdev, max_wqe_sz_sq); - bool inline_umr = MLX5E_UMR_WQE_INLINE_SZ <= max_wqe_sz_cap; + bool striding_rq_umr, inline_umr; + u16 max_wqe_sz_cap; + striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && MLX5_CAP_GEN(mdev, umr_ptr_rlky) && + MLX5_CAP_ETH(mdev, reg_umr_sq); + max_wqe_sz_cap = mlx5e_get_max_sq_wqebbs(mdev) * MLX5_SEND_WQE_BB; + inline_umr = max_wqe_sz_cap >= MLX5E_UMR_WQE_INLINE_SZ; if (!striding_rq_umr) return false; if (!inline_umr) { @@ -594,6 +595,7 @@ static int mlx5e_alloc_rq(struct mlx5e_params *params, rq->mpwqe.log_stride_sz = mlx5e_mpwqe_get_log_stride_size(mdev, params, xsk); rq->mpwqe.num_strides = BIT(mlx5e_mpwqe_get_log_num_strides(mdev, params, xsk)); + rq->mpwqe.min_wqe_bulk = mlx5e_mpwqe_get_min_wqe_bulk(wq_sz); rq->buff.frame0_sz = (1 << rq->mpwqe.log_stride_sz); @@ -778,7 +780,7 @@ static void mlx5e_free_rq(struct mlx5e_rq *rq) * entered, and it's safe to call mlx5e_page_release_dynamic * directly. */ - mlx5e_page_release_dynamic(rq, dma_info, false); + mlx5e_page_release_dynamic(rq, dma_info->page, false); } xdp_rxq_info_unreg(&rq->xdp_rxq); @@ -1164,6 +1166,9 @@ static int mlx5e_alloc_xdpsq(struct mlx5e_channel *c, is_redirect ? &c->priv->channel_stats[c->ix]->xdpsq : &c->priv->channel_stats[c->ix]->rq_xdpsq; + sq->max_sq_wqebbs = mlx5e_get_max_sq_wqebbs(mdev); + sq->stop_room = MLX5E_STOP_ROOM(sq->max_sq_wqebbs); + sq->max_sq_mpw_wqebbs = mlx5e_get_sw_max_sq_mpw_wqebbs(sq->max_sq_wqebbs); param->wq.db_numa_node = cpu_to_node(c->cpu); err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, wq, &sq->wq_ctrl); @@ -1238,6 +1243,7 @@ static int mlx5e_alloc_icosq(struct mlx5e_channel *c, sq->channel = c; sq->uar_map = mdev->mlx5e_res.hw_objs.bfreg.map; sq->reserved_room = param->stop_room; + sq->max_sq_wqebbs = mlx5e_get_max_sq_wqebbs(mdev); param->wq.db_numa_node = cpu_to_node(c->cpu); err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, wq, &sq->wq_ctrl); @@ -1313,7 +1319,6 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, int err; sq->pdev = c->pdev; - sq->tstamp = c->tstamp; sq->clock = &mdev->clock; sq->mkey_be = c->mkey_be; sq->netdev = c->netdev; @@ -1324,6 +1329,8 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, sq->uar_map = mdev->mlx5e_res.hw_objs.bfreg.map; sq->min_inline_mode = params->tx_min_inline_mode; sq->hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu); + sq->max_sq_wqebbs = mlx5e_get_max_sq_wqebbs(mdev); + sq->max_sq_mpw_wqebbs = mlx5e_get_sw_max_sq_mpw_wqebbs(sq->max_sq_wqebbs); INIT_WORK(&sq->recover_work, mlx5e_tx_err_cqe_work); if (!MLX5_CAP_ETH(mdev, wqe_vlan_insert)) set_bit(MLX5E_SQ_STATE_VLAN_NEED_L2_INLINE, &sq->state); @@ -1659,14 +1666,22 @@ int mlx5e_open_xdpsq(struct mlx5e_channel *c, struct mlx5e_params *params, csp.wq_ctrl = &sq->wq_ctrl; csp.min_inline_mode = sq->min_inline_mode; set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); + + /* Don't enable multi buffer on XDP_REDIRECT SQ, as it's not yet + * supported by upstream, and there is no defined trigger to allow + * transmitting redirected multi-buffer frames. + */ + if (param->is_xdp_mb && !is_redirect) + set_bit(MLX5E_SQ_STATE_XDP_MULTIBUF, &sq->state); + err = mlx5e_create_sq_rdy(c->mdev, param, &csp, 0, &sq->sqn); if (err) goto err_free_xdpsq; mlx5e_set_xmit_fp(sq, param->is_mpw); - if (!param->is_mpw) { - unsigned int ds_cnt = MLX5E_XDP_TX_DS_COUNT; + if (!param->is_mpw && !test_bit(MLX5E_SQ_STATE_XDP_MULTIBUF, &sq->state)) { + unsigned int ds_cnt = MLX5E_TX_WQE_EMPTY_DS_COUNT + 1; unsigned int inline_hdr_sz = 0; int i; @@ -2677,39 +2692,41 @@ static void mlx5e_build_txq_maps(struct mlx5e_priv *priv) struct mlx5e_txqsq *sq = &c->sq[tc]; priv->txq2sq[sq->txq_ix] = sq; - priv->channel_tc2realtxq[i][tc] = i + tc * ch; } } if (!priv->channels.ptp) - return; + goto out; if (!test_bit(MLX5E_PTP_STATE_TX, priv->channels.ptp->state)) - return; + goto out; for (tc = 0; tc < num_tc; tc++) { struct mlx5e_ptp *c = priv->channels.ptp; struct mlx5e_txqsq *sq = &c->ptpsq[tc].txqsq; priv->txq2sq[sq->txq_ix] = sq; - priv->port_ptp_tc2realtxq[tc] = priv->num_tc_x_num_ch + tc; } -} -static void mlx5e_update_num_tc_x_num_ch(struct mlx5e_priv *priv) -{ - /* Sync with mlx5e_select_queue. */ - WRITE_ONCE(priv->num_tc_x_num_ch, - mlx5e_get_dcb_num_tc(&priv->channels.params) * priv->channels.num); +out: + /* Make the change to txq2sq visible before the queue is started. + * As mlx5e_xmit runs under a spinlock, there is an implicit ACQUIRE, + * which pairs with this barrier. + */ + smp_wmb(); } void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) { - mlx5e_update_num_tc_x_num_ch(priv); mlx5e_build_txq_maps(priv); mlx5e_activate_channels(&priv->channels); mlx5e_qos_activate_queues(priv); mlx5e_xdp_tx_enable(priv); + + /* dev_watchdog() wants all TX queues to be started when the carrier is + * OK, including the ones in range real_num_tx_queues..num_tx_queues-1. + * Make it happy to avoid TX timeout false alarms. + */ netif_tx_start_all_queues(priv->netdev); if (mlx5e_is_vport_rep(priv)) @@ -2729,11 +2746,13 @@ void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv) if (mlx5e_is_vport_rep(priv)) mlx5e_remove_sqs_fwd_rules(priv); - /* FIXME: This is a W/A only for tx timeout watch dog false alarm when - * polling for inactive tx queues. + /* The results of ndo_select_queue are unreliable, while netdev config + * is being changed (real_num_tx_queues, num_tc). Stop all queues to + * prevent ndo_start_xmit from being called, so that it can assume that + * the selected queue is always valid. */ - netif_tx_stop_all_queues(priv->netdev); netif_tx_disable(priv->netdev); + mlx5e_xdp_tx_disable(priv); mlx5e_deactivate_channels(&priv->channels); } @@ -2793,6 +2812,7 @@ static int mlx5e_switch_priv_channels(struct mlx5e_priv *priv, mlx5e_close_channels(&old_chs); priv->profile->update_rx(priv); + mlx5e_selq_apply(&priv->selq); out: mlx5e_activate_priv_channels(priv); @@ -2816,13 +2836,24 @@ int mlx5e_safe_switch_params(struct mlx5e_priv *priv, return mlx5e_switch_priv_params(priv, params, preactivate, context); new_chs.params = *params; + + mlx5e_selq_prepare(&priv->selq, &new_chs.params, !!priv->htb.maj_id); + err = mlx5e_open_channels(priv, &new_chs); if (err) - return err; + goto err_cancel_selq; + err = mlx5e_switch_priv_channels(priv, &new_chs, preactivate, context); if (err) - mlx5e_close_channels(&new_chs); + goto err_close; + return 0; + +err_close: + mlx5e_close_channels(&new_chs); + +err_cancel_selq: + mlx5e_selq_cancel(&priv->selq); return err; } @@ -2862,6 +2893,8 @@ int mlx5e_open_locked(struct net_device *netdev) struct mlx5e_priv *priv = netdev_priv(netdev); int err; + mlx5e_selq_prepare(&priv->selq, &priv->channels.params, !!priv->htb.maj_id); + set_bit(MLX5E_STATE_OPENED, &priv->state); err = mlx5e_open_channels(priv, &priv->channels); @@ -2869,6 +2902,7 @@ int mlx5e_open_locked(struct net_device *netdev) goto err_clear_state_opened_flag; priv->profile->update_rx(priv); + mlx5e_selq_apply(&priv->selq); mlx5e_activate_priv_channels(priv); mlx5e_apply_traps(priv, true); if (priv->profile->update_carrier) @@ -2879,6 +2913,7 @@ int mlx5e_open_locked(struct net_device *netdev) err_clear_state_opened_flag: clear_bit(MLX5E_STATE_OPENED, &priv->state); + mlx5e_selq_cancel(&priv->selq); return err; } @@ -3918,6 +3953,31 @@ static bool mlx5e_xsk_validate_mtu(struct net_device *netdev, return true; } +static bool mlx5e_params_validate_xdp(struct net_device *netdev, struct mlx5e_params *params) +{ + bool is_linear; + + /* No XSK params: AF_XDP can't be enabled yet at the point of setting + * the XDP program. + */ + is_linear = mlx5e_rx_is_linear_skb(params, NULL); + + if (!is_linear && params->rq_wq_type != MLX5_WQ_TYPE_CYCLIC) { + netdev_warn(netdev, "XDP is not allowed with striding RQ and MTU(%d) > %d\n", + params->sw_mtu, + mlx5e_xdp_max_mtu(params, NULL)); + return false; + } + if (!is_linear && !params->xdp_prog->aux->xdp_has_frags) { + netdev_warn(netdev, "MTU(%d) > %d, too big for an XDP program not aware of multi buffer\n", + params->sw_mtu, + mlx5e_xdp_max_mtu(params, NULL)); + return false; + } + + return true; +} + int mlx5e_change_mtu(struct net_device *netdev, int new_mtu, mlx5e_fp_preactivate preactivate) { @@ -3937,10 +3997,7 @@ int mlx5e_change_mtu(struct net_device *netdev, int new_mtu, if (err) goto out; - if (params->xdp_prog && - !mlx5e_rx_is_linear_skb(&new_params, NULL)) { - netdev_err(netdev, "MTU(%d) > %d is not allowed while XDP enabled\n", - new_mtu, mlx5e_xdp_max_mtu(params, NULL)); + if (new_params.xdp_prog && !mlx5e_params_validate_xdp(netdev, &new_params)) { err = -EINVAL; goto out; } @@ -4423,15 +4480,8 @@ static int mlx5e_xdp_allowed(struct mlx5e_priv *priv, struct bpf_prog *prog) new_params = priv->channels.params; new_params.xdp_prog = prog; - /* No XSK params: AF_XDP can't be enabled yet at the point of setting - * the XDP program. - */ - if (!mlx5e_rx_is_linear_skb(&new_params, NULL)) { - netdev_warn(netdev, "XDP is not allowed with MTU(%d) > %d\n", - new_params.sw_mtu, - mlx5e_xdp_max_mtu(&new_params, NULL)); + if (!mlx5e_params_validate_xdp(netdev, &new_params)) return -EINVAL; - } return 0; } @@ -4636,11 +4686,6 @@ void mlx5e_build_nic_params(struct mlx5e_priv *priv, struct mlx5e_xsk *xsk, u16 priv->max_nch); mlx5e_params_mqprio_reset(params); - /* Set an initial non-zero value, so that mlx5e_select_queue won't - * divide by zero if called before first activating channels. - */ - priv->num_tc_x_num_ch = params->num_channels * params->mqprio.num_tc; - /* SQ */ params->log_sq_size = is_kdump_kernel() ? MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE : @@ -5193,7 +5238,8 @@ int mlx5e_priv_init(struct mlx5e_priv *priv, struct net_device *netdev, struct mlx5_core_dev *mdev) { - int nch, num_txqs, node, i; + int nch, num_txqs, node; + int err; num_txqs = netdev->num_tx_queues; nch = mlx5e_calc_max_nch(mdev, netdev, profile); @@ -5210,6 +5256,11 @@ int mlx5e_priv_init(struct mlx5e_priv *priv, return -ENOMEM; mutex_init(&priv->state_lock); + + err = mlx5e_selq_init(&priv->selq, &priv->state_lock); + if (err) + goto err_free_cpumask; + hash_init(priv->htb.qos_tc2node); INIT_WORK(&priv->update_carrier_work, mlx5e_update_carrier_work); INIT_WORK(&priv->set_rx_mode_work, mlx5e_set_rx_mode_work); @@ -5218,7 +5269,7 @@ int mlx5e_priv_init(struct mlx5e_priv *priv, priv->wq = create_singlethread_workqueue("mlx5e"); if (!priv->wq) - goto err_free_cpumask; + goto err_free_selq; priv->txq2sq = kcalloc_node(num_txqs, sizeof(*priv->txq2sq), GFP_KERNEL, node); if (!priv->txq2sq) @@ -5228,36 +5279,21 @@ int mlx5e_priv_init(struct mlx5e_priv *priv, if (!priv->tx_rates) goto err_free_txq2sq; - priv->channel_tc2realtxq = - kcalloc_node(nch, sizeof(*priv->channel_tc2realtxq), GFP_KERNEL, node); - if (!priv->channel_tc2realtxq) - goto err_free_tx_rates; - - for (i = 0; i < nch; i++) { - priv->channel_tc2realtxq[i] = - kcalloc_node(profile->max_tc, sizeof(**priv->channel_tc2realtxq), - GFP_KERNEL, node); - if (!priv->channel_tc2realtxq[i]) - goto err_free_channel_tc2realtxq; - } - priv->channel_stats = kcalloc_node(nch, sizeof(*priv->channel_stats), GFP_KERNEL, node); if (!priv->channel_stats) - goto err_free_channel_tc2realtxq; + goto err_free_tx_rates; return 0; -err_free_channel_tc2realtxq: - while (--i >= 0) - kfree(priv->channel_tc2realtxq[i]); - kfree(priv->channel_tc2realtxq); err_free_tx_rates: kfree(priv->tx_rates); err_free_txq2sq: kfree(priv->txq2sq); err_destroy_workqueue: destroy_workqueue(priv->wq); +err_free_selq: + mlx5e_selq_cleanup(&priv->selq); err_free_cpumask: free_cpumask_var(priv->scratchpad.cpumask); return -ENOMEM; @@ -5274,12 +5310,12 @@ void mlx5e_priv_cleanup(struct mlx5e_priv *priv) for (i = 0; i < priv->stats_nch; i++) kvfree(priv->channel_stats[i]); kfree(priv->channel_stats); - for (i = 0; i < priv->max_nch; i++) - kfree(priv->channel_tc2realtxq[i]); - kfree(priv->channel_tc2realtxq); kfree(priv->tx_rates); kfree(priv->txq2sq); destroy_workqueue(priv->wq); + mutex_lock(&priv->state_lock); + mlx5e_selq_cleanup(&priv->selq); + mutex_unlock(&priv->state_lock); free_cpumask_var(priv->scratchpad.cpumask); for (i = 0; i < priv->htb.max_qos_sqs; i++) @@ -5345,6 +5381,7 @@ mlx5e_create_netdev(struct mlx5_core_dev *mdev, const struct mlx5e_profile *prof } netif_carrier_off(netdev); + netif_tx_disable(netdev); dev_net_set(netdev, mlx5_core_net(mdev)); return netdev; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 06d1f46f1688..6b7e7ea6ded2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -55,6 +55,7 @@ #include "diag/en_rep_tracepoint.h" #include "en_accel/ipsec.h" #include "en/tc/int_port.h" +#include "en/ptp.h" #define MLX5E_REP_PARAMS_DEF_LOG_SQ_SIZE \ max(0x7, MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE) @@ -401,13 +402,18 @@ int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5e_rep_priv *rpriv = priv->ppriv; struct mlx5_eswitch_rep *rep = rpriv->rep; + int n, tc, nch, num_sqs = 0; struct mlx5e_channel *c; - int n, tc, num_sqs = 0; int err = -ENOMEM; + bool ptp_sq; u32 *sqs; - sqs = kcalloc(priv->channels.num * mlx5e_get_dcb_num_tc(&priv->channels.params), - sizeof(*sqs), GFP_KERNEL); + ptp_sq = !!(priv->channels.ptp && + MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_TX_PORT_TS)); + nch = priv->channels.num + ptp_sq; + + sqs = kcalloc(nch * mlx5e_get_dcb_num_tc(&priv->channels.params), sizeof(*sqs), + GFP_KERNEL); if (!sqs) goto out; @@ -416,6 +422,12 @@ int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) for (tc = 0; tc < c->num_tc; tc++) sqs[num_sqs++] = c->sq[tc].sqn; } + if (ptp_sq) { + struct mlx5e_ptp *ptp_ch = priv->channels.ptp; + + for (tc = 0; tc < ptp_ch->num_tc; tc++) + sqs[num_sqs++] = ptp_ch->ptpsq[tc].txqsq.sqn; + } err = mlx5e_sqs2vport_start(esw, rep, sqs, num_sqs); kfree(sqs); @@ -632,11 +644,6 @@ static void mlx5e_build_rep_params(struct net_device *netdev) params->mqprio.num_tc = 1; params->tunneled_offload_en = false; - /* Set an initial non-zero value, so that mlx5e_select_queue won't - * divide by zero if called before first activating channels. - */ - priv->num_tc_x_num_ch = params->num_channels * params->mqprio.num_tc; - mlx5_query_min_inline(mdev, ¶ms->tx_min_inline_mode); } @@ -935,15 +942,21 @@ static int mlx5e_init_rep_tx(struct mlx5e_priv *priv) return err; } + err = mlx5e_tc_ht_init(&rpriv->tc_ht); + if (err) + goto err_ht_init; + if (rpriv->rep->vport == MLX5_VPORT_UPLINK) { err = mlx5e_init_uplink_rep_tx(rpriv); if (err) - goto destroy_tises; + goto err_init_tx; } return 0; -destroy_tises: +err_init_tx: + mlx5e_tc_ht_cleanup(&rpriv->tc_ht); +err_ht_init: mlx5e_destroy_tises(priv); return err; } @@ -963,6 +976,8 @@ static void mlx5e_cleanup_rep_tx(struct mlx5e_priv *priv) if (rpriv->rep->vport == MLX5_VPORT_UPLINK) mlx5e_cleanup_uplink_rep_tx(rpriv); + + mlx5e_tc_ht_cleanup(&rpriv->tc_ht); } static void mlx5e_rep_enable(struct mlx5e_priv *priv) @@ -1099,6 +1114,7 @@ static mlx5e_stats_grp_t mlx5e_ul_rep_stats_grps[] = { &MLX5E_STATS_GRP(ipsec_sw), &MLX5E_STATS_GRP(ipsec_hw), #endif + &MLX5E_STATS_GRP(ptp), }; static unsigned int mlx5e_ul_rep_stats_grps_num(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h index b3f7520dfd08..adf5cc6a7b8c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -64,11 +64,6 @@ struct mlx5e_tc_tun_encap; struct mlx5e_post_act; struct mlx5_rep_uplink_priv { - /* Filters DB - instantiated by the uplink representor and shared by - * the uplink's VFs - */ - struct rhashtable tc_ht; - /* indirect block callbacks are invoked on bind/unbind events * on registered higher level devices (e.g. tunnel devices) * @@ -113,6 +108,7 @@ struct mlx5e_rep_priv { struct list_head vport_sqs_list; struct mlx5_rep_uplink_priv uplink_priv; /* valid for uplink rep */ struct rtnl_link_stats64 prev_vf_vport_stats; + struct rhashtable tc_ht; }; static inline diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 6530d7bd5045..56bb58704bf9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -221,8 +222,7 @@ static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, return mlx5e_decompress_cqes_cont(rq, wq, 1, budget_rem) - 1; } -static inline bool mlx5e_rx_cache_put(struct mlx5e_rq *rq, - struct mlx5e_dma_info *dma_info) +static inline bool mlx5e_rx_cache_put(struct mlx5e_rq *rq, struct page *page) { struct mlx5e_page_cache *cache = &rq->page_cache; u32 tail_next = (cache->tail + 1) & (MLX5E_CACHE_SIZE - 1); @@ -233,12 +233,13 @@ static inline bool mlx5e_rx_cache_put(struct mlx5e_rq *rq, return false; } - if (!dev_page_is_reusable(dma_info->page)) { + if (!dev_page_is_reusable(page)) { stats->cache_waive++; return false; } - cache->page_cache[cache->tail] = *dma_info; + cache->page_cache[cache->tail].page = page; + cache->page_cache[cache->tail].addr = page_pool_get_dma_addr(page); cache->tail = tail_next; return true; } @@ -286,6 +287,7 @@ static inline int mlx5e_page_alloc_pool(struct mlx5e_rq *rq, dma_info->page = NULL; return -ENOMEM; } + page_pool_set_dma_addr(dma_info->page, dma_info->addr); return 0; } @@ -299,26 +301,27 @@ static inline int mlx5e_page_alloc(struct mlx5e_rq *rq, return mlx5e_page_alloc_pool(rq, dma_info); } -void mlx5e_page_dma_unmap(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info) +void mlx5e_page_dma_unmap(struct mlx5e_rq *rq, struct page *page) { - dma_unmap_page_attrs(rq->pdev, dma_info->addr, PAGE_SIZE, rq->buff.map_dir, + dma_addr_t dma_addr = page_pool_get_dma_addr(page); + + dma_unmap_page_attrs(rq->pdev, dma_addr, PAGE_SIZE, rq->buff.map_dir, DMA_ATTR_SKIP_CPU_SYNC); + page_pool_set_dma_addr(page, 0); } -void mlx5e_page_release_dynamic(struct mlx5e_rq *rq, - struct mlx5e_dma_info *dma_info, - bool recycle) +void mlx5e_page_release_dynamic(struct mlx5e_rq *rq, struct page *page, bool recycle) { if (likely(recycle)) { - if (mlx5e_rx_cache_put(rq, dma_info)) + if (mlx5e_rx_cache_put(rq, page)) return; - mlx5e_page_dma_unmap(rq, dma_info); - page_pool_recycle_direct(rq->page_pool, dma_info->page); + mlx5e_page_dma_unmap(rq, page); + page_pool_recycle_direct(rq->page_pool, page); } else { - mlx5e_page_dma_unmap(rq, dma_info); - page_pool_release_page(rq->page_pool, dma_info->page); - put_page(dma_info->page); + mlx5e_page_dma_unmap(rq, page); + page_pool_release_page(rq->page_pool, page); + put_page(page); } } @@ -333,7 +336,7 @@ static inline void mlx5e_page_release(struct mlx5e_rq *rq, */ xsk_buff_free(dma_info->xsk); else - mlx5e_page_release_dynamic(rq, dma_info, recycle); + mlx5e_page_release_dynamic(rq, dma_info->page, recycle); } static inline int mlx5e_get_rx_frag(struct mlx5e_rq *rq, @@ -373,12 +376,15 @@ static int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe_cyc *wqe, int i; for (i = 0; i < rq->wqe.info.num_frags; i++, frag++) { + u16 headroom; + err = mlx5e_get_rx_frag(rq, frag); if (unlikely(err)) goto free_frags; + headroom = i == 0 ? rq->buff.headroom : 0; wqe->data[i].addr = cpu_to_be64(frag->di->addr + - frag->offset + rq->buff.headroom); + frag->offset + headroom); } return 0; @@ -620,7 +626,7 @@ static int mlx5e_alloc_rx_hd_mpwqe(struct mlx5e_rq *rq) struct mlx5e_icosq *sq = rq->icosq; int i, err, max_klm_entries, len; - max_klm_entries = MLX5E_MAX_KLM_PER_WQE; + max_klm_entries = MLX5E_MAX_KLM_PER_WQE(rq->mdev); klm_entries = bitmap_find_window(shampo->bitmap, shampo->hd_per_wqe, shampo->hd_per_wq, shampo->pi); @@ -960,8 +966,7 @@ INDIRECT_CALLABLE_SCOPE bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq) if (unlikely(rq->mpwqe.umr_in_progress > rq->mpwqe.umr_last_bulk)) rq->stats->congst_umr++; -#define UMR_WQE_BULK (2) - if (likely(missing < UMR_WQE_BULK)) + if (likely(missing < rq->mpwqe.min_wqe_bulk)) return false; if (rq->page_pool) @@ -1490,7 +1495,7 @@ static inline void mlx5e_complete_rx_cqe(struct mlx5e_rq *rq, static inline struct sk_buff *mlx5e_build_linear_skb(struct mlx5e_rq *rq, void *va, u32 frag_size, u16 headroom, - u32 cqe_bcnt) + u32 cqe_bcnt, u32 metasize) { struct sk_buff *skb = build_skb(va, frag_size); @@ -1502,6 +1507,9 @@ struct sk_buff *mlx5e_build_linear_skb(struct mlx5e_rq *rq, void *va, skb_reserve(skb, headroom); skb_put(skb, cqe_bcnt); + if (metasize) + skb_metadata_set(skb, metasize); + return skb; } @@ -1509,7 +1517,7 @@ static void mlx5e_fill_xdp_buff(struct mlx5e_rq *rq, void *va, u16 headroom, u32 len, struct xdp_buff *xdp) { xdp_init_buff(xdp, rq->buff.frame0_sz, &rq->xdp_rxq); - xdp_prepare_buff(xdp, va, headroom, len, false); + xdp_prepare_buff(xdp, va, headroom, len, true); } static struct sk_buff * @@ -1518,8 +1526,9 @@ mlx5e_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe, { struct mlx5e_dma_info *di = wi->di; u16 rx_headroom = rq->buff.headroom; - struct xdp_buff xdp; + struct bpf_prog *prog; struct sk_buff *skb; + u32 metasize = 0; void *va, *data; u32 frag_size; @@ -1529,16 +1538,23 @@ mlx5e_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe, dma_sync_single_range_for_cpu(rq->pdev, di->addr, wi->offset, frag_size, DMA_FROM_DEVICE); - net_prefetchw(va); /* xdp_frame data area */ net_prefetch(data); - mlx5e_fill_xdp_buff(rq, va, rx_headroom, cqe_bcnt, &xdp); - if (mlx5e_xdp_handle(rq, di, &cqe_bcnt, &xdp)) - return NULL; /* page/packet was consumed by XDP */ + prog = rcu_dereference(rq->xdp_prog); + if (prog) { + struct xdp_buff xdp; - rx_headroom = xdp.data - xdp.data_hard_start; + net_prefetchw(va); /* xdp_frame data area */ + mlx5e_fill_xdp_buff(rq, va, rx_headroom, cqe_bcnt, &xdp); + if (mlx5e_xdp_handle(rq, di->page, prog, &xdp)) + return NULL; /* page/packet was consumed by XDP */ + + rx_headroom = xdp.data - xdp.data_hard_start; + metasize = xdp.data - xdp.data_meta; + cqe_bcnt = xdp.data_end - xdp.data; + } frag_size = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt); - skb = mlx5e_build_linear_skb(rq, va, frag_size, rx_headroom, cqe_bcnt); + skb = mlx5e_build_linear_skb(rq, va, frag_size, rx_headroom, cqe_bcnt, metasize); if (unlikely(!skb)) return NULL; @@ -1554,41 +1570,103 @@ mlx5e_skb_from_cqe_nonlinear(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe, { struct mlx5e_rq_frag_info *frag_info = &rq->wqe.info.arr[0]; struct mlx5e_wqe_frag_info *head_wi = wi; - u16 headlen = min_t(u32, MLX5E_RX_MAX_HEAD, cqe_bcnt); - u16 frag_headlen = headlen; - u16 byte_cnt = cqe_bcnt - headlen; + u16 rx_headroom = rq->buff.headroom; + struct mlx5e_dma_info *di = wi->di; + struct skb_shared_info *sinfo; + u32 frag_consumed_bytes; + struct bpf_prog *prog; + struct xdp_buff xdp; struct sk_buff *skb; + u32 truesize; + void *va; - /* XDP is not supported in this configuration, as incoming packets - * might spread among multiple pages. - */ - skb = napi_alloc_skb(rq->cq.napi, - ALIGN(MLX5E_RX_MAX_HEAD, sizeof(long))); - if (unlikely(!skb)) { - rq->stats->buff_alloc_err++; - return NULL; - } + va = page_address(di->page) + wi->offset; + frag_consumed_bytes = min_t(u32, frag_info->frag_size, cqe_bcnt); - net_prefetchw(skb->data); + dma_sync_single_range_for_cpu(rq->pdev, di->addr, wi->offset, + rq->buff.frame0_sz, DMA_FROM_DEVICE); + net_prefetchw(va); /* xdp_frame data area */ + net_prefetch(va + rx_headroom); - while (byte_cnt) { - u16 frag_consumed_bytes = - min_t(u16, frag_info->frag_size - frag_headlen, byte_cnt); + mlx5e_fill_xdp_buff(rq, va, rx_headroom, frag_consumed_bytes, &xdp); + sinfo = xdp_get_shared_info_from_buff(&xdp); + truesize = 0; - mlx5e_add_skb_frag(rq, skb, wi->di, wi->offset + frag_headlen, - frag_consumed_bytes, frag_info->frag_stride); - byte_cnt -= frag_consumed_bytes; - frag_headlen = 0; + cqe_bcnt -= frag_consumed_bytes; + frag_info++; + wi++; + + while (cqe_bcnt) { + skb_frag_t *frag; + + di = wi->di; + + frag_consumed_bytes = min_t(u32, frag_info->frag_size, cqe_bcnt); + + dma_sync_single_for_cpu(rq->pdev, di->addr + wi->offset, + frag_consumed_bytes, DMA_FROM_DEVICE); + + if (!xdp_buff_has_frags(&xdp)) { + /* Init on the first fragment to avoid cold cache access + * when possible. + */ + sinfo->nr_frags = 0; + sinfo->xdp_frags_size = 0; + xdp_buff_set_frags_flag(&xdp); + } + + frag = &sinfo->frags[sinfo->nr_frags++]; + __skb_frag_set_page(frag, di->page); + skb_frag_off_set(frag, wi->offset); + skb_frag_size_set(frag, frag_consumed_bytes); + + if (page_is_pfmemalloc(di->page)) + xdp_buff_set_frag_pfmemalloc(&xdp); + + sinfo->xdp_frags_size += frag_consumed_bytes; + truesize += frag_info->frag_stride; + + cqe_bcnt -= frag_consumed_bytes; frag_info++; wi++; } - /* copy header */ - mlx5e_copy_skb_header(rq->pdev, skb, head_wi->di, head_wi->offset, head_wi->offset, - headlen); - /* skb linear part was allocated with headlen and aligned to long */ - skb->tail += headlen; - skb->len += headlen; + di = head_wi->di; + + prog = rcu_dereference(rq->xdp_prog); + if (prog && mlx5e_xdp_handle(rq, di->page, prog, &xdp)) { + if (test_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) { + int i; + + for (i = wi - head_wi; i < rq->wqe.info.num_frags; i++) + mlx5e_put_rx_frag(rq, &head_wi[i], true); + } + return NULL; /* page/packet was consumed by XDP */ + } + + skb = mlx5e_build_linear_skb(rq, xdp.data_hard_start, rq->buff.frame0_sz, + xdp.data - xdp.data_hard_start, + xdp.data_end - xdp.data, + xdp.data - xdp.data_meta); + if (unlikely(!skb)) + return NULL; + + page_ref_inc(di->page); + + if (unlikely(xdp_buff_has_frags(&xdp))) { + int i; + + /* sinfo->nr_frags is reset by build_skb, calculate again. */ + xdp_update_skb_shared_info(skb, wi - head_wi - 1, + sinfo->xdp_frags_size, truesize, + xdp_buff_is_frag_pfmemalloc(&xdp)); + + for (i = 0; i < sinfo->nr_frags; i++) { + skb_frag_t *frag = &sinfo->frags[i]; + + page_ref_inc(skb_frag_page(frag)); + } + } return skb; } @@ -1832,9 +1910,9 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, { struct mlx5e_dma_info *di = &wi->umr.dma_info[page_idx]; u16 rx_headroom = rq->buff.headroom; - u32 cqe_bcnt32 = cqe_bcnt; - struct xdp_buff xdp; + struct bpf_prog *prog; struct sk_buff *skb; + u32 metasize = 0; void *va, *data; u32 frag_size; @@ -1846,23 +1924,30 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, va = page_address(di->page) + head_offset; data = va + rx_headroom; - frag_size = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt32); + frag_size = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt); dma_sync_single_range_for_cpu(rq->pdev, di->addr, head_offset, frag_size, DMA_FROM_DEVICE); - net_prefetchw(va); /* xdp_frame data area */ net_prefetch(data); - mlx5e_fill_xdp_buff(rq, va, rx_headroom, cqe_bcnt32, &xdp); - if (mlx5e_xdp_handle(rq, di, &cqe_bcnt32, &xdp)) { - if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) - __set_bit(page_idx, wi->xdp_xmit_bitmap); /* non-atomic */ - return NULL; /* page/packet was consumed by XDP */ - } + prog = rcu_dereference(rq->xdp_prog); + if (prog) { + struct xdp_buff xdp; - rx_headroom = xdp.data - xdp.data_hard_start; - frag_size = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt32); - skb = mlx5e_build_linear_skb(rq, va, frag_size, rx_headroom, cqe_bcnt32); + net_prefetchw(va); /* xdp_frame data area */ + mlx5e_fill_xdp_buff(rq, va, rx_headroom, cqe_bcnt, &xdp); + if (mlx5e_xdp_handle(rq, di->page, prog, &xdp)) { + if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) + __set_bit(page_idx, wi->xdp_xmit_bitmap); /* non-atomic */ + return NULL; /* page/packet was consumed by XDP */ + } + + rx_headroom = xdp.data - xdp.data_hard_start; + metasize = xdp.data - xdp.data_meta; + cqe_bcnt = xdp.data_end - xdp.data; + } + frag_size = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt); + skb = mlx5e_build_linear_skb(rq, va, frag_size, rx_headroom, cqe_bcnt, metasize); if (unlikely(!skb)) return NULL; @@ -1893,7 +1978,7 @@ mlx5e_skb_from_cqe_shampo(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, dma_sync_single_range_for_cpu(rq->pdev, head->addr, 0, frag_size, DMA_FROM_DEVICE); prefetchw(hdr); prefetch(data); - skb = mlx5e_build_linear_skb(rq, hdr, frag_size, rx_headroom, head_size); + skb = mlx5e_build_linear_skb(rq, hdr, frag_size, rx_headroom, head_size, 0); if (unlikely(!skb)) return NULL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 00f1d16db456..bdc870f9c2f3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -37,6 +37,10 @@ #include "en/ptp.h" #include "en/port.h" +#ifdef CONFIG_PAGE_POOL_STATS +#include +#endif + static unsigned int stats_grps_num(struct mlx5e_priv *priv) { return !priv->profile->stats_grps_num ? 0 : @@ -183,6 +187,19 @@ static const struct counter_desc sw_stats_desc[] = { { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_congst_umr) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_arfs_err) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_recover) }, +#ifdef CONFIG_PAGE_POOL_STATS + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_pp_alloc_fast) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_pp_alloc_slow) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_pp_alloc_slow_high_order) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_pp_alloc_empty) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_pp_alloc_refill) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_pp_alloc_waive) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_pp_recycle_cached) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_pp_recycle_cache_full) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_pp_recycle_ring) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_pp_recycle_ring_full) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_pp_recycle_released_ref) }, +#endif #ifdef CONFIG_MLX5_EN_TLS { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_tls_decrypted_packets) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_tls_decrypted_bytes) }, @@ -349,6 +366,19 @@ static void mlx5e_stats_grp_sw_update_stats_rq_stats(struct mlx5e_sw_stats *s, s->rx_congst_umr += rq_stats->congst_umr; s->rx_arfs_err += rq_stats->arfs_err; s->rx_recover += rq_stats->recover; +#ifdef CONFIG_PAGE_POOL_STATS + s->rx_pp_alloc_fast += rq_stats->pp_alloc_fast; + s->rx_pp_alloc_slow += rq_stats->pp_alloc_slow; + s->rx_pp_alloc_empty += rq_stats->pp_alloc_empty; + s->rx_pp_alloc_refill += rq_stats->pp_alloc_refill; + s->rx_pp_alloc_waive += rq_stats->pp_alloc_waive; + s->rx_pp_alloc_slow_high_order += rq_stats->pp_alloc_slow_high_order; + s->rx_pp_recycle_cached += rq_stats->pp_recycle_cached; + s->rx_pp_recycle_cache_full += rq_stats->pp_recycle_cache_full; + s->rx_pp_recycle_ring += rq_stats->pp_recycle_ring; + s->rx_pp_recycle_ring_full += rq_stats->pp_recycle_ring_full; + s->rx_pp_recycle_released_ref += rq_stats->pp_recycle_released_ref; +#endif #ifdef CONFIG_MLX5_EN_TLS s->rx_tls_decrypted_packets += rq_stats->tls_decrypted_packets; s->rx_tls_decrypted_bytes += rq_stats->tls_decrypted_bytes; @@ -455,6 +485,35 @@ static void mlx5e_stats_grp_sw_update_stats_qos(struct mlx5e_priv *priv, } } +#ifdef CONFIG_PAGE_POOL_STATS +static void mlx5e_stats_update_stats_rq_page_pool(struct mlx5e_channel *c) +{ + struct mlx5e_rq_stats *rq_stats = c->rq.stats; + struct page_pool *pool = c->rq.page_pool; + struct page_pool_stats stats = { 0 }; + + if (!page_pool_get_stats(pool, &stats)) + return; + + rq_stats->pp_alloc_fast = stats.alloc_stats.fast; + rq_stats->pp_alloc_slow = stats.alloc_stats.slow; + rq_stats->pp_alloc_slow_high_order = stats.alloc_stats.slow_high_order; + rq_stats->pp_alloc_empty = stats.alloc_stats.empty; + rq_stats->pp_alloc_waive = stats.alloc_stats.waive; + rq_stats->pp_alloc_refill = stats.alloc_stats.refill; + + rq_stats->pp_recycle_cached = stats.recycle_stats.cached; + rq_stats->pp_recycle_cache_full = stats.recycle_stats.cache_full; + rq_stats->pp_recycle_ring = stats.recycle_stats.ring; + rq_stats->pp_recycle_ring_full = stats.recycle_stats.ring_full; + rq_stats->pp_recycle_released_ref = stats.recycle_stats.released_refcnt; +} +#else +static void mlx5e_stats_update_stats_rq_page_pool(struct mlx5e_channel *c) +{ +} +#endif + static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw) { struct mlx5e_sw_stats *s = &priv->stats.sw; @@ -462,9 +521,13 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw) memset(s, 0, sizeof(*s)); + for (i = 0; i < priv->channels.num; i++) /* for active channels only */ + mlx5e_stats_update_stats_rq_page_pool(priv->channels.c[i]); + for (i = 0; i < priv->stats_nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; + int j; mlx5e_stats_grp_sw_update_stats_rq_stats(s, &channel_stats->rq); @@ -1887,6 +1950,19 @@ static const struct counter_desc rq_stats_desc[] = { { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, congst_umr) }, { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, arfs_err) }, { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, recover) }, +#ifdef CONFIG_PAGE_POOL_STATS + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, pp_alloc_fast) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, pp_alloc_slow) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, pp_alloc_slow_high_order) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, pp_alloc_empty) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, pp_alloc_refill) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, pp_alloc_waive) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, pp_recycle_cached) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, pp_recycle_cache_full) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, pp_recycle_ring) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, pp_recycle_ring_full) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, pp_recycle_released_ref) }, +#endif #ifdef CONFIG_MLX5_EN_TLS { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, tls_decrypted_packets) }, { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, tls_decrypted_bytes) }, @@ -2348,7 +2424,7 @@ MLX5E_DEFINE_STATS_GRP(channels, 0); MLX5E_DEFINE_STATS_GRP(per_port_buff_congest, 0); MLX5E_DEFINE_STATS_GRP(eth_ext, 0); static MLX5E_DEFINE_STATS_GRP(tls, 0); -static MLX5E_DEFINE_STATS_GRP(ptp, 0); +MLX5E_DEFINE_STATS_GRP(ptp, 0); static MLX5E_DEFINE_STATS_GRP(qos, 0); /* The stats groups order is opposite to the update_stats() order calls */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index 2c1ed5b81be6..a7a025d15c14 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -205,7 +205,19 @@ struct mlx5e_sw_stats { u64 ch_aff_change; u64 ch_force_irq; u64 ch_eq_rearm; - +#ifdef CONFIG_PAGE_POOL_STATS + u64 rx_pp_alloc_fast; + u64 rx_pp_alloc_slow; + u64 rx_pp_alloc_slow_high_order; + u64 rx_pp_alloc_empty; + u64 rx_pp_alloc_refill; + u64 rx_pp_alloc_waive; + u64 rx_pp_recycle_cached; + u64 rx_pp_recycle_cache_full; + u64 rx_pp_recycle_ring; + u64 rx_pp_recycle_ring_full; + u64 rx_pp_recycle_released_ref; +#endif #ifdef CONFIG_MLX5_EN_TLS u64 tx_tls_encrypted_packets; u64 tx_tls_encrypted_bytes; @@ -352,6 +364,19 @@ struct mlx5e_rq_stats { u64 congst_umr; u64 arfs_err; u64 recover; +#ifdef CONFIG_PAGE_POOL_STATS + u64 pp_alloc_fast; + u64 pp_alloc_slow; + u64 pp_alloc_slow_high_order; + u64 pp_alloc_empty; + u64 pp_alloc_refill; + u64 pp_alloc_waive; + u64 pp_recycle_cached; + u64 pp_recycle_cache_full; + u64 pp_recycle_ring; + u64 pp_recycle_ring_full; + u64 pp_recycle_released_ref; +#endif #ifdef CONFIG_MLX5_EN_TLS u64 tls_decrypted_packets; u64 tls_decrypted_bytes; @@ -459,5 +484,6 @@ extern MLX5E_DECLARE_STATS_GRP(channels); extern MLX5E_DECLARE_STATS_GRP(per_port_buff_congest); extern MLX5E_DECLARE_STATS_GRP(ipsec_hw); extern MLX5E_DECLARE_STATS_GRP(ipsec_sw); +extern MLX5E_DECLARE_STATS_GRP(ptp); #endif /* __MLX5_EN_STATS_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index b27532a9301e..e3fc15ae7bb1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -115,6 +115,7 @@ struct mlx5e_tc_attr_to_reg_mapping mlx5e_tc_attr_to_reg_mappings[] = { static struct lock_class_key tc_ht_lock_key; static void mlx5e_put_flow_tunnel_id(struct mlx5e_tc_flow *flow); +static void free_flow_post_acts(struct mlx5e_tc_flow *flow); void mlx5e_tc_match_to_reg_match(struct mlx5_flow_spec *spec, @@ -273,6 +274,23 @@ get_sample_priv(struct mlx5e_priv *priv) return NULL; } +static struct mlx5e_post_act * +get_post_action(struct mlx5e_priv *priv) +{ + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + struct mlx5_rep_uplink_priv *uplink_priv; + struct mlx5e_rep_priv *uplink_rpriv; + + if (is_mdev_switchdev_mode(priv->mdev)) { + uplink_rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH); + uplink_priv = &uplink_rpriv->uplink_priv; + + return uplink_priv->post_act; + } + + return priv->fs.tc.post_act; +} + struct mlx5_flow_handle * mlx5_tc_rule_insert(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec, @@ -295,13 +313,62 @@ mlx5_tc_rule_delete(struct mlx5e_priv *priv, if (is_mdev_switchdev_mode(priv->mdev)) { mlx5_eswitch_del_offloaded_rule(esw, rule, attr); - return; } mlx5e_del_offloaded_nic_rule(priv, rule, attr); } +struct mlx5_flow_handle * +mlx5e_tc_rule_offload(struct mlx5e_priv *priv, + struct mlx5_flow_spec *spec, + struct mlx5_flow_attr *attr) +{ + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + + if (attr->flags & MLX5_ATTR_FLAG_CT) { + struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts = + &attr->parse_attr->mod_hdr_acts; + + return mlx5_tc_ct_flow_offload(get_ct_priv(priv), + spec, attr, + mod_hdr_acts); + } + + if (!is_mdev_switchdev_mode(priv->mdev)) + return mlx5e_add_offloaded_nic_rule(priv, spec, attr); + + if (attr->flags & MLX5_ATTR_FLAG_SAMPLE) + return mlx5e_tc_sample_offload(get_sample_priv(priv), spec, attr); + + return mlx5_eswitch_add_offloaded_rule(esw, spec, attr); +} + +void +mlx5e_tc_rule_unoffload(struct mlx5e_priv *priv, + struct mlx5_flow_handle *rule, + struct mlx5_flow_attr *attr) +{ + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + + if (attr->flags & MLX5_ATTR_FLAG_CT) { + mlx5_tc_ct_delete_flow(get_ct_priv(priv), attr); + return; + } + + if (!is_mdev_switchdev_mode(priv->mdev)) { + mlx5e_del_offloaded_nic_rule(priv, rule, attr); + return; + } + + if (attr->flags & MLX5_ATTR_FLAG_SAMPLE) { + mlx5e_tc_sample_unoffload(get_sample_priv(priv), rule, attr); + return; + } + + mlx5_eswitch_del_offloaded_rule(esw, rule, attr); +} + int mlx5e_tc_match_to_reg_set(struct mlx5_core_dev *mdev, struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts, @@ -1038,6 +1105,21 @@ mlx5e_add_offloaded_nic_rule(struct mlx5e_priv *priv, return ERR_CAST(rule); } +static int +alloc_flow_attr_counter(struct mlx5_core_dev *counter_dev, + struct mlx5_flow_attr *attr) + +{ + struct mlx5_fc *counter; + + counter = mlx5_fc_create(counter_dev, true); + if (IS_ERR(counter)) + return PTR_ERR(counter); + + attr->counter = counter; + return 0; +} + static int mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, @@ -1046,7 +1128,6 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow_parse_attr *parse_attr; struct mlx5_flow_attr *attr = flow->attr; struct mlx5_core_dev *dev = priv->mdev; - struct mlx5_fc *counter; int err; parse_attr = attr->parse_attr; @@ -1058,11 +1139,9 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, } if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { - counter = mlx5_fc_create(dev, true); - if (IS_ERR(counter)) - return PTR_ERR(counter); - - attr->counter = counter; + err = alloc_flow_attr_counter(dev, attr); + if (err) + return err; } if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { @@ -1072,8 +1151,8 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, return err; } - if (flow_flag_test(flow, CT)) - flow->rule[0] = mlx5_tc_ct_flow_offload(get_ct_priv(priv), flow, &parse_attr->spec, + if (attr->flags & MLX5_ATTR_FLAG_CT) + flow->rule[0] = mlx5_tc_ct_flow_offload(get_ct_priv(priv), &parse_attr->spec, attr, &parse_attr->mod_hdr_acts); else flow->rule[0] = mlx5e_add_offloaded_nic_rule(priv, &parse_attr->spec, @@ -1107,8 +1186,8 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv, flow_flag_clear(flow, OFFLOADED); - if (flow_flag_test(flow, CT)) - mlx5_tc_ct_delete_flow(get_ct_priv(flow->priv), flow, attr); + if (attr->flags & MLX5_ATTR_FLAG_CT) + mlx5_tc_ct_delete_flow(get_ct_priv(flow->priv), attr); else if (!IS_ERR_OR_NULL(flow->rule[0])) mlx5e_del_offloaded_nic_rule(priv, flow->rule[0], attr); @@ -1132,6 +1211,8 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv, if (flow_flag_test(flow, HAIRPIN)) mlx5e_hairpin_flow_del(priv, flow); + free_flow_post_acts(flow); + kvfree(attr->parse_attr); kfree(flow->attr); } @@ -1142,40 +1223,27 @@ mlx5e_tc_offload_fdb_rules(struct mlx5_eswitch *esw, struct mlx5_flow_spec *spec, struct mlx5_flow_attr *attr) { - struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts; struct mlx5_flow_handle *rule; - if (attr->flags & MLX5_ESW_ATTR_FLAG_SLOW_PATH) + if (attr->flags & MLX5_ATTR_FLAG_SLOW_PATH) return mlx5_eswitch_add_offloaded_rule(esw, spec, attr); - if (flow_flag_test(flow, CT)) { - mod_hdr_acts = &attr->parse_attr->mod_hdr_acts; - - rule = mlx5_tc_ct_flow_offload(get_ct_priv(flow->priv), - flow, spec, attr, - mod_hdr_acts); - } else if (flow_flag_test(flow, SAMPLE)) { - rule = mlx5e_tc_sample_offload(get_sample_priv(flow->priv), spec, attr, - mlx5e_tc_get_flow_tun_id(flow)); - } else { - rule = mlx5_eswitch_add_offloaded_rule(esw, spec, attr); - } + rule = mlx5e_tc_rule_offload(flow->priv, spec, attr); if (IS_ERR(rule)) return rule; if (attr->esw_attr->split_count) { flow->rule[1] = mlx5_eswitch_add_fwd_rule(esw, spec, attr); - if (IS_ERR(flow->rule[1])) { - if (flow_flag_test(flow, CT)) - mlx5_tc_ct_delete_flow(get_ct_priv(flow->priv), flow, attr); - else - mlx5_eswitch_del_offloaded_rule(esw, rule, attr); - return flow->rule[1]; - } + if (IS_ERR(flow->rule[1])) + goto err_rule1; } return rule; + +err_rule1: + mlx5e_tc_rule_unoffload(flow->priv, rule, attr); + return flow->rule[1]; } void mlx5e_tc_unoffload_fdb_rules(struct mlx5_eswitch *esw, @@ -1184,19 +1252,13 @@ void mlx5e_tc_unoffload_fdb_rules(struct mlx5_eswitch *esw, { flow_flag_clear(flow, OFFLOADED); - if (attr->flags & MLX5_ESW_ATTR_FLAG_SLOW_PATH) - goto offload_rule_0; + if (attr->flags & MLX5_ATTR_FLAG_SLOW_PATH) + return mlx5_eswitch_del_offloaded_rule(esw, flow->rule[0], attr); if (attr->esw_attr->split_count) mlx5_eswitch_del_fwd_rule(esw, flow->rule[1], attr); - if (flow_flag_test(flow, CT)) - mlx5_tc_ct_delete_flow(get_ct_priv(flow->priv), flow, attr); - else if (flow_flag_test(flow, SAMPLE)) - mlx5e_tc_sample_unoffload(get_sample_priv(flow->priv), flow->rule[0], attr); - else -offload_rule_0: - mlx5_eswitch_del_offloaded_rule(esw, flow->rule[0], attr); + mlx5e_tc_rule_unoffload(flow->priv, flow->rule[0], attr); } struct mlx5_flow_handle * @@ -1214,7 +1276,7 @@ mlx5e_tc_offload_to_slow_path(struct mlx5_eswitch *esw, memcpy(slow_attr, flow->attr, ESW_FLOW_ATTR_SZ); slow_attr->action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; slow_attr->esw_attr->split_count = 0; - slow_attr->flags |= MLX5_ESW_ATTR_FLAG_SLOW_PATH; + slow_attr->flags |= MLX5_ATTR_FLAG_SLOW_PATH; rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, slow_attr); if (!IS_ERR(rule)) @@ -1239,7 +1301,7 @@ void mlx5e_tc_unoffload_from_slow_path(struct mlx5_eswitch *esw, memcpy(slow_attr, flow->attr, ESW_FLOW_ATTR_SZ); slow_attr->action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; slow_attr->esw_attr->split_count = 0; - slow_attr->flags |= MLX5_ESW_ATTR_FLAG_SLOW_PATH; + slow_attr->flags |= MLX5_ATTR_FLAG_SLOW_PATH; mlx5e_tc_unoffload_fdb_rules(esw, flow, slow_attr); flow_flag_clear(flow, SLOW); kfree(slow_attr); @@ -1348,10 +1410,10 @@ int mlx5e_tc_query_route_vport(struct net_device *out_dev, struct net_device *ro } int mlx5e_tc_add_flow_mod_hdr(struct mlx5e_priv *priv, - struct mlx5e_tc_flow_parse_attr *parse_attr, - struct mlx5e_tc_flow *flow) + struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr) { - struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts = &parse_attr->mod_hdr_acts; + struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts = &attr->parse_attr->mod_hdr_acts; struct mlx5_modify_hdr *mod_hdr; mod_hdr = mlx5_modify_header_alloc(priv->mdev, @@ -1361,12 +1423,106 @@ int mlx5e_tc_add_flow_mod_hdr(struct mlx5e_priv *priv, if (IS_ERR(mod_hdr)) return PTR_ERR(mod_hdr); - WARN_ON(flow->attr->modify_hdr); - flow->attr->modify_hdr = mod_hdr; + WARN_ON(attr->modify_hdr); + attr->modify_hdr = mod_hdr; return 0; } +static int +set_encap_dests(struct mlx5e_priv *priv, + struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr, + struct netlink_ext_ack *extack, + bool *encap_valid, + bool *vf_tun) +{ + struct mlx5e_tc_flow_parse_attr *parse_attr; + struct mlx5_esw_flow_attr *esw_attr; + struct net_device *encap_dev = NULL; + struct mlx5e_rep_priv *rpriv; + struct mlx5e_priv *out_priv; + int out_index; + int err = 0; + + if (!mlx5e_is_eswitch_flow(flow)) + return 0; + + parse_attr = attr->parse_attr; + esw_attr = attr->esw_attr; + *vf_tun = false; + *encap_valid = true; + + for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++) { + struct net_device *out_dev; + int mirred_ifindex; + + if (!(esw_attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP)) + continue; + + mirred_ifindex = parse_attr->mirred_ifindex[out_index]; + out_dev = dev_get_by_index(dev_net(priv->netdev), mirred_ifindex); + if (!out_dev) { + NL_SET_ERR_MSG_MOD(extack, "Requested mirred device not found"); + err = -ENODEV; + goto out; + } + err = mlx5e_attach_encap(priv, flow, attr, out_dev, out_index, + extack, &encap_dev, encap_valid); + dev_put(out_dev); + if (err) + goto out; + + if (esw_attr->dests[out_index].flags & + MLX5_ESW_DEST_CHAIN_WITH_SRC_PORT_CHANGE && + !esw_attr->dest_int_port) + *vf_tun = true; + + out_priv = netdev_priv(encap_dev); + rpriv = out_priv->ppriv; + esw_attr->dests[out_index].rep = rpriv->rep; + esw_attr->dests[out_index].mdev = out_priv->mdev; + } + + if (*vf_tun && esw_attr->out_count > 1) { + NL_SET_ERR_MSG_MOD(extack, "VF tunnel encap with mirroring is not supported"); + err = -EOPNOTSUPP; + goto out; + } + +out: + return err; +} + +static void +clean_encap_dests(struct mlx5e_priv *priv, + struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr, + bool *vf_tun) +{ + struct mlx5_esw_flow_attr *esw_attr; + int out_index; + + if (!mlx5e_is_eswitch_flow(flow)) + return; + + esw_attr = attr->esw_attr; + *vf_tun = false; + + for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++) { + if (!(esw_attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP)) + continue; + + if (esw_attr->dests[out_index].flags & + MLX5_ESW_DEST_CHAIN_WITH_SRC_PORT_CHANGE && + !esw_attr->dest_int_port) + *vf_tun = true; + + mlx5e_detach_encap(priv, flow, attr, out_index); + kfree(attr->parse_attr->tun_info[out_index]); + } +} + static int mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, @@ -1375,15 +1531,10 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5e_tc_flow_parse_attr *parse_attr; struct mlx5_flow_attr *attr = flow->attr; - bool vf_tun = false, encap_valid = true; - struct net_device *encap_dev = NULL; struct mlx5_esw_flow_attr *esw_attr; - struct mlx5e_rep_priv *rpriv; - struct mlx5e_priv *out_priv; - struct mlx5_fc *counter; + bool vf_tun, encap_valid; u32 max_prio, max_chain; int err = 0; - int out_index; parse_attr = attr->parse_attr; esw_attr = attr->esw_attr; @@ -1472,50 +1623,17 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, esw_attr->int_port = int_port; } - for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++) { - struct net_device *out_dev; - int mirred_ifindex; - - if (!(esw_attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP)) - continue; - - mirred_ifindex = parse_attr->mirred_ifindex[out_index]; - out_dev = dev_get_by_index(dev_net(priv->netdev), mirred_ifindex); - if (!out_dev) { - NL_SET_ERR_MSG_MOD(extack, "Requested mirred device not found"); - err = -ENODEV; - goto err_out; - } - err = mlx5e_attach_encap(priv, flow, out_dev, out_index, - extack, &encap_dev, &encap_valid); - dev_put(out_dev); - if (err) - goto err_out; - - if (esw_attr->dests[out_index].flags & - MLX5_ESW_DEST_CHAIN_WITH_SRC_PORT_CHANGE && - !esw_attr->dest_int_port) - vf_tun = true; - out_priv = netdev_priv(encap_dev); - rpriv = out_priv->ppriv; - esw_attr->dests[out_index].rep = rpriv->rep; - esw_attr->dests[out_index].mdev = out_priv->mdev; - } - - if (vf_tun && esw_attr->out_count > 1) { - NL_SET_ERR_MSG_MOD(extack, "VF tunnel encap with mirroring is not supported"); - err = -EOPNOTSUPP; + err = set_encap_dests(priv, flow, attr, extack, &encap_valid, &vf_tun); + if (err) goto err_out; - } err = mlx5_eswitch_add_vlan_action(esw, attr); if (err) goto err_out; - if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR && - !(attr->ct_attr.ct_action & TCA_CT_ACT_CLEAR)) { + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { if (vf_tun) { - err = mlx5e_tc_add_flow_mod_hdr(priv, parse_attr, flow); + err = mlx5e_tc_add_flow_mod_hdr(priv, flow, attr); if (err) goto err_out; } else { @@ -1526,20 +1644,16 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, } if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { - counter = mlx5_fc_create(esw_attr->counter_dev, true); - if (IS_ERR(counter)) { - err = PTR_ERR(counter); + err = alloc_flow_attr_counter(esw_attr->counter_dev, attr); + if (err) goto err_out; - } - - attr->counter = counter; } /* we get here if one of the following takes place: * (1) there's no error * (2) there's an encap action and we don't have valid neigh */ - if (!encap_valid) + if (!encap_valid || flow_flag_test(flow, SLOW)) flow->rule[0] = mlx5e_tc_offload_to_slow_path(esw, flow, &parse_attr->spec); else flow->rule[0] = mlx5e_tc_offload_fdb_rules(esw, flow, &parse_attr->spec, attr); @@ -1576,8 +1690,7 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5_flow_attr *attr = flow->attr; struct mlx5_esw_flow_attr *esw_attr; - bool vf_tun = false; - int out_index; + bool vf_tun; esw_attr = attr->esw_attr; mlx5e_put_flow_tunnel_id(flow); @@ -1601,16 +1714,7 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, if (flow->decap_route) mlx5e_detach_decap_route(priv, flow); - for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++) { - if (esw_attr->dests[out_index].flags & - MLX5_ESW_DEST_CHAIN_WITH_SRC_PORT_CHANGE && - !esw_attr->dest_int_port) - vf_tun = true; - if (esw_attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP) { - mlx5e_detach_encap(priv, flow, out_index); - kfree(attr->parse_attr->tun_info[out_index]); - } - } + clean_encap_dests(priv, flow, attr, &vf_tun); mlx5_tc_ct_match_del(get_ct_priv(priv), &flow->attr->ct_attr); @@ -1634,7 +1738,8 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, if (flow_flag_test(flow, L3_TO_L2_DECAP)) mlx5e_detach_decap(priv, flow); - kfree(attr->sample_attr); + free_flow_post_acts(flow); + kvfree(attr->esw_attr->rx_tun_attr); kvfree(attr->parse_attr); kfree(flow->attr); @@ -1642,7 +1747,10 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, struct mlx5_fc *mlx5e_tc_get_counter(struct mlx5e_tc_flow *flow) { - return flow->attr->counter; + struct mlx5_flow_attr *attr; + + attr = list_first_entry(&flow->attrs, struct mlx5_flow_attr, list); + return attr->counter; } /* Iterate over tmp_list of flows attached to flow_list head. */ @@ -1854,7 +1962,7 @@ static int mlx5e_get_flow_tunnel_id(struct mlx5e_priv *priv, attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; } - flow->tunnel_id = value; + flow->attr->tunnel_id = value; return 0; err_set: @@ -1868,8 +1976,8 @@ static int mlx5e_get_flow_tunnel_id(struct mlx5e_priv *priv, static void mlx5e_put_flow_tunnel_id(struct mlx5e_tc_flow *flow) { - u32 enc_opts_id = flow->tunnel_id & ENC_OPTS_BITS_MASK; - u32 tun_id = flow->tunnel_id >> ENC_OPTS_BITS; + u32 enc_opts_id = flow->attr->tunnel_id & ENC_OPTS_BITS_MASK; + u32 tun_id = flow->attr->tunnel_id >> ENC_OPTS_BITS; struct mlx5_rep_uplink_priv *uplink_priv; struct mlx5e_rep_priv *uplink_rpriv; struct mlx5_eswitch *esw; @@ -1885,11 +1993,6 @@ static void mlx5e_put_flow_tunnel_id(struct mlx5e_tc_flow *flow) enc_opts_id); } -u32 mlx5e_tc_get_flow_tun_id(struct mlx5e_tc_flow *flow) -{ - return flow->tunnel_id; -} - void mlx5e_tc_set_ethertype(struct mlx5_core_dev *mdev, struct flow_match_basic *match, bool outer, void *headers_c, void *headers_v) @@ -2811,14 +2914,15 @@ static unsigned long mask_to_le(unsigned long mask, int size) return mask; } + static int offload_pedit_fields(struct mlx5e_priv *priv, int namespace, - struct pedit_headers_action *hdrs, struct mlx5e_tc_flow_parse_attr *parse_attr, u32 *action_flags, struct netlink_ext_ack *extack) { struct pedit_headers *set_masks, *add_masks, *set_vals, *add_vals; + struct pedit_headers_action *hdrs = parse_attr->hdrs; void *headers_c, *headers_v, *action, *vals_p; u32 *s_masks_p, *a_masks_p, s_mask, a_mask; struct mlx5e_tc_mod_hdr_acts *mod_acts; @@ -2944,35 +3048,43 @@ static int offload_pedit_fields(struct mlx5e_priv *priv, static const struct pedit_headers zero_masks = {}; -static int alloc_tc_pedit_action(struct mlx5e_priv *priv, int namespace, - struct mlx5e_tc_flow_parse_attr *parse_attr, - struct pedit_headers_action *hdrs, - u32 *action_flags, - struct netlink_ext_ack *extack) +static int verify_offload_pedit_fields(struct mlx5e_priv *priv, + struct mlx5e_tc_flow_parse_attr *parse_attr, + struct netlink_ext_ack *extack) { struct pedit_headers *cmd_masks; - int err; u8 cmd; - err = offload_pedit_fields(priv, namespace, hdrs, parse_attr, - action_flags, extack); - if (err < 0) - goto out_dealloc_parsed_actions; - for (cmd = 0; cmd < __PEDIT_CMD_MAX; cmd++) { - cmd_masks = &hdrs[cmd].masks; + cmd_masks = &parse_attr->hdrs[cmd].masks; if (memcmp(cmd_masks, &zero_masks, sizeof(zero_masks))) { - NL_SET_ERR_MSG_MOD(extack, - "attempt to offload an unsupported field"); + NL_SET_ERR_MSG_MOD(extack, "attempt to offload an unsupported field"); netdev_warn(priv->netdev, "attempt to offload an unsupported field (cmd %d)\n", cmd); print_hex_dump(KERN_WARNING, "mask: ", DUMP_PREFIX_ADDRESS, 16, 1, cmd_masks, sizeof(zero_masks), true); - err = -EOPNOTSUPP; - goto out_dealloc_parsed_actions; + return -EOPNOTSUPP; } } return 0; +} + +static int alloc_tc_pedit_action(struct mlx5e_priv *priv, int namespace, + struct mlx5e_tc_flow_parse_attr *parse_attr, + u32 *action_flags, + struct netlink_ext_ack *extack) +{ + int err; + + err = offload_pedit_fields(priv, namespace, parse_attr, action_flags, extack); + if (err) + goto out_dealloc_parsed_actions; + + err = verify_offload_pedit_fields(priv, parse_attr, extack); + if (err) + goto out_dealloc_parsed_actions; + + return 0; out_dealloc_parsed_actions: mlx5e_mod_hdr_dealloc(&parse_attr->mod_hdr_acts); @@ -3176,11 +3288,11 @@ actions_match_supported_fdb(struct mlx5e_priv *priv, static bool actions_match_supported(struct mlx5e_priv *priv, struct flow_action *flow_action, + u32 actions, struct mlx5e_tc_flow_parse_attr *parse_attr, struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack) { - u32 actions = flow->attr->action; bool ct_flow, ct_clear; ct_clear = flow->attr->ct_attr.ct_action & TCA_CT_ACT_CLEAR; @@ -3247,58 +3359,14 @@ bool mlx5e_same_hw_devs(struct mlx5e_priv *priv, struct mlx5e_priv *peer_priv) return (fsystem_guid == psystem_guid); } -static int -parse_tc_actions(struct mlx5e_tc_act_parse_state *parse_state, - struct flow_action *flow_action) -{ - struct netlink_ext_ack *extack = parse_state->extack; - struct mlx5e_tc_flow *flow = parse_state->flow; - struct mlx5_flow_attr *attr = flow->attr; - enum mlx5_flow_namespace_type ns_type; - struct mlx5e_priv *priv = flow->priv; - const struct flow_action_entry *act; - struct mlx5e_tc_act *tc_act; - int err, i; - - ns_type = mlx5e_get_flow_namespace(flow); - - flow_action_for_each(i, act, flow_action) { - tc_act = mlx5e_tc_act_get(act->id, ns_type); - if (!tc_act) { - NL_SET_ERR_MSG_MOD(extack, "Not implemented offload action"); - return -EOPNOTSUPP; - } - - if (!tc_act->can_offload(parse_state, act, i)) - return -EOPNOTSUPP; - - err = tc_act->parse_action(parse_state, act, priv, attr); - if (err) - return err; - } - - flow_action_for_each(i, act, flow_action) { - tc_act = mlx5e_tc_act_get(act->id, ns_type); - if (!tc_act || !tc_act->post_parse || - !tc_act->can_offload(parse_state, act, i)) - continue; - - err = tc_act->post_parse(parse_state, priv, attr); - if (err) - return err; - } - - return 0; -} - static int actions_prepare_mod_hdr_actions(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr, - struct pedit_headers_action *hdrs, struct netlink_ext_ack *extack) { struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr; + struct pedit_headers_action *hdrs = parse_attr->hdrs; enum mlx5_flow_namespace_type ns_type; int err; @@ -3308,8 +3376,7 @@ actions_prepare_mod_hdr_actions(struct mlx5e_priv *priv, ns_type = mlx5e_get_flow_namespace(flow); - err = alloc_tc_pedit_action(priv, ns_type, parse_attr, hdrs, - &attr->action, extack); + err = alloc_tc_pedit_action(priv, ns_type, parse_attr, &attr->action, extack); if (err) return err; @@ -3330,6 +3397,299 @@ actions_prepare_mod_hdr_actions(struct mlx5e_priv *priv, return 0; } +static struct mlx5_flow_attr* +mlx5e_clone_flow_attr_for_post_act(struct mlx5_flow_attr *attr, + enum mlx5_flow_namespace_type ns_type) +{ + struct mlx5e_tc_flow_parse_attr *parse_attr; + u32 attr_sz = ns_to_attr_sz(ns_type); + struct mlx5_flow_attr *attr2; + + attr2 = mlx5_alloc_flow_attr(ns_type); + parse_attr = kvzalloc(sizeof(*parse_attr), GFP_KERNEL); + if (!attr2 || !parse_attr) { + kvfree(parse_attr); + kfree(attr2); + return NULL; + } + + memcpy(attr2, attr, attr_sz); + INIT_LIST_HEAD(&attr2->list); + parse_attr->filter_dev = attr->parse_attr->filter_dev; + attr2->action = 0; + attr2->flags = 0; + attr2->parse_attr = parse_attr; + return attr2; +} + +static struct mlx5_core_dev * +get_flow_counter_dev(struct mlx5e_tc_flow *flow) +{ + return mlx5e_is_eswitch_flow(flow) ? flow->attr->esw_attr->counter_dev : flow->priv->mdev; +} + +struct mlx5_flow_attr * +mlx5e_tc_get_encap_attr(struct mlx5e_tc_flow *flow) +{ + struct mlx5_esw_flow_attr *esw_attr; + struct mlx5_flow_attr *attr; + int i; + + list_for_each_entry(attr, &flow->attrs, list) { + esw_attr = attr->esw_attr; + for (i = 0; i < MLX5_MAX_FLOW_FWD_VPORTS; i++) { + if (esw_attr->dests[i].flags & MLX5_ESW_DEST_ENCAP) + return attr; + } + } + + return NULL; +} + +void +mlx5e_tc_unoffload_flow_post_acts(struct mlx5e_tc_flow *flow) +{ + struct mlx5e_post_act *post_act = get_post_action(flow->priv); + struct mlx5_flow_attr *attr; + + list_for_each_entry(attr, &flow->attrs, list) { + if (list_is_last(&attr->list, &flow->attrs)) + break; + + mlx5e_tc_post_act_unoffload(post_act, attr->post_act_handle); + } +} + +static void +free_flow_post_acts(struct mlx5e_tc_flow *flow) +{ + struct mlx5_core_dev *counter_dev = get_flow_counter_dev(flow); + struct mlx5e_post_act *post_act = get_post_action(flow->priv); + struct mlx5_flow_attr *attr, *tmp; + bool vf_tun; + + list_for_each_entry_safe(attr, tmp, &flow->attrs, list) { + if (list_is_last(&attr->list, &flow->attrs)) + break; + + if (attr->post_act_handle) + mlx5e_tc_post_act_del(post_act, attr->post_act_handle); + + clean_encap_dests(flow->priv, flow, attr, &vf_tun); + + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) + mlx5_fc_destroy(counter_dev, attr->counter); + + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { + mlx5e_mod_hdr_dealloc(&attr->parse_attr->mod_hdr_acts); + if (attr->modify_hdr) + mlx5_modify_header_dealloc(flow->priv->mdev, attr->modify_hdr); + } + + list_del(&attr->list); + kvfree(attr->parse_attr); + kfree(attr); + } +} + +int +mlx5e_tc_offload_flow_post_acts(struct mlx5e_tc_flow *flow) +{ + struct mlx5e_post_act *post_act = get_post_action(flow->priv); + struct mlx5_flow_attr *attr; + int err = 0; + + list_for_each_entry(attr, &flow->attrs, list) { + if (list_is_last(&attr->list, &flow->attrs)) + break; + + err = mlx5e_tc_post_act_offload(post_act, attr->post_act_handle); + if (err) + break; + } + + return err; +} + +/* TC filter rule HW translation: + * + * +---------------------+ + * + ft prio (tc chain) + + * + original match + + * +---------------------+ + * | + * | if multi table action + * | + * v + * +---------------------+ + * + post act ft |<----. + * + match fte id | | split on multi table action + * + do actions |-----' + * +---------------------+ + * | + * | + * v + * Do rest of the actions after last multi table action. + */ +static int +alloc_flow_post_acts(struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack) +{ + struct mlx5e_post_act *post_act = get_post_action(flow->priv); + struct mlx5_flow_attr *attr, *next_attr = NULL; + struct mlx5e_post_act_handle *handle; + bool vf_tun, encap_valid = true; + int err; + + /* This is going in reverse order as needed. + * The first entry is the last attribute. + */ + list_for_each_entry(attr, &flow->attrs, list) { + if (!next_attr) { + /* Set counter action on last post act rule. */ + attr->action |= MLX5_FLOW_CONTEXT_ACTION_COUNT; + } else { + err = mlx5e_tc_act_set_next_post_act(flow, attr, next_attr); + if (err) + goto out_free; + } + + /* Don't add post_act rule for first attr (last in the list). + * It's being handled by the caller. + */ + if (list_is_last(&attr->list, &flow->attrs)) + break; + + err = set_encap_dests(flow->priv, flow, attr, extack, &encap_valid, &vf_tun); + if (err) + goto out_free; + + if (!encap_valid) + flow_flag_set(flow, SLOW); + + err = actions_prepare_mod_hdr_actions(flow->priv, flow, attr, extack); + if (err) + goto out_free; + + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { + err = mlx5e_tc_add_flow_mod_hdr(flow->priv, flow, attr); + if (err) + goto out_free; + } + + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { + err = alloc_flow_attr_counter(get_flow_counter_dev(flow), attr); + if (err) + goto out_free; + } + + handle = mlx5e_tc_post_act_add(post_act, attr); + if (IS_ERR(handle)) { + err = PTR_ERR(handle); + goto out_free; + } + + attr->post_act_handle = handle; + next_attr = attr; + } + + if (flow_flag_test(flow, SLOW)) + goto out; + + err = mlx5e_tc_offload_flow_post_acts(flow); + if (err) + goto out_free; + +out: + return 0; + +out_free: + free_flow_post_acts(flow); + return err; +} + +static int +parse_tc_actions(struct mlx5e_tc_act_parse_state *parse_state, + struct flow_action *flow_action) +{ + struct netlink_ext_ack *extack = parse_state->extack; + struct mlx5e_tc_flow_action flow_action_reorder; + struct mlx5e_tc_flow *flow = parse_state->flow; + struct mlx5_flow_attr *attr = flow->attr; + enum mlx5_flow_namespace_type ns_type; + struct mlx5e_priv *priv = flow->priv; + struct flow_action_entry *act, **_act; + struct mlx5e_tc_act *tc_act; + int err, i; + + flow_action_reorder.num_entries = flow_action->num_entries; + flow_action_reorder.entries = kcalloc(flow_action->num_entries, + sizeof(flow_action), GFP_KERNEL); + if (!flow_action_reorder.entries) + return -ENOMEM; + + mlx5e_tc_act_reorder_flow_actions(flow_action, &flow_action_reorder); + + ns_type = mlx5e_get_flow_namespace(flow); + list_add(&attr->list, &flow->attrs); + + flow_action_for_each(i, _act, &flow_action_reorder) { + act = *_act; + tc_act = mlx5e_tc_act_get(act->id, ns_type); + if (!tc_act) { + NL_SET_ERR_MSG_MOD(extack, "Not implemented offload action"); + err = -EOPNOTSUPP; + goto out_free; + } + + if (!tc_act->can_offload(parse_state, act, i, attr)) { + err = -EOPNOTSUPP; + goto out_free; + } + + err = tc_act->parse_action(parse_state, act, priv, attr); + if (err) + goto out_free; + + parse_state->actions |= attr->action; + + /* Split attr for multi table act if not the last act. */ + if (tc_act->is_multi_table_act && + tc_act->is_multi_table_act(priv, act, attr) && + i < flow_action_reorder.num_entries - 1) { + err = mlx5e_tc_act_post_parse(parse_state, flow_action, attr, ns_type); + if (err) + goto out_free; + + attr = mlx5e_clone_flow_attr_for_post_act(flow->attr, ns_type); + if (!attr) { + err = -ENOMEM; + goto out_free; + } + + list_add(&attr->list, &flow->attrs); + } + } + + kfree(flow_action_reorder.entries); + + err = mlx5e_tc_act_post_parse(parse_state, flow_action, attr, ns_type); + if (err) + goto out_free_post_acts; + + err = alloc_flow_post_acts(flow, extack); + if (err) + goto out_free_post_acts; + + return 0; + +out_free: + kfree(flow_action_reorder.entries); +out_free_post_acts: + free_flow_post_acts(flow); + + return err; +} + static int flow_action_supported(struct flow_action *flow_action, struct netlink_ext_ack *extack) @@ -3357,7 +3717,6 @@ parse_tc_nic_actions(struct mlx5e_priv *priv, struct mlx5e_tc_act_parse_state *parse_state; struct mlx5e_tc_flow_parse_attr *parse_attr; struct mlx5_flow_attr *attr = flow->attr; - struct pedit_headers_action *hdrs; int err; err = flow_action_supported(flow_action, extack); @@ -3369,17 +3728,17 @@ parse_tc_nic_actions(struct mlx5e_priv *priv, parse_state = &parse_attr->parse_state; mlx5e_tc_act_init_parse_state(parse_state, flow, flow_action, extack); parse_state->ct_priv = get_ct_priv(priv); - hdrs = parse_state->hdrs; err = parse_tc_actions(parse_state, flow_action); if (err) return err; - err = actions_prepare_mod_hdr_actions(priv, flow, attr, hdrs, extack); + err = actions_prepare_mod_hdr_actions(priv, flow, attr, extack); if (err) return err; - if (!actions_match_supported(priv, flow_action, parse_attr, flow, extack)) + if (!actions_match_supported(priv, flow_action, parse_state->actions, + parse_attr, flow, extack)) return -EOPNOTSUPP; return 0; @@ -3480,7 +3839,6 @@ parse_tc_fdb_actions(struct mlx5e_priv *priv, struct mlx5e_tc_flow_parse_attr *parse_attr; struct mlx5_flow_attr *attr = flow->attr; struct mlx5_esw_flow_attr *esw_attr; - struct pedit_headers_action *hdrs; int err; err = flow_action_supported(flow_action, extack); @@ -3492,7 +3850,6 @@ parse_tc_fdb_actions(struct mlx5e_priv *priv, parse_state = &parse_attr->parse_state; mlx5e_tc_act_init_parse_state(parse_state, flow, flow_action, extack); parse_state->ct_priv = get_ct_priv(priv); - hdrs = parse_state->hdrs; err = parse_tc_actions(parse_state, flow_action); if (err) @@ -3506,11 +3863,12 @@ parse_tc_fdb_actions(struct mlx5e_priv *priv, return -EOPNOTSUPP; } - err = actions_prepare_mod_hdr_actions(priv, flow, attr, hdrs, extack); + err = actions_prepare_mod_hdr_actions(priv, flow, attr, extack); if (err) return err; - if (!actions_match_supported(priv, flow_action, parse_attr, flow, extack)) + if (!actions_match_supported(priv, flow_action, parse_state->actions, + parse_attr, flow, extack)) return -EOPNOTSUPP; return 0; @@ -3545,12 +3903,11 @@ static const struct rhashtable_params tc_ht_params = { static struct rhashtable *get_tc_ht(struct mlx5e_priv *priv, unsigned long flags) { - struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; - struct mlx5e_rep_priv *uplink_rpriv; + struct mlx5e_rep_priv *rpriv; if (flags & MLX5_TC_FLAG(ESW_OFFLOAD)) { - uplink_rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH); - return &uplink_rpriv->uplink_priv.tc_ht; + rpriv = priv->ppriv; + return &rpriv->tc_ht; } else /* NIC offload */ return &priv->fs.tc.ht; } @@ -3585,7 +3942,12 @@ mlx5_alloc_flow_attr(enum mlx5_flow_namespace_type type) sizeof(struct mlx5_nic_flow_attr); struct mlx5_flow_attr *attr; - return kzalloc(sizeof(*attr) + ex_attr_size, GFP_KERNEL); + attr = kzalloc(sizeof(*attr) + ex_attr_size, GFP_KERNEL); + if (!attr) + return attr; + + INIT_LIST_HEAD(&attr->list); + return attr; } static int @@ -3619,6 +3981,7 @@ mlx5e_alloc_flow(struct mlx5e_priv *priv, int attr_size, INIT_LIST_HEAD(&flow->encaps[out_index].list); INIT_LIST_HEAD(&flow->hairpin); INIT_LIST_HEAD(&flow->l3_to_l2_reformat); + INIT_LIST_HEAD(&flow->attrs); refcount_set(&flow->refcnt, 1); init_completion(&flow->init_done); init_completion(&flow->del_hw_done); @@ -4119,6 +4482,46 @@ static int apply_police_params(struct mlx5e_priv *priv, u64 rate, return err; } +static int mlx5e_policer_validate(const struct flow_action *action, + const struct flow_action_entry *act, + struct netlink_ext_ack *extack) +{ + if (act->police.exceed.act_id != FLOW_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when exceed action is not drop"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && + act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not pipe or ok"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && + !flow_action_is_last_entry(action, act)) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is ok, but action is not last"); + return -EOPNOTSUPP; + } + + if (act->police.peakrate_bytes_ps || + act->police.avrate || act->police.overhead) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when peakrate/avrate/overhead is configured"); + return -EOPNOTSUPP; + } + + if (act->police.rate_pkt_ps) { + NL_SET_ERR_MSG_MOD(extack, + "QoS offload not support packets per second"); + return -EOPNOTSUPP; + } + + return 0; +} + static int scan_tc_matchall_fdb_actions(struct mlx5e_priv *priv, struct flow_action *flow_action, struct netlink_ext_ack *extack) @@ -4146,10 +4549,10 @@ static int scan_tc_matchall_fdb_actions(struct mlx5e_priv *priv, flow_action_for_each(i, act, flow_action) { switch (act->id) { case FLOW_ACTION_POLICE: - if (act->police.rate_pkt_ps) { - NL_SET_ERR_MSG_MOD(extack, "QoS offload not support packets per second"); - return -EOPNOTSUPP; - } + err = mlx5e_policer_validate(flow_action, act, extack); + if (err) + return err; + err = apply_police_params(priv, act->police.rate_bytes_ps, extack); if (err) return err; @@ -4383,10 +4786,27 @@ void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv) mlx5_chains_destroy(tc->chains); } -int mlx5e_tc_esw_init(struct rhashtable *tc_ht) +int mlx5e_tc_ht_init(struct rhashtable *tc_ht) +{ + int err; + + err = rhashtable_init(tc_ht, &tc_ht_params); + if (err) + return err; + + lockdep_set_class(&tc_ht->mutex, &tc_ht_lock_key); + + return 0; +} + +void mlx5e_tc_ht_cleanup(struct rhashtable *tc_ht) +{ + rhashtable_free_and_destroy(tc_ht, _mlx5e_tc_del_flow, NULL); +} + +int mlx5e_tc_esw_init(struct mlx5_rep_uplink_priv *uplink_priv) { const size_t sz_enc_opts = sizeof(struct tunnel_match_enc_opts); - struct mlx5_rep_uplink_priv *uplink_priv; struct mlx5e_rep_priv *rpriv; struct mapping_ctx *mapping; struct mlx5_eswitch *esw; @@ -4394,7 +4814,6 @@ int mlx5e_tc_esw_init(struct rhashtable *tc_ht) u64 mapping_id; int err = 0; - uplink_priv = container_of(tc_ht, struct mlx5_rep_uplink_priv, tc_ht); rpriv = container_of(uplink_priv, struct mlx5e_rep_priv, uplink_priv); priv = netdev_priv(rpriv->netdev); esw = priv->mdev->priv.eswitch; @@ -4434,12 +4853,6 @@ int mlx5e_tc_esw_init(struct rhashtable *tc_ht) } uplink_priv->tunnel_enc_opts_mapping = mapping; - err = rhashtable_init(tc_ht, &tc_ht_params); - if (err) - goto err_ht_init; - - lockdep_set_class(&tc_ht->mutex, &tc_ht_lock_key); - uplink_priv->encap = mlx5e_tc_tun_init(priv); if (IS_ERR(uplink_priv->encap)) { err = PTR_ERR(uplink_priv->encap); @@ -4449,8 +4862,6 @@ int mlx5e_tc_esw_init(struct rhashtable *tc_ht) return 0; err_register_fib_notifier: - rhashtable_destroy(tc_ht); -err_ht_init: mapping_destroy(uplink_priv->tunnel_enc_opts_mapping); err_enc_opts_mapping: mapping_destroy(uplink_priv->tunnel_mapping); @@ -4464,13 +4875,8 @@ int mlx5e_tc_esw_init(struct rhashtable *tc_ht) return err; } -void mlx5e_tc_esw_cleanup(struct rhashtable *tc_ht) +void mlx5e_tc_esw_cleanup(struct mlx5_rep_uplink_priv *uplink_priv) { - struct mlx5_rep_uplink_priv *uplink_priv; - - uplink_priv = container_of(tc_ht, struct mlx5_rep_uplink_priv, tc_ht); - - rhashtable_free_and_destroy(tc_ht, _mlx5e_tc_del_flow, NULL); mlx5e_tc_tun_cleanup(uplink_priv->encap); mapping_destroy(uplink_priv->tunnel_enc_opts_mapping); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index 5ffae9b13066..a80b00946f1b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -53,7 +53,6 @@ ESW_FLOW_ATTR_SZ :\ NIC_FLOW_ATTR_SZ) - int mlx5e_tc_num_filters(struct mlx5e_priv *priv, unsigned long flags); struct mlx5e_tc_update_priv { @@ -71,7 +70,7 @@ struct mlx5_flow_attr { struct mlx5_fc *counter; struct mlx5_modify_hdr *modify_hdr; struct mlx5_ct_attr ct_attr; - struct mlx5e_sample_attr *sample_attr; + struct mlx5e_sample_attr sample_attr; struct mlx5e_tc_flow_parse_attr *parse_attr; u32 chain; u16 prio; @@ -82,13 +81,33 @@ struct mlx5_flow_attr { u8 outer_match_level; u8 ip_version; u8 tun_ip_version; + int tunnel_id; /* mapped tunnel id */ u32 flags; + struct list_head list; + struct mlx5e_post_act_handle *post_act_handle; union { struct mlx5_esw_flow_attr esw_attr[0]; struct mlx5_nic_flow_attr nic_attr[0]; }; }; +enum { + MLX5_ATTR_FLAG_VLAN_HANDLED = BIT(0), + MLX5_ATTR_FLAG_SLOW_PATH = BIT(1), + MLX5_ATTR_FLAG_NO_IN_PORT = BIT(2), + MLX5_ATTR_FLAG_SRC_REWRITE = BIT(3), + MLX5_ATTR_FLAG_SAMPLE = BIT(4), + MLX5_ATTR_FLAG_ACCEPT = BIT(5), + MLX5_ATTR_FLAG_CT = BIT(6), +}; + +/* Returns true if any of the flags that require skipping further TC/NF processing are set. */ +static inline bool +mlx5e_tc_attr_flags_skip(u32 attr_flags) +{ + return attr_flags & (MLX5_ATTR_FLAG_SLOW_PATH | MLX5_ATTR_FLAG_ACCEPT); +} + struct mlx5_rx_tun_attr { u16 decap_vport; union { @@ -149,8 +168,11 @@ enum { #define MLX5_TC_FLAG(flag) BIT(MLX5E_TC_FLAG_##flag##_BIT) -int mlx5e_tc_esw_init(struct rhashtable *tc_ht); -void mlx5e_tc_esw_cleanup(struct rhashtable *tc_ht); +int mlx5e_tc_esw_init(struct mlx5_rep_uplink_priv *uplink_priv); +void mlx5e_tc_esw_cleanup(struct mlx5_rep_uplink_priv *uplink_priv); + +int mlx5e_tc_ht_init(struct rhashtable *tc_ht); +void mlx5e_tc_ht_cleanup(struct rhashtable *tc_ht); int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv, struct flow_cls_offload *f, unsigned long flags); @@ -243,11 +265,8 @@ int mlx5e_tc_match_to_reg_set_and_get_id(struct mlx5_core_dev *mdev, u32 data); int mlx5e_tc_add_flow_mod_hdr(struct mlx5e_priv *priv, - struct mlx5e_tc_flow_parse_attr *parse_attr, - struct mlx5e_tc_flow *flow); - -struct mlx5e_tc_flow; -u32 mlx5e_tc_get_flow_tun_id(struct mlx5e_tc_flow *flow); + struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr); void mlx5e_tc_set_ethertype(struct mlx5_core_dev *mdev, struct flow_match_basic *match, bool outer, @@ -289,6 +308,8 @@ int mlx5e_set_fwd_to_int_port_actions(struct mlx5e_priv *priv, #else /* CONFIG_MLX5_CLS_ACT */ static inline int mlx5e_tc_nic_init(struct mlx5e_priv *priv) { return 0; } static inline void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv) {} +static inline int mlx5e_tc_ht_init(struct rhashtable *tc_ht) { return 0; } +static inline void mlx5e_tc_ht_cleanup(struct rhashtable *tc_ht) {} static inline int mlx5e_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) { return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index ee7ecb88adc1..2dc48406cd08 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -53,117 +53,6 @@ static void mlx5e_dma_unmap_wqe_err(struct mlx5e_txqsq *sq, u8 num_dma) } } -#ifdef CONFIG_MLX5_CORE_EN_DCB -static inline int mlx5e_get_dscp_up(struct mlx5e_priv *priv, struct sk_buff *skb) -{ - int dscp_cp = 0; - - if (skb->protocol == htons(ETH_P_IP)) - dscp_cp = ipv4_get_dsfield(ip_hdr(skb)) >> 2; - else if (skb->protocol == htons(ETH_P_IPV6)) - dscp_cp = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2; - - return priv->dcbx_dp.dscp2prio[dscp_cp]; -} -#endif - -static u16 mlx5e_select_ptpsq(struct net_device *dev, struct sk_buff *skb) -{ - struct mlx5e_priv *priv = netdev_priv(dev); - int up = 0; - - if (!netdev_get_num_tc(dev)) - goto return_txq; - -#ifdef CONFIG_MLX5_CORE_EN_DCB - if (priv->dcbx_dp.trust_state == MLX5_QPTS_TRUST_DSCP) - up = mlx5e_get_dscp_up(priv, skb); - else -#endif - if (skb_vlan_tag_present(skb)) - up = skb_vlan_tag_get_prio(skb); - -return_txq: - return priv->port_ptp_tc2realtxq[up]; -} - -static int mlx5e_select_htb_queue(struct mlx5e_priv *priv, struct sk_buff *skb, - u16 htb_maj_id) -{ - u16 classid; - - if ((TC_H_MAJ(skb->priority) >> 16) == htb_maj_id) - classid = TC_H_MIN(skb->priority); - else - classid = READ_ONCE(priv->htb.defcls); - - if (!classid) - return 0; - - return mlx5e_get_txq_by_classid(priv, classid); -} - -u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb, - struct net_device *sb_dev) -{ - struct mlx5e_priv *priv = netdev_priv(dev); - int num_tc_x_num_ch; - int txq_ix; - int up = 0; - int ch_ix; - - /* Sync with mlx5e_update_num_tc_x_num_ch - avoid refetching. */ - num_tc_x_num_ch = READ_ONCE(priv->num_tc_x_num_ch); - if (unlikely(dev->real_num_tx_queues > num_tc_x_num_ch)) { - struct mlx5e_ptp *ptp_channel; - - /* Order maj_id before defcls - pairs with mlx5e_htb_root_add. */ - u16 htb_maj_id = smp_load_acquire(&priv->htb.maj_id); - - if (unlikely(htb_maj_id)) { - txq_ix = mlx5e_select_htb_queue(priv, skb, htb_maj_id); - if (txq_ix > 0) - return txq_ix; - } - - ptp_channel = READ_ONCE(priv->channels.ptp); - if (unlikely(ptp_channel && - test_bit(MLX5E_PTP_STATE_TX, ptp_channel->state) && - mlx5e_use_ptpsq(skb))) - return mlx5e_select_ptpsq(dev, skb); - - txq_ix = netdev_pick_tx(dev, skb, NULL); - /* Fix netdev_pick_tx() not to choose ptp_channel and HTB txqs. - * If they are selected, switch to regular queues. - * Driver to select these queues only at mlx5e_select_ptpsq() - * and mlx5e_select_htb_queue(). - */ - if (unlikely(txq_ix >= num_tc_x_num_ch)) - txq_ix %= num_tc_x_num_ch; - } else { - txq_ix = netdev_pick_tx(dev, skb, NULL); - } - - if (!netdev_get_num_tc(dev)) - return txq_ix; - -#ifdef CONFIG_MLX5_CORE_EN_DCB - if (priv->dcbx_dp.trust_state == MLX5_QPTS_TRUST_DSCP) - up = mlx5e_get_dscp_up(priv, skb); - else -#endif - if (skb_vlan_tag_present(skb)) - up = skb_vlan_tag_get_prio(skb); - - /* Normalize any picked txq_ix to [0, num_channels), - * So we can return a txq_ix that matches the channel and - * packet UP. - */ - ch_ix = priv->txq2sq[txq_ix]->ch_ix; - - return priv->channel_tc2realtxq[ch_ix][up]; -} - static inline int mlx5e_skb_l2_header_offset(struct sk_buff *skb) { #define MLX5E_MIN_INLINE (ETH_HLEN + VLAN_HLEN) @@ -544,7 +433,7 @@ static void mlx5e_tx_mpwqe_session_start(struct mlx5e_txqsq *sq, struct mlx5e_tx_wqe *wqe; u16 pi; - pi = mlx5e_txqsq_get_next_pi(sq, MLX5E_TX_MPW_MAX_WQEBBS); + pi = mlx5e_txqsq_get_next_pi(sq, sq->max_sq_mpw_wqebbs); wqe = MLX5E_TX_FETCH_WQE(sq, pi); net_prefetchw(wqe->data); @@ -645,7 +534,7 @@ mlx5e_sq_xmit_mpwqe(struct mlx5e_txqsq *sq, struct sk_buff *skb, mlx5e_tx_skb_update_hwts_flags(skb); - if (unlikely(mlx5e_tx_mpwqe_is_full(&sq->mpwqe))) { + if (unlikely(mlx5e_tx_mpwqe_is_full(&sq->mpwqe, sq->max_sq_mpw_wqebbs))) { /* Might stop the queue and affect the retval of __netdev_tx_sent_queue. */ cseg = mlx5e_tx_mpwqe_session_complete(sq); @@ -691,8 +580,21 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev) struct mlx5e_txqsq *sq; u16 pi; + /* All changes to txq2sq are performed in sync with mlx5e_xmit, when the + * queue being changed is disabled, and smp_wmb guarantees that the + * changes are visible before mlx5e_xmit tries to read from txq2sq. It + * guarantees that the value of txq2sq[qid] doesn't change while + * mlx5e_xmit is running on queue number qid. smb_wmb is paired with + * HARD_TX_LOCK around ndo_start_xmit, which serves as an ACQUIRE. + */ sq = priv->txq2sq[skb_get_queue_mapping(skb)]; if (unlikely(!sq)) { + /* Two cases when sq can be NULL: + * 1. The HTB node is registered, and mlx5e_select_queue + * selected its queue ID, but the SQ itself is not yet created. + * 2. HTB SQ creation failed. Similar to the previous case, but + * the SQ won't be created. + */ dev_kfree_skb_any(skb); return NETDEV_TX_OK; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index 48a45aa54a3c..229728c80233 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -439,7 +438,8 @@ int mlx5_eq_table_init(struct mlx5_core_dev *dev) struct mlx5_eq_table *eq_table; int i; - eq_table = kvzalloc(sizeof(*eq_table), GFP_KERNEL); + eq_table = kvzalloc_node(sizeof(*eq_table), GFP_KERNEL, + dev->priv.numa_node); if (!eq_table) return -ENOMEM; @@ -728,7 +728,8 @@ struct mlx5_eq * mlx5_eq_create_generic(struct mlx5_core_dev *dev, struct mlx5_eq_param *param) { - struct mlx5_eq *eq = kvzalloc(sizeof(*eq), GFP_KERNEL); + struct mlx5_eq *eq = kvzalloc_node(sizeof(*eq), GFP_KERNEL, + dev->priv.numa_node); int err; if (!eq) @@ -888,10 +889,11 @@ static int create_comp_eqs(struct mlx5_core_dev *dev) return ncomp_eqs; INIT_LIST_HEAD(&table->comp_eqs_list); nent = comp_eq_depth_devlink_param_get(dev); + for (i = 0; i < ncomp_eqs; i++) { struct mlx5_eq_param param = {}; - eq = kzalloc(sizeof(*eq), GFP_KERNEL); + eq = kzalloc_node(sizeof(*eq), GFP_KERNEL, dev->priv.numa_node); if (!eq) { err = -ENOMEM; goto clean; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_ofld.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_ofld.c index 39e948bc1204..a994e71e05c1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_ofld.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_ofld.c @@ -92,6 +92,7 @@ static int esw_acl_ingress_mod_metadata_create(struct mlx5_eswitch *esw, flow_act.action = MLX5_FLOW_CONTEXT_ACTION_MOD_HDR | MLX5_FLOW_CONTEXT_ACTION_ALLOW; flow_act.modify_hdr = vport->ingress.offloads.modify_metadata; + flow_act.fg = vport->ingress.offloads.metadata_allmatch_grp; vport->ingress.offloads.modify_metadata_rule = mlx5_add_flow_rules(vport->ingress.acl, NULL, &flow_act, NULL, 0); @@ -117,6 +118,36 @@ static void esw_acl_ingress_mod_metadata_destroy(struct mlx5_eswitch *esw, vport->ingress.offloads.modify_metadata_rule = NULL; } +static int esw_acl_ingress_src_port_drop_create(struct mlx5_eswitch *esw, + struct mlx5_vport *vport) +{ + struct mlx5_flow_act flow_act = {}; + struct mlx5_flow_handle *flow_rule; + int err = 0; + + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP; + flow_act.fg = vport->ingress.offloads.drop_grp; + flow_rule = mlx5_add_flow_rules(vport->ingress.acl, NULL, &flow_act, NULL, 0); + if (IS_ERR(flow_rule)) { + err = PTR_ERR(flow_rule); + goto out; + } + + vport->ingress.offloads.drop_rule = flow_rule; +out: + return err; +} + +static void esw_acl_ingress_src_port_drop_destroy(struct mlx5_eswitch *esw, + struct mlx5_vport *vport) +{ + if (!vport->ingress.offloads.drop_rule) + return; + + mlx5_del_flow_rules(vport->ingress.offloads.drop_rule); + vport->ingress.offloads.drop_rule = NULL; +} + static int esw_acl_ingress_ofld_rules_create(struct mlx5_eswitch *esw, struct mlx5_vport *vport) { @@ -154,6 +185,7 @@ static void esw_acl_ingress_ofld_rules_destroy(struct mlx5_eswitch *esw, { esw_acl_ingress_allow_rule_destroy(vport); esw_acl_ingress_mod_metadata_destroy(esw, vport); + esw_acl_ingress_src_port_drop_destroy(esw, vport); } static int esw_acl_ingress_ofld_groups_create(struct mlx5_eswitch *esw, @@ -170,10 +202,29 @@ static int esw_acl_ingress_ofld_groups_create(struct mlx5_eswitch *esw, if (!flow_group_in) return -ENOMEM; + if (vport->vport == MLX5_VPORT_UPLINK) { + /* This group can hold an FTE to drop all traffic. + * Need in case LAG is enabled. + */ + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, flow_index); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, flow_index); + + g = mlx5_create_flow_group(vport->ingress.acl, flow_group_in); + if (IS_ERR(g)) { + ret = PTR_ERR(g); + esw_warn(esw->dev, "vport[%d] ingress create drop flow group, err(%d)\n", + vport->vport, ret); + goto drop_err; + } + vport->ingress.offloads.drop_grp = g; + flow_index++; + } + if (esw_acl_ingress_prio_tag_enabled(esw, vport)) { /* This group is to hold FTE to match untagged packets when prio_tag * is enabled. */ + memset(flow_group_in, 0, inlen); match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria); MLX5_SET(create_flow_group_in, flow_group_in, @@ -221,6 +272,11 @@ static int esw_acl_ingress_ofld_groups_create(struct mlx5_eswitch *esw, vport->ingress.offloads.metadata_prio_tag_grp = NULL; } prio_tag_err: + if (!IS_ERR_OR_NULL(vport->ingress.offloads.drop_grp)) { + mlx5_destroy_flow_group(vport->ingress.offloads.drop_grp); + vport->ingress.offloads.drop_grp = NULL; + } +drop_err: kvfree(flow_group_in); return ret; } @@ -236,6 +292,11 @@ static void esw_acl_ingress_ofld_groups_destroy(struct mlx5_vport *vport) mlx5_destroy_flow_group(vport->ingress.offloads.metadata_prio_tag_grp); vport->ingress.offloads.metadata_prio_tag_grp = NULL; } + + if (vport->ingress.offloads.drop_grp) { + mlx5_destroy_flow_group(vport->ingress.offloads.drop_grp); + vport->ingress.offloads.drop_grp = NULL; + } } int esw_acl_ingress_ofld_setup(struct mlx5_eswitch *esw, @@ -252,6 +313,8 @@ int esw_acl_ingress_ofld_setup(struct mlx5_eswitch *esw, if (mlx5_eswitch_vport_match_metadata_enabled(esw)) num_ftes++; + if (vport->vport == MLX5_VPORT_UPLINK) + num_ftes++; if (esw_acl_ingress_prio_tag_enabled(esw, vport)) num_ftes++; @@ -320,3 +383,27 @@ int mlx5_esw_acl_ingress_vport_bond_update(struct mlx5_eswitch *esw, u16 vport_n vport->metadata = vport->default_metadata; return err; } + +int mlx5_esw_acl_ingress_vport_drop_rule_create(struct mlx5_eswitch *esw, u16 vport_num) +{ + struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num); + + if (IS_ERR(vport)) { + esw_warn(esw->dev, "vport(%d) invalid!\n", vport_num); + return PTR_ERR(vport); + } + + return esw_acl_ingress_src_port_drop_create(esw, vport); +} + +void mlx5_esw_acl_ingress_vport_drop_rule_destroy(struct mlx5_eswitch *esw, u16 vport_num) +{ + struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num); + + if (WARN_ON_ONCE(IS_ERR(vport))) { + esw_warn(esw->dev, "vport(%d) invalid!\n", vport_num); + return; + } + + esw_acl_ingress_src_port_drop_destroy(esw, vport); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ofld.h b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ofld.h index c57869b93d60..11d3d3978848 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ofld.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ofld.h @@ -6,6 +6,7 @@ #include "eswitch.h" +#ifdef CONFIG_MLX5_ESWITCH /* Eswitch acl egress external APIs */ int esw_acl_egress_ofld_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport); void esw_acl_egress_ofld_cleanup(struct mlx5_vport *vport); @@ -25,5 +26,19 @@ int esw_acl_ingress_ofld_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vpor void esw_acl_ingress_ofld_cleanup(struct mlx5_eswitch *esw, struct mlx5_vport *vport); int mlx5_esw_acl_ingress_vport_bond_update(struct mlx5_eswitch *esw, u16 vport_num, u32 metadata); +void mlx5_esw_acl_ingress_vport_drop_rule_destroy(struct mlx5_eswitch *esw, u16 vport_num); +int mlx5_esw_acl_ingress_vport_drop_rule_create(struct mlx5_eswitch *esw, u16 vport_num); +#else /* CONFIG_MLX5_ESWITCH */ +static void +mlx5_esw_acl_ingress_vport_drop_rule_destroy(struct mlx5_eswitch *esw, + u16 vport_num) +{} + +static int mlx5_esw_acl_ingress_vport_drop_rule_create(struct mlx5_eswitch *esw, + u16 vport_num) +{ + return 0; +} +#endif /* CONFIG_MLX5_ESWITCH */ #endif /* __MLX5_ESWITCH_ACL_OFLD_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/indir_table.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/indir_table.c index c275fe028b6d..0abef71cb839 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/indir_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/indir_table.c @@ -86,7 +86,7 @@ mlx5_esw_indir_table_needed(struct mlx5_eswitch *esw, mlx5_eswitch_is_vf_vport(esw, vport_num) && esw->dev == dest_mdev && attr->ip_version && - attr->flags & MLX5_ESW_ATTR_FLAG_SRC_REWRITE; + attr->flags & MLX5_ATTR_FLAG_SRC_REWRITE; } u16 diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index ead5e8acc8be..bac5160837c5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -113,8 +113,11 @@ struct vport_ingress { * packet with metadata. */ struct mlx5_flow_group *metadata_allmatch_grp; + /* Optional group to add a drop all rule */ + struct mlx5_flow_group *drop_grp; struct mlx5_modify_hdr *modify_metadata; struct mlx5_flow_handle *modify_metadata_rule; + struct mlx5_flow_handle *drop_rule; } offloads; }; @@ -448,22 +451,6 @@ enum { MLX5_ESW_DEST_CHAIN_WITH_SRC_PORT_CHANGE = BIT(2), }; -enum { - MLX5_ESW_ATTR_FLAG_VLAN_HANDLED = BIT(0), - MLX5_ESW_ATTR_FLAG_SLOW_PATH = BIT(1), - MLX5_ESW_ATTR_FLAG_NO_IN_PORT = BIT(2), - MLX5_ESW_ATTR_FLAG_SRC_REWRITE = BIT(3), - MLX5_ESW_ATTR_FLAG_SAMPLE = BIT(4), - MLX5_ESW_ATTR_FLAG_ACCEPT = BIT(5), -}; - -/* Returns true if any of the flags that require skipping further TC/NF processing are set. */ -static inline bool -mlx5_esw_attr_flags_skip(u32 attr_flags) -{ - return attr_flags & (MLX5_ESW_ATTR_FLAG_SLOW_PATH | MLX5_ESW_ATTR_FLAG_ACCEPT); -} - struct mlx5_esw_flow_attr { struct mlx5_eswitch_rep *in_rep; struct mlx5_core_dev *in_mdev; @@ -487,6 +474,7 @@ struct mlx5_esw_flow_attr { int src_port_rewrite_act_id; } dests[MLX5_MAX_FLOW_FWD_VPORTS]; struct mlx5_rx_tun_attr *rx_tun_attr; + struct ethhdr eth; struct mlx5_pkt_reformat *decap_pkt_reformat; }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index cfcd72bad9af..3f63df127091 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -180,7 +180,7 @@ esw_setup_decap_indir(struct mlx5_eswitch *esw, { struct mlx5_flow_table *ft; - if (!(attr->flags & MLX5_ESW_ATTR_FLAG_SRC_REWRITE)) + if (!(attr->flags & MLX5_ATTR_FLAG_SRC_REWRITE)) return -EOPNOTSUPP; ft = mlx5_esw_indir_table_get(esw, attr, spec, @@ -201,12 +201,12 @@ esw_cleanup_decap_indir(struct mlx5_eswitch *esw, static int esw_setup_sampler_dest(struct mlx5_flow_destination *dest, struct mlx5_flow_act *flow_act, - struct mlx5_flow_attr *attr, + u32 sampler_id, int i) { flow_act->flags |= FLOW_ACT_IGNORE_FLOW_LEVEL; dest[i].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_SAMPLER; - dest[i].sampler_id = attr->sample_attr->sampler_id; + dest[i].sampler_id = sampler_id; return 0; } @@ -297,7 +297,7 @@ esw_setup_chain_src_port_rewrite(struct mlx5_flow_destination *dest, struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr; int err; - if (!(attr->flags & MLX5_ESW_ATTR_FLAG_SRC_REWRITE)) + if (!(attr->flags & MLX5_ATTR_FLAG_SRC_REWRITE)) return -EOPNOTSUPP; /* flow steering cannot handle more than one dest with the same ft @@ -364,7 +364,7 @@ esw_setup_indir_table(struct mlx5_flow_destination *dest, struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr; int j, err; - if (!(attr->flags & MLX5_ESW_ATTR_FLAG_SRC_REWRITE)) + if (!(attr->flags & MLX5_ATTR_FLAG_SRC_REWRITE)) return -EOPNOTSUPP; for (j = esw_attr->split_count; j < esw_attr->out_count; j++, (*i)++) { @@ -463,15 +463,16 @@ esw_setup_dests(struct mlx5_flow_destination *dest, if (!mlx5_eswitch_termtbl_required(esw, attr, flow_act, spec) && esw_src_port_rewrite_supported(esw)) - attr->flags |= MLX5_ESW_ATTR_FLAG_SRC_REWRITE; + attr->flags |= MLX5_ATTR_FLAG_SRC_REWRITE; - if (attr->flags & MLX5_ESW_ATTR_FLAG_SAMPLE) { - esw_setup_sampler_dest(dest, flow_act, attr, *i); + if (attr->flags & MLX5_ATTR_FLAG_SAMPLE && + !(attr->flags & MLX5_ATTR_FLAG_SLOW_PATH)) { + esw_setup_sampler_dest(dest, flow_act, attr->sample_attr.sampler_id, *i); (*i)++; } else if (attr->dest_ft) { esw_setup_ft_dest(dest, flow_act, esw, attr, spec, *i); (*i)++; - } else if (mlx5_esw_attr_flags_skip(attr->flags)) { + } else if (mlx5e_tc_attr_flags_skip(attr->flags)) { esw_setup_slow_path_dest(dest, flow_act, chains, *i); (*i)++; } else if (attr->dest_chain) { @@ -498,7 +499,7 @@ esw_cleanup_dests(struct mlx5_eswitch *esw, if (attr->dest_ft) { esw_cleanup_decap_indir(esw, attr); - } else if (!mlx5_esw_attr_flags_skip(attr->flags)) { + } else if (!mlx5e_tc_attr_flags_skip(attr->flags)) { if (attr->dest_chain) esw_cleanup_chain_dest(chains, attr->dest_chain, 1, 0); else if (esw_is_indir_table(esw, attr)) @@ -589,7 +590,7 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, else fdb = attr->ft; - if (!(attr->flags & MLX5_ESW_ATTR_FLAG_NO_IN_PORT)) + if (!(attr->flags & MLX5_ATTR_FLAG_NO_IN_PORT)) mlx5_eswitch_set_rule_source_port(esw, spec, attr, esw_attr->in_mdev->priv.eswitch, esw_attr->in_rep->vport); @@ -721,7 +722,7 @@ __mlx5_eswitch_del_rule(struct mlx5_eswitch *esw, mlx5_del_flow_rules(rule); - if (!mlx5_esw_attr_flags_skip(attr->flags)) { + if (!mlx5e_tc_attr_flags_skip(attr->flags)) { /* unref the term table */ for (i = 0; i < MLX5_MAX_FLOW_FWD_VPORTS; i++) { if (esw_attr->dests[i].termtbl) @@ -863,7 +864,7 @@ int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw, if (err) goto unlock; - attr->flags &= ~MLX5_ESW_ATTR_FLAG_VLAN_HANDLED; + attr->flags &= ~MLX5_ATTR_FLAG_VLAN_HANDLED; vport = esw_vlan_action_get_vport(esw_attr, push, pop); @@ -871,7 +872,7 @@ int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw, /* tracks VF --> wire rules without vlan push action */ if (esw_attr->dests[0].rep->vport == MLX5_VPORT_UPLINK) { vport->vlan_refcount++; - attr->flags |= MLX5_ESW_ATTR_FLAG_VLAN_HANDLED; + attr->flags |= MLX5_ATTR_FLAG_VLAN_HANDLED; } goto unlock; @@ -902,7 +903,7 @@ int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw, } out: if (!err) - attr->flags |= MLX5_ESW_ATTR_FLAG_VLAN_HANDLED; + attr->flags |= MLX5_ATTR_FLAG_VLAN_HANDLED; unlock: mutex_unlock(&esw->state_lock); return err; @@ -921,7 +922,7 @@ int mlx5_eswitch_del_vlan_action(struct mlx5_eswitch *esw, if (mlx5_eswitch_vlan_actions_supported(esw->dev, 1)) return 0; - if (!(attr->flags & MLX5_ESW_ATTR_FLAG_VLAN_HANDLED)) + if (!(attr->flags & MLX5_ATTR_FLAG_VLAN_HANDLED)) return 0; push = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH); @@ -2378,60 +2379,6 @@ void esw_offloads_unload_rep(struct mlx5_eswitch *esw, u16 vport_num) mlx5_esw_offloads_devlink_port_unregister(esw, vport_num); } -static int esw_set_uplink_slave_ingress_root(struct mlx5_core_dev *master, - struct mlx5_core_dev *slave) -{ - u32 in[MLX5_ST_SZ_DW(set_flow_table_root_in)] = {}; - u32 out[MLX5_ST_SZ_DW(set_flow_table_root_out)] = {}; - struct mlx5_eswitch *esw; - struct mlx5_flow_root_namespace *root; - struct mlx5_flow_namespace *ns; - struct mlx5_vport *vport; - int err; - - MLX5_SET(set_flow_table_root_in, in, opcode, - MLX5_CMD_OP_SET_FLOW_TABLE_ROOT); - MLX5_SET(set_flow_table_root_in, in, table_type, FS_FT_ESW_INGRESS_ACL); - MLX5_SET(set_flow_table_root_in, in, other_vport, 1); - MLX5_SET(set_flow_table_root_in, in, vport_number, MLX5_VPORT_UPLINK); - - if (master) { - esw = master->priv.eswitch; - vport = mlx5_eswitch_get_vport(esw, MLX5_VPORT_UPLINK); - MLX5_SET(set_flow_table_root_in, in, table_of_other_vport, 1); - MLX5_SET(set_flow_table_root_in, in, table_vport_number, - MLX5_VPORT_UPLINK); - - ns = mlx5_get_flow_vport_acl_namespace(master, - MLX5_FLOW_NAMESPACE_ESW_INGRESS, - vport->index); - root = find_root(&ns->node); - mutex_lock(&root->chain_lock); - - MLX5_SET(set_flow_table_root_in, in, - table_eswitch_owner_vhca_id_valid, 1); - MLX5_SET(set_flow_table_root_in, in, - table_eswitch_owner_vhca_id, - MLX5_CAP_GEN(master, vhca_id)); - MLX5_SET(set_flow_table_root_in, in, table_id, - root->root_ft->id); - } else { - esw = slave->priv.eswitch; - vport = mlx5_eswitch_get_vport(esw, MLX5_VPORT_UPLINK); - ns = mlx5_get_flow_vport_acl_namespace(slave, - MLX5_FLOW_NAMESPACE_ESW_INGRESS, - vport->index); - root = find_root(&ns->node); - mutex_lock(&root->chain_lock); - MLX5_SET(set_flow_table_root_in, in, table_id, root->root_ft->id); - } - - err = mlx5_cmd_exec(slave, in, sizeof(in), out, sizeof(out)); - mutex_unlock(&root->chain_lock); - - return err; -} - static int esw_set_slave_root_fdb(struct mlx5_core_dev *master, struct mlx5_core_dev *slave) { @@ -2613,15 +2560,10 @@ int mlx5_eswitch_offloads_config_single_fdb(struct mlx5_eswitch *master_esw, { int err; - err = esw_set_uplink_slave_ingress_root(master_esw->dev, - slave_esw->dev); - if (err) - return -EINVAL; - err = esw_set_slave_root_fdb(master_esw->dev, slave_esw->dev); if (err) - goto err_fdb; + return err; err = esw_set_master_egress_rule(master_esw->dev, slave_esw->dev); @@ -2633,9 +2575,6 @@ int mlx5_eswitch_offloads_config_single_fdb(struct mlx5_eswitch *master_esw, err_acl: esw_set_slave_root_fdb(NULL, slave_esw->dev); -err_fdb: - esw_set_uplink_slave_ingress_root(NULL, slave_esw->dev); - return err; } @@ -2644,7 +2583,6 @@ void mlx5_eswitch_offloads_destroy_single_fdb(struct mlx5_eswitch *master_esw, { esw_unset_master_egress_rule(master_esw->dev); esw_set_slave_root_fdb(NULL, slave_esw->dev); - esw_set_uplink_slave_ingress_root(NULL, slave_esw->dev); } #define ESW_OFFLOADS_DEVCOM_PAIR (0) @@ -2841,6 +2779,19 @@ bool mlx5_esw_vport_match_metadata_supported(const struct mlx5_eswitch *esw) return true; } +#define MLX5_ESW_METADATA_RSVD_UPLINK 1 + +/* Share the same metadata for uplink's. This is fine because: + * (a) In shared FDB mode (LAG) both uplink's are treated the + * same and tagged with the same metadata. + * (b) In non shared FDB mode, packets from physical port0 + * cannot hit eswitch of PF1 and vice versa. + */ +static u32 mlx5_esw_match_metadata_reserved(struct mlx5_eswitch *esw) +{ + return MLX5_ESW_METADATA_RSVD_UPLINK; +} + u32 mlx5_esw_match_metadata_alloc(struct mlx5_eswitch *esw) { u32 vport_end_ida = (1 << ESW_VPORT_BITS) - 1; @@ -2855,8 +2806,10 @@ u32 mlx5_esw_match_metadata_alloc(struct mlx5_eswitch *esw) return 0; /* Metadata is 4 bits of PFNUM and 12 bits of unique id */ - /* Use only non-zero vport_id (1-4095) for all PF's */ - id = ida_alloc_range(&esw->offloads.vport_metadata_ida, 1, vport_end_ida, GFP_KERNEL); + /* Use only non-zero vport_id (2-4095) for all PF's */ + id = ida_alloc_range(&esw->offloads.vport_metadata_ida, + MLX5_ESW_METADATA_RSVD_UPLINK + 1, + vport_end_ida, GFP_KERNEL); if (id < 0) return 0; id = (pf_num << ESW_VPORT_BITS) | id; @@ -2874,7 +2827,11 @@ void mlx5_esw_match_metadata_free(struct mlx5_eswitch *esw, u32 metadata) static int esw_offloads_vport_metadata_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport) { - vport->default_metadata = mlx5_esw_match_metadata_alloc(esw); + if (vport->vport == MLX5_VPORT_UPLINK) + vport->default_metadata = mlx5_esw_match_metadata_reserved(esw); + else + vport->default_metadata = mlx5_esw_match_metadata_alloc(esw); + vport->metadata = vport->default_metadata; return vport->metadata ? 0 : -ENOSPC; } @@ -2885,6 +2842,9 @@ static void esw_offloads_vport_metadata_cleanup(struct mlx5_eswitch *esw, if (!vport->default_metadata) return; + if (vport->vport == MLX5_VPORT_UPLINK) + return; + WARN_ON(vport->metadata != vport->default_metadata); mlx5_esw_match_metadata_free(esw, vport->default_metadata); } @@ -3377,6 +3337,27 @@ static int eswitch_devlink_esw_mode_check(const struct mlx5_eswitch *esw) !mlx5_core_is_ecpf_esw_manager(esw->dev)) ? -EOPNOTSUPP : 0; } +/* FIXME: devl_unlock() followed by devl_lock() inside driver callback + * is never correct and prone to races. It's a transitional workaround, + * never repeat this pattern. + * + * This code MUST be fixed before removing devlink_mutex as it is safe + * to do only because of that mutex. + */ +static void mlx5_eswtich_mode_callback_enter(struct devlink *devlink, + struct mlx5_eswitch *esw) +{ + devl_unlock(devlink); + down_write(&esw->mode_lock); +} + +static void mlx5_eswtich_mode_callback_exit(struct devlink *devlink, + struct mlx5_eswitch *esw) +{ + up_write(&esw->mode_lock); + devl_lock(devlink); +} + int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, struct netlink_ext_ack *extack) { @@ -3391,6 +3372,15 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, if (esw_mode_from_devlink(mode, &mlx5_mode)) return -EINVAL; + /* FIXME: devl_unlock() followed by devl_lock() inside driver callback + * is never correct and prone to races. It's a transitional workaround, + * never repeat this pattern. + * + * This code MUST be fixed before removing devlink_mutex as it is safe + * to do only because of that mutex. + */ + devl_unlock(devlink); + mlx5_lag_disable_change(esw->dev); err = mlx5_esw_try_lock(esw); if (err < 0) { @@ -3421,6 +3411,7 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, mlx5_esw_unlock(esw); enable_lag: mlx5_lag_enable_change(esw->dev); + devl_lock(devlink); return err; } @@ -3433,14 +3424,14 @@ int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode) if (IS_ERR(esw)) return PTR_ERR(esw); - down_write(&esw->mode_lock); + mlx5_eswtich_mode_callback_enter(devlink, esw); err = eswitch_devlink_esw_mode_check(esw); if (err) goto unlock; err = esw_mode_to_devlink(esw->mode, mode); unlock: - up_write(&esw->mode_lock); + mlx5_eswtich_mode_callback_exit(devlink, esw); return err; } @@ -3487,7 +3478,7 @@ int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode, if (IS_ERR(esw)) return PTR_ERR(esw); - down_write(&esw->mode_lock); + mlx5_eswtich_mode_callback_enter(devlink, esw); err = eswitch_devlink_esw_mode_check(esw); if (err) goto out; @@ -3524,11 +3515,11 @@ int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode, goto out; esw->offloads.inline_mode = mlx5_mode; - up_write(&esw->mode_lock); + mlx5_eswtich_mode_callback_exit(devlink, esw); return 0; out: - up_write(&esw->mode_lock); + mlx5_eswtich_mode_callback_exit(devlink, esw); return err; } @@ -3541,14 +3532,14 @@ int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode) if (IS_ERR(esw)) return PTR_ERR(esw); - down_write(&esw->mode_lock); + mlx5_eswtich_mode_callback_enter(devlink, esw); err = eswitch_devlink_esw_mode_check(esw); if (err) goto unlock; err = esw_inline_mode_to_devlink(esw->offloads.inline_mode, mode); unlock: - up_write(&esw->mode_lock); + mlx5_eswtich_mode_callback_exit(devlink, esw); return err; } @@ -3564,7 +3555,7 @@ int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, if (IS_ERR(esw)) return PTR_ERR(esw); - down_write(&esw->mode_lock); + mlx5_eswtich_mode_callback_enter(devlink, esw); err = eswitch_devlink_esw_mode_check(esw); if (err) goto unlock; @@ -3610,7 +3601,7 @@ int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, } unlock: - up_write(&esw->mode_lock); + mlx5_eswtich_mode_callback_exit(devlink, esw); return err; } @@ -3624,15 +3615,14 @@ int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink, if (IS_ERR(esw)) return PTR_ERR(esw); - - down_write(&esw->mode_lock); + mlx5_eswtich_mode_callback_enter(devlink, esw); err = eswitch_devlink_esw_mode_check(esw); if (err) goto unlock; *encap = esw->offloads.encap; unlock: - up_write(&esw->mode_lock); + mlx5_eswtich_mode_callback_exit(devlink, esw); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c index 182306bbefaa..ee568bf34ae2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c @@ -219,12 +219,14 @@ mlx5_eswitch_termtbl_required(struct mlx5_eswitch *esw, if (!MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, termination_table) || !MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, ignore_flow_level) || - mlx5_esw_attr_flags_skip(attr->flags) || + mlx5e_tc_attr_flags_skip(attr->flags) || (!mlx5_eswitch_offload_is_uplink_port(esw, spec) && !esw_attr->int_port)) return false; /* push vlan on RX */ - if (flow_act->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH) + if (flow_act->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH && + !(mlx5_fs_get_capabilities(esw->dev, MLX5_FLOW_NAMESPACE_FDB) & + MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX)) return true; /* hairpin */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c index 2ce4241459ce..39c03dcbd196 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c @@ -30,7 +30,6 @@ * SOFTWARE. */ -#include #include #include diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index dafe341358c7..a0ac17c3f12f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -152,6 +152,12 @@ static int mlx5_cmd_stub_destroy_ns(struct mlx5_flow_root_namespace *ns) return 0; } +static u32 mlx5_cmd_stub_get_capabilities(struct mlx5_flow_root_namespace *ns, + enum fs_flow_table_type ft_type) +{ + return 0; +} + static int mlx5_cmd_set_slave_root_fdb(struct mlx5_core_dev *master, struct mlx5_core_dev *slave, bool ft_id_valid, @@ -971,6 +977,12 @@ static int mlx5_cmd_create_match_definer(struct mlx5_flow_root_namespace *ns, return err ? err : MLX5_GET(general_obj_out_cmd_hdr, out, obj_id); } +static u32 mlx5_cmd_get_capabilities(struct mlx5_flow_root_namespace *ns, + enum fs_flow_table_type ft_type) +{ + return 0; +} + static const struct mlx5_flow_cmds mlx5_flow_cmds = { .create_flow_table = mlx5_cmd_create_flow_table, .destroy_flow_table = mlx5_cmd_destroy_flow_table, @@ -990,6 +1002,7 @@ static const struct mlx5_flow_cmds mlx5_flow_cmds = { .set_peer = mlx5_cmd_stub_set_peer, .create_ns = mlx5_cmd_stub_create_ns, .destroy_ns = mlx5_cmd_stub_destroy_ns, + .get_capabilities = mlx5_cmd_get_capabilities, }; static const struct mlx5_flow_cmds mlx5_flow_cmd_stubs = { @@ -1011,6 +1024,7 @@ static const struct mlx5_flow_cmds mlx5_flow_cmd_stubs = { .set_peer = mlx5_cmd_stub_set_peer, .create_ns = mlx5_cmd_stub_create_ns, .destroy_ns = mlx5_cmd_stub_destroy_ns, + .get_capabilities = mlx5_cmd_stub_get_capabilities, }; const struct mlx5_flow_cmds *mlx5_fs_cmd_get_fw_cmds(void) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index 220ec632d35a..274004e80f03 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -101,6 +101,9 @@ struct mlx5_flow_cmds { u16 format_id, u32 *match_mask); int (*destroy_match_definer)(struct mlx5_flow_root_namespace *ns, int definer_id); + + u32 (*get_capabilities)(struct mlx5_flow_root_namespace *ns, + enum fs_flow_table_type ft_type); }; int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u32 *id); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 537c82b9aa53..816d991f7621 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -1696,6 +1696,7 @@ static void free_match_list(struct match_list *head, bool ft_locked) static int build_match_list(struct match_list *match_head, struct mlx5_flow_table *ft, const struct mlx5_flow_spec *spec, + struct mlx5_flow_group *fg, bool ft_locked) { struct rhlist_head *tmp, *list; @@ -1710,6 +1711,9 @@ static int build_match_list(struct match_list *match_head, rhl_for_each_entry_rcu(g, tmp, list, hash) { struct match_list *curr_match; + if (fg && fg != g) + continue; + if (unlikely(!tree_get_node(&g->node))) continue; @@ -1889,6 +1893,9 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft, if (!check_valid_spec(spec)) return ERR_PTR(-EINVAL); + if (flow_act->fg && ft->autogroup.active) + return ERR_PTR(-EINVAL); + for (i = 0; i < dest_num; i++) { if (!dest_is_valid(&dest[i], flow_act, ft)) return ERR_PTR(-EINVAL); @@ -1898,7 +1905,7 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft, version = atomic_read(&ft->node.version); /* Collect all fgs which has a matching match_criteria */ - err = build_match_list(&match_head, ft, spec, take_write); + err = build_match_list(&match_head, ft, spec, flow_act->fg, take_write); if (err) { if (take_write) up_write_ref_node(&ft->node, false); @@ -3042,6 +3049,22 @@ void mlx5_fs_ingress_acls_cleanup(struct mlx5_core_dev *dev) steering->esw_ingress_root_ns = NULL; } +u32 mlx5_fs_get_capabilities(struct mlx5_core_dev *dev, enum mlx5_flow_namespace_type type) +{ + struct mlx5_flow_root_namespace *root; + struct mlx5_flow_namespace *ns; + + ns = mlx5_get_flow_namespace(dev, type); + if (!ns) + return 0; + + root = find_root(&ns->node); + if (!root) + return 0; + + return root->cmds->get_capabilities(root, root->table_type); +} + static int init_egress_root_ns(struct mlx5_flow_steering *steering) { int err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 5469b08d635f..c488a7c5b07e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -120,6 +120,11 @@ enum mlx5_flow_steering_mode { MLX5_FLOW_STEERING_MODE_SMFS }; +enum mlx5_flow_steering_capabilty { + MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX = 1UL << 0, + MLX5_FLOW_STEERING_CAP_VLAN_POP_ON_TX = 1UL << 1, +}; + struct mlx5_flow_steering { struct mlx5_core_dev *dev; enum mlx5_flow_steering_mode mode; @@ -301,6 +306,8 @@ void mlx5_fs_egress_acls_cleanup(struct mlx5_core_dev *dev); int mlx5_fs_ingress_acls_init(struct mlx5_core_dev *dev, int total_vports); void mlx5_fs_ingress_acls_cleanup(struct mlx5_core_dev *dev); +u32 mlx5_fs_get_capabilities(struct mlx5_core_dev *dev, enum mlx5_flow_namespace_type type); + struct mlx5_flow_root_namespace *find_root(struct fs_node *node); #define fs_get_obj(v, _node) {v = container_of((_node), typeof(*v), node); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index 2d8406fab844..614687e0e3d9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -32,7 +32,6 @@ #include #include -#include #include "mlx5_core.h" #include "../../mlxfw/mlxfw.h" #include "lib/tout.h" diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c index 84dbe46d5ede..4aa22dce9b77 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c @@ -57,7 +57,8 @@ static int mlx5_reg_mfrl_set(struct mlx5_core_dev *dev, u8 reset_level, return mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out), MLX5_REG_MFRL, 0, 1); } -static int mlx5_reg_mfrl_query(struct mlx5_core_dev *dev, u8 *reset_level, u8 *reset_type) +static int mlx5_reg_mfrl_query(struct mlx5_core_dev *dev, u8 *reset_level, + u8 *reset_type, u8 *reset_state) { u32 out[MLX5_ST_SZ_DW(mfrl_reg)] = {}; u32 in[MLX5_ST_SZ_DW(mfrl_reg)] = {}; @@ -71,25 +72,67 @@ static int mlx5_reg_mfrl_query(struct mlx5_core_dev *dev, u8 *reset_level, u8 *r *reset_level = MLX5_GET(mfrl_reg, out, reset_level); if (reset_type) *reset_type = MLX5_GET(mfrl_reg, out, reset_type); + if (reset_state) + *reset_state = MLX5_GET(mfrl_reg, out, reset_state); return 0; } int mlx5_fw_reset_query(struct mlx5_core_dev *dev, u8 *reset_level, u8 *reset_type) { - return mlx5_reg_mfrl_query(dev, reset_level, reset_type); + return mlx5_reg_mfrl_query(dev, reset_level, reset_type, NULL); } -int mlx5_fw_reset_set_reset_sync(struct mlx5_core_dev *dev, u8 reset_type_sel) +static int mlx5_fw_reset_get_reset_state_err(struct mlx5_core_dev *dev, + struct netlink_ext_ack *extack) +{ + u8 reset_state; + + if (mlx5_reg_mfrl_query(dev, NULL, NULL, &reset_state)) + goto out; + + switch (reset_state) { + case MLX5_MFRL_REG_RESET_STATE_IN_NEGOTIATION: + case MLX5_MFRL_REG_RESET_STATE_RESET_IN_PROGRESS: + NL_SET_ERR_MSG_MOD(extack, "Sync reset was already triggered"); + return -EBUSY; + case MLX5_MFRL_REG_RESET_STATE_TIMEOUT: + NL_SET_ERR_MSG_MOD(extack, "Sync reset got timeout"); + return -ETIMEDOUT; + case MLX5_MFRL_REG_RESET_STATE_NACK: + NL_SET_ERR_MSG_MOD(extack, "One of the hosts disabled reset"); + return -EPERM; + } + +out: + NL_SET_ERR_MSG_MOD(extack, "Sync reset failed"); + return -EIO; +} + +int mlx5_fw_reset_set_reset_sync(struct mlx5_core_dev *dev, u8 reset_type_sel, + struct netlink_ext_ack *extack) { struct mlx5_fw_reset *fw_reset = dev->priv.fw_reset; + u32 out[MLX5_ST_SZ_DW(mfrl_reg)] = {}; + u32 in[MLX5_ST_SZ_DW(mfrl_reg)] = {}; int err; set_bit(MLX5_FW_RESET_FLAGS_PENDING_COMP, &fw_reset->reset_flags); - err = mlx5_reg_mfrl_set(dev, MLX5_MFRL_REG_RESET_LEVEL3, reset_type_sel, 0, true); - if (err) - clear_bit(MLX5_FW_RESET_FLAGS_PENDING_COMP, &fw_reset->reset_flags); - return err; + + MLX5_SET(mfrl_reg, in, reset_level, MLX5_MFRL_REG_RESET_LEVEL3); + MLX5_SET(mfrl_reg, in, rst_type_sel, reset_type_sel); + MLX5_SET(mfrl_reg, in, pci_sync_for_fw_update_start, 1); + err = mlx5_access_reg(dev, in, sizeof(in), out, sizeof(out), + MLX5_REG_MFRL, 0, 1, false); + if (!err) + return 0; + + clear_bit(MLX5_FW_RESET_FLAGS_PENDING_COMP, &fw_reset->reset_flags); + if (err == -EREMOTEIO && MLX5_CAP_MCAM_FEATURE(dev, reset_state)) + return mlx5_fw_reset_get_reset_state_err(dev, extack); + + NL_SET_ERR_MSG_MOD(extack, "Sync reset command failed"); + return mlx5_cmd_check(dev, err, in, out); } int mlx5_fw_reset_set_live_patch(struct mlx5_core_dev *dev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h index 7761ee5fc7d0..694fc7cb2684 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h @@ -9,7 +9,8 @@ void mlx5_fw_reset_enable_remote_dev_reset_set(struct mlx5_core_dev *dev, bool enable); bool mlx5_fw_reset_enable_remote_dev_reset_get(struct mlx5_core_dev *dev); int mlx5_fw_reset_query(struct mlx5_core_dev *dev, u8 *reset_level, u8 *reset_type); -int mlx5_fw_reset_set_reset_sync(struct mlx5_core_dev *dev, u8 reset_type_sel); +int mlx5_fw_reset_set_reset_sync(struct mlx5_core_dev *dev, u8 reset_type_sel, + struct netlink_ext_ack *extack); int mlx5_fw_reset_set_live_patch(struct mlx5_core_dev *dev); int mlx5_fw_reset_wait_reset_done(struct mlx5_core_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index 737df402c927..659021c31cbd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -31,7 +31,6 @@ */ #include -#include #include #include #include diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c index 4ddf6b330a44..6cad3b72c133 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c @@ -31,15 +31,22 @@ */ #include +#include #include #include #include #include "lib/devcom.h" #include "mlx5_core.h" #include "eswitch.h" +#include "esw/acl/ofld.h" #include "lag.h" #include "mp.h" +enum { + MLX5_LAG_EGRESS_PORT_1 = 1, + MLX5_LAG_EGRESS_PORT_2, +}; + /* General purpose, use for short periods of time. * Beware of lock dependencies (preferably, no locks should be acquired * under it). @@ -193,15 +200,71 @@ static void mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker, p2en = tracker->netdev_state[MLX5_LAG_P2].tx_enabled && tracker->netdev_state[MLX5_LAG_P2].link_up; - *port1 = 1; - *port2 = 2; + *port1 = MLX5_LAG_EGRESS_PORT_1; + *port2 = MLX5_LAG_EGRESS_PORT_2; if ((!p1en && !p2en) || (p1en && p2en)) return; if (p1en) - *port2 = 1; + *port2 = MLX5_LAG_EGRESS_PORT_1; else - *port1 = 2; + *port1 = MLX5_LAG_EGRESS_PORT_2; +} + +static bool mlx5_lag_has_drop_rule(struct mlx5_lag *ldev) +{ + return ldev->pf[MLX5_LAG_P1].has_drop || ldev->pf[MLX5_LAG_P2].has_drop; +} + +static void mlx5_lag_drop_rule_cleanup(struct mlx5_lag *ldev) +{ + int i; + + for (i = 0; i < MLX5_MAX_PORTS; i++) { + if (!ldev->pf[i].has_drop) + continue; + + mlx5_esw_acl_ingress_vport_drop_rule_destroy(ldev->pf[i].dev->priv.eswitch, + MLX5_VPORT_UPLINK); + ldev->pf[i].has_drop = false; + } +} + +static void mlx5_lag_drop_rule_setup(struct mlx5_lag *ldev, + struct lag_tracker *tracker) +{ + struct mlx5_core_dev *dev0 = ldev->pf[MLX5_LAG_P1].dev; + struct mlx5_core_dev *dev1 = ldev->pf[MLX5_LAG_P2].dev; + struct mlx5_core_dev *inactive; + u8 v2p_port1, v2p_port2; + int inactive_idx; + int err; + + /* First delete the current drop rule so there won't be any dropped + * packets + */ + mlx5_lag_drop_rule_cleanup(ldev); + + if (!ldev->tracker.has_inactive) + return; + + mlx5_infer_tx_affinity_mapping(tracker, &v2p_port1, &v2p_port2); + + if (v2p_port1 == MLX5_LAG_EGRESS_PORT_1) { + inactive = dev1; + inactive_idx = MLX5_LAG_P2; + } else { + inactive = dev0; + inactive_idx = MLX5_LAG_P1; + } + + err = mlx5_esw_acl_ingress_vport_drop_rule_create(inactive->priv.eswitch, + MLX5_VPORT_UPLINK); + if (!err) + ldev->pf[inactive_idx].has_drop = true; + else + mlx5_core_err(inactive, + "Failed to create lag drop rule, error: %d", err); } static int _mlx5_modify_lag(struct mlx5_lag *ldev, u8 v2p_port1, u8 v2p_port2) @@ -238,6 +301,10 @@ void mlx5_modify_lag(struct mlx5_lag *ldev, ldev->v2p_map[MLX5_LAG_P1], ldev->v2p_map[MLX5_LAG_P2]); } + + if (tracker->tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP && + !(ldev->flags & MLX5_LAG_FLAG_ROCE)) + mlx5_lag_drop_rule_setup(ldev, tracker); } static void mlx5_lag_set_port_sel_mode(struct mlx5_lag *ldev, @@ -339,6 +406,10 @@ int mlx5_activate_lag(struct mlx5_lag *ldev, return err; } + if (tracker->tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP && + !roce_lag) + mlx5_lag_drop_rule_setup(ldev, tracker); + ldev->flags |= flags; ldev->shared_fdb = shared_fdb; return 0; @@ -347,6 +418,7 @@ int mlx5_activate_lag(struct mlx5_lag *ldev, static int mlx5_deactivate_lag(struct mlx5_lag *ldev) { struct mlx5_core_dev *dev0 = ldev->pf[MLX5_LAG_P1].dev; + struct mlx5_core_dev *dev1 = ldev->pf[MLX5_LAG_P2].dev; u32 in[MLX5_ST_SZ_DW(destroy_lag_in)] = {}; bool roce_lag = __mlx5_lag_is_roce(ldev); u8 flags = ldev->flags; @@ -356,8 +428,8 @@ static int mlx5_deactivate_lag(struct mlx5_lag *ldev) mlx5_lag_mp_reset(ldev); if (ldev->shared_fdb) { - mlx5_eswitch_offloads_destroy_single_fdb(ldev->pf[MLX5_LAG_P1].dev->priv.eswitch, - ldev->pf[MLX5_LAG_P2].dev->priv.eswitch); + mlx5_eswitch_offloads_destroy_single_fdb(dev0->priv.eswitch, + dev1->priv.eswitch); ldev->shared_fdb = false; } @@ -372,11 +444,15 @@ static int mlx5_deactivate_lag(struct mlx5_lag *ldev) "Failed to deactivate VF LAG; driver restart required\n" "Make sure all VFs are unbound prior to VF LAG activation or deactivation\n"); } - } else if (flags & MLX5_LAG_FLAG_HASH_BASED) { - mlx5_lag_port_sel_destroy(ldev); + return err; } - return err; + if (flags & MLX5_LAG_FLAG_HASH_BASED) + mlx5_lag_port_sel_destroy(ldev); + if (mlx5_lag_has_drop_rule(ldev)) + mlx5_lag_drop_rule_cleanup(ldev); + + return 0; } static bool mlx5_lag_check_prereq(struct mlx5_lag *ldev) @@ -613,6 +689,8 @@ static int mlx5_handle_changeupper_event(struct mlx5_lag *ldev, struct net_device *upper = info->upper_dev, *ndev_tmp; struct netdev_lag_upper_info *lag_upper_info = NULL; bool is_bonded, is_in_lag, mode_supported; + bool has_inactive = 0; + struct slave *slave; int bond_status = 0; int num_slaves = 0; int changed = 0; @@ -632,8 +710,12 @@ static int mlx5_handle_changeupper_event(struct mlx5_lag *ldev, rcu_read_lock(); for_each_netdev_in_bond_rcu(upper, ndev_tmp) { idx = mlx5_lag_dev_get_netdev_idx(ldev, ndev_tmp); - if (idx >= 0) + if (idx >= 0) { + slave = bond_slave_get_rcu(ndev_tmp); + if (slave) + has_inactive |= bond_is_slave_inactive(slave); bond_status |= (1 << idx); + } num_slaves++; } @@ -648,6 +730,7 @@ static int mlx5_handle_changeupper_event(struct mlx5_lag *ldev, tracker->hash_type = lag_upper_info->hash_type; } + tracker->has_inactive = has_inactive; /* Determine bonding status: * A device is considered bonded if both its physical ports are slaves * of the same lag master, and only them. @@ -704,6 +787,38 @@ static int mlx5_handle_changelowerstate_event(struct mlx5_lag *ldev, return 1; } +static int mlx5_handle_changeinfodata_event(struct mlx5_lag *ldev, + struct lag_tracker *tracker, + struct net_device *ndev) +{ + struct net_device *ndev_tmp; + struct slave *slave; + bool has_inactive = 0; + int idx; + + if (!netif_is_lag_master(ndev)) + return 0; + + rcu_read_lock(); + for_each_netdev_in_bond_rcu(ndev, ndev_tmp) { + idx = mlx5_lag_dev_get_netdev_idx(ldev, ndev_tmp); + if (idx < 0) + continue; + + slave = bond_slave_get_rcu(ndev_tmp); + if (slave) + has_inactive |= bond_is_slave_inactive(slave); + } + rcu_read_unlock(); + + if (tracker->has_inactive == has_inactive) + return 0; + + tracker->has_inactive = has_inactive; + + return 1; +} + static int mlx5_lag_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { @@ -712,7 +827,9 @@ static int mlx5_lag_netdev_event(struct notifier_block *this, struct mlx5_lag *ldev; int changed = 0; - if ((event != NETDEV_CHANGEUPPER) && (event != NETDEV_CHANGELOWERSTATE)) + if (event != NETDEV_CHANGEUPPER && + event != NETDEV_CHANGELOWERSTATE && + event != NETDEV_CHANGEINFODATA) return NOTIFY_DONE; ldev = container_of(this, struct mlx5_lag, nb); @@ -728,6 +845,9 @@ static int mlx5_lag_netdev_event(struct notifier_block *this, changed = mlx5_handle_changelowerstate_event(ldev, &tracker, ndev, ptr); break; + case NETDEV_CHANGEINFODATA: + changed = mlx5_handle_changeinfodata_event(ldev, &tracker, ndev); + break; } ldev->tracker = tracker; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h index e5d231c31b54..cbf9a9003e55 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h @@ -28,6 +28,7 @@ enum { struct lag_func { struct mlx5_core_dev *dev; struct net_device *netdev; + bool has_drop; }; /* Used for collection of netdev event info. */ @@ -35,6 +36,7 @@ struct lag_tracker { enum netdev_lag_tx_type tx_type; struct netdev_lag_lower_state_info netdev_state[MLX5_MAX_PORTS]; unsigned int is_bonded:1; + unsigned int has_inactive:1; enum netdev_lag_hash hash_type; }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c index 626aa60b6099..4a6ec15ef046 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c @@ -50,7 +50,7 @@ bool mlx5_lag_is_multipath(struct mlx5_core_dev *dev) static void mlx5_lag_set_port_affinity(struct mlx5_lag *ldev, enum mlx5_lag_port_affinity port) { - struct lag_tracker tracker; + struct lag_tracker tracker = {}; if (!__mlx5_lag_is_multipath(ldev)) return; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h index 4bad6a5fde56..f240ffe5116c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h @@ -92,13 +92,6 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, static inline void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent) { } - -static inline int -mlx5_hv_vhca_write_agent(struct mlx5_hv_vhca_agent *agent, - void *buf, int len) -{ - return 0; -} #endif #endif /* __LIB_HV_VHCA_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c index e042e0924079..4571c56ec3c9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ /* Copyright (c) 2019 Mellanox Technologies. */ -#include #include #include #include "mlx5_core.h" diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/smfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/smfs.c new file mode 100644 index 000000000000..9b8c051ccf65 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/smfs.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. */ + +#include +#include + +#include "smfs.h" + +struct mlx5dr_matcher * +mlx5_smfs_matcher_create(struct mlx5dr_table *table, u32 priority, struct mlx5_flow_spec *spec) +{ + struct mlx5dr_match_parameters matcher_mask = {}; + + matcher_mask.match_buf = (u64 *)&spec->match_criteria; + matcher_mask.match_sz = DR_SZ_MATCH_PARAM; + + return mlx5dr_matcher_create(table, priority, spec->match_criteria_enable, &matcher_mask); +} + +void +mlx5_smfs_matcher_destroy(struct mlx5dr_matcher *matcher) +{ + mlx5dr_matcher_destroy(matcher); +} + +struct mlx5dr_table * +mlx5_smfs_table_get_from_fs_ft(struct mlx5_flow_table *ft) +{ + return mlx5dr_table_get_from_fs_ft(ft); +} + +struct mlx5dr_action * +mlx5_smfs_action_create_dest_table(struct mlx5dr_table *table) +{ + return mlx5dr_action_create_dest_table(table); +} + +struct mlx5dr_action * +mlx5_smfs_action_create_flow_counter(u32 counter_id) +{ + return mlx5dr_action_create_flow_counter(counter_id); +} + +void +mlx5_smfs_action_destroy(struct mlx5dr_action *action) +{ + mlx5dr_action_destroy(action); +} + +struct mlx5dr_rule * +mlx5_smfs_rule_create(struct mlx5dr_matcher *matcher, struct mlx5_flow_spec *spec, + size_t num_actions, struct mlx5dr_action *actions[], + u32 flow_source) +{ + struct mlx5dr_match_parameters value = {}; + + value.match_buf = (u64 *)spec->match_value; + value.match_sz = DR_SZ_MATCH_PARAM; + + return mlx5dr_rule_create(matcher, &value, num_actions, actions, flow_source); +} + +void +mlx5_smfs_rule_destroy(struct mlx5dr_rule *rule) +{ + mlx5dr_rule_destroy(rule); +} + diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/smfs.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/smfs.h new file mode 100644 index 000000000000..452d0df339ac --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/smfs.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. */ + +#ifndef __MLX5_LIB_SMFS_H__ +#define __MLX5_LIB_SMFS_H__ + +#include "steering/mlx5dr.h" +#include "steering/dr_types.h" + +struct mlx5dr_matcher * +mlx5_smfs_matcher_create(struct mlx5dr_table *table, u32 priority, struct mlx5_flow_spec *spec); + +void +mlx5_smfs_matcher_destroy(struct mlx5dr_matcher *matcher); + +struct mlx5dr_table * +mlx5_smfs_table_get_from_fs_ft(struct mlx5_flow_table *ft); + +struct mlx5dr_action * +mlx5_smfs_action_create_dest_table(struct mlx5dr_table *table); + +struct mlx5dr_action * +mlx5_smfs_action_create_flow_counter(u32 counter_id); + +void +mlx5_smfs_action_destroy(struct mlx5dr_action *action); + +struct mlx5dr_rule * +mlx5_smfs_rule_create(struct mlx5dr_matcher *matcher, struct mlx5_flow_spec *spec, + size_t num_actions, struct mlx5dr_action *actions[], + u32 flow_source); + +void +mlx5_smfs_rule_destroy(struct mlx5dr_rule *rule); + +#endif /* __MLX5_LIB_SMFS_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c index e3b0a131c3e1..d55e15c1f380 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c @@ -31,7 +31,6 @@ */ #include -#include #include #include #include diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 976578d1904c..2589e39eb9c7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -736,10 +736,9 @@ static int mlx5_core_set_issi(struct mlx5_core_dev *dev) MLX5_SET(query_issi_in, query_in, opcode, MLX5_CMD_OP_QUERY_ISSI); err = mlx5_cmd_exec_inout(dev, query_issi, query_in, query_out); if (err) { - u32 syndrome; - u8 status; + u32 syndrome = MLX5_GET(query_issi_out, query_out, syndrome); + u8 status = MLX5_GET(query_issi_out, query_out, status); - mlx5_cmd_mbox_status(query_out, &status, &syndrome); if (!status || syndrome == MLX5_DRIVER_SYND) { mlx5_core_err(dev, "Failed to query ISSI err(%d) status(%d) synd(%d)\n", err, status, syndrome); @@ -1488,8 +1487,8 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) INIT_LIST_HEAD(&priv->pgdir_list); priv->numa_node = dev_to_node(mlx5_core_dma_dev(dev)); - priv->dbg_root = debugfs_create_dir(dev_name(dev->device), - mlx5_debugfs_root); + priv->dbg.dbg_root = debugfs_create_dir(dev_name(dev->device), + mlx5_debugfs_root); INIT_LIST_HEAD(&priv->traps); err = mlx5_tout_init(dev); @@ -1525,7 +1524,7 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) err_health_init: mlx5_tout_cleanup(dev); err_timeout_init: - debugfs_remove(dev->priv.dbg_root); + debugfs_remove(dev->priv.dbg.dbg_root); mutex_destroy(&priv->pgdir_mutex); mutex_destroy(&priv->alloc_mutex); mutex_destroy(&priv->bfregs.wc_head.lock); @@ -1543,7 +1542,7 @@ void mlx5_mdev_uninit(struct mlx5_core_dev *dev) mlx5_pagealloc_cleanup(dev); mlx5_health_cleanup(dev); mlx5_tout_cleanup(dev); - debugfs_remove_recursive(dev->priv.dbg_root); + debugfs_remove_recursive(dev->priv.dbg.dbg_root); mutex_destroy(&priv->pgdir_mutex); mutex_destroy(&priv->alloc_mutex); mutex_destroy(&priv->bfregs.wc_head.lock); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mcg.c b/drivers/net/ethernet/mellanox/mlx5/core/mcg.c index e019d68062d8..495cca58dccc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mcg.c @@ -31,7 +31,6 @@ */ #include -#include #include #include #include "mlx5_core.h" diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 37b2805b3bf3..a9b2d6ead542 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -177,7 +177,6 @@ int mlx5_destroy_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy, u32 element_id); int mlx5_wait_for_pages(struct mlx5_core_dev *dev, int *pages); -void mlx5_cmd_trigger_completions(struct mlx5_core_dev *dev); void mlx5_cmd_flush(struct mlx5_core_dev *dev); void mlx5_cq_debugfs_init(struct mlx5_core_dev *dev); void mlx5_cq_debugfs_cleanup(struct mlx5_core_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mr.c b/drivers/net/ethernet/mellanox/mlx5/core/mr.c index f099a087400e..9d735c343a3b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mr.c @@ -31,7 +31,6 @@ */ #include -#include #include #include "mlx5_core.h" diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c index f6b5451328fc..ec76a8b1acc1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -32,7 +32,6 @@ #include #include -#include #include #include #include @@ -327,11 +326,12 @@ static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_id, } static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, - int notify_fail, bool ec_function) + int event, bool ec_function) { u32 function = get_function(func_id, ec_function); u32 out[MLX5_ST_SZ_DW(manage_pages_out)] = {0}; int inlen = MLX5_ST_SZ_BYTES(manage_pages_in); + int notify_fail = event; u64 addr; int err; u32 *in; @@ -351,8 +351,10 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, if (err) { if (err == -ENOMEM) err = alloc_system_page(dev, function); - if (err) + if (err) { + dev->priv.fw_pages_alloc_failed += (npages - i); goto out_4k; + } goto retry; } @@ -365,11 +367,20 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, MLX5_SET(manage_pages_in, in, input_num_entries, npages); MLX5_SET(manage_pages_in, in, embedded_cpu_function, ec_function); - err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out)); + err = mlx5_cmd_do(dev, in, inlen, out, sizeof(out)); + if (err == -EREMOTEIO) { + notify_fail = 0; + /* if triggered by FW and failed by FW ignore */ + if (event) { + err = 0; + goto out_dropped; + } + } if (err) { + err = mlx5_cmd_check(dev, err, in, out); mlx5_core_warn(dev, "func_id 0x%x, npages %d, err %d\n", func_id, npages, err); - goto out_4k; + goto out_dropped; } dev->priv.fw_pages += npages; @@ -384,6 +395,8 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, kvfree(in); return 0; +out_dropped: + dev->priv.give_pages_dropped += npages; out_4k: for (i--; i >= 0; i--) free_4k(dev, MLX5_GET64(manage_pages_in, in, pas[i]), function); @@ -455,7 +468,7 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev, u32 i = 0; if (!mlx5_cmd_is_down(dev)) - return mlx5_cmd_exec(dev, in, in_size, out, out_size); + return mlx5_cmd_do(dev, in, in_size, out, out_size); /* No hard feelings, we want our pages back! */ npages = MLX5_GET(manage_pages_in, in, input_num_entries); @@ -479,7 +492,7 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev, } static int reclaim_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, - int *nclaimed, bool ec_function) + int *nclaimed, bool event, bool ec_function) { u32 function = get_function(func_id, ec_function); int outlen = MLX5_ST_SZ_BYTES(manage_pages_out); @@ -507,6 +520,14 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, func_id, npages, outlen); err = reclaim_pages_cmd(dev, in, sizeof(in), out, outlen); if (err) { + npages = MLX5_GET(manage_pages_in, in, input_num_entries); + dev->priv.reclaim_pages_discard += npages; + } + /* if triggered by FW event and failed by FW then ignore */ + if (event && err == -EREMOTEIO) + err = 0; + if (err) { + err = mlx5_cmd_check(dev, err, in, out); mlx5_core_err(dev, "failed reclaiming pages: err %d\n", err); goto out_free; } @@ -546,7 +567,7 @@ static void pages_work_handler(struct work_struct *work) release_all_pages(dev, req->func_id, req->ec_function); else if (req->npages < 0) err = reclaim_pages(dev, req->func_id, -1 * req->npages, NULL, - req->ec_function); + true, req->ec_function); else if (req->npages > 0) err = give_pages(dev, req->func_id, req->npages, 1, req->ec_function); @@ -645,7 +666,7 @@ static int mlx5_reclaim_root_pages(struct mlx5_core_dev *dev, int err; err = reclaim_pages(dev, func_id, optimal_reclaimed_pages(), - &nclaimed, mlx5_core_is_ecpf(dev)); + &nclaimed, false, mlx5_core_is_ecpf(dev)); if (err) { mlx5_core_warn(dev, "failed reclaiming pages (%d) for func id 0x%x\n", err, func_id); @@ -700,12 +721,14 @@ int mlx5_pagealloc_init(struct mlx5_core_dev *dev) return -ENOMEM; xa_init(&dev->priv.page_root_xa); + mlx5_pages_debugfs_init(dev); return 0; } void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev) { + mlx5_pages_debugfs_cleanup(dev); xa_destroy(&dev->priv.page_root_xa); destroy_workqueue(dev->priv.pg_wq); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c index 41807ef55201..db77f1d2eeb4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c @@ -3,7 +3,6 @@ #include #include -#include #include #include "mlx5_core.h" #include "mlx5_irq.h" @@ -601,7 +600,8 @@ int mlx5_irq_table_init(struct mlx5_core_dev *dev) if (mlx5_core_is_sf(dev)) return 0; - irq_table = kvzalloc(sizeof(*irq_table), GFP_KERNEL); + irq_table = kvzalloc_node(sizeof(*irq_table), GFP_KERNEL, + dev->priv.numa_node); if (!irq_table) return -ENOMEM; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pd.c b/drivers/net/ethernet/mellanox/mlx5/core/pd.c index aabc53ad8bdd..ee5ffdeb9015 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pd.c @@ -31,7 +31,6 @@ */ #include -#include #include #include "mlx5_core.h" diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index 7b16a1188aab..e1bd54574ea5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -33,9 +33,10 @@ #include #include "mlx5_core.h" -int mlx5_core_access_reg(struct mlx5_core_dev *dev, void *data_in, - int size_in, void *data_out, int size_out, - u16 reg_id, int arg, int write) +/* calling with verbose false will not print error to log */ +int mlx5_access_reg(struct mlx5_core_dev *dev, void *data_in, int size_in, + void *data_out, int size_out, u16 reg_id, int arg, + int write, bool verbose) { int outlen = MLX5_ST_SZ_BYTES(access_register_out) + size_out; int inlen = MLX5_ST_SZ_BYTES(access_register_in) + size_in; @@ -57,7 +58,9 @@ int mlx5_core_access_reg(struct mlx5_core_dev *dev, void *data_in, MLX5_SET(access_register_in, in, argument, arg); MLX5_SET(access_register_in, in, register_id, reg_id); - err = mlx5_cmd_exec(dev, in, inlen, out, outlen); + err = mlx5_cmd_do(dev, in, inlen, out, outlen); + if (verbose) + err = mlx5_cmd_check(dev, err, in, out); if (err) goto out; @@ -69,6 +72,15 @@ int mlx5_core_access_reg(struct mlx5_core_dev *dev, void *data_in, kvfree(in); return err; } +EXPORT_SYMBOL_GPL(mlx5_access_reg); + +int mlx5_core_access_reg(struct mlx5_core_dev *dev, void *data_in, + int size_in, void *data_out, int size_out, + u16 reg_id, int arg, int write) +{ + return mlx5_access_reg(dev, data_in, size_in, data_out, size_out, + reg_id, arg, write, true); +} EXPORT_SYMBOL_GPL(mlx5_core_access_reg); int mlx5_query_pcam_reg(struct mlx5_core_dev *dev, u32 *pcam, u8 feature_group, @@ -263,7 +275,6 @@ static int mlx5_query_module_num(struct mlx5_core_dev *dev, int *module_num) { u32 in[MLX5_ST_SZ_DW(pmlp_reg)] = {0}; u32 out[MLX5_ST_SZ_DW(pmlp_reg)]; - int module_mapping; int err; MLX5_SET(pmlp_reg, in, local_port, 1); @@ -272,8 +283,9 @@ static int mlx5_query_module_num(struct mlx5_core_dev *dev, int *module_num) if (err) return err; - module_mapping = MLX5_GET(pmlp_reg, out, lane0_module_mapping); - *module_num = module_mapping & MLX5_EEPROM_IDENTIFIER_BYTE_MASK; + *module_num = MLX5_GET(lane_2_module_mapping, + MLX5_ADDR_OF(pmlp_reg, out, lane0_module_mapping), + module); return 0; } @@ -353,6 +365,12 @@ static void mlx5_sfp_eeprom_params_set(u16 *i2c_addr, int *page_num, u16 *offset *offset -= MLX5_EEPROM_PAGE_LENGTH; } +static int mlx5_mcia_max_bytes(struct mlx5_core_dev *dev) +{ + /* mcia supports either 12 dwords or 32 dwords */ + return (MLX5_CAP_MCAM_FEATURE(dev, mcia_32dwords) ? 32 : 12) * sizeof(u32); +} + static int mlx5_query_mcia(struct mlx5_core_dev *dev, struct mlx5_module_eeprom_query_params *params, u8 *data) { @@ -362,7 +380,7 @@ static int mlx5_query_mcia(struct mlx5_core_dev *dev, void *ptr; u16 size; - size = min_t(int, params->size, MLX5_EEPROM_MAX_BYTES); + size = min_t(int, params->size, mlx5_mcia_max_bytes(dev)); MLX5_SET(mcia_reg, in, l, 0); MLX5_SET(mcia_reg, in, size, size); @@ -433,35 +451,12 @@ int mlx5_query_module_eeprom_by_page(struct mlx5_core_dev *dev, struct mlx5_module_eeprom_query_params *params, u8 *data) { - u8 module_id; int err; err = mlx5_query_module_num(dev, ¶ms->module_number); if (err) return err; - err = mlx5_query_module_id(dev, params->module_number, &module_id); - if (err) - return err; - - switch (module_id) { - case MLX5_MODULE_ID_SFP: - if (params->page > 0) - return -EINVAL; - break; - case MLX5_MODULE_ID_QSFP: - case MLX5_MODULE_ID_QSFP28: - case MLX5_MODULE_ID_QSFP_PLUS: - if (params->page > 3) - return -EINVAL; - break; - case MLX5_MODULE_ID_DSFP: - break; - default: - mlx5_core_err(dev, "Module ID not recognized: 0x%x\n", module_id); - return -EINVAL; - } - if (params->i2c_address != MLX5_I2C_ADDR_HIGH && params->i2c_address != MLX5_I2C_ADDR_LOW) { mlx5_core_err(dev, "I2C address not recognized: 0x%x\n", params->i2c_address); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/rl.c b/drivers/net/ethernet/mellanox/mlx5/core/rl.c index 7161220afe30..9f8b4005f4bd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/rl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/rl.c @@ -31,7 +31,6 @@ */ #include -#include #include #include "mlx5_core.h" diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c index c61a5e83c78c..850937cd8bf9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c @@ -570,6 +570,7 @@ int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher, for (i = 0; i < num_actions; i++) { struct mlx5dr_action_dest_tbl *dest_tbl; + struct mlx5dr_icm_chunk *chunk; struct mlx5dr_action *action; int max_actions_type = 1; u32 action_type; @@ -598,9 +599,9 @@ int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher, matcher->tbl->level, dest_tbl->tbl->level); } - attr.final_icm_addr = rx_rule ? - dest_tbl->tbl->rx.s_anchor->chunk->icm_addr : - dest_tbl->tbl->tx.s_anchor->chunk->icm_addr; + chunk = rx_rule ? dest_tbl->tbl->rx.s_anchor->chunk : + dest_tbl->tbl->tx.s_anchor->chunk; + attr.final_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(chunk); } else { struct mlx5dr_cmd_query_flow_table_details output; int ret; @@ -669,15 +670,9 @@ int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher, case DR_ACTION_TYP_VPORT: attr.hit_gvmi = action->vport->caps->vhca_gvmi; dest_action = action; - if (rx_rule) { - if (action->vport->caps->num == MLX5_VPORT_UPLINK) { - mlx5dr_dbg(dmn, "Device doesn't support Loopback on WIRE vport\n"); - return -EOPNOTSUPP; - } - attr.final_icm_addr = action->vport->caps->icm_address_rx; - } else { - attr.final_icm_addr = action->vport->caps->icm_address_tx; - } + attr.final_icm_addr = rx_rule ? + action->vport->caps->icm_address_rx : + action->vport->caps->icm_address_tx; break; case DR_ACTION_TYP_POP_VLAN: if (!rx_rule && !(dmn->ste_ctx->actions_caps & @@ -1129,7 +1124,8 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn, } action->rewrite->data = (void *)hw_actions; - action->rewrite->index = (action->rewrite->chunk->icm_addr - + action->rewrite->index = (mlx5dr_icm_pool_get_chunk_icm_addr + (action->rewrite->chunk) - dmn->info.caps.hdr_modify_icm_addr) / ACTION_CACHE_LINE_SIZE; @@ -1708,7 +1704,7 @@ static int dr_action_create_modify_action(struct mlx5dr_domain *dmn, action->rewrite->modify_ttl = modify_ttl; action->rewrite->data = (u8 *)hw_actions; action->rewrite->num_of_actions = num_hw_actions; - action->rewrite->index = (chunk->icm_addr - + action->rewrite->index = (mlx5dr_icm_pool_get_chunk_icm_addr(chunk) - dmn->info.caps.hdr_modify_icm_addr) / ACTION_CACHE_LINE_SIZE; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c index 2784cd59fefe..d5998ef59be4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c @@ -3,7 +3,6 @@ #include #include -#include #include #include "dr_types.h" @@ -218,7 +217,8 @@ dr_dump_rule_mem(struct seq_file *file, struct mlx5dr_ste *ste, DR_DUMP_REC_TYPE_RULE_TX_ENTRY_V1; } - dr_dump_hex_print(hw_ste_dump, (char *)ste->hw_ste, DR_STE_SIZE_REDUCED); + dr_dump_hex_print(hw_ste_dump, (char *)mlx5dr_ste_get_hw_ste(ste), + DR_STE_SIZE_REDUCED); seq_printf(file, "%d,0x%llx,0x%llx,%s\n", mem_rec_type, dr_dump_icm_to_idx(mlx5dr_ste_get_icm_addr(ste)), rule_id, @@ -347,16 +347,19 @@ dr_dump_matcher_rx_tx(struct seq_file *file, bool is_rx, const u64 matcher_id) { enum dr_dump_rec_type rec_type; + u64 s_icm_addr, e_icm_addr; int i, ret; rec_type = is_rx ? DR_DUMP_REC_TYPE_MATCHER_RX : DR_DUMP_REC_TYPE_MATCHER_TX; + s_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(matcher_rx_tx->s_htbl->chunk); + e_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(matcher_rx_tx->e_anchor->chunk); seq_printf(file, "%d,0x%llx,0x%llx,%d,0x%llx,0x%llx\n", rec_type, DR_DBG_PTR_TO_ID(matcher_rx_tx), matcher_id, matcher_rx_tx->num_of_builders, - dr_dump_icm_to_idx(matcher_rx_tx->s_htbl->chunk->icm_addr), - dr_dump_icm_to_idx(matcher_rx_tx->e_anchor->chunk->icm_addr)); + dr_dump_icm_to_idx(s_icm_addr), + dr_dump_icm_to_idx(e_icm_addr)); for (i = 0; i < matcher_rx_tx->num_of_builders; i++) { ret = dr_dump_matcher_builder(file, @@ -427,12 +430,14 @@ dr_dump_table_rx_tx(struct seq_file *file, bool is_rx, const u64 table_id) { enum dr_dump_rec_type rec_type; + u64 s_icm_addr; rec_type = is_rx ? DR_DUMP_REC_TYPE_TABLE_RX : DR_DUMP_REC_TYPE_TABLE_TX; + s_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(table_rx_tx->s_anchor->chunk); seq_printf(file, "%d,0x%llx,0x%llx\n", rec_type, table_id, - dr_dump_icm_to_idx(table_rx_tx->s_anchor->chunk->icm_addr)); + dr_dump_icm_to_idx(s_icm_addr)); return 0; } @@ -630,7 +635,7 @@ void mlx5dr_dbg_init_dump(struct mlx5dr_domain *dmn) } dmn->dump_info.steering_debugfs = - debugfs_create_dir("steering", dev->priv.dbg_root); + debugfs_create_dir("steering", mlx5_debugfs_get_dev_root(dev)); dmn->dump_info.fdb_debugfs = debugfs_create_dir("fdb", dmn->dump_info.steering_debugfs); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c index 5fa7f9d6d8b9..fc6ae49b5ecc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c @@ -8,7 +8,7 @@ #define DR_DOMAIN_SW_STEERING_SUPPORTED(dmn, dmn_type) \ ((dmn)->info.caps.dmn_type##_sw_owner || \ ((dmn)->info.caps.dmn_type##_sw_owner_v2 && \ - (dmn)->info.caps.sw_format_ver <= MLX5_STEERING_FORMAT_CONNECTX_6DX)) + (dmn)->info.caps.sw_format_ver <= MLX5_STEERING_FORMAT_CONNECTX_7)) static void dr_domain_init_csum_recalc_fts(struct mlx5dr_domain *dmn) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c index e289cfdbce07..4ca67fa24cc6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c @@ -57,6 +57,36 @@ static int dr_icm_create_dm_mkey(struct mlx5_core_dev *mdev, return mlx5_core_create_mkey(mdev, mkey, in, inlen); } +u64 mlx5dr_icm_pool_get_chunk_mr_addr(struct mlx5dr_icm_chunk *chunk) +{ + u32 offset = mlx5dr_icm_pool_dm_type_to_entry_size(chunk->buddy_mem->pool->icm_type); + + return (u64)offset * chunk->seg; +} + +u32 mlx5dr_icm_pool_get_chunk_rkey(struct mlx5dr_icm_chunk *chunk) +{ + return chunk->buddy_mem->icm_mr->mkey; +} + +u64 mlx5dr_icm_pool_get_chunk_icm_addr(struct mlx5dr_icm_chunk *chunk) +{ + u32 size = mlx5dr_icm_pool_dm_type_to_entry_size(chunk->buddy_mem->pool->icm_type); + + return (u64)chunk->buddy_mem->icm_mr->icm_start_addr + size * chunk->seg; +} + +u32 mlx5dr_icm_pool_get_chunk_byte_size(struct mlx5dr_icm_chunk *chunk) +{ + return mlx5dr_icm_pool_chunk_size_to_byte(chunk->size, + chunk->buddy_mem->pool->icm_type); +} + +u32 mlx5dr_icm_pool_get_chunk_num_of_entries(struct mlx5dr_icm_chunk *chunk) +{ + return mlx5dr_icm_pool_chunk_size_to_entries(chunk->size); +} + static struct mlx5dr_icm_mr * dr_icm_pool_mr_create(struct mlx5dr_icm_pool *pool) { @@ -158,12 +188,13 @@ static void dr_icm_chunk_ste_init(struct mlx5dr_icm_chunk *chunk, int offset) static void dr_icm_chunk_ste_cleanup(struct mlx5dr_icm_chunk *chunk) { + int num_of_entries = mlx5dr_icm_pool_get_chunk_num_of_entries(chunk); struct mlx5dr_icm_buddy_mem *buddy = chunk->buddy_mem; memset(chunk->hw_ste_arr, 0, - chunk->num_of_entries * dr_icm_buddy_get_ste_size(buddy)); + num_of_entries * dr_icm_buddy_get_ste_size(buddy)); memset(chunk->ste_arr, 0, - chunk->num_of_entries * sizeof(chunk->ste_arr[0])); + num_of_entries * sizeof(chunk->ste_arr[0])); } static enum mlx5dr_icm_type @@ -177,7 +208,7 @@ static void dr_icm_chunk_destroy(struct mlx5dr_icm_chunk *chunk, { enum mlx5dr_icm_type icm_type = get_chunk_icm_type(chunk); - buddy->used_memory -= chunk->byte_size; + buddy->used_memory -= mlx5dr_icm_pool_get_chunk_byte_size(chunk); list_del(&chunk->chunk_list); if (icm_type == DR_ICM_TYPE_STE) @@ -298,21 +329,14 @@ dr_icm_chunk_create(struct mlx5dr_icm_pool *pool, offset = mlx5dr_icm_pool_dm_type_to_entry_size(pool->icm_type) * seg; - chunk->rkey = buddy_mem_pool->icm_mr->mkey; - chunk->mr_addr = offset; - chunk->icm_addr = - (uintptr_t)buddy_mem_pool->icm_mr->icm_start_addr + offset; - chunk->num_of_entries = - mlx5dr_icm_pool_chunk_size_to_entries(chunk_size); - chunk->byte_size = - mlx5dr_icm_pool_chunk_size_to_byte(chunk_size, pool->icm_type); chunk->seg = seg; + chunk->size = chunk_size; chunk->buddy_mem = buddy_mem_pool; if (pool->icm_type == DR_ICM_TYPE_STE) dr_icm_chunk_ste_init(chunk, offset); - buddy_mem_pool->used_memory += chunk->byte_size; + buddy_mem_pool->used_memory += mlx5dr_icm_pool_get_chunk_byte_size(chunk); INIT_LIST_HEAD(&chunk->chunk_list); /* chunk now is part of the used_list */ @@ -336,6 +360,7 @@ static bool dr_icm_pool_is_sync_required(struct mlx5dr_icm_pool *pool) static int dr_icm_pool_sync_all_buddy_pools(struct mlx5dr_icm_pool *pool) { struct mlx5dr_icm_buddy_mem *buddy, *tmp_buddy; + u32 num_entries; int err; err = mlx5dr_cmd_sync_steering(pool->dmn->mdev); @@ -348,9 +373,9 @@ static int dr_icm_pool_sync_all_buddy_pools(struct mlx5dr_icm_pool *pool) struct mlx5dr_icm_chunk *chunk, *tmp_chunk; list_for_each_entry_safe(chunk, tmp_chunk, &buddy->hot_list, chunk_list) { - mlx5dr_buddy_free_mem(buddy, chunk->seg, - ilog2(chunk->num_of_entries)); - pool->hot_memory_size -= chunk->byte_size; + num_entries = mlx5dr_icm_pool_get_chunk_num_of_entries(chunk); + mlx5dr_buddy_free_mem(buddy, chunk->seg, ilog2(num_entries)); + pool->hot_memory_size -= mlx5dr_icm_pool_get_chunk_byte_size(chunk); dr_icm_chunk_destroy(chunk, buddy); } @@ -448,7 +473,7 @@ void mlx5dr_icm_free_chunk(struct mlx5dr_icm_chunk *chunk) /* move the memory to the waiting list AKA "hot" */ mutex_lock(&pool->mutex); list_move_tail(&chunk->chunk_list, &buddy->hot_list); - pool->hot_memory_size += chunk->byte_size; + pool->hot_memory_size += mlx5dr_icm_pool_get_chunk_byte_size(chunk); /* Check if we have chunks that are waiting for sync-ste */ if (dr_icm_pool_is_sync_required(pool)) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_matcher.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_matcher.c index 38971fe1dfe1..0726848eb3ff 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_matcher.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_matcher.c @@ -47,6 +47,11 @@ static bool dr_mask_is_ttl_set(struct mlx5dr_match_spec *spec) return spec->ttl_hoplimit; } +static bool dr_mask_is_ipv4_ihl_set(struct mlx5dr_match_spec *spec) +{ + return spec->ipv4_ihl; +} + #define DR_MASK_IS_L2_DST(_spec, _misc, _inner_outer) (_spec.first_vid || \ (_spec).first_cfi || (_spec).first_prio || (_spec).cvlan_tag || \ (_spec).svlan_tag || (_spec).dmac_47_16 || (_spec).dmac_15_0 || \ @@ -103,7 +108,7 @@ dr_mask_is_vxlan_gpe_set(struct mlx5dr_match_misc3 *misc3) static bool dr_matcher_supp_vxlan_gpe(struct mlx5dr_cmd_caps *caps) { - return (caps->sw_format_ver == MLX5_STEERING_FORMAT_CONNECTX_6DX) || + return (caps->sw_format_ver >= MLX5_STEERING_FORMAT_CONNECTX_6DX) || (caps->flex_protocols & MLX5_FLEX_PARSER_VXLAN_GPE_ENABLED); } @@ -144,7 +149,7 @@ static bool dr_mask_is_tnl_geneve_tlv_opt_exist_set(struct mlx5dr_match_misc *mi static bool dr_matcher_supp_tnl_geneve(struct mlx5dr_cmd_caps *caps) { - return (caps->sw_format_ver == MLX5_STEERING_FORMAT_CONNECTX_6DX) || + return (caps->sw_format_ver >= MLX5_STEERING_FORMAT_CONNECTX_6DX) || (caps->flex_protocols & MLX5_FLEX_PARSER_GENEVE_ENABLED); } @@ -261,13 +266,13 @@ static bool dr_mask_is_tnl_gtpu_any(struct mlx5dr_match_param *mask, static int dr_matcher_supp_icmp_v4(struct mlx5dr_cmd_caps *caps) { - return (caps->sw_format_ver == MLX5_STEERING_FORMAT_CONNECTX_6DX) || + return (caps->sw_format_ver >= MLX5_STEERING_FORMAT_CONNECTX_6DX) || (caps->flex_protocols & MLX5_FLEX_PARSER_ICMP_V4_ENABLED); } static int dr_matcher_supp_icmp_v6(struct mlx5dr_cmd_caps *caps) { - return (caps->sw_format_ver == MLX5_STEERING_FORMAT_CONNECTX_6DX) || + return (caps->sw_format_ver >= MLX5_STEERING_FORMAT_CONNECTX_6DX) || (caps->flex_protocols & MLX5_FLEX_PARSER_ICMP_V6_ENABLED); } @@ -507,7 +512,8 @@ static int dr_matcher_set_ste_builders(struct mlx5dr_matcher *matcher, mlx5dr_ste_build_eth_l3_ipv4_5_tuple(ste_ctx, &sb[idx++], &mask, inner, rx); - if (dr_mask_is_ttl_set(&mask.outer)) + if (dr_mask_is_ttl_set(&mask.outer) || + dr_mask_is_ipv4_ihl_set(&mask.outer)) mlx5dr_ste_build_eth_l3_ipv4_misc(ste_ctx, &sb[idx++], &mask, inner, rx); } @@ -614,7 +620,8 @@ static int dr_matcher_set_ste_builders(struct mlx5dr_matcher *matcher, mlx5dr_ste_build_eth_l3_ipv4_5_tuple(ste_ctx, &sb[idx++], &mask, inner, rx); - if (dr_mask_is_ttl_set(&mask.inner)) + if (dr_mask_is_ttl_set(&mask.inner) || + dr_mask_is_ipv4_ihl_set(&mask.inner)) mlx5dr_ste_build_eth_l3_ipv4_misc(ste_ctx, &sb[idx++], &mask, inner, rx); } @@ -698,7 +705,7 @@ static int dr_nic_matcher_connect(struct mlx5dr_domain *dmn, /* Connect start hash table to end anchor */ info.type = CONNECT_MISS; - info.miss_icm_addr = curr_nic_matcher->e_anchor->chunk->icm_addr; + info.miss_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(curr_nic_matcher->e_anchor->chunk); ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn, curr_nic_matcher->s_htbl, &info, false); @@ -719,12 +726,14 @@ static int dr_nic_matcher_connect(struct mlx5dr_domain *dmn, return ret; /* Update the pointing ste and next hash table */ - curr_nic_matcher->s_htbl->pointing_ste = prev_htbl->ste_arr; - prev_htbl->ste_arr[0].next_htbl = curr_nic_matcher->s_htbl; + curr_nic_matcher->s_htbl->pointing_ste = prev_htbl->chunk->ste_arr; + prev_htbl->chunk->ste_arr[0].next_htbl = curr_nic_matcher->s_htbl; if (next_nic_matcher) { - next_nic_matcher->s_htbl->pointing_ste = curr_nic_matcher->e_anchor->ste_arr; - curr_nic_matcher->e_anchor->ste_arr[0].next_htbl = next_nic_matcher->s_htbl; + next_nic_matcher->s_htbl->pointing_ste = + curr_nic_matcher->e_anchor->chunk->ste_arr; + curr_nic_matcher->e_anchor->chunk->ste_arr[0].next_htbl = + next_nic_matcher->s_htbl; } return 0; @@ -1036,12 +1045,12 @@ static int dr_matcher_disconnect_nic(struct mlx5dr_domain *dmn, if (next_nic_matcher) { info.type = CONNECT_HIT; info.hit_next_htbl = next_nic_matcher->s_htbl; - next_nic_matcher->s_htbl->pointing_ste = prev_anchor->ste_arr; - prev_anchor->ste_arr[0].next_htbl = next_nic_matcher->s_htbl; + next_nic_matcher->s_htbl->pointing_ste = prev_anchor->chunk->ste_arr; + prev_anchor->chunk->ste_arr[0].next_htbl = next_nic_matcher->s_htbl; } else { info.type = CONNECT_MISS; info.miss_icm_addr = nic_tbl->default_icm_addr; - prev_anchor->ste_arr[0].next_htbl = NULL; + prev_anchor->chunk->ste_arr[0].next_htbl = NULL; } return mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn, prev_anchor, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c index b4374578425b..ddfaf7891188 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c @@ -21,12 +21,12 @@ static int dr_rule_append_to_miss_list(struct mlx5dr_ste_ctx *ste_ctx, if (!ste_info_last) return -ENOMEM; - mlx5dr_ste_set_miss_addr(ste_ctx, last_ste->hw_ste, + mlx5dr_ste_set_miss_addr(ste_ctx, mlx5dr_ste_get_hw_ste(last_ste), mlx5dr_ste_get_icm_addr(new_last_ste)); list_add_tail(&new_last_ste->miss_list_node, miss_list); mlx5dr_send_fill_and_append_ste_send_info(last_ste, DR_STE_SIZE_CTRL, - 0, last_ste->hw_ste, + 0, mlx5dr_ste_get_hw_ste(last_ste), ste_info_last, send_list, true); return 0; @@ -41,6 +41,7 @@ dr_rule_create_collision_htbl(struct mlx5dr_matcher *matcher, struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx; struct mlx5dr_ste_htbl *new_htbl; struct mlx5dr_ste *ste; + u64 icm_addr; /* Create new table for miss entry */ new_htbl = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool, @@ -53,9 +54,9 @@ dr_rule_create_collision_htbl(struct mlx5dr_matcher *matcher, } /* One and only entry, never grows */ - ste = new_htbl->ste_arr; - mlx5dr_ste_set_miss_addr(ste_ctx, hw_ste, - nic_matcher->e_anchor->chunk->icm_addr); + ste = new_htbl->chunk->ste_arr; + icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(nic_matcher->e_anchor->chunk); + mlx5dr_ste_set_miss_addr(ste_ctx, hw_ste, icm_addr); mlx5dr_htbl_get(new_htbl); return ste; @@ -79,7 +80,7 @@ dr_rule_create_collision_entry(struct mlx5dr_matcher *matcher, ste->htbl->pointing_ste = orig_ste->htbl->pointing_ste; /* In collision entry, all members share the same miss_list_head */ - ste->htbl->miss_list = mlx5dr_ste_get_miss_list(orig_ste); + ste->htbl->chunk->miss_list = mlx5dr_ste_get_miss_list(orig_ste); /* Next table */ if (mlx5dr_ste_create_next_htbl(matcher, nic_matcher, ste, hw_ste, @@ -107,9 +108,11 @@ dr_rule_handle_one_ste_in_update_list(struct mlx5dr_ste_send_info *ste_info, * is already written to the hw. */ if (ste_info->size == DR_STE_SIZE_CTRL) - memcpy(ste_info->ste->hw_ste, ste_info->data, DR_STE_SIZE_CTRL); + memcpy(mlx5dr_ste_get_hw_ste(ste_info->ste), + ste_info->data, DR_STE_SIZE_CTRL); else - memcpy(ste_info->ste->hw_ste, ste_info->data, DR_STE_SIZE_REDUCED); + memcpy(mlx5dr_ste_get_hw_ste(ste_info->ste), + ste_info->data, DR_STE_SIZE_REDUCED); ret = mlx5dr_send_postsend_ste(dmn, ste_info->ste, ste_info->data, ste_info->size, ste_info->offset); @@ -159,7 +162,7 @@ dr_rule_find_ste_in_miss_list(struct list_head *miss_list, u8 *hw_ste) /* Check if hw_ste is present in the list */ list_for_each_entry(ste, miss_list, miss_list_node) { - if (mlx5dr_ste_equal_tag(ste->hw_ste, hw_ste)) + if (mlx5dr_ste_equal_tag(mlx5dr_ste_get_hw_ste(ste), hw_ste)) return ste; } @@ -185,7 +188,7 @@ dr_rule_rehash_handle_collision(struct mlx5dr_matcher *matcher, new_ste->htbl->pointing_ste = col_ste->htbl->pointing_ste; /* In collision entry, all members share the same miss_list_head */ - new_ste->htbl->miss_list = mlx5dr_ste_get_miss_list(col_ste); + new_ste->htbl->chunk->miss_list = mlx5dr_ste_get_miss_list(col_ste); /* Update the previous from the list */ ret = dr_rule_append_to_miss_list(dmn->ste_ctx, new_ste, @@ -235,6 +238,7 @@ dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher, bool use_update_list = false; u8 hw_ste[DR_STE_SIZE] = {}; struct mlx5dr_ste *new_ste; + u64 icm_addr; int new_idx; u8 sb_idx; @@ -243,12 +247,12 @@ dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher, mlx5dr_ste_set_bit_mask(hw_ste, nic_matcher->ste_builder[sb_idx].bit_mask); /* Copy STE control and tag */ - memcpy(hw_ste, cur_ste->hw_ste, DR_STE_SIZE_REDUCED); - mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste, - nic_matcher->e_anchor->chunk->icm_addr); + icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(nic_matcher->e_anchor->chunk); + memcpy(hw_ste, mlx5dr_ste_get_hw_ste(cur_ste), DR_STE_SIZE_REDUCED); + mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste, icm_addr); new_idx = mlx5dr_ste_calc_hash_index(hw_ste, new_htbl); - new_ste = &new_htbl->ste_arr[new_idx]; + new_ste = &new_htbl->chunk->ste_arr[new_idx]; if (mlx5dr_ste_is_not_used(new_ste)) { mlx5dr_htbl_get(new_htbl); @@ -269,7 +273,7 @@ dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher, use_update_list = true; } - memcpy(new_ste->hw_ste, hw_ste, DR_STE_SIZE_REDUCED); + memcpy(mlx5dr_ste_get_hw_ste(new_ste), hw_ste, DR_STE_SIZE_REDUCED); new_htbl->ctrl.num_of_valid_entries++; @@ -334,7 +338,7 @@ static int dr_rule_rehash_copy_htbl(struct mlx5dr_matcher *matcher, int err = 0; int i; - cur_entries = mlx5dr_icm_pool_chunk_size_to_entries(cur_htbl->chunk_size); + cur_entries = mlx5dr_icm_pool_chunk_size_to_entries(cur_htbl->chunk->size); if (cur_entries < 1) { mlx5dr_dbg(matcher->tbl->dmn, "Invalid number of entries\n"); @@ -342,7 +346,7 @@ static int dr_rule_rehash_copy_htbl(struct mlx5dr_matcher *matcher, } for (i = 0; i < cur_entries; i++) { - cur_ste = &cur_htbl->ste_arr[i]; + cur_ste = &cur_htbl->chunk->ste_arr[i]; if (mlx5dr_ste_is_not_used(cur_ste)) /* Empty, nothing to copy */ continue; @@ -398,7 +402,7 @@ dr_rule_rehash_htbl(struct mlx5dr_rule *rule, /* Write new table to HW */ info.type = CONNECT_MISS; - info.miss_icm_addr = nic_matcher->e_anchor->chunk->icm_addr; + info.miss_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(nic_matcher->e_anchor->chunk); mlx5dr_ste_set_formatted_ste(dmn->ste_ctx, dmn->info.caps.gvmi, nic_dmn->type, @@ -446,21 +450,21 @@ dr_rule_rehash_htbl(struct mlx5dr_rule *rule, * (48B len) which works only on first 32B */ mlx5dr_ste_set_hit_addr(dmn->ste_ctx, - prev_htbl->ste_arr[0].hw_ste, - new_htbl->chunk->icm_addr, - new_htbl->chunk->num_of_entries); + prev_htbl->chunk->hw_ste_arr, + mlx5dr_icm_pool_get_chunk_icm_addr(new_htbl->chunk), + mlx5dr_icm_pool_get_chunk_num_of_entries(new_htbl->chunk)); - ste_to_update = &prev_htbl->ste_arr[0]; + ste_to_update = &prev_htbl->chunk->ste_arr[0]; } else { mlx5dr_ste_set_hit_addr_by_next_htbl(dmn->ste_ctx, - cur_htbl->pointing_ste->hw_ste, + mlx5dr_ste_get_hw_ste(cur_htbl->pointing_ste), new_htbl); ste_to_update = cur_htbl->pointing_ste; } mlx5dr_send_fill_and_append_ste_send_info(ste_to_update, DR_STE_SIZE_CTRL, - 0, ste_to_update->hw_ste, ste_info, - update_list, false); + 0, mlx5dr_ste_get_hw_ste(ste_to_update), + ste_info, update_list, false); return new_htbl; @@ -489,10 +493,10 @@ static struct mlx5dr_ste_htbl *dr_rule_rehash(struct mlx5dr_rule *rule, struct mlx5dr_domain *dmn = rule->matcher->tbl->dmn; enum mlx5dr_icm_chunk_size new_size; - new_size = mlx5dr_icm_next_higher_chunk(cur_htbl->chunk_size); + new_size = mlx5dr_icm_next_higher_chunk(cur_htbl->chunk->size); new_size = min_t(u32, new_size, dmn->info.max_log_sw_icm_sz); - if (new_size == cur_htbl->chunk_size) + if (new_size == cur_htbl->chunk->size) return NULL; /* Skip rehash, we already at the max size */ return dr_rule_rehash_htbl(rule, nic_rule, cur_htbl, ste_location, @@ -659,13 +663,13 @@ static bool dr_rule_need_enlarge_hash(struct mlx5dr_ste_htbl *htbl, struct mlx5dr_ste_htbl_ctrl *ctrl = &htbl->ctrl; int threshold; - if (dmn->info.max_log_sw_icm_sz <= htbl->chunk_size) + if (dmn->info.max_log_sw_icm_sz <= htbl->chunk->size) return false; if (!mlx5dr_ste_htbl_may_grow(htbl)) return false; - if (dr_get_bits_per_mask(htbl->byte_mask) * BITS_PER_BYTE <= htbl->chunk_size) + if (dr_get_bits_per_mask(htbl->byte_mask) * BITS_PER_BYTE <= htbl->chunk->size) return false; threshold = mlx5dr_ste_htbl_increase_threshold(htbl); @@ -755,6 +759,7 @@ static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher, { struct mlx5dr_domain *dmn = matcher->tbl->dmn; struct mlx5dr_ste_send_info *ste_info; + u64 icm_addr; /* Take ref on table, only on first time this ste is used */ mlx5dr_htbl_get(cur_htbl); @@ -762,8 +767,8 @@ static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher, /* new entry -> new branch */ list_add_tail(&ste->miss_list_node, miss_list); - mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste, - nic_matcher->e_anchor->chunk->icm_addr); + icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(nic_matcher->e_anchor->chunk); + mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste, icm_addr); ste->ste_chain_location = ste_location; @@ -822,7 +827,7 @@ dr_rule_handle_ste_branch(struct mlx5dr_rule *rule, again: index = mlx5dr_ste_calc_hash_index(hw_ste, cur_htbl); miss_list = &cur_htbl->chunk->miss_list[index]; - ste = &cur_htbl->ste_arr[index]; + ste = &cur_htbl->chunk->ste_arr[index]; if (mlx5dr_ste_is_not_used(ste)) { if (dr_rule_handle_empty_entry(matcher, nic_matcher, cur_htbl, @@ -858,7 +863,7 @@ dr_rule_handle_ste_branch(struct mlx5dr_rule *rule, ste_location, send_ste_list); if (!new_htbl) { mlx5dr_err(dmn, "Failed creating rehash table, htbl-log_size: %d\n", - cur_htbl->chunk_size); + cur_htbl->chunk->size); mlx5dr_htbl_put(cur_htbl); } else { cur_htbl = new_htbl; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c index 00aef47d7682..ef19a66f5233 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c @@ -407,17 +407,17 @@ static int dr_get_tbl_copy_details(struct mlx5dr_domain *dmn, int *iterations, int *num_stes) { + u32 chunk_byte_size = mlx5dr_icm_pool_get_chunk_byte_size(htbl->chunk); int alloc_size; - if (htbl->chunk->byte_size > dmn->send_ring->max_post_send_size) { - *iterations = htbl->chunk->byte_size / - dmn->send_ring->max_post_send_size; + if (chunk_byte_size > dmn->send_ring->max_post_send_size) { + *iterations = chunk_byte_size / dmn->send_ring->max_post_send_size; *byte_size = dmn->send_ring->max_post_send_size; alloc_size = *byte_size; *num_stes = *byte_size / DR_STE_SIZE; } else { *iterations = 1; - *num_stes = htbl->chunk->num_of_entries; + *num_stes = mlx5dr_icm_pool_get_chunk_num_of_entries(htbl->chunk); alloc_size = *num_stes * DR_STE_SIZE; } @@ -453,7 +453,7 @@ int mlx5dr_send_postsend_ste(struct mlx5dr_domain *dmn, struct mlx5dr_ste *ste, send_info.write.length = size; send_info.write.lkey = 0; send_info.remote_addr = mlx5dr_ste_get_mr_addr(ste) + offset; - send_info.rkey = ste->htbl->chunk->rkey; + send_info.rkey = mlx5dr_icm_pool_get_chunk_rkey(ste->htbl->chunk); return dr_postsend_icm_data(dmn, &send_info); } @@ -462,7 +462,7 @@ int mlx5dr_send_postsend_htbl(struct mlx5dr_domain *dmn, struct mlx5dr_ste_htbl *htbl, u8 *formatted_ste, u8 *mask) { - u32 byte_size = htbl->chunk->byte_size; + u32 byte_size = mlx5dr_icm_pool_get_chunk_byte_size(htbl->chunk); int num_stes_per_iter; int iterations; u8 *data; @@ -486,7 +486,7 @@ int mlx5dr_send_postsend_htbl(struct mlx5dr_domain *dmn, * need to add the bit_mask */ for (j = 0; j < num_stes_per_iter; j++) { - struct mlx5dr_ste *ste = &htbl->ste_arr[ste_index + j]; + struct mlx5dr_ste *ste = &htbl->chunk->ste_arr[ste_index + j]; u32 ste_off = j * DR_STE_SIZE; if (mlx5dr_ste_is_not_used(ste)) { @@ -495,7 +495,8 @@ int mlx5dr_send_postsend_htbl(struct mlx5dr_domain *dmn, } else { /* Copy data */ memcpy(data + ste_off, - htbl->ste_arr[ste_index + j].hw_ste, + htbl->chunk->hw_ste_arr + + DR_STE_SIZE_REDUCED * (ste_index + j), DR_STE_SIZE_REDUCED); /* Copy bit_mask */ memcpy(data + ste_off + DR_STE_SIZE_REDUCED, @@ -511,8 +512,8 @@ int mlx5dr_send_postsend_htbl(struct mlx5dr_domain *dmn, send_info.write.length = byte_size; send_info.write.lkey = 0; send_info.remote_addr = - mlx5dr_ste_get_mr_addr(htbl->ste_arr + ste_index); - send_info.rkey = htbl->chunk->rkey; + mlx5dr_ste_get_mr_addr(htbl->chunk->ste_arr + ste_index); + send_info.rkey = mlx5dr_icm_pool_get_chunk_rkey(htbl->chunk); ret = dr_postsend_icm_data(dmn, &send_info); if (ret) @@ -530,7 +531,7 @@ int mlx5dr_send_postsend_formatted_htbl(struct mlx5dr_domain *dmn, u8 *ste_init_data, bool update_hw_ste) { - u32 byte_size = htbl->chunk->byte_size; + u32 byte_size = mlx5dr_icm_pool_get_chunk_byte_size(htbl->chunk); int iterations; int num_stes; u8 *copy_dst; @@ -546,7 +547,7 @@ int mlx5dr_send_postsend_formatted_htbl(struct mlx5dr_domain *dmn, if (update_hw_ste) { /* Copy the reduced STE to hash table ste_arr */ for (i = 0; i < num_stes; i++) { - copy_dst = htbl->hw_ste_arr + i * DR_STE_SIZE_REDUCED; + copy_dst = htbl->chunk->hw_ste_arr + i * DR_STE_SIZE_REDUCED; memcpy(copy_dst, ste_init_data, DR_STE_SIZE_REDUCED); } } @@ -568,8 +569,8 @@ int mlx5dr_send_postsend_formatted_htbl(struct mlx5dr_domain *dmn, send_info.write.length = byte_size; send_info.write.lkey = 0; send_info.remote_addr = - mlx5dr_ste_get_mr_addr(htbl->ste_arr + ste_index); - send_info.rkey = htbl->chunk->rkey; + mlx5dr_ste_get_mr_addr(htbl->chunk->ste_arr + ste_index); + send_info.rkey = mlx5dr_icm_pool_get_chunk_rkey(htbl->chunk); ret = dr_postsend_icm_data(dmn, &send_info); if (ret) @@ -591,8 +592,9 @@ int mlx5dr_send_postsend_action(struct mlx5dr_domain *dmn, send_info.write.length = action->rewrite->num_of_actions * DR_MODIFY_ACTION_SIZE; send_info.write.lkey = 0; - send_info.remote_addr = action->rewrite->chunk->mr_addr; - send_info.rkey = action->rewrite->chunk->rkey; + send_info.remote_addr = + mlx5dr_icm_pool_get_chunk_mr_addr(action->rewrite->chunk); + send_info.rkey = mlx5dr_icm_pool_get_chunk_rkey(action->rewrite->chunk); ret = dr_postsend_icm_data(dmn, &send_info); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c index 187e29b409b6..09ebd3088857 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c @@ -25,6 +25,7 @@ bool mlx5dr_ste_supp_ttl_cs_recalc(struct mlx5dr_cmd_caps *caps) u32 mlx5dr_ste_calc_hash_index(u8 *hw_ste_p, struct mlx5dr_ste_htbl *htbl) { + u32 num_entries = mlx5dr_icm_pool_get_chunk_num_of_entries(htbl->chunk); struct dr_hw_ste_format *hw_ste = (struct dr_hw_ste_format *)hw_ste_p; u8 masked[DR_STE_SIZE_TAG] = {}; u32 crc32, index; @@ -32,7 +33,7 @@ u32 mlx5dr_ste_calc_hash_index(u8 *hw_ste_p, struct mlx5dr_ste_htbl *htbl) int i; /* Don't calculate CRC if the result is predicted */ - if (htbl->chunk->num_of_entries == 1 || htbl->byte_mask == 0) + if (num_entries == 1 || htbl->byte_mask == 0) return 0; /* Mask tag using byte mask, bit per byte */ @@ -45,7 +46,7 @@ u32 mlx5dr_ste_calc_hash_index(u8 *hw_ste_p, struct mlx5dr_ste_htbl *htbl) } crc32 = dr_ste_crc32_calc(masked, DR_STE_SIZE_TAG); - index = crc32 & (htbl->chunk->num_of_entries - 1); + index = crc32 & (num_entries - 1); return index; } @@ -96,13 +97,11 @@ void mlx5dr_ste_set_miss_addr(struct mlx5dr_ste_ctx *ste_ctx, } static void dr_ste_always_miss_addr(struct mlx5dr_ste_ctx *ste_ctx, - struct mlx5dr_ste *ste, u64 miss_addr) + u8 *hw_ste, u64 miss_addr) { - u8 *hw_ste_p = ste->hw_ste; - - ste_ctx->set_next_lu_type(hw_ste_p, MLX5DR_STE_LU_TYPE_DONT_CARE); - ste_ctx->set_miss_addr(hw_ste_p, miss_addr); - dr_ste_set_always_miss((struct dr_hw_ste_format *)ste->hw_ste); + ste_ctx->set_next_lu_type(hw_ste, MLX5DR_STE_LU_TYPE_DONT_CARE); + ste_ctx->set_miss_addr(hw_ste, miss_addr); + dr_ste_set_always_miss((struct dr_hw_ste_format *)hw_ste); } void mlx5dr_ste_set_hit_addr(struct mlx5dr_ste_ctx *ste_ctx, @@ -113,37 +112,45 @@ void mlx5dr_ste_set_hit_addr(struct mlx5dr_ste_ctx *ste_ctx, u64 mlx5dr_ste_get_icm_addr(struct mlx5dr_ste *ste) { - u32 index = ste - ste->htbl->ste_arr; + u64 base_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(ste->htbl->chunk); + u32 index = ste - ste->htbl->chunk->ste_arr; - return ste->htbl->chunk->icm_addr + DR_STE_SIZE * index; + return base_icm_addr + DR_STE_SIZE * index; } u64 mlx5dr_ste_get_mr_addr(struct mlx5dr_ste *ste) { - u32 index = ste - ste->htbl->ste_arr; + u32 index = ste - ste->htbl->chunk->ste_arr; - return ste->htbl->chunk->mr_addr + DR_STE_SIZE * index; + return mlx5dr_icm_pool_get_chunk_mr_addr(ste->htbl->chunk) + DR_STE_SIZE * index; +} + +u8 *mlx5dr_ste_get_hw_ste(struct mlx5dr_ste *ste) +{ + u64 index = ste - ste->htbl->chunk->ste_arr; + + return ste->htbl->chunk->hw_ste_arr + DR_STE_SIZE_REDUCED * index; } struct list_head *mlx5dr_ste_get_miss_list(struct mlx5dr_ste *ste) { - u32 index = ste - ste->htbl->ste_arr; + u32 index = ste - ste->htbl->chunk->ste_arr; - return &ste->htbl->miss_list[index]; + return &ste->htbl->chunk->miss_list[index]; } static void dr_ste_always_hit_htbl(struct mlx5dr_ste_ctx *ste_ctx, - struct mlx5dr_ste *ste, + u8 *hw_ste, struct mlx5dr_ste_htbl *next_htbl) { struct mlx5dr_icm_chunk *chunk = next_htbl->chunk; - u8 *hw_ste = ste->hw_ste; ste_ctx->set_byte_mask(hw_ste, next_htbl->byte_mask); ste_ctx->set_next_lu_type(hw_ste, next_htbl->lu_type); - ste_ctx->set_hit_addr(hw_ste, chunk->icm_addr, chunk->num_of_entries); + ste_ctx->set_hit_addr(hw_ste, mlx5dr_icm_pool_get_chunk_icm_addr(chunk), + mlx5dr_icm_pool_get_chunk_num_of_entries(chunk)); - dr_ste_set_always_hit((struct dr_hw_ste_format *)ste->hw_ste); + dr_ste_set_always_hit((struct dr_hw_ste_format *)hw_ste); } bool mlx5dr_ste_is_last_in_rule(struct mlx5dr_matcher_rx_tx *nic_matcher, @@ -166,7 +173,8 @@ bool mlx5dr_ste_is_last_in_rule(struct mlx5dr_matcher_rx_tx *nic_matcher, */ static void dr_ste_replace(struct mlx5dr_ste *dst, struct mlx5dr_ste *src) { - memcpy(dst->hw_ste, src->hw_ste, DR_STE_SIZE_REDUCED); + memcpy(mlx5dr_ste_get_hw_ste(dst), mlx5dr_ste_get_hw_ste(src), + DR_STE_SIZE_REDUCED); dst->next_htbl = src->next_htbl; if (dst->next_htbl) dst->next_htbl->pointing_ste = dst; @@ -184,18 +192,17 @@ dr_ste_remove_head_ste(struct mlx5dr_ste_ctx *ste_ctx, struct mlx5dr_ste_htbl *stats_tbl) { u8 tmp_data_ste[DR_STE_SIZE] = {}; - struct mlx5dr_ste tmp_ste = {}; u64 miss_addr; - tmp_ste.hw_ste = tmp_data_ste; + miss_addr = mlx5dr_icm_pool_get_chunk_icm_addr(nic_matcher->e_anchor->chunk); /* Use temp ste because dr_ste_always_miss_addr * touches bit_mask area which doesn't exist at ste->hw_ste. + * Need to use a full-sized (DR_STE_SIZE) hw_ste. */ - memcpy(tmp_ste.hw_ste, ste->hw_ste, DR_STE_SIZE_REDUCED); - miss_addr = nic_matcher->e_anchor->chunk->icm_addr; - dr_ste_always_miss_addr(ste_ctx, &tmp_ste, miss_addr); - memcpy(ste->hw_ste, tmp_ste.hw_ste, DR_STE_SIZE_REDUCED); + memcpy(tmp_data_ste, mlx5dr_ste_get_hw_ste(ste), DR_STE_SIZE_REDUCED); + dr_ste_always_miss_addr(ste_ctx, tmp_data_ste, miss_addr); + memcpy(mlx5dr_ste_get_hw_ste(ste), tmp_data_ste, DR_STE_SIZE_REDUCED); list_del_init(&ste->miss_list_node); @@ -237,7 +244,7 @@ dr_ste_replace_head_ste(struct mlx5dr_matcher_rx_tx *nic_matcher, mlx5dr_rule_set_last_member(next_ste->rule_rx_tx, ste, false); /* Copy all 64 hw_ste bytes */ - memcpy(hw_ste, ste->hw_ste, DR_STE_SIZE_REDUCED); + memcpy(hw_ste, mlx5dr_ste_get_hw_ste(ste), DR_STE_SIZE_REDUCED); sb_idx = ste->ste_chain_location - 1; mlx5dr_ste_set_bit_mask(hw_ste, nic_matcher->ste_builder[sb_idx].bit_mask); @@ -273,12 +280,13 @@ static void dr_ste_remove_middle_ste(struct mlx5dr_ste_ctx *ste_ctx, if (WARN_ON(!prev_ste)) return; - miss_addr = ste_ctx->get_miss_addr(ste->hw_ste); - ste_ctx->set_miss_addr(prev_ste->hw_ste, miss_addr); + miss_addr = ste_ctx->get_miss_addr(mlx5dr_ste_get_hw_ste(ste)); + ste_ctx->set_miss_addr(mlx5dr_ste_get_hw_ste(prev_ste), miss_addr); mlx5dr_send_fill_and_append_ste_send_info(prev_ste, DR_STE_SIZE_CTRL, 0, - prev_ste->hw_ste, ste_info, - send_ste_list, true /* Copy data*/); + mlx5dr_ste_get_hw_ste(prev_ste), + ste_info, send_ste_list, + true /* Copy data*/); list_del_init(&ste->miss_list_node); @@ -364,9 +372,11 @@ void mlx5dr_ste_set_hit_addr_by_next_htbl(struct mlx5dr_ste_ctx *ste_ctx, u8 *hw_ste, struct mlx5dr_ste_htbl *next_htbl) { - struct mlx5dr_icm_chunk *chunk = next_htbl->chunk; + u64 icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(next_htbl->chunk); + u32 num_entries = + mlx5dr_icm_pool_get_chunk_num_of_entries(next_htbl->chunk); - ste_ctx->set_hit_addr(hw_ste, chunk->icm_addr, chunk->num_of_entries); + ste_ctx->set_hit_addr(hw_ste, icm_addr, num_entries); } void mlx5dr_ste_prepare_for_postsend(struct mlx5dr_ste_ctx *ste_ctx, @@ -385,15 +395,22 @@ void mlx5dr_ste_set_formatted_ste(struct mlx5dr_ste_ctx *ste_ctx, struct mlx5dr_htbl_connect_info *connect_info) { bool is_rx = nic_type == DR_DOMAIN_NIC_TYPE_RX; - struct mlx5dr_ste ste = {}; + u8 tmp_hw_ste[DR_STE_SIZE] = {0}; ste_ctx->ste_init(formatted_ste, htbl->lu_type, is_rx, gvmi); - ste.hw_ste = formatted_ste; + /* Use temp ste because dr_ste_always_miss_addr/hit_htbl + * touches bit_mask area which doesn't exist at ste->hw_ste. + * Need to use a full-sized (DR_STE_SIZE) hw_ste. + */ + memcpy(tmp_hw_ste, formatted_ste, DR_STE_SIZE_REDUCED); if (connect_info->type == CONNECT_HIT) - dr_ste_always_hit_htbl(ste_ctx, &ste, connect_info->hit_next_htbl); + dr_ste_always_hit_htbl(ste_ctx, tmp_hw_ste, + connect_info->hit_next_htbl); else - dr_ste_always_miss_addr(ste_ctx, &ste, connect_info->miss_icm_addr); + dr_ste_always_miss_addr(ste_ctx, tmp_hw_ste, + connect_info->miss_icm_addr); + memcpy(formatted_ste, tmp_hw_ste, DR_STE_SIZE_REDUCED); } int mlx5dr_ste_htbl_init_and_postsend(struct mlx5dr_domain *dmn, @@ -444,7 +461,8 @@ int mlx5dr_ste_create_next_htbl(struct mlx5dr_matcher *matcher, /* Write new table to HW */ info.type = CONNECT_MISS; - info.miss_icm_addr = nic_matcher->e_anchor->chunk->icm_addr; + info.miss_icm_addr = + mlx5dr_icm_pool_get_chunk_icm_addr(nic_matcher->e_anchor->chunk); if (mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn, next_htbl, &info, false)) { mlx5dr_info(dmn, "Failed writing table to HW\n"); @@ -470,6 +488,7 @@ struct mlx5dr_ste_htbl *mlx5dr_ste_htbl_alloc(struct mlx5dr_icm_pool *pool, { struct mlx5dr_icm_chunk *chunk; struct mlx5dr_ste_htbl *htbl; + u32 num_entries; int i; htbl = kzalloc(sizeof(*htbl), GFP_KERNEL); @@ -483,22 +502,18 @@ struct mlx5dr_ste_htbl *mlx5dr_ste_htbl_alloc(struct mlx5dr_icm_pool *pool, htbl->chunk = chunk; htbl->lu_type = lu_type; htbl->byte_mask = byte_mask; - htbl->ste_arr = chunk->ste_arr; - htbl->hw_ste_arr = chunk->hw_ste_arr; - htbl->miss_list = chunk->miss_list; htbl->refcount = 0; + num_entries = mlx5dr_icm_pool_get_chunk_num_of_entries(chunk); - for (i = 0; i < chunk->num_of_entries; i++) { - struct mlx5dr_ste *ste = &htbl->ste_arr[i]; + for (i = 0; i < num_entries; i++) { + struct mlx5dr_ste *ste = &chunk->ste_arr[i]; - ste->hw_ste = htbl->hw_ste_arr + i * DR_STE_SIZE_REDUCED; ste->htbl = htbl; ste->refcount = 0; INIT_LIST_HEAD(&ste->miss_list_node); - INIT_LIST_HEAD(&htbl->miss_list[i]); + INIT_LIST_HEAD(&chunk->miss_list[i]); } - htbl->chunk_size = chunk_size; return htbl; out_free_htbl: @@ -523,8 +538,8 @@ void mlx5dr_ste_set_actions_tx(struct mlx5dr_ste_ctx *ste_ctx, struct mlx5dr_ste_actions_attr *attr, u32 *added_stes) { - ste_ctx->set_actions_tx(dmn, action_type_set, hw_ste_arr, - attr, added_stes); + ste_ctx->set_actions_tx(dmn, action_type_set, ste_ctx->actions_caps, + hw_ste_arr, attr, added_stes); } void mlx5dr_ste_set_actions_rx(struct mlx5dr_ste_ctx *ste_ctx, @@ -534,8 +549,8 @@ void mlx5dr_ste_set_actions_rx(struct mlx5dr_ste_ctx *ste_ctx, struct mlx5dr_ste_actions_attr *attr, u32 *added_stes) { - ste_ctx->set_actions_rx(dmn, action_type_set, hw_ste_arr, - attr, added_stes); + ste_ctx->set_actions_rx(dmn, action_type_set, ste_ctx->actions_caps, + hw_ste_arr, attr, added_stes); } const struct mlx5dr_ste_action_modify_field * @@ -793,6 +808,7 @@ static void dr_ste_copy_mask_spec(char *mask, struct mlx5dr_match_spec *spec, bo spec->tcp_sport = IFC_GET_CLR(fte_match_set_lyr_2_4, mask, tcp_sport, clr); spec->tcp_dport = IFC_GET_CLR(fte_match_set_lyr_2_4, mask, tcp_dport, clr); + spec->ipv4_ihl = IFC_GET_CLR(fte_match_set_lyr_2_4, mask, ipv4_ihl, clr); spec->ttl_hoplimit = IFC_GET_CLR(fte_match_set_lyr_2_4, mask, ttl_hoplimit, clr); spec->udp_sport = IFC_GET_CLR(fte_match_set_lyr_2_4, mask, udp_sport, clr); @@ -1360,15 +1376,14 @@ void mlx5dr_ste_build_tnl_header_0_1(struct mlx5dr_ste_ctx *ste_ctx, ste_ctx->build_tnl_header_0_1_init(sb, mask); } -static struct mlx5dr_ste_ctx *mlx5dr_ste_ctx_arr[] = { - [MLX5_STEERING_FORMAT_CONNECTX_5] = &ste_ctx_v0, - [MLX5_STEERING_FORMAT_CONNECTX_6DX] = &ste_ctx_v1, -}; - struct mlx5dr_ste_ctx *mlx5dr_ste_get_ctx(u8 version) { - if (version > MLX5_STEERING_FORMAT_CONNECTX_6DX) - return NULL; + if (version == MLX5_STEERING_FORMAT_CONNECTX_5) + return mlx5dr_ste_get_ctx_v0(); + else if (version == MLX5_STEERING_FORMAT_CONNECTX_6DX) + return mlx5dr_ste_get_ctx_v1(); + else if (version == MLX5_STEERING_FORMAT_CONNECTX_7) + return mlx5dr_ste_get_ctx_v2(); - return mlx5dr_ste_ctx_arr[version]; + return NULL; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h index ca8fa32b8680..17513baff9b0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h @@ -161,11 +161,13 @@ struct mlx5dr_ste_ctx { u32 actions_caps; void (*set_actions_rx)(struct mlx5dr_domain *dmn, u8 *action_type_set, + u32 actions_caps, u8 *hw_ste_arr, struct mlx5dr_ste_actions_attr *attr, u32 *added_stes); void (*set_actions_tx)(struct mlx5dr_domain *dmn, u8 *action_type_set, + u32 actions_caps, u8 *hw_ste_arr, struct mlx5dr_ste_actions_attr *attr, u32 *added_stes); @@ -197,7 +199,8 @@ struct mlx5dr_ste_ctx { void (*prepare_for_postsend)(u8 *hw_ste_p, u32 ste_size); }; -extern struct mlx5dr_ste_ctx ste_ctx_v0; -extern struct mlx5dr_ste_ctx ste_ctx_v1; +struct mlx5dr_ste_ctx *mlx5dr_ste_get_ctx_v0(void); +struct mlx5dr_ste_ctx *mlx5dr_ste_get_ctx_v1(void); +struct mlx5dr_ste_ctx *mlx5dr_ste_get_ctx_v2(void); #endif /* _DR_STE_ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c index 2d62950f7a29..5a322335f204 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c @@ -408,6 +408,7 @@ static void dr_ste_v0_arr_init_next(u8 **last_ste, static void dr_ste_v0_set_actions_tx(struct mlx5dr_domain *dmn, u8 *action_type_set, + u32 actions_caps, u8 *last_ste, struct mlx5dr_ste_actions_attr *attr, u32 *added_stes) @@ -477,6 +478,7 @@ dr_ste_v0_set_actions_tx(struct mlx5dr_domain *dmn, static void dr_ste_v0_set_actions_rx(struct mlx5dr_domain *dmn, u8 *action_type_set, + u32 actions_caps, u8 *last_ste, struct mlx5dr_ste_actions_attr *attr, u32 *added_stes) @@ -1152,6 +1154,7 @@ dr_ste_v0_build_eth_l3_ipv4_misc_tag(struct mlx5dr_match_param *value, struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; DR_STE_SET_TAG(eth_l3_ipv4_misc, tag, time_to_live, spec, ttl_hoplimit); + DR_STE_SET_TAG(eth_l3_ipv4_misc, tag, ihl, spec, ipv4_ihl); return 0; } @@ -1897,7 +1900,7 @@ static void dr_ste_v0_build_tnl_header_0_1_init(struct mlx5dr_ste_build *sb, sb->ste_build_tag_func = &dr_ste_v0_build_tnl_header_0_1_tag; } -struct mlx5dr_ste_ctx ste_ctx_v0 = { +static struct mlx5dr_ste_ctx ste_ctx_v0 = { /* Builders */ .build_eth_l2_src_dst_init = &dr_ste_v0_build_eth_l2_src_dst_init, .build_eth_l3_ipv6_src_init = &dr_ste_v0_build_eth_l3_ipv6_src_init, @@ -1950,3 +1953,8 @@ struct mlx5dr_ste_ctx ste_ctx_v0 = { .set_action_copy = &dr_ste_v0_set_action_copy, .set_action_decap_l3_list = &dr_ste_v0_set_action_decap_l3_list, }; + +struct mlx5dr_ste_ctx *mlx5dr_ste_get_ctx_v0(void) +{ + return &ste_ctx_v0; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c index 6ca06800f1d9..fcb962c6db2e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c @@ -3,7 +3,7 @@ #include #include "mlx5_ifc_dr_ste_v1.h" -#include "dr_ste.h" +#include "dr_ste_v1.h" #define DR_STE_CALC_DFNR_TYPE(lookup_type, inner) \ ((inner) ? DR_STE_V1_LU_TYPE_##lookup_type##_I : \ @@ -121,12 +121,12 @@ enum { DR_STE_V1_ACTION_MDFY_FLD_CFG_HDR_0_1 = 0x70, DR_STE_V1_ACTION_MDFY_FLD_METADATA_2_CQE = 0x7b, DR_STE_V1_ACTION_MDFY_FLD_GNRL_PURPOSE = 0x7c, - DR_STE_V1_ACTION_MDFY_FLD_REGISTER_2 = 0x8c, - DR_STE_V1_ACTION_MDFY_FLD_REGISTER_3 = 0x8d, - DR_STE_V1_ACTION_MDFY_FLD_REGISTER_4 = 0x8e, - DR_STE_V1_ACTION_MDFY_FLD_REGISTER_5 = 0x8f, - DR_STE_V1_ACTION_MDFY_FLD_REGISTER_6 = 0x90, - DR_STE_V1_ACTION_MDFY_FLD_REGISTER_7 = 0x91, + DR_STE_V1_ACTION_MDFY_FLD_REGISTER_2_0 = 0x8c, + DR_STE_V1_ACTION_MDFY_FLD_REGISTER_2_1 = 0x8d, + DR_STE_V1_ACTION_MDFY_FLD_REGISTER_1_0 = 0x8e, + DR_STE_V1_ACTION_MDFY_FLD_REGISTER_1_1 = 0x8f, + DR_STE_V1_ACTION_MDFY_FLD_REGISTER_0_0 = 0x90, + DR_STE_V1_ACTION_MDFY_FLD_REGISTER_0_1 = 0x91, }; static const struct mlx5dr_ste_action_modify_field dr_ste_v1_action_modify_field_arr[] = { @@ -223,22 +223,22 @@ static const struct mlx5dr_ste_action_modify_field dr_ste_v1_action_modify_field .hw_field = DR_STE_V1_ACTION_MDFY_FLD_METADATA_2_CQE, .start = 0, .end = 31, }, [MLX5_ACTION_IN_FIELD_METADATA_REG_C_0] = { - .hw_field = DR_STE_V1_ACTION_MDFY_FLD_REGISTER_6, .start = 0, .end = 31, + .hw_field = DR_STE_V1_ACTION_MDFY_FLD_REGISTER_0_0, .start = 0, .end = 31, }, [MLX5_ACTION_IN_FIELD_METADATA_REG_C_1] = { - .hw_field = DR_STE_V1_ACTION_MDFY_FLD_REGISTER_7, .start = 0, .end = 31, + .hw_field = DR_STE_V1_ACTION_MDFY_FLD_REGISTER_0_1, .start = 0, .end = 31, }, [MLX5_ACTION_IN_FIELD_METADATA_REG_C_2] = { - .hw_field = DR_STE_V1_ACTION_MDFY_FLD_REGISTER_4, .start = 0, .end = 31, + .hw_field = DR_STE_V1_ACTION_MDFY_FLD_REGISTER_1_0, .start = 0, .end = 31, }, [MLX5_ACTION_IN_FIELD_METADATA_REG_C_3] = { - .hw_field = DR_STE_V1_ACTION_MDFY_FLD_REGISTER_5, .start = 0, .end = 31, + .hw_field = DR_STE_V1_ACTION_MDFY_FLD_REGISTER_1_1, .start = 0, .end = 31, }, [MLX5_ACTION_IN_FIELD_METADATA_REG_C_4] = { - .hw_field = DR_STE_V1_ACTION_MDFY_FLD_REGISTER_2, .start = 0, .end = 31, + .hw_field = DR_STE_V1_ACTION_MDFY_FLD_REGISTER_2_0, .start = 0, .end = 31, }, [MLX5_ACTION_IN_FIELD_METADATA_REG_C_5] = { - .hw_field = DR_STE_V1_ACTION_MDFY_FLD_REGISTER_3, .start = 0, .end = 31, + .hw_field = DR_STE_V1_ACTION_MDFY_FLD_REGISTER_2_1, .start = 0, .end = 31, }, [MLX5_ACTION_IN_FIELD_OUT_TCP_SEQ_NUM] = { .hw_field = DR_STE_V1_ACTION_MDFY_FLD_TCP_MISC_0, .start = 0, .end = 31, @@ -262,7 +262,7 @@ static void dr_ste_v1_set_entry_type(u8 *hw_ste_p, u8 entry_type) MLX5_SET(ste_match_bwc_v1, hw_ste_p, entry_format, entry_type); } -static void dr_ste_v1_set_miss_addr(u8 *hw_ste_p, u64 miss_addr) +void dr_ste_v1_set_miss_addr(u8 *hw_ste_p, u64 miss_addr) { u64 index = miss_addr >> 6; @@ -270,7 +270,7 @@ static void dr_ste_v1_set_miss_addr(u8 *hw_ste_p, u64 miss_addr) MLX5_SET(ste_match_bwc_v1, hw_ste_p, miss_address_31_6, index); } -static u64 dr_ste_v1_get_miss_addr(u8 *hw_ste_p) +u64 dr_ste_v1_get_miss_addr(u8 *hw_ste_p) { u64 index = ((u64)MLX5_GET(ste_match_bwc_v1, hw_ste_p, miss_address_31_6) | @@ -279,12 +279,12 @@ static u64 dr_ste_v1_get_miss_addr(u8 *hw_ste_p) return index << 6; } -static void dr_ste_v1_set_byte_mask(u8 *hw_ste_p, u16 byte_mask) +void dr_ste_v1_set_byte_mask(u8 *hw_ste_p, u16 byte_mask) { MLX5_SET(ste_match_bwc_v1, hw_ste_p, byte_mask, byte_mask); } -static u16 dr_ste_v1_get_byte_mask(u8 *hw_ste_p) +u16 dr_ste_v1_get_byte_mask(u8 *hw_ste_p) { return MLX5_GET(ste_match_bwc_v1, hw_ste_p, byte_mask); } @@ -295,13 +295,13 @@ static void dr_ste_v1_set_lu_type(u8 *hw_ste_p, u16 lu_type) MLX5_SET(ste_match_bwc_v1, hw_ste_p, match_definer_ctx_idx, lu_type & 0xFF); } -static void dr_ste_v1_set_next_lu_type(u8 *hw_ste_p, u16 lu_type) +void dr_ste_v1_set_next_lu_type(u8 *hw_ste_p, u16 lu_type) { MLX5_SET(ste_match_bwc_v1, hw_ste_p, next_entry_format, lu_type >> 8); MLX5_SET(ste_match_bwc_v1, hw_ste_p, hash_definer_ctx_idx, lu_type & 0xFF); } -static u16 dr_ste_v1_get_next_lu_type(u8 *hw_ste_p) +u16 dr_ste_v1_get_next_lu_type(u8 *hw_ste_p) { u8 mode = MLX5_GET(ste_match_bwc_v1, hw_ste_p, next_entry_format); u8 index = MLX5_GET(ste_match_bwc_v1, hw_ste_p, hash_definer_ctx_idx); @@ -314,7 +314,7 @@ static void dr_ste_v1_set_hit_gvmi(u8 *hw_ste_p, u16 gvmi) MLX5_SET(ste_match_bwc_v1, hw_ste_p, next_table_base_63_48, gvmi); } -static void dr_ste_v1_set_hit_addr(u8 *hw_ste_p, u64 icm_addr, u32 ht_size) +void dr_ste_v1_set_hit_addr(u8 *hw_ste_p, u64 icm_addr, u32 ht_size) { u64 index = (icm_addr >> 5) | ht_size; @@ -322,8 +322,7 @@ static void dr_ste_v1_set_hit_addr(u8 *hw_ste_p, u64 icm_addr, u32 ht_size) MLX5_SET(ste_match_bwc_v1, hw_ste_p, next_table_base_31_5_size, index); } -static void dr_ste_v1_init(u8 *hw_ste_p, u16 lu_type, - bool is_rx, u16 gvmi) +void dr_ste_v1_init(u8 *hw_ste_p, u16 lu_type, bool is_rx, u16 gvmi) { dr_ste_v1_set_lu_type(hw_ste_p, lu_type); dr_ste_v1_set_next_lu_type(hw_ste_p, MLX5DR_STE_LU_TYPE_DONT_CARE); @@ -333,8 +332,7 @@ static void dr_ste_v1_init(u8 *hw_ste_p, u16 lu_type, MLX5_SET(ste_match_bwc_v1, hw_ste_p, miss_address_63_48, gvmi); } -static void dr_ste_v1_prepare_for_postsend(u8 *hw_ste_p, - u32 ste_size) +void dr_ste_v1_prepare_for_postsend(u8 *hw_ste_p, u32 ste_size) { u8 *tag = hw_ste_p + DR_STE_SIZE_CTRL; u8 *mask = tag + DR_STE_SIZE_TAG; @@ -511,11 +509,12 @@ static void dr_ste_v1_arr_init_next_match(u8 **last_ste, memset(action, 0, MLX5_FLD_SZ_BYTES(ste_mask_and_match_v1, action)); } -static void dr_ste_v1_set_actions_tx(struct mlx5dr_domain *dmn, - u8 *action_type_set, - u8 *last_ste, - struct mlx5dr_ste_actions_attr *attr, - u32 *added_stes) +void dr_ste_v1_set_actions_tx(struct mlx5dr_domain *dmn, + u8 *action_type_set, + u32 actions_caps, + u8 *last_ste, + struct mlx5dr_ste_actions_attr *attr, + u32 *added_stes) { u8 *action = MLX5_ADDR_OF(ste_match_bwc_v1, last_ste, action); u8 action_sz = DR_STE_ACTION_DOUBLE_SZ; @@ -533,7 +532,10 @@ static void dr_ste_v1_set_actions_tx(struct mlx5dr_domain *dmn, dr_ste_v1_set_pop_vlan(last_ste, action, attr->vlans.count); action_sz -= DR_STE_ACTION_SINGLE_SZ; action += DR_STE_ACTION_SINGLE_SZ; - allow_modify_hdr = false; + + /* Check if vlan_pop and modify_hdr on same STE is supported */ + if (!(actions_caps & DR_STE_CTX_ACTION_CAP_POP_MDFY)) + allow_modify_hdr = false; } if (action_type_set[DR_ACTION_TYP_CTR]) @@ -631,11 +633,12 @@ static void dr_ste_v1_set_actions_tx(struct mlx5dr_domain *dmn, dr_ste_v1_set_hit_addr(last_ste, attr->final_icm_addr, 1); } -static void dr_ste_v1_set_actions_rx(struct mlx5dr_domain *dmn, - u8 *action_type_set, - u8 *last_ste, - struct mlx5dr_ste_actions_attr *attr, - u32 *added_stes) +void dr_ste_v1_set_actions_rx(struct mlx5dr_domain *dmn, + u8 *action_type_set, + u32 actions_caps, + u8 *last_ste, + struct mlx5dr_ste_actions_attr *attr, + u32 *added_stes) { u8 *action = MLX5_ADDR_OF(ste_match_bwc_v1, last_ste, action); u8 action_sz = DR_STE_ACTION_DOUBLE_SZ; @@ -677,13 +680,16 @@ static void dr_ste_v1_set_actions_rx(struct mlx5dr_domain *dmn, dr_ste_v1_arr_init_next_match(&last_ste, added_stes, attr->gvmi); action = MLX5_ADDR_OF(ste_mask_and_match_v1, last_ste, action); action_sz = DR_STE_ACTION_TRIPLE_SZ; - allow_modify_hdr = false; - allow_ctr = false; } dr_ste_v1_set_pop_vlan(last_ste, action, attr->vlans.count); action_sz -= DR_STE_ACTION_SINGLE_SZ; action += DR_STE_ACTION_SINGLE_SZ; + allow_ctr = false; + + /* Check if vlan_pop and modify_hdr on same STE is supported */ + if (!(actions_caps & DR_STE_CTX_ACTION_CAP_POP_MDFY)) + allow_modify_hdr = false; } if (action_type_set[DR_ACTION_TYP_MODIFY_HDR]) { @@ -731,9 +737,9 @@ static void dr_ste_v1_set_actions_rx(struct mlx5dr_domain *dmn, action = MLX5_ADDR_OF(ste_mask_and_match_v1, last_ste, action); action_sz = DR_STE_ACTION_TRIPLE_SZ; allow_modify_hdr = true; - allow_ctr = false; } dr_ste_v1_set_counter_id(last_ste, attr->ctr_id); + allow_ctr = false; } if (action_type_set[DR_ACTION_TYP_L2_TO_TNL_L2]) { @@ -800,11 +806,11 @@ static void dr_ste_v1_set_actions_rx(struct mlx5dr_domain *dmn, dr_ste_v1_set_hit_addr(last_ste, attr->final_icm_addr, 1); } -static void dr_ste_v1_set_action_set(u8 *d_action, - u8 hw_field, - u8 shifter, - u8 length, - u32 data) +void dr_ste_v1_set_action_set(u8 *d_action, + u8 hw_field, + u8 shifter, + u8 length, + u32 data) { shifter += MLX5_MODIFY_HEADER_V1_QW_OFFSET; MLX5_SET(ste_double_action_set_v1, d_action, action_id, DR_STE_V1_ACTION_ID_SET); @@ -814,11 +820,11 @@ static void dr_ste_v1_set_action_set(u8 *d_action, MLX5_SET(ste_double_action_set_v1, d_action, inline_data, data); } -static void dr_ste_v1_set_action_add(u8 *d_action, - u8 hw_field, - u8 shifter, - u8 length, - u32 data) +void dr_ste_v1_set_action_add(u8 *d_action, + u8 hw_field, + u8 shifter, + u8 length, + u32 data) { shifter += MLX5_MODIFY_HEADER_V1_QW_OFFSET; MLX5_SET(ste_double_action_add_v1, d_action, action_id, DR_STE_V1_ACTION_ID_ADD); @@ -828,12 +834,12 @@ static void dr_ste_v1_set_action_add(u8 *d_action, MLX5_SET(ste_double_action_add_v1, d_action, add_value, data); } -static void dr_ste_v1_set_action_copy(u8 *d_action, - u8 dst_hw_field, - u8 dst_shifter, - u8 dst_len, - u8 src_hw_field, - u8 src_shifter) +void dr_ste_v1_set_action_copy(u8 *d_action, + u8 dst_hw_field, + u8 dst_shifter, + u8 dst_len, + u8 src_hw_field, + u8 src_shifter) { dst_shifter += MLX5_MODIFY_HEADER_V1_QW_OFFSET; src_shifter += MLX5_MODIFY_HEADER_V1_QW_OFFSET; @@ -848,11 +854,11 @@ static void dr_ste_v1_set_action_copy(u8 *d_action, #define DR_STE_DECAP_L3_ACTION_NUM 8 #define DR_STE_L2_HDR_MAX_SZ 20 -static int dr_ste_v1_set_action_decap_l3_list(void *data, - u32 data_sz, - u8 *hw_action, - u32 hw_action_sz, - u16 *used_hw_action_num) +int dr_ste_v1_set_action_decap_l3_list(void *data, + u32 data_sz, + u8 *hw_action, + u32 hw_action_sz, + u16 *used_hw_action_num) { u8 padded_data[DR_STE_L2_HDR_MAX_SZ] = {}; void *data_ptr = padded_data; @@ -977,8 +983,8 @@ static int dr_ste_v1_build_eth_l2_src_dst_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_eth_l2_src_dst_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_eth_l2_src_dst_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_eth_l2_src_dst_bit_mask(mask, sb->inner, sb->bit_mask); @@ -1001,8 +1007,8 @@ static int dr_ste_v1_build_eth_l3_ipv6_dst_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_eth_l3_ipv6_dst_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_eth_l3_ipv6_dst_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_eth_l3_ipv6_dst_tag(mask, sb, sb->bit_mask); @@ -1025,8 +1031,8 @@ static int dr_ste_v1_build_eth_l3_ipv6_src_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_eth_l3_ipv6_src_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_eth_l3_ipv6_src_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_eth_l3_ipv6_src_tag(mask, sb, sb->bit_mask); @@ -1060,8 +1066,8 @@ static int dr_ste_v1_build_eth_l3_ipv4_5_tuple_tag(struct mlx5dr_match_param *va return 0; } -static void dr_ste_v1_build_eth_l3_ipv4_5_tuple_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_eth_l3_ipv4_5_tuple_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_eth_l3_ipv4_5_tuple_tag(mask, sb, sb->bit_mask); @@ -1079,8 +1085,8 @@ static void dr_ste_v1_build_eth_l2_src_or_dst_bit_mask(struct mlx5dr_match_param DR_STE_SET_TAG(eth_l2_src_v1, bit_mask, first_vlan_id, mask, first_vid); DR_STE_SET_TAG(eth_l2_src_v1, bit_mask, first_cfi, mask, first_cfi); DR_STE_SET_TAG(eth_l2_src_v1, bit_mask, first_priority, mask, first_prio); - DR_STE_SET_TAG(eth_l2_src_v1, bit_mask, ip_fragmented, mask, frag); // ? - DR_STE_SET_TAG(eth_l2_src_v1, bit_mask, l3_ethertype, mask, ethertype); // ? + DR_STE_SET_TAG(eth_l2_src_v1, bit_mask, ip_fragmented, mask, frag); + DR_STE_SET_TAG(eth_l2_src_v1, bit_mask, l3_ethertype, mask, ethertype); DR_STE_SET_ONES(eth_l2_src_v1, bit_mask, l3_type, mask, ip_version); if (mask->svlan_tag || mask->cvlan_tag) { @@ -1201,8 +1207,8 @@ static int dr_ste_v1_build_eth_l2_src_tag(struct mlx5dr_match_param *value, return dr_ste_v1_build_eth_l2_src_or_dst_tag(value, sb->inner, tag); } -static void dr_ste_v1_build_eth_l2_src_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_eth_l2_src_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_eth_l2_src_bit_mask(mask, sb->inner, sb->bit_mask); @@ -1234,8 +1240,8 @@ static int dr_ste_v1_build_eth_l2_dst_tag(struct mlx5dr_match_param *value, return dr_ste_v1_build_eth_l2_src_or_dst_tag(value, sb->inner, tag); } -static void dr_ste_v1_build_eth_l2_dst_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_eth_l2_dst_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_eth_l2_dst_bit_mask(mask, sb->inner, sb->bit_mask); @@ -1314,8 +1320,8 @@ static int dr_ste_v1_build_eth_l2_tnl_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_eth_l2_tnl_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_eth_l2_tnl_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_eth_l2_tnl_bit_mask(mask, sb->inner, sb->bit_mask); @@ -1331,12 +1337,13 @@ static int dr_ste_v1_build_eth_l3_ipv4_misc_tag(struct mlx5dr_match_param *value struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer; DR_STE_SET_TAG(eth_l3_ipv4_misc_v1, tag, time_to_live, spec, ttl_hoplimit); + DR_STE_SET_TAG(eth_l3_ipv4_misc_v1, tag, ihl, spec, ipv4_ihl); return 0; } -static void dr_ste_v1_build_eth_l3_ipv4_misc_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_eth_l3_ipv4_misc_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_eth_l3_ipv4_misc_tag(mask, sb, sb->bit_mask); @@ -1375,8 +1382,8 @@ static int dr_ste_v1_build_eth_ipv6_l3_l4_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_eth_ipv6_l3_l4_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_eth_ipv6_l3_l4_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_eth_ipv6_l3_l4_tag(mask, sb, sb->bit_mask); @@ -1399,8 +1406,8 @@ static int dr_ste_v1_build_mpls_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_mpls_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_mpls_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_mpls_tag(mask, sb, sb->bit_mask); @@ -1426,8 +1433,8 @@ static int dr_ste_v1_build_tnl_gre_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_tnl_gre_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_tnl_gre_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_tnl_gre_tag(mask, sb, sb->bit_mask); @@ -1471,8 +1478,8 @@ static int dr_ste_v1_build_tnl_mpls_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_tnl_mpls_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_tnl_mpls_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_tnl_mpls_tag(mask, sb, sb->bit_mask); @@ -1506,8 +1513,8 @@ static int dr_ste_v1_build_tnl_mpls_over_udp_tag(struct mlx5dr_match_param *valu return 0; } -static void dr_ste_v1_build_tnl_mpls_over_udp_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_tnl_mpls_over_udp_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_tnl_mpls_over_udp_tag(mask, sb, sb->bit_mask); @@ -1547,8 +1554,8 @@ static int dr_ste_v1_build_tnl_mpls_over_gre_tag(struct mlx5dr_match_param *valu return 0; } -static void dr_ste_v1_build_tnl_mpls_over_gre_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_tnl_mpls_over_gre_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_tnl_mpls_over_gre_tag(mask, sb, sb->bit_mask); @@ -1594,8 +1601,8 @@ static int dr_ste_v1_build_icmp_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_icmp_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_icmp_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_icmp_tag(mask, sb, sb->bit_mask); @@ -1616,8 +1623,8 @@ static int dr_ste_v1_build_general_purpose_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_general_purpose_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_general_purpose_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_general_purpose_tag(mask, sb, sb->bit_mask); @@ -1643,8 +1650,8 @@ static int dr_ste_v1_build_eth_l4_misc_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_eth_l4_misc_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_eth_l4_misc_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_eth_l4_misc_tag(mask, sb, sb->bit_mask); @@ -1673,9 +1680,8 @@ dr_ste_v1_build_flex_parser_tnl_vxlan_gpe_tag(struct mlx5dr_match_param *value, return 0; } -static void -dr_ste_v1_build_flex_parser_tnl_vxlan_gpe_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_flex_parser_tnl_vxlan_gpe_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_flex_parser_tnl_vxlan_gpe_tag(mask, sb, sb->bit_mask); @@ -1703,9 +1709,8 @@ dr_ste_v1_build_flex_parser_tnl_geneve_tag(struct mlx5dr_match_param *value, return 0; } -static void -dr_ste_v1_build_flex_parser_tnl_geneve_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_flex_parser_tnl_geneve_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_flex_parser_tnl_geneve_tag(mask, sb, sb->bit_mask); @@ -1726,8 +1731,8 @@ static int dr_ste_v1_build_tnl_header_0_1_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_tnl_header_0_1_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_tnl_header_0_1_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { sb->lu_type = DR_STE_V1_LU_TYPE_FLEX_PARSER_TNL_HEADER; dr_ste_v1_build_tnl_header_0_1_tag(mask, sb, sb->bit_mask); @@ -1749,8 +1754,8 @@ static int dr_ste_v1_build_register_0_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_register_0_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_register_0_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_register_0_tag(mask, sb, sb->bit_mask); @@ -1773,8 +1778,8 @@ static int dr_ste_v1_build_register_1_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_register_1_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_register_1_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_register_1_tag(mask, sb, sb->bit_mask); @@ -1837,8 +1842,8 @@ static int dr_ste_v1_build_src_gvmi_qpn_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_src_gvmi_qpn_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_src_gvmi_qpn_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_src_gvmi_qpn_bit_mask(mask, sb->bit_mask); @@ -1892,8 +1897,8 @@ static int dr_ste_v1_build_felx_parser_tag(struct mlx5dr_match_param *value, return 0; } -static void dr_ste_v1_build_flex_parser_0_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_flex_parser_0_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { sb->lu_type = DR_STE_V1_LU_TYPE_FLEX_PARSER_0; dr_ste_v1_build_felx_parser_tag(mask, sb, sb->bit_mask); @@ -1901,8 +1906,8 @@ static void dr_ste_v1_build_flex_parser_0_init(struct mlx5dr_ste_build *sb, sb->ste_build_tag_func = &dr_ste_v1_build_felx_parser_tag; } -static void dr_ste_v1_build_flex_parser_1_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_flex_parser_1_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { sb->lu_type = DR_STE_V1_LU_TYPE_FLEX_PARSER_1; dr_ste_v1_build_felx_parser_tag(mask, sb, sb->bit_mask); @@ -1926,7 +1931,7 @@ dr_ste_v1_build_flex_parser_tnl_geneve_tlv_opt_tag(struct mlx5dr_match_param *va return 0; } -static void +void dr_ste_v1_build_flex_parser_tnl_geneve_tlv_opt_init(struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask) { @@ -1959,7 +1964,7 @@ dr_ste_v1_build_flex_parser_tnl_geneve_tlv_opt_exist_tag(struct mlx5dr_match_par return 0; } -static void +void dr_ste_v1_build_flex_parser_tnl_geneve_tlv_opt_exist_init(struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask) { @@ -1982,8 +1987,8 @@ static int dr_ste_v1_build_flex_parser_tnl_gtpu_tag(struct mlx5dr_match_param *v return 0; } -static void dr_ste_v1_build_flex_parser_tnl_gtpu_init(struct mlx5dr_ste_build *sb, - struct mlx5dr_match_param *mask) +void dr_ste_v1_build_flex_parser_tnl_gtpu_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask) { dr_ste_v1_build_flex_parser_tnl_gtpu_tag(mask, sb, sb->bit_mask); @@ -2008,7 +2013,7 @@ dr_ste_v1_build_tnl_gtpu_flex_parser_0_tag(struct mlx5dr_match_param *value, return 0; } -static void +void dr_ste_v1_build_tnl_gtpu_flex_parser_0_init(struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask) { @@ -2035,7 +2040,7 @@ dr_ste_v1_build_tnl_gtpu_flex_parser_1_tag(struct mlx5dr_match_param *value, return 0; } -static void +void dr_ste_v1_build_tnl_gtpu_flex_parser_1_init(struct mlx5dr_ste_build *sb, struct mlx5dr_match_param *mask) { @@ -2046,7 +2051,7 @@ dr_ste_v1_build_tnl_gtpu_flex_parser_1_init(struct mlx5dr_ste_build *sb, sb->ste_build_tag_func = &dr_ste_v1_build_tnl_gtpu_flex_parser_1_tag; } -struct mlx5dr_ste_ctx ste_ctx_v1 = { +static struct mlx5dr_ste_ctx ste_ctx_v1 = { /* Builders */ .build_eth_l2_src_dst_init = &dr_ste_v1_build_eth_l2_src_dst_init, .build_eth_l3_ipv6_src_init = &dr_ste_v1_build_eth_l3_ipv6_src_init, @@ -2091,7 +2096,8 @@ struct mlx5dr_ste_ctx ste_ctx_v1 = { /* Actions */ .actions_caps = DR_STE_CTX_ACTION_CAP_TX_POP | DR_STE_CTX_ACTION_CAP_RX_PUSH | - DR_STE_CTX_ACTION_CAP_RX_ENCAP, + DR_STE_CTX_ACTION_CAP_RX_ENCAP | + DR_STE_CTX_ACTION_CAP_POP_MDFY, .set_actions_rx = &dr_ste_v1_set_actions_rx, .set_actions_tx = &dr_ste_v1_set_actions_tx, .modify_field_arr_sz = ARRAY_SIZE(dr_ste_v1_action_modify_field_arr), @@ -2103,3 +2109,8 @@ struct mlx5dr_ste_ctx ste_ctx_v1 = { /* Send */ .prepare_for_postsend = &dr_ste_v1_prepare_for_postsend, }; + +struct mlx5dr_ste_ctx *mlx5dr_ste_get_ctx_v1(void) +{ + return &ste_ctx_v1; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.h new file mode 100644 index 000000000000..8a1d49790c6e --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#ifndef _DR_STE_V1_ +#define _DR_STE_V1_ + +#include "dr_types.h" +#include "dr_ste.h" + +void dr_ste_v1_set_miss_addr(u8 *hw_ste_p, u64 miss_addr); +u64 dr_ste_v1_get_miss_addr(u8 *hw_ste_p); +void dr_ste_v1_set_byte_mask(u8 *hw_ste_p, u16 byte_mask); +u16 dr_ste_v1_get_byte_mask(u8 *hw_ste_p); +void dr_ste_v1_set_next_lu_type(u8 *hw_ste_p, u16 lu_type); +u16 dr_ste_v1_get_next_lu_type(u8 *hw_ste_p); +void dr_ste_v1_set_hit_addr(u8 *hw_ste_p, u64 icm_addr, u32 ht_size); +void dr_ste_v1_init(u8 *hw_ste_p, u16 lu_type, bool is_rx, u16 gvmi); +void dr_ste_v1_prepare_for_postsend(u8 *hw_ste_p, u32 ste_size); +void dr_ste_v1_set_actions_tx(struct mlx5dr_domain *dmn, u8 *action_type_set, + u32 actions_caps, u8 *last_ste, + struct mlx5dr_ste_actions_attr *attr, u32 *added_stes); +void dr_ste_v1_set_actions_rx(struct mlx5dr_domain *dmn, u8 *action_type_set, + u32 actions_caps, u8 *last_ste, + struct mlx5dr_ste_actions_attr *attr, u32 *added_stes); +void dr_ste_v1_set_action_set(u8 *d_action, u8 hw_field, u8 shifter, + u8 length, u32 data); +void dr_ste_v1_set_action_add(u8 *d_action, u8 hw_field, u8 shifter, + u8 length, u32 data); +void dr_ste_v1_set_action_copy(u8 *d_action, u8 dst_hw_field, u8 dst_shifter, + u8 dst_len, u8 src_hw_field, u8 src_shifter); +int dr_ste_v1_set_action_decap_l3_list(void *data, u32 data_sz, u8 *hw_action, + u32 hw_action_sz, u16 *used_hw_action_num); +void dr_ste_v1_build_eth_l2_src_dst_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_eth_l3_ipv6_dst_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_eth_l3_ipv6_src_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_eth_l3_ipv4_5_tuple_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_eth_l2_src_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_eth_l2_dst_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_eth_l2_tnl_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_eth_l3_ipv4_misc_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_eth_ipv6_l3_l4_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_mpls_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_tnl_gre_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_tnl_mpls_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_tnl_mpls_over_udp_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_tnl_mpls_over_gre_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_icmp_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_general_purpose_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_eth_l4_misc_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_flex_parser_tnl_vxlan_gpe_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_flex_parser_tnl_geneve_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_tnl_header_0_1_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_register_0_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_register_1_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_src_gvmi_qpn_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_flex_parser_0_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_flex_parser_1_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_flex_parser_tnl_geneve_tlv_opt_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_flex_parser_tnl_geneve_tlv_opt_exist_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_flex_parser_tnl_gtpu_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_tnl_gtpu_flex_parser_0_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); +void dr_ste_v1_build_tnl_gtpu_flex_parser_1_init(struct mlx5dr_ste_build *sb, + struct mlx5dr_match_param *mask); + +#endif /* _DR_STE_V1_ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v2.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v2.c new file mode 100644 index 000000000000..c60fddd125d2 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v2.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#include "dr_ste_v1.h" + +enum { + DR_STE_V2_ACTION_MDFY_FLD_L2_OUT_0 = 0x00, + DR_STE_V2_ACTION_MDFY_FLD_L2_OUT_1 = 0x01, + DR_STE_V2_ACTION_MDFY_FLD_L2_OUT_2 = 0x02, + DR_STE_V2_ACTION_MDFY_FLD_SRC_L2_OUT_0 = 0x08, + DR_STE_V2_ACTION_MDFY_FLD_SRC_L2_OUT_1 = 0x09, + DR_STE_V2_ACTION_MDFY_FLD_L3_OUT_0 = 0x0e, + DR_STE_V2_ACTION_MDFY_FLD_L4_OUT_0 = 0x18, + DR_STE_V2_ACTION_MDFY_FLD_L4_OUT_1 = 0x19, + DR_STE_V2_ACTION_MDFY_FLD_IPV4_OUT_0 = 0x40, + DR_STE_V2_ACTION_MDFY_FLD_IPV4_OUT_1 = 0x41, + DR_STE_V2_ACTION_MDFY_FLD_IPV6_DST_OUT_0 = 0x44, + DR_STE_V2_ACTION_MDFY_FLD_IPV6_DST_OUT_1 = 0x45, + DR_STE_V2_ACTION_MDFY_FLD_IPV6_DST_OUT_2 = 0x46, + DR_STE_V2_ACTION_MDFY_FLD_IPV6_DST_OUT_3 = 0x47, + DR_STE_V2_ACTION_MDFY_FLD_IPV6_SRC_OUT_0 = 0x4c, + DR_STE_V2_ACTION_MDFY_FLD_IPV6_SRC_OUT_1 = 0x4d, + DR_STE_V2_ACTION_MDFY_FLD_IPV6_SRC_OUT_2 = 0x4e, + DR_STE_V2_ACTION_MDFY_FLD_IPV6_SRC_OUT_3 = 0x4f, + DR_STE_V2_ACTION_MDFY_FLD_TCP_MISC_0 = 0x5e, + DR_STE_V2_ACTION_MDFY_FLD_TCP_MISC_1 = 0x5f, + DR_STE_V2_ACTION_MDFY_FLD_CFG_HDR_0_0 = 0x6f, + DR_STE_V2_ACTION_MDFY_FLD_CFG_HDR_0_1 = 0x70, + DR_STE_V2_ACTION_MDFY_FLD_METADATA_2_CQE = 0x7b, + DR_STE_V2_ACTION_MDFY_FLD_GNRL_PURPOSE = 0x7c, + DR_STE_V2_ACTION_MDFY_FLD_REGISTER_2_0 = 0x90, + DR_STE_V2_ACTION_MDFY_FLD_REGISTER_2_1 = 0x91, + DR_STE_V2_ACTION_MDFY_FLD_REGISTER_1_0 = 0x92, + DR_STE_V2_ACTION_MDFY_FLD_REGISTER_1_1 = 0x93, + DR_STE_V2_ACTION_MDFY_FLD_REGISTER_0_0 = 0x94, + DR_STE_V2_ACTION_MDFY_FLD_REGISTER_0_1 = 0x95, +}; + +static const struct mlx5dr_ste_action_modify_field dr_ste_v2_action_modify_field_arr[] = { + [MLX5_ACTION_IN_FIELD_OUT_SMAC_47_16] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_SRC_L2_OUT_0, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_OUT_SMAC_15_0] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_SRC_L2_OUT_1, .start = 16, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_OUT_ETHERTYPE] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_L2_OUT_1, .start = 0, .end = 15, + }, + [MLX5_ACTION_IN_FIELD_OUT_DMAC_47_16] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_L2_OUT_0, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_OUT_DMAC_15_0] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_L2_OUT_1, .start = 16, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_OUT_IP_DSCP] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_L3_OUT_0, .start = 18, .end = 23, + }, + [MLX5_ACTION_IN_FIELD_OUT_TCP_FLAGS] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_L4_OUT_1, .start = 16, .end = 24, + .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_TCP, + }, + [MLX5_ACTION_IN_FIELD_OUT_TCP_SPORT] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_L4_OUT_0, .start = 16, .end = 31, + .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_TCP, + }, + [MLX5_ACTION_IN_FIELD_OUT_TCP_DPORT] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_L4_OUT_0, .start = 0, .end = 15, + .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_TCP, + }, + [MLX5_ACTION_IN_FIELD_OUT_IP_TTL] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_L3_OUT_0, .start = 8, .end = 15, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV4, + }, + [MLX5_ACTION_IN_FIELD_OUT_IPV6_HOPLIMIT] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_L3_OUT_0, .start = 8, .end = 15, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_UDP_SPORT] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_L4_OUT_0, .start = 16, .end = 31, + .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_UDP, + }, + [MLX5_ACTION_IN_FIELD_OUT_UDP_DPORT] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_L4_OUT_0, .start = 0, .end = 15, + .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_UDP, + }, + [MLX5_ACTION_IN_FIELD_OUT_SIPV6_127_96] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_IPV6_SRC_OUT_0, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_SIPV6_95_64] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_IPV6_SRC_OUT_1, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_SIPV6_63_32] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_IPV6_SRC_OUT_2, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_SIPV6_31_0] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_IPV6_SRC_OUT_3, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_DIPV6_127_96] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_IPV6_DST_OUT_0, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_DIPV6_95_64] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_IPV6_DST_OUT_1, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_DIPV6_63_32] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_IPV6_DST_OUT_2, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_DIPV6_31_0] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_IPV6_DST_OUT_3, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6, + }, + [MLX5_ACTION_IN_FIELD_OUT_SIPV4] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_IPV4_OUT_0, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV4, + }, + [MLX5_ACTION_IN_FIELD_OUT_DIPV4] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_IPV4_OUT_1, .start = 0, .end = 31, + .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV4, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_A] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_GNRL_PURPOSE, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_B] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_METADATA_2_CQE, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_C_0] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_REGISTER_0_0, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_C_1] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_REGISTER_0_1, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_C_2] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_REGISTER_1_0, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_C_3] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_REGISTER_1_1, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_C_4] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_REGISTER_2_0, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_METADATA_REG_C_5] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_REGISTER_2_1, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_OUT_TCP_SEQ_NUM] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_TCP_MISC_0, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_OUT_TCP_ACK_NUM] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_TCP_MISC_1, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_OUT_FIRST_VID] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_L2_OUT_2, .start = 0, .end = 15, + }, + [MLX5_ACTION_IN_FIELD_OUT_EMD_31_0] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_CFG_HDR_0_1, .start = 0, .end = 31, + }, + [MLX5_ACTION_IN_FIELD_OUT_EMD_47_32] = { + .hw_field = DR_STE_V2_ACTION_MDFY_FLD_CFG_HDR_0_0, .start = 0, .end = 15, + }, +}; + +static struct mlx5dr_ste_ctx ste_ctx_v2 = { + /* Builders */ + .build_eth_l2_src_dst_init = &dr_ste_v1_build_eth_l2_src_dst_init, + .build_eth_l3_ipv6_src_init = &dr_ste_v1_build_eth_l3_ipv6_src_init, + .build_eth_l3_ipv6_dst_init = &dr_ste_v1_build_eth_l3_ipv6_dst_init, + .build_eth_l3_ipv4_5_tuple_init = &dr_ste_v1_build_eth_l3_ipv4_5_tuple_init, + .build_eth_l2_src_init = &dr_ste_v1_build_eth_l2_src_init, + .build_eth_l2_dst_init = &dr_ste_v1_build_eth_l2_dst_init, + .build_eth_l2_tnl_init = &dr_ste_v1_build_eth_l2_tnl_init, + .build_eth_l3_ipv4_misc_init = &dr_ste_v1_build_eth_l3_ipv4_misc_init, + .build_eth_ipv6_l3_l4_init = &dr_ste_v1_build_eth_ipv6_l3_l4_init, + .build_mpls_init = &dr_ste_v1_build_mpls_init, + .build_tnl_gre_init = &dr_ste_v1_build_tnl_gre_init, + .build_tnl_mpls_init = &dr_ste_v1_build_tnl_mpls_init, + .build_tnl_mpls_over_udp_init = &dr_ste_v1_build_tnl_mpls_over_udp_init, + .build_tnl_mpls_over_gre_init = &dr_ste_v1_build_tnl_mpls_over_gre_init, + .build_icmp_init = &dr_ste_v1_build_icmp_init, + .build_general_purpose_init = &dr_ste_v1_build_general_purpose_init, + .build_eth_l4_misc_init = &dr_ste_v1_build_eth_l4_misc_init, + .build_tnl_vxlan_gpe_init = &dr_ste_v1_build_flex_parser_tnl_vxlan_gpe_init, + .build_tnl_geneve_init = &dr_ste_v1_build_flex_parser_tnl_geneve_init, + .build_tnl_geneve_tlv_opt_init = &dr_ste_v1_build_flex_parser_tnl_geneve_tlv_opt_init, + .build_tnl_geneve_tlv_opt_exist_init = + &dr_ste_v1_build_flex_parser_tnl_geneve_tlv_opt_exist_init, + .build_register_0_init = &dr_ste_v1_build_register_0_init, + .build_register_1_init = &dr_ste_v1_build_register_1_init, + .build_src_gvmi_qpn_init = &dr_ste_v1_build_src_gvmi_qpn_init, + .build_flex_parser_0_init = &dr_ste_v1_build_flex_parser_0_init, + .build_flex_parser_1_init = &dr_ste_v1_build_flex_parser_1_init, + .build_tnl_gtpu_init = &dr_ste_v1_build_flex_parser_tnl_gtpu_init, + .build_tnl_header_0_1_init = &dr_ste_v1_build_tnl_header_0_1_init, + .build_tnl_gtpu_flex_parser_0_init = &dr_ste_v1_build_tnl_gtpu_flex_parser_0_init, + .build_tnl_gtpu_flex_parser_1_init = &dr_ste_v1_build_tnl_gtpu_flex_parser_1_init, + + /* Getters and Setters */ + .ste_init = &dr_ste_v1_init, + .set_next_lu_type = &dr_ste_v1_set_next_lu_type, + .get_next_lu_type = &dr_ste_v1_get_next_lu_type, + .set_miss_addr = &dr_ste_v1_set_miss_addr, + .get_miss_addr = &dr_ste_v1_get_miss_addr, + .set_hit_addr = &dr_ste_v1_set_hit_addr, + .set_byte_mask = &dr_ste_v1_set_byte_mask, + .get_byte_mask = &dr_ste_v1_get_byte_mask, + + /* Actions */ + .actions_caps = DR_STE_CTX_ACTION_CAP_TX_POP | + DR_STE_CTX_ACTION_CAP_RX_PUSH | + DR_STE_CTX_ACTION_CAP_RX_ENCAP, + .set_actions_rx = &dr_ste_v1_set_actions_rx, + .set_actions_tx = &dr_ste_v1_set_actions_tx, + .modify_field_arr_sz = ARRAY_SIZE(dr_ste_v2_action_modify_field_arr), + .modify_field_arr = dr_ste_v2_action_modify_field_arr, + .set_action_set = &dr_ste_v1_set_action_set, + .set_action_add = &dr_ste_v1_set_action_add, + .set_action_copy = &dr_ste_v1_set_action_copy, + .set_action_decap_l3_list = &dr_ste_v1_set_action_decap_l3_list, + + /* Send */ + .prepare_for_postsend = &dr_ste_v1_prepare_for_postsend, +}; + +struct mlx5dr_ste_ctx *mlx5dr_ste_get_ctx_v2(void) +{ + return &ste_ctx_v2; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c index 8ca110643cc0..e5f6412baea9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c @@ -10,6 +10,7 @@ static int dr_table_set_miss_action_nic(struct mlx5dr_domain *dmn, struct mlx5dr_matcher_rx_tx *last_nic_matcher = NULL; struct mlx5dr_htbl_connect_info info; struct mlx5dr_ste_htbl *last_htbl; + struct mlx5dr_icm_chunk *chunk; int ret; if (!list_empty(&nic_tbl->nic_matcher_list)) @@ -22,13 +23,14 @@ static int dr_table_set_miss_action_nic(struct mlx5dr_domain *dmn, else last_htbl = nic_tbl->s_anchor; - if (action) - nic_tbl->default_icm_addr = - nic_tbl->nic_dmn->type == DR_DOMAIN_NIC_TYPE_RX ? - action->dest_tbl->tbl->rx.s_anchor->chunk->icm_addr : - action->dest_tbl->tbl->tx.s_anchor->chunk->icm_addr; - else + if (action) { + chunk = nic_tbl->nic_dmn->type == DR_DOMAIN_NIC_TYPE_RX ? + action->dest_tbl->tbl->rx.s_anchor->chunk : + action->dest_tbl->tbl->tx.s_anchor->chunk; + nic_tbl->default_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(chunk); + } else { nic_tbl->default_icm_addr = nic_tbl->nic_dmn->default_icm_addr; + } info.type = CONNECT_MISS; info.miss_icm_addr = nic_tbl->default_icm_addr; @@ -222,10 +224,10 @@ static int dr_table_create_sw_owned_tbl(struct mlx5dr_table *tbl) int ret; if (tbl->rx.s_anchor) - icm_addr_rx = tbl->rx.s_anchor->chunk->icm_addr; + icm_addr_rx = mlx5dr_icm_pool_get_chunk_icm_addr(tbl->rx.s_anchor->chunk); if (tbl->tx.s_anchor) - icm_addr_tx = tbl->tx.s_anchor->chunk->icm_addr; + icm_addr_tx = mlx5dr_icm_pool_get_chunk_icm_addr(tbl->tx.s_anchor->chunk); ft_attr.table_type = tbl->table_type; ft_attr.icm_addr_rx = icm_addr_rx; @@ -305,3 +307,8 @@ u32 mlx5dr_table_get_id(struct mlx5dr_table *tbl) { return tbl->table_id; } + +struct mlx5dr_table *mlx5dr_table_get_from_fs_ft(struct mlx5_flow_table *ft) +{ + return ft->fs_dr_table.dr_table; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index 55fcb751e24a..46866a5fc5ca 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -91,6 +91,7 @@ enum mlx5dr_ste_ctx_action_cap { DR_STE_CTX_ACTION_CAP_TX_POP = 1 << 0, DR_STE_CTX_ACTION_CAP_RX_PUSH = 1 << 1, DR_STE_CTX_ACTION_CAP_RX_ENCAP = 1 << 2, + DR_STE_CTX_ACTION_CAP_POP_MDFY = 1 << 3, }; enum { @@ -146,10 +147,12 @@ struct mlx5dr_matcher_rx_tx; struct mlx5dr_ste_ctx; struct mlx5dr_ste { - u8 *hw_ste; /* refcount: indicates the num of rules that using this ste */ u32 refcount; + /* this ste is part of a rule, located in ste's chain */ + u8 ste_chain_location; + /* attached to the miss_list head at each htbl entry */ struct list_head miss_list_node; @@ -160,9 +163,6 @@ struct mlx5dr_ste { /* The rule this STE belongs to */ struct mlx5dr_rule_rx_tx *rule_rx_tx; - - /* this ste is part of a rule, located in ste's chain */ - u8 ste_chain_location; }; struct mlx5dr_ste_htbl_ctrl { @@ -180,14 +180,7 @@ struct mlx5dr_ste_htbl { u16 byte_mask; u32 refcount; struct mlx5dr_icm_chunk *chunk; - struct mlx5dr_ste *ste_arr; - u8 *hw_ste_arr; - - struct list_head *miss_list; - - enum mlx5dr_icm_chunk_size chunk_size; struct mlx5dr_ste *pointing_ste; - struct mlx5dr_ste_htbl_ctrl ctrl; }; @@ -555,7 +548,9 @@ struct mlx5dr_match_spec { */ u32 tcp_dport:16; - u32 reserved_auto1:24; + u32 reserved_auto1:16; + u32 ipv4_ihl:4; + u32 reserved_auto2:4; u32 ttl_hoplimit:8; /* UDP source port.;tcp and udp sport/dport are mutually exclusive */ @@ -1094,16 +1089,12 @@ int mlx5dr_rule_get_reverse_rule_members(struct mlx5dr_ste **ste_arr, struct mlx5dr_icm_chunk { struct mlx5dr_icm_buddy_mem *buddy_mem; struct list_head chunk_list; - u32 rkey; - u32 num_of_entries; - u32 byte_size; - u64 icm_addr; - u64 mr_addr; /* indicates the index of this chunk in the whole memory, * used for deleting the chunk from the buddy */ unsigned int seg; + enum mlx5dr_icm_chunk_size size; /* Memory optimisation */ struct mlx5dr_ste *ste_arr; @@ -1143,6 +1134,13 @@ int mlx5dr_matcher_select_builders(struct mlx5dr_matcher *matcher, enum mlx5dr_ipv outer_ipv, enum mlx5dr_ipv inner_ipv); +u64 mlx5dr_icm_pool_get_chunk_mr_addr(struct mlx5dr_icm_chunk *chunk); +u32 mlx5dr_icm_pool_get_chunk_rkey(struct mlx5dr_icm_chunk *chunk); +u64 mlx5dr_icm_pool_get_chunk_icm_addr(struct mlx5dr_icm_chunk *chunk); +u32 mlx5dr_icm_pool_get_chunk_num_of_entries(struct mlx5dr_icm_chunk *chunk); +u32 mlx5dr_icm_pool_get_chunk_byte_size(struct mlx5dr_icm_chunk *chunk); +u8 *mlx5dr_ste_get_hw_ste(struct mlx5dr_ste *ste); + static inline int mlx5dr_icm_pool_dm_type_to_entry_size(enum mlx5dr_icm_type icm_type) { @@ -1175,7 +1173,7 @@ static inline int mlx5dr_ste_htbl_increase_threshold(struct mlx5dr_ste_htbl *htbl) { int num_of_entries = - mlx5dr_icm_pool_chunk_size_to_entries(htbl->chunk_size); + mlx5dr_icm_pool_chunk_size_to_entries(htbl->chunk->size); /* Threshold is 50%, one is added to table of size 1 */ return (num_of_entries + 1) / 2; @@ -1184,7 +1182,7 @@ mlx5dr_ste_htbl_increase_threshold(struct mlx5dr_ste_htbl *htbl) static inline bool mlx5dr_ste_htbl_may_grow(struct mlx5dr_ste_htbl *htbl) { - if (htbl->chunk_size == DR_CHUNK_SIZE_MAX - 1 || !htbl->byte_mask) + if (htbl->chunk->size == DR_CHUNK_SIZE_MAX - 1 || !htbl->byte_mask) return false; return true; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c index 3f311462bedf..045b0cf90063 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c @@ -754,6 +754,16 @@ static int mlx5_cmd_dr_destroy_ns(struct mlx5_flow_root_namespace *ns) return mlx5dr_domain_destroy(ns->fs_dr_domain.dr_domain); } +static u32 mlx5_cmd_dr_get_capabilities(struct mlx5_flow_root_namespace *ns, + enum fs_flow_table_type ft_type) +{ + if (ft_type != FS_FT_FDB || + MLX5_CAP_GEN(ns->dev, steering_format_version) == MLX5_STEERING_FORMAT_CONNECTX_5) + return 0; + + return MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX | MLX5_FLOW_STEERING_CAP_VLAN_POP_ON_TX; +} + bool mlx5_fs_dr_is_supported(struct mlx5_core_dev *dev) { return mlx5dr_is_supported(dev); @@ -778,6 +788,7 @@ static const struct mlx5_flow_cmds mlx5_flow_cmds_dr = { .set_peer = mlx5_cmd_dr_set_peer, .create_ns = mlx5_cmd_dr_create_ns, .destroy_ns = mlx5_cmd_dr_destroy_ns, + .get_capabilities = mlx5_cmd_dr_get_capabilities, }; const struct mlx5_flow_cmds *mlx5_fs_cmd_get_dr_cmds(void) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h index dfa223415fe2..ec5cbec0d455 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h @@ -53,6 +53,9 @@ void mlx5dr_domain_set_peer(struct mlx5dr_domain *dmn, struct mlx5dr_table * mlx5dr_table_create(struct mlx5dr_domain *domain, u32 level, u32 flags); +struct mlx5dr_table * +mlx5dr_table_get_from_fs_ft(struct mlx5_flow_table *ft); + int mlx5dr_table_destroy(struct mlx5dr_table *table); u32 mlx5dr_table_get_id(struct mlx5dr_table *table); @@ -136,7 +139,7 @@ mlx5dr_is_supported(struct mlx5_core_dev *dev) (MLX5_CAP_ESW_FLOWTABLE_FDB(dev, sw_owner) || (MLX5_CAP_ESW_FLOWTABLE_FDB(dev, sw_owner_v2) && (MLX5_CAP_GEN(dev, steering_format_version) <= - MLX5_STEERING_FORMAT_CONNECTX_6DX))); + MLX5_STEERING_FORMAT_CONNECTX_7))); } /* buddy functions & structure */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/uar.c b/drivers/net/ethernet/mellanox/mlx5/core/uar.c index 01e9c412977c..8455e79bc44a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/uar.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/uar.c @@ -31,7 +31,6 @@ */ #include -#include #include #include #include "mlx5_core.h" @@ -100,19 +99,21 @@ static struct mlx5_uars_page *alloc_uars_page(struct mlx5_core_dev *mdev, int err = -ENOMEM; phys_addr_t pfn; int bfregs; + int node; int i; bfregs = uars_per_sys_page(mdev) * MLX5_BFREGS_PER_UAR; - up = kzalloc(sizeof(*up), GFP_KERNEL); + node = mdev->priv.numa_node; + up = kzalloc_node(sizeof(*up), GFP_KERNEL, node); if (!up) return ERR_PTR(err); up->mdev = mdev; - up->reg_bitmap = bitmap_zalloc(bfregs, GFP_KERNEL); + up->reg_bitmap = bitmap_zalloc_node(bfregs, GFP_KERNEL, node); if (!up->reg_bitmap) goto error1; - up->fp_bitmap = bitmap_zalloc(bfregs, GFP_KERNEL); + up->fp_bitmap = bitmap_zalloc_node(bfregs, GFP_KERNEL, node); if (!up->fp_bitmap) goto error1; diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 866b9357939b..b13e0f8d232a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -177,17 +177,6 @@ void *mlxsw_core_driver_priv(struct mlxsw_core *mlxsw_core) } EXPORT_SYMBOL(mlxsw_core_driver_priv); -bool mlxsw_core_res_query_enabled(const struct mlxsw_core *mlxsw_core) -{ - return mlxsw_core->driver->res_query_enabled; -} -EXPORT_SYMBOL(mlxsw_core_res_query_enabled); - -bool mlxsw_core_temp_warn_enabled(const struct mlxsw_core *mlxsw_core) -{ - return mlxsw_core->driver->temp_warn_enabled; -} - bool mlxsw_core_fw_rev_minor_subminor_validate(const struct mlxsw_fw_rev *rev, const struct mlxsw_fw_rev *req_rev) @@ -212,6 +201,32 @@ struct mlxsw_event_listener_item { void *priv; }; +static const u8 mlxsw_core_trap_groups[] = { + MLXSW_REG_HTGT_TRAP_GROUP_EMAD, + MLXSW_REG_HTGT_TRAP_GROUP_CORE_EVENT, +}; + +static int mlxsw_core_trap_groups_set(struct mlxsw_core *mlxsw_core) +{ + char htgt_pl[MLXSW_REG_HTGT_LEN]; + int err; + int i; + + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) + return 0; + + for (i = 0; i < ARRAY_SIZE(mlxsw_core_trap_groups); i++) { + mlxsw_reg_htgt_pack(htgt_pl, mlxsw_core_trap_groups[i], + MLXSW_REG_HTGT_INVALID_POLICER, + MLXSW_REG_HTGT_DEFAULT_PRIORITY, + MLXSW_REG_HTGT_DEFAULT_TC); + err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); + if (err) + return err; + } + return 0; +} + /****************** * EMAD processing ******************/ @@ -777,16 +792,10 @@ static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core) if (err) goto err_trap_register; - err = mlxsw_core->driver->basic_trap_groups_set(mlxsw_core); - if (err) - goto err_emad_trap_set; mlxsw_core->emad.use_emad = true; return 0; -err_emad_trap_set: - mlxsw_core_trap_unregister(mlxsw_core, &mlxsw_emad_rx_listener, - mlxsw_core); err_trap_register: destroy_workqueue(mlxsw_core->emad_wq); return err; @@ -1208,36 +1217,37 @@ static void mlxsw_core_fw_params_unregister(struct mlxsw_core *mlxsw_core) ARRAY_SIZE(mlxsw_core_fw_devlink_params)); } +static void *__dl_port(struct devlink_port *devlink_port) +{ + return container_of(devlink_port, struct mlxsw_core_port, devlink_port); +} + static int mlxsw_devlink_port_split(struct devlink *devlink, - unsigned int port_index, + struct devlink_port *port, unsigned int count, struct netlink_ext_ack *extack) { + struct mlxsw_core_port *mlxsw_core_port = __dl_port(port); struct mlxsw_core *mlxsw_core = devlink_priv(devlink); - if (port_index >= mlxsw_core->max_ports) { - NL_SET_ERR_MSG_MOD(extack, "Port index exceeds maximum number of ports"); - return -EINVAL; - } if (!mlxsw_core->driver->port_split) return -EOPNOTSUPP; - return mlxsw_core->driver->port_split(mlxsw_core, port_index, count, - extack); + return mlxsw_core->driver->port_split(mlxsw_core, + mlxsw_core_port->local_port, + count, extack); } static int mlxsw_devlink_port_unsplit(struct devlink *devlink, - unsigned int port_index, + struct devlink_port *port, struct netlink_ext_ack *extack) { + struct mlxsw_core_port *mlxsw_core_port = __dl_port(port); struct mlxsw_core *mlxsw_core = devlink_priv(devlink); - if (port_index >= mlxsw_core->max_ports) { - NL_SET_ERR_MSG_MOD(extack, "Port index exceeds maximum number of ports"); - return -EINVAL; - } if (!mlxsw_core->driver->port_unsplit) return -EOPNOTSUPP; - return mlxsw_core->driver->port_unsplit(mlxsw_core, port_index, + return mlxsw_core->driver->port_unsplit(mlxsw_core, + mlxsw_core_port->local_port, extack); } @@ -1271,11 +1281,6 @@ mlxsw_devlink_sb_pool_set(struct devlink *devlink, extack); } -static void *__dl_port(struct devlink_port *devlink_port) -{ - return container_of(devlink_port, struct mlxsw_core_port, devlink_port); -} - static int mlxsw_devlink_port_type_set(struct devlink_port *devlink_port, enum devlink_port_type port_type) { @@ -1706,7 +1711,7 @@ static void mlxsw_core_health_listener_func(const struct mlxsw_reg_info *reg, } static const struct mlxsw_listener mlxsw_core_health_listener = - MLXSW_EVENTL(mlxsw_core_health_listener_func, MFDE, MFDE); + MLXSW_CORE_EVENTL(mlxsw_core_health_listener_func, MFDE); static int mlxsw_core_health_fw_fatal_dump_fatal_cause(const char *mfde_pl, @@ -2019,7 +2024,7 @@ static int mlxsw_core_health_init(struct mlxsw_core *mlxsw_core) struct devlink_health_reporter *fw_fatal; int err; - if (!mlxsw_core->driver->fw_fatal_enabled) + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) return 0; fw_fatal = devlink_health_reporter_create(devlink, &mlxsw_core_health_fw_fatal_ops, @@ -2049,7 +2054,7 @@ static int mlxsw_core_health_init(struct mlxsw_core *mlxsw_core) static void mlxsw_core_health_fini(struct mlxsw_core *mlxsw_core) { - if (!mlxsw_core->driver->fw_fatal_enabled) + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) return; mlxsw_core_health_fw_fatal_config(mlxsw_core, false); @@ -2069,7 +2074,6 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, const char *device_kind = mlxsw_bus_info->device_kind; struct mlxsw_core *mlxsw_core; struct mlxsw_driver *mlxsw_driver; - struct mlxsw_res *res; size_t alloc_size; int err; @@ -2095,8 +2099,8 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, mlxsw_core->bus_priv = bus_priv; mlxsw_core->bus_info = mlxsw_bus_info; - res = mlxsw_driver->res_query_enabled ? &mlxsw_core->res : NULL; - err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile, res); + err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile, + &mlxsw_core->res); if (err) goto err_bus_init; @@ -2122,6 +2126,10 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, } } + err = mlxsw_core_trap_groups_set(mlxsw_core); + if (err) + goto err_trap_groups_set; + err = mlxsw_emad_init(mlxsw_core); if (err) goto err_emad_init; @@ -2181,6 +2189,7 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, err_register_params: mlxsw_emad_fini(mlxsw_core); err_emad_init: +err_trap_groups_set: kfree(mlxsw_core->lag.mapping); err_alloc_lag_mapping: mlxsw_ports_fini(mlxsw_core, reload); @@ -2500,6 +2509,9 @@ int mlxsw_core_trap_register(struct mlxsw_core *mlxsw_core, char hpkt_pl[MLXSW_REG_HPKT_LEN]; int err; + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) + return 0; + err = mlxsw_core_listener_register(mlxsw_core, listener, priv, listener->enabled_on_register); if (err) @@ -2529,6 +2541,9 @@ void mlxsw_core_trap_unregister(struct mlxsw_core *mlxsw_core, { char hpkt_pl[MLXSW_REG_HPKT_LEN]; + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) + return; + if (!listener->is_event) { mlxsw_reg_hpkt_pack(hpkt_pl, listener->dis_action, listener->trap_id, listener->dis_trap_group, @@ -2540,6 +2555,45 @@ void mlxsw_core_trap_unregister(struct mlxsw_core *mlxsw_core, } EXPORT_SYMBOL(mlxsw_core_trap_unregister); +int mlxsw_core_traps_register(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listeners, + size_t listeners_count, void *priv) +{ + int i, err; + + for (i = 0; i < listeners_count; i++) { + err = mlxsw_core_trap_register(mlxsw_core, + &listeners[i], + priv); + if (err) + goto err_listener_register; + } + return 0; + +err_listener_register: + for (i--; i >= 0; i--) { + mlxsw_core_trap_unregister(mlxsw_core, + &listeners[i], + priv); + } + return err; +} +EXPORT_SYMBOL(mlxsw_core_traps_register); + +void mlxsw_core_traps_unregister(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listeners, + size_t listeners_count, void *priv) +{ + int i; + + for (i = 0; i < listeners_count; i++) { + mlxsw_core_trap_unregister(mlxsw_core, + &listeners[i], + priv); + } +} +EXPORT_SYMBOL(mlxsw_core_traps_unregister); + int mlxsw_core_trap_state_set(struct mlxsw_core *mlxsw_core, const struct mlxsw_listener *listener, bool enabled) @@ -2925,7 +2979,7 @@ static int __mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u16 local_port, attrs.switch_id.id_len = switch_id_len; mlxsw_core_port->local_port = local_port; devlink_port_attrs_set(devlink_port, &attrs); - err = devlink_port_register(devlink, devlink_port, local_port); + err = devl_port_register(devlink, devlink_port, local_port); if (err) memset(mlxsw_core_port, 0, sizeof(*mlxsw_core_port)); return err; @@ -2937,7 +2991,7 @@ static void __mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u16 local_port &mlxsw_core->ports[local_port]; struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; - devlink_port_unregister(devlink_port); + devl_port_unregister(devlink_port); memset(mlxsw_core_port, 0, sizeof(*mlxsw_core_port)); } @@ -3181,9 +3235,6 @@ int mlxsw_core_resources_query(struct mlxsw_core *mlxsw_core, char *mbox, u16 id; int err; - if (!res) - return 0; - mlxsw_cmd_mbox_zero(mbox); for (index = 0; index < MLXSW_CMD_QUERY_RESOURCES_MAX_QUERIES; diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index f30bb8614e69..16ee5e90973d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -35,10 +35,6 @@ unsigned int mlxsw_core_max_ports(const struct mlxsw_core *mlxsw_core); void *mlxsw_core_driver_priv(struct mlxsw_core *mlxsw_core); -bool mlxsw_core_res_query_enabled(const struct mlxsw_core *mlxsw_core); - -bool mlxsw_core_temp_warn_enabled(const struct mlxsw_core *mlxsw_core); - bool mlxsw_core_fw_rev_minor_subminor_validate(const struct mlxsw_fw_rev *rev, const struct mlxsw_fw_rev *req_rev); @@ -163,6 +159,9 @@ struct mlxsw_listener { .enabled_on_register = true, \ } +#define MLXSW_CORE_EVENTL(_func, _trap_id) \ + MLXSW_EVENTL(_func, _trap_id, CORE_EVENT) + int mlxsw_core_rx_listener_register(struct mlxsw_core *mlxsw_core, const struct mlxsw_rx_listener *rxl, void *priv, bool enabled); @@ -181,6 +180,12 @@ int mlxsw_core_trap_register(struct mlxsw_core *mlxsw_core, void mlxsw_core_trap_unregister(struct mlxsw_core *mlxsw_core, const struct mlxsw_listener *listener, void *priv); +int mlxsw_core_traps_register(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listeners, + size_t listeners_count, void *priv); +void mlxsw_core_traps_unregister(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listeners, + size_t listeners_count, void *priv); int mlxsw_core_trap_state_set(struct mlxsw_core *mlxsw_core, const struct mlxsw_listener *listener, bool enabled); @@ -315,7 +320,6 @@ struct mlxsw_driver { const struct mlxsw_bus_info *mlxsw_bus_info, struct netlink_ext_ack *extack); void (*fini)(struct mlxsw_core *mlxsw_core); - int (*basic_trap_groups_set)(struct mlxsw_core *mlxsw_core); int (*port_type_set)(struct mlxsw_core *mlxsw_core, u16 local_port, enum devlink_port_type new_type); int (*port_split)(struct mlxsw_core *mlxsw_core, u16 local_port, @@ -398,9 +402,6 @@ struct mlxsw_driver { u8 txhdr_len; const struct mlxsw_config_profile *profile; - bool res_query_enabled; - bool fw_fatal_enabled; - bool temp_warn_enabled; }; int mlxsw_core_kvd_sizes_get(struct mlxsw_core *mlxsw_core, diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c index 77e82e6cf6e8..fa33caecc91d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c @@ -1957,6 +1957,83 @@ int mlxsw_afa_block_append_mcrouter(struct mlxsw_afa_block *block, } EXPORT_SYMBOL(mlxsw_afa_block_append_mcrouter); +/* SIP DIP Action + * -------------- + * The SIP_DIP_ACTION is used for modifying the SIP and DIP fields of the + * packet, e.g. for NAT. The L3 checksum is updated. Also, if the L4 is TCP or + * if the L4 is UDP and the checksum field is not zero, then the L4 checksum is + * updated. + */ + +#define MLXSW_AFA_IP_CODE 0x11 +#define MLXSW_AFA_IP_SIZE 2 + +enum mlxsw_afa_ip_s_d { + /* ip refers to dip */ + MLXSW_AFA_IP_S_D_DIP, + /* ip refers to sip */ + MLXSW_AFA_IP_S_D_SIP, +}; + +/* afa_ip_s_d + * Source or destination. + */ +MLXSW_ITEM32(afa, ip, s_d, 0x00, 31, 1); + +enum mlxsw_afa_ip_m_l { + /* LSB: ip[63:0] refers to ip[63:0] */ + MLXSW_AFA_IP_M_L_LSB, + /* MSB: ip[63:0] refers to ip[127:64] */ + MLXSW_AFA_IP_M_L_MSB, +}; + +/* afa_ip_m_l + * MSB or LSB. + */ +MLXSW_ITEM32(afa, ip, m_l, 0x00, 30, 1); + +/* afa_ip_ip_63_32 + * Bits [63:32] in the IP address to change to. + */ +MLXSW_ITEM32(afa, ip, ip_63_32, 0x08, 0, 32); + +/* afa_ip_ip_31_0 + * Bits [31:0] in the IP address to change to. + */ +MLXSW_ITEM32(afa, ip, ip_31_0, 0x0C, 0, 32); + +static void mlxsw_afa_ip_pack(char *payload, enum mlxsw_afa_ip_s_d s_d, + enum mlxsw_afa_ip_m_l m_l, u32 ip_31_0, + u32 ip_63_32) +{ + mlxsw_afa_ip_s_d_set(payload, s_d); + mlxsw_afa_ip_m_l_set(payload, m_l); + mlxsw_afa_ip_ip_31_0_set(payload, ip_31_0); + mlxsw_afa_ip_ip_63_32_set(payload, ip_63_32); +} + +int mlxsw_afa_block_append_ip(struct mlxsw_afa_block *block, bool is_dip, + bool is_lsb, u32 val_31_0, u32 val_63_32, + struct netlink_ext_ack *extack) +{ + enum mlxsw_afa_ip_s_d s_d = is_dip ? MLXSW_AFA_IP_S_D_DIP : + MLXSW_AFA_IP_S_D_SIP; + enum mlxsw_afa_ip_m_l m_l = is_lsb ? MLXSW_AFA_IP_M_L_LSB : + MLXSW_AFA_IP_M_L_MSB; + char *act = mlxsw_afa_block_append_action(block, + MLXSW_AFA_IP_CODE, + MLXSW_AFA_IP_SIZE); + + if (IS_ERR(act)) { + NL_SET_ERR_MSG_MOD(extack, "Cannot append IP action"); + return PTR_ERR(act); + } + + mlxsw_afa_ip_pack(act, s_d, m_l, val_31_0, val_63_32); + return 0; +} +EXPORT_SYMBOL(mlxsw_afa_block_append_ip); + /* L4 Port Action * -------------- * The L4_PORT_ACTION is used for modifying the sport and dport fields of the packet, e.g. for NAT. diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h index 16cbd6acbb01..db58037be46e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h @@ -92,6 +92,9 @@ int mlxsw_afa_block_append_fid_set(struct mlxsw_afa_block *block, u16 fid, int mlxsw_afa_block_append_mcrouter(struct mlxsw_afa_block *block, u16 expected_irif, u16 min_mtu, bool rmid_valid, u32 kvdl_index); +int mlxsw_afa_block_append_ip(struct mlxsw_afa_block *block, bool is_dip, + bool is_lsb, u32 val_31_0, u32 val_63_32, + struct netlink_ext_ack *extack); int mlxsw_afa_block_append_l4port(struct mlxsw_afa_block *block, bool is_dport, u16 l4_port, struct netlink_ext_ack *extack); int mlxsw_afa_block_append_police(struct mlxsw_afa_block *block, diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c index 6dd4ae2f45f4..29a74b8bd5b5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c @@ -18,6 +18,7 @@ struct mlxsw_env_module_info { int num_ports_mapped; int num_ports_up; enum ethtool_module_power_mode_policy power_mode_policy; + enum mlxsw_reg_pmtm_module_type type; }; struct mlxsw_env { @@ -27,14 +28,47 @@ struct mlxsw_env { struct mlxsw_env_module_info module_info[]; }; -static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id, - bool *qsfp, bool *cmis) +static int __mlxsw_env_validate_module_type(struct mlxsw_core *core, u8 module) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(core); + int err; + + switch (mlxsw_env->module_info[module].type) { + case MLXSW_REG_PMTM_MODULE_TYPE_TWISTED_PAIR: + err = -EINVAL; + break; + default: + err = 0; + } + + return err; +} + +static int mlxsw_env_validate_module_type(struct mlxsw_core *core, u8 module) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(core); + int err; + + mutex_lock(&mlxsw_env->module_info_lock); + err = __mlxsw_env_validate_module_type(core, module); + mutex_unlock(&mlxsw_env->module_info_lock); + + return err; +} + +static int +mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id, bool *qsfp, + bool *cmis) { char mcia_pl[MLXSW_REG_MCIA_LEN]; char *eeprom_tmp; u8 ident; int err; + err = mlxsw_env_validate_module_type(core, id); + if (err) + return err; + mlxsw_reg_mcia_pack(mcia_pl, id, 0, MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1, MLXSW_REG_MCIA_I2C_ADDR_LOW); err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl); @@ -53,6 +87,7 @@ static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id, *qsfp = true; break; case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD: + case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_OSFP: *qsfp = true; *cmis = true; break; @@ -206,7 +241,8 @@ int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module, return 0; } -int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module, +int mlxsw_env_get_module_info(struct net_device *netdev, + struct mlxsw_core *mlxsw_core, int module, struct ethtool_modinfo *modinfo) { u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE]; @@ -215,6 +251,13 @@ int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module, unsigned int read_size; int err; + err = mlxsw_env_validate_module_type(mlxsw_core, module); + if (err) { + netdev_err(netdev, + "EEPROM is not equipped on port module type"); + return err; + } + err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset, module_info, false, &read_size); if (err) @@ -261,6 +304,7 @@ int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module, modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2; break; case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD: + case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_OSFP: /* Use SFF_8636 as base type. ethtool should recognize specific * type through the identifier value. */ @@ -356,6 +400,13 @@ mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module, { u32 bytes_read = 0; u16 device_addr; + int err; + + err = mlxsw_env_validate_module_type(mlxsw_core, module); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "EEPROM is not equipped on port module type"); + return err; + } /* Offset cannot be larger than 2 * ETH_MODULE_EEPROM_PAGE_LEN */ device_addr = page->offset; @@ -364,7 +415,6 @@ mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module, char mcia_pl[MLXSW_REG_MCIA_LEN]; char *eeprom_tmp; u8 size; - int err; size = min_t(u8, page->length - bytes_read, MLXSW_REG_MCIA_EEPROM_SIZE); @@ -414,11 +464,14 @@ int mlxsw_env_reset_module(struct net_device *netdev, !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) return 0; - if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) - return -EINVAL; - mutex_lock(&mlxsw_env->module_info_lock); + err = __mlxsw_env_validate_module_type(mlxsw_core, module); + if (err) { + netdev_err(netdev, "Reset module is not supported on port module type\n"); + goto out; + } + if (mlxsw_env->module_info[module].num_ports_up) { netdev_err(netdev, "Cannot reset module when ports using it are administratively up\n"); err = -EINVAL; @@ -456,11 +509,14 @@ mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 module, u32 status_bits; int err; - if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) - return -EINVAL; - mutex_lock(&mlxsw_env->module_info_lock); + err = __mlxsw_env_validate_module_type(mlxsw_core, module); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Power mode is not supported on port module type"); + goto out; + } + params->policy = mlxsw_env->module_info[module].power_mode_policy; mlxsw_reg_mcion_pack(mcion_pl, module); @@ -560,9 +616,6 @@ mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 module, bool low_power; int err = 0; - if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) - return -EINVAL; - if (policy != ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH && policy != ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO) { NL_SET_ERR_MSG_MOD(extack, "Unsupported power mode policy"); @@ -571,6 +624,13 @@ mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 module, mutex_lock(&mlxsw_env->module_info_lock); + err = __mlxsw_env_validate_module_type(mlxsw_core, module); + if (err) { + NL_SET_ERR_MSG_MOD(extack, + "Power mode set is not supported on port module type"); + goto out; + } + if (mlxsw_env->module_info[module].power_mode_policy == policy) goto out; @@ -661,13 +721,12 @@ static int mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core, return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl); } -static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core, - u8 module_count) +static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core) { int i, err, sensor_index; bool has_temp_sensor; - for (i = 0; i < module_count; i++) { + for (i = 0; i < mlxsw_core_env(mlxsw_core)->module_count; i++) { err = mlxsw_env_module_has_temp_sensor(mlxsw_core, i, &has_temp_sensor); if (err) @@ -759,15 +818,12 @@ mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info *reg, char *mtwe_pl, } static const struct mlxsw_listener mlxsw_env_temp_warn_listener = - MLXSW_EVENTL(mlxsw_env_mtwe_listener_func, MTWE, MTWE); + MLXSW_CORE_EVENTL(mlxsw_env_mtwe_listener_func, MTWE); static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core) { struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); - if (!mlxsw_core_temp_warn_enabled(mlxsw_core)) - return 0; - return mlxsw_core_trap_register(mlxsw_core, &mlxsw_env_temp_warn_listener, mlxsw_env); @@ -775,9 +831,6 @@ static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core) static void mlxsw_env_temp_warn_event_unregister(struct mlxsw_env *mlxsw_env) { - if (!mlxsw_core_temp_warn_enabled(mlxsw_env->core)) - return; - mlxsw_core_trap_unregister(mlxsw_env->core, &mlxsw_env_temp_warn_listener, mlxsw_env); } @@ -849,16 +902,13 @@ mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl, } static const struct mlxsw_listener mlxsw_env_module_plug_listener = - MLXSW_EVENTL(mlxsw_env_pmpe_listener_func, PMPE, PMPE); + MLXSW_CORE_EVENTL(mlxsw_env_pmpe_listener_func, PMPE); static int mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core) { struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); - if (!mlxsw_core_temp_warn_enabled(mlxsw_core)) - return 0; - return mlxsw_core_trap_register(mlxsw_core, &mlxsw_env_module_plug_listener, mlxsw_env); @@ -867,21 +917,17 @@ mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core) static void mlxsw_env_module_plug_event_unregister(struct mlxsw_env *mlxsw_env) { - if (!mlxsw_core_temp_warn_enabled(mlxsw_env->core)) - return; - mlxsw_core_trap_unregister(mlxsw_env->core, &mlxsw_env_module_plug_listener, mlxsw_env); } static int -mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core, - u8 module_count) +mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core) { int i, err; - for (i = 0; i < module_count; i++) { + for (i = 0; i < mlxsw_core_env(mlxsw_core)->module_count; i++) { char pmaos_pl[MLXSW_REG_PMAOS_LEN]; mlxsw_reg_pmaos_pack(pmaos_pl, i); @@ -901,9 +947,6 @@ mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module, { struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); - if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) - return -EINVAL; - mutex_lock(&mlxsw_env->module_info_lock); *p_counter = mlxsw_env->module_info[module].module_overheat_counter; mutex_unlock(&mlxsw_env->module_info_lock); @@ -916,9 +959,6 @@ void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 module) { struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); - if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) - return; - mutex_lock(&mlxsw_env->module_info_lock); mlxsw_env->module_info[module].num_ports_mapped++; mutex_unlock(&mlxsw_env->module_info_lock); @@ -929,9 +969,6 @@ void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 module) { struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); - if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) - return; - mutex_lock(&mlxsw_env->module_info_lock); mlxsw_env->module_info[module].num_ports_mapped--; mutex_unlock(&mlxsw_env->module_info_lock); @@ -943,9 +980,6 @@ int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 module) struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); int err = 0; - if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) - return -EINVAL; - mutex_lock(&mlxsw_env->module_info_lock); if (mlxsw_env->module_info[module].power_mode_policy != @@ -975,9 +1009,6 @@ void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 module) { struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); - if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) - return; - mutex_lock(&mlxsw_env->module_info_lock); mlxsw_env->module_info[module].num_ports_up--; @@ -999,6 +1030,28 @@ void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 module) } EXPORT_SYMBOL(mlxsw_env_module_port_down); +static int +mlxsw_env_module_type_set(struct mlxsw_core *mlxsw_core) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + int i; + + for (i = 0; i < mlxsw_env->module_count; i++) { + char pmtm_pl[MLXSW_REG_PMTM_LEN]; + int err; + + mlxsw_reg_pmtm_pack(pmtm_pl, 0, i); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtm), pmtm_pl); + if (err) + return err; + + mlxsw_env->module_info[i].type = + mlxsw_reg_pmtm_module_type_get(pmtm_pl); + } + + return 0; +} + int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env) { char mgpir_pl[MLXSW_REG_MGPIR_LEN]; @@ -1037,17 +1090,21 @@ int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env) if (err) goto err_module_plug_event_register; - err = mlxsw_env_module_oper_state_event_enable(mlxsw_core, - env->module_count); + err = mlxsw_env_module_oper_state_event_enable(mlxsw_core); if (err) goto err_oper_state_event_enable; - err = mlxsw_env_module_temp_event_enable(mlxsw_core, env->module_count); + err = mlxsw_env_module_temp_event_enable(mlxsw_core); if (err) goto err_temp_event_enable; + err = mlxsw_env_module_type_set(mlxsw_core); + if (err) + goto err_type_set; + return 0; +err_type_set: err_temp_event_enable: err_oper_state_event_enable: mlxsw_env_module_plug_event_unregister(env); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.h b/drivers/net/ethernet/mellanox/mlxsw/core_env.h index da121b1a84b4..ec6564e5d2ee 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.h @@ -12,7 +12,8 @@ struct ethtool_eeprom; int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module, int off, int *temp); -int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module, +int mlxsw_env_get_module_info(struct net_device *netdev, + struct mlxsw_core *mlxsw_core, int module, struct ethtool_modinfo *modinfo); int mlxsw_env_get_module_eeprom(struct net_device *netdev, diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c index d41afdfbd085..8b170ad92302 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c @@ -57,14 +57,14 @@ static ssize_t mlxsw_hwmon_temp_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); - struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_attr->hwmon; char mtmp_pl[MLXSW_REG_MTMP_LEN]; int temp, index; int err; - index = mlxsw_hwmon_get_attr_index(mlwsw_hwmon_attr->type_index, + index = mlxsw_hwmon_get_attr_index(mlxsw_hwmon_attr->type_index, mlxsw_hwmon->module_sensor_max); mlxsw_reg_mtmp_pack(mtmp_pl, index, false, false); err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl); @@ -80,14 +80,14 @@ static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); - struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_attr->hwmon; char mtmp_pl[MLXSW_REG_MTMP_LEN]; int temp_max, index; int err; - index = mlxsw_hwmon_get_attr_index(mlwsw_hwmon_attr->type_index, + index = mlxsw_hwmon_get_attr_index(mlxsw_hwmon_attr->type_index, mlxsw_hwmon->module_sensor_max); mlxsw_reg_mtmp_pack(mtmp_pl, index, false, false); err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl); @@ -103,9 +103,9 @@ static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); - struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_attr->hwmon; char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0}; unsigned long val; int index; @@ -117,7 +117,7 @@ static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev, if (val != 1) return -EINVAL; - index = mlxsw_hwmon_get_attr_index(mlwsw_hwmon_attr->type_index, + index = mlxsw_hwmon_get_attr_index(mlxsw_hwmon_attr->type_index, mlxsw_hwmon->module_sensor_max); mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, index); @@ -138,13 +138,13 @@ static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); - struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_attr->hwmon; char mfsm_pl[MLXSW_REG_MFSM_LEN]; int err; - mlxsw_reg_mfsm_pack(mfsm_pl, mlwsw_hwmon_attr->type_index); + mlxsw_reg_mfsm_pack(mfsm_pl, mlxsw_hwmon_attr->type_index); err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsm), mfsm_pl); if (err) { dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n"); @@ -157,9 +157,9 @@ static ssize_t mlxsw_hwmon_fan_fault_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); - struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_attr->hwmon; char fore_pl[MLXSW_REG_FORE_LEN]; bool fault; int err; @@ -169,7 +169,7 @@ static ssize_t mlxsw_hwmon_fan_fault_show(struct device *dev, dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n"); return err; } - mlxsw_reg_fore_unpack(fore_pl, mlwsw_hwmon_attr->type_index, &fault); + mlxsw_reg_fore_unpack(fore_pl, mlxsw_hwmon_attr->type_index, &fault); return sprintf(buf, "%u\n", fault); } @@ -178,13 +178,13 @@ static ssize_t mlxsw_hwmon_pwm_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); - struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_attr->hwmon; char mfsc_pl[MLXSW_REG_MFSC_LEN]; int err; - mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, 0); + mlxsw_reg_mfsc_pack(mfsc_pl, mlxsw_hwmon_attr->type_index, 0); err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl); if (err) { dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query PWM\n"); @@ -198,9 +198,9 @@ static ssize_t mlxsw_hwmon_pwm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); - struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_attr->hwmon; char mfsc_pl[MLXSW_REG_MFSC_LEN]; unsigned long val; int err; @@ -211,7 +211,7 @@ static ssize_t mlxsw_hwmon_pwm_store(struct device *dev, if (val > 255) return -EINVAL; - mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, val); + mlxsw_reg_mfsc_pack(mfsc_pl, mlxsw_hwmon_attr->type_index, val); err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl); if (err) { dev_err(mlxsw_hwmon->bus_info->dev, "Failed to write PWM\n"); @@ -224,14 +224,14 @@ static int mlxsw_hwmon_module_temp_get(struct device *dev, struct device_attribute *attr, int *p_temp) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); - struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_attr->hwmon; char mtmp_pl[MLXSW_REG_MTMP_LEN]; u8 module; int err; - module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count; + module = mlxsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count; mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module, false, false); err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl); @@ -261,15 +261,15 @@ static ssize_t mlxsw_hwmon_module_temp_fault_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); - struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_attr->hwmon; char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0}; u8 module, fault; u16 temp; int err; - module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count; + module = mlxsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count; mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module, 1); err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtbr), mtbr_pl); @@ -303,13 +303,13 @@ static int mlxsw_hwmon_module_temp_critical_get(struct device *dev, struct device_attribute *attr, int *p_temp) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); - struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_attr->hwmon; u8 module; int err; - module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count; + module = mlxsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count; err = mlxsw_env_module_temp_thresholds_get(mlxsw_hwmon->core, module, SFP_TEMP_HIGH_WARN, p_temp); if (err) { @@ -337,13 +337,13 @@ static int mlxsw_hwmon_module_temp_emergency_get(struct device *dev, struct device_attribute *attr, int *p_temp) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); - struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_attr->hwmon; u8 module; int err; - module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count; + module = mlxsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count; err = mlxsw_env_module_temp_thresholds_get(mlxsw_hwmon->core, module, SFP_TEMP_HIGH_ALARM, p_temp); if (err) { @@ -373,11 +373,11 @@ mlxsw_hwmon_module_temp_label_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); return sprintf(buf, "front panel %03u\n", - mlwsw_hwmon_attr->type_index); + mlxsw_hwmon_attr->type_index); } static ssize_t @@ -385,10 +385,10 @@ mlxsw_hwmon_gbox_temp_label_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = container_of(attr, struct mlxsw_hwmon_attr, dev_attr); - struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; - int index = mlwsw_hwmon_attr->type_index - + struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_attr->hwmon; + int index = mlxsw_hwmon_attr->type_index - mlxsw_hwmon->module_sensor_max + 1; return sprintf(buf, "gearbox %03u\n", index); @@ -655,9 +655,6 @@ static int mlxsw_hwmon_module_init(struct mlxsw_hwmon *mlxsw_hwmon) u8 module_sensor_max; int i, err; - if (!mlxsw_core_res_query_enabled(mlxsw_hwmon->core)) - return 0; - mlxsw_reg_mgpir_pack(mgpir_pl); err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mgpir), mgpir_pl); if (err) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c index b29824448aa8..05f54bd982c0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c @@ -357,6 +357,10 @@ static int mlxsw_thermal_trend_get(struct thermal_zone_device *tzdev, return 0; } +static struct thermal_zone_params mlxsw_thermal_params = { + .no_hwmon = true, +}; + static struct thermal_zone_device_ops mlxsw_thermal_ops = { .bind = mlxsw_thermal_bind, .unbind = mlxsw_thermal_unbind, @@ -388,11 +392,11 @@ static int mlxsw_thermal_module_bind(struct thermal_zone_device *tzdev, trip->min_state, THERMAL_WEIGHT_DEFAULT); if (err < 0) - goto err_bind_cooling_device; + goto err_thermal_zone_bind_cooling_device; } return 0; -err_bind_cooling_device: +err_thermal_zone_bind_cooling_device: for (j = i - 1; j >= 0; j--) thermal_zone_unbind_cooling_device(tzdev, j, cdev); return err; @@ -678,7 +682,8 @@ mlxsw_thermal_module_tz_init(struct mlxsw_thermal_module *module_tz) MLXSW_THERMAL_TRIP_MASK, module_tz, &mlxsw_thermal_module_ops, - NULL, 0, + &mlxsw_thermal_params, + 0, module_tz->parent->polling_delay); if (IS_ERR(module_tz->tzdev)) { err = PTR_ERR(module_tz->tzdev); @@ -741,9 +746,6 @@ mlxsw_thermal_modules_init(struct device *dev, struct mlxsw_core *core, char mgpir_pl[MLXSW_REG_MGPIR_LEN]; int i, err; - if (!mlxsw_core_res_query_enabled(core)) - return 0; - mlxsw_reg_mgpir_pack(mgpir_pl); err = mlxsw_reg_query(core, MLXSW_REG(mgpir), mgpir_pl); if (err) @@ -761,7 +763,7 @@ mlxsw_thermal_modules_init(struct device *dev, struct mlxsw_core *core, for (i = 0; i < thermal->tz_module_num; i++) { err = mlxsw_thermal_module_init(dev, core, thermal, i); if (err) - goto err_unreg_tz_module_arr; + goto err_thermal_module_init; } for (i = 0; i < thermal->tz_module_num; i++) { @@ -770,12 +772,13 @@ mlxsw_thermal_modules_init(struct device *dev, struct mlxsw_core *core, continue; err = mlxsw_thermal_module_tz_init(module_tz); if (err) - goto err_unreg_tz_module_arr; + goto err_thermal_module_tz_init; } return 0; -err_unreg_tz_module_arr: +err_thermal_module_tz_init: +err_thermal_module_init: for (i = thermal->tz_module_num - 1; i >= 0; i--) mlxsw_thermal_module_fini(&thermal->tz_module_arr[i]); kfree(thermal->tz_module_arr); @@ -787,9 +790,6 @@ mlxsw_thermal_modules_fini(struct mlxsw_thermal *thermal) { int i; - if (!mlxsw_core_res_query_enabled(thermal->core)) - return; - for (i = thermal->tz_module_num - 1; i >= 0; i--) mlxsw_thermal_module_fini(&thermal->tz_module_arr[i]); kfree(thermal->tz_module_arr); @@ -808,7 +808,7 @@ mlxsw_thermal_gearbox_tz_init(struct mlxsw_thermal_module *gearbox_tz) MLXSW_THERMAL_TRIP_MASK, gearbox_tz, &mlxsw_thermal_gearbox_ops, - NULL, 0, + &mlxsw_thermal_params, 0, gearbox_tz->parent->polling_delay); if (IS_ERR(gearbox_tz->tzdev)) return PTR_ERR(gearbox_tz->tzdev); @@ -837,9 +837,6 @@ mlxsw_thermal_gearboxes_init(struct device *dev, struct mlxsw_core *core, int i; int err; - if (!mlxsw_core_res_query_enabled(core)) - return 0; - mlxsw_reg_mgpir_pack(mgpir_pl); err = mlxsw_reg_query(core, MLXSW_REG(mgpir), mgpir_pl); if (err) @@ -866,12 +863,12 @@ mlxsw_thermal_gearboxes_init(struct device *dev, struct mlxsw_core *core, gearbox_tz->parent = thermal; err = mlxsw_thermal_gearbox_tz_init(gearbox_tz); if (err) - goto err_unreg_tz_gearbox; + goto err_thermal_gearbox_tz_init; } return 0; -err_unreg_tz_gearbox: +err_thermal_gearbox_tz_init: for (i--; i >= 0; i--) mlxsw_thermal_gearbox_tz_fini(&thermal->tz_gearbox_arr[i]); kfree(thermal->tz_gearbox_arr); @@ -883,9 +880,6 @@ mlxsw_thermal_gearboxes_fini(struct mlxsw_thermal *thermal) { int i; - if (!mlxsw_core_res_query_enabled(thermal->core)) - return; - for (i = thermal->tz_gearbox_num - 1; i >= 0; i--) mlxsw_thermal_gearbox_tz_fini(&thermal->tz_gearbox_arr[i]); kfree(thermal->tz_gearbox_arr); @@ -915,7 +909,7 @@ int mlxsw_thermal_init(struct mlxsw_core *core, err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfcr), mfcr_pl); if (err) { dev_err(dev, "Failed to probe PWMs\n"); - goto err_free_thermal; + goto err_reg_query; } mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active); @@ -929,14 +923,14 @@ int mlxsw_thermal_init(struct mlxsw_core *core, err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfsl), mfsl_pl); if (err) - goto err_free_thermal; + goto err_reg_query; /* set the minimal RPMs to 0 */ mlxsw_reg_mfsl_tach_min_set(mfsl_pl, 0); err = mlxsw_reg_write(thermal->core, MLXSW_REG(mfsl), mfsl_pl); if (err) - goto err_free_thermal; + goto err_reg_write; } } for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++) { @@ -949,7 +943,7 @@ int mlxsw_thermal_init(struct mlxsw_core *core, if (IS_ERR(cdev)) { err = PTR_ERR(cdev); dev_err(dev, "Failed to register cooling device\n"); - goto err_unreg_cdevs; + goto err_thermal_cooling_device_register; } thermal->cdevs[i] = cdev; } @@ -968,43 +962,45 @@ int mlxsw_thermal_init(struct mlxsw_core *core, MLXSW_THERMAL_TRIP_MASK, thermal, &mlxsw_thermal_ops, - NULL, 0, + &mlxsw_thermal_params, 0, thermal->polling_delay); if (IS_ERR(thermal->tzdev)) { err = PTR_ERR(thermal->tzdev); dev_err(dev, "Failed to register thermal zone\n"); - goto err_unreg_cdevs; + goto err_thermal_zone_device_register; } err = mlxsw_thermal_modules_init(dev, core, thermal); if (err) - goto err_unreg_tzdev; + goto err_thermal_modules_init; err = mlxsw_thermal_gearboxes_init(dev, core, thermal); if (err) - goto err_unreg_modules_tzdev; + goto err_thermal_gearboxes_init; err = thermal_zone_device_enable(thermal->tzdev); if (err) - goto err_unreg_gearboxes; + goto err_thermal_zone_device_enable; *p_thermal = thermal; return 0; -err_unreg_gearboxes: +err_thermal_zone_device_enable: mlxsw_thermal_gearboxes_fini(thermal); -err_unreg_modules_tzdev: +err_thermal_gearboxes_init: mlxsw_thermal_modules_fini(thermal); -err_unreg_tzdev: +err_thermal_modules_init: if (thermal->tzdev) { thermal_zone_device_unregister(thermal->tzdev); thermal->tzdev = NULL; } -err_unreg_cdevs: +err_thermal_zone_device_register: +err_thermal_cooling_device_register: for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++) if (thermal->cdevs[i]) thermal_cooling_device_unregister(thermal->cdevs[i]); -err_free_thermal: +err_reg_write: +err_reg_query: devm_kfree(dev, thermal); return err; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index 10d13f5f9c7d..3bc012dafd08 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -110,7 +110,8 @@ static int mlxsw_m_get_module_info(struct net_device *netdev, struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; - return mlxsw_env_get_module_info(core, mlxsw_m_port->module, modinfo); + return mlxsw_env_get_module_info(netdev, core, mlxsw_m_port->module, + modinfo); } static int @@ -421,6 +422,7 @@ static int mlxsw_m_init(struct mlxsw_core *mlxsw_core, struct netlink_ext_ack *extack) { struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core); + struct devlink *devlink = priv_to_devlink(mlxsw_core); int err; mlxsw_m->core = mlxsw_core; @@ -436,7 +438,9 @@ static int mlxsw_m_init(struct mlxsw_core *mlxsw_core, return err; } + devl_lock(devlink); err = mlxsw_m_ports_create(mlxsw_m); + devl_unlock(devlink); if (err) { dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n"); return err; @@ -448,8 +452,11 @@ static int mlxsw_m_init(struct mlxsw_core *mlxsw_core, static void mlxsw_m_fini(struct mlxsw_core *mlxsw_core) { struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core); + struct devlink *devlink = priv_to_devlink(mlxsw_core); + devl_lock(devlink); mlxsw_m_ports_remove(mlxsw_m); + devl_unlock(devlink); } static const struct mlxsw_config_profile mlxsw_m_config_profile; @@ -460,7 +467,6 @@ static struct mlxsw_driver mlxsw_m_driver = { .init = mlxsw_m_init, .fini = mlxsw_m_fini, .profile = &mlxsw_m_config_profile, - .res_query_enabled = true, }; static const struct i2c_device_id mlxsw_m_i2c_id[] = { diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 24cc65018b41..67b1a2f8397f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4482,6 +4482,8 @@ MLXSW_ITEM32(reg, ptys, ext_eth_proto_cap, 0x08, 0, 32); #define MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 BIT(21) #define MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 BIT(22) #define MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4 BIT(23) +#define MLXSW_REG_PTYS_ETH_SPEED_100BASE_T BIT(24) +#define MLXSW_REG_PTYS_ETH_SPEED_1000BASE_T BIT(25) #define MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR BIT(27) #define MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR BIT(28) #define MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR BIT(29) @@ -6062,6 +6064,58 @@ static inline void mlxsw_reg_pllp_unpack(char *payload, u8 *label_port, *slot_index = mlxsw_reg_pllp_slot_index_get(payload); } +/* PMTM - Port Module Type Mapping Register + * ---------------------------------------- + * The PMTM register allows query or configuration of module types. + * The register can only be set when the module is disabled by PMAOS register + */ +#define MLXSW_REG_PMTM_ID 0x5067 +#define MLXSW_REG_PMTM_LEN 0x10 + +MLXSW_REG_DEFINE(pmtm, MLXSW_REG_PMTM_ID, MLXSW_REG_PMTM_LEN); + +/* reg_pmtm_slot_index + * Slot index. + * Access: Index + */ +MLXSW_ITEM32(reg, pmtm, slot_index, 0x00, 24, 4); + +/* reg_pmtm_module + * Module number. + * Access: Index + */ +MLXSW_ITEM32(reg, pmtm, module, 0x00, 16, 8); + +enum mlxsw_reg_pmtm_module_type { + MLXSW_REG_PMTM_MODULE_TYPE_BACKPLANE_4_LANES = 0, + MLXSW_REG_PMTM_MODULE_TYPE_QSFP = 1, + MLXSW_REG_PMTM_MODULE_TYPE_SFP = 2, + MLXSW_REG_PMTM_MODULE_TYPE_BACKPLANE_SINGLE_LANE = 4, + MLXSW_REG_PMTM_MODULE_TYPE_BACKPLANE_2_LANES = 8, + MLXSW_REG_PMTM_MODULE_TYPE_CHIP2CHIP4X = 10, + MLXSW_REG_PMTM_MODULE_TYPE_CHIP2CHIP2X = 11, + MLXSW_REG_PMTM_MODULE_TYPE_CHIP2CHIP1X = 12, + MLXSW_REG_PMTM_MODULE_TYPE_QSFP_DD = 14, + MLXSW_REG_PMTM_MODULE_TYPE_OSFP = 15, + MLXSW_REG_PMTM_MODULE_TYPE_SFP_DD = 16, + MLXSW_REG_PMTM_MODULE_TYPE_DSFP = 17, + MLXSW_REG_PMTM_MODULE_TYPE_CHIP2CHIP8X = 18, + MLXSW_REG_PMTM_MODULE_TYPE_TWISTED_PAIR = 19, +}; + +/* reg_pmtm_module_type + * Module type. + * Access: RW + */ +MLXSW_ITEM32(reg, pmtm, module_type, 0x04, 0, 5); + +static inline void mlxsw_reg_pmtm_pack(char *payload, u8 slot_index, u8 module) +{ + MLXSW_REG_ZERO(pmtm, payload); + mlxsw_reg_pmtm_slot_index_set(payload, slot_index); + mlxsw_reg_pmtm_module_set(payload, module); +} + /* HTGT - Host Trap Group Table * ---------------------------- * Configures the properties for forwarding to CPU. @@ -6087,9 +6141,7 @@ MLXSW_ITEM32(reg, htgt, type, 0x00, 8, 4); enum mlxsw_reg_htgt_trap_group { MLXSW_REG_HTGT_TRAP_GROUP_EMAD, - MLXSW_REG_HTGT_TRAP_GROUP_MFDE, - MLXSW_REG_HTGT_TRAP_GROUP_MTWE, - MLXSW_REG_HTGT_TRAP_GROUP_PMPE, + MLXSW_REG_HTGT_TRAP_GROUP_CORE_EVENT, MLXSW_REG_HTGT_TRAP_GROUP_SP_STP, MLXSW_REG_HTGT_TRAP_GROUP_SP_LACP, MLXSW_REG_HTGT_TRAP_GROUP_SP_LLDP, @@ -6732,12 +6784,14 @@ static inline void mlxsw_reg_ritr_counter_pack(char *payload, u32 index, set_type = MLXSW_REG_RITR_COUNTER_SET_TYPE_BASIC; else set_type = MLXSW_REG_RITR_COUNTER_SET_TYPE_NO_COUNT; - mlxsw_reg_ritr_egress_counter_set_type_set(payload, set_type); - if (egress) + if (egress) { + mlxsw_reg_ritr_egress_counter_set_type_set(payload, set_type); mlxsw_reg_ritr_egress_counter_index_set(payload, index); - else + } else { + mlxsw_reg_ritr_ingress_counter_set_type_set(payload, set_type); mlxsw_reg_ritr_ingress_counter_index_set(payload, index); + } } static inline void mlxsw_reg_ritr_rif_pack(char *payload, u16 rif) @@ -9985,6 +10039,7 @@ enum mlxsw_reg_mcia_eeprom_module_info_id { MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS = 0x0D, MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 = 0x11, MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD = 0x18, + MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_OSFP = 0x19, }; enum mlxsw_reg_mcia_eeprom_module_info { @@ -11271,24 +11326,24 @@ enum mlxsw_reg_mgpir_device_type { MLXSW_REG_MGPIR_DEVICE_TYPE_GEARBOX_DIE, }; -/* device_type +/* mgpir_device_type * Access: RO */ MLXSW_ITEM32(reg, mgpir, device_type, 0x00, 24, 4); -/* devices_per_flash +/* mgpir_devices_per_flash * Number of devices of device_type per flash (can be shared by few devices). * Access: RO */ MLXSW_ITEM32(reg, mgpir, devices_per_flash, 0x00, 16, 8); -/* num_of_devices +/* mgpir_num_of_devices * Number of devices of device_type. * Access: RO */ MLXSW_ITEM32(reg, mgpir, num_of_devices, 0x00, 0, 8); -/* num_of_modules +/* mgpir_num_of_modules * Number of modules. * Access: RO */ @@ -12568,6 +12623,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(pddr), MLXSW_REG(pmmp), MLXSW_REG(pllp), + MLXSW_REG(pmtm), MLXSW_REG(htgt), MLXSW_REG(hpkt), MLXSW_REG(rgcr), diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h index c7fc650608eb..daacf6291253 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/resources.h +++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h @@ -33,6 +33,7 @@ enum mlxsw_res_id { MLXSW_RES_ID_ACL_MAX_REGIONS, MLXSW_RES_ID_ACL_MAX_GROUPS, MLXSW_RES_ID_ACL_MAX_GROUP_SIZE, + MLXSW_RES_ID_ACL_MAX_DEFAULT_ACTIONS, MLXSW_RES_ID_ACL_FLEX_KEYS, MLXSW_RES_ID_ACL_MAX_ACTION_PER_RULE, MLXSW_RES_ID_ACL_ACTIONS_PER_SET, @@ -90,6 +91,7 @@ static u16 mlxsw_res_ids[] = { [MLXSW_RES_ID_ACL_MAX_REGIONS] = 0x2903, [MLXSW_RES_ID_ACL_MAX_GROUPS] = 0x2904, [MLXSW_RES_ID_ACL_MAX_GROUP_SIZE] = 0x2905, + [MLXSW_RES_ID_ACL_MAX_DEFAULT_ACTIONS] = 0x2908, [MLXSW_RES_ID_ACL_FLEX_KEYS] = 0x2910, [MLXSW_RES_ID_ACL_MAX_ACTION_PER_RULE] = 0x2911, [MLXSW_RES_ID_ACL_ACTIONS_PER_SET] = 0x2912, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index aa411dec62f0..8eb05090ffec 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -45,52 +45,49 @@ #include "spectrum_ptp.h" #include "spectrum_trap.h" +#define MLXSW_SP_FWREV_MINOR 2010 +#define MLXSW_SP_FWREV_SUBMINOR 1006 + #define MLXSW_SP1_FWREV_MAJOR 13 -#define MLXSW_SP1_FWREV_MINOR 2010 -#define MLXSW_SP1_FWREV_SUBMINOR 1006 #define MLXSW_SP1_FWREV_CAN_RESET_MINOR 1702 static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = { .major = MLXSW_SP1_FWREV_MAJOR, - .minor = MLXSW_SP1_FWREV_MINOR, - .subminor = MLXSW_SP1_FWREV_SUBMINOR, + .minor = MLXSW_SP_FWREV_MINOR, + .subminor = MLXSW_SP_FWREV_SUBMINOR, .can_reset_minor = MLXSW_SP1_FWREV_CAN_RESET_MINOR, }; #define MLXSW_SP1_FW_FILENAME \ "mellanox/mlxsw_spectrum-" __stringify(MLXSW_SP1_FWREV_MAJOR) \ - "." __stringify(MLXSW_SP1_FWREV_MINOR) \ - "." __stringify(MLXSW_SP1_FWREV_SUBMINOR) ".mfa2" + "." __stringify(MLXSW_SP_FWREV_MINOR) \ + "." __stringify(MLXSW_SP_FWREV_SUBMINOR) ".mfa2" #define MLXSW_SP2_FWREV_MAJOR 29 -#define MLXSW_SP2_FWREV_MINOR 2010 -#define MLXSW_SP2_FWREV_SUBMINOR 1006 static const struct mlxsw_fw_rev mlxsw_sp2_fw_rev = { .major = MLXSW_SP2_FWREV_MAJOR, - .minor = MLXSW_SP2_FWREV_MINOR, - .subminor = MLXSW_SP2_FWREV_SUBMINOR, + .minor = MLXSW_SP_FWREV_MINOR, + .subminor = MLXSW_SP_FWREV_SUBMINOR, }; #define MLXSW_SP2_FW_FILENAME \ "mellanox/mlxsw_spectrum2-" __stringify(MLXSW_SP2_FWREV_MAJOR) \ - "." __stringify(MLXSW_SP2_FWREV_MINOR) \ - "." __stringify(MLXSW_SP2_FWREV_SUBMINOR) ".mfa2" + "." __stringify(MLXSW_SP_FWREV_MINOR) \ + "." __stringify(MLXSW_SP_FWREV_SUBMINOR) ".mfa2" #define MLXSW_SP3_FWREV_MAJOR 30 -#define MLXSW_SP3_FWREV_MINOR 2010 -#define MLXSW_SP3_FWREV_SUBMINOR 1006 static const struct mlxsw_fw_rev mlxsw_sp3_fw_rev = { .major = MLXSW_SP3_FWREV_MAJOR, - .minor = MLXSW_SP3_FWREV_MINOR, - .subminor = MLXSW_SP3_FWREV_SUBMINOR, + .minor = MLXSW_SP_FWREV_MINOR, + .subminor = MLXSW_SP_FWREV_SUBMINOR, }; #define MLXSW_SP3_FW_FILENAME \ "mellanox/mlxsw_spectrum3-" __stringify(MLXSW_SP3_FWREV_MAJOR) \ - "." __stringify(MLXSW_SP3_FWREV_MINOR) \ - "." __stringify(MLXSW_SP3_FWREV_SUBMINOR) ".mfa2" + "." __stringify(MLXSW_SP_FWREV_MINOR) \ + "." __stringify(MLXSW_SP_FWREV_SUBMINOR) ".mfa2" static const char mlxsw_sp1_driver_name[] = "mlxsw_spectrum"; static const char mlxsw_sp2_driver_name[] = "mlxsw_spectrum2"; @@ -2148,13 +2145,11 @@ static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg, struct mlxsw_sp *mlxsw_sp = priv; struct mlxsw_sp_port *mlxsw_sp_port; enum mlxsw_reg_pude_oper_status status; - unsigned int max_ports; u16 local_port; - max_ports = mlxsw_core_max_ports(mlxsw_sp->core); local_port = mlxsw_reg_pude_local_port_get(pude_pl); - if (WARN_ON_ONCE(!local_port || local_port >= max_ports)) + if (WARN_ON_ONCE(!mlxsw_sp_local_port_is_valid(mlxsw_sp, local_port))) return; mlxsw_sp_port = mlxsw_sp->ports[local_port]; if (!mlxsw_sp_port) @@ -2393,45 +2388,6 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) return 0; } -static int mlxsw_sp_traps_register(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_listener listeners[], - size_t listeners_count) -{ - int i; - int err; - - for (i = 0; i < listeners_count; i++) { - err = mlxsw_core_trap_register(mlxsw_sp->core, - &listeners[i], - mlxsw_sp); - if (err) - goto err_listener_register; - - } - return 0; - -err_listener_register: - for (i--; i >= 0; i--) { - mlxsw_core_trap_unregister(mlxsw_sp->core, - &listeners[i], - mlxsw_sp); - } - return err; -} - -static void mlxsw_sp_traps_unregister(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_listener listeners[], - size_t listeners_count) -{ - int i; - - for (i = 0; i < listeners_count; i++) { - mlxsw_core_trap_unregister(mlxsw_sp->core, - &listeners[i], - mlxsw_sp); - } -} - static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) { struct mlxsw_sp_trap *trap; @@ -2456,21 +2412,23 @@ static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) if (err) goto err_trap_groups_set; - err = mlxsw_sp_traps_register(mlxsw_sp, mlxsw_sp_listener, - ARRAY_SIZE(mlxsw_sp_listener)); + err = mlxsw_core_traps_register(mlxsw_sp->core, mlxsw_sp_listener, + ARRAY_SIZE(mlxsw_sp_listener), + mlxsw_sp); if (err) goto err_traps_register; - err = mlxsw_sp_traps_register(mlxsw_sp, mlxsw_sp->listeners, - mlxsw_sp->listeners_count); + err = mlxsw_core_traps_register(mlxsw_sp->core, mlxsw_sp->listeners, + mlxsw_sp->listeners_count, mlxsw_sp); if (err) goto err_extra_traps_init; return 0; err_extra_traps_init: - mlxsw_sp_traps_unregister(mlxsw_sp, mlxsw_sp_listener, - ARRAY_SIZE(mlxsw_sp_listener)); + mlxsw_core_traps_unregister(mlxsw_sp->core, mlxsw_sp_listener, + ARRAY_SIZE(mlxsw_sp_listener), + mlxsw_sp); err_traps_register: err_trap_groups_set: err_cpu_policers_set: @@ -2480,10 +2438,11 @@ static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp) { - mlxsw_sp_traps_unregister(mlxsw_sp, mlxsw_sp->listeners, - mlxsw_sp->listeners_count); - mlxsw_sp_traps_unregister(mlxsw_sp, mlxsw_sp_listener, - ARRAY_SIZE(mlxsw_sp_listener)); + mlxsw_core_traps_unregister(mlxsw_sp->core, mlxsw_sp->listeners, + mlxsw_sp->listeners_count, + mlxsw_sp); + mlxsw_core_traps_unregister(mlxsw_sp->core, mlxsw_sp_listener, + ARRAY_SIZE(mlxsw_sp_listener), mlxsw_sp); kfree(mlxsw_sp->trap); } @@ -2528,42 +2487,6 @@ static void mlxsw_sp_lag_fini(struct mlxsw_sp *mlxsw_sp) kfree(mlxsw_sp->lags); } -static int mlxsw_sp_basic_trap_groups_set(struct mlxsw_core *mlxsw_core) -{ - char htgt_pl[MLXSW_REG_HTGT_LEN]; - int err; - - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD, - MLXSW_REG_HTGT_INVALID_POLICER, - MLXSW_REG_HTGT_DEFAULT_PRIORITY, - MLXSW_REG_HTGT_DEFAULT_TC); - err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); - if (err) - return err; - - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_MFDE, - MLXSW_REG_HTGT_INVALID_POLICER, - MLXSW_REG_HTGT_DEFAULT_PRIORITY, - MLXSW_REG_HTGT_DEFAULT_TC); - err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); - if (err) - return err; - - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_MTWE, - MLXSW_REG_HTGT_INVALID_POLICER, - MLXSW_REG_HTGT_DEFAULT_PRIORITY, - MLXSW_REG_HTGT_DEFAULT_TC); - err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); - if (err) - return err; - - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_PMPE, - MLXSW_REG_HTGT_INVALID_POLICER, - MLXSW_REG_HTGT_DEFAULT_PRIORITY, - MLXSW_REG_HTGT_DEFAULT_TC); - return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); -} - static const struct mlxsw_sp_ptp_ops mlxsw_sp1_ptp_ops = { .clock_init = mlxsw_sp1_ptp_clock_init, .clock_fini = mlxsw_sp1_ptp_clock_fini, @@ -2895,6 +2818,7 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, struct netlink_ext_ack *extack) { struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); + struct devlink *devlink = priv_to_devlink(mlxsw_core); int err; mlxsw_sp->core = mlxsw_core; @@ -3055,7 +2979,9 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, goto err_sample_trigger_init; } + devl_lock(devlink); err = mlxsw_sp_ports_create(mlxsw_sp); + devl_unlock(devlink); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n"); goto err_ports_create; @@ -3236,8 +3162,12 @@ static int mlxsw_sp4_init(struct mlxsw_core *mlxsw_core, static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) { struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); + struct devlink *devlink = priv_to_devlink(mlxsw_core); + devl_lock(devlink); mlxsw_sp_ports_remove(mlxsw_sp); + devl_unlock(devlink); + rhashtable_destroy(&mlxsw_sp->sample_trigger_ht); mlxsw_sp_port_module_info_fini(mlxsw_sp); mlxsw_sp_dpipe_fini(mlxsw_sp); @@ -3677,7 +3607,6 @@ static struct mlxsw_driver mlxsw_sp1_driver = { .fw_filename = MLXSW_SP1_FW_FILENAME, .init = mlxsw_sp1_init, .fini = mlxsw_sp_fini, - .basic_trap_groups_set = mlxsw_sp_basic_trap_groups_set, .port_split = mlxsw_sp_port_split, .port_unsplit = mlxsw_sp_port_unsplit, .sb_pool_get = mlxsw_sp_sb_pool_get, @@ -3705,9 +3634,6 @@ static struct mlxsw_driver mlxsw_sp1_driver = { .ptp_transmitted = mlxsw_sp_ptp_transmitted, .txhdr_len = MLXSW_TXHDR_LEN, .profile = &mlxsw_sp1_config_profile, - .res_query_enabled = true, - .fw_fatal_enabled = true, - .temp_warn_enabled = true, }; static struct mlxsw_driver mlxsw_sp2_driver = { @@ -3717,7 +3643,6 @@ static struct mlxsw_driver mlxsw_sp2_driver = { .fw_filename = MLXSW_SP2_FW_FILENAME, .init = mlxsw_sp2_init, .fini = mlxsw_sp_fini, - .basic_trap_groups_set = mlxsw_sp_basic_trap_groups_set, .port_split = mlxsw_sp_port_split, .port_unsplit = mlxsw_sp_port_unsplit, .sb_pool_get = mlxsw_sp_sb_pool_get, @@ -3746,9 +3671,6 @@ static struct mlxsw_driver mlxsw_sp2_driver = { .ptp_transmitted = mlxsw_sp_ptp_transmitted, .txhdr_len = MLXSW_TXHDR_LEN, .profile = &mlxsw_sp2_config_profile, - .res_query_enabled = true, - .fw_fatal_enabled = true, - .temp_warn_enabled = true, }; static struct mlxsw_driver mlxsw_sp3_driver = { @@ -3758,7 +3680,6 @@ static struct mlxsw_driver mlxsw_sp3_driver = { .fw_filename = MLXSW_SP3_FW_FILENAME, .init = mlxsw_sp3_init, .fini = mlxsw_sp_fini, - .basic_trap_groups_set = mlxsw_sp_basic_trap_groups_set, .port_split = mlxsw_sp_port_split, .port_unsplit = mlxsw_sp_port_unsplit, .sb_pool_get = mlxsw_sp_sb_pool_get, @@ -3787,9 +3708,6 @@ static struct mlxsw_driver mlxsw_sp3_driver = { .ptp_transmitted = mlxsw_sp_ptp_transmitted, .txhdr_len = MLXSW_TXHDR_LEN, .profile = &mlxsw_sp2_config_profile, - .res_query_enabled = true, - .fw_fatal_enabled = true, - .temp_warn_enabled = true, }; static struct mlxsw_driver mlxsw_sp4_driver = { @@ -3797,7 +3715,6 @@ static struct mlxsw_driver mlxsw_sp4_driver = { .priv_size = sizeof(struct mlxsw_sp), .init = mlxsw_sp4_init, .fini = mlxsw_sp_fini, - .basic_trap_groups_set = mlxsw_sp_basic_trap_groups_set, .port_split = mlxsw_sp_port_split, .port_unsplit = mlxsw_sp_port_unsplit, .sb_pool_get = mlxsw_sp_sb_pool_get, @@ -3826,9 +3743,6 @@ static struct mlxsw_driver mlxsw_sp4_driver = { .ptp_transmitted = mlxsw_sp_ptp_transmitted, .txhdr_len = MLXSW_TXHDR_LEN, .profile = &mlxsw_sp2_config_profile, - .res_query_enabled = true, - .fw_fatal_enabled = true, - .temp_warn_enabled = true, }; bool mlxsw_sp_port_dev_check(const struct net_device *dev) @@ -4916,6 +4830,22 @@ static int mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp *mlxsw_sp, return 0; } +static bool mlxsw_sp_netdevice_event_is_router(unsigned long event) +{ + switch (event) { + case NETDEV_PRE_CHANGEADDR: + case NETDEV_CHANGEADDR: + case NETDEV_CHANGEMTU: + case NETDEV_OFFLOAD_XSTATS_ENABLE: + case NETDEV_OFFLOAD_XSTATS_DISABLE: + case NETDEV_OFFLOAD_XSTATS_REPORT_USED: + case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA: + return true; + default: + return false; + } +} + static int mlxsw_sp_netdevice_event(struct notifier_block *nb, unsigned long event, void *ptr) { @@ -4940,9 +4870,7 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *nb, else if (mlxsw_sp_netdev_is_ipip_ul(mlxsw_sp, dev)) err = mlxsw_sp_netdevice_ipip_ul_event(mlxsw_sp, dev, event, ptr); - else if (event == NETDEV_PRE_CHANGEADDR || - event == NETDEV_CHANGEADDR || - event == NETDEV_CHANGEMTU) + else if (mlxsw_sp_netdevice_event_is_router(event)) err = mlxsw_sp_netdevice_router_port_event(dev, event, ptr); else if (mlxsw_sp_is_vrf_event(event, ptr)) err = mlxsw_sp_netdevice_vrf_event(dev, event, ptr); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index bb2442e1f705..20588e699588 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -481,6 +481,13 @@ int mlxsw_sp_port_vlan_classification_set(struct mlxsw_sp_port *mlxsw_sp_port, bool is_8021ad_tagged, bool is_8021q_tagged); +static inline bool +mlxsw_sp_local_port_is_valid(struct mlxsw_sp *mlxsw_sp, u16 local_port) +{ + unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); + + return local_port < max_ports && local_port; +} /* spectrum_buffers.c */ struct mlxsw_sp_hdroom_prio { @@ -813,6 +820,24 @@ int mlxsw_sp1_kvdl_resources_register(struct mlxsw_core *mlxsw_core); /* spectrum2_kvdl.c */ extern const struct mlxsw_sp_kvdl_ops mlxsw_sp2_kvdl_ops; +enum mlxsw_sp_acl_mangle_field { + MLXSW_SP_ACL_MANGLE_FIELD_IP_DSFIELD, + MLXSW_SP_ACL_MANGLE_FIELD_IP_DSCP, + MLXSW_SP_ACL_MANGLE_FIELD_IP_ECN, + MLXSW_SP_ACL_MANGLE_FIELD_IP_SPORT, + MLXSW_SP_ACL_MANGLE_FIELD_IP_DPORT, + MLXSW_SP_ACL_MANGLE_FIELD_IP4_SIP, + MLXSW_SP_ACL_MANGLE_FIELD_IP4_DIP, + MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_1, + MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_2, + MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_3, + MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_4, + MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_1, + MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_2, + MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_3, + MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_4, +}; + struct mlxsw_sp_acl_rule_info { unsigned int priority; struct mlxsw_afk_element_values values; @@ -821,9 +846,14 @@ struct mlxsw_sp_acl_rule_info { ingress_bind_blocker:1, egress_bind_blocker:1, counter_valid:1, - policer_index_valid:1; + policer_index_valid:1, + ipv6_valid:1; unsigned int counter_index; u16 policer_index; + struct { + u32 prev_val; + enum mlxsw_sp_acl_mangle_field prev_field; + } ipv6; }; /* spectrum_flow.c */ diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum1_kvdl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum1_kvdl.c index a9fff8adc75e..d20e794e01ca 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum1_kvdl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum1_kvdl.c @@ -213,7 +213,6 @@ mlxsw_sp1_kvdl_part_init(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp1_kvdl_part *part; bool need_update = true; unsigned int nr_entries; - size_t usage_size; u64 resource_size; int err; @@ -225,8 +224,8 @@ mlxsw_sp1_kvdl_part_init(struct mlxsw_sp *mlxsw_sp, } nr_entries = div_u64(resource_size, info->alloc_size); - usage_size = BITS_TO_LONGS(nr_entries) * sizeof(unsigned long); - part = kzalloc(sizeof(*part) + usage_size, GFP_KERNEL); + part = kzalloc(struct_size(part, usage, BITS_TO_LONGS(nr_entries)), + GFP_KERNEL); if (!part) return ERR_PTR(-ENOMEM); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum2_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum2_acl_tcam.c index ad69913f19c1..5b0210862655 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum2_acl_tcam.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum2_acl_tcam.c @@ -77,7 +77,14 @@ static int mlxsw_sp2_acl_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv, int i; int err; + /* Some TCAM regions are not exposed to the host and used internally + * by the device. Allocate KVDL entries for the default actions of + * these regions to avoid the host from overwriting them. + */ tcam->kvdl_count = _tcam->max_regions; + if (MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_DEFAULT_ACTIONS)) + tcam->kvdl_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, + ACL_MAX_DEFAULT_ACTIONS); err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ACTSET, tcam->kvdl_count, &tcam->kvdl_index); if (err) @@ -97,7 +104,10 @@ static int mlxsw_sp2_acl_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv, goto err_afa_block_continue; enc_actions = mlxsw_afa_block_cur_set(afa_block); - for (i = 0; i < tcam->kvdl_count; i++) { + /* Only write to KVDL entries used by TCAM regions exposed to the + * host. + */ + for (i = 0; i < _tcam->max_regions; i++) { mlxsw_reg_pefa_pack(pefa_pl, tcam->kvdl_index + i, true, enc_actions); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pefa), pefa_pl); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 70c11bfac08f..6c5af018546f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -505,14 +505,6 @@ int mlxsw_sp_acl_rulei_act_priority(struct mlxsw_sp *mlxsw_sp, extack); } -enum mlxsw_sp_acl_mangle_field { - MLXSW_SP_ACL_MANGLE_FIELD_IP_DSFIELD, - MLXSW_SP_ACL_MANGLE_FIELD_IP_DSCP, - MLXSW_SP_ACL_MANGLE_FIELD_IP_ECN, - MLXSW_SP_ACL_MANGLE_FIELD_IP_SPORT, - MLXSW_SP_ACL_MANGLE_FIELD_IP_DPORT, -}; - struct mlxsw_sp_acl_mangle_action { enum flow_action_mangle_base htype; /* Offset is u32-aligned. */ @@ -561,6 +553,18 @@ static struct mlxsw_sp_acl_mangle_action mlxsw_sp_acl_mangle_actions[] = { MLXSW_SP_ACL_MANGLE_ACTION_UDP(0, 0x0000ffff, 16, IP_SPORT), MLXSW_SP_ACL_MANGLE_ACTION_UDP(0, 0xffff0000, 0, IP_DPORT), + + MLXSW_SP_ACL_MANGLE_ACTION_IP4(12, 0x00000000, 0, IP4_SIP), + MLXSW_SP_ACL_MANGLE_ACTION_IP4(16, 0x00000000, 0, IP4_DIP), + + MLXSW_SP_ACL_MANGLE_ACTION_IP6(8, 0x00000000, 0, IP6_SIP_1), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(12, 0x00000000, 0, IP6_SIP_2), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(16, 0x00000000, 0, IP6_SIP_3), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(20, 0x00000000, 0, IP6_SIP_4), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(24, 0x00000000, 0, IP6_DIP_1), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(28, 0x00000000, 0, IP6_DIP_2), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(32, 0x00000000, 0, IP6_DIP_3), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(36, 0x00000000, 0, IP6_DIP_4), }; static int @@ -599,6 +603,22 @@ static int mlxsw_sp1_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp, return err; } +static int +mlxsw_sp2_acl_rulei_act_mangle_field_ip_odd(struct mlxsw_sp_acl_rule_info *rulei, + enum mlxsw_sp_acl_mangle_field field, + u32 val, struct netlink_ext_ack *extack) +{ + if (!rulei->ipv6_valid) { + rulei->ipv6.prev_val = val; + rulei->ipv6_valid = true; + rulei->ipv6.prev_field = field; + return 0; + } + + NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field order"); + return -EOPNOTSUPP; +} + static int mlxsw_sp2_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_rule_info *rulei, struct mlxsw_sp_acl_mangle_action *mact, @@ -615,6 +635,61 @@ static int mlxsw_sp2_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp, return mlxsw_afa_block_append_l4port(rulei->act_block, false, val, extack); case MLXSW_SP_ACL_MANGLE_FIELD_IP_DPORT: return mlxsw_afa_block_append_l4port(rulei->act_block, true, val, extack); + /* IPv4 fields */ + case MLXSW_SP_ACL_MANGLE_FIELD_IP4_SIP: + return mlxsw_afa_block_append_ip(rulei->act_block, false, + true, val, 0, extack); + case MLXSW_SP_ACL_MANGLE_FIELD_IP4_DIP: + return mlxsw_afa_block_append_ip(rulei->act_block, true, + true, val, 0, extack); + /* IPv6 fields */ + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_1: + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_3: + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_1: + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_3: + return mlxsw_sp2_acl_rulei_act_mangle_field_ip_odd(rulei, + mact->field, + val, extack); + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_2: + if (rulei->ipv6_valid && + rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_1) { + rulei->ipv6_valid = false; + return mlxsw_afa_block_append_ip(rulei->act_block, + false, false, val, + rulei->ipv6.prev_val, + extack); + } + break; + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_4: + if (rulei->ipv6_valid && + rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_3) { + rulei->ipv6_valid = false; + return mlxsw_afa_block_append_ip(rulei->act_block, + false, true, val, + rulei->ipv6.prev_val, + extack); + } + break; + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_2: + if (rulei->ipv6_valid && + rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_1) { + rulei->ipv6_valid = false; + return mlxsw_afa_block_append_ip(rulei->act_block, + true, false, val, + rulei->ipv6.prev_val, + extack); + } + break; + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_4: + if (rulei->ipv6_valid && + rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_3) { + rulei->ipv6_valid = false; + return mlxsw_afa_block_append_ip(rulei->act_block, + true, true, val, + rulei->ipv6.prev_val, + extack); + } + break; default: break; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 1a2fef2a5379..5d494fabf93d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -266,10 +266,10 @@ static int mlxsw_sp_dpipe_table_erif_counters_update(void *priv, bool enable) if (!rif) continue; if (enable) - mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, + mlxsw_sp_rif_counter_alloc(rif, MLXSW_SP_RIF_COUNTER_EGRESS); else - mlxsw_sp_rif_counter_free(mlxsw_sp, rif, + mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_EGRESS); } mutex_unlock(&mlxsw_sp->router->lock); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c index 20530712eadb..8b5d7f83b9b0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c @@ -1034,13 +1034,10 @@ static int mlxsw_sp_get_module_info(struct net_device *netdev, { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev); struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - int err; - err = mlxsw_env_get_module_info(mlxsw_sp->core, - mlxsw_sp_port->mapping.module, - modinfo); - - return err; + return mlxsw_env_get_module_info(netdev, mlxsw_sp->core, + mlxsw_sp_port->mapping.module, + modinfo); } static int mlxsw_sp_get_module_eeprom(struct net_device *netdev, @@ -1048,13 +1045,10 @@ static int mlxsw_sp_get_module_eeprom(struct net_device *netdev, { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev); struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - int err; - err = mlxsw_env_get_module_eeprom(netdev, mlxsw_sp->core, - mlxsw_sp_port->mapping.module, ee, - data); - - return err; + return mlxsw_env_get_module_eeprom(netdev, mlxsw_sp->core, + mlxsw_sp_port->mapping.module, ee, + data); } static int @@ -1272,12 +1266,22 @@ struct mlxsw_sp1_port_link_mode { }; static const struct mlxsw_sp1_port_link_mode mlxsw_sp1_port_link_mode[] = { + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_100BASE_T, + .mask_ethtool = ETHTOOL_LINK_MODE_100baseT_Full_BIT, + .speed = SPEED_100, + }, { .mask = MLXSW_REG_PTYS_ETH_SPEED_SGMII | MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX, .mask_ethtool = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, .speed = SPEED_1000, }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_1000BASE_T, + .mask_ethtool = ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + .speed = SPEED_1000, + }, { .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4 | MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index bb417db773b9..e91fb205e0b4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -15,6 +15,46 @@ #include "spectrum.h" #include "core_acl_flex_keys.h" +static int mlxsw_sp_policer_validate(const struct flow_action *action, + const struct flow_action_entry *act, + struct netlink_ext_ack *extack) +{ + if (act->police.exceed.act_id != FLOW_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when exceed action is not drop"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && + act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not pipe or ok"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && + !flow_action_is_last_entry(action, act)) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is ok, but action is not last"); + return -EOPNOTSUPP; + } + + if (act->police.peakrate_bytes_ps || + act->police.avrate || act->police.overhead) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when peakrate/avrate/overhead is configured"); + return -EOPNOTSUPP; + } + + if (act->police.rate_pkt_ps) { + NL_SET_ERR_MSG_MOD(extack, + "QoS offload not support packets per second"); + return -EOPNOTSUPP; + } + + return 0; +} + static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_flow_block *block, struct mlxsw_sp_acl_rule_info *rulei, @@ -191,10 +231,9 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, return -EOPNOTSUPP; } - if (act->police.rate_pkt_ps) { - NL_SET_ERR_MSG_MOD(extack, "QoS offload not support packets per second"); - return -EOPNOTSUPP; - } + err = mlxsw_sp_policer_validate(flow_action, act, extack); + if (err) + return err; /* The kernel might adjust the requested burst size so * that it is not exactly a power of two. Re-adjust it @@ -233,6 +272,12 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, return -EOPNOTSUPP; } } + + if (rulei->ipv6_valid) { + NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field"); + return -EOPNOTSUPP; + } + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c index 0ff163fbc775..35422e64d89f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c @@ -568,12 +568,11 @@ void mlxsw_sp1_ptp_got_timestamp(struct mlxsw_sp *mlxsw_sp, bool ingress, u8 domain_number, u16 sequence_id, u64 timestamp) { - unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); struct mlxsw_sp_port *mlxsw_sp_port; struct mlxsw_sp1_ptp_key key; u8 types; - if (WARN_ON_ONCE(local_port >= max_ports)) + if (WARN_ON_ONCE(!mlxsw_sp_local_port_is_valid(mlxsw_sp, local_port))) return; mlxsw_sp_port = mlxsw_sp->ports[local_port]; if (!mlxsw_sp_port) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index d40762cfc453..79deb19e3a19 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -225,6 +225,64 @@ int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp, return 0; } +struct mlxsw_sp_rif_counter_set_basic { + u64 good_unicast_packets; + u64 good_multicast_packets; + u64 good_broadcast_packets; + u64 good_unicast_bytes; + u64 good_multicast_bytes; + u64 good_broadcast_bytes; + u64 error_packets; + u64 discard_packets; + u64 error_bytes; + u64 discard_bytes; +}; + +static int +mlxsw_sp_rif_counter_fetch_clear(struct mlxsw_sp_rif *rif, + enum mlxsw_sp_rif_counter_dir dir, + struct mlxsw_sp_rif_counter_set_basic *set) +{ + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + char ricnt_pl[MLXSW_REG_RICNT_LEN]; + unsigned int *p_counter_index; + int err; + + if (!mlxsw_sp_rif_counter_valid_get(rif, dir)) + return -EINVAL; + + p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir); + if (!p_counter_index) + return -EINVAL; + + mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index, + MLXSW_REG_RICNT_OPCODE_CLEAR); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl); + if (err) + return err; + + if (!set) + return 0; + +#define MLXSW_SP_RIF_COUNTER_EXTRACT(NAME) \ + (set->NAME = mlxsw_reg_ricnt_ ## NAME ## _get(ricnt_pl)) + + MLXSW_SP_RIF_COUNTER_EXTRACT(good_unicast_packets); + MLXSW_SP_RIF_COUNTER_EXTRACT(good_multicast_packets); + MLXSW_SP_RIF_COUNTER_EXTRACT(good_broadcast_packets); + MLXSW_SP_RIF_COUNTER_EXTRACT(good_unicast_bytes); + MLXSW_SP_RIF_COUNTER_EXTRACT(good_multicast_bytes); + MLXSW_SP_RIF_COUNTER_EXTRACT(good_broadcast_bytes); + MLXSW_SP_RIF_COUNTER_EXTRACT(error_packets); + MLXSW_SP_RIF_COUNTER_EXTRACT(discard_packets); + MLXSW_SP_RIF_COUNTER_EXTRACT(error_bytes); + MLXSW_SP_RIF_COUNTER_EXTRACT(discard_bytes); + +#undef MLXSW_SP_RIF_COUNTER_EXTRACT + + return 0; +} + static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp, unsigned int counter_index) { @@ -235,16 +293,20 @@ static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl); } -int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *rif, +int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp_rif *rif, enum mlxsw_sp_rif_counter_dir dir) { + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; unsigned int *p_counter_index; int err; + if (mlxsw_sp_rif_counter_valid_get(rif, dir)) + return 0; + p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir); if (!p_counter_index) return -EINVAL; + err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF, p_counter_index); if (err) @@ -268,10 +330,10 @@ int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp, return err; } -void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *rif, +void mlxsw_sp_rif_counter_free(struct mlxsw_sp_rif *rif, enum mlxsw_sp_rif_counter_dir dir) { + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; unsigned int *p_counter_index; if (!mlxsw_sp_rif_counter_valid_get(rif, dir)) @@ -296,14 +358,12 @@ static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif) if (!devlink_dpipe_table_counter_enabled(devlink, MLXSW_SP_DPIPE_TABLE_NAME_ERIF)) return; - mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS); + mlxsw_sp_rif_counter_alloc(rif, MLXSW_SP_RIF_COUNTER_EGRESS); } static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif) { - struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; - - mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS); + mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_EGRESS); } #define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1) @@ -8148,6 +8208,166 @@ u16 mlxsw_sp_ipip_lb_ul_rif_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif) return lb_rif->ul_rif_id; } +static bool +mlxsw_sp_router_port_l3_stats_enabled(struct mlxsw_sp_rif *rif) +{ + return mlxsw_sp_rif_counter_valid_get(rif, + MLXSW_SP_RIF_COUNTER_EGRESS) && + mlxsw_sp_rif_counter_valid_get(rif, + MLXSW_SP_RIF_COUNTER_INGRESS); +} + +static int +mlxsw_sp_router_port_l3_stats_enable(struct mlxsw_sp_rif *rif) +{ + int err; + + err = mlxsw_sp_rif_counter_alloc(rif, MLXSW_SP_RIF_COUNTER_INGRESS); + if (err) + return err; + + /* Clear stale data. */ + err = mlxsw_sp_rif_counter_fetch_clear(rif, + MLXSW_SP_RIF_COUNTER_INGRESS, + NULL); + if (err) + goto err_clear_ingress; + + err = mlxsw_sp_rif_counter_alloc(rif, MLXSW_SP_RIF_COUNTER_EGRESS); + if (err) + goto err_alloc_egress; + + /* Clear stale data. */ + err = mlxsw_sp_rif_counter_fetch_clear(rif, + MLXSW_SP_RIF_COUNTER_EGRESS, + NULL); + if (err) + goto err_clear_egress; + + return 0; + +err_clear_egress: + mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_EGRESS); +err_alloc_egress: +err_clear_ingress: + mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_INGRESS); + return err; +} + +static void +mlxsw_sp_router_port_l3_stats_disable(struct mlxsw_sp_rif *rif) +{ + mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_EGRESS); + mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_INGRESS); +} + +static void +mlxsw_sp_router_port_l3_stats_report_used(struct mlxsw_sp_rif *rif, + struct netdev_notifier_offload_xstats_info *info) +{ + if (!mlxsw_sp_router_port_l3_stats_enabled(rif)) + return; + netdev_offload_xstats_report_used(info->report_used); +} + +static int +mlxsw_sp_router_port_l3_stats_fetch(struct mlxsw_sp_rif *rif, + struct rtnl_hw_stats64 *p_stats) +{ + struct mlxsw_sp_rif_counter_set_basic ingress; + struct mlxsw_sp_rif_counter_set_basic egress; + int err; + + err = mlxsw_sp_rif_counter_fetch_clear(rif, + MLXSW_SP_RIF_COUNTER_INGRESS, + &ingress); + if (err) + return err; + + err = mlxsw_sp_rif_counter_fetch_clear(rif, + MLXSW_SP_RIF_COUNTER_EGRESS, + &egress); + if (err) + return err; + +#define MLXSW_SP_ROUTER_ALL_GOOD(SET, SFX) \ + ((SET.good_unicast_ ## SFX) + \ + (SET.good_multicast_ ## SFX) + \ + (SET.good_broadcast_ ## SFX)) + + p_stats->rx_packets = MLXSW_SP_ROUTER_ALL_GOOD(ingress, packets); + p_stats->tx_packets = MLXSW_SP_ROUTER_ALL_GOOD(egress, packets); + p_stats->rx_bytes = MLXSW_SP_ROUTER_ALL_GOOD(ingress, bytes); + p_stats->tx_bytes = MLXSW_SP_ROUTER_ALL_GOOD(egress, bytes); + p_stats->rx_errors = ingress.error_packets; + p_stats->tx_errors = egress.error_packets; + p_stats->rx_dropped = ingress.discard_packets; + p_stats->tx_dropped = egress.discard_packets; + p_stats->multicast = ingress.good_multicast_packets + + ingress.good_broadcast_packets; + +#undef MLXSW_SP_ROUTER_ALL_GOOD + + return 0; +} + +static int +mlxsw_sp_router_port_l3_stats_report_delta(struct mlxsw_sp_rif *rif, + struct netdev_notifier_offload_xstats_info *info) +{ + struct rtnl_hw_stats64 stats = {}; + int err; + + if (!mlxsw_sp_router_port_l3_stats_enabled(rif)) + return 0; + + err = mlxsw_sp_router_port_l3_stats_fetch(rif, &stats); + if (err) + return err; + + netdev_offload_xstats_report_delta(info->report_delta, &stats); + return 0; +} + +struct mlxsw_sp_router_hwstats_notify_work { + struct work_struct work; + struct net_device *dev; +}; + +static void mlxsw_sp_router_hwstats_notify_work(struct work_struct *work) +{ + struct mlxsw_sp_router_hwstats_notify_work *hws_work = + container_of(work, struct mlxsw_sp_router_hwstats_notify_work, + work); + + rtnl_lock(); + rtnl_offload_xstats_notify(hws_work->dev); + rtnl_unlock(); + dev_put(hws_work->dev); + kfree(hws_work); +} + +static void +mlxsw_sp_router_hwstats_notify_schedule(struct net_device *dev) +{ + struct mlxsw_sp_router_hwstats_notify_work *hws_work; + + /* To collect notification payload, the core ends up sending another + * notifier block message, which would deadlock on the attempt to + * acquire the router lock again. Just postpone the notification until + * later. + */ + + hws_work = kzalloc(sizeof(*hws_work), GFP_KERNEL); + if (!hws_work) + return; + + INIT_WORK(&hws_work->work, mlxsw_sp_router_hwstats_notify_work); + dev_hold(dev); + hws_work->dev = dev; + mlxsw_core_schedule_work(&hws_work->work); +} + int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif) { return rif->dev->ifindex; @@ -8158,6 +8378,16 @@ const struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif) return rif->dev; } +static void mlxsw_sp_rif_push_l3_stats(struct mlxsw_sp_rif *rif) +{ + struct rtnl_hw_stats64 stats = {}; + + if (!mlxsw_sp_router_port_l3_stats_fetch(rif, &stats)) + netdev_offload_xstats_push_delta(rif->dev, + NETDEV_OFFLOAD_XSTATS_TYPE_L3, + &stats); +} + static struct mlxsw_sp_rif * mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, const struct mlxsw_sp_rif_params *params, @@ -8218,10 +8448,19 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, goto err_mr_rif_add; } - mlxsw_sp_rif_counters_alloc(rif); + if (netdev_offload_xstats_enabled(rif->dev, + NETDEV_OFFLOAD_XSTATS_TYPE_L3)) { + err = mlxsw_sp_router_port_l3_stats_enable(rif); + if (err) + goto err_stats_enable; + mlxsw_sp_router_hwstats_notify_schedule(rif->dev); + } else { + mlxsw_sp_rif_counters_alloc(rif); + } return rif; +err_stats_enable: err_mr_rif_add: for (i--; i >= 0; i--) mlxsw_sp_mr_rif_del(vr->mr_table[i], rif); @@ -8251,7 +8490,15 @@ static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif) mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif); vr = &mlxsw_sp->router->vrs[rif->vr_id]; - mlxsw_sp_rif_counters_free(rif); + if (netdev_offload_xstats_enabled(rif->dev, + NETDEV_OFFLOAD_XSTATS_TYPE_L3)) { + mlxsw_sp_rif_push_l3_stats(rif); + mlxsw_sp_router_port_l3_stats_disable(rif); + mlxsw_sp_router_hwstats_notify_schedule(rif->dev); + } else { + mlxsw_sp_rif_counters_free(rif); + } + for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++) mlxsw_sp_mr_rif_del(vr->mr_table[i], rif); ops->deconfigure(rif); @@ -9128,6 +9375,35 @@ static int mlxsw_sp_router_port_pre_changeaddr_event(struct mlxsw_sp_rif *rif, return -ENOBUFS; } +static int +mlxsw_sp_router_port_offload_xstats_cmd(struct mlxsw_sp_rif *rif, + unsigned long event, + struct netdev_notifier_offload_xstats_info *info) +{ + switch (info->type) { + case NETDEV_OFFLOAD_XSTATS_TYPE_L3: + break; + default: + return 0; + } + + switch (event) { + case NETDEV_OFFLOAD_XSTATS_ENABLE: + return mlxsw_sp_router_port_l3_stats_enable(rif); + case NETDEV_OFFLOAD_XSTATS_DISABLE: + mlxsw_sp_router_port_l3_stats_disable(rif); + return 0; + case NETDEV_OFFLOAD_XSTATS_REPORT_USED: + mlxsw_sp_router_port_l3_stats_report_used(rif, info); + return 0; + case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA: + return mlxsw_sp_router_port_l3_stats_report_delta(rif, info); + } + + WARN_ON_ONCE(1); + return 0; +} + int mlxsw_sp_netdevice_router_port_event(struct net_device *dev, unsigned long event, void *ptr) { @@ -9153,6 +9429,15 @@ int mlxsw_sp_netdevice_router_port_event(struct net_device *dev, case NETDEV_PRE_CHANGEADDR: err = mlxsw_sp_router_port_pre_changeaddr_event(rif, ptr); break; + case NETDEV_OFFLOAD_XSTATS_ENABLE: + case NETDEV_OFFLOAD_XSTATS_DISABLE: + case NETDEV_OFFLOAD_XSTATS_REPORT_USED: + case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA: + err = mlxsw_sp_router_port_offload_xstats_cmd(rif, event, ptr); + break; + default: + WARN_ON_ONCE(1); + break; } out: diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index 99e8371a82a5..fa829658a11b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -159,11 +159,9 @@ int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_rif *rif, enum mlxsw_sp_rif_counter_dir dir, u64 *cnt); -void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *rif, +void mlxsw_sp_rif_counter_free(struct mlxsw_sp_rif *rif, enum mlxsw_sp_rif_counter_dir dir); -int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *rif, +int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp_rif *rif, enum mlxsw_sp_rif_counter_dir dir); struct mlxsw_sp_neigh_entry * mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c index f9671cc53002..b73466470f75 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c @@ -269,8 +269,7 @@ mlxsw_sp_span_entry_bridge_8021q(const struct net_device *br_dev, if (!vid && WARN_ON(br_vlan_get_pvid(br_dev, &vid))) return NULL; - if (!vid || - br_vlan_get_info(br_dev, vid, &vinfo) || + if (!vid || br_vlan_get_info(br_dev, vid, &vinfo) || !(vinfo.flags & BRIDGE_VLAN_INFO_BRENTRY)) return NULL; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 65c1724c63b0..3bf12092a8a2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -1234,8 +1234,7 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, if (netif_is_bridge_master(orig_dev)) { int err = 0; - if ((vlan->flags & BRIDGE_VLAN_INFO_BRENTRY) && - br_vlan_enabled(orig_dev)) + if (br_vlan_enabled(orig_dev)) err = mlxsw_sp_br_ban_rif_pvid_change(mlxsw_sp, orig_dev, vlan); if (!err) @@ -2616,7 +2615,6 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, char *sfn_pl, int rec_index, bool adding) { - unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; struct mlxsw_sp_bridge_device *bridge_device; struct mlxsw_sp_bridge_port *bridge_port; @@ -2630,7 +2628,7 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &fid, &local_port); - if (WARN_ON_ONCE(local_port >= max_ports)) + if (WARN_ON_ONCE(!mlxsw_sp_local_port_is_valid(mlxsw_sp, local_port))) return; mlxsw_sp_port = mlxsw_sp->ports[local_port]; if (!mlxsw_sp_port) { diff --git a/drivers/net/ethernet/micrel/ks8851_spi.c b/drivers/net/ethernet/micrel/ks8851_spi.c index d167d93e4c12..82d55fc27edc 100644 --- a/drivers/net/ethernet/micrel/ks8851_spi.c +++ b/drivers/net/ethernet/micrel/ks8851_spi.c @@ -293,7 +293,7 @@ static void ks8851_wrfifo_spi(struct ks8851_net *ks, struct sk_buff *txp, */ static void ks8851_rx_skb_spi(struct ks8851_net *ks, struct sk_buff *skb) { - netif_rx_ni(skb); + netif_rx(skb); } /** diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index d024983815da..2b3eb5ed8233 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -5225,7 +5225,6 @@ static irqreturn_t netdev_intr(int irq, void *dev_id) * Linux network device functions */ -static unsigned long next_jiffies; #ifdef CONFIG_NET_POLL_CONTROLLER static void netdev_netpoll(struct net_device *dev) @@ -5411,10 +5410,12 @@ static int netdev_open(struct net_device *dev) struct dev_info *hw_priv = priv->adapter; struct ksz_hw *hw = &hw_priv->hw; struct ksz_port *port = &priv->port; + unsigned long next_jiffies; int i; int p; int rc = 0; + next_jiffies = jiffies + HZ * 2; priv->multicast = 0; priv->promiscuous = 0; @@ -5428,10 +5429,7 @@ static int netdev_open(struct net_device *dev) if (rc) return rc; for (i = 0; i < hw->mib_port_cnt; i++) { - if (next_jiffies < jiffies) - next_jiffies = jiffies + HZ * 2; - else - next_jiffies += HZ * 1; + next_jiffies += HZ * 1; hw_priv->counter[i].time = next_jiffies; hw->port_mib[i].state = media_disconnected; port_init_cnt(hw, i); @@ -6563,6 +6561,7 @@ static void mib_read_work(struct work_struct *work) struct dev_info *hw_priv = container_of(work, struct dev_info, mib_read); struct ksz_hw *hw = &hw_priv->hw; + unsigned long next_jiffies; struct ksz_port_mib *mib; int i; diff --git a/drivers/net/ethernet/microchip/enc28j60.c b/drivers/net/ethernet/microchip/enc28j60.c index db5a3edb4c3c..559ad94a44d0 100644 --- a/drivers/net/ethernet/microchip/enc28j60.c +++ b/drivers/net/ethernet/microchip/enc28j60.c @@ -975,7 +975,7 @@ static void enc28j60_hw_rx(struct net_device *ndev) /* update statistics */ ndev->stats.rx_packets++; ndev->stats.rx_bytes += len; - netif_rx_ni(skb); + netif_rx(skb); } } /* diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.c b/drivers/net/ethernet/microchip/lan743x_ethtool.c index 91a755efe2e6..c8fe8b31f07b 100644 --- a/drivers/net/ethernet/microchip/lan743x_ethtool.c +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.c @@ -7,6 +7,8 @@ #include #include "lan743x_main.h" #include "lan743x_ethtool.h" +#include +#include /* eeprom */ #define LAN743X_EEPROM_MAGIC (0x74A5) @@ -19,6 +21,10 @@ #define OTP_INDICATOR_1 (0xF3) #define OTP_INDICATOR_2 (0xF7) +#define LOCK_TIMEOUT_MAX_CNT (100) // 1 sec (10 msce * 100) + +#define LAN743X_CSR_READ_OP(offset) lan743x_csr_read(adapter, offset) + static int lan743x_otp_power_up(struct lan743x_adapter *adapter) { u32 reg_value; @@ -149,6 +155,217 @@ static int lan743x_otp_write(struct lan743x_adapter *adapter, u32 offset, return 0; } +static int lan743x_hs_syslock_acquire(struct lan743x_adapter *adapter, + u16 timeout) +{ + u16 timeout_cnt = 0; + u32 val; + + do { + spin_lock(&adapter->eth_syslock_spinlock); + if (adapter->eth_syslock_acquire_cnt == 0) { + lan743x_csr_write(adapter, ETH_SYSTEM_SYS_LOCK_REG, + SYS_LOCK_REG_ENET_SS_LOCK_); + val = lan743x_csr_read(adapter, + ETH_SYSTEM_SYS_LOCK_REG); + if (val & SYS_LOCK_REG_ENET_SS_LOCK_) { + adapter->eth_syslock_acquire_cnt++; + WARN_ON(adapter->eth_syslock_acquire_cnt == 0); + spin_unlock(&adapter->eth_syslock_spinlock); + break; + } + } else { + adapter->eth_syslock_acquire_cnt++; + WARN_ON(adapter->eth_syslock_acquire_cnt == 0); + spin_unlock(&adapter->eth_syslock_spinlock); + break; + } + + spin_unlock(&adapter->eth_syslock_spinlock); + + if (timeout_cnt++ < timeout) + usleep_range(10000, 11000); + else + return -ETIMEDOUT; + } while (true); + + return 0; +} + +static void lan743x_hs_syslock_release(struct lan743x_adapter *adapter) +{ + u32 val; + + spin_lock(&adapter->eth_syslock_spinlock); + WARN_ON(adapter->eth_syslock_acquire_cnt == 0); + + if (adapter->eth_syslock_acquire_cnt) { + adapter->eth_syslock_acquire_cnt--; + if (adapter->eth_syslock_acquire_cnt == 0) { + lan743x_csr_write(adapter, ETH_SYSTEM_SYS_LOCK_REG, 0); + val = lan743x_csr_read(adapter, + ETH_SYSTEM_SYS_LOCK_REG); + WARN_ON((val & SYS_LOCK_REG_ENET_SS_LOCK_) != 0); + } + } + + spin_unlock(&adapter->eth_syslock_spinlock); +} + +static void lan743x_hs_otp_power_up(struct lan743x_adapter *adapter) +{ + u32 reg_value; + + reg_value = lan743x_csr_read(adapter, HS_OTP_PWR_DN); + if (reg_value & OTP_PWR_DN_PWRDN_N_) { + reg_value &= ~OTP_PWR_DN_PWRDN_N_; + lan743x_csr_write(adapter, HS_OTP_PWR_DN, reg_value); + /* To flush the posted write so the subsequent delay is + * guaranteed to happen after the write at the hardware + */ + lan743x_csr_read(adapter, HS_OTP_PWR_DN); + udelay(1); + } +} + +static void lan743x_hs_otp_power_down(struct lan743x_adapter *adapter) +{ + u32 reg_value; + + reg_value = lan743x_csr_read(adapter, HS_OTP_PWR_DN); + if (!(reg_value & OTP_PWR_DN_PWRDN_N_)) { + reg_value |= OTP_PWR_DN_PWRDN_N_; + lan743x_csr_write(adapter, HS_OTP_PWR_DN, reg_value); + /* To flush the posted write so the subsequent delay is + * guaranteed to happen after the write at the hardware + */ + lan743x_csr_read(adapter, HS_OTP_PWR_DN); + udelay(1); + } +} + +static void lan743x_hs_otp_set_address(struct lan743x_adapter *adapter, + u32 address) +{ + lan743x_csr_write(adapter, HS_OTP_ADDR_HIGH, (address >> 8) & 0x03); + lan743x_csr_write(adapter, HS_OTP_ADDR_LOW, address & 0xFF); +} + +static void lan743x_hs_otp_read_go(struct lan743x_adapter *adapter) +{ + lan743x_csr_write(adapter, HS_OTP_FUNC_CMD, OTP_FUNC_CMD_READ_); + lan743x_csr_write(adapter, HS_OTP_CMD_GO, OTP_CMD_GO_GO_); +} + +static int lan743x_hs_otp_cmd_cmplt_chk(struct lan743x_adapter *adapter) +{ + u32 val; + + return readx_poll_timeout(LAN743X_CSR_READ_OP, HS_OTP_STATUS, val, + !(val & OTP_STATUS_BUSY_), + 80, 10000); +} + +static int lan743x_hs_otp_read(struct lan743x_adapter *adapter, u32 offset, + u32 length, u8 *data) +{ + int ret; + int i; + + ret = lan743x_hs_syslock_acquire(adapter, LOCK_TIMEOUT_MAX_CNT); + if (ret < 0) + return ret; + + lan743x_hs_otp_power_up(adapter); + + ret = lan743x_hs_otp_cmd_cmplt_chk(adapter); + if (ret < 0) + goto power_down; + + lan743x_hs_syslock_release(adapter); + + for (i = 0; i < length; i++) { + ret = lan743x_hs_syslock_acquire(adapter, + LOCK_TIMEOUT_MAX_CNT); + if (ret < 0) + return ret; + + lan743x_hs_otp_set_address(adapter, offset + i); + + lan743x_hs_otp_read_go(adapter); + ret = lan743x_hs_otp_cmd_cmplt_chk(adapter); + if (ret < 0) + goto power_down; + + data[i] = lan743x_csr_read(adapter, HS_OTP_READ_DATA); + + lan743x_hs_syslock_release(adapter); + } + + ret = lan743x_hs_syslock_acquire(adapter, + LOCK_TIMEOUT_MAX_CNT); + if (ret < 0) + return ret; + +power_down: + lan743x_hs_otp_power_down(adapter); + lan743x_hs_syslock_release(adapter); + + return ret; +} + +static int lan743x_hs_otp_write(struct lan743x_adapter *adapter, u32 offset, + u32 length, u8 *data) +{ + int ret; + int i; + + ret = lan743x_hs_syslock_acquire(adapter, LOCK_TIMEOUT_MAX_CNT); + if (ret < 0) + return ret; + + lan743x_hs_otp_power_up(adapter); + + ret = lan743x_hs_otp_cmd_cmplt_chk(adapter); + if (ret < 0) + goto power_down; + + /* set to BYTE program mode */ + lan743x_csr_write(adapter, HS_OTP_PRGM_MODE, OTP_PRGM_MODE_BYTE_); + + lan743x_hs_syslock_release(adapter); + + for (i = 0; i < length; i++) { + ret = lan743x_hs_syslock_acquire(adapter, + LOCK_TIMEOUT_MAX_CNT); + if (ret < 0) + return ret; + + lan743x_hs_otp_set_address(adapter, offset + i); + + lan743x_csr_write(adapter, HS_OTP_PRGM_DATA, data[i]); + lan743x_csr_write(adapter, HS_OTP_TST_CMD, + OTP_TST_CMD_PRGVRFY_); + lan743x_csr_write(adapter, HS_OTP_CMD_GO, OTP_CMD_GO_GO_); + + ret = lan743x_hs_otp_cmd_cmplt_chk(adapter); + if (ret < 0) + goto power_down; + + lan743x_hs_syslock_release(adapter); + } + + ret = lan743x_hs_syslock_acquire(adapter, LOCK_TIMEOUT_MAX_CNT); + if (ret < 0) + return ret; + +power_down: + lan743x_hs_otp_power_down(adapter); + lan743x_hs_syslock_release(adapter); + + return ret; +} + static int lan743x_eeprom_wait(struct lan743x_adapter *adapter) { unsigned long start_time = jiffies; @@ -263,6 +480,100 @@ static int lan743x_eeprom_write(struct lan743x_adapter *adapter, return 0; } +static int lan743x_hs_eeprom_cmd_cmplt_chk(struct lan743x_adapter *adapter) +{ + u32 val; + + return readx_poll_timeout(LAN743X_CSR_READ_OP, HS_E2P_CMD, val, + (!(val & HS_E2P_CMD_EPC_BUSY_) || + (val & HS_E2P_CMD_EPC_TIMEOUT_)), + 50, 10000); +} + +static int lan743x_hs_eeprom_read(struct lan743x_adapter *adapter, + u32 offset, u32 length, u8 *data) +{ + int retval; + u32 val; + int i; + + retval = lan743x_hs_syslock_acquire(adapter, LOCK_TIMEOUT_MAX_CNT); + if (retval < 0) + return retval; + + retval = lan743x_hs_eeprom_cmd_cmplt_chk(adapter); + lan743x_hs_syslock_release(adapter); + if (retval < 0) + return retval; + + for (i = 0; i < length; i++) { + retval = lan743x_hs_syslock_acquire(adapter, + LOCK_TIMEOUT_MAX_CNT); + if (retval < 0) + return retval; + + val = HS_E2P_CMD_EPC_BUSY_ | HS_E2P_CMD_EPC_CMD_READ_; + val |= (offset & HS_E2P_CMD_EPC_ADDR_MASK_); + lan743x_csr_write(adapter, HS_E2P_CMD, val); + retval = lan743x_hs_eeprom_cmd_cmplt_chk(adapter); + if (retval < 0) { + lan743x_hs_syslock_release(adapter); + return retval; + } + + val = lan743x_csr_read(adapter, HS_E2P_DATA); + + lan743x_hs_syslock_release(adapter); + + data[i] = val & 0xFF; + offset++; + } + + return 0; +} + +static int lan743x_hs_eeprom_write(struct lan743x_adapter *adapter, + u32 offset, u32 length, u8 *data) +{ + int retval; + u32 val; + int i; + + retval = lan743x_hs_syslock_acquire(adapter, LOCK_TIMEOUT_MAX_CNT); + if (retval < 0) + return retval; + + retval = lan743x_hs_eeprom_cmd_cmplt_chk(adapter); + lan743x_hs_syslock_release(adapter); + if (retval < 0) + return retval; + + for (i = 0; i < length; i++) { + retval = lan743x_hs_syslock_acquire(adapter, + LOCK_TIMEOUT_MAX_CNT); + if (retval < 0) + return retval; + + /* Fill data register */ + val = data[i]; + lan743x_csr_write(adapter, HS_E2P_DATA, val); + + /* Send "write" command */ + val = HS_E2P_CMD_EPC_BUSY_ | HS_E2P_CMD_EPC_CMD_WRITE_; + val |= (offset & HS_E2P_CMD_EPC_ADDR_MASK_); + lan743x_csr_write(adapter, HS_E2P_CMD, val); + + retval = lan743x_hs_eeprom_cmd_cmplt_chk(adapter); + lan743x_hs_syslock_release(adapter); + if (retval < 0) + return retval; + + offset++; + } + + return 0; +} + static void lan743x_ethtool_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { @@ -304,10 +615,21 @@ static int lan743x_ethtool_get_eeprom(struct net_device *netdev, struct lan743x_adapter *adapter = netdev_priv(netdev); int ret = 0; - if (adapter->flags & LAN743X_ADAPTER_FLAG_OTP) - ret = lan743x_otp_read(adapter, ee->offset, ee->len, data); - else - ret = lan743x_eeprom_read(adapter, ee->offset, ee->len, data); + if (adapter->flags & LAN743X_ADAPTER_FLAG_OTP) { + if (adapter->is_pci11x1x) + ret = lan743x_hs_otp_read(adapter, ee->offset, + ee->len, data); + else + ret = lan743x_otp_read(adapter, ee->offset, + ee->len, data); + } else { + if (adapter->is_pci11x1x) + ret = lan743x_hs_eeprom_read(adapter, ee->offset, + ee->len, data); + else + ret = lan743x_eeprom_read(adapter, ee->offset, + ee->len, data); + } return ret; } @@ -321,13 +643,22 @@ static int lan743x_ethtool_set_eeprom(struct net_device *netdev, if (adapter->flags & LAN743X_ADAPTER_FLAG_OTP) { /* Beware! OTP is One Time Programming ONLY! */ if (ee->magic == LAN743X_OTP_MAGIC) { - ret = lan743x_otp_write(adapter, ee->offset, - ee->len, data); + if (adapter->is_pci11x1x) + ret = lan743x_hs_otp_write(adapter, ee->offset, + ee->len, data); + else + ret = lan743x_otp_write(adapter, ee->offset, + ee->len, data); } } else { if (ee->magic == LAN743X_EEPROM_MAGIC) { - ret = lan743x_eeprom_write(adapter, ee->offset, - ee->len, data); + if (adapter->is_pci11x1x) + ret = lan743x_hs_eeprom_write(adapter, + ee->offset, + ee->len, data); + else + ret = lan743x_eeprom_write(adapter, ee->offset, + ee->len, data); } } @@ -365,6 +696,14 @@ static const char lan743x_set1_sw_cnt_strings[][ETH_GSTRING_LEN] = { "RX Queue 3 Frames", }; +static const char lan743x_tx_queue_cnt_strings[][ETH_GSTRING_LEN] = { + "TX Queue 0 Frames", + "TX Queue 1 Frames", + "TX Queue 2 Frames", + "TX Queue 3 Frames", + "TX Total Queue Frames", +}; + static const char lan743x_set2_hw_cnt_strings[][ETH_GSTRING_LEN] = { "RX Total Frames", "EEE RX LPI Transitions", @@ -462,6 +801,8 @@ static const char lan743x_priv_flags_strings[][ETH_GSTRING_LEN] = { static void lan743x_ethtool_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { + struct lan743x_adapter *adapter = netdev_priv(netdev); + switch (stringset) { case ETH_SS_STATS: memcpy(data, lan743x_set0_hw_cnt_strings, @@ -473,6 +814,13 @@ static void lan743x_ethtool_get_strings(struct net_device *netdev, sizeof(lan743x_set1_sw_cnt_strings)], lan743x_set2_hw_cnt_strings, sizeof(lan743x_set2_hw_cnt_strings)); + if (adapter->is_pci11x1x) { + memcpy(&data[sizeof(lan743x_set0_hw_cnt_strings) + + sizeof(lan743x_set1_sw_cnt_strings) + + sizeof(lan743x_set2_hw_cnt_strings)], + lan743x_tx_queue_cnt_strings, + sizeof(lan743x_tx_queue_cnt_strings)); + } break; case ETH_SS_PRIV_FLAGS: memcpy(data, lan743x_priv_flags_strings, @@ -486,7 +834,9 @@ static void lan743x_ethtool_get_ethtool_stats(struct net_device *netdev, u64 *data) { struct lan743x_adapter *adapter = netdev_priv(netdev); + u64 total_queue_count = 0; int data_index = 0; + u64 pkt_cnt; u32 buf; int i; @@ -500,6 +850,14 @@ static void lan743x_ethtool_get_ethtool_stats(struct net_device *netdev, buf = lan743x_csr_read(adapter, lan743x_set2_hw_cnt_addr[i]); data[data_index++] = (u64)buf; } + if (adapter->is_pci11x1x) { + for (i = 0; i < ARRAY_SIZE(adapter->tx); i++) { + pkt_cnt = (u64)(adapter->tx[i].frame_count); + data[data_index++] = pkt_cnt; + total_queue_count += pkt_cnt; + } + data[data_index++] = total_queue_count; + } } static u32 lan743x_ethtool_get_priv_flags(struct net_device *netdev) @@ -520,6 +878,8 @@ static int lan743x_ethtool_set_priv_flags(struct net_device *netdev, u32 flags) static int lan743x_ethtool_get_sset_count(struct net_device *netdev, int sset) { + struct lan743x_adapter *adapter = netdev_priv(netdev); + switch (sset) { case ETH_SS_STATS: { @@ -528,6 +888,8 @@ static int lan743x_ethtool_get_sset_count(struct net_device *netdev, int sset) ret = ARRAY_SIZE(lan743x_set0_hw_cnt_strings); ret += ARRAY_SIZE(lan743x_set1_sw_cnt_strings); ret += ARRAY_SIZE(lan743x_set2_hw_cnt_strings); + if (adapter->is_pci11x1x) + ret += ARRAY_SIZE(lan743x_tx_queue_cnt_strings); return ret; } case ETH_SS_PRIV_FLAGS: @@ -750,7 +1112,7 @@ static int lan743x_ethtool_set_eee(struct net_device *netdev, } if (eee->eee_enabled) { - ret = phy_init_eee(phydev, 0); + ret = phy_init_eee(phydev, false); if (ret) { netif_err(adapter, drv, adapter->netdev, "EEE initialization failed\n"); diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index 8c6390d95158..9ac0c2b96a15 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -18,6 +18,51 @@ #include "lan743x_main.h" #include "lan743x_ethtool.h" +#define MMD_ACCESS_ADDRESS 0 +#define MMD_ACCESS_WRITE 1 +#define MMD_ACCESS_READ 2 +#define MMD_ACCESS_READ_INC 3 + +static void pci11x1x_strap_get_status(struct lan743x_adapter *adapter) +{ + u32 chip_rev; + u32 strap; + + strap = lan743x_csr_read(adapter, STRAP_READ); + if (strap & STRAP_READ_USE_SGMII_EN_) { + if (strap & STRAP_READ_SGMII_EN_) + adapter->is_sgmii_en = true; + else + adapter->is_sgmii_en = false; + netif_dbg(adapter, drv, adapter->netdev, + "STRAP_READ: 0x%08X\n", strap); + } else { + chip_rev = lan743x_csr_read(adapter, FPGA_REV); + if (chip_rev) { + if (chip_rev & FPGA_SGMII_OP) + adapter->is_sgmii_en = true; + else + adapter->is_sgmii_en = false; + netif_dbg(adapter, drv, adapter->netdev, + "FPGA_REV: 0x%08X\n", chip_rev); + } else { + adapter->is_sgmii_en = false; + } + } +} + +static bool is_pci11x1x_chip(struct lan743x_adapter *adapter) +{ + struct lan743x_csr *csr = &adapter->csr; + u32 id_rev = csr->id_rev; + + if (((id_rev & 0xFFFF0000) == ID_REV_ID_A011_) || + ((id_rev & 0xFFFF0000) == ID_REV_ID_A041_)) { + return true; + } + return false; +} + static void lan743x_pci_cleanup(struct lan743x_adapter *adapter) { pci_release_selected_regions(adapter->pdev, @@ -250,7 +295,7 @@ static void lan743x_intr_shared_isr(void *context, u32 int_sts, u32 flags) } } if (int_sts & INT_BIT_ALL_TX_) { - for (channel = 0; channel < LAN743X_USED_TX_CHANNELS; + for (channel = 0; channel < adapter->used_tx_channels; channel++) { u32 int_bit = INT_BIT_DMA_TX_(channel); @@ -410,7 +455,7 @@ static u32 lan743x_intr_get_vector_flags(struct lan743x_adapter *adapter, { int index; - for (index = 0; index < LAN743X_MAX_VECTOR_COUNT; index++) { + for (index = 0; index < adapter->max_vector_count; index++) { if (adapter->intr.vector_list[index].int_mask & int_mask) return adapter->intr.vector_list[index].flags; } @@ -423,9 +468,12 @@ static void lan743x_intr_close(struct lan743x_adapter *adapter) int index = 0; lan743x_csr_write(adapter, INT_EN_CLR, INT_BIT_MAS_); - lan743x_csr_write(adapter, INT_VEC_EN_CLR, 0x000000FF); + if (adapter->is_pci11x1x) + lan743x_csr_write(adapter, INT_VEC_EN_CLR, 0x0000FFFF); + else + lan743x_csr_write(adapter, INT_VEC_EN_CLR, 0x000000FF); - for (index = 0; index < LAN743X_MAX_VECTOR_COUNT; index++) { + for (index = 0; index < intr->number_of_vectors; index++) { if (intr->flags & INTR_FLAG_IRQ_REQUESTED(index)) { lan743x_intr_unregister_isr(adapter, index); intr->flags &= ~INTR_FLAG_IRQ_REQUESTED(index); @@ -445,9 +493,11 @@ static void lan743x_intr_close(struct lan743x_adapter *adapter) static int lan743x_intr_open(struct lan743x_adapter *adapter) { - struct msix_entry msix_entries[LAN743X_MAX_VECTOR_COUNT]; + struct msix_entry msix_entries[PCI11X1X_MAX_VECTOR_COUNT]; struct lan743x_intr *intr = &adapter->intr; + unsigned int used_tx_channels; u32 int_vec_en_auto_clr = 0; + u8 max_vector_count; u32 int_vec_map0 = 0; u32 int_vec_map1 = 0; int ret = -ENODEV; @@ -457,13 +507,15 @@ static int lan743x_intr_open(struct lan743x_adapter *adapter) intr->number_of_vectors = 0; /* Try to set up MSIX interrupts */ + max_vector_count = adapter->max_vector_count; memset(&msix_entries[0], 0, - sizeof(struct msix_entry) * LAN743X_MAX_VECTOR_COUNT); - for (index = 0; index < LAN743X_MAX_VECTOR_COUNT; index++) + sizeof(struct msix_entry) * max_vector_count); + for (index = 0; index < max_vector_count; index++) msix_entries[index].entry = index; + used_tx_channels = adapter->used_tx_channels; ret = pci_enable_msix_range(adapter->pdev, msix_entries, 1, - 1 + LAN743X_USED_TX_CHANNELS + + 1 + used_tx_channels + LAN743X_USED_RX_CHANNELS); if (ret > 0) { @@ -556,8 +608,15 @@ static int lan743x_intr_open(struct lan743x_adapter *adapter) lan743x_csr_write(adapter, INT_MOD_CFG5, LAN743X_INT_MOD); lan743x_csr_write(adapter, INT_MOD_CFG6, LAN743X_INT_MOD); lan743x_csr_write(adapter, INT_MOD_CFG7, LAN743X_INT_MOD); - lan743x_csr_write(adapter, INT_MOD_MAP0, 0x00005432); - lan743x_csr_write(adapter, INT_MOD_MAP1, 0x00000001); + if (adapter->is_pci11x1x) { + lan743x_csr_write(adapter, INT_MOD_CFG8, LAN743X_INT_MOD); + lan743x_csr_write(adapter, INT_MOD_CFG9, LAN743X_INT_MOD); + lan743x_csr_write(adapter, INT_MOD_MAP0, 0x00007654); + lan743x_csr_write(adapter, INT_MOD_MAP1, 0x00003210); + } else { + lan743x_csr_write(adapter, INT_MOD_MAP0, 0x00005432); + lan743x_csr_write(adapter, INT_MOD_MAP1, 0x00000001); + } lan743x_csr_write(adapter, INT_MOD_MAP2, 0x00FFFFFF); } @@ -570,8 +629,8 @@ static int lan743x_intr_open(struct lan743x_adapter *adapter) if (intr->number_of_vectors > 1) { int number_of_tx_vectors = intr->number_of_vectors - 1; - if (number_of_tx_vectors > LAN743X_USED_TX_CHANNELS) - number_of_tx_vectors = LAN743X_USED_TX_CHANNELS; + if (number_of_tx_vectors > used_tx_channels) + number_of_tx_vectors = used_tx_channels; flags = LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ | LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C | LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK | @@ -609,9 +668,9 @@ static int lan743x_intr_open(struct lan743x_adapter *adapter) INT_VEC_EN_(vector)); } } - if ((intr->number_of_vectors - LAN743X_USED_TX_CHANNELS) > 1) { + if ((intr->number_of_vectors - used_tx_channels) > 1) { int number_of_rx_vectors = intr->number_of_vectors - - LAN743X_USED_TX_CHANNELS - 1; + used_tx_channels - 1; if (number_of_rx_vectors > LAN743X_USED_RX_CHANNELS) number_of_rx_vectors = LAN743X_USED_RX_CHANNELS; @@ -632,7 +691,7 @@ static int lan743x_intr_open(struct lan743x_adapter *adapter) LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR; } for (index = 0; index < number_of_rx_vectors; index++) { - int vector = index + 1 + LAN743X_USED_TX_CHANNELS; + int vector = index + 1 + used_tx_channels; u32 int_bit = INT_BIT_DMA_RX_(index); /* map RX interrupt to vector */ @@ -760,6 +819,96 @@ static int lan743x_mdiobus_write(struct mii_bus *bus, return ret; } +static u32 lan743x_mac_mmd_access(int id, int index, int op) +{ + u16 dev_addr; + u32 ret; + + dev_addr = (index >> 16) & 0x1f; + ret = (id << MAC_MII_ACC_PHY_ADDR_SHIFT_) & + MAC_MII_ACC_PHY_ADDR_MASK_; + ret |= (dev_addr << MAC_MII_ACC_MIIMMD_SHIFT_) & + MAC_MII_ACC_MIIMMD_MASK_; + if (op == MMD_ACCESS_WRITE) + ret |= MAC_MII_ACC_MIICMD_WRITE_; + else if (op == MMD_ACCESS_READ) + ret |= MAC_MII_ACC_MIICMD_READ_; + else if (op == MMD_ACCESS_READ_INC) + ret |= MAC_MII_ACC_MIICMD_READ_INC_; + else + ret |= MAC_MII_ACC_MIICMD_ADDR_; + ret |= (MAC_MII_ACC_MII_BUSY_ | MAC_MII_ACC_MIICL45_); + + return ret; +} + +static int lan743x_mdiobus_c45_read(struct mii_bus *bus, int phy_id, int index) +{ + struct lan743x_adapter *adapter = bus->priv; + u32 mmd_access; + int ret; + + /* comfirm MII not busy */ + ret = lan743x_mac_mii_wait_till_not_busy(adapter); + if (ret < 0) + return ret; + if (index & MII_ADDR_C45) { + /* Load Register Address */ + lan743x_csr_write(adapter, MAC_MII_DATA, (u32)(index & 0xffff)); + mmd_access = lan743x_mac_mmd_access(phy_id, index, + MMD_ACCESS_ADDRESS); + lan743x_csr_write(adapter, MAC_MII_ACC, mmd_access); + ret = lan743x_mac_mii_wait_till_not_busy(adapter); + if (ret < 0) + return ret; + /* Read Data */ + mmd_access = lan743x_mac_mmd_access(phy_id, index, + MMD_ACCESS_READ); + lan743x_csr_write(adapter, MAC_MII_ACC, mmd_access); + ret = lan743x_mac_mii_wait_till_not_busy(adapter); + if (ret < 0) + return ret; + ret = lan743x_csr_read(adapter, MAC_MII_DATA); + return (int)(ret & 0xFFFF); + } + + ret = lan743x_mdiobus_read(bus, phy_id, index); + return ret; +} + +static int lan743x_mdiobus_c45_write(struct mii_bus *bus, + int phy_id, int index, u16 regval) +{ + struct lan743x_adapter *adapter = bus->priv; + u32 mmd_access; + int ret; + + /* confirm MII not busy */ + ret = lan743x_mac_mii_wait_till_not_busy(adapter); + if (ret < 0) + return ret; + if (index & MII_ADDR_C45) { + /* Load Register Address */ + lan743x_csr_write(adapter, MAC_MII_DATA, (u32)(index & 0xffff)); + mmd_access = lan743x_mac_mmd_access(phy_id, index, + MMD_ACCESS_ADDRESS); + lan743x_csr_write(adapter, MAC_MII_ACC, mmd_access); + ret = lan743x_mac_mii_wait_till_not_busy(adapter); + if (ret < 0) + return ret; + /* Write Data */ + lan743x_csr_write(adapter, MAC_MII_DATA, (u32)regval); + mmd_access = lan743x_mac_mmd_access(phy_id, index, + MMD_ACCESS_WRITE); + lan743x_csr_write(adapter, MAC_MII_ACC, mmd_access); + ret = lan743x_mac_mii_wait_till_not_busy(adapter); + } else { + ret = lan743x_mdiobus_write(bus, phy_id, index, regval); + } + + return ret; +} + static void lan743x_mac_set_address(struct lan743x_adapter *adapter, u8 *addr) { @@ -1627,6 +1776,7 @@ static netdev_tx_t lan743x_tx_xmit_frame(struct lan743x_tx *tx, dev_kfree_skb_irq(skb); goto unlock; } + tx->frame_count++; if (gso) lan743x_tx_frame_add_lso(tx, frame_length, nr_frags); @@ -2491,7 +2641,8 @@ static int lan743x_netdev_close(struct net_device *netdev) struct lan743x_adapter *adapter = netdev_priv(netdev); int index; - lan743x_tx_close(&adapter->tx[0]); + for (index = 0; index < adapter->used_tx_channels; index++) + lan743x_tx_close(&adapter->tx[index]); for (index = 0; index < LAN743X_USED_RX_CHANNELS; index++) lan743x_rx_close(&adapter->rx[index]); @@ -2537,12 +2688,19 @@ static int lan743x_netdev_open(struct net_device *netdev) goto close_rx; } - ret = lan743x_tx_open(&adapter->tx[0]); - if (ret) - goto close_rx; - + for (index = 0; index < adapter->used_tx_channels; index++) { + ret = lan743x_tx_open(&adapter->tx[index]); + if (ret) + goto close_tx; + } return 0; +close_tx: + for (index = 0; index < adapter->used_tx_channels; index++) { + if (adapter->tx[index].ring_cpu_ptr) + lan743x_tx_close(&adapter->tx[index]); + } + close_rx: for (index = 0; index < LAN743X_USED_RX_CHANNELS; index++) { if (adapter->rx[index].ring_cpu_ptr) @@ -2569,8 +2727,12 @@ static netdev_tx_t lan743x_netdev_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct lan743x_adapter *adapter = netdev_priv(netdev); + u8 ch = 0; - return lan743x_tx_xmit_frame(&adapter->tx[0], skb); + if (adapter->is_pci11x1x) + ch = skb->queue_mapping % PCI11X1X_USED_TX_CHANNELS; + + return lan743x_tx_xmit_frame(&adapter->tx[ch], skb); } static int lan743x_netdev_ioctl(struct net_device *netdev, @@ -2701,6 +2863,19 @@ static int lan743x_hardware_init(struct lan743x_adapter *adapter, int index; int ret; + adapter->is_pci11x1x = is_pci11x1x_chip(adapter); + if (adapter->is_pci11x1x) { + adapter->max_tx_channels = PCI11X1X_MAX_TX_CHANNELS; + adapter->used_tx_channels = PCI11X1X_USED_TX_CHANNELS; + adapter->max_vector_count = PCI11X1X_MAX_VECTOR_COUNT; + pci11x1x_strap_get_status(adapter); + spin_lock_init(&adapter->eth_syslock_spinlock); + } else { + adapter->max_tx_channels = LAN743X_MAX_TX_CHANNELS; + adapter->used_tx_channels = LAN743X_USED_TX_CHANNELS; + adapter->max_vector_count = LAN743X_MAX_VECTOR_COUNT; + } + adapter->intr.irq = adapter->pdev->irq; lan743x_csr_write(adapter, INT_EN_CLR, 0xFFFFFFFF); @@ -2731,15 +2906,19 @@ static int lan743x_hardware_init(struct lan743x_adapter *adapter, adapter->rx[index].channel_number = index; } - tx = &adapter->tx[0]; - tx->adapter = adapter; - tx->channel_number = 0; - spin_lock_init(&tx->ring_lock); + for (index = 0; index < adapter->used_tx_channels; index++) { + tx = &adapter->tx[index]; + tx->adapter = adapter; + tx->channel_number = index; + spin_lock_init(&tx->ring_lock); + } + return 0; } static int lan743x_mdiobus_init(struct lan743x_adapter *adapter) { + u32 sgmii_ctl; int ret; adapter->mdiobus = devm_mdiobus_alloc(&adapter->pdev->dev); @@ -2749,9 +2928,35 @@ static int lan743x_mdiobus_init(struct lan743x_adapter *adapter) } adapter->mdiobus->priv = (void *)adapter; - adapter->mdiobus->read = lan743x_mdiobus_read; - adapter->mdiobus->write = lan743x_mdiobus_write; - adapter->mdiobus->name = "lan743x-mdiobus"; + if (adapter->is_pci11x1x) { + if (adapter->is_sgmii_en) { + sgmii_ctl = lan743x_csr_read(adapter, SGMII_CTL); + sgmii_ctl |= SGMII_CTL_SGMII_ENABLE_; + sgmii_ctl &= ~SGMII_CTL_SGMII_POWER_DN_; + lan743x_csr_write(adapter, SGMII_CTL, sgmii_ctl); + netif_dbg(adapter, drv, adapter->netdev, + "SGMII operation\n"); + } else { + sgmii_ctl = lan743x_csr_read(adapter, SGMII_CTL); + sgmii_ctl &= ~SGMII_CTL_SGMII_ENABLE_; + sgmii_ctl |= SGMII_CTL_SGMII_POWER_DN_; + lan743x_csr_write(adapter, SGMII_CTL, sgmii_ctl); + netif_dbg(adapter, drv, adapter->netdev, + "(R)GMII operation\n"); + } + + adapter->mdiobus->probe_capabilities = MDIOBUS_C22_C45; + adapter->mdiobus->read = lan743x_mdiobus_c45_read; + adapter->mdiobus->write = lan743x_mdiobus_c45_write; + adapter->mdiobus->name = "lan743x-mdiobus-c45"; + netif_dbg(adapter, drv, adapter->netdev, "lan743x-mdiobus-c45\n"); + } else { + adapter->mdiobus->read = lan743x_mdiobus_read; + adapter->mdiobus->write = lan743x_mdiobus_write; + adapter->mdiobus->name = "lan743x-mdiobus"; + netif_dbg(adapter, drv, adapter->netdev, "lan743x-mdiobus\n"); + } + snprintf(adapter->mdiobus->id, MII_BUS_ID_SIZE, "pci-%s", pci_name(adapter->pdev)); @@ -2786,8 +2991,17 @@ static int lan743x_pcidev_probe(struct pci_dev *pdev, struct net_device *netdev = NULL; int ret = -ENODEV; - netdev = devm_alloc_etherdev(&pdev->dev, - sizeof(struct lan743x_adapter)); + if (id->device == PCI_DEVICE_ID_SMSC_A011 || + id->device == PCI_DEVICE_ID_SMSC_A041) { + netdev = devm_alloc_etherdev_mqs(&pdev->dev, + sizeof(struct lan743x_adapter), + PCI11X1X_USED_TX_CHANNELS, + LAN743X_USED_RX_CHANNELS); + } else { + netdev = devm_alloc_etherdev(&pdev->dev, + sizeof(struct lan743x_adapter)); + } + if (!netdev) goto return_error; @@ -3056,6 +3270,8 @@ static const struct dev_pm_ops lan743x_pm_ops = { static const struct pci_device_id lan743x_pcidev_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_SMSC, PCI_DEVICE_ID_SMSC_LAN7430) }, { PCI_DEVICE(PCI_VENDOR_ID_SMSC, PCI_DEVICE_ID_SMSC_LAN7431) }, + { PCI_DEVICE(PCI_VENDOR_ID_SMSC, PCI_DEVICE_ID_SMSC_A011) }, + { PCI_DEVICE(PCI_VENDOR_ID_SMSC, PCI_DEVICE_ID_SMSC_A041) }, { 0, } }; diff --git a/drivers/net/ethernet/microchip/lan743x_main.h b/drivers/net/ethernet/microchip/lan743x_main.h index aaf7aaeaba0c..1ca5f3216403 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.h +++ b/drivers/net/ethernet/microchip/lan743x_main.h @@ -16,8 +16,13 @@ #define ID_REV_ID_MASK_ (0xFFFF0000) #define ID_REV_ID_LAN7430_ (0x74300000) #define ID_REV_ID_LAN7431_ (0x74310000) -#define ID_REV_IS_VALID_CHIP_ID_(id_rev) \ - (((id_rev) & 0xFFF00000) == 0x74300000) +#define ID_REV_ID_LAN743X_ (0x74300000) +#define ID_REV_ID_A011_ (0xA0110000) // PCI11010 +#define ID_REV_ID_A041_ (0xA0410000) // PCI11414 +#define ID_REV_ID_A0X1_ (0xA0010000) +#define ID_REV_IS_VALID_CHIP_ID_(id_rev) \ + ((((id_rev) & 0xFFF00000) == ID_REV_ID_LAN743X_) || \ + (((id_rev) & 0xFF0F0000) == ID_REV_ID_A0X1_)) #define ID_REV_CHIP_REV_MASK_ (0x0000FFFF) #define ID_REV_CHIP_REV_A0_ (0x00000000) #define ID_REV_CHIP_REV_B0_ (0x00000010) @@ -25,6 +30,17 @@ #define FPGA_REV (0x04) #define FPGA_REV_GET_MINOR_(fpga_rev) (((fpga_rev) >> 8) & 0x000000FF) #define FPGA_REV_GET_MAJOR_(fpga_rev) ((fpga_rev) & 0x000000FF) +#define FPGA_SGMII_OP BIT(24) + +#define STRAP_READ (0x0C) +#define STRAP_READ_USE_SGMII_EN_ BIT(22) +#define STRAP_READ_SGMII_EN_ BIT(6) +#define STRAP_READ_SGMII_REFCLK_ BIT(5) +#define STRAP_READ_SGMII_2_5G_ BIT(4) +#define STRAP_READ_BASE_X_ BIT(3) +#define STRAP_READ_RGMII_TXC_DELAY_EN_ BIT(2) +#define STRAP_READ_RGMII_RXC_DELAY_EN_ BIT(1) +#define STRAP_READ_ADV_PM_DISABLE_ BIT(0) #define HW_CFG (0x010) #define HW_CFG_RELOAD_TYPE_ALL_ (0x00000FC0) @@ -70,6 +86,40 @@ #define E2P_DATA (0x044) +/* Hearthstone top level & System Reg Addresses */ +#define ETH_CTRL_REG_ADDR_BASE (0x0000) +#define ETH_SYS_REG_ADDR_BASE (0x4000) +#define CONFIG_REG_ADDR_BASE (0x0000) +#define ETH_EEPROM_REG_ADDR_BASE (0x0E00) +#define ETH_OTP_REG_ADDR_BASE (0x1000) +#define SYS_LOCK_REG (0x00A0) +#define SYS_LOCK_REG_MAIN_LOCK_ BIT(7) +#define SYS_LOCK_REG_GEN_PERI_LOCK_ BIT(5) +#define SYS_LOCK_REG_SPI_PERI_LOCK_ BIT(4) +#define SYS_LOCK_REG_SMBUS_PERI_LOCK_ BIT(3) +#define SYS_LOCK_REG_UART_SS_LOCK_ BIT(2) +#define SYS_LOCK_REG_ENET_SS_LOCK_ BIT(1) +#define SYS_LOCK_REG_USB_SS_LOCK_ BIT(0) +#define ETH_SYSTEM_SYS_LOCK_REG (ETH_SYS_REG_ADDR_BASE + \ + CONFIG_REG_ADDR_BASE + \ + SYS_LOCK_REG) +#define HS_EEPROM_REG_ADDR_BASE (ETH_SYS_REG_ADDR_BASE + \ + ETH_EEPROM_REG_ADDR_BASE) +#define HS_E2P_CMD (HS_EEPROM_REG_ADDR_BASE + 0x0000) +#define HS_E2P_CMD_EPC_BUSY_ BIT(31) +#define HS_E2P_CMD_EPC_CMD_WRITE_ GENMASK(29, 28) +#define HS_E2P_CMD_EPC_CMD_READ_ (0x0) +#define HS_E2P_CMD_EPC_TIMEOUT_ BIT(17) +#define HS_E2P_CMD_EPC_ADDR_MASK_ GENMASK(15, 0) +#define HS_E2P_DATA (HS_EEPROM_REG_ADDR_BASE + 0x0004) +#define HS_E2P_DATA_MASK_ GENMASK(7, 0) +#define HS_E2P_CFG (HS_EEPROM_REG_ADDR_BASE + 0x0008) +#define HS_E2P_CFG_I2C_PULSE_MASK_ GENMASK(19, 16) +#define HS_E2P_CFG_EEPROM_SIZE_SEL_ BIT(12) +#define HS_E2P_CFG_I2C_BAUD_RATE_MASK_ GENMASK(9, 8) +#define HS_E2P_CFG_TEST_EEPR_TO_BYP_ BIT(0) +#define HS_E2P_PAD_CTL (HS_EEPROM_REG_ADDR_BASE + 0x000C) + #define GPIO_CFG0 (0x050) #define GPIO_CFG0_GPIO_DIR_BIT_(bit) BIT(16 + (bit)) #define GPIO_CFG0_GPIO_DATA_BIT_(bit) BIT(0 + (bit)) @@ -135,6 +185,13 @@ #define MAC_RX_ADDRL (0x11C) #define MAC_MII_ACC (0x120) +#define MAC_MII_ACC_MDC_CYCLE_SHIFT_ (16) +#define MAC_MII_ACC_MDC_CYCLE_MASK_ (0x00070000) +#define MAC_MII_ACC_MDC_CYCLE_2_5MHZ_ (0) +#define MAC_MII_ACC_MDC_CYCLE_5MHZ_ (1) +#define MAC_MII_ACC_MDC_CYCLE_12_5MHZ_ (2) +#define MAC_MII_ACC_MDC_CYCLE_25MHZ_ (3) +#define MAC_MII_ACC_MDC_CYCLE_1_25MHZ_ (4) #define MAC_MII_ACC_PHY_ADDR_SHIFT_ (11) #define MAC_MII_ACC_PHY_ADDR_MASK_ (0x0000F800) #define MAC_MII_ACC_MIIRINDA_SHIFT_ (6) @@ -143,6 +200,15 @@ #define MAC_MII_ACC_MII_WRITE_ (0x00000002) #define MAC_MII_ACC_MII_BUSY_ BIT(0) +#define MAC_MII_ACC_MIIMMD_SHIFT_ (6) +#define MAC_MII_ACC_MIIMMD_MASK_ (0x000007C0) +#define MAC_MII_ACC_MIICL45_ BIT(3) +#define MAC_MII_ACC_MIICMD_MASK_ (0x00000006) +#define MAC_MII_ACC_MIICMD_ADDR_ (0x00000000) +#define MAC_MII_ACC_MIICMD_WRITE_ (0x00000002) +#define MAC_MII_ACC_MIICMD_READ_ (0x00000004) +#define MAC_MII_ACC_MIICMD_READ_INC_ (0x00000006) + #define MAC_MII_DATA (0x124) #define MAC_EEE_TX_LPI_REQ_DLY_CNT (0x130) @@ -214,6 +280,11 @@ #define MAC_WUCSR2 (0x600) +#define SGMII_CTL (0x728) +#define SGMII_CTL_SGMII_ENABLE_ BIT(31) +#define SGMII_CTL_LINK_STATUS_SOURCE_ BIT(8) +#define SGMII_CTL_SGMII_POWER_DN_ BIT(1) + #define INT_STS (0x780) #define INT_BIT_DMA_RX_(channel) BIT(24 + (channel)) #define INT_BIT_ALL_RX_ (0x0F000000) @@ -261,8 +332,11 @@ #define INT_MOD_CFG5 (0x7D4) #define INT_MOD_CFG6 (0x7D8) #define INT_MOD_CFG7 (0x7DC) +#define INT_MOD_CFG8 (0x7E0) +#define INT_MOD_CFG9 (0x7E4) #define PTP_CMD_CTL (0x0A00) +#define PTP_CMD_CTL_PTP_LTC_TARGET_READ_ BIT(13) #define PTP_CMD_CTL_PTP_CLK_STP_NSEC_ BIT(6) #define PTP_CMD_CTL_PTP_CLOCK_STEP_SEC_ BIT(5) #define PTP_CMD_CTL_PTP_CLOCK_LOAD_ BIT(4) @@ -284,9 +358,51 @@ (((value) & 0x7) << (1 + ((channel) << 2))) #define PTP_GENERAL_CONFIG_RELOAD_ADD_X_(channel) (BIT((channel) << 2)) +#define HS_PTP_GENERAL_CONFIG (0x0A04) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_X_MASK_(channel) \ + (0xf << (4 + ((channel) << 2))) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_100NS_ (0) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_500NS_ (1) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_1US_ (2) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_5US_ (3) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_10US_ (4) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_50US_ (5) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_100US_ (6) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_500US_ (7) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_1MS_ (8) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_5MS_ (9) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_10MS_ (10) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_50MS_ (11) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_100MS_ (12) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_200MS_ (13) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_TOGG_ (14) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_INT_ (15) +#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_X_SET_(channel, value) \ + (((value) & 0xf) << (4 + ((channel) << 2))) +#define HS_PTP_GENERAL_CONFIG_EVENT_POL_X_(channel) (BIT(1 + ((channel) * 2))) +#define HS_PTP_GENERAL_CONFIG_RELOAD_ADD_X_(channel) (BIT((channel) * 2)) + #define PTP_INT_STS (0x0A08) +#define PTP_INT_IO_FE_MASK_ GENMASK(31, 24) +#define PTP_INT_IO_FE_SHIFT_ (24) +#define PTP_INT_IO_FE_SET_(channel) BIT(24 + (channel)) +#define PTP_INT_IO_RE_MASK_ GENMASK(23, 16) +#define PTP_INT_IO_RE_SHIFT_ (16) +#define PTP_INT_IO_RE_SET_(channel) BIT(16 + (channel)) +#define PTP_INT_TX_TS_OVRFL_INT_ BIT(14) +#define PTP_INT_TX_SWTS_ERR_INT_ BIT(13) +#define PTP_INT_TX_TS_INT_ BIT(12) +#define PTP_INT_RX_TS_OVRFL_INT_ BIT(9) +#define PTP_INT_RX_TS_INT_ BIT(8) +#define PTP_INT_TIMER_INT_B_ BIT(1) +#define PTP_INT_TIMER_INT_A_ BIT(0) #define PTP_INT_EN_SET (0x0A0C) +#define PTP_INT_EN_FE_EN_SET_(channel) BIT(24 + (channel)) +#define PTP_INT_EN_RE_EN_SET_(channel) BIT(16 + (channel)) +#define PTP_INT_EN_TIMER_SET_(channel) BIT(channel) #define PTP_INT_EN_CLR (0x0A10) +#define PTP_INT_EN_FE_EN_CLR_(channel) BIT(24 + (channel)) +#define PTP_INT_EN_RE_EN_CLR_(channel) BIT(16 + (channel)) #define PTP_INT_BIT_TX_SWTS_ERR_ BIT(13) #define PTP_INT_BIT_TX_TS_ BIT(12) #define PTP_INT_BIT_TIMER_B_ BIT(1) @@ -304,6 +420,16 @@ #define PTP_CLOCK_TARGET_NS_X(channel) (0x0A34 + ((channel) << 4)) #define PTP_CLOCK_TARGET_RELOAD_SEC_X(channel) (0x0A38 + ((channel) << 4)) #define PTP_CLOCK_TARGET_RELOAD_NS_X(channel) (0x0A3C + ((channel) << 4)) +#define PTP_LTC_SET_SEC_HI (0x0A50) +#define PTP_LTC_SET_SEC_HI_SEC_47_32_MASK_ GENMASK(15, 0) +#define PTP_VERSION (0x0A54) +#define PTP_VERSION_TX_UP_MASK_ GENMASK(31, 24) +#define PTP_VERSION_TX_LO_MASK_ GENMASK(23, 16) +#define PTP_VERSION_RX_UP_MASK_ GENMASK(15, 8) +#define PTP_VERSION_RX_LO_MASK_ GENMASK(7, 0) +#define PTP_IO_SEL (0x0A58) +#define PTP_IO_SEL_MASK_ GENMASK(10, 8) +#define PTP_IO_SEL_SHIFT_ (8) #define PTP_LATENCY (0x0A5C) #define PTP_LATENCY_TX_SET_(tx_latency) (((u32)(tx_latency)) << 16) #define PTP_LATENCY_RX_SET_(rx_latency) \ @@ -328,6 +454,59 @@ #define PTP_TX_MSG_HEADER_MSG_TYPE_ (0x000F0000) #define PTP_TX_MSG_HEADER_MSG_TYPE_SYNC_ (0x00000000) +#define PTP_TX_CAP_INFO (0x0AB8) +#define PTP_TX_CAP_INFO_TX_CH_MASK_ GENMASK(1, 0) +#define PTP_TX_DOMAIN (0x0ABC) +#define PTP_TX_DOMAIN_MASK_ GENMASK(23, 16) +#define PTP_TX_DOMAIN_RANGE_EN_ BIT(15) +#define PTP_TX_DOMAIN_RANGE_MASK_ GENMASK(7, 0) +#define PTP_TX_SDOID (0x0AC0) +#define PTP_TX_SDOID_MASK_ GENMASK(23, 16) +#define PTP_TX_SDOID_RANGE_EN_ BIT(15) +#define PTP_TX_SDOID_11_0_MASK_ GENMASK(7, 0) +#define PTP_IO_CAP_CONFIG (0x0AC4) +#define PTP_IO_CAP_CONFIG_LOCK_FE_(channel) BIT(24 + (channel)) +#define PTP_IO_CAP_CONFIG_LOCK_RE_(channel) BIT(16 + (channel)) +#define PTP_IO_CAP_CONFIG_FE_CAP_EN_(channel) BIT(8 + (channel)) +#define PTP_IO_CAP_CONFIG_RE_CAP_EN_(channel) BIT(0 + (channel)) +#define PTP_IO_RE_LTC_SEC_CAP_X (0x0AC8) +#define PTP_IO_RE_LTC_NS_CAP_X (0x0ACC) +#define PTP_IO_FE_LTC_SEC_CAP_X (0x0AD0) +#define PTP_IO_FE_LTC_NS_CAP_X (0x0AD4) +#define PTP_IO_EVENT_OUTPUT_CFG (0x0AD8) +#define PTP_IO_EVENT_OUTPUT_CFG_SEL_(channel) BIT(16 + (channel)) +#define PTP_IO_EVENT_OUTPUT_CFG_EN_(channel) BIT(0 + (channel)) +#define PTP_IO_PIN_CFG (0x0ADC) +#define PTP_IO_PIN_CFG_OBUF_TYPE_(channel) BIT(0 + (channel)) +#define PTP_LTC_RD_SEC_HI (0x0AF0) +#define PTP_LTC_RD_SEC_HI_SEC_47_32_MASK_ GENMASK(15, 0) +#define PTP_LTC_RD_SEC_LO (0x0AF4) +#define PTP_LTC_RD_NS (0x0AF8) +#define PTP_LTC_RD_NS_29_0_MASK_ GENMASK(29, 0) +#define PTP_LTC_RD_SUBNS (0x0AFC) +#define PTP_RX_USER_MAC_HI (0x0B00) +#define PTP_RX_USER_MAC_HI_47_32_MASK_ GENMASK(15, 0) +#define PTP_RX_USER_MAC_LO (0x0B04) +#define PTP_RX_USER_IP_ADDR_0 (0x0B20) +#define PTP_RX_USER_IP_ADDR_1 (0x0B24) +#define PTP_RX_USER_IP_ADDR_2 (0x0B28) +#define PTP_RX_USER_IP_ADDR_3 (0x0B2C) +#define PTP_RX_USER_IP_MASK_0 (0x0B30) +#define PTP_RX_USER_IP_MASK_1 (0x0B34) +#define PTP_RX_USER_IP_MASK_2 (0x0B38) +#define PTP_RX_USER_IP_MASK_3 (0x0B3C) +#define PTP_TX_USER_MAC_HI (0x0B40) +#define PTP_TX_USER_MAC_HI_47_32_MASK_ GENMASK(15, 0) +#define PTP_TX_USER_MAC_LO (0x0B44) +#define PTP_TX_USER_IP_ADDR_0 (0x0B60) +#define PTP_TX_USER_IP_ADDR_1 (0x0B64) +#define PTP_TX_USER_IP_ADDR_2 (0x0B68) +#define PTP_TX_USER_IP_ADDR_3 (0x0B6C) +#define PTP_TX_USER_IP_MASK_0 (0x0B70) +#define PTP_TX_USER_IP_MASK_1 (0x0B74) +#define PTP_TX_USER_IP_MASK_2 (0x0B78) +#define PTP_TX_USER_IP_MASK_3 (0x0B7C) + #define DMAC_CFG (0xC00) #define DMAC_CFG_COAL_EN_ BIT(16) #define DMAC_CFG_CH_ARB_SEL_RX_HIGH_ (0x00000000) @@ -483,6 +662,20 @@ #define OTP_STATUS (0x1030) #define OTP_STATUS_BUSY_ BIT(0) +/* Hearthstone OTP block registers */ +#define HS_OTP_BLOCK_BASE (ETH_SYS_REG_ADDR_BASE + \ + ETH_OTP_REG_ADDR_BASE) +#define HS_OTP_PWR_DN (HS_OTP_BLOCK_BASE + 0x0) +#define HS_OTP_ADDR_HIGH (HS_OTP_BLOCK_BASE + 0x4) +#define HS_OTP_ADDR_LOW (HS_OTP_BLOCK_BASE + 0x8) +#define HS_OTP_PRGM_DATA (HS_OTP_BLOCK_BASE + 0x10) +#define HS_OTP_PRGM_MODE (HS_OTP_BLOCK_BASE + 0x14) +#define HS_OTP_READ_DATA (HS_OTP_BLOCK_BASE + 0x18) +#define HS_OTP_FUNC_CMD (HS_OTP_BLOCK_BASE + 0x20) +#define HS_OTP_TST_CMD (HS_OTP_BLOCK_BASE + 0x24) +#define HS_OTP_CMD_GO (HS_OTP_BLOCK_BASE + 0x28) +#define HS_OTP_STATUS (HS_OTP_BLOCK_BASE + 0x30) + /* MAC statistics registers */ #define STAT_RX_FCS_ERRORS (0x1200) #define STAT_RX_ALIGNMENT_ERRORS (0x1204) @@ -541,10 +734,12 @@ #define LAN743X_MAX_RX_CHANNELS (4) #define LAN743X_MAX_TX_CHANNELS (1) +#define PCI11X1X_MAX_TX_CHANNELS (4) struct lan743x_adapter; #define LAN743X_USED_RX_CHANNELS (4) #define LAN743X_USED_TX_CHANNELS (1) +#define PCI11X1X_USED_TX_CHANNELS (4) #define LAN743X_INT_MOD (400) #if (LAN743X_USED_RX_CHANNELS > LAN743X_MAX_RX_CHANNELS) @@ -553,12 +748,17 @@ struct lan743x_adapter; #if (LAN743X_USED_TX_CHANNELS > LAN743X_MAX_TX_CHANNELS) #error Invalid LAN743X_USED_TX_CHANNELS #endif +#if (PCI11X1X_USED_TX_CHANNELS > PCI11X1X_MAX_TX_CHANNELS) +#error Invalid PCI11X1X_USED_TX_CHANNELS +#endif /* PCI */ /* SMSC acquired EFAR late 1990's, MCHP acquired SMSC 2012 */ #define PCI_VENDOR_ID_SMSC PCI_VENDOR_ID_EFAR #define PCI_DEVICE_ID_SMSC_LAN7430 (0x7430) #define PCI_DEVICE_ID_SMSC_LAN7431 (0x7431) +#define PCI_DEVICE_ID_SMSC_A011 (0xA011) +#define PCI_DEVICE_ID_SMSC_A041 (0xA041) #define PCI_CONFIG_LENGTH (0x1000) @@ -607,13 +807,14 @@ struct lan743x_vector { }; #define LAN743X_MAX_VECTOR_COUNT (8) +#define PCI11X1X_MAX_VECTOR_COUNT (16) struct lan743x_intr { int flags; unsigned int irq; - struct lan743x_vector vector_list[LAN743X_MAX_VECTOR_COUNT]; + struct lan743x_vector vector_list[PCI11X1X_MAX_VECTOR_COUNT]; int number_of_vectors; bool using_vectors; @@ -668,6 +869,7 @@ struct lan743x_tx { int last_tail; struct napi_struct napi; + u32 frame_count; struct sk_buff *overflow_skb; }; @@ -721,8 +923,17 @@ struct lan743x_adapter { u8 mac_address[ETH_ALEN]; struct lan743x_phy phy; - struct lan743x_tx tx[LAN743X_MAX_TX_CHANNELS]; - struct lan743x_rx rx[LAN743X_MAX_RX_CHANNELS]; + struct lan743x_tx tx[PCI11X1X_USED_TX_CHANNELS]; + struct lan743x_rx rx[LAN743X_USED_RX_CHANNELS]; + bool is_pci11x1x; + bool is_sgmii_en; + /* protect ethernet syslock */ + spinlock_t eth_syslock_spinlock; + bool eth_syslock_en; + u32 eth_syslock_acquire_cnt; + u8 max_tx_channels; + u8 used_tx_channels; + u8 max_vector_count; #define LAN743X_ADAPTER_FLAG_OTP BIT(0) u32 flags; diff --git a/drivers/net/ethernet/microchip/lan743x_ptp.c b/drivers/net/ethernet/microchip/lan743x_ptp.c index 8b7a8d879083..6a11e2ceb013 100644 --- a/drivers/net/ethernet/microchip/lan743x_ptp.c +++ b/drivers/net/ethernet/microchip/lan743x_ptp.c @@ -25,6 +25,18 @@ static void lan743x_ptp_clock_set(struct lan743x_adapter *adapter, u32 seconds, u32 nano_seconds, u32 sub_nano_seconds); +static int lan743x_get_channel(u32 ch_map) +{ + int idx; + + for (idx = 0; idx < 32; idx++) { + if (ch_map & (0x1 << idx)) + return idx; + } + + return -EINVAL; +} + int lan743x_gpio_init(struct lan743x_adapter *adapter) { struct lan743x_gpio *gpio = &adapter->gpio; @@ -179,6 +191,8 @@ static void lan743x_ptp_release_event_ch(struct lan743x_adapter *adapter, static void lan743x_ptp_clock_get(struct lan743x_adapter *adapter, u32 *seconds, u32 *nano_seconds, u32 *sub_nano_seconds); +static void lan743x_ptp_io_clock_get(struct lan743x_adapter *adapter, + u32 *sec, u32 *nsec, u32 *sub_nsec); static void lan743x_ptp_clock_step(struct lan743x_adapter *adapter, s64 time_step_ns); @@ -407,7 +421,11 @@ static int lan743x_ptpci_gettime64(struct ptp_clock_info *ptpci, u32 nano_seconds = 0; u32 seconds = 0; - lan743x_ptp_clock_get(adapter, &seconds, &nano_seconds, NULL); + if (adapter->is_pci11x1x) + lan743x_ptp_io_clock_get(adapter, &seconds, &nano_seconds, + NULL); + else + lan743x_ptp_clock_get(adapter, &seconds, &nano_seconds, NULL); ts->tv_sec = seconds; ts->tv_nsec = nano_seconds; @@ -671,6 +689,322 @@ static int lan743x_ptp_perout(struct lan743x_adapter *adapter, int on, return ret; } +static void lan743x_ptp_io_perout_off(struct lan743x_adapter *adapter, + u32 index) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + int perout_pin; + int event_ch; + u32 gen_cfg; + int val; + + event_ch = ptp->ptp_io_perout[index]; + if (event_ch >= 0) { + /* set target to far in the future, effectively disabling it */ + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_SEC_X(event_ch), + 0xFFFF0000); + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_NS_X(event_ch), + 0); + + gen_cfg = lan743x_csr_read(adapter, HS_PTP_GENERAL_CONFIG); + gen_cfg &= ~(HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_X_MASK_ + (event_ch)); + gen_cfg &= ~(HS_PTP_GENERAL_CONFIG_EVENT_POL_X_(event_ch)); + gen_cfg |= HS_PTP_GENERAL_CONFIG_RELOAD_ADD_X_(event_ch); + lan743x_csr_write(adapter, HS_PTP_GENERAL_CONFIG, gen_cfg); + if (event_ch) + lan743x_csr_write(adapter, PTP_INT_STS, + PTP_INT_TIMER_INT_B_); + else + lan743x_csr_write(adapter, PTP_INT_STS, + PTP_INT_TIMER_INT_A_); + lan743x_ptp_release_event_ch(adapter, event_ch); + ptp->ptp_io_perout[index] = -1; + } + + perout_pin = ptp_find_pin(ptp->ptp_clock, PTP_PF_PEROUT, index); + + /* Deselect Event output */ + val = lan743x_csr_read(adapter, PTP_IO_EVENT_OUTPUT_CFG); + + /* Disables the output of Local Time Target compare events */ + val &= ~PTP_IO_EVENT_OUTPUT_CFG_EN_(perout_pin); + lan743x_csr_write(adapter, PTP_IO_EVENT_OUTPUT_CFG, val); + + /* Configured as an opendrain driver*/ + val = lan743x_csr_read(adapter, PTP_IO_PIN_CFG); + val &= ~PTP_IO_PIN_CFG_OBUF_TYPE_(perout_pin); + lan743x_csr_write(adapter, PTP_IO_PIN_CFG, val); + /* Dummy read to make sure write operation success */ + val = lan743x_csr_read(adapter, PTP_IO_PIN_CFG); +} + +static int lan743x_ptp_io_perout(struct lan743x_adapter *adapter, int on, + struct ptp_perout_request *perout_request) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + u32 period_sec, period_nsec; + u32 start_sec, start_nsec; + u32 pulse_sec, pulse_nsec; + int pulse_width; + int perout_pin; + int event_ch; + u32 gen_cfg; + u32 index; + int val; + + index = perout_request->index; + event_ch = ptp->ptp_io_perout[index]; + + if (on) { + perout_pin = ptp_find_pin(ptp->ptp_clock, PTP_PF_PEROUT, index); + if (perout_pin < 0) + return -EBUSY; + } else { + lan743x_ptp_io_perout_off(adapter, index); + return 0; + } + + if (event_ch >= LAN743X_PTP_N_EVENT_CHAN) { + /* already on, turn off first */ + lan743x_ptp_io_perout_off(adapter, index); + } + + event_ch = lan743x_ptp_reserve_event_ch(adapter, index); + if (event_ch < 0) { + netif_warn(adapter, drv, adapter->netdev, + "Failed to reserve event channel %d for PEROUT\n", + index); + goto failed; + } + ptp->ptp_io_perout[index] = event_ch; + + if (perout_request->flags & PTP_PEROUT_DUTY_CYCLE) { + pulse_sec = perout_request->on.sec; + pulse_sec += perout_request->on.nsec / 1000000000; + pulse_nsec = perout_request->on.nsec % 1000000000; + } else { + pulse_sec = perout_request->period.sec; + pulse_sec += perout_request->period.nsec / 1000000000; + pulse_nsec = perout_request->period.nsec % 1000000000; + } + + if (pulse_sec == 0) { + if (pulse_nsec >= 400000000) { + pulse_width = PTP_GENERAL_CONFIG_CLOCK_EVENT_200MS_; + } else if (pulse_nsec >= 200000000) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_100MS_; + } else if (pulse_nsec >= 100000000) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_50MS_; + } else if (pulse_nsec >= 20000000) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_10MS_; + } else if (pulse_nsec >= 10000000) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_5MS_; + } else if (pulse_nsec >= 2000000) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_1MS_; + } else if (pulse_nsec >= 1000000) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_500US_; + } else if (pulse_nsec >= 200000) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_100US_; + } else if (pulse_nsec >= 100000) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_50US_; + } else if (pulse_nsec >= 20000) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_10US_; + } else if (pulse_nsec >= 10000) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_5US_; + } else if (pulse_nsec >= 2000) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_1US_; + } else if (pulse_nsec >= 1000) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_500NS_; + } else if (pulse_nsec >= 200) { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_100NS_; + } else { + netif_warn(adapter, drv, adapter->netdev, + "perout period too small, min is 200nS\n"); + goto failed; + } + } else { + pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_200MS_; + } + + /* turn off by setting target far in future */ + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_SEC_X(event_ch), + 0xFFFF0000); + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_NS_X(event_ch), 0); + + /* Configure to pulse every period */ + gen_cfg = lan743x_csr_read(adapter, HS_PTP_GENERAL_CONFIG); + gen_cfg &= ~(HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_X_MASK_(event_ch)); + gen_cfg |= HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_X_SET_ + (event_ch, pulse_width); + gen_cfg |= HS_PTP_GENERAL_CONFIG_EVENT_POL_X_(event_ch); + gen_cfg &= ~(HS_PTP_GENERAL_CONFIG_RELOAD_ADD_X_(event_ch)); + lan743x_csr_write(adapter, HS_PTP_GENERAL_CONFIG, gen_cfg); + + /* set the reload to one toggle cycle */ + period_sec = perout_request->period.sec; + period_sec += perout_request->period.nsec / 1000000000; + period_nsec = perout_request->period.nsec % 1000000000; + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_RELOAD_SEC_X(event_ch), + period_sec); + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_RELOAD_NS_X(event_ch), + period_nsec); + + start_sec = perout_request->start.sec; + start_sec += perout_request->start.nsec / 1000000000; + start_nsec = perout_request->start.nsec % 1000000000; + + /* set the start time */ + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_SEC_X(event_ch), + start_sec); + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_NS_X(event_ch), + start_nsec); + + /* Enable LTC Target Read */ + val = lan743x_csr_read(adapter, PTP_CMD_CTL); + val |= PTP_CMD_CTL_PTP_LTC_TARGET_READ_; + lan743x_csr_write(adapter, PTP_CMD_CTL, val); + + /* Configure as an push/pull driver */ + val = lan743x_csr_read(adapter, PTP_IO_PIN_CFG); + val |= PTP_IO_PIN_CFG_OBUF_TYPE_(perout_pin); + lan743x_csr_write(adapter, PTP_IO_PIN_CFG, val); + + /* Select Event output */ + val = lan743x_csr_read(adapter, PTP_IO_EVENT_OUTPUT_CFG); + if (event_ch) + /* Channel B as the output */ + val |= PTP_IO_EVENT_OUTPUT_CFG_SEL_(perout_pin); + else + /* Channel A as the output */ + val &= ~PTP_IO_EVENT_OUTPUT_CFG_SEL_(perout_pin); + + /* Enables the output of Local Time Target compare events */ + val |= PTP_IO_EVENT_OUTPUT_CFG_EN_(perout_pin); + lan743x_csr_write(adapter, PTP_IO_EVENT_OUTPUT_CFG, val); + + return 0; + +failed: + lan743x_ptp_io_perout_off(adapter, index); + return -ENODEV; +} + +static void lan743x_ptp_io_extts_off(struct lan743x_adapter *adapter, + u32 index) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + struct lan743x_extts *extts; + int val; + + extts = &ptp->extts[index]; + /* PTP Interrupt Enable Clear Register */ + if (extts->flags & PTP_FALLING_EDGE) + val = PTP_INT_EN_FE_EN_CLR_(index); + else + val = PTP_INT_EN_RE_EN_CLR_(index); + lan743x_csr_write(adapter, PTP_INT_EN_CLR, val); + + /* Disables PTP-IO edge lock */ + val = lan743x_csr_read(adapter, PTP_IO_CAP_CONFIG); + if (extts->flags & PTP_FALLING_EDGE) { + val &= ~PTP_IO_CAP_CONFIG_LOCK_FE_(index); + val &= ~PTP_IO_CAP_CONFIG_FE_CAP_EN_(index); + } else { + val &= ~PTP_IO_CAP_CONFIG_LOCK_RE_(index); + val &= ~PTP_IO_CAP_CONFIG_RE_CAP_EN_(index); + } + lan743x_csr_write(adapter, PTP_IO_CAP_CONFIG, val); + + /* PTP-IO De-select register */ + val = lan743x_csr_read(adapter, PTP_IO_SEL); + val &= ~PTP_IO_SEL_MASK_; + lan743x_csr_write(adapter, PTP_IO_SEL, val); + + /* Clear timestamp */ + memset(&extts->ts, 0, sizeof(struct timespec64)); + extts->flags = 0; +} + +static int lan743x_ptp_io_event_cap_en(struct lan743x_adapter *adapter, + u32 flags, u32 channel) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + int val; + + if ((flags & PTP_EXTTS_EDGES) == PTP_EXTTS_EDGES) + return -EOPNOTSUPP; + + mutex_lock(&ptp->command_lock); + /* PTP-IO Event Capture Enable */ + val = lan743x_csr_read(adapter, PTP_IO_CAP_CONFIG); + if (flags & PTP_FALLING_EDGE) { + val &= ~PTP_IO_CAP_CONFIG_LOCK_RE_(channel); + val &= ~PTP_IO_CAP_CONFIG_RE_CAP_EN_(channel); + val |= PTP_IO_CAP_CONFIG_LOCK_FE_(channel); + val |= PTP_IO_CAP_CONFIG_FE_CAP_EN_(channel); + } else { + /* Rising eventing as Default */ + val &= ~PTP_IO_CAP_CONFIG_LOCK_FE_(channel); + val &= ~PTP_IO_CAP_CONFIG_FE_CAP_EN_(channel); + val |= PTP_IO_CAP_CONFIG_LOCK_RE_(channel); + val |= PTP_IO_CAP_CONFIG_RE_CAP_EN_(channel); + } + lan743x_csr_write(adapter, PTP_IO_CAP_CONFIG, val); + + /* PTP-IO Select */ + val = lan743x_csr_read(adapter, PTP_IO_SEL); + val &= ~PTP_IO_SEL_MASK_; + val |= channel << PTP_IO_SEL_SHIFT_; + lan743x_csr_write(adapter, PTP_IO_SEL, val); + + /* PTP Interrupt Enable Register */ + if (flags & PTP_FALLING_EDGE) + val = PTP_INT_EN_FE_EN_SET_(channel); + else + val = PTP_INT_EN_RE_EN_SET_(channel); + lan743x_csr_write(adapter, PTP_INT_EN_SET, val); + + mutex_unlock(&ptp->command_lock); + + return 0; +} + +static int lan743x_ptp_io_extts(struct lan743x_adapter *adapter, int on, + struct ptp_extts_request *extts_request) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + u32 flags = extts_request->flags; + u32 index = extts_request->index; + struct lan743x_extts *extts; + int extts_pin; + int ret = 0; + + extts = &ptp->extts[index]; + + if (on) { + extts_pin = ptp_find_pin(ptp->ptp_clock, PTP_PF_EXTTS, index); + if (extts_pin < 0) + return -EBUSY; + + ret = lan743x_ptp_io_event_cap_en(adapter, flags, index); + if (!ret) + extts->flags = flags; + } else { + lan743x_ptp_io_extts_off(adapter, index); + } + + return ret; +} + static int lan743x_ptpci_enable(struct ptp_clock_info *ptpci, struct ptp_clock_request *request, int on) { @@ -682,11 +1016,19 @@ static int lan743x_ptpci_enable(struct ptp_clock_info *ptpci, if (request) { switch (request->type) { case PTP_CLK_REQ_EXTTS: + if (request->extts.index < ptpci->n_ext_ts) + return lan743x_ptp_io_extts(adapter, on, + &request->extts); return -EINVAL; case PTP_CLK_REQ_PEROUT: - if (request->perout.index < ptpci->n_per_out) - return lan743x_ptp_perout(adapter, on, + if (request->perout.index < ptpci->n_per_out) { + if (adapter->is_pci11x1x) + return lan743x_ptp_io_perout(adapter, on, + &request->perout); + else + return lan743x_ptp_perout(adapter, on, &request->perout); + } return -EINVAL; case PTP_CLK_REQ_PPS: return -EINVAL; @@ -715,8 +1057,8 @@ static int lan743x_ptpci_verify_pin_config(struct ptp_clock_info *ptp, switch (func) { case PTP_PF_NONE: case PTP_PF_PEROUT: - break; case PTP_PF_EXTTS: + break; case PTP_PF_PHYSYNC: default: result = -1; @@ -725,6 +1067,33 @@ static int lan743x_ptpci_verify_pin_config(struct ptp_clock_info *ptp, return result; } +static void lan743x_ptp_io_event_clock_get(struct lan743x_adapter *adapter, + bool fe, u8 channel, + struct timespec64 *ts) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + struct lan743x_extts *extts; + u32 sec, nsec; + + mutex_lock(&ptp->command_lock); + if (fe) { + sec = lan743x_csr_read(adapter, PTP_IO_FE_LTC_SEC_CAP_X); + nsec = lan743x_csr_read(adapter, PTP_IO_FE_LTC_NS_CAP_X); + } else { + sec = lan743x_csr_read(adapter, PTP_IO_RE_LTC_SEC_CAP_X); + nsec = lan743x_csr_read(adapter, PTP_IO_RE_LTC_NS_CAP_X); + } + + mutex_unlock(&ptp->command_lock); + + /* Update Local timestamp */ + extts = &ptp->extts[channel]; + extts->ts.tv_sec = sec; + extts->ts.tv_nsec = nsec; + ts->tv_sec = sec; + ts->tv_nsec = nsec; +} + static long lan743x_ptpci_do_aux_work(struct ptp_clock_info *ptpci) { struct lan743x_ptp *ptp = @@ -733,41 +1102,121 @@ static long lan743x_ptpci_do_aux_work(struct ptp_clock_info *ptpci) container_of(ptp, struct lan743x_adapter, ptp); u32 cap_info, cause, header, nsec, seconds; bool new_timestamp_available = false; + struct ptp_clock_event ptp_event; + struct timespec64 ts; + int ptp_int_sts; int count = 0; + int channel; + s64 ns; - while ((count < 100) && - (lan743x_csr_read(adapter, PTP_INT_STS) & PTP_INT_BIT_TX_TS_)) { + ptp_int_sts = lan743x_csr_read(adapter, PTP_INT_STS); + while ((count < 100) && ptp_int_sts) { count++; - cap_info = lan743x_csr_read(adapter, PTP_CAP_INFO); - if (PTP_CAP_INFO_TX_TS_CNT_GET_(cap_info) > 0) { - seconds = lan743x_csr_read(adapter, - PTP_TX_EGRESS_SEC); - nsec = lan743x_csr_read(adapter, PTP_TX_EGRESS_NS); - cause = (nsec & - PTP_TX_EGRESS_NS_CAPTURE_CAUSE_MASK_); - header = lan743x_csr_read(adapter, - PTP_TX_MSG_HEADER); + if (ptp_int_sts & PTP_INT_BIT_TX_TS_) { + cap_info = lan743x_csr_read(adapter, PTP_CAP_INFO); - if (cause == PTP_TX_EGRESS_NS_CAPTURE_CAUSE_SW_) { - nsec &= PTP_TX_EGRESS_NS_TS_NS_MASK_; - lan743x_ptp_tx_ts_enqueue_ts(adapter, - seconds, nsec, - header); - new_timestamp_available = true; - } else if (cause == - PTP_TX_EGRESS_NS_CAPTURE_CAUSE_AUTO_) { - netif_err(adapter, drv, adapter->netdev, - "Auto capture cause not supported\n"); + if (PTP_CAP_INFO_TX_TS_CNT_GET_(cap_info) > 0) { + seconds = lan743x_csr_read(adapter, + PTP_TX_EGRESS_SEC); + nsec = lan743x_csr_read(adapter, + PTP_TX_EGRESS_NS); + cause = (nsec & + PTP_TX_EGRESS_NS_CAPTURE_CAUSE_MASK_); + header = lan743x_csr_read(adapter, + PTP_TX_MSG_HEADER); + + if (cause == + PTP_TX_EGRESS_NS_CAPTURE_CAUSE_SW_) { + nsec &= PTP_TX_EGRESS_NS_TS_NS_MASK_; + lan743x_ptp_tx_ts_enqueue_ts(adapter, + seconds, + nsec, + header); + new_timestamp_available = true; + } else if (cause == + PTP_TX_EGRESS_NS_CAPTURE_CAUSE_AUTO_) { + netif_err(adapter, drv, adapter->netdev, + "Auto capture cause not supported\n"); + } else { + netif_warn(adapter, drv, adapter->netdev, + "unknown tx timestamp capture cause\n"); + } } else { netif_warn(adapter, drv, adapter->netdev, - "unknown tx timestamp capture cause\n"); + "TX TS INT but no TX TS CNT\n"); } - } else { - netif_warn(adapter, drv, adapter->netdev, - "TX TS INT but no TX TS CNT\n"); + lan743x_csr_write(adapter, PTP_INT_STS, + PTP_INT_BIT_TX_TS_); } - lan743x_csr_write(adapter, PTP_INT_STS, PTP_INT_BIT_TX_TS_); + + if (ptp_int_sts & PTP_INT_IO_FE_MASK_) { + do { + channel = lan743x_get_channel((ptp_int_sts & + PTP_INT_IO_FE_MASK_) >> + PTP_INT_IO_FE_SHIFT_); + if (channel >= 0 && + channel < PCI11X1X_PTP_IO_MAX_CHANNELS) { + lan743x_ptp_io_event_clock_get(adapter, + true, + channel, + &ts); + /* PTP Falling Event post */ + ns = timespec64_to_ns(&ts); + ptp_event.timestamp = ns; + ptp_event.index = channel; + ptp_event.type = PTP_CLOCK_EXTTS; + ptp_clock_event(ptp->ptp_clock, + &ptp_event); + lan743x_csr_write(adapter, PTP_INT_STS, + PTP_INT_IO_FE_SET_ + (channel)); + ptp_int_sts &= ~(1 << + (PTP_INT_IO_FE_SHIFT_ + + channel)); + } else { + /* Clear falling event interrupts */ + lan743x_csr_write(adapter, PTP_INT_STS, + PTP_INT_IO_FE_MASK_); + ptp_int_sts &= ~PTP_INT_IO_FE_MASK_; + } + } while (ptp_int_sts & PTP_INT_IO_FE_MASK_); + } + + if (ptp_int_sts & PTP_INT_IO_RE_MASK_) { + do { + channel = lan743x_get_channel((ptp_int_sts & + PTP_INT_IO_RE_MASK_) >> + PTP_INT_IO_RE_SHIFT_); + if (channel >= 0 && + channel < PCI11X1X_PTP_IO_MAX_CHANNELS) { + lan743x_ptp_io_event_clock_get(adapter, + false, + channel, + &ts); + /* PTP Rising Event post */ + ns = timespec64_to_ns(&ts); + ptp_event.timestamp = ns; + ptp_event.index = channel; + ptp_event.type = PTP_CLOCK_EXTTS; + ptp_clock_event(ptp->ptp_clock, + &ptp_event); + lan743x_csr_write(adapter, PTP_INT_STS, + PTP_INT_IO_RE_SET_ + (channel)); + ptp_int_sts &= ~(1 << + (PTP_INT_IO_RE_SHIFT_ + + channel)); + } else { + /* Clear Rising event interrupt */ + lan743x_csr_write(adapter, PTP_INT_STS, + PTP_INT_IO_RE_MASK_); + ptp_int_sts &= ~PTP_INT_IO_RE_MASK_; + } + } while (ptp_int_sts & PTP_INT_IO_RE_MASK_); + } + + ptp_int_sts = lan743x_csr_read(adapter, PTP_INT_STS); } if (new_timestamp_available) @@ -802,6 +1251,28 @@ static void lan743x_ptp_clock_get(struct lan743x_adapter *adapter, mutex_unlock(&ptp->command_lock); } +static void lan743x_ptp_io_clock_get(struct lan743x_adapter *adapter, + u32 *sec, u32 *nsec, u32 *sub_nsec) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + + mutex_lock(&ptp->command_lock); + lan743x_csr_write(adapter, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_READ_); + lan743x_ptp_wait_till_cmd_done(adapter, PTP_CMD_CTL_PTP_CLOCK_READ_); + + if (sec) + (*sec) = lan743x_csr_read(adapter, PTP_LTC_RD_SEC_LO); + + if (nsec) + (*nsec) = lan743x_csr_read(adapter, PTP_LTC_RD_NS); + + if (sub_nsec) + (*sub_nsec) = + lan743x_csr_read(adapter, PTP_LTC_RD_SUBNS); + + mutex_unlock(&ptp->command_lock); +} + static void lan743x_ptp_clock_step(struct lan743x_adapter *adapter, s64 time_step_ns) { @@ -815,8 +1286,12 @@ static void lan743x_ptp_clock_step(struct lan743x_adapter *adapter, if (time_step_ns > 15000000000LL) { /* convert to clock set */ - lan743x_ptp_clock_get(adapter, &unsigned_seconds, - &nano_seconds, NULL); + if (adapter->is_pci11x1x) + lan743x_ptp_io_clock_get(adapter, &unsigned_seconds, + &nano_seconds, NULL); + else + lan743x_ptp_clock_get(adapter, &unsigned_seconds, + &nano_seconds, NULL); unsigned_seconds += div_u64_rem(time_step_ns, 1000000000LL, &remainder); nano_seconds += remainder; @@ -831,8 +1306,13 @@ static void lan743x_ptp_clock_step(struct lan743x_adapter *adapter, /* convert to clock set */ time_step_ns = -time_step_ns; - lan743x_ptp_clock_get(adapter, &unsigned_seconds, - &nano_seconds, NULL); + if (adapter->is_pci11x1x) { + lan743x_ptp_io_clock_get(adapter, &unsigned_seconds, + &nano_seconds, NULL); + } else { + lan743x_ptp_clock_get(adapter, &unsigned_seconds, + &nano_seconds, NULL); + } unsigned_seconds -= div_u64_rem(time_step_ns, 1000000000LL, &remainder); nano_seconds_step = remainder; @@ -1061,6 +1541,8 @@ int lan743x_ptp_open(struct lan743x_adapter *adapter) n_pins = LAN7430_N_GPIO; break; case ID_REV_ID_LAN7431_: + case ID_REV_ID_A011_: + case ID_REV_ID_A041_: n_pins = LAN7431_N_GPIO; break; default: @@ -1088,10 +1570,10 @@ int lan743x_ptp_open(struct lan743x_adapter *adapter) adapter->netdev->dev_addr); ptp->ptp_clock_info.max_adj = LAN743X_PTP_MAX_FREQ_ADJ_IN_PPB; ptp->ptp_clock_info.n_alarm = 0; - ptp->ptp_clock_info.n_ext_ts = 0; + ptp->ptp_clock_info.n_ext_ts = LAN743X_PTP_N_EXTTS; ptp->ptp_clock_info.n_per_out = LAN743X_PTP_N_EVENT_CHAN; ptp->ptp_clock_info.n_pins = n_pins; - ptp->ptp_clock_info.pps = 0; + ptp->ptp_clock_info.pps = LAN743X_PTP_N_PPS; ptp->ptp_clock_info.pin_config = ptp->pin_config; ptp->ptp_clock_info.adjfine = lan743x_ptpci_adjfine; ptp->ptp_clock_info.adjfreq = lan743x_ptpci_adjfreq; @@ -1307,21 +1789,21 @@ int lan743x_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) switch (config.tx_type) { case HWTSTAMP_TX_OFF: - for (index = 0; index < LAN743X_MAX_TX_CHANNELS; - index++) + for (index = 0; index < adapter->used_tx_channels; + index++) lan743x_tx_set_timestamping_mode(&adapter->tx[index], false, false); lan743x_ptp_set_sync_ts_insert(adapter, false); break; case HWTSTAMP_TX_ON: - for (index = 0; index < LAN743X_MAX_TX_CHANNELS; + for (index = 0; index < adapter->used_tx_channels; index++) lan743x_tx_set_timestamping_mode(&adapter->tx[index], true, false); lan743x_ptp_set_sync_ts_insert(adapter, false); break; case HWTSTAMP_TX_ONESTEP_SYNC: - for (index = 0; index < LAN743X_MAX_TX_CHANNELS; + for (index = 0; index < adapter->used_tx_channels; index++) lan743x_tx_set_timestamping_mode(&adapter->tx[index], true, true); diff --git a/drivers/net/ethernet/microchip/lan743x_ptp.h b/drivers/net/ethernet/microchip/lan743x_ptp.h index 7663bf5d2e33..e26d4eff7133 100644 --- a/drivers/net/ethernet/microchip/lan743x_ptp.h +++ b/drivers/net/ethernet/microchip/lan743x_ptp.h @@ -18,6 +18,9 @@ */ #define LAN743X_PTP_N_EVENT_CHAN 2 #define LAN743X_PTP_N_PEROUT LAN743X_PTP_N_EVENT_CHAN +#define LAN743X_PTP_N_EXTTS 4 +#define LAN743X_PTP_N_PPS 0 +#define PCI11X1X_PTP_IO_MAX_CHANNELS 8 struct lan743x_adapter; @@ -60,6 +63,11 @@ struct lan743x_ptp_perout { int gpio_pin; /* GPIO pin where output appears */ }; +struct lan743x_extts { + int flags; + struct timespec64 ts; +}; + struct lan743x_ptp { int flags; @@ -72,6 +80,8 @@ struct lan743x_ptp { unsigned long used_event_ch; struct lan743x_ptp_perout perout[LAN743X_PTP_N_PEROUT]; + int ptp_io_perout[LAN743X_PTP_N_PEROUT]; /* PTP event channel (0=channel A, 1=channel B) */ + struct lan743x_extts extts[LAN743X_PTP_N_EXTTS]; bool leds_multiplexed; bool led_enabled[LAN7430_N_LED]; diff --git a/drivers/net/ethernet/microchip/lan966x/Kconfig b/drivers/net/ethernet/microchip/lan966x/Kconfig index ac273f84b69e..4241ff0e5098 100644 --- a/drivers/net/ethernet/microchip/lan966x/Kconfig +++ b/drivers/net/ethernet/microchip/lan966x/Kconfig @@ -1,5 +1,6 @@ config LAN966X_SWITCH tristate "Lan966x switch driver" + depends on PTP_1588_CLOCK_OPTIONAL depends on HAS_IOMEM depends on OF depends on NET_SWITCHDEV diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile index 040cfff9f577..a9ffc719aa0e 100644 --- a/drivers/net/ethernet/microchip/lan966x/Makefile +++ b/drivers/net/ethernet/microchip/lan966x/Makefile @@ -7,4 +7,5 @@ obj-$(CONFIG_LAN966X_SWITCH) += lan966x-switch.o lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ lan966x_mac.o lan966x_ethtool.o lan966x_switchdev.o \ - lan966x_vlan.o lan966x_fdb.o lan966x_mdb.o + lan966x_vlan.o lan966x_fdb.o lan966x_mdb.o \ + lan966x_ptp.o diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c index 614f12c2fe6a..e58a27fd8b50 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c @@ -545,6 +545,39 @@ static int lan966x_set_pauseparam(struct net_device *dev, return phylink_ethtool_set_pauseparam(port->phylink, pause); } +static int lan966x_get_ts_info(struct net_device *dev, + struct ethtool_ts_info *info) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + struct lan966x_phc *phc; + + if (!lan966x->ptp) + return ethtool_op_get_ts_info(dev, info); + + phc = &lan966x->phc[LAN966X_PHC_PORT]; + + info->phc_index = phc->clock ? ptp_clock_index(phc->clock) : -1; + if (info->phc_index == -1) { + info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + return 0; + } + info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) | + BIT(HWTSTAMP_TX_ONESTEP_SYNC); + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | + BIT(HWTSTAMP_FILTER_ALL); + + return 0; +} + const struct ethtool_ops lan966x_ethtool_ops = { .get_link_ksettings = lan966x_get_link_ksettings, .set_link_ksettings = lan966x_set_link_ksettings, @@ -556,6 +589,7 @@ const struct ethtool_ops lan966x_ethtool_ops = { .get_eth_mac_stats = lan966x_get_eth_mac_stats, .get_rmon_stats = lan966x_get_eth_rmon_stats, .get_link = ethtool_op_get_link, + .get_ts_info = lan966x_get_ts_info, }; static void lan966x_check_stats_work(struct work_struct *work) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 1f60fd125a1d..e1bcb28039dc 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -4,11 +4,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include "lan966x_main.h" @@ -44,6 +46,7 @@ static const struct lan966x_main_io_resource lan966x_main_iomap[] = { { TARGET_ORG, 0, 1 }, /* 0xe2000000 */ { TARGET_GCB, 0x4000, 1 }, /* 0xe2004000 */ { TARGET_QS, 0x8000, 1 }, /* 0xe2008000 */ + { TARGET_PTP, 0xc000, 1 }, /* 0xe200c000 */ { TARGET_CHIP_TOP, 0x10000, 1 }, /* 0xe2010000 */ { TARGET_REW, 0x14000, 1 }, /* 0xe2014000 */ { TARGET_SYS, 0x28000, 1 }, /* 0xe2028000 */ @@ -182,6 +185,9 @@ static int lan966x_port_inj_ready(struct lan966x *lan966x, u8 grp) { u32 val; + if (lan_rd(lan966x, QS_INJ_STATUS) & QS_INJ_STATUS_FIFO_RDY_SET(BIT(grp))) + return 0; + return readx_poll_timeout_atomic(lan966x_port_inj_status, lan966x, val, QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp), READL_SLEEP_US, READL_TIMEOUT_US); @@ -201,7 +207,7 @@ static int lan966x_port_ifh_xmit(struct sk_buff *skb, val = lan_rd(lan966x, QS_INJ_STATUS); if (!(QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp)) || (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp))) - return NETDEV_TX_BUSY; + goto err; /* Write start of frame */ lan_wr(QS_INJ_CTRL_GAP_SIZE_SET(1) | @@ -213,7 +219,7 @@ static int lan966x_port_ifh_xmit(struct sk_buff *skb, /* Wait until the fifo is ready */ err = lan966x_port_inj_ready(lan966x, grp); if (err) - return NETDEV_TX_BUSY; + goto err; lan_wr((__force u32)ifh[i], lan966x, QS_INJ_WR(grp)); } @@ -225,7 +231,7 @@ static int lan966x_port_ifh_xmit(struct sk_buff *skb, /* Wait until the fifo is ready */ err = lan966x_port_inj_ready(lan966x, grp); if (err) - return NETDEV_TX_BUSY; + goto err; lan_wr(((u32 *)skb->data)[i], lan966x, QS_INJ_WR(grp)); } @@ -235,7 +241,7 @@ static int lan966x_port_ifh_xmit(struct sk_buff *skb, /* Wait until the fifo is ready */ err = lan966x_port_inj_ready(lan966x, grp); if (err) - return NETDEV_TX_BUSY; + goto err; lan_wr(0, lan966x, QS_INJ_WR(grp)); ++i; @@ -255,8 +261,19 @@ static int lan966x_port_ifh_xmit(struct sk_buff *skb, dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && + LAN966X_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP) + return NETDEV_TX_OK; + dev_consume_skb_any(skb); return NETDEV_TX_OK; + +err: + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && + LAN966X_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP) + lan966x_ptp_txtstamp_release(port, skb); + + return NETDEV_TX_BUSY; } static void lan966x_ifh_set_bypass(void *ifh, u64 bypass) @@ -289,10 +306,24 @@ static void lan966x_ifh_set_vid(void *ifh, u64 vid) IFH_POS_TCI, IFH_LEN * 4, PACK, 0); } +static void lan966x_ifh_set_rew_op(void *ifh, u64 rew_op) +{ + packing(ifh, &rew_op, IFH_POS_REW_CMD + IFH_WID_REW_CMD - 1, + IFH_POS_REW_CMD, IFH_LEN * 4, PACK, 0); +} + +static void lan966x_ifh_set_timestamp(void *ifh, u64 timestamp) +{ + packing(ifh, ×tamp, IFH_POS_TIMESTAMP + IFH_WID_TIMESTAMP - 1, + IFH_POS_TIMESTAMP, IFH_LEN * 4, PACK, 0); +} + static int lan966x_port_xmit(struct sk_buff *skb, struct net_device *dev) { struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; __be32 ifh[IFH_LEN]; + int err; memset(ifh, 0x0, sizeof(__be32) * IFH_LEN); @@ -302,7 +333,20 @@ static int lan966x_port_xmit(struct sk_buff *skb, struct net_device *dev) lan966x_ifh_set_ipv(ifh, skb->priority >= 7 ? 0x7 : skb->priority); lan966x_ifh_set_vid(ifh, skb_vlan_tag_get(skb)); - return lan966x_port_ifh_xmit(skb, ifh, dev); + if (port->lan966x->ptp && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { + err = lan966x_ptp_txtstamp_request(port, skb); + if (err) + return err; + + lan966x_ifh_set_rew_op(ifh, LAN966X_SKB_CB(skb)->rew_op); + lan966x_ifh_set_timestamp(ifh, LAN966X_SKB_CB(skb)->ts_id); + } + + spin_lock(&lan966x->tx_lock); + err = lan966x_port_ifh_xmit(skb, ifh, dev); + spin_unlock(&lan966x->tx_lock); + + return err; } static int lan966x_port_change_mtu(struct net_device *dev, int new_mtu) @@ -350,6 +394,23 @@ static int lan966x_port_get_parent_id(struct net_device *dev, return 0; } +static int lan966x_port_ioctl(struct net_device *dev, struct ifreq *ifr, + int cmd) +{ + struct lan966x_port *port = netdev_priv(dev); + + if (!phy_has_hwtstamp(dev->phydev) && port->lan966x->ptp) { + switch (cmd) { + case SIOCSHWTSTAMP: + return lan966x_ptp_hwtstamp_set(port, ifr); + case SIOCGHWTSTAMP: + return lan966x_ptp_hwtstamp_get(port, ifr); + } + } + + return phy_mii_ioctl(dev->phydev, ifr, cmd); +} + static const struct net_device_ops lan966x_port_netdev_ops = { .ndo_open = lan966x_port_open, .ndo_stop = lan966x_port_stop, @@ -360,6 +421,7 @@ static const struct net_device_ops lan966x_port_netdev_ops = { .ndo_get_stats64 = lan966x_stats_get, .ndo_set_mac_address = lan966x_port_set_mac_address, .ndo_get_port_parent_id = lan966x_port_get_parent_id, + .ndo_eth_ioctl = lan966x_port_ioctl, }; bool lan966x_netdevice_check(const struct net_device *dev) @@ -367,6 +429,33 @@ bool lan966x_netdevice_check(const struct net_device *dev) return dev->netdev_ops == &lan966x_port_netdev_ops; } +static bool lan966x_hw_offload(struct lan966x *lan966x, u32 port, + struct sk_buff *skb) +{ + u32 val; + + /* The IGMP and MLD frames are not forward by the HW if + * multicast snooping is enabled, therefor don't mark as + * offload to allow the SW to forward the frames accordingly. + */ + val = lan_rd(lan966x, ANA_CPU_FWD_CFG(port)); + if (!(val & (ANA_CPU_FWD_CFG_IGMP_REDIR_ENA | + ANA_CPU_FWD_CFG_MLD_REDIR_ENA))) + return true; + + if (skb->protocol == htons(ETH_P_IP) && + ip_hdr(skb)->protocol == IPPROTO_IGMP) + return false; + + if (IS_ENABLED(CONFIG_IPV6) && + skb->protocol == htons(ETH_P_IPV6) && + ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) && + !ipv6_mc_check_mld(skb)) + return false; + + return true; +} + static int lan966x_port_xtr_status(struct lan966x *lan966x, u8 grp) { return lan_rd(lan966x, QS_XTR_RD(grp)); @@ -434,6 +523,12 @@ static void lan966x_ifh_get_len(void *ifh, u64 *len) IFH_POS_LEN, IFH_LEN * 4, UNPACK, 0); } +static void lan966x_ifh_get_timestamp(void *ifh, u64 *timestamp) +{ + packing(ifh, timestamp, IFH_POS_TIMESTAMP + IFH_WID_TIMESTAMP - 1, + IFH_POS_TIMESTAMP, IFH_LEN * 4, UNPACK, 0); +} + static irqreturn_t lan966x_xtr_irq_handler(int irq, void *args) { struct lan966x *lan966x = args; @@ -443,10 +538,10 @@ static irqreturn_t lan966x_xtr_irq_handler(int irq, void *args) return IRQ_NONE; do { + u64 src_port, len, timestamp; struct net_device *dev; struct sk_buff *skb; int sz = 0, buf_len; - u64 src_port, len; u32 ifh[IFH_LEN]; u32 *buf; u32 val; @@ -461,6 +556,7 @@ static irqreturn_t lan966x_xtr_irq_handler(int irq, void *args) lan966x_ifh_get_src_port(ifh, &src_port); lan966x_ifh_get_len(ifh, &len); + lan966x_ifh_get_timestamp(ifh, ×tamp); WARN_ON(src_port >= lan966x->num_phys_ports); @@ -501,12 +597,20 @@ static irqreturn_t lan966x_xtr_irq_handler(int irq, void *args) *buf = val; } + lan966x_ptp_rxtstamp(lan966x, skb, timestamp); skb->protocol = eth_type_trans(skb, dev); - if (lan966x->bridge_mask & BIT(src_port)) + if (lan966x->bridge_mask & BIT(src_port)) { skb->offload_fwd_mark = 1; - netif_rx_ni(skb); + skb_reset_network_header(skb); + if (!lan966x_hw_offload(lan966x, src_port, skb)) + skb->offload_fwd_mark = 0; + } + + if (!skb_defer_rx_timestamp(skb)) + netif_rx(skb); + dev->stats.rx_bytes += len; dev->stats.rx_packets++; @@ -628,7 +732,6 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, } port->phylink = phylink; - phylink_set_pcs(phylink, &port->phylink_pcs); err = register_netdev(dev); if (err) { @@ -708,7 +811,7 @@ static void lan966x_init(struct lan966x *lan966x) /* Setup flooding PGIDs */ lan_wr(ANA_FLOODING_IPMC_FLD_MC4_DATA_SET(PGID_MCIPV4) | ANA_FLOODING_IPMC_FLD_MC4_CTRL_SET(PGID_MC) | - ANA_FLOODING_IPMC_FLD_MC6_DATA_SET(PGID_MC) | + ANA_FLOODING_IPMC_FLD_MC6_DATA_SET(PGID_MCIPV6) | ANA_FLOODING_IPMC_FLD_MC6_CTRL_SET(PGID_MC), lan966x, ANA_FLOODING_IPMC); @@ -770,6 +873,10 @@ static void lan966x_init(struct lan966x *lan966x) ANA_PGID_PGID, lan966x, ANA_PGID(PGID_MCIPV4)); + lan_rmw(GENMASK(lan966x->num_phys_ports - 1, 0), + ANA_PGID_PGID, + lan966x, ANA_PGID(PGID_MCIPV6)); + /* Unicast to all other ports */ lan_rmw(GENMASK(lan966x->num_phys_ports - 1, 0), ANA_PGID_PGID, @@ -786,6 +893,8 @@ static void lan966x_init(struct lan966x *lan966x) lan_rmw(ANA_ANAINTR_INTR_ENA_SET(1), ANA_ANAINTR_INTR_ENA, lan966x, ANA_ANAINTR); + + spin_lock_init(&lan966x->tx_lock); } static int lan966x_ram_init(struct lan966x *lan966x) @@ -897,6 +1006,17 @@ static int lan966x_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, err, "Unable to use ana irq"); } + lan966x->ptp_irq = platform_get_irq_byname(pdev, "ptp"); + if (lan966x->ptp_irq > 0) { + err = devm_request_threaded_irq(&pdev->dev, lan966x->ptp_irq, NULL, + lan966x_ptp_irq_handler, IRQF_ONESHOT, + "ptp irq", lan966x); + if (err) + return dev_err_probe(&pdev->dev, err, "Unable to use ptp irq"); + + lan966x->ptp = 1; + } + /* init switch */ lan966x_init(lan966x); lan966x_stats_init(lan966x); @@ -931,8 +1051,15 @@ static int lan966x_probe(struct platform_device *pdev) if (err) goto cleanup_ports; + err = lan966x_ptp_init(lan966x); + if (err) + goto cleanup_fdb; + return 0; +cleanup_fdb: + lan966x_fdb_deinit(lan966x); + cleanup_ports: fwnode_handle_put(portnp); @@ -958,6 +1085,7 @@ static int lan966x_remove(struct platform_device *pdev) lan966x_mac_purge_entries(lan966x); lan966x_mdb_deinit(lan966x); lan966x_fdb_deinit(lan966x); + lan966x_ptp_deinit(lan966x); return 0; } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index 99c6d0a9f946..ae282da1da74 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "lan966x_regs.h" @@ -50,6 +51,13 @@ #define LAN966X_SPEED_100 2 #define LAN966X_SPEED_10 3 +#define LAN966X_PHC_COUNT 3 +#define LAN966X_PHC_PORT 0 + +#define IFH_REW_OP_NOOP 0x0 +#define IFH_REW_OP_ONE_STEP_PTP 0x3 +#define IFH_REW_OP_TWO_STEP_PTP 0x4 + /* MAC table entry types. * ENTRYTYPE_NORMAL is subject to aging. * ENTRYTYPE_LOCKED is not subject to aging. @@ -70,6 +78,24 @@ struct lan966x_stat_layout { char name[ETH_GSTRING_LEN]; }; +struct lan966x_phc { + struct ptp_clock *clock; + struct ptp_clock_info info; + struct hwtstamp_config hwtstamp_config; + struct lan966x *lan966x; + u8 index; +}; + +struct lan966x_skb_cb { + u8 rew_op; + u16 ts_id; + unsigned long jiffies; +}; + +#define LAN966X_PTP_TIMEOUT msecs_to_jiffies(10) +#define LAN966X_SKB_CB(skb) \ + ((struct lan966x_skb_cb *)((skb)->cb)) + struct lan966x { struct device *dev; @@ -82,6 +108,8 @@ struct lan966x { u8 base_mac[ETH_ALEN]; + spinlock_t tx_lock; /* lock for frame transmition */ + struct net_device *bridge; u16 bridge_mask; u16 bridge_fwd_mask; @@ -105,6 +133,7 @@ struct lan966x { /* interrupts */ int xtr_irq; int ana_irq; + int ptp_irq; /* worqueue for fdb */ struct workqueue_struct *fdb_work; @@ -113,6 +142,14 @@ struct lan966x { /* mdb */ struct list_head mdb_entries; struct list_head pgid_entries; + + /* ptp */ + bool ptp; + struct lan966x_phc phc[LAN966X_PHC_COUNT]; + spinlock_t ptp_clock_lock; /* lock for phc */ + spinlock_t ptp_ts_id_lock; /* lock for ts_id */ + struct mutex ptp_lock; /* lock for ptp interface state */ + u16 ptp_skbs; }; struct lan966x_port_config { @@ -135,6 +172,7 @@ struct lan966x_port { bool vlan_aware; bool learn_ena; + bool mcast_ena; struct phylink_config phylink_config; struct phylink_pcs phylink_pcs; @@ -142,6 +180,10 @@ struct lan966x_port { struct phylink *phylink; struct phy *serdes; struct fwnode_handle *fwnode; + + u8 ptp_cmd; + u16 ts_id; + struct sk_buff_head tx_skbs; }; extern const struct phylink_mac_ops lan966x_phylink_mac_ops; @@ -227,6 +269,20 @@ int lan966x_handle_port_mdb_del(struct lan966x_port *port, const struct switchdev_obj *obj); void lan966x_mdb_erase_entries(struct lan966x *lan966x, u16 vid); void lan966x_mdb_write_entries(struct lan966x *lan966x, u16 vid); +void lan966x_mdb_clear_entries(struct lan966x *lan966x); +void lan966x_mdb_restore_entries(struct lan966x *lan966x); + +int lan966x_ptp_init(struct lan966x *lan966x); +void lan966x_ptp_deinit(struct lan966x *lan966x); +int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr); +int lan966x_ptp_hwtstamp_get(struct lan966x_port *port, struct ifreq *ifr); +void lan966x_ptp_rxtstamp(struct lan966x *lan966x, struct sk_buff *skb, + u64 timestamp); +int lan966x_ptp_txtstamp_request(struct lan966x_port *port, + struct sk_buff *skb); +void lan966x_ptp_txtstamp_release(struct lan966x_port *port, + struct sk_buff *skb); +irqreturn_t lan966x_ptp_irq_handler(int irq, void *args); static inline void __iomem *lan_addr(void __iomem *base[], int id, int tinst, int tcnt, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_mdb.c b/drivers/net/ethernet/microchip/lan966x/lan966x_mdb.c index c68d0a99d292..2af55268bf4d 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_mdb.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_mdb.c @@ -504,3 +504,48 @@ void lan966x_mdb_erase_entries(struct lan966x *lan966x, u16 vid) lan966x_mdb_l2_cpu_remove(lan966x, mdb_entry, type); } } + +void lan966x_mdb_clear_entries(struct lan966x *lan966x) +{ + struct lan966x_mdb_entry *mdb_entry; + enum macaccess_entry_type type; + unsigned char mac[ETH_ALEN]; + + list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) { + type = lan966x_mdb_classify(mdb_entry->mac); + + lan966x_mdb_encode_mac(mac, mdb_entry, type); + /* Remove just the MAC entry, still keep the PGID in case of L2 + * entries because this can be restored at later point + */ + lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type); + } +} + +void lan966x_mdb_restore_entries(struct lan966x *lan966x) +{ + struct lan966x_mdb_entry *mdb_entry; + enum macaccess_entry_type type; + unsigned char mac[ETH_ALEN]; + bool cpu_copy = false; + + list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) { + type = lan966x_mdb_classify(mdb_entry->mac); + + lan966x_mdb_encode_mac(mac, mdb_entry, type); + if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6) { + /* Copy the frame to CPU only if the CPU is in the VLAN */ + if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, + mdb_entry->vid) && + mdb_entry->cpu_copy) + cpu_copy = true; + + lan966x_mac_ip_learn(lan966x, cpu_copy, mac, + mdb_entry->vid, type); + } else { + lan966x_mac_learn(lan966x, mdb_entry->pgid->index, + mdb_entry->mac, + mdb_entry->vid, type); + } + } +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c index b66a9aa00ea4..38a7e95d69b4 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c @@ -9,6 +9,14 @@ #include "lan966x_main.h" +static struct phylink_pcs *lan966x_phylink_mac_select(struct phylink_config *config, + phy_interface_t interface) +{ + struct lan966x_port *port = netdev_priv(to_net_dev(config->dev)); + + return &port->phylink_pcs; +} + static void lan966x_phylink_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) @@ -114,6 +122,7 @@ static void lan966x_pcs_aneg_restart(struct phylink_pcs *pcs) const struct phylink_mac_ops lan966x_phylink_mac_ops = { .validate = phylink_generic_validate, + .mac_select_pcs = lan966x_phylink_mac_select, .mac_config = lan966x_phylink_mac_config, .mac_prepare = lan966x_phylink_mac_prepare, .mac_link_down = lan966x_phylink_mac_link_down, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c new file mode 100644 index 000000000000..ae782778d6dd --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include + +#include "lan966x_main.h" + +#define LAN966X_MAX_PTP_ID 512 + +/* Represents 1ppm adjustment in 2^59 format with 6.037735849ns as reference + * The value is calculated as following: (1/1000000)/((2^-59)/6.037735849) + */ +#define LAN966X_1PPM_FORMAT 3480517749723LL + +/* Represents 1ppb adjustment in 2^29 format with 6.037735849ns as reference + * The value is calculated as following: (1/1000000000)/((2^59)/6.037735849) + */ +#define LAN966X_1PPB_FORMAT 3480517749LL + +#define TOD_ACC_PIN 0x5 + +enum { + PTP_PIN_ACTION_IDLE = 0, + PTP_PIN_ACTION_LOAD, + PTP_PIN_ACTION_SAVE, + PTP_PIN_ACTION_CLOCK, + PTP_PIN_ACTION_DELTA, + PTP_PIN_ACTION_TOD +}; + +static u64 lan966x_ptp_get_nominal_value(void) +{ + u64 res = 0x304d2df1; + + res <<= 32; + return res; +} + +int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr) +{ + struct lan966x *lan966x = port->lan966x; + struct hwtstamp_config cfg; + struct lan966x_phc *phc; + + /* For now don't allow to run ptp on ports that are part of a bridge, + * because in case of transparent clock the HW will still forward the + * frames, so there would be duplicate frames + */ + if (lan966x->bridge_mask & BIT(port->chip_port)) + return -EINVAL; + + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + switch (cfg.tx_type) { + case HWTSTAMP_TX_ON: + port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; + break; + case HWTSTAMP_TX_ONESTEP_SYNC: + port->ptp_cmd = IFH_REW_OP_ONE_STEP_PTP; + break; + case HWTSTAMP_TX_OFF: + port->ptp_cmd = IFH_REW_OP_NOOP; + break; + default: + return -ERANGE; + } + + switch (cfg.rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_NTP_ALL: + cfg.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + return -ERANGE; + } + + /* Commit back the result & save it */ + mutex_lock(&lan966x->ptp_lock); + phc = &lan966x->phc[LAN966X_PHC_PORT]; + memcpy(&phc->hwtstamp_config, &cfg, sizeof(cfg)); + mutex_unlock(&lan966x->ptp_lock); + + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; +} + +int lan966x_ptp_hwtstamp_get(struct lan966x_port *port, struct ifreq *ifr) +{ + struct lan966x *lan966x = port->lan966x; + struct lan966x_phc *phc; + + phc = &lan966x->phc[LAN966X_PHC_PORT]; + return copy_to_user(ifr->ifr_data, &phc->hwtstamp_config, + sizeof(phc->hwtstamp_config)) ? -EFAULT : 0; +} + +static int lan966x_ptp_classify(struct lan966x_port *port, struct sk_buff *skb) +{ + struct ptp_header *header; + u8 msgtype; + int type; + + if (port->ptp_cmd == IFH_REW_OP_NOOP) + return IFH_REW_OP_NOOP; + + type = ptp_classify_raw(skb); + if (type == PTP_CLASS_NONE) + return IFH_REW_OP_NOOP; + + header = ptp_parse_header(skb, type); + if (!header) + return IFH_REW_OP_NOOP; + + if (port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) + return IFH_REW_OP_TWO_STEP_PTP; + + /* If it is sync and run 1 step then set the correct operation, + * otherwise run as 2 step + */ + msgtype = ptp_get_msgtype(header, type); + if ((msgtype & 0xf) == 0) + return IFH_REW_OP_ONE_STEP_PTP; + + return IFH_REW_OP_TWO_STEP_PTP; +} + +static void lan966x_ptp_txtstamp_old_release(struct lan966x_port *port) +{ + struct sk_buff *skb, *skb_tmp; + unsigned long flags; + + spin_lock_irqsave(&port->tx_skbs.lock, flags); + skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { + if time_after(LAN966X_SKB_CB(skb)->jiffies + LAN966X_PTP_TIMEOUT, + jiffies) + break; + + __skb_unlink(skb, &port->tx_skbs); + dev_kfree_skb_any(skb); + } + spin_unlock_irqrestore(&port->tx_skbs.lock, flags); +} + +int lan966x_ptp_txtstamp_request(struct lan966x_port *port, + struct sk_buff *skb) +{ + struct lan966x *lan966x = port->lan966x; + unsigned long flags; + u8 rew_op; + + rew_op = lan966x_ptp_classify(port, skb); + LAN966X_SKB_CB(skb)->rew_op = rew_op; + + if (rew_op != IFH_REW_OP_TWO_STEP_PTP) + return 0; + + lan966x_ptp_txtstamp_old_release(port); + + spin_lock_irqsave(&lan966x->ptp_ts_id_lock, flags); + if (lan966x->ptp_skbs == LAN966X_MAX_PTP_ID) { + spin_unlock_irqrestore(&lan966x->ptp_ts_id_lock, flags); + return -EBUSY; + } + + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + + skb_queue_tail(&port->tx_skbs, skb); + LAN966X_SKB_CB(skb)->ts_id = port->ts_id; + LAN966X_SKB_CB(skb)->jiffies = jiffies; + + lan966x->ptp_skbs++; + port->ts_id++; + if (port->ts_id == LAN966X_MAX_PTP_ID) + port->ts_id = 0; + + spin_unlock_irqrestore(&lan966x->ptp_ts_id_lock, flags); + + return 0; +} + +void lan966x_ptp_txtstamp_release(struct lan966x_port *port, + struct sk_buff *skb) +{ + struct lan966x *lan966x = port->lan966x; + unsigned long flags; + + spin_lock_irqsave(&lan966x->ptp_ts_id_lock, flags); + port->ts_id--; + lan966x->ptp_skbs--; + skb_unlink(skb, &port->tx_skbs); + spin_unlock_irqrestore(&lan966x->ptp_ts_id_lock, flags); +} + +static void lan966x_get_hwtimestamp(struct lan966x *lan966x, + struct timespec64 *ts, + u32 nsec) +{ + /* Read current PTP time to get seconds */ + unsigned long flags; + u32 curr_nsec; + + spin_lock_irqsave(&lan966x->ptp_clock_lock, flags); + + lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_SAVE) | + PTP_PIN_CFG_PIN_DOM_SET(LAN966X_PHC_PORT) | + PTP_PIN_CFG_PIN_SYNC_SET(0), + PTP_PIN_CFG_PIN_ACTION | + PTP_PIN_CFG_PIN_DOM | + PTP_PIN_CFG_PIN_SYNC, + lan966x, PTP_PIN_CFG(TOD_ACC_PIN)); + + ts->tv_sec = lan_rd(lan966x, PTP_TOD_SEC_LSB(TOD_ACC_PIN)); + curr_nsec = lan_rd(lan966x, PTP_TOD_NSEC(TOD_ACC_PIN)); + + ts->tv_nsec = nsec; + + /* Sec has incremented since the ts was registered */ + if (curr_nsec < nsec) + ts->tv_sec--; + + spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags); +} + +irqreturn_t lan966x_ptp_irq_handler(int irq, void *args) +{ + int budget = LAN966X_MAX_PTP_ID; + struct lan966x *lan966x = args; + + while (budget--) { + struct sk_buff *skb, *skb_tmp, *skb_match = NULL; + struct skb_shared_hwtstamps shhwtstamps; + struct lan966x_port *port; + struct timespec64 ts; + unsigned long flags; + u32 val, id, txport; + u32 delay; + + val = lan_rd(lan966x, PTP_TWOSTEP_CTRL); + + /* Check if a timestamp can be retrieved */ + if (!(val & PTP_TWOSTEP_CTRL_VLD)) + break; + + WARN_ON(val & PTP_TWOSTEP_CTRL_OVFL); + + if (!(val & PTP_TWOSTEP_CTRL_STAMP_TX)) + continue; + + /* Retrieve the ts Tx port */ + txport = PTP_TWOSTEP_CTRL_STAMP_PORT_GET(val); + + /* Retrieve its associated skb */ + port = lan966x->ports[txport]; + + /* Retrieve the delay */ + delay = lan_rd(lan966x, PTP_TWOSTEP_STAMP); + delay = PTP_TWOSTEP_STAMP_STAMP_NSEC_GET(delay); + + /* Get next timestamp from fifo, which needs to be the + * rx timestamp which represents the id of the frame + */ + lan_rmw(PTP_TWOSTEP_CTRL_NXT_SET(1), + PTP_TWOSTEP_CTRL_NXT, + lan966x, PTP_TWOSTEP_CTRL); + + val = lan_rd(lan966x, PTP_TWOSTEP_CTRL); + + /* Check if a timestamp can be retried */ + if (!(val & PTP_TWOSTEP_CTRL_VLD)) + break; + + /* Read RX timestamping to get the ID */ + id = lan_rd(lan966x, PTP_TWOSTEP_STAMP); + + spin_lock_irqsave(&port->tx_skbs.lock, flags); + skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { + if (LAN966X_SKB_CB(skb)->ts_id != id) + continue; + + __skb_unlink(skb, &port->tx_skbs); + skb_match = skb; + break; + } + spin_unlock_irqrestore(&port->tx_skbs.lock, flags); + + /* Next ts */ + lan_rmw(PTP_TWOSTEP_CTRL_NXT_SET(1), + PTP_TWOSTEP_CTRL_NXT, + lan966x, PTP_TWOSTEP_CTRL); + + if (WARN_ON(!skb_match)) + continue; + + spin_lock(&lan966x->ptp_ts_id_lock); + lan966x->ptp_skbs--; + spin_unlock(&lan966x->ptp_ts_id_lock); + + /* Get the h/w timestamp */ + lan966x_get_hwtimestamp(lan966x, &ts, delay); + + /* Set the timestamp into the skb */ + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + skb_tstamp_tx(skb_match, &shhwtstamps); + + dev_kfree_skb_any(skb_match); + } + + return IRQ_HANDLED; +} + +static int lan966x_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info); + struct lan966x *lan966x = phc->lan966x; + unsigned long flags; + bool neg_adj = 0; + u64 tod_inc; + u64 ref; + + if (!scaled_ppm) + return 0; + + if (scaled_ppm < 0) { + neg_adj = 1; + scaled_ppm = -scaled_ppm; + } + + tod_inc = lan966x_ptp_get_nominal_value(); + + /* The multiplication is split in 2 separate additions because of + * overflow issues. If scaled_ppm with 16bit fractional part was bigger + * than 20ppm then we got overflow. + */ + ref = LAN966X_1PPM_FORMAT * (scaled_ppm >> 16); + ref += (LAN966X_1PPM_FORMAT * (0xffff & scaled_ppm)) >> 16; + tod_inc = neg_adj ? tod_inc - ref : tod_inc + ref; + + spin_lock_irqsave(&lan966x->ptp_clock_lock, flags); + + lan_rmw(PTP_DOM_CFG_CLKCFG_DIS_SET(1 << BIT(phc->index)), + PTP_DOM_CFG_CLKCFG_DIS, + lan966x, PTP_DOM_CFG); + + lan_wr((u32)tod_inc & 0xFFFFFFFF, lan966x, + PTP_CLK_PER_CFG(phc->index, 0)); + lan_wr((u32)(tod_inc >> 32), lan966x, + PTP_CLK_PER_CFG(phc->index, 1)); + + lan_rmw(PTP_DOM_CFG_CLKCFG_DIS_SET(0), + PTP_DOM_CFG_CLKCFG_DIS, + lan966x, PTP_DOM_CFG); + + spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags); + + return 0; +} + +static int lan966x_ptp_settime64(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info); + struct lan966x *lan966x = phc->lan966x; + unsigned long flags; + + spin_lock_irqsave(&lan966x->ptp_clock_lock, flags); + + /* Must be in IDLE mode before the time can be loaded */ + lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_IDLE) | + PTP_PIN_CFG_PIN_DOM_SET(phc->index) | + PTP_PIN_CFG_PIN_SYNC_SET(0), + PTP_PIN_CFG_PIN_ACTION | + PTP_PIN_CFG_PIN_DOM | + PTP_PIN_CFG_PIN_SYNC, + lan966x, PTP_PIN_CFG(TOD_ACC_PIN)); + + /* Set new value */ + lan_wr(PTP_TOD_SEC_MSB_TOD_SEC_MSB_SET(upper_32_bits(ts->tv_sec)), + lan966x, PTP_TOD_SEC_MSB(TOD_ACC_PIN)); + lan_wr(lower_32_bits(ts->tv_sec), + lan966x, PTP_TOD_SEC_LSB(TOD_ACC_PIN)); + lan_wr(ts->tv_nsec, lan966x, PTP_TOD_NSEC(TOD_ACC_PIN)); + + /* Apply new values */ + lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_LOAD) | + PTP_PIN_CFG_PIN_DOM_SET(phc->index) | + PTP_PIN_CFG_PIN_SYNC_SET(0), + PTP_PIN_CFG_PIN_ACTION | + PTP_PIN_CFG_PIN_DOM | + PTP_PIN_CFG_PIN_SYNC, + lan966x, PTP_PIN_CFG(TOD_ACC_PIN)); + + spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags); + + return 0; +} + +static int lan966x_ptp_gettime64(struct ptp_clock_info *ptp, + struct timespec64 *ts) +{ + struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info); + struct lan966x *lan966x = phc->lan966x; + unsigned long flags; + time64_t s; + s64 ns; + + spin_lock_irqsave(&lan966x->ptp_clock_lock, flags); + + lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_SAVE) | + PTP_PIN_CFG_PIN_DOM_SET(phc->index) | + PTP_PIN_CFG_PIN_SYNC_SET(0), + PTP_PIN_CFG_PIN_ACTION | + PTP_PIN_CFG_PIN_DOM | + PTP_PIN_CFG_PIN_SYNC, + lan966x, PTP_PIN_CFG(TOD_ACC_PIN)); + + s = lan_rd(lan966x, PTP_TOD_SEC_MSB(TOD_ACC_PIN)); + s <<= 32; + s |= lan_rd(lan966x, PTP_TOD_SEC_LSB(TOD_ACC_PIN)); + ns = lan_rd(lan966x, PTP_TOD_NSEC(TOD_ACC_PIN)); + ns &= PTP_TOD_NSEC_TOD_NSEC; + + spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags); + + /* Deal with negative values */ + if ((ns & 0xFFFFFFF0) == 0x3FFFFFF0) { + s--; + ns &= 0xf; + ns += 999999984; + } + + set_normalized_timespec64(ts, s, ns); + return 0; +} + +static int lan966x_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info); + struct lan966x *lan966x = phc->lan966x; + + if (delta > -(NSEC_PER_SEC / 2) && delta < (NSEC_PER_SEC / 2)) { + unsigned long flags; + + spin_lock_irqsave(&lan966x->ptp_clock_lock, flags); + + /* Must be in IDLE mode before the time can be loaded */ + lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_IDLE) | + PTP_PIN_CFG_PIN_DOM_SET(phc->index) | + PTP_PIN_CFG_PIN_SYNC_SET(0), + PTP_PIN_CFG_PIN_ACTION | + PTP_PIN_CFG_PIN_DOM | + PTP_PIN_CFG_PIN_SYNC, + lan966x, PTP_PIN_CFG(TOD_ACC_PIN)); + + lan_wr(PTP_TOD_NSEC_TOD_NSEC_SET(delta), + lan966x, PTP_TOD_NSEC(TOD_ACC_PIN)); + + /* Adjust time with the value of PTP_TOD_NSEC */ + lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_DELTA) | + PTP_PIN_CFG_PIN_DOM_SET(phc->index) | + PTP_PIN_CFG_PIN_SYNC_SET(0), + PTP_PIN_CFG_PIN_ACTION | + PTP_PIN_CFG_PIN_DOM | + PTP_PIN_CFG_PIN_SYNC, + lan966x, PTP_PIN_CFG(TOD_ACC_PIN)); + + spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags); + } else { + /* Fall back using lan966x_ptp_settime64 which is not exact */ + struct timespec64 ts; + u64 now; + + lan966x_ptp_gettime64(ptp, &ts); + + now = ktime_to_ns(timespec64_to_ktime(ts)); + ts = ns_to_timespec64(now + delta); + + lan966x_ptp_settime64(ptp, &ts); + } + + return 0; +} + +static struct ptp_clock_info lan966x_ptp_clock_info = { + .owner = THIS_MODULE, + .name = "lan966x ptp", + .max_adj = 200000, + .gettime64 = lan966x_ptp_gettime64, + .settime64 = lan966x_ptp_settime64, + .adjtime = lan966x_ptp_adjtime, + .adjfine = lan966x_ptp_adjfine, +}; + +static int lan966x_ptp_phc_init(struct lan966x *lan966x, + int index, + struct ptp_clock_info *clock_info) +{ + struct lan966x_phc *phc = &lan966x->phc[index]; + + phc->info = *clock_info; + phc->clock = ptp_clock_register(&phc->info, lan966x->dev); + if (IS_ERR(phc->clock)) + return PTR_ERR(phc->clock); + + phc->index = index; + phc->lan966x = lan966x; + + /* PTP Rx stamping is always enabled. */ + phc->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + + return 0; +} + +int lan966x_ptp_init(struct lan966x *lan966x) +{ + u64 tod_adj = lan966x_ptp_get_nominal_value(); + struct lan966x_port *port; + int err, i; + + if (!lan966x->ptp) + return 0; + + for (i = 0; i < LAN966X_PHC_COUNT; ++i) { + err = lan966x_ptp_phc_init(lan966x, i, &lan966x_ptp_clock_info); + if (err) + return err; + } + + spin_lock_init(&lan966x->ptp_clock_lock); + spin_lock_init(&lan966x->ptp_ts_id_lock); + mutex_init(&lan966x->ptp_lock); + + /* Disable master counters */ + lan_wr(PTP_DOM_CFG_ENA_SET(0), lan966x, PTP_DOM_CFG); + + /* Configure the nominal TOD increment per clock cycle */ + lan_rmw(PTP_DOM_CFG_CLKCFG_DIS_SET(0x7), + PTP_DOM_CFG_CLKCFG_DIS, + lan966x, PTP_DOM_CFG); + + for (i = 0; i < LAN966X_PHC_COUNT; ++i) { + lan_wr((u32)tod_adj & 0xFFFFFFFF, lan966x, + PTP_CLK_PER_CFG(i, 0)); + lan_wr((u32)(tod_adj >> 32), lan966x, + PTP_CLK_PER_CFG(i, 1)); + } + + lan_rmw(PTP_DOM_CFG_CLKCFG_DIS_SET(0), + PTP_DOM_CFG_CLKCFG_DIS, + lan966x, PTP_DOM_CFG); + + /* Enable master counters */ + lan_wr(PTP_DOM_CFG_ENA_SET(0x7), lan966x, PTP_DOM_CFG); + + for (i = 0; i < lan966x->num_phys_ports; i++) { + port = lan966x->ports[i]; + if (!port) + continue; + + skb_queue_head_init(&port->tx_skbs); + } + + return 0; +} + +void lan966x_ptp_deinit(struct lan966x *lan966x) +{ + struct lan966x_port *port; + int i; + + for (i = 0; i < lan966x->num_phys_ports; i++) { + port = lan966x->ports[i]; + if (!port) + continue; + + skb_queue_purge(&port->tx_skbs); + } + + for (i = 0; i < LAN966X_PHC_COUNT; ++i) + ptp_clock_unregister(lan966x->phc[i].clock); +} + +void lan966x_ptp_rxtstamp(struct lan966x *lan966x, struct sk_buff *skb, + u64 timestamp) +{ + struct skb_shared_hwtstamps *shhwtstamps; + struct lan966x_phc *phc; + struct timespec64 ts; + u64 full_ts_in_ns; + + if (!lan966x->ptp) + return; + + phc = &lan966x->phc[LAN966X_PHC_PORT]; + lan966x_ptp_gettime64(&phc->info, &ts); + + /* Drop the sub-ns precision */ + timestamp = timestamp >> 2; + if (ts.tv_nsec < timestamp) + ts.tv_sec--; + ts.tv_nsec = timestamp; + full_ts_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec); + + shhwtstamps = skb_hwtstamps(skb); + shhwtstamps->hwtstamp = full_ts_in_ns; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h index 797560172aca..0c0b3e173d53 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h @@ -19,6 +19,7 @@ enum lan966x_target { TARGET_DEV = 13, TARGET_GCB = 27, TARGET_ORG = 36, + TARGET_PTP = 41, TARGET_QS = 42, TARGET_QSYS = 46, TARGET_REW = 47, @@ -298,6 +299,24 @@ enum lan966x_target { /* ANA:PORT:CPU_FWD_CFG */ #define ANA_CPU_FWD_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 96, 0, 1, 4) +#define ANA_CPU_FWD_CFG_MLD_REDIR_ENA BIT(6) +#define ANA_CPU_FWD_CFG_MLD_REDIR_ENA_SET(x)\ + FIELD_PREP(ANA_CPU_FWD_CFG_MLD_REDIR_ENA, x) +#define ANA_CPU_FWD_CFG_MLD_REDIR_ENA_GET(x)\ + FIELD_GET(ANA_CPU_FWD_CFG_MLD_REDIR_ENA, x) + +#define ANA_CPU_FWD_CFG_IGMP_REDIR_ENA BIT(5) +#define ANA_CPU_FWD_CFG_IGMP_REDIR_ENA_SET(x)\ + FIELD_PREP(ANA_CPU_FWD_CFG_IGMP_REDIR_ENA, x) +#define ANA_CPU_FWD_CFG_IGMP_REDIR_ENA_GET(x)\ + FIELD_GET(ANA_CPU_FWD_CFG_IGMP_REDIR_ENA, x) + +#define ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA BIT(4) +#define ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA_SET(x)\ + FIELD_PREP(ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA, x) +#define ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA_GET(x)\ + FIELD_GET(ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA, x) + #define ANA_CPU_FWD_CFG_SRC_COPY_ENA BIT(3) #define ANA_CPU_FWD_CFG_SRC_COPY_ENA_SET(x)\ FIELD_PREP(ANA_CPU_FWD_CFG_SRC_COPY_ENA, x) @@ -559,6 +578,108 @@ enum lan966x_target { #define DEV_PCS1G_STICKY_LINK_DOWN_STICKY_GET(x)\ FIELD_GET(DEV_PCS1G_STICKY_LINK_DOWN_STICKY, x) +/* PTP:PTP_CFG:PTP_DOM_CFG */ +#define PTP_DOM_CFG __REG(TARGET_PTP, 0, 1, 512, 0, 1, 16, 12, 0, 1, 4) + +#define PTP_DOM_CFG_ENA GENMASK(11, 9) +#define PTP_DOM_CFG_ENA_SET(x)\ + FIELD_PREP(PTP_DOM_CFG_ENA, x) +#define PTP_DOM_CFG_ENA_GET(x)\ + FIELD_GET(PTP_DOM_CFG_ENA, x) + +#define PTP_DOM_CFG_CLKCFG_DIS GENMASK(2, 0) +#define PTP_DOM_CFG_CLKCFG_DIS_SET(x)\ + FIELD_PREP(PTP_DOM_CFG_CLKCFG_DIS, x) +#define PTP_DOM_CFG_CLKCFG_DIS_GET(x)\ + FIELD_GET(PTP_DOM_CFG_CLKCFG_DIS, x) + +/* PTP:PTP_TOD_DOMAINS:CLK_PER_CFG */ +#define PTP_CLK_PER_CFG(g, r) __REG(TARGET_PTP, 0, 1, 528, g, 3, 28, 0, r, 2, 4) + +/* PTP:PTP_PINS:PTP_PIN_CFG */ +#define PTP_PIN_CFG(g) __REG(TARGET_PTP, 0, 1, 0, g, 8, 64, 0, 0, 1, 4) + +#define PTP_PIN_CFG_PIN_ACTION GENMASK(29, 27) +#define PTP_PIN_CFG_PIN_ACTION_SET(x)\ + FIELD_PREP(PTP_PIN_CFG_PIN_ACTION, x) +#define PTP_PIN_CFG_PIN_ACTION_GET(x)\ + FIELD_GET(PTP_PIN_CFG_PIN_ACTION, x) + +#define PTP_PIN_CFG_PIN_SYNC GENMASK(26, 25) +#define PTP_PIN_CFG_PIN_SYNC_SET(x)\ + FIELD_PREP(PTP_PIN_CFG_PIN_SYNC, x) +#define PTP_PIN_CFG_PIN_SYNC_GET(x)\ + FIELD_GET(PTP_PIN_CFG_PIN_SYNC, x) + +#define PTP_PIN_CFG_PIN_DOM GENMASK(17, 16) +#define PTP_PIN_CFG_PIN_DOM_SET(x)\ + FIELD_PREP(PTP_PIN_CFG_PIN_DOM, x) +#define PTP_PIN_CFG_PIN_DOM_GET(x)\ + FIELD_GET(PTP_PIN_CFG_PIN_DOM, x) + +/* PTP:PTP_PINS:PTP_TOD_SEC_MSB */ +#define PTP_TOD_SEC_MSB(g) __REG(TARGET_PTP, 0, 1, 0, g, 8, 64, 4, 0, 1, 4) + +#define PTP_TOD_SEC_MSB_TOD_SEC_MSB GENMASK(15, 0) +#define PTP_TOD_SEC_MSB_TOD_SEC_MSB_SET(x)\ + FIELD_PREP(PTP_TOD_SEC_MSB_TOD_SEC_MSB, x) +#define PTP_TOD_SEC_MSB_TOD_SEC_MSB_GET(x)\ + FIELD_GET(PTP_TOD_SEC_MSB_TOD_SEC_MSB, x) + +/* PTP:PTP_PINS:PTP_TOD_SEC_LSB */ +#define PTP_TOD_SEC_LSB(g) __REG(TARGET_PTP, 0, 1, 0, g, 8, 64, 8, 0, 1, 4) + +/* PTP:PTP_PINS:PTP_TOD_NSEC */ +#define PTP_TOD_NSEC(g) __REG(TARGET_PTP, 0, 1, 0, g, 8, 64, 12, 0, 1, 4) + +#define PTP_TOD_NSEC_TOD_NSEC GENMASK(29, 0) +#define PTP_TOD_NSEC_TOD_NSEC_SET(x)\ + FIELD_PREP(PTP_TOD_NSEC_TOD_NSEC, x) +#define PTP_TOD_NSEC_TOD_NSEC_GET(x)\ + FIELD_GET(PTP_TOD_NSEC_TOD_NSEC, x) + +/* PTP:PTP_TS_FIFO:PTP_TWOSTEP_CTRL */ +#define PTP_TWOSTEP_CTRL __REG(TARGET_PTP, 0, 1, 612, 0, 1, 12, 0, 0, 1, 4) + +#define PTP_TWOSTEP_CTRL_NXT BIT(11) +#define PTP_TWOSTEP_CTRL_NXT_SET(x)\ + FIELD_PREP(PTP_TWOSTEP_CTRL_NXT, x) +#define PTP_TWOSTEP_CTRL_NXT_GET(x)\ + FIELD_GET(PTP_TWOSTEP_CTRL_NXT, x) + +#define PTP_TWOSTEP_CTRL_VLD BIT(10) +#define PTP_TWOSTEP_CTRL_VLD_SET(x)\ + FIELD_PREP(PTP_TWOSTEP_CTRL_VLD, x) +#define PTP_TWOSTEP_CTRL_VLD_GET(x)\ + FIELD_GET(PTP_TWOSTEP_CTRL_VLD, x) + +#define PTP_TWOSTEP_CTRL_STAMP_TX BIT(9) +#define PTP_TWOSTEP_CTRL_STAMP_TX_SET(x)\ + FIELD_PREP(PTP_TWOSTEP_CTRL_STAMP_TX, x) +#define PTP_TWOSTEP_CTRL_STAMP_TX_GET(x)\ + FIELD_GET(PTP_TWOSTEP_CTRL_STAMP_TX, x) + +#define PTP_TWOSTEP_CTRL_STAMP_PORT GENMASK(8, 1) +#define PTP_TWOSTEP_CTRL_STAMP_PORT_SET(x)\ + FIELD_PREP(PTP_TWOSTEP_CTRL_STAMP_PORT, x) +#define PTP_TWOSTEP_CTRL_STAMP_PORT_GET(x)\ + FIELD_GET(PTP_TWOSTEP_CTRL_STAMP_PORT, x) + +#define PTP_TWOSTEP_CTRL_OVFL BIT(0) +#define PTP_TWOSTEP_CTRL_OVFL_SET(x)\ + FIELD_PREP(PTP_TWOSTEP_CTRL_OVFL, x) +#define PTP_TWOSTEP_CTRL_OVFL_GET(x)\ + FIELD_GET(PTP_TWOSTEP_CTRL_OVFL, x) + +/* PTP:PTP_TS_FIFO:PTP_TWOSTEP_STAMP */ +#define PTP_TWOSTEP_STAMP __REG(TARGET_PTP, 0, 1, 612, 0, 1, 12, 4, 0, 1, 4) + +#define PTP_TWOSTEP_STAMP_STAMP_NSEC GENMASK(31, 2) +#define PTP_TWOSTEP_STAMP_STAMP_NSEC_SET(x)\ + FIELD_PREP(PTP_TWOSTEP_STAMP_STAMP_NSEC, x) +#define PTP_TWOSTEP_STAMP_STAMP_NSEC_GET(x)\ + FIELD_GET(PTP_TWOSTEP_STAMP_STAMP_NSEC, x) + /* DEVCPU_QS:XTR:XTR_GRP_CFG */ #define QS_XTR_GRP_CFG(r) __REG(TARGET_QS, 0, 1, 0, 0, 1, 36, 0, r, 2, 4) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c b/drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c index 7de55f6a4da8..e3555c94294d 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c @@ -9,6 +9,37 @@ static struct notifier_block lan966x_netdevice_nb __read_mostly; static struct notifier_block lan966x_switchdev_nb __read_mostly; static struct notifier_block lan966x_switchdev_blocking_nb __read_mostly; +static void lan966x_port_set_mcast_ip_flood(struct lan966x_port *port, + u32 pgid_ip) +{ + struct lan966x *lan966x = port->lan966x; + u32 flood_mask_ip; + + flood_mask_ip = lan_rd(lan966x, ANA_PGID(pgid_ip)); + flood_mask_ip = ANA_PGID_PGID_GET(flood_mask_ip); + + /* If mcast snooping is not enabled then use mcast flood mask + * to decide to enable multicast flooding or not. + */ + if (!port->mcast_ena) { + u32 flood_mask; + + flood_mask = lan_rd(lan966x, ANA_PGID(PGID_MC)); + flood_mask = ANA_PGID_PGID_GET(flood_mask); + + if (flood_mask & BIT(port->chip_port)) + flood_mask_ip |= BIT(port->chip_port); + else + flood_mask_ip &= ~BIT(port->chip_port); + } else { + flood_mask_ip &= ~BIT(port->chip_port); + } + + lan_rmw(ANA_PGID_PGID_SET(flood_mask_ip), + ANA_PGID_PGID, + lan966x, ANA_PGID(pgid_ip)); +} + static void lan966x_port_set_mcast_flood(struct lan966x_port *port, bool enabled) { @@ -23,6 +54,11 @@ static void lan966x_port_set_mcast_flood(struct lan966x_port *port, lan_rmw(ANA_PGID_PGID_SET(val), ANA_PGID_PGID, port->lan966x, ANA_PGID(PGID_MC)); + + if (!port->mcast_ena) { + lan966x_port_set_mcast_ip_flood(port, PGID_MCIPV4); + lan966x_port_set_mcast_ip_flood(port, PGID_MCIPV6); + } } static void lan966x_port_set_ucast_flood(struct lan966x_port *port, @@ -144,6 +180,28 @@ static void lan966x_port_ageing_set(struct lan966x_port *port, lan966x_mac_set_ageing(port->lan966x, ageing_time); } +static void lan966x_port_mc_set(struct lan966x_port *port, bool mcast_ena) +{ + struct lan966x *lan966x = port->lan966x; + + port->mcast_ena = mcast_ena; + if (mcast_ena) + lan966x_mdb_restore_entries(lan966x); + else + lan966x_mdb_clear_entries(lan966x); + + lan_rmw(ANA_CPU_FWD_CFG_IGMP_REDIR_ENA_SET(mcast_ena) | + ANA_CPU_FWD_CFG_MLD_REDIR_ENA_SET(mcast_ena) | + ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA_SET(mcast_ena), + ANA_CPU_FWD_CFG_IGMP_REDIR_ENA | + ANA_CPU_FWD_CFG_MLD_REDIR_ENA | + ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA, + lan966x, ANA_CPU_FWD_CFG(port->chip_port)); + + lan966x_port_set_mcast_ip_flood(port, PGID_MCIPV4); + lan966x_port_set_mcast_ip_flood(port, PGID_MCIPV6); +} + static int lan966x_port_attr_set(struct net_device *dev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) @@ -171,6 +229,9 @@ static int lan966x_port_attr_set(struct net_device *dev, const void *ctx, lan966x_vlan_port_set_vlan_aware(port, attr->u.vlan_filtering); lan966x_vlan_port_apply(port); break; + case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: + lan966x_port_mc_set(port, !attr->u.mc_disabled); + break; default: err = -EOPNOTSUPP; break; @@ -358,6 +419,9 @@ static int lan966x_netdevice_event(struct notifier_block *nb, return notifier_from_errno(ret); } +/* We don't offload uppers such as LAG as bridge ports, so every device except + * the bridge itself is foreign. + */ static bool lan966x_foreign_dev_check(const struct net_device *dev, const struct net_device *foreign_dev) { @@ -365,10 +429,10 @@ static bool lan966x_foreign_dev_check(const struct net_device *dev, struct lan966x *lan966x = port->lan966x; if (netif_is_bridge_master(foreign_dev)) - if (lan966x->bridge != foreign_dev) - return true; + if (lan966x->bridge == foreign_dev) + return false; - return false; + return true; } static int lan966x_switchdev_event(struct notifier_block *nb, @@ -388,8 +452,7 @@ static int lan966x_switchdev_event(struct notifier_block *nb, err = switchdev_handle_fdb_event_to_device(dev, event, ptr, lan966x_netdevice_check, lan966x_foreign_dev_check, - lan966x_handle_fdb, - NULL); + lan966x_handle_fdb); return notifier_from_errno(err); } @@ -402,18 +465,6 @@ static int lan966x_handle_port_vlan_add(struct lan966x_port *port, const struct switchdev_obj_port_vlan *v = SWITCHDEV_OBJ_PORT_VLAN(obj); struct lan966x *lan966x = port->lan966x; - /* When adding a port to a vlan, we get a callback for the port but - * also for the bridge. When get the callback for the bridge just bail - * out. Then when the bridge is added to the vlan, then we get a - * callback here but in this case the flags has set: - * BRIDGE_VLAN_INFO_BRENTRY. In this case it means that the CPU - * port is added to the vlan, so the broadcast frames and unicast frames - * with dmac of the bridge should be foward to CPU. - */ - if (netif_is_bridge_master(obj->orig_dev) && - !(v->flags & BRIDGE_VLAN_INFO_BRENTRY)) - return 0; - if (!netif_is_bridge_master(obj->orig_dev)) lan966x_vlan_port_add_vlan(port, v->vid, v->flags & BRIDGE_VLAN_INFO_PVID, diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile index c271e86ee292..4402c3ed1dc5 100644 --- a/drivers/net/ethernet/microchip/sparx5/Makefile +++ b/drivers/net/ethernet/microchip/sparx5/Makefile @@ -7,4 +7,5 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o sparx5-switch-objs := sparx5_main.o sparx5_packet.o \ sparx5_netdev.o sparx5_phylink.o sparx5_port.o sparx5_mactable.o sparx5_vlan.o \ - sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o + sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o \ + sparx5_ptp.o sparx5_pgid.o diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c index 10b866e9f726..6b0febcb7fa9 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c @@ -1183,6 +1183,39 @@ static void sparx5_config_port_stats(struct sparx5 *sparx5, int portno) sparx5, ANA_AC_PORT_STAT_CFG(portno, SPX5_PORT_POLICER_DROPS)); } +static int sparx5_get_ts_info(struct net_device *dev, + struct ethtool_ts_info *info) +{ + struct sparx5_port *port = netdev_priv(dev); + struct sparx5 *sparx5 = port->sparx5; + struct sparx5_phc *phc; + + if (!sparx5->ptp) + return ethtool_op_get_ts_info(dev, info); + + phc = &sparx5->phc[SPARX5_PHC_PORT]; + + info->phc_index = phc->clock ? ptp_clock_index(phc->clock) : -1; + if (info->phc_index == -1) { + info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + return 0; + } + info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) | + BIT(HWTSTAMP_TX_ONESTEP_SYNC); + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | + BIT(HWTSTAMP_FILTER_ALL); + + return 0; +} + const struct ethtool_ops sparx5_ethtool_ops = { .get_sset_count = sparx5_get_sset_count, .get_strings = sparx5_get_sset_strings, @@ -1194,6 +1227,7 @@ const struct ethtool_ops sparx5_ethtool_ops = { .get_eth_mac_stats = sparx5_get_eth_mac_stats, .get_eth_ctrl_stats = sparx5_get_eth_mac_ctrl_stats, .get_rmon_stats = sparx5_get_eth_rmon_stats, + .get_ts_info = sparx5_get_ts_info, }; int sparx_stats_init(struct sparx5 *sparx5) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c index 7436f62fa152..2dc87584023a 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c @@ -240,6 +240,8 @@ static bool sparx5_fdma_rx_get_frame(struct sparx5 *sparx5, struct sparx5_rx *rx skb_pull(skb, IFH_LEN * sizeof(u32)); if (likely(!(skb->dev->features & NETIF_F_RXFCS))) skb_trim(skb, skb->len - ETH_FCS_LEN); + + sparx5_ptp_rxtstamp(sparx5, skb, fi.timestamp); skb->protocol = eth_type_trans(skb, skb->dev); /* Everything we see on an interface that is in the HW bridge * has already been forwarded diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c index 9a8e4f201eb1..35abb3d0ce19 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c @@ -186,11 +186,11 @@ bool sparx5_mact_getnext(struct sparx5 *sparx5, return ret == 0; } -static int sparx5_mact_lookup(struct sparx5 *sparx5, - const unsigned char mac[ETH_ALEN], - u16 vid) +bool sparx5_mact_find(struct sparx5 *sparx5, + const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2) { int ret; + u32 cfg2; mutex_lock(&sparx5->lock); @@ -202,16 +202,29 @@ static int sparx5_mact_lookup(struct sparx5 *sparx5, sparx5, LRN_COMMON_ACCESS_CTRL); ret = sparx5_mact_wait_for_completion(sparx5); - if (ret) - goto out; + if (ret == 0) { + cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2); + if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) + *pcfg2 = cfg2; + else + ret = -ENOENT; + } - ret = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET - (spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2)); - -out: mutex_unlock(&sparx5->lock); - return ret; + return ret == 0; +} + +static int sparx5_mact_lookup(struct sparx5 *sparx5, + const unsigned char mac[ETH_ALEN], + u16 vid) +{ + u32 pcfg2; + + if (sparx5_mact_find(sparx5, mac, vid, &pcfg2)) + return 1; + + return 0; } int sparx5_mact_forget(struct sparx5 *sparx5, @@ -286,7 +299,8 @@ static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type, } int sparx5_add_mact_entry(struct sparx5 *sparx5, - struct sparx5_port *port, + struct net_device *dev, + u16 portno, const unsigned char *addr, u16 vid) { struct sparx5_mact_entry *mact_entry; @@ -302,14 +316,14 @@ int sparx5_add_mact_entry(struct sparx5 *sparx5, * mact thread to start the frame will reach CPU and the CPU will * add the entry but without the extern_learn flag. */ - mact_entry = find_mact_entry(sparx5, addr, vid, port->portno); + mact_entry = find_mact_entry(sparx5, addr, vid, portno); if (mact_entry) goto update_hw; /* Add the entry in SW MAC table not to get the notification when * SW is pulling again */ - mact_entry = alloc_mact_entry(sparx5, addr, vid, port->portno); + mact_entry = alloc_mact_entry(sparx5, addr, vid, portno); if (!mact_entry) return -ENOMEM; @@ -318,13 +332,13 @@ int sparx5_add_mact_entry(struct sparx5 *sparx5, mutex_unlock(&sparx5->mact_lock); update_hw: - ret = sparx5_mact_learn(sparx5, port->portno, addr, vid); + ret = sparx5_mact_learn(sparx5, portno, addr, vid); /* New entry? */ if (mact_entry->flags == 0) { mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */ sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, addr, vid, - port->ndev, true); + dev, true); } return ret; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c index 16266275dd36..01be7bd84181 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c @@ -190,6 +190,7 @@ static const struct sparx5_main_io_resource sparx5_main_iomap[] = { { TARGET_ASM, 0x10600000, 1 }, /* 0x610600000 */ { TARGET_GCB, 0x11010000, 2 }, /* 0x611010000 */ { TARGET_QS, 0x11030000, 2 }, /* 0x611030000 */ + { TARGET_PTP, 0x11040000, 2 }, /* 0x611040000 */ { TARGET_ANA_ACL, 0x11050000, 2 }, /* 0x611050000 */ { TARGET_LRN, 0x11060000, 2 }, /* 0x611060000 */ { TARGET_VCAP_SUPER, 0x11080000, 2 }, /* 0x611080000 */ @@ -291,7 +292,6 @@ static int sparx5_create_port(struct sparx5 *sparx5, /* Create a phylink for PHY management. Also handles SFPs */ spx5_port->phylink_config.dev = &spx5_port->ndev->dev; spx5_port->phylink_config.type = PHYLINK_NETDEV; - spx5_port->phylink_config.pcs_poll = true; spx5_port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD | MAC_2500FD | MAC_5000FD | MAC_10000FD | MAC_25000FD; @@ -328,7 +328,6 @@ static int sparx5_create_port(struct sparx5 *sparx5, return PTR_ERR(phylink); spx5_port->phylink = phylink; - phylink_set_pcs(phylink, &spx5_port->phylink_pcs); return 0; } @@ -627,6 +626,9 @@ static int sparx5_start(struct sparx5 *sparx5) /* Init MAC table, ageing */ sparx5_mact_init(sparx5); + /* Init PGID table arbitrator */ + sparx5_pgid_init(sparx5); + /* Setup VLANs */ sparx5_vlan_init(sparx5); @@ -694,6 +696,18 @@ static int sparx5_start(struct sparx5 *sparx5) } else { sparx5->xtr_irq = -ENXIO; } + + if (sparx5->ptp_irq >= 0) { + err = devm_request_threaded_irq(sparx5->dev, sparx5->ptp_irq, + NULL, sparx5_ptp_irq_handler, + IRQF_ONESHOT, "sparx5-ptp", + sparx5); + if (err) + sparx5->ptp_irq = -ENXIO; + + sparx5->ptp = 1; + } + return err; } @@ -810,6 +824,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev) sparx5->fdma_irq = platform_get_irq_byname(sparx5->pdev, "fdma"); sparx5->xtr_irq = platform_get_irq_byname(sparx5->pdev, "xtr"); + sparx5->ptp_irq = platform_get_irq_byname(sparx5->pdev, "ptp"); /* Read chip ID to check CPU interface */ sparx5->chip_id = spx5_rd(sparx5, GCB_CHIP_ID); @@ -848,6 +863,12 @@ static int mchp_sparx5_probe(struct platform_device *pdev) dev_err(sparx5->dev, "Start failed\n"); goto cleanup_ports; } + + err = sparx5_ptp_init(sparx5); + if (err) { + dev_err(sparx5->dev, "PTP failed\n"); + goto cleanup_ports; + } goto cleanup_config; cleanup_ports: @@ -871,6 +892,7 @@ static int mchp_sparx5_remove(struct platform_device *pdev) disable_irq(sparx5->fdma_irq); sparx5->fdma_irq = -ENXIO; } + sparx5_ptp_deinit(sparx5); sparx5_fdma_stop(sparx5); sparx5_cleanup_ports(sparx5); /* Unregister netdevs */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index d40e18ce3293..7a04b8f2a546 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include "sparx5_main_regs.h" @@ -64,6 +66,12 @@ enum sparx5_vlan_port_type { #define PGID_BCAST (PGID_BASE + 6) #define PGID_CPU (PGID_BASE + 7) +#define PGID_TABLE_SIZE 3290 + +#define PGID_MCAST_START 65 +#define PGID_GLAG_START 833 +#define PGID_GLAG_END 1088 + #define IFH_LEN 9 /* 36 bytes */ #define NULL_VID 0 #define SPX5_MACT_PULL_DELAY (2 * HZ) @@ -79,6 +87,18 @@ enum sparx5_vlan_port_type { #define FDMA_RX_DCB_MAX_DBS 15 #define FDMA_TX_DCB_MAX_DBS 1 +#define SPARX5_PHC_COUNT 3 +#define SPARX5_PHC_PORT 0 + +#define IFH_REW_OP_NOOP 0x0 +#define IFH_REW_OP_ONE_STEP_PTP 0x3 +#define IFH_REW_OP_TWO_STEP_PTP 0x4 + +#define IFH_PDU_TYPE_NONE 0x0 +#define IFH_PDU_TYPE_PTP 0x5 +#define IFH_PDU_TYPE_IPV4_UDP_PTP 0x6 +#define IFH_PDU_TYPE_IPV6_UDP_PTP 0x7 + struct sparx5; struct sparx5_db_hw { @@ -167,9 +187,12 @@ struct sparx5_port { enum sparx5_port_max_tags max_vlan_tags; enum sparx5_vlan_port_type vlan_type; u32 custom_etype; - u32 ifh[IFH_LEN]; bool vlan_aware; struct hrtimer inj_timer; + /* ptp */ + u8 ptp_cmd; + u16 ts_id; + struct sk_buff_head tx_skbs; }; enum sparx5_core_clockfreq { @@ -179,6 +202,26 @@ enum sparx5_core_clockfreq { SPX5_CORE_CLOCK_625MHZ, /* 625MHZ core clock frequency */ }; +struct sparx5_phc { + struct ptp_clock *clock; + struct ptp_clock_info info; + struct hwtstamp_config hwtstamp_config; + struct sparx5 *sparx5; + u8 index; +}; + +struct sparx5_skb_cb { + u8 rew_op; + u8 pdu_type; + u8 pdu_w16_offset; + u16 ts_id; + unsigned long jiffies; +}; + +#define SPARX5_PTP_TIMEOUT msecs_to_jiffies(10) +#define SPARX5_SKB_CB(skb) \ + ((struct sparx5_skb_cb *)((skb)->cb)) + struct sparx5 { struct platform_device *pdev; struct device *dev; @@ -226,6 +269,16 @@ struct sparx5 { int fdma_irq; struct sparx5_rx rx; struct sparx5_tx tx; + /* PTP */ + bool ptp; + struct sparx5_phc phc[SPARX5_PHC_COUNT]; + spinlock_t ptp_clock_lock; /* lock for phc */ + spinlock_t ptp_ts_id_lock; /* lock for ts_id */ + struct mutex ptp_lock; /* lock for ptp interface state */ + u16 ptp_skbs; + int ptp_irq; + /* PGID allocation map */ + u8 pgid_map[PGID_TABLE_SIZE]; }; /* sparx5_switchdev.c */ @@ -235,6 +288,7 @@ void sparx5_unregister_notifier_blocks(struct sparx5 *sparx5); /* sparx5_packet.c */ struct frame_info { int src_port; + u32 timestamp; }; void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp); @@ -256,10 +310,13 @@ int sparx5_mact_learn(struct sparx5 *sparx5, int port, const unsigned char mac[ETH_ALEN], u16 vid); bool sparx5_mact_getnext(struct sparx5 *sparx5, unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2); +bool sparx5_mact_find(struct sparx5 *sparx5, + const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2); int sparx5_mact_forget(struct sparx5 *sparx5, const unsigned char mac[ETH_ALEN], u16 vid); int sparx5_add_mact_entry(struct sparx5 *sparx5, - struct sparx5_port *port, + struct net_device *dev, + u16 portno, const unsigned char *addr, u16 vid); int sparx5_del_mact_entry(struct sparx5 *sparx5, const unsigned char *addr, @@ -288,12 +345,43 @@ void sparx5_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats int sparx_stats_init(struct sparx5 *sparx5); /* sparx5_netdev.c */ +void sparx5_set_port_ifh_timestamp(void *ifh_hdr, u64 timestamp); +void sparx5_set_port_ifh_rew_op(void *ifh_hdr, u32 rew_op); +void sparx5_set_port_ifh_pdu_type(void *ifh_hdr, u32 pdu_type); +void sparx5_set_port_ifh_pdu_w16_offset(void *ifh_hdr, u32 pdu_w16_offset); +void sparx5_set_port_ifh(void *ifh_hdr, u16 portno); bool sparx5_netdevice_check(const struct net_device *dev); struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno); int sparx5_register_netdevs(struct sparx5 *sparx5); void sparx5_destroy_netdevs(struct sparx5 *sparx5); void sparx5_unregister_netdevs(struct sparx5 *sparx5); +/* sparx5_ptp.c */ +int sparx5_ptp_init(struct sparx5 *sparx5); +void sparx5_ptp_deinit(struct sparx5 *sparx5); +int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr); +int sparx5_ptp_hwtstamp_get(struct sparx5_port *port, struct ifreq *ifr); +void sparx5_ptp_rxtstamp(struct sparx5 *sparx5, struct sk_buff *skb, + u64 timestamp); +int sparx5_ptp_txtstamp_request(struct sparx5_port *port, + struct sk_buff *skb); +void sparx5_ptp_txtstamp_release(struct sparx5_port *port, + struct sk_buff *skb); +irqreturn_t sparx5_ptp_irq_handler(int irq, void *args); + +/* sparx5_pgid.c */ +enum sparx5_pgid_type { + SPX5_PGID_FREE, + SPX5_PGID_RESERVED, + SPX5_PGID_MULTICAST, + SPX5_PGID_GLAG +}; + +void sparx5_pgid_init(struct sparx5 *spx5); +int sparx5_pgid_alloc_glag(struct sparx5 *spx5, u16 *idx); +int sparx5_pgid_alloc_mcast(struct sparx5 *spx5, u16 *idx); +int sparx5_pgid_free(struct sparx5 *spx5, u16 idx); + /* Clock period in picoseconds */ static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock) { diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h index 5ab2373a7178..c94de436b281 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h @@ -4,8 +4,8 @@ * Copyright (c) 2021 Microchip Technology Inc. */ -/* This file is autogenerated by cml-utils 2021-05-06 13:06:37 +0200. - * Commit ID: 9ae4ec441e25e4b9003f4e514df5cb12a36b84d3 +/* This file is autogenerated by cml-utils 2022-02-26 14:15:01 +0100. + * Commit ID: 98bdd3d171cc2a1afd30d241d41a4281d471a48c (dirty) */ #ifndef _SPARX5_MAIN_REGS_H_ @@ -40,6 +40,7 @@ enum sparx5_target { TARGET_PCS25G_BR = 144, TARGET_PCS5G_BR = 160, TARGET_PORT_CONF = 173, + TARGET_PTP = 174, TARGET_QFWD = 175, TARGET_QRES = 176, TARGET_QS = 177, @@ -4156,6 +4157,249 @@ enum sparx5_target { #define PORT_CONF_USGMII_CFG_QUAD_MODE_GET(x)\ FIELD_GET(PORT_CONF_USGMII_CFG_QUAD_MODE, x) +/* DEVCPU_PTP:PTP_CFG:PTP_PIN_INTR */ +#define PTP_PTP_PIN_INTR __REG(TARGET_PTP, 0, 1, 320, 0, 1, 16, 0, 0, 1, 4) + +#define PTP_PTP_PIN_INTR_INTR_PTP GENMASK(4, 0) +#define PTP_PTP_PIN_INTR_INTR_PTP_SET(x)\ + FIELD_PREP(PTP_PTP_PIN_INTR_INTR_PTP, x) +#define PTP_PTP_PIN_INTR_INTR_PTP_GET(x)\ + FIELD_GET(PTP_PTP_PIN_INTR_INTR_PTP, x) + +/* DEVCPU_PTP:PTP_CFG:PTP_PIN_INTR_ENA */ +#define PTP_PTP_PIN_INTR_ENA __REG(TARGET_PTP, 0, 1, 320, 0, 1, 16, 4, 0, 1, 4) + +#define PTP_PTP_PIN_INTR_ENA_INTR_PTP_ENA GENMASK(4, 0) +#define PTP_PTP_PIN_INTR_ENA_INTR_PTP_ENA_SET(x)\ + FIELD_PREP(PTP_PTP_PIN_INTR_ENA_INTR_PTP_ENA, x) +#define PTP_PTP_PIN_INTR_ENA_INTR_PTP_ENA_GET(x)\ + FIELD_GET(PTP_PTP_PIN_INTR_ENA_INTR_PTP_ENA, x) + +/* DEVCPU_PTP:PTP_CFG:PTP_INTR_IDENT */ +#define PTP_PTP_INTR_IDENT __REG(TARGET_PTP, 0, 1, 320, 0, 1, 16, 8, 0, 1, 4) + +#define PTP_PTP_INTR_IDENT_INTR_PTP_IDENT GENMASK(4, 0) +#define PTP_PTP_INTR_IDENT_INTR_PTP_IDENT_SET(x)\ + FIELD_PREP(PTP_PTP_INTR_IDENT_INTR_PTP_IDENT, x) +#define PTP_PTP_INTR_IDENT_INTR_PTP_IDENT_GET(x)\ + FIELD_GET(PTP_PTP_INTR_IDENT_INTR_PTP_IDENT, x) + +/* DEVCPU_PTP:PTP_CFG:PTP_DOM_CFG */ +#define PTP_PTP_DOM_CFG __REG(TARGET_PTP, 0, 1, 320, 0, 1, 16, 12, 0, 1, 4) + +#define PTP_PTP_DOM_CFG_PTP_ENA GENMASK(11, 9) +#define PTP_PTP_DOM_CFG_PTP_ENA_SET(x)\ + FIELD_PREP(PTP_PTP_DOM_CFG_PTP_ENA, x) +#define PTP_PTP_DOM_CFG_PTP_ENA_GET(x)\ + FIELD_GET(PTP_PTP_DOM_CFG_PTP_ENA, x) + +#define PTP_PTP_DOM_CFG_PTP_HOLD GENMASK(8, 6) +#define PTP_PTP_DOM_CFG_PTP_HOLD_SET(x)\ + FIELD_PREP(PTP_PTP_DOM_CFG_PTP_HOLD, x) +#define PTP_PTP_DOM_CFG_PTP_HOLD_GET(x)\ + FIELD_GET(PTP_PTP_DOM_CFG_PTP_HOLD, x) + +#define PTP_PTP_DOM_CFG_PTP_TOD_FREEZE GENMASK(5, 3) +#define PTP_PTP_DOM_CFG_PTP_TOD_FREEZE_SET(x)\ + FIELD_PREP(PTP_PTP_DOM_CFG_PTP_TOD_FREEZE, x) +#define PTP_PTP_DOM_CFG_PTP_TOD_FREEZE_GET(x)\ + FIELD_GET(PTP_PTP_DOM_CFG_PTP_TOD_FREEZE, x) + +#define PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS GENMASK(2, 0) +#define PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS_SET(x)\ + FIELD_PREP(PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS, x) +#define PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS_GET(x)\ + FIELD_GET(PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS, x) + +/* DEVCPU_PTP:PTP_TOD_DOMAINS:CLK_PER_CFG */ +#define PTP_CLK_PER_CFG(g, r) __REG(TARGET_PTP, 0, 1, 336, g, 3, 28, 0, r, 2, 4) + +/* DEVCPU_PTP:PTP_TOD_DOMAINS:PTP_CUR_NSEC */ +#define PTP_PTP_CUR_NSEC(g) __REG(TARGET_PTP, 0, 1, 336, g, 3, 28, 8, 0, 1, 4) + +#define PTP_PTP_CUR_NSEC_PTP_CUR_NSEC GENMASK(29, 0) +#define PTP_PTP_CUR_NSEC_PTP_CUR_NSEC_SET(x)\ + FIELD_PREP(PTP_PTP_CUR_NSEC_PTP_CUR_NSEC, x) +#define PTP_PTP_CUR_NSEC_PTP_CUR_NSEC_GET(x)\ + FIELD_GET(PTP_PTP_CUR_NSEC_PTP_CUR_NSEC, x) + +/* DEVCPU_PTP:PTP_TOD_DOMAINS:PTP_CUR_NSEC_FRAC */ +#define PTP_PTP_CUR_NSEC_FRAC(g) __REG(TARGET_PTP, 0, 1, 336, g, 3, 28, 12, 0, 1, 4) + +#define PTP_PTP_CUR_NSEC_FRAC_PTP_CUR_NSEC_FRAC GENMASK(7, 0) +#define PTP_PTP_CUR_NSEC_FRAC_PTP_CUR_NSEC_FRAC_SET(x)\ + FIELD_PREP(PTP_PTP_CUR_NSEC_FRAC_PTP_CUR_NSEC_FRAC, x) +#define PTP_PTP_CUR_NSEC_FRAC_PTP_CUR_NSEC_FRAC_GET(x)\ + FIELD_GET(PTP_PTP_CUR_NSEC_FRAC_PTP_CUR_NSEC_FRAC, x) + +/* DEVCPU_PTP:PTP_TOD_DOMAINS:PTP_CUR_SEC_LSB */ +#define PTP_PTP_CUR_SEC_LSB(g) __REG(TARGET_PTP, 0, 1, 336, g, 3, 28, 16, 0, 1, 4) + +/* DEVCPU_PTP:PTP_TOD_DOMAINS:PTP_CUR_SEC_MSB */ +#define PTP_PTP_CUR_SEC_MSB(g) __REG(TARGET_PTP, 0, 1, 336, g, 3, 28, 20, 0, 1, 4) + +#define PTP_PTP_CUR_SEC_MSB_PTP_CUR_SEC_MSB GENMASK(15, 0) +#define PTP_PTP_CUR_SEC_MSB_PTP_CUR_SEC_MSB_SET(x)\ + FIELD_PREP(PTP_PTP_CUR_SEC_MSB_PTP_CUR_SEC_MSB, x) +#define PTP_PTP_CUR_SEC_MSB_PTP_CUR_SEC_MSB_GET(x)\ + FIELD_GET(PTP_PTP_CUR_SEC_MSB_PTP_CUR_SEC_MSB, x) + +/* DEVCPU_PTP:PTP_TOD_DOMAINS:NTP_CUR_NSEC */ +#define PTP_NTP_CUR_NSEC(g) __REG(TARGET_PTP, 0, 1, 336, g, 3, 28, 24, 0, 1, 4) + +/* DEVCPU_PTP:PTP_PINS:PTP_PIN_CFG */ +#define PTP_PTP_PIN_CFG(g) __REG(TARGET_PTP, 0, 1, 0, g, 5, 64, 0, 0, 1, 4) + +#define PTP_PTP_PIN_CFG_PTP_PIN_ACTION GENMASK(28, 26) +#define PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(x)\ + FIELD_PREP(PTP_PTP_PIN_CFG_PTP_PIN_ACTION, x) +#define PTP_PTP_PIN_CFG_PTP_PIN_ACTION_GET(x)\ + FIELD_GET(PTP_PTP_PIN_CFG_PTP_PIN_ACTION, x) + +#define PTP_PTP_PIN_CFG_PTP_PIN_SYNC GENMASK(25, 24) +#define PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(x)\ + FIELD_PREP(PTP_PTP_PIN_CFG_PTP_PIN_SYNC, x) +#define PTP_PTP_PIN_CFG_PTP_PIN_SYNC_GET(x)\ + FIELD_GET(PTP_PTP_PIN_CFG_PTP_PIN_SYNC, x) + +#define PTP_PTP_PIN_CFG_PTP_PIN_INV_POL BIT(23) +#define PTP_PTP_PIN_CFG_PTP_PIN_INV_POL_SET(x)\ + FIELD_PREP(PTP_PTP_PIN_CFG_PTP_PIN_INV_POL, x) +#define PTP_PTP_PIN_CFG_PTP_PIN_INV_POL_GET(x)\ + FIELD_GET(PTP_PTP_PIN_CFG_PTP_PIN_INV_POL, x) + +#define PTP_PTP_PIN_CFG_PTP_PIN_SELECT GENMASK(22, 21) +#define PTP_PTP_PIN_CFG_PTP_PIN_SELECT_SET(x)\ + FIELD_PREP(PTP_PTP_PIN_CFG_PTP_PIN_SELECT, x) +#define PTP_PTP_PIN_CFG_PTP_PIN_SELECT_GET(x)\ + FIELD_GET(PTP_PTP_PIN_CFG_PTP_PIN_SELECT, x) + +#define PTP_PTP_PIN_CFG_PTP_CLK_SELECT GENMASK(20, 18) +#define PTP_PTP_PIN_CFG_PTP_CLK_SELECT_SET(x)\ + FIELD_PREP(PTP_PTP_PIN_CFG_PTP_CLK_SELECT, x) +#define PTP_PTP_PIN_CFG_PTP_CLK_SELECT_GET(x)\ + FIELD_GET(PTP_PTP_PIN_CFG_PTP_CLK_SELECT, x) + +#define PTP_PTP_PIN_CFG_PTP_PIN_DOM GENMASK(17, 16) +#define PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(x)\ + FIELD_PREP(PTP_PTP_PIN_CFG_PTP_PIN_DOM, x) +#define PTP_PTP_PIN_CFG_PTP_PIN_DOM_GET(x)\ + FIELD_GET(PTP_PTP_PIN_CFG_PTP_PIN_DOM, x) + +#define PTP_PTP_PIN_CFG_PTP_PIN_OPT GENMASK(15, 14) +#define PTP_PTP_PIN_CFG_PTP_PIN_OPT_SET(x)\ + FIELD_PREP(PTP_PTP_PIN_CFG_PTP_PIN_OPT, x) +#define PTP_PTP_PIN_CFG_PTP_PIN_OPT_GET(x)\ + FIELD_GET(PTP_PTP_PIN_CFG_PTP_PIN_OPT, x) + +#define PTP_PTP_PIN_CFG_PTP_PIN_EMBEDDED_CLK BIT(13) +#define PTP_PTP_PIN_CFG_PTP_PIN_EMBEDDED_CLK_SET(x)\ + FIELD_PREP(PTP_PTP_PIN_CFG_PTP_PIN_EMBEDDED_CLK, x) +#define PTP_PTP_PIN_CFG_PTP_PIN_EMBEDDED_CLK_GET(x)\ + FIELD_GET(PTP_PTP_PIN_CFG_PTP_PIN_EMBEDDED_CLK, x) + +#define PTP_PTP_PIN_CFG_PTP_PIN_OUTP_OFS GENMASK(12, 0) +#define PTP_PTP_PIN_CFG_PTP_PIN_OUTP_OFS_SET(x)\ + FIELD_PREP(PTP_PTP_PIN_CFG_PTP_PIN_OUTP_OFS, x) +#define PTP_PTP_PIN_CFG_PTP_PIN_OUTP_OFS_GET(x)\ + FIELD_GET(PTP_PTP_PIN_CFG_PTP_PIN_OUTP_OFS, x) + +/* DEVCPU_PTP:PTP_PINS:PTP_TOD_SEC_MSB */ +#define PTP_PTP_TOD_SEC_MSB(g) __REG(TARGET_PTP, 0, 1, 0, g, 5, 64, 4, 0, 1, 4) + +#define PTP_PTP_TOD_SEC_MSB_PTP_TOD_SEC_MSB GENMASK(15, 0) +#define PTP_PTP_TOD_SEC_MSB_PTP_TOD_SEC_MSB_SET(x)\ + FIELD_PREP(PTP_PTP_TOD_SEC_MSB_PTP_TOD_SEC_MSB, x) +#define PTP_PTP_TOD_SEC_MSB_PTP_TOD_SEC_MSB_GET(x)\ + FIELD_GET(PTP_PTP_TOD_SEC_MSB_PTP_TOD_SEC_MSB, x) + +/* DEVCPU_PTP:PTP_PINS:PTP_TOD_SEC_LSB */ +#define PTP_PTP_TOD_SEC_LSB(g) __REG(TARGET_PTP, 0, 1, 0, g, 5, 64, 8, 0, 1, 4) + +/* DEVCPU_PTP:PTP_PINS:PTP_TOD_NSEC */ +#define PTP_PTP_TOD_NSEC(g) __REG(TARGET_PTP, 0, 1, 0, g, 5, 64, 12, 0, 1, 4) + +#define PTP_PTP_TOD_NSEC_PTP_TOD_NSEC GENMASK(29, 0) +#define PTP_PTP_TOD_NSEC_PTP_TOD_NSEC_SET(x)\ + FIELD_PREP(PTP_PTP_TOD_NSEC_PTP_TOD_NSEC, x) +#define PTP_PTP_TOD_NSEC_PTP_TOD_NSEC_GET(x)\ + FIELD_GET(PTP_PTP_TOD_NSEC_PTP_TOD_NSEC, x) + +/* DEVCPU_PTP:PTP_PINS:PTP_TOD_NSEC_FRAC */ +#define PTP_PTP_TOD_NSEC_FRAC(g) __REG(TARGET_PTP, 0, 1, 0, g, 5, 64, 16, 0, 1, 4) + +#define PTP_PTP_TOD_NSEC_FRAC_PTP_TOD_NSEC_FRAC GENMASK(7, 0) +#define PTP_PTP_TOD_NSEC_FRAC_PTP_TOD_NSEC_FRAC_SET(x)\ + FIELD_PREP(PTP_PTP_TOD_NSEC_FRAC_PTP_TOD_NSEC_FRAC, x) +#define PTP_PTP_TOD_NSEC_FRAC_PTP_TOD_NSEC_FRAC_GET(x)\ + FIELD_GET(PTP_PTP_TOD_NSEC_FRAC_PTP_TOD_NSEC_FRAC, x) + +/* DEVCPU_PTP:PTP_PINS:NTP_NSEC */ +#define PTP_NTP_NSEC(g) __REG(TARGET_PTP, 0, 1, 0, g, 5, 64, 20, 0, 1, 4) + +/* DEVCPU_PTP:PTP_PINS:PIN_WF_HIGH_PERIOD */ +#define PTP_PIN_WF_HIGH_PERIOD(g) __REG(TARGET_PTP, 0, 1, 0, g, 5, 64, 24, 0, 1, 4) + +#define PTP_PIN_WF_HIGH_PERIOD_PIN_WFH GENMASK(29, 0) +#define PTP_PIN_WF_HIGH_PERIOD_PIN_WFH_SET(x)\ + FIELD_PREP(PTP_PIN_WF_HIGH_PERIOD_PIN_WFH, x) +#define PTP_PIN_WF_HIGH_PERIOD_PIN_WFH_GET(x)\ + FIELD_GET(PTP_PIN_WF_HIGH_PERIOD_PIN_WFH, x) + +/* DEVCPU_PTP:PTP_PINS:PIN_WF_LOW_PERIOD */ +#define PTP_PIN_WF_LOW_PERIOD(g) __REG(TARGET_PTP, 0, 1, 0, g, 5, 64, 28, 0, 1, 4) + +#define PTP_PIN_WF_LOW_PERIOD_PIN_WFL GENMASK(29, 0) +#define PTP_PIN_WF_LOW_PERIOD_PIN_WFL_SET(x)\ + FIELD_PREP(PTP_PIN_WF_LOW_PERIOD_PIN_WFL, x) +#define PTP_PIN_WF_LOW_PERIOD_PIN_WFL_GET(x)\ + FIELD_GET(PTP_PIN_WF_LOW_PERIOD_PIN_WFL, x) + +/* DEVCPU_PTP:PTP_PINS:PIN_IOBOUNCH_DELAY */ +#define PTP_PIN_IOBOUNCH_DELAY(g) __REG(TARGET_PTP, 0, 1, 0, g, 5, 64, 32, 0, 1, 4) + +#define PTP_PIN_IOBOUNCH_DELAY_PIN_IOBOUNCH_VAL GENMASK(18, 3) +#define PTP_PIN_IOBOUNCH_DELAY_PIN_IOBOUNCH_VAL_SET(x)\ + FIELD_PREP(PTP_PIN_IOBOUNCH_DELAY_PIN_IOBOUNCH_VAL, x) +#define PTP_PIN_IOBOUNCH_DELAY_PIN_IOBOUNCH_VAL_GET(x)\ + FIELD_GET(PTP_PIN_IOBOUNCH_DELAY_PIN_IOBOUNCH_VAL, x) + +#define PTP_PIN_IOBOUNCH_DELAY_PIN_IOBOUNCH_CFG GENMASK(2, 0) +#define PTP_PIN_IOBOUNCH_DELAY_PIN_IOBOUNCH_CFG_SET(x)\ + FIELD_PREP(PTP_PIN_IOBOUNCH_DELAY_PIN_IOBOUNCH_CFG, x) +#define PTP_PIN_IOBOUNCH_DELAY_PIN_IOBOUNCH_CFG_GET(x)\ + FIELD_GET(PTP_PIN_IOBOUNCH_DELAY_PIN_IOBOUNCH_CFG, x) + +/* DEVCPU_PTP:PHASE_DETECTOR_CTRL:PHAD_CTRL */ +#define PTP_PHAD_CTRL(g) __REG(TARGET_PTP, 0, 1, 420, g, 5, 8, 0, 0, 1, 4) + +#define PTP_PHAD_CTRL_PHAD_ENA BIT(7) +#define PTP_PHAD_CTRL_PHAD_ENA_SET(x)\ + FIELD_PREP(PTP_PHAD_CTRL_PHAD_ENA, x) +#define PTP_PHAD_CTRL_PHAD_ENA_GET(x)\ + FIELD_GET(PTP_PHAD_CTRL_PHAD_ENA, x) + +#define PTP_PHAD_CTRL_PHAD_FAILED BIT(6) +#define PTP_PHAD_CTRL_PHAD_FAILED_SET(x)\ + FIELD_PREP(PTP_PHAD_CTRL_PHAD_FAILED, x) +#define PTP_PHAD_CTRL_PHAD_FAILED_GET(x)\ + FIELD_GET(PTP_PHAD_CTRL_PHAD_FAILED, x) + +#define PTP_PHAD_CTRL_REDUCED_RES GENMASK(5, 3) +#define PTP_PHAD_CTRL_REDUCED_RES_SET(x)\ + FIELD_PREP(PTP_PHAD_CTRL_REDUCED_RES, x) +#define PTP_PHAD_CTRL_REDUCED_RES_GET(x)\ + FIELD_GET(PTP_PHAD_CTRL_REDUCED_RES, x) + +#define PTP_PHAD_CTRL_LOCK_ACC GENMASK(2, 0) +#define PTP_PHAD_CTRL_LOCK_ACC_SET(x)\ + FIELD_PREP(PTP_PHAD_CTRL_LOCK_ACC, x) +#define PTP_PHAD_CTRL_LOCK_ACC_GET(x)\ + FIELD_GET(PTP_PHAD_CTRL_LOCK_ACC, x) + +/* DEVCPU_PTP:PHASE_DETECTOR_CTRL:PHAD_CYC_STAT */ +#define PTP_PHAD_CYC_STAT(g) __REG(TARGET_PTP, 0, 1, 420, g, 5, 8, 4, 0, 1, 4) + /* QFWD:SYSTEM:SWITCH_PORT_MODE */ #define QFWD_SWITCH_PORT_MODE(r) __REG(TARGET_QFWD, 0, 1, 0, 0, 1, 340, 0, r, 70, 4) @@ -4528,6 +4772,93 @@ enum sparx5_target { #define REW_TAG_CTRL_TAG_DEI_CFG_GET(x)\ FIELD_GET(REW_TAG_CTRL_TAG_DEI_CFG, x) +/* REW:PTP_CTRL:PTP_TWOSTEP_CTRL */ +#define REW_PTP_TWOSTEP_CTRL __REG(TARGET_REW, 0, 1, 378368, 0, 1, 40, 0, 0, 1, 4) + +#define REW_PTP_TWOSTEP_CTRL_PTP_OVWR_ENA BIT(12) +#define REW_PTP_TWOSTEP_CTRL_PTP_OVWR_ENA_SET(x)\ + FIELD_PREP(REW_PTP_TWOSTEP_CTRL_PTP_OVWR_ENA, x) +#define REW_PTP_TWOSTEP_CTRL_PTP_OVWR_ENA_GET(x)\ + FIELD_GET(REW_PTP_TWOSTEP_CTRL_PTP_OVWR_ENA, x) + +#define REW_PTP_TWOSTEP_CTRL_PTP_NXT BIT(11) +#define REW_PTP_TWOSTEP_CTRL_PTP_NXT_SET(x)\ + FIELD_PREP(REW_PTP_TWOSTEP_CTRL_PTP_NXT, x) +#define REW_PTP_TWOSTEP_CTRL_PTP_NXT_GET(x)\ + FIELD_GET(REW_PTP_TWOSTEP_CTRL_PTP_NXT, x) + +#define REW_PTP_TWOSTEP_CTRL_PTP_VLD BIT(10) +#define REW_PTP_TWOSTEP_CTRL_PTP_VLD_SET(x)\ + FIELD_PREP(REW_PTP_TWOSTEP_CTRL_PTP_VLD, x) +#define REW_PTP_TWOSTEP_CTRL_PTP_VLD_GET(x)\ + FIELD_GET(REW_PTP_TWOSTEP_CTRL_PTP_VLD, x) + +#define REW_PTP_TWOSTEP_CTRL_STAMP_TX BIT(9) +#define REW_PTP_TWOSTEP_CTRL_STAMP_TX_SET(x)\ + FIELD_PREP(REW_PTP_TWOSTEP_CTRL_STAMP_TX, x) +#define REW_PTP_TWOSTEP_CTRL_STAMP_TX_GET(x)\ + FIELD_GET(REW_PTP_TWOSTEP_CTRL_STAMP_TX, x) + +#define REW_PTP_TWOSTEP_CTRL_STAMP_PORT GENMASK(8, 1) +#define REW_PTP_TWOSTEP_CTRL_STAMP_PORT_SET(x)\ + FIELD_PREP(REW_PTP_TWOSTEP_CTRL_STAMP_PORT, x) +#define REW_PTP_TWOSTEP_CTRL_STAMP_PORT_GET(x)\ + FIELD_GET(REW_PTP_TWOSTEP_CTRL_STAMP_PORT, x) + +#define REW_PTP_TWOSTEP_CTRL_PTP_OVFL BIT(0) +#define REW_PTP_TWOSTEP_CTRL_PTP_OVFL_SET(x)\ + FIELD_PREP(REW_PTP_TWOSTEP_CTRL_PTP_OVFL, x) +#define REW_PTP_TWOSTEP_CTRL_PTP_OVFL_GET(x)\ + FIELD_GET(REW_PTP_TWOSTEP_CTRL_PTP_OVFL, x) + +/* REW:PTP_CTRL:PTP_TWOSTEP_STAMP */ +#define REW_PTP_TWOSTEP_STAMP __REG(TARGET_REW, 0, 1, 378368, 0, 1, 40, 4, 0, 1, 4) + +#define REW_PTP_TWOSTEP_STAMP_STAMP_NSEC GENMASK(29, 0) +#define REW_PTP_TWOSTEP_STAMP_STAMP_NSEC_SET(x)\ + FIELD_PREP(REW_PTP_TWOSTEP_STAMP_STAMP_NSEC, x) +#define REW_PTP_TWOSTEP_STAMP_STAMP_NSEC_GET(x)\ + FIELD_GET(REW_PTP_TWOSTEP_STAMP_STAMP_NSEC, x) + +/* REW:PTP_CTRL:PTP_TWOSTEP_STAMP_SUBNS */ +#define REW_PTP_TWOSTEP_STAMP_SUBNS __REG(TARGET_REW, 0, 1, 378368, 0, 1, 40, 8, 0, 1, 4) + +#define REW_PTP_TWOSTEP_STAMP_SUBNS_STAMP_SUB_NSEC GENMASK(7, 0) +#define REW_PTP_TWOSTEP_STAMP_SUBNS_STAMP_SUB_NSEC_SET(x)\ + FIELD_PREP(REW_PTP_TWOSTEP_STAMP_SUBNS_STAMP_SUB_NSEC, x) +#define REW_PTP_TWOSTEP_STAMP_SUBNS_STAMP_SUB_NSEC_GET(x)\ + FIELD_GET(REW_PTP_TWOSTEP_STAMP_SUBNS_STAMP_SUB_NSEC, x) + +/* REW:PTP_CTRL:PTP_RSRV_NOT_ZERO */ +#define REW_PTP_RSRV_NOT_ZERO __REG(TARGET_REW, 0, 1, 378368, 0, 1, 40, 12, 0, 1, 4) + +/* REW:PTP_CTRL:PTP_RSRV_NOT_ZERO1 */ +#define REW_PTP_RSRV_NOT_ZERO1 __REG(TARGET_REW, 0, 1, 378368, 0, 1, 40, 16, 0, 1, 4) + +/* REW:PTP_CTRL:PTP_RSRV_NOT_ZERO2 */ +#define REW_PTP_RSRV_NOT_ZERO2 __REG(TARGET_REW, 0, 1, 378368, 0, 1, 40, 20, 0, 1, 4) + +#define REW_PTP_RSRV_NOT_ZERO2_PTP_RSRV_NOT_ZERO2 GENMASK(5, 0) +#define REW_PTP_RSRV_NOT_ZERO2_PTP_RSRV_NOT_ZERO2_SET(x)\ + FIELD_PREP(REW_PTP_RSRV_NOT_ZERO2_PTP_RSRV_NOT_ZERO2, x) +#define REW_PTP_RSRV_NOT_ZERO2_PTP_RSRV_NOT_ZERO2_GET(x)\ + FIELD_GET(REW_PTP_RSRV_NOT_ZERO2_PTP_RSRV_NOT_ZERO2, x) + +/* REW:PTP_CTRL:PTP_GEN_STAMP_FMT */ +#define REW_PTP_GEN_STAMP_FMT(r) __REG(TARGET_REW, 0, 1, 378368, 0, 1, 40, 24, r, 4, 4) + +#define REW_PTP_GEN_STAMP_FMT_RT_OFS GENMASK(6, 2) +#define REW_PTP_GEN_STAMP_FMT_RT_OFS_SET(x)\ + FIELD_PREP(REW_PTP_GEN_STAMP_FMT_RT_OFS, x) +#define REW_PTP_GEN_STAMP_FMT_RT_OFS_GET(x)\ + FIELD_GET(REW_PTP_GEN_STAMP_FMT_RT_OFS, x) + +#define REW_PTP_GEN_STAMP_FMT_RT_FMT GENMASK(1, 0) +#define REW_PTP_GEN_STAMP_FMT_RT_FMT_SET(x)\ + FIELD_PREP(REW_PTP_GEN_STAMP_FMT_RT_FMT, x) +#define REW_PTP_GEN_STAMP_FMT_RT_FMT_GET(x)\ + FIELD_GET(REW_PTP_GEN_STAMP_FMT_RT_FMT, x) + /* REW:RAM_CTRL:RAM_INIT */ #define REW_RAM_INIT __REG(TARGET_REW, 0, 1, 378696, 0, 1, 4, 0, 0, 1, 4) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c index e042f117dc7a..af4d3e1f1a6d 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c @@ -54,7 +54,7 @@ static void __ifh_encode_bitfield(void *ifh, u64 value, u32 pos, u32 width) ifh_hdr[byte - 5] |= (u8)((encode & 0xFF0000000000) >> 40); } -static void sparx5_set_port_ifh(void *ifh_hdr, u16 portno) +void sparx5_set_port_ifh(void *ifh_hdr, u16 portno) { /* VSTAX.RSV = 1. MSBit must be 1 */ ifh_encode_bitfield(ifh_hdr, 1, VSTAX + 79, 1); @@ -74,6 +74,26 @@ static void sparx5_set_port_ifh(void *ifh_hdr, u16 portno) ifh_encode_bitfield(ifh_hdr, 1, 67, 1); } +void sparx5_set_port_ifh_rew_op(void *ifh_hdr, u32 rew_op) +{ + ifh_encode_bitfield(ifh_hdr, rew_op, VSTAX + 32, 10); +} + +void sparx5_set_port_ifh_pdu_type(void *ifh_hdr, u32 pdu_type) +{ + ifh_encode_bitfield(ifh_hdr, pdu_type, 191, 4); +} + +void sparx5_set_port_ifh_pdu_w16_offset(void *ifh_hdr, u32 pdu_w16_offset) +{ + ifh_encode_bitfield(ifh_hdr, pdu_w16_offset, 195, 6); +} + +void sparx5_set_port_ifh_timestamp(void *ifh_hdr, u64 timestamp) +{ + ifh_encode_bitfield(ifh_hdr, timestamp, 232, 40); +} + static int sparx5_port_open(struct net_device *ndev) { struct sparx5_port *port = netdev_priv(ndev); @@ -179,6 +199,24 @@ static int sparx5_get_port_parent_id(struct net_device *dev, return 0; } +static int sparx5_port_ioctl(struct net_device *dev, struct ifreq *ifr, + int cmd) +{ + struct sparx5_port *sparx5_port = netdev_priv(dev); + struct sparx5 *sparx5 = sparx5_port->sparx5; + + if (!phy_has_hwtstamp(dev->phydev) && sparx5->ptp) { + switch (cmd) { + case SIOCSHWTSTAMP: + return sparx5_ptp_hwtstamp_set(sparx5_port, ifr); + case SIOCGHWTSTAMP: + return sparx5_ptp_hwtstamp_get(sparx5_port, ifr); + } + } + + return phy_mii_ioctl(dev->phydev, ifr, cmd); +} + static const struct net_device_ops sparx5_port_netdev_ops = { .ndo_open = sparx5_port_open, .ndo_stop = sparx5_port_stop, @@ -189,6 +227,7 @@ static const struct net_device_ops sparx5_port_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_get_stats64 = sparx5_get_stats64, .ndo_get_port_parent_id = sparx5_get_port_parent_id, + .ndo_eth_ioctl = sparx5_port_ioctl, }; bool sparx5_netdevice_check(const struct net_device *dev) @@ -210,7 +249,6 @@ struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno) spx5_port->ndev = ndev; spx5_port->sparx5 = sparx5; spx5_port->portno = portno; - sparx5_set_port_ifh(spx5_port->ifh, portno); ndev->netdev_ops = &sparx5_port_netdev_ops; ndev->ethtool_ops = &sparx5_ethtool_ops; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c index 148d431fcde4..304f84aadc36 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c @@ -44,6 +44,12 @@ void sparx5_ifh_parse(u32 *ifh, struct frame_info *info) ((u32)xtr_hdr[30] << 0); fwd = (fwd >> 5); info->src_port = FIELD_GET(GENMASK(7, 1), fwd); + + info->timestamp = + ((u64)xtr_hdr[2] << 24) | + ((u64)xtr_hdr[3] << 16) | + ((u64)xtr_hdr[4] << 8) | + ((u64)xtr_hdr[5] << 0); } static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap) @@ -144,6 +150,7 @@ static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap) /* Finish up skb */ skb_put(skb, byte_cnt - ETH_FCS_LEN); eth_skb_pad(skb); + sparx5_ptp_rxtstamp(sparx5, skb, fi.timestamp); skb->protocol = eth_type_trans(skb, netdev); netdev->stats.rx_bytes += skb->len; netdev->stats.rx_packets++; @@ -218,20 +225,44 @@ int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev) struct net_device_stats *stats = &dev->stats; struct sparx5_port *port = netdev_priv(dev); struct sparx5 *sparx5 = port->sparx5; + u32 ifh[IFH_LEN]; int ret; + memset(ifh, 0, IFH_LEN * 4); + sparx5_set_port_ifh(ifh, port->portno); + + if (sparx5->ptp && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { + ret = sparx5_ptp_txtstamp_request(port, skb); + if (ret) + return ret; + + sparx5_set_port_ifh_rew_op(ifh, SPARX5_SKB_CB(skb)->rew_op); + sparx5_set_port_ifh_pdu_type(ifh, SPARX5_SKB_CB(skb)->pdu_type); + sparx5_set_port_ifh_pdu_w16_offset(ifh, SPARX5_SKB_CB(skb)->pdu_w16_offset); + sparx5_set_port_ifh_timestamp(ifh, SPARX5_SKB_CB(skb)->ts_id); + } + + skb_tx_timestamp(skb); if (sparx5->fdma_irq > 0) - ret = sparx5_fdma_xmit(sparx5, port->ifh, skb); + ret = sparx5_fdma_xmit(sparx5, ifh, skb); else - ret = sparx5_inject(sparx5, port->ifh, skb, dev); + ret = sparx5_inject(sparx5, ifh, skb, dev); if (ret == NETDEV_TX_OK) { stats->tx_bytes += skb->len; stats->tx_packets++; - skb_tx_timestamp(skb); + + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && + SPARX5_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP) + return ret; + dev_kfree_skb_any(skb); } else { stats->tx_dropped++; + + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && + SPARX5_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP) + sparx5_ptp_txtstamp_release(port, skb); } return ret; } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_pgid.c b/drivers/net/ethernet/microchip/sparx5/sparx5_pgid.c new file mode 100644 index 000000000000..90366fcb9958 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_pgid.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include "sparx5_main.h" + +void sparx5_pgid_init(struct sparx5 *spx5) +{ + int i; + + for (i = 0; i < PGID_TABLE_SIZE; i++) + spx5->pgid_map[i] = SPX5_PGID_FREE; + + /* Reserved for unicast, flood control, broadcast, and CPU. + * These cannot be freed. + */ + for (i = 0; i <= PGID_CPU; i++) + spx5->pgid_map[i] = SPX5_PGID_RESERVED; +} + +int sparx5_pgid_alloc_glag(struct sparx5 *spx5, u16 *idx) +{ + int i; + + for (i = PGID_GLAG_START; i <= PGID_GLAG_END; i++) + if (spx5->pgid_map[i] == SPX5_PGID_FREE) { + spx5->pgid_map[i] = SPX5_PGID_GLAG; + *idx = i; + return 0; + } + + return -EBUSY; +} + +int sparx5_pgid_alloc_mcast(struct sparx5 *spx5, u16 *idx) +{ + int i; + + for (i = PGID_MCAST_START; i < PGID_TABLE_SIZE; i++) { + if (i == PGID_GLAG_START) + i = PGID_GLAG_END + 1; + + if (spx5->pgid_map[i] == SPX5_PGID_FREE) { + spx5->pgid_map[i] = SPX5_PGID_MULTICAST; + *idx = i; + return 0; + } + } + + return -EBUSY; +} + +int sparx5_pgid_free(struct sparx5 *spx5, u16 idx) +{ + if (idx <= PGID_CPU || idx >= PGID_TABLE_SIZE) + return -EINVAL; + + if (spx5->pgid_map[idx] == SPX5_PGID_FREE) + return -EINVAL; + + spx5->pgid_map[idx] = SPX5_PGID_FREE; + return 0; +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c index 8ba33bc1a001..830da0e5ff27 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c @@ -26,6 +26,15 @@ static bool port_conf_has_changed(struct sparx5_port_config *a, struct sparx5_po return false; } +static struct phylink_pcs * +sparx5_phylink_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct sparx5_port *port = netdev_priv(to_net_dev(config->dev)); + + return &port->phylink_pcs; +} + static void sparx5_phylink_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) @@ -130,6 +139,7 @@ const struct phylink_pcs_ops sparx5_phylink_pcs_ops = { const struct phylink_mac_ops sparx5_phylink_mac_ops = { .validate = phylink_generic_validate, + .mac_select_pcs = sparx5_phylink_mac_select_pcs, .mac_config = sparx5_phylink_mac_config, .mac_link_down = sparx5_phylink_mac_link_down, .mac_link_up = sparx5_phylink_mac_link_up, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c new file mode 100644 index 000000000000..0ed1ea7727c5 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c @@ -0,0 +1,685 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip Sparx5 Switch driver + * + * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries. + * + * The Sparx5 Chip Register Model can be browsed at this location: + * https://github.com/microchip-ung/sparx-5_reginfo + */ +#include + +#include "sparx5_main_regs.h" +#include "sparx5_main.h" + +#define SPARX5_MAX_PTP_ID 512 + +#define TOD_ACC_PIN 0x4 + +enum { + PTP_PIN_ACTION_IDLE = 0, + PTP_PIN_ACTION_LOAD, + PTP_PIN_ACTION_SAVE, + PTP_PIN_ACTION_CLOCK, + PTP_PIN_ACTION_DELTA, + PTP_PIN_ACTION_TOD +}; + +static u64 sparx5_ptp_get_1ppm(struct sparx5 *sparx5) +{ + /* Represents 1ppm adjustment in 2^59 format with 1.59687500000(625) + * 1.99609375000(500), 3.99218750000(250) as reference + * The value is calculated as following: + * (1/1000000)/((2^-59)/X) + */ + + u64 res = 0; + + switch (sparx5->coreclock) { + case SPX5_CORE_CLOCK_250MHZ: + res = 2301339409586; + break; + case SPX5_CORE_CLOCK_500MHZ: + res = 1150669704793; + break; + case SPX5_CORE_CLOCK_625MHZ: + res = 920535763834; + break; + default: + WARN(1, "Invalid core clock"); + break; + } + + return res; +} + +static u64 sparx5_ptp_get_nominal_value(struct sparx5 *sparx5) +{ + u64 res = 0; + + switch (sparx5->coreclock) { + case SPX5_CORE_CLOCK_250MHZ: + res = 0x1FF0000000000000; + break; + case SPX5_CORE_CLOCK_500MHZ: + res = 0x0FF8000000000000; + break; + case SPX5_CORE_CLOCK_625MHZ: + res = 0x0CC6666666666666; + break; + default: + WARN(1, "Invalid core clock"); + break; + } + + return res; +} + +int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr) +{ + struct sparx5 *sparx5 = port->sparx5; + struct hwtstamp_config cfg; + struct sparx5_phc *phc; + + /* For now don't allow to run ptp on ports that are part of a bridge, + * because in case of transparent clock the HW will still forward the + * frames, so there would be duplicate frames + */ + + if (test_bit(port->portno, sparx5->bridge_mask)) + return -EINVAL; + + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + switch (cfg.tx_type) { + case HWTSTAMP_TX_ON: + port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; + break; + case HWTSTAMP_TX_ONESTEP_SYNC: + port->ptp_cmd = IFH_REW_OP_ONE_STEP_PTP; + break; + case HWTSTAMP_TX_OFF: + port->ptp_cmd = IFH_REW_OP_NOOP; + break; + default: + return -ERANGE; + } + + switch (cfg.rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_NTP_ALL: + cfg.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + return -ERANGE; + } + + /* Commit back the result & save it */ + mutex_lock(&sparx5->ptp_lock); + phc = &sparx5->phc[SPARX5_PHC_PORT]; + memcpy(&phc->hwtstamp_config, &cfg, sizeof(cfg)); + mutex_unlock(&sparx5->ptp_lock); + + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; +} + +int sparx5_ptp_hwtstamp_get(struct sparx5_port *port, struct ifreq *ifr) +{ + struct sparx5 *sparx5 = port->sparx5; + struct sparx5_phc *phc; + + phc = &sparx5->phc[SPARX5_PHC_PORT]; + return copy_to_user(ifr->ifr_data, &phc->hwtstamp_config, + sizeof(phc->hwtstamp_config)) ? -EFAULT : 0; +} + +static void sparx5_ptp_classify(struct sparx5_port *port, struct sk_buff *skb, + u8 *rew_op, u8 *pdu_type, u8 *pdu_w16_offset) +{ + struct ptp_header *header; + u8 msgtype; + int type; + + if (port->ptp_cmd == IFH_REW_OP_NOOP) { + *rew_op = IFH_REW_OP_NOOP; + *pdu_type = IFH_PDU_TYPE_NONE; + *pdu_w16_offset = 0; + return; + } + + type = ptp_classify_raw(skb); + if (type == PTP_CLASS_NONE) { + *rew_op = IFH_REW_OP_NOOP; + *pdu_type = IFH_PDU_TYPE_NONE; + *pdu_w16_offset = 0; + return; + } + + header = ptp_parse_header(skb, type); + if (!header) { + *rew_op = IFH_REW_OP_NOOP; + *pdu_type = IFH_PDU_TYPE_NONE; + *pdu_w16_offset = 0; + return; + } + + *pdu_w16_offset = 7; + if (type & PTP_CLASS_L2) + *pdu_type = IFH_PDU_TYPE_PTP; + if (type & PTP_CLASS_IPV4) + *pdu_type = IFH_PDU_TYPE_IPV4_UDP_PTP; + if (type & PTP_CLASS_IPV6) + *pdu_type = IFH_PDU_TYPE_IPV6_UDP_PTP; + + if (port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { + *rew_op = IFH_REW_OP_TWO_STEP_PTP; + return; + } + + /* If it is sync and run 1 step then set the correct operation, + * otherwise run as 2 step + */ + msgtype = ptp_get_msgtype(header, type); + if ((msgtype & 0xf) == 0) { + *rew_op = IFH_REW_OP_ONE_STEP_PTP; + return; + } + + *rew_op = IFH_REW_OP_TWO_STEP_PTP; +} + +static void sparx5_ptp_txtstamp_old_release(struct sparx5_port *port) +{ + struct sk_buff *skb, *skb_tmp; + unsigned long flags; + + spin_lock_irqsave(&port->tx_skbs.lock, flags); + skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { + if time_after(SPARX5_SKB_CB(skb)->jiffies + SPARX5_PTP_TIMEOUT, + jiffies) + break; + + __skb_unlink(skb, &port->tx_skbs); + dev_kfree_skb_any(skb); + } + spin_unlock_irqrestore(&port->tx_skbs.lock, flags); +} + +int sparx5_ptp_txtstamp_request(struct sparx5_port *port, + struct sk_buff *skb) +{ + struct sparx5 *sparx5 = port->sparx5; + u8 rew_op, pdu_type, pdu_w16_offset; + unsigned long flags; + + sparx5_ptp_classify(port, skb, &rew_op, &pdu_type, &pdu_w16_offset); + SPARX5_SKB_CB(skb)->rew_op = rew_op; + SPARX5_SKB_CB(skb)->pdu_type = pdu_type; + SPARX5_SKB_CB(skb)->pdu_w16_offset = pdu_w16_offset; + + if (rew_op != IFH_REW_OP_TWO_STEP_PTP) + return 0; + + sparx5_ptp_txtstamp_old_release(port); + + spin_lock_irqsave(&sparx5->ptp_ts_id_lock, flags); + if (sparx5->ptp_skbs == SPARX5_MAX_PTP_ID) { + spin_unlock_irqrestore(&sparx5->ptp_ts_id_lock, flags); + return -EBUSY; + } + + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + + skb_queue_tail(&port->tx_skbs, skb); + SPARX5_SKB_CB(skb)->ts_id = port->ts_id; + SPARX5_SKB_CB(skb)->jiffies = jiffies; + + sparx5->ptp_skbs++; + port->ts_id++; + if (port->ts_id == SPARX5_MAX_PTP_ID) + port->ts_id = 0; + + spin_unlock_irqrestore(&sparx5->ptp_ts_id_lock, flags); + + return 0; +} + +void sparx5_ptp_txtstamp_release(struct sparx5_port *port, + struct sk_buff *skb) +{ + struct sparx5 *sparx5 = port->sparx5; + unsigned long flags; + + spin_lock_irqsave(&sparx5->ptp_ts_id_lock, flags); + port->ts_id--; + sparx5->ptp_skbs--; + skb_unlink(skb, &port->tx_skbs); + spin_unlock_irqrestore(&sparx5->ptp_ts_id_lock, flags); +} + +static void sparx5_get_hwtimestamp(struct sparx5 *sparx5, + struct timespec64 *ts, + u32 nsec) +{ + /* Read current PTP time to get seconds */ + unsigned long flags; + u32 curr_nsec; + + spin_lock_irqsave(&sparx5->ptp_clock_lock, flags); + + spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_SAVE) | + PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(SPARX5_PHC_PORT) | + PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(0), + PTP_PTP_PIN_CFG_PTP_PIN_ACTION | + PTP_PTP_PIN_CFG_PTP_PIN_DOM | + PTP_PTP_PIN_CFG_PTP_PIN_SYNC, + sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN)); + + ts->tv_sec = spx5_rd(sparx5, PTP_PTP_TOD_SEC_LSB(TOD_ACC_PIN)); + curr_nsec = spx5_rd(sparx5, PTP_PTP_TOD_NSEC(TOD_ACC_PIN)); + + ts->tv_nsec = nsec; + + /* Sec has incremented since the ts was registered */ + if (curr_nsec < nsec) + ts->tv_sec--; + + spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags); +} + +irqreturn_t sparx5_ptp_irq_handler(int irq, void *args) +{ + int budget = SPARX5_MAX_PTP_ID; + struct sparx5 *sparx5 = args; + + while (budget--) { + struct sk_buff *skb, *skb_tmp, *skb_match = NULL; + struct skb_shared_hwtstamps shhwtstamps; + struct sparx5_port *port; + struct timespec64 ts; + unsigned long flags; + u32 val, id, txport; + u32 delay; + + val = spx5_rd(sparx5, REW_PTP_TWOSTEP_CTRL); + + /* Check if a timestamp can be retrieved */ + if (!(val & REW_PTP_TWOSTEP_CTRL_PTP_VLD)) + break; + + WARN_ON(val & REW_PTP_TWOSTEP_CTRL_PTP_OVFL); + + if (!(val & REW_PTP_TWOSTEP_CTRL_STAMP_TX)) + continue; + + /* Retrieve the ts Tx port */ + txport = REW_PTP_TWOSTEP_CTRL_STAMP_PORT_GET(val); + + /* Retrieve its associated skb */ + port = sparx5->ports[txport]; + + /* Retrieve the delay */ + delay = spx5_rd(sparx5, REW_PTP_TWOSTEP_STAMP); + delay = REW_PTP_TWOSTEP_STAMP_STAMP_NSEC_GET(delay); + + /* Get next timestamp from fifo, which needs to be the + * rx timestamp which represents the id of the frame + */ + spx5_rmw(REW_PTP_TWOSTEP_CTRL_PTP_NXT_SET(1), + REW_PTP_TWOSTEP_CTRL_PTP_NXT, + sparx5, REW_PTP_TWOSTEP_CTRL); + + val = spx5_rd(sparx5, REW_PTP_TWOSTEP_CTRL); + + /* Check if a timestamp can be retried */ + if (!(val & REW_PTP_TWOSTEP_CTRL_PTP_VLD)) + break; + + /* Read RX timestamping to get the ID */ + id = spx5_rd(sparx5, REW_PTP_TWOSTEP_STAMP); + id <<= 8; + id |= spx5_rd(sparx5, REW_PTP_TWOSTEP_STAMP_SUBNS); + + spin_lock_irqsave(&port->tx_skbs.lock, flags); + skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { + if (SPARX5_SKB_CB(skb)->ts_id != id) + continue; + + __skb_unlink(skb, &port->tx_skbs); + skb_match = skb; + break; + } + spin_unlock_irqrestore(&port->tx_skbs.lock, flags); + + /* Next ts */ + spx5_rmw(REW_PTP_TWOSTEP_CTRL_PTP_NXT_SET(1), + REW_PTP_TWOSTEP_CTRL_PTP_NXT, + sparx5, REW_PTP_TWOSTEP_CTRL); + + if (WARN_ON(!skb_match)) + continue; + + spin_lock(&sparx5->ptp_ts_id_lock); + sparx5->ptp_skbs--; + spin_unlock(&sparx5->ptp_ts_id_lock); + + /* Get the h/w timestamp */ + sparx5_get_hwtimestamp(sparx5, &ts, delay); + + /* Set the timestamp into the skb */ + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + skb_tstamp_tx(skb_match, &shhwtstamps); + + dev_kfree_skb_any(skb_match); + } + + return IRQ_HANDLED; +} + +static int sparx5_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct sparx5_phc *phc = container_of(ptp, struct sparx5_phc, info); + struct sparx5 *sparx5 = phc->sparx5; + unsigned long flags; + bool neg_adj = 0; + u64 tod_inc; + u64 ref; + + if (!scaled_ppm) + return 0; + + if (scaled_ppm < 0) { + neg_adj = 1; + scaled_ppm = -scaled_ppm; + } + + tod_inc = sparx5_ptp_get_nominal_value(sparx5); + + /* The multiplication is split in 2 separate additions because of + * overflow issues. If scaled_ppm with 16bit fractional part was bigger + * than 20ppm then we got overflow. + */ + ref = sparx5_ptp_get_1ppm(sparx5) * (scaled_ppm >> 16); + ref += (sparx5_ptp_get_1ppm(sparx5) * (0xffff & scaled_ppm)) >> 16; + tod_inc = neg_adj ? tod_inc - ref : tod_inc + ref; + + spin_lock_irqsave(&sparx5->ptp_clock_lock, flags); + + spx5_rmw(PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS_SET(1 << BIT(phc->index)), + PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS, + sparx5, PTP_PTP_DOM_CFG); + + spx5_wr((u32)tod_inc & 0xFFFFFFFF, sparx5, + PTP_CLK_PER_CFG(phc->index, 0)); + spx5_wr((u32)(tod_inc >> 32), sparx5, + PTP_CLK_PER_CFG(phc->index, 1)); + + spx5_rmw(PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS_SET(0), + PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS, sparx5, + PTP_PTP_DOM_CFG); + + spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags); + + return 0; +} + +static int sparx5_ptp_settime64(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct sparx5_phc *phc = container_of(ptp, struct sparx5_phc, info); + struct sparx5 *sparx5 = phc->sparx5; + unsigned long flags; + + spin_lock_irqsave(&sparx5->ptp_clock_lock, flags); + + /* Must be in IDLE mode before the time can be loaded */ + spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_IDLE) | + PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(phc->index) | + PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(0), + PTP_PTP_PIN_CFG_PTP_PIN_ACTION | + PTP_PTP_PIN_CFG_PTP_PIN_DOM | + PTP_PTP_PIN_CFG_PTP_PIN_SYNC, + sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN)); + + /* Set new value */ + spx5_wr(PTP_PTP_TOD_SEC_MSB_PTP_TOD_SEC_MSB_SET(upper_32_bits(ts->tv_sec)), + sparx5, PTP_PTP_TOD_SEC_MSB(TOD_ACC_PIN)); + spx5_wr(lower_32_bits(ts->tv_sec), + sparx5, PTP_PTP_TOD_SEC_LSB(TOD_ACC_PIN)); + spx5_wr(ts->tv_nsec, sparx5, PTP_PTP_TOD_NSEC(TOD_ACC_PIN)); + + /* Apply new values */ + spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_LOAD) | + PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(phc->index) | + PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(0), + PTP_PTP_PIN_CFG_PTP_PIN_ACTION | + PTP_PTP_PIN_CFG_PTP_PIN_DOM | + PTP_PTP_PIN_CFG_PTP_PIN_SYNC, + sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN)); + + spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags); + + return 0; +} + +static int sparx5_ptp_gettime64(struct ptp_clock_info *ptp, + struct timespec64 *ts) +{ + struct sparx5_phc *phc = container_of(ptp, struct sparx5_phc, info); + struct sparx5 *sparx5 = phc->sparx5; + unsigned long flags; + time64_t s; + s64 ns; + + spin_lock_irqsave(&sparx5->ptp_clock_lock, flags); + + spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_SAVE) | + PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(phc->index) | + PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(0), + PTP_PTP_PIN_CFG_PTP_PIN_ACTION | + PTP_PTP_PIN_CFG_PTP_PIN_DOM | + PTP_PTP_PIN_CFG_PTP_PIN_SYNC, + sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN)); + + s = spx5_rd(sparx5, PTP_PTP_TOD_SEC_MSB(TOD_ACC_PIN)); + s <<= 32; + s |= spx5_rd(sparx5, PTP_PTP_TOD_SEC_LSB(TOD_ACC_PIN)); + ns = spx5_rd(sparx5, PTP_PTP_TOD_NSEC(TOD_ACC_PIN)); + ns &= PTP_PTP_TOD_NSEC_PTP_TOD_NSEC; + + spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags); + + /* Deal with negative values */ + if ((ns & 0xFFFFFFF0) == 0x3FFFFFF0) { + s--; + ns &= 0xf; + ns += 999999984; + } + + set_normalized_timespec64(ts, s, ns); + return 0; +} + +static int sparx5_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct sparx5_phc *phc = container_of(ptp, struct sparx5_phc, info); + struct sparx5 *sparx5 = phc->sparx5; + + if (delta > -(NSEC_PER_SEC / 2) && delta < (NSEC_PER_SEC / 2)) { + unsigned long flags; + + spin_lock_irqsave(&sparx5->ptp_clock_lock, flags); + + /* Must be in IDLE mode before the time can be loaded */ + spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_IDLE) | + PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(phc->index) | + PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(0), + PTP_PTP_PIN_CFG_PTP_PIN_ACTION | + PTP_PTP_PIN_CFG_PTP_PIN_DOM | + PTP_PTP_PIN_CFG_PTP_PIN_SYNC, + sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN)); + + spx5_wr(PTP_PTP_TOD_NSEC_PTP_TOD_NSEC_SET(delta), + sparx5, PTP_PTP_TOD_NSEC(TOD_ACC_PIN)); + + /* Adjust time with the value of PTP_TOD_NSEC */ + spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_DELTA) | + PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(phc->index) | + PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(0), + PTP_PTP_PIN_CFG_PTP_PIN_ACTION | + PTP_PTP_PIN_CFG_PTP_PIN_DOM | + PTP_PTP_PIN_CFG_PTP_PIN_SYNC, + sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN)); + + spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags); + } else { + /* Fall back using sparx5_ptp_settime64 which is not exact */ + struct timespec64 ts; + u64 now; + + sparx5_ptp_gettime64(ptp, &ts); + + now = ktime_to_ns(timespec64_to_ktime(ts)); + ts = ns_to_timespec64(now + delta); + + sparx5_ptp_settime64(ptp, &ts); + } + + return 0; +} + +static struct ptp_clock_info sparx5_ptp_clock_info = { + .owner = THIS_MODULE, + .name = "sparx5 ptp", + .max_adj = 200000, + .gettime64 = sparx5_ptp_gettime64, + .settime64 = sparx5_ptp_settime64, + .adjtime = sparx5_ptp_adjtime, + .adjfine = sparx5_ptp_adjfine, +}; + +static int sparx5_ptp_phc_init(struct sparx5 *sparx5, + int index, + struct ptp_clock_info *clock_info) +{ + struct sparx5_phc *phc = &sparx5->phc[index]; + + phc->info = *clock_info; + phc->clock = ptp_clock_register(&phc->info, sparx5->dev); + if (IS_ERR(phc->clock)) + return PTR_ERR(phc->clock); + + phc->index = index; + phc->sparx5 = sparx5; + + /* PTP Rx stamping is always enabled. */ + phc->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + + return 0; +} + +int sparx5_ptp_init(struct sparx5 *sparx5) +{ + u64 tod_adj = sparx5_ptp_get_nominal_value(sparx5); + struct sparx5_port *port; + int err, i; + + if (!sparx5->ptp) + return 0; + + for (i = 0; i < SPARX5_PHC_COUNT; ++i) { + err = sparx5_ptp_phc_init(sparx5, i, &sparx5_ptp_clock_info); + if (err) + return err; + } + + spin_lock_init(&sparx5->ptp_clock_lock); + spin_lock_init(&sparx5->ptp_ts_id_lock); + mutex_init(&sparx5->ptp_lock); + + /* Disable master counters */ + spx5_wr(PTP_PTP_DOM_CFG_PTP_ENA_SET(0), sparx5, PTP_PTP_DOM_CFG); + + /* Configure the nominal TOD increment per clock cycle */ + spx5_rmw(PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS_SET(0x7), + PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS, + sparx5, PTP_PTP_DOM_CFG); + + for (i = 0; i < SPARX5_PHC_COUNT; ++i) { + spx5_wr((u32)tod_adj & 0xFFFFFFFF, sparx5, + PTP_CLK_PER_CFG(i, 0)); + spx5_wr((u32)(tod_adj >> 32), sparx5, + PTP_CLK_PER_CFG(i, 1)); + } + + spx5_rmw(PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS_SET(0), + PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS, + sparx5, PTP_PTP_DOM_CFG); + + /* Enable master counters */ + spx5_wr(PTP_PTP_DOM_CFG_PTP_ENA_SET(0x7), sparx5, PTP_PTP_DOM_CFG); + + for (i = 0; i < sparx5->port_count; i++) { + port = sparx5->ports[i]; + if (!port) + continue; + + skb_queue_head_init(&port->tx_skbs); + } + + return 0; +} + +void sparx5_ptp_deinit(struct sparx5 *sparx5) +{ + struct sparx5_port *port; + int i; + + for (i = 0; i < sparx5->port_count; i++) { + port = sparx5->ports[i]; + if (!port) + continue; + + skb_queue_purge(&port->tx_skbs); + } + + for (i = 0; i < SPARX5_PHC_COUNT; ++i) + ptp_clock_unregister(sparx5->phc[i].clock); +} + +void sparx5_ptp_rxtstamp(struct sparx5 *sparx5, struct sk_buff *skb, + u64 timestamp) +{ + struct skb_shared_hwtstamps *shhwtstamps; + struct sparx5_phc *phc; + struct timespec64 ts; + u64 full_ts_in_ns; + + if (!sparx5->ptp) + return; + + phc = &sparx5->phc[SPARX5_PHC_PORT]; + sparx5_ptp_gettime64(&phc->info, &ts); + + if (ts.tv_nsec < timestamp) + ts.tv_sec--; + ts.tv_nsec = timestamp; + full_ts_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec); + + shhwtstamps = skb_hwtstamps(skb); + shhwtstamps->hwtstamp = full_ts_in_ns; +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c index 649ca609884a..2d8e0b81c839 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c @@ -16,14 +16,31 @@ struct sparx5_switchdev_event_work { struct work_struct work; struct switchdev_notifier_fdb_info fdb_info; struct net_device *dev; + struct sparx5 *sparx5; unsigned long event; }; +static int sparx5_port_attr_pre_bridge_flags(struct sparx5_port *port, + struct switchdev_brport_flags flags) +{ + if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD)) + return -EINVAL; + + return 0; +} + static void sparx5_port_attr_bridge_flags(struct sparx5_port *port, struct switchdev_brport_flags flags) { + int pgid; + if (flags.mask & BR_MCAST_FLOOD) - sparx5_pgid_update_mask(port, PGID_MC_FLOOD, true); + for (pgid = PGID_MC_FLOOD; pgid <= PGID_IPV6_MC_CTRL; pgid++) + sparx5_pgid_update_mask(port, pgid, !!(flags.val & BR_MCAST_FLOOD)); + if (flags.mask & BR_FLOOD) + sparx5_pgid_update_mask(port, PGID_UC_FLOOD, !!(flags.val & BR_FLOOD)); + if (flags.mask & BR_BCAST_FLOOD) + sparx5_pgid_update_mask(port, PGID_BCAST, !!(flags.val & BR_BCAST_FLOOD)); } static void sparx5_attr_stp_state_set(struct sparx5_port *port, @@ -72,6 +89,9 @@ static int sparx5_port_attr_set(struct net_device *dev, const void *ctx, struct sparx5_port *port = netdev_priv(dev); switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: + return sparx5_port_attr_pre_bridge_flags(port, + attr->u.brport_flags); case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: sparx5_port_attr_bridge_flags(port, attr->u.brport_flags); break; @@ -82,6 +102,11 @@ static int sparx5_port_attr_set(struct net_device *dev, const void *ctx, sparx5_port_attr_ageing_set(port, attr->u.ageing_time); break; case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: + /* Used PVID 1 when default_pvid is 0, to avoid + * collision with non-bridged ports. + */ + if (port->pvid == 0) + port->pvid = 1; port->vlan_aware = attr->u.vlan_filtering; sparx5_vlan_port_apply(port->sparx5, port); break; @@ -117,6 +142,9 @@ static int sparx5_port_bridge_join(struct sparx5_port *port, if (err) goto err_switchdev_offload; + /* Remove standalone port entry */ + sparx5_mact_forget(sparx5, ndev->dev_addr, 0); + /* Port enters in bridge mode therefor don't need to copy to CPU * frames for multicast in case the bridge is not requesting them */ @@ -145,6 +173,9 @@ static void sparx5_port_bridge_leave(struct sparx5_port *port, port->pvid = NULL_VID; port->vid = NULL_VID; + /* Forward frames to CPU */ + sparx5_mact_learn(sparx5, PGID_CPU, port->ndev->dev_addr, 0); + /* Port enters in host more therefore restore mc list */ __dev_mc_sync(port->ndev, sparx5_mc_sync, sparx5_mc_unsync); } @@ -228,31 +259,43 @@ static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work) struct switchdev_notifier_fdb_info *fdb_info; struct sparx5_port *port; struct sparx5 *sparx5; + bool host_addr; + u16 vid; rtnl_lock(); - if (!sparx5_netdevice_check(dev)) - goto out; - - port = netdev_priv(dev); - sparx5 = port->sparx5; + if (!sparx5_netdevice_check(dev)) { + host_addr = true; + sparx5 = switchdev_work->sparx5; + } else { + host_addr = false; + sparx5 = switchdev_work->sparx5; + port = netdev_priv(dev); + } fdb_info = &switchdev_work->fdb_info; + /* Used PVID 1 when default_pvid is 0, to avoid + * collision with non-bridged ports. + */ + if (fdb_info->vid == 0) + vid = 1; + else + vid = fdb_info->vid; + switch (switchdev_work->event) { case SWITCHDEV_FDB_ADD_TO_DEVICE: - if (!fdb_info->added_by_user) - break; - sparx5_add_mact_entry(sparx5, port, fdb_info->addr, - fdb_info->vid); + if (host_addr) + sparx5_add_mact_entry(sparx5, dev, PGID_CPU, + fdb_info->addr, vid); + else + sparx5_add_mact_entry(sparx5, port->ndev, port->portno, + fdb_info->addr, vid); break; case SWITCHDEV_FDB_DEL_TO_DEVICE: - if (!fdb_info->added_by_user) - break; - sparx5_del_mact_entry(sparx5, fdb_info->addr, fdb_info->vid); + sparx5_del_mact_entry(sparx5, fdb_info->addr, vid); break; } -out: rtnl_unlock(); kfree(switchdev_work->fdb_info.addr); kfree(switchdev_work); @@ -264,15 +307,18 @@ static void sparx5_schedule_work(struct work_struct *work) queue_work(sparx5_owq, work); } -static int sparx5_switchdev_event(struct notifier_block *unused, +static int sparx5_switchdev_event(struct notifier_block *nb, unsigned long event, void *ptr) { struct net_device *dev = switchdev_notifier_info_to_dev(ptr); struct sparx5_switchdev_event_work *switchdev_work; struct switchdev_notifier_fdb_info *fdb_info; struct switchdev_notifier_info *info = ptr; + struct sparx5 *spx5; int err; + spx5 = container_of(nb, struct sparx5, switchdev_nb); + switch (event) { case SWITCHDEV_PORT_ATTR_SET: err = switchdev_handle_port_attr_set(dev, ptr, @@ -288,6 +334,7 @@ static int sparx5_switchdev_event(struct notifier_block *unused, switchdev_work->dev = dev; switchdev_work->event = event; + switchdev_work->sparx5 = spx5; fdb_info = container_of(info, struct switchdev_notifier_fdb_info, @@ -314,54 +361,6 @@ static int sparx5_switchdev_event(struct notifier_block *unused, return NOTIFY_BAD; } -static void sparx5_sync_port_dev_addr(struct sparx5 *sparx5, - struct sparx5_port *port, - u16 vid, bool add) -{ - if (!port || - !test_bit(port->portno, sparx5->bridge_mask)) - return; /* Skip null/host interfaces */ - - /* Bridge connects to vid? */ - if (add) { - /* Add port MAC address from the VLAN */ - sparx5_mact_learn(sparx5, PGID_CPU, - port->ndev->dev_addr, vid); - } else { - /* Control port addr visibility depending on - * port VLAN connectivity. - */ - if (test_bit(port->portno, sparx5->vlan_mask[vid])) - sparx5_mact_learn(sparx5, PGID_CPU, - port->ndev->dev_addr, vid); - else - sparx5_mact_forget(sparx5, - port->ndev->dev_addr, vid); - } -} - -static void sparx5_sync_bridge_dev_addr(struct net_device *dev, - struct sparx5 *sparx5, - u16 vid, bool add) -{ - int i; - - /* First, handle bridge address'es */ - if (add) { - sparx5_mact_learn(sparx5, PGID_CPU, dev->dev_addr, - vid); - sparx5_mact_learn(sparx5, PGID_BCAST, dev->broadcast, - vid); - } else { - sparx5_mact_forget(sparx5, dev->dev_addr, vid); - sparx5_mact_forget(sparx5, dev->broadcast, vid); - } - - /* Now look at bridged ports */ - for (i = 0; i < SPX5_PORTS; i++) - sparx5_sync_port_dev_addr(sparx5, sparx5->ports[i], vid, add); -} - static int sparx5_handle_port_vlan_add(struct net_device *dev, struct notifier_block *nb, const struct switchdev_obj_port_vlan *v) @@ -369,13 +368,13 @@ static int sparx5_handle_port_vlan_add(struct net_device *dev, struct sparx5_port *port = netdev_priv(dev); if (netif_is_bridge_master(dev)) { - if (v->flags & BRIDGE_VLAN_INFO_BRENTRY) { - struct sparx5 *sparx5 = - container_of(nb, struct sparx5, - switchdev_blocking_nb); + struct sparx5 *sparx5 = + container_of(nb, struct sparx5, + switchdev_blocking_nb); - sparx5_sync_bridge_dev_addr(dev, sparx5, v->vid, true); - } + /* Flood broadcast to CPU */ + sparx5_mact_learn(sparx5, PGID_BCAST, dev->broadcast, + v->vid); return 0; } @@ -387,6 +386,109 @@ static int sparx5_handle_port_vlan_add(struct net_device *dev, v->flags & BRIDGE_VLAN_INFO_UNTAGGED); } +static int sparx5_handle_port_mdb_add(struct net_device *dev, + struct notifier_block *nb, + const struct switchdev_obj_port_mdb *v) +{ + struct sparx5_port *port = netdev_priv(dev); + struct sparx5 *spx5 = port->sparx5; + u16 pgid_idx, vid; + u32 mact_entry; + int res, err; + + /* When VLAN unaware the vlan value is not parsed and we receive vid 0. + * Fall back to bridge vid 1. + */ + if (!br_vlan_enabled(spx5->hw_bridge_dev)) + vid = 1; + else + vid = v->vid; + + res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry); + + if (res) { + pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry); + + /* MC_IDX has an offset of 65 in the PGID table. */ + pgid_idx += PGID_MCAST_START; + sparx5_pgid_update_mask(port, pgid_idx, true); + } else { + err = sparx5_pgid_alloc_mcast(spx5, &pgid_idx); + if (err) { + netdev_warn(dev, "multicast pgid table full\n"); + return err; + } + sparx5_pgid_update_mask(port, pgid_idx, true); + err = sparx5_mact_learn(spx5, pgid_idx, v->addr, vid); + if (err) { + netdev_warn(dev, "could not learn mac address %pM\n", v->addr); + sparx5_pgid_update_mask(port, pgid_idx, false); + return err; + } + } + + return 0; +} + +static int sparx5_mdb_del_entry(struct net_device *dev, + struct sparx5 *spx5, + const unsigned char mac[ETH_ALEN], + const u16 vid, + u16 pgid_idx) +{ + int err; + + err = sparx5_mact_forget(spx5, mac, vid); + if (err) { + netdev_warn(dev, "could not forget mac address %pM", mac); + return err; + } + err = sparx5_pgid_free(spx5, pgid_idx); + if (err) { + netdev_err(dev, "attempted to free already freed pgid\n"); + return err; + } + return 0; +} + +static int sparx5_handle_port_mdb_del(struct net_device *dev, + struct notifier_block *nb, + const struct switchdev_obj_port_mdb *v) +{ + struct sparx5_port *port = netdev_priv(dev); + struct sparx5 *spx5 = port->sparx5; + u16 pgid_idx, vid; + u32 mact_entry, res, pgid_entry[3]; + int err; + + if (!br_vlan_enabled(spx5->hw_bridge_dev)) + vid = 1; + else + vid = v->vid; + + res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry); + + if (res) { + pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry); + + /* MC_IDX has an offset of 65 in the PGID table. */ + pgid_idx += PGID_MCAST_START; + sparx5_pgid_update_mask(port, pgid_idx, false); + + pgid_entry[0] = spx5_rd(spx5, ANA_AC_PGID_CFG(pgid_idx)); + pgid_entry[1] = spx5_rd(spx5, ANA_AC_PGID_CFG1(pgid_idx)); + pgid_entry[2] = spx5_rd(spx5, ANA_AC_PGID_CFG2(pgid_idx)); + if (pgid_entry[0] == 0 && pgid_entry[1] == 0 && pgid_entry[2] == 0) { + /* No ports are in MC group. Remove entry */ + err = sparx5_mdb_del_entry(dev, spx5, v->addr, vid, pgid_idx); + if (err) + return err; + } + } + + return 0; +} + static int sparx5_handle_port_obj_add(struct net_device *dev, struct notifier_block *nb, struct switchdev_notifier_port_obj_info *info) @@ -399,6 +501,10 @@ static int sparx5_handle_port_obj_add(struct net_device *dev, err = sparx5_handle_port_vlan_add(dev, nb, SWITCHDEV_OBJ_PORT_VLAN(obj)); break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + err = sparx5_handle_port_mdb_add(dev, nb, + SWITCHDEV_OBJ_PORT_MDB(obj)); + break; default: err = -EOPNOTSUPP; break; @@ -421,7 +527,7 @@ static int sparx5_handle_port_vlan_del(struct net_device *dev, container_of(nb, struct sparx5, switchdev_blocking_nb); - sparx5_sync_bridge_dev_addr(dev, sparx5, vid, false); + sparx5_mact_forget(sparx5, dev->broadcast, vid); return 0; } @@ -432,9 +538,6 @@ static int sparx5_handle_port_vlan_del(struct net_device *dev, if (ret) return ret; - /* Delete the port MAC address with the matching VLAN information */ - sparx5_mact_forget(port->sparx5, port->ndev->dev_addr, vid); - return 0; } @@ -450,6 +553,10 @@ static int sparx5_handle_port_obj_del(struct net_device *dev, err = sparx5_handle_port_vlan_del(dev, nb, SWITCHDEV_OBJ_PORT_VLAN(obj)->vid); break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + err = sparx5_handle_port_mdb_del(dev, nb, + SWITCHDEV_OBJ_PORT_MDB(obj)); + break; default: err = -EOPNOTSUPP; break; diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index 636dfef24a6c..49b85ca578b0 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -663,7 +663,7 @@ static int mana_gd_create_dma_region(struct gdma_dev *gd, struct gdma_context *gc = gd->gdma_context; struct hw_channel_context *hwc; u32 length = gmi->length; - u32 req_msg_size; + size_t req_msg_size; int err; int i; @@ -674,7 +674,7 @@ static int mana_gd_create_dma_region(struct gdma_dev *gd, return -EINVAL; hwc = gc->hwc.driver_data; - req_msg_size = sizeof(*req) + num_page * sizeof(u64); + req_msg_size = struct_size(req, page_addr_list, num_page); if (req_msg_size > hwc->max_req_msg_size) return -EINVAL; diff --git a/drivers/net/ethernet/microsoft/mana/mana.h b/drivers/net/ethernet/microsoft/mana/mana.h index 9a12607fb511..d36405af9432 100644 --- a/drivers/net/ethernet/microsoft/mana/mana.h +++ b/drivers/net/ethernet/microsoft/mana/mana.h @@ -48,7 +48,15 @@ enum TRI_STATE { #define MAX_PORTS_IN_MANA_DEV 256 -struct mana_stats { +struct mana_stats_rx { + u64 packets; + u64 bytes; + u64 xdp_drop; + u64 xdp_tx; + struct u64_stats_sync syncp; +}; + +struct mana_stats_tx { u64 packets; u64 bytes; struct u64_stats_sync syncp; @@ -76,7 +84,7 @@ struct mana_txq { atomic_t pending_sends; - struct mana_stats stats; + struct mana_stats_tx stats; }; /* skb data and frags dma mappings */ @@ -298,10 +306,11 @@ struct mana_rxq { u32 buf_index; - struct mana_stats stats; + struct mana_stats_rx stats; struct bpf_prog __rcu *bpf_prog; struct xdp_rxq_info xdp_rxq; + struct page *xdp_save_page; /* MUST BE THE LAST MEMBER: * Each receive buffer has an associated mana_recv_buf_oob. diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 498d0f999275..b7d3ba1b4d17 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -136,7 +136,7 @@ int mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) bool ipv4 = false, ipv6 = false; struct mana_tx_package pkg = {}; struct netdev_queue *net_txq; - struct mana_stats *tx_stats; + struct mana_stats_tx *tx_stats; struct gdma_queue *gdma_sq; unsigned int csum_type; struct mana_txq *txq; @@ -299,7 +299,8 @@ static void mana_get_stats64(struct net_device *ndev, { struct mana_port_context *apc = netdev_priv(ndev); unsigned int num_queues = apc->num_queues; - struct mana_stats *stats; + struct mana_stats_rx *rx_stats; + struct mana_stats_tx *tx_stats; unsigned int start; u64 packets, bytes; int q; @@ -310,26 +311,26 @@ static void mana_get_stats64(struct net_device *ndev, netdev_stats_to_stats64(st, &ndev->stats); for (q = 0; q < num_queues; q++) { - stats = &apc->rxqs[q]->stats; + rx_stats = &apc->rxqs[q]->stats; do { - start = u64_stats_fetch_begin_irq(&stats->syncp); - packets = stats->packets; - bytes = stats->bytes; - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + start = u64_stats_fetch_begin_irq(&rx_stats->syncp); + packets = rx_stats->packets; + bytes = rx_stats->bytes; + } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); st->rx_packets += packets; st->rx_bytes += bytes; } for (q = 0; q < num_queues; q++) { - stats = &apc->tx_qp[q].txq.stats; + tx_stats = &apc->tx_qp[q].txq.stats; do { - start = u64_stats_fetch_begin_irq(&stats->syncp); - packets = stats->packets; - bytes = stats->bytes; - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + start = u64_stats_fetch_begin_irq(&tx_stats->syncp); + packets = tx_stats->packets; + bytes = tx_stats->bytes; + } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); st->tx_packets += packets; st->tx_bytes += bytes; @@ -986,7 +987,7 @@ static struct sk_buff *mana_build_skb(void *buf_va, uint pkt_len, static void mana_rx_skb(void *buf_va, struct mana_rxcomp_oob *cqe, struct mana_rxq *rxq) { - struct mana_stats *rx_stats = &rxq->stats; + struct mana_stats_rx *rx_stats = &rxq->stats; struct net_device *ndev = rxq->ndev; uint pkt_len = cqe->ppi[0].pkt_len; u16 rxq_idx = rxq->rxq_idx; @@ -1007,7 +1008,7 @@ static void mana_rx_skb(void *buf_va, struct mana_rxcomp_oob *cqe, act = mana_run_xdp(ndev, rxq, &xdp, buf_va, pkt_len); if (act != XDP_PASS && act != XDP_TX) - goto drop; + goto drop_xdp; skb = mana_build_skb(buf_va, pkt_len, &xdp); @@ -1034,6 +1035,14 @@ static void mana_rx_skb(void *buf_va, struct mana_rxcomp_oob *cqe, skb_set_hash(skb, hash_value, PKT_HASH_TYPE_L3); } + u64_stats_update_begin(&rx_stats->syncp); + rx_stats->packets++; + rx_stats->bytes += pkt_len; + + if (act == XDP_TX) + rx_stats->xdp_tx++; + u64_stats_update_end(&rx_stats->syncp); + if (act == XDP_TX) { skb_set_queue_mapping(skb, rxq_idx); mana_xdp_tx(skb, ndev); @@ -1042,15 +1051,19 @@ static void mana_rx_skb(void *buf_va, struct mana_rxcomp_oob *cqe, napi_gro_receive(napi, skb); - u64_stats_update_begin(&rx_stats->syncp); - rx_stats->packets++; - rx_stats->bytes += pkt_len; - u64_stats_update_end(&rx_stats->syncp); return; +drop_xdp: + u64_stats_update_begin(&rx_stats->syncp); + rx_stats->xdp_drop++; + u64_stats_update_end(&rx_stats->syncp); + drop: - free_page((unsigned long)buf_va); + WARN_ON_ONCE(rxq->xdp_save_page); + rxq->xdp_save_page = virt_to_page(buf_va); + ++ndev->stats.rx_dropped; + return; } @@ -1072,8 +1085,10 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, break; case CQE_RX_TRUNCATED: - netdev_err(ndev, "Dropped a truncated packet\n"); - return; + ++ndev->stats.rx_dropped; + rxbuf_oob = &rxq->rx_oobs[rxq->buf_index]; + netdev_warn_once(ndev, "Dropped a truncated packet\n"); + goto drop; case CQE_RX_COALESCED_4: netdev_err(ndev, "RX coalescing is unsupported\n"); @@ -1089,9 +1104,6 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, return; } - if (oob->cqe_hdr.cqe_type != CQE_RX_OKAY) - return; - pktlen = oob->ppi[0].pkt_len; if (pktlen == 0) { @@ -1105,7 +1117,13 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, rxbuf_oob = &rxq->rx_oobs[curr]; WARN_ON_ONCE(rxbuf_oob->wqe_inf.wqe_size_in_bu != 1); - new_page = alloc_page(GFP_ATOMIC); + /* Reuse XDP dropped page if available */ + if (rxq->xdp_save_page) { + new_page = rxq->xdp_save_page; + rxq->xdp_save_page = NULL; + } else { + new_page = alloc_page(GFP_ATOMIC); + } if (new_page) { da = dma_map_page(dev, new_page, XDP_PACKET_HEADROOM, rxq->datasize, @@ -1135,6 +1153,7 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, mana_rx_skb(old_buf, oob, rxq); +drop: mana_move_wq_tail(rxq->gdma_rq, rxbuf_oob->wqe_inf.wqe_size_in_bu); mana_post_pkt_rxq(rxq); @@ -1392,6 +1411,9 @@ static void mana_destroy_rxq(struct mana_port_context *apc, mana_deinit_cq(apc, &rxq->rx_cq); + if (rxq->xdp_save_page) + __free_page(rxq->xdp_save_page); + for (i = 0; i < rxq->num_rx_buf; i++) { rx_oob = &rxq->rx_oobs[i]; diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c index c3c81ae3fafd..e13f2453eabb 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c +++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c @@ -23,7 +23,7 @@ static int mana_get_sset_count(struct net_device *ndev, int stringset) if (stringset != ETH_SS_STATS) return -EINVAL; - return ARRAY_SIZE(mana_eth_stats) + num_queues * 4; + return ARRAY_SIZE(mana_eth_stats) + num_queues * 6; } static void mana_get_strings(struct net_device *ndev, u32 stringset, u8 *data) @@ -46,6 +46,10 @@ static void mana_get_strings(struct net_device *ndev, u32 stringset, u8 *data) p += ETH_GSTRING_LEN; sprintf(p, "rx_%d_bytes", i); p += ETH_GSTRING_LEN; + sprintf(p, "rx_%d_xdp_drop", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rx_%d_xdp_tx", i); + p += ETH_GSTRING_LEN; } for (i = 0; i < num_queues; i++) { @@ -62,9 +66,12 @@ static void mana_get_ethtool_stats(struct net_device *ndev, struct mana_port_context *apc = netdev_priv(ndev); unsigned int num_queues = apc->num_queues; void *eth_stats = &apc->eth_stats; - struct mana_stats *stats; + struct mana_stats_rx *rx_stats; + struct mana_stats_tx *tx_stats; unsigned int start; u64 packets, bytes; + u64 xdp_drop; + u64 xdp_tx; int q, i = 0; if (!apc->port_is_up) @@ -74,26 +81,30 @@ static void mana_get_ethtool_stats(struct net_device *ndev, data[i++] = *(u64 *)(eth_stats + mana_eth_stats[q].offset); for (q = 0; q < num_queues; q++) { - stats = &apc->rxqs[q]->stats; + rx_stats = &apc->rxqs[q]->stats; do { - start = u64_stats_fetch_begin_irq(&stats->syncp); - packets = stats->packets; - bytes = stats->bytes; - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + start = u64_stats_fetch_begin_irq(&rx_stats->syncp); + packets = rx_stats->packets; + bytes = rx_stats->bytes; + xdp_drop = rx_stats->xdp_drop; + xdp_tx = rx_stats->xdp_tx; + } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); data[i++] = packets; data[i++] = bytes; + data[i++] = xdp_drop; + data[i++] = xdp_tx; } for (q = 0; q < num_queues; q++) { - stats = &apc->tx_qp[q].txq.stats; + tx_stats = &apc->tx_qp[q].txq.stats; do { - start = u64_stats_fetch_begin_irq(&stats->syncp); - packets = stats->packets; - bytes = stats->bytes; - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + start = u64_stats_fetch_begin_irq(&tx_stats->syncp); + packets = tx_stats->packets; + bytes = tx_stats->bytes; + } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); data[i++] = packets; data[i++] = bytes; diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index 15179b9529e1..afb7dcadb8d2 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -510,14 +510,14 @@ static int moxart_mac_probe(struct platform_device *pdev) } priv->tx_buf_base = kmalloc_array(priv->tx_buf_size, TX_DESC_NUM, - GFP_ATOMIC); + GFP_KERNEL); if (!priv->tx_buf_base) { ret = -ENOMEM; goto init_fail; } priv->rx_buf_base = kmalloc_array(priv->rx_buf_size, RX_DESC_NUM, - GFP_ATOMIC); + GFP_KERNEL); if (!priv->rx_buf_base) { ret = -ENOMEM; goto init_fail; diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index fd3ceb74620d..e443bd8b2d09 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -13,6 +13,7 @@ #define TABLE_UPDATE_SLEEP_US 10 #define TABLE_UPDATE_TIMEOUT_US 100000 +#define OCELOT_RSV_VLAN_RANGE_START 4000 struct ocelot_mact_entry { u8 mac[ETH_ALEN]; @@ -221,6 +222,35 @@ static void ocelot_vcap_enable(struct ocelot *ocelot, int port) REW_PORT_CFG, port); } +static int ocelot_single_vlan_aware_bridge(struct ocelot *ocelot, + struct netlink_ext_ack *extack) +{ + struct net_device *bridge = NULL; + int port; + + for (port = 0; port < ocelot->num_phys_ports; port++) { + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + if (!ocelot_port || !ocelot_port->bridge || + !br_vlan_enabled(ocelot_port->bridge)) + continue; + + if (!bridge) { + bridge = ocelot_port->bridge; + continue; + } + + if (bridge == ocelot_port->bridge) + continue; + + NL_SET_ERR_MSG_MOD(extack, + "Only one VLAN-aware bridge is supported"); + return -EBUSY; + } + + return 0; +} + static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot) { return ocelot_read(ocelot, ANA_TABLES_VLANACCESS); @@ -347,12 +377,45 @@ static void ocelot_port_manage_port_tag(struct ocelot *ocelot, int port) } } +int ocelot_bridge_num_find(struct ocelot *ocelot, + const struct net_device *bridge) +{ + int port; + + for (port = 0; port < ocelot->num_phys_ports; port++) { + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + if (ocelot_port && ocelot_port->bridge == bridge) + return ocelot_port->bridge_num; + } + + return -1; +} +EXPORT_SYMBOL_GPL(ocelot_bridge_num_find); + +static u16 ocelot_vlan_unaware_pvid(struct ocelot *ocelot, + const struct net_device *bridge) +{ + int bridge_num; + + /* Standalone ports use VID 0 */ + if (!bridge) + return 0; + + bridge_num = ocelot_bridge_num_find(ocelot, bridge); + if (WARN_ON(bridge_num < 0)) + return 0; + + /* VLAN-unaware bridges use a reserved VID going from 4095 downwards */ + return VLAN_N_VID - bridge_num - 1; +} + /* Default vlan to clasify for untagged frames (may be zero) */ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, const struct ocelot_bridge_vlan *pvid_vlan) { struct ocelot_port *ocelot_port = ocelot->ports[port]; - u16 pvid = OCELOT_VLAN_UNAWARE_PVID; + u16 pvid = ocelot_vlan_unaware_pvid(ocelot, ocelot_port->bridge); u32 val = 0; ocelot_port->pvid_vlan = pvid_vlan; @@ -466,12 +529,29 @@ static int ocelot_vlan_member_del(struct ocelot *ocelot, int port, u16 vid) return 0; } +static int ocelot_add_vlan_unaware_pvid(struct ocelot *ocelot, int port, + const struct net_device *bridge) +{ + u16 vid = ocelot_vlan_unaware_pvid(ocelot, bridge); + + return ocelot_vlan_member_add(ocelot, port, vid, true); +} + +static int ocelot_del_vlan_unaware_pvid(struct ocelot *ocelot, int port, + const struct net_device *bridge) +{ + u16 vid = ocelot_vlan_unaware_pvid(ocelot, bridge); + + return ocelot_vlan_member_del(ocelot, port, vid); +} + int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, bool vlan_aware, struct netlink_ext_ack *extack) { struct ocelot_vcap_block *block = &ocelot->block[VCAP_IS1]; struct ocelot_port *ocelot_port = ocelot->ports[port]; struct ocelot_vcap_filter *filter; + int err; u32 val; list_for_each_entry(filter, &block->rules, list) { @@ -483,6 +563,19 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, } } + err = ocelot_single_vlan_aware_bridge(ocelot, extack); + if (err) + return err; + + if (vlan_aware) + err = ocelot_del_vlan_unaware_pvid(ocelot, port, + ocelot_port->bridge); + else + err = ocelot_add_vlan_unaware_pvid(ocelot, port, + ocelot_port->bridge); + if (err) + return err; + ocelot_port->vlan_aware = vlan_aware; if (vlan_aware) @@ -521,6 +614,12 @@ int ocelot_vlan_prepare(struct ocelot *ocelot, int port, u16 vid, bool pvid, } } + if (vid > OCELOT_RSV_VLAN_RANGE_START) { + NL_SET_ERR_MSG_MOD(extack, + "VLAN range 4000-4095 reserved for VLAN-unaware bridging"); + return -EBUSY; + } + return 0; } EXPORT_SYMBOL(ocelot_vlan_prepare); @@ -584,11 +683,11 @@ static void ocelot_vlan_init(struct ocelot *ocelot) for (vid = 1; vid < VLAN_N_VID; vid++) ocelot_vlant_set_mask(ocelot, vid, 0); - /* Because VLAN filtering is enabled, we need VID 0 to get untagged - * traffic. It is added automatically if 8021q module is loaded, but - * we can't rely on it since module may be not loaded. + /* We need VID 0 to get traffic on standalone ports. + * It is added automatically if the 8021q module is loaded, but we + * can't rely on that since it might not be. */ - ocelot_vlant_set_mask(ocelot, OCELOT_VLAN_UNAWARE_PVID, all_ports); + ocelot_vlant_set_mask(ocelot, OCELOT_STANDALONE_PVID, all_ports); /* Set vlan ingress filter mask to all ports but the CPU port by * default. @@ -1237,21 +1336,27 @@ void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp) } EXPORT_SYMBOL(ocelot_drain_cpu_queue); -int ocelot_fdb_add(struct ocelot *ocelot, int port, - const unsigned char *addr, u16 vid) +int ocelot_fdb_add(struct ocelot *ocelot, int port, const unsigned char *addr, + u16 vid, const struct net_device *bridge) { int pgid = port; if (port == ocelot->npi) pgid = PGID_CPU; + if (!vid) + vid = ocelot_vlan_unaware_pvid(ocelot, bridge); + return ocelot_mact_learn(ocelot, pgid, addr, vid, ENTRYTYPE_LOCKED); } EXPORT_SYMBOL(ocelot_fdb_add); -int ocelot_fdb_del(struct ocelot *ocelot, int port, - const unsigned char *addr, u16 vid) +int ocelot_fdb_del(struct ocelot *ocelot, int port, const unsigned char *addr, + u16 vid, const struct net_device *bridge) { + if (!vid) + vid = ocelot_vlan_unaware_pvid(ocelot, bridge); + return ocelot_mact_forget(ocelot, addr, vid); } EXPORT_SYMBOL(ocelot_fdb_del); @@ -1413,6 +1518,12 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port, is_static = (entry.type == ENTRYTYPE_LOCKED); + /* Hide the reserved VLANs used for + * VLAN-unaware bridging. + */ + if (entry.vid > OCELOT_RSV_VLAN_RANGE_START) + entry.vid = 0; + err = cb(entry.mac, entry.vid, is_static, data); if (err) break; @@ -1472,9 +1583,9 @@ ocelot_populate_ipv6_ptp_general_trap_key(struct ocelot_vcap_filter *trap) trap->key.ipv6.dport.mask = 0xffff; } -static int ocelot_trap_add(struct ocelot *ocelot, int port, - unsigned long cookie, - void (*populate)(struct ocelot_vcap_filter *f)) +int ocelot_trap_add(struct ocelot *ocelot, int port, + unsigned long cookie, bool take_ts, + void (*populate)(struct ocelot_vcap_filter *f)) { struct ocelot_vcap_block *block_vcap_is2; struct ocelot_vcap_filter *trap; @@ -1500,6 +1611,8 @@ static int ocelot_trap_add(struct ocelot *ocelot, int port, trap->action.cpu_copy_ena = true; trap->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; trap->action.port_mask = 0; + trap->take_ts = take_ts; + list_add_tail(&trap->trap_list, &ocelot->traps); new = true; } @@ -1511,16 +1624,17 @@ static int ocelot_trap_add(struct ocelot *ocelot, int port, err = ocelot_vcap_filter_replace(ocelot, trap); if (err) { trap->ingress_port_mask &= ~BIT(port); - if (!trap->ingress_port_mask) + if (!trap->ingress_port_mask) { + list_del(&trap->trap_list); kfree(trap); + } return err; } return 0; } -static int ocelot_trap_del(struct ocelot *ocelot, int port, - unsigned long cookie) +int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie) { struct ocelot_vcap_block *block_vcap_is2; struct ocelot_vcap_filter *trap; @@ -1533,39 +1647,42 @@ static int ocelot_trap_del(struct ocelot *ocelot, int port, return 0; trap->ingress_port_mask &= ~BIT(port); - if (!trap->ingress_port_mask) + if (!trap->ingress_port_mask) { + list_del(&trap->trap_list); + return ocelot_vcap_filter_del(ocelot, trap); + } return ocelot_vcap_filter_replace(ocelot, trap); } static int ocelot_l2_ptp_trap_add(struct ocelot *ocelot, int port) { - unsigned long l2_cookie = ocelot->num_phys_ports + 1; + unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot); - return ocelot_trap_add(ocelot, port, l2_cookie, + return ocelot_trap_add(ocelot, port, l2_cookie, true, ocelot_populate_l2_ptp_trap_key); } static int ocelot_l2_ptp_trap_del(struct ocelot *ocelot, int port) { - unsigned long l2_cookie = ocelot->num_phys_ports + 1; + unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot); return ocelot_trap_del(ocelot, port, l2_cookie); } static int ocelot_ipv4_ptp_trap_add(struct ocelot *ocelot, int port) { - unsigned long ipv4_gen_cookie = ocelot->num_phys_ports + 2; - unsigned long ipv4_ev_cookie = ocelot->num_phys_ports + 3; + unsigned long ipv4_gen_cookie = OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot); + unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot); int err; - err = ocelot_trap_add(ocelot, port, ipv4_ev_cookie, + err = ocelot_trap_add(ocelot, port, ipv4_ev_cookie, true, ocelot_populate_ipv4_ptp_event_trap_key); if (err) return err; - err = ocelot_trap_add(ocelot, port, ipv4_gen_cookie, + err = ocelot_trap_add(ocelot, port, ipv4_gen_cookie, false, ocelot_populate_ipv4_ptp_general_trap_key); if (err) ocelot_trap_del(ocelot, port, ipv4_ev_cookie); @@ -1575,8 +1692,8 @@ static int ocelot_ipv4_ptp_trap_add(struct ocelot *ocelot, int port) static int ocelot_ipv4_ptp_trap_del(struct ocelot *ocelot, int port) { - unsigned long ipv4_gen_cookie = ocelot->num_phys_ports + 2; - unsigned long ipv4_ev_cookie = ocelot->num_phys_ports + 3; + unsigned long ipv4_gen_cookie = OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot); + unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot); int err; err = ocelot_trap_del(ocelot, port, ipv4_ev_cookie); @@ -1586,16 +1703,16 @@ static int ocelot_ipv4_ptp_trap_del(struct ocelot *ocelot, int port) static int ocelot_ipv6_ptp_trap_add(struct ocelot *ocelot, int port) { - unsigned long ipv6_gen_cookie = ocelot->num_phys_ports + 4; - unsigned long ipv6_ev_cookie = ocelot->num_phys_ports + 5; + unsigned long ipv6_gen_cookie = OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot); + unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot); int err; - err = ocelot_trap_add(ocelot, port, ipv6_ev_cookie, + err = ocelot_trap_add(ocelot, port, ipv6_ev_cookie, true, ocelot_populate_ipv6_ptp_event_trap_key); if (err) return err; - err = ocelot_trap_add(ocelot, port, ipv6_gen_cookie, + err = ocelot_trap_add(ocelot, port, ipv6_gen_cookie, false, ocelot_populate_ipv6_ptp_general_trap_key); if (err) ocelot_trap_del(ocelot, port, ipv6_ev_cookie); @@ -1605,8 +1722,8 @@ static int ocelot_ipv6_ptp_trap_add(struct ocelot *ocelot, int port) static int ocelot_ipv6_ptp_trap_del(struct ocelot *ocelot, int port) { - unsigned long ipv6_gen_cookie = ocelot->num_phys_ports + 4; - unsigned long ipv6_ev_cookie = ocelot->num_phys_ports + 5; + unsigned long ipv6_gen_cookie = OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot); + unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot); int err; err = ocelot_trap_del(ocelot, port, ipv6_ev_cookie); @@ -1750,28 +1867,36 @@ void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) EXPORT_SYMBOL(ocelot_get_strings); /* Caller must hold &ocelot->stats_lock */ -static void ocelot_update_stats(struct ocelot *ocelot) +static int ocelot_port_update_stats(struct ocelot *ocelot, int port) { - int i, j; + unsigned int idx = port * ocelot->num_stats; + struct ocelot_stats_region *region; + int err, j; - for (i = 0; i < ocelot->num_phys_ports; i++) { - /* Configure the port to read the stats from */ - ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(i), SYS_STAT_CFG); + /* Configure the port to read the stats from */ + ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG); - for (j = 0; j < ocelot->num_stats; j++) { - u32 val; - unsigned int idx = i * ocelot->num_stats + j; + list_for_each_entry(region, &ocelot->stats_regions, node) { + err = ocelot_bulk_read_rix(ocelot, SYS_COUNT_RX_OCTETS, + region->offset, region->buf, + region->count); + if (err) + return err; - val = ocelot_read_rix(ocelot, SYS_COUNT_RX_OCTETS, - ocelot->stats_layout[j].offset); + for (j = 0; j < region->count; j++) { + u64 *stat = &ocelot->stats[idx + j]; + u64 val = region->buf[j]; - if (val < (ocelot->stats[idx] & U32_MAX)) - ocelot->stats[idx] += (u64)1 << 32; + if (val < (*stat & U32_MAX)) + *stat += (u64)1 << 32; - ocelot->stats[idx] = (ocelot->stats[idx] & - ~(u64)U32_MAX) + val; + *stat = (*stat & ~(u64)U32_MAX) + val; } + + idx += region->count; } + + return err; } static void ocelot_check_stats_work(struct work_struct *work) @@ -1779,29 +1904,40 @@ static void ocelot_check_stats_work(struct work_struct *work) struct delayed_work *del_work = to_delayed_work(work); struct ocelot *ocelot = container_of(del_work, struct ocelot, stats_work); + int i, err; mutex_lock(&ocelot->stats_lock); - ocelot_update_stats(ocelot); + for (i = 0; i < ocelot->num_phys_ports; i++) { + err = ocelot_port_update_stats(ocelot, i); + if (err) + break; + } mutex_unlock(&ocelot->stats_lock); + if (err) + dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err); + queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, OCELOT_STATS_CHECK_DELAY); } void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) { - int i; + int i, err; mutex_lock(&ocelot->stats_lock); /* check and update now */ - ocelot_update_stats(ocelot); + err = ocelot_port_update_stats(ocelot, port); /* Copy all counters */ for (i = 0; i < ocelot->num_stats; i++) *data++ = ocelot->stats[port * ocelot->num_stats + i]; mutex_unlock(&ocelot->stats_lock); + + if (err) + dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err); } EXPORT_SYMBOL(ocelot_get_ethtool_stats); @@ -1814,6 +1950,41 @@ int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) } EXPORT_SYMBOL(ocelot_get_sset_count); +static int ocelot_prepare_stats_regions(struct ocelot *ocelot) +{ + struct ocelot_stats_region *region = NULL; + unsigned int last; + int i; + + INIT_LIST_HEAD(&ocelot->stats_regions); + + for (i = 0; i < ocelot->num_stats; i++) { + if (region && ocelot->stats_layout[i].offset == last + 1) { + region->count++; + } else { + region = devm_kzalloc(ocelot->dev, sizeof(*region), + GFP_KERNEL); + if (!region) + return -ENOMEM; + + region->offset = ocelot->stats_layout[i].offset; + region->count = 1; + list_add_tail(®ion->node, &ocelot->stats_regions); + } + + last = ocelot->stats_layout[i].offset; + } + + list_for_each_entry(region, &ocelot->stats_regions, node) { + region->buf = devm_kcalloc(ocelot->dev, region->count, + sizeof(*region->buf), GFP_KERNEL); + if (!region->buf) + return -ENOMEM; + } + + return 0; +} + int ocelot_get_ts_info(struct ocelot *ocelot, int port, struct ethtool_ts_info *info) { @@ -1847,6 +2018,8 @@ static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond) u32 mask = 0; int port; + lockdep_assert_held(&ocelot->fwd_domain_lock); + for (port = 0; port < ocelot->num_phys_ports; port++) { struct ocelot_port *ocelot_port = ocelot->ports[port]; @@ -1860,6 +2033,19 @@ static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond) return mask; } +/* The logical port number of a LAG is equal to the lowest numbered physical + * port ID present in that LAG. It may change if that port ever leaves the LAG. + */ +static int ocelot_bond_get_id(struct ocelot *ocelot, struct net_device *bond) +{ + int bond_mask = ocelot_get_bond_mask(ocelot, bond); + + if (!bond_mask) + return -ENOENT; + + return __ffs(bond_mask); +} + u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port) { struct ocelot_port *ocelot_port = ocelot->ports[src_port]; @@ -1979,6 +2165,28 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining) } EXPORT_SYMBOL(ocelot_apply_bridge_fwd_mask); +void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port) +{ + u16 vid; + + ocelot->ports[port]->is_dsa_8021q_cpu = true; + + for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) + ocelot_vlan_member_add(ocelot, port, vid, true); +} +EXPORT_SYMBOL_GPL(ocelot_port_set_dsa_8021q_cpu); + +void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int port) +{ + u16 vid; + + ocelot->ports[port]->is_dsa_8021q_cpu = false; + + for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) + ocelot_vlan_member_del(ocelot, port, vid); +} +EXPORT_SYMBOL_GPL(ocelot_port_unset_dsa_8021q_cpu); + void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state) { struct ocelot_port *ocelot_port = ocelot->ports[port]; @@ -2123,7 +2331,8 @@ static void ocelot_encode_ports_to_mdb(unsigned char *addr, } int ocelot_port_mdb_add(struct ocelot *ocelot, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + const struct net_device *bridge) { unsigned char addr[ETH_ALEN]; struct ocelot_multicast *mc; @@ -2133,6 +2342,9 @@ int ocelot_port_mdb_add(struct ocelot *ocelot, int port, if (port == ocelot->npi) port = ocelot->num_phys_ports; + if (!vid) + vid = ocelot_vlan_unaware_pvid(ocelot, bridge); + mc = ocelot_multicast_get(ocelot, mdb->addr, vid); if (!mc) { /* New entry */ @@ -2179,7 +2391,8 @@ int ocelot_port_mdb_add(struct ocelot *ocelot, int port, EXPORT_SYMBOL(ocelot_port_mdb_add); int ocelot_port_mdb_del(struct ocelot *ocelot, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + const struct net_device *bridge) { unsigned char addr[ETH_ALEN]; struct ocelot_multicast *mc; @@ -2189,6 +2402,9 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port, if (port == ocelot->npi) port = ocelot->num_phys_ports; + if (!vid) + vid = ocelot_vlan_unaware_pvid(ocelot, bridge); + mc = ocelot_multicast_get(ocelot, mdb->addr, vid); if (!mc) return -ENOENT; @@ -2222,18 +2438,30 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port, } EXPORT_SYMBOL(ocelot_port_mdb_del); -void ocelot_port_bridge_join(struct ocelot *ocelot, int port, - struct net_device *bridge) +int ocelot_port_bridge_join(struct ocelot *ocelot, int port, + struct net_device *bridge, int bridge_num, + struct netlink_ext_ack *extack) { struct ocelot_port *ocelot_port = ocelot->ports[port]; + int err; + + err = ocelot_single_vlan_aware_bridge(ocelot, extack); + if (err) + return err; mutex_lock(&ocelot->fwd_domain_lock); ocelot_port->bridge = bridge; + ocelot_port->bridge_num = bridge_num; ocelot_apply_bridge_fwd_mask(ocelot, true); mutex_unlock(&ocelot->fwd_domain_lock); + + if (br_vlan_enabled(bridge)) + return 0; + + return ocelot_add_vlan_unaware_pvid(ocelot, port, bridge); } EXPORT_SYMBOL(ocelot_port_bridge_join); @@ -2244,7 +2472,11 @@ void ocelot_port_bridge_leave(struct ocelot *ocelot, int port, mutex_lock(&ocelot->fwd_domain_lock); + if (!br_vlan_enabled(bridge)) + ocelot_del_vlan_unaware_pvid(ocelot, port, bridge); + ocelot_port->bridge = NULL; + ocelot_port->bridge_num = -1; ocelot_port_set_pvid(ocelot, port, NULL); ocelot_port_manage_port_tag(ocelot, port); @@ -2353,7 +2585,7 @@ static void ocelot_setup_logical_port_ids(struct ocelot *ocelot) bond = ocelot_port->bond; if (bond) { - int lag = __ffs(ocelot_get_bond_mask(ocelot, bond)); + int lag = ocelot_bond_get_id(ocelot, bond); ocelot_rmw_gix(ocelot, ANA_PORT_PORT_CFG_PORTID_VAL(lag), @@ -2368,6 +2600,46 @@ static void ocelot_setup_logical_port_ids(struct ocelot *ocelot) } } +/* Documentation for PORTID_VAL says: + * Logical port number for front port. If port is not a member of a LLAG, + * then PORTID must be set to the physical port number. + * If port is a member of a LLAG, then PORTID must be set to the common + * PORTID_VAL used for all member ports of the LLAG. + * The value must not exceed the number of physical ports on the device. + * + * This means we have little choice but to migrate FDB entries pointing towards + * a logical port when that changes. + */ +static void ocelot_migrate_lag_fdbs(struct ocelot *ocelot, + struct net_device *bond, + int lag) +{ + struct ocelot_lag_fdb *fdb; + int err; + + lockdep_assert_held(&ocelot->fwd_domain_lock); + + list_for_each_entry(fdb, &ocelot->lag_fdbs, list) { + if (fdb->bond != bond) + continue; + + err = ocelot_mact_forget(ocelot, fdb->addr, fdb->vid); + if (err) { + dev_err(ocelot->dev, + "failed to delete LAG %s FDB %pM vid %d: %pe\n", + bond->name, fdb->addr, fdb->vid, ERR_PTR(err)); + } + + err = ocelot_mact_learn(ocelot, lag, fdb->addr, fdb->vid, + ENTRYTYPE_LOCKED); + if (err) { + dev_err(ocelot->dev, + "failed to migrate LAG %s FDB %pM vid %d: %pe\n", + bond->name, fdb->addr, fdb->vid, ERR_PTR(err)); + } + } +} + int ocelot_port_lag_join(struct ocelot *ocelot, int port, struct net_device *bond, struct netdev_lag_upper_info *info) @@ -2392,14 +2664,23 @@ EXPORT_SYMBOL(ocelot_port_lag_join); void ocelot_port_lag_leave(struct ocelot *ocelot, int port, struct net_device *bond) { + int old_lag_id, new_lag_id; + mutex_lock(&ocelot->fwd_domain_lock); + old_lag_id = ocelot_bond_get_id(ocelot, bond); + ocelot->ports[port]->bond = NULL; ocelot_setup_logical_port_ids(ocelot); ocelot_apply_bridge_fwd_mask(ocelot, false); ocelot_set_aggr_pgids(ocelot); + new_lag_id = ocelot_bond_get_id(ocelot, bond); + + if (new_lag_id >= 0 && old_lag_id != new_lag_id) + ocelot_migrate_lag_fdbs(ocelot, bond, new_lag_id); + mutex_unlock(&ocelot->fwd_domain_lock); } EXPORT_SYMBOL(ocelot_port_lag_leave); @@ -2408,13 +2689,83 @@ void ocelot_port_lag_change(struct ocelot *ocelot, int port, bool lag_tx_active) { struct ocelot_port *ocelot_port = ocelot->ports[port]; + mutex_lock(&ocelot->fwd_domain_lock); + ocelot_port->lag_tx_active = lag_tx_active; /* Rebalance the LAGs */ ocelot_set_aggr_pgids(ocelot); + + mutex_unlock(&ocelot->fwd_domain_lock); } EXPORT_SYMBOL(ocelot_port_lag_change); +int ocelot_lag_fdb_add(struct ocelot *ocelot, struct net_device *bond, + const unsigned char *addr, u16 vid, + const struct net_device *bridge) +{ + struct ocelot_lag_fdb *fdb; + int lag, err; + + fdb = kzalloc(sizeof(*fdb), GFP_KERNEL); + if (!fdb) + return -ENOMEM; + + mutex_lock(&ocelot->fwd_domain_lock); + + if (!vid) + vid = ocelot_vlan_unaware_pvid(ocelot, bridge); + + ether_addr_copy(fdb->addr, addr); + fdb->vid = vid; + fdb->bond = bond; + + lag = ocelot_bond_get_id(ocelot, bond); + + err = ocelot_mact_learn(ocelot, lag, addr, vid, ENTRYTYPE_LOCKED); + if (err) { + mutex_unlock(&ocelot->fwd_domain_lock); + kfree(fdb); + return err; + } + + list_add_tail(&fdb->list, &ocelot->lag_fdbs); + mutex_unlock(&ocelot->fwd_domain_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(ocelot_lag_fdb_add); + +int ocelot_lag_fdb_del(struct ocelot *ocelot, struct net_device *bond, + const unsigned char *addr, u16 vid, + const struct net_device *bridge) +{ + struct ocelot_lag_fdb *fdb, *tmp; + + mutex_lock(&ocelot->fwd_domain_lock); + + if (!vid) + vid = ocelot_vlan_unaware_pvid(ocelot, bridge); + + list_for_each_entry_safe(fdb, tmp, &ocelot->lag_fdbs, list) { + if (!ether_addr_equal(fdb->addr, addr) || fdb->vid != vid || + fdb->bond != bond) + continue; + + ocelot_mact_forget(ocelot, addr, vid); + list_del(&fdb->list); + mutex_unlock(&ocelot->fwd_domain_lock); + kfree(fdb); + + return 0; + } + + mutex_unlock(&ocelot->fwd_domain_lock); + + return -ENOENT; +} +EXPORT_SYMBOL_GPL(ocelot_lag_fdb_del); + /* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu. * The length of VLAN tags is accounted for automatically via DEV_MAC_TAGS_CFG. * In the special case that it's the NPI port that we're configuring, the @@ -2535,6 +2886,9 @@ EXPORT_SYMBOL(ocelot_port_pre_bridge_flags); void ocelot_port_bridge_flags(struct ocelot *ocelot, int port, struct switchdev_brport_flags flags) { + if (port == ocelot->npi) + port = ocelot->num_phys_ports; + if (flags.mask & BR_LEARNING) ocelot_port_set_learning(ocelot, port, !!(flags.val & BR_LEARNING)); @@ -2553,6 +2907,198 @@ void ocelot_port_bridge_flags(struct ocelot *ocelot, int port, } EXPORT_SYMBOL(ocelot_port_bridge_flags); +int ocelot_port_get_default_prio(struct ocelot *ocelot, int port) +{ + int val = ocelot_read_gix(ocelot, ANA_PORT_QOS_CFG, port); + + return ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL_X(val); +} +EXPORT_SYMBOL_GPL(ocelot_port_get_default_prio); + +int ocelot_port_set_default_prio(struct ocelot *ocelot, int port, u8 prio) +{ + if (prio >= OCELOT_NUM_TC) + return -ERANGE; + + ocelot_rmw_gix(ocelot, + ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL(prio), + ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL_M, + ANA_PORT_QOS_CFG, + port); + + return 0; +} +EXPORT_SYMBOL_GPL(ocelot_port_set_default_prio); + +int ocelot_port_get_dscp_prio(struct ocelot *ocelot, int port, u8 dscp) +{ + int qos_cfg = ocelot_read_gix(ocelot, ANA_PORT_QOS_CFG, port); + int dscp_cfg = ocelot_read_rix(ocelot, ANA_DSCP_CFG, dscp); + + /* Return error if DSCP prioritization isn't enabled */ + if (!(qos_cfg & ANA_PORT_QOS_CFG_QOS_DSCP_ENA)) + return -EOPNOTSUPP; + + if (qos_cfg & ANA_PORT_QOS_CFG_DSCP_TRANSLATE_ENA) { + dscp = ANA_DSCP_CFG_DSCP_TRANSLATE_VAL_X(dscp_cfg); + /* Re-read ANA_DSCP_CFG for the translated DSCP */ + dscp_cfg = ocelot_read_rix(ocelot, ANA_DSCP_CFG, dscp); + } + + /* If the DSCP value is not trusted, the QoS classification falls back + * to VLAN PCP or port-based default. + */ + if (!(dscp_cfg & ANA_DSCP_CFG_DSCP_TRUST_ENA)) + return -EOPNOTSUPP; + + return ANA_DSCP_CFG_QOS_DSCP_VAL_X(dscp_cfg); +} +EXPORT_SYMBOL_GPL(ocelot_port_get_dscp_prio); + +int ocelot_port_add_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio) +{ + int mask, val; + + if (prio >= OCELOT_NUM_TC) + return -ERANGE; + + /* There is at least one app table priority (this one), so we need to + * make sure DSCP prioritization is enabled on the port. + * Also make sure DSCP translation is disabled + * (dcbnl doesn't support it). + */ + mask = ANA_PORT_QOS_CFG_QOS_DSCP_ENA | + ANA_PORT_QOS_CFG_DSCP_TRANSLATE_ENA; + + ocelot_rmw_gix(ocelot, ANA_PORT_QOS_CFG_QOS_DSCP_ENA, mask, + ANA_PORT_QOS_CFG, port); + + /* Trust this DSCP value and map it to the given QoS class */ + val = ANA_DSCP_CFG_DSCP_TRUST_ENA | ANA_DSCP_CFG_QOS_DSCP_VAL(prio); + + ocelot_write_rix(ocelot, val, ANA_DSCP_CFG, dscp); + + return 0; +} +EXPORT_SYMBOL_GPL(ocelot_port_add_dscp_prio); + +int ocelot_port_del_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio) +{ + int dscp_cfg = ocelot_read_rix(ocelot, ANA_DSCP_CFG, dscp); + int mask, i; + + /* During a "dcb app replace" command, the new app table entry will be + * added first, then the old one will be deleted. But the hardware only + * supports one QoS class per DSCP value (duh), so if we blindly delete + * the app table entry for this DSCP value, we end up deleting the + * entry with the new priority. Avoid that by checking whether user + * space wants to delete the priority which is currently configured, or + * something else which is no longer current. + */ + if (ANA_DSCP_CFG_QOS_DSCP_VAL_X(dscp_cfg) != prio) + return 0; + + /* Untrust this DSCP value */ + ocelot_write_rix(ocelot, 0, ANA_DSCP_CFG, dscp); + + for (i = 0; i < 64; i++) { + int dscp_cfg = ocelot_read_rix(ocelot, ANA_DSCP_CFG, i); + + /* There are still app table entries on the port, so we need to + * keep DSCP enabled, nothing to do. + */ + if (dscp_cfg & ANA_DSCP_CFG_DSCP_TRUST_ENA) + return 0; + } + + /* Disable DSCP QoS classification if there isn't any trusted + * DSCP value left. + */ + mask = ANA_PORT_QOS_CFG_QOS_DSCP_ENA | + ANA_PORT_QOS_CFG_DSCP_TRANSLATE_ENA; + + ocelot_rmw_gix(ocelot, 0, mask, ANA_PORT_QOS_CFG, port); + + return 0; +} +EXPORT_SYMBOL_GPL(ocelot_port_del_dscp_prio); + +struct ocelot_mirror *ocelot_mirror_get(struct ocelot *ocelot, int to, + struct netlink_ext_ack *extack) +{ + struct ocelot_mirror *m = ocelot->mirror; + + if (m) { + if (m->to != to) { + NL_SET_ERR_MSG_MOD(extack, + "Mirroring already configured towards different egress port"); + return ERR_PTR(-EBUSY); + } + + refcount_inc(&m->refcount); + return m; + } + + m = kzalloc(sizeof(*m), GFP_KERNEL); + if (!m) + return ERR_PTR(-ENOMEM); + + m->to = to; + refcount_set(&m->refcount, 1); + ocelot->mirror = m; + + /* Program the mirror port to hardware */ + ocelot_write(ocelot, BIT(to), ANA_MIRRORPORTS); + + return m; +} + +void ocelot_mirror_put(struct ocelot *ocelot) +{ + struct ocelot_mirror *m = ocelot->mirror; + + if (!refcount_dec_and_test(&m->refcount)) + return; + + ocelot_write(ocelot, 0, ANA_MIRRORPORTS); + ocelot->mirror = NULL; + kfree(m); +} + +int ocelot_port_mirror_add(struct ocelot *ocelot, int from, int to, + bool ingress, struct netlink_ext_ack *extack) +{ + struct ocelot_mirror *m = ocelot_mirror_get(ocelot, to, extack); + + if (IS_ERR(m)) + return PTR_ERR(m); + + if (ingress) { + ocelot_rmw_gix(ocelot, ANA_PORT_PORT_CFG_SRC_MIRROR_ENA, + ANA_PORT_PORT_CFG_SRC_MIRROR_ENA, + ANA_PORT_PORT_CFG, from); + } else { + ocelot_rmw(ocelot, BIT(from), BIT(from), + ANA_EMIRRORPORTS); + } + + return 0; +} +EXPORT_SYMBOL_GPL(ocelot_port_mirror_add); + +void ocelot_port_mirror_del(struct ocelot *ocelot, int from, bool ingress) +{ + if (ingress) { + ocelot_rmw_gix(ocelot, 0, ANA_PORT_PORT_CFG_SRC_MIRROR_ENA, + ANA_PORT_PORT_CFG, from); + } else { + ocelot_rmw(ocelot, 0, BIT(from), ANA_EMIRRORPORTS); + } + + ocelot_mirror_put(ocelot); +} +EXPORT_SYMBOL_GPL(ocelot_port_mirror_del); + void ocelot_init_port(struct ocelot *ocelot, int port) { struct ocelot_port *ocelot_port = ocelot->ports[port]; @@ -2647,7 +3193,7 @@ static void ocelot_cpu_port_init(struct ocelot *ocelot) /* Configure the CPU port to be VLAN aware */ ocelot_write_gix(ocelot, - ANA_PORT_VLAN_CFG_VLAN_VID(OCELOT_VLAN_UNAWARE_PVID) | + ANA_PORT_VLAN_CFG_VLAN_VID(OCELOT_STANDALONE_PVID) | ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1), ANA_PORT_VLAN_CFG, cpu); @@ -2709,6 +3255,7 @@ int ocelot_init(struct ocelot *ocelot) INIT_LIST_HEAD(&ocelot->multicast); INIT_LIST_HEAD(&ocelot->pgids); INIT_LIST_HEAD(&ocelot->vlans); + INIT_LIST_HEAD(&ocelot->lag_fdbs); ocelot_detect_features(ocelot); ocelot_mact_init(ocelot); ocelot_vlan_init(ocelot); @@ -2814,6 +3361,13 @@ int ocelot_init(struct ocelot *ocelot) ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL(6), ANA_CPUQ_8021_CFG, i); + ret = ocelot_prepare_stats_regions(ocelot); + if (ret) { + destroy_workqueue(ocelot->stats_queue); + destroy_workqueue(ocelot->owq); + return ret; + } + INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work); queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, OCELOT_STATS_CHECK_DELAY); diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h index bf4eff6d7086..d0fa8ab6cc81 100644 --- a/drivers/net/ethernet/mscc/ocelot.h +++ b/drivers/net/ethernet/mscc/ocelot.h @@ -21,11 +21,12 @@ #include #include #include +#include #include #include "ocelot_rew.h" #include "ocelot_qs.h" -#define OCELOT_VLAN_UNAWARE_PVID 0 +#define OCELOT_STANDALONE_PVID 0 #define OCELOT_BUFFER_CELL_SZ 60 #define OCELOT_STATS_CHECK_DELAY (2 * HZ) @@ -37,7 +38,8 @@ struct ocelot_port_tc { bool block_shared; unsigned long offload_cnt; - + unsigned long ingress_mirred_id; + unsigned long egress_mirred_id; unsigned long police_id; }; @@ -80,6 +82,9 @@ struct ocelot_multicast { struct ocelot_pgid *pgid; }; +int ocelot_bridge_num_find(struct ocelot *ocelot, + const struct net_device *bridge); + int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid, bool is_static, void *data); int ocelot_mact_learn(struct ocelot *ocelot, int port, @@ -102,6 +107,15 @@ int ocelot_port_devlink_init(struct ocelot *ocelot, int port, enum devlink_port_flavour flavour); void ocelot_port_devlink_teardown(struct ocelot *ocelot, int port); +int ocelot_trap_add(struct ocelot *ocelot, int port, + unsigned long cookie, bool take_ts, + void (*populate)(struct ocelot_vcap_filter *f)); +int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie); + +struct ocelot_mirror *ocelot_mirror_get(struct ocelot *ocelot, int to, + struct netlink_ext_ack *extack); +void ocelot_mirror_put(struct ocelot *ocelot); + extern struct notifier_block ocelot_netdevice_nb; extern struct notifier_block ocelot_switchdev_nb; extern struct notifier_block ocelot_switchdev_blocking_nb; diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c index fdb4d7e7296c..03b5e59d033e 100644 --- a/drivers/net/ethernet/mscc/ocelot_flower.c +++ b/drivers/net/ethernet/mscc/ocelot_flower.c @@ -6,6 +6,7 @@ #include #include #include +#include "ocelot_police.h" #include "ocelot_vcap.h" /* Arbitrarily chosen constants for encoding the VCAP block and lookup number @@ -231,6 +232,7 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, bool ingress, struct flow_cls_offload *f, struct ocelot_vcap_filter *filter) { + const struct flow_action *action = &f->rule->action; struct netlink_ext_ack *extack = f->common.extack; bool allow_missing_goto_target = false; const struct flow_action_entry *a; @@ -258,7 +260,7 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, filter->goto_target = -1; filter->type = OCELOT_VCAP_FILTER_DUMMY; - flow_action_for_each(i, a, &f->rule->action) { + flow_action_for_each(i, a, action) { switch (a->id) { case FLOW_ACTION_DROP: if (filter->block_id != VCAP_IS2) { @@ -293,6 +295,7 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, filter->action.cpu_copy_ena = true; filter->action.cpu_qu_num = 0; filter->type = OCELOT_VCAP_FILTER_OFFLOAD; + list_add_tail(&filter->trap_list, &ocelot->traps); break; case FLOW_ACTION_POLICE: if (filter->block_id == PSFP_BLOCK_ID) { @@ -310,11 +313,11 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, "Last action must be GOTO"); return -EOPNOTSUPP; } - if (a->police.rate_pkt_ps) { - NL_SET_ERR_MSG_MOD(extack, - "QoS offload not support packets per second"); - return -EOPNOTSUPP; - } + + err = ocelot_policer_validate(action, a, extack); + if (err) + return err; + filter->action.police_ena = true; pol_ix = a->hw_index + ocelot->vcap_pol.base; @@ -356,6 +359,27 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, filter->action.port_mask = BIT(egress_port); filter->type = OCELOT_VCAP_FILTER_OFFLOAD; break; + case FLOW_ACTION_MIRRED: + if (filter->block_id != VCAP_IS2) { + NL_SET_ERR_MSG_MOD(extack, + "Mirror action can only be offloaded to VCAP IS2"); + return -EOPNOTSUPP; + } + if (filter->goto_target != -1) { + NL_SET_ERR_MSG_MOD(extack, + "Last action must be GOTO"); + return -EOPNOTSUPP; + } + egress_port = ocelot->ops->netdev_to_port(a->dev); + if (egress_port < 0) { + NL_SET_ERR_MSG_MOD(extack, + "Destination not an ocelot port"); + return -EOPNOTSUPP; + } + filter->egress_port.value = egress_port; + filter->action.mirror_ena = true; + filter->type = OCELOT_VCAP_FILTER_OFFLOAD; + break; case FLOW_ACTION_VLAN_POP: if (filter->block_id != VCAP_IS1) { NL_SET_ERR_MSG_MOD(extack, @@ -854,6 +878,8 @@ int ocelot_cls_flower_replace(struct ocelot *ocelot, int port, ret = ocelot_flower_parse(ocelot, port, ingress, f, filter); if (ret) { + if (!list_empty(&filter->trap_list)) + list_del(&filter->trap_list); kfree(filter); return ret; } diff --git a/drivers/net/ethernet/mscc/ocelot_io.c b/drivers/net/ethernet/mscc/ocelot_io.c index 7390fa3980ec..2067382d0ee1 100644 --- a/drivers/net/ethernet/mscc/ocelot_io.c +++ b/drivers/net/ethernet/mscc/ocelot_io.c @@ -10,6 +10,19 @@ #include "ocelot.h" +int __ocelot_bulk_read_ix(struct ocelot *ocelot, u32 reg, u32 offset, void *buf, + int count) +{ + u16 target = reg >> TARGET_OFFSET; + + WARN_ON(!target); + + return regmap_bulk_read(ocelot->targets[target], + ocelot->map[target][reg & REG_MASK] + offset, + buf, count); +} +EXPORT_SYMBOL_GPL(__ocelot_bulk_read_ix); + u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset) { u16 target = reg >> TARGET_OFFSET; diff --git a/drivers/net/ethernet/mscc/ocelot_mrp.c b/drivers/net/ethernet/mscc/ocelot_mrp.c index 1fa58546abdc..3ccec488a304 100644 --- a/drivers/net/ethernet/mscc/ocelot_mrp.c +++ b/drivers/net/ethernet/mscc/ocelot_mrp.c @@ -60,7 +60,7 @@ static int ocelot_mrp_redirect_add_vcap(struct ocelot *ocelot, int src_port, filter->key_type = OCELOT_VCAP_KEY_ETYPE; filter->prio = 1; - filter->id.cookie = src_port; + filter->id.cookie = OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, src_port); filter->id.tc_offload = false; filter->block_id = VCAP_IS2; filter->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -77,55 +77,46 @@ static int ocelot_mrp_redirect_add_vcap(struct ocelot *ocelot, int src_port, return err; } -static int ocelot_mrp_copy_add_vcap(struct ocelot *ocelot, int port, - int prio, unsigned long cookie) +static void ocelot_populate_mrp_trap_key(struct ocelot_vcap_filter *filter) { const u8 mrp_mask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; - struct ocelot_vcap_filter *filter; - int err; - filter = kzalloc(sizeof(*filter), GFP_KERNEL); - if (!filter) - return -ENOMEM; - - filter->key_type = OCELOT_VCAP_KEY_ETYPE; - filter->prio = prio; - filter->id.cookie = cookie; - filter->id.tc_offload = false; - filter->block_id = VCAP_IS2; - filter->type = OCELOT_VCAP_FILTER_OFFLOAD; - filter->ingress_port_mask = BIT(port); /* Here is possible to use control or test dmac because the mask * doesn't cover the LSB */ ether_addr_copy(filter->key.etype.dmac.value, mrp_test_dmac); ether_addr_copy(filter->key.etype.dmac.mask, mrp_mask); - filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; - filter->action.port_mask = 0x0; - filter->action.cpu_copy_ena = true; - filter->action.cpu_qu_num = OCELOT_MRP_CPUQ; +} - err = ocelot_vcap_filter_add(ocelot, filter, NULL); - if (err) - kfree(filter); +static int ocelot_mrp_trap_add(struct ocelot *ocelot, int port) +{ + unsigned long cookie = OCELOT_VCAP_IS2_MRP_TRAP(ocelot); - return err; + return ocelot_trap_add(ocelot, port, cookie, false, + ocelot_populate_mrp_trap_key); +} + +static int ocelot_mrp_trap_del(struct ocelot *ocelot, int port) +{ + unsigned long cookie = OCELOT_VCAP_IS2_MRP_TRAP(ocelot); + + return ocelot_trap_del(ocelot, port, cookie); } static void ocelot_mrp_save_mac(struct ocelot *ocelot, struct ocelot_port *port) { ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_test_dmac, - OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); + OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED); ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_control_dmac, - OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); + OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED); } static void ocelot_mrp_del_mac(struct ocelot *ocelot, struct ocelot_port *port) { - ocelot_mact_forget(ocelot, mrp_test_dmac, OCELOT_VLAN_UNAWARE_PVID); - ocelot_mact_forget(ocelot, mrp_control_dmac, OCELOT_VLAN_UNAWARE_PVID); + ocelot_mact_forget(ocelot, mrp_test_dmac, OCELOT_STANDALONE_PVID); + ocelot_mact_forget(ocelot, mrp_control_dmac, OCELOT_STANDALONE_PVID); } int ocelot_mrp_add(struct ocelot *ocelot, int port, @@ -186,7 +177,7 @@ int ocelot_mrp_add_ring_role(struct ocelot *ocelot, int port, ocelot_mrp_save_mac(ocelot, ocelot_port); if (mrp->ring_role != BR_MRP_RING_ROLE_MRC) - return ocelot_mrp_copy_add_vcap(ocelot, port, 1, port); + return ocelot_mrp_trap_add(ocelot, port); dst_port = ocelot_mrp_find_partner_port(ocelot, ocelot_port); if (dst_port == -1) @@ -196,10 +187,10 @@ int ocelot_mrp_add_ring_role(struct ocelot *ocelot, int port, if (err) return err; - err = ocelot_mrp_copy_add_vcap(ocelot, port, 2, - port + ocelot->num_phys_ports); + err = ocelot_mrp_trap_add(ocelot, port); if (err) { - ocelot_mrp_del_vcap(ocelot, port); + ocelot_mrp_del_vcap(ocelot, + OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port)); return err; } @@ -211,7 +202,7 @@ int ocelot_mrp_del_ring_role(struct ocelot *ocelot, int port, const struct switchdev_obj_ring_role_mrp *mrp) { struct ocelot_port *ocelot_port = ocelot->ports[port]; - int i; + int err, i; if (!ocelot_port) return -EOPNOTSUPP; @@ -222,8 +213,11 @@ int ocelot_mrp_del_ring_role(struct ocelot *ocelot, int port, if (ocelot_port->mrp_ring_id != mrp->ring_id) return 0; - ocelot_mrp_del_vcap(ocelot, port); - ocelot_mrp_del_vcap(ocelot, port + ocelot->num_phys_ports); + err = ocelot_mrp_trap_del(ocelot, port); + if (err) + return err; + + ocelot_mrp_del_vcap(ocelot, OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port)); for (i = 0; i < ocelot->num_phys_ports; ++i) { ocelot_port = ocelot->ports[i]; diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index e271b6225b72..247bc105bdd2 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -14,11 +14,14 @@ #include #include #include "ocelot.h" +#include "ocelot_police.h" #include "ocelot_vcap.h" #include "ocelot_fdma.h" #define OCELOT_MAC_QUIRKS OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP +static bool ocelot_netdevice_dev_check(const struct net_device *dev); + static struct ocelot *devlink_port_to_ocelot(struct devlink_port *dlp) { return devlink_priv(dlp->devlink); @@ -215,14 +218,14 @@ int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv, } } -static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, - struct tc_cls_matchall_offload *f, - bool ingress) +static int ocelot_setup_tc_cls_matchall_police(struct ocelot_port_private *priv, + struct tc_cls_matchall_offload *f, + bool ingress, + struct netlink_ext_ack *extack) { - struct netlink_ext_ack *extack = f->common.extack; + struct flow_action_entry *action = &f->rule->action.entries[0]; struct ocelot *ocelot = priv->port.ocelot; struct ocelot_policer pol = { 0 }; - struct flow_action_entry *action; int port = priv->chip_port; int err; @@ -231,6 +234,119 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, return -EOPNOTSUPP; } + if (priv->tc.police_id && priv->tc.police_id != f->cookie) { + NL_SET_ERR_MSG_MOD(extack, + "Only one policer per port is supported"); + return -EEXIST; + } + + err = ocelot_policer_validate(&f->rule->action, action, extack); + if (err) + return err; + + pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8; + pol.burst = action->police.burst; + + err = ocelot_port_policer_add(ocelot, port, &pol); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Could not add policer"); + return err; + } + + priv->tc.police_id = f->cookie; + priv->tc.offload_cnt++; + + return 0; +} + +static int ocelot_setup_tc_cls_matchall_mirred(struct ocelot_port_private *priv, + struct tc_cls_matchall_offload *f, + bool ingress, + struct netlink_ext_ack *extack) +{ + struct flow_action *action = &f->rule->action; + struct ocelot *ocelot = priv->port.ocelot; + struct ocelot_port_private *other_priv; + const struct flow_action_entry *a; + int err; + + if (f->common.protocol != htons(ETH_P_ALL)) + return -EOPNOTSUPP; + + if (!flow_action_basic_hw_stats_check(action, extack)) + return -EOPNOTSUPP; + + a = &action->entries[0]; + if (!a->dev) + return -EINVAL; + + if (!ocelot_netdevice_dev_check(a->dev)) { + NL_SET_ERR_MSG_MOD(extack, + "Destination not an ocelot port"); + return -EOPNOTSUPP; + } + + other_priv = netdev_priv(a->dev); + + err = ocelot_port_mirror_add(ocelot, priv->chip_port, + other_priv->chip_port, ingress, extack); + if (err) + return err; + + if (ingress) + priv->tc.ingress_mirred_id = f->cookie; + else + priv->tc.egress_mirred_id = f->cookie; + priv->tc.offload_cnt++; + + return 0; +} + +static int ocelot_del_tc_cls_matchall_police(struct ocelot_port_private *priv, + struct netlink_ext_ack *extack) +{ + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + int err; + + err = ocelot_port_policer_del(ocelot, port); + if (err) { + NL_SET_ERR_MSG_MOD(extack, + "Could not delete policer"); + return err; + } + + priv->tc.police_id = 0; + priv->tc.offload_cnt--; + + return 0; +} + +static int ocelot_del_tc_cls_matchall_mirred(struct ocelot_port_private *priv, + bool ingress, + struct netlink_ext_ack *extack) +{ + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + ocelot_port_mirror_del(ocelot, port, ingress); + + if (ingress) + priv->tc.ingress_mirred_id = 0; + else + priv->tc.egress_mirred_id = 0; + priv->tc.offload_cnt--; + + return 0; +} + +static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, + struct tc_cls_matchall_offload *f, + bool ingress) +{ + struct netlink_ext_ack *extack = f->common.extack; + struct flow_action_entry *action; + switch (f->command) { case TC_CLSMATCHALL_REPLACE: if (!flow_offload_has_one_action(&f->rule->action)) { @@ -241,54 +357,41 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, if (priv->tc.block_shared) { NL_SET_ERR_MSG_MOD(extack, - "Rate limit is not supported on shared blocks"); + "Matchall offloads not supported on shared blocks"); return -EOPNOTSUPP; } action = &f->rule->action.entries[0]; - if (action->id != FLOW_ACTION_POLICE) { + switch (action->id) { + case FLOW_ACTION_POLICE: + return ocelot_setup_tc_cls_matchall_police(priv, f, + ingress, + extack); + break; + case FLOW_ACTION_MIRRED: + return ocelot_setup_tc_cls_matchall_mirred(priv, f, + ingress, + extack); + default: NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); return -EOPNOTSUPP; } - if (priv->tc.police_id && priv->tc.police_id != f->cookie) { - NL_SET_ERR_MSG_MOD(extack, - "Only one policer per port is supported"); - return -EEXIST; - } - - if (action->police.rate_pkt_ps) { - NL_SET_ERR_MSG_MOD(extack, - "QoS offload not support packets per second"); - return -EOPNOTSUPP; - } - - pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8; - pol.burst = action->police.burst; - - err = ocelot_port_policer_add(ocelot, port, &pol); - if (err) { - NL_SET_ERR_MSG_MOD(extack, "Could not add policer"); - return err; - } - - priv->tc.police_id = f->cookie; - priv->tc.offload_cnt++; - return 0; + break; case TC_CLSMATCHALL_DESTROY: - if (priv->tc.police_id != f->cookie) + action = &f->rule->action.entries[0]; + + if (f->cookie == priv->tc.police_id) + return ocelot_del_tc_cls_matchall_police(priv, extack); + else if (f->cookie == priv->tc.ingress_mirred_id || + f->cookie == priv->tc.egress_mirred_id) + return ocelot_del_tc_cls_matchall_mirred(priv, ingress, + extack); + else return -ENOENT; - err = ocelot_port_policer_del(ocelot, port); - if (err) { - NL_SET_ERR_MSG_MOD(extack, - "Could not delete policer"); - return err; - } - priv->tc.police_id = 0; - priv->tc.offload_cnt--; - return 0; + break; case TC_CLSMATCHALL_STATS: default: return -EOPNOTSUPP; @@ -419,7 +522,7 @@ static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid) * with VLAN filtering feature. We need to keep it to receive * untagged traffic. */ - if (vid == OCELOT_VLAN_UNAWARE_PVID) + if (vid == OCELOT_STANDALONE_PVID) return 0; ret = ocelot_vlan_del(ocelot, port, vid); @@ -559,7 +662,7 @@ static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr) struct ocelot_mact_work_ctx w; ether_addr_copy(w.forget.addr, addr); - w.forget.vid = OCELOT_VLAN_UNAWARE_PVID; + w.forget.vid = OCELOT_STANDALONE_PVID; w.type = OCELOT_MACT_FORGET; return ocelot_enqueue_mact_action(ocelot, &w); @@ -573,7 +676,7 @@ static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr) struct ocelot_mact_work_ctx w; ether_addr_copy(w.learn.addr, addr); - w.learn.vid = OCELOT_VLAN_UNAWARE_PVID; + w.learn.vid = OCELOT_STANDALONE_PVID; w.learn.pgid = PGID_CPU; w.learn.entry_type = ENTRYTYPE_LOCKED; w.type = OCELOT_MACT_LEARN; @@ -608,9 +711,9 @@ static int ocelot_port_set_mac_address(struct net_device *dev, void *p) /* Learn the new net device MAC address in the mac table. */ ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, - OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); + OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED); /* Then forget the previous one. */ - ocelot_mact_forget(ocelot, dev->dev_addr, OCELOT_VLAN_UNAWARE_PVID); + ocelot_mact_forget(ocelot, dev->dev_addr, OCELOT_STANDALONE_PVID); eth_hw_addr_set(dev, addr->sa_data); return 0; @@ -662,10 +765,11 @@ static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], struct netlink_ext_ack *extack) { struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; int port = priv->chip_port; - return ocelot_fdb_add(ocelot, port, addr, vid); + return ocelot_fdb_add(ocelot, port, addr, vid, ocelot_port->bridge); } static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], @@ -673,10 +777,11 @@ static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], const unsigned char *addr, u16 vid) { struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; int port = priv->chip_port; - return ocelot_fdb_del(ocelot, port, addr, vid); + return ocelot_fdb_del(ocelot, port, addr, vid, ocelot_port->bridge); } static int ocelot_port_fdb_dump(struct sk_buff *skb, @@ -988,7 +1093,7 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev, struct ocelot *ocelot = ocelot_port->ocelot; int port = priv->chip_port; - return ocelot_port_mdb_add(ocelot, port, mdb); + return ocelot_port_mdb_add(ocelot, port, mdb, ocelot_port->bridge); } static int ocelot_port_obj_del_mdb(struct net_device *dev, @@ -999,7 +1104,7 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev, struct ocelot *ocelot = ocelot_port->ocelot; int port = priv->chip_port; - return ocelot_port_mdb_del(ocelot, port, mdb); + return ocelot_port_mdb_del(ocelot, port, mdb, ocelot_port->bridge); } static int ocelot_port_obj_mrp_add(struct net_device *dev, @@ -1173,6 +1278,33 @@ static int ocelot_switchdev_unsync(struct ocelot *ocelot, int port) return 0; } +static int ocelot_bridge_num_get(struct ocelot *ocelot, + const struct net_device *bridge_dev) +{ + int bridge_num = ocelot_bridge_num_find(ocelot, bridge_dev); + + if (bridge_num < 0) { + /* First port that offloads this bridge */ + bridge_num = find_first_zero_bit(&ocelot->bridges, + ocelot->num_phys_ports); + + set_bit(bridge_num, &ocelot->bridges); + } + + return bridge_num; +} + +static void ocelot_bridge_num_put(struct ocelot *ocelot, + const struct net_device *bridge_dev, + int bridge_num) +{ + /* Check if the bridge is still in use, otherwise it is time + * to clean it up so we can reuse this bridge_num later. + */ + if (!ocelot_bridge_num_find(ocelot, bridge_dev)) + clear_bit(bridge_num, &ocelot->bridges); +} + static int ocelot_netdevice_bridge_join(struct net_device *dev, struct net_device *brport_dev, struct net_device *bridge, @@ -1182,9 +1314,14 @@ static int ocelot_netdevice_bridge_join(struct net_device *dev, struct ocelot_port *ocelot_port = &priv->port; struct ocelot *ocelot = ocelot_port->ocelot; int port = priv->chip_port; - int err; + int bridge_num, err; - ocelot_port_bridge_join(ocelot, port, bridge); + bridge_num = ocelot_bridge_num_get(ocelot, bridge); + + err = ocelot_port_bridge_join(ocelot, port, bridge, bridge_num, + extack); + if (err) + goto err_join; err = switchdev_bridge_port_offload(brport_dev, dev, priv, &ocelot_switchdev_nb, @@ -1205,6 +1342,8 @@ static int ocelot_netdevice_bridge_join(struct net_device *dev, &ocelot_switchdev_blocking_nb); err_switchdev_offload: ocelot_port_bridge_leave(ocelot, port, bridge); +err_join: + ocelot_bridge_num_put(ocelot, bridge, bridge_num); return err; } @@ -1225,6 +1364,7 @@ static int ocelot_netdevice_bridge_leave(struct net_device *dev, struct ocelot_port_private *priv = netdev_priv(dev); struct ocelot_port *ocelot_port = &priv->port; struct ocelot *ocelot = ocelot_port->ocelot; + int bridge_num = ocelot_port->bridge_num; int port = priv->chip_port; int err; @@ -1233,6 +1373,7 @@ static int ocelot_netdevice_bridge_leave(struct net_device *dev, return err; ocelot_port_bridge_leave(ocelot, port, bridge); + ocelot_bridge_num_put(ocelot, bridge, bridge_num); return 0; } @@ -1700,7 +1841,7 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target, eth_hw_addr_gen(dev, ocelot->base_mac, port); ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, - OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); + OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED); ocelot_init_port(ocelot, port); diff --git a/drivers/net/ethernet/mscc/ocelot_police.c b/drivers/net/ethernet/mscc/ocelot_police.c index 6f5068c1041a..a65606bb84a0 100644 --- a/drivers/net/ethernet/mscc/ocelot_police.c +++ b/drivers/net/ethernet/mscc/ocelot_police.c @@ -154,6 +154,47 @@ int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix, return 0; } +int ocelot_policer_validate(const struct flow_action *action, + const struct flow_action_entry *a, + struct netlink_ext_ack *extack) +{ + if (a->police.exceed.act_id != FLOW_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when exceed action is not drop"); + return -EOPNOTSUPP; + } + + if (a->police.notexceed.act_id != FLOW_ACTION_PIPE && + a->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not pipe or ok"); + return -EOPNOTSUPP; + } + + if (a->police.notexceed.act_id == FLOW_ACTION_ACCEPT && + !flow_action_is_last_entry(action, a)) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is ok, but police action is not last"); + return -EOPNOTSUPP; + } + + if (a->police.peakrate_bytes_ps || + a->police.avrate || a->police.overhead) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when peakrate/avrate/overhead is configured"); + return -EOPNOTSUPP; + } + + if (a->police.rate_pkt_ps) { + NL_SET_ERR_MSG_MOD(extack, + "Offload does not support packets per second"); + return -EOPNOTSUPP; + } + + return 0; +} +EXPORT_SYMBOL(ocelot_policer_validate); + int ocelot_port_policer_add(struct ocelot *ocelot, int port, struct ocelot_policer *pol) { diff --git a/drivers/net/ethernet/mscc/ocelot_police.h b/drivers/net/ethernet/mscc/ocelot_police.h index 7adb05f71999..7552995f8b17 100644 --- a/drivers/net/ethernet/mscc/ocelot_police.h +++ b/drivers/net/ethernet/mscc/ocelot_police.h @@ -8,6 +8,7 @@ #define _MSCC_OCELOT_POLICE_H_ #include "ocelot.h" +#include enum mscc_qos_rate_mode { MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */ @@ -33,4 +34,8 @@ struct qos_policer_conf { int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix, struct qos_policer_conf *conf); +int ocelot_policer_validate(const struct flow_action *action, + const struct flow_action_entry *a, + struct netlink_ext_ack *extack); + #endif /* _MSCC_OCELOT_POLICE_H_ */ diff --git a/drivers/net/ethernet/mscc/ocelot_vcap.c b/drivers/net/ethernet/mscc/ocelot_vcap.c index d3544413a8a4..c8701ac955a8 100644 --- a/drivers/net/ethernet/mscc/ocelot_vcap.c +++ b/drivers/net/ethernet/mscc/ocelot_vcap.c @@ -335,6 +335,7 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data, vcap_action_set(vcap, data, VCAP_IS2_ACT_MASK_MODE, a->mask_mode); vcap_action_set(vcap, data, VCAP_IS2_ACT_PORT_MASK, a->port_mask); + vcap_action_set(vcap, data, VCAP_IS2_ACT_MIRROR_ENA, a->mirror_ena); vcap_action_set(vcap, data, VCAP_IS2_ACT_POLICE_ENA, a->police_ena); vcap_action_set(vcap, data, VCAP_IS2_ACT_POLICE_IDX, a->pol_ix); vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_QU_NUM, a->cpu_qu_num); @@ -564,9 +565,9 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, val = proto.value[0]; msk = proto.mask[0]; type = IS2_TYPE_IP_UDP_TCP; - if (msk == 0xff && (val == 6 || val == 17)) { + if (msk == 0xff && (val == IPPROTO_TCP || val == IPPROTO_UDP)) { /* UDP/TCP protocol match */ - tcp = (val == 6 ? + tcp = (val == IPPROTO_TCP ? OCELOT_VCAP_BIT_1 : OCELOT_VCAP_BIT_0); vcap_key_bit_set(vcap, &data, VCAP_IS2_HK_TCP, tcp); vcap_key_l4_port_set(vcap, &data, @@ -955,14 +956,21 @@ int ocelot_vcap_policer_del(struct ocelot *ocelot, u32 pol_ix) } EXPORT_SYMBOL(ocelot_vcap_policer_del); -static int ocelot_vcap_filter_add_to_block(struct ocelot *ocelot, - struct ocelot_vcap_block *block, - struct ocelot_vcap_filter *filter) +static int +ocelot_vcap_filter_add_aux_resources(struct ocelot *ocelot, + struct ocelot_vcap_filter *filter, + struct netlink_ext_ack *extack) { - struct ocelot_vcap_filter *tmp; - struct list_head *pos, *n; + struct ocelot_mirror *m; int ret; + if (filter->block_id == VCAP_IS2 && filter->action.mirror_ena) { + m = ocelot_mirror_get(ocelot, filter->egress_port.value, + extack); + if (IS_ERR(m)) + return PTR_ERR(m); + } + if (filter->block_id == VCAP_IS2 && filter->action.police_ena) { ret = ocelot_vcap_policer_add(ocelot, filter->action.pol_ix, &filter->action.pol); @@ -970,6 +978,33 @@ static int ocelot_vcap_filter_add_to_block(struct ocelot *ocelot, return ret; } + return 0; +} + +static void +ocelot_vcap_filter_del_aux_resources(struct ocelot *ocelot, + struct ocelot_vcap_filter *filter) +{ + if (filter->block_id == VCAP_IS2 && filter->action.police_ena) + ocelot_vcap_policer_del(ocelot, filter->action.pol_ix); + + if (filter->block_id == VCAP_IS2 && filter->action.mirror_ena) + ocelot_mirror_put(ocelot); +} + +static int ocelot_vcap_filter_add_to_block(struct ocelot *ocelot, + struct ocelot_vcap_block *block, + struct ocelot_vcap_filter *filter, + struct netlink_ext_ack *extack) +{ + struct ocelot_vcap_filter *tmp; + struct list_head *pos, *n; + int ret; + + ret = ocelot_vcap_filter_add_aux_resources(ocelot, filter, extack); + if (ret) + return ret; + block->count++; if (list_empty(&block->rules)) { @@ -1168,7 +1203,7 @@ int ocelot_vcap_filter_add(struct ocelot *ocelot, } /* Add filter to the linked list */ - ret = ocelot_vcap_filter_add_to_block(ocelot, block, filter); + ret = ocelot_vcap_filter_add_to_block(ocelot, block, filter, extack); if (ret) return ret; @@ -1195,18 +1230,12 @@ static void ocelot_vcap_block_remove_filter(struct ocelot *ocelot, struct ocelot_vcap_block *block, struct ocelot_vcap_filter *filter) { - struct ocelot_vcap_filter *tmp; - struct list_head *pos, *q; + struct ocelot_vcap_filter *tmp, *n; - list_for_each_safe(pos, q, &block->rules) { - tmp = list_entry(pos, struct ocelot_vcap_filter, list); + list_for_each_entry_safe(tmp, n, &block->rules, list) { if (ocelot_vcap_filter_equal(filter, tmp)) { - if (tmp->block_id == VCAP_IS2 && - tmp->action.police_ena) - ocelot_vcap_policer_del(ocelot, - tmp->action.pol_ix); - - list_del(pos); + ocelot_vcap_filter_del_aux_resources(ocelot, tmp); + list_del(&tmp->list); kfree(tmp); } } @@ -1401,6 +1430,7 @@ int ocelot_vcap_init(struct ocelot *ocelot) } INIT_LIST_HEAD(&ocelot->dummy_rules); + INIT_LIST_HEAD(&ocelot->traps); INIT_LIST_HEAD(&ocelot->vcap_pol.pol_list); return 0; diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile index 9cff3d48acbc..9c0861d03634 100644 --- a/drivers/net/ethernet/netronome/nfp/Makefile +++ b/drivers/net/ethernet/netronome/nfp/Makefile @@ -5,6 +5,7 @@ nfp-objs := \ nfpcore/nfp6000_pcie.o \ nfpcore/nfp_cppcore.o \ nfpcore/nfp_cpplib.o \ + nfpcore/nfp_dev.o \ nfpcore/nfp_hwinfo.o \ nfpcore/nfp_mip.o \ nfpcore/nfp_mutex.o \ @@ -19,18 +20,25 @@ nfp-objs := \ ccm_mbox.o \ devlink_param.o \ nfp_asm.o \ + nfd3/dp.o \ + nfd3/rings.o \ + nfd3/xsk.o \ + nfdk/dp.o \ + nfdk/rings.o \ nfp_app.o \ nfp_app_nic.o \ nfp_devlink.o \ nfp_hwmon.o \ nfp_main.o \ nfp_net_common.o \ + nfp_net_dp.o \ nfp_net_ctrl.o \ nfp_net_debugdump.o \ nfp_net_ethtool.o \ nfp_net_main.o \ nfp_net_repr.o \ nfp_net_sriov.o \ + nfp_net_xsk.o \ nfp_netvf_main.o \ nfp_port.o \ nfp_shared_buf.o \ diff --git a/drivers/net/ethernet/netronome/nfp/flower/action.c b/drivers/net/ethernet/netronome/nfp/flower/action.c index a3242b36e216..1b9421e844a9 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/action.c +++ b/drivers/net/ethernet/netronome/nfp/flower/action.c @@ -922,6 +922,51 @@ nfp_fl_pedit(const struct flow_action_entry *act, } } +static struct nfp_fl_meter *nfp_fl_meter(char *act_data) +{ + size_t act_size = sizeof(struct nfp_fl_meter); + struct nfp_fl_meter *meter_act; + + meter_act = (struct nfp_fl_meter *)act_data; + + memset(meter_act, 0, act_size); + + meter_act->head.jump_id = NFP_FL_ACTION_OPCODE_METER; + meter_act->head.len_lw = act_size >> NFP_FL_LW_SIZ; + + return meter_act; +} + +static int +nfp_flower_meter_action(struct nfp_app *app, + const struct flow_action_entry *action, + struct nfp_fl_payload *nfp_fl, int *a_len, + struct net_device *netdev, + struct netlink_ext_ack *extack) +{ + struct nfp_fl_meter *fl_meter; + u32 meter_id; + + if (*a_len + sizeof(struct nfp_fl_meter) > NFP_FL_MAX_A_SIZ) { + NL_SET_ERR_MSG_MOD(extack, + "unsupported offload:meter action size beyond the allowed maximum"); + return -EOPNOTSUPP; + } + + meter_id = action->hw_index; + if (!nfp_flower_search_meter_entry(app, meter_id)) { + NL_SET_ERR_MSG_MOD(extack, + "can not offload flow table with unsupported police action."); + return -EOPNOTSUPP; + } + + fl_meter = nfp_fl_meter(&nfp_fl->action_data[*a_len]); + *a_len += sizeof(struct nfp_fl_meter); + fl_meter->meter_id = cpu_to_be32(meter_id); + + return 0; +} + static int nfp_flower_output_action(struct nfp_app *app, const struct flow_action_entry *act, @@ -985,6 +1030,7 @@ nfp_flower_loop_action(struct nfp_app *app, const struct flow_action_entry *act, struct nfp_flower_pedit_acts *set_act, bool *pkt_host, struct netlink_ext_ack *extack, int act_idx) { + struct nfp_flower_priv *fl_priv = app->priv; struct nfp_fl_pre_tunnel *pre_tun; struct nfp_fl_set_tun *set_tun; struct nfp_fl_push_vlan *psh_v; @@ -1149,6 +1195,18 @@ nfp_flower_loop_action(struct nfp_app *app, const struct flow_action_entry *act, *pkt_host = true; break; + case FLOW_ACTION_POLICE: + if (!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_METER)) { + NL_SET_ERR_MSG_MOD(extack, + "unsupported offload: unsupported police action in action list"); + return -EOPNOTSUPP; + } + + err = nfp_flower_meter_action(app, act, nfp_fl, a_len, netdev, + extack); + if (err) + return err; + break; default: /* Currently we do not handle any other actions. */ NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported action in action list"); diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h index 1543e47456d5..68e8a2fb1a29 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h @@ -85,6 +85,7 @@ #define NFP_FL_ACTION_OPCODE_SET_TCP 15 #define NFP_FL_ACTION_OPCODE_PRE_LAG 16 #define NFP_FL_ACTION_OPCODE_PRE_TUNNEL 17 +#define NFP_FL_ACTION_OPCODE_METER 24 #define NFP_FL_ACTION_OPCODE_PUSH_GENEVE 26 #define NFP_FL_ACTION_OPCODE_NUM 32 @@ -260,6 +261,12 @@ struct nfp_fl_set_mpls { __be32 lse; }; +struct nfp_fl_meter { + struct nfp_fl_act_head head; + __be16 reserved; + __be32 meter_id; +}; + /* Metadata with L2 (1W/4B) * ---------------------------------------------------------------- * 3 2 1 diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c index ac1dcfa1d179..4d960a9641b3 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.c +++ b/drivers/net/ethernet/netronome/nfp/flower/main.c @@ -266,7 +266,7 @@ nfp_flower_reprs_reify(struct nfp_app *app, enum nfp_repr_type type, int i, err, count = 0; reprs = rcu_dereference_protected(app->reprs[type], - lockdep_is_held(&app->pf->lock)); + nfp_app_is_locked(app)); if (!reprs) return 0; @@ -295,7 +295,7 @@ nfp_flower_wait_repr_reify(struct nfp_app *app, atomic_t *replies, int tot_repl) if (!tot_repl) return 0; - lockdep_assert_held(&app->pf->lock); + assert_nfp_app_locked(app); if (!wait_event_timeout(priv->reify_wait_queue, atomic_read(replies) >= tot_repl, NFP_FL_REPLY_TIMEOUT)) { diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 917c450a7aad..fa902ce2dd82 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -12,7 +12,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -48,6 +50,7 @@ struct nfp_app; #define NFP_FL_FEATS_IPV6_TUN BIT(7) #define NFP_FL_FEATS_VLAN_QINQ BIT(8) #define NFP_FL_FEATS_QOS_PPS BIT(9) +#define NFP_FL_FEATS_QOS_METER BIT(10) #define NFP_FL_FEATS_HOST_ACK BIT(31) #define NFP_FL_ENABLE_FLOW_MERGE BIT(0) @@ -63,7 +66,8 @@ struct nfp_app; NFP_FL_FEATS_PRE_TUN_RULES | \ NFP_FL_FEATS_IPV6_TUN | \ NFP_FL_FEATS_VLAN_QINQ | \ - NFP_FL_FEATS_QOS_PPS) + NFP_FL_FEATS_QOS_PPS | \ + NFP_FL_FEATS_QOS_METER) struct nfp_fl_mask_id { struct circ_buf mask_id_free_list; @@ -191,6 +195,8 @@ struct nfp_fl_internal_ports { * @qos_stats_work: Workqueue for qos stats processing * @qos_rate_limiters: Current active qos rate limiters * @qos_stats_lock: Lock on qos stats updates + * @meter_stats_lock: Lock on meter stats updates + * @meter_table: Hash table used to store the meter table * @pre_tun_rule_cnt: Number of pre-tunnel rules offloaded * @merge_table: Hash table to store merged flows * @ct_zone_table: Hash table used to store the different zones @@ -228,6 +234,8 @@ struct nfp_flower_priv { struct delayed_work qos_stats_work; unsigned int qos_rate_limiters; spinlock_t qos_stats_lock; /* Protect the qos stats */ + struct mutex meter_stats_lock; /* Protect the meter stats */ + struct rhashtable meter_table; int pre_tun_rule_cnt; struct rhashtable merge_table; struct rhashtable ct_zone_table; @@ -374,6 +382,31 @@ struct nfp_fl_stats_frame { __be64 stats_cookie; }; +struct nfp_meter_stats_entry { + u64 pkts; + u64 bytes; + u64 drops; +}; + +struct nfp_meter_entry { + struct rhash_head ht_node; + u32 meter_id; + bool bps; + u32 rate; + u32 burst; + u64 used; + struct nfp_meter_stats { + u64 update; + struct nfp_meter_stats_entry curr; + struct nfp_meter_stats_entry prev; + } stats; +}; + +enum nfp_meter_op { + NFP_METER_ADD, + NFP_METER_DEL, +}; + static inline bool nfp_flower_internal_port_can_offload(struct nfp_app *app, struct net_device *netdev) @@ -569,4 +602,18 @@ nfp_flower_xmit_flow(struct nfp_app *app, struct nfp_fl_payload *nfp_flow, void nfp_flower_update_merge_stats(struct nfp_app *app, struct nfp_fl_payload *sub_flow); + +int nfp_setup_tc_act_offload(struct nfp_app *app, + struct flow_offload_action *fl_act); +int nfp_init_meter_table(struct nfp_app *app); +void nfp_flower_stats_meter_request_all(struct nfp_flower_priv *fl_priv); +void nfp_act_stats_reply(struct nfp_app *app, void *pmsg); +int nfp_flower_offload_one_police(struct nfp_app *app, bool ingress, + bool pps, u32 id, u32 rate, u32 burst); +int nfp_flower_setup_meter_entry(struct nfp_app *app, + const struct flow_action_entry *action, + enum nfp_meter_op op, + u32 meter_id); +struct nfp_meter_entry * +nfp_flower_search_meter_entry(struct nfp_app *app, u32 meter_id); #endif diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index f97eff5afd12..92e8ade4854e 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -1861,6 +1861,20 @@ nfp_flower_setup_indr_tc_block(struct net_device *netdev, struct Qdisc *sch, str return 0; } +static int +nfp_setup_tc_no_dev(struct nfp_app *app, enum tc_setup_type type, void *data) +{ + if (!data) + return -EOPNOTSUPP; + + switch (type) { + case TC_SETUP_ACT: + return nfp_setup_tc_act_offload(app, data); + default: + return -EOPNOTSUPP; + } +} + int nfp_flower_indr_setup_tc_cb(struct net_device *netdev, struct Qdisc *sch, void *cb_priv, enum tc_setup_type type, void *type_data, @@ -1868,7 +1882,7 @@ nfp_flower_indr_setup_tc_cb(struct net_device *netdev, struct Qdisc *sch, void * void (*cleanup)(struct flow_block_cb *block_cb)) { if (!netdev) - return -EOPNOTSUPP; + return nfp_setup_tc_no_dev(cb_priv, type, data); if (!nfp_fl_is_netdev_to_offload(netdev)) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c b/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c index 784c6dbf8bc4..3206ba83b1aa 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c @@ -1,7 +1,11 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (C) 2019 Netronome Systems, Inc. */ +#include +#include +#include #include +#include #include #include @@ -11,10 +15,14 @@ #define NFP_FL_QOS_UPDATE msecs_to_jiffies(1000) #define NFP_FL_QOS_PPS BIT(15) +#define NFP_FL_QOS_METER BIT(10) struct nfp_police_cfg_head { __be32 flags_opts; - __be32 port; + union { + __be32 meter_id; + __be32 port; + }; }; enum NFP_FL_QOS_TYPES { @@ -46,7 +54,15 @@ enum NFP_FL_QOS_TYPES { * | Committed Information Rate | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * Word[0](FLag options): - * [15] p(pps) 1 for pps ,0 for bps + * [15] p(pps) 1 for pps, 0 for bps + * + * Meter control message + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-------------------------------+-+---+-----+-+---------+-+---+-+ + * | Reserved |p| Y |TYPE |E|TSHFV |P| PC|R| + * +-------------------------------+-+---+-----+-+---------+-+---+-+ + * | meter ID | + * +-------------------------------+-------------------------------+ * */ struct nfp_police_config { @@ -67,6 +83,74 @@ struct nfp_police_stats_reply { __be64 drop_pkts; }; +int nfp_flower_offload_one_police(struct nfp_app *app, bool ingress, + bool pps, u32 id, u32 rate, u32 burst) +{ + struct nfp_police_config *config; + struct sk_buff *skb; + + skb = nfp_flower_cmsg_alloc(app, sizeof(struct nfp_police_config), + NFP_FLOWER_CMSG_TYPE_QOS_MOD, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + config = nfp_flower_cmsg_get_data(skb); + memset(config, 0, sizeof(struct nfp_police_config)); + if (pps) + config->head.flags_opts |= cpu_to_be32(NFP_FL_QOS_PPS); + if (!ingress) + config->head.flags_opts |= cpu_to_be32(NFP_FL_QOS_METER); + + if (ingress) + config->head.port = cpu_to_be32(id); + else + config->head.meter_id = cpu_to_be32(id); + + config->bkt_tkn_p = cpu_to_be32(burst); + config->bkt_tkn_c = cpu_to_be32(burst); + config->pbs = cpu_to_be32(burst); + config->cbs = cpu_to_be32(burst); + config->pir = cpu_to_be32(rate); + config->cir = cpu_to_be32(rate); + nfp_ctrl_tx(app->ctrl, skb); + + return 0; +} + +static int nfp_policer_validate(const struct flow_action *action, + const struct flow_action_entry *act, + struct netlink_ext_ack *extack) +{ + if (act->police.exceed.act_id != FLOW_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when exceed action is not drop"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && + act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not pipe or ok"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && + !flow_action_is_last_entry(action, act)) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is ok, but action is not last"); + return -EOPNOTSUPP; + } + + if (act->police.peakrate_bytes_ps || + act->police.avrate || act->police.overhead) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when peakrate/avrate/overhead is configured"); + return -EOPNOTSUPP; + } + + return 0; +} + static int nfp_flower_install_rate_limiter(struct nfp_app *app, struct net_device *netdev, struct tc_cls_matchall_offload *flow, @@ -77,15 +161,15 @@ nfp_flower_install_rate_limiter(struct nfp_app *app, struct net_device *netdev, struct nfp_flower_priv *fl_priv = app->priv; struct flow_action_entry *action = NULL; struct nfp_flower_repr_priv *repr_priv; - struct nfp_police_config *config; u32 netdev_port_id, i; struct nfp_repr *repr; - struct sk_buff *skb; bool pps_support; u32 bps_num = 0; u32 pps_num = 0; u32 burst; + bool pps; u64 rate; + int err; if (!nfp_netdev_is_nfp_repr(netdev)) { NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on higher level port"); @@ -132,6 +216,11 @@ nfp_flower_install_rate_limiter(struct nfp_app *app, struct net_device *netdev, "unsupported offload: qos rate limit offload requires police action"); return -EOPNOTSUPP; } + + err = nfp_policer_validate(&flow->rule->action, action, extack); + if (err) + return err; + if (action->police.rate_bytes_ps > 0) { if (bps_num++) { NL_SET_ERR_MSG_MOD(extack, @@ -169,23 +258,12 @@ nfp_flower_install_rate_limiter(struct nfp_app *app, struct net_device *netdev, } if (rate != 0) { - skb = nfp_flower_cmsg_alloc(repr->app, sizeof(struct nfp_police_config), - NFP_FLOWER_CMSG_TYPE_QOS_MOD, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - config = nfp_flower_cmsg_get_data(skb); - memset(config, 0, sizeof(struct nfp_police_config)); + pps = false; if (action->police.rate_pkt_ps > 0) - config->head.flags_opts = cpu_to_be32(NFP_FL_QOS_PPS); - config->head.port = cpu_to_be32(netdev_port_id); - config->bkt_tkn_p = cpu_to_be32(burst); - config->bkt_tkn_c = cpu_to_be32(burst); - config->pbs = cpu_to_be32(burst); - config->cbs = cpu_to_be32(burst); - config->pir = cpu_to_be32(rate); - config->cir = cpu_to_be32(rate); - nfp_ctrl_tx(repr->app->ctrl, skb); + pps = true; + nfp_flower_offload_one_police(repr->app, true, + pps, netdev_port_id, + rate, burst); } } repr_priv->qos_table.netdev_port_id = netdev_port_id; @@ -266,6 +344,9 @@ void nfp_flower_stats_rlim_reply(struct nfp_app *app, struct sk_buff *skb) u32 netdev_port_id; msg = nfp_flower_cmsg_get_data(skb); + if (be32_to_cpu(msg->head.flags_opts) & NFP_FL_QOS_METER) + return nfp_act_stats_reply(app, msg); + netdev_port_id = be32_to_cpu(msg->head.port); rcu_read_lock(); netdev = nfp_app_dev_get(app, netdev_port_id, NULL); @@ -297,7 +378,7 @@ void nfp_flower_stats_rlim_reply(struct nfp_app *app, struct sk_buff *skb) static void nfp_flower_stats_rlim_request(struct nfp_flower_priv *fl_priv, - u32 netdev_port_id) + u32 id, bool ingress) { struct nfp_police_cfg_head *head; struct sk_buff *skb; @@ -308,10 +389,15 @@ nfp_flower_stats_rlim_request(struct nfp_flower_priv *fl_priv, GFP_ATOMIC); if (!skb) return; - head = nfp_flower_cmsg_get_data(skb); + memset(head, 0, sizeof(struct nfp_police_cfg_head)); - head->port = cpu_to_be32(netdev_port_id); + if (ingress) { + head->port = cpu_to_be32(id); + } else { + head->flags_opts = cpu_to_be32(NFP_FL_QOS_METER); + head->meter_id = cpu_to_be32(id); + } nfp_ctrl_tx(fl_priv->app->ctrl, skb); } @@ -341,7 +427,8 @@ nfp_flower_stats_rlim_request_all(struct nfp_flower_priv *fl_priv) if (!netdev_port_id) continue; - nfp_flower_stats_rlim_request(fl_priv, netdev_port_id); + nfp_flower_stats_rlim_request(fl_priv, + netdev_port_id, true); } } @@ -359,6 +446,8 @@ static void update_stats_cache(struct work_struct *work) qos_stats_work); nfp_flower_stats_rlim_request_all(fl_priv); + nfp_flower_stats_meter_request_all(fl_priv); + schedule_delayed_work(&fl_priv->qos_stats_work, NFP_FL_QOS_UPDATE); } @@ -406,6 +495,9 @@ void nfp_flower_qos_init(struct nfp_app *app) struct nfp_flower_priv *fl_priv = app->priv; spin_lock_init(&fl_priv->qos_stats_lock); + mutex_init(&fl_priv->meter_stats_lock); + nfp_init_meter_table(app); + INIT_DELAYED_WORK(&fl_priv->qos_stats_work, &update_stats_cache); } @@ -441,3 +533,333 @@ int nfp_flower_setup_qos_offload(struct nfp_app *app, struct net_device *netdev, return -EOPNOTSUPP; } } + +/* offload tc action, currently only for tc police */ + +static const struct rhashtable_params stats_meter_table_params = { + .key_offset = offsetof(struct nfp_meter_entry, meter_id), + .head_offset = offsetof(struct nfp_meter_entry, ht_node), + .key_len = sizeof(u32), +}; + +struct nfp_meter_entry * +nfp_flower_search_meter_entry(struct nfp_app *app, u32 meter_id) +{ + struct nfp_flower_priv *priv = app->priv; + + return rhashtable_lookup_fast(&priv->meter_table, &meter_id, + stats_meter_table_params); +} + +static struct nfp_meter_entry * +nfp_flower_add_meter_entry(struct nfp_app *app, u32 meter_id) +{ + struct nfp_meter_entry *meter_entry = NULL; + struct nfp_flower_priv *priv = app->priv; + + meter_entry = rhashtable_lookup_fast(&priv->meter_table, + &meter_id, + stats_meter_table_params); + if (meter_entry) + return meter_entry; + + meter_entry = kzalloc(sizeof(*meter_entry), GFP_KERNEL); + if (!meter_entry) + return NULL; + + meter_entry->meter_id = meter_id; + meter_entry->used = jiffies; + if (rhashtable_insert_fast(&priv->meter_table, &meter_entry->ht_node, + stats_meter_table_params)) { + kfree(meter_entry); + return NULL; + } + + priv->qos_rate_limiters++; + if (priv->qos_rate_limiters == 1) + schedule_delayed_work(&priv->qos_stats_work, + NFP_FL_QOS_UPDATE); + + return meter_entry; +} + +static void nfp_flower_del_meter_entry(struct nfp_app *app, u32 meter_id) +{ + struct nfp_meter_entry *meter_entry = NULL; + struct nfp_flower_priv *priv = app->priv; + + meter_entry = rhashtable_lookup_fast(&priv->meter_table, &meter_id, + stats_meter_table_params); + if (!meter_entry) + return; + + rhashtable_remove_fast(&priv->meter_table, + &meter_entry->ht_node, + stats_meter_table_params); + kfree(meter_entry); + priv->qos_rate_limiters--; + if (!priv->qos_rate_limiters) + cancel_delayed_work_sync(&priv->qos_stats_work); +} + +int nfp_flower_setup_meter_entry(struct nfp_app *app, + const struct flow_action_entry *action, + enum nfp_meter_op op, + u32 meter_id) +{ + struct nfp_flower_priv *fl_priv = app->priv; + struct nfp_meter_entry *meter_entry = NULL; + int err = 0; + + mutex_lock(&fl_priv->meter_stats_lock); + + switch (op) { + case NFP_METER_DEL: + nfp_flower_del_meter_entry(app, meter_id); + goto exit_unlock; + case NFP_METER_ADD: + meter_entry = nfp_flower_add_meter_entry(app, meter_id); + break; + default: + err = -EOPNOTSUPP; + goto exit_unlock; + } + + if (!meter_entry) { + err = -ENOMEM; + goto exit_unlock; + } + + if (action->police.rate_bytes_ps > 0) { + meter_entry->bps = true; + meter_entry->rate = action->police.rate_bytes_ps; + meter_entry->burst = action->police.burst; + } else { + meter_entry->bps = false; + meter_entry->rate = action->police.rate_pkt_ps; + meter_entry->burst = action->police.burst_pkt; + } + +exit_unlock: + mutex_unlock(&fl_priv->meter_stats_lock); + return err; +} + +int nfp_init_meter_table(struct nfp_app *app) +{ + struct nfp_flower_priv *priv = app->priv; + + return rhashtable_init(&priv->meter_table, &stats_meter_table_params); +} + +void +nfp_flower_stats_meter_request_all(struct nfp_flower_priv *fl_priv) +{ + struct nfp_meter_entry *meter_entry = NULL; + struct rhashtable_iter iter; + + mutex_lock(&fl_priv->meter_stats_lock); + rhashtable_walk_enter(&fl_priv->meter_table, &iter); + rhashtable_walk_start(&iter); + + while ((meter_entry = rhashtable_walk_next(&iter)) != NULL) { + if (IS_ERR(meter_entry)) + continue; + nfp_flower_stats_rlim_request(fl_priv, + meter_entry->meter_id, false); + } + + rhashtable_walk_stop(&iter); + rhashtable_walk_exit(&iter); + mutex_unlock(&fl_priv->meter_stats_lock); +} + +static int +nfp_act_install_actions(struct nfp_app *app, struct flow_offload_action *fl_act, + struct netlink_ext_ack *extack) +{ + struct flow_action_entry *paction = &fl_act->action.entries[0]; + u32 action_num = fl_act->action.num_entries; + struct nfp_flower_priv *fl_priv = app->priv; + struct flow_action_entry *action = NULL; + u32 burst, i, meter_id; + bool pps_support, pps; + bool add = false; + u64 rate; + + pps_support = !!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_PPS); + + for (i = 0 ; i < action_num; i++) { + /*set qos associate data for this interface */ + action = paction + i; + if (action->id != FLOW_ACTION_POLICE) { + NL_SET_ERR_MSG_MOD(extack, + "unsupported offload: qos rate limit offload requires police action"); + continue; + } + if (action->police.rate_bytes_ps > 0) { + rate = action->police.rate_bytes_ps; + burst = action->police.burst; + } else if (action->police.rate_pkt_ps > 0 && pps_support) { + rate = action->police.rate_pkt_ps; + burst = action->police.burst_pkt; + } else { + NL_SET_ERR_MSG_MOD(extack, + "unsupported offload: unsupported qos rate limit"); + continue; + } + + if (rate != 0) { + meter_id = action->hw_index; + if (nfp_flower_setup_meter_entry(app, action, NFP_METER_ADD, meter_id)) + continue; + + pps = false; + if (action->police.rate_pkt_ps > 0) + pps = true; + nfp_flower_offload_one_police(app, false, pps, meter_id, + rate, burst); + add = true; + } + } + + return add ? 0 : -EOPNOTSUPP; +} + +static int +nfp_act_remove_actions(struct nfp_app *app, struct flow_offload_action *fl_act, + struct netlink_ext_ack *extack) +{ + struct nfp_meter_entry *meter_entry = NULL; + struct nfp_police_config *config; + struct sk_buff *skb; + u32 meter_id; + bool pps; + + /*delete qos associate data for this interface */ + if (fl_act->id != FLOW_ACTION_POLICE) { + NL_SET_ERR_MSG_MOD(extack, + "unsupported offload: qos rate limit offload requires police action"); + return -EOPNOTSUPP; + } + + meter_id = fl_act->index; + meter_entry = nfp_flower_search_meter_entry(app, meter_id); + if (!meter_entry) { + NL_SET_ERR_MSG_MOD(extack, + "no meter entry when delete the action index."); + return -ENOENT; + } + pps = !meter_entry->bps; + + skb = nfp_flower_cmsg_alloc(app, sizeof(struct nfp_police_config), + NFP_FLOWER_CMSG_TYPE_QOS_DEL, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + config = nfp_flower_cmsg_get_data(skb); + memset(config, 0, sizeof(struct nfp_police_config)); + config->head.flags_opts = cpu_to_be32(NFP_FL_QOS_METER); + config->head.meter_id = cpu_to_be32(meter_id); + if (pps) + config->head.flags_opts |= cpu_to_be32(NFP_FL_QOS_PPS); + + nfp_ctrl_tx(app->ctrl, skb); + nfp_flower_setup_meter_entry(app, NULL, NFP_METER_DEL, meter_id); + + return 0; +} + +void +nfp_act_stats_reply(struct nfp_app *app, void *pmsg) +{ + struct nfp_flower_priv *fl_priv = app->priv; + struct nfp_meter_entry *meter_entry = NULL; + struct nfp_police_stats_reply *msg = pmsg; + u32 meter_id; + + meter_id = be32_to_cpu(msg->head.meter_id); + mutex_lock(&fl_priv->meter_stats_lock); + + meter_entry = nfp_flower_search_meter_entry(app, meter_id); + if (!meter_entry) + goto exit_unlock; + + meter_entry->stats.curr.pkts = be64_to_cpu(msg->pass_pkts) + + be64_to_cpu(msg->drop_pkts); + meter_entry->stats.curr.bytes = be64_to_cpu(msg->pass_bytes) + + be64_to_cpu(msg->drop_bytes); + meter_entry->stats.curr.drops = be64_to_cpu(msg->drop_pkts); + if (!meter_entry->stats.update) { + meter_entry->stats.prev.pkts = meter_entry->stats.curr.pkts; + meter_entry->stats.prev.bytes = meter_entry->stats.curr.bytes; + meter_entry->stats.prev.drops = meter_entry->stats.curr.drops; + } + + meter_entry->stats.update = jiffies; + +exit_unlock: + mutex_unlock(&fl_priv->meter_stats_lock); +} + +static int +nfp_act_stats_actions(struct nfp_app *app, struct flow_offload_action *fl_act, + struct netlink_ext_ack *extack) +{ + struct nfp_flower_priv *fl_priv = app->priv; + struct nfp_meter_entry *meter_entry = NULL; + u64 diff_bytes, diff_pkts, diff_drops; + int err = 0; + + if (fl_act->id != FLOW_ACTION_POLICE) { + NL_SET_ERR_MSG_MOD(extack, + "unsupported offload: qos rate limit offload requires police action"); + return -EOPNOTSUPP; + } + + mutex_lock(&fl_priv->meter_stats_lock); + meter_entry = nfp_flower_search_meter_entry(app, fl_act->index); + if (!meter_entry) { + err = -ENOENT; + goto exit_unlock; + } + diff_pkts = meter_entry->stats.curr.pkts > meter_entry->stats.prev.pkts ? + meter_entry->stats.curr.pkts - meter_entry->stats.prev.pkts : 0; + diff_bytes = meter_entry->stats.curr.bytes > meter_entry->stats.prev.bytes ? + meter_entry->stats.curr.bytes - meter_entry->stats.prev.bytes : 0; + diff_drops = meter_entry->stats.curr.drops > meter_entry->stats.prev.drops ? + meter_entry->stats.curr.drops - meter_entry->stats.prev.drops : 0; + + flow_stats_update(&fl_act->stats, diff_bytes, diff_pkts, diff_drops, + meter_entry->stats.update, + FLOW_ACTION_HW_STATS_DELAYED); + + meter_entry->stats.prev.pkts = meter_entry->stats.curr.pkts; + meter_entry->stats.prev.bytes = meter_entry->stats.curr.bytes; + meter_entry->stats.prev.drops = meter_entry->stats.curr.drops; + +exit_unlock: + mutex_unlock(&fl_priv->meter_stats_lock); + return err; +} + +int nfp_setup_tc_act_offload(struct nfp_app *app, + struct flow_offload_action *fl_act) +{ + struct netlink_ext_ack *extack = fl_act->extack; + struct nfp_flower_priv *fl_priv = app->priv; + + if (!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_METER)) + return -EOPNOTSUPP; + + switch (fl_act->command) { + case FLOW_ACT_REPLACE: + return nfp_act_install_actions(app, fl_act, extack); + case FLOW_ACT_DESTROY: + return nfp_act_remove_actions(app, fl_act, extack); + case FLOW_ACT_STATS: + return nfp_act_stats_actions(app, fl_act, extack); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c index cb43651ea9ba..c71bd555f482 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c @@ -356,7 +356,7 @@ __nfp_tun_add_route_to_cache(struct list_head *route_list, return 0; } - entry = kmalloc(sizeof(*entry) + add_len, GFP_ATOMIC); + entry = kmalloc(struct_size(entry, ip_add, add_len), GFP_ATOMIC); if (!entry) { spin_unlock_bh(list_lock); return -ENOMEM; @@ -942,8 +942,8 @@ nfp_tunnel_add_shared_mac(struct nfp_app *app, struct net_device *netdev, if (!nfp_mac_idx) { /* Assign a global index if non-repr or MAC is now shared. */ if (entry || !port) { - ida_idx = ida_simple_get(&priv->tun.mac_off_ids, 0, - NFP_MAX_MAC_INDEX, GFP_KERNEL); + ida_idx = ida_alloc_max(&priv->tun.mac_off_ids, + NFP_MAX_MAC_INDEX, GFP_KERNEL); if (ida_idx < 0) return ida_idx; @@ -998,7 +998,7 @@ nfp_tunnel_add_shared_mac(struct nfp_app *app, struct net_device *netdev, kfree(entry); err_free_ida: if (ida_idx != -1) - ida_simple_remove(&priv->tun.mac_off_ids, ida_idx); + ida_free(&priv->tun.mac_off_ids, ida_idx); return err; } @@ -1061,7 +1061,7 @@ nfp_tunnel_del_shared_mac(struct nfp_app *app, struct net_device *netdev, } ida_idx = nfp_tunnel_get_ida_from_global_mac_idx(entry->index); - ida_simple_remove(&priv->tun.mac_off_ids, ida_idx); + ida_free(&priv->tun.mac_off_ids, ida_idx); entry->index = nfp_mac_idx; return 0; } @@ -1081,7 +1081,7 @@ nfp_tunnel_del_shared_mac(struct nfp_app *app, struct net_device *netdev, /* If MAC has global ID then extract and free the ida entry. */ if (nfp_tunnel_is_mac_idx_global(nfp_mac_idx)) { ida_idx = nfp_tunnel_get_ida_from_global_mac_idx(entry->index); - ida_simple_remove(&priv->tun.mac_off_ids, ida_idx); + ida_free(&priv->tun.mac_off_ids, ida_idx); } kfree(entry); diff --git a/drivers/net/ethernet/netronome/nfp/nfd3/dp.c b/drivers/net/ethernet/netronome/nfp/nfd3/dp.c new file mode 100644 index 000000000000..7db56abaa582 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfd3/dp.c @@ -0,0 +1,1350 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (C) 2015-2019 Netronome Systems, Inc. */ + +#include +#include + +#include "../nfp_app.h" +#include "../nfp_net.h" +#include "../nfp_net_dp.h" +#include "../nfp_net_xsk.h" +#include "../crypto/crypto.h" +#include "../crypto/fw.h" +#include "nfd3.h" + +/* Transmit processing + * + * One queue controller peripheral queue is used for transmit. The + * driver en-queues packets for transmit by advancing the write + * pointer. The device indicates that packets have transmitted by + * advancing the read pointer. The driver maintains a local copy of + * the read and write pointer in @struct nfp_net_tx_ring. The driver + * keeps @wr_p in sync with the queue controller write pointer and can + * determine how many packets have been transmitted by comparing its + * copy of the read pointer @rd_p with the read pointer maintained by + * the queue controller peripheral. + */ + +/* Wrappers for deciding when to stop and restart TX queues */ +static int nfp_nfd3_tx_ring_should_wake(struct nfp_net_tx_ring *tx_ring) +{ + return !nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS * 4); +} + +static int nfp_nfd3_tx_ring_should_stop(struct nfp_net_tx_ring *tx_ring) +{ + return nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS + 1); +} + +/** + * nfp_nfd3_tx_ring_stop() - stop tx ring + * @nd_q: netdev queue + * @tx_ring: driver tx queue structure + * + * Safely stop TX ring. Remember that while we are running .start_xmit() + * someone else may be cleaning the TX ring completions so we need to be + * extra careful here. + */ +static void +nfp_nfd3_tx_ring_stop(struct netdev_queue *nd_q, + struct nfp_net_tx_ring *tx_ring) +{ + netif_tx_stop_queue(nd_q); + + /* We can race with the TX completion out of NAPI so recheck */ + smp_mb(); + if (unlikely(nfp_nfd3_tx_ring_should_wake(tx_ring))) + netif_tx_start_queue(nd_q); +} + +/** + * nfp_nfd3_tx_tso() - Set up Tx descriptor for LSO + * @r_vec: per-ring structure + * @txbuf: Pointer to driver soft TX descriptor + * @txd: Pointer to HW TX descriptor + * @skb: Pointer to SKB + * @md_bytes: Prepend length + * + * Set up Tx descriptor for LSO, do nothing for non-LSO skbs. + * Return error on packet header greater than maximum supported LSO header size. + */ +static void +nfp_nfd3_tx_tso(struct nfp_net_r_vector *r_vec, struct nfp_nfd3_tx_buf *txbuf, + struct nfp_nfd3_tx_desc *txd, struct sk_buff *skb, u32 md_bytes) +{ + u32 l3_offset, l4_offset, hdrlen; + u16 mss; + + if (!skb_is_gso(skb)) + return; + + if (!skb->encapsulation) { + l3_offset = skb_network_offset(skb); + l4_offset = skb_transport_offset(skb); + hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb); + } else { + l3_offset = skb_inner_network_offset(skb); + l4_offset = skb_inner_transport_offset(skb); + hdrlen = skb_inner_transport_header(skb) - skb->data + + inner_tcp_hdrlen(skb); + } + + txbuf->pkt_cnt = skb_shinfo(skb)->gso_segs; + txbuf->real_len += hdrlen * (txbuf->pkt_cnt - 1); + + mss = skb_shinfo(skb)->gso_size & NFD3_DESC_TX_MSS_MASK; + txd->l3_offset = l3_offset - md_bytes; + txd->l4_offset = l4_offset - md_bytes; + txd->lso_hdrlen = hdrlen - md_bytes; + txd->mss = cpu_to_le16(mss); + txd->flags |= NFD3_DESC_TX_LSO; + + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_lso++; + u64_stats_update_end(&r_vec->tx_sync); +} + +/** + * nfp_nfd3_tx_csum() - Set TX CSUM offload flags in TX descriptor + * @dp: NFP Net data path struct + * @r_vec: per-ring structure + * @txbuf: Pointer to driver soft TX descriptor + * @txd: Pointer to TX descriptor + * @skb: Pointer to SKB + * + * This function sets the TX checksum flags in the TX descriptor based + * on the configuration and the protocol of the packet to be transmitted. + */ +static void +nfp_nfd3_tx_csum(struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, + struct nfp_nfd3_tx_buf *txbuf, struct nfp_nfd3_tx_desc *txd, + struct sk_buff *skb) +{ + struct ipv6hdr *ipv6h; + struct iphdr *iph; + u8 l4_hdr; + + if (!(dp->ctrl & NFP_NET_CFG_CTRL_TXCSUM)) + return; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + return; + + txd->flags |= NFD3_DESC_TX_CSUM; + if (skb->encapsulation) + txd->flags |= NFD3_DESC_TX_ENCAP; + + iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); + ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb); + + if (iph->version == 4) { + txd->flags |= NFD3_DESC_TX_IP4_CSUM; + l4_hdr = iph->protocol; + } else if (ipv6h->version == 6) { + l4_hdr = ipv6h->nexthdr; + } else { + nn_dp_warn(dp, "partial checksum but ipv=%x!\n", iph->version); + return; + } + + switch (l4_hdr) { + case IPPROTO_TCP: + txd->flags |= NFD3_DESC_TX_TCP_CSUM; + break; + case IPPROTO_UDP: + txd->flags |= NFD3_DESC_TX_UDP_CSUM; + break; + default: + nn_dp_warn(dp, "partial checksum but l4 proto=%x!\n", l4_hdr); + return; + } + + u64_stats_update_begin(&r_vec->tx_sync); + if (skb->encapsulation) + r_vec->hw_csum_tx_inner += txbuf->pkt_cnt; + else + r_vec->hw_csum_tx += txbuf->pkt_cnt; + u64_stats_update_end(&r_vec->tx_sync); +} + +static int nfp_nfd3_prep_tx_meta(struct sk_buff *skb, u64 tls_handle) +{ + struct metadata_dst *md_dst = skb_metadata_dst(skb); + unsigned char *data; + u32 meta_id = 0; + int md_bytes; + + if (likely(!md_dst && !tls_handle)) + return 0; + if (unlikely(md_dst && md_dst->type != METADATA_HW_PORT_MUX)) { + if (!tls_handle) + return 0; + md_dst = NULL; + } + + md_bytes = 4 + !!md_dst * 4 + !!tls_handle * 8; + + if (unlikely(skb_cow_head(skb, md_bytes))) + return -ENOMEM; + + meta_id = 0; + data = skb_push(skb, md_bytes) + md_bytes; + if (md_dst) { + data -= 4; + put_unaligned_be32(md_dst->u.port_info.port_id, data); + meta_id = NFP_NET_META_PORTID; + } + if (tls_handle) { + /* conn handle is opaque, we just use u64 to be able to quickly + * compare it to zero + */ + data -= 8; + memcpy(data, &tls_handle, sizeof(tls_handle)); + meta_id <<= NFP_NET_META_FIELD_SIZE; + meta_id |= NFP_NET_META_CONN_HANDLE; + } + + data -= 4; + put_unaligned_be32(meta_id, data); + + return md_bytes; +} + +/** + * nfp_nfd3_tx() - Main transmit entry point + * @skb: SKB to transmit + * @netdev: netdev structure + * + * Return: NETDEV_TX_OK on success. + */ +netdev_tx_t nfp_nfd3_tx(struct sk_buff *skb, struct net_device *netdev) +{ + struct nfp_net *nn = netdev_priv(netdev); + int f, nr_frags, wr_idx, md_bytes; + struct nfp_net_tx_ring *tx_ring; + struct nfp_net_r_vector *r_vec; + struct nfp_nfd3_tx_buf *txbuf; + struct nfp_nfd3_tx_desc *txd; + struct netdev_queue *nd_q; + const skb_frag_t *frag; + struct nfp_net_dp *dp; + dma_addr_t dma_addr; + unsigned int fsize; + u64 tls_handle = 0; + u16 qidx; + + dp = &nn->dp; + qidx = skb_get_queue_mapping(skb); + tx_ring = &dp->tx_rings[qidx]; + r_vec = tx_ring->r_vec; + + nr_frags = skb_shinfo(skb)->nr_frags; + + if (unlikely(nfp_net_tx_full(tx_ring, nr_frags + 1))) { + nn_dp_warn(dp, "TX ring %d busy. wrp=%u rdp=%u\n", + qidx, tx_ring->wr_p, tx_ring->rd_p); + nd_q = netdev_get_tx_queue(dp->netdev, qidx); + netif_tx_stop_queue(nd_q); + nfp_net_tx_xmit_more_flush(tx_ring); + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_busy++; + u64_stats_update_end(&r_vec->tx_sync); + return NETDEV_TX_BUSY; + } + + skb = nfp_net_tls_tx(dp, r_vec, skb, &tls_handle, &nr_frags); + if (unlikely(!skb)) { + nfp_net_tx_xmit_more_flush(tx_ring); + return NETDEV_TX_OK; + } + + md_bytes = nfp_nfd3_prep_tx_meta(skb, tls_handle); + if (unlikely(md_bytes < 0)) + goto err_flush; + + /* Start with the head skbuf */ + dma_addr = dma_map_single(dp->dev, skb->data, skb_headlen(skb), + DMA_TO_DEVICE); + if (dma_mapping_error(dp->dev, dma_addr)) + goto err_dma_err; + + wr_idx = D_IDX(tx_ring, tx_ring->wr_p); + + /* Stash the soft descriptor of the head then initialize it */ + txbuf = &tx_ring->txbufs[wr_idx]; + txbuf->skb = skb; + txbuf->dma_addr = dma_addr; + txbuf->fidx = -1; + txbuf->pkt_cnt = 1; + txbuf->real_len = skb->len; + + /* Build TX descriptor */ + txd = &tx_ring->txds[wr_idx]; + txd->offset_eop = (nr_frags ? 0 : NFD3_DESC_TX_EOP) | md_bytes; + txd->dma_len = cpu_to_le16(skb_headlen(skb)); + nfp_desc_set_dma_addr(txd, dma_addr); + txd->data_len = cpu_to_le16(skb->len); + + txd->flags = 0; + txd->mss = 0; + txd->lso_hdrlen = 0; + + /* Do not reorder - tso may adjust pkt cnt, vlan may override fields */ + nfp_nfd3_tx_tso(r_vec, txbuf, txd, skb, md_bytes); + nfp_nfd3_tx_csum(dp, r_vec, txbuf, txd, skb); + if (skb_vlan_tag_present(skb) && dp->ctrl & NFP_NET_CFG_CTRL_TXVLAN) { + txd->flags |= NFD3_DESC_TX_VLAN; + txd->vlan = cpu_to_le16(skb_vlan_tag_get(skb)); + } + + /* Gather DMA */ + if (nr_frags > 0) { + __le64 second_half; + + /* all descs must match except for in addr, length and eop */ + second_half = txd->vals8[1]; + + for (f = 0; f < nr_frags; f++) { + frag = &skb_shinfo(skb)->frags[f]; + fsize = skb_frag_size(frag); + + dma_addr = skb_frag_dma_map(dp->dev, frag, 0, + fsize, DMA_TO_DEVICE); + if (dma_mapping_error(dp->dev, dma_addr)) + goto err_unmap; + + wr_idx = D_IDX(tx_ring, wr_idx + 1); + tx_ring->txbufs[wr_idx].skb = skb; + tx_ring->txbufs[wr_idx].dma_addr = dma_addr; + tx_ring->txbufs[wr_idx].fidx = f; + + txd = &tx_ring->txds[wr_idx]; + txd->dma_len = cpu_to_le16(fsize); + nfp_desc_set_dma_addr(txd, dma_addr); + txd->offset_eop = md_bytes | + ((f == nr_frags - 1) ? NFD3_DESC_TX_EOP : 0); + txd->vals8[1] = second_half; + } + + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_gather++; + u64_stats_update_end(&r_vec->tx_sync); + } + + skb_tx_timestamp(skb); + + nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx); + + tx_ring->wr_p += nr_frags + 1; + if (nfp_nfd3_tx_ring_should_stop(tx_ring)) + nfp_nfd3_tx_ring_stop(nd_q, tx_ring); + + tx_ring->wr_ptr_add += nr_frags + 1; + if (__netdev_tx_sent_queue(nd_q, txbuf->real_len, netdev_xmit_more())) + nfp_net_tx_xmit_more_flush(tx_ring); + + return NETDEV_TX_OK; + +err_unmap: + while (--f >= 0) { + frag = &skb_shinfo(skb)->frags[f]; + dma_unmap_page(dp->dev, tx_ring->txbufs[wr_idx].dma_addr, + skb_frag_size(frag), DMA_TO_DEVICE); + tx_ring->txbufs[wr_idx].skb = NULL; + tx_ring->txbufs[wr_idx].dma_addr = 0; + tx_ring->txbufs[wr_idx].fidx = -2; + wr_idx = wr_idx - 1; + if (wr_idx < 0) + wr_idx += tx_ring->cnt; + } + dma_unmap_single(dp->dev, tx_ring->txbufs[wr_idx].dma_addr, + skb_headlen(skb), DMA_TO_DEVICE); + tx_ring->txbufs[wr_idx].skb = NULL; + tx_ring->txbufs[wr_idx].dma_addr = 0; + tx_ring->txbufs[wr_idx].fidx = -2; +err_dma_err: + nn_dp_warn(dp, "Failed to map DMA TX buffer\n"); +err_flush: + nfp_net_tx_xmit_more_flush(tx_ring); + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_errors++; + u64_stats_update_end(&r_vec->tx_sync); + nfp_net_tls_tx_undo(skb, tls_handle); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +/** + * nfp_nfd3_tx_complete() - Handled completed TX packets + * @tx_ring: TX ring structure + * @budget: NAPI budget (only used as bool to determine if in NAPI context) + */ +void nfp_nfd3_tx_complete(struct nfp_net_tx_ring *tx_ring, int budget) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; + u32 done_pkts = 0, done_bytes = 0; + struct netdev_queue *nd_q; + u32 qcp_rd_p; + int todo; + + if (tx_ring->wr_p == tx_ring->rd_p) + return; + + /* Work out how many descriptors have been transmitted */ + qcp_rd_p = nfp_net_read_tx_cmpl(tx_ring, dp); + + if (qcp_rd_p == tx_ring->qcp_rd_p) + return; + + todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p); + + while (todo--) { + const skb_frag_t *frag; + struct nfp_nfd3_tx_buf *tx_buf; + struct sk_buff *skb; + int fidx, nr_frags; + int idx; + + idx = D_IDX(tx_ring, tx_ring->rd_p++); + tx_buf = &tx_ring->txbufs[idx]; + + skb = tx_buf->skb; + if (!skb) + continue; + + nr_frags = skb_shinfo(skb)->nr_frags; + fidx = tx_buf->fidx; + + if (fidx == -1) { + /* unmap head */ + dma_unmap_single(dp->dev, tx_buf->dma_addr, + skb_headlen(skb), DMA_TO_DEVICE); + + done_pkts += tx_buf->pkt_cnt; + done_bytes += tx_buf->real_len; + } else { + /* unmap fragment */ + frag = &skb_shinfo(skb)->frags[fidx]; + dma_unmap_page(dp->dev, tx_buf->dma_addr, + skb_frag_size(frag), DMA_TO_DEVICE); + } + + /* check for last gather fragment */ + if (fidx == nr_frags - 1) + napi_consume_skb(skb, budget); + + tx_buf->dma_addr = 0; + tx_buf->skb = NULL; + tx_buf->fidx = -2; + } + + tx_ring->qcp_rd_p = qcp_rd_p; + + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_bytes += done_bytes; + r_vec->tx_pkts += done_pkts; + u64_stats_update_end(&r_vec->tx_sync); + + if (!dp->netdev) + return; + + nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx); + netdev_tx_completed_queue(nd_q, done_pkts, done_bytes); + if (nfp_nfd3_tx_ring_should_wake(tx_ring)) { + /* Make sure TX thread will see updated tx_ring->rd_p */ + smp_mb(); + + if (unlikely(netif_tx_queue_stopped(nd_q))) + netif_tx_wake_queue(nd_q); + } + + WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt, + "TX ring corruption rd_p=%u wr_p=%u cnt=%u\n", + tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt); +} + +static bool nfp_nfd3_xdp_complete(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; + u32 done_pkts = 0, done_bytes = 0; + bool done_all; + int idx, todo; + u32 qcp_rd_p; + + /* Work out how many descriptors have been transmitted */ + qcp_rd_p = nfp_net_read_tx_cmpl(tx_ring, dp); + + if (qcp_rd_p == tx_ring->qcp_rd_p) + return true; + + todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p); + + done_all = todo <= NFP_NET_XDP_MAX_COMPLETE; + todo = min(todo, NFP_NET_XDP_MAX_COMPLETE); + + tx_ring->qcp_rd_p = D_IDX(tx_ring, tx_ring->qcp_rd_p + todo); + + done_pkts = todo; + while (todo--) { + idx = D_IDX(tx_ring, tx_ring->rd_p); + tx_ring->rd_p++; + + done_bytes += tx_ring->txbufs[idx].real_len; + } + + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_bytes += done_bytes; + r_vec->tx_pkts += done_pkts; + u64_stats_update_end(&r_vec->tx_sync); + + WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt, + "XDP TX ring corruption rd_p=%u wr_p=%u cnt=%u\n", + tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt); + + return done_all; +} + +/* Receive processing + */ + +static void * +nfp_nfd3_napi_alloc_one(struct nfp_net_dp *dp, dma_addr_t *dma_addr) +{ + void *frag; + + if (!dp->xdp_prog) { + frag = napi_alloc_frag(dp->fl_bufsz); + if (unlikely(!frag)) + return NULL; + } else { + struct page *page; + + page = dev_alloc_page(); + if (unlikely(!page)) + return NULL; + frag = page_address(page); + } + + *dma_addr = nfp_net_dma_map_rx(dp, frag); + if (dma_mapping_error(dp->dev, *dma_addr)) { + nfp_net_free_frag(frag, dp->xdp_prog); + nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); + return NULL; + } + + return frag; +} + +/** + * nfp_nfd3_rx_give_one() - Put mapped skb on the software and hardware rings + * @dp: NFP Net data path struct + * @rx_ring: RX ring structure + * @frag: page fragment buffer + * @dma_addr: DMA address of skb mapping + */ +static void +nfp_nfd3_rx_give_one(const struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring, + void *frag, dma_addr_t dma_addr) +{ + unsigned int wr_idx; + + wr_idx = D_IDX(rx_ring, rx_ring->wr_p); + + nfp_net_dma_sync_dev_rx(dp, dma_addr); + + /* Stash SKB and DMA address away */ + rx_ring->rxbufs[wr_idx].frag = frag; + rx_ring->rxbufs[wr_idx].dma_addr = dma_addr; + + /* Fill freelist descriptor */ + rx_ring->rxds[wr_idx].fld.reserved = 0; + rx_ring->rxds[wr_idx].fld.meta_len_dd = 0; + nfp_desc_set_dma_addr(&rx_ring->rxds[wr_idx].fld, + dma_addr + dp->rx_dma_off); + + rx_ring->wr_p++; + if (!(rx_ring->wr_p % NFP_NET_FL_BATCH)) { + /* Update write pointer of the freelist queue. Make + * sure all writes are flushed before telling the hardware. + */ + wmb(); + nfp_qcp_wr_ptr_add(rx_ring->qcp_fl, NFP_NET_FL_BATCH); + } +} + +/** + * nfp_nfd3_rx_ring_fill_freelist() - Give buffers from the ring to FW + * @dp: NFP Net data path struct + * @rx_ring: RX ring to fill + */ +void nfp_nfd3_rx_ring_fill_freelist(struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring) +{ + unsigned int i; + + if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx)) + return nfp_net_xsk_rx_ring_fill_freelist(rx_ring); + + for (i = 0; i < rx_ring->cnt - 1; i++) + nfp_nfd3_rx_give_one(dp, rx_ring, rx_ring->rxbufs[i].frag, + rx_ring->rxbufs[i].dma_addr); +} + +/** + * nfp_nfd3_rx_csum_has_errors() - group check if rxd has any csum errors + * @flags: RX descriptor flags field in CPU byte order + */ +static int nfp_nfd3_rx_csum_has_errors(u16 flags) +{ + u16 csum_all_checked, csum_all_ok; + + csum_all_checked = flags & __PCIE_DESC_RX_CSUM_ALL; + csum_all_ok = flags & __PCIE_DESC_RX_CSUM_ALL_OK; + + return csum_all_checked != (csum_all_ok << PCIE_DESC_RX_CSUM_OK_SHIFT); +} + +/** + * nfp_nfd3_rx_csum() - set SKB checksum field based on RX descriptor flags + * @dp: NFP Net data path struct + * @r_vec: per-ring structure + * @rxd: Pointer to RX descriptor + * @meta: Parsed metadata prepend + * @skb: Pointer to SKB + */ +void +nfp_nfd3_rx_csum(const struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, + const struct nfp_net_rx_desc *rxd, + const struct nfp_meta_parsed *meta, struct sk_buff *skb) +{ + skb_checksum_none_assert(skb); + + if (!(dp->netdev->features & NETIF_F_RXCSUM)) + return; + + if (meta->csum_type) { + skb->ip_summed = meta->csum_type; + skb->csum = meta->csum; + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->hw_csum_rx_complete++; + u64_stats_update_end(&r_vec->rx_sync); + return; + } + + if (nfp_nfd3_rx_csum_has_errors(le16_to_cpu(rxd->rxd.flags))) { + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->hw_csum_rx_error++; + u64_stats_update_end(&r_vec->rx_sync); + return; + } + + /* Assume that the firmware will never report inner CSUM_OK unless outer + * L4 headers were successfully parsed. FW will always report zero UDP + * checksum as CSUM_OK. + */ + if (rxd->rxd.flags & PCIE_DESC_RX_TCP_CSUM_OK || + rxd->rxd.flags & PCIE_DESC_RX_UDP_CSUM_OK) { + __skb_incr_checksum_unnecessary(skb); + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->hw_csum_rx_ok++; + u64_stats_update_end(&r_vec->rx_sync); + } + + if (rxd->rxd.flags & PCIE_DESC_RX_I_TCP_CSUM_OK || + rxd->rxd.flags & PCIE_DESC_RX_I_UDP_CSUM_OK) { + __skb_incr_checksum_unnecessary(skb); + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->hw_csum_rx_inner_ok++; + u64_stats_update_end(&r_vec->rx_sync); + } +} + +static void +nfp_nfd3_set_hash(struct net_device *netdev, struct nfp_meta_parsed *meta, + unsigned int type, __be32 *hash) +{ + if (!(netdev->features & NETIF_F_RXHASH)) + return; + + switch (type) { + case NFP_NET_RSS_IPV4: + case NFP_NET_RSS_IPV6: + case NFP_NET_RSS_IPV6_EX: + meta->hash_type = PKT_HASH_TYPE_L3; + break; + default: + meta->hash_type = PKT_HASH_TYPE_L4; + break; + } + + meta->hash = get_unaligned_be32(hash); +} + +static void +nfp_nfd3_set_hash_desc(struct net_device *netdev, struct nfp_meta_parsed *meta, + void *data, struct nfp_net_rx_desc *rxd) +{ + struct nfp_net_rx_hash *rx_hash = data; + + if (!(rxd->rxd.flags & PCIE_DESC_RX_RSS)) + return; + + nfp_nfd3_set_hash(netdev, meta, get_unaligned_be32(&rx_hash->hash_type), + &rx_hash->hash); +} + +bool +nfp_nfd3_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta, + void *data, void *pkt, unsigned int pkt_len, int meta_len) +{ + u32 meta_info; + + meta_info = get_unaligned_be32(data); + data += 4; + + while (meta_info) { + switch (meta_info & NFP_NET_META_FIELD_MASK) { + case NFP_NET_META_HASH: + meta_info >>= NFP_NET_META_FIELD_SIZE; + nfp_nfd3_set_hash(netdev, meta, + meta_info & NFP_NET_META_FIELD_MASK, + (__be32 *)data); + data += 4; + break; + case NFP_NET_META_MARK: + meta->mark = get_unaligned_be32(data); + data += 4; + break; + case NFP_NET_META_PORTID: + meta->portid = get_unaligned_be32(data); + data += 4; + break; + case NFP_NET_META_CSUM: + meta->csum_type = CHECKSUM_COMPLETE; + meta->csum = + (__force __wsum)__get_unaligned_cpu32(data); + data += 4; + break; + case NFP_NET_META_RESYNC_INFO: + if (nfp_net_tls_rx_resync_req(netdev, data, pkt, + pkt_len)) + return false; + data += sizeof(struct nfp_net_tls_resync_req); + break; + default: + return true; + } + + meta_info >>= NFP_NET_META_FIELD_SIZE; + } + + return data != pkt; +} + +static void +nfp_nfd3_rx_drop(const struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, + struct nfp_net_rx_ring *rx_ring, struct nfp_net_rx_buf *rxbuf, + struct sk_buff *skb) +{ + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->rx_drops++; + /* If we have both skb and rxbuf the replacement buffer allocation + * must have failed, count this as an alloc failure. + */ + if (skb && rxbuf) + r_vec->rx_replace_buf_alloc_fail++; + u64_stats_update_end(&r_vec->rx_sync); + + /* skb is build based on the frag, free_skb() would free the frag + * so to be able to reuse it we need an extra ref. + */ + if (skb && rxbuf && skb->head == rxbuf->frag) + page_ref_inc(virt_to_head_page(rxbuf->frag)); + if (rxbuf) + nfp_nfd3_rx_give_one(dp, rx_ring, rxbuf->frag, rxbuf->dma_addr); + if (skb) + dev_kfree_skb_any(skb); +} + +static bool +nfp_nfd3_tx_xdp_buf(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, + struct nfp_net_tx_ring *tx_ring, + struct nfp_net_rx_buf *rxbuf, unsigned int dma_off, + unsigned int pkt_len, bool *completed) +{ + unsigned int dma_map_sz = dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA; + struct nfp_nfd3_tx_buf *txbuf; + struct nfp_nfd3_tx_desc *txd; + int wr_idx; + + /* Reject if xdp_adjust_tail grow packet beyond DMA area */ + if (pkt_len + dma_off > dma_map_sz) + return false; + + if (unlikely(nfp_net_tx_full(tx_ring, 1))) { + if (!*completed) { + nfp_nfd3_xdp_complete(tx_ring); + *completed = true; + } + + if (unlikely(nfp_net_tx_full(tx_ring, 1))) { + nfp_nfd3_rx_drop(dp, rx_ring->r_vec, rx_ring, rxbuf, + NULL); + return false; + } + } + + wr_idx = D_IDX(tx_ring, tx_ring->wr_p); + + /* Stash the soft descriptor of the head then initialize it */ + txbuf = &tx_ring->txbufs[wr_idx]; + + nfp_nfd3_rx_give_one(dp, rx_ring, txbuf->frag, txbuf->dma_addr); + + txbuf->frag = rxbuf->frag; + txbuf->dma_addr = rxbuf->dma_addr; + txbuf->fidx = -1; + txbuf->pkt_cnt = 1; + txbuf->real_len = pkt_len; + + dma_sync_single_for_device(dp->dev, rxbuf->dma_addr + dma_off, + pkt_len, DMA_BIDIRECTIONAL); + + /* Build TX descriptor */ + txd = &tx_ring->txds[wr_idx]; + txd->offset_eop = NFD3_DESC_TX_EOP; + txd->dma_len = cpu_to_le16(pkt_len); + nfp_desc_set_dma_addr(txd, rxbuf->dma_addr + dma_off); + txd->data_len = cpu_to_le16(pkt_len); + + txd->flags = 0; + txd->mss = 0; + txd->lso_hdrlen = 0; + + tx_ring->wr_p++; + tx_ring->wr_ptr_add++; + return true; +} + +/** + * nfp_nfd3_rx() - receive up to @budget packets on @rx_ring + * @rx_ring: RX ring to receive from + * @budget: NAPI budget + * + * Note, this function is separated out from the napi poll function to + * more cleanly separate packet receive code from other bookkeeping + * functions performed in the napi poll function. + * + * Return: Number of packets received. + */ +static int nfp_nfd3_rx(struct nfp_net_rx_ring *rx_ring, int budget) +{ + struct nfp_net_r_vector *r_vec = rx_ring->r_vec; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; + struct nfp_net_tx_ring *tx_ring; + struct bpf_prog *xdp_prog; + bool xdp_tx_cmpl = false; + unsigned int true_bufsz; + struct sk_buff *skb; + int pkts_polled = 0; + struct xdp_buff xdp; + int idx; + + xdp_prog = READ_ONCE(dp->xdp_prog); + true_bufsz = xdp_prog ? PAGE_SIZE : dp->fl_bufsz; + xdp_init_buff(&xdp, PAGE_SIZE - NFP_NET_RX_BUF_HEADROOM, + &rx_ring->xdp_rxq); + tx_ring = r_vec->xdp_ring; + + while (pkts_polled < budget) { + unsigned int meta_len, data_len, meta_off, pkt_len, pkt_off; + struct nfp_net_rx_buf *rxbuf; + struct nfp_net_rx_desc *rxd; + struct nfp_meta_parsed meta; + bool redir_egress = false; + struct net_device *netdev; + dma_addr_t new_dma_addr; + u32 meta_len_xdp = 0; + void *new_frag; + + idx = D_IDX(rx_ring, rx_ring->rd_p); + + rxd = &rx_ring->rxds[idx]; + if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) + break; + + /* Memory barrier to ensure that we won't do other reads + * before the DD bit. + */ + dma_rmb(); + + memset(&meta, 0, sizeof(meta)); + + rx_ring->rd_p++; + pkts_polled++; + + rxbuf = &rx_ring->rxbufs[idx]; + /* < meta_len > + * <-- [rx_offset] --> + * --------------------------------------------------------- + * | [XX] | metadata | packet | XXXX | + * --------------------------------------------------------- + * <---------------- data_len ---------------> + * + * The rx_offset is fixed for all packets, the meta_len can vary + * on a packet by packet basis. If rx_offset is set to zero + * (_RX_OFFSET_DYNAMIC) metadata starts at the beginning of the + * buffer and is immediately followed by the packet (no [XX]). + */ + meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK; + data_len = le16_to_cpu(rxd->rxd.data_len); + pkt_len = data_len - meta_len; + + pkt_off = NFP_NET_RX_BUF_HEADROOM + dp->rx_dma_off; + if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) + pkt_off += meta_len; + else + pkt_off += dp->rx_offset; + meta_off = pkt_off - meta_len; + + /* Stats update */ + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->rx_pkts++; + r_vec->rx_bytes += pkt_len; + u64_stats_update_end(&r_vec->rx_sync); + + if (unlikely(meta_len > NFP_NET_MAX_PREPEND || + (dp->rx_offset && meta_len > dp->rx_offset))) { + nn_dp_warn(dp, "oversized RX packet metadata %u\n", + meta_len); + nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); + continue; + } + + nfp_net_dma_sync_cpu_rx(dp, rxbuf->dma_addr + meta_off, + data_len); + + if (!dp->chained_metadata_format) { + nfp_nfd3_set_hash_desc(dp->netdev, &meta, + rxbuf->frag + meta_off, rxd); + } else if (meta_len) { + if (unlikely(nfp_nfd3_parse_meta(dp->netdev, &meta, + rxbuf->frag + meta_off, + rxbuf->frag + pkt_off, + pkt_len, meta_len))) { + nn_dp_warn(dp, "invalid RX packet metadata\n"); + nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, + NULL); + continue; + } + } + + if (xdp_prog && !meta.portid) { + void *orig_data = rxbuf->frag + pkt_off; + unsigned int dma_off; + int act; + + xdp_prepare_buff(&xdp, + rxbuf->frag + NFP_NET_RX_BUF_HEADROOM, + pkt_off - NFP_NET_RX_BUF_HEADROOM, + pkt_len, true); + + act = bpf_prog_run_xdp(xdp_prog, &xdp); + + pkt_len = xdp.data_end - xdp.data; + pkt_off += xdp.data - orig_data; + + switch (act) { + case XDP_PASS: + meta_len_xdp = xdp.data - xdp.data_meta; + break; + case XDP_TX: + dma_off = pkt_off - NFP_NET_RX_BUF_HEADROOM; + if (unlikely(!nfp_nfd3_tx_xdp_buf(dp, rx_ring, + tx_ring, + rxbuf, + dma_off, + pkt_len, + &xdp_tx_cmpl))) + trace_xdp_exception(dp->netdev, + xdp_prog, act); + continue; + default: + bpf_warn_invalid_xdp_action(dp->netdev, xdp_prog, act); + fallthrough; + case XDP_ABORTED: + trace_xdp_exception(dp->netdev, xdp_prog, act); + fallthrough; + case XDP_DROP: + nfp_nfd3_rx_give_one(dp, rx_ring, rxbuf->frag, + rxbuf->dma_addr); + continue; + } + } + + if (likely(!meta.portid)) { + netdev = dp->netdev; + } else if (meta.portid == NFP_META_PORT_ID_CTRL) { + struct nfp_net *nn = netdev_priv(dp->netdev); + + nfp_app_ctrl_rx_raw(nn->app, rxbuf->frag + pkt_off, + pkt_len); + nfp_nfd3_rx_give_one(dp, rx_ring, rxbuf->frag, + rxbuf->dma_addr); + continue; + } else { + struct nfp_net *nn; + + nn = netdev_priv(dp->netdev); + netdev = nfp_app_dev_get(nn->app, meta.portid, + &redir_egress); + if (unlikely(!netdev)) { + nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, + NULL); + continue; + } + + if (nfp_netdev_is_nfp_repr(netdev)) + nfp_repr_inc_rx_stats(netdev, pkt_len); + } + + skb = build_skb(rxbuf->frag, true_bufsz); + if (unlikely(!skb)) { + nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); + continue; + } + new_frag = nfp_nfd3_napi_alloc_one(dp, &new_dma_addr); + if (unlikely(!new_frag)) { + nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, skb); + continue; + } + + nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr); + + nfp_nfd3_rx_give_one(dp, rx_ring, new_frag, new_dma_addr); + + skb_reserve(skb, pkt_off); + skb_put(skb, pkt_len); + + skb->mark = meta.mark; + skb_set_hash(skb, meta.hash, meta.hash_type); + + skb_record_rx_queue(skb, rx_ring->idx); + skb->protocol = eth_type_trans(skb, netdev); + + nfp_nfd3_rx_csum(dp, r_vec, rxd, &meta, skb); + +#ifdef CONFIG_TLS_DEVICE + if (rxd->rxd.flags & PCIE_DESC_RX_DECRYPTED) { + skb->decrypted = true; + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->hw_tls_rx++; + u64_stats_update_end(&r_vec->rx_sync); + } +#endif + + if (rxd->rxd.flags & PCIE_DESC_RX_VLAN) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + le16_to_cpu(rxd->rxd.vlan)); + if (meta_len_xdp) + skb_metadata_set(skb, meta_len_xdp); + + if (likely(!redir_egress)) { + napi_gro_receive(&rx_ring->r_vec->napi, skb); + } else { + skb->dev = netdev; + skb_reset_network_header(skb); + __skb_push(skb, ETH_HLEN); + dev_queue_xmit(skb); + } + } + + if (xdp_prog) { + if (tx_ring->wr_ptr_add) + nfp_net_tx_xmit_more_flush(tx_ring); + else if (unlikely(tx_ring->wr_p != tx_ring->rd_p) && + !xdp_tx_cmpl) + if (!nfp_nfd3_xdp_complete(tx_ring)) + pkts_polled = budget; + } + + return pkts_polled; +} + +/** + * nfp_nfd3_poll() - napi poll function + * @napi: NAPI structure + * @budget: NAPI budget + * + * Return: number of packets polled. + */ +int nfp_nfd3_poll(struct napi_struct *napi, int budget) +{ + struct nfp_net_r_vector *r_vec = + container_of(napi, struct nfp_net_r_vector, napi); + unsigned int pkts_polled = 0; + + if (r_vec->tx_ring) + nfp_nfd3_tx_complete(r_vec->tx_ring, budget); + if (r_vec->rx_ring) + pkts_polled = nfp_nfd3_rx(r_vec->rx_ring, budget); + + if (pkts_polled < budget) + if (napi_complete_done(napi, pkts_polled)) + nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_entry); + + if (r_vec->nfp_net->rx_coalesce_adapt_on && r_vec->rx_ring) { + struct dim_sample dim_sample = {}; + unsigned int start; + u64 pkts, bytes; + + do { + start = u64_stats_fetch_begin(&r_vec->rx_sync); + pkts = r_vec->rx_pkts; + bytes = r_vec->rx_bytes; + } while (u64_stats_fetch_retry(&r_vec->rx_sync, start)); + + dim_update_sample(r_vec->event_ctr, pkts, bytes, &dim_sample); + net_dim(&r_vec->rx_dim, dim_sample); + } + + if (r_vec->nfp_net->tx_coalesce_adapt_on && r_vec->tx_ring) { + struct dim_sample dim_sample = {}; + unsigned int start; + u64 pkts, bytes; + + do { + start = u64_stats_fetch_begin(&r_vec->tx_sync); + pkts = r_vec->tx_pkts; + bytes = r_vec->tx_bytes; + } while (u64_stats_fetch_retry(&r_vec->tx_sync, start)); + + dim_update_sample(r_vec->event_ctr, pkts, bytes, &dim_sample); + net_dim(&r_vec->tx_dim, dim_sample); + } + + return pkts_polled; +} + +/* Control device data path + */ + +bool +nfp_nfd3_ctrl_tx_one(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, + struct sk_buff *skb, bool old) +{ + unsigned int real_len = skb->len, meta_len = 0; + struct nfp_net_tx_ring *tx_ring; + struct nfp_nfd3_tx_buf *txbuf; + struct nfp_nfd3_tx_desc *txd; + struct nfp_net_dp *dp; + dma_addr_t dma_addr; + int wr_idx; + + dp = &r_vec->nfp_net->dp; + tx_ring = r_vec->tx_ring; + + if (WARN_ON_ONCE(skb_shinfo(skb)->nr_frags)) { + nn_dp_warn(dp, "Driver's CTRL TX does not implement gather\n"); + goto err_free; + } + + if (unlikely(nfp_net_tx_full(tx_ring, 1))) { + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_busy++; + u64_stats_update_end(&r_vec->tx_sync); + if (!old) + __skb_queue_tail(&r_vec->queue, skb); + else + __skb_queue_head(&r_vec->queue, skb); + return true; + } + + if (nfp_app_ctrl_has_meta(nn->app)) { + if (unlikely(skb_headroom(skb) < 8)) { + nn_dp_warn(dp, "CTRL TX on skb without headroom\n"); + goto err_free; + } + meta_len = 8; + put_unaligned_be32(NFP_META_PORT_ID_CTRL, skb_push(skb, 4)); + put_unaligned_be32(NFP_NET_META_PORTID, skb_push(skb, 4)); + } + + /* Start with the head skbuf */ + dma_addr = dma_map_single(dp->dev, skb->data, skb_headlen(skb), + DMA_TO_DEVICE); + if (dma_mapping_error(dp->dev, dma_addr)) + goto err_dma_warn; + + wr_idx = D_IDX(tx_ring, tx_ring->wr_p); + + /* Stash the soft descriptor of the head then initialize it */ + txbuf = &tx_ring->txbufs[wr_idx]; + txbuf->skb = skb; + txbuf->dma_addr = dma_addr; + txbuf->fidx = -1; + txbuf->pkt_cnt = 1; + txbuf->real_len = real_len; + + /* Build TX descriptor */ + txd = &tx_ring->txds[wr_idx]; + txd->offset_eop = meta_len | NFD3_DESC_TX_EOP; + txd->dma_len = cpu_to_le16(skb_headlen(skb)); + nfp_desc_set_dma_addr(txd, dma_addr); + txd->data_len = cpu_to_le16(skb->len); + + txd->flags = 0; + txd->mss = 0; + txd->lso_hdrlen = 0; + + tx_ring->wr_p++; + tx_ring->wr_ptr_add++; + nfp_net_tx_xmit_more_flush(tx_ring); + + return false; + +err_dma_warn: + nn_dp_warn(dp, "Failed to DMA map TX CTRL buffer\n"); +err_free: + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_errors++; + u64_stats_update_end(&r_vec->tx_sync); + dev_kfree_skb_any(skb); + return false; +} + +static void __nfp_ctrl_tx_queued(struct nfp_net_r_vector *r_vec) +{ + struct sk_buff *skb; + + while ((skb = __skb_dequeue(&r_vec->queue))) + if (nfp_nfd3_ctrl_tx_one(r_vec->nfp_net, r_vec, skb, true)) + return; +} + +static bool +nfp_ctrl_meta_ok(struct nfp_net *nn, void *data, unsigned int meta_len) +{ + u32 meta_type, meta_tag; + + if (!nfp_app_ctrl_has_meta(nn->app)) + return !meta_len; + + if (meta_len != 8) + return false; + + meta_type = get_unaligned_be32(data); + meta_tag = get_unaligned_be32(data + 4); + + return (meta_type == NFP_NET_META_PORTID && + meta_tag == NFP_META_PORT_ID_CTRL); +} + +static bool +nfp_ctrl_rx_one(struct nfp_net *nn, struct nfp_net_dp *dp, + struct nfp_net_r_vector *r_vec, struct nfp_net_rx_ring *rx_ring) +{ + unsigned int meta_len, data_len, meta_off, pkt_len, pkt_off; + struct nfp_net_rx_buf *rxbuf; + struct nfp_net_rx_desc *rxd; + dma_addr_t new_dma_addr; + struct sk_buff *skb; + void *new_frag; + int idx; + + idx = D_IDX(rx_ring, rx_ring->rd_p); + + rxd = &rx_ring->rxds[idx]; + if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) + return false; + + /* Memory barrier to ensure that we won't do other reads + * before the DD bit. + */ + dma_rmb(); + + rx_ring->rd_p++; + + rxbuf = &rx_ring->rxbufs[idx]; + meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK; + data_len = le16_to_cpu(rxd->rxd.data_len); + pkt_len = data_len - meta_len; + + pkt_off = NFP_NET_RX_BUF_HEADROOM + dp->rx_dma_off; + if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) + pkt_off += meta_len; + else + pkt_off += dp->rx_offset; + meta_off = pkt_off - meta_len; + + /* Stats update */ + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->rx_pkts++; + r_vec->rx_bytes += pkt_len; + u64_stats_update_end(&r_vec->rx_sync); + + nfp_net_dma_sync_cpu_rx(dp, rxbuf->dma_addr + meta_off, data_len); + + if (unlikely(!nfp_ctrl_meta_ok(nn, rxbuf->frag + meta_off, meta_len))) { + nn_dp_warn(dp, "incorrect metadata for ctrl packet (%d)\n", + meta_len); + nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); + return true; + } + + skb = build_skb(rxbuf->frag, dp->fl_bufsz); + if (unlikely(!skb)) { + nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); + return true; + } + new_frag = nfp_nfd3_napi_alloc_one(dp, &new_dma_addr); + if (unlikely(!new_frag)) { + nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, skb); + return true; + } + + nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr); + + nfp_nfd3_rx_give_one(dp, rx_ring, new_frag, new_dma_addr); + + skb_reserve(skb, pkt_off); + skb_put(skb, pkt_len); + + nfp_app_ctrl_rx(nn->app, skb); + + return true; +} + +static bool nfp_ctrl_rx(struct nfp_net_r_vector *r_vec) +{ + struct nfp_net_rx_ring *rx_ring = r_vec->rx_ring; + struct nfp_net *nn = r_vec->nfp_net; + struct nfp_net_dp *dp = &nn->dp; + unsigned int budget = 512; + + while (nfp_ctrl_rx_one(nn, dp, r_vec, rx_ring) && budget--) + continue; + + return budget; +} + +void nfp_nfd3_ctrl_poll(struct tasklet_struct *t) +{ + struct nfp_net_r_vector *r_vec = from_tasklet(r_vec, t, tasklet); + + spin_lock(&r_vec->lock); + nfp_nfd3_tx_complete(r_vec->tx_ring, 0); + __nfp_ctrl_tx_queued(r_vec); + spin_unlock(&r_vec->lock); + + if (nfp_ctrl_rx(r_vec)) { + nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_entry); + } else { + tasklet_schedule(&r_vec->tasklet); + nn_dp_warn(&r_vec->nfp_net->dp, + "control message budget exceeded!\n"); + } +} diff --git a/drivers/net/ethernet/netronome/nfp/nfd3/nfd3.h b/drivers/net/ethernet/netronome/nfp/nfd3/nfd3.h new file mode 100644 index 000000000000..7a0df9e6c3c4 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfd3/nfd3.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* Copyright (C) 2015-2019 Netronome Systems, Inc. */ + +#ifndef _NFP_DP_NFD3_H_ +#define _NFP_DP_NFD3_H_ + +struct sk_buff; +struct net_device; + +/* TX descriptor format */ + +#define NFD3_DESC_TX_EOP BIT(7) +#define NFD3_DESC_TX_OFFSET_MASK GENMASK(6, 0) +#define NFD3_DESC_TX_MSS_MASK GENMASK(13, 0) + +/* Flags in the host TX descriptor */ +#define NFD3_DESC_TX_CSUM BIT(7) +#define NFD3_DESC_TX_IP4_CSUM BIT(6) +#define NFD3_DESC_TX_TCP_CSUM BIT(5) +#define NFD3_DESC_TX_UDP_CSUM BIT(4) +#define NFD3_DESC_TX_VLAN BIT(3) +#define NFD3_DESC_TX_LSO BIT(2) +#define NFD3_DESC_TX_ENCAP BIT(1) +#define NFD3_DESC_TX_O_IP4_CSUM BIT(0) + +struct nfp_nfd3_tx_desc { + union { + struct { + u8 dma_addr_hi; /* High bits of host buf address */ + __le16 dma_len; /* Length to DMA for this desc */ + u8 offset_eop; /* Offset in buf where pkt starts + + * highest bit is eop flag. + */ + __le32 dma_addr_lo; /* Low 32bit of host buf addr */ + + __le16 mss; /* MSS to be used for LSO */ + u8 lso_hdrlen; /* LSO, TCP payload offset */ + u8 flags; /* TX Flags, see @NFD3_DESC_TX_* */ + union { + struct { + u8 l3_offset; /* L3 header offset */ + u8 l4_offset; /* L4 header offset */ + }; + __le16 vlan; /* VLAN tag to add if indicated */ + }; + __le16 data_len; /* Length of frame + meta data */ + } __packed; + __le32 vals[4]; + __le64 vals8[2]; + }; +}; + +/** + * struct nfp_nfd3_tx_buf - software TX buffer descriptor + * @skb: normal ring, sk_buff associated with this buffer + * @frag: XDP ring, page frag associated with this buffer + * @xdp: XSK buffer pool handle (for AF_XDP) + * @dma_addr: DMA mapping address of the buffer + * @fidx: Fragment index (-1 for the head and [0..nr_frags-1] for frags) + * @pkt_cnt: Number of packets to be produced out of the skb associated + * with this buffer (valid only on the head's buffer). + * Will be 1 for all non-TSO packets. + * @is_xsk_tx: Flag if buffer is a RX buffer after a XDP_TX action and not a + * buffer from the TX queue (for AF_XDP). + * @real_len: Number of bytes which to be produced out of the skb (valid only + * on the head's buffer). Equal to skb->len for non-TSO packets. + */ +struct nfp_nfd3_tx_buf { + union { + struct sk_buff *skb; + void *frag; + struct xdp_buff *xdp; + }; + dma_addr_t dma_addr; + union { + struct { + short int fidx; + u16 pkt_cnt; + }; + struct { + bool is_xsk_tx; + }; + }; + u32 real_len; +}; + +void +nfp_nfd3_rx_csum(const struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, + const struct nfp_net_rx_desc *rxd, + const struct nfp_meta_parsed *meta, struct sk_buff *skb); +bool +nfp_nfd3_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta, + void *data, void *pkt, unsigned int pkt_len, int meta_len); +void nfp_nfd3_tx_complete(struct nfp_net_tx_ring *tx_ring, int budget); +int nfp_nfd3_poll(struct napi_struct *napi, int budget); +netdev_tx_t nfp_nfd3_tx(struct sk_buff *skb, struct net_device *netdev); +bool +nfp_nfd3_ctrl_tx_one(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, + struct sk_buff *skb, bool old); +void nfp_nfd3_ctrl_poll(struct tasklet_struct *t); +void nfp_nfd3_rx_ring_fill_freelist(struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring); +void nfp_nfd3_xsk_tx_free(struct nfp_nfd3_tx_buf *txbuf); +int nfp_nfd3_xsk_poll(struct napi_struct *napi, int budget); + +#endif diff --git a/drivers/net/ethernet/netronome/nfp/nfd3/rings.c b/drivers/net/ethernet/netronome/nfp/nfd3/rings.c new file mode 100644 index 000000000000..47604d5e25eb --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfd3/rings.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (C) 2015-2019 Netronome Systems, Inc. */ + +#include + +#include "../nfp_net.h" +#include "../nfp_net_dp.h" +#include "../nfp_net_xsk.h" +#include "nfd3.h" + +static void nfp_nfd3_xsk_tx_bufs_free(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_nfd3_tx_buf *txbuf; + unsigned int idx; + + while (tx_ring->rd_p != tx_ring->wr_p) { + idx = D_IDX(tx_ring, tx_ring->rd_p); + txbuf = &tx_ring->txbufs[idx]; + + txbuf->real_len = 0; + + tx_ring->qcp_rd_p++; + tx_ring->rd_p++; + + if (tx_ring->r_vec->xsk_pool) { + if (txbuf->is_xsk_tx) + nfp_nfd3_xsk_tx_free(txbuf); + + xsk_tx_completed(tx_ring->r_vec->xsk_pool, 1); + } + } +} + +/** + * nfp_nfd3_tx_ring_reset() - Free any untransmitted buffers and reset pointers + * @dp: NFP Net data path struct + * @tx_ring: TX ring structure + * + * Assumes that the device is stopped, must be idempotent. + */ +static void +nfp_nfd3_tx_ring_reset(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) +{ + struct netdev_queue *nd_q; + const skb_frag_t *frag; + + while (!tx_ring->is_xdp && tx_ring->rd_p != tx_ring->wr_p) { + struct nfp_nfd3_tx_buf *tx_buf; + struct sk_buff *skb; + int idx, nr_frags; + + idx = D_IDX(tx_ring, tx_ring->rd_p); + tx_buf = &tx_ring->txbufs[idx]; + + skb = tx_ring->txbufs[idx].skb; + nr_frags = skb_shinfo(skb)->nr_frags; + + if (tx_buf->fidx == -1) { + /* unmap head */ + dma_unmap_single(dp->dev, tx_buf->dma_addr, + skb_headlen(skb), DMA_TO_DEVICE); + } else { + /* unmap fragment */ + frag = &skb_shinfo(skb)->frags[tx_buf->fidx]; + dma_unmap_page(dp->dev, tx_buf->dma_addr, + skb_frag_size(frag), DMA_TO_DEVICE); + } + + /* check for last gather fragment */ + if (tx_buf->fidx == nr_frags - 1) + dev_kfree_skb_any(skb); + + tx_buf->dma_addr = 0; + tx_buf->skb = NULL; + tx_buf->fidx = -2; + + tx_ring->qcp_rd_p++; + tx_ring->rd_p++; + } + + if (tx_ring->is_xdp) + nfp_nfd3_xsk_tx_bufs_free(tx_ring); + + memset(tx_ring->txds, 0, tx_ring->size); + tx_ring->wr_p = 0; + tx_ring->rd_p = 0; + tx_ring->qcp_rd_p = 0; + tx_ring->wr_ptr_add = 0; + + if (tx_ring->is_xdp || !dp->netdev) + return; + + nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx); + netdev_tx_reset_queue(nd_q); +} + +/** + * nfp_nfd3_tx_ring_free() - Free resources allocated to a TX ring + * @tx_ring: TX ring to free + */ +static void nfp_nfd3_tx_ring_free(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; + + kvfree(tx_ring->txbufs); + + if (tx_ring->txds) + dma_free_coherent(dp->dev, tx_ring->size, + tx_ring->txds, tx_ring->dma); + + tx_ring->cnt = 0; + tx_ring->txbufs = NULL; + tx_ring->txds = NULL; + tx_ring->dma = 0; + tx_ring->size = 0; +} + +/** + * nfp_nfd3_tx_ring_alloc() - Allocate resource for a TX ring + * @dp: NFP Net data path struct + * @tx_ring: TX Ring structure to allocate + * + * Return: 0 on success, negative errno otherwise. + */ +static int +nfp_nfd3_tx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + + tx_ring->cnt = dp->txd_cnt; + + tx_ring->size = array_size(tx_ring->cnt, sizeof(*tx_ring->txds)); + tx_ring->txds = dma_alloc_coherent(dp->dev, tx_ring->size, + &tx_ring->dma, + GFP_KERNEL | __GFP_NOWARN); + if (!tx_ring->txds) { + netdev_warn(dp->netdev, "failed to allocate TX descriptor ring memory, requested descriptor count: %d, consider lowering descriptor count\n", + tx_ring->cnt); + goto err_alloc; + } + + tx_ring->txbufs = kvcalloc(tx_ring->cnt, sizeof(*tx_ring->txbufs), + GFP_KERNEL); + if (!tx_ring->txbufs) + goto err_alloc; + + if (!tx_ring->is_xdp && dp->netdev) + netif_set_xps_queue(dp->netdev, &r_vec->affinity_mask, + tx_ring->idx); + + return 0; + +err_alloc: + nfp_nfd3_tx_ring_free(tx_ring); + return -ENOMEM; +} + +static void +nfp_nfd3_tx_ring_bufs_free(struct nfp_net_dp *dp, + struct nfp_net_tx_ring *tx_ring) +{ + unsigned int i; + + if (!tx_ring->is_xdp) + return; + + for (i = 0; i < tx_ring->cnt; i++) { + if (!tx_ring->txbufs[i].frag) + return; + + nfp_net_dma_unmap_rx(dp, tx_ring->txbufs[i].dma_addr); + __free_page(virt_to_page(tx_ring->txbufs[i].frag)); + } +} + +static int +nfp_nfd3_tx_ring_bufs_alloc(struct nfp_net_dp *dp, + struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_nfd3_tx_buf *txbufs = tx_ring->txbufs; + unsigned int i; + + if (!tx_ring->is_xdp) + return 0; + + for (i = 0; i < tx_ring->cnt; i++) { + txbufs[i].frag = nfp_net_rx_alloc_one(dp, &txbufs[i].dma_addr); + if (!txbufs[i].frag) { + nfp_nfd3_tx_ring_bufs_free(dp, tx_ring); + return -ENOMEM; + } + } + + return 0; +} + +static void +nfp_nfd3_print_tx_descs(struct seq_file *file, + struct nfp_net_r_vector *r_vec, + struct nfp_net_tx_ring *tx_ring, + u32 d_rd_p, u32 d_wr_p) +{ + struct nfp_nfd3_tx_desc *txd; + u32 txd_cnt = tx_ring->cnt; + int i; + + for (i = 0; i < txd_cnt; i++) { + struct xdp_buff *xdp; + struct sk_buff *skb; + + txd = &tx_ring->txds[i]; + seq_printf(file, "%04d: 0x%08x 0x%08x 0x%08x 0x%08x", i, + txd->vals[0], txd->vals[1], + txd->vals[2], txd->vals[3]); + + if (!tx_ring->is_xdp) { + skb = READ_ONCE(tx_ring->txbufs[i].skb); + if (skb) + seq_printf(file, " skb->head=%p skb->data=%p", + skb->head, skb->data); + } else { + xdp = READ_ONCE(tx_ring->txbufs[i].xdp); + if (xdp) + seq_printf(file, " xdp->data=%p", xdp->data); + } + + if (tx_ring->txbufs[i].dma_addr) + seq_printf(file, " dma_addr=%pad", + &tx_ring->txbufs[i].dma_addr); + + if (i == tx_ring->rd_p % txd_cnt) + seq_puts(file, " H_RD"); + if (i == tx_ring->wr_p % txd_cnt) + seq_puts(file, " H_WR"); + if (i == d_rd_p % txd_cnt) + seq_puts(file, " D_RD"); + if (i == d_wr_p % txd_cnt) + seq_puts(file, " D_WR"); + + seq_putc(file, '\n'); + } +} + +#define NFP_NFD3_CFG_CTRL_SUPPORTED \ + (NFP_NET_CFG_CTRL_ENABLE | NFP_NET_CFG_CTRL_PROMISC | \ + NFP_NET_CFG_CTRL_L2BC | NFP_NET_CFG_CTRL_L2MC | \ + NFP_NET_CFG_CTRL_RXCSUM | NFP_NET_CFG_CTRL_TXCSUM | \ + NFP_NET_CFG_CTRL_RXVLAN | NFP_NET_CFG_CTRL_TXVLAN | \ + NFP_NET_CFG_CTRL_GATHER | NFP_NET_CFG_CTRL_LSO | \ + NFP_NET_CFG_CTRL_CTAG_FILTER | NFP_NET_CFG_CTRL_CMSG_DATA | \ + NFP_NET_CFG_CTRL_RINGCFG | NFP_NET_CFG_CTRL_RSS | \ + NFP_NET_CFG_CTRL_IRQMOD | NFP_NET_CFG_CTRL_TXRWB | \ + NFP_NET_CFG_CTRL_VXLAN | NFP_NET_CFG_CTRL_NVGRE | \ + NFP_NET_CFG_CTRL_BPF | NFP_NET_CFG_CTRL_LSO2 | \ + NFP_NET_CFG_CTRL_RSS2 | NFP_NET_CFG_CTRL_CSUM_COMPLETE | \ + NFP_NET_CFG_CTRL_LIVE_ADDR) + +const struct nfp_dp_ops nfp_nfd3_ops = { + .version = NFP_NFD_VER_NFD3, + .tx_min_desc_per_pkt = 1, + .cap_mask = NFP_NFD3_CFG_CTRL_SUPPORTED, + .poll = nfp_nfd3_poll, + .xsk_poll = nfp_nfd3_xsk_poll, + .ctrl_poll = nfp_nfd3_ctrl_poll, + .xmit = nfp_nfd3_tx, + .ctrl_tx_one = nfp_nfd3_ctrl_tx_one, + .rx_ring_fill_freelist = nfp_nfd3_rx_ring_fill_freelist, + .tx_ring_alloc = nfp_nfd3_tx_ring_alloc, + .tx_ring_reset = nfp_nfd3_tx_ring_reset, + .tx_ring_free = nfp_nfd3_tx_ring_free, + .tx_ring_bufs_alloc = nfp_nfd3_tx_ring_bufs_alloc, + .tx_ring_bufs_free = nfp_nfd3_tx_ring_bufs_free, + .print_tx_descs = nfp_nfd3_print_tx_descs +}; diff --git a/drivers/net/ethernet/netronome/nfp/nfd3/xsk.c b/drivers/net/ethernet/netronome/nfp/nfd3/xsk.c new file mode 100644 index 000000000000..c16c4b42ecfd --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfd3/xsk.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (C) 2018 Netronome Systems, Inc */ +/* Copyright (C) 2021 Corigine, Inc */ + +#include +#include + +#include "../nfp_app.h" +#include "../nfp_net.h" +#include "../nfp_net_dp.h" +#include "../nfp_net_xsk.h" +#include "nfd3.h" + +static bool +nfp_nfd3_xsk_tx_xdp(const struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, + struct nfp_net_rx_ring *rx_ring, + struct nfp_net_tx_ring *tx_ring, + struct nfp_net_xsk_rx_buf *xrxbuf, unsigned int pkt_len, + int pkt_off) +{ + struct xsk_buff_pool *pool = r_vec->xsk_pool; + struct nfp_nfd3_tx_buf *txbuf; + struct nfp_nfd3_tx_desc *txd; + unsigned int wr_idx; + + if (nfp_net_tx_space(tx_ring) < 1) + return false; + + xsk_buff_raw_dma_sync_for_device(pool, xrxbuf->dma_addr + pkt_off, + pkt_len); + + wr_idx = D_IDX(tx_ring, tx_ring->wr_p); + + txbuf = &tx_ring->txbufs[wr_idx]; + txbuf->xdp = xrxbuf->xdp; + txbuf->real_len = pkt_len; + txbuf->is_xsk_tx = true; + + /* Build TX descriptor */ + txd = &tx_ring->txds[wr_idx]; + txd->offset_eop = NFD3_DESC_TX_EOP; + txd->dma_len = cpu_to_le16(pkt_len); + nfp_desc_set_dma_addr(txd, xrxbuf->dma_addr + pkt_off); + txd->data_len = cpu_to_le16(pkt_len); + + txd->flags = 0; + txd->mss = 0; + txd->lso_hdrlen = 0; + + tx_ring->wr_ptr_add++; + tx_ring->wr_p++; + + return true; +} + +static void nfp_nfd3_xsk_rx_skb(struct nfp_net_rx_ring *rx_ring, + const struct nfp_net_rx_desc *rxd, + struct nfp_net_xsk_rx_buf *xrxbuf, + const struct nfp_meta_parsed *meta, + unsigned int pkt_len, + bool meta_xdp, + unsigned int *skbs_polled) +{ + struct nfp_net_r_vector *r_vec = rx_ring->r_vec; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; + struct net_device *netdev; + struct sk_buff *skb; + + if (likely(!meta->portid)) { + netdev = dp->netdev; + } else { + struct nfp_net *nn = netdev_priv(dp->netdev); + + netdev = nfp_app_dev_get(nn->app, meta->portid, NULL); + if (unlikely(!netdev)) { + nfp_net_xsk_rx_drop(r_vec, xrxbuf); + return; + } + nfp_repr_inc_rx_stats(netdev, pkt_len); + } + + skb = napi_alloc_skb(&r_vec->napi, pkt_len); + if (!skb) { + nfp_net_xsk_rx_drop(r_vec, xrxbuf); + return; + } + memcpy(skb_put(skb, pkt_len), xrxbuf->xdp->data, pkt_len); + + skb->mark = meta->mark; + skb_set_hash(skb, meta->hash, meta->hash_type); + + skb_record_rx_queue(skb, rx_ring->idx); + skb->protocol = eth_type_trans(skb, netdev); + + nfp_nfd3_rx_csum(dp, r_vec, rxd, meta, skb); + + if (rxd->rxd.flags & PCIE_DESC_RX_VLAN) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + le16_to_cpu(rxd->rxd.vlan)); + if (meta_xdp) + skb_metadata_set(skb, + xrxbuf->xdp->data - xrxbuf->xdp->data_meta); + + napi_gro_receive(&rx_ring->r_vec->napi, skb); + + nfp_net_xsk_rx_free(xrxbuf); + + (*skbs_polled)++; +} + +static unsigned int +nfp_nfd3_xsk_rx(struct nfp_net_rx_ring *rx_ring, int budget, + unsigned int *skbs_polled) +{ + struct nfp_net_r_vector *r_vec = rx_ring->r_vec; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; + struct nfp_net_tx_ring *tx_ring; + struct bpf_prog *xdp_prog; + bool xdp_redir = false; + int pkts_polled = 0; + + xdp_prog = READ_ONCE(dp->xdp_prog); + tx_ring = r_vec->xdp_ring; + + while (pkts_polled < budget) { + unsigned int meta_len, data_len, pkt_len, pkt_off; + struct nfp_net_xsk_rx_buf *xrxbuf; + struct nfp_net_rx_desc *rxd; + struct nfp_meta_parsed meta; + int idx, act; + + idx = D_IDX(rx_ring, rx_ring->rd_p); + + rxd = &rx_ring->rxds[idx]; + if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) + break; + + rx_ring->rd_p++; + pkts_polled++; + + xrxbuf = &rx_ring->xsk_rxbufs[idx]; + + /* If starved of buffers "drop" it and scream. */ + if (rx_ring->rd_p >= rx_ring->wr_p) { + nn_dp_warn(dp, "Starved of RX buffers\n"); + nfp_net_xsk_rx_drop(r_vec, xrxbuf); + break; + } + + /* Memory barrier to ensure that we won't do other reads + * before the DD bit. + */ + dma_rmb(); + + memset(&meta, 0, sizeof(meta)); + + /* Only supporting AF_XDP with dynamic metadata so buffer layout + * is always: + * + * --------------------------------------------------------- + * | off | metadata | packet | XXXX | + * --------------------------------------------------------- + */ + meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK; + data_len = le16_to_cpu(rxd->rxd.data_len); + pkt_len = data_len - meta_len; + + if (unlikely(meta_len > NFP_NET_MAX_PREPEND)) { + nn_dp_warn(dp, "Oversized RX packet metadata %u\n", + meta_len); + nfp_net_xsk_rx_drop(r_vec, xrxbuf); + continue; + } + + /* Stats update. */ + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->rx_pkts++; + r_vec->rx_bytes += pkt_len; + u64_stats_update_end(&r_vec->rx_sync); + + xrxbuf->xdp->data += meta_len; + xrxbuf->xdp->data_end = xrxbuf->xdp->data + pkt_len; + xdp_set_data_meta_invalid(xrxbuf->xdp); + xsk_buff_dma_sync_for_cpu(xrxbuf->xdp, r_vec->xsk_pool); + net_prefetch(xrxbuf->xdp->data); + + if (meta_len) { + if (unlikely(nfp_nfd3_parse_meta(dp->netdev, &meta, + xrxbuf->xdp->data - + meta_len, + xrxbuf->xdp->data, + pkt_len, meta_len))) { + nn_dp_warn(dp, "Invalid RX packet metadata\n"); + nfp_net_xsk_rx_drop(r_vec, xrxbuf); + continue; + } + + if (unlikely(meta.portid)) { + struct nfp_net *nn = netdev_priv(dp->netdev); + + if (meta.portid != NFP_META_PORT_ID_CTRL) { + nfp_nfd3_xsk_rx_skb(rx_ring, rxd, + xrxbuf, &meta, + pkt_len, false, + skbs_polled); + continue; + } + + nfp_app_ctrl_rx_raw(nn->app, xrxbuf->xdp->data, + pkt_len); + nfp_net_xsk_rx_free(xrxbuf); + continue; + } + } + + act = bpf_prog_run_xdp(xdp_prog, xrxbuf->xdp); + + pkt_len = xrxbuf->xdp->data_end - xrxbuf->xdp->data; + pkt_off = xrxbuf->xdp->data - xrxbuf->xdp->data_hard_start; + + switch (act) { + case XDP_PASS: + nfp_nfd3_xsk_rx_skb(rx_ring, rxd, xrxbuf, &meta, pkt_len, + true, skbs_polled); + break; + case XDP_TX: + if (!nfp_nfd3_xsk_tx_xdp(dp, r_vec, rx_ring, tx_ring, + xrxbuf, pkt_len, pkt_off)) + nfp_net_xsk_rx_drop(r_vec, xrxbuf); + else + nfp_net_xsk_rx_unstash(xrxbuf); + break; + case XDP_REDIRECT: + if (xdp_do_redirect(dp->netdev, xrxbuf->xdp, xdp_prog)) { + nfp_net_xsk_rx_drop(r_vec, xrxbuf); + } else { + nfp_net_xsk_rx_unstash(xrxbuf); + xdp_redir = true; + } + break; + default: + bpf_warn_invalid_xdp_action(dp->netdev, xdp_prog, act); + fallthrough; + case XDP_ABORTED: + trace_xdp_exception(dp->netdev, xdp_prog, act); + fallthrough; + case XDP_DROP: + nfp_net_xsk_rx_drop(r_vec, xrxbuf); + break; + } + } + + nfp_net_xsk_rx_ring_fill_freelist(r_vec->rx_ring); + + if (xdp_redir) + xdp_do_flush_map(); + + if (tx_ring->wr_ptr_add) + nfp_net_tx_xmit_more_flush(tx_ring); + + return pkts_polled; +} + +void nfp_nfd3_xsk_tx_free(struct nfp_nfd3_tx_buf *txbuf) +{ + xsk_buff_free(txbuf->xdp); + + txbuf->dma_addr = 0; + txbuf->xdp = NULL; +} + +static bool nfp_nfd3_xsk_complete(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + u32 done_pkts = 0, done_bytes = 0, reused = 0; + bool done_all; + int idx, todo; + u32 qcp_rd_p; + + if (tx_ring->wr_p == tx_ring->rd_p) + return true; + + /* Work out how many descriptors have been transmitted. */ + qcp_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q); + + if (qcp_rd_p == tx_ring->qcp_rd_p) + return true; + + todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p); + + done_all = todo <= NFP_NET_XDP_MAX_COMPLETE; + todo = min(todo, NFP_NET_XDP_MAX_COMPLETE); + + tx_ring->qcp_rd_p = D_IDX(tx_ring, tx_ring->qcp_rd_p + todo); + + done_pkts = todo; + while (todo--) { + struct nfp_nfd3_tx_buf *txbuf; + + idx = D_IDX(tx_ring, tx_ring->rd_p); + tx_ring->rd_p++; + + txbuf = &tx_ring->txbufs[idx]; + if (unlikely(!txbuf->real_len)) + continue; + + done_bytes += txbuf->real_len; + txbuf->real_len = 0; + + if (txbuf->is_xsk_tx) { + nfp_nfd3_xsk_tx_free(txbuf); + reused++; + } + } + + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_bytes += done_bytes; + r_vec->tx_pkts += done_pkts; + u64_stats_update_end(&r_vec->tx_sync); + + xsk_tx_completed(r_vec->xsk_pool, done_pkts - reused); + + WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt, + "XDP TX ring corruption rd_p=%u wr_p=%u cnt=%u\n", + tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt); + + return done_all; +} + +static void nfp_nfd3_xsk_tx(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct xdp_desc desc[NFP_NET_XSK_TX_BATCH]; + struct xsk_buff_pool *xsk_pool; + struct nfp_nfd3_tx_desc *txd; + u32 pkts = 0, wr_idx; + u32 i, got; + + xsk_pool = r_vec->xsk_pool; + + while (nfp_net_tx_space(tx_ring) >= NFP_NET_XSK_TX_BATCH) { + for (i = 0; i < NFP_NET_XSK_TX_BATCH; i++) + if (!xsk_tx_peek_desc(xsk_pool, &desc[i])) + break; + got = i; + if (!got) + break; + + wr_idx = D_IDX(tx_ring, tx_ring->wr_p + i); + prefetchw(&tx_ring->txds[wr_idx]); + + for (i = 0; i < got; i++) + xsk_buff_raw_dma_sync_for_device(xsk_pool, desc[i].addr, + desc[i].len); + + for (i = 0; i < got; i++) { + wr_idx = D_IDX(tx_ring, tx_ring->wr_p + i); + + tx_ring->txbufs[wr_idx].real_len = desc[i].len; + tx_ring->txbufs[wr_idx].is_xsk_tx = false; + + /* Build TX descriptor. */ + txd = &tx_ring->txds[wr_idx]; + nfp_desc_set_dma_addr(txd, + xsk_buff_raw_get_dma(xsk_pool, + desc[i].addr + )); + txd->offset_eop = NFD3_DESC_TX_EOP; + txd->dma_len = cpu_to_le16(desc[i].len); + txd->data_len = cpu_to_le16(desc[i].len); + } + + tx_ring->wr_p += got; + pkts += got; + } + + if (!pkts) + return; + + xsk_tx_release(xsk_pool); + /* Ensure all records are visible before incrementing write counter. */ + wmb(); + nfp_qcp_wr_ptr_add(tx_ring->qcp_q, pkts); +} + +int nfp_nfd3_xsk_poll(struct napi_struct *napi, int budget) +{ + struct nfp_net_r_vector *r_vec = + container_of(napi, struct nfp_net_r_vector, napi); + unsigned int pkts_polled, skbs = 0; + + pkts_polled = nfp_nfd3_xsk_rx(r_vec->rx_ring, budget, &skbs); + + if (pkts_polled < budget) { + if (r_vec->tx_ring) + nfp_nfd3_tx_complete(r_vec->tx_ring, budget); + + if (!nfp_nfd3_xsk_complete(r_vec->xdp_ring)) + pkts_polled = budget; + + nfp_nfd3_xsk_tx(r_vec->xdp_ring); + + if (pkts_polled < budget && napi_complete_done(napi, skbs)) + nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_entry); + } + + return pkts_polled; +} diff --git a/drivers/net/ethernet/netronome/nfp/nfdk/dp.c b/drivers/net/ethernet/netronome/nfp/nfdk/dp.c new file mode 100644 index 000000000000..e3da9ac20e57 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfdk/dp.c @@ -0,0 +1,1524 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (C) 2015-2019 Netronome Systems, Inc. */ + +#include +#include +#include +#include +#include + +#include "../nfp_app.h" +#include "../nfp_net.h" +#include "../nfp_net_dp.h" +#include "../crypto/crypto.h" +#include "../crypto/fw.h" +#include "nfdk.h" + +static int nfp_nfdk_tx_ring_should_wake(struct nfp_net_tx_ring *tx_ring) +{ + return !nfp_net_tx_full(tx_ring, NFDK_TX_DESC_STOP_CNT * 2); +} + +static int nfp_nfdk_tx_ring_should_stop(struct nfp_net_tx_ring *tx_ring) +{ + return nfp_net_tx_full(tx_ring, NFDK_TX_DESC_STOP_CNT); +} + +static void nfp_nfdk_tx_ring_stop(struct netdev_queue *nd_q, + struct nfp_net_tx_ring *tx_ring) +{ + netif_tx_stop_queue(nd_q); + + /* We can race with the TX completion out of NAPI so recheck */ + smp_mb(); + if (unlikely(nfp_nfdk_tx_ring_should_wake(tx_ring))) + netif_tx_start_queue(nd_q); +} + +static __le64 +nfp_nfdk_tx_tso(struct nfp_net_r_vector *r_vec, struct nfp_nfdk_tx_buf *txbuf, + struct sk_buff *skb) +{ + u32 segs, hdrlen, l3_offset, l4_offset; + struct nfp_nfdk_tx_desc txd; + u16 mss; + + if (!skb->encapsulation) { + l3_offset = skb_network_offset(skb); + l4_offset = skb_transport_offset(skb); + hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb); + } else { + l3_offset = skb_inner_network_offset(skb); + l4_offset = skb_inner_transport_offset(skb); + hdrlen = skb_inner_transport_header(skb) - skb->data + + inner_tcp_hdrlen(skb); + } + + segs = skb_shinfo(skb)->gso_segs; + mss = skb_shinfo(skb)->gso_size & NFDK_DESC_TX_MSS_MASK; + + /* Note: TSO of the packet with metadata prepended to skb is not + * supported yet, in which case l3/l4_offset and lso_hdrlen need + * be correctly handled here. + * Concern: + * The driver doesn't have md_bytes easily available at this point. + * The PCI.IN PD ME won't have md_bytes bytes to add to lso_hdrlen, + * so it needs the full length there. The app MEs might prefer + * l3_offset and l4_offset relative to the start of packet data, + * but could probably cope with it being relative to the CTM buf + * data offset. + */ + txd.l3_offset = l3_offset; + txd.l4_offset = l4_offset; + txd.lso_meta_res = 0; + txd.mss = cpu_to_le16(mss); + txd.lso_hdrlen = hdrlen; + txd.lso_totsegs = segs; + + txbuf->pkt_cnt = segs; + txbuf->real_len = skb->len + hdrlen * (txbuf->pkt_cnt - 1); + + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_lso++; + u64_stats_update_end(&r_vec->tx_sync); + + return txd.raw; +} + +static u8 +nfp_nfdk_tx_csum(struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, + unsigned int pkt_cnt, struct sk_buff *skb, u64 flags) +{ + struct ipv6hdr *ipv6h; + struct iphdr *iph; + + if (!(dp->ctrl & NFP_NET_CFG_CTRL_TXCSUM)) + return flags; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + return flags; + + flags |= NFDK_DESC_TX_L4_CSUM; + + iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); + ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb); + + /* L3 checksum offloading flag is not required for ipv6 */ + if (iph->version == 4) { + flags |= NFDK_DESC_TX_L3_CSUM; + } else if (ipv6h->version != 6) { + nn_dp_warn(dp, "partial checksum but ipv=%x!\n", iph->version); + return flags; + } + + u64_stats_update_begin(&r_vec->tx_sync); + if (!skb->encapsulation) { + r_vec->hw_csum_tx += pkt_cnt; + } else { + flags |= NFDK_DESC_TX_ENCAP; + r_vec->hw_csum_tx_inner += pkt_cnt; + } + u64_stats_update_end(&r_vec->tx_sync); + + return flags; +} + +static int +nfp_nfdk_tx_maybe_close_block(struct nfp_net_tx_ring *tx_ring, + unsigned int nr_frags, struct sk_buff *skb) +{ + unsigned int n_descs, wr_p, nop_slots; + const skb_frag_t *frag, *fend; + struct nfp_nfdk_tx_desc *txd; + unsigned int wr_idx; + int err; + +recount_descs: + n_descs = nfp_nfdk_headlen_to_segs(skb_headlen(skb)); + + frag = skb_shinfo(skb)->frags; + fend = frag + nr_frags; + for (; frag < fend; frag++) + n_descs += DIV_ROUND_UP(skb_frag_size(frag), + NFDK_TX_MAX_DATA_PER_DESC); + + if (unlikely(n_descs > NFDK_TX_DESC_GATHER_MAX)) { + if (skb_is_nonlinear(skb)) { + err = skb_linearize(skb); + if (err) + return err; + goto recount_descs; + } + return -EINVAL; + } + + /* Under count by 1 (don't count meta) for the round down to work out */ + n_descs += !!skb_is_gso(skb); + + if (round_down(tx_ring->wr_p, NFDK_TX_DESC_BLOCK_CNT) != + round_down(tx_ring->wr_p + n_descs, NFDK_TX_DESC_BLOCK_CNT)) + goto close_block; + + if ((u32)tx_ring->data_pending + skb->len > NFDK_TX_MAX_DATA_PER_BLOCK) + goto close_block; + + return 0; + +close_block: + wr_p = tx_ring->wr_p; + nop_slots = D_BLOCK_CPL(wr_p); + + wr_idx = D_IDX(tx_ring, wr_p); + tx_ring->ktxbufs[wr_idx].skb = NULL; + txd = &tx_ring->ktxds[wr_idx]; + + memset(txd, 0, array_size(nop_slots, sizeof(struct nfp_nfdk_tx_desc))); + + tx_ring->data_pending = 0; + tx_ring->wr_p += nop_slots; + tx_ring->wr_ptr_add += nop_slots; + + return 0; +} + +static int nfp_nfdk_prep_port_id(struct sk_buff *skb) +{ + struct metadata_dst *md_dst = skb_metadata_dst(skb); + unsigned char *data; + + if (likely(!md_dst)) + return 0; + if (unlikely(md_dst->type != METADATA_HW_PORT_MUX)) + return 0; + + /* Note: Unsupported case when TSO a skb with metedata prepended. + * See the comments in `nfp_nfdk_tx_tso` for details. + */ + if (unlikely(md_dst && skb_is_gso(skb))) + return -EOPNOTSUPP; + + if (unlikely(skb_cow_head(skb, sizeof(md_dst->u.port_info.port_id)))) + return -ENOMEM; + + data = skb_push(skb, sizeof(md_dst->u.port_info.port_id)); + put_unaligned_be32(md_dst->u.port_info.port_id, data); + + return sizeof(md_dst->u.port_info.port_id); +} + +static int +nfp_nfdk_prep_tx_meta(struct nfp_app *app, struct sk_buff *skb, + struct nfp_net_r_vector *r_vec) +{ + unsigned char *data; + int res, md_bytes; + u32 meta_id = 0; + + res = nfp_nfdk_prep_port_id(skb); + if (unlikely(res <= 0)) + return res; + + md_bytes = res; + meta_id = NFP_NET_META_PORTID; + + if (unlikely(skb_cow_head(skb, sizeof(meta_id)))) + return -ENOMEM; + + md_bytes += sizeof(meta_id); + + meta_id = FIELD_PREP(NFDK_META_LEN, md_bytes) | + FIELD_PREP(NFDK_META_FIELDS, meta_id); + + data = skb_push(skb, sizeof(meta_id)); + put_unaligned_be32(meta_id, data); + + return NFDK_DESC_TX_CHAIN_META; +} + +/** + * nfp_nfdk_tx() - Main transmit entry point + * @skb: SKB to transmit + * @netdev: netdev structure + * + * Return: NETDEV_TX_OK on success. + */ +netdev_tx_t nfp_nfdk_tx(struct sk_buff *skb, struct net_device *netdev) +{ + struct nfp_net *nn = netdev_priv(netdev); + struct nfp_nfdk_tx_buf *txbuf, *etxbuf; + u32 cnt, tmp_dlen, dlen_type = 0; + struct nfp_net_tx_ring *tx_ring; + struct nfp_net_r_vector *r_vec; + const skb_frag_t *frag, *fend; + struct nfp_nfdk_tx_desc *txd; + unsigned int real_len, qidx; + unsigned int dma_len, type; + struct netdev_queue *nd_q; + struct nfp_net_dp *dp; + int nr_frags, wr_idx; + dma_addr_t dma_addr; + u64 metadata; + + dp = &nn->dp; + qidx = skb_get_queue_mapping(skb); + tx_ring = &dp->tx_rings[qidx]; + r_vec = tx_ring->r_vec; + nd_q = netdev_get_tx_queue(dp->netdev, qidx); + + /* Don't bother counting frags, assume the worst */ + if (unlikely(nfp_net_tx_full(tx_ring, NFDK_TX_DESC_STOP_CNT))) { + nn_dp_warn(dp, "TX ring %d busy. wrp=%u rdp=%u\n", + qidx, tx_ring->wr_p, tx_ring->rd_p); + netif_tx_stop_queue(nd_q); + nfp_net_tx_xmit_more_flush(tx_ring); + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_busy++; + u64_stats_update_end(&r_vec->tx_sync); + return NETDEV_TX_BUSY; + } + + metadata = nfp_nfdk_prep_tx_meta(nn->app, skb, r_vec); + if (unlikely((int)metadata < 0)) + goto err_flush; + + nr_frags = skb_shinfo(skb)->nr_frags; + if (nfp_nfdk_tx_maybe_close_block(tx_ring, nr_frags, skb)) + goto err_flush; + + /* DMA map all */ + wr_idx = D_IDX(tx_ring, tx_ring->wr_p); + txd = &tx_ring->ktxds[wr_idx]; + txbuf = &tx_ring->ktxbufs[wr_idx]; + + dma_len = skb_headlen(skb); + if (skb_is_gso(skb)) + type = NFDK_DESC_TX_TYPE_TSO; + else if (!nr_frags && dma_len < NFDK_TX_MAX_DATA_PER_HEAD) + type = NFDK_DESC_TX_TYPE_SIMPLE; + else + type = NFDK_DESC_TX_TYPE_GATHER; + + dma_addr = dma_map_single(dp->dev, skb->data, dma_len, DMA_TO_DEVICE); + if (dma_mapping_error(dp->dev, dma_addr)) + goto err_warn_dma; + + txbuf->skb = skb; + txbuf++; + + txbuf->dma_addr = dma_addr; + txbuf++; + + /* FIELD_PREP() implicitly truncates to chunk */ + dma_len -= 1; + dlen_type = FIELD_PREP(NFDK_DESC_TX_DMA_LEN_HEAD, dma_len) | + FIELD_PREP(NFDK_DESC_TX_TYPE_HEAD, type); + + txd->dma_len_type = cpu_to_le16(dlen_type); + nfp_desc_set_dma_addr(txd, dma_addr); + + /* starts at bit 0 */ + BUILD_BUG_ON(!(NFDK_DESC_TX_DMA_LEN_HEAD & 1)); + + /* Preserve the original dlen_type, this way below the EOP logic + * can use dlen_type. + */ + tmp_dlen = dlen_type & NFDK_DESC_TX_DMA_LEN_HEAD; + dma_len -= tmp_dlen; + dma_addr += tmp_dlen + 1; + txd++; + + /* The rest of the data (if any) will be in larger dma descritors + * and is handled with the fragment loop. + */ + frag = skb_shinfo(skb)->frags; + fend = frag + nr_frags; + + while (true) { + while (dma_len > 0) { + dma_len -= 1; + dlen_type = FIELD_PREP(NFDK_DESC_TX_DMA_LEN, dma_len); + + txd->dma_len_type = cpu_to_le16(dlen_type); + nfp_desc_set_dma_addr(txd, dma_addr); + + dma_len -= dlen_type; + dma_addr += dlen_type + 1; + txd++; + } + + if (frag >= fend) + break; + + dma_len = skb_frag_size(frag); + dma_addr = skb_frag_dma_map(dp->dev, frag, 0, dma_len, + DMA_TO_DEVICE); + if (dma_mapping_error(dp->dev, dma_addr)) + goto err_unmap; + + txbuf->dma_addr = dma_addr; + txbuf++; + + frag++; + } + + (txd - 1)->dma_len_type = cpu_to_le16(dlen_type | NFDK_DESC_TX_EOP); + + if (!skb_is_gso(skb)) { + real_len = skb->len; + /* Metadata desc */ + metadata = nfp_nfdk_tx_csum(dp, r_vec, 1, skb, metadata); + txd->raw = cpu_to_le64(metadata); + txd++; + } else { + /* lso desc should be placed after metadata desc */ + (txd + 1)->raw = nfp_nfdk_tx_tso(r_vec, txbuf, skb); + real_len = txbuf->real_len; + /* Metadata desc */ + metadata = nfp_nfdk_tx_csum(dp, r_vec, txbuf->pkt_cnt, skb, metadata); + txd->raw = cpu_to_le64(metadata); + txd += 2; + txbuf++; + } + + cnt = txd - tx_ring->ktxds - wr_idx; + if (unlikely(round_down(wr_idx, NFDK_TX_DESC_BLOCK_CNT) != + round_down(wr_idx + cnt - 1, NFDK_TX_DESC_BLOCK_CNT))) + goto err_warn_overflow; + + skb_tx_timestamp(skb); + + tx_ring->wr_p += cnt; + if (tx_ring->wr_p % NFDK_TX_DESC_BLOCK_CNT) + tx_ring->data_pending += skb->len; + else + tx_ring->data_pending = 0; + + if (nfp_nfdk_tx_ring_should_stop(tx_ring)) + nfp_nfdk_tx_ring_stop(nd_q, tx_ring); + + tx_ring->wr_ptr_add += cnt; + if (__netdev_tx_sent_queue(nd_q, real_len, netdev_xmit_more())) + nfp_net_tx_xmit_more_flush(tx_ring); + + return NETDEV_TX_OK; + +err_warn_overflow: + WARN_ONCE(1, "unable to fit packet into a descriptor wr_idx:%d head:%d frags:%d cnt:%d", + wr_idx, skb_headlen(skb), nr_frags, cnt); + if (skb_is_gso(skb)) + txbuf--; +err_unmap: + /* txbuf pointed to the next-to-use */ + etxbuf = txbuf; + /* first txbuf holds the skb */ + txbuf = &tx_ring->ktxbufs[wr_idx + 1]; + if (txbuf < etxbuf) { + dma_unmap_single(dp->dev, txbuf->dma_addr, + skb_headlen(skb), DMA_TO_DEVICE); + txbuf->raw = 0; + txbuf++; + } + frag = skb_shinfo(skb)->frags; + while (etxbuf < txbuf) { + dma_unmap_page(dp->dev, txbuf->dma_addr, + skb_frag_size(frag), DMA_TO_DEVICE); + txbuf->raw = 0; + frag++; + txbuf++; + } +err_warn_dma: + nn_dp_warn(dp, "Failed to map DMA TX buffer\n"); +err_flush: + nfp_net_tx_xmit_more_flush(tx_ring); + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_errors++; + u64_stats_update_end(&r_vec->tx_sync); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +/** + * nfp_nfdk_tx_complete() - Handled completed TX packets + * @tx_ring: TX ring structure + * @budget: NAPI budget (only used as bool to determine if in NAPI context) + */ +static void nfp_nfdk_tx_complete(struct nfp_net_tx_ring *tx_ring, int budget) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; + u32 done_pkts = 0, done_bytes = 0; + struct nfp_nfdk_tx_buf *ktxbufs; + struct device *dev = dp->dev; + struct netdev_queue *nd_q; + u32 rd_p, qcp_rd_p; + int todo; + + rd_p = tx_ring->rd_p; + if (tx_ring->wr_p == rd_p) + return; + + /* Work out how many descriptors have been transmitted */ + qcp_rd_p = nfp_net_read_tx_cmpl(tx_ring, dp); + + if (qcp_rd_p == tx_ring->qcp_rd_p) + return; + + todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p); + ktxbufs = tx_ring->ktxbufs; + + while (todo > 0) { + const skb_frag_t *frag, *fend; + unsigned int size, n_descs = 1; + struct nfp_nfdk_tx_buf *txbuf; + struct sk_buff *skb; + + txbuf = &ktxbufs[D_IDX(tx_ring, rd_p)]; + skb = txbuf->skb; + txbuf++; + + /* Closed block */ + if (!skb) { + n_descs = D_BLOCK_CPL(rd_p); + goto next; + } + + /* Unmap head */ + size = skb_headlen(skb); + n_descs += nfp_nfdk_headlen_to_segs(size); + dma_unmap_single(dev, txbuf->dma_addr, size, DMA_TO_DEVICE); + txbuf++; + + /* Unmap frags */ + frag = skb_shinfo(skb)->frags; + fend = frag + skb_shinfo(skb)->nr_frags; + for (; frag < fend; frag++) { + size = skb_frag_size(frag); + n_descs += DIV_ROUND_UP(size, + NFDK_TX_MAX_DATA_PER_DESC); + dma_unmap_page(dev, txbuf->dma_addr, + skb_frag_size(frag), DMA_TO_DEVICE); + txbuf++; + } + + if (!skb_is_gso(skb)) { + done_bytes += skb->len; + done_pkts++; + } else { + done_bytes += txbuf->real_len; + done_pkts += txbuf->pkt_cnt; + n_descs++; + } + + napi_consume_skb(skb, budget); +next: + rd_p += n_descs; + todo -= n_descs; + } + + tx_ring->rd_p = rd_p; + tx_ring->qcp_rd_p = qcp_rd_p; + + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_bytes += done_bytes; + r_vec->tx_pkts += done_pkts; + u64_stats_update_end(&r_vec->tx_sync); + + if (!dp->netdev) + return; + + nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx); + netdev_tx_completed_queue(nd_q, done_pkts, done_bytes); + if (nfp_nfdk_tx_ring_should_wake(tx_ring)) { + /* Make sure TX thread will see updated tx_ring->rd_p */ + smp_mb(); + + if (unlikely(netif_tx_queue_stopped(nd_q))) + netif_tx_wake_queue(nd_q); + } + + WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt, + "TX ring corruption rd_p=%u wr_p=%u cnt=%u\n", + tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt); +} + +/* Receive processing */ +static void * +nfp_nfdk_napi_alloc_one(struct nfp_net_dp *dp, dma_addr_t *dma_addr) +{ + void *frag; + + if (!dp->xdp_prog) { + frag = napi_alloc_frag(dp->fl_bufsz); + if (unlikely(!frag)) + return NULL; + } else { + struct page *page; + + page = dev_alloc_page(); + if (unlikely(!page)) + return NULL; + frag = page_address(page); + } + + *dma_addr = nfp_net_dma_map_rx(dp, frag); + if (dma_mapping_error(dp->dev, *dma_addr)) { + nfp_net_free_frag(frag, dp->xdp_prog); + nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); + return NULL; + } + + return frag; +} + +/** + * nfp_nfdk_rx_give_one() - Put mapped skb on the software and hardware rings + * @dp: NFP Net data path struct + * @rx_ring: RX ring structure + * @frag: page fragment buffer + * @dma_addr: DMA address of skb mapping + */ +static void +nfp_nfdk_rx_give_one(const struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring, + void *frag, dma_addr_t dma_addr) +{ + unsigned int wr_idx; + + wr_idx = D_IDX(rx_ring, rx_ring->wr_p); + + nfp_net_dma_sync_dev_rx(dp, dma_addr); + + /* Stash SKB and DMA address away */ + rx_ring->rxbufs[wr_idx].frag = frag; + rx_ring->rxbufs[wr_idx].dma_addr = dma_addr; + + /* Fill freelist descriptor */ + rx_ring->rxds[wr_idx].fld.reserved = 0; + rx_ring->rxds[wr_idx].fld.meta_len_dd = 0; + nfp_desc_set_dma_addr(&rx_ring->rxds[wr_idx].fld, + dma_addr + dp->rx_dma_off); + + rx_ring->wr_p++; + if (!(rx_ring->wr_p % NFP_NET_FL_BATCH)) { + /* Update write pointer of the freelist queue. Make + * sure all writes are flushed before telling the hardware. + */ + wmb(); + nfp_qcp_wr_ptr_add(rx_ring->qcp_fl, NFP_NET_FL_BATCH); + } +} + +/** + * nfp_nfdk_rx_ring_fill_freelist() - Give buffers from the ring to FW + * @dp: NFP Net data path struct + * @rx_ring: RX ring to fill + */ +void nfp_nfdk_rx_ring_fill_freelist(struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring) +{ + unsigned int i; + + for (i = 0; i < rx_ring->cnt - 1; i++) + nfp_nfdk_rx_give_one(dp, rx_ring, rx_ring->rxbufs[i].frag, + rx_ring->rxbufs[i].dma_addr); +} + +/** + * nfp_nfdk_rx_csum_has_errors() - group check if rxd has any csum errors + * @flags: RX descriptor flags field in CPU byte order + */ +static int nfp_nfdk_rx_csum_has_errors(u16 flags) +{ + u16 csum_all_checked, csum_all_ok; + + csum_all_checked = flags & __PCIE_DESC_RX_CSUM_ALL; + csum_all_ok = flags & __PCIE_DESC_RX_CSUM_ALL_OK; + + return csum_all_checked != (csum_all_ok << PCIE_DESC_RX_CSUM_OK_SHIFT); +} + +/** + * nfp_nfdk_rx_csum() - set SKB checksum field based on RX descriptor flags + * @dp: NFP Net data path struct + * @r_vec: per-ring structure + * @rxd: Pointer to RX descriptor + * @meta: Parsed metadata prepend + * @skb: Pointer to SKB + */ +static void +nfp_nfdk_rx_csum(struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, + struct nfp_net_rx_desc *rxd, struct nfp_meta_parsed *meta, + struct sk_buff *skb) +{ + skb_checksum_none_assert(skb); + + if (!(dp->netdev->features & NETIF_F_RXCSUM)) + return; + + if (meta->csum_type) { + skb->ip_summed = meta->csum_type; + skb->csum = meta->csum; + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->hw_csum_rx_complete++; + u64_stats_update_end(&r_vec->rx_sync); + return; + } + + if (nfp_nfdk_rx_csum_has_errors(le16_to_cpu(rxd->rxd.flags))) { + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->hw_csum_rx_error++; + u64_stats_update_end(&r_vec->rx_sync); + return; + } + + /* Assume that the firmware will never report inner CSUM_OK unless outer + * L4 headers were successfully parsed. FW will always report zero UDP + * checksum as CSUM_OK. + */ + if (rxd->rxd.flags & PCIE_DESC_RX_TCP_CSUM_OK || + rxd->rxd.flags & PCIE_DESC_RX_UDP_CSUM_OK) { + __skb_incr_checksum_unnecessary(skb); + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->hw_csum_rx_ok++; + u64_stats_update_end(&r_vec->rx_sync); + } + + if (rxd->rxd.flags & PCIE_DESC_RX_I_TCP_CSUM_OK || + rxd->rxd.flags & PCIE_DESC_RX_I_UDP_CSUM_OK) { + __skb_incr_checksum_unnecessary(skb); + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->hw_csum_rx_inner_ok++; + u64_stats_update_end(&r_vec->rx_sync); + } +} + +static void +nfp_nfdk_set_hash(struct net_device *netdev, struct nfp_meta_parsed *meta, + unsigned int type, __be32 *hash) +{ + if (!(netdev->features & NETIF_F_RXHASH)) + return; + + switch (type) { + case NFP_NET_RSS_IPV4: + case NFP_NET_RSS_IPV6: + case NFP_NET_RSS_IPV6_EX: + meta->hash_type = PKT_HASH_TYPE_L3; + break; + default: + meta->hash_type = PKT_HASH_TYPE_L4; + break; + } + + meta->hash = get_unaligned_be32(hash); +} + +static bool +nfp_nfdk_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta, + void *data, void *pkt, unsigned int pkt_len, int meta_len) +{ + u32 meta_info; + + meta_info = get_unaligned_be32(data); + data += 4; + + while (meta_info) { + switch (meta_info & NFP_NET_META_FIELD_MASK) { + case NFP_NET_META_HASH: + meta_info >>= NFP_NET_META_FIELD_SIZE; + nfp_nfdk_set_hash(netdev, meta, + meta_info & NFP_NET_META_FIELD_MASK, + (__be32 *)data); + data += 4; + break; + case NFP_NET_META_MARK: + meta->mark = get_unaligned_be32(data); + data += 4; + break; + case NFP_NET_META_PORTID: + meta->portid = get_unaligned_be32(data); + data += 4; + break; + case NFP_NET_META_CSUM: + meta->csum_type = CHECKSUM_COMPLETE; + meta->csum = + (__force __wsum)__get_unaligned_cpu32(data); + data += 4; + break; + case NFP_NET_META_RESYNC_INFO: + if (nfp_net_tls_rx_resync_req(netdev, data, pkt, + pkt_len)) + return false; + data += sizeof(struct nfp_net_tls_resync_req); + break; + default: + return true; + } + + meta_info >>= NFP_NET_META_FIELD_SIZE; + } + + return data != pkt; +} + +static void +nfp_nfdk_rx_drop(const struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, + struct nfp_net_rx_ring *rx_ring, struct nfp_net_rx_buf *rxbuf, + struct sk_buff *skb) +{ + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->rx_drops++; + /* If we have both skb and rxbuf the replacement buffer allocation + * must have failed, count this as an alloc failure. + */ + if (skb && rxbuf) + r_vec->rx_replace_buf_alloc_fail++; + u64_stats_update_end(&r_vec->rx_sync); + + /* skb is build based on the frag, free_skb() would free the frag + * so to be able to reuse it we need an extra ref. + */ + if (skb && rxbuf && skb->head == rxbuf->frag) + page_ref_inc(virt_to_head_page(rxbuf->frag)); + if (rxbuf) + nfp_nfdk_rx_give_one(dp, rx_ring, rxbuf->frag, rxbuf->dma_addr); + if (skb) + dev_kfree_skb_any(skb); +} + +static bool nfp_nfdk_xdp_complete(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; + struct nfp_net_rx_ring *rx_ring; + u32 qcp_rd_p, done = 0; + bool done_all; + int todo; + + /* Work out how many descriptors have been transmitted */ + qcp_rd_p = nfp_net_read_tx_cmpl(tx_ring, dp); + if (qcp_rd_p == tx_ring->qcp_rd_p) + return true; + + todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p); + + done_all = todo <= NFP_NET_XDP_MAX_COMPLETE; + todo = min(todo, NFP_NET_XDP_MAX_COMPLETE); + + rx_ring = r_vec->rx_ring; + while (todo > 0) { + int idx = D_IDX(tx_ring, tx_ring->rd_p + done); + struct nfp_nfdk_tx_buf *txbuf; + unsigned int step = 1; + + txbuf = &tx_ring->ktxbufs[idx]; + if (!txbuf->raw) + goto next; + + if (NFDK_TX_BUF_INFO(txbuf->val) != NFDK_TX_BUF_INFO_SOP) { + WARN_ONCE(1, "Unexpected TX buffer in XDP TX ring\n"); + goto next; + } + + /* Two successive txbufs are used to stash virtual and dma + * address respectively, recycle and clean them here. + */ + nfp_nfdk_rx_give_one(dp, rx_ring, + (void *)NFDK_TX_BUF_PTR(txbuf[0].val), + txbuf[1].dma_addr); + txbuf[0].raw = 0; + txbuf[1].raw = 0; + step = 2; + + u64_stats_update_begin(&r_vec->tx_sync); + /* Note: tx_bytes not accumulated. */ + r_vec->tx_pkts++; + u64_stats_update_end(&r_vec->tx_sync); +next: + todo -= step; + done += step; + } + + tx_ring->qcp_rd_p = D_IDX(tx_ring, tx_ring->qcp_rd_p + done); + tx_ring->rd_p += done; + + WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt, + "XDP TX ring corruption rd_p=%u wr_p=%u cnt=%u\n", + tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt); + + return done_all; +} + +static bool +nfp_nfdk_tx_xdp_buf(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, + struct nfp_net_tx_ring *tx_ring, + struct nfp_net_rx_buf *rxbuf, unsigned int dma_off, + unsigned int pkt_len, bool *completed) +{ + unsigned int dma_map_sz = dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA; + unsigned int dma_len, type, cnt, dlen_type, tmp_dlen; + struct nfp_nfdk_tx_buf *txbuf; + struct nfp_nfdk_tx_desc *txd; + unsigned int n_descs; + dma_addr_t dma_addr; + int wr_idx; + + /* Reject if xdp_adjust_tail grow packet beyond DMA area */ + if (pkt_len + dma_off > dma_map_sz) + return false; + + /* Make sure there's still at least one block available after + * aligning to block boundary, so that the txds used below + * won't wrap around the tx_ring. + */ + if (unlikely(nfp_net_tx_full(tx_ring, NFDK_TX_DESC_STOP_CNT))) { + if (!*completed) { + nfp_nfdk_xdp_complete(tx_ring); + *completed = true; + } + + if (unlikely(nfp_net_tx_full(tx_ring, NFDK_TX_DESC_STOP_CNT))) { + nfp_nfdk_rx_drop(dp, rx_ring->r_vec, rx_ring, rxbuf, + NULL); + return false; + } + } + + /* Check if cross block boundary */ + n_descs = nfp_nfdk_headlen_to_segs(pkt_len); + if ((round_down(tx_ring->wr_p, NFDK_TX_DESC_BLOCK_CNT) != + round_down(tx_ring->wr_p + n_descs, NFDK_TX_DESC_BLOCK_CNT)) || + ((u32)tx_ring->data_pending + pkt_len > + NFDK_TX_MAX_DATA_PER_BLOCK)) { + unsigned int nop_slots = D_BLOCK_CPL(tx_ring->wr_p); + + wr_idx = D_IDX(tx_ring, tx_ring->wr_p); + txd = &tx_ring->ktxds[wr_idx]; + memset(txd, 0, + array_size(nop_slots, sizeof(struct nfp_nfdk_tx_desc))); + + tx_ring->data_pending = 0; + tx_ring->wr_p += nop_slots; + tx_ring->wr_ptr_add += nop_slots; + } + + wr_idx = D_IDX(tx_ring, tx_ring->wr_p); + + txbuf = &tx_ring->ktxbufs[wr_idx]; + + txbuf[0].val = (unsigned long)rxbuf->frag | NFDK_TX_BUF_INFO_SOP; + txbuf[1].dma_addr = rxbuf->dma_addr; + /* Note: pkt len not stored */ + + dma_sync_single_for_device(dp->dev, rxbuf->dma_addr + dma_off, + pkt_len, DMA_BIDIRECTIONAL); + + /* Build TX descriptor */ + txd = &tx_ring->ktxds[wr_idx]; + dma_len = pkt_len; + dma_addr = rxbuf->dma_addr + dma_off; + + if (dma_len < NFDK_TX_MAX_DATA_PER_HEAD) + type = NFDK_DESC_TX_TYPE_SIMPLE; + else + type = NFDK_DESC_TX_TYPE_GATHER; + + /* FIELD_PREP() implicitly truncates to chunk */ + dma_len -= 1; + dlen_type = FIELD_PREP(NFDK_DESC_TX_DMA_LEN_HEAD, dma_len) | + FIELD_PREP(NFDK_DESC_TX_TYPE_HEAD, type); + + txd->dma_len_type = cpu_to_le16(dlen_type); + nfp_desc_set_dma_addr(txd, dma_addr); + + tmp_dlen = dlen_type & NFDK_DESC_TX_DMA_LEN_HEAD; + dma_len -= tmp_dlen; + dma_addr += tmp_dlen + 1; + txd++; + + while (dma_len > 0) { + dma_len -= 1; + dlen_type = FIELD_PREP(NFDK_DESC_TX_DMA_LEN, dma_len); + txd->dma_len_type = cpu_to_le16(dlen_type); + nfp_desc_set_dma_addr(txd, dma_addr); + + dlen_type &= NFDK_DESC_TX_DMA_LEN; + dma_len -= dlen_type; + dma_addr += dlen_type + 1; + txd++; + } + + (txd - 1)->dma_len_type = cpu_to_le16(dlen_type | NFDK_DESC_TX_EOP); + + /* Metadata desc */ + txd->raw = 0; + txd++; + + cnt = txd - tx_ring->ktxds - wr_idx; + tx_ring->wr_p += cnt; + if (tx_ring->wr_p % NFDK_TX_DESC_BLOCK_CNT) + tx_ring->data_pending += pkt_len; + else + tx_ring->data_pending = 0; + + tx_ring->wr_ptr_add += cnt; + return true; +} + +/** + * nfp_nfdk_rx() - receive up to @budget packets on @rx_ring + * @rx_ring: RX ring to receive from + * @budget: NAPI budget + * + * Note, this function is separated out from the napi poll function to + * more cleanly separate packet receive code from other bookkeeping + * functions performed in the napi poll function. + * + * Return: Number of packets received. + */ +static int nfp_nfdk_rx(struct nfp_net_rx_ring *rx_ring, int budget) +{ + struct nfp_net_r_vector *r_vec = rx_ring->r_vec; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; + struct nfp_net_tx_ring *tx_ring; + struct bpf_prog *xdp_prog; + bool xdp_tx_cmpl = false; + unsigned int true_bufsz; + struct sk_buff *skb; + int pkts_polled = 0; + struct xdp_buff xdp; + int idx; + + xdp_prog = READ_ONCE(dp->xdp_prog); + true_bufsz = xdp_prog ? PAGE_SIZE : dp->fl_bufsz; + xdp_init_buff(&xdp, PAGE_SIZE - NFP_NET_RX_BUF_HEADROOM, + &rx_ring->xdp_rxq); + tx_ring = r_vec->xdp_ring; + + while (pkts_polled < budget) { + unsigned int meta_len, data_len, meta_off, pkt_len, pkt_off; + struct nfp_net_rx_buf *rxbuf; + struct nfp_net_rx_desc *rxd; + struct nfp_meta_parsed meta; + bool redir_egress = false; + struct net_device *netdev; + dma_addr_t new_dma_addr; + u32 meta_len_xdp = 0; + void *new_frag; + + idx = D_IDX(rx_ring, rx_ring->rd_p); + + rxd = &rx_ring->rxds[idx]; + if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) + break; + + /* Memory barrier to ensure that we won't do other reads + * before the DD bit. + */ + dma_rmb(); + + memset(&meta, 0, sizeof(meta)); + + rx_ring->rd_p++; + pkts_polled++; + + rxbuf = &rx_ring->rxbufs[idx]; + /* < meta_len > + * <-- [rx_offset] --> + * --------------------------------------------------------- + * | [XX] | metadata | packet | XXXX | + * --------------------------------------------------------- + * <---------------- data_len ---------------> + * + * The rx_offset is fixed for all packets, the meta_len can vary + * on a packet by packet basis. If rx_offset is set to zero + * (_RX_OFFSET_DYNAMIC) metadata starts at the beginning of the + * buffer and is immediately followed by the packet (no [XX]). + */ + meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK; + data_len = le16_to_cpu(rxd->rxd.data_len); + pkt_len = data_len - meta_len; + + pkt_off = NFP_NET_RX_BUF_HEADROOM + dp->rx_dma_off; + if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) + pkt_off += meta_len; + else + pkt_off += dp->rx_offset; + meta_off = pkt_off - meta_len; + + /* Stats update */ + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->rx_pkts++; + r_vec->rx_bytes += pkt_len; + u64_stats_update_end(&r_vec->rx_sync); + + if (unlikely(meta_len > NFP_NET_MAX_PREPEND || + (dp->rx_offset && meta_len > dp->rx_offset))) { + nn_dp_warn(dp, "oversized RX packet metadata %u\n", + meta_len); + nfp_nfdk_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); + continue; + } + + nfp_net_dma_sync_cpu_rx(dp, rxbuf->dma_addr + meta_off, + data_len); + + if (meta_len) { + if (unlikely(nfp_nfdk_parse_meta(dp->netdev, &meta, + rxbuf->frag + meta_off, + rxbuf->frag + pkt_off, + pkt_len, meta_len))) { + nn_dp_warn(dp, "invalid RX packet metadata\n"); + nfp_nfdk_rx_drop(dp, r_vec, rx_ring, rxbuf, + NULL); + continue; + } + } + + if (xdp_prog && !meta.portid) { + void *orig_data = rxbuf->frag + pkt_off; + unsigned int dma_off; + int act; + + xdp_prepare_buff(&xdp, + rxbuf->frag + NFP_NET_RX_BUF_HEADROOM, + pkt_off - NFP_NET_RX_BUF_HEADROOM, + pkt_len, true); + + act = bpf_prog_run_xdp(xdp_prog, &xdp); + + pkt_len = xdp.data_end - xdp.data; + pkt_off += xdp.data - orig_data; + + switch (act) { + case XDP_PASS: + meta_len_xdp = xdp.data - xdp.data_meta; + break; + case XDP_TX: + dma_off = pkt_off - NFP_NET_RX_BUF_HEADROOM; + if (unlikely(!nfp_nfdk_tx_xdp_buf(dp, rx_ring, + tx_ring, + rxbuf, + dma_off, + pkt_len, + &xdp_tx_cmpl))) + trace_xdp_exception(dp->netdev, + xdp_prog, act); + continue; + default: + bpf_warn_invalid_xdp_action(dp->netdev, xdp_prog, act); + fallthrough; + case XDP_ABORTED: + trace_xdp_exception(dp->netdev, xdp_prog, act); + fallthrough; + case XDP_DROP: + nfp_nfdk_rx_give_one(dp, rx_ring, rxbuf->frag, + rxbuf->dma_addr); + continue; + } + } + + if (likely(!meta.portid)) { + netdev = dp->netdev; + } else if (meta.portid == NFP_META_PORT_ID_CTRL) { + struct nfp_net *nn = netdev_priv(dp->netdev); + + nfp_app_ctrl_rx_raw(nn->app, rxbuf->frag + pkt_off, + pkt_len); + nfp_nfdk_rx_give_one(dp, rx_ring, rxbuf->frag, + rxbuf->dma_addr); + continue; + } else { + struct nfp_net *nn; + + nn = netdev_priv(dp->netdev); + netdev = nfp_app_dev_get(nn->app, meta.portid, + &redir_egress); + if (unlikely(!netdev)) { + nfp_nfdk_rx_drop(dp, r_vec, rx_ring, rxbuf, + NULL); + continue; + } + + if (nfp_netdev_is_nfp_repr(netdev)) + nfp_repr_inc_rx_stats(netdev, pkt_len); + } + + skb = build_skb(rxbuf->frag, true_bufsz); + if (unlikely(!skb)) { + nfp_nfdk_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); + continue; + } + new_frag = nfp_nfdk_napi_alloc_one(dp, &new_dma_addr); + if (unlikely(!new_frag)) { + nfp_nfdk_rx_drop(dp, r_vec, rx_ring, rxbuf, skb); + continue; + } + + nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr); + + nfp_nfdk_rx_give_one(dp, rx_ring, new_frag, new_dma_addr); + + skb_reserve(skb, pkt_off); + skb_put(skb, pkt_len); + + skb->mark = meta.mark; + skb_set_hash(skb, meta.hash, meta.hash_type); + + skb_record_rx_queue(skb, rx_ring->idx); + skb->protocol = eth_type_trans(skb, netdev); + + nfp_nfdk_rx_csum(dp, r_vec, rxd, &meta, skb); + + if (rxd->rxd.flags & PCIE_DESC_RX_VLAN) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + le16_to_cpu(rxd->rxd.vlan)); + if (meta_len_xdp) + skb_metadata_set(skb, meta_len_xdp); + + if (likely(!redir_egress)) { + napi_gro_receive(&rx_ring->r_vec->napi, skb); + } else { + skb->dev = netdev; + skb_reset_network_header(skb); + __skb_push(skb, ETH_HLEN); + dev_queue_xmit(skb); + } + } + + if (xdp_prog) { + if (tx_ring->wr_ptr_add) + nfp_net_tx_xmit_more_flush(tx_ring); + else if (unlikely(tx_ring->wr_p != tx_ring->rd_p) && + !xdp_tx_cmpl) + if (!nfp_nfdk_xdp_complete(tx_ring)) + pkts_polled = budget; + } + + return pkts_polled; +} + +/** + * nfp_nfdk_poll() - napi poll function + * @napi: NAPI structure + * @budget: NAPI budget + * + * Return: number of packets polled. + */ +int nfp_nfdk_poll(struct napi_struct *napi, int budget) +{ + struct nfp_net_r_vector *r_vec = + container_of(napi, struct nfp_net_r_vector, napi); + unsigned int pkts_polled = 0; + + if (r_vec->tx_ring) + nfp_nfdk_tx_complete(r_vec->tx_ring, budget); + if (r_vec->rx_ring) + pkts_polled = nfp_nfdk_rx(r_vec->rx_ring, budget); + + if (pkts_polled < budget) + if (napi_complete_done(napi, pkts_polled)) + nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_entry); + + if (r_vec->nfp_net->rx_coalesce_adapt_on && r_vec->rx_ring) { + struct dim_sample dim_sample = {}; + unsigned int start; + u64 pkts, bytes; + + do { + start = u64_stats_fetch_begin(&r_vec->rx_sync); + pkts = r_vec->rx_pkts; + bytes = r_vec->rx_bytes; + } while (u64_stats_fetch_retry(&r_vec->rx_sync, start)); + + dim_update_sample(r_vec->event_ctr, pkts, bytes, &dim_sample); + net_dim(&r_vec->rx_dim, dim_sample); + } + + if (r_vec->nfp_net->tx_coalesce_adapt_on && r_vec->tx_ring) { + struct dim_sample dim_sample = {}; + unsigned int start; + u64 pkts, bytes; + + do { + start = u64_stats_fetch_begin(&r_vec->tx_sync); + pkts = r_vec->tx_pkts; + bytes = r_vec->tx_bytes; + } while (u64_stats_fetch_retry(&r_vec->tx_sync, start)); + + dim_update_sample(r_vec->event_ctr, pkts, bytes, &dim_sample); + net_dim(&r_vec->tx_dim, dim_sample); + } + + return pkts_polled; +} + +/* Control device data path + */ + +bool +nfp_nfdk_ctrl_tx_one(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, + struct sk_buff *skb, bool old) +{ + u32 cnt, tmp_dlen, dlen_type = 0; + struct nfp_net_tx_ring *tx_ring; + struct nfp_nfdk_tx_buf *txbuf; + struct nfp_nfdk_tx_desc *txd; + unsigned int dma_len, type; + struct nfp_net_dp *dp; + dma_addr_t dma_addr; + u64 metadata = 0; + int wr_idx; + + dp = &r_vec->nfp_net->dp; + tx_ring = r_vec->tx_ring; + + if (WARN_ON_ONCE(skb_shinfo(skb)->nr_frags)) { + nn_dp_warn(dp, "Driver's CTRL TX does not implement gather\n"); + goto err_free; + } + + /* Don't bother counting frags, assume the worst */ + if (unlikely(nfp_net_tx_full(tx_ring, NFDK_TX_DESC_STOP_CNT))) { + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_busy++; + u64_stats_update_end(&r_vec->tx_sync); + if (!old) + __skb_queue_tail(&r_vec->queue, skb); + else + __skb_queue_head(&r_vec->queue, skb); + return NETDEV_TX_BUSY; + } + + if (nfp_app_ctrl_has_meta(nn->app)) { + if (unlikely(skb_headroom(skb) < 8)) { + nn_dp_warn(dp, "CTRL TX on skb without headroom\n"); + goto err_free; + } + metadata = NFDK_DESC_TX_CHAIN_META; + put_unaligned_be32(NFP_META_PORT_ID_CTRL, skb_push(skb, 4)); + put_unaligned_be32(FIELD_PREP(NFDK_META_LEN, 8) | + FIELD_PREP(NFDK_META_FIELDS, + NFP_NET_META_PORTID), + skb_push(skb, 4)); + } + + if (nfp_nfdk_tx_maybe_close_block(tx_ring, 0, skb)) + goto err_free; + + /* DMA map all */ + wr_idx = D_IDX(tx_ring, tx_ring->wr_p); + txd = &tx_ring->ktxds[wr_idx]; + txbuf = &tx_ring->ktxbufs[wr_idx]; + + dma_len = skb_headlen(skb); + if (dma_len < NFDK_TX_MAX_DATA_PER_HEAD) + type = NFDK_DESC_TX_TYPE_SIMPLE; + else + type = NFDK_DESC_TX_TYPE_GATHER; + + dma_addr = dma_map_single(dp->dev, skb->data, dma_len, DMA_TO_DEVICE); + if (dma_mapping_error(dp->dev, dma_addr)) + goto err_warn_dma; + + txbuf->skb = skb; + txbuf++; + + txbuf->dma_addr = dma_addr; + txbuf++; + + dma_len -= 1; + dlen_type = FIELD_PREP(NFDK_DESC_TX_DMA_LEN_HEAD, dma_len) | + FIELD_PREP(NFDK_DESC_TX_TYPE_HEAD, type); + + txd->dma_len_type = cpu_to_le16(dlen_type); + nfp_desc_set_dma_addr(txd, dma_addr); + + tmp_dlen = dlen_type & NFDK_DESC_TX_DMA_LEN_HEAD; + dma_len -= tmp_dlen; + dma_addr += tmp_dlen + 1; + txd++; + + while (dma_len > 0) { + dma_len -= 1; + dlen_type = FIELD_PREP(NFDK_DESC_TX_DMA_LEN, dma_len); + txd->dma_len_type = cpu_to_le16(dlen_type); + nfp_desc_set_dma_addr(txd, dma_addr); + + dlen_type &= NFDK_DESC_TX_DMA_LEN; + dma_len -= dlen_type; + dma_addr += dlen_type + 1; + txd++; + } + + (txd - 1)->dma_len_type = cpu_to_le16(dlen_type | NFDK_DESC_TX_EOP); + + /* Metadata desc */ + txd->raw = cpu_to_le64(metadata); + txd++; + + cnt = txd - tx_ring->ktxds - wr_idx; + if (unlikely(round_down(wr_idx, NFDK_TX_DESC_BLOCK_CNT) != + round_down(wr_idx + cnt - 1, NFDK_TX_DESC_BLOCK_CNT))) + goto err_warn_overflow; + + tx_ring->wr_p += cnt; + if (tx_ring->wr_p % NFDK_TX_DESC_BLOCK_CNT) + tx_ring->data_pending += skb->len; + else + tx_ring->data_pending = 0; + + tx_ring->wr_ptr_add += cnt; + nfp_net_tx_xmit_more_flush(tx_ring); + + return NETDEV_TX_OK; + +err_warn_overflow: + WARN_ONCE(1, "unable to fit packet into a descriptor wr_idx:%d head:%d frags:%d cnt:%d", + wr_idx, skb_headlen(skb), 0, cnt); + txbuf--; + dma_unmap_single(dp->dev, txbuf->dma_addr, + skb_headlen(skb), DMA_TO_DEVICE); + txbuf->raw = 0; +err_warn_dma: + nn_dp_warn(dp, "Failed to map DMA TX buffer\n"); +err_free: + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_errors++; + u64_stats_update_end(&r_vec->tx_sync); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +static void __nfp_ctrl_tx_queued(struct nfp_net_r_vector *r_vec) +{ + struct sk_buff *skb; + + while ((skb = __skb_dequeue(&r_vec->queue))) + if (nfp_nfdk_ctrl_tx_one(r_vec->nfp_net, r_vec, skb, true)) + return; +} + +static bool +nfp_ctrl_meta_ok(struct nfp_net *nn, void *data, unsigned int meta_len) +{ + u32 meta_type, meta_tag; + + if (!nfp_app_ctrl_has_meta(nn->app)) + return !meta_len; + + if (meta_len != 8) + return false; + + meta_type = get_unaligned_be32(data); + meta_tag = get_unaligned_be32(data + 4); + + return (meta_type == NFP_NET_META_PORTID && + meta_tag == NFP_META_PORT_ID_CTRL); +} + +static bool +nfp_ctrl_rx_one(struct nfp_net *nn, struct nfp_net_dp *dp, + struct nfp_net_r_vector *r_vec, struct nfp_net_rx_ring *rx_ring) +{ + unsigned int meta_len, data_len, meta_off, pkt_len, pkt_off; + struct nfp_net_rx_buf *rxbuf; + struct nfp_net_rx_desc *rxd; + dma_addr_t new_dma_addr; + struct sk_buff *skb; + void *new_frag; + int idx; + + idx = D_IDX(rx_ring, rx_ring->rd_p); + + rxd = &rx_ring->rxds[idx]; + if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) + return false; + + /* Memory barrier to ensure that we won't do other reads + * before the DD bit. + */ + dma_rmb(); + + rx_ring->rd_p++; + + rxbuf = &rx_ring->rxbufs[idx]; + meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK; + data_len = le16_to_cpu(rxd->rxd.data_len); + pkt_len = data_len - meta_len; + + pkt_off = NFP_NET_RX_BUF_HEADROOM + dp->rx_dma_off; + if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) + pkt_off += meta_len; + else + pkt_off += dp->rx_offset; + meta_off = pkt_off - meta_len; + + /* Stats update */ + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->rx_pkts++; + r_vec->rx_bytes += pkt_len; + u64_stats_update_end(&r_vec->rx_sync); + + nfp_net_dma_sync_cpu_rx(dp, rxbuf->dma_addr + meta_off, data_len); + + if (unlikely(!nfp_ctrl_meta_ok(nn, rxbuf->frag + meta_off, meta_len))) { + nn_dp_warn(dp, "incorrect metadata for ctrl packet (%d)\n", + meta_len); + nfp_nfdk_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); + return true; + } + + skb = build_skb(rxbuf->frag, dp->fl_bufsz); + if (unlikely(!skb)) { + nfp_nfdk_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); + return true; + } + new_frag = nfp_nfdk_napi_alloc_one(dp, &new_dma_addr); + if (unlikely(!new_frag)) { + nfp_nfdk_rx_drop(dp, r_vec, rx_ring, rxbuf, skb); + return true; + } + + nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr); + + nfp_nfdk_rx_give_one(dp, rx_ring, new_frag, new_dma_addr); + + skb_reserve(skb, pkt_off); + skb_put(skb, pkt_len); + + nfp_app_ctrl_rx(nn->app, skb); + + return true; +} + +static bool nfp_ctrl_rx(struct nfp_net_r_vector *r_vec) +{ + struct nfp_net_rx_ring *rx_ring = r_vec->rx_ring; + struct nfp_net *nn = r_vec->nfp_net; + struct nfp_net_dp *dp = &nn->dp; + unsigned int budget = 512; + + while (nfp_ctrl_rx_one(nn, dp, r_vec, rx_ring) && budget--) + continue; + + return budget; +} + +void nfp_nfdk_ctrl_poll(struct tasklet_struct *t) +{ + struct nfp_net_r_vector *r_vec = from_tasklet(r_vec, t, tasklet); + + spin_lock(&r_vec->lock); + nfp_nfdk_tx_complete(r_vec->tx_ring, 0); + __nfp_ctrl_tx_queued(r_vec); + spin_unlock(&r_vec->lock); + + if (nfp_ctrl_rx(r_vec)) { + nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_entry); + } else { + tasklet_schedule(&r_vec->tasklet); + nn_dp_warn(&r_vec->nfp_net->dp, + "control message budget exceeded!\n"); + } +} diff --git a/drivers/net/ethernet/netronome/nfp/nfdk/nfdk.h b/drivers/net/ethernet/netronome/nfp/nfdk/nfdk.h new file mode 100644 index 000000000000..c41e0975eb73 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfdk/nfdk.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* Copyright (C) 2019 Netronome Systems, Inc. */ + +#ifndef _NFP_DP_NFDK_H_ +#define _NFP_DP_NFDK_H_ + +#include +#include + +#define NFDK_TX_DESC_PER_SIMPLE_PKT 2 + +#define NFDK_TX_MAX_DATA_PER_HEAD SZ_4K +#define NFDK_TX_MAX_DATA_PER_DESC SZ_16K +#define NFDK_TX_DESC_BLOCK_SZ 256 +#define NFDK_TX_DESC_BLOCK_CNT (NFDK_TX_DESC_BLOCK_SZ / \ + sizeof(struct nfp_nfdk_tx_desc)) +#define NFDK_TX_DESC_STOP_CNT (NFDK_TX_DESC_BLOCK_CNT * \ + NFDK_TX_DESC_PER_SIMPLE_PKT) +#define NFDK_TX_MAX_DATA_PER_BLOCK SZ_64K +#define NFDK_TX_DESC_GATHER_MAX 17 + +/* TX descriptor format */ + +#define NFDK_DESC_TX_MSS_MASK GENMASK(13, 0) + +#define NFDK_DESC_TX_CHAIN_META BIT(3) +#define NFDK_DESC_TX_ENCAP BIT(2) +#define NFDK_DESC_TX_L4_CSUM BIT(1) +#define NFDK_DESC_TX_L3_CSUM BIT(0) + +#define NFDK_DESC_TX_DMA_LEN_HEAD GENMASK(11, 0) +#define NFDK_DESC_TX_TYPE_HEAD GENMASK(15, 12) +#define NFDK_DESC_TX_DMA_LEN GENMASK(13, 0) +#define NFDK_DESC_TX_TYPE_NOP 0 +#define NFDK_DESC_TX_TYPE_GATHER 1 +#define NFDK_DESC_TX_TYPE_TSO 2 +#define NFDK_DESC_TX_TYPE_SIMPLE 8 +#define NFDK_DESC_TX_EOP BIT(14) + +#define NFDK_META_LEN GENMASK(7, 0) +#define NFDK_META_FIELDS GENMASK(31, 8) + +#define D_BLOCK_CPL(idx) (NFDK_TX_DESC_BLOCK_CNT - \ + (idx) % NFDK_TX_DESC_BLOCK_CNT) + +struct nfp_nfdk_tx_desc { + union { + struct { + u8 dma_addr_hi; /* High bits of host buf address */ + u8 padding; /* Must be zero */ + __le16 dma_len_type; /* Length to DMA for this desc */ + __le32 dma_addr_lo; /* Low 32bit of host buf addr */ + }; + + struct { + __le16 mss; /* MSS to be used for LSO */ + u8 lso_hdrlen; /* LSO, TCP payload offset */ + u8 lso_totsegs; /* LSO, total segments */ + u8 l3_offset; /* L3 header offset */ + u8 l4_offset; /* L4 header offset */ + __le16 lso_meta_res; /* Rsvd bits in TSO metadata */ + }; + + struct { + u8 flags; /* TX Flags, see @NFDK_DESC_TX_* */ + u8 reserved[7]; /* meta byte placeholder */ + }; + + __le32 vals[2]; + __le64 raw; + }; +}; + +/* The device don't make use of the 2 or 3 least significant bits of the address + * due to alignment constraints. The driver can make use of those bits to carry + * information about the buffer before giving it to the device. + * + * NOTE: The driver must clear the lower bits before handing the buffer to the + * device. + * + * - NFDK_TX_BUF_INFO_SOP - Start of a packet + * Mark the buffer as a start of a packet. This is used in the XDP TX process + * to stash virtual and DMA address so that they can be recycled when the TX + * operation is completed. + */ +#define NFDK_TX_BUF_PTR(val) ((val) & ~(sizeof(void *) - 1)) +#define NFDK_TX_BUF_INFO(val) ((val) & (sizeof(void *) - 1)) +#define NFDK_TX_BUF_INFO_SOP BIT(0) + +struct nfp_nfdk_tx_buf { + union { + /* First slot */ + union { + struct sk_buff *skb; + void *frag; + unsigned long val; + }; + + /* 1 + nr_frags next slots */ + dma_addr_t dma_addr; + + /* TSO (optional) */ + struct { + u32 pkt_cnt; + u32 real_len; + }; + + u64 raw; + }; +}; + +static inline int nfp_nfdk_headlen_to_segs(unsigned int headlen) +{ + /* First descriptor fits less data, so adjust for that */ + return DIV_ROUND_UP(headlen + + NFDK_TX_MAX_DATA_PER_DESC - + NFDK_TX_MAX_DATA_PER_HEAD, + NFDK_TX_MAX_DATA_PER_DESC); +} + +int nfp_nfdk_poll(struct napi_struct *napi, int budget); +netdev_tx_t nfp_nfdk_tx(struct sk_buff *skb, struct net_device *netdev); +bool +nfp_nfdk_ctrl_tx_one(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, + struct sk_buff *skb, bool old); +void nfp_nfdk_ctrl_poll(struct tasklet_struct *t); +void nfp_nfdk_rx_ring_fill_freelist(struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring); +#endif diff --git a/drivers/net/ethernet/netronome/nfp/nfdk/rings.c b/drivers/net/ethernet/netronome/nfp/nfdk/rings.c new file mode 100644 index 000000000000..301f11108826 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfdk/rings.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (C) 2019 Netronome Systems, Inc. */ + +#include + +#include "../nfp_net.h" +#include "../nfp_net_dp.h" +#include "nfdk.h" + +static void +nfp_nfdk_tx_ring_reset(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) +{ + struct device *dev = dp->dev; + struct netdev_queue *nd_q; + + while (!tx_ring->is_xdp && tx_ring->rd_p != tx_ring->wr_p) { + const skb_frag_t *frag, *fend; + unsigned int size, n_descs = 1; + struct nfp_nfdk_tx_buf *txbuf; + int nr_frags, rd_idx; + struct sk_buff *skb; + + rd_idx = D_IDX(tx_ring, tx_ring->rd_p); + txbuf = &tx_ring->ktxbufs[rd_idx]; + + skb = txbuf->skb; + if (!skb) { + n_descs = D_BLOCK_CPL(tx_ring->rd_p); + goto next; + } + + nr_frags = skb_shinfo(skb)->nr_frags; + txbuf++; + + /* Unmap head */ + size = skb_headlen(skb); + dma_unmap_single(dev, txbuf->dma_addr, size, DMA_TO_DEVICE); + n_descs += nfp_nfdk_headlen_to_segs(size); + txbuf++; + + frag = skb_shinfo(skb)->frags; + fend = frag + nr_frags; + for (; frag < fend; frag++) { + size = skb_frag_size(frag); + dma_unmap_page(dev, txbuf->dma_addr, + skb_frag_size(frag), DMA_TO_DEVICE); + n_descs += DIV_ROUND_UP(size, + NFDK_TX_MAX_DATA_PER_DESC); + txbuf++; + } + + if (skb_is_gso(skb)) + n_descs++; + + dev_kfree_skb_any(skb); +next: + tx_ring->rd_p += n_descs; + } + + memset(tx_ring->txds, 0, tx_ring->size); + tx_ring->data_pending = 0; + tx_ring->wr_p = 0; + tx_ring->rd_p = 0; + tx_ring->qcp_rd_p = 0; + tx_ring->wr_ptr_add = 0; + + if (tx_ring->is_xdp || !dp->netdev) + return; + + nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx); + netdev_tx_reset_queue(nd_q); +} + +static void nfp_nfdk_tx_ring_free(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; + + kvfree(tx_ring->ktxbufs); + + if (tx_ring->ktxds) + dma_free_coherent(dp->dev, tx_ring->size, + tx_ring->ktxds, tx_ring->dma); + + tx_ring->cnt = 0; + tx_ring->txbufs = NULL; + tx_ring->txds = NULL; + tx_ring->dma = 0; + tx_ring->size = 0; +} + +static int +nfp_nfdk_tx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + + tx_ring->cnt = dp->txd_cnt * NFDK_TX_DESC_PER_SIMPLE_PKT; + tx_ring->size = array_size(tx_ring->cnt, sizeof(*tx_ring->ktxds)); + tx_ring->ktxds = dma_alloc_coherent(dp->dev, tx_ring->size, + &tx_ring->dma, + GFP_KERNEL | __GFP_NOWARN); + if (!tx_ring->ktxds) { + netdev_warn(dp->netdev, "failed to allocate TX descriptor ring memory, requested descriptor count: %d, consider lowering descriptor count\n", + tx_ring->cnt); + goto err_alloc; + } + + tx_ring->ktxbufs = kvcalloc(tx_ring->cnt, sizeof(*tx_ring->ktxbufs), + GFP_KERNEL); + if (!tx_ring->ktxbufs) + goto err_alloc; + + if (!tx_ring->is_xdp && dp->netdev) + netif_set_xps_queue(dp->netdev, &r_vec->affinity_mask, + tx_ring->idx); + + return 0; + +err_alloc: + nfp_nfdk_tx_ring_free(tx_ring); + return -ENOMEM; +} + +static void +nfp_nfdk_tx_ring_bufs_free(struct nfp_net_dp *dp, + struct nfp_net_tx_ring *tx_ring) +{ +} + +static int +nfp_nfdk_tx_ring_bufs_alloc(struct nfp_net_dp *dp, + struct nfp_net_tx_ring *tx_ring) +{ + return 0; +} + +static void +nfp_nfdk_print_tx_descs(struct seq_file *file, + struct nfp_net_r_vector *r_vec, + struct nfp_net_tx_ring *tx_ring, + u32 d_rd_p, u32 d_wr_p) +{ + struct nfp_nfdk_tx_desc *txd; + u32 txd_cnt = tx_ring->cnt; + int i; + + for (i = 0; i < txd_cnt; i++) { + txd = &tx_ring->ktxds[i]; + + seq_printf(file, "%04d: 0x%08x 0x%08x 0x%016llx", i, + txd->vals[0], txd->vals[1], tx_ring->ktxbufs[i].raw); + + if (i == tx_ring->rd_p % txd_cnt) + seq_puts(file, " H_RD"); + if (i == tx_ring->wr_p % txd_cnt) + seq_puts(file, " H_WR"); + if (i == d_rd_p % txd_cnt) + seq_puts(file, " D_RD"); + if (i == d_wr_p % txd_cnt) + seq_puts(file, " D_WR"); + + seq_putc(file, '\n'); + } +} + +#define NFP_NFDK_CFG_CTRL_SUPPORTED \ + (NFP_NET_CFG_CTRL_ENABLE | NFP_NET_CFG_CTRL_PROMISC | \ + NFP_NET_CFG_CTRL_L2BC | NFP_NET_CFG_CTRL_L2MC | \ + NFP_NET_CFG_CTRL_RXCSUM | NFP_NET_CFG_CTRL_TXCSUM | \ + NFP_NET_CFG_CTRL_RXVLAN | \ + NFP_NET_CFG_CTRL_GATHER | NFP_NET_CFG_CTRL_LSO | \ + NFP_NET_CFG_CTRL_CTAG_FILTER | NFP_NET_CFG_CTRL_CMSG_DATA | \ + NFP_NET_CFG_CTRL_RINGCFG | NFP_NET_CFG_CTRL_IRQMOD | \ + NFP_NET_CFG_CTRL_TXRWB | \ + NFP_NET_CFG_CTRL_VXLAN | NFP_NET_CFG_CTRL_NVGRE | \ + NFP_NET_CFG_CTRL_BPF | NFP_NET_CFG_CTRL_LSO2 | \ + NFP_NET_CFG_CTRL_RSS2 | NFP_NET_CFG_CTRL_CSUM_COMPLETE | \ + NFP_NET_CFG_CTRL_LIVE_ADDR) + +const struct nfp_dp_ops nfp_nfdk_ops = { + .version = NFP_NFD_VER_NFDK, + .tx_min_desc_per_pkt = NFDK_TX_DESC_PER_SIMPLE_PKT, + .cap_mask = NFP_NFDK_CFG_CTRL_SUPPORTED, + .poll = nfp_nfdk_poll, + .ctrl_poll = nfp_nfdk_ctrl_poll, + .xmit = nfp_nfdk_tx, + .ctrl_tx_one = nfp_nfdk_ctrl_tx_one, + .rx_ring_fill_freelist = nfp_nfdk_rx_ring_fill_freelist, + .tx_ring_alloc = nfp_nfdk_tx_ring_alloc, + .tx_ring_reset = nfp_nfdk_tx_ring_reset, + .tx_ring_free = nfp_nfdk_tx_ring_free, + .tx_ring_bufs_alloc = nfp_nfdk_tx_ring_bufs_alloc, + .tx_ring_bufs_free = nfp_nfdk_tx_ring_bufs_free, + .print_tx_descs = nfp_nfdk_print_tx_descs +}; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.c b/drivers/net/ethernet/netronome/nfp/nfp_app.c index 3a973282b2bb..09f250e74dfa 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.c @@ -121,7 +121,7 @@ struct nfp_reprs * nfp_reprs_get_locked(struct nfp_app *app, enum nfp_repr_type type) { return rcu_dereference_protected(app->reprs[type], - lockdep_is_held(&app->pf->lock)); + nfp_app_is_locked(app)); } struct nfp_reprs * diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index 3e9baff07100..dd56207df246 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -75,7 +75,7 @@ extern const struct nfp_app_type app_abm; * @bpf: BPF ndo offload-related calls * @xdp_offload: offload an XDP program * @eswitch_mode_get: get SR-IOV eswitch mode - * @eswitch_mode_set: set SR-IOV eswitch mode (under pf->lock) + * @eswitch_mode_set: set SR-IOV eswitch mode * @sriov_enable: app-specific sriov initialisation * @sriov_disable: app-specific sriov clean-up * @dev_get: get representor or internal port representing netdev @@ -174,6 +174,16 @@ struct nfp_app { void *priv; }; +static inline void assert_nfp_app_locked(struct nfp_app *app) +{ + devl_assert_locked(priv_to_devlink(app->pf)); +} + +static inline bool nfp_app_is_locked(struct nfp_app *app) +{ + return devl_lock_is_held(priv_to_devlink(app->pf)); +} + void nfp_check_rhashtable_empty(void *ptr, void *arg); bool __nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb); bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c index bea978df7713..405786c00334 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c @@ -26,12 +26,11 @@ nfp_devlink_fill_eth_port(struct nfp_port *port, } static int -nfp_devlink_fill_eth_port_from_id(struct nfp_pf *pf, unsigned int port_index, +nfp_devlink_fill_eth_port_from_id(struct nfp_pf *pf, + struct devlink_port *dl_port, struct nfp_eth_table_port *copy) { - struct nfp_port *port; - - port = nfp_port_from_id(pf, NFP_PORT_PHYS_PORT, port_index); + struct nfp_port *port = container_of(dl_port, struct nfp_port, dl_port); return nfp_devlink_fill_eth_port(port, copy); } @@ -62,7 +61,7 @@ nfp_devlink_set_lanes(struct nfp_pf *pf, unsigned int idx, unsigned int lanes) } static int -nfp_devlink_port_split(struct devlink *devlink, unsigned int port_index, +nfp_devlink_port_split(struct devlink *devlink, struct devlink_port *port, unsigned int count, struct netlink_ext_ack *extack) { struct nfp_pf *pf = devlink_priv(devlink); @@ -70,33 +69,25 @@ nfp_devlink_port_split(struct devlink *devlink, unsigned int port_index, unsigned int lanes; int ret; - mutex_lock(&pf->lock); - rtnl_lock(); - ret = nfp_devlink_fill_eth_port_from_id(pf, port_index, ð_port); + ret = nfp_devlink_fill_eth_port_from_id(pf, port, ð_port); rtnl_unlock(); if (ret) - goto out; + return ret; - if (eth_port.port_lanes % count) { - ret = -EINVAL; - goto out; - } + if (eth_port.port_lanes % count) + return -EINVAL; /* Special case the 100G CXP -> 2x40G split */ lanes = eth_port.port_lanes / count; if (eth_port.lanes == 10 && count == 2) lanes = 8 / count; - ret = nfp_devlink_set_lanes(pf, eth_port.index, lanes); -out: - mutex_unlock(&pf->lock); - - return ret; + return nfp_devlink_set_lanes(pf, eth_port.index, lanes); } static int -nfp_devlink_port_unsplit(struct devlink *devlink, unsigned int port_index, +nfp_devlink_port_unsplit(struct devlink *devlink, struct devlink_port *port, struct netlink_ext_ack *extack) { struct nfp_pf *pf = devlink_priv(devlink); @@ -104,29 +95,21 @@ nfp_devlink_port_unsplit(struct devlink *devlink, unsigned int port_index, unsigned int lanes; int ret; - mutex_lock(&pf->lock); - rtnl_lock(); - ret = nfp_devlink_fill_eth_port_from_id(pf, port_index, ð_port); + ret = nfp_devlink_fill_eth_port_from_id(pf, port, ð_port); rtnl_unlock(); if (ret) - goto out; + return ret; - if (!eth_port.is_split) { - ret = -EINVAL; - goto out; - } + if (!eth_port.is_split) + return -EINVAL; /* Special case the 100G CXP -> 2x40G unsplit */ lanes = eth_port.port_lanes; if (eth_port.port_lanes == 8) lanes = 10; - ret = nfp_devlink_set_lanes(pf, eth_port.index, lanes); -out: - mutex_unlock(&pf->lock); - - return ret; + return nfp_devlink_set_lanes(pf, eth_port.index, lanes); } static int @@ -161,13 +144,8 @@ static int nfp_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, struct netlink_ext_ack *extack) { struct nfp_pf *pf = devlink_priv(devlink); - int ret; - mutex_lock(&pf->lock); - ret = nfp_app_eswitch_mode_set(pf->app, mode); - mutex_unlock(&pf->lock); - - return ret; + return nfp_app_eswitch_mode_set(pf->app, mode); } static const struct nfp_devlink_versions_simple { @@ -375,12 +353,12 @@ int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port) devlink = priv_to_devlink(app->pf); - return devlink_port_register(devlink, &port->dl_port, port->eth_id); + return devl_port_register(devlink, &port->dl_port, port->eth_id); } void nfp_devlink_port_unregister(struct nfp_port *port) { - devlink_port_unregister(&port->dl_port); + devl_port_unregister(&port->dl_port); } void nfp_devlink_port_type_eth_set(struct nfp_port *port) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index bb3b8a7f6c5d..eeda39e34f84 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -19,6 +19,7 @@ #include "nfpcore/nfp.h" #include "nfpcore/nfp_cpp.h" +#include "nfpcore/nfp_dev.h" #include "nfpcore/nfp_nffw.h" #include "nfpcore/nfp_nsp.h" @@ -32,17 +33,21 @@ static const char nfp_driver_name[] = "nfp"; static const struct pci_device_id nfp_pci_device_ids[] = { - { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP6000, + { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP3800, PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, - PCI_ANY_ID, 0, - }, - { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP5000, - PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, - PCI_ANY_ID, 0, + PCI_ANY_ID, 0, NFP_DEV_NFP3800, }, { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP4000, PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, - PCI_ANY_ID, 0, + PCI_ANY_ID, 0, NFP_DEV_NFP6000, + }, + { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP5000, + PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, + PCI_ANY_ID, 0, NFP_DEV_NFP6000, + }, + { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP6000, + PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, + PCI_ANY_ID, 0, NFP_DEV_NFP6000, }, { 0, } /* Required last entry. */ }; @@ -222,6 +227,7 @@ static int nfp_pcie_sriov_enable(struct pci_dev *pdev, int num_vfs) { #ifdef CONFIG_PCI_IOV struct nfp_pf *pf = pci_get_drvdata(pdev); + struct devlink *devlink; int err; if (num_vfs > pf->limit_vfs) { @@ -236,7 +242,8 @@ static int nfp_pcie_sriov_enable(struct pci_dev *pdev, int num_vfs) return err; } - mutex_lock(&pf->lock); + devlink = priv_to_devlink(pf); + devl_lock(devlink); err = nfp_app_sriov_enable(pf->app, num_vfs); if (err) { @@ -250,11 +257,11 @@ static int nfp_pcie_sriov_enable(struct pci_dev *pdev, int num_vfs) dev_dbg(&pdev->dev, "Created %d VFs.\n", pf->num_vfs); - mutex_unlock(&pf->lock); + devl_unlock(devlink); return num_vfs; err_sriov_disable: - mutex_unlock(&pf->lock); + devl_unlock(devlink); pci_disable_sriov(pdev); return err; #endif @@ -265,8 +272,10 @@ static int nfp_pcie_sriov_disable(struct pci_dev *pdev) { #ifdef CONFIG_PCI_IOV struct nfp_pf *pf = pci_get_drvdata(pdev); + struct devlink *devlink; - mutex_lock(&pf->lock); + devlink = priv_to_devlink(pf); + devl_lock(devlink); /* If the VFs are assigned we cannot shut down SR-IOV without * causing issues, so just leave the hardware available but @@ -274,7 +283,7 @@ static int nfp_pcie_sriov_disable(struct pci_dev *pdev) */ if (pci_vfs_assigned(pdev)) { dev_warn(&pdev->dev, "Disabling while VFs assigned - VFs will not be deallocated\n"); - mutex_unlock(&pf->lock); + devl_unlock(devlink); return -EPERM; } @@ -282,7 +291,7 @@ static int nfp_pcie_sriov_disable(struct pci_dev *pdev) pf->num_vfs = 0; - mutex_unlock(&pf->lock); + devl_unlock(devlink); pci_disable_sriov(pdev); dev_dbg(&pdev->dev, "Removed VFs.\n"); @@ -667,6 +676,7 @@ static int nfp_pf_find_rtsyms(struct nfp_pf *pf) static int nfp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) { + const struct nfp_dev_info *dev_info; struct devlink *devlink; struct nfp_pf *pf; int err; @@ -675,14 +685,15 @@ static int nfp_pci_probe(struct pci_dev *pdev, pdev->device == PCI_DEVICE_ID_NETRONOME_NFP6000_VF) dev_warn(&pdev->dev, "Binding NFP VF device to the NFP PF driver, the VF driver is called 'nfp_netvf'\n"); + dev_info = &nfp_dev_info[pci_id->driver_data]; + err = pci_enable_device(pdev); if (err < 0) return err; pci_set_master(pdev); - err = dma_set_mask_and_coherent(&pdev->dev, - DMA_BIT_MASK(NFP_NET_MAX_DMA_BITS)); + err = dma_set_mask_and_coherent(&pdev->dev, dev_info->dma_mask); if (err) goto err_pci_disable; @@ -700,9 +711,9 @@ static int nfp_pci_probe(struct pci_dev *pdev, pf = devlink_priv(devlink); INIT_LIST_HEAD(&pf->vnics); INIT_LIST_HEAD(&pf->ports); - mutex_init(&pf->lock); pci_set_drvdata(pdev, pf); pf->pdev = pdev; + pf->dev_info = dev_info; pf->wq = alloc_workqueue("nfp-%s", 0, 2, pci_name(pdev)); if (!pf->wq) { @@ -710,7 +721,7 @@ static int nfp_pci_probe(struct pci_dev *pdev, goto err_pci_priv_unset; } - pf->cpp = nfp_cpp_from_nfp6000_pcie(pdev); + pf->cpp = nfp_cpp_from_nfp6000_pcie(pdev, dev_info); if (IS_ERR(pf->cpp)) { err = PTR_ERR(pf->cpp); goto err_disable_msix; @@ -790,7 +801,6 @@ static int nfp_pci_probe(struct pci_dev *pdev, destroy_workqueue(pf->wq); err_pci_priv_unset: pci_set_drvdata(pdev, NULL); - mutex_destroy(&pf->lock); devlink_free(devlink); err_rel_regions: pci_release_regions(pdev); @@ -827,7 +837,6 @@ static void __nfp_pci_shutdown(struct pci_dev *pdev, bool unload_fw) kfree(pf->eth_tbl); kfree(pf->nspi); - mutex_destroy(&pf->lock); devlink_free(priv_to_devlink(pf)); pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index a7dede946a33..f56ca11de134 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -48,6 +47,7 @@ struct nfp_dumpspec { /** * struct nfp_pf - NFP PF-specific device structure * @pdev: Backpointer to PCI device + * @dev_info: NFP ASIC params * @cpp: Pointer to the CPP handle * @app: Pointer to the APP handle * @data_vnic_bar: Pointer to the CPP area for the data vNICs' BARs @@ -84,10 +84,12 @@ struct nfp_dumpspec { * @port_refresh_work: Work entry for taking netdevs out * @shared_bufs: Array of shared buffer structures if FW has any SBs * @num_shared_bufs: Number of elements in @shared_bufs - * @lock: Protects all fields which may change after probe + * + * Fields which may change after proble are protected by devlink instance lock. */ struct nfp_pf { struct pci_dev *pdev; + const struct nfp_dev_info *dev_info; struct nfp_cpp *cpp; @@ -139,8 +141,6 @@ struct nfp_pf { struct nfp_shared_buf *shared_bufs; unsigned int num_shared_bufs; - - struct mutex lock; }; extern struct pci_driver nfp_netvf_pci_driver; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 0b1865e9f0b5..428783b7018b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -63,9 +63,6 @@ #define NFP_NET_Q0_BAR 2 #define NFP_NET_Q1_BAR 4 /* OBSOLETE */ -/* Max bits in DMA address */ -#define NFP_NET_MAX_DMA_BITS 40 - /* Default size for MTU and freelist buffer sizes */ #define NFP_NET_DEFAULT_MTU 1500U @@ -85,11 +82,6 @@ NFP_NET_MAX_TX_RINGS : NFP_NET_MAX_RX_RINGS) #define NFP_NET_MAX_IRQS (NFP_NET_NON_Q_VECTORS + NFP_NET_MAX_R_VECS) -#define NFP_NET_MIN_TX_DESCS 256 /* Min. # of Tx descs per ring */ -#define NFP_NET_MIN_RX_DESCS 256 /* Min. # of Rx descs per ring */ -#define NFP_NET_MAX_TX_DESCS (256 * 1024) /* Max. # of Tx descs per ring */ -#define NFP_NET_MAX_RX_DESCS (256 * 1024) /* Max. # of Rx descs per ring */ - #define NFP_NET_TX_DESCS_DEFAULT 4096 /* Default # of Tx descs per ring */ #define NFP_NET_RX_DESCS_DEFAULT 4096 /* Default # of Rx descs per ring */ @@ -105,10 +97,19 @@ /* Forward declarations */ struct nfp_cpp; +struct nfp_dev_info; +struct nfp_dp_ops; struct nfp_eth_table_port; struct nfp_net; struct nfp_net_r_vector; struct nfp_port; +struct xsk_buff_pool; + +struct nfp_nfd3_tx_desc; +struct nfp_nfd3_tx_buf; + +struct nfp_nfdk_tx_desc; +struct nfp_nfdk_tx_buf; /* Convenience macro for wrapping descriptor index on ring size */ #define D_IDX(ring, idx) ((idx) & ((ring)->cnt - 1)) @@ -123,86 +124,25 @@ struct nfp_port; __d->dma_addr_hi = upper_32_bits(__addr) & 0xff; \ } while (0) -/* TX descriptor format */ - -#define PCIE_DESC_TX_EOP BIT(7) -#define PCIE_DESC_TX_OFFSET_MASK GENMASK(6, 0) -#define PCIE_DESC_TX_MSS_MASK GENMASK(13, 0) - -/* Flags in the host TX descriptor */ -#define PCIE_DESC_TX_CSUM BIT(7) -#define PCIE_DESC_TX_IP4_CSUM BIT(6) -#define PCIE_DESC_TX_TCP_CSUM BIT(5) -#define PCIE_DESC_TX_UDP_CSUM BIT(4) -#define PCIE_DESC_TX_VLAN BIT(3) -#define PCIE_DESC_TX_LSO BIT(2) -#define PCIE_DESC_TX_ENCAP BIT(1) -#define PCIE_DESC_TX_O_IP4_CSUM BIT(0) - -struct nfp_net_tx_desc { - union { - struct { - u8 dma_addr_hi; /* High bits of host buf address */ - __le16 dma_len; /* Length to DMA for this desc */ - u8 offset_eop; /* Offset in buf where pkt starts + - * highest bit is eop flag. - */ - __le32 dma_addr_lo; /* Low 32bit of host buf addr */ - - __le16 mss; /* MSS to be used for LSO */ - u8 lso_hdrlen; /* LSO, TCP payload offset */ - u8 flags; /* TX Flags, see @PCIE_DESC_TX_* */ - union { - struct { - u8 l3_offset; /* L3 header offset */ - u8 l4_offset; /* L4 header offset */ - }; - __le16 vlan; /* VLAN tag to add if indicated */ - }; - __le16 data_len; /* Length of frame + meta data */ - } __packed; - __le32 vals[4]; - __le64 vals8[2]; - }; -}; - -/** - * struct nfp_net_tx_buf - software TX buffer descriptor - * @skb: normal ring, sk_buff associated with this buffer - * @frag: XDP ring, page frag associated with this buffer - * @dma_addr: DMA mapping address of the buffer - * @fidx: Fragment index (-1 for the head and [0..nr_frags-1] for frags) - * @pkt_cnt: Number of packets to be produced out of the skb associated - * with this buffer (valid only on the head's buffer). - * Will be 1 for all non-TSO packets. - * @real_len: Number of bytes which to be produced out of the skb (valid only - * on the head's buffer). Equal to skb->len for non-TSO packets. - */ -struct nfp_net_tx_buf { - union { - struct sk_buff *skb; - void *frag; - }; - dma_addr_t dma_addr; - short int fidx; - u16 pkt_cnt; - u32 real_len; -}; - /** * struct nfp_net_tx_ring - TX ring structure * @r_vec: Back pointer to ring vector structure * @idx: Ring index from Linux's perspective - * @qcidx: Queue Controller Peripheral (QCP) queue index for the TX queue + * @data_pending: number of bytes added to current block (NFDK only) * @qcp_q: Pointer to base of the QCP TX queue + * @txrwb: TX pointer write back area * @cnt: Size of the queue in number of descriptors * @wr_p: TX ring write pointer (free running) * @rd_p: TX ring read pointer (free running) * @qcp_rd_p: Local copy of QCP TX queue read pointer * @wr_ptr_add: Accumulated number of buffers to add to QCP write pointer * (used for .xmit_more delayed kick) - * @txbufs: Array of transmitted TX buffers, to free on transmit - * @txds: Virtual address of TX ring in host memory + * @txbufs: Array of transmitted TX buffers, to free on transmit (NFD3) + * @ktxbufs: Array of transmitted TX buffers, to free on transmit (NFDK) + * @txds: Virtual address of TX ring in host memory (NFD3) + * @ktxds: Virtual address of TX ring in host memory (NFDK) + * + * @qcidx: Queue Controller Peripheral (QCP) queue index for the TX queue * @dma: DMA address of the TX ring * @size: Size, in bytes, of the TX ring (needed to free) * @is_xdp: Is this a XDP TX ring? @@ -210,9 +150,10 @@ struct nfp_net_tx_buf { struct nfp_net_tx_ring { struct nfp_net_r_vector *r_vec; - u32 idx; - int qcidx; + u16 idx; + u16 data_pending; u8 __iomem *qcp_q; + u64 *txrwb; u32 cnt; u32 wr_p; @@ -221,8 +162,17 @@ struct nfp_net_tx_ring { u32 wr_ptr_add; - struct nfp_net_tx_buf *txbufs; - struct nfp_net_tx_desc *txds; + union { + struct nfp_nfd3_tx_buf *txbufs; + struct nfp_nfdk_tx_buf *ktxbufs; + }; + union { + struct nfp_nfd3_tx_desc *txds; + struct nfp_nfdk_tx_desc *ktxds; + }; + + /* Cold data follows */ + int qcidx; dma_addr_t dma; size_t size; @@ -314,6 +264,16 @@ struct nfp_net_rx_buf { dma_addr_t dma_addr; }; +/** + * struct nfp_net_xsk_rx_buf - software RX XSK buffer descriptor + * @dma_addr: DMA mapping address of the buffer + * @xdp: XSK buffer pool handle (for AF_XDP) + */ +struct nfp_net_xsk_rx_buf { + dma_addr_t dma_addr; + struct xdp_buff *xdp; +}; + /** * struct nfp_net_rx_ring - RX ring structure * @r_vec: Back pointer to ring vector structure @@ -324,6 +284,7 @@ struct nfp_net_rx_buf { * @fl_qcidx: Queue Controller Peripheral (QCP) queue index for the freelist * @qcp_fl: Pointer to base of the QCP freelist queue * @rxbufs: Array of transmitted FL/RX buffers + * @xsk_rxbufs: Array of transmitted FL/RX buffers (for AF_XDP) * @rxds: Virtual address of FL/RX ring in host memory * @xdp_rxq: RX-ring info avail for XDP * @dma: DMA address of the FL/RX ring @@ -342,6 +303,7 @@ struct nfp_net_rx_ring { u8 __iomem *qcp_fl; struct nfp_net_rx_buf *rxbufs; + struct nfp_net_xsk_rx_buf *xsk_rxbufs; struct nfp_net_rx_desc *rxds; struct xdp_rxq_info xdp_rxq; @@ -360,6 +322,7 @@ struct nfp_net_rx_ring { * @tx_ring: Pointer to TX ring * @rx_ring: Pointer to RX ring * @xdp_ring: Pointer to an extra TX ring for XDP + * @xsk_pool: XSK buffer pool active on vector queue pair (for AF_XDP) * @irq_entry: MSI-X table entry (use for talking to the device) * @event_ctr: Number of interrupt * @rx_dim: Dynamic interrupt moderation structure for RX @@ -431,6 +394,7 @@ struct nfp_net_r_vector { u64 rx_replace_buf_alloc_fail; struct nfp_net_tx_ring *xdp_ring; + struct xsk_buff_pool *xsk_pool; struct u64_stats_sync tx_sync; u64 tx_pkts; @@ -460,13 +424,17 @@ struct nfp_net_fw_version { u8 minor; u8 major; u8 class; - u8 resv; + + /* This byte can be exploited for more use, currently, + * BIT0: dp type, BIT[7:1]: reserved + */ + u8 extend; } __packed; static inline bool nfp_net_fw_ver_eq(struct nfp_net_fw_version *fw_ver, - u8 resv, u8 class, u8 major, u8 minor) + u8 extend, u8 class, u8 major, u8 minor) { - return fw_ver->resv == resv && + return fw_ver->extend == extend && fw_ver->class == class && fw_ver->major == major && fw_ver->minor == minor; @@ -494,13 +462,17 @@ struct nfp_stat_pair { * @rx_rings: Array of pre-allocated RX ring structures * @ctrl_bar: Pointer to mapped control BAR * - * @txd_cnt: Size of the TX ring in number of descriptors - * @rxd_cnt: Size of the RX ring in number of descriptors + * @ops: Callbacks and parameters for this vNIC's NFD version + * @txrwb: TX pointer write back area (indexed by queue id) + * @txrwb_dma: TX pointer write back area DMA address + * @txd_cnt: Size of the TX ring in number of min size packets + * @rxd_cnt: Size of the RX ring in number of min size packets * @num_r_vecs: Number of used ring vectors * @num_tx_rings: Currently configured number of TX rings * @num_stack_tx_rings: Number of TX rings used by the stack (not XDP) * @num_rx_rings: Currently configured number of RX rings * @mtu: Device MTU + * @xsk_pools: XSK buffer pools, @max_r_vecs in size (for AF_XDP). */ struct nfp_net_dp { struct device *dev; @@ -527,6 +499,11 @@ struct nfp_net_dp { /* Cold data follows */ + const struct nfp_dp_ops *ops; + + u64 *txrwb; + dma_addr_t txrwb_dma; + unsigned int txd_cnt; unsigned int rxd_cnt; @@ -537,11 +514,14 @@ struct nfp_net_dp { unsigned int num_rx_rings; unsigned int mtu; + + struct xsk_buff_pool **xsk_pools; }; /** * struct nfp_net - NFP network device structure * @dp: Datapath structure + * @dev_info: NFP ASIC params * @id: vNIC id within the PF (0 for VFs) * @fw_ver: Firmware version * @cap: Capabilities advertised by the Firmware @@ -615,6 +595,7 @@ struct nfp_net_dp { struct nfp_net { struct nfp_net_dp dp; + const struct nfp_dev_info *dev_info; struct nfp_net_fw_version fw_ver; u32 id; @@ -767,7 +748,6 @@ static inline void nn_pci_flush(struct nfp_net *nn) * either add to a pointer or to read the pointer value. */ #define NFP_QCP_QUEUE_ADDR_SZ 0x800 -#define NFP_QCP_QUEUE_AREA_SZ 0x80000 #define NFP_QCP_QUEUE_OFF(_x) ((_x) * NFP_QCP_QUEUE_ADDR_SZ) #define NFP_QCP_QUEUE_ADD_RPTR 0x0000 #define NFP_QCP_QUEUE_ADD_WPTR 0x0004 @@ -776,50 +756,21 @@ static inline void nn_pci_flush(struct nfp_net *nn) #define NFP_QCP_QUEUE_STS_HI 0x000c #define NFP_QCP_QUEUE_STS_HI_WRITEPTR_mask 0x3ffff -/* The offset of a QCP queues in the PCIe Target */ -#define NFP_PCIE_QUEUE(_q) (0x80000 + (NFP_QCP_QUEUE_ADDR_SZ * ((_q) & 0xff))) - /* nfp_qcp_ptr - Read or Write Pointer of a queue */ enum nfp_qcp_ptr { NFP_QCP_READ_PTR = 0, NFP_QCP_WRITE_PTR }; -/* There appear to be an *undocumented* upper limit on the value which - * one can add to a queue and that value is either 0x3f or 0x7f. We - * go with 0x3f as a conservative measure. - */ -#define NFP_QCP_MAX_ADD 0x3f - -static inline void _nfp_qcp_ptr_add(u8 __iomem *q, - enum nfp_qcp_ptr ptr, u32 val) -{ - u32 off; - - if (ptr == NFP_QCP_READ_PTR) - off = NFP_QCP_QUEUE_ADD_RPTR; - else - off = NFP_QCP_QUEUE_ADD_WPTR; - - while (val > NFP_QCP_MAX_ADD) { - writel(NFP_QCP_MAX_ADD, q + off); - val -= NFP_QCP_MAX_ADD; - } - - writel(val, q + off); -} - /** * nfp_qcp_rd_ptr_add() - Add the value to the read pointer of a queue * * @q: Base address for queue structure * @val: Value to add to the queue pointer - * - * If @val is greater than @NFP_QCP_MAX_ADD multiple writes are performed. */ static inline void nfp_qcp_rd_ptr_add(u8 __iomem *q, u32 val) { - _nfp_qcp_ptr_add(q, NFP_QCP_READ_PTR, val); + writel(val, q + NFP_QCP_QUEUE_ADD_RPTR); } /** @@ -827,12 +778,10 @@ static inline void nfp_qcp_rd_ptr_add(u8 __iomem *q, u32 val) * * @q: Base address for queue structure * @val: Value to add to the queue pointer - * - * If @val is greater than @NFP_QCP_MAX_ADD multiple writes are performed. */ static inline void nfp_qcp_wr_ptr_add(u8 __iomem *q, u32 val) { - _nfp_qcp_ptr_add(q, NFP_QCP_WRITE_PTR, val); + writel(val, q + NFP_QCP_QUEUE_ADD_WPTR); } static inline u32 _nfp_qcp_read(u8 __iomem *q, enum nfp_qcp_ptr ptr) @@ -875,6 +824,8 @@ static inline u32 nfp_qcp_wr_ptr_read(u8 __iomem *q) return _nfp_qcp_read(q, NFP_QCP_WRITE_PTR); } +u32 nfp_qcp_queue_offset(const struct nfp_dev_info *dev_info, u16 queue); + static inline bool nfp_net_is_data_vnic(struct nfp_net *nn) { WARN_ON_ONCE(!nn->dp.netdev && nn->port); @@ -921,11 +872,13 @@ static inline void nn_ctrl_bar_unlock(struct nfp_net *nn) /* Globals */ extern const char nfp_driver_version[]; -extern const struct net_device_ops nfp_net_netdev_ops; +extern const struct net_device_ops nfp_nfd3_netdev_ops; +extern const struct net_device_ops nfp_nfdk_netdev_ops; static inline bool nfp_netdev_is_nfp_net(struct net_device *netdev) { - return netdev->netdev_ops == &nfp_net_netdev_ops; + return netdev->netdev_ops == &nfp_nfd3_netdev_ops || + netdev->netdev_ops == &nfp_nfdk_netdev_ops; } static inline int nfp_net_coalesce_para_check(u32 usecs, u32 pkts) @@ -941,7 +894,8 @@ void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver, void __iomem *ctrl_bar); struct nfp_net * -nfp_net_alloc(struct pci_dev *pdev, void __iomem *ctrl_bar, bool needs_netdev, +nfp_net_alloc(struct pci_dev *pdev, const struct nfp_dev_info *dev_info, + void __iomem *ctrl_bar, bool needs_netdev, unsigned int max_tx_rings, unsigned int max_rx_rings); void nfp_net_free(struct nfp_net *nn); @@ -972,6 +926,10 @@ void nfp_net_irqs_disable(struct pci_dev *pdev); void nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries, unsigned int n); +struct sk_buff * +nfp_net_tls_tx(struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, + struct sk_buff *skb, u64 *tls_handle, int *nr_frags); +void nfp_net_tls_tx_undo(struct sk_buff *skb, u64 tls_handle); struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn); int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 79257ec41987..b412670d89b2 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -/* Copyright (C) 2015-2018 Netronome Systems, Inc. */ +/* Copyright (C) 2015-2019 Netronome Systems, Inc. */ /* * nfp_net_common.c @@ -13,7 +13,6 @@ #include #include -#include #include #include #include @@ -38,13 +37,17 @@ #include #include +#include +#include "nfpcore/nfp_dev.h" #include "nfpcore/nfp_nsp.h" #include "ccm.h" #include "nfp_app.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" +#include "nfp_net_dp.h" #include "nfp_net_sriov.h" +#include "nfp_net_xsk.h" #include "nfp_port.h" #include "crypto/crypto.h" #include "crypto/fw.h" @@ -63,33 +66,10 @@ void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver, put_unaligned_le32(reg, fw_ver); } -static dma_addr_t nfp_net_dma_map_rx(struct nfp_net_dp *dp, void *frag) +u32 nfp_qcp_queue_offset(const struct nfp_dev_info *dev_info, u16 queue) { - return dma_map_single_attrs(dp->dev, frag + NFP_NET_RX_BUF_HEADROOM, - dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, - dp->rx_dma_dir, DMA_ATTR_SKIP_CPU_SYNC); -} - -static void -nfp_net_dma_sync_dev_rx(const struct nfp_net_dp *dp, dma_addr_t dma_addr) -{ - dma_sync_single_for_device(dp->dev, dma_addr, - dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, - dp->rx_dma_dir); -} - -static void nfp_net_dma_unmap_rx(struct nfp_net_dp *dp, dma_addr_t dma_addr) -{ - dma_unmap_single_attrs(dp->dev, dma_addr, - dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, - dp->rx_dma_dir, DMA_ATTR_SKIP_CPU_SYNC); -} - -static void nfp_net_dma_sync_cpu_rx(struct nfp_net_dp *dp, dma_addr_t dma_addr, - unsigned int len) -{ - dma_sync_single_for_cpu(dp->dev, dma_addr - NFP_NET_RX_BUF_HEADROOM, - len, dp->rx_dma_dir); + queue &= dev_info->qc_idx_mask; + return dev_info->qc_addr_offset + NFP_QCP_QUEUE_ADDR_SZ * queue; } /* Firmware reconfig @@ -374,19 +354,6 @@ int nfp_net_mbox_reconfig_and_unlock(struct nfp_net *nn, u32 mbox_cmd) /* Interrupt configuration and handling */ -/** - * nfp_net_irq_unmask() - Unmask automasked interrupt - * @nn: NFP Network structure - * @entry_nr: MSI-X table entry - * - * Clear the ICR for the IRQ entry. - */ -static void nfp_net_irq_unmask(struct nfp_net *nn, unsigned int entry_nr) -{ - nn_writeb(nn, NFP_NET_CFG_ICR(entry_nr), NFP_NET_CFG_ICR_UNMASKED); - nn_pci_flush(nn); -} - /** * nfp_net_irqs_alloc() - allocates MSI-X irqs * @pdev: PCI device structure @@ -568,49 +535,6 @@ static irqreturn_t nfp_net_irq_exn(int irq, void *data) return IRQ_HANDLED; } -/** - * nfp_net_tx_ring_init() - Fill in the boilerplate for a TX ring - * @tx_ring: TX ring structure - * @r_vec: IRQ vector servicing this ring - * @idx: Ring index - * @is_xdp: Is this an XDP TX ring? - */ -static void -nfp_net_tx_ring_init(struct nfp_net_tx_ring *tx_ring, - struct nfp_net_r_vector *r_vec, unsigned int idx, - bool is_xdp) -{ - struct nfp_net *nn = r_vec->nfp_net; - - tx_ring->idx = idx; - tx_ring->r_vec = r_vec; - tx_ring->is_xdp = is_xdp; - u64_stats_init(&tx_ring->r_vec->tx_sync); - - tx_ring->qcidx = tx_ring->idx * nn->stride_tx; - tx_ring->qcp_q = nn->tx_bar + NFP_QCP_QUEUE_OFF(tx_ring->qcidx); -} - -/** - * nfp_net_rx_ring_init() - Fill in the boilerplate for a RX ring - * @rx_ring: RX ring structure - * @r_vec: IRQ vector servicing this ring - * @idx: Ring index - */ -static void -nfp_net_rx_ring_init(struct nfp_net_rx_ring *rx_ring, - struct nfp_net_r_vector *r_vec, unsigned int idx) -{ - struct nfp_net *nn = r_vec->nfp_net; - - rx_ring->idx = idx; - rx_ring->r_vec = r_vec; - u64_stats_init(&rx_ring->r_vec->rx_sync); - - rx_ring->fl_qcidx = rx_ring->idx * nn->stride_rx; - rx_ring->qcp_fl = nn->rx_bar + NFP_QCP_QUEUE_OFF(rx_ring->fl_qcidx); -} - /** * nfp_net_aux_irq_request() - Request an auxiliary interrupt (LSC or EXN) * @nn: NFP Network structure @@ -658,178 +582,7 @@ static void nfp_net_aux_irq_free(struct nfp_net *nn, u32 ctrl_offset, free_irq(nn->irq_entries[vector_idx].vector, nn); } -/* Transmit - * - * One queue controller peripheral queue is used for transmit. The - * driver en-queues packets for transmit by advancing the write - * pointer. The device indicates that packets have transmitted by - * advancing the read pointer. The driver maintains a local copy of - * the read and write pointer in @struct nfp_net_tx_ring. The driver - * keeps @wr_p in sync with the queue controller write pointer and can - * determine how many packets have been transmitted by comparing its - * copy of the read pointer @rd_p with the read pointer maintained by - * the queue controller peripheral. - */ - -/** - * nfp_net_tx_full() - Check if the TX ring is full - * @tx_ring: TX ring to check - * @dcnt: Number of descriptors that need to be enqueued (must be >= 1) - * - * This function checks, based on the *host copy* of read/write - * pointer if a given TX ring is full. The real TX queue may have - * some newly made available slots. - * - * Return: True if the ring is full. - */ -static int nfp_net_tx_full(struct nfp_net_tx_ring *tx_ring, int dcnt) -{ - return (tx_ring->wr_p - tx_ring->rd_p) >= (tx_ring->cnt - dcnt); -} - -/* Wrappers for deciding when to stop and restart TX queues */ -static int nfp_net_tx_ring_should_wake(struct nfp_net_tx_ring *tx_ring) -{ - return !nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS * 4); -} - -static int nfp_net_tx_ring_should_stop(struct nfp_net_tx_ring *tx_ring) -{ - return nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS + 1); -} - -/** - * nfp_net_tx_ring_stop() - stop tx ring - * @nd_q: netdev queue - * @tx_ring: driver tx queue structure - * - * Safely stop TX ring. Remember that while we are running .start_xmit() - * someone else may be cleaning the TX ring completions so we need to be - * extra careful here. - */ -static void nfp_net_tx_ring_stop(struct netdev_queue *nd_q, - struct nfp_net_tx_ring *tx_ring) -{ - netif_tx_stop_queue(nd_q); - - /* We can race with the TX completion out of NAPI so recheck */ - smp_mb(); - if (unlikely(nfp_net_tx_ring_should_wake(tx_ring))) - netif_tx_start_queue(nd_q); -} - -/** - * nfp_net_tx_tso() - Set up Tx descriptor for LSO - * @r_vec: per-ring structure - * @txbuf: Pointer to driver soft TX descriptor - * @txd: Pointer to HW TX descriptor - * @skb: Pointer to SKB - * @md_bytes: Prepend length - * - * Set up Tx descriptor for LSO, do nothing for non-LSO skbs. - * Return error on packet header greater than maximum supported LSO header size. - */ -static void nfp_net_tx_tso(struct nfp_net_r_vector *r_vec, - struct nfp_net_tx_buf *txbuf, - struct nfp_net_tx_desc *txd, struct sk_buff *skb, - u32 md_bytes) -{ - u32 l3_offset, l4_offset, hdrlen; - u16 mss; - - if (!skb_is_gso(skb)) - return; - - if (!skb->encapsulation) { - l3_offset = skb_network_offset(skb); - l4_offset = skb_transport_offset(skb); - hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb); - } else { - l3_offset = skb_inner_network_offset(skb); - l4_offset = skb_inner_transport_offset(skb); - hdrlen = skb_inner_transport_header(skb) - skb->data + - inner_tcp_hdrlen(skb); - } - - txbuf->pkt_cnt = skb_shinfo(skb)->gso_segs; - txbuf->real_len += hdrlen * (txbuf->pkt_cnt - 1); - - mss = skb_shinfo(skb)->gso_size & PCIE_DESC_TX_MSS_MASK; - txd->l3_offset = l3_offset - md_bytes; - txd->l4_offset = l4_offset - md_bytes; - txd->lso_hdrlen = hdrlen - md_bytes; - txd->mss = cpu_to_le16(mss); - txd->flags |= PCIE_DESC_TX_LSO; - - u64_stats_update_begin(&r_vec->tx_sync); - r_vec->tx_lso++; - u64_stats_update_end(&r_vec->tx_sync); -} - -/** - * nfp_net_tx_csum() - Set TX CSUM offload flags in TX descriptor - * @dp: NFP Net data path struct - * @r_vec: per-ring structure - * @txbuf: Pointer to driver soft TX descriptor - * @txd: Pointer to TX descriptor - * @skb: Pointer to SKB - * - * This function sets the TX checksum flags in the TX descriptor based - * on the configuration and the protocol of the packet to be transmitted. - */ -static void nfp_net_tx_csum(struct nfp_net_dp *dp, - struct nfp_net_r_vector *r_vec, - struct nfp_net_tx_buf *txbuf, - struct nfp_net_tx_desc *txd, struct sk_buff *skb) -{ - struct ipv6hdr *ipv6h; - struct iphdr *iph; - u8 l4_hdr; - - if (!(dp->ctrl & NFP_NET_CFG_CTRL_TXCSUM)) - return; - - if (skb->ip_summed != CHECKSUM_PARTIAL) - return; - - txd->flags |= PCIE_DESC_TX_CSUM; - if (skb->encapsulation) - txd->flags |= PCIE_DESC_TX_ENCAP; - - iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); - ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb); - - if (iph->version == 4) { - txd->flags |= PCIE_DESC_TX_IP4_CSUM; - l4_hdr = iph->protocol; - } else if (ipv6h->version == 6) { - l4_hdr = ipv6h->nexthdr; - } else { - nn_dp_warn(dp, "partial checksum but ipv=%x!\n", iph->version); - return; - } - - switch (l4_hdr) { - case IPPROTO_TCP: - txd->flags |= PCIE_DESC_TX_TCP_CSUM; - break; - case IPPROTO_UDP: - txd->flags |= PCIE_DESC_TX_UDP_CSUM; - break; - default: - nn_dp_warn(dp, "partial checksum but l4 proto=%x!\n", l4_hdr); - return; - } - - u64_stats_update_begin(&r_vec->tx_sync); - if (skb->encapsulation) - r_vec->hw_csum_tx_inner += txbuf->pkt_cnt; - else - r_vec->hw_csum_tx += txbuf->pkt_cnt; - u64_stats_update_end(&r_vec->tx_sync); -} - -static struct sk_buff * +struct sk_buff * nfp_net_tls_tx(struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, struct sk_buff *skb, u64 *tls_handle, int *nr_frags) { @@ -901,7 +654,7 @@ nfp_net_tls_tx(struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, return skb; } -static void nfp_net_tls_tx_undo(struct sk_buff *skb, u64 tls_handle) +void nfp_net_tls_tx_undo(struct sk_buff *skb, u64 tls_handle) { #ifdef CONFIG_TLS_DEVICE struct nfp_net_tls_offload_ctx *ntls; @@ -923,411 +676,6 @@ static void nfp_net_tls_tx_undo(struct sk_buff *skb, u64 tls_handle) #endif } -static void nfp_net_tx_xmit_more_flush(struct nfp_net_tx_ring *tx_ring) -{ - wmb(); - nfp_qcp_wr_ptr_add(tx_ring->qcp_q, tx_ring->wr_ptr_add); - tx_ring->wr_ptr_add = 0; -} - -static int nfp_net_prep_tx_meta(struct sk_buff *skb, u64 tls_handle) -{ - struct metadata_dst *md_dst = skb_metadata_dst(skb); - unsigned char *data; - u32 meta_id = 0; - int md_bytes; - - if (likely(!md_dst && !tls_handle)) - return 0; - if (unlikely(md_dst && md_dst->type != METADATA_HW_PORT_MUX)) { - if (!tls_handle) - return 0; - md_dst = NULL; - } - - md_bytes = 4 + !!md_dst * 4 + !!tls_handle * 8; - - if (unlikely(skb_cow_head(skb, md_bytes))) - return -ENOMEM; - - meta_id = 0; - data = skb_push(skb, md_bytes) + md_bytes; - if (md_dst) { - data -= 4; - put_unaligned_be32(md_dst->u.port_info.port_id, data); - meta_id = NFP_NET_META_PORTID; - } - if (tls_handle) { - /* conn handle is opaque, we just use u64 to be able to quickly - * compare it to zero - */ - data -= 8; - memcpy(data, &tls_handle, sizeof(tls_handle)); - meta_id <<= NFP_NET_META_FIELD_SIZE; - meta_id |= NFP_NET_META_CONN_HANDLE; - } - - data -= 4; - put_unaligned_be32(meta_id, data); - - return md_bytes; -} - -/** - * nfp_net_tx() - Main transmit entry point - * @skb: SKB to transmit - * @netdev: netdev structure - * - * Return: NETDEV_TX_OK on success. - */ -static netdev_tx_t nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) -{ - struct nfp_net *nn = netdev_priv(netdev); - const skb_frag_t *frag; - int f, nr_frags, wr_idx, md_bytes; - struct nfp_net_tx_ring *tx_ring; - struct nfp_net_r_vector *r_vec; - struct nfp_net_tx_buf *txbuf; - struct nfp_net_tx_desc *txd; - struct netdev_queue *nd_q; - struct nfp_net_dp *dp; - dma_addr_t dma_addr; - unsigned int fsize; - u64 tls_handle = 0; - u16 qidx; - - dp = &nn->dp; - qidx = skb_get_queue_mapping(skb); - tx_ring = &dp->tx_rings[qidx]; - r_vec = tx_ring->r_vec; - - nr_frags = skb_shinfo(skb)->nr_frags; - - if (unlikely(nfp_net_tx_full(tx_ring, nr_frags + 1))) { - nn_dp_warn(dp, "TX ring %d busy. wrp=%u rdp=%u\n", - qidx, tx_ring->wr_p, tx_ring->rd_p); - nd_q = netdev_get_tx_queue(dp->netdev, qidx); - netif_tx_stop_queue(nd_q); - nfp_net_tx_xmit_more_flush(tx_ring); - u64_stats_update_begin(&r_vec->tx_sync); - r_vec->tx_busy++; - u64_stats_update_end(&r_vec->tx_sync); - return NETDEV_TX_BUSY; - } - - skb = nfp_net_tls_tx(dp, r_vec, skb, &tls_handle, &nr_frags); - if (unlikely(!skb)) { - nfp_net_tx_xmit_more_flush(tx_ring); - return NETDEV_TX_OK; - } - - md_bytes = nfp_net_prep_tx_meta(skb, tls_handle); - if (unlikely(md_bytes < 0)) - goto err_flush; - - /* Start with the head skbuf */ - dma_addr = dma_map_single(dp->dev, skb->data, skb_headlen(skb), - DMA_TO_DEVICE); - if (dma_mapping_error(dp->dev, dma_addr)) - goto err_dma_err; - - wr_idx = D_IDX(tx_ring, tx_ring->wr_p); - - /* Stash the soft descriptor of the head then initialize it */ - txbuf = &tx_ring->txbufs[wr_idx]; - txbuf->skb = skb; - txbuf->dma_addr = dma_addr; - txbuf->fidx = -1; - txbuf->pkt_cnt = 1; - txbuf->real_len = skb->len; - - /* Build TX descriptor */ - txd = &tx_ring->txds[wr_idx]; - txd->offset_eop = (nr_frags ? 0 : PCIE_DESC_TX_EOP) | md_bytes; - txd->dma_len = cpu_to_le16(skb_headlen(skb)); - nfp_desc_set_dma_addr(txd, dma_addr); - txd->data_len = cpu_to_le16(skb->len); - - txd->flags = 0; - txd->mss = 0; - txd->lso_hdrlen = 0; - - /* Do not reorder - tso may adjust pkt cnt, vlan may override fields */ - nfp_net_tx_tso(r_vec, txbuf, txd, skb, md_bytes); - nfp_net_tx_csum(dp, r_vec, txbuf, txd, skb); - if (skb_vlan_tag_present(skb) && dp->ctrl & NFP_NET_CFG_CTRL_TXVLAN) { - txd->flags |= PCIE_DESC_TX_VLAN; - txd->vlan = cpu_to_le16(skb_vlan_tag_get(skb)); - } - - /* Gather DMA */ - if (nr_frags > 0) { - __le64 second_half; - - /* all descs must match except for in addr, length and eop */ - second_half = txd->vals8[1]; - - for (f = 0; f < nr_frags; f++) { - frag = &skb_shinfo(skb)->frags[f]; - fsize = skb_frag_size(frag); - - dma_addr = skb_frag_dma_map(dp->dev, frag, 0, - fsize, DMA_TO_DEVICE); - if (dma_mapping_error(dp->dev, dma_addr)) - goto err_unmap; - - wr_idx = D_IDX(tx_ring, wr_idx + 1); - tx_ring->txbufs[wr_idx].skb = skb; - tx_ring->txbufs[wr_idx].dma_addr = dma_addr; - tx_ring->txbufs[wr_idx].fidx = f; - - txd = &tx_ring->txds[wr_idx]; - txd->dma_len = cpu_to_le16(fsize); - nfp_desc_set_dma_addr(txd, dma_addr); - txd->offset_eop = md_bytes | - ((f == nr_frags - 1) ? PCIE_DESC_TX_EOP : 0); - txd->vals8[1] = second_half; - } - - u64_stats_update_begin(&r_vec->tx_sync); - r_vec->tx_gather++; - u64_stats_update_end(&r_vec->tx_sync); - } - - skb_tx_timestamp(skb); - - nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx); - - tx_ring->wr_p += nr_frags + 1; - if (nfp_net_tx_ring_should_stop(tx_ring)) - nfp_net_tx_ring_stop(nd_q, tx_ring); - - tx_ring->wr_ptr_add += nr_frags + 1; - if (__netdev_tx_sent_queue(nd_q, txbuf->real_len, netdev_xmit_more())) - nfp_net_tx_xmit_more_flush(tx_ring); - - return NETDEV_TX_OK; - -err_unmap: - while (--f >= 0) { - frag = &skb_shinfo(skb)->frags[f]; - dma_unmap_page(dp->dev, tx_ring->txbufs[wr_idx].dma_addr, - skb_frag_size(frag), DMA_TO_DEVICE); - tx_ring->txbufs[wr_idx].skb = NULL; - tx_ring->txbufs[wr_idx].dma_addr = 0; - tx_ring->txbufs[wr_idx].fidx = -2; - wr_idx = wr_idx - 1; - if (wr_idx < 0) - wr_idx += tx_ring->cnt; - } - dma_unmap_single(dp->dev, tx_ring->txbufs[wr_idx].dma_addr, - skb_headlen(skb), DMA_TO_DEVICE); - tx_ring->txbufs[wr_idx].skb = NULL; - tx_ring->txbufs[wr_idx].dma_addr = 0; - tx_ring->txbufs[wr_idx].fidx = -2; -err_dma_err: - nn_dp_warn(dp, "Failed to map DMA TX buffer\n"); -err_flush: - nfp_net_tx_xmit_more_flush(tx_ring); - u64_stats_update_begin(&r_vec->tx_sync); - r_vec->tx_errors++; - u64_stats_update_end(&r_vec->tx_sync); - nfp_net_tls_tx_undo(skb, tls_handle); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; -} - -/** - * nfp_net_tx_complete() - Handled completed TX packets - * @tx_ring: TX ring structure - * @budget: NAPI budget (only used as bool to determine if in NAPI context) - */ -static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring, int budget) -{ - struct nfp_net_r_vector *r_vec = tx_ring->r_vec; - struct nfp_net_dp *dp = &r_vec->nfp_net->dp; - struct netdev_queue *nd_q; - u32 done_pkts = 0, done_bytes = 0; - u32 qcp_rd_p; - int todo; - - if (tx_ring->wr_p == tx_ring->rd_p) - return; - - /* Work out how many descriptors have been transmitted */ - qcp_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q); - - if (qcp_rd_p == tx_ring->qcp_rd_p) - return; - - todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p); - - while (todo--) { - const skb_frag_t *frag; - struct nfp_net_tx_buf *tx_buf; - struct sk_buff *skb; - int fidx, nr_frags; - int idx; - - idx = D_IDX(tx_ring, tx_ring->rd_p++); - tx_buf = &tx_ring->txbufs[idx]; - - skb = tx_buf->skb; - if (!skb) - continue; - - nr_frags = skb_shinfo(skb)->nr_frags; - fidx = tx_buf->fidx; - - if (fidx == -1) { - /* unmap head */ - dma_unmap_single(dp->dev, tx_buf->dma_addr, - skb_headlen(skb), DMA_TO_DEVICE); - - done_pkts += tx_buf->pkt_cnt; - done_bytes += tx_buf->real_len; - } else { - /* unmap fragment */ - frag = &skb_shinfo(skb)->frags[fidx]; - dma_unmap_page(dp->dev, tx_buf->dma_addr, - skb_frag_size(frag), DMA_TO_DEVICE); - } - - /* check for last gather fragment */ - if (fidx == nr_frags - 1) - napi_consume_skb(skb, budget); - - tx_buf->dma_addr = 0; - tx_buf->skb = NULL; - tx_buf->fidx = -2; - } - - tx_ring->qcp_rd_p = qcp_rd_p; - - u64_stats_update_begin(&r_vec->tx_sync); - r_vec->tx_bytes += done_bytes; - r_vec->tx_pkts += done_pkts; - u64_stats_update_end(&r_vec->tx_sync); - - if (!dp->netdev) - return; - - nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx); - netdev_tx_completed_queue(nd_q, done_pkts, done_bytes); - if (nfp_net_tx_ring_should_wake(tx_ring)) { - /* Make sure TX thread will see updated tx_ring->rd_p */ - smp_mb(); - - if (unlikely(netif_tx_queue_stopped(nd_q))) - netif_tx_wake_queue(nd_q); - } - - WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt, - "TX ring corruption rd_p=%u wr_p=%u cnt=%u\n", - tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt); -} - -static bool nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring) -{ - struct nfp_net_r_vector *r_vec = tx_ring->r_vec; - u32 done_pkts = 0, done_bytes = 0; - bool done_all; - int idx, todo; - u32 qcp_rd_p; - - /* Work out how many descriptors have been transmitted */ - qcp_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q); - - if (qcp_rd_p == tx_ring->qcp_rd_p) - return true; - - todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p); - - done_all = todo <= NFP_NET_XDP_MAX_COMPLETE; - todo = min(todo, NFP_NET_XDP_MAX_COMPLETE); - - tx_ring->qcp_rd_p = D_IDX(tx_ring, tx_ring->qcp_rd_p + todo); - - done_pkts = todo; - while (todo--) { - idx = D_IDX(tx_ring, tx_ring->rd_p); - tx_ring->rd_p++; - - done_bytes += tx_ring->txbufs[idx].real_len; - } - - u64_stats_update_begin(&r_vec->tx_sync); - r_vec->tx_bytes += done_bytes; - r_vec->tx_pkts += done_pkts; - u64_stats_update_end(&r_vec->tx_sync); - - WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt, - "XDP TX ring corruption rd_p=%u wr_p=%u cnt=%u\n", - tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt); - - return done_all; -} - -/** - * nfp_net_tx_ring_reset() - Free any untransmitted buffers and reset pointers - * @dp: NFP Net data path struct - * @tx_ring: TX ring structure - * - * Assumes that the device is stopped, must be idempotent. - */ -static void -nfp_net_tx_ring_reset(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) -{ - const skb_frag_t *frag; - struct netdev_queue *nd_q; - - while (!tx_ring->is_xdp && tx_ring->rd_p != tx_ring->wr_p) { - struct nfp_net_tx_buf *tx_buf; - struct sk_buff *skb; - int idx, nr_frags; - - idx = D_IDX(tx_ring, tx_ring->rd_p); - tx_buf = &tx_ring->txbufs[idx]; - - skb = tx_ring->txbufs[idx].skb; - nr_frags = skb_shinfo(skb)->nr_frags; - - if (tx_buf->fidx == -1) { - /* unmap head */ - dma_unmap_single(dp->dev, tx_buf->dma_addr, - skb_headlen(skb), DMA_TO_DEVICE); - } else { - /* unmap fragment */ - frag = &skb_shinfo(skb)->frags[tx_buf->fidx]; - dma_unmap_page(dp->dev, tx_buf->dma_addr, - skb_frag_size(frag), DMA_TO_DEVICE); - } - - /* check for last gather fragment */ - if (tx_buf->fidx == nr_frags - 1) - dev_kfree_skb_any(skb); - - tx_buf->dma_addr = 0; - tx_buf->skb = NULL; - tx_buf->fidx = -2; - - tx_ring->qcp_rd_p++; - tx_ring->rd_p++; - } - - memset(tx_ring->txds, 0, tx_ring->size); - tx_ring->wr_p = 0; - tx_ring->rd_p = 0; - tx_ring->qcp_rd_p = 0; - tx_ring->wr_ptr_add = 0; - - if (tx_ring->is_xdp || !dp->netdev) - return; - - nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx); - netdev_tx_reset_queue(nd_q); -} - static void nfp_net_tx_timeout(struct net_device *netdev, unsigned int txqueue) { struct nfp_net *nn = netdev_priv(netdev); @@ -1335,1008 +683,43 @@ static void nfp_net_tx_timeout(struct net_device *netdev, unsigned int txqueue) nn_warn(nn, "TX watchdog timeout on ring: %u\n", txqueue); } -/* Receive processing - */ +/* Receive processing */ static unsigned int -nfp_net_calc_fl_bufsz(struct nfp_net_dp *dp) +nfp_net_calc_fl_bufsz_data(struct nfp_net_dp *dp) { - unsigned int fl_bufsz; + unsigned int fl_bufsz = 0; - fl_bufsz = NFP_NET_RX_BUF_HEADROOM; - fl_bufsz += dp->rx_dma_off; if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) fl_bufsz += NFP_NET_MAX_PREPEND; else fl_bufsz += dp->rx_offset; fl_bufsz += ETH_HLEN + VLAN_HLEN * 2 + dp->mtu; + return fl_bufsz; +} + +static unsigned int nfp_net_calc_fl_bufsz(struct nfp_net_dp *dp) +{ + unsigned int fl_bufsz; + + fl_bufsz = NFP_NET_RX_BUF_HEADROOM; + fl_bufsz += dp->rx_dma_off; + fl_bufsz += nfp_net_calc_fl_bufsz_data(dp); + fl_bufsz = SKB_DATA_ALIGN(fl_bufsz); fl_bufsz += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); return fl_bufsz; } -static void -nfp_net_free_frag(void *frag, bool xdp) +static unsigned int nfp_net_calc_fl_bufsz_xsk(struct nfp_net_dp *dp) { - if (!xdp) - skb_free_frag(frag); - else - __free_page(virt_to_page(frag)); -} + unsigned int fl_bufsz; -/** - * nfp_net_rx_alloc_one() - Allocate and map page frag for RX - * @dp: NFP Net data path struct - * @dma_addr: Pointer to storage for DMA address (output param) - * - * This function will allcate a new page frag, map it for DMA. - * - * Return: allocated page frag or NULL on failure. - */ -static void *nfp_net_rx_alloc_one(struct nfp_net_dp *dp, dma_addr_t *dma_addr) -{ - void *frag; + fl_bufsz = XDP_PACKET_HEADROOM; + fl_bufsz += nfp_net_calc_fl_bufsz_data(dp); - if (!dp->xdp_prog) { - frag = netdev_alloc_frag(dp->fl_bufsz); - } else { - struct page *page; - - page = alloc_page(GFP_KERNEL); - frag = page ? page_address(page) : NULL; - } - if (!frag) { - nn_dp_warn(dp, "Failed to alloc receive page frag\n"); - return NULL; - } - - *dma_addr = nfp_net_dma_map_rx(dp, frag); - if (dma_mapping_error(dp->dev, *dma_addr)) { - nfp_net_free_frag(frag, dp->xdp_prog); - nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); - return NULL; - } - - return frag; -} - -static void *nfp_net_napi_alloc_one(struct nfp_net_dp *dp, dma_addr_t *dma_addr) -{ - void *frag; - - if (!dp->xdp_prog) { - frag = napi_alloc_frag(dp->fl_bufsz); - if (unlikely(!frag)) - return NULL; - } else { - struct page *page; - - page = dev_alloc_page(); - if (unlikely(!page)) - return NULL; - frag = page_address(page); - } - - *dma_addr = nfp_net_dma_map_rx(dp, frag); - if (dma_mapping_error(dp->dev, *dma_addr)) { - nfp_net_free_frag(frag, dp->xdp_prog); - nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); - return NULL; - } - - return frag; -} - -/** - * nfp_net_rx_give_one() - Put mapped skb on the software and hardware rings - * @dp: NFP Net data path struct - * @rx_ring: RX ring structure - * @frag: page fragment buffer - * @dma_addr: DMA address of skb mapping - */ -static void nfp_net_rx_give_one(const struct nfp_net_dp *dp, - struct nfp_net_rx_ring *rx_ring, - void *frag, dma_addr_t dma_addr) -{ - unsigned int wr_idx; - - wr_idx = D_IDX(rx_ring, rx_ring->wr_p); - - nfp_net_dma_sync_dev_rx(dp, dma_addr); - - /* Stash SKB and DMA address away */ - rx_ring->rxbufs[wr_idx].frag = frag; - rx_ring->rxbufs[wr_idx].dma_addr = dma_addr; - - /* Fill freelist descriptor */ - rx_ring->rxds[wr_idx].fld.reserved = 0; - rx_ring->rxds[wr_idx].fld.meta_len_dd = 0; - nfp_desc_set_dma_addr(&rx_ring->rxds[wr_idx].fld, - dma_addr + dp->rx_dma_off); - - rx_ring->wr_p++; - if (!(rx_ring->wr_p % NFP_NET_FL_BATCH)) { - /* Update write pointer of the freelist queue. Make - * sure all writes are flushed before telling the hardware. - */ - wmb(); - nfp_qcp_wr_ptr_add(rx_ring->qcp_fl, NFP_NET_FL_BATCH); - } -} - -/** - * nfp_net_rx_ring_reset() - Reflect in SW state of freelist after disable - * @rx_ring: RX ring structure - * - * Assumes that the device is stopped, must be idempotent. - */ -static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring) -{ - unsigned int wr_idx, last_idx; - - /* wr_p == rd_p means ring was never fed FL bufs. RX rings are always - * kept at cnt - 1 FL bufs. - */ - if (rx_ring->wr_p == 0 && rx_ring->rd_p == 0) - return; - - /* Move the empty entry to the end of the list */ - wr_idx = D_IDX(rx_ring, rx_ring->wr_p); - last_idx = rx_ring->cnt - 1; - rx_ring->rxbufs[wr_idx].dma_addr = rx_ring->rxbufs[last_idx].dma_addr; - rx_ring->rxbufs[wr_idx].frag = rx_ring->rxbufs[last_idx].frag; - rx_ring->rxbufs[last_idx].dma_addr = 0; - rx_ring->rxbufs[last_idx].frag = NULL; - - memset(rx_ring->rxds, 0, rx_ring->size); - rx_ring->wr_p = 0; - rx_ring->rd_p = 0; -} - -/** - * nfp_net_rx_ring_bufs_free() - Free any buffers currently on the RX ring - * @dp: NFP Net data path struct - * @rx_ring: RX ring to remove buffers from - * - * Assumes that the device is stopped and buffers are in [0, ring->cnt - 1) - * entries. After device is disabled nfp_net_rx_ring_reset() must be called - * to restore required ring geometry. - */ -static void -nfp_net_rx_ring_bufs_free(struct nfp_net_dp *dp, - struct nfp_net_rx_ring *rx_ring) -{ - unsigned int i; - - for (i = 0; i < rx_ring->cnt - 1; i++) { - /* NULL skb can only happen when initial filling of the ring - * fails to allocate enough buffers and calls here to free - * already allocated ones. - */ - if (!rx_ring->rxbufs[i].frag) - continue; - - nfp_net_dma_unmap_rx(dp, rx_ring->rxbufs[i].dma_addr); - nfp_net_free_frag(rx_ring->rxbufs[i].frag, dp->xdp_prog); - rx_ring->rxbufs[i].dma_addr = 0; - rx_ring->rxbufs[i].frag = NULL; - } -} - -/** - * nfp_net_rx_ring_bufs_alloc() - Fill RX ring with buffers (don't give to FW) - * @dp: NFP Net data path struct - * @rx_ring: RX ring to remove buffers from - */ -static int -nfp_net_rx_ring_bufs_alloc(struct nfp_net_dp *dp, - struct nfp_net_rx_ring *rx_ring) -{ - struct nfp_net_rx_buf *rxbufs; - unsigned int i; - - rxbufs = rx_ring->rxbufs; - - for (i = 0; i < rx_ring->cnt - 1; i++) { - rxbufs[i].frag = nfp_net_rx_alloc_one(dp, &rxbufs[i].dma_addr); - if (!rxbufs[i].frag) { - nfp_net_rx_ring_bufs_free(dp, rx_ring); - return -ENOMEM; - } - } - - return 0; -} - -/** - * nfp_net_rx_ring_fill_freelist() - Give buffers from the ring to FW - * @dp: NFP Net data path struct - * @rx_ring: RX ring to fill - */ -static void -nfp_net_rx_ring_fill_freelist(struct nfp_net_dp *dp, - struct nfp_net_rx_ring *rx_ring) -{ - unsigned int i; - - for (i = 0; i < rx_ring->cnt - 1; i++) - nfp_net_rx_give_one(dp, rx_ring, rx_ring->rxbufs[i].frag, - rx_ring->rxbufs[i].dma_addr); -} - -/** - * nfp_net_rx_csum_has_errors() - group check if rxd has any csum errors - * @flags: RX descriptor flags field in CPU byte order - */ -static int nfp_net_rx_csum_has_errors(u16 flags) -{ - u16 csum_all_checked, csum_all_ok; - - csum_all_checked = flags & __PCIE_DESC_RX_CSUM_ALL; - csum_all_ok = flags & __PCIE_DESC_RX_CSUM_ALL_OK; - - return csum_all_checked != (csum_all_ok << PCIE_DESC_RX_CSUM_OK_SHIFT); -} - -/** - * nfp_net_rx_csum() - set SKB checksum field based on RX descriptor flags - * @dp: NFP Net data path struct - * @r_vec: per-ring structure - * @rxd: Pointer to RX descriptor - * @meta: Parsed metadata prepend - * @skb: Pointer to SKB - */ -static void nfp_net_rx_csum(struct nfp_net_dp *dp, - struct nfp_net_r_vector *r_vec, - struct nfp_net_rx_desc *rxd, - struct nfp_meta_parsed *meta, struct sk_buff *skb) -{ - skb_checksum_none_assert(skb); - - if (!(dp->netdev->features & NETIF_F_RXCSUM)) - return; - - if (meta->csum_type) { - skb->ip_summed = meta->csum_type; - skb->csum = meta->csum; - u64_stats_update_begin(&r_vec->rx_sync); - r_vec->hw_csum_rx_complete++; - u64_stats_update_end(&r_vec->rx_sync); - return; - } - - if (nfp_net_rx_csum_has_errors(le16_to_cpu(rxd->rxd.flags))) { - u64_stats_update_begin(&r_vec->rx_sync); - r_vec->hw_csum_rx_error++; - u64_stats_update_end(&r_vec->rx_sync); - return; - } - - /* Assume that the firmware will never report inner CSUM_OK unless outer - * L4 headers were successfully parsed. FW will always report zero UDP - * checksum as CSUM_OK. - */ - if (rxd->rxd.flags & PCIE_DESC_RX_TCP_CSUM_OK || - rxd->rxd.flags & PCIE_DESC_RX_UDP_CSUM_OK) { - __skb_incr_checksum_unnecessary(skb); - u64_stats_update_begin(&r_vec->rx_sync); - r_vec->hw_csum_rx_ok++; - u64_stats_update_end(&r_vec->rx_sync); - } - - if (rxd->rxd.flags & PCIE_DESC_RX_I_TCP_CSUM_OK || - rxd->rxd.flags & PCIE_DESC_RX_I_UDP_CSUM_OK) { - __skb_incr_checksum_unnecessary(skb); - u64_stats_update_begin(&r_vec->rx_sync); - r_vec->hw_csum_rx_inner_ok++; - u64_stats_update_end(&r_vec->rx_sync); - } -} - -static void -nfp_net_set_hash(struct net_device *netdev, struct nfp_meta_parsed *meta, - unsigned int type, __be32 *hash) -{ - if (!(netdev->features & NETIF_F_RXHASH)) - return; - - switch (type) { - case NFP_NET_RSS_IPV4: - case NFP_NET_RSS_IPV6: - case NFP_NET_RSS_IPV6_EX: - meta->hash_type = PKT_HASH_TYPE_L3; - break; - default: - meta->hash_type = PKT_HASH_TYPE_L4; - break; - } - - meta->hash = get_unaligned_be32(hash); -} - -static void -nfp_net_set_hash_desc(struct net_device *netdev, struct nfp_meta_parsed *meta, - void *data, struct nfp_net_rx_desc *rxd) -{ - struct nfp_net_rx_hash *rx_hash = data; - - if (!(rxd->rxd.flags & PCIE_DESC_RX_RSS)) - return; - - nfp_net_set_hash(netdev, meta, get_unaligned_be32(&rx_hash->hash_type), - &rx_hash->hash); -} - -static bool -nfp_net_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta, - void *data, void *pkt, unsigned int pkt_len, int meta_len) -{ - u32 meta_info; - - meta_info = get_unaligned_be32(data); - data += 4; - - while (meta_info) { - switch (meta_info & NFP_NET_META_FIELD_MASK) { - case NFP_NET_META_HASH: - meta_info >>= NFP_NET_META_FIELD_SIZE; - nfp_net_set_hash(netdev, meta, - meta_info & NFP_NET_META_FIELD_MASK, - (__be32 *)data); - data += 4; - break; - case NFP_NET_META_MARK: - meta->mark = get_unaligned_be32(data); - data += 4; - break; - case NFP_NET_META_PORTID: - meta->portid = get_unaligned_be32(data); - data += 4; - break; - case NFP_NET_META_CSUM: - meta->csum_type = CHECKSUM_COMPLETE; - meta->csum = - (__force __wsum)__get_unaligned_cpu32(data); - data += 4; - break; - case NFP_NET_META_RESYNC_INFO: - if (nfp_net_tls_rx_resync_req(netdev, data, pkt, - pkt_len)) - return false; - data += sizeof(struct nfp_net_tls_resync_req); - break; - default: - return true; - } - - meta_info >>= NFP_NET_META_FIELD_SIZE; - } - - return data != pkt; -} - -static void -nfp_net_rx_drop(const struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, - struct nfp_net_rx_ring *rx_ring, struct nfp_net_rx_buf *rxbuf, - struct sk_buff *skb) -{ - u64_stats_update_begin(&r_vec->rx_sync); - r_vec->rx_drops++; - /* If we have both skb and rxbuf the replacement buffer allocation - * must have failed, count this as an alloc failure. - */ - if (skb && rxbuf) - r_vec->rx_replace_buf_alloc_fail++; - u64_stats_update_end(&r_vec->rx_sync); - - /* skb is build based on the frag, free_skb() would free the frag - * so to be able to reuse it we need an extra ref. - */ - if (skb && rxbuf && skb->head == rxbuf->frag) - page_ref_inc(virt_to_head_page(rxbuf->frag)); - if (rxbuf) - nfp_net_rx_give_one(dp, rx_ring, rxbuf->frag, rxbuf->dma_addr); - if (skb) - dev_kfree_skb_any(skb); -} - -static bool -nfp_net_tx_xdp_buf(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, - struct nfp_net_tx_ring *tx_ring, - struct nfp_net_rx_buf *rxbuf, unsigned int dma_off, - unsigned int pkt_len, bool *completed) -{ - unsigned int dma_map_sz = dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA; - struct nfp_net_tx_buf *txbuf; - struct nfp_net_tx_desc *txd; - int wr_idx; - - /* Reject if xdp_adjust_tail grow packet beyond DMA area */ - if (pkt_len + dma_off > dma_map_sz) - return false; - - if (unlikely(nfp_net_tx_full(tx_ring, 1))) { - if (!*completed) { - nfp_net_xdp_complete(tx_ring); - *completed = true; - } - - if (unlikely(nfp_net_tx_full(tx_ring, 1))) { - nfp_net_rx_drop(dp, rx_ring->r_vec, rx_ring, rxbuf, - NULL); - return false; - } - } - - wr_idx = D_IDX(tx_ring, tx_ring->wr_p); - - /* Stash the soft descriptor of the head then initialize it */ - txbuf = &tx_ring->txbufs[wr_idx]; - - nfp_net_rx_give_one(dp, rx_ring, txbuf->frag, txbuf->dma_addr); - - txbuf->frag = rxbuf->frag; - txbuf->dma_addr = rxbuf->dma_addr; - txbuf->fidx = -1; - txbuf->pkt_cnt = 1; - txbuf->real_len = pkt_len; - - dma_sync_single_for_device(dp->dev, rxbuf->dma_addr + dma_off, - pkt_len, DMA_BIDIRECTIONAL); - - /* Build TX descriptor */ - txd = &tx_ring->txds[wr_idx]; - txd->offset_eop = PCIE_DESC_TX_EOP; - txd->dma_len = cpu_to_le16(pkt_len); - nfp_desc_set_dma_addr(txd, rxbuf->dma_addr + dma_off); - txd->data_len = cpu_to_le16(pkt_len); - - txd->flags = 0; - txd->mss = 0; - txd->lso_hdrlen = 0; - - tx_ring->wr_p++; - tx_ring->wr_ptr_add++; - return true; -} - -/** - * nfp_net_rx() - receive up to @budget packets on @rx_ring - * @rx_ring: RX ring to receive from - * @budget: NAPI budget - * - * Note, this function is separated out from the napi poll function to - * more cleanly separate packet receive code from other bookkeeping - * functions performed in the napi poll function. - * - * Return: Number of packets received. - */ -static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) -{ - struct nfp_net_r_vector *r_vec = rx_ring->r_vec; - struct nfp_net_dp *dp = &r_vec->nfp_net->dp; - struct nfp_net_tx_ring *tx_ring; - struct bpf_prog *xdp_prog; - bool xdp_tx_cmpl = false; - unsigned int true_bufsz; - struct sk_buff *skb; - int pkts_polled = 0; - struct xdp_buff xdp; - int idx; - - xdp_prog = READ_ONCE(dp->xdp_prog); - true_bufsz = xdp_prog ? PAGE_SIZE : dp->fl_bufsz; - xdp_init_buff(&xdp, PAGE_SIZE - NFP_NET_RX_BUF_HEADROOM, - &rx_ring->xdp_rxq); - tx_ring = r_vec->xdp_ring; - - while (pkts_polled < budget) { - unsigned int meta_len, data_len, meta_off, pkt_len, pkt_off; - struct nfp_net_rx_buf *rxbuf; - struct nfp_net_rx_desc *rxd; - struct nfp_meta_parsed meta; - bool redir_egress = false; - struct net_device *netdev; - dma_addr_t new_dma_addr; - u32 meta_len_xdp = 0; - void *new_frag; - - idx = D_IDX(rx_ring, rx_ring->rd_p); - - rxd = &rx_ring->rxds[idx]; - if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) - break; - - /* Memory barrier to ensure that we won't do other reads - * before the DD bit. - */ - dma_rmb(); - - memset(&meta, 0, sizeof(meta)); - - rx_ring->rd_p++; - pkts_polled++; - - rxbuf = &rx_ring->rxbufs[idx]; - /* < meta_len > - * <-- [rx_offset] --> - * --------------------------------------------------------- - * | [XX] | metadata | packet | XXXX | - * --------------------------------------------------------- - * <---------------- data_len ---------------> - * - * The rx_offset is fixed for all packets, the meta_len can vary - * on a packet by packet basis. If rx_offset is set to zero - * (_RX_OFFSET_DYNAMIC) metadata starts at the beginning of the - * buffer and is immediately followed by the packet (no [XX]). - */ - meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK; - data_len = le16_to_cpu(rxd->rxd.data_len); - pkt_len = data_len - meta_len; - - pkt_off = NFP_NET_RX_BUF_HEADROOM + dp->rx_dma_off; - if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) - pkt_off += meta_len; - else - pkt_off += dp->rx_offset; - meta_off = pkt_off - meta_len; - - /* Stats update */ - u64_stats_update_begin(&r_vec->rx_sync); - r_vec->rx_pkts++; - r_vec->rx_bytes += pkt_len; - u64_stats_update_end(&r_vec->rx_sync); - - if (unlikely(meta_len > NFP_NET_MAX_PREPEND || - (dp->rx_offset && meta_len > dp->rx_offset))) { - nn_dp_warn(dp, "oversized RX packet metadata %u\n", - meta_len); - nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); - continue; - } - - nfp_net_dma_sync_cpu_rx(dp, rxbuf->dma_addr + meta_off, - data_len); - - if (!dp->chained_metadata_format) { - nfp_net_set_hash_desc(dp->netdev, &meta, - rxbuf->frag + meta_off, rxd); - } else if (meta_len) { - if (unlikely(nfp_net_parse_meta(dp->netdev, &meta, - rxbuf->frag + meta_off, - rxbuf->frag + pkt_off, - pkt_len, meta_len))) { - nn_dp_warn(dp, "invalid RX packet metadata\n"); - nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, - NULL); - continue; - } - } - - if (xdp_prog && !meta.portid) { - void *orig_data = rxbuf->frag + pkt_off; - unsigned int dma_off; - int act; - - xdp_prepare_buff(&xdp, - rxbuf->frag + NFP_NET_RX_BUF_HEADROOM, - pkt_off - NFP_NET_RX_BUF_HEADROOM, - pkt_len, true); - - act = bpf_prog_run_xdp(xdp_prog, &xdp); - - pkt_len = xdp.data_end - xdp.data; - pkt_off += xdp.data - orig_data; - - switch (act) { - case XDP_PASS: - meta_len_xdp = xdp.data - xdp.data_meta; - break; - case XDP_TX: - dma_off = pkt_off - NFP_NET_RX_BUF_HEADROOM; - if (unlikely(!nfp_net_tx_xdp_buf(dp, rx_ring, - tx_ring, rxbuf, - dma_off, - pkt_len, - &xdp_tx_cmpl))) - trace_xdp_exception(dp->netdev, - xdp_prog, act); - continue; - default: - bpf_warn_invalid_xdp_action(dp->netdev, xdp_prog, act); - fallthrough; - case XDP_ABORTED: - trace_xdp_exception(dp->netdev, xdp_prog, act); - fallthrough; - case XDP_DROP: - nfp_net_rx_give_one(dp, rx_ring, rxbuf->frag, - rxbuf->dma_addr); - continue; - } - } - - if (likely(!meta.portid)) { - netdev = dp->netdev; - } else if (meta.portid == NFP_META_PORT_ID_CTRL) { - struct nfp_net *nn = netdev_priv(dp->netdev); - - nfp_app_ctrl_rx_raw(nn->app, rxbuf->frag + pkt_off, - pkt_len); - nfp_net_rx_give_one(dp, rx_ring, rxbuf->frag, - rxbuf->dma_addr); - continue; - } else { - struct nfp_net *nn; - - nn = netdev_priv(dp->netdev); - netdev = nfp_app_dev_get(nn->app, meta.portid, - &redir_egress); - if (unlikely(!netdev)) { - nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, - NULL); - continue; - } - - if (nfp_netdev_is_nfp_repr(netdev)) - nfp_repr_inc_rx_stats(netdev, pkt_len); - } - - skb = build_skb(rxbuf->frag, true_bufsz); - if (unlikely(!skb)) { - nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); - continue; - } - new_frag = nfp_net_napi_alloc_one(dp, &new_dma_addr); - if (unlikely(!new_frag)) { - nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, skb); - continue; - } - - nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr); - - nfp_net_rx_give_one(dp, rx_ring, new_frag, new_dma_addr); - - skb_reserve(skb, pkt_off); - skb_put(skb, pkt_len); - - skb->mark = meta.mark; - skb_set_hash(skb, meta.hash, meta.hash_type); - - skb_record_rx_queue(skb, rx_ring->idx); - skb->protocol = eth_type_trans(skb, netdev); - - nfp_net_rx_csum(dp, r_vec, rxd, &meta, skb); - -#ifdef CONFIG_TLS_DEVICE - if (rxd->rxd.flags & PCIE_DESC_RX_DECRYPTED) { - skb->decrypted = true; - u64_stats_update_begin(&r_vec->rx_sync); - r_vec->hw_tls_rx++; - u64_stats_update_end(&r_vec->rx_sync); - } -#endif - - if (rxd->rxd.flags & PCIE_DESC_RX_VLAN) - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), - le16_to_cpu(rxd->rxd.vlan)); - if (meta_len_xdp) - skb_metadata_set(skb, meta_len_xdp); - - if (likely(!redir_egress)) { - napi_gro_receive(&rx_ring->r_vec->napi, skb); - } else { - skb->dev = netdev; - skb_reset_network_header(skb); - __skb_push(skb, ETH_HLEN); - dev_queue_xmit(skb); - } - } - - if (xdp_prog) { - if (tx_ring->wr_ptr_add) - nfp_net_tx_xmit_more_flush(tx_ring); - else if (unlikely(tx_ring->wr_p != tx_ring->rd_p) && - !xdp_tx_cmpl) - if (!nfp_net_xdp_complete(tx_ring)) - pkts_polled = budget; - } - - return pkts_polled; -} - -/** - * nfp_net_poll() - napi poll function - * @napi: NAPI structure - * @budget: NAPI budget - * - * Return: number of packets polled. - */ -static int nfp_net_poll(struct napi_struct *napi, int budget) -{ - struct nfp_net_r_vector *r_vec = - container_of(napi, struct nfp_net_r_vector, napi); - unsigned int pkts_polled = 0; - - if (r_vec->tx_ring) - nfp_net_tx_complete(r_vec->tx_ring, budget); - if (r_vec->rx_ring) - pkts_polled = nfp_net_rx(r_vec->rx_ring, budget); - - if (pkts_polled < budget) - if (napi_complete_done(napi, pkts_polled)) - nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_entry); - - if (r_vec->nfp_net->rx_coalesce_adapt_on && r_vec->rx_ring) { - struct dim_sample dim_sample = {}; - unsigned int start; - u64 pkts, bytes; - - do { - start = u64_stats_fetch_begin(&r_vec->rx_sync); - pkts = r_vec->rx_pkts; - bytes = r_vec->rx_bytes; - } while (u64_stats_fetch_retry(&r_vec->rx_sync, start)); - - dim_update_sample(r_vec->event_ctr, pkts, bytes, &dim_sample); - net_dim(&r_vec->rx_dim, dim_sample); - } - - if (r_vec->nfp_net->tx_coalesce_adapt_on && r_vec->tx_ring) { - struct dim_sample dim_sample = {}; - unsigned int start; - u64 pkts, bytes; - - do { - start = u64_stats_fetch_begin(&r_vec->tx_sync); - pkts = r_vec->tx_pkts; - bytes = r_vec->tx_bytes; - } while (u64_stats_fetch_retry(&r_vec->tx_sync, start)); - - dim_update_sample(r_vec->event_ctr, pkts, bytes, &dim_sample); - net_dim(&r_vec->tx_dim, dim_sample); - } - - return pkts_polled; -} - -/* Control device data path - */ - -static bool -nfp_ctrl_tx_one(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, - struct sk_buff *skb, bool old) -{ - unsigned int real_len = skb->len, meta_len = 0; - struct nfp_net_tx_ring *tx_ring; - struct nfp_net_tx_buf *txbuf; - struct nfp_net_tx_desc *txd; - struct nfp_net_dp *dp; - dma_addr_t dma_addr; - int wr_idx; - - dp = &r_vec->nfp_net->dp; - tx_ring = r_vec->tx_ring; - - if (WARN_ON_ONCE(skb_shinfo(skb)->nr_frags)) { - nn_dp_warn(dp, "Driver's CTRL TX does not implement gather\n"); - goto err_free; - } - - if (unlikely(nfp_net_tx_full(tx_ring, 1))) { - u64_stats_update_begin(&r_vec->tx_sync); - r_vec->tx_busy++; - u64_stats_update_end(&r_vec->tx_sync); - if (!old) - __skb_queue_tail(&r_vec->queue, skb); - else - __skb_queue_head(&r_vec->queue, skb); - return true; - } - - if (nfp_app_ctrl_has_meta(nn->app)) { - if (unlikely(skb_headroom(skb) < 8)) { - nn_dp_warn(dp, "CTRL TX on skb without headroom\n"); - goto err_free; - } - meta_len = 8; - put_unaligned_be32(NFP_META_PORT_ID_CTRL, skb_push(skb, 4)); - put_unaligned_be32(NFP_NET_META_PORTID, skb_push(skb, 4)); - } - - /* Start with the head skbuf */ - dma_addr = dma_map_single(dp->dev, skb->data, skb_headlen(skb), - DMA_TO_DEVICE); - if (dma_mapping_error(dp->dev, dma_addr)) - goto err_dma_warn; - - wr_idx = D_IDX(tx_ring, tx_ring->wr_p); - - /* Stash the soft descriptor of the head then initialize it */ - txbuf = &tx_ring->txbufs[wr_idx]; - txbuf->skb = skb; - txbuf->dma_addr = dma_addr; - txbuf->fidx = -1; - txbuf->pkt_cnt = 1; - txbuf->real_len = real_len; - - /* Build TX descriptor */ - txd = &tx_ring->txds[wr_idx]; - txd->offset_eop = meta_len | PCIE_DESC_TX_EOP; - txd->dma_len = cpu_to_le16(skb_headlen(skb)); - nfp_desc_set_dma_addr(txd, dma_addr); - txd->data_len = cpu_to_le16(skb->len); - - txd->flags = 0; - txd->mss = 0; - txd->lso_hdrlen = 0; - - tx_ring->wr_p++; - tx_ring->wr_ptr_add++; - nfp_net_tx_xmit_more_flush(tx_ring); - - return false; - -err_dma_warn: - nn_dp_warn(dp, "Failed to DMA map TX CTRL buffer\n"); -err_free: - u64_stats_update_begin(&r_vec->tx_sync); - r_vec->tx_errors++; - u64_stats_update_end(&r_vec->tx_sync); - dev_kfree_skb_any(skb); - return false; -} - -bool __nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb) -{ - struct nfp_net_r_vector *r_vec = &nn->r_vecs[0]; - - return nfp_ctrl_tx_one(nn, r_vec, skb, false); -} - -bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb) -{ - struct nfp_net_r_vector *r_vec = &nn->r_vecs[0]; - bool ret; - - spin_lock_bh(&r_vec->lock); - ret = nfp_ctrl_tx_one(nn, r_vec, skb, false); - spin_unlock_bh(&r_vec->lock); - - return ret; -} - -static void __nfp_ctrl_tx_queued(struct nfp_net_r_vector *r_vec) -{ - struct sk_buff *skb; - - while ((skb = __skb_dequeue(&r_vec->queue))) - if (nfp_ctrl_tx_one(r_vec->nfp_net, r_vec, skb, true)) - return; -} - -static bool -nfp_ctrl_meta_ok(struct nfp_net *nn, void *data, unsigned int meta_len) -{ - u32 meta_type, meta_tag; - - if (!nfp_app_ctrl_has_meta(nn->app)) - return !meta_len; - - if (meta_len != 8) - return false; - - meta_type = get_unaligned_be32(data); - meta_tag = get_unaligned_be32(data + 4); - - return (meta_type == NFP_NET_META_PORTID && - meta_tag == NFP_META_PORT_ID_CTRL); -} - -static bool -nfp_ctrl_rx_one(struct nfp_net *nn, struct nfp_net_dp *dp, - struct nfp_net_r_vector *r_vec, struct nfp_net_rx_ring *rx_ring) -{ - unsigned int meta_len, data_len, meta_off, pkt_len, pkt_off; - struct nfp_net_rx_buf *rxbuf; - struct nfp_net_rx_desc *rxd; - dma_addr_t new_dma_addr; - struct sk_buff *skb; - void *new_frag; - int idx; - - idx = D_IDX(rx_ring, rx_ring->rd_p); - - rxd = &rx_ring->rxds[idx]; - if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) - return false; - - /* Memory barrier to ensure that we won't do other reads - * before the DD bit. - */ - dma_rmb(); - - rx_ring->rd_p++; - - rxbuf = &rx_ring->rxbufs[idx]; - meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK; - data_len = le16_to_cpu(rxd->rxd.data_len); - pkt_len = data_len - meta_len; - - pkt_off = NFP_NET_RX_BUF_HEADROOM + dp->rx_dma_off; - if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) - pkt_off += meta_len; - else - pkt_off += dp->rx_offset; - meta_off = pkt_off - meta_len; - - /* Stats update */ - u64_stats_update_begin(&r_vec->rx_sync); - r_vec->rx_pkts++; - r_vec->rx_bytes += pkt_len; - u64_stats_update_end(&r_vec->rx_sync); - - nfp_net_dma_sync_cpu_rx(dp, rxbuf->dma_addr + meta_off, data_len); - - if (unlikely(!nfp_ctrl_meta_ok(nn, rxbuf->frag + meta_off, meta_len))) { - nn_dp_warn(dp, "incorrect metadata for ctrl packet (%d)\n", - meta_len); - nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); - return true; - } - - skb = build_skb(rxbuf->frag, dp->fl_bufsz); - if (unlikely(!skb)) { - nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); - return true; - } - new_frag = nfp_net_napi_alloc_one(dp, &new_dma_addr); - if (unlikely(!new_frag)) { - nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, skb); - return true; - } - - nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr); - - nfp_net_rx_give_one(dp, rx_ring, new_frag, new_dma_addr); - - skb_reserve(skb, pkt_off); - skb_put(skb, pkt_len); - - nfp_app_ctrl_rx(nn->app, skb); - - return true; -} - -static bool nfp_ctrl_rx(struct nfp_net_r_vector *r_vec) -{ - struct nfp_net_rx_ring *rx_ring = r_vec->rx_ring; - struct nfp_net *nn = r_vec->nfp_net; - struct nfp_net_dp *dp = &nn->dp; - unsigned int budget = 512; - - while (nfp_ctrl_rx_one(nn, dp, r_vec, rx_ring) && budget--) - continue; - - return budget; -} - -static void nfp_ctrl_poll(struct tasklet_struct *t) -{ - struct nfp_net_r_vector *r_vec = from_tasklet(r_vec, t, tasklet); - - spin_lock(&r_vec->lock); - nfp_net_tx_complete(r_vec->tx_ring, 0); - __nfp_ctrl_tx_queued(r_vec); - spin_unlock(&r_vec->lock); - - if (nfp_ctrl_rx(r_vec)) { - nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_entry); - } else { - tasklet_schedule(&r_vec->tasklet); - nn_dp_warn(&r_vec->nfp_net->dp, - "control message budget exceeded!\n"); - } + return fl_bufsz; } /* Setup and Configuration @@ -2371,7 +754,7 @@ static void nfp_net_vecs_init(struct nfp_net *nn) __skb_queue_head_init(&r_vec->queue); spin_lock_init(&r_vec->lock); - tasklet_setup(&r_vec->tasklet, nfp_ctrl_poll); + tasklet_setup(&r_vec->tasklet, nn->dp.ops->ctrl_poll); tasklet_disable(&r_vec->tasklet); } @@ -2379,263 +762,25 @@ static void nfp_net_vecs_init(struct nfp_net *nn) } } -/** - * nfp_net_tx_ring_free() - Free resources allocated to a TX ring - * @tx_ring: TX ring to free - */ -static void nfp_net_tx_ring_free(struct nfp_net_tx_ring *tx_ring) +static void +nfp_net_napi_add(struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, int idx) { - struct nfp_net_r_vector *r_vec = tx_ring->r_vec; - struct nfp_net_dp *dp = &r_vec->nfp_net->dp; - - kvfree(tx_ring->txbufs); - - if (tx_ring->txds) - dma_free_coherent(dp->dev, tx_ring->size, - tx_ring->txds, tx_ring->dma); - - tx_ring->cnt = 0; - tx_ring->txbufs = NULL; - tx_ring->txds = NULL; - tx_ring->dma = 0; - tx_ring->size = 0; -} - -/** - * nfp_net_tx_ring_alloc() - Allocate resource for a TX ring - * @dp: NFP Net data path struct - * @tx_ring: TX Ring structure to allocate - * - * Return: 0 on success, negative errno otherwise. - */ -static int -nfp_net_tx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) -{ - struct nfp_net_r_vector *r_vec = tx_ring->r_vec; - - tx_ring->cnt = dp->txd_cnt; - - tx_ring->size = array_size(tx_ring->cnt, sizeof(*tx_ring->txds)); - tx_ring->txds = dma_alloc_coherent(dp->dev, tx_ring->size, - &tx_ring->dma, - GFP_KERNEL | __GFP_NOWARN); - if (!tx_ring->txds) { - netdev_warn(dp->netdev, "failed to allocate TX descriptor ring memory, requested descriptor count: %d, consider lowering descriptor count\n", - tx_ring->cnt); - goto err_alloc; - } - - tx_ring->txbufs = kvcalloc(tx_ring->cnt, sizeof(*tx_ring->txbufs), - GFP_KERNEL); - if (!tx_ring->txbufs) - goto err_alloc; - - if (!tx_ring->is_xdp && dp->netdev) - netif_set_xps_queue(dp->netdev, &r_vec->affinity_mask, - tx_ring->idx); - - return 0; - -err_alloc: - nfp_net_tx_ring_free(tx_ring); - return -ENOMEM; + if (dp->netdev) + netif_napi_add(dp->netdev, &r_vec->napi, + nfp_net_has_xsk_pool_slow(dp, idx) ? + dp->ops->xsk_poll : dp->ops->poll, + NAPI_POLL_WEIGHT); + else + tasklet_enable(&r_vec->tasklet); } static void -nfp_net_tx_ring_bufs_free(struct nfp_net_dp *dp, - struct nfp_net_tx_ring *tx_ring) +nfp_net_napi_del(struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec) { - unsigned int i; - - if (!tx_ring->is_xdp) - return; - - for (i = 0; i < tx_ring->cnt; i++) { - if (!tx_ring->txbufs[i].frag) - return; - - nfp_net_dma_unmap_rx(dp, tx_ring->txbufs[i].dma_addr); - __free_page(virt_to_page(tx_ring->txbufs[i].frag)); - } -} - -static int -nfp_net_tx_ring_bufs_alloc(struct nfp_net_dp *dp, - struct nfp_net_tx_ring *tx_ring) -{ - struct nfp_net_tx_buf *txbufs = tx_ring->txbufs; - unsigned int i; - - if (!tx_ring->is_xdp) - return 0; - - for (i = 0; i < tx_ring->cnt; i++) { - txbufs[i].frag = nfp_net_rx_alloc_one(dp, &txbufs[i].dma_addr); - if (!txbufs[i].frag) { - nfp_net_tx_ring_bufs_free(dp, tx_ring); - return -ENOMEM; - } - } - - return 0; -} - -static int nfp_net_tx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp) -{ - unsigned int r; - - dp->tx_rings = kcalloc(dp->num_tx_rings, sizeof(*dp->tx_rings), - GFP_KERNEL); - if (!dp->tx_rings) - return -ENOMEM; - - for (r = 0; r < dp->num_tx_rings; r++) { - int bias = 0; - - if (r >= dp->num_stack_tx_rings) - bias = dp->num_stack_tx_rings; - - nfp_net_tx_ring_init(&dp->tx_rings[r], &nn->r_vecs[r - bias], - r, bias); - - if (nfp_net_tx_ring_alloc(dp, &dp->tx_rings[r])) - goto err_free_prev; - - if (nfp_net_tx_ring_bufs_alloc(dp, &dp->tx_rings[r])) - goto err_free_ring; - } - - return 0; - -err_free_prev: - while (r--) { - nfp_net_tx_ring_bufs_free(dp, &dp->tx_rings[r]); -err_free_ring: - nfp_net_tx_ring_free(&dp->tx_rings[r]); - } - kfree(dp->tx_rings); - return -ENOMEM; -} - -static void nfp_net_tx_rings_free(struct nfp_net_dp *dp) -{ - unsigned int r; - - for (r = 0; r < dp->num_tx_rings; r++) { - nfp_net_tx_ring_bufs_free(dp, &dp->tx_rings[r]); - nfp_net_tx_ring_free(&dp->tx_rings[r]); - } - - kfree(dp->tx_rings); -} - -/** - * nfp_net_rx_ring_free() - Free resources allocated to a RX ring - * @rx_ring: RX ring to free - */ -static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring) -{ - struct nfp_net_r_vector *r_vec = rx_ring->r_vec; - struct nfp_net_dp *dp = &r_vec->nfp_net->dp; - if (dp->netdev) - xdp_rxq_info_unreg(&rx_ring->xdp_rxq); - kvfree(rx_ring->rxbufs); - - if (rx_ring->rxds) - dma_free_coherent(dp->dev, rx_ring->size, - rx_ring->rxds, rx_ring->dma); - - rx_ring->cnt = 0; - rx_ring->rxbufs = NULL; - rx_ring->rxds = NULL; - rx_ring->dma = 0; - rx_ring->size = 0; -} - -/** - * nfp_net_rx_ring_alloc() - Allocate resource for a RX ring - * @dp: NFP Net data path struct - * @rx_ring: RX ring to allocate - * - * Return: 0 on success, negative errno otherwise. - */ -static int -nfp_net_rx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring) -{ - int err; - - if (dp->netdev) { - err = xdp_rxq_info_reg(&rx_ring->xdp_rxq, dp->netdev, - rx_ring->idx, rx_ring->r_vec->napi.napi_id); - if (err < 0) - return err; - } - - rx_ring->cnt = dp->rxd_cnt; - rx_ring->size = array_size(rx_ring->cnt, sizeof(*rx_ring->rxds)); - rx_ring->rxds = dma_alloc_coherent(dp->dev, rx_ring->size, - &rx_ring->dma, - GFP_KERNEL | __GFP_NOWARN); - if (!rx_ring->rxds) { - netdev_warn(dp->netdev, "failed to allocate RX descriptor ring memory, requested descriptor count: %d, consider lowering descriptor count\n", - rx_ring->cnt); - goto err_alloc; - } - - rx_ring->rxbufs = kvcalloc(rx_ring->cnt, sizeof(*rx_ring->rxbufs), - GFP_KERNEL); - if (!rx_ring->rxbufs) - goto err_alloc; - - return 0; - -err_alloc: - nfp_net_rx_ring_free(rx_ring); - return -ENOMEM; -} - -static int nfp_net_rx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp) -{ - unsigned int r; - - dp->rx_rings = kcalloc(dp->num_rx_rings, sizeof(*dp->rx_rings), - GFP_KERNEL); - if (!dp->rx_rings) - return -ENOMEM; - - for (r = 0; r < dp->num_rx_rings; r++) { - nfp_net_rx_ring_init(&dp->rx_rings[r], &nn->r_vecs[r], r); - - if (nfp_net_rx_ring_alloc(dp, &dp->rx_rings[r])) - goto err_free_prev; - - if (nfp_net_rx_ring_bufs_alloc(dp, &dp->rx_rings[r])) - goto err_free_ring; - } - - return 0; - -err_free_prev: - while (r--) { - nfp_net_rx_ring_bufs_free(dp, &dp->rx_rings[r]); -err_free_ring: - nfp_net_rx_ring_free(&dp->rx_rings[r]); - } - kfree(dp->rx_rings); - return -ENOMEM; -} - -static void nfp_net_rx_rings_free(struct nfp_net_dp *dp) -{ - unsigned int r; - - for (r = 0; r < dp->num_rx_rings; r++) { - nfp_net_rx_ring_bufs_free(dp, &dp->rx_rings[r]); - nfp_net_rx_ring_free(&dp->rx_rings[r]); - } - - kfree(dp->rx_rings); + netif_napi_del(&r_vec->napi); + else + tasklet_disable(&r_vec->tasklet); } static void @@ -2648,6 +793,17 @@ nfp_net_vector_assign_rings(struct nfp_net_dp *dp, r_vec->xdp_ring = idx < dp->num_tx_rings - dp->num_stack_tx_rings ? &dp->tx_rings[dp->num_stack_tx_rings + idx] : NULL; + + if (nfp_net_has_xsk_pool_slow(dp, idx) || r_vec->xsk_pool) { + r_vec->xsk_pool = dp->xdp_prog ? dp->xsk_pools[idx] : NULL; + + if (r_vec->xsk_pool) + xsk_pool_set_rxq_info(r_vec->xsk_pool, + &r_vec->rx_ring->xdp_rxq); + + nfp_net_napi_del(dp, r_vec); + nfp_net_napi_add(dp, r_vec, idx); + } } static int @@ -2656,23 +812,14 @@ nfp_net_prepare_vector(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, { int err; - /* Setup NAPI */ - if (nn->dp.netdev) - netif_napi_add(nn->dp.netdev, &r_vec->napi, - nfp_net_poll, NAPI_POLL_WEIGHT); - else - tasklet_enable(&r_vec->tasklet); + nfp_net_napi_add(&nn->dp, r_vec, idx); snprintf(r_vec->name, sizeof(r_vec->name), "%s-rxtx-%d", nfp_net_name(nn), idx); err = request_irq(r_vec->irq_vector, r_vec->handler, 0, r_vec->name, r_vec); if (err) { - if (nn->dp.netdev) - netif_napi_del(&r_vec->napi); - else - tasklet_disable(&r_vec->tasklet); - + nfp_net_napi_del(&nn->dp, r_vec); nn_err(nn, "Error requesting IRQ %d\n", r_vec->irq_vector); return err; } @@ -2690,11 +837,7 @@ static void nfp_net_cleanup_vector(struct nfp_net *nn, struct nfp_net_r_vector *r_vec) { irq_set_affinity_hint(r_vec->irq_vector, NULL); - if (nn->dp.netdev) - netif_napi_del(&r_vec->napi); - else - tasklet_disable(&r_vec->tasklet); - + nfp_net_napi_del(&nn->dp, r_vec); free_irq(r_vec->irq_vector, r_vec); } @@ -2768,17 +911,6 @@ static void nfp_net_write_mac_addr(struct nfp_net *nn, const u8 *addr) nn_writew(nn, NFP_NET_CFG_MACADDR + 6, get_unaligned_be16(addr + 4)); } -static void nfp_net_vec_clear_ring_data(struct nfp_net *nn, unsigned int idx) -{ - nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(idx), 0); - nn_writeb(nn, NFP_NET_CFG_RXR_SZ(idx), 0); - nn_writeb(nn, NFP_NET_CFG_RXR_VEC(idx), 0); - - nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(idx), 0); - nn_writeb(nn, NFP_NET_CFG_TXR_SZ(idx), 0); - nn_writeb(nn, NFP_NET_CFG_TXR_VEC(idx), 0); -} - /** * nfp_net_clear_config_and_disable() - Clear control BAR and disable NFP * @nn: NFP Net device to reconfigure @@ -2808,8 +940,11 @@ static void nfp_net_clear_config_and_disable(struct nfp_net *nn) if (err) nn_err(nn, "Could not disable device: %d\n", err); - for (r = 0; r < nn->dp.num_rx_rings; r++) + for (r = 0; r < nn->dp.num_rx_rings; r++) { nfp_net_rx_ring_reset(&nn->dp.rx_rings[r]); + if (nfp_net_has_xsk_pool_slow(&nn->dp, nn->dp.rx_rings[r].idx)) + nfp_net_xsk_rx_bufs_free(&nn->dp.rx_rings[r]); + } for (r = 0; r < nn->dp.num_tx_rings; r++) nfp_net_tx_ring_reset(&nn->dp, &nn->dp.tx_rings[r]); for (r = 0; r < nn->dp.num_r_vecs; r++) @@ -2818,25 +953,6 @@ static void nfp_net_clear_config_and_disable(struct nfp_net *nn) nn->dp.ctrl = new_ctrl; } -static void -nfp_net_rx_ring_hw_cfg_write(struct nfp_net *nn, - struct nfp_net_rx_ring *rx_ring, unsigned int idx) -{ - /* Write the DMA address, size and MSI-X info to the device */ - nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(idx), rx_ring->dma); - nn_writeb(nn, NFP_NET_CFG_RXR_SZ(idx), ilog2(rx_ring->cnt)); - nn_writeb(nn, NFP_NET_CFG_RXR_VEC(idx), rx_ring->r_vec->irq_entry); -} - -static void -nfp_net_tx_ring_hw_cfg_write(struct nfp_net *nn, - struct nfp_net_tx_ring *tx_ring, unsigned int idx) -{ - nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(idx), tx_ring->dma); - nn_writeb(nn, NFP_NET_CFG_TXR_SZ(idx), ilog2(tx_ring->cnt)); - nn_writeb(nn, NFP_NET_CFG_TXR_VEC(idx), tx_ring->r_vec->irq_entry); -} - /** * nfp_net_set_config_and_enable() - Write control BAR and enable NFP * @nn: NFP Net device to reconfigure @@ -2866,11 +982,11 @@ static int nfp_net_set_config_and_enable(struct nfp_net *nn) for (r = 0; r < nn->dp.num_rx_rings; r++) nfp_net_rx_ring_hw_cfg_write(nn, &nn->dp.rx_rings[r], r); - nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, nn->dp.num_tx_rings == 64 ? - 0xffffffffffffffffULL : ((u64)1 << nn->dp.num_tx_rings) - 1); + nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, + U64_MAX >> (64 - nn->dp.num_tx_rings)); - nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, nn->dp.num_rx_rings == 64 ? - 0xffffffffffffffffULL : ((u64)1 << nn->dp.num_rx_rings) - 1); + nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, + U64_MAX >> (64 - nn->dp.num_rx_rings)); if (nn->dp.netdev) nfp_net_write_mac_addr(nn, nn->dp.netdev->dev_addr); @@ -3296,20 +1412,39 @@ struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn) *new = nn->dp; + new->xsk_pools = kmemdup(new->xsk_pools, + array_size(nn->max_r_vecs, + sizeof(new->xsk_pools)), + GFP_KERNEL); + if (!new->xsk_pools) { + kfree(new); + return NULL; + } + /* Clear things which need to be recomputed */ new->fl_bufsz = 0; new->tx_rings = NULL; new->rx_rings = NULL; new->num_r_vecs = 0; new->num_stack_tx_rings = 0; + new->txrwb = NULL; + new->txrwb_dma = 0; return new; } +static void nfp_net_free_dp(struct nfp_net_dp *dp) +{ + kfree(dp->xsk_pools); + kfree(dp); +} + static int nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp, struct netlink_ext_ack *extack) { + unsigned int r, xsk_min_fl_bufsz; + /* XDP-enabled tests */ if (!dp->xdp_prog) return 0; @@ -3322,6 +1457,18 @@ nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp, return -EINVAL; } + xsk_min_fl_bufsz = nfp_net_calc_fl_bufsz_xsk(dp); + for (r = 0; r < nn->max_r_vecs; r++) { + if (!dp->xsk_pools[r]) + continue; + + if (xsk_pool_get_rx_frame_size(dp->xsk_pools[r]) < xsk_min_fl_bufsz) { + NL_SET_ERR_MSG_MOD(extack, + "XSK buffer pool chunk size too small"); + return -EINVAL; + } + } + return 0; } @@ -3389,7 +1536,7 @@ int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, nfp_net_open_stack(nn); exit_free_dp: - kfree(dp); + nfp_net_free_dp(dp); return err; @@ -3398,7 +1545,7 @@ int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, err_cleanup_vecs: for (r = dp->num_r_vecs - 1; r >= nn->dp.num_r_vecs; r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); - kfree(dp); + nfp_net_free_dp(dp); return err; } @@ -3716,6 +1863,9 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_bpf *xdp) return nfp_net_xdp_setup_drv(nn, xdp); case XDP_SETUP_PROG_HW: return nfp_net_xdp_setup_hw(nn, xdp); + case XDP_SETUP_XSK_POOL: + return nfp_net_xsk_setup_pool(netdev, xdp->xsk.pool, + xdp->xsk.queue_id); default: return nfp_app_bpf(nn->app, nn, xdp); } @@ -3742,7 +1892,35 @@ static int nfp_net_set_mac_address(struct net_device *netdev, void *addr) return 0; } -const struct net_device_ops nfp_net_netdev_ops = { +const struct net_device_ops nfp_nfd3_netdev_ops = { + .ndo_init = nfp_app_ndo_init, + .ndo_uninit = nfp_app_ndo_uninit, + .ndo_open = nfp_net_netdev_open, + .ndo_stop = nfp_net_netdev_close, + .ndo_start_xmit = nfp_net_tx, + .ndo_get_stats64 = nfp_net_stat64, + .ndo_vlan_rx_add_vid = nfp_net_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = nfp_net_vlan_rx_kill_vid, + .ndo_set_vf_mac = nfp_app_set_vf_mac, + .ndo_set_vf_vlan = nfp_app_set_vf_vlan, + .ndo_set_vf_spoofchk = nfp_app_set_vf_spoofchk, + .ndo_set_vf_trust = nfp_app_set_vf_trust, + .ndo_get_vf_config = nfp_app_get_vf_config, + .ndo_set_vf_link_state = nfp_app_set_vf_link_state, + .ndo_setup_tc = nfp_port_setup_tc, + .ndo_tx_timeout = nfp_net_tx_timeout, + .ndo_set_rx_mode = nfp_net_set_rx_mode, + .ndo_change_mtu = nfp_net_change_mtu, + .ndo_set_mac_address = nfp_net_set_mac_address, + .ndo_set_features = nfp_net_set_features, + .ndo_features_check = nfp_net_features_check, + .ndo_get_phys_port_name = nfp_net_get_phys_port_name, + .ndo_bpf = nfp_net_xdp, + .ndo_xsk_wakeup = nfp_net_xsk_wakeup, + .ndo_get_devlink_port = nfp_devlink_get_devlink_port, +}; + +const struct net_device_ops nfp_nfdk_netdev_ops = { .ndo_init = nfp_app_ndo_init, .ndo_uninit = nfp_app_ndo_uninit, .ndo_open = nfp_net_netdev_open, @@ -3811,10 +1989,10 @@ void nfp_net_info(struct nfp_net *nn) nn->dp.num_tx_rings, nn->max_tx_rings, nn->dp.num_rx_rings, nn->max_rx_rings); nn_info(nn, "VER: %d.%d.%d.%d, Maximum supported MTU: %d\n", - nn->fw_ver.resv, nn->fw_ver.class, + nn->fw_ver.extend, nn->fw_ver.class, nn->fw_ver.major, nn->fw_ver.minor, nn->max_mtu); - nn_info(nn, "CAP: %#x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + nn_info(nn, "CAP: %#x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", nn->cap, nn->cap & NFP_NET_CFG_CTRL_PROMISC ? "PROMISC " : "", nn->cap & NFP_NET_CFG_CTRL_L2BC ? "L2BCFILT " : "", @@ -3832,6 +2010,7 @@ void nfp_net_info(struct nfp_net *nn) nn->cap & NFP_NET_CFG_CTRL_CTAG_FILTER ? "CTAG_FILTER " : "", nn->cap & NFP_NET_CFG_CTRL_MSIXAUTO ? "AUTOMASK " : "", nn->cap & NFP_NET_CFG_CTRL_IRQMOD ? "IRQMOD " : "", + nn->cap & NFP_NET_CFG_CTRL_TXRWB ? "TXRWB " : "", nn->cap & NFP_NET_CFG_CTRL_VXLAN ? "VXLAN " : "", nn->cap & NFP_NET_CFG_CTRL_NVGRE ? "NVGRE " : "", nn->cap & NFP_NET_CFG_CTRL_CSUM_COMPLETE ? @@ -3843,6 +2022,7 @@ void nfp_net_info(struct nfp_net *nn) /** * nfp_net_alloc() - Allocate netdev and related structure * @pdev: PCI device + * @dev_info: NFP ASIC params * @ctrl_bar: PCI IOMEM with vNIC config memory * @needs_netdev: Whether to allocate a netdev for this vNIC * @max_tx_rings: Maximum number of TX rings supported by device @@ -3855,7 +2035,8 @@ void nfp_net_info(struct nfp_net *nn) * Return: NFP Net device structure, or ERR_PTR on error. */ struct nfp_net * -nfp_net_alloc(struct pci_dev *pdev, void __iomem *ctrl_bar, bool needs_netdev, +nfp_net_alloc(struct pci_dev *pdev, const struct nfp_dev_info *dev_info, + void __iomem *ctrl_bar, bool needs_netdev, unsigned int max_tx_rings, unsigned int max_rx_rings) { struct nfp_net *nn; @@ -3880,7 +2061,28 @@ nfp_net_alloc(struct pci_dev *pdev, void __iomem *ctrl_bar, bool needs_netdev, nn->dp.dev = &pdev->dev; nn->dp.ctrl_bar = ctrl_bar; + nn->dev_info = dev_info; nn->pdev = pdev; + nfp_net_get_fw_version(&nn->fw_ver, ctrl_bar); + + switch (FIELD_GET(NFP_NET_CFG_VERSION_DP_MASK, nn->fw_ver.extend)) { + case NFP_NET_CFG_VERSION_DP_NFD3: + nn->dp.ops = &nfp_nfd3_ops; + break; + case NFP_NET_CFG_VERSION_DP_NFDK: + if (nn->fw_ver.major < 5) { + dev_err(&pdev->dev, + "NFDK must use ABI 5 or newer, found: %d\n", + nn->fw_ver.major); + err = -EINVAL; + goto err_free_nn; + } + nn->dp.ops = &nfp_nfdk_ops; + break; + default: + err = -EINVAL; + goto err_free_nn; + } nn->max_tx_rings = max_tx_rings; nn->max_rx_rings = max_rx_rings; @@ -3893,6 +2095,14 @@ nfp_net_alloc(struct pci_dev *pdev, void __iomem *ctrl_bar, bool needs_netdev, nn->dp.num_r_vecs = max(nn->dp.num_tx_rings, nn->dp.num_rx_rings); nn->dp.num_r_vecs = min_t(unsigned int, nn->dp.num_r_vecs, num_online_cpus()); + nn->max_r_vecs = nn->dp.num_r_vecs; + + nn->dp.xsk_pools = kcalloc(nn->max_r_vecs, sizeof(nn->dp.xsk_pools), + GFP_KERNEL); + if (!nn->dp.xsk_pools) { + err = -ENOMEM; + goto err_free_nn; + } nn->dp.txd_cnt = NFP_NET_TX_DESCS_DEFAULT; nn->dp.rxd_cnt = NFP_NET_RX_DESCS_DEFAULT; @@ -3932,6 +2142,7 @@ void nfp_net_free(struct nfp_net *nn) WARN_ON(timer_pending(&nn->reconfig_timer) || nn->reconfig_posted); nfp_ccm_mbox_free(nn); + kfree(nn->dp.xsk_pools); if (nn->dp.netdev) free_netdev(nn->dp.netdev); else @@ -4090,7 +2301,15 @@ static void nfp_net_netdev_init(struct nfp_net *nn) nn->dp.ctrl &= ~NFP_NET_CFG_CTRL_LSO_ANY; /* Finalise the netdev setup */ - netdev->netdev_ops = &nfp_net_netdev_ops; + switch (nn->dp.ops->version) { + case NFP_NFD_VER_NFD3: + netdev->netdev_ops = &nfp_nfd3_netdev_ops; + break; + case NFP_NFD_VER_NFDK: + netdev->netdev_ops = &nfp_nfdk_netdev_ops; + break; + } + netdev->watchdog_timeo = msecs_to_jiffies(5 * 1000); /* MTU range: 68 - hw-specific max */ @@ -4138,6 +2357,9 @@ static int nfp_net_read_caps(struct nfp_net *nn) nn->dp.rx_offset = NFP_NET_RX_OFFSET; } + /* Mask out NFD-version-specific features */ + nn->cap &= nn->dp.ops->cap_mask; + /* For control vNICs mask out the capabilities app doesn't want. */ if (!nn->dp.netdev) nn->cap &= nn->app->type->ctrl_cap_mask; @@ -4190,6 +2412,10 @@ int nfp_net_init(struct nfp_net *nn) nn->dp.ctrl |= NFP_NET_CFG_CTRL_IRQMOD; } + /* Enable TX pointer writeback, if supported */ + if (nn->cap & NFP_NET_CFG_CTRL_TXRWB) + nn->dp.ctrl |= NFP_NET_CFG_CTRL_TXRWB; + /* Stash the re-configuration queue away. First odd queue in TX Bar */ nn->qcp_cfg = nn->tx_bar + NFP_QCP_QUEUE_ADDR_SZ; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index 3d61a8cb60b0..8892a94f00c3 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ /* Copyright (C) 2015-2018 Netronome Systems, Inc. */ -/* - * nfp_net_ctrl.h +/* nfp_net_ctrl.h * Netronome network device driver: Control BAR layout * Authors: Jakub Kicinski * Jason McMullan @@ -15,30 +14,24 @@ #include -/** - * Configuration BAR size. +/* Configuration BAR size. * * The configuration BAR is 8K in size, but due to * THB-350, 32k needs to be reserved. */ #define NFP_NET_CFG_BAR_SZ (32 * 1024) -/** - * Offset in Freelist buffer where packet starts on RX - */ +/* Offset in Freelist buffer where packet starts on RX */ #define NFP_NET_RX_OFFSET 32 -/** - * LSO parameters +/* LSO parameters * %NFP_NET_LSO_MAX_HDR_SZ: Maximum header size supported for LSO frames * %NFP_NET_LSO_MAX_SEGS: Maximum number of segments LSO frame can produce */ #define NFP_NET_LSO_MAX_HDR_SZ 255 #define NFP_NET_LSO_MAX_SEGS 64 -/** - * Prepend field types - */ +/* Prepend field types */ #define NFP_NET_META_FIELD_SIZE 4 #define NFP_NET_META_HASH 1 /* next field carries hash type */ #define NFP_NET_META_MARK 2 @@ -49,9 +42,7 @@ #define NFP_META_PORT_ID_CTRL ~0U -/** - * Hash type pre-pended when a RSS hash was computed - */ +/* Hash type pre-pended when a RSS hash was computed */ #define NFP_NET_RSS_NONE 0 #define NFP_NET_RSS_IPV4 1 #define NFP_NET_RSS_IPV6 2 @@ -63,16 +54,14 @@ #define NFP_NET_RSS_IPV6_UDP 8 #define NFP_NET_RSS_IPV6_EX_UDP 9 -/** - * Ring counts +/* Ring counts * %NFP_NET_TXR_MAX: Maximum number of TX rings * %NFP_NET_RXR_MAX: Maximum number of RX rings */ #define NFP_NET_TXR_MAX 64 #define NFP_NET_RXR_MAX 64 -/** - * Read/Write config words (0x0000 - 0x002c) +/* Read/Write config words (0x0000 - 0x002c) * %NFP_NET_CFG_CTRL: Global control * %NFP_NET_CFG_UPDATE: Indicate which fields are updated * %NFP_NET_CFG_TXRS_ENABLE: Bitmask of enabled TX rings @@ -103,7 +92,6 @@ #define NFP_NET_CFG_CTRL_RINGCFG (0x1 << 16) /* Ring runtime changes */ #define NFP_NET_CFG_CTRL_RSS (0x1 << 17) /* RSS (version 1) */ #define NFP_NET_CFG_CTRL_IRQMOD (0x1 << 18) /* Interrupt moderation */ -#define NFP_NET_CFG_CTRL_RINGPRIO (0x1 << 19) /* Ring priorities */ #define NFP_NET_CFG_CTRL_MSIXAUTO (0x1 << 20) /* MSI-X auto-masking */ #define NFP_NET_CFG_CTRL_TXRWB (0x1 << 21) /* Write-back of TX ring*/ #define NFP_NET_CFG_CTRL_VXLAN (0x1 << 24) /* VXLAN tunnel support */ @@ -147,8 +135,7 @@ #define NFP_NET_CFG_LSC 0x0020 #define NFP_NET_CFG_MACADDR 0x0024 -/** - * Read-only words (0x0030 - 0x0050): +/* Read-only words (0x0030 - 0x0050): * %NFP_NET_CFG_VERSION: Firmware version number * %NFP_NET_CFG_STS: Status * %NFP_NET_CFG_CAP: Capabilities (same bits as %NFP_NET_CFG_CTRL) @@ -162,7 +149,10 @@ * - define more STS bits */ #define NFP_NET_CFG_VERSION 0x0030 -#define NFP_NET_CFG_VERSION_RESERVED_MASK (0xff << 24) +#define NFP_NET_CFG_VERSION_RESERVED_MASK (0xfe << 24) +#define NFP_NET_CFG_VERSION_DP_NFD3 0 +#define NFP_NET_CFG_VERSION_DP_NFDK 1 +#define NFP_NET_CFG_VERSION_DP_MASK 1 #define NFP_NET_CFG_VERSION_CLASS_MASK (0xff << 16) #define NFP_NET_CFG_VERSION_CLASS(x) (((x) & 0xff) << 16) #define NFP_NET_CFG_VERSION_CLASS_GENERIC 0 @@ -193,36 +183,31 @@ #define NFP_NET_CFG_START_TXQ 0x0048 #define NFP_NET_CFG_START_RXQ 0x004c -/** - * Prepend configuration +/* Prepend configuration */ #define NFP_NET_CFG_RX_OFFSET 0x0050 #define NFP_NET_CFG_RX_OFFSET_DYNAMIC 0 /* Prepend mode */ -/** - * RSS capabilities +/* RSS capabilities * %NFP_NET_CFG_RSS_CAP_HFUNC: supported hash functions (same bits as * %NFP_NET_CFG_RSS_HFUNC) */ #define NFP_NET_CFG_RSS_CAP 0x0054 #define NFP_NET_CFG_RSS_CAP_HFUNC 0xff000000 -/** - * TLV area start +/* TLV area start * %NFP_NET_CFG_TLV_BASE: start anchor of the TLV area */ #define NFP_NET_CFG_TLV_BASE 0x0058 -/** - * VXLAN/UDP encap configuration +/* VXLAN/UDP encap configuration * %NFP_NET_CFG_VXLAN_PORT: Base address of table of tunnels' UDP dst ports * %NFP_NET_CFG_VXLAN_SZ: Size of the UDP port table in bytes */ #define NFP_NET_CFG_VXLAN_PORT 0x0060 #define NFP_NET_CFG_VXLAN_SZ 0x0008 -/** - * BPF section +/* BPF section * %NFP_NET_CFG_BPF_ABI: BPF ABI version * %NFP_NET_CFG_BPF_CAP: BPF capabilities * %NFP_NET_CFG_BPF_MAX_LEN: Maximum size of JITed BPF code in bytes @@ -247,14 +232,12 @@ #define NFP_NET_CFG_BPF_CFG_MASK 7ULL #define NFP_NET_CFG_BPF_ADDR_MASK (~NFP_NET_CFG_BPF_CFG_MASK) -/** - * 40B reserved for future use (0x0098 - 0x00c0) +/* 40B reserved for future use (0x0098 - 0x00c0) */ #define NFP_NET_CFG_RESERVED 0x0098 #define NFP_NET_CFG_RESERVED_SZ 0x0028 -/** - * RSS configuration (0x0100 - 0x01ac): +/* RSS configuration (0x0100 - 0x01ac): * Used only when NFP_NET_CFG_CTRL_RSS is enabled * %NFP_NET_CFG_RSS_CFG: RSS configuration word * %NFP_NET_CFG_RSS_KEY: RSS "secret" key @@ -281,8 +264,7 @@ NFP_NET_CFG_RSS_KEY_SZ) #define NFP_NET_CFG_RSS_ITBL_SZ 0x80 -/** - * TX ring configuration (0x200 - 0x800) +/* TX ring configuration (0x200 - 0x800) * %NFP_NET_CFG_TXR_BASE: Base offset for TX ring configuration * %NFP_NET_CFG_TXR_ADDR: Per TX ring DMA address (8B entries) * %NFP_NET_CFG_TXR_WB_ADDR: Per TX ring write back DMA address (8B entries) @@ -301,8 +283,7 @@ #define NFP_NET_CFG_TXR_IRQ_MOD(_x) (NFP_NET_CFG_TXR_BASE + 0x500 + \ ((_x) * 0x4)) -/** - * RX ring configuration (0x0800 - 0x0c00) +/* RX ring configuration (0x0800 - 0x0c00) * %NFP_NET_CFG_RXR_BASE: Base offset for RX ring configuration * %NFP_NET_CFG_RXR_ADDR: Per RX ring DMA address (8B entries) * %NFP_NET_CFG_RXR_SZ: Per RX ring ring size (1B entries) @@ -318,8 +299,7 @@ #define NFP_NET_CFG_RXR_IRQ_MOD(_x) (NFP_NET_CFG_RXR_BASE + 0x300 + \ ((_x) * 0x4)) -/** - * Interrupt Control/Cause registers (0x0c00 - 0x0d00) +/* Interrupt Control/Cause registers (0x0c00 - 0x0d00) * These registers are only used when MSI-X auto-masking is not * enabled (%NFP_NET_CFG_CTRL_MSIXAUTO not set). The array is index * by MSI-X entry and are 1B in size. If an entry is zero, the @@ -334,8 +314,7 @@ #define NFP_NET_CFG_ICR_RXTX 0x1 #define NFP_NET_CFG_ICR_LSC 0x2 -/** - * General device stats (0x0d00 - 0x0d90) +/* General device stats (0x0d00 - 0x0d90) * all counters are 64bit. */ #define NFP_NET_CFG_STATS_BASE 0x0d00 @@ -368,8 +347,7 @@ #define NFP_NET_CFG_STATS_APP3_FRAMES (NFP_NET_CFG_STATS_BASE + 0xc0) #define NFP_NET_CFG_STATS_APP3_BYTES (NFP_NET_CFG_STATS_BASE + 0xc8) -/** - * Per ring stats (0x1000 - 0x1800) +/* Per ring stats (0x1000 - 0x1800) * options, 64bit per entry * %NFP_NET_CFG_TXR_STATS: TX ring statistics (Packet and Byte count) * %NFP_NET_CFG_RXR_STATS: RX ring statistics (Packet and Byte count) @@ -381,8 +359,7 @@ #define NFP_NET_CFG_RXR_STATS(_x) (NFP_NET_CFG_RXR_STATS_BASE + \ ((_x) * 0x10)) -/** - * General use mailbox area (0x1800 - 0x19ff) +/* General use mailbox area (0x1800 - 0x19ff) * 4B used for update command and 4B return code * followed by a max of 504B of variable length value */ @@ -399,8 +376,7 @@ #define NFP_NET_CFG_MBOX_CMD_PCI_DSCP_PRIOMAP_SET 5 #define NFP_NET_CFG_MBOX_CMD_TLV_CMSG 6 -/** - * VLAN filtering using general use mailbox +/* VLAN filtering using general use mailbox * %NFP_NET_CFG_VLAN_FILTER: Base address of VLAN filter mailbox * %NFP_NET_CFG_VLAN_FILTER_VID: VLAN ID to filter * %NFP_NET_CFG_VLAN_FILTER_PROTO: VLAN proto to filter @@ -411,8 +387,7 @@ #define NFP_NET_CFG_VLAN_FILTER_PROTO (NFP_NET_CFG_VLAN_FILTER + 2) #define NFP_NET_CFG_VLAN_FILTER_SZ 0x0004 -/** - * TLV capabilities +/* TLV capabilities * %NFP_NET_CFG_TLV_TYPE: Offset of type within the TLV * %NFP_NET_CFG_TLV_TYPE_REQUIRED: Driver must be able to parse the TLV * %NFP_NET_CFG_TLV_LENGTH: Offset of length within the TLV @@ -438,8 +413,7 @@ #define NFP_NET_CFG_TLV_HEADER_TYPE 0x7fff0000 #define NFP_NET_CFG_TLV_HEADER_LENGTH 0x0000ffff -/** - * Capability TLV types +/* Capability TLV types * * %NFP_NET_CFG_TLV_TYPE_UNKNOWN: * Special TLV type to catch bugs, should never be encountered. Drivers should @@ -512,8 +486,7 @@ struct device; -/** - * struct nfp_net_tlv_caps - parsed control BAR TLV capabilities +/* struct nfp_net_tlv_caps - parsed control BAR TLV capabilities * @me_freq_mhz: ME clock_freq (MHz) * @mbox_off: vNIC mailbox area offset * @mbox_len: vNIC mailbox area length diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c index 553c708694e8..d8b735ccf899 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c @@ -1,10 +1,11 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -/* Copyright (C) 2015-2018 Netronome Systems, Inc. */ +/* Copyright (C) 2015-2019 Netronome Systems, Inc. */ #include #include #include #include "nfp_net.h" +#include "nfp_net_dp.h" static struct dentry *nfp_dir; @@ -42,13 +43,19 @@ static int nfp_rx_q_show(struct seq_file *file, void *data) seq_printf(file, "%04d: 0x%08x 0x%08x", i, rxd->vals[0], rxd->vals[1]); - frag = READ_ONCE(rx_ring->rxbufs[i].frag); - if (frag) - seq_printf(file, " frag=%p", frag); + if (!r_vec->xsk_pool) { + frag = READ_ONCE(rx_ring->rxbufs[i].frag); + if (frag) + seq_printf(file, " frag=%p", frag); - if (rx_ring->rxbufs[i].dma_addr) - seq_printf(file, " dma_addr=%pad", - &rx_ring->rxbufs[i].dma_addr); + if (rx_ring->rxbufs[i].dma_addr) + seq_printf(file, " dma_addr=%pad", + &rx_ring->rxbufs[i].dma_addr); + } else { + if (rx_ring->xsk_rxbufs[i].dma_addr) + seq_printf(file, " dma_addr=%pad", + &rx_ring->xsk_rxbufs[i].dma_addr); + } if (i == rx_ring->rd_p % rxd_cnt) seq_puts(file, " H_RD "); @@ -74,10 +81,8 @@ static int nfp_tx_q_show(struct seq_file *file, void *data) { struct nfp_net_r_vector *r_vec = file->private; struct nfp_net_tx_ring *tx_ring; - struct nfp_net_tx_desc *txd; - int d_rd_p, d_wr_p, txd_cnt; struct nfp_net *nn; - int i; + int d_rd_p, d_wr_p; rtnl_lock(); @@ -91,49 +96,20 @@ static int nfp_tx_q_show(struct seq_file *file, void *data) if (!nfp_net_running(nn)) goto out; - txd_cnt = tx_ring->cnt; - d_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q); d_wr_p = nfp_qcp_wr_ptr_read(tx_ring->qcp_q); - seq_printf(file, "TX[%02d,%02d%s]: cnt=%u dma=%pad host=%p H_RD=%u H_WR=%u D_RD=%u D_WR=%u\n", + seq_printf(file, "TX[%02d,%02d%s]: cnt=%u dma=%pad host=%p H_RD=%u H_WR=%u D_RD=%u D_WR=%u", tx_ring->idx, tx_ring->qcidx, tx_ring == r_vec->tx_ring ? "" : "xdp", tx_ring->cnt, &tx_ring->dma, tx_ring->txds, tx_ring->rd_p, tx_ring->wr_p, d_rd_p, d_wr_p); + if (tx_ring->txrwb) + seq_printf(file, " TXRWB=%llu", *tx_ring->txrwb); + seq_putc(file, '\n'); - for (i = 0; i < txd_cnt; i++) { - txd = &tx_ring->txds[i]; - seq_printf(file, "%04d: 0x%08x 0x%08x 0x%08x 0x%08x", i, - txd->vals[0], txd->vals[1], - txd->vals[2], txd->vals[3]); - - if (tx_ring == r_vec->tx_ring) { - struct sk_buff *skb = READ_ONCE(tx_ring->txbufs[i].skb); - - if (skb) - seq_printf(file, " skb->head=%p skb->data=%p", - skb->head, skb->data); - } else { - seq_printf(file, " frag=%p", - READ_ONCE(tx_ring->txbufs[i].frag)); - } - - if (tx_ring->txbufs[i].dma_addr) - seq_printf(file, " dma_addr=%pad", - &tx_ring->txbufs[i].dma_addr); - - if (i == tx_ring->rd_p % txd_cnt) - seq_puts(file, " H_RD"); - if (i == tx_ring->wr_p % txd_cnt) - seq_puts(file, " H_WR"); - if (i == d_rd_p % txd_cnt) - seq_puts(file, " D_RD"); - if (i == d_wr_p % txd_cnt) - seq_puts(file, " D_WR"); - - seq_putc(file, '\n'); - } + nfp_net_debugfs_print_tx_descs(file, &nn->dp, r_vec, tx_ring, + d_rd_p, d_wr_p); out: rtnl_unlock(); return 0; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_dp.c b/drivers/net/ethernet/netronome/nfp/nfp_net_dp.c new file mode 100644 index 000000000000..34dd94811df3 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_dp.c @@ -0,0 +1,442 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (C) 2015-2019 Netronome Systems, Inc. */ + +#include "nfp_app.h" +#include "nfp_net_dp.h" +#include "nfp_net_xsk.h" + +/** + * nfp_net_rx_alloc_one() - Allocate and map page frag for RX + * @dp: NFP Net data path struct + * @dma_addr: Pointer to storage for DMA address (output param) + * + * This function will allcate a new page frag, map it for DMA. + * + * Return: allocated page frag or NULL on failure. + */ +void *nfp_net_rx_alloc_one(struct nfp_net_dp *dp, dma_addr_t *dma_addr) +{ + void *frag; + + if (!dp->xdp_prog) { + frag = netdev_alloc_frag(dp->fl_bufsz); + } else { + struct page *page; + + page = alloc_page(GFP_KERNEL); + frag = page ? page_address(page) : NULL; + } + if (!frag) { + nn_dp_warn(dp, "Failed to alloc receive page frag\n"); + return NULL; + } + + *dma_addr = nfp_net_dma_map_rx(dp, frag); + if (dma_mapping_error(dp->dev, *dma_addr)) { + nfp_net_free_frag(frag, dp->xdp_prog); + nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); + return NULL; + } + + return frag; +} + +/** + * nfp_net_tx_ring_init() - Fill in the boilerplate for a TX ring + * @tx_ring: TX ring structure + * @dp: NFP Net data path struct + * @r_vec: IRQ vector servicing this ring + * @idx: Ring index + * @is_xdp: Is this an XDP TX ring? + */ +static void +nfp_net_tx_ring_init(struct nfp_net_tx_ring *tx_ring, struct nfp_net_dp *dp, + struct nfp_net_r_vector *r_vec, unsigned int idx, + bool is_xdp) +{ + struct nfp_net *nn = r_vec->nfp_net; + + tx_ring->idx = idx; + tx_ring->r_vec = r_vec; + tx_ring->is_xdp = is_xdp; + u64_stats_init(&tx_ring->r_vec->tx_sync); + + tx_ring->qcidx = tx_ring->idx * nn->stride_tx; + tx_ring->txrwb = dp->txrwb ? &dp->txrwb[idx] : NULL; + tx_ring->qcp_q = nn->tx_bar + NFP_QCP_QUEUE_OFF(tx_ring->qcidx); +} + +/** + * nfp_net_rx_ring_init() - Fill in the boilerplate for a RX ring + * @rx_ring: RX ring structure + * @r_vec: IRQ vector servicing this ring + * @idx: Ring index + */ +static void +nfp_net_rx_ring_init(struct nfp_net_rx_ring *rx_ring, + struct nfp_net_r_vector *r_vec, unsigned int idx) +{ + struct nfp_net *nn = r_vec->nfp_net; + + rx_ring->idx = idx; + rx_ring->r_vec = r_vec; + u64_stats_init(&rx_ring->r_vec->rx_sync); + + rx_ring->fl_qcidx = rx_ring->idx * nn->stride_rx; + rx_ring->qcp_fl = nn->rx_bar + NFP_QCP_QUEUE_OFF(rx_ring->fl_qcidx); +} + +/** + * nfp_net_rx_ring_reset() - Reflect in SW state of freelist after disable + * @rx_ring: RX ring structure + * + * Assumes that the device is stopped, must be idempotent. + */ +void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring) +{ + unsigned int wr_idx, last_idx; + + /* wr_p == rd_p means ring was never fed FL bufs. RX rings are always + * kept at cnt - 1 FL bufs. + */ + if (rx_ring->wr_p == 0 && rx_ring->rd_p == 0) + return; + + /* Move the empty entry to the end of the list */ + wr_idx = D_IDX(rx_ring, rx_ring->wr_p); + last_idx = rx_ring->cnt - 1; + if (rx_ring->r_vec->xsk_pool) { + rx_ring->xsk_rxbufs[wr_idx] = rx_ring->xsk_rxbufs[last_idx]; + memset(&rx_ring->xsk_rxbufs[last_idx], 0, + sizeof(*rx_ring->xsk_rxbufs)); + } else { + rx_ring->rxbufs[wr_idx] = rx_ring->rxbufs[last_idx]; + memset(&rx_ring->rxbufs[last_idx], 0, sizeof(*rx_ring->rxbufs)); + } + + memset(rx_ring->rxds, 0, rx_ring->size); + rx_ring->wr_p = 0; + rx_ring->rd_p = 0; +} + +/** + * nfp_net_rx_ring_bufs_free() - Free any buffers currently on the RX ring + * @dp: NFP Net data path struct + * @rx_ring: RX ring to remove buffers from + * + * Assumes that the device is stopped and buffers are in [0, ring->cnt - 1) + * entries. After device is disabled nfp_net_rx_ring_reset() must be called + * to restore required ring geometry. + */ +static void +nfp_net_rx_ring_bufs_free(struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring) +{ + unsigned int i; + + if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx)) + return; + + for (i = 0; i < rx_ring->cnt - 1; i++) { + /* NULL skb can only happen when initial filling of the ring + * fails to allocate enough buffers and calls here to free + * already allocated ones. + */ + if (!rx_ring->rxbufs[i].frag) + continue; + + nfp_net_dma_unmap_rx(dp, rx_ring->rxbufs[i].dma_addr); + nfp_net_free_frag(rx_ring->rxbufs[i].frag, dp->xdp_prog); + rx_ring->rxbufs[i].dma_addr = 0; + rx_ring->rxbufs[i].frag = NULL; + } +} + +/** + * nfp_net_rx_ring_bufs_alloc() - Fill RX ring with buffers (don't give to FW) + * @dp: NFP Net data path struct + * @rx_ring: RX ring to remove buffers from + */ +static int +nfp_net_rx_ring_bufs_alloc(struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring) +{ + struct nfp_net_rx_buf *rxbufs; + unsigned int i; + + if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx)) + return 0; + + rxbufs = rx_ring->rxbufs; + + for (i = 0; i < rx_ring->cnt - 1; i++) { + rxbufs[i].frag = nfp_net_rx_alloc_one(dp, &rxbufs[i].dma_addr); + if (!rxbufs[i].frag) { + nfp_net_rx_ring_bufs_free(dp, rx_ring); + return -ENOMEM; + } + } + + return 0; +} + +int nfp_net_tx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp) +{ + unsigned int r; + + dp->tx_rings = kcalloc(dp->num_tx_rings, sizeof(*dp->tx_rings), + GFP_KERNEL); + if (!dp->tx_rings) + return -ENOMEM; + + if (dp->ctrl & NFP_NET_CFG_CTRL_TXRWB) { + dp->txrwb = dma_alloc_coherent(dp->dev, + dp->num_tx_rings * sizeof(u64), + &dp->txrwb_dma, GFP_KERNEL); + if (!dp->txrwb) + goto err_free_rings; + } + + for (r = 0; r < dp->num_tx_rings; r++) { + int bias = 0; + + if (r >= dp->num_stack_tx_rings) + bias = dp->num_stack_tx_rings; + + nfp_net_tx_ring_init(&dp->tx_rings[r], dp, + &nn->r_vecs[r - bias], r, bias); + + if (nfp_net_tx_ring_alloc(dp, &dp->tx_rings[r])) + goto err_free_prev; + + if (nfp_net_tx_ring_bufs_alloc(dp, &dp->tx_rings[r])) + goto err_free_ring; + } + + return 0; + +err_free_prev: + while (r--) { + nfp_net_tx_ring_bufs_free(dp, &dp->tx_rings[r]); +err_free_ring: + nfp_net_tx_ring_free(dp, &dp->tx_rings[r]); + } + if (dp->txrwb) + dma_free_coherent(dp->dev, dp->num_tx_rings * sizeof(u64), + dp->txrwb, dp->txrwb_dma); +err_free_rings: + kfree(dp->tx_rings); + return -ENOMEM; +} + +void nfp_net_tx_rings_free(struct nfp_net_dp *dp) +{ + unsigned int r; + + for (r = 0; r < dp->num_tx_rings; r++) { + nfp_net_tx_ring_bufs_free(dp, &dp->tx_rings[r]); + nfp_net_tx_ring_free(dp, &dp->tx_rings[r]); + } + + if (dp->txrwb) + dma_free_coherent(dp->dev, dp->num_tx_rings * sizeof(u64), + dp->txrwb, dp->txrwb_dma); + kfree(dp->tx_rings); +} + +/** + * nfp_net_rx_ring_free() - Free resources allocated to a RX ring + * @rx_ring: RX ring to free + */ +static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring) +{ + struct nfp_net_r_vector *r_vec = rx_ring->r_vec; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; + + if (dp->netdev) + xdp_rxq_info_unreg(&rx_ring->xdp_rxq); + + if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx)) + kvfree(rx_ring->xsk_rxbufs); + else + kvfree(rx_ring->rxbufs); + + if (rx_ring->rxds) + dma_free_coherent(dp->dev, rx_ring->size, + rx_ring->rxds, rx_ring->dma); + + rx_ring->cnt = 0; + rx_ring->rxbufs = NULL; + rx_ring->xsk_rxbufs = NULL; + rx_ring->rxds = NULL; + rx_ring->dma = 0; + rx_ring->size = 0; +} + +/** + * nfp_net_rx_ring_alloc() - Allocate resource for a RX ring + * @dp: NFP Net data path struct + * @rx_ring: RX ring to allocate + * + * Return: 0 on success, negative errno otherwise. + */ +static int +nfp_net_rx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring) +{ + enum xdp_mem_type mem_type; + size_t rxbuf_sw_desc_sz; + int err; + + if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx)) { + mem_type = MEM_TYPE_XSK_BUFF_POOL; + rxbuf_sw_desc_sz = sizeof(*rx_ring->xsk_rxbufs); + } else { + mem_type = MEM_TYPE_PAGE_ORDER0; + rxbuf_sw_desc_sz = sizeof(*rx_ring->rxbufs); + } + + if (dp->netdev) { + err = xdp_rxq_info_reg(&rx_ring->xdp_rxq, dp->netdev, + rx_ring->idx, rx_ring->r_vec->napi.napi_id); + if (err < 0) + return err; + + err = xdp_rxq_info_reg_mem_model(&rx_ring->xdp_rxq, mem_type, NULL); + if (err) + goto err_alloc; + } + + rx_ring->cnt = dp->rxd_cnt; + rx_ring->size = array_size(rx_ring->cnt, sizeof(*rx_ring->rxds)); + rx_ring->rxds = dma_alloc_coherent(dp->dev, rx_ring->size, + &rx_ring->dma, + GFP_KERNEL | __GFP_NOWARN); + if (!rx_ring->rxds) { + netdev_warn(dp->netdev, "failed to allocate RX descriptor ring memory, requested descriptor count: %d, consider lowering descriptor count\n", + rx_ring->cnt); + goto err_alloc; + } + + if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx)) { + rx_ring->xsk_rxbufs = kvcalloc(rx_ring->cnt, rxbuf_sw_desc_sz, + GFP_KERNEL); + if (!rx_ring->xsk_rxbufs) + goto err_alloc; + } else { + rx_ring->rxbufs = kvcalloc(rx_ring->cnt, rxbuf_sw_desc_sz, + GFP_KERNEL); + if (!rx_ring->rxbufs) + goto err_alloc; + } + + return 0; + +err_alloc: + nfp_net_rx_ring_free(rx_ring); + return -ENOMEM; +} + +int nfp_net_rx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp) +{ + unsigned int r; + + dp->rx_rings = kcalloc(dp->num_rx_rings, sizeof(*dp->rx_rings), + GFP_KERNEL); + if (!dp->rx_rings) + return -ENOMEM; + + for (r = 0; r < dp->num_rx_rings; r++) { + nfp_net_rx_ring_init(&dp->rx_rings[r], &nn->r_vecs[r], r); + + if (nfp_net_rx_ring_alloc(dp, &dp->rx_rings[r])) + goto err_free_prev; + + if (nfp_net_rx_ring_bufs_alloc(dp, &dp->rx_rings[r])) + goto err_free_ring; + } + + return 0; + +err_free_prev: + while (r--) { + nfp_net_rx_ring_bufs_free(dp, &dp->rx_rings[r]); +err_free_ring: + nfp_net_rx_ring_free(&dp->rx_rings[r]); + } + kfree(dp->rx_rings); + return -ENOMEM; +} + +void nfp_net_rx_rings_free(struct nfp_net_dp *dp) +{ + unsigned int r; + + for (r = 0; r < dp->num_rx_rings; r++) { + nfp_net_rx_ring_bufs_free(dp, &dp->rx_rings[r]); + nfp_net_rx_ring_free(&dp->rx_rings[r]); + } + + kfree(dp->rx_rings); +} + +void +nfp_net_rx_ring_hw_cfg_write(struct nfp_net *nn, + struct nfp_net_rx_ring *rx_ring, unsigned int idx) +{ + /* Write the DMA address, size and MSI-X info to the device */ + nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(idx), rx_ring->dma); + nn_writeb(nn, NFP_NET_CFG_RXR_SZ(idx), ilog2(rx_ring->cnt)); + nn_writeb(nn, NFP_NET_CFG_RXR_VEC(idx), rx_ring->r_vec->irq_entry); +} + +void +nfp_net_tx_ring_hw_cfg_write(struct nfp_net *nn, + struct nfp_net_tx_ring *tx_ring, unsigned int idx) +{ + nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(idx), tx_ring->dma); + if (tx_ring->txrwb) { + *tx_ring->txrwb = 0; + nn_writeq(nn, NFP_NET_CFG_TXR_WB_ADDR(idx), + nn->dp.txrwb_dma + idx * sizeof(u64)); + } + nn_writeb(nn, NFP_NET_CFG_TXR_SZ(idx), ilog2(tx_ring->cnt)); + nn_writeb(nn, NFP_NET_CFG_TXR_VEC(idx), tx_ring->r_vec->irq_entry); +} + +void nfp_net_vec_clear_ring_data(struct nfp_net *nn, unsigned int idx) +{ + nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(idx), 0); + nn_writeb(nn, NFP_NET_CFG_RXR_SZ(idx), 0); + nn_writeb(nn, NFP_NET_CFG_RXR_VEC(idx), 0); + + nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(idx), 0); + nn_writeq(nn, NFP_NET_CFG_TXR_WB_ADDR(idx), 0); + nn_writeb(nn, NFP_NET_CFG_TXR_SZ(idx), 0); + nn_writeb(nn, NFP_NET_CFG_TXR_VEC(idx), 0); +} + +netdev_tx_t nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) +{ + struct nfp_net *nn = netdev_priv(netdev); + + return nn->dp.ops->xmit(skb, netdev); +} + +bool __nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb) +{ + struct nfp_net_r_vector *r_vec = &nn->r_vecs[0]; + + return nn->dp.ops->ctrl_tx_one(nn, r_vec, skb, false); +} + +bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb) +{ + struct nfp_net_r_vector *r_vec = &nn->r_vecs[0]; + bool ret; + + spin_lock_bh(&r_vec->lock); + ret = nn->dp.ops->ctrl_tx_one(nn, r_vec, skb, false); + spin_unlock_bh(&r_vec->lock); + + return ret; +} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_dp.h b/drivers/net/ethernet/netronome/nfp/nfp_net_dp.h new file mode 100644 index 000000000000..c934cc2d3208 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_dp.h @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* Copyright (C) 2019 Netronome Systems, Inc. */ + +#ifndef _NFP_NET_DP_ +#define _NFP_NET_DP_ + +#include "nfp_net.h" + +static inline dma_addr_t nfp_net_dma_map_rx(struct nfp_net_dp *dp, void *frag) +{ + return dma_map_single_attrs(dp->dev, frag + NFP_NET_RX_BUF_HEADROOM, + dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, + dp->rx_dma_dir, DMA_ATTR_SKIP_CPU_SYNC); +} + +static inline void +nfp_net_dma_sync_dev_rx(const struct nfp_net_dp *dp, dma_addr_t dma_addr) +{ + dma_sync_single_for_device(dp->dev, dma_addr, + dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, + dp->rx_dma_dir); +} + +static inline void nfp_net_dma_unmap_rx(struct nfp_net_dp *dp, + dma_addr_t dma_addr) +{ + dma_unmap_single_attrs(dp->dev, dma_addr, + dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, + dp->rx_dma_dir, DMA_ATTR_SKIP_CPU_SYNC); +} + +static inline void nfp_net_dma_sync_cpu_rx(struct nfp_net_dp *dp, + dma_addr_t dma_addr, + unsigned int len) +{ + dma_sync_single_for_cpu(dp->dev, dma_addr - NFP_NET_RX_BUF_HEADROOM, + len, dp->rx_dma_dir); +} + +/** + * nfp_net_tx_full() - check if the TX ring is full + * @tx_ring: TX ring to check + * @dcnt: Number of descriptors that need to be enqueued (must be >= 1) + * + * This function checks, based on the *host copy* of read/write + * pointer if a given TX ring is full. The real TX queue may have + * some newly made available slots. + * + * Return: True if the ring is full. + */ +static inline int nfp_net_tx_full(struct nfp_net_tx_ring *tx_ring, int dcnt) +{ + return (tx_ring->wr_p - tx_ring->rd_p) >= (tx_ring->cnt - dcnt); +} + +static inline void nfp_net_tx_xmit_more_flush(struct nfp_net_tx_ring *tx_ring) +{ + wmb(); /* drain writebuffer */ + nfp_qcp_wr_ptr_add(tx_ring->qcp_q, tx_ring->wr_ptr_add); + tx_ring->wr_ptr_add = 0; +} + +static inline u32 +nfp_net_read_tx_cmpl(struct nfp_net_tx_ring *tx_ring, struct nfp_net_dp *dp) +{ + if (tx_ring->txrwb) + return *tx_ring->txrwb; + return nfp_qcp_rd_ptr_read(tx_ring->qcp_q); +} + +static inline void nfp_net_free_frag(void *frag, bool xdp) +{ + if (!xdp) + skb_free_frag(frag); + else + __free_page(virt_to_page(frag)); +} + +/** + * nfp_net_irq_unmask() - Unmask automasked interrupt + * @nn: NFP Network structure + * @entry_nr: MSI-X table entry + * + * Clear the ICR for the IRQ entry. + */ +static inline void nfp_net_irq_unmask(struct nfp_net *nn, unsigned int entry_nr) +{ + nn_writeb(nn, NFP_NET_CFG_ICR(entry_nr), NFP_NET_CFG_ICR_UNMASKED); + nn_pci_flush(nn); +} + +struct seq_file; + +/* Common */ +void +nfp_net_rx_ring_hw_cfg_write(struct nfp_net *nn, + struct nfp_net_rx_ring *rx_ring, unsigned int idx); +void +nfp_net_tx_ring_hw_cfg_write(struct nfp_net *nn, + struct nfp_net_tx_ring *tx_ring, unsigned int idx); +void nfp_net_vec_clear_ring_data(struct nfp_net *nn, unsigned int idx); + +void *nfp_net_rx_alloc_one(struct nfp_net_dp *dp, dma_addr_t *dma_addr); +int nfp_net_rx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp); +int nfp_net_tx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp); +void nfp_net_rx_rings_free(struct nfp_net_dp *dp); +void nfp_net_tx_rings_free(struct nfp_net_dp *dp); +void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring); + +enum nfp_nfd_version { + NFP_NFD_VER_NFD3, + NFP_NFD_VER_NFDK, +}; + +/** + * struct nfp_dp_ops - Hooks to wrap different implementation of different dp + * @version: Indicate dp type + * @tx_min_desc_per_pkt: Minimal TX descs needed for each packet + * @cap_mask: Mask of supported features + * @poll: Napi poll for normal rx/tx + * @xsk_poll: Napi poll when xsk is enabled + * @ctrl_poll: Tasklet poll for ctrl rx/tx + * @xmit: Xmit for normal path + * @ctrl_tx_one: Xmit for ctrl path + * @rx_ring_fill_freelist: Give buffers from the ring to FW + * @tx_ring_alloc: Allocate resource for a TX ring + * @tx_ring_reset: Free any untransmitted buffers and reset pointers + * @tx_ring_free: Free resources allocated to a TX ring + * @tx_ring_bufs_alloc: Allocate resource for each TX buffer + * @tx_ring_bufs_free: Free resources allocated to each TX buffer + * @print_tx_descs: Show TX ring's info for debug purpose + */ +struct nfp_dp_ops { + enum nfp_nfd_version version; + unsigned int tx_min_desc_per_pkt; + u32 cap_mask; + + int (*poll)(struct napi_struct *napi, int budget); + int (*xsk_poll)(struct napi_struct *napi, int budget); + void (*ctrl_poll)(struct tasklet_struct *t); + netdev_tx_t (*xmit)(struct sk_buff *skb, struct net_device *netdev); + bool (*ctrl_tx_one)(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, + struct sk_buff *skb, bool old); + void (*rx_ring_fill_freelist)(struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring); + int (*tx_ring_alloc)(struct nfp_net_dp *dp, + struct nfp_net_tx_ring *tx_ring); + void (*tx_ring_reset)(struct nfp_net_dp *dp, + struct nfp_net_tx_ring *tx_ring); + void (*tx_ring_free)(struct nfp_net_tx_ring *tx_ring); + int (*tx_ring_bufs_alloc)(struct nfp_net_dp *dp, + struct nfp_net_tx_ring *tx_ring); + void (*tx_ring_bufs_free)(struct nfp_net_dp *dp, + struct nfp_net_tx_ring *tx_ring); + + void (*print_tx_descs)(struct seq_file *file, + struct nfp_net_r_vector *r_vec, + struct nfp_net_tx_ring *tx_ring, + u32 d_rd_p, u32 d_wr_p); +}; + +static inline void +nfp_net_tx_ring_reset(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) +{ + return dp->ops->tx_ring_reset(dp, tx_ring); +} + +static inline void +nfp_net_rx_ring_fill_freelist(struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring) +{ + dp->ops->rx_ring_fill_freelist(dp, rx_ring); +} + +static inline int +nfp_net_tx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) +{ + return dp->ops->tx_ring_alloc(dp, tx_ring); +} + +static inline void +nfp_net_tx_ring_free(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) +{ + dp->ops->tx_ring_free(tx_ring); +} + +static inline int +nfp_net_tx_ring_bufs_alloc(struct nfp_net_dp *dp, + struct nfp_net_tx_ring *tx_ring) +{ + return dp->ops->tx_ring_bufs_alloc(dp, tx_ring); +} + +static inline void +nfp_net_tx_ring_bufs_free(struct nfp_net_dp *dp, + struct nfp_net_tx_ring *tx_ring) +{ + dp->ops->tx_ring_bufs_free(dp, tx_ring); +} + +static inline void +nfp_net_debugfs_print_tx_descs(struct seq_file *file, struct nfp_net_dp *dp, + struct nfp_net_r_vector *r_vec, + struct nfp_net_tx_ring *tx_ring, + u32 d_rd_p, u32 d_wr_p) +{ + dp->ops->print_tx_descs(file, r_vec, tx_ring, d_rd_p, d_wr_p); +} + +extern const struct nfp_dp_ops nfp_nfd3_ops; +extern const struct nfp_dp_ops nfp_nfdk_ops; + +netdev_tx_t nfp_net_tx(struct sk_buff *skb, struct net_device *netdev); + +#endif /* _NFP_NET_DP_ */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index e0c27471bcdb..61c8b450aafb 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -21,10 +21,12 @@ #include #include "nfpcore/nfp.h" +#include "nfpcore/nfp_dev.h" #include "nfpcore/nfp_nsp.h" #include "nfp_app.h" #include "nfp_main.h" #include "nfp_net_ctrl.h" +#include "nfp_net_dp.h" #include "nfp_net.h" #include "nfp_port.h" @@ -217,7 +219,7 @@ nfp_net_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) struct nfp_net *nn = netdev_priv(netdev); snprintf(vnic_version, sizeof(vnic_version), "%d.%d.%d.%d", - nn->fw_ver.resv, nn->fw_ver.class, + nn->fw_ver.extend, nn->fw_ver.class, nn->fw_ver.major, nn->fw_ver.minor); strlcpy(drvinfo->bus_info, pci_name(nn->pdev), sizeof(drvinfo->bus_info)); @@ -386,9 +388,10 @@ static void nfp_net_get_ringparam(struct net_device *netdev, struct netlink_ext_ack *extack) { struct nfp_net *nn = netdev_priv(netdev); + u32 qc_max = nn->dev_info->max_qc_size; - ring->rx_max_pending = NFP_NET_MAX_RX_DESCS; - ring->tx_max_pending = NFP_NET_MAX_TX_DESCS; + ring->rx_max_pending = qc_max; + ring->tx_max_pending = qc_max / nn->dp.ops->tx_min_desc_per_pkt; ring->rx_pending = nn->dp.rxd_cnt; ring->tx_pending = nn->dp.txd_cnt; } @@ -412,19 +415,22 @@ static int nfp_net_set_ringparam(struct net_device *netdev, struct kernel_ethtool_ringparam *kernel_ring, struct netlink_ext_ack *extack) { + u32 tx_dpp, qc_min, qc_max, rxd_cnt, txd_cnt; struct nfp_net *nn = netdev_priv(netdev); - u32 rxd_cnt, txd_cnt; /* We don't have separate queues/rings for small/large frames. */ if (ring->rx_mini_pending || ring->rx_jumbo_pending) return -EINVAL; + qc_min = nn->dev_info->min_qc_size; + qc_max = nn->dev_info->max_qc_size; + tx_dpp = nn->dp.ops->tx_min_desc_per_pkt; /* Round up to supported values */ rxd_cnt = roundup_pow_of_two(ring->rx_pending); txd_cnt = roundup_pow_of_two(ring->tx_pending); - if (rxd_cnt < NFP_NET_MIN_RX_DESCS || rxd_cnt > NFP_NET_MAX_RX_DESCS || - txd_cnt < NFP_NET_MIN_TX_DESCS || txd_cnt > NFP_NET_MAX_TX_DESCS) + if (rxd_cnt < qc_min || rxd_cnt > qc_max || + txd_cnt < qc_min / tx_dpp || txd_cnt > qc_max / tx_dpp) return -EINVAL; if (nn->dp.rxd_cnt == rxd_cnt && nn->dp.txd_cnt == txd_cnt) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 751f76cd4f79..ca4e05650fe6 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -22,6 +22,7 @@ #include "nfpcore/nfp.h" #include "nfpcore/nfp_cpp.h" +#include "nfpcore/nfp_dev.h" #include "nfpcore/nfp_nffw.h" #include "nfpcore/nfp_nsp.h" #include "nfpcore/nfp6000_pcie.h" @@ -116,13 +117,12 @@ nfp_net_pf_alloc_vnic(struct nfp_pf *pf, bool needs_netdev, n_rx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_RXRINGS); /* Allocate and initialise the vNIC */ - nn = nfp_net_alloc(pf->pdev, ctrl_bar, needs_netdev, + nn = nfp_net_alloc(pf->pdev, pf->dev_info, ctrl_bar, needs_netdev, n_tx_rings, n_rx_rings); if (IS_ERR(nn)) return nn; nn->app = pf->app; - nfp_net_get_fw_version(&nn->fw_ver, ctrl_bar); nn->tx_bar = qc_bar + tx_base * NFP_QCP_QUEUE_ADDR_SZ; nn->rx_bar = qc_bar + rx_base * NFP_QCP_QUEUE_ADDR_SZ; nn->dp.is_vf = 0; @@ -307,6 +307,7 @@ static int nfp_net_pf_init_vnics(struct nfp_pf *pf) static int nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride) { + struct devlink *devlink = priv_to_devlink(pf); u8 __iomem *ctrl_bar; int err; @@ -314,9 +315,9 @@ nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride) if (IS_ERR(pf->app)) return PTR_ERR(pf->app); - mutex_lock(&pf->lock); + devl_lock(devlink); err = nfp_app_init(pf->app); - mutex_unlock(&pf->lock); + devl_unlock(devlink); if (err) goto err_free; @@ -343,9 +344,9 @@ nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride) err_unmap: nfp_cpp_area_release_free(pf->ctrl_vnic_bar); err_app_clean: - mutex_lock(&pf->lock); + devl_lock(devlink); nfp_app_clean(pf->app); - mutex_unlock(&pf->lock); + devl_unlock(devlink); err_free: nfp_app_free(pf->app); pf->app = NULL; @@ -354,14 +355,16 @@ nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride) static void nfp_net_pf_app_clean(struct nfp_pf *pf) { + struct devlink *devlink = priv_to_devlink(pf); + if (pf->ctrl_vnic) { nfp_net_pf_free_vnic(pf, pf->ctrl_vnic); nfp_cpp_area_release_free(pf->ctrl_vnic_bar); } - mutex_lock(&pf->lock); + devl_lock(devlink); nfp_app_clean(pf->app); - mutex_unlock(&pf->lock); + devl_unlock(devlink); nfp_app_free(pf->app); pf->app = NULL; @@ -495,8 +498,9 @@ static int nfp_net_pci_map_mem(struct nfp_pf *pf) } cpp_id = NFP_CPP_ISLAND_ID(0, NFP_CPP_ACTION_RW, 0, 0); - mem = nfp_cpp_map_area(pf->cpp, "net.qc", cpp_id, NFP_PCIE_QUEUE(0), - NFP_QCP_QUEUE_AREA_SZ, &pf->qc_area); + mem = nfp_cpp_map_area(pf->cpp, "net.qc", cpp_id, + nfp_qcp_queue_offset(pf->dev_info, 0), + pf->dev_info->qc_area_sz, &pf->qc_area); if (IS_ERR(mem)) { nfp_err(pf->cpp, "Failed to map Queue Controller area.\n"); err = PTR_ERR(mem); @@ -546,12 +550,13 @@ nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port, int nfp_net_refresh_port_table_sync(struct nfp_pf *pf) { + struct devlink *devlink = priv_to_devlink(pf); struct nfp_eth_table *eth_table; struct nfp_net *nn, *next; struct nfp_port *port; int err; - lockdep_assert_held(&pf->lock); + devl_assert_locked(devlink); /* Check for nfp_net_pci_remove() racing against us */ if (list_empty(&pf->vnics)) @@ -600,10 +605,11 @@ static void nfp_net_refresh_vnics(struct work_struct *work) { struct nfp_pf *pf = container_of(work, struct nfp_pf, port_refresh_work); + struct devlink *devlink = priv_to_devlink(pf); - mutex_lock(&pf->lock); + devl_lock(devlink); nfp_net_refresh_port_table_sync(pf); - mutex_unlock(&pf->lock); + devl_unlock(devlink); } void nfp_net_refresh_port_table(struct nfp_port *port) @@ -672,9 +678,11 @@ int nfp_net_pci_probe(struct nfp_pf *pf) } nfp_net_get_fw_version(&fw_ver, ctrl_bar); - if (fw_ver.resv || fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { + if (fw_ver.extend & NFP_NET_CFG_VERSION_RESERVED_MASK || + fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { nfp_err(pf->cpp, "Unknown Firmware ABI %d.%d.%d.%d\n", - fw_ver.resv, fw_ver.class, fw_ver.major, fw_ver.minor); + fw_ver.extend, fw_ver.class, + fw_ver.major, fw_ver.minor); err = -EINVAL; goto err_unmap; } @@ -690,7 +698,7 @@ int nfp_net_pci_probe(struct nfp_pf *pf) break; default: nfp_err(pf->cpp, "Unsupported Firmware ABI %d.%d.%d.%d\n", - fw_ver.resv, fw_ver.class, + fw_ver.extend, fw_ver.class, fw_ver.major, fw_ver.minor); err = -EINVAL; goto err_unmap; @@ -709,7 +717,7 @@ int nfp_net_pci_probe(struct nfp_pf *pf) if (err) goto err_shared_buf_unreg; - mutex_lock(&pf->lock); + devl_lock(devlink); pf->ddir = nfp_net_debugfs_device_add(pf->pdev); /* Allocate the vnics and do basic init */ @@ -729,7 +737,7 @@ int nfp_net_pci_probe(struct nfp_pf *pf) if (err) goto err_stop_app; - mutex_unlock(&pf->lock); + devl_unlock(devlink); devlink_register(devlink); return 0; @@ -742,7 +750,7 @@ int nfp_net_pci_probe(struct nfp_pf *pf) nfp_net_pf_free_vnics(pf); err_clean_ddir: nfp_net_debugfs_dir_clean(&pf->ddir); - mutex_unlock(&pf->lock); + devl_unlock(devlink); nfp_devlink_params_unregister(pf); err_shared_buf_unreg: nfp_shared_buf_unregister(pf); @@ -756,10 +764,11 @@ int nfp_net_pci_probe(struct nfp_pf *pf) void nfp_net_pci_remove(struct nfp_pf *pf) { + struct devlink *devlink = priv_to_devlink(pf); struct nfp_net *nn, *next; devlink_unregister(priv_to_devlink(pf)); - mutex_lock(&pf->lock); + devl_lock(devlink); list_for_each_entry_safe(nn, next, &pf->vnics, vnic_list) { if (!nfp_net_is_data_vnic(nn)) continue; @@ -771,7 +780,7 @@ void nfp_net_pci_remove(struct nfp_pf *pf) /* stop app first, to avoid double free of ctrl vNIC's ddir */ nfp_net_debugfs_dir_clean(&pf->ddir); - mutex_unlock(&pf->lock); + devl_unlock(devlink); nfp_devlink_params_unregister(pf); nfp_shared_buf_unregister(pf); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index 181ac8e789a3..ba3fa7eac98d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -20,7 +20,7 @@ struct net_device * nfp_repr_get_locked(struct nfp_app *app, struct nfp_reprs *set, unsigned int id) { return rcu_dereference_protected(set->reprs[id], - lockdep_is_held(&app->pf->lock)); + nfp_app_is_locked(app)); } static void @@ -476,7 +476,7 @@ nfp_reprs_clean_and_free_by_type(struct nfp_app *app, enum nfp_repr_type type) int i; reprs = rcu_dereference_protected(app->reprs[type], - lockdep_is_held(&app->pf->lock)); + nfp_app_is_locked(app)); if (!reprs) return; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h index a3db0cbf6425..786be58a907e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h @@ -4,8 +4,7 @@ #ifndef _NFP_NET_SRIOV_H_ #define _NFP_NET_SRIOV_H_ -/** - * SRIOV VF configuration. +/* SRIOV VF configuration. * The configuration memory begins with a mailbox region for communication with * the firmware followed by individual VF entries. */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_xsk.c b/drivers/net/ethernet/netronome/nfp/nfp_net_xsk.c new file mode 100644 index 000000000000..86829446c637 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_xsk.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (C) 2018 Netronome Systems, Inc */ +/* Copyright (C) 2021 Corigine, Inc */ + +#include +#include +#include +#include +#include + +#include "nfp_app.h" +#include "nfp_net.h" +#include "nfp_net_dp.h" +#include "nfp_net_xsk.h" + +static void +nfp_net_xsk_rx_bufs_stash(struct nfp_net_rx_ring *rx_ring, unsigned int idx, + struct xdp_buff *xdp) +{ + unsigned int headroom; + + headroom = xsk_pool_get_headroom(rx_ring->r_vec->xsk_pool); + + rx_ring->rxds[idx].fld.reserved = 0; + rx_ring->rxds[idx].fld.meta_len_dd = 0; + + rx_ring->xsk_rxbufs[idx].xdp = xdp; + rx_ring->xsk_rxbufs[idx].dma_addr = + xsk_buff_xdp_get_frame_dma(xdp) + headroom; +} + +void nfp_net_xsk_rx_unstash(struct nfp_net_xsk_rx_buf *rxbuf) +{ + rxbuf->dma_addr = 0; + rxbuf->xdp = NULL; +} + +void nfp_net_xsk_rx_free(struct nfp_net_xsk_rx_buf *rxbuf) +{ + if (rxbuf->xdp) + xsk_buff_free(rxbuf->xdp); + + nfp_net_xsk_rx_unstash(rxbuf); +} + +void nfp_net_xsk_rx_bufs_free(struct nfp_net_rx_ring *rx_ring) +{ + unsigned int i; + + if (!rx_ring->cnt) + return; + + for (i = 0; i < rx_ring->cnt - 1; i++) + nfp_net_xsk_rx_free(&rx_ring->xsk_rxbufs[i]); +} + +void nfp_net_xsk_rx_ring_fill_freelist(struct nfp_net_rx_ring *rx_ring) +{ + struct nfp_net_r_vector *r_vec = rx_ring->r_vec; + struct xsk_buff_pool *pool = r_vec->xsk_pool; + unsigned int wr_idx, wr_ptr_add = 0; + struct xdp_buff *xdp; + + while (nfp_net_rx_space(rx_ring)) { + wr_idx = D_IDX(rx_ring, rx_ring->wr_p); + + xdp = xsk_buff_alloc(pool); + if (!xdp) + break; + + nfp_net_xsk_rx_bufs_stash(rx_ring, wr_idx, xdp); + + nfp_desc_set_dma_addr(&rx_ring->rxds[wr_idx].fld, + rx_ring->xsk_rxbufs[wr_idx].dma_addr); + + rx_ring->wr_p++; + wr_ptr_add++; + } + + /* Ensure all records are visible before incrementing write counter. */ + wmb(); + nfp_qcp_wr_ptr_add(rx_ring->qcp_fl, wr_ptr_add); +} + +void nfp_net_xsk_rx_drop(struct nfp_net_r_vector *r_vec, + struct nfp_net_xsk_rx_buf *xrxbuf) +{ + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->rx_drops++; + u64_stats_update_end(&r_vec->rx_sync); + + nfp_net_xsk_rx_free(xrxbuf); +} + +static void nfp_net_xsk_pool_unmap(struct device *dev, + struct xsk_buff_pool *pool) +{ + return xsk_pool_dma_unmap(pool, 0); +} + +static int nfp_net_xsk_pool_map(struct device *dev, struct xsk_buff_pool *pool) +{ + return xsk_pool_dma_map(pool, dev, 0); +} + +int nfp_net_xsk_setup_pool(struct net_device *netdev, + struct xsk_buff_pool *pool, u16 queue_id) +{ + struct nfp_net *nn = netdev_priv(netdev); + + struct xsk_buff_pool *prev_pool; + struct nfp_net_dp *dp; + int err; + + /* NFDK doesn't implement xsk yet. */ + if (nn->dp.ops->version == NFP_NFD_VER_NFDK) + return -EOPNOTSUPP; + + /* Reject on old FWs so we can drop some checks on datapath. */ + if (nn->dp.rx_offset != NFP_NET_CFG_RX_OFFSET_DYNAMIC) + return -EOPNOTSUPP; + if (!nn->dp.chained_metadata_format) + return -EOPNOTSUPP; + + /* Install */ + if (pool) { + err = nfp_net_xsk_pool_map(nn->dp.dev, pool); + if (err) + return err; + } + + /* Reconfig/swap */ + dp = nfp_net_clone_dp(nn); + if (!dp) { + err = -ENOMEM; + goto err_unmap; + } + + prev_pool = dp->xsk_pools[queue_id]; + dp->xsk_pools[queue_id] = pool; + + err = nfp_net_ring_reconfig(nn, dp, NULL); + if (err) + goto err_unmap; + + /* Uninstall */ + if (prev_pool) + nfp_net_xsk_pool_unmap(nn->dp.dev, prev_pool); + + return 0; +err_unmap: + if (pool) + nfp_net_xsk_pool_unmap(nn->dp.dev, pool); + + return err; +} + +int nfp_net_xsk_wakeup(struct net_device *netdev, u32 queue_id, u32 flags) +{ + struct nfp_net *nn = netdev_priv(netdev); + + /* queue_id comes from a zero-copy socket, installed with XDP_SETUP_XSK_POOL, + * so it must be within our vector range. Moreover, our napi structs + * are statically allocated, so we can always kick them without worrying + * if reconfig is in progress or interface down. + */ + napi_schedule(&nn->r_vecs[queue_id].napi); + + return 0; +} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_xsk.h b/drivers/net/ethernet/netronome/nfp/nfp_net_xsk.h new file mode 100644 index 000000000000..6d281eb2fc1c --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_xsk.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* Copyright (C) 2018 Netronome Systems, Inc */ +/* Copyright (C) 2021 Corigine, Inc */ + +#ifndef _NFP_XSK_H_ +#define _NFP_XSK_H_ + +#include + +#define NFP_NET_XSK_TX_BATCH 16 /* XSK TX transmission batch size. */ + +static inline bool nfp_net_has_xsk_pool_slow(struct nfp_net_dp *dp, + unsigned int qid) +{ + return dp->xdp_prog && dp->xsk_pools[qid]; +} + +static inline int nfp_net_rx_space(struct nfp_net_rx_ring *rx_ring) +{ + return rx_ring->cnt - rx_ring->wr_p + rx_ring->rd_p - 1; +} + +static inline int nfp_net_tx_space(struct nfp_net_tx_ring *tx_ring) +{ + return tx_ring->cnt - tx_ring->wr_p + tx_ring->rd_p - 1; +} + +void nfp_net_xsk_rx_unstash(struct nfp_net_xsk_rx_buf *rxbuf); +void nfp_net_xsk_rx_free(struct nfp_net_xsk_rx_buf *rxbuf); +void nfp_net_xsk_rx_drop(struct nfp_net_r_vector *r_vec, + struct nfp_net_xsk_rx_buf *xrxbuf); +int nfp_net_xsk_setup_pool(struct net_device *netdev, struct xsk_buff_pool *pool, + u16 queue_id); + +void nfp_net_xsk_rx_bufs_free(struct nfp_net_rx_ring *rx_ring); + +void nfp_net_xsk_rx_ring_fill_freelist(struct nfp_net_rx_ring *rx_ring); + +int nfp_net_xsk_wakeup(struct net_device *netdev, u32 queue_id, u32 flags); + +#endif /* _NFP_XSK_H_ */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c index 87f2268b16d6..a51eb26dd977 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c @@ -13,6 +13,7 @@ #include #include +#include "nfpcore/nfp_dev.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" #include "nfp_main.h" @@ -36,11 +37,14 @@ struct nfp_net_vf { static const char nfp_net_driver_name[] = "nfp_netvf"; -#define PCI_DEVICE_NFP6000VF 0x6003 static const struct pci_device_id nfp_netvf_pci_device_ids[] = { - { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_NFP6000VF, + { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP3800_VF, PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, - PCI_ANY_ID, 0, + PCI_ANY_ID, 0, NFP_DEV_NFP3800_VF, + }, + { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP6000_VF, + PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, + PCI_ANY_ID, 0, NFP_DEV_NFP6000_VF, }, { 0, } /* Required last entry. */ }; @@ -65,6 +69,7 @@ static void nfp_netvf_get_mac_addr(struct nfp_net *nn) static int nfp_netvf_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) { + const struct nfp_dev_info *dev_info; struct nfp_net_fw_version fw_ver; int max_tx_rings, max_rx_rings; u32 tx_bar_off, rx_bar_off; @@ -78,6 +83,8 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, int stride; int err; + dev_info = &nfp_dev_info[pci_id->driver_data]; + vf = kzalloc(sizeof(*vf), GFP_KERNEL); if (!vf) return -ENOMEM; @@ -95,8 +102,7 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, pci_set_master(pdev); - err = dma_set_mask_and_coherent(&pdev->dev, - DMA_BIT_MASK(NFP_NET_MAX_DMA_BITS)); + err = dma_set_mask_and_coherent(&pdev->dev, dev_info->dma_mask); if (err) goto err_pci_regions; @@ -116,9 +122,11 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, } nfp_net_get_fw_version(&fw_ver, ctrl_bar); - if (fw_ver.resv || fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { + if (fw_ver.extend & NFP_NET_CFG_VERSION_RESERVED_MASK || + fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { dev_err(&pdev->dev, "Unknown Firmware ABI %d.%d.%d.%d\n", - fw_ver.resv, fw_ver.class, fw_ver.major, fw_ver.minor); + fw_ver.extend, fw_ver.class, + fw_ver.major, fw_ver.minor); err = -EINVAL; goto err_ctrl_unmap; } @@ -138,7 +146,7 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, break; default: dev_err(&pdev->dev, "Unsupported Firmware ABI %d.%d.%d.%d\n", - fw_ver.resv, fw_ver.class, + fw_ver.extend, fw_ver.class, fw_ver.major, fw_ver.minor); err = -EINVAL; goto err_ctrl_unmap; @@ -167,19 +175,19 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, } startq = readl(ctrl_bar + NFP_NET_CFG_START_TXQ); - tx_bar_off = NFP_PCIE_QUEUE(startq); + tx_bar_off = nfp_qcp_queue_offset(dev_info, startq); startq = readl(ctrl_bar + NFP_NET_CFG_START_RXQ); - rx_bar_off = NFP_PCIE_QUEUE(startq); + rx_bar_off = nfp_qcp_queue_offset(dev_info, startq); /* Allocate and initialise the netdev */ - nn = nfp_net_alloc(pdev, ctrl_bar, true, max_tx_rings, max_rx_rings); + nn = nfp_net_alloc(pdev, dev_info, ctrl_bar, true, + max_tx_rings, max_rx_rings); if (IS_ERR(nn)) { err = PTR_ERR(nn); goto err_ctrl_unmap; } vf->nn = nn; - nn->fw_ver = fw_ver; nn->dp.is_vf = 1; nn->stride_tx = stride; nn->stride_rx = stride; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c index 93c5bfc0510b..4f2308570dcf 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c @@ -75,23 +75,6 @@ int nfp_port_set_features(struct net_device *netdev, netdev_features_t features) return 0; } -struct nfp_port * -nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id) -{ - struct nfp_port *port; - - lockdep_assert_held(&pf->lock); - - if (type != NFP_PORT_PHYS_PORT) - return NULL; - - list_for_each_entry(port, &pf->ports, port_list) - if (port->eth_id == id) - return port; - - return NULL; -} - struct nfp_eth_table_port *__nfp_port_get_eth_port(struct nfp_port *port) { if (!port) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index ae4da189d955..d1ebe6c72f7f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -106,8 +106,6 @@ nfp_port_set_features(struct net_device *netdev, netdev_features_t features); struct nfp_port *nfp_port_from_netdev(struct net_device *netdev); int nfp_port_get_port_parent_id(struct net_device *netdev, struct netdev_phys_item_id *ppid); -struct nfp_port * -nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id); struct nfp_eth_table_port *__nfp_port_get_eth_port(struct nfp_port *port); struct nfp_eth_table_port *nfp_port_get_eth_port(struct nfp_port *port); @@ -132,8 +130,7 @@ void nfp_devlink_port_unregister(struct nfp_port *port); void nfp_devlink_port_type_eth_set(struct nfp_port *port); void nfp_devlink_port_type_clear(struct nfp_port *port); -/** - * Mac stats (0x0000 - 0x0200) +/* Mac stats (0x0000 - 0x0200) * all counters are 64bit. */ #define NFP_MAC_STATS_BASE 0x0000 diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c index 252fe06f58aa..0d1d39edbbae 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c @@ -28,6 +28,7 @@ #include #include "nfp_cpp.h" +#include "nfp_dev.h" #include "nfp6000/nfp6000.h" @@ -100,11 +101,7 @@ #define NFP_PCIE_P2C_GENERAL_TOKEN_OFFSET(bar, x) ((x) << ((bar)->bitsize - 4)) #define NFP_PCIE_P2C_GENERAL_SIZE(bar) (1 << ((bar)->bitsize - 4)) -#define NFP_PCIE_CFG_BAR_PCIETOCPPEXPANSIONBAR(bar, slot) \ - (0x400 + ((bar) * 8 + (slot)) * 4) - -#define NFP_PCIE_CPP_BAR_PCIETOCPPEXPANSIONBAR(bar, slot) \ - (((bar) * 8 + (slot)) * 4) +#define NFP_PCIE_P2C_EXPBAR_OFFSET(bar_index) ((bar_index) * 4) /* The number of explicit BARs to reserve. * Minimum is 0, maximum is 4 on the NFP6000. @@ -145,6 +142,7 @@ struct nfp_bar { struct nfp6000_pcie { struct pci_dev *pdev; struct device *dev; + const struct nfp_dev_info *dev_info; /* PCI BAR management */ spinlock_t bar_lock; /* Protect the PCI2CPP BAR cache */ @@ -269,19 +267,16 @@ compute_bar(const struct nfp6000_pcie *nfp, const struct nfp_bar *bar, static int nfp6000_bar_write(struct nfp6000_pcie *nfp, struct nfp_bar *bar, u32 newcfg) { - int base, slot; - int xbar; + unsigned int xbar; - base = bar->index >> 3; - slot = bar->index & 7; + xbar = NFP_PCIE_P2C_EXPBAR_OFFSET(bar->index); if (nfp->iomem.csr) { - xbar = NFP_PCIE_CPP_BAR_PCIETOCPPEXPANSIONBAR(base, slot); writel(newcfg, nfp->iomem.csr + xbar); /* Readback to ensure BAR is flushed */ readl(nfp->iomem.csr + xbar); } else { - xbar = NFP_PCIE_CFG_BAR_PCIETOCPPEXPANSIONBAR(base, slot); + xbar += nfp->dev_info->pcie_cfg_expbar_offset; pci_write_config_dword(nfp->pdev, xbar, newcfg); } @@ -622,7 +617,8 @@ static int enable_bars(struct nfp6000_pcie *nfp, u16 interface) nfp6000_bar_write(nfp, bar, barcfg_msix_general); - nfp->expl.data = bar->iomem + NFP_PCIE_SRAM + 0x1000; + nfp->expl.data = bar->iomem + NFP_PCIE_SRAM + + nfp->dev_info->pcie_expl_offset; switch (nfp->pdev->device) { case PCI_DEVICE_ID_NETRONOME_NFP3800: @@ -1306,18 +1302,20 @@ static const struct nfp_cpp_operations nfp6000_pcie_ops = { /** * nfp_cpp_from_nfp6000_pcie() - Build a NFP CPP bus from a NFP6000 PCI device * @pdev: NFP6000 PCI device + * @dev_info: NFP ASIC params * * Return: NFP CPP handle */ -struct nfp_cpp *nfp_cpp_from_nfp6000_pcie(struct pci_dev *pdev) +struct nfp_cpp * +nfp_cpp_from_nfp6000_pcie(struct pci_dev *pdev, const struct nfp_dev_info *dev_info) { struct nfp6000_pcie *nfp; u16 interface; int err; /* Finished with card initialization. */ - dev_info(&pdev->dev, - "Netronome Flow Processor NFP4000/NFP5000/NFP6000 PCIe Card Probe\n"); + dev_info(&pdev->dev, "Netronome Flow Processor %s PCIe Card Probe\n", + dev_info->chip_names); pcie_print_link_status(pdev); nfp = kzalloc(sizeof(*nfp), GFP_KERNEL); @@ -1328,6 +1326,7 @@ struct nfp_cpp *nfp_cpp_from_nfp6000_pcie(struct pci_dev *pdev) nfp->dev = &pdev->dev; nfp->pdev = pdev; + nfp->dev_info = dev_info; init_waitqueue_head(&nfp->bar_waiters); spin_lock_init(&nfp->bar_lock); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.h index 6d1bffa6eac6..097660b673db 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.h @@ -11,6 +11,7 @@ #include "nfp_cpp.h" -struct nfp_cpp *nfp_cpp_from_nfp6000_pcie(struct pci_dev *pdev); +struct nfp_cpp * +nfp_cpp_from_nfp6000_pcie(struct pci_dev *pdev, const struct nfp_dev_info *dev_info); #endif /* NFP6000_PCIE_H */ diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h index 2dd0f5842873..3d379e937184 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h @@ -32,10 +32,6 @@ #define PCI_64BIT_BAR_COUNT 3 -/* NFP hardware vendor/device ids. - */ -#define PCI_DEVICE_ID_NETRONOME_NFP3800 0x3800 - #define NFP_CPP_NUM_TARGETS 16 /* Max size of area it should be safe to request */ #define NFP_CPP_SAFE_AREA_SIZE SZ_2M diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpplib.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpplib.c index 85734c6badf5..508ae6b571ca 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpplib.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpplib.c @@ -22,6 +22,7 @@ #include "nfp6000/nfp_xpb.h" /* NFP6000 PL */ +#define NFP_PL_DEVICE_PART_NFP6000 0x6200 #define NFP_PL_DEVICE_ID 0x00000004 #define NFP_PL_DEVICE_ID_MASK GENMASK(7, 0) #define NFP_PL_DEVICE_PART_MASK GENMASK(31, 16) @@ -130,8 +131,12 @@ int nfp_cpp_model_autodetect(struct nfp_cpp *cpp, u32 *model) return err; *model = reg & NFP_PL_DEVICE_MODEL_MASK; - if (*model & NFP_PL_DEVICE_ID_MASK) - *model -= 0x10; + /* Disambiguate the NFP4000/NFP5000/NFP6000 chips */ + if (FIELD_GET(NFP_PL_DEVICE_PART_MASK, reg) == + NFP_PL_DEVICE_PART_NFP6000) { + if (*model & NFP_PL_DEVICE_ID_MASK) + *model -= 0x10; + } return 0; } diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_dev.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_dev.c new file mode 100644 index 000000000000..28384d6d1c6f --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_dev.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (C) 2019 Netronome Systems, Inc. */ + +#include +#include +#include + +#include "nfp_dev.h" + +const struct nfp_dev_info nfp_dev_info[NFP_DEV_CNT] = { + [NFP_DEV_NFP3800] = { + .dma_mask = DMA_BIT_MASK(40), + .qc_idx_mask = GENMASK(8, 0), + .qc_addr_offset = 0x400000, + .min_qc_size = 512, + .max_qc_size = SZ_64K, + + .chip_names = "NFP3800", + .pcie_cfg_expbar_offset = 0x0a00, + .pcie_expl_offset = 0xd000, + .qc_area_sz = 0x100000, + }, + [NFP_DEV_NFP3800_VF] = { + .dma_mask = DMA_BIT_MASK(40), + .qc_idx_mask = GENMASK(8, 0), + .qc_addr_offset = 0, + .min_qc_size = 512, + .max_qc_size = SZ_64K, + }, + [NFP_DEV_NFP6000] = { + .dma_mask = DMA_BIT_MASK(40), + .qc_idx_mask = GENMASK(7, 0), + .qc_addr_offset = 0x80000, + .min_qc_size = 256, + .max_qc_size = SZ_256K, + + .chip_names = "NFP4000/NFP5000/NFP6000", + .pcie_cfg_expbar_offset = 0x0400, + .pcie_expl_offset = 0x1000, + .qc_area_sz = 0x80000, + }, + [NFP_DEV_NFP6000_VF] = { + .dma_mask = DMA_BIT_MASK(40), + .qc_idx_mask = GENMASK(7, 0), + .qc_addr_offset = 0, + .min_qc_size = 256, + .max_qc_size = SZ_256K, + }, +}; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_dev.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_dev.h new file mode 100644 index 000000000000..d4189869cf7b --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_dev.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* Copyright (C) 2019 Netronome Systems, Inc. */ + +#ifndef _NFP_DEV_H_ +#define _NFP_DEV_H_ + +#include + +enum nfp_dev_id { + NFP_DEV_NFP3800, + NFP_DEV_NFP3800_VF, + NFP_DEV_NFP6000, + NFP_DEV_NFP6000_VF, + NFP_DEV_CNT, +}; + +struct nfp_dev_info { + /* Required fields */ + u64 dma_mask; + u32 qc_idx_mask; + u32 qc_addr_offset; + u32 min_qc_size; + u32 max_qc_size; + + /* PF-only fields */ + const char *chip_names; + u32 pcie_cfg_expbar_offset; + u32 pcie_expl_offset; + u32 qc_area_sz; +}; + +extern const struct nfp_dev_info nfp_dev_info[NFP_DEV_CNT]; + +#endif diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 10e7d8b21c46..730fea214b8a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -513,7 +513,7 @@ nfp_nsp_command_buf_dma_sg(struct nfp_nsp *nsp, dma_size = BIT_ULL(dma_order); nseg = DIV_ROUND_UP(max_size, chunk_size); - chunks = kzalloc(array_size(sizeof(*chunks), nseg), GFP_KERNEL); + chunks = kcalloc(nseg, sizeof(*chunks), GFP_KERNEL); if (!chunks) return -ENOMEM; diff --git a/drivers/net/ethernet/ni/nixge.c b/drivers/net/ethernet/ni/nixge.c index 07a00dd9cfe0..4b3482ce90a1 100644 --- a/drivers/net/ethernet/ni/nixge.c +++ b/drivers/net/ethernet/ni/nixge.c @@ -324,8 +324,9 @@ static int nixge_hw_dma_bd_init(struct net_device *ndev) + sizeof(*priv->rx_bd_v) * ((i + 1) % RX_BD_NUM)); - skb = netdev_alloc_skb_ip_align(ndev, - NIXGE_MAX_JUMBO_FRAME_SIZE); + skb = __netdev_alloc_skb_ip_align(ndev, + NIXGE_MAX_JUMBO_FRAME_SIZE, + GFP_KERNEL); if (!skb) goto out; diff --git a/drivers/net/ethernet/packetengines/yellowfin.c b/drivers/net/ethernet/packetengines/yellowfin.c index 12105f62cbdd..03650022d444 100644 --- a/drivers/net/ethernet/packetengines/yellowfin.c +++ b/drivers/net/ethernet/packetengines/yellowfin.c @@ -191,7 +191,7 @@ IV. Notes Thanks to Kim Stearns of Packet Engines for providing a pair of G-NIC boards. Thanks to Bruce Faust of Digitalscape for providing both their SYM53C885 board -and an AlphaStation to verifty the Alpha port! +and an AlphaStation to verify the Alpha port! IVb. References diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h index 5e25411ff02f..602f4d45d529 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic.h +++ b/drivers/net/ethernet/pensando/ionic/ionic.h @@ -18,7 +18,7 @@ struct ionic_lif; #define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_PF 0x1002 #define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_VF 0x1003 -#define DEVCMD_TIMEOUT 10 +#define DEVCMD_TIMEOUT 5 #define IONIC_ADMINQ_TIME_SLICE msecs_to_jiffies(100) #define IONIC_PHC_UPDATE_NS 10000000000 /* 10s in nanoseconds */ @@ -78,6 +78,9 @@ void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode, u8 status, int err); int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_wait); +int ionic_dev_cmd_wait_nomsg(struct ionic *ionic, unsigned long max_wait); +void ionic_dev_cmd_dev_err_print(struct ionic *ionic, u8 opcode, u8 status, + int err); int ionic_set_dma_mask(struct ionic *ionic); int ionic_setup(struct ionic *ionic); @@ -89,4 +92,6 @@ int ionic_port_identify(struct ionic *ionic); int ionic_port_init(struct ionic *ionic); int ionic_port_reset(struct ionic *ionic); +const char *ionic_vf_attr_to_str(enum ionic_vf_attr attr); + #endif /* _IONIC_H_ */ diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c index 7e296fa71b36..6ffc62c41165 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c @@ -109,8 +109,8 @@ void ionic_bus_unmap_dbpage(struct ionic *ionic, void __iomem *page) static void ionic_vf_dealloc_locked(struct ionic *ionic) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_STATSADDR }; struct ionic_vf *v; - dma_addr_t dma = 0; int i; if (!ionic->vfs) @@ -120,9 +120,8 @@ static void ionic_vf_dealloc_locked(struct ionic *ionic) v = &ionic->vfs[i]; if (v->stats_pa) { - (void)ionic_set_vf_config(ionic, i, - IONIC_VF_ATTR_STATSADDR, - (u8 *)&dma); + vfc.stats_pa = 0; + (void)ionic_set_vf_config(ionic, i, &vfc); dma_unmap_single(ionic->dev, v->stats_pa, sizeof(v->stats), DMA_FROM_DEVICE); v->stats_pa = 0; @@ -143,6 +142,7 @@ static void ionic_vf_dealloc(struct ionic *ionic) static int ionic_vf_alloc(struct ionic *ionic, int num_vfs) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_STATSADDR }; struct ionic_vf *v; int err = 0; int i; @@ -166,9 +166,10 @@ static int ionic_vf_alloc(struct ionic *ionic, int num_vfs) } ionic->num_vfs++; + /* ignore failures from older FW, we just won't get stats */ - (void)ionic_set_vf_config(ionic, i, IONIC_VF_ATTR_STATSADDR, - (u8 *)&v->stats_pa); + vfc.stats_pa = cpu_to_le64(v->stats_pa); + (void)ionic_set_vf_config(ionic, i, &vfc); } out: @@ -331,6 +332,9 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_deregister_lifs; } + mod_timer(&ionic->watchdog_timer, + round_jiffies(jiffies + ionic->watchdog_period)); + return 0; err_out_deregister_lifs: @@ -348,7 +352,6 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err_out_reset: ionic_reset(ionic); err_out_teardown: - del_timer_sync(&ionic->watchdog_timer); pci_clear_master(pdev); /* Don't fail the probe for these errors, keep * the hw interface around for inspection diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.c b/drivers/net/ethernet/pensando/ionic/ionic_dev.c index d57e80d44c9d..9d0514cfeb5c 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.c @@ -33,7 +33,8 @@ static void ionic_watchdog_cb(struct timer_list *t) !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) ionic_link_status_check_request(lif, CAN_NOT_SLEEP); - if (test_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state)) { + if (test_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state) && + !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { work = kzalloc(sizeof(*work), GFP_ATOMIC); if (!work) { netdev_err(lif->netdev, "rxmode change dropped\n"); @@ -46,6 +47,24 @@ static void ionic_watchdog_cb(struct timer_list *t) } } +static void ionic_watchdog_init(struct ionic *ionic) +{ + struct ionic_dev *idev = &ionic->idev; + + timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0); + ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ; + + /* set times to ensure the first check will proceed */ + atomic_long_set(&idev->last_check_time, jiffies - 2 * HZ); + idev->last_hb_time = jiffies - 2 * ionic->watchdog_period; + /* init as ready, so no transition if the first check succeeds */ + idev->last_fw_hb = 0; + idev->fw_hb_ready = true; + idev->fw_status_ready = true; + idev->fw_generation = IONIC_FW_STS_F_GENERATION & + ioread8(&idev->dev_info_regs->fw_status); +} + void ionic_init_devinfo(struct ionic *ionic) { struct ionic_dev *idev = &ionic->idev; @@ -109,21 +128,7 @@ int ionic_dev_setup(struct ionic *ionic) return -EFAULT; } - timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0); - ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ; - - /* set times to ensure the first check will proceed */ - atomic_long_set(&idev->last_check_time, jiffies - 2 * HZ); - idev->last_hb_time = jiffies - 2 * ionic->watchdog_period; - /* init as ready, so no transition if the first check succeeds */ - idev->last_fw_hb = 0; - idev->fw_hb_ready = true; - idev->fw_status_ready = true; - idev->fw_generation = IONIC_FW_STS_F_GENERATION & - ioread8(&idev->dev_info_regs->fw_status); - - mod_timer(&ionic->watchdog_timer, - round_jiffies(jiffies + ionic->watchdog_period)); + ionic_watchdog_init(ionic); idev->db_pages = bar->vaddr; idev->phy_db_pages = bar->bus_addr; @@ -132,10 +137,21 @@ int ionic_dev_setup(struct ionic *ionic) } /* Devcmd Interface */ +bool ionic_is_fw_running(struct ionic_dev *idev) +{ + u8 fw_status = ioread8(&idev->dev_info_regs->fw_status); + + /* firmware is useful only if the running bit is set and + * fw_status != 0xff (bad PCI read) + */ + return (fw_status != 0xff) && (fw_status & IONIC_FW_STS_F_RUNNING); +} + int ionic_heartbeat_check(struct ionic *ionic) { - struct ionic_dev *idev = &ionic->idev; unsigned long check_time, last_check_time; + struct ionic_dev *idev = &ionic->idev; + struct ionic_lif *lif = ionic->lif; bool fw_status_ready = true; bool fw_hb_ready; u8 fw_generation; @@ -155,13 +171,10 @@ int ionic_heartbeat_check(struct ionic *ionic) goto do_check_time; } - /* firmware is useful only if the running bit is set and - * fw_status != 0xff (bad PCI read) - * If fw_status is not ready don't bother with the generation. - */ fw_status = ioread8(&idev->dev_info_regs->fw_status); - if (fw_status == 0xff || !(fw_status & IONIC_FW_STS_F_RUNNING)) { + /* If fw_status is not ready don't bother with the generation */ + if (!ionic_is_fw_running(idev)) { fw_status_ready = false; } else { fw_generation = fw_status & IONIC_FW_STS_F_GENERATION; @@ -176,26 +189,40 @@ int ionic_heartbeat_check(struct ionic *ionic) * the down, the next watchdog will see the fw is up * and the generation value stable, so will trigger * the fw-up activity. + * + * If we had already moved to FW_RESET from a RESET event, + * it is possible that we never saw the fw_status go to 0, + * so we fake the current idev->fw_status_ready here to + * force the transition and get FW up again. */ - fw_status_ready = false; + if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) + idev->fw_status_ready = false; /* go to running */ + else + fw_status_ready = false; /* go to down */ } } + dev_dbg(ionic->dev, "fw_status 0x%02x ready %d idev->ready %d last_hb 0x%x state 0x%02lx\n", + fw_status, fw_status_ready, idev->fw_status_ready, + idev->last_fw_hb, lif->state[0]); + /* is this a transition? */ - if (fw_status_ready != idev->fw_status_ready) { - struct ionic_lif *lif = ionic->lif; + if (fw_status_ready != idev->fw_status_ready && + !test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { bool trigger = false; idev->fw_status_ready = fw_status_ready; - if (!fw_status_ready) { - dev_info(ionic->dev, "FW stopped %u\n", fw_status); - if (lif && !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) - trigger = true; - } else { - dev_info(ionic->dev, "FW running %u\n", fw_status); - if (lif && test_bit(IONIC_LIF_F_FW_RESET, lif->state)) - trigger = true; + if (!fw_status_ready && + !test_bit(IONIC_LIF_F_FW_RESET, lif->state) && + !test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { + dev_info(ionic->dev, "FW stopped 0x%02x\n", fw_status); + trigger = true; + + } else if (fw_status_ready && + test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { + dev_info(ionic->dev, "FW running 0x%02x\n", fw_status); + trigger = true; } if (trigger) { @@ -210,12 +237,14 @@ int ionic_heartbeat_check(struct ionic *ionic) } } - if (!fw_status_ready) + if (!idev->fw_status_ready) return -ENXIO; - /* wait at least one watchdog period since the last heartbeat */ + /* Because of some variability in the actual FW heartbeat, we + * wait longer than the DEVCMD_TIMEOUT before checking again. + */ last_check_time = idev->last_hb_time; - if (time_before(check_time, last_check_time + ionic->watchdog_period)) + if (time_before(check_time, last_check_time + DEVCMD_TIMEOUT * 2 * HZ)) return 0; fw_hb = ioread32(&idev->dev_info_regs->fw_heartbeat); @@ -392,60 +421,63 @@ void ionic_dev_cmd_port_pause(struct ionic_dev *idev, u8 pause_type) } /* VF commands */ -int ionic_set_vf_config(struct ionic *ionic, int vf, u8 attr, u8 *data) +int ionic_set_vf_config(struct ionic *ionic, int vf, + struct ionic_vf_setattr_cmd *vfc) { union ionic_dev_cmd cmd = { .vf_setattr.opcode = IONIC_CMD_VF_SETATTR, - .vf_setattr.attr = attr, + .vf_setattr.attr = vfc->attr, .vf_setattr.vf_index = cpu_to_le16(vf), }; int err; + memcpy(cmd.vf_setattr.pad, vfc->pad, sizeof(vfc->pad)); + + mutex_lock(&ionic->dev_cmd_lock); + ionic_dev_cmd_go(&ionic->idev, &cmd); + err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); + mutex_unlock(&ionic->dev_cmd_lock); + + return err; +} + +int ionic_dev_cmd_vf_getattr(struct ionic *ionic, int vf, u8 attr, + struct ionic_vf_getattr_comp *comp) +{ + union ionic_dev_cmd cmd = { + .vf_getattr.opcode = IONIC_CMD_VF_GETATTR, + .vf_getattr.attr = attr, + .vf_getattr.vf_index = cpu_to_le16(vf), + }; + int err; + + if (vf >= ionic->num_vfs) + return -EINVAL; + switch (attr) { case IONIC_VF_ATTR_SPOOFCHK: - cmd.vf_setattr.spoofchk = *data; - dev_dbg(ionic->dev, "%s: vf %d spoof %d\n", - __func__, vf, *data); - break; case IONIC_VF_ATTR_TRUST: - cmd.vf_setattr.trust = *data; - dev_dbg(ionic->dev, "%s: vf %d trust %d\n", - __func__, vf, *data); - break; case IONIC_VF_ATTR_LINKSTATE: - cmd.vf_setattr.linkstate = *data; - dev_dbg(ionic->dev, "%s: vf %d linkstate %d\n", - __func__, vf, *data); - break; case IONIC_VF_ATTR_MAC: - ether_addr_copy(cmd.vf_setattr.macaddr, data); - dev_dbg(ionic->dev, "%s: vf %d macaddr %pM\n", - __func__, vf, data); - break; case IONIC_VF_ATTR_VLAN: - cmd.vf_setattr.vlanid = cpu_to_le16(*(u16 *)data); - dev_dbg(ionic->dev, "%s: vf %d vlan %d\n", - __func__, vf, *(u16 *)data); - break; case IONIC_VF_ATTR_RATE: - cmd.vf_setattr.maxrate = cpu_to_le32(*(u32 *)data); - dev_dbg(ionic->dev, "%s: vf %d maxrate %d\n", - __func__, vf, *(u32 *)data); break; case IONIC_VF_ATTR_STATSADDR: - cmd.vf_setattr.stats_pa = cpu_to_le64(*(u64 *)data); - dev_dbg(ionic->dev, "%s: vf %d stats_pa 0x%08llx\n", - __func__, vf, *(u64 *)data); - break; default: return -EINVAL; } mutex_lock(&ionic->dev_cmd_lock); ionic_dev_cmd_go(&ionic->idev, &cmd); - err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); + err = ionic_dev_cmd_wait_nomsg(ionic, DEVCMD_TIMEOUT); + memcpy_fromio(comp, &ionic->idev.dev_cmd_regs->comp.vf_getattr, + sizeof(*comp)); mutex_unlock(&ionic->dev_cmd_lock); + if (err && comp->status != IONIC_RC_ENOSUPP) + ionic_dev_cmd_dev_err_print(ionic, cmd.vf_getattr.opcode, + comp->status, err); + return err; } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h index e5acf3bd62b2..563c302eb033 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h @@ -318,7 +318,10 @@ void ionic_dev_cmd_port_autoneg(struct ionic_dev *idev, u8 an_enable); void ionic_dev_cmd_port_fec(struct ionic_dev *idev, u8 fec_type); void ionic_dev_cmd_port_pause(struct ionic_dev *idev, u8 pause_type); -int ionic_set_vf_config(struct ionic *ionic, int vf, u8 attr, u8 *data); +int ionic_set_vf_config(struct ionic *ionic, int vf, + struct ionic_vf_setattr_cmd *vfc); +int ionic_dev_cmd_vf_getattr(struct ionic *ionic, int vf, u8 attr, + struct ionic_vf_getattr_comp *comp); void ionic_dev_cmd_queue_identify(struct ionic_dev *idev, u16 lif_type, u8 qtype, u8 qver); void ionic_dev_cmd_lif_identify(struct ionic_dev *idev, u8 type, u8 ver); @@ -353,5 +356,6 @@ void ionic_q_rewind(struct ionic_queue *q, struct ionic_desc_info *start); void ionic_q_service(struct ionic_queue *q, struct ionic_cq_info *cq_info, unsigned int stop_index); int ionic_heartbeat_check(struct ionic *ionic); +bool ionic_is_fw_running(struct ionic_dev *idev); #endif /* _IONIC_DEV_H_ */ diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c index 386a5cf1e224..01c22701482d 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c @@ -74,10 +74,10 @@ static void ionic_get_drvinfo(struct net_device *netdev, struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; - strlcpy(drvinfo->driver, IONIC_DRV_NAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->fw_version, ionic->idev.dev_info.fw_version, + strscpy(drvinfo->driver, IONIC_DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->fw_version, ionic->idev.dev_info.fw_version, sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, ionic_bus_info(ionic), + strscpy(drvinfo->bus_info, ionic_bus_info(ionic), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_if.h b/drivers/net/ethernet/pensando/ionic/ionic_if.h index 278610ed7227..4a90f611c611 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_if.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_if.h @@ -759,7 +759,7 @@ enum ionic_txq_desc_opcode { * IONIC_TXQ_DESC_OPCODE_CSUM_HW: * Offload 16-bit checksum computation to hardware. * If @csum_l3 is set then the packet's L3 checksum is - * updated. Similarly, if @csum_l4 is set the the L4 + * updated. Similarly, if @csum_l4 is set the L4 * checksum is updated. If @encap is set then encap header * checksums are also updated. * @@ -1368,9 +1368,9 @@ union ionic_port_config { * @status: link status (enum ionic_port_oper_status) * @id: port id * @speed: link speed (in Mbps) - * @link_down_count: number of times link went from from up to down + * @link_down_count: number of times link went from up to down * @fec_type: fec type (enum ionic_port_fec_type) - * @xcvr: tranceiver status + * @xcvr: transceiver status */ struct ionic_port_status { __le32 id; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index 2ff7be17e5af..f3568901eb91 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "ionic.h" #include "ionic_bus.h" @@ -393,11 +394,11 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq) ionic_qcq_intr_free(lif, qcq); if (qcq->cq.info) { - devm_kfree(dev, qcq->cq.info); + vfree(qcq->cq.info); qcq->cq.info = NULL; } if (qcq->q.info) { - devm_kfree(dev, qcq->q.info); + vfree(qcq->q.info); qcq->q.info = NULL; } } @@ -528,8 +529,7 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, new->q.dev = dev; new->flags = flags; - new->q.info = devm_kcalloc(dev, num_descs, sizeof(*new->q.info), - GFP_KERNEL); + new->q.info = vzalloc(num_descs * sizeof(*new->q.info)); if (!new->q.info) { netdev_err(lif->netdev, "Cannot allocate queue info\n"); err = -ENOMEM; @@ -550,8 +550,7 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, if (err) goto err_out; - new->cq.info = devm_kcalloc(dev, num_descs, sizeof(*new->cq.info), - GFP_KERNEL); + new->cq.info = vzalloc(num_descs * sizeof(*new->cq.info)); if (!new->cq.info) { netdev_err(lif->netdev, "Cannot allocate completion queue info\n"); err = -ENOMEM; @@ -640,14 +639,14 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, err_out_free_q: dma_free_coherent(dev, new->q_size, new->q_base, new->q_base_pa); err_out_free_cq_info: - devm_kfree(dev, new->cq.info); + vfree(new->cq.info); err_out_free_irq: if (flags & IONIC_QCQ_F_INTR) { devm_free_irq(dev, new->intr.vector, &new->napi); ionic_intr_free(lif->ionic, new->intr.index); } err_out_free_q_info: - devm_kfree(dev, new->q.info); + vfree(new->q.info); err_out_free_qcq: devm_kfree(dev, new); err_out: @@ -1112,12 +1111,17 @@ static bool ionic_notifyq_service(struct ionic_cq *cq, ionic_link_status_check_request(lif, CAN_NOT_SLEEP); break; case IONIC_EVENT_RESET: - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) { - netdev_err(lif->netdev, "Reset event dropped\n"); - } else { - work->type = IONIC_DW_TYPE_LIF_RESET; - ionic_lif_deferred_enqueue(&lif->deferred, work); + if (lif->ionic->idev.fw_status_ready && + !test_bit(IONIC_LIF_F_FW_RESET, lif->state) && + !test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { + work = kzalloc(sizeof(*work), GFP_ATOMIC); + if (!work) { + netdev_err(lif->netdev, "Reset event dropped\n"); + clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state); + } else { + work->type = IONIC_DW_TYPE_LIF_RESET; + ionic_lif_deferred_enqueue(&lif->deferred, work); + } } break; default: @@ -1782,7 +1786,7 @@ static void ionic_lif_quiesce(struct ionic_lif *lif) err = ionic_adminq_post_wait(lif, &ctx); if (err) - netdev_err(lif->netdev, "lif quiesce failed %d\n", err); + netdev_dbg(lif->netdev, "lif quiesce failed %d\n", err); } static void ionic_txrx_disable(struct ionic_lif *lif) @@ -2152,6 +2156,76 @@ static int ionic_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd } } +static int ionic_update_cached_vf_config(struct ionic *ionic, int vf) +{ + struct ionic_vf_getattr_comp comp = { 0 }; + int err; + u8 attr; + + attr = IONIC_VF_ATTR_VLAN; + err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); + if (err && comp.status != IONIC_RC_ENOSUPP) + goto err_out; + if (!err) + ionic->vfs[vf].vlanid = comp.vlanid; + + attr = IONIC_VF_ATTR_SPOOFCHK; + err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); + if (err && comp.status != IONIC_RC_ENOSUPP) + goto err_out; + if (!err) + ionic->vfs[vf].spoofchk = comp.spoofchk; + + attr = IONIC_VF_ATTR_LINKSTATE; + err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); + if (err && comp.status != IONIC_RC_ENOSUPP) + goto err_out; + if (!err) { + switch (comp.linkstate) { + case IONIC_VF_LINK_STATUS_UP: + ionic->vfs[vf].linkstate = IFLA_VF_LINK_STATE_ENABLE; + break; + case IONIC_VF_LINK_STATUS_DOWN: + ionic->vfs[vf].linkstate = IFLA_VF_LINK_STATE_DISABLE; + break; + case IONIC_VF_LINK_STATUS_AUTO: + ionic->vfs[vf].linkstate = IFLA_VF_LINK_STATE_AUTO; + break; + default: + dev_warn(ionic->dev, "Unexpected link state %u\n", comp.linkstate); + break; + } + } + + attr = IONIC_VF_ATTR_RATE; + err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); + if (err && comp.status != IONIC_RC_ENOSUPP) + goto err_out; + if (!err) + ionic->vfs[vf].maxrate = comp.maxrate; + + attr = IONIC_VF_ATTR_TRUST; + err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); + if (err && comp.status != IONIC_RC_ENOSUPP) + goto err_out; + if (!err) + ionic->vfs[vf].trusted = comp.trust; + + attr = IONIC_VF_ATTR_MAC; + err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); + if (err && comp.status != IONIC_RC_ENOSUPP) + goto err_out; + if (!err) + ether_addr_copy(ionic->vfs[vf].macaddr, comp.macaddr); + +err_out: + if (err) + dev_err(ionic->dev, "Failed to get %s for VF %d\n", + ionic_vf_attr_to_str(attr), vf); + + return err; +} + static int ionic_get_vf_config(struct net_device *netdev, int vf, struct ifla_vf_info *ivf) { @@ -2167,14 +2241,18 @@ static int ionic_get_vf_config(struct net_device *netdev, if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ivf->vf = vf; - ivf->vlan = le16_to_cpu(ionic->vfs[vf].vlanid); - ivf->qos = 0; - ivf->spoofchk = ionic->vfs[vf].spoofchk; - ivf->linkstate = ionic->vfs[vf].linkstate; - ivf->max_tx_rate = le32_to_cpu(ionic->vfs[vf].maxrate); - ivf->trusted = ionic->vfs[vf].trusted; - ether_addr_copy(ivf->mac, ionic->vfs[vf].macaddr); + ivf->vf = vf; + ivf->qos = 0; + + ret = ionic_update_cached_vf_config(ionic, vf); + if (!ret) { + ivf->vlan = le16_to_cpu(ionic->vfs[vf].vlanid); + ivf->spoofchk = ionic->vfs[vf].spoofchk; + ivf->linkstate = ionic->vfs[vf].linkstate; + ivf->max_tx_rate = le32_to_cpu(ionic->vfs[vf].maxrate); + ivf->trusted = ionic->vfs[vf].trusted; + ether_addr_copy(ivf->mac, ionic->vfs[vf].macaddr); + } } up_read(&ionic->vf_op_lock); @@ -2220,6 +2298,7 @@ static int ionic_get_vf_stats(struct net_device *netdev, int vf, static int ionic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_MAC }; struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; int ret; @@ -2235,7 +2314,11 @@ static int ionic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ret = ionic_set_vf_config(ionic, vf, IONIC_VF_ATTR_MAC, mac); + ether_addr_copy(vfc.macaddr, mac); + dev_dbg(ionic->dev, "%s: vf %d macaddr %pM\n", + __func__, vf, vfc.macaddr); + + ret = ionic_set_vf_config(ionic, vf, &vfc); if (!ret) ether_addr_copy(ionic->vfs[vf].macaddr, mac); } @@ -2247,6 +2330,7 @@ static int ionic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) static int ionic_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, __be16 proto) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_VLAN }; struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; int ret; @@ -2269,8 +2353,11 @@ static int ionic_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ret = ionic_set_vf_config(ionic, vf, - IONIC_VF_ATTR_VLAN, (u8 *)&vlan); + vfc.vlanid = cpu_to_le16(vlan); + dev_dbg(ionic->dev, "%s: vf %d vlan %d\n", + __func__, vf, le16_to_cpu(vfc.vlanid)); + + ret = ionic_set_vf_config(ionic, vf, &vfc); if (!ret) ionic->vfs[vf].vlanid = cpu_to_le16(vlan); } @@ -2282,6 +2369,7 @@ static int ionic_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, static int ionic_set_vf_rate(struct net_device *netdev, int vf, int tx_min, int tx_max) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_RATE }; struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; int ret; @@ -2298,8 +2386,11 @@ static int ionic_set_vf_rate(struct net_device *netdev, int vf, if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ret = ionic_set_vf_config(ionic, vf, - IONIC_VF_ATTR_RATE, (u8 *)&tx_max); + vfc.maxrate = cpu_to_le32(tx_max); + dev_dbg(ionic->dev, "%s: vf %d maxrate %d\n", + __func__, vf, le32_to_cpu(vfc.maxrate)); + + ret = ionic_set_vf_config(ionic, vf, &vfc); if (!ret) lif->ionic->vfs[vf].maxrate = cpu_to_le32(tx_max); } @@ -2310,9 +2401,9 @@ static int ionic_set_vf_rate(struct net_device *netdev, int vf, static int ionic_set_vf_spoofchk(struct net_device *netdev, int vf, bool set) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_SPOOFCHK }; struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; - u8 data = set; /* convert to u8 for config */ int ret; if (!netif_device_present(netdev)) @@ -2323,10 +2414,13 @@ static int ionic_set_vf_spoofchk(struct net_device *netdev, int vf, bool set) if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ret = ionic_set_vf_config(ionic, vf, - IONIC_VF_ATTR_SPOOFCHK, &data); + vfc.spoofchk = set; + dev_dbg(ionic->dev, "%s: vf %d spoof %d\n", + __func__, vf, vfc.spoofchk); + + ret = ionic_set_vf_config(ionic, vf, &vfc); if (!ret) - ionic->vfs[vf].spoofchk = data; + ionic->vfs[vf].spoofchk = set; } up_write(&ionic->vf_op_lock); @@ -2335,9 +2429,9 @@ static int ionic_set_vf_spoofchk(struct net_device *netdev, int vf, bool set) static int ionic_set_vf_trust(struct net_device *netdev, int vf, bool set) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_TRUST }; struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; - u8 data = set; /* convert to u8 for config */ int ret; if (!netif_device_present(netdev)) @@ -2348,10 +2442,13 @@ static int ionic_set_vf_trust(struct net_device *netdev, int vf, bool set) if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ret = ionic_set_vf_config(ionic, vf, - IONIC_VF_ATTR_TRUST, &data); + vfc.trust = set; + dev_dbg(ionic->dev, "%s: vf %d trust %d\n", + __func__, vf, vfc.trust); + + ret = ionic_set_vf_config(ionic, vf, &vfc); if (!ret) - ionic->vfs[vf].trusted = data; + ionic->vfs[vf].trusted = set; } up_write(&ionic->vf_op_lock); @@ -2360,20 +2457,21 @@ static int ionic_set_vf_trust(struct net_device *netdev, int vf, bool set) static int ionic_set_vf_link_state(struct net_device *netdev, int vf, int set) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_LINKSTATE }; struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; - u8 data; + u8 vfls; int ret; switch (set) { case IFLA_VF_LINK_STATE_ENABLE: - data = IONIC_VF_LINK_STATUS_UP; + vfls = IONIC_VF_LINK_STATUS_UP; break; case IFLA_VF_LINK_STATE_DISABLE: - data = IONIC_VF_LINK_STATUS_DOWN; + vfls = IONIC_VF_LINK_STATUS_DOWN; break; case IFLA_VF_LINK_STATE_AUTO: - data = IONIC_VF_LINK_STATUS_AUTO; + vfls = IONIC_VF_LINK_STATUS_AUTO; break; default: return -EINVAL; @@ -2387,8 +2485,11 @@ static int ionic_set_vf_link_state(struct net_device *netdev, int vf, int set) if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ret = ionic_set_vf_config(ionic, vf, - IONIC_VF_ATTR_LINKSTATE, &data); + vfc.linkstate = vfls; + dev_dbg(ionic->dev, "%s: vf %d linkstate %d\n", + __func__, vf, vfc.linkstate); + + ret = ionic_set_vf_config(ionic, vf, &vfc); if (!ret) ionic->vfs[vf].linkstate = set; } @@ -2835,6 +2936,7 @@ static void ionic_lif_handle_fw_down(struct ionic_lif *lif) mutex_unlock(&lif->queue_lock); + clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state); dev_info(ionic->dev, "FW Down: LIFs stopped\n"); } @@ -2934,8 +3036,6 @@ void ionic_lif_free(struct ionic_lif *lif) /* unmap doorbell page */ ionic_bus_unmap_dbpage(lif->ionic, lif->kern_dbpage); lif->kern_dbpage = NULL; - kfree(lif->dbid_inuse); - lif->dbid_inuse = NULL; mutex_destroy(&lif->config_lock); mutex_destroy(&lif->queue_lock); @@ -3135,22 +3235,12 @@ int ionic_lif_init(struct ionic_lif *lif) return -EINVAL; } - lif->dbid_inuse = bitmap_zalloc(lif->dbid_count, GFP_KERNEL); - if (!lif->dbid_inuse) { - dev_err(dev, "Failed alloc doorbell id bitmap, aborting\n"); - return -ENOMEM; - } - - /* first doorbell id reserved for kernel (dbid aka pid == zero) */ - set_bit(0, lif->dbid_inuse); lif->kern_pid = 0; - dbpage_num = ionic_db_page_num(lif, lif->kern_pid); lif->kern_dbpage = ionic_bus_map_dbpage(lif->ionic, dbpage_num); if (!lif->kern_dbpage) { dev_err(dev, "Cannot map dbpage, aborting\n"); - err = -ENOMEM; - goto err_out_free_dbid; + return -ENOMEM; } err = ionic_lif_adminq_init(lif); @@ -3186,15 +3276,13 @@ int ionic_lif_init(struct ionic_lif *lif) return 0; err_out_notifyq_deinit: + napi_disable(&lif->adminqcq->napi); ionic_lif_qcq_deinit(lif, lif->notifyqcq); err_out_adminq_deinit: ionic_lif_qcq_deinit(lif, lif->adminqcq); ionic_lif_reset(lif); ionic_bus_unmap_dbpage(lif->ionic, lif->kern_dbpage); lif->kern_dbpage = NULL; -err_out_free_dbid: - kfree(lif->dbid_inuse); - lif->dbid_inuse = NULL; return err; } @@ -3214,7 +3302,7 @@ static void ionic_lif_set_netdev_info(struct ionic_lif *lif) }, }; - strlcpy(ctx.cmd.lif_setattr.name, lif->netdev->name, + strscpy(ctx.cmd.lif_setattr.name, lif->netdev->name, sizeof(ctx.cmd.lif_setattr.name)); ionic_adminq_post_wait(lif, &ctx); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h index 9f7ab2f17f93..a53984bf3544 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h @@ -135,6 +135,7 @@ enum ionic_lif_state_flags { IONIC_LIF_F_LINK_CHECK_REQUESTED, IONIC_LIF_F_FILTER_SYNC_NEEDED, IONIC_LIF_F_FW_RESET, + IONIC_LIF_F_FW_STOPPING, IONIC_LIF_F_SPLIT_INTR, IONIC_LIF_F_BROKEN, IONIC_LIF_F_TX_DIM_INTR, @@ -213,7 +214,6 @@ struct ionic_lif { u32 rx_coalesce_hw; /* what the hw is using */ u32 tx_coalesce_usecs; /* what the user asked for */ u32 tx_coalesce_hw; /* what the hw is using */ - unsigned long *dbid_inuse; unsigned int dbid_count; struct ionic_phc *phc; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c index 875f4ec42efe..4029b4e021f8 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_main.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c @@ -188,6 +188,28 @@ static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode) } } +const char *ionic_vf_attr_to_str(enum ionic_vf_attr attr) +{ + switch (attr) { + case IONIC_VF_ATTR_SPOOFCHK: + return "IONIC_VF_ATTR_SPOOFCHK"; + case IONIC_VF_ATTR_TRUST: + return "IONIC_VF_ATTR_TRUST"; + case IONIC_VF_ATTR_LINKSTATE: + return "IONIC_VF_ATTR_LINKSTATE"; + case IONIC_VF_ATTR_MAC: + return "IONIC_VF_ATTR_MAC"; + case IONIC_VF_ATTR_VLAN: + return "IONIC_VF_ATTR_VLAN"; + case IONIC_VF_ATTR_RATE: + return "IONIC_VF_ATTR_RATE"; + case IONIC_VF_ATTR_STATSADDR: + return "IONIC_VF_ATTR_STATSADDR"; + default: + return "IONIC_VF_ATTR_UNKNOWN"; + } +} + static void ionic_adminq_flush(struct ionic_lif *lif) { struct ionic_desc_info *desc_info; @@ -215,9 +237,13 @@ static void ionic_adminq_flush(struct ionic_lif *lif) void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode, u8 status, int err) { + const char *stat_str; + + stat_str = (err == -ETIMEDOUT) ? "TIMEOUT" : + ionic_error_to_str(status); + netdev_err(lif->netdev, "%s (%d) failed: %s (%d)\n", - ionic_opcode_to_str(opcode), opcode, - ionic_error_to_str(status), err); + ionic_opcode_to_str(opcode), opcode, stat_str, err); } static int ionic_adminq_check_err(struct ionic_lif *lif, @@ -318,6 +344,7 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, if (do_msg && !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) netdev_err(netdev, "Posting of %s (%d) failed: %d\n", name, ctx->cmd.cmd.opcode, err); + ctx->comp.comp.status = IONIC_RC_ERROR; return err; } @@ -331,11 +358,15 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, if (remaining) break; - /* interrupt the wait if FW stopped */ - if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { + /* force a check of FW status and break out if FW reset */ + (void)ionic_heartbeat_check(lif->ionic); + if ((test_bit(IONIC_LIF_F_FW_RESET, lif->state) && + !lif->ionic->idev.fw_status_ready) || + test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { if (do_msg) - netdev_err(netdev, "%s (%d) interrupted, FW in reset\n", - name, ctx->cmd.cmd.opcode); + netdev_warn(netdev, "%s (%d) interrupted, FW in reset\n", + name, ctx->cmd.cmd.opcode); + ctx->comp.comp.status = IONIC_RC_ERROR; return -ENXIO; } @@ -370,21 +401,34 @@ int ionic_adminq_post_wait_nomsg(struct ionic_lif *lif, struct ionic_admin_ctx * static void ionic_dev_cmd_clean(struct ionic *ionic) { - union __iomem ionic_dev_cmd_regs *regs = ionic->idev.dev_cmd_regs; + struct ionic_dev *idev = &ionic->idev; - iowrite32(0, ®s->doorbell); - memset_io(®s->cmd, 0, sizeof(regs->cmd)); + iowrite32(0, &idev->dev_cmd_regs->doorbell); + memset_io(&idev->dev_cmd_regs->cmd, 0, sizeof(idev->dev_cmd_regs->cmd)); } -int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds) +void ionic_dev_cmd_dev_err_print(struct ionic *ionic, u8 opcode, u8 status, + int err) +{ + const char *stat_str; + + stat_str = (err == -ETIMEDOUT) ? "TIMEOUT" : + ionic_error_to_str(status); + + dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) failed\n", + ionic_opcode_to_str(opcode), opcode, stat_str, err); +} + +static int __ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds, + const bool do_msg) { struct ionic_dev *idev = &ionic->idev; unsigned long start_time; unsigned long max_wait; unsigned long duration; + int done = 0; + bool fw_up; int opcode; - int hb = 0; - int done; int err; /* Wait for dev cmd to complete, retrying if we get EAGAIN, @@ -394,31 +438,24 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds) try_again: opcode = readb(&idev->dev_cmd_regs->cmd.cmd.opcode); start_time = jiffies; - do { + for (fw_up = ionic_is_fw_running(idev); + !done && fw_up && time_before(jiffies, max_wait); + fw_up = ionic_is_fw_running(idev)) { done = ionic_dev_cmd_done(idev); if (done) break; usleep_range(100, 200); - - /* Don't check the heartbeat on FW_CONTROL commands as they are - * notorious for interrupting the firmware's heartbeat update. - */ - if (opcode != IONIC_CMD_FW_CONTROL) - hb = ionic_heartbeat_check(ionic); - } while (!done && !hb && time_before(jiffies, max_wait)); + } duration = jiffies - start_time; dev_dbg(ionic->dev, "DEVCMD %s (%d) done=%d took %ld secs (%ld jiffies)\n", ionic_opcode_to_str(opcode), opcode, done, duration / HZ, duration); - if (!done && hb) { - /* It is possible (but unlikely) that FW was busy and missed a - * heartbeat check but is still alive and will process this - * request, so don't clean the dev_cmd in this case. - */ - dev_dbg(ionic->dev, "DEVCMD %s (%d) failed - FW halted\n", - ionic_opcode_to_str(opcode), opcode); + if (!done && !fw_up) { + ionic_dev_cmd_clean(ionic); + dev_warn(ionic->dev, "DEVCMD %s (%d) interrupted - FW is down\n", + ionic_opcode_to_str(opcode), opcode); return -ENXIO; } @@ -444,9 +481,9 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds) } if (!(opcode == IONIC_CMD_FW_CONTROL && err == IONIC_RC_EAGAIN)) - dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) failed\n", - ionic_opcode_to_str(opcode), opcode, - ionic_error_to_str(err), err); + if (do_msg) + ionic_dev_cmd_dev_err_print(ionic, opcode, err, + ionic_error_to_errno(err)); return ionic_error_to_errno(err); } @@ -454,6 +491,16 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds) return 0; } +int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds) +{ + return __ionic_dev_cmd_wait(ionic, max_seconds, true); +} + +int ionic_dev_cmd_wait_nomsg(struct ionic *ionic, unsigned long max_seconds) +{ + return __ionic_dev_cmd_wait(ionic, max_seconds, false); +} + int ionic_setup(struct ionic *ionic) { int err; @@ -540,6 +587,9 @@ int ionic_reset(struct ionic *ionic) struct ionic_dev *idev = &ionic->idev; int err; + if (!ionic_is_fw_running(idev)) + return 0; + mutex_lock(&ionic->dev_cmd_lock); ionic_dev_cmd_reset(idev); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); @@ -612,15 +662,17 @@ int ionic_port_init(struct ionic *ionic) int ionic_port_reset(struct ionic *ionic) { struct ionic_dev *idev = &ionic->idev; - int err; + int err = 0; if (!idev->port_info) return 0; - mutex_lock(&ionic->dev_cmd_lock); - ionic_dev_cmd_port_reset(idev); - err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); - mutex_unlock(&ionic->dev_cmd_lock); + if (ionic_is_fw_running(idev)) { + mutex_lock(&ionic->dev_cmd_lock); + ionic_dev_cmd_port_reset(idev); + err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); + mutex_unlock(&ionic->dev_cmd_lock); + } dma_free_coherent(ionic->dev, idev->port_info_sz, idev->port_info, idev->port_info_pa); @@ -628,9 +680,6 @@ int ionic_port_reset(struct ionic *ionic) idev->port_info = NULL; idev->port_info_pa = 0; - if (err) - dev_err(ionic->dev, "Failed to reset port\n"); - return err; } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c index f6e785f949f9..b7363376dfc8 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c @@ -376,10 +376,24 @@ static int ionic_lif_filter_add(struct ionic_lif *lif, spin_unlock_bh(&lif->rx_filters.lock); - if (err == -ENOSPC) { - if (le16_to_cpu(ctx.cmd.rx_filter_add.match) == IONIC_RX_FILTER_MATCH_VLAN) - lif->max_vlans = lif->nvlans; + /* store the max_vlans limit that we found */ + if (err == -ENOSPC && + le16_to_cpu(ctx.cmd.rx_filter_add.match) == IONIC_RX_FILTER_MATCH_VLAN) + lif->max_vlans = lif->nvlans; + + /* Prevent unnecessary error messages on recoverable + * errors as the filter will get retried on the next + * sync attempt. + */ + switch (err) { + case -ENOSPC: + case -ENXIO: + case -ETIMEDOUT: + case -EAGAIN: + case -EBUSY: return 0; + default: + break; } ionic_adminq_netdev_err_print(lif, ctx.cmd.cmd.opcode, @@ -494,9 +508,22 @@ static int ionic_lif_filter_del(struct ionic_lif *lif, spin_unlock_bh(&lif->rx_filters.lock); if (state != IONIC_FILTER_STATE_NEW) { - err = ionic_adminq_post_wait(lif, &ctx); - if (err && err != -EEXIST) + err = ionic_adminq_post_wait_nomsg(lif, &ctx); + + switch (err) { + /* ignore these errors */ + case -EEXIST: + case -ENXIO: + case -ETIMEDOUT: + case -EAGAIN: + case -EBUSY: + case 0: + break; + default: + ionic_adminq_netdev_err_print(lif, ctx.cmd.cmd.opcode, + ctx.comp.comp.status, err); return err; + } } return 0; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.c b/drivers/net/ethernet/pensando/ionic/ionic_stats.c index fd6806b4a1b9..9859a4432985 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_stats.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.c @@ -151,7 +151,6 @@ static const struct ionic_stat_desc ionic_rx_stats_desc[] = { IONIC_RX_STAT_DESC(vlan_stripped), }; - #define IONIC_NUM_LIF_STATS ARRAY_SIZE(ionic_lif_stats_desc) #define IONIC_NUM_PORT_STATS ARRAY_SIZE(ionic_port_stats_desc) #define IONIC_NUM_TX_STATS ARRAY_SIZE(ionic_tx_stats_desc) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index 94384f5d2a22..f54035455ad6 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -10,7 +10,6 @@ #include "ionic_lif.h" #include "ionic_txrx.h" - static inline void ionic_txq_post(struct ionic_queue *q, bool ring_dbell, ionic_desc_cb cb_func, void *cb_arg) { @@ -669,27 +668,37 @@ static int ionic_tx_map_skb(struct ionic_queue *q, struct sk_buff *skb, return -EIO; } +static void ionic_tx_desc_unmap_bufs(struct ionic_queue *q, + struct ionic_desc_info *desc_info) +{ + struct ionic_buf_info *buf_info = desc_info->bufs; + struct device *dev = q->dev; + unsigned int i; + + if (!desc_info->nbufs) + return; + + dma_unmap_single(dev, (dma_addr_t)buf_info->dma_addr, + buf_info->len, DMA_TO_DEVICE); + buf_info++; + for (i = 1; i < desc_info->nbufs; i++, buf_info++) + dma_unmap_page(dev, (dma_addr_t)buf_info->dma_addr, + buf_info->len, DMA_TO_DEVICE); + + desc_info->nbufs = 0; +} + static void ionic_tx_clean(struct ionic_queue *q, struct ionic_desc_info *desc_info, struct ionic_cq_info *cq_info, void *cb_arg) { - struct ionic_buf_info *buf_info = desc_info->bufs; struct ionic_tx_stats *stats = q_to_tx_stats(q); struct ionic_qcq *qcq = q_to_qcq(q); struct sk_buff *skb = cb_arg; - struct device *dev = q->dev; - unsigned int i; u16 qi; - if (desc_info->nbufs) { - dma_unmap_single(dev, (dma_addr_t)buf_info->dma_addr, - buf_info->len, DMA_TO_DEVICE); - buf_info++; - for (i = 1; i < desc_info->nbufs; i++, buf_info++) - dma_unmap_page(dev, (dma_addr_t)buf_info->dma_addr, - buf_info->len, DMA_TO_DEVICE); - } + ionic_tx_desc_unmap_bufs(q, desc_info); if (!skb) return; @@ -931,8 +940,11 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) err = ionic_tx_tcp_inner_pseudo_csum(skb); else err = ionic_tx_tcp_pseudo_csum(skb); - if (err) + if (err) { + /* clean up mapping from ionic_tx_map_skb */ + ionic_tx_desc_unmap_bufs(q, desc_info); return err; + } if (encap) hdrlen = skb_inner_transport_header(skb) - skb->data + @@ -1003,8 +1015,8 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) return 0; } -static int ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb, - struct ionic_desc_info *desc_info) +static void ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb, + struct ionic_desc_info *desc_info) { struct ionic_txq_desc *desc = desc_info->txq_desc; struct ionic_buf_info *buf_info = desc_info->bufs; @@ -1038,12 +1050,10 @@ static int ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb, stats->crc32_csum++; else stats->csum++; - - return 0; } -static int ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb, - struct ionic_desc_info *desc_info) +static void ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb, + struct ionic_desc_info *desc_info) { struct ionic_txq_desc *desc = desc_info->txq_desc; struct ionic_buf_info *buf_info = desc_info->bufs; @@ -1074,12 +1084,10 @@ static int ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb, desc->csum_offset = 0; stats->csum_none++; - - return 0; } -static int ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb, - struct ionic_desc_info *desc_info) +static void ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb, + struct ionic_desc_info *desc_info) { struct ionic_txq_sg_desc *sg_desc = desc_info->txq_sg_desc; struct ionic_buf_info *buf_info = &desc_info->bufs[1]; @@ -1093,31 +1101,24 @@ static int ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb, } stats->frags += skb_shinfo(skb)->nr_frags; - - return 0; } static int ionic_tx(struct ionic_queue *q, struct sk_buff *skb) { struct ionic_desc_info *desc_info = &q->info[q->head_idx]; struct ionic_tx_stats *stats = q_to_tx_stats(q); - int err; if (unlikely(ionic_tx_map_skb(q, skb, desc_info))) return -EIO; /* set up the initial descriptor */ if (skb->ip_summed == CHECKSUM_PARTIAL) - err = ionic_tx_calc_csum(q, skb, desc_info); + ionic_tx_calc_csum(q, skb, desc_info); else - err = ionic_tx_calc_no_csum(q, skb, desc_info); - if (err) - return err; + ionic_tx_calc_no_csum(q, skb, desc_info); /* add frags */ - err = ionic_tx_skb_frags(q, skb, desc_info); - if (err) - return err; + ionic_tx_skb_frags(q, skb, desc_info); skb_tx_timestamp(skb); stats->pkts++; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index cc4ec2bb36db..672480c9d195 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -3098,6 +3098,9 @@ int qed_hw_init(struct qed_dev *cdev, struct qed_hw_init_params *p_params) continue; } + /* Some flows may keep variable set */ + p_hwfn->mcp_info->mcp_handling_status = 0; + rc = qed_calc_hw_mode(p_hwfn); if (rc) return rc; diff --git a/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c b/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c index 0ce37f2460a4..407029a36fa1 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c +++ b/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c @@ -1835,8 +1835,6 @@ struct phys_mem_desc *qed_fw_overlay_mem_alloc(struct qed_hwfn *p_hwfn, if (!allocated_mem) return NULL; - memset(allocated_mem, 0, NUM_STORMS * sizeof(struct phys_mem_desc)); - /* For each Storm, set physical address in RAM */ while (buf_offset < buf_size) { struct phys_mem_desc *storm_mem_desc; diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index da1eadabcb41..9fb1fa479d4b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -140,7 +140,7 @@ static struct qed_mcp_cmd_elem *qed_mcp_cmd_get_elem(struct qed_hwfn *p_hwfn, int qed_mcp_free(struct qed_hwfn *p_hwfn) { if (p_hwfn->mcp_info) { - struct qed_mcp_cmd_elem *p_cmd_elem, *p_tmp; + struct qed_mcp_cmd_elem *p_cmd_elem = NULL, *p_tmp; kfree(p_hwfn->mcp_info->mfw_mb_cur); kfree(p_hwfn->mcp_info->mfw_mb_shadow); @@ -249,6 +249,7 @@ int qed_mcp_cmd_init(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) /* Initialize the MFW spinlock */ spin_lock_init(&p_info->cmd_lock); spin_lock_init(&p_info->link_lock); + spin_lock_init(&p_info->unload_lock); INIT_LIST_HEAD(&p_info->cmd_list); @@ -614,12 +615,13 @@ static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, usecs); } -int qed_mcp_cmd(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - u32 cmd, - u32 param, - u32 *o_mcp_resp, - u32 *o_mcp_param) +static int _qed_mcp_cmd(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 cmd, + u32 param, + u32 *o_mcp_resp, + u32 *o_mcp_param, + bool can_sleep) { struct qed_mcp_mb_params mb_params; int rc; @@ -627,6 +629,7 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn, memset(&mb_params, 0, sizeof(mb_params)); mb_params.cmd = cmd; mb_params.param = param; + mb_params.flags = can_sleep ? QED_MB_FLAG_CAN_SLEEP : 0; rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); if (rc) @@ -638,6 +641,28 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn, return 0; } +int qed_mcp_cmd(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 cmd, + u32 param, + u32 *o_mcp_resp, + u32 *o_mcp_param) +{ + return (_qed_mcp_cmd(p_hwfn, p_ptt, cmd, param, + o_mcp_resp, o_mcp_param, true)); +} + +int qed_mcp_cmd_nosleep(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 cmd, + u32 param, + u32 *o_mcp_resp, + u32 *o_mcp_param) +{ + return (_qed_mcp_cmd(p_hwfn, p_ptt, cmd, param, + o_mcp_resp, o_mcp_param, false)); +} + static int qed_mcp_nvm_wr_cmd(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, @@ -1071,10 +1096,15 @@ int qed_mcp_load_done(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) return 0; } +#define MFW_COMPLETION_MAX_ITER 5000 +#define MFW_COMPLETION_INTERVAL_MS 1 + int qed_mcp_unload_req(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { struct qed_mcp_mb_params mb_params; + u32 cnt = MFW_COMPLETION_MAX_ITER; u32 wol_param; + int rc; switch (p_hwfn->cdev->wol_config) { case QED_OV_WOL_DISABLED: @@ -1097,7 +1127,23 @@ int qed_mcp_unload_req(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) mb_params.param = wol_param; mb_params.flags = QED_MB_FLAG_CAN_SLEEP | QED_MB_FLAG_AVOID_BLOCK; - return qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); + spin_lock_bh(&p_hwfn->mcp_info->unload_lock); + set_bit(QED_MCP_BYPASS_PROC_BIT, + &p_hwfn->mcp_info->mcp_handling_status); + spin_unlock_bh(&p_hwfn->mcp_info->unload_lock); + + rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); + + while (test_bit(QED_MCP_IN_PROCESSING_BIT, + &p_hwfn->mcp_info->mcp_handling_status) && --cnt) + msleep(MFW_COMPLETION_INTERVAL_MS); + + if (!cnt) + DP_NOTICE(p_hwfn, + "Failed to wait MFW event completion after %d msec\n", + MFW_COMPLETION_MAX_ITER * MFW_COMPLETION_INTERVAL_MS); + + return rc; } int qed_mcp_unload_done(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) @@ -1728,8 +1774,8 @@ static void qed_mcp_update_bw(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) qed_configure_pf_max_bandwidth(p_hwfn->cdev, p_info->bandwidth_max); /* Acknowledge the MFW */ - qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_BW_UPDATE_ACK, 0, &resp, - ¶m); + qed_mcp_cmd_nosleep(p_hwfn, p_ptt, DRV_MSG_CODE_BW_UPDATE_ACK, 0, &resp, + ¶m); } static void qed_mcp_update_stag(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) @@ -1766,8 +1812,8 @@ static void qed_mcp_update_stag(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) p_hwfn->mcp_info->func_info.ovlan, p_hwfn->hw_info.hw_mode); /* Acknowledge the MFW */ - qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_S_TAG_UPDATE_ACK, 0, - &resp, ¶m); + qed_mcp_cmd_nosleep(p_hwfn, p_ptt, DRV_MSG_CODE_S_TAG_UPDATE_ACK, 0, + &resp, ¶m); } static void qed_mcp_handle_fan_failure(struct qed_hwfn *p_hwfn, @@ -1997,6 +2043,19 @@ int qed_mcp_handle_events(struct qed_hwfn *p_hwfn, "Msg [%d] - old CMD 0x%02x, new CMD 0x%02x\n", i, info->mfw_mb_shadow[i], info->mfw_mb_cur[i]); + spin_lock_bh(&p_hwfn->mcp_info->unload_lock); + if (test_bit(QED_MCP_BYPASS_PROC_BIT, + &p_hwfn->mcp_info->mcp_handling_status)) { + spin_unlock_bh(&p_hwfn->mcp_info->unload_lock); + DP_INFO(p_hwfn, + "Msg [%d] is bypassed on unload flow\n", i); + continue; + } + + set_bit(QED_MCP_IN_PROCESSING_BIT, + &p_hwfn->mcp_info->mcp_handling_status); + spin_unlock_bh(&p_hwfn->mcp_info->unload_lock); + switch (i) { case MFW_DRV_MSG_LINK_CHANGE: qed_mcp_handle_link_change(p_hwfn, p_ptt, false); @@ -2050,6 +2109,9 @@ int qed_mcp_handle_events(struct qed_hwfn *p_hwfn, DP_INFO(p_hwfn, "Unimplemented MFW message %d\n", i); rc = -EINVAL; } + + clear_bit(QED_MCP_IN_PROCESSING_BIT, + &p_hwfn->mcp_info->mcp_handling_status); } /* ACK everything */ @@ -3675,8 +3737,8 @@ static int qed_mcp_resource_cmd(struct qed_hwfn *p_hwfn, { int rc; - rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_RESOURCE_CMD, param, - p_mcp_resp, p_mcp_param); + rc = qed_mcp_cmd_nosleep(p_hwfn, p_ptt, DRV_MSG_CODE_RESOURCE_CMD, + param, p_mcp_resp, p_mcp_param); if (rc) return rc; diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index 369e1892450a..9bd0565fe8ab 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -393,11 +393,12 @@ int qed_mcp_get_board_config(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 *p_board_config); /** - * qed_mcp_cmd(): General function for sending commands to the MCP + * qed_mcp_cmd(): Sleepable function for sending commands to the MCP * mailbox. It acquire mutex lock for the entire * operation, from sending the request until the MCP * response. Waiting for MCP response will be checked up - * to 5 seconds every 5ms. + * to 5 seconds every 10ms. Should not be called from atomic + * context. * * @p_hwfn: HW device data. * @p_ptt: PTT required for register access. @@ -416,6 +417,31 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn, u32 *o_mcp_resp, u32 *o_mcp_param); +/** + * qed_mcp_cmd_nosleep(): Function for sending commands to the MCP + * mailbox. It acquire mutex lock for the entire + * operation, from sending the request until the MCP + * response. Waiting for MCP response will be checked up + * to 5 seconds every 10us. Should be called when sleep + * is not allowed. + * + * @p_hwfn: HW device data. + * @p_ptt: PTT required for register access. + * @cmd: command to be sent to the MCP. + * @param: Optional param + * @o_mcp_resp: The MCP response code (exclude sequence). + * @o_mcp_param: Optional parameter provided by the MCP + * response + * + * Return: Int - 0 - Operation was successul. + */ +int qed_mcp_cmd_nosleep(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 cmd, + u32 param, + u32 *o_mcp_resp, + u32 *o_mcp_param); + /** * qed_mcp_drain(): drains the nig, allowing completion to pass in * case of pauses. @@ -762,6 +788,14 @@ struct qed_mcp_info { /* S/N for debug data mailbox commands */ atomic_t dbg_data_seq; + + /* Spinlock used to sync the flag mcp_handling_status with + * the mfw events handler + */ + spinlock_t unload_lock; + unsigned long mcp_handling_status; +#define QED_MCP_BYPASS_PROC_BIT 0 +#define QED_MCP_IN_PROCESSING_BIT 1 }; struct qed_mcp_mb_params { diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 48cf4355bc47..0848b5529d48 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -2984,12 +2984,16 @@ static int qed_iov_pre_update_vport(struct qed_hwfn *hwfn, u8 mask = QED_ACCEPT_UCAST_UNMATCHED | QED_ACCEPT_MCAST_UNMATCHED; struct qed_filter_accept_flags *flags = ¶ms->accept_flags; struct qed_public_vf_info *vf_info; + u16 tlv_mask; + + tlv_mask = BIT(QED_IOV_VP_UPDATE_ACCEPT_PARAM) | + BIT(QED_IOV_VP_UPDATE_ACCEPT_ANY_VLAN); /* Untrusted VFs can't even be trusted to know that fact. * Simply indicate everything is configured fine, and trace * configuration 'behind their back'. */ - if (!(*tlvs & BIT(QED_IOV_VP_UPDATE_ACCEPT_PARAM))) + if (!(*tlvs & tlv_mask)) return 0; vf_info = qed_iov_get_public_vf_info(hwfn, vfid, true); @@ -3006,6 +3010,13 @@ static int qed_iov_pre_update_vport(struct qed_hwfn *hwfn, flags->tx_accept_filter &= ~mask; } + if (params->update_accept_any_vlan_flg) { + vf_info->accept_any_vlan = params->accept_any_vlan; + + if (vf_info->forced_vlan && !vf_info->is_trusted_configured) + params->accept_any_vlan = false; + } + return 0; } @@ -4719,6 +4730,7 @@ static int qed_get_vf_config(struct qed_dev *cdev, tx_rate = vf_info->tx_rate; ivi->max_tx_rate = tx_rate ? tx_rate : link.speed; ivi->min_tx_rate = qed_iov_get_vf_min_rate(hwfn, vf_id); + ivi->trusted = vf_info->is_trusted_request; return 0; } @@ -5149,6 +5161,12 @@ static void qed_iov_handle_trust_change(struct qed_hwfn *hwfn) params.update_ctl_frame_check = 1; params.mac_chk_en = !vf_info->is_trusted_configured; + params.update_accept_any_vlan_flg = 0; + + if (vf_info->accept_any_vlan && vf_info->forced_vlan) { + params.update_accept_any_vlan_flg = 1; + params.accept_any_vlan = vf_info->accept_any_vlan; + } if (vf_info->rx_accept_mode & mask) { flags->update_rx_mode_config = 1; @@ -5164,13 +5182,20 @@ static void qed_iov_handle_trust_change(struct qed_hwfn *hwfn) if (!vf_info->is_trusted_configured) { flags->rx_accept_filter &= ~mask; flags->tx_accept_filter &= ~mask; + params.accept_any_vlan = false; } if (flags->update_rx_mode_config || flags->update_tx_mode_config || - params.update_ctl_frame_check) + params.update_ctl_frame_check || + params.update_accept_any_vlan_flg) { + DP_VERBOSE(hwfn, QED_MSG_IOV, + "vport update config for %s VF[abs 0x%x rel 0x%x]\n", + vf_info->is_trusted_configured ? "trusted" : "untrusted", + vf->abs_vf_id, vf->relative_vf_id); qed_sp_vport_update(hwfn, ¶ms, QED_SPQ_MODE_EBLOCK, NULL); + } } } diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.h b/drivers/net/ethernet/qlogic/qed/qed_sriov.h index f448e3dd6c8b..6ee2493de164 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.h +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.h @@ -62,6 +62,7 @@ struct qed_public_vf_info { bool is_trusted_request; u8 rx_accept_mode; u8 tx_accept_mode; + bool accept_any_vlan; }; struct qed_iov_vf_init_params { diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index e10fe071a40f..54a2d653be63 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -1355,7 +1355,7 @@ static void qlcnic_get_ethtool_stats(struct net_device *dev, memset(data, 0, stats->n_stats * sizeof(u64)); - for (ring = 0, index = 0; ring < adapter->drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { if (adapter->is_up == QLCNIC_ADAPTER_UP_MAGIC) { tx_ring = &adapter->tx_ring[ring]; data = qlcnic_fill_tx_queue_stats(data, tx_ring); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c index 29cdcb2285b1..bcf3746220df 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "qlcnic.h" @@ -332,7 +333,7 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter, hlist_for_each_entry_safe(tmp_fil, n, head, fnode) { if (ether_addr_equal(tmp_fil->faddr, (u8 *)&src_addr) && tmp_fil->vlan_id == vlan_id) { - if (jiffies > (QLCNIC_READD_AGE * HZ + tmp_fil->ftime)) + if (time_is_before_jiffies(QLCNIC_READD_AGE * HZ + tmp_fil->ftime)) qlcnic_change_filter(adapter, &src_addr, vlan_id, tx_ring); tmp_fil->ftime = jiffies; diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 3c5494afd3c0..c865a4be05ee 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -435,7 +435,7 @@ qcaspi_receive(struct qcaspi *qca) qca->rx_skb->protocol = eth_type_trans( qca->rx_skb, qca->rx_skb->dev); skb_checksum_none_assert(qca->rx_skb); - netif_rx_ni(qca->rx_skb); + netif_rx(qca->rx_skb); qca->rx_skb = netdev_alloc_skb_ip_align(net_dev, net_dev->mtu + VLAN_ETH_HLEN); if (!qca->rx_skb) { diff --git a/drivers/net/ethernet/qualcomm/qca_uart.c b/drivers/net/ethernet/qualcomm/qca_uart.c index 27c4f43176aa..26646cb6a20a 100644 --- a/drivers/net/ethernet/qualcomm/qca_uart.c +++ b/drivers/net/ethernet/qualcomm/qca_uart.c @@ -108,7 +108,7 @@ qca_tty_receive(struct serdev_device *serdev, const unsigned char *data, qca->rx_skb->protocol = eth_type_trans( qca->rx_skb, qca->rx_skb->dev); skb_checksum_none_assert(qca->rx_skb); - netif_rx_ni(qca->rx_skb); + netif_rx(qca->rx_skb); qca->rx_skb = netdev_alloc_skb_ip_align(netdev, netdev->mtu + VLAN_ETH_HLEN); diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c index bfbd7847f946..a313242a762e 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c @@ -207,7 +207,7 @@ rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) dev = skb->dev; port = rmnet_get_port_rcu(dev); if (unlikely(!port)) { - atomic_long_inc(&skb->dev->rx_nohandler); + dev_core_stats_rx_nohandler_inc(skb->dev); kfree_skb(skb); goto done; } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c index 3676976c875b..ba194698cc14 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c @@ -298,7 +298,6 @@ struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, { struct rmnet_map_header *map_header; u32 padding, map_datalen; - u8 *padbytes; map_datalen = skb->len - hdrlen; map_header = (struct rmnet_map_header *) @@ -323,8 +322,7 @@ struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, if (skb_tailroom(skb) < padding) return NULL; - padbytes = (u8 *)skb_put(skb, padding); - memset(padbytes, 0, padding); + skb_put_zero(skb, padding); done: map_header->pkt_len = htons(map_datalen + padding); diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 19e2621e0645..33f5c5698ccb 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -1397,8 +1397,11 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) rtl_lock_config_regs(tp); device_set_wakeup_enable(tp_to_dev(tp), wolopts); - rtl_set_d3_pll_down(tp, !wolopts); - tp->dev->wol_enabled = wolopts ? 1 : 0; + + if (tp->dash_type == RTL_DASH_NONE) { + rtl_set_d3_pll_down(tp, !wolopts); + tp->dev->wol_enabled = wolopts ? 1 : 0; + } } static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) @@ -2667,10 +2670,7 @@ static void rtl_enable_exit_l1(struct rtl8169_private *tp) case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_38: rtl_eri_set_bits(tp, 0xd4, 0x0c00); break; - case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_53: - rtl_eri_set_bits(tp, 0xd4, 0x1f80); - break; - case RTL_GIGA_MAC_VER_60 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63: r8168_mac_ocp_modify(tp, 0xc0ac, 0, 0x1f80); break; default: @@ -2678,13 +2678,48 @@ static void rtl_enable_exit_l1(struct rtl8169_private *tp) } } +static void rtl_disable_exit_l1(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38: + rtl_eri_clear_bits(tp, 0xd4, 0x1f00); + break; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63: + r8168_mac_ocp_modify(tp, 0xc0ac, 0x1f80, 0); + break; + default: + break; + } +} + static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) { /* Don't enable ASPM in the chip if OS can't control ASPM */ if (enable && tp->aspm_manageable) { RTL_W8(tp, Config5, RTL_R8(tp, Config5) | ASPM_en); RTL_W8(tp, Config2, RTL_R8(tp, Config2) | ClkReqEn); + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_45 ... RTL_GIGA_MAC_VER_48: + case RTL_GIGA_MAC_VER_60 ... RTL_GIGA_MAC_VER_63: + /* reset ephy tx/rx disable timer */ + r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0); + /* chip can trigger L1.2 */ + r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, BIT(2)); + break; + default: + break; + } } else { + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_45 ... RTL_GIGA_MAC_VER_48: + case RTL_GIGA_MAC_VER_60 ... RTL_GIGA_MAC_VER_63: + r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0); + break; + default: + break; + } + RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn); RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en); } @@ -4683,7 +4718,7 @@ static void rtl8169_down(struct rtl8169_private *tp) rtl_pci_commit(tp); rtl8169_cleanup(tp, true); - + rtl_disable_exit_l1(tp); rtl_prepare_power_down(tp); } @@ -4843,8 +4878,6 @@ static void rtl8169_net_suspend(struct rtl8169_private *tp) rtl8169_down(tp); } -#ifdef CONFIG_PM - static int rtl8169_runtime_resume(struct device *dev) { struct rtl8169_private *tp = dev_get_drvdata(dev); @@ -4860,7 +4893,7 @@ static int rtl8169_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused rtl8169_suspend(struct device *device) +static int rtl8169_suspend(struct device *device) { struct rtl8169_private *tp = dev_get_drvdata(device); @@ -4873,7 +4906,7 @@ static int __maybe_unused rtl8169_suspend(struct device *device) return 0; } -static int __maybe_unused rtl8169_resume(struct device *device) +static int rtl8169_resume(struct device *device) { struct rtl8169_private *tp = dev_get_drvdata(device); @@ -4908,6 +4941,9 @@ static int rtl8169_runtime_idle(struct device *device) { struct rtl8169_private *tp = dev_get_drvdata(device); + if (tp->dash_type != RTL_DASH_NONE) + return -EBUSY; + if (!netif_running(tp->dev) || !netif_carrier_ok(tp->dev)) pm_schedule_suspend(device, 10000); @@ -4915,13 +4951,11 @@ static int rtl8169_runtime_idle(struct device *device) } static const struct dev_pm_ops rtl8169_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(rtl8169_suspend, rtl8169_resume) - SET_RUNTIME_PM_OPS(rtl8169_runtime_suspend, rtl8169_runtime_resume, - rtl8169_runtime_idle) + SYSTEM_SLEEP_PM_OPS(rtl8169_suspend, rtl8169_resume) + RUNTIME_PM_OPS(rtl8169_runtime_suspend, rtl8169_runtime_resume, + rtl8169_runtime_idle) }; -#endif /* CONFIG_PM */ - static void rtl_wol_shutdown_quirk(struct rtl8169_private *tp) { /* WoL fails with 8168b when the receiver is disabled. */ @@ -4950,7 +4984,8 @@ static void rtl_shutdown(struct pci_dev *pdev) /* Restore original MAC address */ rtl_rar_set(tp, tp->dev->perm_addr); - if (system_state == SYSTEM_POWER_OFF) { + if (system_state == SYSTEM_POWER_OFF && + tp->dash_type == RTL_DASH_NONE) { if (tp->saved_wolopts) rtl_wol_shutdown_quirk(tp); @@ -5255,6 +5290,16 @@ static void rtl_init_mac_address(struct rtl8169_private *tp) rtl_rar_set(tp, mac_addr); } +/* register is set if system vendor successfully tested ASPM 1.2 */ +static bool rtl_aspm_is_safe(struct rtl8169_private *tp) +{ + if (tp->mac_version >= RTL_GIGA_MAC_VER_60 && + r8168_mac_ocp_read(tp, 0xc0b2) & 0xf) + return true; + + return false; +} + static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct rtl8169_private *tp; @@ -5333,7 +5378,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) * Chips from RTL8168h partially have issues with L1.2, but seem * to work fine with L1 and L1.1. */ - if (tp->mac_version >= RTL_GIGA_MAC_VER_45) + if (rtl_aspm_is_safe(tp)) + rc = 0; + else if (tp->mac_version >= RTL_GIGA_MAC_VER_45) rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1_2); else rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1); @@ -5409,7 +5456,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* configure chip for default features */ rtl8169_set_features(dev, dev->features); - rtl_set_d3_pll_down(tp, true); + if (tp->dash_type == RTL_DASH_NONE) { + rtl_set_d3_pll_down(tp, true); + } else { + rtl_set_d3_pll_down(tp, false); + dev->wol_enabled = 1; + } jumbo_max = rtl_jumbo_max(tp); if (jumbo_max) @@ -5460,9 +5512,7 @@ static struct pci_driver rtl8169_pci_driver = { .probe = rtl_init_one, .remove = rtl_remove_one, .shutdown = rtl_shutdown, -#ifdef CONFIG_PM - .driver.pm = &rtl8169_pm_ops, -#endif + .driver.pm = pm_ptr(&rtl8169_pm_ops), }; module_pci_driver(rtl8169_pci_driver); diff --git a/drivers/net/ethernet/realtek/r8169_phy_config.c b/drivers/net/ethernet/realtek/r8169_phy_config.c index f7ad5487879b..15c295f90196 100644 --- a/drivers/net/ethernet/realtek/r8169_phy_config.c +++ b/drivers/net/ethernet/realtek/r8169_phy_config.c @@ -429,15 +429,6 @@ static const struct phy_reg rtl8168d_1_phy_reg_init_0[] = { { 0x0d, 0xf880 } }; -static const struct phy_reg rtl8168d_1_phy_reg_init_1[] = { - { 0x1f, 0x0002 }, - { 0x05, 0x669a }, - { 0x1f, 0x0005 }, - { 0x05, 0x8330 }, - { 0x06, 0x669a }, - { 0x1f, 0x0002 } -}; - static void rtl8168d_apply_firmware_cond(struct rtl8169_private *tp, struct phy_device *phydev, u16 val) @@ -455,6 +446,29 @@ static void rtl8168d_apply_firmware_cond(struct rtl8169_private *tp, r8169_apply_firmware(tp); } +static void rtl8168d_1_common(struct phy_device *phydev) +{ + u16 val; + + phy_write_paged(phydev, 0x0002, 0x05, 0x669a); + r8168d_phy_param(phydev, 0x8330, 0xffff, 0x669a); + phy_write(phydev, 0x1f, 0x0002); + + val = phy_read(phydev, 0x0d); + + if ((val & 0x00ff) != 0x006c) { + static const u16 set[] = { + 0x0065, 0x0066, 0x0067, 0x0068, + 0x0069, 0x006a, 0x006b, 0x006c + }; + int i; + + val &= 0xff00; + for (i = 0; i < ARRAY_SIZE(set); i++) + phy_write(phydev, 0x0d, val | set[i]); + } +} + static void rtl8168d_1_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev) { @@ -469,25 +483,7 @@ static void rtl8168d_1_hw_phy_config(struct rtl8169_private *tp, phy_modify(phydev, 0x0c, 0x5d00, 0xa200); if (rtl8168d_efuse_read(tp, 0x01) == 0xb1) { - int val; - - rtl_writephy_batch(phydev, rtl8168d_1_phy_reg_init_1); - - val = phy_read(phydev, 0x0d); - - if ((val & 0x00ff) != 0x006c) { - static const u32 set[] = { - 0x0065, 0x0066, 0x0067, 0x0068, - 0x0069, 0x006a, 0x006b, 0x006c - }; - int i; - - phy_write(phydev, 0x1f, 0x0002); - - val &= 0xff00; - for (i = 0; i < ARRAY_SIZE(set); i++) - phy_write(phydev, 0x0d, val | set[i]); - } + rtl8168d_1_common(phydev); } else { phy_write_paged(phydev, 0x0002, 0x05, 0x6662); r8168d_phy_param(phydev, 0x8330, 0xffff, 0x6662); @@ -513,24 +509,7 @@ static void rtl8168d_2_hw_phy_config(struct rtl8169_private *tp, rtl_writephy_batch(phydev, rtl8168d_1_phy_reg_init_0); if (rtl8168d_efuse_read(tp, 0x01) == 0xb1) { - int val; - - rtl_writephy_batch(phydev, rtl8168d_1_phy_reg_init_1); - - val = phy_read(phydev, 0x0d); - if ((val & 0x00ff) != 0x006c) { - static const u32 set[] = { - 0x0065, 0x0066, 0x0067, 0x0068, - 0x0069, 0x006a, 0x006b, 0x006c - }; - int i; - - phy_write(phydev, 0x1f, 0x0002); - - val &= 0xff00; - for (i = 0; i < ARRAY_SIZE(set); i++) - phy_write(phydev, 0x0d, val | set[i]); - } + rtl8168d_1_common(phydev); } else { phy_write_paged(phydev, 0x0002, 0x05, 0x2642); r8168d_phy_param(phydev, 0x8330, 0xffff, 0x2642); diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index b215cde68e10..525d66f71f02 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -475,7 +475,7 @@ static int ravb_ring_init(struct net_device *ndev, int q) goto error; for (i = 0; i < priv->num_rx_ring[q]; i++) { - skb = netdev_alloc_skb(ndev, info->max_rx_len); + skb = __netdev_alloc_skb(ndev, info->max_rx_len, GFP_KERNEL); if (!skb) goto error; ravb_set_buffer_align(skb); @@ -1432,11 +1432,7 @@ static int ravb_phy_init(struct net_device *ndev) * at this time. */ if (soc_device_match(r8a7795es10)) { - err = phy_set_max_speed(phydev, SPEED_100); - if (err) { - netdev_err(ndev, "failed to limit PHY to 100Mbit/s\n"); - goto err_phy_disconnect; - } + phy_set_max_speed(phydev, SPEED_100); netdev_info(ndev, "limited PHY to 100Mbit/s\n"); } @@ -1457,8 +1453,6 @@ static int ravb_phy_init(struct net_device *ndev) return 0; -err_phy_disconnect: - phy_disconnect(phydev); err_deregister_fixed_link: if (of_phy_is_fixed_link(np)) of_phy_deregister_fixed_link(np); @@ -2854,7 +2848,6 @@ static int ravb_wol_restore(struct net_device *ndev) { struct ravb_private *priv = netdev_priv(ndev); const struct ravb_hw_info *info = priv->info; - int ret; if (info->nc_queues) napi_enable(&priv->napi[RAVB_NC]); @@ -2863,9 +2856,7 @@ static int ravb_wol_restore(struct net_device *ndev) /* Disable MagicPacket */ ravb_modify(ndev, ECMR, ECMR_MPDE, 0); - ret = ravb_close(ndev); - if (ret < 0) - return ret; + ravb_close(ndev); return disable_irq_wake(priv->emac_irq); } diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index d947a628e166..67ade78fb767 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2026,14 +2026,8 @@ static int sh_eth_phy_init(struct net_device *ndev) } /* mask with MAC supported features */ - if (mdp->cd->register_type != SH_ETH_REG_GIGABIT) { - int err = phy_set_max_speed(phydev, SPEED_100); - if (err) { - netdev_err(ndev, "failed to limit PHY to 100 Mbit/s\n"); - phy_disconnect(phydev); - return err; - } - } + if (mdp->cd->register_type != SH_ETH_REG_GIGABIT) + phy_set_max_speed(phydev, SPEED_100); phy_attached_info(phydev); @@ -3450,9 +3444,7 @@ static int sh_eth_wol_restore(struct net_device *ndev) * both be reset and all registers restored. This is what * happens during suspend and resume without WoL enabled. */ - ret = sh_eth_close(ndev); - if (ret < 0) - return ret; + sh_eth_close(ndev); ret = sh_eth_open(ndev); if (ret < 0) return ret; @@ -3464,7 +3456,7 @@ static int sh_eth_suspend(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct sh_eth_private *mdp = netdev_priv(ndev); - int ret = 0; + int ret; if (!netif_running(ndev)) return 0; @@ -3483,7 +3475,7 @@ static int sh_eth_resume(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct sh_eth_private *mdp = netdev_priv(ndev); - int ret = 0; + int ret; if (!netif_running(ndev)) return 0; diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c index 2881f5b2b5f4..407a1f8e3059 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c @@ -127,7 +127,7 @@ bool sxgbe_eee_init(struct sxgbe_priv_data * const priv) /* MAC core supports the EEE feature. */ if (priv->hw_cap.eee) { /* Check if the PHY supports EEE */ - if (phy_init_eee(ndev->phydev, 1)) + if (phy_init_eee(ndev->phydev, true)) return false; priv->eee_active = 1; diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index cf366ed2557c..50d535981a35 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -3990,6 +3990,30 @@ static unsigned int ef10_check_caps(const struct efx_nic *efx, } } +static unsigned int efx_ef10_recycle_ring_size(const struct efx_nic *efx) +{ + unsigned int ret = EFX_RECYCLE_RING_SIZE_10G; + + /* There is no difference between PFs and VFs. The side is based on + * the maximum link speed of a given NIC. + */ + switch (efx->pci_dev->device & 0xfff) { + case 0x0903: /* Farmingdale can do up to 10G */ + break; + case 0x0923: /* Greenport can do up to 40G */ + case 0x0a03: /* Medford can do up to 40G */ + ret *= 4; + break; + default: /* Medford2 can do up to 100G */ + ret *= 10; + } + + if (IS_ENABLED(CONFIG_PPC64)) + ret *= 4; + + return ret; +} + #define EF10_OFFLOAD_FEATURES \ (NETIF_F_IP_CSUM | \ NETIF_F_HW_VLAN_CTAG_FILTER | \ @@ -4106,6 +4130,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = { .check_caps = ef10_check_caps, .print_additional_fwver = efx_ef10_print_additional_fwver, .sensor_event = efx_mcdi_sensor_event, + .rx_recycle_ring_size = efx_ef10_recycle_ring_size, }; const struct efx_nic_type efx_hunt_a0_nic_type = { @@ -4243,4 +4268,5 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .check_caps = ef10_check_caps, .print_additional_fwver = efx_ef10_print_additional_fwver, .sensor_event = efx_mcdi_sensor_event, + .rx_recycle_ring_size = efx_ef10_recycle_ring_size, }; diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c index f79b14a119ae..a07cbf45a326 100644 --- a/drivers/net/ethernet/sfc/ef100_nic.c +++ b/drivers/net/ethernet/sfc/ef100_nic.c @@ -23,6 +23,7 @@ #include "ef100_rx.h" #include "ef100_tx.h" #include "ef100_netdev.h" +#include "rx_common.h" #define EF100_MAX_VIS 4096 #define EF100_NUM_MCDI_BUFFERS 1 @@ -696,6 +697,12 @@ static unsigned int ef100_check_caps(const struct efx_nic *efx, } } +static unsigned int efx_ef100_recycle_ring_size(const struct efx_nic *efx) +{ + /* Maximum link speed for Riverhead is 100G */ + return 10 * EFX_RECYCLE_RING_SIZE_10G; +} + /* NIC level access functions */ #define EF100_OFFLOAD_FEATURES (NETIF_F_HW_CSUM | NETIF_F_RXCSUM | \ @@ -770,6 +777,7 @@ const struct efx_nic_type ef100_pf_nic_type = { .rx_push_rss_context_config = efx_mcdi_rx_push_rss_context_config, .rx_pull_rss_context_config = efx_mcdi_rx_pull_rss_context_config, .rx_restore_rss_contexts = efx_mcdi_rx_restore_rss_contexts, + .rx_recycle_ring_size = efx_ef100_recycle_ring_size, .reconfigure_mac = ef100_reconfigure_mac, .reconfigure_port = efx_mcdi_port_reconfigure, @@ -849,6 +857,7 @@ const struct efx_nic_type ef100_vf_nic_type = { .rx_pull_rss_config = efx_mcdi_rx_pull_rss_config, .rx_push_rss_config = efx_mcdi_pf_rx_push_rss_config, .rx_restore_rss_contexts = efx_mcdi_rx_restore_rss_contexts, + .rx_recycle_ring_size = efx_ef100_recycle_ring_size, .reconfigure_mac = ef100_reconfigure_mac, .test_nvram = efx_new_mcdi_nvram_test_all, diff --git a/drivers/net/ethernet/sfc/efx_channels.c b/drivers/net/ethernet/sfc/efx_channels.c index ead550ae2709..d6fdcdc530ca 100644 --- a/drivers/net/ethernet/sfc/efx_channels.c +++ b/drivers/net/ethernet/sfc/efx_channels.c @@ -78,31 +78,48 @@ static const struct efx_channel_type efx_default_channel_type = { * INTERRUPTS *************/ -static unsigned int efx_wanted_parallelism(struct efx_nic *efx) +static unsigned int count_online_cores(struct efx_nic *efx, bool local_node) { - cpumask_var_t thread_mask; + cpumask_var_t filter_mask; unsigned int count; int cpu; + if (unlikely(!zalloc_cpumask_var(&filter_mask, GFP_KERNEL))) { + netif_warn(efx, probe, efx->net_dev, + "RSS disabled due to allocation failure\n"); + return 1; + } + + cpumask_copy(filter_mask, cpu_online_mask); + if (local_node) { + int numa_node = pcibus_to_node(efx->pci_dev->bus); + + cpumask_and(filter_mask, filter_mask, cpumask_of_node(numa_node)); + } + + count = 0; + for_each_cpu(cpu, filter_mask) { + ++count; + cpumask_andnot(filter_mask, filter_mask, topology_sibling_cpumask(cpu)); + } + + free_cpumask_var(filter_mask); + + return count; +} + +static unsigned int efx_wanted_parallelism(struct efx_nic *efx) +{ + unsigned int count; + if (rss_cpus) { count = rss_cpus; } else { - if (unlikely(!zalloc_cpumask_var(&thread_mask, GFP_KERNEL))) { - netif_warn(efx, probe, efx->net_dev, - "RSS disabled due to allocation failure\n"); - return 1; - } + count = count_online_cores(efx, true); - count = 0; - for_each_online_cpu(cpu) { - if (!cpumask_test_cpu(cpu, thread_mask)) { - ++count; - cpumask_or(thread_mask, thread_mask, - topology_sibling_cpumask(cpu)); - } - } - - free_cpumask_var(thread_mask); + /* If no online CPUs in local node, fallback to any online CPUs */ + if (count == 0) + count = count_online_cores(efx, false); } if (count > EFX_MAX_RX_QUEUES) { @@ -369,12 +386,20 @@ int efx_probe_interrupts(struct efx_nic *efx) #if defined(CONFIG_SMP) void efx_set_interrupt_affinity(struct efx_nic *efx) { + int numa_node = pcibus_to_node(efx->pci_dev->bus); + const struct cpumask *numa_mask = cpumask_of_node(numa_node); struct efx_channel *channel; unsigned int cpu; + /* If no online CPUs in local node, fallback to any online CPU */ + if (cpumask_first_and(cpu_online_mask, numa_mask) >= nr_cpu_ids) + numa_mask = cpu_online_mask; + + cpu = -1; efx_for_each_channel(channel, efx) { - cpu = cpumask_local_spread(channel->channel, - pcibus_to_node(efx->pci_dev->bus)); + cpu = cpumask_next_and(cpu, cpu_online_mask, numa_mask); + if (cpu >= nr_cpu_ids) + cpu = cpumask_first_and(cpu_online_mask, numa_mask); irq_set_affinity_hint(channel->irq, cpumask_of(cpu)); } } diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index cc15ee8812d9..c75dc75e2857 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -1282,6 +1282,7 @@ struct efx_udp_tunnel { * @udp_tnl_has_port: Check if a port has been added as UDP tunnel * @print_additional_fwver: Dump NIC-specific additional FW version info * @sensor_event: Handle a sensor event from MCDI + * @rx_recycle_ring_size: Size of the RX recycle ring * @revision: Hardware architecture revision * @txd_ptr_tbl_base: TX descriptor ring base address * @rxd_ptr_tbl_base: RX descriptor ring base address @@ -1460,6 +1461,7 @@ struct efx_nic_type { size_t (*print_additional_fwver)(struct efx_nic *efx, char *buf, size_t len); void (*sensor_event)(struct efx_nic *efx, efx_qword_t *ev); + unsigned int (*rx_recycle_ring_size)(const struct efx_nic *efx); int revision; unsigned int txd_ptr_tbl_base; diff --git a/drivers/net/ethernet/sfc/nic_common.h b/drivers/net/ethernet/sfc/nic_common.h index b9cafe9cd568..0cef35c0c559 100644 --- a/drivers/net/ethernet/sfc/nic_common.h +++ b/drivers/net/ethernet/sfc/nic_common.h @@ -195,6 +195,11 @@ static inline void efx_sensor_event(struct efx_nic *efx, efx_qword_t *ev) efx->type->sensor_event(efx, ev); } +static inline unsigned int efx_rx_recycle_ring_size(const struct efx_nic *efx) +{ + return efx->type->rx_recycle_ring_size(efx); +} + /* Some statistics are computed as A - B where A and B each increase * linearly with some hardware counter(s) and the counters are read * asynchronously. If the counters contributing to B are always read diff --git a/drivers/net/ethernet/sfc/rx_common.c b/drivers/net/ethernet/sfc/rx_common.c index 633ca77a26fd..1b22c7be0088 100644 --- a/drivers/net/ethernet/sfc/rx_common.c +++ b/drivers/net/ethernet/sfc/rx_common.c @@ -23,13 +23,6 @@ module_param(rx_refill_threshold, uint, 0444); MODULE_PARM_DESC(rx_refill_threshold, "RX descriptor ring refill threshold (%)"); -/* Number of RX buffers to recycle pages for. When creating the RX page recycle - * ring, this number is divided by the number of buffers per page to calculate - * the number of pages to store in the RX page recycle ring. - */ -#define EFX_RECYCLE_RING_SIZE_IOMMU 4096 -#define EFX_RECYCLE_RING_SIZE_NOIOMMU (2 * EFX_RX_PREFERRED_BATCH) - /* RX maximum head room required. * * This must be at least 1 to prevent overflow, plus one packet-worth @@ -141,16 +134,7 @@ static void efx_init_rx_recycle_ring(struct efx_rx_queue *rx_queue) unsigned int bufs_in_recycle_ring, page_ring_size; struct efx_nic *efx = rx_queue->efx; - /* Set the RX recycle ring size */ -#ifdef CONFIG_PPC64 - bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_IOMMU; -#else - if (iommu_present(&pci_bus_type)) - bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_IOMMU; - else - bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_NOIOMMU; -#endif /* CONFIG_PPC64 */ - + bufs_in_recycle_ring = efx_rx_recycle_ring_size(efx); page_ring_size = roundup_pow_of_two(bufs_in_recycle_ring / efx->rx_bufs_per_page); rx_queue->page_ring = kcalloc(page_ring_size, diff --git a/drivers/net/ethernet/sfc/rx_common.h b/drivers/net/ethernet/sfc/rx_common.h index 207ccd8ba062..fbd2769307f9 100644 --- a/drivers/net/ethernet/sfc/rx_common.h +++ b/drivers/net/ethernet/sfc/rx_common.h @@ -18,6 +18,12 @@ #define EFX_RX_MAX_FRAGS DIV_ROUND_UP(EFX_MAX_FRAME_LEN(EFX_MAX_MTU), \ EFX_RX_USR_BUF_SIZE) +/* Number of RX buffers to recycle pages for. When creating the RX page recycle + * ring, this number is divided by the number of buffers per page to calculate + * the number of pages to store in the RX page recycle ring. + */ +#define EFX_RECYCLE_RING_SIZE_10G 256 + static inline u8 *efx_rx_buf_va(struct efx_rx_buffer *buf) { return page_address(buf->page) + buf->page_offset; diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index 16347a6d0c47..ce3060e15b54 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -25,6 +25,7 @@ #include "mcdi_port_common.h" #include "selftest.h" #include "siena_sriov.h" +#include "rx_common.h" /* Hardware control for SFC9000 family including SFL9021 (aka Siena). */ @@ -958,6 +959,12 @@ static unsigned int siena_check_caps(const struct efx_nic *efx, return 0; } +static unsigned int efx_siena_recycle_ring_size(const struct efx_nic *efx) +{ + /* Maximum link speed is 10G */ + return EFX_RECYCLE_RING_SIZE_10G; +} + /************************************************************************** * * Revision-dependent attributes used by efx.c and nic.c @@ -1098,4 +1105,5 @@ const struct efx_nic_type siena_a0_nic_type = { .rx_hash_key_size = 16, .check_caps = siena_check_caps, .sensor_event = efx_mcdi_sensor_event, + .rx_recycle_ring_size = efx_siena_recycle_ring_size, }; diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c index 556bd353dd42..b0c5a44785fa 100644 --- a/drivers/net/ethernet/socionext/netsec.c +++ b/drivers/net/ethernet/socionext/netsec.c @@ -1044,7 +1044,7 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget) "rx failed to build skb\n"); break; } - page_pool_release_page(dring->page_pool, page); + skb_mark_for_recycle(skb); skb_reserve(skb, xdp.data - xdp.data_hard_start); skb_put(skb, xdp.data_end - xdp.data); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c index 8e8778cfbbad..63754a9c4ba7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c @@ -383,10 +383,10 @@ static int intel_crosststamp(ktime_t *device, /* Repeat until the timestamps are from the FIFO last segment */ for (i = 0; i < num_snapshot; i++) { - spin_lock_irqsave(&priv->ptp_lock, flags); + read_lock_irqsave(&priv->ptp_lock, flags); stmmac_get_ptptime(priv, ptpaddr, &ptp_time); *device = ns_to_ktime(ptp_time); - spin_unlock_irqrestore(&priv->ptp_lock, flags); + read_unlock_irqrestore(&priv->ptp_lock, flags); get_arttime(priv->mii, intel_priv->mdio_adhoc_addr, &art_time); *system = convert_art_to_tsc(art_time); } @@ -721,6 +721,7 @@ static int tgl_common_data(struct pci_dev *pdev, plat->rx_queues_to_use = 6; plat->tx_queues_to_use = 4; plat->clk_ptp_rate = 200000000; + plat->speed_mode_2500 = intel_speed_mode_2500; plat->safety_feat_cfg->tsoee = 1; plat->safety_feat_cfg->mrxpee = 0; @@ -740,7 +741,6 @@ static int tgl_sgmii_phy0_data(struct pci_dev *pdev, { plat->bus_id = 1; plat->phy_interface = PHY_INTERFACE_MODE_SGMII; - plat->speed_mode_2500 = intel_speed_mode_2500; plat->serdes_powerup = intel_serdes_powerup; plat->serdes_powerdown = intel_serdes_powerdown; return tgl_common_data(pdev, plat); @@ -755,7 +755,6 @@ static int tgl_sgmii_phy1_data(struct pci_dev *pdev, { plat->bus_id = 2; plat->phy_interface = PHY_INTERFACE_MODE_SGMII; - plat->speed_mode_2500 = intel_speed_mode_2500; plat->serdes_powerup = intel_serdes_powerup; plat->serdes_powerdown = intel_serdes_powerdown; return tgl_common_data(pdev, plat); @@ -1160,6 +1159,7 @@ static SIMPLE_DEV_PM_OPS(intel_eth_pm_ops, intel_eth_pci_suspend, #define PCI_DEVICE_ID_INTEL_TGL_SGMII1G 0xa0ac #define PCI_DEVICE_ID_INTEL_ADLS_SGMII1G_0 0x7aac #define PCI_DEVICE_ID_INTEL_ADLS_SGMII1G_1 0x7aad +#define PCI_DEVICE_ID_INTEL_ADLN_SGMII1G 0x54ac static const struct pci_device_id intel_eth_pci_id_table[] = { { PCI_DEVICE_DATA(INTEL, QUARK, &quark_info) }, @@ -1177,6 +1177,7 @@ static const struct pci_device_id intel_eth_pci_id_table[] = { { PCI_DEVICE_DATA(INTEL, TGLH_SGMII1G_1, &tgl_sgmii1g_phy1_info) }, { PCI_DEVICE_DATA(INTEL, ADLS_SGMII1G_0, &adls_sgmii1g_phy0_info) }, { PCI_DEVICE_DATA(INTEL, ADLS_SGMII1G_1, &adls_sgmii1g_phy1_info) }, + { PCI_DEVICE_DATA(INTEL, ADLN_SGMII1G, &tgl_sgmii1g_phy0_info) }, {} }; MODULE_DEVICE_TABLE(pci, intel_eth_pci_id_table); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c index 58c0feaa8131..6ff88df58767 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -40,6 +39,33 @@ #define ETH_FINE_DLY_GTXC BIT(1) #define ETH_FINE_DLY_RXC BIT(0) +/* Peri Configuration register for mt8195 */ +#define MT8195_PERI_ETH_CTRL0 0xFD0 +#define MT8195_RMII_CLK_SRC_INTERNAL BIT(28) +#define MT8195_RMII_CLK_SRC_RXC BIT(27) +#define MT8195_ETH_INTF_SEL GENMASK(26, 24) +#define MT8195_RGMII_TXC_PHASE_CTRL BIT(22) +#define MT8195_EXT_PHY_MODE BIT(21) +#define MT8195_DLY_GTXC_INV BIT(12) +#define MT8195_DLY_GTXC_ENABLE BIT(5) +#define MT8195_DLY_GTXC_STAGES GENMASK(4, 0) + +#define MT8195_PERI_ETH_CTRL1 0xFD4 +#define MT8195_DLY_RXC_INV BIT(25) +#define MT8195_DLY_RXC_ENABLE BIT(18) +#define MT8195_DLY_RXC_STAGES GENMASK(17, 13) +#define MT8195_DLY_TXC_INV BIT(12) +#define MT8195_DLY_TXC_ENABLE BIT(5) +#define MT8195_DLY_TXC_STAGES GENMASK(4, 0) + +#define MT8195_PERI_ETH_CTRL2 0xFD8 +#define MT8195_DLY_RMII_RXC_INV BIT(25) +#define MT8195_DLY_RMII_RXC_ENABLE BIT(18) +#define MT8195_DLY_RMII_RXC_STAGES GENMASK(17, 13) +#define MT8195_DLY_RMII_TXC_INV BIT(12) +#define MT8195_DLY_RMII_TXC_ENABLE BIT(5) +#define MT8195_DLY_RMII_TXC_STAGES GENMASK(4, 0) + struct mac_delay_struct { u32 tx_delay; u32 rx_delay; @@ -50,19 +76,21 @@ struct mac_delay_struct { struct mediatek_dwmac_plat_data { const struct mediatek_dwmac_variant *variant; struct mac_delay_struct mac_delay; + struct clk *rmii_internal_clk; struct clk_bulk_data *clks; - struct device_node *np; struct regmap *peri_regmap; + struct device_node *np; struct device *dev; phy_interface_t phy_mode; - int num_clks_to_config; bool rmii_clk_from_mac; bool rmii_rxc; + bool mac_wol; }; struct mediatek_dwmac_variant { int (*dwmac_set_phy_interface)(struct mediatek_dwmac_plat_data *plat); int (*dwmac_set_delay)(struct mediatek_dwmac_plat_data *plat); + void (*dwmac_fix_mac_speed)(void *priv, unsigned int speed); /* clock ids to be requested */ const char * const *clk_list; @@ -75,7 +103,11 @@ struct mediatek_dwmac_variant { /* list of clocks required for mac */ static const char * const mt2712_dwmac_clk_l[] = { - "axi", "apb", "mac_main", "ptp_ref", "rmii_internal" + "axi", "apb", "mac_main", "ptp_ref" +}; + +static const char * const mt8195_dwmac_clk_l[] = { + "axi", "apb", "mac_cg", "mac_main", "ptp_ref" }; static int mt2712_set_interface(struct mediatek_dwmac_plat_data *plat) @@ -84,23 +116,12 @@ static int mt2712_set_interface(struct mediatek_dwmac_plat_data *plat) int rmii_rxc = plat->rmii_rxc ? RMII_CLK_SRC_RXC : 0; u32 intf_val = 0; - /* The clock labeled as "rmii_internal" in mt2712_dwmac_clk_l is needed - * only in RMII(when MAC provides the reference clock), and useless for - * RGMII/MII/RMII(when PHY provides the reference clock). - * num_clks_to_config indicates the real number of clocks should be - * configured, equals to (plat->variant->num_clks - 1) in default for all the case, - * then +1 for rmii_clk_from_mac case. - */ - plat->num_clks_to_config = plat->variant->num_clks - 1; - /* select phy interface in top control domain */ switch (plat->phy_mode) { case PHY_INTERFACE_MODE_MII: intf_val |= PHY_INTF_MII; break; case PHY_INTERFACE_MODE_RMII: - if (plat->rmii_clk_from_mac) - plat->num_clks_to_config++; intf_val |= (PHY_INTF_RMII | rmii_rxc | rmii_clk_from_mac); break; case PHY_INTERFACE_MODE_RGMII: @@ -268,6 +289,193 @@ static const struct mediatek_dwmac_variant mt2712_gmac_variant = { .tx_delay_max = 17600, }; +static int mt8195_set_interface(struct mediatek_dwmac_plat_data *plat) +{ + int rmii_clk_from_mac = plat->rmii_clk_from_mac ? MT8195_RMII_CLK_SRC_INTERNAL : 0; + int rmii_rxc = plat->rmii_rxc ? MT8195_RMII_CLK_SRC_RXC : 0; + u32 intf_val = 0; + + /* select phy interface in top control domain */ + switch (plat->phy_mode) { + case PHY_INTERFACE_MODE_MII: + intf_val |= FIELD_PREP(MT8195_ETH_INTF_SEL, PHY_INTF_MII); + break; + case PHY_INTERFACE_MODE_RMII: + intf_val |= (rmii_rxc | rmii_clk_from_mac); + intf_val |= FIELD_PREP(MT8195_ETH_INTF_SEL, PHY_INTF_RMII); + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_ID: + intf_val |= FIELD_PREP(MT8195_ETH_INTF_SEL, PHY_INTF_RGMII); + break; + default: + dev_err(plat->dev, "phy interface not supported\n"); + return -EINVAL; + } + + /* MT8195 only support external PHY */ + intf_val |= MT8195_EXT_PHY_MODE; + + regmap_write(plat->peri_regmap, MT8195_PERI_ETH_CTRL0, intf_val); + + return 0; +} + +static void mt8195_delay_ps2stage(struct mediatek_dwmac_plat_data *plat) +{ + struct mac_delay_struct *mac_delay = &plat->mac_delay; + + /* 290ps per stage */ + mac_delay->tx_delay /= 290; + mac_delay->rx_delay /= 290; +} + +static void mt8195_delay_stage2ps(struct mediatek_dwmac_plat_data *plat) +{ + struct mac_delay_struct *mac_delay = &plat->mac_delay; + + /* 290ps per stage */ + mac_delay->tx_delay *= 290; + mac_delay->rx_delay *= 290; +} + +static int mt8195_set_delay(struct mediatek_dwmac_plat_data *plat) +{ + struct mac_delay_struct *mac_delay = &plat->mac_delay; + u32 gtxc_delay_val = 0, delay_val = 0, rmii_delay_val = 0; + + mt8195_delay_ps2stage(plat); + + switch (plat->phy_mode) { + case PHY_INTERFACE_MODE_MII: + delay_val |= FIELD_PREP(MT8195_DLY_TXC_ENABLE, !!mac_delay->tx_delay); + delay_val |= FIELD_PREP(MT8195_DLY_TXC_STAGES, mac_delay->tx_delay); + delay_val |= FIELD_PREP(MT8195_DLY_TXC_INV, mac_delay->tx_inv); + + delay_val |= FIELD_PREP(MT8195_DLY_RXC_ENABLE, !!mac_delay->rx_delay); + delay_val |= FIELD_PREP(MT8195_DLY_RXC_STAGES, mac_delay->rx_delay); + delay_val |= FIELD_PREP(MT8195_DLY_RXC_INV, mac_delay->rx_inv); + break; + case PHY_INTERFACE_MODE_RMII: + if (plat->rmii_clk_from_mac) { + /* case 1: mac provides the rmii reference clock, + * and the clock output to TXC pin. + * The egress timing can be adjusted by RMII_TXC delay macro circuit. + * The ingress timing can be adjusted by RMII_RXC delay macro circuit. + */ + rmii_delay_val |= FIELD_PREP(MT8195_DLY_RMII_TXC_ENABLE, + !!mac_delay->tx_delay); + rmii_delay_val |= FIELD_PREP(MT8195_DLY_RMII_TXC_STAGES, + mac_delay->tx_delay); + rmii_delay_val |= FIELD_PREP(MT8195_DLY_RMII_TXC_INV, + mac_delay->tx_inv); + + rmii_delay_val |= FIELD_PREP(MT8195_DLY_RMII_RXC_ENABLE, + !!mac_delay->rx_delay); + rmii_delay_val |= FIELD_PREP(MT8195_DLY_RMII_RXC_STAGES, + mac_delay->rx_delay); + rmii_delay_val |= FIELD_PREP(MT8195_DLY_RMII_RXC_INV, + mac_delay->rx_inv); + } else { + /* case 2: the rmii reference clock is from external phy, + * and the property "rmii_rxc" indicates which pin(TXC/RXC) + * the reference clk is connected to. The reference clock is a + * received signal, so rx_delay/rx_inv are used to indicate + * the reference clock timing adjustment + */ + if (plat->rmii_rxc) { + /* the rmii reference clock from outside is connected + * to RXC pin, the reference clock will be adjusted + * by RXC delay macro circuit. + */ + delay_val |= FIELD_PREP(MT8195_DLY_RXC_ENABLE, + !!mac_delay->rx_delay); + delay_val |= FIELD_PREP(MT8195_DLY_RXC_STAGES, + mac_delay->rx_delay); + delay_val |= FIELD_PREP(MT8195_DLY_RXC_INV, + mac_delay->rx_inv); + } else { + /* the rmii reference clock from outside is connected + * to TXC pin, the reference clock will be adjusted + * by TXC delay macro circuit. + */ + delay_val |= FIELD_PREP(MT8195_DLY_TXC_ENABLE, + !!mac_delay->rx_delay); + delay_val |= FIELD_PREP(MT8195_DLY_TXC_STAGES, + mac_delay->rx_delay); + delay_val |= FIELD_PREP(MT8195_DLY_TXC_INV, + mac_delay->rx_inv); + } + } + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_ID: + gtxc_delay_val |= FIELD_PREP(MT8195_DLY_GTXC_ENABLE, !!mac_delay->tx_delay); + gtxc_delay_val |= FIELD_PREP(MT8195_DLY_GTXC_STAGES, mac_delay->tx_delay); + gtxc_delay_val |= FIELD_PREP(MT8195_DLY_GTXC_INV, mac_delay->tx_inv); + + delay_val |= FIELD_PREP(MT8195_DLY_RXC_ENABLE, !!mac_delay->rx_delay); + delay_val |= FIELD_PREP(MT8195_DLY_RXC_STAGES, mac_delay->rx_delay); + delay_val |= FIELD_PREP(MT8195_DLY_RXC_INV, mac_delay->rx_inv); + + break; + default: + dev_err(plat->dev, "phy interface not supported\n"); + return -EINVAL; + } + + regmap_update_bits(plat->peri_regmap, + MT8195_PERI_ETH_CTRL0, + MT8195_RGMII_TXC_PHASE_CTRL | + MT8195_DLY_GTXC_INV | + MT8195_DLY_GTXC_ENABLE | + MT8195_DLY_GTXC_STAGES, + gtxc_delay_val); + regmap_write(plat->peri_regmap, MT8195_PERI_ETH_CTRL1, delay_val); + regmap_write(plat->peri_regmap, MT8195_PERI_ETH_CTRL2, rmii_delay_val); + + mt8195_delay_stage2ps(plat); + + return 0; +} + +static void mt8195_fix_mac_speed(void *priv, unsigned int speed) +{ + struct mediatek_dwmac_plat_data *priv_plat = priv; + + if ((phy_interface_mode_is_rgmii(priv_plat->phy_mode))) { + /* prefer 2ns fixed delay which is controlled by TXC_PHASE_CTRL, + * when link speed is 1Gbps with RGMII interface, + * Fall back to delay macro circuit for 10/100Mbps link speed. + */ + if (speed == SPEED_1000) + regmap_update_bits(priv_plat->peri_regmap, + MT8195_PERI_ETH_CTRL0, + MT8195_RGMII_TXC_PHASE_CTRL | + MT8195_DLY_GTXC_ENABLE | + MT8195_DLY_GTXC_INV | + MT8195_DLY_GTXC_STAGES, + MT8195_RGMII_TXC_PHASE_CTRL); + else + mt8195_set_delay(priv_plat); + } +} + +static const struct mediatek_dwmac_variant mt8195_gmac_variant = { + .dwmac_set_phy_interface = mt8195_set_interface, + .dwmac_set_delay = mt8195_set_delay, + .dwmac_fix_mac_speed = mt8195_fix_mac_speed, + .clk_list = mt8195_dwmac_clk_l, + .num_clks = ARRAY_SIZE(mt8195_dwmac_clk_l), + .dma_bit_mask = 35, + .rx_delay_max = 9280, + .tx_delay_max = 9280, +}; + static int mediatek_dwmac_config_dt(struct mediatek_dwmac_plat_data *plat) { struct mac_delay_struct *mac_delay = &plat->mac_delay; @@ -308,6 +516,7 @@ static int mediatek_dwmac_config_dt(struct mediatek_dwmac_plat_data *plat) mac_delay->rx_inv = of_property_read_bool(plat->np, "mediatek,rxc-inverse"); plat->rmii_rxc = of_property_read_bool(plat->np, "mediatek,rmii-rxc"); plat->rmii_clk_from_mac = of_property_read_bool(plat->np, "mediatek,rmii-clk-from-mac"); + plat->mac_wol = of_property_read_bool(plat->np, "mediatek,mac-wol"); return 0; } @@ -315,18 +524,34 @@ static int mediatek_dwmac_config_dt(struct mediatek_dwmac_plat_data *plat) static int mediatek_dwmac_clk_init(struct mediatek_dwmac_plat_data *plat) { const struct mediatek_dwmac_variant *variant = plat->variant; - int i, num = variant->num_clks; + int i, ret; - plat->clks = devm_kcalloc(plat->dev, num, sizeof(*plat->clks), GFP_KERNEL); + plat->clks = devm_kcalloc(plat->dev, variant->num_clks, sizeof(*plat->clks), GFP_KERNEL); if (!plat->clks) return -ENOMEM; - for (i = 0; i < num; i++) + for (i = 0; i < variant->num_clks; i++) plat->clks[i].id = variant->clk_list[i]; - plat->num_clks_to_config = variant->num_clks; + ret = devm_clk_bulk_get(plat->dev, variant->num_clks, plat->clks); + if (ret) + return ret; - return devm_clk_bulk_get(plat->dev, num, plat->clks); + /* The clock labeled as "rmii_internal" is needed only in RMII(when + * MAC provides the reference clock), and useless for RGMII/MII or + * RMII(when PHY provides the reference clock). + * So, "rmii_internal" clock is got and configured only when + * reference clock of RMII is from MAC. + */ + if (plat->rmii_clk_from_mac) { + plat->rmii_internal_clk = devm_clk_get(plat->dev, "rmii_internal"); + if (IS_ERR(plat->rmii_internal_clk)) + ret = PTR_ERR(plat->rmii_internal_clk); + } else { + plat->rmii_internal_clk = NULL; + } + + return ret; } static int mediatek_dwmac_init(struct platform_device *pdev, void *priv) @@ -335,44 +560,117 @@ static int mediatek_dwmac_init(struct platform_device *pdev, void *priv) const struct mediatek_dwmac_variant *variant = plat->variant; int ret; - ret = dma_set_mask_and_coherent(plat->dev, DMA_BIT_MASK(variant->dma_bit_mask)); - if (ret) { - dev_err(plat->dev, "No suitable DMA available, err = %d\n", ret); - return ret; + if (variant->dwmac_set_phy_interface) { + ret = variant->dwmac_set_phy_interface(plat); + if (ret) { + dev_err(plat->dev, "failed to set phy interface, err = %d\n", ret); + return ret; + } } - ret = variant->dwmac_set_phy_interface(plat); - if (ret) { - dev_err(plat->dev, "failed to set phy interface, err = %d\n", ret); - return ret; + if (variant->dwmac_set_delay) { + ret = variant->dwmac_set_delay(plat); + if (ret) { + dev_err(plat->dev, "failed to set delay value, err = %d\n", ret); + return ret; + } } - ret = variant->dwmac_set_delay(plat); - if (ret) { - dev_err(plat->dev, "failed to set delay value, err = %d\n", ret); - return ret; - } - - ret = clk_bulk_prepare_enable(plat->num_clks_to_config, plat->clks); + ret = clk_bulk_prepare_enable(variant->num_clks, plat->clks); if (ret) { dev_err(plat->dev, "failed to enable clks, err = %d\n", ret); return ret; } - pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); + ret = clk_prepare_enable(plat->rmii_internal_clk); + if (ret) { + dev_err(plat->dev, "failed to enable rmii internal clk, err = %d\n", ret); + goto err_clk; + } return 0; + +err_clk: + clk_bulk_disable_unprepare(variant->num_clks, plat->clks); + return ret; } static void mediatek_dwmac_exit(struct platform_device *pdev, void *priv) { struct mediatek_dwmac_plat_data *plat = priv; + const struct mediatek_dwmac_variant *variant = plat->variant; - clk_bulk_disable_unprepare(plat->num_clks_to_config, plat->clks); + clk_disable_unprepare(plat->rmii_internal_clk); + clk_bulk_disable_unprepare(variant->num_clks, plat->clks); +} - pm_runtime_put_sync(&pdev->dev); - pm_runtime_disable(&pdev->dev); +static int mediatek_dwmac_clks_config(void *priv, bool enabled) +{ + struct mediatek_dwmac_plat_data *plat = priv; + const struct mediatek_dwmac_variant *variant = plat->variant; + int ret = 0; + + if (enabled) { + ret = clk_bulk_prepare_enable(variant->num_clks, plat->clks); + if (ret) { + dev_err(plat->dev, "failed to enable clks, err = %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(plat->rmii_internal_clk); + if (ret) { + dev_err(plat->dev, "failed to enable rmii internal clk, err = %d\n", ret); + return ret; + } + } else { + clk_disable_unprepare(plat->rmii_internal_clk); + clk_bulk_disable_unprepare(variant->num_clks, plat->clks); + } + + return ret; +} + +static int mediatek_dwmac_common_data(struct platform_device *pdev, + struct plat_stmmacenet_data *plat, + struct mediatek_dwmac_plat_data *priv_plat) +{ + int i; + + plat->interface = priv_plat->phy_mode; + plat->use_phy_wol = priv_plat->mac_wol ? 0 : 1; + plat->riwt_off = 1; + plat->maxmtu = ETH_DATA_LEN; + plat->addr64 = priv_plat->variant->dma_bit_mask; + plat->bsp_priv = priv_plat; + plat->init = mediatek_dwmac_init; + plat->exit = mediatek_dwmac_exit; + plat->clks_config = mediatek_dwmac_clks_config; + if (priv_plat->variant->dwmac_fix_mac_speed) + plat->fix_mac_speed = priv_plat->variant->dwmac_fix_mac_speed; + + plat->safety_feat_cfg = devm_kzalloc(&pdev->dev, + sizeof(*plat->safety_feat_cfg), + GFP_KERNEL); + if (!plat->safety_feat_cfg) + return -ENOMEM; + + plat->safety_feat_cfg->tsoee = 1; + plat->safety_feat_cfg->mrxpee = 0; + plat->safety_feat_cfg->mestee = 1; + plat->safety_feat_cfg->mrxee = 1; + plat->safety_feat_cfg->mtxee = 1; + plat->safety_feat_cfg->epsi = 0; + plat->safety_feat_cfg->edpp = 1; + plat->safety_feat_cfg->prtyen = 1; + plat->safety_feat_cfg->tmouten = 1; + + for (i = 0; i < plat->tx_queues_to_use; i++) { + /* Default TX Q0 to use TSO and rest TXQ for TBS */ + if (i > 0) + plat->tx_queues_cfg[i].tbs_en = 1; + } + + return 0; } static int mediatek_dwmac_probe(struct platform_device *pdev) @@ -411,15 +709,7 @@ static int mediatek_dwmac_probe(struct platform_device *pdev) if (IS_ERR(plat_dat)) return PTR_ERR(plat_dat); - plat_dat->interface = priv_plat->phy_mode; - plat_dat->has_gmac4 = 1; - plat_dat->has_gmac = 0; - plat_dat->pmt = 0; - plat_dat->riwt_off = 1; - plat_dat->maxmtu = ETH_DATA_LEN; - plat_dat->bsp_priv = priv_plat; - plat_dat->init = mediatek_dwmac_init; - plat_dat->exit = mediatek_dwmac_exit; + mediatek_dwmac_common_data(pdev, plat_dat, priv_plat); mediatek_dwmac_init(pdev, priv_plat); ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); @@ -434,6 +724,8 @@ static int mediatek_dwmac_probe(struct platform_device *pdev) static const struct of_device_id mediatek_dwmac_match[] = { { .compatible = "mediatek,mt2712-gmac", .data = &mt2712_gmac_variant }, + { .compatible = "mediatek,mt8195-gmac", + .data = &mt8195_gmac_variant }, { } }; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 2ffa0a11eea5..0cc28c79cc61 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -78,6 +78,7 @@ struct ethqos_emac_por { struct ethqos_emac_driver_data { const struct ethqos_emac_por *por; unsigned int num_por; + bool rgmii_config_looback_en; }; struct qcom_ethqos { @@ -90,6 +91,7 @@ struct qcom_ethqos { const struct ethqos_emac_por *por; unsigned int num_por; + bool rgmii_config_looback_en; }; static int rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset) @@ -181,6 +183,22 @@ static const struct ethqos_emac_por emac_v2_3_0_por[] = { static const struct ethqos_emac_driver_data emac_v2_3_0_data = { .por = emac_v2_3_0_por, .num_por = ARRAY_SIZE(emac_v2_3_0_por), + .rgmii_config_looback_en = true, +}; + +static const struct ethqos_emac_por emac_v2_1_0_por[] = { + { .offset = RGMII_IO_MACRO_CONFIG, .value = 0x40C01343 }, + { .offset = SDCC_HC_REG_DLL_CONFIG, .value = 0x2004642C }, + { .offset = SDCC_HC_REG_DDR_CONFIG, .value = 0x00000000 }, + { .offset = SDCC_HC_REG_DLL_CONFIG2, .value = 0x00200000 }, + { .offset = SDCC_USR_CTL, .value = 0x00010800 }, + { .offset = RGMII_IO_MACRO_CONFIG2, .value = 0x00002060 }, +}; + +static const struct ethqos_emac_driver_data emac_v2_1_0_data = { + .por = emac_v2_1_0_por, + .num_por = ARRAY_SIZE(emac_v2_1_0_por), + .rgmii_config_looback_en = false, }; static int ethqos_dll_configure(struct qcom_ethqos *ethqos) @@ -297,8 +315,12 @@ static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos) rgmii_updatel(ethqos, SDCC_DDR_CONFIG_PRG_DLY_EN, SDCC_DDR_CONFIG_PRG_DLY_EN, SDCC_HC_REG_DDR_CONFIG); - rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN, - RGMII_CONFIG_LOOPBACK_EN, RGMII_IO_MACRO_CONFIG); + if (ethqos->rgmii_config_looback_en) + rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN, + RGMII_CONFIG_LOOPBACK_EN, RGMII_IO_MACRO_CONFIG); + else + rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN, + 0, RGMII_IO_MACRO_CONFIG); break; case SPEED_100: @@ -331,8 +353,13 @@ static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos) rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN, SDCC_HC_REG_DDR_CONFIG); - rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN, - RGMII_CONFIG_LOOPBACK_EN, RGMII_IO_MACRO_CONFIG); + if (ethqos->rgmii_config_looback_en) + rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN, + RGMII_CONFIG_LOOPBACK_EN, RGMII_IO_MACRO_CONFIG); + else + rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN, + 0, RGMII_IO_MACRO_CONFIG); + break; case SPEED_10: @@ -504,6 +531,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) data = of_device_get_match_data(&pdev->dev); ethqos->por = data->por; ethqos->num_por = data->num_por; + ethqos->rgmii_config_looback_en = data->rgmii_config_looback_en; ethqos->rgmii_clk = devm_clk_get(&pdev->dev, "rgmii"); if (IS_ERR(ethqos->rgmii_clk)) { @@ -558,6 +586,7 @@ static int qcom_ethqos_remove(struct platform_device *pdev) static const struct of_device_id qcom_ethqos_match[] = { { .compatible = "qcom,qcs404-ethqos", .data = &emac_v2_3_0_data}, + { .compatible = "qcom,sm8150-ethqos", .data = &emac_v2_1_0_data}, { } }; MODULE_DEVICE_TABLE(of, qcom_ethqos_match); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index 09644ab0d87a..f86cc83003f2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -57,7 +58,6 @@ struct emac_variant { }; /* struct sunxi_priv_data - hold all sunxi private data - * @tx_clk: reference to MAC TX clock * @ephy_clk: reference to the optional EPHY clock for the internal PHY * @regulator: reference to the optional regulator * @rst_ephy: reference to the optional EPHY reset for the internal PHY @@ -68,7 +68,6 @@ struct emac_variant { * @mux_handle: Internal pointer used by mdio-mux lib */ struct sunxi_priv_data { - struct clk *tx_clk; struct clk *ephy_clk; struct regulator *regulator; struct reset_control *rst_ephy; @@ -579,22 +578,14 @@ static int sun8i_dwmac_init(struct platform_device *pdev, void *priv) } } - ret = clk_prepare_enable(gmac->tx_clk); - if (ret) { - dev_err(&pdev->dev, "Could not enable AHB clock\n"); - goto err_disable_regulator; - } - if (gmac->use_internal_phy) { ret = sun8i_dwmac_power_internal_phy(netdev_priv(ndev)); if (ret) - goto err_disable_clk; + goto err_disable_regulator; } return 0; -err_disable_clk: - clk_disable_unprepare(gmac->tx_clk); err_disable_regulator: if (gmac->regulator) regulator_disable(gmac->regulator); @@ -1043,8 +1034,6 @@ static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv) if (gmac->variant->soc_has_internal_phy) sun8i_dwmac_unpower_internal_phy(gmac); - clk_disable_unprepare(gmac->tx_clk); - if (gmac->regulator) regulator_disable(gmac->regulator); } @@ -1167,12 +1156,6 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) return -EINVAL; } - gmac->tx_clk = devm_clk_get(dev, "stmmaceth"); - if (IS_ERR(gmac->tx_clk)) { - dev_err(dev, "Could not get TX clock\n"); - return PTR_ERR(gmac->tx_clk); - } - /* Optional regulator for PHY */ gmac->regulator = devm_regulator_get_optional(dev, "phy"); if (IS_ERR(gmac->regulator)) { @@ -1254,6 +1237,12 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) ndev = dev_get_drvdata(&pdev->dev); priv = netdev_priv(ndev); + /* the MAC is runtime suspended after stmmac_dvr_probe(), so we + * need to ensure the MAC resume back before other operations such + * as reset. + */ + pm_runtime_get_sync(&pdev->dev); + /* The mux must be registered after parent MDIO * so after stmmac_dvr_probe() */ @@ -1272,12 +1261,15 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) goto dwmac_remove; } + pm_runtime_put(&pdev->dev); + return 0; dwmac_mux: reset_control_put(gmac->rst_ephy); clk_put(gmac->ephy_clk); dwmac_remove: + pm_runtime_put_noidle(&pdev->dev); stmmac_dvr_remove(&pdev->dev); dwmac_exit: sun8i_dwmac_exit(pdev, gmac); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 5b195d5051d6..57970ae2178d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -263,7 +263,7 @@ struct stmmac_priv { u32 adv_ts; int use_riwt; int irq_wake; - spinlock_t ptp_lock; + rwlock_t ptp_lock; /* Protects auxiliary snapshot registers from concurrent access. */ struct mutex aux_ts_lock; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c index a7ec9f4d46ce..22fea0f67245 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c @@ -196,9 +196,9 @@ static void timestamp_interrupt(struct stmmac_priv *priv) GMAC_TIMESTAMP_ATSNS_SHIFT; for (i = 0; i < num_snapshot; i++) { - spin_lock_irqsave(&priv->ptp_lock, flags); + read_lock_irqsave(&priv->ptp_lock, flags); get_ptptime(priv->ptpaddr, &ptp_time); - spin_unlock_irqrestore(&priv->ptp_lock, flags); + read_unlock_irqrestore(&priv->ptp_lock, flags); event.type = PTP_CLOCK_EXTTS; event.index = 0; event.timestamp = ptp_time; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 422e3225f476..4a4b3651ab3e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -938,105 +938,15 @@ static void stmmac_mac_flow_ctrl(struct stmmac_priv *priv, u32 duplex) priv->pause, tx_cnt); } -static void stmmac_validate(struct phylink_config *config, - unsigned long *supported, - struct phylink_link_state *state) +static struct phylink_pcs *stmmac_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) { struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); - __ETHTOOL_DECLARE_LINK_MODE_MASK(mac_supported) = { 0, }; - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - int tx_cnt = priv->plat->tx_queues_to_use; - int max_speed = priv->plat->max_speed; - phylink_set(mac_supported, 10baseT_Half); - phylink_set(mac_supported, 10baseT_Full); - phylink_set(mac_supported, 100baseT_Half); - phylink_set(mac_supported, 100baseT_Full); - phylink_set(mac_supported, 1000baseT_Half); - phylink_set(mac_supported, 1000baseT_Full); - phylink_set(mac_supported, 1000baseKX_Full); + if (!priv->hw->xpcs) + return NULL; - phylink_set(mac_supported, Autoneg); - phylink_set(mac_supported, Pause); - phylink_set(mac_supported, Asym_Pause); - phylink_set_port_modes(mac_supported); - - /* Cut down 1G if asked to */ - if ((max_speed > 0) && (max_speed < 1000)) { - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); - } else if (priv->plat->has_gmac4) { - if (!max_speed || max_speed >= 2500) { - phylink_set(mac_supported, 2500baseT_Full); - phylink_set(mac_supported, 2500baseX_Full); - } - } else if (priv->plat->has_xgmac) { - if (!max_speed || (max_speed >= 2500)) { - phylink_set(mac_supported, 2500baseT_Full); - phylink_set(mac_supported, 2500baseX_Full); - } - if (!max_speed || (max_speed >= 5000)) { - phylink_set(mac_supported, 5000baseT_Full); - } - if (!max_speed || (max_speed >= 10000)) { - phylink_set(mac_supported, 10000baseSR_Full); - phylink_set(mac_supported, 10000baseLR_Full); - phylink_set(mac_supported, 10000baseER_Full); - phylink_set(mac_supported, 10000baseLRM_Full); - phylink_set(mac_supported, 10000baseT_Full); - phylink_set(mac_supported, 10000baseKX4_Full); - phylink_set(mac_supported, 10000baseKR_Full); - } - if (!max_speed || (max_speed >= 25000)) { - phylink_set(mac_supported, 25000baseCR_Full); - phylink_set(mac_supported, 25000baseKR_Full); - phylink_set(mac_supported, 25000baseSR_Full); - } - if (!max_speed || (max_speed >= 40000)) { - phylink_set(mac_supported, 40000baseKR4_Full); - phylink_set(mac_supported, 40000baseCR4_Full); - phylink_set(mac_supported, 40000baseSR4_Full); - phylink_set(mac_supported, 40000baseLR4_Full); - } - if (!max_speed || (max_speed >= 50000)) { - phylink_set(mac_supported, 50000baseCR2_Full); - phylink_set(mac_supported, 50000baseKR2_Full); - phylink_set(mac_supported, 50000baseSR2_Full); - phylink_set(mac_supported, 50000baseKR_Full); - phylink_set(mac_supported, 50000baseSR_Full); - phylink_set(mac_supported, 50000baseCR_Full); - phylink_set(mac_supported, 50000baseLR_ER_FR_Full); - phylink_set(mac_supported, 50000baseDR_Full); - } - if (!max_speed || (max_speed >= 100000)) { - phylink_set(mac_supported, 100000baseKR4_Full); - phylink_set(mac_supported, 100000baseSR4_Full); - phylink_set(mac_supported, 100000baseCR4_Full); - phylink_set(mac_supported, 100000baseLR4_ER4_Full); - phylink_set(mac_supported, 100000baseKR2_Full); - phylink_set(mac_supported, 100000baseSR2_Full); - phylink_set(mac_supported, 100000baseCR2_Full); - phylink_set(mac_supported, 100000baseLR2_ER2_FR2_Full); - phylink_set(mac_supported, 100000baseDR2_Full); - } - } - - /* Half-Duplex can only work with single queue */ - if (tx_cnt > 1) { - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 1000baseT_Half); - } - - linkmode_and(supported, supported, mac_supported); - linkmode_andnot(supported, supported, mask); - - linkmode_and(state->advertising, state->advertising, mac_supported); - linkmode_andnot(state->advertising, state->advertising, mask); - - /* If PCS is supported, check which modes it supports. */ - if (priv->hw->xpcs) - xpcs_validate(priv->hw->xpcs, supported, state); + return &priv->hw->xpcs->pcs; } static void stmmac_mac_config(struct phylink_config *config, unsigned int mode, @@ -1175,7 +1085,8 @@ static void stmmac_mac_link_up(struct phylink_config *config, } static const struct phylink_mac_ops stmmac_phylink_mac_ops = { - .validate = stmmac_validate, + .validate = phylink_generic_validate, + .mac_select_pcs = stmmac_mac_select_pcs, .mac_config = stmmac_mac_config, .mac_link_down = stmmac_mac_link_down, .mac_link_up = stmmac_mac_link_up, @@ -1255,12 +1166,12 @@ static int stmmac_phy_setup(struct stmmac_priv *priv) { struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data; struct fwnode_handle *fwnode = of_fwnode_handle(priv->plat->phylink_node); + int max_speed = priv->plat->max_speed; int mode = priv->plat->phy_interface; struct phylink *phylink; priv->phylink_config.dev = &priv->dev->dev; priv->phylink_config.type = PHYLINK_NETDEV; - priv->phylink_config.pcs_poll = true; if (priv->plat->mdio_bus_data) priv->phylink_config.ovr_an_inband = mdio_bus_data->xpcs_an_inband; @@ -1268,14 +1179,50 @@ static int stmmac_phy_setup(struct stmmac_priv *priv) if (!fwnode) fwnode = dev_fwnode(priv->device); + /* Set the platform/firmware specified interface mode */ + __set_bit(mode, priv->phylink_config.supported_interfaces); + + /* If we have an xpcs, it defines which PHY interfaces are supported. */ + if (priv->hw->xpcs) + xpcs_get_interfaces(priv->hw->xpcs, + priv->phylink_config.supported_interfaces); + + priv->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100; + + if (!max_speed || max_speed >= 1000) + priv->phylink_config.mac_capabilities |= MAC_1000; + + if (priv->plat->has_gmac4) { + if (!max_speed || max_speed >= 2500) + priv->phylink_config.mac_capabilities |= MAC_2500FD; + } else if (priv->plat->has_xgmac) { + if (!max_speed || max_speed >= 2500) + priv->phylink_config.mac_capabilities |= MAC_2500FD; + if (!max_speed || max_speed >= 5000) + priv->phylink_config.mac_capabilities |= MAC_5000FD; + if (!max_speed || max_speed >= 10000) + priv->phylink_config.mac_capabilities |= MAC_10000FD; + if (!max_speed || max_speed >= 25000) + priv->phylink_config.mac_capabilities |= MAC_25000FD; + if (!max_speed || max_speed >= 40000) + priv->phylink_config.mac_capabilities |= MAC_40000FD; + if (!max_speed || max_speed >= 50000) + priv->phylink_config.mac_capabilities |= MAC_50000FD; + if (!max_speed || max_speed >= 100000) + priv->phylink_config.mac_capabilities |= MAC_100000FD; + } + + /* Half-Duplex can only work with single queue */ + if (priv->plat->tx_queues_to_use > 1) + priv->phylink_config.mac_capabilities &= + ~(MAC_10HD | MAC_100HD | MAC_1000HD); + phylink = phylink_create(&priv->phylink_config, fwnode, mode, &stmmac_phylink_mac_ops); if (IS_ERR(phylink)) return PTR_ERR(phylink); - if (priv->hw->xpcs) - phylink_set_pcs(phylink, &priv->hw->xpcs->pcs); - priv->phylink = phylink; return 0; } @@ -1721,7 +1668,7 @@ static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags) { struct stmmac_priv *priv = netdev_priv(dev); u32 rx_count = priv->plat->rx_queues_to_use; - u32 queue; + int queue; int ret; /* RX INITIALIZATION */ @@ -1748,9 +1695,6 @@ static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags) rx_q->buf_alloc_num = 0; rx_q->xsk_pool = NULL; - if (queue == 0) - break; - queue--; } @@ -3328,7 +3272,7 @@ static int stmmac_hw_setup(struct net_device *dev, bool ptp_register) ret = stmmac_init_ptp(priv); if (ret == -EOPNOTSUPP) - netdev_warn(priv->dev, "PTP not supported by HW\n"); + netdev_info(priv->dev, "PTP not supported by HW\n"); else if (ret) netdev_warn(priv->dev, "PTP init failed\n"); else if (ptp_register) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c index 1c9f02f9c317..e45fb191d8e6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c @@ -39,9 +39,9 @@ static int stmmac_adjust_freq(struct ptp_clock_info *ptp, s32 ppb) diff = div_u64(adj, 1000000000ULL); addend = neg_adj ? (addend - diff) : (addend + diff); - spin_lock_irqsave(&priv->ptp_lock, flags); + write_lock_irqsave(&priv->ptp_lock, flags); stmmac_config_addend(priv, priv->ptpaddr, addend); - spin_unlock_irqrestore(&priv->ptp_lock, flags); + write_unlock_irqrestore(&priv->ptp_lock, flags); return 0; } @@ -86,9 +86,9 @@ static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta) mutex_unlock(&priv->plat->est->lock); } - spin_lock_irqsave(&priv->ptp_lock, flags); + write_lock_irqsave(&priv->ptp_lock, flags); stmmac_adjust_systime(priv, priv->ptpaddr, sec, nsec, neg_adj, xmac); - spin_unlock_irqrestore(&priv->ptp_lock, flags); + write_unlock_irqrestore(&priv->ptp_lock, flags); /* Caculate new basetime and re-configured EST after PTP time adjust. */ if (est_rst) { @@ -137,9 +137,9 @@ static int stmmac_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts) unsigned long flags; u64 ns = 0; - spin_lock_irqsave(&priv->ptp_lock, flags); + read_lock_irqsave(&priv->ptp_lock, flags); stmmac_get_systime(priv, priv->ptpaddr, &ns); - spin_unlock_irqrestore(&priv->ptp_lock, flags); + read_unlock_irqrestore(&priv->ptp_lock, flags); *ts = ns_to_timespec64(ns); @@ -162,9 +162,9 @@ static int stmmac_set_time(struct ptp_clock_info *ptp, container_of(ptp, struct stmmac_priv, ptp_clock_ops); unsigned long flags; - spin_lock_irqsave(&priv->ptp_lock, flags); + write_lock_irqsave(&priv->ptp_lock, flags); stmmac_init_systime(priv, priv->ptpaddr, ts->tv_sec, ts->tv_nsec); - spin_unlock_irqrestore(&priv->ptp_lock, flags); + write_unlock_irqrestore(&priv->ptp_lock, flags); return 0; } @@ -194,12 +194,12 @@ static int stmmac_enable(struct ptp_clock_info *ptp, cfg->period.tv_sec = rq->perout.period.sec; cfg->period.tv_nsec = rq->perout.period.nsec; - spin_lock_irqsave(&priv->ptp_lock, flags); + write_lock_irqsave(&priv->ptp_lock, flags); ret = stmmac_flex_pps_config(priv, priv->ioaddr, rq->perout.index, cfg, on, priv->sub_second_inc, priv->systime_flags); - spin_unlock_irqrestore(&priv->ptp_lock, flags); + write_unlock_irqrestore(&priv->ptp_lock, flags); break; case PTP_CLK_REQ_EXTTS: priv->plat->ext_snapshot_en = on; @@ -314,7 +314,7 @@ void stmmac_ptp_register(struct stmmac_priv *priv) stmmac_ptp_clock_ops.n_per_out = priv->dma_cap.pps_out_num; stmmac_ptp_clock_ops.n_ext_ts = priv->dma_cap.aux_snapshot_n; - spin_lock_init(&priv->ptp_lock); + rwlock_init(&priv->ptp_lock); mutex_init(&priv->aux_ts_lock); priv->ptp_clock_ops = stmmac_ptp_clock_ops; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c index be3cb63675a5..9f1759593b94 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c @@ -1777,9 +1777,9 @@ static int stmmac_test_tbs(struct stmmac_priv *priv) if (ret) return ret; - spin_lock_irqsave(&priv->ptp_lock, flags); + read_lock_irqsave(&priv->ptp_lock, flags); stmmac_get_systime(priv, priv->ptpaddr, &curr_time); - spin_unlock_irqrestore(&priv->ptp_lock, flags); + read_unlock_irqrestore(&priv->ptp_lock, flags); if (!curr_time) { ret = -EOPNOTSUPP; @@ -1799,9 +1799,9 @@ static int stmmac_test_tbs(struct stmmac_priv *priv) goto fail_disable; /* Check if expected time has elapsed */ - spin_lock_irqsave(&priv->ptp_lock, flags); + read_lock_irqsave(&priv->ptp_lock, flags); stmmac_get_systime(priv, priv->ptpaddr, &curr_time); - spin_unlock_irqrestore(&priv->ptp_lock, flags); + read_unlock_irqrestore(&priv->ptp_lock, flags); if ((curr_time - start_time) < STMMAC_TBS_LT_OFFSET) ret = -EINVAL; diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index dba9f12efa1c..b04a6a7bf566 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -88,6 +88,7 @@ #include #include #include +#include #define cas_page_map(x) kmap_atomic((x)) #define cas_page_unmap(x) kunmap_atomic((x)) @@ -1234,19 +1235,6 @@ static void cas_init_rx_dma(struct cas *cp) */ readl(cp->regs + REG_INTR_STATUS_ALIAS); writel(INTR_RX_DONE | INTR_RX_BUF_UNAVAIL, cp->regs + REG_ALIAS_CLEAR); - if (cp->cas_flags & CAS_FLAG_REG_PLUS) { - for (i = 1; i < N_RX_COMP_RINGS; i++) - readl(cp->regs + REG_PLUS_INTRN_STATUS_ALIAS(i)); - - /* 2 is different from 3 and 4 */ - if (N_RX_COMP_RINGS > 1) - writel(INTR_RX_DONE_ALT | INTR_RX_BUF_UNAVAIL_1, - cp->regs + REG_PLUS_ALIASN_CLEAR(1)); - - for (i = 2; i < N_RX_COMP_RINGS; i++) - writel(INTR_RX_DONE_ALT, - cp->regs + REG_PLUS_ALIASN_CLEAR(i)); - } /* set up pause thresholds */ val = CAS_BASE(RX_PAUSE_THRESH_OFF, @@ -3508,9 +3496,6 @@ static inline void cas_start_dma(struct cas *cp) if (N_RX_DESC_RINGS > 1) writel(RX_DESC_RINGN_SIZE(1) - 4, cp->regs + REG_PLUS_RX_KICK1); - - for (i = 1; i < N_RX_COMP_RINGS; i++) - writel(0, cp->regs + REG_PLUS_RX_COMPN_TAIL(i)); } } @@ -4063,8 +4048,8 @@ static void cas_link_timer(struct timer_list *t) if (link_transition_timeout != 0 && cp->link_transition_jiffies_valid && - ((jiffies - cp->link_transition_jiffies) > - (link_transition_timeout))) { + time_is_before_jiffies(cp->link_transition_jiffies + + link_transition_timeout)) { /* One-second counter so link-down workaround doesn't * cause resets to occur so fast as to fool the switch * into thinking the link is down. @@ -4679,7 +4664,7 @@ static void cas_set_msglevel(struct net_device *dev, u32 value) static int cas_get_regs_len(struct net_device *dev) { struct cas *cp = netdev_priv(dev); - return cp->casreg_len < CAS_MAX_REGS ? cp->casreg_len: CAS_MAX_REGS; + return min_t(int, cp->casreg_len, CAS_MAX_REGS); } static void cas_get_regs(struct net_device *dev, struct ethtool_regs *regs, diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index ba8ad76313a9..42460c0885fc 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -7909,7 +7909,7 @@ static int niu_ldg_assign_ldn(struct niu *np, struct niu_parent *parent, * won't get any interrupts and that's painful to debug. */ if (nr64(LDG_NUM(ldn)) != ldg) { - dev_err(np->device, "Port %u, mis-matched LDG assignment for ldn %d, should be %d is %llu\n", + dev_err(np->device, "Port %u, mismatched LDG assignment for ldn %d, should be %d is %llu\n", np->port, ldn, ldg, (unsigned long long) nr64(LDG_NUM(ldn))); return -EINVAL; diff --git a/drivers/net/ethernet/ti/am65-cpsw-ethtool.c b/drivers/net/ethernet/ti/am65-cpsw-ethtool.c index d45b6bb86f0b..72acdf802258 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-ethtool.c +++ b/drivers/net/ethernet/ti/am65-cpsw-ethtool.c @@ -6,7 +6,7 @@ */ #include -#include +#include #include #include @@ -471,9 +471,7 @@ static void am65_cpsw_get_pauseparam(struct net_device *ndev, { struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); - pause->autoneg = AUTONEG_DISABLE; - pause->rx_pause = salve->rx_pause ? true : false; - pause->tx_pause = salve->tx_pause ? true : false; + phylink_ethtool_get_pauseparam(salve->phylink, pause); } static int am65_cpsw_set_pauseparam(struct net_device *ndev, @@ -481,18 +479,7 @@ static int am65_cpsw_set_pauseparam(struct net_device *ndev, { struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); - if (!salve->phy) - return -EINVAL; - - if (!phy_validate_pause(salve->phy, pause)) - return -EINVAL; - - salve->rx_pause = pause->rx_pause ? true : false; - salve->tx_pause = pause->tx_pause ? true : false; - - phy_set_asym_pause(salve->phy, salve->rx_pause, salve->tx_pause); - - return 0; + return phylink_ethtool_set_pauseparam(salve->phylink, pause); } static void am65_cpsw_get_wol(struct net_device *ndev, @@ -500,11 +487,7 @@ static void am65_cpsw_get_wol(struct net_device *ndev, { struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); - wol->supported = 0; - wol->wolopts = 0; - - if (salve->phy) - phy_ethtool_get_wol(salve->phy, wol); + phylink_ethtool_get_wol(salve->phylink, wol); } static int am65_cpsw_set_wol(struct net_device *ndev, @@ -512,10 +495,7 @@ static int am65_cpsw_set_wol(struct net_device *ndev, { struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); - if (!salve->phy) - return -EOPNOTSUPP; - - return phy_ethtool_set_wol(salve->phy, wol); + return phylink_ethtool_set_wol(salve->phylink, wol); } static int am65_cpsw_get_link_ksettings(struct net_device *ndev, @@ -523,11 +503,7 @@ static int am65_cpsw_get_link_ksettings(struct net_device *ndev, { struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); - if (!salve->phy) - return -EOPNOTSUPP; - - phy_ethtool_ksettings_get(salve->phy, ecmd); - return 0; + return phylink_ethtool_ksettings_get(salve->phylink, ecmd); } static int @@ -536,40 +512,28 @@ am65_cpsw_set_link_ksettings(struct net_device *ndev, { struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); - if (!salve->phy || phy_is_pseudo_fixed_link(salve->phy)) - return -EOPNOTSUPP; - - return phy_ethtool_ksettings_set(salve->phy, ecmd); + return phylink_ethtool_ksettings_set(salve->phylink, ecmd); } static int am65_cpsw_get_eee(struct net_device *ndev, struct ethtool_eee *edata) { struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); - if (!salve->phy || phy_is_pseudo_fixed_link(salve->phy)) - return -EOPNOTSUPP; - - return phy_ethtool_get_eee(salve->phy, edata); + return phylink_ethtool_get_eee(salve->phylink, edata); } static int am65_cpsw_set_eee(struct net_device *ndev, struct ethtool_eee *edata) { struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); - if (!salve->phy || phy_is_pseudo_fixed_link(salve->phy)) - return -EOPNOTSUPP; - - return phy_ethtool_set_eee(salve->phy, edata); + return phylink_ethtool_set_eee(salve->phylink, edata); } static int am65_cpsw_nway_reset(struct net_device *ndev) { struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); - if (!salve->phy || phy_is_pseudo_fixed_link(salve->phy)) - return -EOPNOTSUPP; - - return phy_restart_aneg(salve->phy); + return phylink_ethtool_nway_reset(salve->phylink); } static int am65_cpsw_get_regs_len(struct net_device *ndev) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 8251d7eb001b..d2747e9db286 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -159,69 +159,6 @@ static void am65_cpsw_nuss_get_ver(struct am65_cpsw_common *common) common->pdata.quirks); } -void am65_cpsw_nuss_adjust_link(struct net_device *ndev) -{ - struct am65_cpsw_common *common = am65_ndev_to_common(ndev); - struct am65_cpsw_port *port = am65_ndev_to_port(ndev); - struct phy_device *phy = port->slave.phy; - u32 mac_control = 0; - - if (!phy) - return; - - if (phy->link) { - mac_control = CPSW_SL_CTL_GMII_EN; - - if (phy->speed == 1000) - mac_control |= CPSW_SL_CTL_GIG; - if (phy->speed == 10 && phy_interface_is_rgmii(phy)) - /* Can be used with in band mode only */ - mac_control |= CPSW_SL_CTL_EXT_EN; - if (phy->speed == 100 && phy->interface == PHY_INTERFACE_MODE_RMII) - mac_control |= CPSW_SL_CTL_IFCTL_A; - if (phy->duplex) - mac_control |= CPSW_SL_CTL_FULLDUPLEX; - - /* RGMII speed is 100M if !CPSW_SL_CTL_GIG*/ - - /* rx_pause/tx_pause */ - if (port->slave.rx_pause) - mac_control |= CPSW_SL_CTL_RX_FLOW_EN; - - if (port->slave.tx_pause) - mac_control |= CPSW_SL_CTL_TX_FLOW_EN; - - cpsw_sl_ctl_set(port->slave.mac_sl, mac_control); - - /* enable forwarding */ - cpsw_ale_control_set(common->ale, port->port_id, - ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); - - am65_cpsw_qos_link_up(ndev, phy->speed); - netif_tx_wake_all_queues(ndev); - } else { - int tmo; - - /* disable forwarding */ - cpsw_ale_control_set(common->ale, port->port_id, - ALE_PORT_STATE, ALE_PORT_STATE_DISABLE); - - cpsw_sl_ctl_set(port->slave.mac_sl, CPSW_SL_CTL_CMD_IDLE); - - tmo = cpsw_sl_wait_for_idle(port->slave.mac_sl, 100); - dev_dbg(common->dev, "donw msc_sl %08x tmo %d\n", - cpsw_sl_reg_read(port->slave.mac_sl, CPSW_SL_MACSTATUS), - tmo); - - cpsw_sl_ctl_reset(port->slave.mac_sl); - - am65_cpsw_qos_link_down(ndev); - netif_tx_stop_all_queues(ndev); - } - - phy_print_status(phy); -} - static int am65_cpsw_nuss_ndo_slave_add_vid(struct net_device *ndev, __be16 proto, u16 vid) { @@ -589,15 +526,11 @@ static int am65_cpsw_nuss_ndo_slave_stop(struct net_device *ndev) struct am65_cpsw_port *port = am65_ndev_to_port(ndev); int ret; - if (port->slave.phy) - phy_stop(port->slave.phy); + phylink_stop(port->slave.phylink); netif_tx_stop_all_queues(ndev); - if (port->slave.phy) { - phy_disconnect(port->slave.phy); - port->slave.phy = NULL; - } + phylink_disconnect_phy(port->slave.phylink); ret = am65_cpsw_nuss_common_stop(common); if (ret) @@ -667,25 +600,14 @@ static int am65_cpsw_nuss_ndo_slave_open(struct net_device *ndev) if (ret) goto error_cleanup; - if (port->slave.phy_node) { - port->slave.phy = of_phy_connect(ndev, - port->slave.phy_node, - &am65_cpsw_nuss_adjust_link, - 0, port->slave.phy_if); - if (!port->slave.phy) { - dev_err(common->dev, "phy %pOF not found on slave %d\n", - port->slave.phy_node, - port->port_id); - ret = -ENODEV; - goto error_cleanup; - } - } + ret = phylink_of_phy_connect(port->slave.phylink, port->slave.phy_node, 0); + if (ret) + goto error_cleanup; /* restore vlan configurations */ vlan_for_each(ndev, cpsw_restore_vlans, port); - phy_attached_info(port->slave.phy); - phy_start(port->slave.phy); + phylink_start(port->slave.phylink); return 0; @@ -1431,10 +1353,7 @@ static int am65_cpsw_nuss_ndo_slave_ioctl(struct net_device *ndev, return am65_cpsw_nuss_hwtstamp_get(ndev, req); } - if (!port->slave.phy) - return -EOPNOTSUPP; - - return phy_mii_ioctl(port->slave.phy, req, cmd); + return phylink_mii_ioctl(port->slave.phylink, req, cmd); } static void am65_cpsw_nuss_ndo_get_stats(struct net_device *dev, @@ -1494,6 +1413,81 @@ static const struct net_device_ops am65_cpsw_nuss_netdev_ops = { .ndo_get_devlink_port = am65_cpsw_ndo_get_devlink_port, }; +static void am65_cpsw_nuss_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + /* Currently not used */ +} + +static void am65_cpsw_nuss_mac_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct am65_cpsw_slave_data *slave = container_of(config, struct am65_cpsw_slave_data, + phylink_config); + struct am65_cpsw_port *port = container_of(slave, struct am65_cpsw_port, slave); + struct am65_cpsw_common *common = port->common; + struct net_device *ndev = port->ndev; + int tmo; + + /* disable forwarding */ + cpsw_ale_control_set(common->ale, port->port_id, ALE_PORT_STATE, ALE_PORT_STATE_DISABLE); + + cpsw_sl_ctl_set(port->slave.mac_sl, CPSW_SL_CTL_CMD_IDLE); + + tmo = cpsw_sl_wait_for_idle(port->slave.mac_sl, 100); + dev_dbg(common->dev, "down msc_sl %08x tmo %d\n", + cpsw_sl_reg_read(port->slave.mac_sl, CPSW_SL_MACSTATUS), tmo); + + cpsw_sl_ctl_reset(port->slave.mac_sl); + + am65_cpsw_qos_link_down(ndev); + netif_tx_stop_all_queues(ndev); +} + +static void am65_cpsw_nuss_mac_link_up(struct phylink_config *config, struct phy_device *phy, + unsigned int mode, phy_interface_t interface, int speed, + int duplex, bool tx_pause, bool rx_pause) +{ + struct am65_cpsw_slave_data *slave = container_of(config, struct am65_cpsw_slave_data, + phylink_config); + struct am65_cpsw_port *port = container_of(slave, struct am65_cpsw_port, slave); + struct am65_cpsw_common *common = port->common; + u32 mac_control = CPSW_SL_CTL_GMII_EN; + struct net_device *ndev = port->ndev; + + if (speed == SPEED_1000) + mac_control |= CPSW_SL_CTL_GIG; + if (speed == SPEED_10 && interface == PHY_INTERFACE_MODE_RGMII) + /* Can be used with in band mode only */ + mac_control |= CPSW_SL_CTL_EXT_EN; + if (speed == SPEED_100 && interface == PHY_INTERFACE_MODE_RMII) + mac_control |= CPSW_SL_CTL_IFCTL_A; + if (duplex) + mac_control |= CPSW_SL_CTL_FULLDUPLEX; + + /* rx_pause/tx_pause */ + if (rx_pause) + mac_control |= CPSW_SL_CTL_RX_FLOW_EN; + + if (tx_pause) + mac_control |= CPSW_SL_CTL_TX_FLOW_EN; + + cpsw_sl_ctl_set(port->slave.mac_sl, mac_control); + + /* enable forwarding */ + cpsw_ale_control_set(common->ale, port->port_id, ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); + + am65_cpsw_qos_link_up(ndev, speed); + netif_tx_wake_all_queues(ndev); +} + +static const struct phylink_mac_ops am65_cpsw_phylink_mac_ops = { + .validate = phylink_generic_validate, + .mac_config = am65_cpsw_nuss_mac_config, + .mac_link_down = am65_cpsw_nuss_mac_link_down, + .mac_link_up = am65_cpsw_nuss_mac_link_up, +}; + static void am65_cpsw_nuss_slave_disable_unused(struct am65_cpsw_port *port) { struct am65_cpsw_common *common = port->common; @@ -1890,27 +1884,7 @@ static int am65_cpsw_nuss_init_slave_ports(struct am65_cpsw_common *common) of_property_read_bool(port_np, "ti,mac-only"); /* get phy/link info */ - if (of_phy_is_fixed_link(port_np)) { - ret = of_phy_register_fixed_link(port_np); - if (ret) { - ret = dev_err_probe(dev, ret, - "failed to register fixed-link phy %pOF\n", - port_np); - goto of_node_put; - } - port->slave.phy_node = of_node_get(port_np); - } else { - port->slave.phy_node = - of_parse_phandle(port_np, "phy-handle", 0); - } - - if (!port->slave.phy_node) { - dev_err(dev, - "slave[%d] no phy found\n", port_id); - ret = -ENODEV; - goto of_node_put; - } - + port->slave.phy_node = port_np; ret = of_get_phy_mode(port_np, &port->slave.phy_if); if (ret) { dev_err(dev, "%pOF read phy-mode err %d\n", @@ -1952,12 +1926,25 @@ static void am65_cpsw_pcpu_stats_free(void *data) free_percpu(stats); } +static void am65_cpsw_nuss_phylink_cleanup(struct am65_cpsw_common *common) +{ + struct am65_cpsw_port *port; + int i; + + for (i = 0; i < common->port_num; i++) { + port = &common->ports[i]; + if (port->slave.phylink) + phylink_destroy(port->slave.phylink); + } +} + static int am65_cpsw_nuss_init_port_ndev(struct am65_cpsw_common *common, u32 port_idx) { struct am65_cpsw_ndev_priv *ndev_priv; struct device *dev = common->dev; struct am65_cpsw_port *port; + struct phylink *phylink; int ret; port = &common->ports[port_idx]; @@ -1995,6 +1982,20 @@ am65_cpsw_nuss_init_port_ndev(struct am65_cpsw_common *common, u32 port_idx) port->ndev->netdev_ops = &am65_cpsw_nuss_netdev_ops; port->ndev->ethtool_ops = &am65_cpsw_ethtool_ops_slave; + /* Configuring Phylink */ + port->slave.phylink_config.dev = &port->ndev->dev; + port->slave.phylink_config.type = PHYLINK_NETDEV; + port->slave.phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD; + + phy_interface_set_rgmii(port->slave.phylink_config.supported_interfaces); + + phylink = phylink_create(&port->slave.phylink_config, dev->fwnode, port->slave.phy_if, + &am65_cpsw_phylink_mac_ops); + if (IS_ERR(phylink)) + return PTR_ERR(phylink); + + port->slave.phylink = phylink; + /* Disable TX checksum offload by default due to HW bug */ if (common->pdata.quirks & AM65_CPSW_QUIRK_I2027_NO_TX_CSUM) port->ndev->features &= ~NETIF_F_HW_CSUM; @@ -2761,15 +2762,17 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) ret = am65_cpsw_nuss_init_ndevs(common); if (ret) - goto err_of_clear; + goto err_free_phylink; ret = am65_cpsw_nuss_register_ndevs(common); if (ret) - goto err_of_clear; + goto err_free_phylink; pm_runtime_put(dev); return 0; +err_free_phylink: + am65_cpsw_nuss_phylink_cleanup(common); err_of_clear: of_platform_device_destroy(common->mdio_dev, NULL); err_pm_clear: @@ -2792,6 +2795,7 @@ static int am65_cpsw_nuss_remove(struct platform_device *pdev) return ret; } + am65_cpsw_nuss_phylink_cleanup(common); am65_cpsw_unregister_devlink(common); am65_cpsw_unregister_notifiers(common); diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.h b/drivers/net/ethernet/ti/am65-cpsw-nuss.h index 048ed10143c1..ac945631bf2f 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.h +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.h @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -30,13 +30,14 @@ struct am65_cpsw_slave_data { bool mac_only; struct cpsw_sl *mac_sl; struct device_node *phy_node; - struct phy_device *phy; phy_interface_t phy_if; struct phy *ifphy; bool rx_pause; bool tx_pause; u8 mac_addr[ETH_ALEN]; int port_vlan; + struct phylink *phylink; + struct phylink_config phylink_config; }; struct am65_cpsw_port { diff --git a/drivers/net/ethernet/ti/am65-cpsw-switchdev.c b/drivers/net/ethernet/ti/am65-cpsw-switchdev.c index 599708a3e81d..d4c56da98a6a 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-switchdev.c +++ b/drivers/net/ethernet/ti/am65-cpsw-switchdev.c @@ -237,15 +237,11 @@ static int am65_cpsw_port_vlans_add(struct am65_cpsw_port *port, { bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; struct net_device *orig_dev = vlan->obj.orig_dev; - bool cpu_port = netif_is_bridge_master(orig_dev); bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; netdev_dbg(port->ndev, "VID add: %s: vid:%u flags:%X\n", port->ndev->name, vlan->vid, vlan->flags); - if (cpu_port && !(vlan->flags & BRIDGE_VLAN_INFO_BRENTRY)) - return 0; - return am65_cpsw_port_vlan_add(port, untag, pvid, vlan->vid, orig_dev); } diff --git a/drivers/net/ethernet/ti/cpsw_ethtool.c b/drivers/net/ethernet/ti/cpsw_ethtool.c index aa42141be3c0..a557a477d039 100644 --- a/drivers/net/ethernet/ti/cpsw_ethtool.c +++ b/drivers/net/ethernet/ti/cpsw_ethtool.c @@ -364,11 +364,9 @@ int cpsw_ethtool_op_begin(struct net_device *ndev) struct cpsw_common *cpsw = priv->cpsw; int ret; - ret = pm_runtime_get_sync(cpsw->dev); - if (ret < 0) { + ret = pm_runtime_resume_and_get(cpsw->dev); + if (ret < 0) cpsw_err(priv, drv, "ethtool begin failed %d\n", ret); - pm_runtime_put_noidle(cpsw->dev); - } return ret; } diff --git a/drivers/net/ethernet/ti/cpsw_switchdev.c b/drivers/net/ethernet/ti/cpsw_switchdev.c index a7d97d429e06..ce85f7610273 100644 --- a/drivers/net/ethernet/ti/cpsw_switchdev.c +++ b/drivers/net/ethernet/ti/cpsw_switchdev.c @@ -252,15 +252,11 @@ static int cpsw_port_vlans_add(struct cpsw_priv *priv, { bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; struct net_device *orig_dev = vlan->obj.orig_dev; - bool cpu_port = netif_is_bridge_master(orig_dev); bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; dev_dbg(priv->dev, "VID add: %s: vid:%u flags:%X\n", priv->ndev->name, vlan->vid, vlan->flags); - if (cpu_port && !(vlan->flags & BRIDGE_VLAN_INFO_BRENTRY)) - return 0; - return cpsw_port_vlan_add(priv, untag, pvid, vlan->vid, orig_dev); } diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 31df3267a01a..4b6aed78d392 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1604,6 +1604,7 @@ static int emac_dev_stop(struct net_device *ndev) int irq_num; struct emac_priv *priv = netdev_priv(ndev); struct device *emac_dev = &ndev->dev; + int ret = 0; /* inform the upper layers. */ netif_stop_queue(ndev); @@ -1618,17 +1619,31 @@ static int emac_dev_stop(struct net_device *ndev) phy_disconnect(ndev->phydev); /* Free IRQ */ - while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i))) { - for (irq_num = res->start; irq_num <= res->end; irq_num++) - free_irq(irq_num, priv->ndev); - i++; + if (dev_of_node(&priv->pdev->dev)) { + do { + ret = platform_get_irq_optional(priv->pdev, i); + if (ret < 0 && ret != -ENXIO) + break; + if (ret > 0) { + free_irq(ret, priv->ndev); + } else { + ret = 0; + break; + } + } while (++i); + } else { + while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i))) { + for (irq_num = res->start; irq_num <= res->end; irq_num++) + free_irq(irq_num, priv->ndev); + i++; + } } if (netif_msg_drv(priv)) dev_notice(emac_dev, "DaVinci EMAC: %s stopped\n", ndev->name); pm_runtime_put(&priv->pdev->dev); - return 0; + return ret; } /** diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index b818e4579f6f..16507bff652a 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -2082,7 +2082,7 @@ static int netcp_create_interface(struct netcp_device *netcp_device, netcp->tx_pool_region_id = temp[1]; if (netcp->tx_pool_size < MAX_SKB_FRAGS) { - dev_err(dev, "tx-pool size too small, must be atleast(%ld)\n", + dev_err(dev, "tx-pool size too small, must be at least %ld\n", MAX_SKB_FRAGS); ret = -ENODEV; goto quit; diff --git a/drivers/net/ethernet/vertexcom/mse102x.c b/drivers/net/ethernet/vertexcom/mse102x.c index 25739b182ac7..eb39a45de012 100644 --- a/drivers/net/ethernet/vertexcom/mse102x.c +++ b/drivers/net/ethernet/vertexcom/mse102x.c @@ -362,7 +362,7 @@ static void mse102x_rx_pkt_spi(struct mse102x_net *mse) mse102x_dump_packet(__func__, skb->len, skb->data); skb->protocol = eth_type_trans(skb, mse->ndev); - netif_rx_ni(skb); + netif_rx(skb); mse->ndev->stats.rx_packets++; mse->ndev->stats.rx_bytes += rxlen; diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c index ae24d6b86803..4fd7c39e1123 100644 --- a/drivers/net/ethernet/wiznet/w5100.c +++ b/drivers/net/ethernet/wiznet/w5100.c @@ -883,7 +883,7 @@ static void w5100_rx_work(struct work_struct *work) struct sk_buff *skb; while ((skb = w5100_rx_skb(priv->ndev))) - netif_rx_ni(skb); + netif_rx(skb); w5100_enable_intr(priv); } diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig index 911b5ef9e680..0014729b8865 100644 --- a/drivers/net/ethernet/xilinx/Kconfig +++ b/drivers/net/ethernet/xilinx/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only # -# Xilink device configuration +# Xilinx device configuration # config NET_VENDOR_XILINX diff --git a/drivers/net/ethernet/xilinx/ll_temac.h b/drivers/net/ethernet/xilinx/ll_temac.h index 4a73127e10a6..c6395c406418 100644 --- a/drivers/net/ethernet/xilinx/ll_temac.h +++ b/drivers/net/ethernet/xilinx/ll_temac.h @@ -271,7 +271,7 @@ This option defaults to enabled (set) */ #define XTE_TIE_OFFSET 0x000003A4 /* Interrupt enable */ -/** MII Mamagement Control register (MGTCR) */ +/* MII Management Control register (MGTCR) */ #define XTE_MGTDR_OFFSET 0x000003B0 /* MII data */ #define XTE_MIIMAI_OFFSET 0x000003B4 /* MII control */ @@ -283,7 +283,7 @@ This option defaults to enabled (set) */ #define STS_CTRL_APP0_ERR (1 << 31) #define STS_CTRL_APP0_IRQONEND (1 << 30) -/* undoccumented */ +/* undocumented */ #define STS_CTRL_APP0_STOPONEND (1 << 29) #define STS_CTRL_APP0_CMPLT (1 << 28) #define STS_CTRL_APP0_SOP (1 << 27) diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 64c7e26c3b75..869e362e09c1 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -361,8 +361,9 @@ static int temac_dma_bd_init(struct net_device *ndev) lp->rx_bd_v[i].next = cpu_to_be32(lp->rx_bd_p + sizeof(*lp->rx_bd_v) * ((i + 1) % lp->rx_bd_num)); - skb = netdev_alloc_skb_ip_align(ndev, - XTE_MAX_JUMBO_FRAME_SIZE); + skb = __netdev_alloc_skb_ip_align(ndev, + XTE_MAX_JUMBO_FRAME_SIZE, + GFP_KERNEL); if (!skb) goto out; @@ -1008,7 +1009,7 @@ static void ll_temac_recv(struct net_device *ndev) (skb->len > 64)) { /* Convert from device endianness (be32) to cpu - * endiannes, and if necessary swap the bytes + * endianness, and if necessary swap the bytes * (back) for proper IP checksum byte order * (be16). */ diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index 5b4d153b1492..0f9c88dd1a4a 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -119,11 +119,11 @@ #define XAXIDMA_IRQ_ERROR_MASK 0x00004000 /* Error interrupt */ #define XAXIDMA_IRQ_ALL_MASK 0x00007000 /* All interrupts */ -/* Default TX/RX Threshold and waitbound values for SGDMA mode */ +/* Default TX/RX Threshold and delay timer values for SGDMA mode */ #define XAXIDMA_DFT_TX_THRESHOLD 24 -#define XAXIDMA_DFT_TX_WAITBOUND 254 -#define XAXIDMA_DFT_RX_THRESHOLD 24 -#define XAXIDMA_DFT_RX_WAITBOUND 254 +#define XAXIDMA_DFT_TX_USEC 50 +#define XAXIDMA_DFT_RX_THRESHOLD 1 +#define XAXIDMA_DFT_RX_USEC 50 #define XAXIDMA_BD_CTRL_TXSOF_MASK 0x08000000 /* First tx packet */ #define XAXIDMA_BD_CTRL_TXEOF_MASK 0x04000000 /* Last tx packet */ @@ -385,7 +385,9 @@ struct axidma_bd { * @phy_node: Pointer to device node structure * @phylink: Pointer to phylink instance * @phylink_config: phylink configuration settings + * @napi: NAPI control structure * @pcs_phy: Reference to PCS/PMA PHY if used + * @pcs: phylink pcs structure for PCS PHY * @switch_x_sgmii: Whether switchable 1000BaseX/SGMII mode is enabled in the core * @axi_clk: AXI4-Lite bus clock * @misc_clks: Misc ethernet clocks (AXI4-Stream, Ref, MGT clocks) @@ -394,6 +396,7 @@ struct axidma_bd { * @regs_start: Resource start for axienet device addresses * @regs: Base address for the axienet_local device address space * @dma_regs: Base address for the axidma device address space + * @rx_dma_cr: Nominal content of RX DMA control register * @dma_err_task: Work structure to process Axi DMA errors * @tx_irq: Axidma TX IRQ number * @rx_irq: Axidma RX IRQ number @@ -422,7 +425,9 @@ struct axidma_bd { * @csum_offload_on_tx_path: Stores the checksum selection on TX side. * @csum_offload_on_rx_path: Stores the checksum selection on RX side. * @coalesce_count_rx: Store the irq coalesce on RX side. + * @coalesce_usec_rx: IRQ coalesce delay for RX * @coalesce_count_tx: Store the irq coalesce on TX side. + * @coalesce_usec_tx: IRQ coalesce delay for TX */ struct axienet_local { struct net_device *ndev; @@ -433,7 +438,10 @@ struct axienet_local { struct phylink *phylink; struct phylink_config phylink_config; + struct napi_struct napi; + struct mdio_device *pcs_phy; + struct phylink_pcs pcs; bool switch_x_sgmii; @@ -447,6 +455,8 @@ struct axienet_local { void __iomem *regs; void __iomem *dma_regs; + u32 rx_dma_cr; + struct work_struct dma_err_task; int tx_irq; @@ -474,7 +484,9 @@ struct axienet_local { int csum_offload_on_rx_path; u32 coalesce_count_rx; + u32 coalesce_usec_rx; u32 coalesce_count_tx; + u32 coalesce_usec_tx; }; /** diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 377c94ec2486..c7eb05e4a6bf 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -7,7 +7,7 @@ * Copyright (c) 2008-2009 Secret Lab Technologies Ltd. * Copyright (c) 2010 - 2011 Michal Simek * Copyright (c) 2010 - 2011 PetaLogix - * Copyright (c) 2019 SED Systems, a division of Calian Ltd. + * Copyright (c) 2019 - 2022 Calian Advanced Technologies * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved. * * This is a driver for the Xilinx Axi Ethernet which is used in the Virtex6 @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include #include @@ -190,7 +190,7 @@ static void axienet_dma_bd_release(struct net_device *ndev) struct axienet_local *lp = netdev_priv(ndev); /* If we end up here, tx_bd_v must have been DMA allocated. */ - dma_free_coherent(ndev->dev.parent, + dma_free_coherent(lp->dev, sizeof(*lp->tx_bd_v) * lp->tx_bd_num, lp->tx_bd_v, lp->tx_bd_p); @@ -215,17 +215,89 @@ static void axienet_dma_bd_release(struct net_device *ndev) */ if (lp->rx_bd_v[i].cntrl) { phys = desc_get_phys_addr(lp, &lp->rx_bd_v[i]); - dma_unmap_single(ndev->dev.parent, phys, + dma_unmap_single(lp->dev, phys, lp->max_frm_size, DMA_FROM_DEVICE); } } - dma_free_coherent(ndev->dev.parent, + dma_free_coherent(lp->dev, sizeof(*lp->rx_bd_v) * lp->rx_bd_num, lp->rx_bd_v, lp->rx_bd_p); } +/** + * axienet_usec_to_timer - Calculate IRQ delay timer value + * @lp: Pointer to the axienet_local structure + * @coalesce_usec: Microseconds to convert into timer value + */ +static u32 axienet_usec_to_timer(struct axienet_local *lp, u32 coalesce_usec) +{ + u32 result; + u64 clk_rate = 125000000; /* arbitrary guess if no clock rate set */ + + if (lp->axi_clk) + clk_rate = clk_get_rate(lp->axi_clk); + + /* 1 Timeout Interval = 125 * (clock period of SG clock) */ + result = DIV64_U64_ROUND_CLOSEST((u64)coalesce_usec * clk_rate, + (u64)125000000); + if (result > 255) + result = 255; + + return result; +} + +/** + * axienet_dma_start - Set up DMA registers and start DMA operation + * @lp: Pointer to the axienet_local structure + */ +static void axienet_dma_start(struct axienet_local *lp) +{ + u32 tx_cr; + + /* Start updating the Rx channel control register */ + lp->rx_dma_cr = (lp->coalesce_count_rx << XAXIDMA_COALESCE_SHIFT) | + XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_ERROR_MASK; + /* Only set interrupt delay timer if not generating an interrupt on + * the first RX packet. Otherwise leave at 0 to disable delay interrupt. + */ + if (lp->coalesce_count_rx > 1) + lp->rx_dma_cr |= (axienet_usec_to_timer(lp, lp->coalesce_usec_rx) + << XAXIDMA_DELAY_SHIFT) | + XAXIDMA_IRQ_DELAY_MASK; + axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, lp->rx_dma_cr); + + /* Start updating the Tx channel control register */ + tx_cr = (lp->coalesce_count_tx << XAXIDMA_COALESCE_SHIFT) | + XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_ERROR_MASK; + /* Only set interrupt delay timer if not generating an interrupt on + * the first TX packet. Otherwise leave at 0 to disable delay interrupt. + */ + if (lp->coalesce_count_tx > 1) + tx_cr |= (axienet_usec_to_timer(lp, lp->coalesce_usec_tx) + << XAXIDMA_DELAY_SHIFT) | + XAXIDMA_IRQ_DELAY_MASK; + axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, tx_cr); + + /* Populate the tail pointer and bring the Rx Axi DMA engine out of + * halted state. This will make the Rx side ready for reception. + */ + axienet_dma_out_addr(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p); + lp->rx_dma_cr |= XAXIDMA_CR_RUNSTOP_MASK; + axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, lp->rx_dma_cr); + axienet_dma_out_addr(lp, XAXIDMA_RX_TDESC_OFFSET, lp->rx_bd_p + + (sizeof(*lp->rx_bd_v) * (lp->rx_bd_num - 1))); + + /* Write to the RS (Run-stop) bit in the Tx channel control register. + * Tx channel is now ready to run. But only after we write to the + * tail pointer register that the Tx channel will start transmitting. + */ + axienet_dma_out_addr(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p); + tx_cr |= XAXIDMA_CR_RUNSTOP_MASK; + axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, tx_cr); +} + /** * axienet_dma_bd_init - Setup buffer descriptor rings for Axi DMA * @ndev: Pointer to the net_device structure @@ -238,7 +310,6 @@ static void axienet_dma_bd_release(struct net_device *ndev) */ static int axienet_dma_bd_init(struct net_device *ndev) { - u32 cr; int i; struct sk_buff *skb; struct axienet_local *lp = netdev_priv(ndev); @@ -249,13 +320,13 @@ static int axienet_dma_bd_init(struct net_device *ndev) lp->rx_bd_ci = 0; /* Allocate the Tx and Rx buffer descriptors. */ - lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent, + lp->tx_bd_v = dma_alloc_coherent(lp->dev, sizeof(*lp->tx_bd_v) * lp->tx_bd_num, &lp->tx_bd_p, GFP_KERNEL); if (!lp->tx_bd_v) return -ENOMEM; - lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent, + lp->rx_bd_v = dma_alloc_coherent(lp->dev, sizeof(*lp->rx_bd_v) * lp->rx_bd_num, &lp->rx_bd_p, GFP_KERNEL); if (!lp->rx_bd_v) @@ -285,9 +356,9 @@ static int axienet_dma_bd_init(struct net_device *ndev) goto out; lp->rx_bd_v[i].skb = skb; - addr = dma_map_single(ndev->dev.parent, skb->data, + addr = dma_map_single(lp->dev, skb->data, lp->max_frm_size, DMA_FROM_DEVICE); - if (dma_mapping_error(ndev->dev.parent, addr)) { + if (dma_mapping_error(lp->dev, addr)) { netdev_err(ndev, "DMA mapping error\n"); goto out; } @@ -296,50 +367,7 @@ static int axienet_dma_bd_init(struct net_device *ndev) lp->rx_bd_v[i].cntrl = lp->max_frm_size; } - /* Start updating the Rx channel control register */ - cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); - /* Update the interrupt coalesce count */ - cr = ((cr & ~XAXIDMA_COALESCE_MASK) | - ((lp->coalesce_count_rx) << XAXIDMA_COALESCE_SHIFT)); - /* Update the delay timer count */ - cr = ((cr & ~XAXIDMA_DELAY_MASK) | - (XAXIDMA_DFT_RX_WAITBOUND << XAXIDMA_DELAY_SHIFT)); - /* Enable coalesce, delay timer and error interrupts */ - cr |= XAXIDMA_IRQ_ALL_MASK; - /* Write to the Rx channel control register */ - axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); - - /* Start updating the Tx channel control register */ - cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); - /* Update the interrupt coalesce count */ - cr = (((cr & ~XAXIDMA_COALESCE_MASK)) | - ((lp->coalesce_count_tx) << XAXIDMA_COALESCE_SHIFT)); - /* Update the delay timer count */ - cr = (((cr & ~XAXIDMA_DELAY_MASK)) | - (XAXIDMA_DFT_TX_WAITBOUND << XAXIDMA_DELAY_SHIFT)); - /* Enable coalesce, delay timer and error interrupts */ - cr |= XAXIDMA_IRQ_ALL_MASK; - /* Write to the Tx channel control register */ - axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr); - - /* Populate the tail pointer and bring the Rx Axi DMA engine out of - * halted state. This will make the Rx side ready for reception. - */ - axienet_dma_out_addr(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p); - cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); - axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, - cr | XAXIDMA_CR_RUNSTOP_MASK); - axienet_dma_out_addr(lp, XAXIDMA_RX_TDESC_OFFSET, lp->rx_bd_p + - (sizeof(*lp->rx_bd_v) * (lp->rx_bd_num - 1))); - - /* Write to the RS (Run-stop) bit in the Tx channel control register. - * Tx channel is now ready to run. But only after we write to the - * tail pointer register that the Tx channel will start transmitting. - */ - axienet_dma_out_addr(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p); - cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); - axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, - cr | XAXIDMA_CR_RUNSTOP_MASK); + axienet_dma_start(lp); return 0; out: @@ -530,6 +558,44 @@ static int __axienet_device_reset(struct axienet_local *lp) return 0; } +/** + * axienet_dma_stop - Stop DMA operation + * @lp: Pointer to the axienet_local structure + */ +static void axienet_dma_stop(struct axienet_local *lp) +{ + int count; + u32 cr, sr; + + cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); + cr &= ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK); + axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); + synchronize_irq(lp->rx_irq); + + cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); + cr &= ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK); + axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr); + synchronize_irq(lp->tx_irq); + + /* Give DMAs a chance to halt gracefully */ + sr = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET); + for (count = 0; !(sr & XAXIDMA_SR_HALT_MASK) && count < 5; ++count) { + msleep(20); + sr = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET); + } + + sr = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET); + for (count = 0; !(sr & XAXIDMA_SR_HALT_MASK) && count < 5; ++count) { + msleep(20); + sr = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET); + } + + /* Do a reset to ensure DMA is really stopped */ + axienet_lock_mii(lp); + __axienet_device_reset(lp); + axienet_unlock_mii(lp); +} + /** * axienet_device_reset - Reset and initialize the Axi Ethernet hardware. * @ndev: Pointer to the net_device structure @@ -537,7 +603,7 @@ static int __axienet_device_reset(struct axienet_local *lp) * This function is called to reset and initialize the Axi Ethernet core. This * is typically called during initialization. It does a reset of the Axi DMA * Rx/Tx channels and initializes the Axi DMA BDs. Since Axi DMA reset lines - * areconnected to Axi Ethernet reset lines, this in turn resets the Axi + * are connected to Axi Ethernet reset lines, this in turn resets the Axi * Ethernet core. No separate hardware reset is done for the Axi Ethernet * core. * Returns 0 on success or a negative error number otherwise. @@ -636,7 +702,7 @@ static int axienet_free_tx_chain(struct net_device *ndev, u32 first_bd, /* Ensure we see complete descriptor update */ dma_rmb(); phys = desc_get_phys_addr(lp, cur_p); - dma_unmap_single(ndev->dev.parent, phys, + dma_unmap_single(lp->dev, phys, (cur_p->cntrl & XAXIDMA_BD_CTRL_LENGTH_MASK), DMA_TO_DEVICE); @@ -774,9 +840,9 @@ axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev) cur_p->app0 |= 2; /* Tx Full Checksum Offload Enabled */ } - phys = dma_map_single(ndev->dev.parent, skb->data, + phys = dma_map_single(lp->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(ndev->dev.parent, phys))) { + if (unlikely(dma_mapping_error(lp->dev, phys))) { if (net_ratelimit()) netdev_err(ndev, "TX DMA mapping error\n"); ndev->stats.tx_dropped++; @@ -790,11 +856,11 @@ axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev) lp->tx_bd_tail = 0; cur_p = &lp->tx_bd_v[lp->tx_bd_tail]; frag = &skb_shinfo(skb)->frags[ii]; - phys = dma_map_single(ndev->dev.parent, + phys = dma_map_single(lp->dev, skb_frag_address(frag), skb_frag_size(frag), DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(ndev->dev.parent, phys))) { + if (unlikely(dma_mapping_error(lp->dev, phys))) { if (net_ratelimit()) netdev_err(ndev, "TX DMA mapping error\n"); ndev->stats.tx_dropped++; @@ -833,79 +899,84 @@ axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev) } /** - * axienet_recv - Is called from Axi DMA Rx Isr to complete the received - * BD processing. - * @ndev: Pointer to net_device structure. + * axienet_poll - Triggered by RX ISR to complete the received BD processing. + * @napi: Pointer to NAPI structure. + * @budget: Max number of packets to process. * - * This function is invoked from the Axi DMA Rx isr to process the Rx BDs. It - * does minimal processing and invokes "netif_rx" to complete further - * processing. + * Return: Number of RX packets processed. */ -static void axienet_recv(struct net_device *ndev) +static int axienet_poll(struct napi_struct *napi, int budget) { u32 length; u32 csumstatus; u32 size = 0; - u32 packets = 0; + int packets = 0; dma_addr_t tail_p = 0; - struct axienet_local *lp = netdev_priv(ndev); - struct sk_buff *skb, *new_skb; struct axidma_bd *cur_p; + struct sk_buff *skb, *new_skb; + struct axienet_local *lp = container_of(napi, struct axienet_local, napi); cur_p = &lp->rx_bd_v[lp->rx_bd_ci]; - while ((cur_p->status & XAXIDMA_BD_STS_COMPLETE_MASK)) { + while (packets < budget && (cur_p->status & XAXIDMA_BD_STS_COMPLETE_MASK)) { dma_addr_t phys; - tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * lp->rx_bd_ci; - /* Ensure we see complete descriptor update */ dma_rmb(); - phys = desc_get_phys_addr(lp, cur_p); - dma_unmap_single(ndev->dev.parent, phys, lp->max_frm_size, - DMA_FROM_DEVICE); skb = cur_p->skb; cur_p->skb = NULL; - length = cur_p->app4 & 0x0000FFFF; - skb_put(skb, length); - skb->protocol = eth_type_trans(skb, ndev); - /*skb_checksum_none_assert(skb);*/ - skb->ip_summed = CHECKSUM_NONE; + /* skb could be NULL if a previous pass already received the + * packet for this slot in the ring, but failed to refill it + * with a newly allocated buffer. In this case, don't try to + * receive it again. + */ + if (likely(skb)) { + length = cur_p->app4 & 0x0000FFFF; - /* if we're doing Rx csum offload, set it up */ - if (lp->features & XAE_FEATURE_FULL_RX_CSUM) { - csumstatus = (cur_p->app2 & - XAE_FULL_CSUM_STATUS_MASK) >> 3; - if ((csumstatus == XAE_IP_TCP_CSUM_VALIDATED) || - (csumstatus == XAE_IP_UDP_CSUM_VALIDATED)) { - skb->ip_summed = CHECKSUM_UNNECESSARY; + phys = desc_get_phys_addr(lp, cur_p); + dma_unmap_single(lp->dev, phys, lp->max_frm_size, + DMA_FROM_DEVICE); + + skb_put(skb, length); + skb->protocol = eth_type_trans(skb, lp->ndev); + /*skb_checksum_none_assert(skb);*/ + skb->ip_summed = CHECKSUM_NONE; + + /* if we're doing Rx csum offload, set it up */ + if (lp->features & XAE_FEATURE_FULL_RX_CSUM) { + csumstatus = (cur_p->app2 & + XAE_FULL_CSUM_STATUS_MASK) >> 3; + if (csumstatus == XAE_IP_TCP_CSUM_VALIDATED || + csumstatus == XAE_IP_UDP_CSUM_VALIDATED) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + } else if ((lp->features & XAE_FEATURE_PARTIAL_RX_CSUM) != 0 && + skb->protocol == htons(ETH_P_IP) && + skb->len > 64) { + skb->csum = be32_to_cpu(cur_p->app3 & 0xFFFF); + skb->ip_summed = CHECKSUM_COMPLETE; } - } else if ((lp->features & XAE_FEATURE_PARTIAL_RX_CSUM) != 0 && - skb->protocol == htons(ETH_P_IP) && - skb->len > 64) { - skb->csum = be32_to_cpu(cur_p->app3 & 0xFFFF); - skb->ip_summed = CHECKSUM_COMPLETE; + + napi_gro_receive(napi, skb); + + size += length; + packets++; } - netif_rx(skb); - - size += length; - packets++; - - new_skb = netdev_alloc_skb_ip_align(ndev, lp->max_frm_size); + new_skb = napi_alloc_skb(napi, lp->max_frm_size); if (!new_skb) - return; + break; - phys = dma_map_single(ndev->dev.parent, new_skb->data, + phys = dma_map_single(lp->dev, new_skb->data, lp->max_frm_size, DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(ndev->dev.parent, phys))) { + if (unlikely(dma_mapping_error(lp->dev, phys))) { if (net_ratelimit()) - netdev_err(ndev, "RX DMA mapping error\n"); + netdev_err(lp->ndev, "RX DMA mapping error\n"); dev_kfree_skb(new_skb); - return; + break; } desc_set_phys_addr(lp, phys, cur_p); @@ -913,16 +984,30 @@ static void axienet_recv(struct net_device *ndev) cur_p->status = 0; cur_p->skb = new_skb; + /* Only update tail_p to mark this slot as usable after it has + * been successfully refilled. + */ + tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * lp->rx_bd_ci; + if (++lp->rx_bd_ci >= lp->rx_bd_num) lp->rx_bd_ci = 0; cur_p = &lp->rx_bd_v[lp->rx_bd_ci]; } - ndev->stats.rx_packets += packets; - ndev->stats.rx_bytes += size; + lp->ndev->stats.rx_packets += packets; + lp->ndev->stats.rx_bytes += size; if (tail_p) axienet_dma_out_addr(lp, XAXIDMA_RX_TDESC_OFFSET, tail_p); + + if (packets < budget && napi_complete_done(napi, packets)) { + /* Re-enable RX completion interrupts. This should + * cause an immediate interrupt if any RX packets are + * already pending. + */ + axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, lp->rx_dma_cr); + } + return packets; } /** @@ -937,41 +1022,27 @@ static void axienet_recv(struct net_device *ndev) */ static irqreturn_t axienet_tx_irq(int irq, void *_ndev) { - u32 cr; unsigned int status; struct net_device *ndev = _ndev; struct axienet_local *lp = netdev_priv(ndev); status = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET); - if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) { - axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status); - axienet_start_xmit_done(lp->ndev); - goto out; - } + if (!(status & XAXIDMA_IRQ_ALL_MASK)) return IRQ_NONE; - if (status & XAXIDMA_IRQ_ERROR_MASK) { - dev_err(&ndev->dev, "DMA Tx error 0x%x\n", status); - dev_err(&ndev->dev, "Current BD is at: 0x%x%08x\n", - (lp->tx_bd_v[lp->tx_bd_ci]).phys_msb, - (lp->tx_bd_v[lp->tx_bd_ci]).phys); - cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); - /* Disable coalesce, delay timer and error interrupts */ - cr &= (~XAXIDMA_IRQ_ALL_MASK); - /* Write to the Tx channel control register */ - axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr); - - cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); - /* Disable coalesce, delay timer and error interrupts */ - cr &= (~XAXIDMA_IRQ_ALL_MASK); - /* Write to the Rx channel control register */ - axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); + axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status); + if (unlikely(status & XAXIDMA_IRQ_ERROR_MASK)) { + netdev_err(ndev, "DMA Tx error 0x%x\n", status); + netdev_err(ndev, "Current BD is at: 0x%x%08x\n", + (lp->tx_bd_v[lp->tx_bd_ci]).phys_msb, + (lp->tx_bd_v[lp->tx_bd_ci]).phys); schedule_work(&lp->dma_err_task); - axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status); + } else { + axienet_start_xmit_done(lp->ndev); } -out: + return IRQ_HANDLED; } @@ -982,46 +1053,40 @@ static irqreturn_t axienet_tx_irq(int irq, void *_ndev) * * Return: IRQ_HANDLED if device generated a RX interrupt, IRQ_NONE otherwise. * - * This is the Axi DMA Rx Isr. It invokes "axienet_recv" to complete the BD + * This is the Axi DMA Rx Isr. It invokes NAPI polling to complete the RX BD * processing. */ static irqreturn_t axienet_rx_irq(int irq, void *_ndev) { - u32 cr; unsigned int status; struct net_device *ndev = _ndev; struct axienet_local *lp = netdev_priv(ndev); status = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET); - if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) { - axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status); - axienet_recv(lp->ndev); - goto out; - } + if (!(status & XAXIDMA_IRQ_ALL_MASK)) return IRQ_NONE; - if (status & XAXIDMA_IRQ_ERROR_MASK) { - dev_err(&ndev->dev, "DMA Rx error 0x%x\n", status); - dev_err(&ndev->dev, "Current BD is at: 0x%x%08x\n", - (lp->rx_bd_v[lp->rx_bd_ci]).phys_msb, - (lp->rx_bd_v[lp->rx_bd_ci]).phys); - cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); - /* Disable coalesce, delay timer and error interrupts */ - cr &= (~XAXIDMA_IRQ_ALL_MASK); - /* Finally write to the Tx channel control register */ - axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr); + axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status); - cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); - /* Disable coalesce, delay timer and error interrupts */ - cr &= (~XAXIDMA_IRQ_ALL_MASK); - /* write to the Rx channel control register */ + if (unlikely(status & XAXIDMA_IRQ_ERROR_MASK)) { + netdev_err(ndev, "DMA Rx error 0x%x\n", status); + netdev_err(ndev, "Current BD is at: 0x%x%08x\n", + (lp->rx_bd_v[lp->rx_bd_ci]).phys_msb, + (lp->rx_bd_v[lp->rx_bd_ci]).phys); + schedule_work(&lp->dma_err_task); + } else { + /* Disable further RX completion interrupts and schedule + * NAPI receive. + */ + u32 cr = lp->rx_dma_cr; + + cr &= ~(XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK); axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); - schedule_work(&lp->dma_err_task); - axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status); + napi_schedule(&lp->napi); } -out: + return IRQ_HANDLED; } @@ -1095,6 +1160,8 @@ static int axienet_open(struct net_device *ndev) /* Enable worker thread for Axi DMA error handling */ INIT_WORK(&lp->dma_err_task, axienet_dma_err_handler); + napi_enable(&lp->napi); + /* Enable interrupts for Axi DMA Tx */ ret = request_irq(lp->tx_irq, axienet_tx_irq, IRQF_SHARED, ndev->name, ndev); @@ -1120,6 +1187,7 @@ static int axienet_open(struct net_device *ndev) err_rx_irq: free_irq(lp->tx_irq, ndev); err_tx_irq: + napi_disable(&lp->napi); phylink_stop(lp->phylink); phylink_disconnect_phy(lp->phylink); cancel_work_sync(&lp->dma_err_task); @@ -1139,46 +1207,22 @@ static int axienet_open(struct net_device *ndev) */ static int axienet_stop(struct net_device *ndev) { - u32 cr, sr; - int count; struct axienet_local *lp = netdev_priv(ndev); dev_dbg(&ndev->dev, "axienet_close()\n"); + napi_disable(&lp->napi); + phylink_stop(lp->phylink); phylink_disconnect_phy(lp->phylink); axienet_setoptions(ndev, lp->options & ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); - cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); - cr &= ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK); - axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); - - cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); - cr &= ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK); - axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr); + axienet_dma_stop(lp); axienet_iow(lp, XAE_IE_OFFSET, 0); - /* Give DMAs a chance to halt gracefully */ - sr = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET); - for (count = 0; !(sr & XAXIDMA_SR_HALT_MASK) && count < 5; ++count) { - msleep(20); - sr = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET); - } - - sr = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET); - for (count = 0; !(sr & XAXIDMA_SR_HALT_MASK) && count < 5; ++count) { - msleep(20); - sr = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET); - } - - /* Do a reset to ensure DMA is really stopped */ - axienet_lock_mii(lp); - __axienet_device_reset(lp); - axienet_unlock_mii(lp); - cancel_work_sync(&lp->dma_err_task); if (lp->eth_irq > 0) @@ -1449,14 +1493,12 @@ axienet_ethtools_get_coalesce(struct net_device *ndev, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack) { - u32 regval = 0; struct axienet_local *lp = netdev_priv(ndev); - regval = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); - ecoalesce->rx_max_coalesced_frames = (regval & XAXIDMA_COALESCE_MASK) - >> XAXIDMA_COALESCE_SHIFT; - regval = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); - ecoalesce->tx_max_coalesced_frames = (regval & XAXIDMA_COALESCE_MASK) - >> XAXIDMA_COALESCE_SHIFT; + + ecoalesce->rx_max_coalesced_frames = lp->coalesce_count_rx; + ecoalesce->rx_coalesce_usecs = lp->coalesce_usec_rx; + ecoalesce->tx_max_coalesced_frames = lp->coalesce_count_tx; + ecoalesce->tx_coalesce_usecs = lp->coalesce_usec_tx; return 0; } @@ -1489,8 +1531,12 @@ axienet_ethtools_set_coalesce(struct net_device *ndev, if (ecoalesce->rx_max_coalesced_frames) lp->coalesce_count_rx = ecoalesce->rx_max_coalesced_frames; + if (ecoalesce->rx_coalesce_usecs) + lp->coalesce_usec_rx = ecoalesce->rx_coalesce_usecs; if (ecoalesce->tx_max_coalesced_frames) lp->coalesce_count_tx = ecoalesce->tx_max_coalesced_frames; + if (ecoalesce->tx_coalesce_usecs) + lp->coalesce_usec_tx = ecoalesce->tx_coalesce_usecs; return 0; } @@ -1521,7 +1567,8 @@ static int axienet_ethtools_nway_reset(struct net_device *dev) } static const struct ethtool_ops axienet_ethtool_ops = { - .supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES, + .supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES | + ETHTOOL_COALESCE_USECS, .get_drvinfo = axienet_ethtools_get_drvinfo, .get_regs_len = axienet_ethtools_get_regs_len, .get_regs = axienet_ethtools_get_regs, @@ -1537,78 +1584,78 @@ static const struct ethtool_ops axienet_ethtool_ops = { .nway_reset = axienet_ethtools_nway_reset, }; -static void axienet_mac_pcs_get_state(struct phylink_config *config, - struct phylink_link_state *state) +static struct axienet_local *pcs_to_axienet_local(struct phylink_pcs *pcs) { - struct net_device *ndev = to_net_dev(config->dev); - struct axienet_local *lp = netdev_priv(ndev); - - switch (state->interface) { - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_1000BASEX: - phylink_mii_c22_pcs_get_state(lp->pcs_phy, state); - break; - default: - break; - } + return container_of(pcs, struct axienet_local, pcs); } -static void axienet_mac_an_restart(struct phylink_config *config) +static void axienet_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) { - struct net_device *ndev = to_net_dev(config->dev); - struct axienet_local *lp = netdev_priv(ndev); + struct mdio_device *pcs_phy = pcs_to_axienet_local(pcs)->pcs_phy; - phylink_mii_c22_pcs_an_restart(lp->pcs_phy); + phylink_mii_c22_pcs_get_state(pcs_phy, state); } -static int axienet_mac_prepare(struct phylink_config *config, unsigned int mode, - phy_interface_t iface) +static void axienet_pcs_an_restart(struct phylink_pcs *pcs) { - struct net_device *ndev = to_net_dev(config->dev); + struct mdio_device *pcs_phy = pcs_to_axienet_local(pcs)->pcs_phy; + + phylink_mii_c22_pcs_an_restart(pcs_phy); +} + +static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct mdio_device *pcs_phy = pcs_to_axienet_local(pcs)->pcs_phy; + struct net_device *ndev = pcs_to_axienet_local(pcs)->ndev; struct axienet_local *lp = netdev_priv(ndev); int ret; - switch (iface) { - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_1000BASEX: - if (!lp->switch_x_sgmii) - return 0; - - ret = mdiobus_write(lp->pcs_phy->bus, - lp->pcs_phy->addr, - XLNX_MII_STD_SELECT_REG, - iface == PHY_INTERFACE_MODE_SGMII ? + if (lp->switch_x_sgmii) { + ret = mdiodev_write(pcs_phy, XLNX_MII_STD_SELECT_REG, + interface == PHY_INTERFACE_MODE_SGMII ? XLNX_MII_STD_SELECT_SGMII : 0); - if (ret < 0) - netdev_warn(ndev, "Failed to switch PHY interface: %d\n", + if (ret < 0) { + netdev_warn(ndev, + "Failed to switch PHY interface: %d\n", ret); - return ret; - default: - return 0; + return ret; + } } + + ret = phylink_mii_c22_pcs_config(pcs_phy, mode, interface, advertising); + if (ret < 0) + netdev_warn(ndev, "Failed to configure PCS: %d\n", ret); + + return ret; +} + +static const struct phylink_pcs_ops axienet_pcs_ops = { + .pcs_get_state = axienet_pcs_get_state, + .pcs_config = axienet_pcs_config, + .pcs_an_restart = axienet_pcs_an_restart, +}; + +static struct phylink_pcs *axienet_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct axienet_local *lp = netdev_priv(ndev); + + if (interface == PHY_INTERFACE_MODE_1000BASEX || + interface == PHY_INTERFACE_MODE_SGMII) + return &lp->pcs; + + return NULL; } static void axienet_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { - struct net_device *ndev = to_net_dev(config->dev); - struct axienet_local *lp = netdev_priv(ndev); - int ret; - - switch (state->interface) { - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_1000BASEX: - ret = phylink_mii_c22_pcs_config(lp->pcs_phy, mode, - state->interface, - state->advertising); - if (ret < 0) - netdev_warn(ndev, "Failed to configure PCS: %d\n", - ret); - break; - - default: - break; - } + /* nothing meaningful to do */ } static void axienet_mac_link_down(struct phylink_config *config, @@ -1663,9 +1710,7 @@ static void axienet_mac_link_up(struct phylink_config *config, static const struct phylink_mac_ops axienet_phylink_ops = { .validate = phylink_generic_validate, - .mac_pcs_get_state = axienet_mac_pcs_get_state, - .mac_an_restart = axienet_mac_an_restart, - .mac_prepare = axienet_mac_prepare, + .mac_select_pcs = axienet_mac_select_pcs, .mac_config = axienet_mac_config, .mac_link_down = axienet_mac_link_down, .mac_link_up = axienet_mac_link_up, @@ -1680,29 +1725,26 @@ static const struct phylink_mac_ops axienet_phylink_ops = { */ static void axienet_dma_err_handler(struct work_struct *work) { + u32 i; u32 axienet_status; - u32 cr, i; + struct axidma_bd *cur_p; struct axienet_local *lp = container_of(work, struct axienet_local, dma_err_task); struct net_device *ndev = lp->ndev; - struct axidma_bd *cur_p; + + napi_disable(&lp->napi); axienet_setoptions(ndev, lp->options & ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); - /* When we do an Axi Ethernet reset, it resets the complete core - * including the MDIO. MDIO must be disabled before resetting. - * Hold MDIO bus lock to avoid MDIO accesses during the reset. - */ - axienet_lock_mii(lp); - __axienet_device_reset(lp); - axienet_unlock_mii(lp); + + axienet_dma_stop(lp); for (i = 0; i < lp->tx_bd_num; i++) { cur_p = &lp->tx_bd_v[i]; if (cur_p->cntrl) { dma_addr_t addr = desc_get_phys_addr(lp, cur_p); - dma_unmap_single(ndev->dev.parent, addr, + dma_unmap_single(lp->dev, addr, (cur_p->cntrl & XAXIDMA_BD_CTRL_LENGTH_MASK), DMA_TO_DEVICE); @@ -1735,50 +1777,7 @@ static void axienet_dma_err_handler(struct work_struct *work) lp->tx_bd_tail = 0; lp->rx_bd_ci = 0; - /* Start updating the Rx channel control register */ - cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); - /* Update the interrupt coalesce count */ - cr = ((cr & ~XAXIDMA_COALESCE_MASK) | - (XAXIDMA_DFT_RX_THRESHOLD << XAXIDMA_COALESCE_SHIFT)); - /* Update the delay timer count */ - cr = ((cr & ~XAXIDMA_DELAY_MASK) | - (XAXIDMA_DFT_RX_WAITBOUND << XAXIDMA_DELAY_SHIFT)); - /* Enable coalesce, delay timer and error interrupts */ - cr |= XAXIDMA_IRQ_ALL_MASK; - /* Finally write to the Rx channel control register */ - axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); - - /* Start updating the Tx channel control register */ - cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); - /* Update the interrupt coalesce count */ - cr = (((cr & ~XAXIDMA_COALESCE_MASK)) | - (XAXIDMA_DFT_TX_THRESHOLD << XAXIDMA_COALESCE_SHIFT)); - /* Update the delay timer count */ - cr = (((cr & ~XAXIDMA_DELAY_MASK)) | - (XAXIDMA_DFT_TX_WAITBOUND << XAXIDMA_DELAY_SHIFT)); - /* Enable coalesce, delay timer and error interrupts */ - cr |= XAXIDMA_IRQ_ALL_MASK; - /* Finally write to the Tx channel control register */ - axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr); - - /* Populate the tail pointer and bring the Rx Axi DMA engine out of - * halted state. This will make the Rx side ready for reception. - */ - axienet_dma_out_addr(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p); - cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); - axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, - cr | XAXIDMA_CR_RUNSTOP_MASK); - axienet_dma_out_addr(lp, XAXIDMA_RX_TDESC_OFFSET, lp->rx_bd_p + - (sizeof(*lp->rx_bd_v) * (lp->rx_bd_num - 1))); - - /* Write to the RS (Run-stop) bit in the Tx channel control register. - * Tx channel is now ready to run. But only after we write to the - * tail pointer register that the Tx channel will start transmitting - */ - axienet_dma_out_addr(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p); - cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); - axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, - cr | XAXIDMA_CR_RUNSTOP_MASK); + axienet_dma_start(lp); axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET); axienet_status &= ~XAE_RCW1_RX_MASK; @@ -1799,6 +1798,7 @@ static void axienet_dma_err_handler(struct work_struct *work) axienet_set_mac_address(ndev, NULL); axienet_set_multicast_list(ndev); axienet_setoptions(ndev, lp->options); + napi_enable(&lp->napi); } /** @@ -1847,6 +1847,8 @@ static int axienet_probe(struct platform_device *pdev) lp->rx_bd_num = RX_BD_NUM_DEFAULT; lp->tx_bd_num = TX_BD_NUM_DEFAULT; + netif_napi_add(ndev, &lp->napi, axienet_poll, NAPI_POLL_WEIGHT); + lp->axi_clk = devm_clk_get_optional(&pdev->dev, "s_axi_lite_clk"); if (!lp->axi_clk) { /* For backward compatibility, if named AXI clock is not present, @@ -2053,7 +2055,9 @@ static int axienet_probe(struct platform_device *pdev) } lp->coalesce_count_rx = XAXIDMA_DFT_RX_THRESHOLD; + lp->coalesce_usec_rx = XAXIDMA_DFT_RX_USEC; lp->coalesce_count_tx = XAXIDMA_DFT_TX_THRESHOLD; + lp->coalesce_usec_tx = XAXIDMA_DFT_TX_USEC; /* Reset core now that clocks are enabled, prior to accessing MDIO */ ret = __axienet_device_reset(lp); @@ -2079,12 +2083,12 @@ static int axienet_probe(struct platform_device *pdev) ret = -EPROBE_DEFER; goto cleanup_mdio; } - lp->phylink_config.pcs_poll = true; + lp->pcs.ops = &axienet_pcs_ops; + lp->pcs.poll = true; } lp->phylink_config.dev = &ndev->dev; lp->phylink_config.type = PHYLINK_NETDEV; - lp->phylink_config.legacy_pre_march2020 = true; lp->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_10FD | MAC_100FD | MAC_1000FD; diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 77fa2cb03aca..57a24f62e353 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -498,7 +498,7 @@ static void xemaclite_update_address(struct net_local *drvdata, * @dev: Pointer to the network device instance * @address: Void pointer to the sockaddr structure * - * This function copies the HW address from the sockaddr strucutre to the + * This function copies the HW address from the sockaddr structure to the * net_device structure and updates the address in HW. * * Return: Error if the net device is busy or 0 if the addr is set diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c index ebd287039a54..5805e4a56385 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c @@ -1514,10 +1514,9 @@ acpi_find_extended_socket_device(acpi_handle obj_handle, u32 level, { struct acpi_device *device; bool *found = context; - int result; - result = acpi_bus_get_device(obj_handle, &device); - if (result) + device = acpi_fetch_acpi_dev(obj_handle); + if (!device) return AE_OK; if (strcmp(acpi_device_hid(device), ACPI_MOTHERBOARD_RESOURCE_HID)) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index c1fdd721a730..7db6c135ac6c 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -56,6 +56,7 @@ struct geneve_config { bool use_udp6_rx_checksums; bool ttl_inherit; enum ifla_geneve_df df; + bool inner_proto_inherit; }; /* Pseudo network device */ @@ -251,17 +252,24 @@ static void geneve_rx(struct geneve_dev *geneve, struct geneve_sock *gs, } } - skb_reset_mac_header(skb); - skb->protocol = eth_type_trans(skb, geneve->dev); - skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); - if (tun_dst) skb_dst_set(skb, &tun_dst->dst); - /* Ignore packet loops (and multicast echo) */ - if (ether_addr_equal(eth_hdr(skb)->h_source, geneve->dev->dev_addr)) { - geneve->dev->stats.rx_errors++; - goto drop; + if (gnvh->proto_type == htons(ETH_P_TEB)) { + skb_reset_mac_header(skb); + skb->protocol = eth_type_trans(skb, geneve->dev); + skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); + + /* Ignore packet loops (and multicast echo) */ + if (ether_addr_equal(eth_hdr(skb)->h_source, + geneve->dev->dev_addr)) { + geneve->dev->stats.rx_errors++; + goto drop; + } + } else { + skb_reset_mac_header(skb); + skb->dev = geneve->dev; + skb->pkt_type = PACKET_HOST; } oiph = skb_network_header(skb); @@ -345,6 +353,7 @@ static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb) struct genevehdr *geneveh; struct geneve_dev *geneve; struct geneve_sock *gs; + __be16 inner_proto; int opts_len; /* Need UDP and Geneve header to be present */ @@ -356,7 +365,11 @@ static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb) if (unlikely(geneveh->ver != GENEVE_VER)) goto drop; - if (unlikely(geneveh->proto_type != htons(ETH_P_TEB))) + inner_proto = geneveh->proto_type; + + if (unlikely((inner_proto != htons(ETH_P_TEB) && + inner_proto != htons(ETH_P_IP) && + inner_proto != htons(ETH_P_IPV6)))) goto drop; gs = rcu_dereference_sk_user_data(sk); @@ -367,9 +380,14 @@ static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb) if (!geneve) goto drop; + if (unlikely((!geneve->cfg.inner_proto_inherit && + inner_proto != htons(ETH_P_TEB)))) { + geneve->dev->stats.rx_dropped++; + goto drop; + } + opts_len = geneveh->opt_len * 4; - if (iptunnel_pull_header(skb, GENEVE_BASE_HLEN + opts_len, - htons(ETH_P_TEB), + if (iptunnel_pull_header(skb, GENEVE_BASE_HLEN + opts_len, inner_proto, !net_eq(geneve->net, dev_net(geneve->dev)))) { geneve->dev->stats.rx_dropped++; goto drop; @@ -717,7 +735,8 @@ static int geneve_stop(struct net_device *dev) } static void geneve_build_header(struct genevehdr *geneveh, - const struct ip_tunnel_info *info) + const struct ip_tunnel_info *info, + __be16 inner_proto) { geneveh->ver = GENEVE_VER; geneveh->opt_len = info->options_len / 4; @@ -725,7 +744,7 @@ static void geneve_build_header(struct genevehdr *geneveh, geneveh->critical = !!(info->key.tun_flags & TUNNEL_CRIT_OPT); geneveh->rsvd1 = 0; tunnel_id_to_vni(info->key.tun_id, geneveh->vni); - geneveh->proto_type = htons(ETH_P_TEB); + geneveh->proto_type = inner_proto; geneveh->rsvd2 = 0; if (info->key.tun_flags & TUNNEL_GENEVE_OPT) @@ -734,10 +753,12 @@ static void geneve_build_header(struct genevehdr *geneveh, static int geneve_build_skb(struct dst_entry *dst, struct sk_buff *skb, const struct ip_tunnel_info *info, - bool xnet, int ip_hdr_len) + bool xnet, int ip_hdr_len, + bool inner_proto_inherit) { bool udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM); struct genevehdr *gnvh; + __be16 inner_proto; int min_headroom; int err; @@ -755,8 +776,9 @@ static int geneve_build_skb(struct dst_entry *dst, struct sk_buff *skb, goto free_dst; gnvh = __skb_push(skb, sizeof(*gnvh) + info->options_len); - geneve_build_header(gnvh, info); - skb_set_inner_protocol(skb, htons(ETH_P_TEB)); + inner_proto = inner_proto_inherit ? skb->protocol : htons(ETH_P_TEB); + geneve_build_header(gnvh, info, inner_proto); + skb_set_inner_protocol(skb, inner_proto); return 0; free_dst: @@ -925,7 +947,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, } skb->protocol = eth_type_trans(skb, geneve->dev); - netif_rx(skb); + __netif_rx(skb); dst_release(&rt->dst); return -EMSGSIZE; } @@ -959,7 +981,8 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, } } - err = geneve_build_skb(&rt->dst, skb, info, xnet, sizeof(struct iphdr)); + err = geneve_build_skb(&rt->dst, skb, info, xnet, sizeof(struct iphdr), + geneve->cfg.inner_proto_inherit); if (unlikely(err)) return err; @@ -1021,7 +1044,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, } skb->protocol = eth_type_trans(skb, geneve->dev); - netif_rx(skb); + __netif_rx(skb); dst_release(dst); return -EMSGSIZE; } @@ -1038,7 +1061,8 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, ttl = key->ttl; ttl = ttl ? : ip6_dst_hoplimit(dst); } - err = geneve_build_skb(dst, skb, info, xnet, sizeof(struct ipv6hdr)); + err = geneve_build_skb(dst, skb, info, xnet, sizeof(struct ipv6hdr), + geneve->cfg.inner_proto_inherit); if (unlikely(err)) return err; @@ -1238,6 +1262,7 @@ static void geneve_setup(struct net_device *dev) } static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = { + [IFLA_GENEVE_UNSPEC] = { .strict_start_type = IFLA_GENEVE_INNER_PROTO_INHERIT }, [IFLA_GENEVE_ID] = { .type = NLA_U32 }, [IFLA_GENEVE_REMOTE] = { .len = sizeof_field(struct iphdr, daddr) }, [IFLA_GENEVE_REMOTE6] = { .len = sizeof(struct in6_addr) }, @@ -1251,6 +1276,7 @@ static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = { [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 }, [IFLA_GENEVE_TTL_INHERIT] = { .type = NLA_U8 }, [IFLA_GENEVE_DF] = { .type = NLA_U8 }, + [IFLA_GENEVE_INNER_PROTO_INHERIT] = { .type = NLA_FLAG }, }; static int geneve_validate(struct nlattr *tb[], struct nlattr *data[], @@ -1388,6 +1414,14 @@ static int geneve_configure(struct net *net, struct net_device *dev, dst_cache_reset(&geneve->cfg.info.dst_cache); memcpy(&geneve->cfg, cfg, sizeof(*cfg)); + if (geneve->cfg.inner_proto_inherit) { + dev->header_ops = NULL; + dev->type = ARPHRD_NONE; + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->flags = IFF_NOARP; + } + err = register_netdevice(dev); if (err) return err; @@ -1561,10 +1595,18 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[], #endif } + if (data[IFLA_GENEVE_INNER_PROTO_INHERIT]) { + if (changelink) { + attrtype = IFLA_GENEVE_INNER_PROTO_INHERIT; + goto change_notsup; + } + cfg->inner_proto_inherit = true; + } + return 0; change_notsup: NL_SET_ERR_MSG_ATTR(extack, data[attrtype], - "Changing VNI, Port, endpoint IP address family, external, and UDP checksum attributes are not supported"); + "Changing VNI, Port, endpoint IP address family, external, inner_proto_inherit, and UDP checksum attributes are not supported"); return -EOPNOTSUPP; } @@ -1740,6 +1782,7 @@ static size_t geneve_get_size(const struct net_device *dev) nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_ZERO_CSUM6_TX */ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_ZERO_CSUM6_RX */ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TTL_INHERIT */ + nla_total_size(0) + /* IFLA_GENEVE_INNER_PROTO_INHERIT */ 0; } @@ -1799,6 +1842,10 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev) if (nla_put_u8(skb, IFLA_GENEVE_TTL_INHERIT, ttl_inherit)) goto nla_put_failure; + if (geneve->cfg.inner_proto_inherit && + nla_put_flag(skb, IFLA_GENEVE_INNER_PROTO_INHERIT)) + goto nla_put_failure; + return 0; nla_put_failure: diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 24e5c54d06c1..a208e2b1a9af 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -66,13 +66,23 @@ struct gtp_dev { struct sock *sk0; struct sock *sk1u; + u8 sk_created; struct net_device *dev; + struct net *net; unsigned int role; unsigned int hash_size; struct hlist_head *tid_hash; struct hlist_head *addr_hash; + + u8 restart_count; +}; + +struct echo_info { + struct in_addr ms_addr_ip4; + struct in_addr peer_addr_ip4; + u8 gtp_version; }; static unsigned int gtp_net_id __read_mostly; @@ -83,6 +93,16 @@ struct gtp_net { static u32 gtp_h_initval; +static struct genl_family gtp_genl_family; + +enum gtp_multicast_groups { + GTP_GENL_MCGRP, +}; + +static const struct genl_multicast_group gtp_genl_mcgrps[] = { + [GTP_GENL_MCGRP] = { .name = GTP_GENL_MCGRP_NAME }, +}; + static void pdp_context_delete(struct pdp_ctx *pctx); static inline u32 gtp0_hashfn(u64 tid) @@ -207,7 +227,7 @@ static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, dev_sw_netstats_rx_add(pctx->dev, skb->len); - netif_rx(skb); + __netif_rx(skb); return 0; err: @@ -215,6 +235,174 @@ static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, return -1; } +static struct rtable *ip4_route_output_gtp(struct flowi4 *fl4, + const struct sock *sk, + __be32 daddr, __be32 saddr) +{ + memset(fl4, 0, sizeof(*fl4)); + fl4->flowi4_oif = sk->sk_bound_dev_if; + fl4->daddr = daddr; + fl4->saddr = saddr; + fl4->flowi4_tos = RT_CONN_FLAGS(sk); + fl4->flowi4_proto = sk->sk_protocol; + + return ip_route_output_key(sock_net(sk), fl4); +} + +/* GSM TS 09.60. 7.3 + * In all Path Management messages: + * - TID: is not used and shall be set to 0. + * - Flow Label is not used and shall be set to 0 + * In signalling messages: + * - number: this field is not yet used in signalling messages. + * It shall be set to 255 by the sender and shall be ignored + * by the receiver + * Returns true if the echo req was correct, false otherwise. + */ +static bool gtp0_validate_echo_hdr(struct gtp0_header *gtp0) +{ + return !(gtp0->tid || (gtp0->flags ^ 0x1e) || + gtp0->number != 0xff || gtp0->flow); +} + +/* msg_type has to be GTP_ECHO_REQ or GTP_ECHO_RSP */ +static void gtp0_build_echo_msg(struct gtp0_header *hdr, __u8 msg_type) +{ + int len_pkt, len_hdr; + + hdr->flags = 0x1e; /* v0, GTP-non-prime. */ + hdr->type = msg_type; + /* GSM TS 09.60. 7.3 In all Path Management Flow Label and TID + * are not used and shall be set to 0. + */ + hdr->flow = 0; + hdr->tid = 0; + hdr->number = 0xff; + hdr->spare[0] = 0xff; + hdr->spare[1] = 0xff; + hdr->spare[2] = 0xff; + + len_pkt = sizeof(struct gtp0_packet); + len_hdr = sizeof(struct gtp0_header); + + if (msg_type == GTP_ECHO_RSP) + hdr->length = htons(len_pkt - len_hdr); + else + hdr->length = 0; +} + +static int gtp0_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) +{ + struct gtp0_packet *gtp_pkt; + struct gtp0_header *gtp0; + struct rtable *rt; + struct flowi4 fl4; + struct iphdr *iph; + __be16 seq; + + gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr)); + + if (!gtp0_validate_echo_hdr(gtp0)) + return -1; + + seq = gtp0->seq; + + /* pull GTP and UDP headers */ + skb_pull_data(skb, sizeof(struct gtp0_header) + sizeof(struct udphdr)); + + gtp_pkt = skb_push(skb, sizeof(struct gtp0_packet)); + memset(gtp_pkt, 0, sizeof(struct gtp0_packet)); + + gtp0_build_echo_msg(>p_pkt->gtp0_h, GTP_ECHO_RSP); + + /* GSM TS 09.60. 7.3 The Sequence Number in a signalling response + * message shall be copied from the signalling request message + * that the GSN is replying to. + */ + gtp_pkt->gtp0_h.seq = seq; + + gtp_pkt->ie.tag = GTPIE_RECOVERY; + gtp_pkt->ie.val = gtp->restart_count; + + iph = ip_hdr(skb); + + /* find route to the sender, + * src address becomes dst address and vice versa. + */ + rt = ip4_route_output_gtp(&fl4, gtp->sk0, iph->saddr, iph->daddr); + if (IS_ERR(rt)) { + netdev_dbg(gtp->dev, "no route for echo response from %pI4\n", + &iph->saddr); + return -1; + } + + udp_tunnel_xmit_skb(rt, gtp->sk0, skb, + fl4.saddr, fl4.daddr, + iph->tos, + ip4_dst_hoplimit(&rt->dst), + 0, + htons(GTP0_PORT), htons(GTP0_PORT), + !net_eq(sock_net(gtp->sk1u), + dev_net(gtp->dev)), + false); + return 0; +} + +static int gtp_genl_fill_echo(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, + int flags, u32 type, struct echo_info echo) +{ + void *genlh; + + genlh = genlmsg_put(skb, snd_portid, snd_seq, >p_genl_family, flags, + type); + if (!genlh) + goto failure; + + if (nla_put_u32(skb, GTPA_VERSION, echo.gtp_version) || + nla_put_be32(skb, GTPA_PEER_ADDRESS, echo.peer_addr_ip4.s_addr) || + nla_put_be32(skb, GTPA_MS_ADDRESS, echo.ms_addr_ip4.s_addr)) + goto failure; + + genlmsg_end(skb, genlh); + return 0; + +failure: + genlmsg_cancel(skb, genlh); + return -EMSGSIZE; +} + +static int gtp0_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) +{ + struct gtp0_header *gtp0; + struct echo_info echo; + struct sk_buff *msg; + struct iphdr *iph; + int ret; + + gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr)); + + if (!gtp0_validate_echo_hdr(gtp0)) + return -1; + + iph = ip_hdr(skb); + echo.ms_addr_ip4.s_addr = iph->daddr; + echo.peer_addr_ip4.s_addr = iph->saddr; + echo.gtp_version = GTP_V0; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!msg) + return -ENOMEM; + + ret = gtp_genl_fill_echo(msg, 0, 0, 0, GTP_CMD_ECHOREQ, echo); + if (ret < 0) { + nlmsg_free(msg); + return ret; + } + + return genlmsg_multicast_netns(>p_genl_family, dev_net(gtp->dev), + msg, 0, GTP_GENL_MCGRP, GFP_ATOMIC); +} + /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) { @@ -231,6 +419,16 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) if ((gtp0->flags >> 5) != GTP_V0) return 1; + /* If the sockets were created in kernel, it means that + * there is no daemon running in userspace which would + * handle echo request. + */ + if (gtp0->type == GTP_ECHO_REQ && gtp->sk_created) + return gtp0_send_echo_resp(gtp, skb); + + if (gtp0->type == GTP_ECHO_RSP && gtp->sk_created) + return gtp0_handle_echo_resp(gtp, skb); + if (gtp0->type != GTP_TPDU) return 1; @@ -243,6 +441,131 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) return gtp_rx(pctx, skb, hdrlen, gtp->role); } +/* msg_type has to be GTP_ECHO_REQ or GTP_ECHO_RSP */ +static void gtp1u_build_echo_msg(struct gtp1_header_long *hdr, __u8 msg_type) +{ + int len_pkt, len_hdr; + + /* S flag must be set to 1 */ + hdr->flags = 0x32; /* v1, GTP-non-prime. */ + hdr->type = msg_type; + /* 3GPP TS 29.281 5.1 - TEID has to be set to 0 */ + hdr->tid = 0; + + /* seq, npdu and next should be counted to the length of the GTP packet + * that's why szie of gtp1_header should be subtracted, + * not size of gtp1_header_long. + */ + + len_hdr = sizeof(struct gtp1_header); + + if (msg_type == GTP_ECHO_RSP) { + len_pkt = sizeof(struct gtp1u_packet); + hdr->length = htons(len_pkt - len_hdr); + } else { + /* GTP_ECHO_REQ does not carry GTP Information Element, + * the why gtp1_header_long is used here. + */ + len_pkt = sizeof(struct gtp1_header_long); + hdr->length = htons(len_pkt - len_hdr); + } +} + +static int gtp1u_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) +{ + struct gtp1_header_long *gtp1u; + struct gtp1u_packet *gtp_pkt; + struct rtable *rt; + struct flowi4 fl4; + struct iphdr *iph; + + gtp1u = (struct gtp1_header_long *)(skb->data + sizeof(struct udphdr)); + + /* 3GPP TS 29.281 5.1 - For the Echo Request, Echo Response, + * Error Indication and Supported Extension Headers Notification + * messages, the S flag shall be set to 1 and TEID shall be set to 0. + */ + if (!(gtp1u->flags & GTP1_F_SEQ) || gtp1u->tid) + return -1; + + /* pull GTP and UDP headers */ + skb_pull_data(skb, + sizeof(struct gtp1_header_long) + sizeof(struct udphdr)); + + gtp_pkt = skb_push(skb, sizeof(struct gtp1u_packet)); + memset(gtp_pkt, 0, sizeof(struct gtp1u_packet)); + + gtp1u_build_echo_msg(>p_pkt->gtp1u_h, GTP_ECHO_RSP); + + /* 3GPP TS 29.281 7.7.2 - The Restart Counter value in the + * Recovery information element shall not be used, i.e. it shall + * be set to zero by the sender and shall be ignored by the receiver. + * The Recovery information element is mandatory due to backwards + * compatibility reasons. + */ + gtp_pkt->ie.tag = GTPIE_RECOVERY; + gtp_pkt->ie.val = 0; + + iph = ip_hdr(skb); + + /* find route to the sender, + * src address becomes dst address and vice versa. + */ + rt = ip4_route_output_gtp(&fl4, gtp->sk1u, iph->saddr, iph->daddr); + if (IS_ERR(rt)) { + netdev_dbg(gtp->dev, "no route for echo response from %pI4\n", + &iph->saddr); + return -1; + } + + udp_tunnel_xmit_skb(rt, gtp->sk1u, skb, + fl4.saddr, fl4.daddr, + iph->tos, + ip4_dst_hoplimit(&rt->dst), + 0, + htons(GTP1U_PORT), htons(GTP1U_PORT), + !net_eq(sock_net(gtp->sk1u), + dev_net(gtp->dev)), + false); + return 0; +} + +static int gtp1u_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) +{ + struct gtp1_header_long *gtp1u; + struct echo_info echo; + struct sk_buff *msg; + struct iphdr *iph; + int ret; + + gtp1u = (struct gtp1_header_long *)(skb->data + sizeof(struct udphdr)); + + /* 3GPP TS 29.281 5.1 - For the Echo Request, Echo Response, + * Error Indication and Supported Extension Headers Notification + * messages, the S flag shall be set to 1 and TEID shall be set to 0. + */ + if (!(gtp1u->flags & GTP1_F_SEQ) || gtp1u->tid) + return -1; + + iph = ip_hdr(skb); + echo.ms_addr_ip4.s_addr = iph->daddr; + echo.peer_addr_ip4.s_addr = iph->saddr; + echo.gtp_version = GTP_V1; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!msg) + return -ENOMEM; + + ret = gtp_genl_fill_echo(msg, 0, 0, 0, GTP_CMD_ECHOREQ, echo); + if (ret < 0) { + nlmsg_free(msg); + return ret; + } + + return genlmsg_multicast_netns(>p_genl_family, dev_net(gtp->dev), + msg, 0, GTP_GENL_MCGRP, GFP_ATOMIC); +} + static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) { unsigned int hdrlen = sizeof(struct udphdr) + @@ -258,6 +581,16 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) if ((gtp1->flags >> 5) != GTP_V1) return 1; + /* If the sockets were created in kernel, it means that + * there is no daemon running in userspace which would + * handle echo request. + */ + if (gtp1->type == GTP_ECHO_REQ && gtp->sk_created) + return gtp1u_send_echo_resp(gtp, skb); + + if (gtp1->type == GTP_ECHO_RSP && gtp->sk_created) + return gtp1u_handle_echo_resp(gtp, skb); + if (gtp1->type != GTP_TPDU) return 1; @@ -320,8 +653,16 @@ static void gtp_encap_disable_sock(struct sock *sk) static void gtp_encap_disable(struct gtp_dev *gtp) { - gtp_encap_disable_sock(gtp->sk0); - gtp_encap_disable_sock(gtp->sk1u); + if (gtp->sk_created) { + udp_tunnel_sock_release(gtp->sk0->sk_socket); + udp_tunnel_sock_release(gtp->sk1u->sk_socket); + gtp->sk_created = false; + gtp->sk0 = NULL; + gtp->sk1u = NULL; + } else { + gtp_encap_disable_sock(gtp->sk0); + gtp_encap_disable_sock(gtp->sk1u); + } } /* UDP encapsulation receive handler. See net/ipv4/udp.c. @@ -388,20 +729,6 @@ static void gtp_dev_uninit(struct net_device *dev) free_percpu(dev->tstats); } -static struct rtable *ip4_route_output_gtp(struct flowi4 *fl4, - const struct sock *sk, - __be32 daddr) -{ - memset(fl4, 0, sizeof(*fl4)); - fl4->flowi4_oif = sk->sk_bound_dev_if; - fl4->daddr = daddr; - fl4->saddr = inet_sk(sk)->inet_saddr; - fl4->flowi4_tos = RT_CONN_FLAGS(sk); - fl4->flowi4_proto = sk->sk_protocol; - - return ip_route_output_key(sock_net(sk), fl4); -} - static inline void gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) { int payload_len = skb->len; @@ -507,7 +834,8 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, } netdev_dbg(dev, "found PDP context %p\n", pctx); - rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->peer_addr_ip4.s_addr); + rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->peer_addr_ip4.s_addr, + inet_sk(pctx->sk)->inet_saddr); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to SSGN %pI4\n", &pctx->peer_addr_ip4.s_addr); @@ -656,17 +984,69 @@ static void gtp_destructor(struct net_device *dev) kfree(gtp->tid_hash); } +static struct sock *gtp_create_sock(int type, struct gtp_dev *gtp) +{ + struct udp_tunnel_sock_cfg tuncfg = {}; + struct udp_port_cfg udp_conf = { + .local_ip.s_addr = htonl(INADDR_ANY), + .family = AF_INET, + }; + struct net *net = gtp->net; + struct socket *sock; + int err; + + if (type == UDP_ENCAP_GTP0) + udp_conf.local_udp_port = htons(GTP0_PORT); + else if (type == UDP_ENCAP_GTP1U) + udp_conf.local_udp_port = htons(GTP1U_PORT); + else + return ERR_PTR(-EINVAL); + + err = udp_sock_create(net, &udp_conf, &sock); + if (err) + return ERR_PTR(err); + + tuncfg.sk_user_data = gtp; + tuncfg.encap_type = type; + tuncfg.encap_rcv = gtp_encap_recv; + tuncfg.encap_destroy = NULL; + + setup_udp_tunnel_sock(net, sock, &tuncfg); + + return sock->sk; +} + +static int gtp_create_sockets(struct gtp_dev *gtp, struct nlattr *data[]) +{ + struct sock *sk1u = NULL; + struct sock *sk0 = NULL; + + sk0 = gtp_create_sock(UDP_ENCAP_GTP0, gtp); + if (IS_ERR(sk0)) + return PTR_ERR(sk0); + + sk1u = gtp_create_sock(UDP_ENCAP_GTP1U, gtp); + if (IS_ERR(sk1u)) { + udp_tunnel_sock_release(sk0->sk_socket); + return PTR_ERR(sk1u); + } + + gtp->sk_created = true; + gtp->sk0 = sk0; + gtp->sk1u = sk1u; + + return 0; +} + static int gtp_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { + unsigned int role = GTP_ROLE_GGSN; struct gtp_dev *gtp; struct gtp_net *gn; int hashsize, err; - if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1]) - return -EINVAL; - gtp = netdev_priv(dev); if (!data[IFLA_GTP_PDP_HASHSIZE]) { @@ -677,11 +1057,28 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, hashsize = 1024; } + if (data[IFLA_GTP_ROLE]) { + role = nla_get_u32(data[IFLA_GTP_ROLE]); + if (role > GTP_ROLE_SGSN) + return -EINVAL; + } + gtp->role = role; + + if (!data[IFLA_GTP_RESTART_COUNT]) + gtp->restart_count = 0; + else + gtp->restart_count = nla_get_u8(data[IFLA_GTP_RESTART_COUNT]); + + gtp->net = src_net; + err = gtp_hashtable_new(gtp, hashsize); if (err < 0) return err; - err = gtp_encap_enable(gtp, data); + if (data[IFLA_GTP_CREATE_SOCKETS]) + err = gtp_create_sockets(gtp, data); + else + err = gtp_encap_enable(gtp, data); if (err < 0) goto out_hashtable; @@ -726,6 +1123,8 @@ static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = { [IFLA_GTP_FD1] = { .type = NLA_U32 }, [IFLA_GTP_PDP_HASHSIZE] = { .type = NLA_U32 }, [IFLA_GTP_ROLE] = { .type = NLA_U32 }, + [IFLA_GTP_CREATE_SOCKETS] = { .type = NLA_U8 }, + [IFLA_GTP_RESTART_COUNT] = { .type = NLA_U8 }, }; static int gtp_validate(struct nlattr *tb[], struct nlattr *data[], @@ -740,7 +1139,8 @@ static int gtp_validate(struct nlattr *tb[], struct nlattr *data[], static size_t gtp_get_size(const struct net_device *dev) { return nla_total_size(sizeof(__u32)) + /* IFLA_GTP_PDP_HASHSIZE */ - nla_total_size(sizeof(__u32)); /* IFLA_GTP_ROLE */ + nla_total_size(sizeof(__u32)) + /* IFLA_GTP_ROLE */ + nla_total_size(sizeof(__u8)); /* IFLA_GTP_RESTART_COUNT */ } static int gtp_fill_info(struct sk_buff *skb, const struct net_device *dev) @@ -751,6 +1151,8 @@ static int gtp_fill_info(struct sk_buff *skb, const struct net_device *dev) goto nla_put_failure; if (nla_put_u32(skb, IFLA_GTP_ROLE, gtp->role)) goto nla_put_failure; + if (nla_put_u8(skb, IFLA_GTP_RESTART_COUNT, gtp->restart_count)) + goto nla_put_failure; return 0; @@ -848,7 +1250,9 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) { struct sock *sk1u = NULL; struct sock *sk0 = NULL; - unsigned int role = GTP_ROLE_GGSN; + + if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1]) + return -EINVAL; if (data[IFLA_GTP_FD0]) { u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]); @@ -868,18 +1272,8 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) } } - if (data[IFLA_GTP_ROLE]) { - role = nla_get_u32(data[IFLA_GTP_ROLE]); - if (role > GTP_ROLE_SGSN) { - gtp_encap_disable_sock(sk0); - gtp_encap_disable_sock(sk1u); - return -EINVAL; - } - } - gtp->sk0 = sk0; gtp->sk1u = sk1u; - gtp->role = role; return 0; } @@ -1183,16 +1577,6 @@ static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) return err; } -static struct genl_family gtp_genl_family; - -enum gtp_multicast_groups { - GTP_GENL_MCGRP, -}; - -static const struct genl_multicast_group gtp_genl_mcgrps[] = { - [GTP_GENL_MCGRP] = { .name = GTP_GENL_MCGRP_NAME }, -}; - static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, int flags, u32 type, struct pdp_ctx *pctx) { @@ -1336,6 +1720,95 @@ static int gtp_genl_dump_pdp(struct sk_buff *skb, return skb->len; } +static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *skb_to_send; + __be32 src_ip, dst_ip; + unsigned int version; + struct gtp_dev *gtp; + struct flowi4 fl4; + struct rtable *rt; + struct sock *sk; + __be16 port; + int len; + + if (!info->attrs[GTPA_VERSION] || + !info->attrs[GTPA_LINK] || + !info->attrs[GTPA_PEER_ADDRESS] || + !info->attrs[GTPA_MS_ADDRESS]) + return -EINVAL; + + version = nla_get_u32(info->attrs[GTPA_VERSION]); + dst_ip = nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]); + src_ip = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); + + gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); + if (!gtp) + return -ENODEV; + + if (!gtp->sk_created) + return -EOPNOTSUPP; + if (!(gtp->dev->flags & IFF_UP)) + return -ENETDOWN; + + if (version == GTP_V0) { + struct gtp0_header *gtp0_h; + + len = LL_RESERVED_SPACE(gtp->dev) + sizeof(struct gtp0_header) + + sizeof(struct iphdr) + sizeof(struct udphdr); + + skb_to_send = netdev_alloc_skb_ip_align(gtp->dev, len); + if (!skb_to_send) + return -ENOMEM; + + sk = gtp->sk0; + port = htons(GTP0_PORT); + + gtp0_h = skb_push(skb_to_send, sizeof(struct gtp0_header)); + memset(gtp0_h, 0, sizeof(struct gtp0_header)); + gtp0_build_echo_msg(gtp0_h, GTP_ECHO_REQ); + } else if (version == GTP_V1) { + struct gtp1_header_long *gtp1u_h; + + len = LL_RESERVED_SPACE(gtp->dev) + + sizeof(struct gtp1_header_long) + + sizeof(struct iphdr) + sizeof(struct udphdr); + + skb_to_send = netdev_alloc_skb_ip_align(gtp->dev, len); + if (!skb_to_send) + return -ENOMEM; + + sk = gtp->sk1u; + port = htons(GTP1U_PORT); + + gtp1u_h = skb_push(skb_to_send, + sizeof(struct gtp1_header_long)); + memset(gtp1u_h, 0, sizeof(struct gtp1_header_long)); + gtp1u_build_echo_msg(gtp1u_h, GTP_ECHO_REQ); + } else { + return -ENODEV; + } + + rt = ip4_route_output_gtp(&fl4, sk, dst_ip, src_ip); + if (IS_ERR(rt)) { + netdev_dbg(gtp->dev, "no route for echo request to %pI4\n", + &dst_ip); + kfree_skb(skb_to_send); + return -ENODEV; + } + + udp_tunnel_xmit_skb(rt, sk, skb_to_send, + fl4.saddr, fl4.daddr, + fl4.flowi4_tos, + ip4_dst_hoplimit(&rt->dst), + 0, + port, port, + !net_eq(sock_net(sk), + dev_net(gtp->dev)), + false); + return 0; +} + static const struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = { [GTPA_LINK] = { .type = NLA_U32, }, [GTPA_VERSION] = { .type = NLA_U32, }, @@ -1368,6 +1841,12 @@ static const struct genl_small_ops gtp_genl_ops[] = { .dumpit = gtp_genl_dump_pdp, .flags = GENL_ADMIN_PERM, }, + { + .cmd = GTP_CMD_ECHOREQ, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = gtp_genl_send_echo_req, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_family gtp_genl_family __ro_after_init = { diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index a03d0b474641..3e69079ed694 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -982,10 +982,10 @@ static int baycom_setmode(struct baycom_state *bc, const char *modestr) bc->cfg.extmodem = 0; if (strstr(modestr,"extmodem")) bc->cfg.extmodem = 1; - if (strstr(modestr,"noloopback")) - bc->cfg.loopback = 0; if (strstr(modestr,"loopback")) bc->cfg.loopback = 1; + if (strstr(modestr, "noloopback")) + bc->cfg.loopback = 0; if ((cp = strstr(modestr,"fclk="))) { bc->cfg.fclk = simple_strtoul(cp+5, NULL, 0); if (bc->cfg.fclk < 1000000) diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index 7e527499d3ad..a2a12208e3ad 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "z8530.h" @@ -377,7 +378,7 @@ static int __init dmascc_init(void) udelay(2000000 / TMR_0_HZ); /* Timing loop */ - while (jiffies - time < 13) { + while (time_is_after_jiffies(time + 13)) { for (i = 0; i < hw[h].num_devs; i++) if (base[i] && counting[i]) { /* Read back Timer 1: latch; read LSB; read MSB */ @@ -525,7 +526,7 @@ static int __init setup_adapter(int card_base, int type, int n) /* Wait and detect IRQ */ time = jiffies; - while (jiffies - time < 2 + HZ / TMR_0_HZ); + while (time_is_after_jiffies(time + 2 + HZ / TMR_0_HZ)); irq = probe_irq_off(irqs); /* Clear pending interrupt, disable interrupts */ @@ -1353,7 +1354,7 @@ static void es_isr(struct scc_priv *priv) /* Switch state */ write_scc(priv, R15, 0); if (priv->tx_count && - (jiffies - priv->tx_start) < priv->param.txtimeout) { + time_is_after_jiffies(priv->tx_start + priv->param.txtimeout)) { priv->state = TX_PAUSE; start_timer(priv, priv->param.txpause, 0); } else { diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index afa81a9480cc..9442f751ad3a 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -154,19 +154,15 @@ static void free_netvsc_device(struct rcu_head *head) kfree(nvdev->extension); - if (nvdev->recv_original_buf) { - hv_unmap_memory(nvdev->recv_buf); + if (nvdev->recv_original_buf) vfree(nvdev->recv_original_buf); - } else { + else vfree(nvdev->recv_buf); - } - if (nvdev->send_original_buf) { - hv_unmap_memory(nvdev->send_buf); + if (nvdev->send_original_buf) vfree(nvdev->send_original_buf); - } else { + else vfree(nvdev->send_buf); - } bitmap_free(nvdev->send_section_map); @@ -765,6 +761,12 @@ void netvsc_device_remove(struct hv_device *device) netvsc_teardown_send_gpadl(device, net_device, ndev); } + if (net_device->recv_original_buf) + hv_unmap_memory(net_device->recv_buf); + + if (net_device->send_original_buf) + hv_unmap_memory(net_device->send_buf); + /* Release all resources */ free_netvsc_device_rcu(net_device); } @@ -1628,7 +1630,6 @@ static int netvsc_process_raw_pkt(struct hv_device *device, case VM_PKT_DATA_USING_XFER_PAGES: return netvsc_receive(ndev, net_device, nvchan, desc); - break; case VM_PKT_DATA_INBAND: netvsc_receive_inband(ndev, net_device, desc); @@ -1821,6 +1822,12 @@ struct netvsc_device *netvsc_device_add(struct hv_device *device, netif_napi_del(&net_device->chan_table[0].napi); cleanup2: + if (net_device->recv_original_buf) + hv_unmap_memory(net_device->recv_buf); + + if (net_device->send_original_buf) + hv_unmap_memory(net_device->send_buf); + free_netvsc_device(&net_device->rcu); return ERR_PTR(ret); diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c index 2f5e7b31032a..07bafbf94680 100644 --- a/drivers/net/ieee802154/atusb.c +++ b/drivers/net/ieee802154/atusb.c @@ -74,81 +74,6 @@ struct atusb_chip_data { int (*set_txpower)(struct ieee802154_hw*, s32); }; -/* ----- USB commands without data ----------------------------------------- */ - -/* To reduce the number of error checks in the code, we record the first error - * in atusb->err and reject all subsequent requests until the error is cleared. - */ - -static int atusb_control_msg(struct atusb *atusb, unsigned int pipe, - __u8 request, __u8 requesttype, - __u16 value, __u16 index, - void *data, __u16 size, int timeout) -{ - struct usb_device *usb_dev = atusb->usb_dev; - int ret; - - if (atusb->err) - return atusb->err; - - ret = usb_control_msg(usb_dev, pipe, request, requesttype, - value, index, data, size, timeout); - if (ret < size) { - ret = ret < 0 ? ret : -ENODATA; - - atusb->err = ret; - dev_err(&usb_dev->dev, - "%s: req 0x%02x val 0x%x idx 0x%x, error %d\n", - __func__, request, value, index, ret); - } - return ret; -} - -static int atusb_command(struct atusb *atusb, u8 cmd, u8 arg) -{ - struct usb_device *usb_dev = atusb->usb_dev; - - dev_dbg(&usb_dev->dev, "%s: cmd = 0x%x\n", __func__, cmd); - return atusb_control_msg(atusb, usb_sndctrlpipe(usb_dev, 0), - cmd, ATUSB_REQ_TO_DEV, arg, 0, NULL, 0, 1000); -} - -static int atusb_write_reg(struct atusb *atusb, u8 reg, u8 value) -{ - struct usb_device *usb_dev = atusb->usb_dev; - - dev_dbg(&usb_dev->dev, "%s: 0x%02x <- 0x%02x\n", __func__, reg, value); - return atusb_control_msg(atusb, usb_sndctrlpipe(usb_dev, 0), - ATUSB_REG_WRITE, ATUSB_REQ_TO_DEV, - value, reg, NULL, 0, 1000); -} - -static int atusb_read_reg(struct atusb *atusb, u8 reg) -{ - struct usb_device *usb_dev = atusb->usb_dev; - int ret; - u8 *buffer; - u8 value; - - buffer = kmalloc(1, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - dev_dbg(&usb_dev->dev, "%s: reg = 0x%x\n", __func__, reg); - ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0), - ATUSB_REG_READ, ATUSB_REQ_FROM_DEV, - 0, reg, buffer, 1, 1000); - - if (ret >= 0) { - value = buffer[0]; - kfree(buffer); - return value; - } else { - kfree(buffer); - return ret; - } -} - static int atusb_write_subreg(struct atusb *atusb, u8 reg, u8 mask, u8 shift, u8 value) { @@ -158,7 +83,10 @@ static int atusb_write_subreg(struct atusb *atusb, u8 reg, u8 mask, dev_dbg(&usb_dev->dev, "%s: 0x%02x <- 0x%02x\n", __func__, reg, value); - orig = atusb_read_reg(atusb, reg); + ret = usb_control_msg_recv(usb_dev, 0, ATUSB_REG_READ, ATUSB_REQ_FROM_DEV, + 0, reg, &orig, 1, 1000, GFP_KERNEL); + if (ret < 0) + return ret; /* Write the value only into that part of the register which is allowed * by the mask. All other bits stay as before. @@ -167,7 +95,8 @@ static int atusb_write_subreg(struct atusb *atusb, u8 reg, u8 mask, tmp |= (value << shift) & mask; if (tmp != orig) - ret = atusb_write_reg(atusb, reg, tmp); + ret = usb_control_msg_send(usb_dev, 0, ATUSB_REG_WRITE, ATUSB_REQ_TO_DEV, + tmp, reg, NULL, 0, 1000, GFP_KERNEL); return ret; } @@ -176,12 +105,16 @@ static int atusb_read_subreg(struct atusb *lp, unsigned int addr, unsigned int mask, unsigned int shift) { - int rc; + int reg, ret; - rc = atusb_read_reg(lp, addr); - rc = (rc & mask) >> shift; + ret = usb_control_msg_recv(lp->usb_dev, 0, ATUSB_REG_READ, ATUSB_REQ_FROM_DEV, + 0, addr, ®, 1, 1000, GFP_KERNEL); + if (ret < 0) + return ret; - return rc; + reg = (reg & mask) >> shift; + + return reg; } static int atusb_get_and_clear_error(struct atusb *atusb) @@ -419,16 +352,22 @@ static int atusb_set_hw_addr_filt(struct ieee802154_hw *hw, u16 addr = le16_to_cpu(filt->short_addr); dev_vdbg(dev, "%s called for saddr\n", __func__); - atusb_write_reg(atusb, RG_SHORT_ADDR_0, addr); - atusb_write_reg(atusb, RG_SHORT_ADDR_1, addr >> 8); + usb_control_msg_send(atusb->usb_dev, 0, ATUSB_REG_WRITE, ATUSB_REQ_TO_DEV, + addr, RG_SHORT_ADDR_0, NULL, 0, 1000, GFP_KERNEL); + + usb_control_msg_send(atusb->usb_dev, 0, ATUSB_REG_WRITE, ATUSB_REQ_TO_DEV, + addr >> 8, RG_SHORT_ADDR_1, NULL, 0, 1000, GFP_KERNEL); } if (changed & IEEE802154_AFILT_PANID_CHANGED) { u16 pan = le16_to_cpu(filt->pan_id); dev_vdbg(dev, "%s called for pan id\n", __func__); - atusb_write_reg(atusb, RG_PAN_ID_0, pan); - atusb_write_reg(atusb, RG_PAN_ID_1, pan >> 8); + usb_control_msg_send(atusb->usb_dev, 0, ATUSB_REG_WRITE, ATUSB_REQ_TO_DEV, + pan, RG_PAN_ID_0, NULL, 0, 1000, GFP_KERNEL); + + usb_control_msg_send(atusb->usb_dev, 0, ATUSB_REG_WRITE, ATUSB_REQ_TO_DEV, + pan >> 8, RG_PAN_ID_1, NULL, 0, 1000, GFP_KERNEL); } if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) { @@ -437,7 +376,9 @@ static int atusb_set_hw_addr_filt(struct ieee802154_hw *hw, memcpy(addr, &filt->ieee_addr, IEEE802154_EXTENDED_ADDR_LEN); dev_vdbg(dev, "%s called for IEEE addr\n", __func__); for (i = 0; i < 8; i++) - atusb_write_reg(atusb, RG_IEEE_ADDR_0 + i, addr[i]); + usb_control_msg_send(atusb->usb_dev, 0, ATUSB_REG_WRITE, ATUSB_REQ_TO_DEV, + addr[i], RG_IEEE_ADDR_0 + i, NULL, 0, + 1000, GFP_KERNEL); } if (changed & IEEE802154_AFILT_PANC_CHANGED) { @@ -459,7 +400,8 @@ static int atusb_start(struct ieee802154_hw *hw) dev_dbg(&usb_dev->dev, "%s\n", __func__); schedule_delayed_work(&atusb->work, 0); - atusb_command(atusb, ATUSB_RX_MODE, 1); + usb_control_msg_send(atusb->usb_dev, 0, ATUSB_RX_MODE, ATUSB_REQ_TO_DEV, 1, 0, + NULL, 0, 1000, GFP_KERNEL); ret = atusb_get_and_clear_error(atusb); if (ret < 0) usb_kill_anchored_urbs(&atusb->idle_urbs); @@ -473,7 +415,8 @@ static void atusb_stop(struct ieee802154_hw *hw) dev_dbg(&usb_dev->dev, "%s\n", __func__); usb_kill_anchored_urbs(&atusb->idle_urbs); - atusb_command(atusb, ATUSB_RX_MODE, 0); + usb_control_msg_send(atusb->usb_dev, 0, ATUSB_RX_MODE, ATUSB_REQ_TO_DEV, 0, 0, + NULL, 0, 1000, GFP_KERNEL); atusb_get_and_clear_error(atusb); } @@ -580,9 +523,11 @@ atusb_set_cca_mode(struct ieee802154_hw *hw, const struct wpan_phy_cca *cca) static int hulusb_set_cca_ed_level(struct atusb *lp, int rssi_base_val) { - unsigned int cca_ed_thres; + int cca_ed_thres; cca_ed_thres = atusb_read_subreg(lp, SR_CCA_ED_THRES); + if (cca_ed_thres < 0) + return cca_ed_thres; switch (rssi_base_val) { case -98: @@ -799,18 +744,13 @@ static int atusb_get_and_show_revision(struct atusb *atusb) { struct usb_device *usb_dev = atusb->usb_dev; char *hw_name; - unsigned char *buffer; + unsigned char buffer[3]; int ret; - buffer = kmalloc(3, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - /* Get a couple of the ATMega Firmware values */ - ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0), - ATUSB_ID, ATUSB_REQ_FROM_DEV, 0, 0, - buffer, 3, 1000); - if (ret >= 0) { + ret = usb_control_msg_recv(atusb->usb_dev, 0, ATUSB_ID, ATUSB_REQ_FROM_DEV, 0, 0, + buffer, 3, 1000, GFP_KERNEL); + if (!ret) { atusb->fw_ver_maj = buffer[0]; atusb->fw_ver_min = buffer[1]; atusb->fw_hw_type = buffer[2]; @@ -849,7 +789,6 @@ static int atusb_get_and_show_revision(struct atusb *atusb) dev_info(&usb_dev->dev, "Please update to version 0.2 or newer"); } - kfree(buffer); return ret; } @@ -863,7 +802,6 @@ static int atusb_get_and_show_build(struct atusb *atusb) if (!build) return -ENOMEM; - /* We cannot call atusb_control_msg() here, since this request may read various length data */ ret = usb_control_msg(atusb->usb_dev, usb_rcvctrlpipe(usb_dev, 0), ATUSB_BUILD, ATUSB_REQ_FROM_DEV, 0, 0, build, ATUSB_BUILD_SIZE, 1000); if (ret >= 0) { @@ -881,14 +819,27 @@ static int atusb_get_and_conf_chip(struct atusb *atusb) u8 man_id_0, man_id_1, part_num, version_num; const char *chip; struct ieee802154_hw *hw = atusb->hw; + int ret; - man_id_0 = atusb_read_reg(atusb, RG_MAN_ID_0); - man_id_1 = atusb_read_reg(atusb, RG_MAN_ID_1); - part_num = atusb_read_reg(atusb, RG_PART_NUM); - version_num = atusb_read_reg(atusb, RG_VERSION_NUM); + ret = usb_control_msg_recv(usb_dev, 0, ATUSB_REG_READ, ATUSB_REQ_FROM_DEV, + 0, RG_MAN_ID_0, &man_id_0, 1, 1000, GFP_KERNEL); + if (ret < 0) + return ret; - if (atusb->err) - return atusb->err; + ret = usb_control_msg_recv(usb_dev, 0, ATUSB_REG_READ, ATUSB_REQ_FROM_DEV, + 0, RG_MAN_ID_1, &man_id_1, 1, 1000, GFP_KERNEL); + if (ret < 0) + return ret; + + ret = usb_control_msg_recv(usb_dev, 0, ATUSB_REG_READ, ATUSB_REQ_FROM_DEV, + 0, RG_PART_NUM, &part_num, 1, 1000, GFP_KERNEL); + if (ret < 0) + return ret; + + ret = usb_control_msg_recv(usb_dev, 0, ATUSB_REG_READ, ATUSB_REQ_FROM_DEV, + 0, RG_VERSION_NUM, &version_num, 1, 1000, GFP_KERNEL); + if (ret < 0) + return ret; hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | IEEE802154_HW_PROMISCUOUS | IEEE802154_HW_CSMA_PARAMS; @@ -969,7 +920,7 @@ static int atusb_get_and_conf_chip(struct atusb *atusb) static int atusb_set_extended_addr(struct atusb *atusb) { struct usb_device *usb_dev = atusb->usb_dev; - unsigned char *buffer; + unsigned char buffer[IEEE802154_EXTENDED_ADDR_LEN]; __le64 extended_addr; u64 addr; int ret; @@ -982,18 +933,12 @@ static int atusb_set_extended_addr(struct atusb *atusb) return 0; } - buffer = kmalloc(IEEE802154_EXTENDED_ADDR_LEN, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - /* Firmware is new enough so we fetch the address from EEPROM */ - ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0), - ATUSB_EUI64_READ, ATUSB_REQ_FROM_DEV, 0, 0, - buffer, IEEE802154_EXTENDED_ADDR_LEN, 1000); + ret = usb_control_msg_recv(atusb->usb_dev, 0, ATUSB_EUI64_READ, ATUSB_REQ_FROM_DEV, 0, 0, + buffer, IEEE802154_EXTENDED_ADDR_LEN, 1000, GFP_KERNEL); if (ret < 0) { dev_err(&usb_dev->dev, "failed to fetch extended address, random address set\n"); ieee802154_random_extended_addr(&atusb->hw->phy->perm_extended_addr); - kfree(buffer); return ret; } @@ -1009,7 +954,6 @@ static int atusb_set_extended_addr(struct atusb *atusb) &addr); } - kfree(buffer); return ret; } @@ -1051,7 +995,8 @@ static int atusb_probe(struct usb_interface *interface, hw->parent = &usb_dev->dev; - atusb_command(atusb, ATUSB_RF_RESET, 0); + usb_control_msg_send(atusb->usb_dev, 0, ATUSB_RF_RESET, ATUSB_REQ_TO_DEV, 0, 0, + NULL, 0, 1000, GFP_KERNEL); atusb_get_and_conf_chip(atusb); atusb_get_and_show_revision(atusb); atusb_get_and_show_build(atusb); @@ -1076,7 +1021,9 @@ static int atusb_probe(struct usb_interface *interface, * explicitly. Any resets after that will send us straight to TRX_OFF, * making the command below redundant. */ - atusb_write_reg(atusb, RG_TRX_STATE, STATE_FORCE_TRX_OFF); + usb_control_msg_send(atusb->usb_dev, 0, ATUSB_REG_WRITE, ATUSB_REQ_TO_DEV, + STATE_FORCE_TRX_OFF, RG_TRX_STATE, NULL, 0, 1000, GFP_KERNEL); + msleep(1); /* reset => TRX_OFF, tTR13 = 37 us */ #if 0 @@ -1104,7 +1051,8 @@ static int atusb_probe(struct usb_interface *interface, atusb_write_subreg(atusb, SR_RX_SAFE_MODE, 1); #endif - atusb_write_reg(atusb, RG_IRQ_MASK, 0xff); + usb_control_msg_send(atusb->usb_dev, 0, ATUSB_REG_WRITE, ATUSB_REQ_TO_DEV, + 0xff, RG_IRQ_MASK, NULL, 0, 1000, GFP_KERNEL); ret = atusb_get_and_clear_error(atusb); if (!ret) diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c index 36f1c5aa98fc..38c217bd7c82 100644 --- a/drivers/net/ieee802154/mac802154_hwsim.c +++ b/drivers/net/ieee802154/mac802154_hwsim.c @@ -791,7 +791,7 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev, phy->idx = idx; INIT_LIST_HEAD(&phy->edges); - hw->flags = IEEE802154_HW_PROMISCUOUS; + hw->flags = IEEE802154_HW_PROMISCUOUS | IEEE802154_HW_RX_DROP_BAD_CKSUM; hw->parent = dev; err = ieee802154_register_hw(hw); diff --git a/drivers/net/ipa/gsi_trans.c b/drivers/net/ipa/gsi_trans.c index 1544564bc283..87e1d43c118c 100644 --- a/drivers/net/ipa/gsi_trans.c +++ b/drivers/net/ipa/gsi_trans.c @@ -320,6 +320,17 @@ gsi_trans_tre_release(struct gsi_trans_info *trans_info, u32 tre_count) atomic_add(tre_count, &trans_info->tre_avail); } +/* Return true if no transactions are allocated, false otherwise */ +bool gsi_channel_trans_idle(struct gsi *gsi, u32 channel_id) +{ + u32 tre_max = gsi_channel_tre_max(gsi, channel_id); + struct gsi_trans_info *trans_info; + + trans_info = &gsi->channel[channel_id].trans_info; + + return atomic_read(&trans_info->tre_avail) == tre_max; +} + /* Allocate a GSI transaction on a channel */ struct gsi_trans *gsi_channel_trans_alloc(struct gsi *gsi, u32 channel_id, u32 tre_count, diff --git a/drivers/net/ipa/gsi_trans.h b/drivers/net/ipa/gsi_trans.h index 17fd1822d8a9..af379b49299e 100644 --- a/drivers/net/ipa/gsi_trans.h +++ b/drivers/net/ipa/gsi_trans.h @@ -129,6 +129,16 @@ void *gsi_trans_pool_alloc_dma(struct gsi_trans_pool *pool, dma_addr_t *addr); */ void gsi_trans_pool_exit_dma(struct device *dev, struct gsi_trans_pool *pool); +/** + * gsi_channel_trans_idle() - Return whether no transactions are allocated + * @gsi: GSI pointer + * @channel_id: Channel the transaction is associated with + * + * Return: True if no transactions are allocated, false otherwise + * + */ +bool gsi_channel_trans_idle(struct gsi *gsi, u32 channel_id); + /** * gsi_channel_trans_alloc() - Allocate a GSI transaction on a channel * @gsi: GSI pointer diff --git a/drivers/net/ipa/ipa_data-v3.1.c b/drivers/net/ipa/ipa_data-v3.1.c index 06ddb85f39b2..8ff351aefd23 100644 --- a/drivers/net/ipa/ipa_data-v3.1.c +++ b/drivers/net/ipa/ipa_data-v3.1.c @@ -101,6 +101,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { .aggregation = true, .status_enable = true, .rx = { + .buffer_size = 8192, .pad_align = ilog2(sizeof(u32)), }, }, @@ -148,6 +149,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { .qmap = true, .aggregation = true, .rx = { + .buffer_size = 8192, .aggr_close_eof = true, }, }, diff --git a/drivers/net/ipa/ipa_data-v3.5.1.c b/drivers/net/ipa/ipa_data-v3.5.1.c index 760c22bbdf70..d1c466abddb2 100644 --- a/drivers/net/ipa/ipa_data-v3.5.1.c +++ b/drivers/net/ipa/ipa_data-v3.5.1.c @@ -92,6 +92,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { .aggregation = true, .status_enable = true, .rx = { + .buffer_size = 8192, .pad_align = ilog2(sizeof(u32)), }, }, @@ -140,6 +141,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { .qmap = true, .aggregation = true, .rx = { + .buffer_size = 8192, .aggr_close_eof = true, }, }, diff --git a/drivers/net/ipa/ipa_data-v4.11.c b/drivers/net/ipa/ipa_data-v4.11.c index fea91451a0c3..b1991cc6f0ca 100644 --- a/drivers/net/ipa/ipa_data-v4.11.c +++ b/drivers/net/ipa/ipa_data-v4.11.c @@ -86,6 +86,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { .aggregation = true, .status_enable = true, .rx = { + .buffer_size = 8192, .pad_align = ilog2(sizeof(u32)), }, }, @@ -133,6 +134,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { .qmap = true, .aggregation = true, .rx = { + .buffer_size = 32768, .aggr_close_eof = true, }, }, diff --git a/drivers/net/ipa/ipa_data-v4.2.c b/drivers/net/ipa/ipa_data-v4.2.c index 2a231e79d5e1..1190a43e8743 100644 --- a/drivers/net/ipa/ipa_data-v4.2.c +++ b/drivers/net/ipa/ipa_data-v4.2.c @@ -82,6 +82,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { .aggregation = true, .status_enable = true, .rx = { + .buffer_size = 8192, .pad_align = ilog2(sizeof(u32)), }, }, @@ -130,6 +131,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { .qmap = true, .aggregation = true, .rx = { + .buffer_size = 8192, .aggr_close_eof = true, }, }, diff --git a/drivers/net/ipa/ipa_data-v4.5.c b/drivers/net/ipa/ipa_data-v4.5.c index 2da2c4194f2e..944f72b0f285 100644 --- a/drivers/net/ipa/ipa_data-v4.5.c +++ b/drivers/net/ipa/ipa_data-v4.5.c @@ -95,6 +95,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { .aggregation = true, .status_enable = true, .rx = { + .buffer_size = 8192, .pad_align = ilog2(sizeof(u32)), }, }, @@ -142,6 +143,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { .qmap = true, .aggregation = true, .rx = { + .buffer_size = 8192, .aggr_close_eof = true, }, }, diff --git a/drivers/net/ipa/ipa_data-v4.9.c b/drivers/net/ipa/ipa_data-v4.9.c index 2421b5abb5d4..16786bff7ef8 100644 --- a/drivers/net/ipa/ipa_data-v4.9.c +++ b/drivers/net/ipa/ipa_data-v4.9.c @@ -87,6 +87,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { .aggregation = true, .status_enable = true, .rx = { + .buffer_size = 8192, .pad_align = ilog2(sizeof(u32)), }, }, @@ -134,6 +135,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { .qmap = true, .aggregation = true, .rx = { + .buffer_size = 8192, .aggr_close_eof = true, }, }, diff --git a/drivers/net/ipa/ipa_data.h b/drivers/net/ipa/ipa_data.h index 6d329e9ce5d2..dbbeecf6df29 100644 --- a/drivers/net/ipa/ipa_data.h +++ b/drivers/net/ipa/ipa_data.h @@ -112,6 +112,7 @@ struct ipa_endpoint_tx_data { /** * struct ipa_endpoint_rx_data - configuration data for RX endpoints + * @buffer_size: requested receive buffer size (bytes) * @pad_align: power-of-2 boundary to which packet payload is aligned * @aggr_close_eof: whether aggregation closes on end-of-frame * @@ -125,6 +126,7 @@ struct ipa_endpoint_tx_data { * a "frame" consisting of several transfers has ended. */ struct ipa_endpoint_rx_data { + u32 buffer_size; u32 pad_align; bool aggr_close_eof; }; diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index 68291a3efd04..888e94278a84 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -25,10 +25,8 @@ #define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) -#define IPA_REPLENISH_BATCH 16 - -/* RX buffer is 1 page (or a power-of-2 contiguous pages) */ -#define IPA_RX_BUFFER_SIZE 8192 /* PAGE_SIZE > 4096 wastes a LOT */ +/* Hardware is told about receive buffers once a "batch" has been queued */ +#define IPA_REPLENISH_BATCH 16 /* Must be non-zero */ /* The amount of RX buffer space consumed by standard skb overhead */ #define IPA_RX_BUFFER_OVERHEAD (PAGE_SIZE - SKB_MAX_ORDER(NET_SKB_PAD, 0)) @@ -75,6 +73,14 @@ struct ipa_status { #define IPA_STATUS_FLAGS1_RT_RULE_ID_FMASK GENMASK(31, 22) #define IPA_STATUS_FLAGS2_TAG_FMASK GENMASK_ULL(63, 16) +static u32 aggr_byte_limit_max(enum ipa_version version) +{ + if (version < IPA_VERSION_4_5) + return field_max(aggr_byte_limit_fmask(true)); + + return field_max(aggr_byte_limit_fmask(false)); +} + static bool ipa_endpoint_data_valid_one(struct ipa *ipa, u32 count, const struct ipa_gsi_endpoint_data *all_data, const struct ipa_gsi_endpoint_data *data) @@ -87,6 +93,9 @@ static bool ipa_endpoint_data_valid_one(struct ipa *ipa, u32 count, return true; if (!data->toward_ipa) { + u32 buffer_size; + u32 limit; + if (data->endpoint.filter_support) { dev_err(dev, "filtering not supported for " "RX endpoint %u\n", @@ -94,6 +103,41 @@ static bool ipa_endpoint_data_valid_one(struct ipa *ipa, u32 count, return false; } + /* Nothing more to check for non-AP RX */ + if (data->ee_id != GSI_EE_AP) + return true; + + buffer_size = data->endpoint.config.rx.buffer_size; + /* The buffer size must hold an MTU plus overhead */ + limit = IPA_MTU + IPA_RX_BUFFER_OVERHEAD; + if (buffer_size < limit) { + dev_err(dev, "RX buffer size too small for RX endpoint %u (%u < %u)\n", + data->endpoint_id, buffer_size, limit); + return false; + } + + /* For an endpoint supporting receive aggregation, the + * aggregation byte limit defines the point at which an + * aggregation window will close. It is programmed into the + * IPA hardware as a number of KB. We don't use "hard byte + * limit" aggregation, so we need to supply enough space in + * a receive buffer to hold a complete MTU plus normal skb + * overhead *after* that aggregation byte limit has been + * crossed. + * + * This check just ensures the receive buffer size doesn't + * exceed what's representable in the aggregation limit field. + */ + if (data->endpoint.config.aggregation) { + limit += SZ_1K * aggr_byte_limit_max(ipa->version); + if (buffer_size > limit) { + dev_err(dev, "RX buffer size too large for aggregated RX endpoint %u (%u > %u)\n", + data->endpoint_id, buffer_size, limit); + + return false; + } + } + return true; /* Nothing more to check for RX */ } @@ -156,21 +200,12 @@ static bool ipa_endpoint_data_valid_one(struct ipa *ipa, u32 count, return true; } -static u32 aggr_byte_limit_max(enum ipa_version version) -{ - if (version < IPA_VERSION_4_5) - return field_max(aggr_byte_limit_fmask(true)); - - return field_max(aggr_byte_limit_fmask(false)); -} - static bool ipa_endpoint_data_valid(struct ipa *ipa, u32 count, const struct ipa_gsi_endpoint_data *data) { const struct ipa_gsi_endpoint_data *dp = data; struct device *dev = &ipa->pdev->dev; enum ipa_endpoint_name name; - u32 limit; if (count > IPA_ENDPOINT_COUNT) { dev_err(dev, "too many endpoints specified (%u > %u)\n", @@ -178,26 +213,6 @@ static bool ipa_endpoint_data_valid(struct ipa *ipa, u32 count, return false; } - /* The aggregation byte limit defines the point at which an - * aggregation window will close. It is programmed into the - * IPA hardware as a number of KB. We don't use "hard byte - * limit" aggregation, which means that we need to supply - * enough space in a receive buffer to hold a complete MTU - * plus normal skb overhead *after* that aggregation byte - * limit has been crossed. - * - * This check ensures we don't define a receive buffer size - * that would exceed what we can represent in the field that - * is used to program its size. - */ - limit = aggr_byte_limit_max(ipa->version) * SZ_1K; - limit += IPA_MTU + IPA_RX_BUFFER_OVERHEAD; - if (limit < IPA_RX_BUFFER_SIZE) { - dev_err(dev, "buffer size too big for aggregation (%u > %u)\n", - IPA_RX_BUFFER_SIZE, limit); - return false; - } - /* Make sure needed endpoints have defined data */ if (ipa_gsi_endpoint_data_empty(&data[IPA_ENDPOINT_AP_COMMAND_TX])) { dev_err(dev, "command TX endpoint not defined\n"); @@ -723,13 +738,15 @@ static void ipa_endpoint_init_aggr(struct ipa_endpoint *endpoint) if (endpoint->data->aggregation) { if (!endpoint->toward_ipa) { + const struct ipa_endpoint_rx_data *rx_data; bool close_eof; u32 limit; + rx_data = &endpoint->data->rx; val |= u32_encode_bits(IPA_ENABLE_AGGR, AGGR_EN_FMASK); val |= u32_encode_bits(IPA_GENERIC, AGGR_TYPE_FMASK); - limit = ipa_aggr_size_kb(IPA_RX_BUFFER_SIZE); + limit = ipa_aggr_size_kb(rx_data->buffer_size); val |= aggr_byte_limit_encoded(version, limit); limit = IPA_AGGR_TIME_LIMIT; @@ -737,7 +754,7 @@ static void ipa_endpoint_init_aggr(struct ipa_endpoint *endpoint) /* AGGR_PKT_LIMIT is 0 (unlimited) */ - close_eof = endpoint->data->rx.aggr_close_eof; + close_eof = rx_data->aggr_close_eof; val |= aggr_sw_eof_active_encoded(version, close_eof); /* AGGR_HARD_BYTE_LIMIT_ENABLE is 0 */ @@ -1020,134 +1037,98 @@ static void ipa_endpoint_status(struct ipa_endpoint *endpoint) iowrite32(val, ipa->reg_virt + offset); } -static int ipa_endpoint_replenish_one(struct ipa_endpoint *endpoint) +static int ipa_endpoint_replenish_one(struct ipa_endpoint *endpoint, + struct gsi_trans *trans) { - struct gsi_trans *trans; - bool doorbell = false; struct page *page; + u32 buffer_size; u32 offset; u32 len; int ret; - page = dev_alloc_pages(get_order(IPA_RX_BUFFER_SIZE)); + buffer_size = endpoint->data->rx.buffer_size; + page = dev_alloc_pages(get_order(buffer_size)); if (!page) return -ENOMEM; - trans = ipa_endpoint_trans_alloc(endpoint, 1); - if (!trans) - goto err_free_pages; - /* Offset the buffer to make space for skb headroom */ offset = NET_SKB_PAD; - len = IPA_RX_BUFFER_SIZE - offset; + len = buffer_size - offset; ret = gsi_trans_page_add(trans, page, len, offset); if (ret) - goto err_trans_free; - trans->data = page; /* transaction owns page now */ + __free_pages(page, get_order(buffer_size)); + else + trans->data = page; /* transaction owns page now */ - if (++endpoint->replenish_ready == IPA_REPLENISH_BATCH) { - doorbell = true; - endpoint->replenish_ready = 0; - } - - gsi_trans_commit(trans, doorbell); - - return 0; - -err_trans_free: - gsi_trans_free(trans); -err_free_pages: - __free_pages(page, get_order(IPA_RX_BUFFER_SIZE)); - - return -ENOMEM; + return ret; } /** * ipa_endpoint_replenish() - Replenish endpoint receive buffers * @endpoint: Endpoint to be replenished - * @add_one: Whether this is replacing a just-consumed buffer * * The IPA hardware can hold a fixed number of receive buffers for an RX * endpoint, based on the number of entries in the underlying channel ring * buffer. If an endpoint's "backlog" is non-zero, it indicates how many * more receive buffers can be supplied to the hardware. Replenishing for - * an endpoint can be disabled, in which case requests to replenish a - * buffer are "saved", and transferred to the backlog once it is re-enabled - * again. + * an endpoint can be disabled, in which case buffers are not queued to + * the hardware. */ -static void ipa_endpoint_replenish(struct ipa_endpoint *endpoint, bool add_one) +static void ipa_endpoint_replenish(struct ipa_endpoint *endpoint) { - struct gsi *gsi; - u32 backlog; - int delta; + struct gsi_trans *trans; - if (!test_bit(IPA_REPLENISH_ENABLED, endpoint->replenish_flags)) { - if (add_one) - atomic_inc(&endpoint->replenish_saved); + if (!test_bit(IPA_REPLENISH_ENABLED, endpoint->replenish_flags)) return; - } - /* If already active, just update the backlog */ - if (test_and_set_bit(IPA_REPLENISH_ACTIVE, endpoint->replenish_flags)) { - if (add_one) - atomic_inc(&endpoint->replenish_backlog); + /* Skip it if it's already active */ + if (test_and_set_bit(IPA_REPLENISH_ACTIVE, endpoint->replenish_flags)) return; - } - while (atomic_dec_not_zero(&endpoint->replenish_backlog)) - if (ipa_endpoint_replenish_one(endpoint)) + while ((trans = ipa_endpoint_trans_alloc(endpoint, 1))) { + bool doorbell; + + if (ipa_endpoint_replenish_one(endpoint, trans)) goto try_again_later; - clear_bit(IPA_REPLENISH_ACTIVE, endpoint->replenish_flags); - if (add_one) - atomic_inc(&endpoint->replenish_backlog); + /* Ring the doorbell if we've got a full batch */ + doorbell = !(++endpoint->replenish_count % IPA_REPLENISH_BATCH); + gsi_trans_commit(trans, doorbell); + } + + clear_bit(IPA_REPLENISH_ACTIVE, endpoint->replenish_flags); return; try_again_later: + gsi_trans_free(trans); clear_bit(IPA_REPLENISH_ACTIVE, endpoint->replenish_flags); - /* The last one didn't succeed, so fix the backlog */ - delta = add_one ? 2 : 1; - backlog = atomic_add_return(delta, &endpoint->replenish_backlog); - /* Whenever a receive buffer transaction completes we'll try to * replenish again. It's unlikely, but if we fail to supply even * one buffer, nothing will trigger another replenish attempt. - * Receive buffer transactions use one TRE, so schedule work to - * try replenishing again if our backlog is *all* available TREs. + * If the hardware has no receive buffers queued, schedule work to + * try replenishing again. */ - gsi = &endpoint->ipa->gsi; - if (backlog == gsi_channel_tre_max(gsi, endpoint->channel_id)) + if (gsi_channel_trans_idle(&endpoint->ipa->gsi, endpoint->channel_id)) schedule_delayed_work(&endpoint->replenish_work, msecs_to_jiffies(1)); } static void ipa_endpoint_replenish_enable(struct ipa_endpoint *endpoint) { - struct gsi *gsi = &endpoint->ipa->gsi; - u32 max_backlog; - u32 saved; - set_bit(IPA_REPLENISH_ENABLED, endpoint->replenish_flags); - while ((saved = atomic_xchg(&endpoint->replenish_saved, 0))) - atomic_add(saved, &endpoint->replenish_backlog); /* Start replenishing if hardware currently has no buffers */ - max_backlog = gsi_channel_tre_max(gsi, endpoint->channel_id); - if (atomic_read(&endpoint->replenish_backlog) == max_backlog) - ipa_endpoint_replenish(endpoint, false); + if (gsi_channel_trans_idle(&endpoint->ipa->gsi, endpoint->channel_id)) + ipa_endpoint_replenish(endpoint); } static void ipa_endpoint_replenish_disable(struct ipa_endpoint *endpoint) { - u32 backlog; - clear_bit(IPA_REPLENISH_ENABLED, endpoint->replenish_flags); - while ((backlog = atomic_xchg(&endpoint->replenish_backlog, 0))) - atomic_add(backlog, &endpoint->replenish_saved); } static void ipa_endpoint_replenish_work(struct work_struct *work) @@ -1157,7 +1138,7 @@ static void ipa_endpoint_replenish_work(struct work_struct *work) endpoint = container_of(dwork, struct ipa_endpoint, replenish_work); - ipa_endpoint_replenish(endpoint, false); + ipa_endpoint_replenish(endpoint); } static void ipa_endpoint_skb_copy(struct ipa_endpoint *endpoint, @@ -1183,15 +1164,16 @@ static void ipa_endpoint_skb_copy(struct ipa_endpoint *endpoint, static bool ipa_endpoint_skb_build(struct ipa_endpoint *endpoint, struct page *page, u32 len) { + u32 buffer_size = endpoint->data->rx.buffer_size; struct sk_buff *skb; /* Nothing to do if there's no netdev */ if (!endpoint->netdev) return false; - WARN_ON(len > SKB_WITH_OVERHEAD(IPA_RX_BUFFER_SIZE - NET_SKB_PAD)); + WARN_ON(len > SKB_WITH_OVERHEAD(buffer_size - NET_SKB_PAD)); - skb = build_skb(page_address(page), IPA_RX_BUFFER_SIZE); + skb = build_skb(page_address(page), buffer_size); if (skb) { /* Reserve the headroom and account for the data */ skb_reserve(skb, NET_SKB_PAD); @@ -1289,8 +1271,9 @@ static bool ipa_endpoint_status_drop(struct ipa_endpoint *endpoint, static void ipa_endpoint_status_parse(struct ipa_endpoint *endpoint, struct page *page, u32 total_len) { + u32 buffer_size = endpoint->data->rx.buffer_size; void *data = page_address(page) + NET_SKB_PAD; - u32 unused = IPA_RX_BUFFER_SIZE - total_len; + u32 unused = buffer_size - total_len; u32 resid = total_len; while (resid) { @@ -1360,10 +1343,8 @@ static void ipa_endpoint_rx_complete(struct ipa_endpoint *endpoint, { struct page *page; - ipa_endpoint_replenish(endpoint, true); - if (trans->cancelled) - return; + goto done; /* Parse or build a socket buffer using the actual received length */ page = trans->data; @@ -1371,6 +1352,8 @@ static void ipa_endpoint_rx_complete(struct ipa_endpoint *endpoint, ipa_endpoint_status_parse(endpoint, page, trans->len); else if (ipa_endpoint_skb_build(endpoint, page, trans->len)) trans->data = NULL; /* Pages have been consumed */ +done: + ipa_endpoint_replenish(endpoint); } void ipa_endpoint_trans_complete(struct ipa_endpoint *endpoint, @@ -1398,8 +1381,11 @@ void ipa_endpoint_trans_release(struct ipa_endpoint *endpoint, } else { struct page *page = trans->data; - if (page) - __free_pages(page, get_order(IPA_RX_BUFFER_SIZE)); + if (page) { + u32 buffer_size = endpoint->data->rx.buffer_size; + + __free_pages(page, get_order(buffer_size)); + } } } @@ -1704,9 +1690,6 @@ static void ipa_endpoint_setup_one(struct ipa_endpoint *endpoint) */ clear_bit(IPA_REPLENISH_ENABLED, endpoint->replenish_flags); clear_bit(IPA_REPLENISH_ACTIVE, endpoint->replenish_flags); - atomic_set(&endpoint->replenish_saved, - gsi_channel_tre_max(gsi, endpoint->channel_id)); - atomic_set(&endpoint->replenish_backlog, 0); INIT_DELAYED_WORK(&endpoint->replenish_work, ipa_endpoint_replenish_work); } @@ -1882,6 +1865,8 @@ u32 ipa_endpoint_init(struct ipa *ipa, u32 count, enum ipa_endpoint_name name; u32 filter_map; + BUILD_BUG_ON(!IPA_REPLENISH_BATCH); + if (!ipa_endpoint_data_valid(ipa, count, data)) return 0; /* Error */ diff --git a/drivers/net/ipa/ipa_endpoint.h b/drivers/net/ipa/ipa_endpoint.h index 0313cdc607de..12fd5b16c18e 100644 --- a/drivers/net/ipa/ipa_endpoint.h +++ b/drivers/net/ipa/ipa_endpoint.h @@ -65,9 +65,7 @@ enum ipa_replenish_flag { * @evt_ring_id: GSI event ring used by the endpoint * @netdev: Network device pointer, if endpoint uses one * @replenish_flags: Replenishing state flags - * @replenish_ready: Number of replenish transactions without doorbell - * @replenish_saved: Replenish requests held while disabled - * @replenish_backlog: Number of buffers needed to fill hardware queue + * @replenish_count: Total number of replenish transactions committed * @replenish_work: Work item used for repeated replenish failures */ struct ipa_endpoint { @@ -86,9 +84,7 @@ struct ipa_endpoint { /* Receive buffer replenishing for RX endpoints */ DECLARE_BITMAP(replenish_flags, IPA_REPLENISH_COUNT); - u32 replenish_ready; - atomic_t replenish_saved; - atomic_t replenish_backlog; + u64 replenish_count; struct delayed_work replenish_work; /* global wq */ }; diff --git a/drivers/net/ipa/ipa_power.c b/drivers/net/ipa/ipa_power.c index f2989aac47a6..db5ac7552286 100644 --- a/drivers/net/ipa/ipa_power.c +++ b/drivers/net/ipa/ipa_power.c @@ -34,18 +34,6 @@ #define IPA_AUTOSUSPEND_DELAY 500 /* milliseconds */ -/** - * struct ipa_interconnect - IPA interconnect information - * @path: Interconnect path - * @average_bandwidth: Average interconnect bandwidth (KB/second) - * @peak_bandwidth: Peak interconnect bandwidth (KB/second) - */ -struct ipa_interconnect { - struct icc_path *path; - u32 average_bandwidth; - u32 peak_bandwidth; -}; - /** * enum ipa_power_flag - IPA power flags * @IPA_POWER_FLAG_RESUMED: Whether resume from suspend has been signaled @@ -79,66 +67,40 @@ struct ipa_power { spinlock_t spinlock; /* used with STOPPED/STARTED power flags */ DECLARE_BITMAP(flags, IPA_POWER_FLAG_COUNT); u32 interconnect_count; - struct ipa_interconnect *interconnect; + struct icc_bulk_data interconnect[]; }; -static int ipa_interconnect_init_one(struct device *dev, - struct ipa_interconnect *interconnect, - const struct ipa_interconnect_data *data) -{ - struct icc_path *path; - - path = of_icc_get(dev, data->name); - if (IS_ERR(path)) { - int ret = PTR_ERR(path); - - dev_err_probe(dev, ret, "error getting %s interconnect\n", - data->name); - - return ret; - } - - interconnect->path = path; - interconnect->average_bandwidth = data->average_bandwidth; - interconnect->peak_bandwidth = data->peak_bandwidth; - - return 0; -} - -static void ipa_interconnect_exit_one(struct ipa_interconnect *interconnect) -{ - icc_put(interconnect->path); - memset(interconnect, 0, sizeof(*interconnect)); -} - /* Initialize interconnects required for IPA operation */ -static int ipa_interconnect_init(struct ipa_power *power, struct device *dev, +static int ipa_interconnect_init(struct ipa_power *power, const struct ipa_interconnect_data *data) { - struct ipa_interconnect *interconnect; - u32 count; + struct icc_bulk_data *interconnect; int ret; + u32 i; - count = power->interconnect_count; - interconnect = kcalloc(count, sizeof(*interconnect), GFP_KERNEL); - if (!interconnect) - return -ENOMEM; - power->interconnect = interconnect; - - while (count--) { - ret = ipa_interconnect_init_one(dev, interconnect, data++); - if (ret) - goto out_unwind; + /* Initialize our interconnect data array for bulk operations */ + interconnect = &power->interconnect[0]; + for (i = 0; i < power->interconnect_count; i++) { + /* interconnect->path is filled in by of_icc_bulk_get() */ + interconnect->name = data->name; + interconnect->avg_bw = data->average_bandwidth; + interconnect->peak_bw = data->peak_bandwidth; + data++; interconnect++; } - return 0; + ret = of_icc_bulk_get(power->dev, power->interconnect_count, + power->interconnect); + if (ret) + return ret; -out_unwind: - while (interconnect-- > power->interconnect) - ipa_interconnect_exit_one(interconnect); - kfree(power->interconnect); - power->interconnect = NULL; + /* All interconnects are initially disabled */ + icc_bulk_disable(power->interconnect_count, power->interconnect); + + /* Set the bandwidth values to be used when enabled */ + ret = icc_bulk_set_bw(power->interconnect_count, power->interconnect); + if (ret) + icc_bulk_put(power->interconnect_count, power->interconnect); return ret; } @@ -146,97 +108,37 @@ static int ipa_interconnect_init(struct ipa_power *power, struct device *dev, /* Inverse of ipa_interconnect_init() */ static void ipa_interconnect_exit(struct ipa_power *power) { - struct ipa_interconnect *interconnect; - - interconnect = power->interconnect + power->interconnect_count; - while (interconnect-- > power->interconnect) - ipa_interconnect_exit_one(interconnect); - kfree(power->interconnect); - power->interconnect = NULL; -} - -/* Currently we only use one bandwidth level, so just "enable" interconnects */ -static int ipa_interconnect_enable(struct ipa *ipa) -{ - struct ipa_interconnect *interconnect; - struct ipa_power *power = ipa->power; - int ret; - u32 i; - - interconnect = power->interconnect; - for (i = 0; i < power->interconnect_count; i++) { - ret = icc_set_bw(interconnect->path, - interconnect->average_bandwidth, - interconnect->peak_bandwidth); - if (ret) { - dev_err(&ipa->pdev->dev, - "error %d enabling %s interconnect\n", - ret, icc_get_name(interconnect->path)); - goto out_unwind; - } - interconnect++; - } - - return 0; - -out_unwind: - while (interconnect-- > power->interconnect) - (void)icc_set_bw(interconnect->path, 0, 0); - - return ret; -} - -/* To disable an interconnect, we just its bandwidth to 0 */ -static int ipa_interconnect_disable(struct ipa *ipa) -{ - struct ipa_interconnect *interconnect; - struct ipa_power *power = ipa->power; - struct device *dev = &ipa->pdev->dev; - int result = 0; - u32 count; - int ret; - - count = power->interconnect_count; - interconnect = power->interconnect + count; - while (count--) { - interconnect--; - ret = icc_set_bw(interconnect->path, 0, 0); - if (ret) { - dev_err(dev, "error %d disabling %s interconnect\n", - ret, icc_get_name(interconnect->path)); - /* Try to disable all; record only the first error */ - if (!result) - result = ret; - } - } - - return result; + icc_bulk_put(power->interconnect_count, power->interconnect); } /* Enable IPA power, enabling interconnects and the core clock */ static int ipa_power_enable(struct ipa *ipa) { + struct ipa_power *power = ipa->power; int ret; - ret = ipa_interconnect_enable(ipa); + ret = icc_bulk_enable(power->interconnect_count, power->interconnect); if (ret) return ret; - ret = clk_prepare_enable(ipa->power->core); + ret = clk_prepare_enable(power->core); if (ret) { - dev_err(&ipa->pdev->dev, "error %d enabling core clock\n", ret); - (void)ipa_interconnect_disable(ipa); + dev_err(power->dev, "error %d enabling core clock\n", ret); + icc_bulk_disable(power->interconnect_count, + power->interconnect); } return ret; } /* Inverse of ipa_power_enable() */ -static int ipa_power_disable(struct ipa *ipa) +static void ipa_power_disable(struct ipa *ipa) { - clk_disable_unprepare(ipa->power->core); + struct ipa_power *power = ipa->power; - return ipa_interconnect_disable(ipa); + clk_disable_unprepare(power->core); + + icc_bulk_disable(power->interconnect_count, power->interconnect); } static int ipa_runtime_suspend(struct device *dev) @@ -250,7 +152,9 @@ static int ipa_runtime_suspend(struct device *dev) gsi_suspend(&ipa->gsi); } - return ipa_power_disable(ipa); + ipa_power_disable(ipa); + + return 0; } static int ipa_runtime_resume(struct device *dev) @@ -453,6 +357,7 @@ ipa_power_init(struct device *dev, const struct ipa_power_data *data) { struct ipa_power *power; struct clk *clk; + size_t size; int ret; clk = clk_get(dev, "core"); @@ -469,7 +374,8 @@ ipa_power_init(struct device *dev, const struct ipa_power_data *data) goto err_clk_put; } - power = kzalloc(sizeof(*power), GFP_KERNEL); + size = struct_size(power, interconnect, data->interconnect_count); + power = kzalloc(size, GFP_KERNEL); if (!power) { ret = -ENOMEM; goto err_clk_put; @@ -479,7 +385,7 @@ ipa_power_init(struct device *dev, const struct ipa_power_data *data) spin_lock_init(&power->spinlock); power->interconnect_count = data->interconnect_count; - ret = ipa_interconnect_init(power, dev, data->interconnect_data); + ret = ipa_interconnect_init(power, data->interconnect_data); if (ret) goto err_kfree; diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index c613900c3811..6ffb27419e64 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -555,7 +555,7 @@ static void ipvlan_multicast_enqueue(struct ipvl_port *port, schedule_work(&port->wq); } else { spin_unlock(&port->backlog.lock); - atomic_long_inc(&skb->dev->rx_dropped); + dev_core_stats_rx_dropped_inc(skb->dev); kfree_skb(skb); } } diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index ed0edf5884ef..720394c0639b 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -74,11 +74,11 @@ static netdev_tx_t loopback_xmit(struct sk_buff *skb, skb_tx_timestamp(skb); /* do not fool net_timestamp_check() with various clock bases */ - skb->tstamp = 0; + skb_clear_tstamp(skb); skb_orphan(skb); - /* Before queueing this packet to netif_rx(), + /* Before queueing this packet to __netif_rx(), * make sure dst is refcounted. */ skb_dst_force(skb); @@ -86,7 +86,7 @@ static netdev_tx_t loopback_xmit(struct sk_buff *skb, skb->protocol = eth_type_trans(skb, dev); len = skb->len; - if (likely(netif_rx(skb) == NET_RX_SUCCESS)) + if (likely(__netif_rx(skb) == NET_RX_SUCCESS)) dev_lstats_add(dev, len); return NETDEV_TX_OK; diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 3d0874331763..832f09ac075e 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -1033,7 +1033,7 @@ static enum rx_handler_result handle_not_macsec(struct sk_buff *skb) else nskb->pkt_type = PACKET_MULTICAST; - netif_rx(nskb); + __netif_rx(nskb); } continue; } @@ -1056,7 +1056,7 @@ static enum rx_handler_result handle_not_macsec(struct sk_buff *skb) nskb->dev = ndev; - if (netif_rx(nskb) == NET_RX_SUCCESS) { + if (__netif_rx(nskb) == NET_RX_SUCCESS) { u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsUntagged++; u64_stats_update_end(&secy_stats->syncp); @@ -1288,7 +1288,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) macsec_reset_skb(nskb, macsec->secy.netdev); - ret = netif_rx(nskb); + ret = __netif_rx(nskb); if (ret == NET_RX_SUCCESS) { u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsUnknownSCI++; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 6ef5f77be4d0..069e8824c264 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -285,7 +285,7 @@ static void macvlan_broadcast(struct sk_buff *skb, if (likely(nskb)) err = macvlan_broadcast_one(nskb, vlan, eth, mode == MACVLAN_MODE_BRIDGE) ?: - netif_rx_ni(nskb); + netif_rx(nskb); macvlan_count_rx(vlan, skb->len + ETH_HLEN, err == NET_RX_SUCCESS, true); } @@ -371,7 +371,7 @@ static void macvlan_broadcast_enqueue(struct macvlan_port *port, free_nskb: kfree_skb(nskb); err: - atomic_long_inc(&skb->dev->rx_dropped); + dev_core_stats_rx_dropped_inc(skb->dev); } static void macvlan_flush_sources(struct macvlan_port *port, @@ -410,7 +410,7 @@ static void macvlan_forward_source_one(struct sk_buff *skb, if (ether_addr_equal_64bits(eth_hdr(skb)->h_dest, dev->dev_addr)) nskb->pkt_type = PACKET_HOST; - ret = netif_rx(nskb); + ret = __netif_rx(nskb); macvlan_count_rx(vlan, len, ret == NET_RX_SUCCESS, false); } @@ -468,7 +468,7 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) /* forward to original port. */ vlan = src; ret = macvlan_broadcast_one(skb, vlan, eth, 0) ?: - netif_rx(skb); + __netif_rx(skb); handle_res = RX_HANDLER_CONSUMED; goto out; } @@ -889,7 +889,7 @@ static void macvlan_set_lockdep_class(struct net_device *dev) static int macvlan_init(struct net_device *dev) { struct macvlan_dev *vlan = netdev_priv(dev); - const struct net_device *lowerdev = vlan->lowerdev; + struct net_device *lowerdev = vlan->lowerdev; struct macvlan_port *port = vlan->port; dev->state = (dev->state & ~MACVLAN_STATE_MASK) | @@ -911,6 +911,9 @@ static int macvlan_init(struct net_device *dev) port->count += 1; + /* Get macvlan's reference to lowerdev */ + dev_hold_track(lowerdev, &vlan->dev_tracker, GFP_KERNEL); + return 0; } @@ -1173,6 +1176,14 @@ static const struct net_device_ops macvlan_netdev_ops = { .ndo_features_check = passthru_features_check, }; +static void macvlan_dev_free(struct net_device *dev) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + + /* Get rid of the macvlan's reference to lowerdev */ + dev_put_track(vlan->lowerdev, &vlan->dev_tracker); +} + void macvlan_common_setup(struct net_device *dev) { ether_setup(dev); @@ -1184,6 +1195,7 @@ void macvlan_common_setup(struct net_device *dev) dev->priv_flags |= IFF_UNICAST_FLT | IFF_CHANGE_PROTO_DOWN; dev->netdev_ops = &macvlan_netdev_ops; dev->needs_free_netdev = true; + dev->priv_destructor = macvlan_dev_free; dev->header_ops = &macvlan_hard_header_ops; dev->ethtool_ops = &macvlan_ethtool_ops; } diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 6b12902a803f..cecf8c63096c 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -133,11 +133,17 @@ static void macvtap_setup(struct net_device *dev) dev->tx_queue_len = TUN_READQ_SIZE; } +static struct net *macvtap_link_net(const struct net_device *dev) +{ + return dev_net(macvlan_dev_real_dev(dev)); +} + static struct rtnl_link_ops macvtap_link_ops __read_mostly = { .kind = "macvtap", .setup = macvtap_setup, .newlink = macvtap_newlink, .dellink = macvtap_dellink, + .get_link_net = macvtap_link_net, .priv_size = sizeof(struct macvtap_dev), }; diff --git a/drivers/net/mctp/Kconfig b/drivers/net/mctp/Kconfig index 2929471395ae..dc71657d9184 100644 --- a/drivers/net/mctp/Kconfig +++ b/drivers/net/mctp/Kconfig @@ -21,6 +21,18 @@ config MCTP_SERIAL Say y here if you need to connect to MCTP endpoints over serial. To compile as a module, use m; the module will be called mctp-serial. +config MCTP_TRANSPORT_I2C + tristate "MCTP SMBus/I2C transport" + # i2c-mux is optional, but we must build as a module if i2c-mux is a module + depends on I2C_MUX || !I2C_MUX + depends on I2C + depends on I2C_SLAVE + select MCTP_FLOWS + help + Provides a driver to access MCTP devices over SMBus/I2C transport, + from DMTF specification DSP0237. A MCTP protocol network device is + created for each I2C bus that has been assigned a mctp-i2c device. + endmenu endif diff --git a/drivers/net/mctp/Makefile b/drivers/net/mctp/Makefile index d32622613ce4..1ca3e6028f77 100644 --- a/drivers/net/mctp/Makefile +++ b/drivers/net/mctp/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_MCTP_SERIAL) += mctp-serial.o +obj-$(CONFIG_MCTP_TRANSPORT_I2C) += mctp-i2c.o diff --git a/drivers/net/mctp/mctp-i2c.c b/drivers/net/mctp/mctp-i2c.c new file mode 100644 index 000000000000..baf7afac7857 --- /dev/null +++ b/drivers/net/mctp/mctp-i2c.c @@ -0,0 +1,1082 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Management Controller Transport Protocol (MCTP) + * Implements DMTF specification + * "DSP0237 Management Component Transport Protocol (MCTP) SMBus/I2C + * Transport Binding" + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0237_1.2.0.pdf + * + * A netdev is created for each I2C bus that handles MCTP. In the case of an I2C + * mux topology a single I2C client is attached to the root of the mux topology, + * shared between all mux I2C busses underneath. For non-mux cases an I2C client + * is attached per netdev. + * + * mctp-i2c-controller.yml devicetree binding has further details. + * + * Copyright (c) 2022 Code Construct + * Copyright (c) 2022 Google + */ + +#include +#include +#include +#include +#include +#include +#include + +/* byte_count is limited to u8 */ +#define MCTP_I2C_MAXBLOCK 255 +/* One byte is taken by source_slave */ +#define MCTP_I2C_MAXMTU (MCTP_I2C_MAXBLOCK - 1) +#define MCTP_I2C_MINMTU (64 + 4) +/* Allow space for dest_address, command, byte_count, data, PEC */ +#define MCTP_I2C_BUFSZ (3 + MCTP_I2C_MAXBLOCK + 1) +#define MCTP_I2C_MINLEN 8 +#define MCTP_I2C_COMMANDCODE 0x0f +#define MCTP_I2C_TX_WORK_LEN 100 +/* Sufficient for 64kB at min mtu */ +#define MCTP_I2C_TX_QUEUE_LEN 1100 + +#define MCTP_I2C_OF_PROP "mctp-controller" + +enum { + MCTP_I2C_FLOW_STATE_NEW = 0, + MCTP_I2C_FLOW_STATE_ACTIVE, +}; + +/* List of all struct mctp_i2c_client + * Lock protects driver_clients and also prevents adding/removing adapters + * during mctp_i2c_client probe/remove. + */ +static DEFINE_MUTEX(driver_clients_lock); +static LIST_HEAD(driver_clients); + +struct mctp_i2c_client; + +/* The netdev structure. One of these per I2C adapter. */ +struct mctp_i2c_dev { + struct net_device *ndev; + struct i2c_adapter *adapter; + struct mctp_i2c_client *client; + struct list_head list; /* For mctp_i2c_client.devs */ + + size_t rx_pos; + u8 rx_buffer[MCTP_I2C_BUFSZ]; + struct completion rx_done; + + struct task_struct *tx_thread; + wait_queue_head_t tx_wq; + struct sk_buff_head tx_queue; + u8 tx_scratch[MCTP_I2C_BUFSZ]; + + /* A fake entry in our tx queue to perform an unlock operation */ + struct sk_buff unlock_marker; + + /* Spinlock protects i2c_lock_count, release_count, allow_rx */ + spinlock_t lock; + int i2c_lock_count; + int release_count; + /* Indicates that the netif is ready to receive incoming packets */ + bool allow_rx; + +}; + +/* The i2c client structure. One per hardware i2c bus at the top of the + * mux tree, shared by multiple netdevs + */ +struct mctp_i2c_client { + struct i2c_client *client; + u8 lladdr; + + struct mctp_i2c_dev *sel; + struct list_head devs; + spinlock_t sel_lock; /* Protects sel and devs */ + + struct list_head list; /* For driver_clients */ +}; + +/* Header on the wire. */ +struct mctp_i2c_hdr { + u8 dest_slave; + u8 command; + /* Count of bytes following byte_count, excluding PEC */ + u8 byte_count; + u8 source_slave; +}; + +static int mctp_i2c_recv(struct mctp_i2c_dev *midev); +static int mctp_i2c_slave_cb(struct i2c_client *client, + enum i2c_slave_event event, u8 *val); +static void mctp_i2c_ndo_uninit(struct net_device *dev); +static int mctp_i2c_ndo_open(struct net_device *dev); + +static struct i2c_adapter *mux_root_adapter(struct i2c_adapter *adap) +{ +#if IS_ENABLED(CONFIG_I2C_MUX) + return i2c_root_adapter(&adap->dev); +#else + /* In non-mux config all i2c adapters are root adapters */ + return adap; +#endif +} + +/* Creates a new i2c slave device attached to the root adapter. + * Sets up the slave callback. + * Must be called with a client on a root adapter. + */ +static struct mctp_i2c_client *mctp_i2c_new_client(struct i2c_client *client) +{ + struct mctp_i2c_client *mcli = NULL; + struct i2c_adapter *root = NULL; + int rc; + + if (client->flags & I2C_CLIENT_TEN) { + dev_err(&client->dev, "failed, MCTP requires a 7-bit I2C address, addr=0x%x\n", + client->addr); + rc = -EINVAL; + goto err; + } + + root = mux_root_adapter(client->adapter); + if (!root) { + dev_err(&client->dev, "failed to find root adapter\n"); + rc = -ENOENT; + goto err; + } + if (root != client->adapter) { + dev_err(&client->dev, + "A mctp-i2c-controller client cannot be placed on an I2C mux adapter.\n" + " It should be placed on the mux tree root adapter\n" + " then set mctp-controller property on adapters to attach\n"); + rc = -EINVAL; + goto err; + } + + mcli = kzalloc(sizeof(*mcli), GFP_KERNEL); + if (!mcli) { + rc = -ENOMEM; + goto err; + } + spin_lock_init(&mcli->sel_lock); + INIT_LIST_HEAD(&mcli->devs); + INIT_LIST_HEAD(&mcli->list); + mcli->lladdr = client->addr & 0xff; + mcli->client = client; + i2c_set_clientdata(client, mcli); + + rc = i2c_slave_register(mcli->client, mctp_i2c_slave_cb); + if (rc < 0) { + dev_err(&client->dev, "i2c register failed %d\n", rc); + mcli->client = NULL; + i2c_set_clientdata(client, NULL); + goto err; + } + + return mcli; +err: + if (mcli) { + if (mcli->client) + i2c_unregister_device(mcli->client); + kfree(mcli); + } + return ERR_PTR(rc); +} + +static void mctp_i2c_free_client(struct mctp_i2c_client *mcli) +{ + int rc; + + WARN_ON(!mutex_is_locked(&driver_clients_lock)); + WARN_ON(!list_empty(&mcli->devs)); + WARN_ON(mcli->sel); /* sanity check, no locking */ + + rc = i2c_slave_unregister(mcli->client); + /* Leak if it fails, we can't propagate errors upwards */ + if (rc < 0) + dev_err(&mcli->client->dev, "i2c unregister failed %d\n", rc); + else + kfree(mcli); +} + +/* Switch the mctp i2c device to receive responses. + * Call with sel_lock held + */ +static void __mctp_i2c_device_select(struct mctp_i2c_client *mcli, + struct mctp_i2c_dev *midev) +{ + assert_spin_locked(&mcli->sel_lock); + if (midev) + dev_hold(midev->ndev); + if (mcli->sel) + dev_put(mcli->sel->ndev); + mcli->sel = midev; +} + +/* Switch the mctp i2c device to receive responses */ +static void mctp_i2c_device_select(struct mctp_i2c_client *mcli, + struct mctp_i2c_dev *midev) +{ + unsigned long flags; + + spin_lock_irqsave(&mcli->sel_lock, flags); + __mctp_i2c_device_select(mcli, midev); + spin_unlock_irqrestore(&mcli->sel_lock, flags); +} + +static int mctp_i2c_slave_cb(struct i2c_client *client, + enum i2c_slave_event event, u8 *val) +{ + struct mctp_i2c_client *mcli = i2c_get_clientdata(client); + struct mctp_i2c_dev *midev = NULL; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&mcli->sel_lock, flags); + midev = mcli->sel; + if (midev) + dev_hold(midev->ndev); + spin_unlock_irqrestore(&mcli->sel_lock, flags); + + if (!midev) + return 0; + + switch (event) { + case I2C_SLAVE_WRITE_RECEIVED: + if (midev->rx_pos < MCTP_I2C_BUFSZ) { + midev->rx_buffer[midev->rx_pos] = *val; + midev->rx_pos++; + } else { + midev->ndev->stats.rx_over_errors++; + } + + break; + case I2C_SLAVE_WRITE_REQUESTED: + /* dest_slave as first byte */ + midev->rx_buffer[0] = mcli->lladdr << 1; + midev->rx_pos = 1; + break; + case I2C_SLAVE_STOP: + rc = mctp_i2c_recv(midev); + break; + default: + break; + } + + dev_put(midev->ndev); + return rc; +} + +/* Processes incoming data that has been accumulated by the slave cb */ +static int mctp_i2c_recv(struct mctp_i2c_dev *midev) +{ + struct net_device *ndev = midev->ndev; + struct mctp_i2c_hdr *hdr; + struct mctp_skb_cb *cb; + struct sk_buff *skb; + unsigned long flags; + u8 pec, calc_pec; + size_t recvlen; + int status; + + /* + 1 for the PEC */ + if (midev->rx_pos < MCTP_I2C_MINLEN + 1) { + ndev->stats.rx_length_errors++; + return -EINVAL; + } + /* recvlen excludes PEC */ + recvlen = midev->rx_pos - 1; + + hdr = (void *)midev->rx_buffer; + if (hdr->command != MCTP_I2C_COMMANDCODE) { + ndev->stats.rx_dropped++; + return -EINVAL; + } + + if (hdr->byte_count + offsetof(struct mctp_i2c_hdr, source_slave) != recvlen) { + ndev->stats.rx_length_errors++; + return -EINVAL; + } + + pec = midev->rx_buffer[midev->rx_pos - 1]; + calc_pec = i2c_smbus_pec(0, midev->rx_buffer, recvlen); + if (pec != calc_pec) { + ndev->stats.rx_crc_errors++; + return -EINVAL; + } + + skb = netdev_alloc_skb(ndev, recvlen); + if (!skb) { + ndev->stats.rx_dropped++; + return -ENOMEM; + } + + skb->protocol = htons(ETH_P_MCTP); + skb_put_data(skb, midev->rx_buffer, recvlen); + skb_reset_mac_header(skb); + skb_pull(skb, sizeof(struct mctp_i2c_hdr)); + skb_reset_network_header(skb); + + cb = __mctp_cb(skb); + cb->halen = 1; + cb->haddr[0] = hdr->source_slave >> 1; + + /* We need to ensure that the netif is not used once netdev + * unregister occurs + */ + spin_lock_irqsave(&midev->lock, flags); + if (midev->allow_rx) { + reinit_completion(&midev->rx_done); + spin_unlock_irqrestore(&midev->lock, flags); + + status = netif_rx(skb); + complete(&midev->rx_done); + } else { + status = NET_RX_DROP; + spin_unlock_irqrestore(&midev->lock, flags); + } + + if (status == NET_RX_SUCCESS) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += recvlen; + } else { + ndev->stats.rx_dropped++; + } + return 0; +} + +enum mctp_i2c_flow_state { + MCTP_I2C_TX_FLOW_INVALID, + MCTP_I2C_TX_FLOW_NONE, + MCTP_I2C_TX_FLOW_NEW, + MCTP_I2C_TX_FLOW_EXISTING, +}; + +static enum mctp_i2c_flow_state +mctp_i2c_get_tx_flow_state(struct mctp_i2c_dev *midev, struct sk_buff *skb) +{ + enum mctp_i2c_flow_state state; + struct mctp_sk_key *key; + struct mctp_flow *flow; + unsigned long flags; + + flow = skb_ext_find(skb, SKB_EXT_MCTP); + if (!flow) + return MCTP_I2C_TX_FLOW_NONE; + + key = flow->key; + if (!key) + return MCTP_I2C_TX_FLOW_NONE; + + spin_lock_irqsave(&key->lock, flags); + /* If the key is present but invalid, we're unlikely to be able + * to handle the flow at all; just drop now + */ + if (!key->valid) { + state = MCTP_I2C_TX_FLOW_INVALID; + + } else if (key->dev_flow_state == MCTP_I2C_FLOW_STATE_NEW) { + key->dev_flow_state = MCTP_I2C_FLOW_STATE_ACTIVE; + state = MCTP_I2C_TX_FLOW_NEW; + } else { + state = MCTP_I2C_TX_FLOW_EXISTING; + } + + spin_unlock_irqrestore(&key->lock, flags); + + return state; +} + +/* We're not contending with ourselves here; we only need to exclude other + * i2c clients from using the bus. refcounts are simply to prevent + * recursive locking. + */ +static void mctp_i2c_lock_nest(struct mctp_i2c_dev *midev) +{ + unsigned long flags; + bool lock; + + spin_lock_irqsave(&midev->lock, flags); + lock = midev->i2c_lock_count == 0; + midev->i2c_lock_count++; + spin_unlock_irqrestore(&midev->lock, flags); + + if (lock) + i2c_lock_bus(midev->adapter, I2C_LOCK_SEGMENT); +} + +static void mctp_i2c_unlock_nest(struct mctp_i2c_dev *midev) +{ + unsigned long flags; + bool unlock; + + spin_lock_irqsave(&midev->lock, flags); + if (!WARN_ONCE(midev->i2c_lock_count == 0, "lock count underflow!")) + midev->i2c_lock_count--; + unlock = midev->i2c_lock_count == 0; + spin_unlock_irqrestore(&midev->lock, flags); + + if (unlock) + i2c_unlock_bus(midev->adapter, I2C_LOCK_SEGMENT); +} + +/* Unlocks the bus if was previously locked, used for cleanup */ +static void mctp_i2c_unlock_reset(struct mctp_i2c_dev *midev) +{ + unsigned long flags; + bool unlock; + + spin_lock_irqsave(&midev->lock, flags); + unlock = midev->i2c_lock_count > 0; + midev->i2c_lock_count = 0; + spin_unlock_irqrestore(&midev->lock, flags); + + if (unlock) + i2c_unlock_bus(midev->adapter, I2C_LOCK_SEGMENT); +} + +static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb) +{ + struct net_device_stats *stats = &midev->ndev->stats; + enum mctp_i2c_flow_state fs; + struct mctp_i2c_hdr *hdr; + struct i2c_msg msg = {0}; + u8 *pecp; + int rc; + + fs = mctp_i2c_get_tx_flow_state(midev, skb); + + hdr = (void *)skb_mac_header(skb); + /* Sanity check that packet contents matches skb length, + * and can't exceed MCTP_I2C_BUFSZ + */ + if (skb->len != hdr->byte_count + 3) { + dev_warn_ratelimited(&midev->adapter->dev, + "Bad tx length %d vs skb %u\n", + hdr->byte_count + 3, skb->len); + return; + } + + if (skb_tailroom(skb) >= 1) { + /* Linear case with space, we can just append the PEC */ + skb_put(skb, 1); + } else { + /* Otherwise need to copy the buffer */ + skb_copy_bits(skb, 0, midev->tx_scratch, skb->len); + hdr = (void *)midev->tx_scratch; + } + + pecp = (void *)&hdr->source_slave + hdr->byte_count; + *pecp = i2c_smbus_pec(0, (u8 *)hdr, hdr->byte_count + 3); + msg.buf = (void *)&hdr->command; + /* command, bytecount, data, pec */ + msg.len = 2 + hdr->byte_count + 1; + msg.addr = hdr->dest_slave >> 1; + + switch (fs) { + case MCTP_I2C_TX_FLOW_NONE: + /* no flow: full lock & unlock */ + mctp_i2c_lock_nest(midev); + mctp_i2c_device_select(midev->client, midev); + rc = __i2c_transfer(midev->adapter, &msg, 1); + mctp_i2c_unlock_nest(midev); + break; + + case MCTP_I2C_TX_FLOW_NEW: + /* new flow: lock, tx, but don't unlock; that will happen + * on flow release + */ + mctp_i2c_lock_nest(midev); + mctp_i2c_device_select(midev->client, midev); + fallthrough; + + case MCTP_I2C_TX_FLOW_EXISTING: + /* existing flow: we already have the lock; just tx */ + rc = __i2c_transfer(midev->adapter, &msg, 1); + break; + + case MCTP_I2C_TX_FLOW_INVALID: + return; + } + + if (rc < 0) { + dev_warn_ratelimited(&midev->adapter->dev, + "__i2c_transfer failed %d\n", rc); + stats->tx_errors++; + } else { + stats->tx_bytes += skb->len; + stats->tx_packets++; + } +} + +static void mctp_i2c_flow_release(struct mctp_i2c_dev *midev) +{ + unsigned long flags; + bool unlock; + + spin_lock_irqsave(&midev->lock, flags); + if (midev->release_count > midev->i2c_lock_count) { + WARN_ONCE(1, "release count overflow"); + midev->release_count = midev->i2c_lock_count; + } + + midev->i2c_lock_count -= midev->release_count; + unlock = midev->i2c_lock_count == 0 && midev->release_count > 0; + midev->release_count = 0; + spin_unlock_irqrestore(&midev->lock, flags); + + if (unlock) + i2c_unlock_bus(midev->adapter, I2C_LOCK_SEGMENT); +} + +static int mctp_i2c_header_create(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned int len) +{ + struct mctp_i2c_hdr *hdr; + struct mctp_hdr *mhdr; + u8 lldst, llsrc; + + if (len > MCTP_I2C_MAXMTU) + return -EMSGSIZE; + + lldst = *((u8 *)daddr); + llsrc = *((u8 *)saddr); + + skb_push(skb, sizeof(struct mctp_i2c_hdr)); + skb_reset_mac_header(skb); + hdr = (void *)skb_mac_header(skb); + mhdr = mctp_hdr(skb); + hdr->dest_slave = (lldst << 1) & 0xff; + hdr->command = MCTP_I2C_COMMANDCODE; + hdr->byte_count = len + 1; + hdr->source_slave = ((llsrc << 1) & 0xff) | 0x01; + mhdr->ver = 0x01; + + return 0; +} + +static int mctp_i2c_tx_thread(void *data) +{ + struct mctp_i2c_dev *midev = data; + struct sk_buff *skb; + unsigned long flags; + + for (;;) { + if (kthread_should_stop()) + break; + + spin_lock_irqsave(&midev->tx_queue.lock, flags); + skb = __skb_dequeue(&midev->tx_queue); + if (netif_queue_stopped(midev->ndev)) + netif_wake_queue(midev->ndev); + spin_unlock_irqrestore(&midev->tx_queue.lock, flags); + + if (skb == &midev->unlock_marker) { + mctp_i2c_flow_release(midev); + + } else if (skb) { + mctp_i2c_xmit(midev, skb); + kfree_skb(skb); + + } else { + wait_event_idle(midev->tx_wq, + !skb_queue_empty(&midev->tx_queue) || + kthread_should_stop()); + } + } + + return 0; +} + +static netdev_tx_t mctp_i2c_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct mctp_i2c_dev *midev = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&midev->tx_queue.lock, flags); + if (skb_queue_len(&midev->tx_queue) >= MCTP_I2C_TX_WORK_LEN) { + netif_stop_queue(dev); + spin_unlock_irqrestore(&midev->tx_queue.lock, flags); + netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + __skb_queue_tail(&midev->tx_queue, skb); + if (skb_queue_len(&midev->tx_queue) == MCTP_I2C_TX_WORK_LEN) + netif_stop_queue(dev); + spin_unlock_irqrestore(&midev->tx_queue.lock, flags); + + wake_up(&midev->tx_wq); + return NETDEV_TX_OK; +} + +static void mctp_i2c_release_flow(struct mctp_dev *mdev, + struct mctp_sk_key *key) + +{ + struct mctp_i2c_dev *midev = netdev_priv(mdev->dev); + unsigned long flags; + + spin_lock_irqsave(&midev->lock, flags); + midev->release_count++; + spin_unlock_irqrestore(&midev->lock, flags); + + /* Ensure we have a release operation queued, through the fake + * marker skb + */ + spin_lock(&midev->tx_queue.lock); + if (!midev->unlock_marker.next) + __skb_queue_tail(&midev->tx_queue, &midev->unlock_marker); + spin_unlock(&midev->tx_queue.lock); + + wake_up(&midev->tx_wq); +} + +static const struct net_device_ops mctp_i2c_ops = { + .ndo_start_xmit = mctp_i2c_start_xmit, + .ndo_uninit = mctp_i2c_ndo_uninit, + .ndo_open = mctp_i2c_ndo_open, +}; + +static const struct header_ops mctp_i2c_headops = { + .create = mctp_i2c_header_create, +}; + +static const struct mctp_netdev_ops mctp_i2c_mctp_ops = { + .release_flow = mctp_i2c_release_flow, +}; + +static void mctp_i2c_net_setup(struct net_device *dev) +{ + dev->type = ARPHRD_MCTP; + + dev->mtu = MCTP_I2C_MAXMTU; + dev->min_mtu = MCTP_I2C_MINMTU; + dev->max_mtu = MCTP_I2C_MAXMTU; + dev->tx_queue_len = MCTP_I2C_TX_QUEUE_LEN; + + dev->hard_header_len = sizeof(struct mctp_i2c_hdr); + dev->addr_len = 1; + + dev->netdev_ops = &mctp_i2c_ops; + dev->header_ops = &mctp_i2c_headops; +} + +/* Populates the mctp_i2c_dev priv struct for a netdev. + * Returns an error pointer on failure. + */ +static struct mctp_i2c_dev *mctp_i2c_midev_init(struct net_device *dev, + struct mctp_i2c_client *mcli, + struct i2c_adapter *adap) +{ + struct mctp_i2c_dev *midev = netdev_priv(dev); + unsigned long flags; + + midev->tx_thread = kthread_create(mctp_i2c_tx_thread, midev, + "%s/tx", dev->name); + if (IS_ERR(midev->tx_thread)) + return ERR_CAST(midev->tx_thread); + + midev->ndev = dev; + get_device(&adap->dev); + midev->adapter = adap; + get_device(&mcli->client->dev); + midev->client = mcli; + INIT_LIST_HEAD(&midev->list); + spin_lock_init(&midev->lock); + midev->i2c_lock_count = 0; + midev->release_count = 0; + init_completion(&midev->rx_done); + complete(&midev->rx_done); + init_waitqueue_head(&midev->tx_wq); + skb_queue_head_init(&midev->tx_queue); + + /* Add to the parent mcli */ + spin_lock_irqsave(&mcli->sel_lock, flags); + list_add(&midev->list, &mcli->devs); + /* Select a device by default */ + if (!mcli->sel) + __mctp_i2c_device_select(mcli, midev); + spin_unlock_irqrestore(&mcli->sel_lock, flags); + + /* Start the worker thread */ + wake_up_process(midev->tx_thread); + + return midev; +} + +/* Counterpart of mctp_i2c_midev_init */ +static void mctp_i2c_midev_free(struct mctp_i2c_dev *midev) +{ + struct mctp_i2c_client *mcli = midev->client; + unsigned long flags; + + if (midev->tx_thread) { + kthread_stop(midev->tx_thread); + midev->tx_thread = NULL; + } + + /* Unconditionally unlock on close */ + mctp_i2c_unlock_reset(midev); + + /* Remove the netdev from the parent i2c client. */ + spin_lock_irqsave(&mcli->sel_lock, flags); + list_del(&midev->list); + if (mcli->sel == midev) { + struct mctp_i2c_dev *first; + + first = list_first_entry_or_null(&mcli->devs, struct mctp_i2c_dev, list); + __mctp_i2c_device_select(mcli, first); + } + spin_unlock_irqrestore(&mcli->sel_lock, flags); + + skb_queue_purge(&midev->tx_queue); + put_device(&midev->adapter->dev); + put_device(&mcli->client->dev); +} + +/* Stops, unregisters, and frees midev */ +static void mctp_i2c_unregister(struct mctp_i2c_dev *midev) +{ + unsigned long flags; + + /* Stop tx thread prior to unregister, it uses netif_() functions */ + kthread_stop(midev->tx_thread); + midev->tx_thread = NULL; + + /* Prevent any new rx in mctp_i2c_recv(), let any pending work finish */ + spin_lock_irqsave(&midev->lock, flags); + midev->allow_rx = false; + spin_unlock_irqrestore(&midev->lock, flags); + wait_for_completion(&midev->rx_done); + + mctp_unregister_netdev(midev->ndev); + /* midev has been freed now by mctp_i2c_ndo_uninit callback */ + + free_netdev(midev->ndev); +} + +static void mctp_i2c_ndo_uninit(struct net_device *dev) +{ + struct mctp_i2c_dev *midev = netdev_priv(dev); + + /* Perform cleanup here to ensure that mcli->sel isn't holding + * a reference that would prevent unregister_netdevice() + * from completing. + */ + mctp_i2c_midev_free(midev); +} + +static int mctp_i2c_ndo_open(struct net_device *dev) +{ + struct mctp_i2c_dev *midev = netdev_priv(dev); + unsigned long flags; + + /* i2c rx handler can only pass packets once the netdev is registered */ + spin_lock_irqsave(&midev->lock, flags); + midev->allow_rx = true; + spin_unlock_irqrestore(&midev->lock, flags); + + return 0; +} + +static int mctp_i2c_add_netdev(struct mctp_i2c_client *mcli, + struct i2c_adapter *adap) +{ + struct mctp_i2c_dev *midev = NULL; + struct net_device *ndev = NULL; + struct i2c_adapter *root; + unsigned long flags; + char namebuf[30]; + int rc; + + root = mux_root_adapter(adap); + if (root != mcli->client->adapter) { + dev_err(&mcli->client->dev, + "I2C adapter %s is not a child bus of %s\n", + mcli->client->adapter->name, root->name); + return -EINVAL; + } + + WARN_ON(!mutex_is_locked(&driver_clients_lock)); + snprintf(namebuf, sizeof(namebuf), "mctpi2c%d", adap->nr); + ndev = alloc_netdev(sizeof(*midev), namebuf, NET_NAME_ENUM, mctp_i2c_net_setup); + if (!ndev) { + dev_err(&mcli->client->dev, "alloc netdev failed\n"); + rc = -ENOMEM; + goto err; + } + dev_net_set(ndev, current->nsproxy->net_ns); + SET_NETDEV_DEV(ndev, &adap->dev); + dev_addr_set(ndev, &mcli->lladdr); + + midev = mctp_i2c_midev_init(ndev, mcli, adap); + if (IS_ERR(midev)) { + rc = PTR_ERR(midev); + midev = NULL; + goto err; + } + + rc = mctp_register_netdev(ndev, &mctp_i2c_mctp_ops); + if (rc < 0) { + dev_err(&mcli->client->dev, + "register netdev \"%s\" failed %d\n", + ndev->name, rc); + goto err; + } + + spin_lock_irqsave(&midev->lock, flags); + midev->allow_rx = false; + spin_unlock_irqrestore(&midev->lock, flags); + + return 0; +err: + if (midev) + mctp_i2c_midev_free(midev); + if (ndev) + free_netdev(ndev); + return rc; +} + +/* Removes any netdev for adap. mcli is the parent root i2c client */ +static void mctp_i2c_remove_netdev(struct mctp_i2c_client *mcli, + struct i2c_adapter *adap) +{ + struct mctp_i2c_dev *midev = NULL, *m = NULL; + unsigned long flags; + + WARN_ON(!mutex_is_locked(&driver_clients_lock)); + spin_lock_irqsave(&mcli->sel_lock, flags); + /* List size is limited by number of MCTP netdevs on a single hardware bus */ + list_for_each_entry(m, &mcli->devs, list) + if (m->adapter == adap) { + midev = m; + break; + } + spin_unlock_irqrestore(&mcli->sel_lock, flags); + + if (midev) + mctp_i2c_unregister(midev); +} + +/* Determines whether a device is an i2c adapter. + * Optionally returns the root i2c_adapter + */ +static struct i2c_adapter *mctp_i2c_get_adapter(struct device *dev, + struct i2c_adapter **ret_root) +{ + struct i2c_adapter *root, *adap; + + if (dev->type != &i2c_adapter_type) + return NULL; + adap = to_i2c_adapter(dev); + root = mux_root_adapter(adap); + WARN_ONCE(!root, "MCTP I2C failed to find root adapter for %s\n", + dev_name(dev)); + if (!root) + return NULL; + if (ret_root) + *ret_root = root; + return adap; +} + +/* Determines whether a device is an i2c adapter with the "mctp-controller" + * devicetree property set. If adap is not an OF node, returns match_no_of + */ +static bool mctp_i2c_adapter_match(struct i2c_adapter *adap, bool match_no_of) +{ + if (!adap->dev.of_node) + return match_no_of; + return of_property_read_bool(adap->dev.of_node, MCTP_I2C_OF_PROP); +} + +/* Called for each existing i2c device (adapter or client) when a + * new mctp-i2c client is probed. + */ +static int mctp_i2c_client_try_attach(struct device *dev, void *data) +{ + struct i2c_adapter *adap = NULL, *root = NULL; + struct mctp_i2c_client *mcli = data; + + adap = mctp_i2c_get_adapter(dev, &root); + if (!adap) + return 0; + if (mcli->client->adapter != root) + return 0; + /* Must either have mctp-controller property on the adapter, or + * be a root adapter if it's non-devicetree + */ + if (!mctp_i2c_adapter_match(adap, adap == root)) + return 0; + + return mctp_i2c_add_netdev(mcli, adap); +} + +static void mctp_i2c_notify_add(struct device *dev) +{ + struct mctp_i2c_client *mcli = NULL, *m = NULL; + struct i2c_adapter *root = NULL, *adap = NULL; + int rc; + + adap = mctp_i2c_get_adapter(dev, &root); + if (!adap) + return; + /* Check for mctp-controller property on the adapter */ + if (!mctp_i2c_adapter_match(adap, false)) + return; + + /* Find an existing mcli for adap's root */ + mutex_lock(&driver_clients_lock); + list_for_each_entry(m, &driver_clients, list) { + if (m->client->adapter == root) { + mcli = m; + break; + } + } + + if (mcli) { + rc = mctp_i2c_add_netdev(mcli, adap); + if (rc < 0) + dev_warn(dev, "Failed adding mctp-i2c net device\n"); + } + mutex_unlock(&driver_clients_lock); +} + +static void mctp_i2c_notify_del(struct device *dev) +{ + struct i2c_adapter *root = NULL, *adap = NULL; + struct mctp_i2c_client *mcli = NULL; + + adap = mctp_i2c_get_adapter(dev, &root); + if (!adap) + return; + + mutex_lock(&driver_clients_lock); + list_for_each_entry(mcli, &driver_clients, list) { + if (mcli->client->adapter == root) { + mctp_i2c_remove_netdev(mcli, adap); + break; + } + } + mutex_unlock(&driver_clients_lock); +} + +static int mctp_i2c_probe(struct i2c_client *client) +{ + struct mctp_i2c_client *mcli = NULL; + int rc; + + mutex_lock(&driver_clients_lock); + mcli = mctp_i2c_new_client(client); + if (IS_ERR(mcli)) { + rc = PTR_ERR(mcli); + mcli = NULL; + goto out; + } else { + list_add(&mcli->list, &driver_clients); + } + + /* Add a netdev for adapters that have a 'mctp-controller' property */ + i2c_for_each_dev(mcli, mctp_i2c_client_try_attach); + rc = 0; +out: + mutex_unlock(&driver_clients_lock); + return rc; +} + +static int mctp_i2c_remove(struct i2c_client *client) +{ + struct mctp_i2c_client *mcli = i2c_get_clientdata(client); + struct mctp_i2c_dev *midev = NULL, *tmp = NULL; + + mutex_lock(&driver_clients_lock); + list_del(&mcli->list); + /* Remove all child adapter netdevs */ + list_for_each_entry_safe(midev, tmp, &mcli->devs, list) + mctp_i2c_unregister(midev); + + mctp_i2c_free_client(mcli); + mutex_unlock(&driver_clients_lock); + /* Callers ignore return code */ + return 0; +} + +/* We look for a 'mctp-controller' property on I2C busses as they are + * added/deleted, creating/removing netdevs as required. + */ +static int mctp_i2c_notifier_call(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + mctp_i2c_notify_add(dev); + break; + case BUS_NOTIFY_DEL_DEVICE: + mctp_i2c_notify_del(dev); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block mctp_i2c_notifier = { + .notifier_call = mctp_i2c_notifier_call, +}; + +static const struct i2c_device_id mctp_i2c_id[] = { + { "mctp-i2c-interface", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, mctp_i2c_id); + +static const struct of_device_id mctp_i2c_of_match[] = { + { .compatible = "mctp-i2c-controller" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mctp_i2c_of_match); + +static struct i2c_driver mctp_i2c_driver = { + .driver = { + .name = "mctp-i2c-interface", + .of_match_table = mctp_i2c_of_match, + }, + .probe_new = mctp_i2c_probe, + .remove = mctp_i2c_remove, + .id_table = mctp_i2c_id, +}; + +static __init int mctp_i2c_mod_init(void) +{ + int rc; + + pr_info("MCTP I2C interface driver\n"); + rc = i2c_add_driver(&mctp_i2c_driver); + if (rc < 0) + return rc; + rc = bus_register_notifier(&i2c_bus_type, &mctp_i2c_notifier); + if (rc < 0) { + i2c_del_driver(&mctp_i2c_driver); + return rc; + } + return 0; +} + +static __exit void mctp_i2c_mod_exit(void) +{ + int rc; + + rc = bus_unregister_notifier(&i2c_bus_type, &mctp_i2c_notifier); + if (rc < 0) + pr_warn("MCTP I2C could not unregister notifier, %d\n", rc); + i2c_del_driver(&mctp_i2c_driver); +} + +module_init(mctp_i2c_mod_init); +module_exit(mctp_i2c_mod_exit); + +MODULE_DESCRIPTION("MCTP I2C device"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Matt Johnston "); diff --git a/drivers/net/mctp/mctp-serial.c b/drivers/net/mctp/mctp-serial.c index 62723a7faa2d..7cd103fd34ef 100644 --- a/drivers/net/mctp/mctp-serial.c +++ b/drivers/net/mctp/mctp-serial.c @@ -286,7 +286,7 @@ static void mctp_serial_rx(struct mctp_serial *dev) cb = __mctp_cb(skb); cb->halen = 0; - netif_rx_ni(skb); + netif_rx(skb); dev->netdev->stats.rx_packets++; dev->netdev->stats.rx_bytes += dev->rxlen; } diff --git a/drivers/net/mdio/mdio-mscc-miim.c b/drivers/net/mdio/mdio-mscc-miim.c index 64fb76c1e395..c483ba67c21f 100644 --- a/drivers/net/mdio/mdio-mscc-miim.c +++ b/drivers/net/mdio/mdio-mscc-miim.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #define MSCC_MIIM_REG_STATUS 0x0 @@ -36,11 +37,19 @@ #define PHY_CFG_PHY_RESET (BIT(5) | BIT(6) | BIT(7) | BIT(8)) #define MSCC_PHY_REG_PHY_STATUS 0x4 +#define LAN966X_CUPHY_COMMON_CFG 0x0 +#define CUPHY_COMMON_CFG_RESET_N BIT(0) + +struct mscc_miim_info { + unsigned int phy_reset_offset; + unsigned int phy_reset_bits; +}; + struct mscc_miim_dev { struct regmap *regs; int mii_status_offset; struct regmap *phy_regs; - int phy_reset_offset; + const struct mscc_miim_info *info; }; /* When high resolution timers aren't built-in: we can't use usleep_range() as @@ -157,27 +166,29 @@ static int mscc_miim_write(struct mii_bus *bus, int mii_id, static int mscc_miim_reset(struct mii_bus *bus) { struct mscc_miim_dev *miim = bus->priv; - int offset = miim->phy_reset_offset; + unsigned int offset, bits; int ret; - if (miim->phy_regs) { - ret = regmap_write(miim->phy_regs, - MSCC_PHY_REG_PHY_CFG + offset, 0); - if (ret < 0) { - WARN_ONCE(1, "mscc reset set error %d\n", ret); - return ret; - } + if (!miim->phy_regs) + return 0; - ret = regmap_write(miim->phy_regs, - MSCC_PHY_REG_PHY_CFG + offset, 0x1ff); - if (ret < 0) { - WARN_ONCE(1, "mscc reset clear error %d\n", ret); - return ret; - } + offset = miim->info->phy_reset_offset; + bits = miim->info->phy_reset_bits; - mdelay(500); + ret = regmap_update_bits(miim->phy_regs, offset, bits, 0); + if (ret < 0) { + WARN_ONCE(1, "mscc reset set error %d\n", ret); + return ret; } + ret = regmap_update_bits(miim->phy_regs, offset, bits, bits); + if (ret < 0) { + WARN_ONCE(1, "mscc reset clear error %d\n", ret); + return ret; + } + + mdelay(500); + return 0; } @@ -272,7 +283,10 @@ static int mscc_miim_probe(struct platform_device *pdev) miim = bus->priv; miim->phy_regs = phy_regmap; - miim->phy_reset_offset = 0; + + miim->info = device_get_match_data(&pdev->dev); + if (!miim->info) + return -EINVAL; ret = of_mdiobus_register(bus, pdev->dev.of_node); if (ret < 0) { @@ -294,8 +308,25 @@ static int mscc_miim_remove(struct platform_device *pdev) return 0; } +static const struct mscc_miim_info mscc_ocelot_miim_info = { + .phy_reset_offset = MSCC_PHY_REG_PHY_CFG, + .phy_reset_bits = PHY_CFG_PHY_ENA | PHY_CFG_PHY_COMMON_RESET | + PHY_CFG_PHY_RESET, +}; + +static const struct mscc_miim_info microchip_lan966x_miim_info = { + .phy_reset_offset = LAN966X_CUPHY_COMMON_CFG, + .phy_reset_bits = CUPHY_COMMON_CFG_RESET_N, +}; + static const struct of_device_id mscc_miim_match[] = { - { .compatible = "mscc,ocelot-miim" }, + { + .compatible = "mscc,ocelot-miim", + .data = &mscc_ocelot_miim_info + }, { + .compatible = "microchip,lan966x-miim", + .data = µchip_lan966x_miim_info + }, { } }; MODULE_DEVICE_TABLE(of, mscc_miim_match); diff --git a/drivers/net/mdio/mdio-mux.c b/drivers/net/mdio/mdio-mux.c index ebd001f0eece..a881e3523328 100644 --- a/drivers/net/mdio/mdio-mux.c +++ b/drivers/net/mdio/mdio-mux.c @@ -168,8 +168,8 @@ int mdio_mux_init(struct device *dev, cb->mii_bus->priv = cb; cb->mii_bus->name = "mdio_mux"; - snprintf(cb->mii_bus->id, MII_BUS_ID_SIZE, "%x.%x", - pb->parent_id, v); + snprintf(cb->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x.%x", + cb->mii_bus->name, pb->parent_id, v); cb->mii_bus->parent = dev; cb->mii_bus->read = mdio_mux_read; cb->mii_bus->write = mdio_mux_write; diff --git a/drivers/net/mdio/mdio-xgene.c b/drivers/net/mdio/mdio-xgene.c index 7ab4e26db08c..7aafc221b5cf 100644 --- a/drivers/net/mdio/mdio-xgene.c +++ b/drivers/net/mdio/mdio-xgene.c @@ -285,7 +285,8 @@ static acpi_status acpi_register_phy(acpi_handle handle, u32 lvl, const union acpi_object *obj; u32 phy_addr; - if (acpi_bus_get_device(handle, &adev)) + adev = acpi_fetch_acpi_dev(handle); + if (!adev) return AE_OK; if (acpi_dev_get_property(adev, "phy-channel", ACPI_TYPE_INTEGER, &obj)) diff --git a/drivers/net/mhi_net.c b/drivers/net/mhi_net.c index aaa628f859fd..0b1b6f650104 100644 --- a/drivers/net/mhi_net.c +++ b/drivers/net/mhi_net.c @@ -225,7 +225,7 @@ static void mhi_net_dl_callback(struct mhi_device *mhi_dev, u64_stats_inc(&mhi_netdev->stats.rx_packets); u64_stats_add(&mhi_netdev->stats.rx_bytes, skb->len); u64_stats_update_end(&mhi_netdev->stats.rx_syncp); - netif_rx(skb); + __netif_rx(skb); } /* Refill if RX buffers queue becomes low */ diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c index 86ec5aae4289..21a0435c02de 100644 --- a/drivers/net/net_failover.c +++ b/drivers/net/net_failover.c @@ -89,7 +89,7 @@ static int net_failover_close(struct net_device *dev) static netdev_tx_t net_failover_drop_xmit(struct sk_buff *skb, struct net_device *dev) { - atomic_long_inc(&dev->tx_dropped); + dev_core_stats_tx_dropped_inc(dev); dev_kfree_skb_any(skb); return NETDEV_TX_OK; } diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile index a1cbfa44a1e1..5735e5b1a2cb 100644 --- a/drivers/net/netdevsim/Makefile +++ b/drivers/net/netdevsim/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_NETDEVSIM) += netdevsim.o netdevsim-objs := \ - netdev.o dev.o ethtool.o fib.o bus.o health.o udp_tunnels.o + netdev.o dev.o ethtool.o fib.o bus.o health.o hwstats.o udp_tunnels.o ifeq ($(CONFIG_BPF_SYSCALL),y) netdevsim-objs += \ diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index 08d7b465a0de..57a3ac893792 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -59,7 +59,7 @@ static struct dentry *nsim_dev_ddir; unsigned int nsim_dev_get_vfs(struct nsim_dev *nsim_dev) { WARN_ON(!lockdep_rtnl_is_held() && - !lockdep_is_held(&nsim_dev->vfs_lock)); + !devl_lock_is_held(priv_to_devlink(nsim_dev))); return nsim_dev->nsim_bus_dev->num_vfs; } @@ -275,7 +275,7 @@ static ssize_t nsim_bus_dev_max_vfs_write(struct file *file, return -ENOMEM; nsim_dev = file->private_data; - mutex_lock(&nsim_dev->vfs_lock); + devl_lock(priv_to_devlink(nsim_dev)); /* Reject if VFs are configured */ if (nsim_dev_get_vfs(nsim_dev)) { ret = -EBUSY; @@ -285,7 +285,7 @@ static ssize_t nsim_bus_dev_max_vfs_write(struct file *file, *ppos += count; ret = count; } - mutex_unlock(&nsim_dev->vfs_lock); + devl_unlock(priv_to_devlink(nsim_dev)); kfree(vfconfigs); return ret; @@ -339,6 +339,7 @@ static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev) debugfs_create_bool("fail_trap_policer_counter_get", 0600, nsim_dev->ddir, &nsim_dev->fail_trap_policer_counter_get); + /* caution, dev_max_vfs write takes devlink lock */ debugfs_create_file("max_vfs", 0600, nsim_dev->ddir, nsim_dev, &nsim_dev_max_vfs_fops); @@ -567,6 +568,9 @@ static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev) devlink_region_destroy(nsim_dev->dummy_region); } +static int +__nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type, + unsigned int port_index); static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port); static int nsim_esw_legacy_enable(struct nsim_dev *nsim_dev, @@ -575,12 +579,10 @@ static int nsim_esw_legacy_enable(struct nsim_dev *nsim_dev, struct devlink *devlink = priv_to_devlink(nsim_dev); struct nsim_dev_port *nsim_dev_port, *tmp; - devlink_rate_nodes_destroy(devlink); - mutex_lock(&nsim_dev->port_list_lock); + devl_rate_nodes_destroy(devlink); list_for_each_entry_safe(nsim_dev_port, tmp, &nsim_dev->port_list, list) if (nsim_dev_port_is_vf(nsim_dev_port)) __nsim_dev_port_del(nsim_dev_port); - mutex_unlock(&nsim_dev->port_list_lock); nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY; return 0; } @@ -588,11 +590,11 @@ static int nsim_esw_legacy_enable(struct nsim_dev *nsim_dev, static int nsim_esw_switchdev_enable(struct nsim_dev *nsim_dev, struct netlink_ext_ack *extack) { - struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev; + struct nsim_dev_port *nsim_dev_port, *tmp; int i, err; for (i = 0; i < nsim_dev_get_vfs(nsim_dev); i++) { - err = nsim_drv_port_add(nsim_bus_dev, NSIM_DEV_PORT_TYPE_VF, i); + err = __nsim_dev_port_add(nsim_dev, NSIM_DEV_PORT_TYPE_VF, i); if (err) { NL_SET_ERR_MSG_MOD(extack, "Failed to initialize VFs' netdevsim ports"); pr_err("Failed to initialize VF id=%d. %d.\n", i, err); @@ -603,8 +605,9 @@ static int nsim_esw_switchdev_enable(struct nsim_dev *nsim_dev, return 0; err_port_add_vfs: - for (i--; i >= 0; i--) - nsim_drv_port_del(nsim_bus_dev, NSIM_DEV_PORT_TYPE_VF, i); + list_for_each_entry_safe(nsim_dev_port, tmp, &nsim_dev->port_list, list) + if (nsim_dev_port_is_vf(nsim_dev_port)) + __nsim_dev_port_del(nsim_dev_port); return err; } @@ -612,22 +615,16 @@ static int nsim_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, struct netlink_ext_ack *extack) { struct nsim_dev *nsim_dev = devlink_priv(devlink); - int err = 0; - mutex_lock(&nsim_dev->vfs_lock); if (mode == nsim_dev->esw_mode) - goto unlock; + return 0; if (mode == DEVLINK_ESWITCH_MODE_LEGACY) - err = nsim_esw_legacy_enable(nsim_dev, extack); - else if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV) - err = nsim_esw_switchdev_enable(nsim_dev, extack); - else - err = -EINVAL; + return nsim_esw_legacy_enable(nsim_dev, extack); + if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV) + return nsim_esw_switchdev_enable(nsim_dev, extack); -unlock: - mutex_unlock(&nsim_dev->vfs_lock); - return err; + return -EINVAL; } static int nsim_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode) @@ -835,14 +832,14 @@ static void nsim_dev_trap_report_work(struct work_struct *work) /* For each running port and enabled packet trap, generate a UDP * packet with a random 5-tuple and report it. */ - mutex_lock(&nsim_dev->port_list_lock); + devl_lock(priv_to_devlink(nsim_dev)); list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) { if (!netif_running(nsim_dev_port->ns->netdev)) continue; nsim_dev_trap_report(nsim_dev_port); } - mutex_unlock(&nsim_dev->port_list_lock); + devl_unlock(priv_to_devlink(nsim_dev)); schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); @@ -924,6 +921,7 @@ static void nsim_dev_traps_exit(struct devlink *devlink) { struct nsim_dev *nsim_dev = devlink_priv(devlink); + /* caution, trap work takes devlink lock */ cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw); devlink_traps_unregister(devlink, nsim_traps_arr, ARRAY_SIZE(nsim_traps_arr)); @@ -1380,8 +1378,8 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_typ memcpy(attrs.switch_id.id, nsim_dev->switch_id.id, nsim_dev->switch_id.id_len); attrs.switch_id.id_len = nsim_dev->switch_id.id_len; devlink_port_attrs_set(devlink_port, &attrs); - err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port, - nsim_dev_port->port_index); + err = devl_port_register(priv_to_devlink(nsim_dev), devlink_port, + nsim_dev_port->port_index); if (err) goto err_port_free; @@ -1396,8 +1394,8 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_typ } if (nsim_dev_port_is_vf(nsim_dev_port)) { - err = devlink_rate_leaf_create(&nsim_dev_port->devlink_port, - nsim_dev_port); + err = devl_rate_leaf_create(&nsim_dev_port->devlink_port, + nsim_dev_port); if (err) goto err_nsim_destroy; } @@ -1412,7 +1410,7 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_typ err_port_debugfs_exit: nsim_dev_port_debugfs_exit(nsim_dev_port); err_dl_port_unregister: - devlink_port_unregister(devlink_port); + devl_port_unregister(devlink_port); err_port_free: kfree(nsim_dev_port); return err; @@ -1424,11 +1422,11 @@ static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port) list_del(&nsim_dev_port->list); if (nsim_dev_port_is_vf(nsim_dev_port)) - devlink_rate_leaf_destroy(&nsim_dev_port->devlink_port); + devl_rate_leaf_destroy(&nsim_dev_port->devlink_port); devlink_port_type_clear(devlink_port); nsim_destroy(nsim_dev_port->ns); nsim_dev_port_debugfs_exit(nsim_dev_port); - devlink_port_unregister(devlink_port); + devl_port_unregister(devlink_port); kfree(nsim_dev_port); } @@ -1436,11 +1434,11 @@ static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev) { struct nsim_dev_port *nsim_dev_port, *tmp; - mutex_lock(&nsim_dev->port_list_lock); + devl_lock(priv_to_devlink(nsim_dev)); list_for_each_entry_safe(nsim_dev_port, tmp, &nsim_dev->port_list, list) __nsim_dev_port_del(nsim_dev_port); - mutex_unlock(&nsim_dev->port_list_lock); + devl_unlock(priv_to_devlink(nsim_dev)); } static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev, @@ -1449,7 +1447,9 @@ static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev, int i, err; for (i = 0; i < port_count; i++) { + devl_lock(priv_to_devlink(nsim_dev)); err = __nsim_dev_port_add(nsim_dev, NSIM_DEV_PORT_TYPE_PF, i); + devl_unlock(priv_to_devlink(nsim_dev)); if (err) goto err_port_del_all; } @@ -1470,7 +1470,6 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, devlink = priv_to_devlink(nsim_dev); nsim_dev = devlink_priv(devlink); INIT_LIST_HEAD(&nsim_dev->port_list); - mutex_init(&nsim_dev->port_list_lock); nsim_dev->fw_update_status = true; nsim_dev->fw_update_overwrite_mask = 0; @@ -1498,10 +1497,14 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, if (err) goto err_health_exit; - err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); + err = nsim_dev_hwstats_init(nsim_dev); if (err) goto err_psample_exit; + err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); + if (err) + goto err_hwstats_exit; + nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 0200, nsim_dev->ddir, @@ -1509,6 +1512,8 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, &nsim_dev_take_snapshot_fops); return 0; +err_hwstats_exit: + nsim_dev_hwstats_exit(nsim_dev); err_psample_exit: nsim_dev_psample_exit(nsim_dev); err_health_exit: @@ -1537,8 +1542,6 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev) nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id); get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len); INIT_LIST_HEAD(&nsim_dev->port_list); - mutex_init(&nsim_dev->vfs_lock); - mutex_init(&nsim_dev->port_list_lock); nsim_dev->fw_update_status = true; nsim_dev->fw_update_overwrite_mask = 0; nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT; @@ -1595,15 +1598,21 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev) if (err) goto err_bpf_dev_exit; - err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); + err = nsim_dev_hwstats_init(nsim_dev); if (err) goto err_psample_exit; + err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); + if (err) + goto err_hwstats_exit; + nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY; devlink_set_features(devlink, DEVLINK_F_RELOAD); devlink_register(devlink); return 0; +err_hwstats_exit: + nsim_dev_hwstats_exit(nsim_dev); err_psample_exit: nsim_dev_psample_exit(nsim_dev); err_bpf_dev_exit: @@ -1639,21 +1648,21 @@ static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev) return; debugfs_remove(nsim_dev->take_snapshot); - mutex_lock(&nsim_dev->vfs_lock); + devl_lock(devlink); if (nsim_dev_get_vfs(nsim_dev)) { nsim_bus_dev_set_vfs(nsim_dev->nsim_bus_dev, 0); if (nsim_esw_mode_is_switchdev(nsim_dev)) nsim_esw_legacy_enable(nsim_dev, NULL); } - mutex_unlock(&nsim_dev->vfs_lock); + devl_unlock(devlink); nsim_dev_port_del_all(nsim_dev); + nsim_dev_hwstats_exit(nsim_dev); nsim_dev_psample_exit(nsim_dev); nsim_dev_health_exit(nsim_dev); nsim_fib_destroy(devlink, nsim_dev->fib_data); nsim_dev_traps_exit(devlink); nsim_dev_dummy_region_exit(nsim_dev); - mutex_destroy(&nsim_dev->port_list_lock); } void nsim_drv_remove(struct nsim_bus_dev *nsim_bus_dev) @@ -1693,12 +1702,12 @@ int nsim_drv_port_add(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); int err; - mutex_lock(&nsim_dev->port_list_lock); + devl_lock(priv_to_devlink(nsim_dev)); if (__nsim_dev_port_lookup(nsim_dev, type, port_index)) err = -EEXIST; else err = __nsim_dev_port_add(nsim_dev, type, port_index); - mutex_unlock(&nsim_dev->port_list_lock); + devl_unlock(priv_to_devlink(nsim_dev)); return err; } @@ -1709,13 +1718,13 @@ int nsim_drv_port_del(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type struct nsim_dev_port *nsim_dev_port; int err = 0; - mutex_lock(&nsim_dev->port_list_lock); + devl_lock(priv_to_devlink(nsim_dev)); nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, type, port_index); if (!nsim_dev_port) err = -ENOENT; else __nsim_dev_port_del(nsim_dev_port); - mutex_unlock(&nsim_dev->port_list_lock); + devl_unlock(priv_to_devlink(nsim_dev)); return err; } @@ -1723,9 +1732,10 @@ int nsim_drv_configure_vfs(struct nsim_bus_dev *nsim_bus_dev, unsigned int num_vfs) { struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); + struct devlink *devlink = priv_to_devlink(nsim_dev); int ret = 0; - mutex_lock(&nsim_dev->vfs_lock); + devl_lock(devlink); if (nsim_bus_dev->num_vfs == num_vfs) goto exit_unlock; if (nsim_bus_dev->num_vfs && num_vfs) { @@ -1751,7 +1761,7 @@ int nsim_drv_configure_vfs(struct nsim_bus_dev *nsim_bus_dev, } exit_unlock: - mutex_unlock(&nsim_dev->vfs_lock); + devl_unlock(devlink); return ret; } diff --git a/drivers/net/netdevsim/hwstats.c b/drivers/net/netdevsim/hwstats.c new file mode 100644 index 000000000000..605a38e16db0 --- /dev/null +++ b/drivers/net/netdevsim/hwstats.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#include "netdevsim.h" + +#define NSIM_DEV_HWSTATS_TRAFFIC_MS 100 + +static struct list_head * +nsim_dev_hwstats_get_list_head(struct nsim_dev_hwstats *hwstats, + enum netdev_offload_xstats_type type) +{ + switch (type) { + case NETDEV_OFFLOAD_XSTATS_TYPE_L3: + return &hwstats->l3_list; + } + + WARN_ON_ONCE(1); + return NULL; +} + +static void nsim_dev_hwstats_traffic_bump(struct nsim_dev_hwstats *hwstats, + enum netdev_offload_xstats_type type) +{ + struct nsim_dev_hwstats_netdev *hwsdev; + struct list_head *hwsdev_list; + + hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type); + if (WARN_ON(!hwsdev_list)) + return; + + list_for_each_entry(hwsdev, hwsdev_list, list) { + if (hwsdev->enabled) { + hwsdev->stats.rx_packets += 1; + hwsdev->stats.tx_packets += 2; + hwsdev->stats.rx_bytes += 100; + hwsdev->stats.tx_bytes += 300; + } + } +} + +static void nsim_dev_hwstats_traffic_work(struct work_struct *work) +{ + struct nsim_dev_hwstats *hwstats; + + hwstats = container_of(work, struct nsim_dev_hwstats, traffic_dw.work); + mutex_lock(&hwstats->hwsdev_list_lock); + nsim_dev_hwstats_traffic_bump(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3); + mutex_unlock(&hwstats->hwsdev_list_lock); + + schedule_delayed_work(&hwstats->traffic_dw, + msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS)); +} + +static struct nsim_dev_hwstats_netdev * +nsim_dev_hwslist_find_hwsdev(struct list_head *hwsdev_list, + int ifindex) +{ + struct nsim_dev_hwstats_netdev *hwsdev; + + list_for_each_entry(hwsdev, hwsdev_list, list) { + if (hwsdev->netdev->ifindex == ifindex) + return hwsdev; + } + + return NULL; +} + +static int nsim_dev_hwsdev_enable(struct nsim_dev_hwstats_netdev *hwsdev, + struct netlink_ext_ack *extack) +{ + if (hwsdev->fail_enable) { + hwsdev->fail_enable = false; + NL_SET_ERR_MSG_MOD(extack, "Stats enablement set to fail"); + return -ECANCELED; + } + + hwsdev->enabled = true; + return 0; +} + +static void nsim_dev_hwsdev_disable(struct nsim_dev_hwstats_netdev *hwsdev) +{ + hwsdev->enabled = false; + memset(&hwsdev->stats, 0, sizeof(hwsdev->stats)); +} + +static int +nsim_dev_hwsdev_report_delta(struct nsim_dev_hwstats_netdev *hwsdev, + struct netdev_notifier_offload_xstats_info *info) +{ + netdev_offload_xstats_report_delta(info->report_delta, &hwsdev->stats); + memset(&hwsdev->stats, 0, sizeof(hwsdev->stats)); + return 0; +} + +static void +nsim_dev_hwsdev_report_used(struct nsim_dev_hwstats_netdev *hwsdev, + struct netdev_notifier_offload_xstats_info *info) +{ + if (hwsdev->enabled) + netdev_offload_xstats_report_used(info->report_used); +} + +static int nsim_dev_hwstats_event_off_xstats(struct nsim_dev_hwstats *hwstats, + struct net_device *dev, + unsigned long event, void *ptr) +{ + struct netdev_notifier_offload_xstats_info *info; + struct nsim_dev_hwstats_netdev *hwsdev; + struct list_head *hwsdev_list; + int err = 0; + + info = ptr; + hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, info->type); + if (!hwsdev_list) + return 0; + + mutex_lock(&hwstats->hwsdev_list_lock); + + hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex); + if (!hwsdev) + goto out; + + switch (event) { + case NETDEV_OFFLOAD_XSTATS_ENABLE: + err = nsim_dev_hwsdev_enable(hwsdev, info->info.extack); + break; + case NETDEV_OFFLOAD_XSTATS_DISABLE: + nsim_dev_hwsdev_disable(hwsdev); + break; + case NETDEV_OFFLOAD_XSTATS_REPORT_USED: + nsim_dev_hwsdev_report_used(hwsdev, info); + break; + case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA: + err = nsim_dev_hwsdev_report_delta(hwsdev, info); + break; + } + +out: + mutex_unlock(&hwstats->hwsdev_list_lock); + return err; +} + +static void nsim_dev_hwsdev_fini(struct nsim_dev_hwstats_netdev *hwsdev) +{ + dev_put(hwsdev->netdev); + kfree(hwsdev); +} + +static void +__nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats, + struct net_device *dev, + enum netdev_offload_xstats_type type) +{ + struct nsim_dev_hwstats_netdev *hwsdev; + struct list_head *hwsdev_list; + + hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type); + if (WARN_ON(!hwsdev_list)) + return; + + hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex); + if (!hwsdev) + return; + + list_del(&hwsdev->list); + nsim_dev_hwsdev_fini(hwsdev); +} + +static void nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats, + struct net_device *dev) +{ + mutex_lock(&hwstats->hwsdev_list_lock); + __nsim_dev_hwstats_event_unregister(hwstats, dev, + NETDEV_OFFLOAD_XSTATS_TYPE_L3); + mutex_unlock(&hwstats->hwsdev_list_lock); +} + +static int nsim_dev_hwstats_event(struct nsim_dev_hwstats *hwstats, + struct net_device *dev, + unsigned long event, void *ptr) +{ + switch (event) { + case NETDEV_OFFLOAD_XSTATS_ENABLE: + case NETDEV_OFFLOAD_XSTATS_DISABLE: + case NETDEV_OFFLOAD_XSTATS_REPORT_USED: + case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA: + return nsim_dev_hwstats_event_off_xstats(hwstats, dev, + event, ptr); + case NETDEV_UNREGISTER: + nsim_dev_hwstats_event_unregister(hwstats, dev); + break; + } + + return 0; +} + +static int nsim_dev_netdevice_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct nsim_dev_hwstats *hwstats; + int err = 0; + + hwstats = container_of(nb, struct nsim_dev_hwstats, netdevice_nb); + err = nsim_dev_hwstats_event(hwstats, dev, event, ptr); + if (err) + return notifier_from_errno(err); + + return NOTIFY_OK; +} + +static int +nsim_dev_hwstats_enable_ifindex(struct nsim_dev_hwstats *hwstats, + int ifindex, + enum netdev_offload_xstats_type type, + struct list_head *hwsdev_list) +{ + struct nsim_dev_hwstats_netdev *hwsdev; + struct nsim_dev *nsim_dev; + struct net_device *netdev; + bool notify = false; + struct net *net; + int err = 0; + + nsim_dev = container_of(hwstats, struct nsim_dev, hwstats); + net = nsim_dev_net(nsim_dev); + + rtnl_lock(); + mutex_lock(&hwstats->hwsdev_list_lock); + hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex); + if (hwsdev) + goto out_unlock_list; + + netdev = dev_get_by_index(net, ifindex); + if (!netdev) { + err = -ENODEV; + goto out_unlock_list; + } + + hwsdev = kzalloc(sizeof(*hwsdev), GFP_KERNEL); + if (!hwsdev) { + err = -ENOMEM; + goto out_put_netdev; + } + + hwsdev->netdev = netdev; + list_add_tail(&hwsdev->list, hwsdev_list); + mutex_unlock(&hwstats->hwsdev_list_lock); + + if (netdev_offload_xstats_enabled(netdev, type)) { + nsim_dev_hwsdev_enable(hwsdev, NULL); + notify = true; + } + + if (notify) + rtnl_offload_xstats_notify(netdev); + rtnl_unlock(); + return err; + +out_put_netdev: + dev_put(netdev); +out_unlock_list: + mutex_unlock(&hwstats->hwsdev_list_lock); + rtnl_unlock(); + return err; +} + +static int +nsim_dev_hwstats_disable_ifindex(struct nsim_dev_hwstats *hwstats, + int ifindex, + enum netdev_offload_xstats_type type, + struct list_head *hwsdev_list) +{ + struct nsim_dev_hwstats_netdev *hwsdev; + int err = 0; + + rtnl_lock(); + mutex_lock(&hwstats->hwsdev_list_lock); + hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex); + if (hwsdev) + list_del(&hwsdev->list); + mutex_unlock(&hwstats->hwsdev_list_lock); + + if (!hwsdev) { + err = -ENOENT; + goto unlock_out; + } + + if (netdev_offload_xstats_enabled(hwsdev->netdev, type)) { + netdev_offload_xstats_push_delta(hwsdev->netdev, type, + &hwsdev->stats); + rtnl_offload_xstats_notify(hwsdev->netdev); + } + nsim_dev_hwsdev_fini(hwsdev); + +unlock_out: + rtnl_unlock(); + return err; +} + +static int +nsim_dev_hwstats_fail_ifindex(struct nsim_dev_hwstats *hwstats, + int ifindex, + enum netdev_offload_xstats_type type, + struct list_head *hwsdev_list) +{ + struct nsim_dev_hwstats_netdev *hwsdev; + int err = 0; + + mutex_lock(&hwstats->hwsdev_list_lock); + + hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex); + if (!hwsdev) { + err = -ENOENT; + goto err_hwsdev_list_unlock; + } + + hwsdev->fail_enable = true; + +err_hwsdev_list_unlock: + mutex_unlock(&hwstats->hwsdev_list_lock); + return err; +} + +enum nsim_dev_hwstats_do { + NSIM_DEV_HWSTATS_DO_DISABLE, + NSIM_DEV_HWSTATS_DO_ENABLE, + NSIM_DEV_HWSTATS_DO_FAIL, +}; + +struct nsim_dev_hwstats_fops { + const struct file_operations fops; + enum nsim_dev_hwstats_do action; + enum netdev_offload_xstats_type type; +}; + +static ssize_t +nsim_dev_hwstats_do_write(struct file *file, + const char __user *data, + size_t count, loff_t *ppos) +{ + struct nsim_dev_hwstats *hwstats = file->private_data; + struct nsim_dev_hwstats_fops *hwsfops; + struct list_head *hwsdev_list; + int ifindex; + int err; + + hwsfops = container_of(debugfs_real_fops(file), + struct nsim_dev_hwstats_fops, fops); + + err = kstrtoint_from_user(data, count, 0, &ifindex); + if (err) + return err; + + hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, hwsfops->type); + if (WARN_ON(!hwsdev_list)) + return -EINVAL; + + switch (hwsfops->action) { + case NSIM_DEV_HWSTATS_DO_DISABLE: + err = nsim_dev_hwstats_disable_ifindex(hwstats, ifindex, + hwsfops->type, + hwsdev_list); + break; + case NSIM_DEV_HWSTATS_DO_ENABLE: + err = nsim_dev_hwstats_enable_ifindex(hwstats, ifindex, + hwsfops->type, + hwsdev_list); + break; + case NSIM_DEV_HWSTATS_DO_FAIL: + err = nsim_dev_hwstats_fail_ifindex(hwstats, ifindex, + hwsfops->type, + hwsdev_list); + break; + } + if (err) + return err; + + return count; +} + +#define NSIM_DEV_HWSTATS_FOPS(ACTION, TYPE) \ + { \ + .fops = { \ + .open = simple_open, \ + .write = nsim_dev_hwstats_do_write, \ + .llseek = generic_file_llseek, \ + .owner = THIS_MODULE, \ + }, \ + .action = ACTION, \ + .type = TYPE, \ + } + +static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_disable_fops = + NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_DISABLE, + NETDEV_OFFLOAD_XSTATS_TYPE_L3); + +static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_enable_fops = + NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_ENABLE, + NETDEV_OFFLOAD_XSTATS_TYPE_L3); + +static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_fail_fops = + NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_FAIL, + NETDEV_OFFLOAD_XSTATS_TYPE_L3); + +#undef NSIM_DEV_HWSTATS_FOPS + +int nsim_dev_hwstats_init(struct nsim_dev *nsim_dev) +{ + struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats; + struct net *net = nsim_dev_net(nsim_dev); + int err; + + mutex_init(&hwstats->hwsdev_list_lock); + INIT_LIST_HEAD(&hwstats->l3_list); + + hwstats->netdevice_nb.notifier_call = nsim_dev_netdevice_event; + err = register_netdevice_notifier_net(net, &hwstats->netdevice_nb); + if (err) + goto err_mutex_destroy; + + hwstats->ddir = debugfs_create_dir("hwstats", nsim_dev->ddir); + if (IS_ERR(hwstats->ddir)) { + err = PTR_ERR(hwstats->ddir); + goto err_unregister_notifier; + } + + hwstats->l3_ddir = debugfs_create_dir("l3", hwstats->ddir); + if (IS_ERR(hwstats->l3_ddir)) { + err = PTR_ERR(hwstats->l3_ddir); + goto err_remove_hwstats_recursive; + } + + debugfs_create_file("enable_ifindex", 0600, hwstats->l3_ddir, hwstats, + &nsim_dev_hwstats_l3_enable_fops.fops); + debugfs_create_file("disable_ifindex", 0600, hwstats->l3_ddir, hwstats, + &nsim_dev_hwstats_l3_disable_fops.fops); + debugfs_create_file("fail_next_enable", 0600, hwstats->l3_ddir, hwstats, + &nsim_dev_hwstats_l3_fail_fops.fops); + + INIT_DELAYED_WORK(&hwstats->traffic_dw, + &nsim_dev_hwstats_traffic_work); + schedule_delayed_work(&hwstats->traffic_dw, + msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS)); + return 0; + +err_remove_hwstats_recursive: + debugfs_remove_recursive(hwstats->ddir); +err_unregister_notifier: + unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb); +err_mutex_destroy: + mutex_destroy(&hwstats->hwsdev_list_lock); + return err; +} + +static void nsim_dev_hwsdev_list_wipe(struct nsim_dev_hwstats *hwstats, + enum netdev_offload_xstats_type type) +{ + struct nsim_dev_hwstats_netdev *hwsdev, *tmp; + struct list_head *hwsdev_list; + + hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type); + if (WARN_ON(!hwsdev_list)) + return; + + mutex_lock(&hwstats->hwsdev_list_lock); + list_for_each_entry_safe(hwsdev, tmp, hwsdev_list, list) { + list_del(&hwsdev->list); + nsim_dev_hwsdev_fini(hwsdev); + } + mutex_unlock(&hwstats->hwsdev_list_lock); +} + +void nsim_dev_hwstats_exit(struct nsim_dev *nsim_dev) +{ + struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats; + struct net *net = nsim_dev_net(nsim_dev); + + cancel_delayed_work_sync(&hwstats->traffic_dw); + debugfs_remove_recursive(hwstats->ddir); + unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb); + nsim_dev_hwsdev_list_wipe(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3); + mutex_destroy(&hwstats->hwsdev_list_lock); +} diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index c49771f27f17..0b122872b2c9 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -184,6 +184,28 @@ struct nsim_dev_health { int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink); void nsim_dev_health_exit(struct nsim_dev *nsim_dev); +struct nsim_dev_hwstats_netdev { + struct list_head list; + struct net_device *netdev; + struct rtnl_hw_stats64 stats; + bool enabled; + bool fail_enable; +}; + +struct nsim_dev_hwstats { + struct dentry *ddir; + struct dentry *l3_ddir; + + struct mutex hwsdev_list_lock; /* protects hwsdev list(s) */ + struct list_head l3_list; + + struct notifier_block netdevice_nb; + struct delayed_work traffic_dw; +}; + +int nsim_dev_hwstats_init(struct nsim_dev *nsim_dev); +void nsim_dev_hwstats_exit(struct nsim_dev *nsim_dev); + #if IS_ENABLED(CONFIG_PSAMPLE) int nsim_dev_psample_init(struct nsim_dev *nsim_dev); void nsim_dev_psample_exit(struct nsim_dev *nsim_dev); @@ -239,7 +261,6 @@ struct nsim_dev { struct dentry *take_snapshot; struct dentry *nodes_ddir; - struct mutex vfs_lock; /* Protects vfconfigs */ struct nsim_vf_config *vfconfigs; struct bpf_offload_dev *bpf_dev; @@ -252,7 +273,6 @@ struct nsim_dev { struct list_head bpf_bound_maps; struct netdev_phys_item_id switch_id; struct list_head port_list; - struct mutex port_list_lock; /* protects port list */ bool fw_update_status; u32 fw_update_overwrite_mask; u32 max_macs; @@ -261,6 +281,7 @@ struct nsim_dev { bool fail_reload; struct devlink_region *dummy_region; struct nsim_dev_health health; + struct nsim_dev_hwstats hwstats; struct flow_action_cookie *fa_cookie; spinlock_t fa_cookie_lock; /* protects fa_cookie */ bool fail_trap_group_set; diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index 98ca6b18415e..80bdc07f2cd3 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -119,7 +119,7 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, skb->protocol = eth_type_trans(skb, ndev); skb->ip_summed = CHECKSUM_NONE; - if (netif_rx(skb) == NET_RX_DROP) { + if (__netif_rx(skb) == NET_RX_DROP) { ndev->stats.rx_errors++; ndev->stats.rx_dropped++; } else { diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index cd6742e6ba8b..61418d4dc0cd 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -632,35 +632,43 @@ static void xpcs_resolve_pma(struct dw_xpcs *xpcs, } } -void xpcs_validate(struct dw_xpcs *xpcs, unsigned long *supported, - struct phylink_link_state *state) +static int xpcs_validate(struct phylink_pcs *pcs, unsigned long *supported, + const struct phylink_link_state *state) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(xpcs_supported); + __ETHTOOL_DECLARE_LINK_MODE_MASK(xpcs_supported) = { 0, }; const struct xpcs_compat *compat; + struct dw_xpcs *xpcs; int i; - /* phylink expects us to report all supported modes with - * PHY_INTERFACE_MODE_NA, just don't limit the supported and - * advertising masks and exit. - */ - if (state->interface == PHY_INTERFACE_MODE_NA) - return; - - linkmode_zero(xpcs_supported); - + xpcs = phylink_pcs_to_xpcs(pcs); compat = xpcs_find_compat(xpcs->id, state->interface); - /* Populate the supported link modes for this - * PHY interface type + /* Populate the supported link modes for this PHY interface type. + * FIXME: what about the port modes and autoneg bit? This masks + * all those away. */ if (compat) for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++) set_bit(compat->supported[i], xpcs_supported); linkmode_and(supported, supported, xpcs_supported); - linkmode_and(state->advertising, state->advertising, xpcs_supported); + + return 0; } -EXPORT_SYMBOL_GPL(xpcs_validate); + +void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces) +{ + int i, j; + + for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) { + const struct xpcs_compat *compat = &xpcs->id->compat[i]; + + for (j = 0; j < compat->num_interfaces; j++) + if (compat->interface[j] < PHY_INTERFACE_MODE_MAX) + __set_bit(compat->interface[j], interfaces); + } +} +EXPORT_SYMBOL_GPL(xpcs_get_interfaces); int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable) { @@ -1106,6 +1114,7 @@ static const struct xpcs_id xpcs_id_list[] = { }; static const struct phylink_pcs_ops xpcs_phylink_ops = { + .pcs_validate = xpcs_validate, .pcs_config = xpcs_config, .pcs_get_state = xpcs_get_state, .pcs_link_up = xpcs_link_up, diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 902495afcb38..ea7571a2b39b 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -220,6 +220,7 @@ config MEDIATEK_GE_PHY config MICREL_PHY tristate "Micrel PHYs" + depends on PTP_1588_CLOCK_OPTIONAL help Supports the KSZ9021, VSC8201, KS8001 PHYs. diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c index 968dd43a2b1e..a8db1a19011b 100644 --- a/drivers/net/phy/aquantia_main.c +++ b/drivers/net/phy/aquantia_main.c @@ -533,9 +533,7 @@ static int aqcs109_config_init(struct phy_device *phydev) * PMA speed ability bits are the same for all members of the family, * AQCS109 however supports speeds up to 2.5G only. */ - ret = phy_set_max_speed(phydev, SPEED_2500); - if (ret) - return ret; + phy_set_max_speed(phydev, SPEED_2500); return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); } diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 29aa811af430..73926006d319 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 @@ -51,6 +53,8 @@ #define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) #define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) #define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) +#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) +#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) #define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) #define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) #define AT803X_INTR_ENABLE_WOL BIT(0) @@ -85,7 +89,17 @@ #define AT803X_DEBUG_DATA 0x1E #define AT803X_MODE_CFG_MASK 0x0F -#define AT803X_MODE_CFG_SGMII 0x01 +#define AT803X_MODE_CFG_BASET_RGMII 0x00 +#define AT803X_MODE_CFG_BASET_SGMII 0x01 +#define AT803X_MODE_CFG_BX1000_RGMII_50OHM 0x02 +#define AT803X_MODE_CFG_BX1000_RGMII_75OHM 0x03 +#define AT803X_MODE_CFG_BX1000_CONV_50OHM 0x04 +#define AT803X_MODE_CFG_BX1000_CONV_75OHM 0x05 +#define AT803X_MODE_CFG_FX100_RGMII_50OHM 0x06 +#define AT803X_MODE_CFG_FX100_CONV_50OHM 0x07 +#define AT803X_MODE_CFG_RGMII_AUTO_MDET 0x0B +#define AT803X_MODE_CFG_FX100_RGMII_75OHM 0x0E +#define AT803X_MODE_CFG_FX100_CONV_75OHM 0x0F #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ #define AT803X_PSSR_MR_AN_COMPLETE 0x0200 @@ -283,6 +297,8 @@ struct at803x_priv { u16 clk_25m_mask; u8 smarteee_lpi_tw_1g; u8 smarteee_lpi_tw_100m; + bool is_fiber; + bool is_1000basex; struct regulator_dev *vddio_rdev; struct regulator_dev *vddh_rdev; struct regulator *vddio; @@ -650,6 +666,55 @@ static int at8031_register_regulators(struct phy_device *phydev) return 0; } +static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +{ + struct phy_device *phydev = upstream; + __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); + __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); + phy_interface_t iface; + + linkmode_zero(phy_support); + phylink_set(phy_support, 1000baseX_Full); + phylink_set(phy_support, 1000baseT_Full); + phylink_set(phy_support, Autoneg); + phylink_set(phy_support, Pause); + phylink_set(phy_support, Asym_Pause); + + linkmode_zero(sfp_support); + sfp_parse_support(phydev->sfp_bus, id, sfp_support); + /* Some modules support 10G modes as well as others we support. + * Mask out non-supported modes so the correct interface is picked. + */ + linkmode_and(sfp_support, phy_support, sfp_support); + + if (linkmode_empty(sfp_support)) { + dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); + return -EINVAL; + } + + iface = sfp_select_interface(phydev->sfp_bus, sfp_support); + + /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes + * interface for use with SFP modules. + * However, some copper modules detected as having a preferred SGMII + * interface do default to and function in 1000Base-X mode, so just + * print a warning and allow such modules, as they may have some chance + * of working. + */ + if (iface == PHY_INTERFACE_MODE_SGMII) + dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); + else if (iface != PHY_INTERFACE_MODE_1000BASEX) + return -EINVAL; + + return 0; +} + +static const struct sfp_upstream_ops at803x_sfp_ops = { + .attach = phy_sfp_attach, + .detach = phy_sfp_detach, + .module_insert = at803x_sfp_insert, +}; + static int at803x_parse_dt(struct phy_device *phydev) { struct device_node *node = phydev->mdio.dev.of_node; @@ -757,6 +822,11 @@ static int at803x_parse_dt(struct phy_device *phydev) phydev_err(phydev, "failed to get VDDIO regulator\n"); return PTR_ERR(priv->vddio); } + + /* Only AR8031/8033 support 1000Base-X for SFP modules */ + ret = phy_sfp_probe(phydev, &at803x_sfp_ops); + if (ret < 0) + return ret; } return 0; @@ -784,16 +854,24 @@ static int at803x_probe(struct phy_device *phydev) return ret; } - /* Some bootloaders leave the fiber page selected. - * Switch to the copper page, as otherwise we read - * the PHY capabilities from the fiber side. - */ if (phydev->drv->phy_id == ATH8031_PHY_ID) { - phy_lock_mdio_bus(phydev); - ret = at803x_write_page(phydev, AT803X_PAGE_COPPER); - phy_unlock_mdio_bus(phydev); - if (ret) + int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); + int mode_cfg; + + if (ccr < 0) goto err; + mode_cfg = ccr & AT803X_MODE_CFG_MASK; + + switch (mode_cfg) { + case AT803X_MODE_CFG_BX1000_RGMII_50OHM: + case AT803X_MODE_CFG_BX1000_RGMII_75OHM: + priv->is_1000basex = true; + fallthrough; + case AT803X_MODE_CFG_FX100_RGMII_50OHM: + case AT803X_MODE_CFG_FX100_RGMII_75OHM: + priv->is_fiber = true; + break; + } } return 0; @@ -815,6 +893,7 @@ static void at803x_remove(struct phy_device *phydev) static int at803x_get_features(struct phy_device *phydev) { + struct at803x_priv *priv = phydev->priv; int err; err = genphy_read_abilities(phydev); @@ -841,12 +920,13 @@ static int at803x_get_features(struct phy_device *phydev) * As a result of that, ESTATUS_1000_XFULL is set * to 1 even when operating in copper TP mode. * - * Remove this mode from the supported link modes, - * as this driver currently only supports copper - * operation. + * Remove this mode from the supported link modes + * when not operating in 1000BaseX mode. */ - linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, - phydev->supported); + if (!priv->is_1000basex) + linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + phydev->supported); + return 0; } @@ -910,8 +990,27 @@ static int at8031_pll_config(struct phy_device *phydev) static int at803x_config_init(struct phy_device *phydev) { + struct at803x_priv *priv = phydev->priv; int ret; + if (phydev->drv->phy_id == ATH8031_PHY_ID) { + /* Some bootloaders leave the fiber page selected. + * Switch to the appropriate page (fiber or copper), as otherwise we + * read the PHY capabilities from the wrong page. + */ + phy_lock_mdio_bus(phydev); + ret = at803x_write_page(phydev, + priv->is_fiber ? AT803X_PAGE_FIBER : + AT803X_PAGE_COPPER); + phy_unlock_mdio_bus(phydev); + if (ret) + return ret; + + ret = at8031_pll_config(phydev); + if (ret < 0) + return ret; + } + /* The RX and TX delay default is: * after HW reset: RX delay enabled and TX delay disabled * after SW reset: RX delay enabled, while TX delay retains the @@ -941,12 +1040,6 @@ static int at803x_config_init(struct phy_device *phydev) if (ret < 0) return ret; - if (phydev->drv->phy_id == ATH8031_PHY_ID) { - ret = at8031_pll_config(phydev); - if (ret < 0) - return ret; - } - /* Ar803x extended next page bit is enabled by default. Cisco * multigig switches read this bit and attempt to negotiate 10Gbps * rates even if the next page bit is disabled. This is incorrect @@ -967,6 +1060,7 @@ static int at803x_ack_interrupt(struct phy_device *phydev) static int at803x_config_intr(struct phy_device *phydev) { + struct at803x_priv *priv = phydev->priv; int err; int value; @@ -983,6 +1077,10 @@ static int at803x_config_intr(struct phy_device *phydev) value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; value |= AT803X_INTR_ENABLE_LINK_FAIL; value |= AT803X_INTR_ENABLE_LINK_SUCCESS; + if (priv->is_fiber) { + value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; + value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; + } err = phy_write(phydev, AT803X_INTR_ENABLE, value); } else { @@ -1115,8 +1213,12 @@ static int at803x_read_specific_status(struct phy_device *phydev) static int at803x_read_status(struct phy_device *phydev) { + struct at803x_priv *priv = phydev->priv; int err, old_link = phydev->link; + if (priv->is_1000basex) + return genphy_c37_read_status(phydev); + /* Update the link, but return if there was an error */ err = genphy_update_link(phydev); if (err) @@ -1170,6 +1272,7 @@ static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) static int at803x_config_aneg(struct phy_device *phydev) { + struct at803x_priv *priv = phydev->priv; int ret; ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); @@ -1186,6 +1289,9 @@ static int at803x_config_aneg(struct phy_device *phydev) return ret; } + if (priv->is_1000basex) + return genphy_c37_config_aneg(phydev); + /* Do not restart auto-negotiation by setting ret to 0 defautly, * when calling __genphy_config_aneg later. */ diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index c2d1a85ec559..ef8b14135133 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -886,7 +886,7 @@ static void decode_rxts(struct dp83640_private *dp83640, spin_unlock_irqrestore(&dp83640->rx_lock, flags); if (shhwtstamps) - netif_rx_ni(skb); + netif_rx(skb); } static void decode_txts(struct dp83640_private *dp83640, @@ -970,17 +970,6 @@ static void decode_status_frame(struct dp83640_private *dp83640, } } -static int is_sync(struct sk_buff *skb, int type) -{ - struct ptp_header *hdr; - - hdr = ptp_parse_header(skb, type); - if (!hdr) - return 0; - - return ptp_get_msgtype(hdr, type) == PTP_MSGTYPE_SYNC; -} - static void dp83640_free_clocks(void) { struct dp83640_clock *clock; @@ -1329,7 +1318,7 @@ static void rx_timestamp_work(struct work_struct *work) break; } - netif_rx_ni(skb); + netif_rx(skb); } if (!skb_queue_empty(&dp83640->rx_queue)) @@ -1380,7 +1369,7 @@ static bool dp83640_rxtstamp(struct mii_timestamper *mii_ts, skb_queue_tail(&dp83640->rx_queue, skb); schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT); } else { - netif_rx_ni(skb); + netif_rx(skb); } return true; @@ -1396,7 +1385,7 @@ static void dp83640_txtstamp(struct mii_timestamper *mii_ts, switch (dp83640->hwts_tx_en) { case HWTSTAMP_TX_ONESTEP_SYNC: - if (is_sync(skb, type)) { + if (ptp_msg_is_sync(skb, type)) { kfree_skb(skb); return; } diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index a7ebcdab415b..19b11e896460 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -28,6 +28,10 @@ #include #include #include +#include +#include +#include +#include /* Operation Mode Strap Override */ #define MII_KSZPHY_OMSO 0x16 @@ -79,6 +83,119 @@ #define LAN8814_INTR_CTRL_REG_POLARITY BIT(1) #define LAN8814_INTR_CTRL_REG_INTR_ENABLE BIT(0) +/* Represents 1ppm adjustment in 2^32 format with + * each nsec contains 4 clock cycles. + * The value is calculated as following: (1/1000000)/((2^-32)/4) + */ +#define LAN8814_1PPM_FORMAT 17179 + +#define PTP_RX_MOD 0x024F +#define PTP_RX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_ BIT(3) +#define PTP_RX_TIMESTAMP_EN 0x024D +#define PTP_TX_TIMESTAMP_EN 0x028D + +#define PTP_TIMESTAMP_EN_SYNC_ BIT(0) +#define PTP_TIMESTAMP_EN_DREQ_ BIT(1) +#define PTP_TIMESTAMP_EN_PDREQ_ BIT(2) +#define PTP_TIMESTAMP_EN_PDRES_ BIT(3) + +#define PTP_RX_LATENCY_1000 0x0224 +#define PTP_TX_LATENCY_1000 0x0225 + +#define PTP_RX_LATENCY_100 0x0222 +#define PTP_TX_LATENCY_100 0x0223 + +#define PTP_RX_LATENCY_10 0x0220 +#define PTP_TX_LATENCY_10 0x0221 + +#define PTP_TX_PARSE_L2_ADDR_EN 0x0284 +#define PTP_RX_PARSE_L2_ADDR_EN 0x0244 + +#define PTP_TX_PARSE_IP_ADDR_EN 0x0285 +#define PTP_RX_PARSE_IP_ADDR_EN 0x0245 +#define LTC_HARD_RESET 0x023F +#define LTC_HARD_RESET_ BIT(0) + +#define TSU_HARD_RESET 0x02C1 +#define TSU_HARD_RESET_ BIT(0) + +#define PTP_CMD_CTL 0x0200 +#define PTP_CMD_CTL_PTP_DISABLE_ BIT(0) +#define PTP_CMD_CTL_PTP_ENABLE_ BIT(1) +#define PTP_CMD_CTL_PTP_CLOCK_READ_ BIT(3) +#define PTP_CMD_CTL_PTP_CLOCK_LOAD_ BIT(4) +#define PTP_CMD_CTL_PTP_LTC_STEP_SEC_ BIT(5) +#define PTP_CMD_CTL_PTP_LTC_STEP_NSEC_ BIT(6) + +#define PTP_CLOCK_SET_SEC_MID 0x0206 +#define PTP_CLOCK_SET_SEC_LO 0x0207 +#define PTP_CLOCK_SET_NS_HI 0x0208 +#define PTP_CLOCK_SET_NS_LO 0x0209 + +#define PTP_CLOCK_READ_SEC_MID 0x022A +#define PTP_CLOCK_READ_SEC_LO 0x022B +#define PTP_CLOCK_READ_NS_HI 0x022C +#define PTP_CLOCK_READ_NS_LO 0x022D + +#define PTP_OPERATING_MODE 0x0241 +#define PTP_OPERATING_MODE_STANDALONE_ BIT(0) + +#define PTP_TX_MOD 0x028F +#define PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_ BIT(12) +#define PTP_TX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_ BIT(3) + +#define PTP_RX_PARSE_CONFIG 0x0242 +#define PTP_RX_PARSE_CONFIG_LAYER2_EN_ BIT(0) +#define PTP_RX_PARSE_CONFIG_IPV4_EN_ BIT(1) +#define PTP_RX_PARSE_CONFIG_IPV6_EN_ BIT(2) + +#define PTP_TX_PARSE_CONFIG 0x0282 +#define PTP_TX_PARSE_CONFIG_LAYER2_EN_ BIT(0) +#define PTP_TX_PARSE_CONFIG_IPV4_EN_ BIT(1) +#define PTP_TX_PARSE_CONFIG_IPV6_EN_ BIT(2) + +#define PTP_CLOCK_RATE_ADJ_HI 0x020C +#define PTP_CLOCK_RATE_ADJ_LO 0x020D +#define PTP_CLOCK_RATE_ADJ_DIR_ BIT(15) + +#define PTP_LTC_STEP_ADJ_HI 0x0212 +#define PTP_LTC_STEP_ADJ_LO 0x0213 +#define PTP_LTC_STEP_ADJ_DIR_ BIT(15) + +#define LAN8814_INTR_STS_REG 0x0033 +#define LAN8814_INTR_STS_REG_1588_TSU0_ BIT(0) +#define LAN8814_INTR_STS_REG_1588_TSU1_ BIT(1) +#define LAN8814_INTR_STS_REG_1588_TSU2_ BIT(2) +#define LAN8814_INTR_STS_REG_1588_TSU3_ BIT(3) + +#define PTP_CAP_INFO 0x022A +#define PTP_CAP_INFO_TX_TS_CNT_GET_(reg_val) (((reg_val) & 0x0f00) >> 8) +#define PTP_CAP_INFO_RX_TS_CNT_GET_(reg_val) ((reg_val) & 0x000f) + +#define PTP_TX_EGRESS_SEC_HI 0x0296 +#define PTP_TX_EGRESS_SEC_LO 0x0297 +#define PTP_TX_EGRESS_NS_HI 0x0294 +#define PTP_TX_EGRESS_NS_LO 0x0295 +#define PTP_TX_MSG_HEADER2 0x0299 + +#define PTP_RX_INGRESS_SEC_HI 0x0256 +#define PTP_RX_INGRESS_SEC_LO 0x0257 +#define PTP_RX_INGRESS_NS_HI 0x0254 +#define PTP_RX_INGRESS_NS_LO 0x0255 +#define PTP_RX_MSG_HEADER2 0x0259 + +#define PTP_TSU_INT_EN 0x0200 +#define PTP_TSU_INT_EN_PTP_TX_TS_OVRFL_EN_ BIT(3) +#define PTP_TSU_INT_EN_PTP_TX_TS_EN_ BIT(2) +#define PTP_TSU_INT_EN_PTP_RX_TS_OVRFL_EN_ BIT(1) +#define PTP_TSU_INT_EN_PTP_RX_TS_EN_ BIT(0) + +#define PTP_TSU_INT_STS 0x0201 +#define PTP_TSU_INT_STS_PTP_TX_TS_OVRFL_INT_ BIT(3) +#define PTP_TSU_INT_STS_PTP_TX_TS_EN_ BIT(2) +#define PTP_TSU_INT_STS_PTP_RX_TS_OVRFL_INT_ BIT(1) +#define PTP_TSU_INT_STS_PTP_RX_TS_EN_ BIT(0) + /* PHY Control 1 */ #define MII_KSZPHY_CTRL_1 0x1e #define KSZ8081_CTRL1_MDIX_STAT BIT(4) @@ -108,6 +225,7 @@ #define MII_KSZPHY_TX_DATA_PAD_SKEW 0x106 #define PS_TO_REG 200 +#define FIFO_SIZE 8 struct kszphy_hw_stat { const char *string; @@ -128,7 +246,57 @@ struct kszphy_type { bool has_rmii_ref_clk_sel; }; +/* Shared structure between the PHYs of the same package. */ +struct lan8814_shared_priv { + struct phy_device *phydev; + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_clock_info; + + /* Reference counter to how many ports in the package are enabling the + * timestamping + */ + u8 ref; + + /* Lock for ptp_clock and ref */ + struct mutex shared_lock; +}; + +struct lan8814_ptp_rx_ts { + struct list_head list; + u32 seconds; + u32 nsec; + u16 seq_id; +}; + +struct kszphy_latencies { + u16 rx_10; + u16 tx_10; + u16 rx_100; + u16 tx_100; + u16 rx_1000; + u16 tx_1000; +}; + +struct kszphy_ptp_priv { + struct mii_timestamper mii_ts; + struct phy_device *phydev; + + struct sk_buff_head tx_queue; + struct sk_buff_head rx_queue; + + struct list_head rx_ts_list; + /* Lock for Rx ts fifo */ + spinlock_t rx_ts_lock; + + int hwts_tx_type; + enum hwtstamp_rx_filters rx_filter; + int layer; + int version; +}; + struct kszphy_priv { + struct kszphy_ptp_priv ptp_priv; + struct kszphy_latencies latencies; const struct kszphy_type *type; int led_mode; bool rmii_ref_clk_sel; @@ -136,6 +304,14 @@ struct kszphy_priv { u64 stats[ARRAY_SIZE(kszphy_hw_stats)]; }; +static struct kszphy_latencies lan8814_latencies = { + .rx_10 = 0x22AA, + .tx_10 = 0x2E4A, + .rx_100 = 0x092A, + .tx_100 = 0x02C1, + .rx_1000 = 0x01AD, + .tx_1000 = 0x00C9, +}; static const struct kszphy_type ksz8021_type = { .led_mode_reg = MII_KSZPHY_CTRL_2, .has_broadcast_disable = true, @@ -1596,11 +1772,13 @@ static int lanphy_read_page_reg(struct phy_device *phydev, int page, u32 addr) { u32 data; - phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, page); - phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, addr); - phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, - (page | LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC)); - data = phy_read(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA); + phy_lock_mdio_bus(phydev); + __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, page); + __phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, addr); + __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, + (page | LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC)); + data = __phy_read(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA); + phy_unlock_mdio_bus(phydev); return data; } @@ -1608,43 +1786,670 @@ static int lanphy_read_page_reg(struct phy_device *phydev, int page, u32 addr) static int lanphy_write_page_reg(struct phy_device *phydev, int page, u16 addr, u16 val) { - phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, page); - phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, addr); - phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, - (page | LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC)); + phy_lock_mdio_bus(phydev); + __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, page); + __phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, addr); + __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, + page | LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC); - val = phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, val); - if (val) { + val = __phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, val); + if (val != 0) phydev_err(phydev, "Error: phy_write has returned error %d\n", val); - return val; - } + phy_unlock_mdio_bus(phydev); + return val; +} + +static int lan8814_config_ts_intr(struct phy_device *phydev, bool enable) +{ + u16 val = 0; + + if (enable) + val = PTP_TSU_INT_EN_PTP_TX_TS_EN_ | + PTP_TSU_INT_EN_PTP_TX_TS_OVRFL_EN_ | + PTP_TSU_INT_EN_PTP_RX_TS_EN_ | + PTP_TSU_INT_EN_PTP_RX_TS_OVRFL_EN_; + + return lanphy_write_page_reg(phydev, 5, PTP_TSU_INT_EN, val); +} + +static void lan8814_ptp_rx_ts_get(struct phy_device *phydev, + u32 *seconds, u32 *nano_seconds, u16 *seq_id) +{ + *seconds = lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_SEC_HI); + *seconds = (*seconds << 16) | + lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_SEC_LO); + + *nano_seconds = lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_NS_HI); + *nano_seconds = ((*nano_seconds & 0x3fff) << 16) | + lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_NS_LO); + + *seq_id = lanphy_read_page_reg(phydev, 5, PTP_RX_MSG_HEADER2); +} + +static void lan8814_ptp_tx_ts_get(struct phy_device *phydev, + u32 *seconds, u32 *nano_seconds, u16 *seq_id) +{ + *seconds = lanphy_read_page_reg(phydev, 5, PTP_TX_EGRESS_SEC_HI); + *seconds = *seconds << 16 | + lanphy_read_page_reg(phydev, 5, PTP_TX_EGRESS_SEC_LO); + + *nano_seconds = lanphy_read_page_reg(phydev, 5, PTP_TX_EGRESS_NS_HI); + *nano_seconds = ((*nano_seconds & 0x3fff) << 16) | + lanphy_read_page_reg(phydev, 5, PTP_TX_EGRESS_NS_LO); + + *seq_id = lanphy_read_page_reg(phydev, 5, PTP_TX_MSG_HEADER2); +} + +static int lan8814_ts_info(struct mii_timestamper *mii_ts, struct ethtool_ts_info *info) +{ + struct kszphy_ptp_priv *ptp_priv = container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); + struct phy_device *phydev = ptp_priv->phydev; + struct lan8814_shared_priv *shared = phydev->shared->priv; + + info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + info->phc_index = ptp_clock_index(shared->ptp_clock); + + info->tx_types = + (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON) | + (1 << HWTSTAMP_TX_ONESTEP_SYNC); + + info->rx_filters = + (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); + return 0; } -static int lan8814_config_init(struct phy_device *phydev) +static void lan8814_flush_fifo(struct phy_device *phydev, bool egress) { - int val; + int i; - /* Reset the PHY */ - val = lanphy_read_page_reg(phydev, 4, LAN8814_QSGMII_SOFT_RESET); - val |= LAN8814_QSGMII_SOFT_RESET_BIT; - lanphy_write_page_reg(phydev, 4, LAN8814_QSGMII_SOFT_RESET, val); + for (i = 0; i < FIFO_SIZE; ++i) + lanphy_read_page_reg(phydev, 5, + egress ? PTP_TX_MSG_HEADER2 : PTP_RX_MSG_HEADER2); - /* Disable ANEG with QSGMII PCS Host side */ - val = lanphy_read_page_reg(phydev, 5, LAN8814_QSGMII_PCS1G_ANEG_CONFIG); - val &= ~LAN8814_QSGMII_PCS1G_ANEG_CONFIG_ANEG_ENA; - lanphy_write_page_reg(phydev, 5, LAN8814_QSGMII_PCS1G_ANEG_CONFIG, val); + /* Read to clear overflow status bit */ + lanphy_read_page_reg(phydev, 5, PTP_TSU_INT_STS); +} - /* MDI-X setting for swap A,B transmit */ - val = lanphy_read_page_reg(phydev, 2, LAN8814_ALIGN_SWAP); - val &= ~LAN8814_ALIGN_TX_A_B_SWAP_MASK; - val |= LAN8814_ALIGN_TX_A_B_SWAP; - lanphy_write_page_reg(phydev, 2, LAN8814_ALIGN_SWAP, val); +static int lan8814_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) +{ + struct kszphy_ptp_priv *ptp_priv = + container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); + struct phy_device *phydev = ptp_priv->phydev; + struct lan8814_shared_priv *shared = phydev->shared->priv; + struct lan8814_ptp_rx_ts *rx_ts, *tmp; + struct hwtstamp_config config; + int txcfg = 0, rxcfg = 0; + int pkt_ts_enable; + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + ptp_priv->hwts_tx_type = config.tx_type; + ptp_priv->rx_filter = config.rx_filter; + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + ptp_priv->layer = 0; + ptp_priv->version = 0; + break; + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + ptp_priv->layer = PTP_CLASS_L4; + ptp_priv->version = PTP_CLASS_V2; + break; + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + ptp_priv->layer = PTP_CLASS_L2; + ptp_priv->version = PTP_CLASS_V2; + break; + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + ptp_priv->layer = PTP_CLASS_L4 | PTP_CLASS_L2; + ptp_priv->version = PTP_CLASS_V2; + break; + default: + return -ERANGE; + } + + if (ptp_priv->layer & PTP_CLASS_L2) { + rxcfg = PTP_RX_PARSE_CONFIG_LAYER2_EN_; + txcfg = PTP_TX_PARSE_CONFIG_LAYER2_EN_; + } else if (ptp_priv->layer & PTP_CLASS_L4) { + rxcfg |= PTP_RX_PARSE_CONFIG_IPV4_EN_ | PTP_RX_PARSE_CONFIG_IPV6_EN_; + txcfg |= PTP_TX_PARSE_CONFIG_IPV4_EN_ | PTP_TX_PARSE_CONFIG_IPV6_EN_; + } + lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_RX_PARSE_CONFIG, rxcfg); + lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_PARSE_CONFIG, txcfg); + + pkt_ts_enable = PTP_TIMESTAMP_EN_SYNC_ | PTP_TIMESTAMP_EN_DREQ_ | + PTP_TIMESTAMP_EN_PDREQ_ | PTP_TIMESTAMP_EN_PDRES_; + lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_RX_TIMESTAMP_EN, pkt_ts_enable); + lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_TIMESTAMP_EN, pkt_ts_enable); + + if (ptp_priv->hwts_tx_type == HWTSTAMP_TX_ONESTEP_SYNC) + lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_MOD, + PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_); + + if (config.rx_filter != HWTSTAMP_FILTER_NONE) + lan8814_config_ts_intr(ptp_priv->phydev, true); + else + lan8814_config_ts_intr(ptp_priv->phydev, false); + + mutex_lock(&shared->shared_lock); + if (config.rx_filter != HWTSTAMP_FILTER_NONE) + shared->ref++; + else + shared->ref--; + + if (shared->ref) + lanphy_write_page_reg(ptp_priv->phydev, 4, PTP_CMD_CTL, + PTP_CMD_CTL_PTP_ENABLE_); + else + lanphy_write_page_reg(ptp_priv->phydev, 4, PTP_CMD_CTL, + PTP_CMD_CTL_PTP_DISABLE_); + mutex_unlock(&shared->shared_lock); + + /* In case of multiple starts and stops, these needs to be cleared */ + list_for_each_entry_safe(rx_ts, tmp, &ptp_priv->rx_ts_list, list) { + list_del(&rx_ts->list); + kfree(rx_ts); + } + skb_queue_purge(&ptp_priv->rx_queue); + skb_queue_purge(&ptp_priv->tx_queue); + + lan8814_flush_fifo(ptp_priv->phydev, false); + lan8814_flush_fifo(ptp_priv->phydev, true); + + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; +} + +static void lan8814_txtstamp(struct mii_timestamper *mii_ts, + struct sk_buff *skb, int type) +{ + struct kszphy_ptp_priv *ptp_priv = container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); + + switch (ptp_priv->hwts_tx_type) { + case HWTSTAMP_TX_ONESTEP_SYNC: + if (ptp_msg_is_sync(skb, type)) { + kfree_skb(skb); + return; + } + fallthrough; + case HWTSTAMP_TX_ON: + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + skb_queue_tail(&ptp_priv->tx_queue, skb); + break; + case HWTSTAMP_TX_OFF: + default: + kfree_skb(skb); + break; + } +} + +static void lan8814_get_sig_rx(struct sk_buff *skb, u16 *sig) +{ + struct ptp_header *ptp_header; + u32 type; + + skb_push(skb, ETH_HLEN); + type = ptp_classify_raw(skb); + ptp_header = ptp_parse_header(skb, type); + skb_pull_inline(skb, ETH_HLEN); + + *sig = (__force u16)(ntohs(ptp_header->sequence_id)); +} + +static bool lan8814_match_rx_ts(struct kszphy_ptp_priv *ptp_priv, + struct sk_buff *skb) +{ + struct skb_shared_hwtstamps *shhwtstamps; + struct lan8814_ptp_rx_ts *rx_ts, *tmp; + unsigned long flags; + bool ret = false; + u16 skb_sig; + + lan8814_get_sig_rx(skb, &skb_sig); + + /* Iterate over all RX timestamps and match it with the received skbs */ + spin_lock_irqsave(&ptp_priv->rx_ts_lock, flags); + list_for_each_entry_safe(rx_ts, tmp, &ptp_priv->rx_ts_list, list) { + /* Check if we found the signature we were looking for. */ + if (memcmp(&skb_sig, &rx_ts->seq_id, sizeof(rx_ts->seq_id))) + continue; + + shhwtstamps = skb_hwtstamps(skb); + memset(shhwtstamps, 0, sizeof(*shhwtstamps)); + shhwtstamps->hwtstamp = ktime_set(rx_ts->seconds, + rx_ts->nsec); + list_del(&rx_ts->list); + kfree(rx_ts); + + ret = true; + break; + } + spin_unlock_irqrestore(&ptp_priv->rx_ts_lock, flags); + + if (ret) + netif_rx(skb); + return ret; +} + +static bool lan8814_rxtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb, int type) +{ + struct kszphy_ptp_priv *ptp_priv = + container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); + + if (ptp_priv->rx_filter == HWTSTAMP_FILTER_NONE || + type == PTP_CLASS_NONE) + return false; + + if ((type & ptp_priv->version) == 0 || (type & ptp_priv->layer) == 0) + return false; + + /* If we failed to match then add it to the queue for when the timestamp + * will come + */ + if (!lan8814_match_rx_ts(ptp_priv, skb)) + skb_queue_tail(&ptp_priv->rx_queue, skb); + + return true; +} + +static void lan8814_ptp_clock_set(struct phy_device *phydev, + u32 seconds, u32 nano_seconds) +{ + u32 sec_low, sec_high, nsec_low, nsec_high; + + sec_low = seconds & 0xffff; + sec_high = (seconds >> 16) & 0xffff; + nsec_low = nano_seconds & 0xffff; + nsec_high = (nano_seconds >> 16) & 0x3fff; + + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_LO, sec_low); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_MID, sec_high); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_LO, nsec_low); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_HI, nsec_high); + + lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_LOAD_); +} + +static void lan8814_ptp_clock_get(struct phy_device *phydev, + u32 *seconds, u32 *nano_seconds) +{ + lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_READ_); + + *seconds = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_MID); + *seconds = (*seconds << 16) | + lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_LO); + + *nano_seconds = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_HI); + *nano_seconds = ((*nano_seconds & 0x3fff) << 16) | + lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_LO); +} + +static int lan8814_ptpci_gettime64(struct ptp_clock_info *ptpci, + struct timespec64 *ts) +{ + struct lan8814_shared_priv *shared = container_of(ptpci, struct lan8814_shared_priv, + ptp_clock_info); + struct phy_device *phydev = shared->phydev; + u32 nano_seconds; + u32 seconds; + + mutex_lock(&shared->shared_lock); + lan8814_ptp_clock_get(phydev, &seconds, &nano_seconds); + mutex_unlock(&shared->shared_lock); + ts->tv_sec = seconds; + ts->tv_nsec = nano_seconds; return 0; } +static int lan8814_ptpci_settime64(struct ptp_clock_info *ptpci, + const struct timespec64 *ts) +{ + struct lan8814_shared_priv *shared = container_of(ptpci, struct lan8814_shared_priv, + ptp_clock_info); + struct phy_device *phydev = shared->phydev; + + mutex_lock(&shared->shared_lock); + lan8814_ptp_clock_set(phydev, ts->tv_sec, ts->tv_nsec); + mutex_unlock(&shared->shared_lock); + + return 0; +} + +static void lan8814_ptp_clock_step(struct phy_device *phydev, + s64 time_step_ns) +{ + u32 nano_seconds_step; + u64 abs_time_step_ns; + u32 unsigned_seconds; + u32 nano_seconds; + u32 remainder; + s32 seconds; + + if (time_step_ns > 15000000000LL) { + /* convert to clock set */ + lan8814_ptp_clock_get(phydev, &unsigned_seconds, &nano_seconds); + unsigned_seconds += div_u64_rem(time_step_ns, 1000000000LL, + &remainder); + nano_seconds += remainder; + if (nano_seconds >= 1000000000) { + unsigned_seconds++; + nano_seconds -= 1000000000; + } + lan8814_ptp_clock_set(phydev, unsigned_seconds, nano_seconds); + return; + } else if (time_step_ns < -15000000000LL) { + /* convert to clock set */ + time_step_ns = -time_step_ns; + + lan8814_ptp_clock_get(phydev, &unsigned_seconds, &nano_seconds); + unsigned_seconds -= div_u64_rem(time_step_ns, 1000000000LL, + &remainder); + nano_seconds_step = remainder; + if (nano_seconds < nano_seconds_step) { + unsigned_seconds--; + nano_seconds += 1000000000; + } + nano_seconds -= nano_seconds_step; + lan8814_ptp_clock_set(phydev, unsigned_seconds, + nano_seconds); + return; + } + + /* do clock step */ + if (time_step_ns >= 0) { + abs_time_step_ns = (u64)time_step_ns; + seconds = (s32)div_u64_rem(abs_time_step_ns, 1000000000, + &remainder); + nano_seconds = remainder; + } else { + abs_time_step_ns = (u64)(-time_step_ns); + seconds = -((s32)div_u64_rem(abs_time_step_ns, 1000000000, + &remainder)); + nano_seconds = remainder; + if (nano_seconds > 0) { + /* subtracting nano seconds is not allowed + * convert to subtracting from seconds, + * and adding to nanoseconds + */ + seconds--; + nano_seconds = (1000000000 - nano_seconds); + } + } + + if (nano_seconds > 0) { + /* add 8 ns to cover the likely normal increment */ + nano_seconds += 8; + } + + if (nano_seconds >= 1000000000) { + /* carry into seconds */ + seconds++; + nano_seconds -= 1000000000; + } + + while (seconds) { + if (seconds > 0) { + u32 adjustment_value = (u32)seconds; + u16 adjustment_value_lo, adjustment_value_hi; + + if (adjustment_value > 0xF) + adjustment_value = 0xF; + + adjustment_value_lo = adjustment_value & 0xffff; + adjustment_value_hi = (adjustment_value >> 16) & 0x3fff; + + lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_LO, + adjustment_value_lo); + lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_HI, + PTP_LTC_STEP_ADJ_DIR_ | + adjustment_value_hi); + seconds -= ((s32)adjustment_value); + } else { + u32 adjustment_value = (u32)(-seconds); + u16 adjustment_value_lo, adjustment_value_hi; + + if (adjustment_value > 0xF) + adjustment_value = 0xF; + + adjustment_value_lo = adjustment_value & 0xffff; + adjustment_value_hi = (adjustment_value >> 16) & 0x3fff; + + lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_LO, + adjustment_value_lo); + lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_HI, + adjustment_value_hi); + seconds += ((s32)adjustment_value); + } + lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, + PTP_CMD_CTL_PTP_LTC_STEP_SEC_); + } + if (nano_seconds) { + u16 nano_seconds_lo; + u16 nano_seconds_hi; + + nano_seconds_lo = nano_seconds & 0xffff; + nano_seconds_hi = (nano_seconds >> 16) & 0x3fff; + + lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_LO, + nano_seconds_lo); + lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_HI, + PTP_LTC_STEP_ADJ_DIR_ | + nano_seconds_hi); + lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, + PTP_CMD_CTL_PTP_LTC_STEP_NSEC_); + } +} + +static int lan8814_ptpci_adjtime(struct ptp_clock_info *ptpci, s64 delta) +{ + struct lan8814_shared_priv *shared = container_of(ptpci, struct lan8814_shared_priv, + ptp_clock_info); + struct phy_device *phydev = shared->phydev; + + mutex_lock(&shared->shared_lock); + lan8814_ptp_clock_step(phydev, delta); + mutex_unlock(&shared->shared_lock); + + return 0; +} + +static int lan8814_ptpci_adjfine(struct ptp_clock_info *ptpci, long scaled_ppm) +{ + struct lan8814_shared_priv *shared = container_of(ptpci, struct lan8814_shared_priv, + ptp_clock_info); + struct phy_device *phydev = shared->phydev; + u16 kszphy_rate_adj_lo, kszphy_rate_adj_hi; + bool positive = true; + u32 kszphy_rate_adj; + + if (scaled_ppm < 0) { + scaled_ppm = -scaled_ppm; + positive = false; + } + + kszphy_rate_adj = LAN8814_1PPM_FORMAT * (scaled_ppm >> 16); + kszphy_rate_adj += (LAN8814_1PPM_FORMAT * (0xffff & scaled_ppm)) >> 16; + + kszphy_rate_adj_lo = kszphy_rate_adj & 0xffff; + kszphy_rate_adj_hi = (kszphy_rate_adj >> 16) & 0x3fff; + + if (positive) + kszphy_rate_adj_hi |= PTP_CLOCK_RATE_ADJ_DIR_; + + mutex_lock(&shared->shared_lock); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_RATE_ADJ_HI, kszphy_rate_adj_hi); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_RATE_ADJ_LO, kszphy_rate_adj_lo); + mutex_unlock(&shared->shared_lock); + + return 0; +} + +static void lan8814_get_sig_tx(struct sk_buff *skb, u16 *sig) +{ + struct ptp_header *ptp_header; + u32 type; + + type = ptp_classify_raw(skb); + ptp_header = ptp_parse_header(skb, type); + + *sig = (__force u16)(ntohs(ptp_header->sequence_id)); +} + +static void lan8814_dequeue_tx_skb(struct kszphy_ptp_priv *ptp_priv) +{ + struct phy_device *phydev = ptp_priv->phydev; + struct skb_shared_hwtstamps shhwtstamps; + struct sk_buff *skb, *skb_tmp; + unsigned long flags; + u32 seconds, nsec; + bool ret = false; + u16 skb_sig; + u16 seq_id; + + lan8814_ptp_tx_ts_get(phydev, &seconds, &nsec, &seq_id); + + spin_lock_irqsave(&ptp_priv->tx_queue.lock, flags); + skb_queue_walk_safe(&ptp_priv->tx_queue, skb, skb_tmp) { + lan8814_get_sig_tx(skb, &skb_sig); + + if (memcmp(&skb_sig, &seq_id, sizeof(seq_id))) + continue; + + __skb_unlink(skb, &ptp_priv->tx_queue); + ret = true; + break; + } + spin_unlock_irqrestore(&ptp_priv->tx_queue.lock, flags); + + if (ret) { + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ktime_set(seconds, nsec); + skb_complete_tx_timestamp(skb, &shhwtstamps); + } +} + +static void lan8814_get_tx_ts(struct kszphy_ptp_priv *ptp_priv) +{ + struct phy_device *phydev = ptp_priv->phydev; + u32 reg; + + do { + lan8814_dequeue_tx_skb(ptp_priv); + + /* If other timestamps are available in the FIFO, + * process them. + */ + reg = lanphy_read_page_reg(phydev, 5, PTP_CAP_INFO); + } while (PTP_CAP_INFO_TX_TS_CNT_GET_(reg) > 0); +} + +static bool lan8814_match_skb(struct kszphy_ptp_priv *ptp_priv, + struct lan8814_ptp_rx_ts *rx_ts) +{ + struct skb_shared_hwtstamps *shhwtstamps; + struct sk_buff *skb, *skb_tmp; + unsigned long flags; + bool ret = false; + u16 skb_sig; + + spin_lock_irqsave(&ptp_priv->rx_queue.lock, flags); + skb_queue_walk_safe(&ptp_priv->rx_queue, skb, skb_tmp) { + lan8814_get_sig_rx(skb, &skb_sig); + + if (memcmp(&skb_sig, &rx_ts->seq_id, sizeof(rx_ts->seq_id))) + continue; + + __skb_unlink(skb, &ptp_priv->rx_queue); + + ret = true; + break; + } + spin_unlock_irqrestore(&ptp_priv->rx_queue.lock, flags); + + if (ret) { + shhwtstamps = skb_hwtstamps(skb); + memset(shhwtstamps, 0, sizeof(*shhwtstamps)); + shhwtstamps->hwtstamp = ktime_set(rx_ts->seconds, rx_ts->nsec); + netif_rx(skb); + } + + return ret; +} + +static void lan8814_get_rx_ts(struct kszphy_ptp_priv *ptp_priv) +{ + struct phy_device *phydev = ptp_priv->phydev; + struct lan8814_ptp_rx_ts *rx_ts; + unsigned long flags; + u32 reg; + + do { + rx_ts = kzalloc(sizeof(*rx_ts), GFP_KERNEL); + if (!rx_ts) + return; + + lan8814_ptp_rx_ts_get(phydev, &rx_ts->seconds, &rx_ts->nsec, + &rx_ts->seq_id); + + /* If we failed to match the skb add it to the queue for when + * the frame will come + */ + if (!lan8814_match_skb(ptp_priv, rx_ts)) { + spin_lock_irqsave(&ptp_priv->rx_ts_lock, flags); + list_add(&rx_ts->list, &ptp_priv->rx_ts_list); + spin_unlock_irqrestore(&ptp_priv->rx_ts_lock, flags); + } else { + kfree(rx_ts); + } + + /* If other timestamps are available in the FIFO, + * process them. + */ + reg = lanphy_read_page_reg(phydev, 5, PTP_CAP_INFO); + } while (PTP_CAP_INFO_RX_TS_CNT_GET_(reg) > 0); +} + +static void lan8814_handle_ptp_interrupt(struct phy_device *phydev) +{ + struct kszphy_priv *priv = phydev->priv; + struct kszphy_ptp_priv *ptp_priv = &priv->ptp_priv; + u16 status; + + status = lanphy_read_page_reg(phydev, 5, PTP_TSU_INT_STS); + if (status & PTP_TSU_INT_STS_PTP_TX_TS_EN_) + lan8814_get_tx_ts(ptp_priv); + + if (status & PTP_TSU_INT_STS_PTP_RX_TS_EN_) + lan8814_get_rx_ts(ptp_priv); + + if (status & PTP_TSU_INT_STS_PTP_TX_TS_OVRFL_INT_) { + lan8814_flush_fifo(phydev, true); + skb_queue_purge(&ptp_priv->tx_queue); + } + + if (status & PTP_TSU_INT_STS_PTP_RX_TS_OVRFL_INT_) { + lan8814_flush_fifo(phydev, false); + skb_queue_purge(&ptp_priv->rx_queue); + } +} + static int lan8804_config_init(struct phy_device *phydev) { int val; @@ -1666,17 +2471,31 @@ static int lan8804_config_init(struct phy_device *phydev) static irqreturn_t lan8814_handle_interrupt(struct phy_device *phydev) { + u16 tsu_irq_status; int irq_status; irq_status = phy_read(phydev, LAN8814_INTS); - if (irq_status < 0) + if (irq_status > 0 && (irq_status & LAN8814_INT_LINK)) + phy_trigger_machine(phydev); + + if (irq_status < 0) { + phy_error(phydev); return IRQ_NONE; + } - if (!(irq_status & LAN8814_INT_LINK)) - return IRQ_NONE; - - phy_trigger_machine(phydev); + while (1) { + tsu_irq_status = lanphy_read_page_reg(phydev, 4, + LAN8814_INTR_STS_REG); + if (tsu_irq_status > 0 && + (tsu_irq_status & (LAN8814_INTR_STS_REG_1588_TSU0_ | + LAN8814_INTR_STS_REG_1588_TSU1_ | + LAN8814_INTR_STS_REG_1588_TSU2_ | + LAN8814_INTR_STS_REG_1588_TSU3_))) + lan8814_handle_ptp_interrupt(phydev); + else + break; + } return IRQ_HANDLED; } @@ -1716,6 +2535,223 @@ static int lan8814_config_intr(struct phy_device *phydev) return err; } +static void lan8814_ptp_init(struct phy_device *phydev) +{ + struct kszphy_priv *priv = phydev->priv; + struct kszphy_ptp_priv *ptp_priv = &priv->ptp_priv; + u32 temp; + + lanphy_write_page_reg(phydev, 5, TSU_HARD_RESET, TSU_HARD_RESET_); + + temp = lanphy_read_page_reg(phydev, 5, PTP_TX_MOD); + temp |= PTP_TX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_; + lanphy_write_page_reg(phydev, 5, PTP_TX_MOD, temp); + + temp = lanphy_read_page_reg(phydev, 5, PTP_RX_MOD); + temp |= PTP_RX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_; + lanphy_write_page_reg(phydev, 5, PTP_RX_MOD, temp); + + lanphy_write_page_reg(phydev, 5, PTP_RX_PARSE_CONFIG, 0); + lanphy_write_page_reg(phydev, 5, PTP_TX_PARSE_CONFIG, 0); + + /* Removing default registers configs related to L2 and IP */ + lanphy_write_page_reg(phydev, 5, PTP_TX_PARSE_L2_ADDR_EN, 0); + lanphy_write_page_reg(phydev, 5, PTP_RX_PARSE_L2_ADDR_EN, 0); + lanphy_write_page_reg(phydev, 5, PTP_TX_PARSE_IP_ADDR_EN, 0); + lanphy_write_page_reg(phydev, 5, PTP_RX_PARSE_IP_ADDR_EN, 0); + + skb_queue_head_init(&ptp_priv->tx_queue); + skb_queue_head_init(&ptp_priv->rx_queue); + INIT_LIST_HEAD(&ptp_priv->rx_ts_list); + spin_lock_init(&ptp_priv->rx_ts_lock); + + ptp_priv->phydev = phydev; + + ptp_priv->mii_ts.rxtstamp = lan8814_rxtstamp; + ptp_priv->mii_ts.txtstamp = lan8814_txtstamp; + ptp_priv->mii_ts.hwtstamp = lan8814_hwtstamp; + ptp_priv->mii_ts.ts_info = lan8814_ts_info; + + phydev->mii_ts = &ptp_priv->mii_ts; +} + +static int lan8814_ptp_probe_once(struct phy_device *phydev) +{ + struct lan8814_shared_priv *shared = phydev->shared->priv; + + /* Initialise shared lock for clock*/ + mutex_init(&shared->shared_lock); + + shared->ptp_clock_info.owner = THIS_MODULE; + snprintf(shared->ptp_clock_info.name, 30, "%s", phydev->drv->name); + shared->ptp_clock_info.max_adj = 31249999; + shared->ptp_clock_info.n_alarm = 0; + shared->ptp_clock_info.n_ext_ts = 0; + shared->ptp_clock_info.n_pins = 0; + shared->ptp_clock_info.pps = 0; + shared->ptp_clock_info.pin_config = NULL; + shared->ptp_clock_info.adjfine = lan8814_ptpci_adjfine; + shared->ptp_clock_info.adjtime = lan8814_ptpci_adjtime; + shared->ptp_clock_info.gettime64 = lan8814_ptpci_gettime64; + shared->ptp_clock_info.settime64 = lan8814_ptpci_settime64; + shared->ptp_clock_info.getcrosststamp = NULL; + + shared->ptp_clock = ptp_clock_register(&shared->ptp_clock_info, + &phydev->mdio.dev); + if (IS_ERR_OR_NULL(shared->ptp_clock)) { + phydev_err(phydev, "ptp_clock_register failed %lu\n", + PTR_ERR(shared->ptp_clock)); + return -EINVAL; + } + + phydev_dbg(phydev, "successfully registered ptp clock\n"); + + shared->phydev = phydev; + + /* The EP.4 is shared between all the PHYs in the package and also it + * can be accessed by any of the PHYs + */ + lanphy_write_page_reg(phydev, 4, LTC_HARD_RESET, LTC_HARD_RESET_); + lanphy_write_page_reg(phydev, 4, PTP_OPERATING_MODE, + PTP_OPERATING_MODE_STANDALONE_); + + return 0; +} + +static int lan8814_read_status(struct phy_device *phydev) +{ + struct kszphy_priv *priv = phydev->priv; + struct kszphy_latencies *latencies = &priv->latencies; + int err; + int regval; + + err = genphy_read_status(phydev); + if (err) + return err; + + switch (phydev->speed) { + case SPEED_1000: + lanphy_write_page_reg(phydev, 5, PTP_RX_LATENCY_1000, + latencies->rx_1000); + lanphy_write_page_reg(phydev, 5, PTP_TX_LATENCY_1000, + latencies->tx_1000); + break; + case SPEED_100: + lanphy_write_page_reg(phydev, 5, PTP_RX_LATENCY_100, + latencies->rx_100); + lanphy_write_page_reg(phydev, 5, PTP_TX_LATENCY_100, + latencies->tx_100); + break; + case SPEED_10: + lanphy_write_page_reg(phydev, 5, PTP_RX_LATENCY_10, + latencies->rx_10); + lanphy_write_page_reg(phydev, 5, PTP_TX_LATENCY_10, + latencies->tx_10); + break; + default: + break; + } + + /* Make sure the PHY is not broken. Read idle error count, + * and reset the PHY if it is maxed out. + */ + regval = phy_read(phydev, MII_STAT1000); + if ((regval & 0xFF) == 0xFF) { + phy_init_hw(phydev); + phydev->link = 0; + if (phydev->drv->config_intr && phy_interrupt_is_valid(phydev)) + phydev->drv->config_intr(phydev); + return genphy_config_aneg(phydev); + } + + return 0; +} + +static int lan8814_config_init(struct phy_device *phydev) +{ + int val; + + /* Reset the PHY */ + val = lanphy_read_page_reg(phydev, 4, LAN8814_QSGMII_SOFT_RESET); + val |= LAN8814_QSGMII_SOFT_RESET_BIT; + lanphy_write_page_reg(phydev, 4, LAN8814_QSGMII_SOFT_RESET, val); + + /* Disable ANEG with QSGMII PCS Host side */ + val = lanphy_read_page_reg(phydev, 5, LAN8814_QSGMII_PCS1G_ANEG_CONFIG); + val &= ~LAN8814_QSGMII_PCS1G_ANEG_CONFIG_ANEG_ENA; + lanphy_write_page_reg(phydev, 5, LAN8814_QSGMII_PCS1G_ANEG_CONFIG, val); + + /* MDI-X setting for swap A,B transmit */ + val = lanphy_read_page_reg(phydev, 2, LAN8814_ALIGN_SWAP); + val &= ~LAN8814_ALIGN_TX_A_B_SWAP_MASK; + val |= LAN8814_ALIGN_TX_A_B_SWAP; + lanphy_write_page_reg(phydev, 2, LAN8814_ALIGN_SWAP, val); + + return 0; +} + +static void lan8814_parse_latency(struct phy_device *phydev) +{ + const struct device_node *np = phydev->mdio.dev.of_node; + struct kszphy_priv *priv = phydev->priv; + struct kszphy_latencies *latency = &priv->latencies; + u32 val; + + if (!of_property_read_u32(np, "lan8814,latency_rx_10", &val)) + latency->rx_10 = val; + if (!of_property_read_u32(np, "lan8814,latency_tx_10", &val)) + latency->tx_10 = val; + if (!of_property_read_u32(np, "lan8814,latency_rx_100", &val)) + latency->rx_100 = val; + if (!of_property_read_u32(np, "lan8814,latency_tx_100", &val)) + latency->tx_100 = val; + if (!of_property_read_u32(np, "lan8814,latency_rx_1000", &val)) + latency->rx_1000 = val; + if (!of_property_read_u32(np, "lan8814,latency_tx_1000", &val)) + latency->tx_1000 = val; +} + +static int lan8814_probe(struct phy_device *phydev) +{ + const struct device_node *np = phydev->mdio.dev.of_node; + struct kszphy_priv *priv; + u16 addr; + int err; + + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->led_mode = -1; + + priv->latencies = lan8814_latencies; + + phydev->priv = priv; + + if (!IS_ENABLED(CONFIG_PTP_1588_CLOCK) || + !IS_ENABLED(CONFIG_NETWORK_PHY_TIMESTAMPING) || + of_property_read_bool(np, "lan8814,ignore-ts")) + return 0; + + /* Strap-in value for PHY address, below register read gives starting + * phy address value + */ + addr = lanphy_read_page_reg(phydev, 4, 0) & 0x1F; + devm_phy_package_join(&phydev->mdio.dev, phydev, + addr, sizeof(struct lan8814_shared_priv)); + + if (phy_package_init_once(phydev)) { + err = lan8814_ptp_probe_once(phydev); + if (err) + return err; + } + + lan8814_parse_latency(phydev); + lan8814_ptp_init(phydev); + + return 0; +} + static struct phy_driver ksphy_driver[] = { { .phy_id = PHY_ID_KS8737, @@ -1890,10 +2926,9 @@ static struct phy_driver ksphy_driver[] = { .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Microchip INDY Gigabit Quad PHY", .config_init = lan8814_config_init, - .driver_data = &ksz9021_type, - .probe = kszphy_probe, + .probe = lan8814_probe, .soft_reset = genphy_soft_reset, - .read_status = ksz9031_read_status, + .read_status = lan8814_read_status, .get_sset_count = kszphy_get_sset_count, .get_strings = kszphy_get_strings, .get_stats = kszphy_get_stats, diff --git a/drivers/net/phy/microchip_t1.c b/drivers/net/phy/microchip_t1.c index bc50224d43dd..389df3f4293c 100644 --- a/drivers/net/phy/microchip_t1.c +++ b/drivers/net/phy/microchip_t1.c @@ -8,11 +8,17 @@ #include #include #include +#include + +#define PHY_ID_LAN87XX 0x0007c150 +#define PHY_ID_LAN937X 0x0007c180 /* External Register Control Register */ #define LAN87XX_EXT_REG_CTL (0x14) #define LAN87XX_EXT_REG_CTL_RD_CTL (0x1000) #define LAN87XX_EXT_REG_CTL_WR_CTL (0x0800) +#define LAN87XX_REG_BANK_SEL_MASK GENMASK(10, 8) +#define LAN87XX_REG_ADDR_MASK GENMASK(7, 0) /* External Register Read Data Register */ #define LAN87XX_EXT_REG_RD_DATA (0x15) @@ -37,6 +43,7 @@ #define PHYACC_ATTR_MODE_READ 0 #define PHYACC_ATTR_MODE_WRITE 1 #define PHYACC_ATTR_MODE_MODIFY 2 +#define PHYACC_ATTR_MODE_POLL 3 #define PHYACC_ATTR_BANK_SMI 0 #define PHYACC_ATTR_BANK_MISC 1 @@ -50,8 +57,33 @@ #define LAN87XX_CABLE_TEST_OPEN 1 #define LAN87XX_CABLE_TEST_SAME_SHORT 2 +/* T1 Registers */ +#define T1_AFE_PORT_CFG1_REG 0x0B +#define T1_POWER_DOWN_CONTROL_REG 0x1A +#define T1_SLV_FD_MULT_CFG_REG 0x18 +#define T1_CDR_CFG_PRE_LOCK_REG 0x05 +#define T1_CDR_CFG_POST_LOCK_REG 0x06 +#define T1_LCK_STG2_MUFACT_CFG_REG 0x1A +#define T1_LCK_STG3_MUFACT_CFG_REG 0x1B +#define T1_POST_LCK_MUFACT_CFG_REG 0x1C +#define T1_TX_RX_FIFO_CFG_REG 0x02 +#define T1_TX_LPF_FIR_CFG_REG 0x55 +#define T1_SQI_CONFIG_REG 0x2E +#define T1_MDIO_CONTROL2_REG 0x10 +#define T1_INTERRUPT_SOURCE_REG 0x18 +#define T1_INTERRUPT2_SOURCE_REG 0x08 +#define T1_EQ_FD_STG1_FRZ_CFG 0x69 +#define T1_EQ_FD_STG2_FRZ_CFG 0x6A +#define T1_EQ_FD_STG3_FRZ_CFG 0x6B +#define T1_EQ_FD_STG4_FRZ_CFG 0x6C +#define T1_EQ_WT_FD_LCK_FRZ_CFG 0x6D +#define T1_PST_EQ_LCK_STG1_FRZ_CFG 0x6E + +#define T1_MODE_STAT_REG 0x11 +#define T1_LINK_UP_MSK BIT(0) + #define DRIVER_AUTHOR "Nisar Sayed " -#define DRIVER_DESC "Microchip LAN87XX T1 PHY driver" +#define DRIVER_DESC "Microchip LAN87XX/LAN937x T1 PHY driver" struct access_ereg_val { u8 mode; @@ -61,6 +93,37 @@ struct access_ereg_val { u16 mask; }; +static int lan937x_dsp_workaround(struct phy_device *phydev, u16 ereg, u8 bank) +{ + u8 prev_bank; + int rc = 0; + u16 val; + + mutex_lock(&phydev->lock); + /* Read previous selected bank */ + rc = phy_read(phydev, LAN87XX_EXT_REG_CTL); + if (rc < 0) + goto out_unlock; + + /* store the prev_bank */ + prev_bank = FIELD_GET(LAN87XX_REG_BANK_SEL_MASK, rc); + + if (bank != prev_bank && bank == PHYACC_ATTR_BANK_DSP) { + val = ereg & ~LAN87XX_REG_ADDR_MASK; + + val &= ~LAN87XX_EXT_REG_CTL_WR_CTL; + val |= LAN87XX_EXT_REG_CTL_RD_CTL; + + /* access twice for DSP bank change,dummy access */ + rc = phy_write(phydev, LAN87XX_EXT_REG_CTL, val); + } + +out_unlock: + mutex_unlock(&phydev->lock); + + return rc; +} + static int access_ereg(struct phy_device *phydev, u8 mode, u8 bank, u8 offset, u16 val) { @@ -89,6 +152,13 @@ static int access_ereg(struct phy_device *phydev, u8 mode, u8 bank, ereg |= (bank << 8) | offset; + /* DSP bank access workaround for lan937x */ + if (phydev->phy_id == PHY_ID_LAN937X) { + rc = lan937x_dsp_workaround(phydev, ereg, bank); + if (rc < 0) + return rc; + } + rc = phy_write(phydev, LAN87XX_EXT_REG_CTL, ereg); if (rc < 0) return rc; @@ -117,6 +187,15 @@ static int access_ereg_modify_changed(struct phy_device *phydev, return rc; } +static int access_smi_poll_timeout(struct phy_device *phydev, + u8 offset, u16 mask, u16 clr) +{ + int val; + + return phy_read_poll_timeout(phydev, offset, val, (val & mask) == clr, + 150, 30000, true); +} + static int lan87xx_config_rgmii_delay(struct phy_device *phydev) { int rc; @@ -157,68 +236,159 @@ static int lan87xx_config_rgmii_delay(struct phy_device *phydev) static int lan87xx_phy_init(struct phy_device *phydev) { static const struct access_ereg_val init[] = { - /* TX Amplitude = 5 */ - {PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_AFE, 0x0B, - 0x000A, 0x001E}, - /* Clear SMI interrupts */ - {PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI, 0x18, - 0, 0}, - /* Clear MISC interrupts */ - {PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_MISC, 0x08, - 0, 0}, - /* Turn on TC10 Ring Oscillator (ROSC) */ - {PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_MISC, 0x20, - 0x0020, 0x0020}, - /* WUR Detect Length to 1.2uS, LPC Detect Length to 1.09uS */ - {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_PCS, 0x20, - 0x283C, 0}, - /* Wake_In Debounce Length to 39uS, Wake_Out Length to 79uS */ - {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x21, - 0x274F, 0}, - /* Enable Auto Wake Forward to Wake_Out, ROSC on, Sleep, - * and Wake_In to wake PHY - */ - {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x20, - 0x80A7, 0}, - /* Enable WUP Auto Fwd, Enable Wake on MDI, Wakeup Debouncer - * to 128 uS - */ - {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x24, - 0xF110, 0}, - /* Enable HW Init */ - {PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_SMI, 0x1A, - 0x0100, 0x0100}, + /* TXPD/TXAMP6 Configs */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_AFE, + T1_AFE_PORT_CFG1_REG, 0x002D, 0 }, + /* HW_Init Hi and Force_ED */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, + T1_POWER_DOWN_CONTROL_REG, 0x0308, 0 }, + /* Equalizer Full Duplex Freeze - T1 Slave */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_EQ_FD_STG1_FRZ_CFG, 0x0002, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_EQ_FD_STG2_FRZ_CFG, 0x0002, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_EQ_FD_STG3_FRZ_CFG, 0x0002, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_EQ_FD_STG4_FRZ_CFG, 0x0002, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_EQ_WT_FD_LCK_FRZ_CFG, 0x0002, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_PST_EQ_LCK_STG1_FRZ_CFG, 0x0002, 0 }, + /* Slave Full Duplex Multi Configs */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_SLV_FD_MULT_CFG_REG, 0x0D53, 0 }, + /* CDR Pre and Post Lock Configs */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_CDR_CFG_PRE_LOCK_REG, 0x0AB2, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_CDR_CFG_POST_LOCK_REG, 0x0AB3, 0 }, + /* Lock Stage 2-3 Multi Factor Config */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_LCK_STG2_MUFACT_CFG_REG, 0x0AEA, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_LCK_STG3_MUFACT_CFG_REG, 0x0AEB, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_POST_LCK_MUFACT_CFG_REG, 0x0AEB, 0 }, + /* Pointer delay */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_RX_FIFO_CFG_REG, 0x1C00, 0 }, + /* Tx iir edits */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1000, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1861, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1061, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1922, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1122, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1983, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1183, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1944, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1144, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x18c5, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x10c5, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1846, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1046, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1807, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1007, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1808, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1008, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1809, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1009, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x180A, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x100A, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x180B, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x100B, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x180C, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x100C, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x180D, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x100D, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x180E, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x100E, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x180F, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x100F, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1810, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1010, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1811, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1011, 0 }, + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_TX_LPF_FIR_CFG_REG, 0x1000, 0 }, + /* SQI enable */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, + T1_SQI_CONFIG_REG, 0x9572, 0 }, + /* Flag LPS and WUR as idle errors */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, + T1_MDIO_CONTROL2_REG, 0x0014, 0 }, + /* HW_Init toggle, undo force ED, TXPD off */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, + T1_POWER_DOWN_CONTROL_REG, 0x0200, 0 }, + /* Reset PCS to trigger hardware initialization */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, + T1_MDIO_CONTROL2_REG, 0x0094, 0 }, + /* Poll till Hardware is initialized */ + { PHYACC_ATTR_MODE_POLL, PHYACC_ATTR_BANK_SMI, + T1_MDIO_CONTROL2_REG, 0x0080, 0 }, + /* Tx AMP - 0x06 */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_AFE, + T1_AFE_PORT_CFG1_REG, 0x000C, 0 }, + /* Read INTERRUPT_SOURCE Register */ + { PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI, + T1_INTERRUPT_SOURCE_REG, 0, 0 }, + /* Read INTERRUPT_SOURCE Register */ + { PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_MISC, + T1_INTERRUPT2_SOURCE_REG, 0, 0 }, + /* HW_Init Hi */ + { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, + T1_POWER_DOWN_CONTROL_REG, 0x0300, 0 }, }; int rc, i; - /* Start manual initialization procedures in Managed Mode */ - rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI, - 0x1a, 0x0000, 0x0100); + /* phy Soft reset */ + rc = genphy_soft_reset(phydev); if (rc < 0) return rc; - /* Soft Reset the SMI block */ - rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI, - 0x00, 0x8000, 0x8000); - if (rc < 0) - return rc; - - /* Check to see if the self-clearing bit is cleared */ - usleep_range(1000, 2000); - rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, - PHYACC_ATTR_BANK_SMI, 0x00, 0); - if (rc < 0) - return rc; - if ((rc & 0x8000) != 0) - return -ETIMEDOUT; - /* PHY Initialization */ for (i = 0; i < ARRAY_SIZE(init); i++) { - if (init[i].mode == PHYACC_ATTR_MODE_MODIFY) { - rc = access_ereg_modify_changed(phydev, init[i].bank, - init[i].offset, - init[i].val, - init[i].mask); + if (init[i].mode == PHYACC_ATTR_MODE_POLL && + init[i].bank == PHYACC_ATTR_BANK_SMI) { + rc = access_smi_poll_timeout(phydev, + init[i].offset, + init[i].val, + init[i].mask); } else { rc = access_ereg(phydev, init[i].mode, init[i].bank, init[i].offset, init[i].val); @@ -504,22 +674,86 @@ static int lan87xx_cable_test_get_status(struct phy_device *phydev, return 0; } +static int lan87xx_read_status(struct phy_device *phydev) +{ + int rc = 0; + + rc = phy_read(phydev, T1_MODE_STAT_REG); + if (rc < 0) + return rc; + + if (rc & T1_LINK_UP_MSK) + phydev->link = 1; + else + phydev->link = 0; + + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; + + rc = genphy_read_master_slave(phydev); + if (rc < 0) + return rc; + + rc = genphy_read_status_fixed(phydev); + if (rc < 0) + return rc; + + return rc; +} + +static int lan87xx_config_aneg(struct phy_device *phydev) +{ + u16 ctl = 0; + int rc; + + switch (phydev->master_slave_set) { + case MASTER_SLAVE_CFG_MASTER_FORCE: + ctl |= CTL1000_AS_MASTER; + break; + case MASTER_SLAVE_CFG_SLAVE_FORCE: + break; + case MASTER_SLAVE_CFG_UNKNOWN: + case MASTER_SLAVE_CFG_UNSUPPORTED: + return 0; + default: + phydev_warn(phydev, "Unsupported Master/Slave mode\n"); + return -EOPNOTSUPP; + } + + rc = phy_modify_changed(phydev, MII_CTRL1000, CTL1000_AS_MASTER, ctl); + if (rc == 1) + rc = genphy_soft_reset(phydev); + + return rc; +} + static struct phy_driver microchip_t1_phy_driver[] = { { - .phy_id = 0x0007c150, - .phy_id_mask = 0xfffffff0, + PHY_ID_MATCH_MODEL(PHY_ID_LAN87XX), .name = "Microchip LAN87xx T1", .flags = PHY_POLL_CABLE_TEST, - .features = PHY_BASIC_T1_FEATURES, - .config_init = lan87xx_config_init, - .config_intr = lan87xx_phy_config_intr, .handle_interrupt = lan87xx_handle_interrupt, - .suspend = genphy_suspend, .resume = genphy_resume, + .config_aneg = lan87xx_config_aneg, + .read_status = lan87xx_read_status, + .cable_test_start = lan87xx_cable_test_start, + .cable_test_get_status = lan87xx_cable_test_get_status, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_LAN937X), + .name = "Microchip LAN937x T1", + .features = PHY_BASIC_T1_FEATURES, + .config_init = lan87xx_config_init, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = lan87xx_config_aneg, + .read_status = lan87xx_read_status, .cable_test_start = lan87xx_cable_test_start, .cable_test_get_status = lan87xx_cable_test_get_status, } @@ -528,7 +762,8 @@ static struct phy_driver microchip_t1_phy_driver[] = { module_phy_driver(microchip_t1_phy_driver); static struct mdio_device_id __maybe_unused microchip_t1_tbl[] = { - { 0x0007c150, 0xfffffff0 }, + { PHY_ID_MATCH_MODEL(PHY_ID_LAN87XX) }, + { PHY_ID_MATCH_MODEL(PHY_ID_LAN937X) }, { } }; diff --git a/drivers/net/phy/mscc/mscc_ptp.c b/drivers/net/phy/mscc/mscc_ptp.c index 34f829845d06..cf728bfd83e2 100644 --- a/drivers/net/phy/mscc/mscc_ptp.c +++ b/drivers/net/phy/mscc/mscc_ptp.c @@ -1212,7 +1212,7 @@ static bool vsc85xx_rxtstamp(struct mii_timestamper *mii_ts, ts.tv_sec--; shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ns); - netif_rx_ni(skb); + netif_rx(skb); return true; } diff --git a/drivers/net/phy/nxp-c45-tja11xx.c b/drivers/net/phy/nxp-c45-tja11xx.c index 06fdbae509a7..047c581457e3 100644 --- a/drivers/net/phy/nxp-c45-tja11xx.c +++ b/drivers/net/phy/nxp-c45-tja11xx.c @@ -478,7 +478,7 @@ static long nxp_c45_do_aux_work(struct ptp_clock_info *ptp) shhwtstamps_rx = skb_hwtstamps(skb); shhwtstamps_rx->hwtstamp = ns_to_ktime(timespec64_to_ns(&ts)); NXP_C45_SKB_CB(skb)->header->reserved2 = 0; - netif_rx_ni(skb); + netif_rx(skb); } if (priv->extts) { diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 271fc01f7f7f..2001f3329133 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -243,7 +243,7 @@ size_t phy_speeds(unsigned int *speeds, size_t size, return count; } -static int __set_linkmode_max_speed(u32 max_speed, unsigned long *addr) +static void __set_linkmode_max_speed(u32 max_speed, unsigned long *addr) { const struct phy_setting *p; int i; @@ -254,13 +254,11 @@ static int __set_linkmode_max_speed(u32 max_speed, unsigned long *addr) else break; } - - return 0; } -static int __set_phy_supported(struct phy_device *phydev, u32 max_speed) +static void __set_phy_supported(struct phy_device *phydev, u32 max_speed) { - return __set_linkmode_max_speed(max_speed, phydev->supported); + __set_linkmode_max_speed(max_speed, phydev->supported); } /** @@ -273,17 +271,11 @@ static int __set_phy_supported(struct phy_device *phydev, u32 max_speed) * is connected to a 1G PHY. This function allows the MAC to indicate its * maximum speed, and so limit what the PHY will advertise. */ -int phy_set_max_speed(struct phy_device *phydev, u32 max_speed) +void phy_set_max_speed(struct phy_device *phydev, u32 max_speed) { - int err; - - err = __set_phy_supported(phydev, max_speed); - if (err) - return err; + __set_phy_supported(phydev, max_speed); phy_advertise_supported(phydev); - - return 0; } EXPORT_SYMBOL(phy_set_max_speed); @@ -440,7 +432,9 @@ int phy_speed_down_core(struct phy_device *phydev) if (min_common_speed == SPEED_UNKNOWN) return -EINVAL; - return __set_linkmode_max_speed(min_common_speed, phydev->advertising); + __set_linkmode_max_speed(min_common_speed, phydev->advertising); + + return 0; } static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad, diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index ce0bb5951b81..8406ac739def 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -2051,17 +2051,11 @@ static int genphy_setup_master_slave(struct phy_device *phydev) CTL1000_PREFER_MASTER), ctl); } -static int genphy_read_master_slave(struct phy_device *phydev) +int genphy_read_master_slave(struct phy_device *phydev) { int cfg, state; int val; - if (!phydev->is_gigabit_capable) { - phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED; - phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; - return 0; - } - phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN; phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN; @@ -2102,6 +2096,7 @@ static int genphy_read_master_slave(struct phy_device *phydev) return 0; } +EXPORT_SYMBOL(genphy_read_master_slave); /** * genphy_restart_aneg - Enable and Restart Autonegotiation @@ -2396,14 +2391,18 @@ int genphy_read_status(struct phy_device *phydev) if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) return 0; + phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED; + phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; phydev->speed = SPEED_UNKNOWN; phydev->duplex = DUPLEX_UNKNOWN; phydev->pause = 0; phydev->asym_pause = 0; - err = genphy_read_master_slave(phydev); - if (err < 0) - return err; + if (phydev->is_gigabit_capable) { + err = genphy_read_master_slave(phydev); + if (err < 0) + return err; + } err = genphy_read_lpa(phydev); if (err < 0) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 420201858564..06943889d747 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -74,6 +74,7 @@ struct phylink { struct work_struct resolve; bool mac_link_dropped; + bool using_mac_select_pcs; struct sfp_bus *sfp_bus; bool sfp_may_have_phy; @@ -132,17 +133,6 @@ void phylink_set_port_modes(unsigned long *mask) } EXPORT_SYMBOL_GPL(phylink_set_port_modes); -void phylink_set_10g_modes(unsigned long *mask) -{ - phylink_set(mask, 10000baseT_Full); - phylink_set(mask, 10000baseCR_Full); - phylink_set(mask, 10000baseSR_Full); - phylink_set(mask, 10000baseLR_Full); - phylink_set(mask, 10000baseLRM_Full); - phylink_set(mask, 10000baseER_Full); -} -EXPORT_SYMBOL_GPL(phylink_set_10g_modes); - static int phylink_is_empty_linkmode(const unsigned long *linkmode) { __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp) = { 0, }; @@ -427,7 +417,7 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl, int ret; /* Get the PCS for this interface mode */ - if (pl->mac_ops->mac_select_pcs) { + if (pl->using_mac_select_pcs) { pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); if (IS_ERR(pcs)) return PTR_ERR(pcs); @@ -802,7 +792,7 @@ static void phylink_major_config(struct phylink *pl, bool restart, phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); - if (pl->mac_ops->mac_select_pcs) { + if (pl->using_mac_select_pcs) { pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); if (IS_ERR(pcs)) { phylink_err(pl, @@ -825,8 +815,18 @@ static void phylink_major_config(struct phylink *pl, bool restart, /* If we have a new PCS, switch to the new PCS after preparing the MAC * for the change. */ - if (pcs) - phylink_set_pcs(pl, pcs); + if (pcs) { + pl->pcs = pcs; + pl->pcs_ops = pcs->ops; + + if (!pl->phylink_disable_state && + pl->cfg_link_an_mode == MLO_AN_INBAND) { + if (pcs->poll) + mod_timer(&pl->link_poll, jiffies + HZ); + else + del_timer(&pl->link_poll); + } + } phylink_mac_config(pl, state); @@ -1182,9 +1182,8 @@ static int phylink_register_sfp(struct phylink *pl, bus = sfp_bus_find_fwnode(fwnode); if (IS_ERR(bus)) { - ret = PTR_ERR(bus); - phylink_err(pl, "unable to attach SFP bus: %d\n", ret); - return ret; + phylink_err(pl, "unable to attach SFP bus: %pe\n", bus); + return PTR_ERR(bus); } pl->sfp_bus = bus; @@ -1216,11 +1215,17 @@ struct phylink *phylink_create(struct phylink_config *config, phy_interface_t iface, const struct phylink_mac_ops *mac_ops) { + bool using_mac_select_pcs = false; struct phylink *pl; int ret; - /* Validate the supplied configuration */ if (mac_ops->mac_select_pcs && + mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) != + ERR_PTR(-EOPNOTSUPP)) + using_mac_select_pcs = true; + + /* Validate the supplied configuration */ + if (using_mac_select_pcs && phy_interface_empty(config->supported_interfaces)) { dev_err(config->dev, "phylink: error: empty supported_interfaces but mac_select_pcs() method present\n"); @@ -1244,6 +1249,7 @@ struct phylink *phylink_create(struct phylink_config *config, return ERR_PTR(-EINVAL); } + pl->using_mac_select_pcs = using_mac_select_pcs; pl->phy_state.interface = iface; pl->link_interface = iface; if (iface == PHY_INTERFACE_MODE_MOCA) @@ -1289,36 +1295,6 @@ struct phylink *phylink_create(struct phylink_config *config, } EXPORT_SYMBOL_GPL(phylink_create); -/** - * phylink_set_pcs() - set the current PCS for phylink to use - * @pl: a pointer to a &struct phylink returned from phylink_create() - * @pcs: a pointer to the &struct phylink_pcs - * - * Bind the MAC PCS to phylink. This may be called after phylink_create(). - * If it is desired to dynamically change the PCS, then the preferred method - * is to use mac_select_pcs(), but it may also be called in mac_prepare() - * or mac_config(). - * - * Please note that there are behavioural changes with the mac_config() - * callback if a PCS is present (denoting a newer setup) so removing a PCS - * is not supported, and if a PCS is going to be used, it must be registered - * by calling phylink_set_pcs() at the latest in the first mac_config() call. - */ -void phylink_set_pcs(struct phylink *pl, struct phylink_pcs *pcs) -{ - pl->pcs = pcs; - pl->pcs_ops = pcs->ops; - - if (!pl->phylink_disable_state && - pl->cfg_link_an_mode == MLO_AN_INBAND) { - if (pl->config->pcs_poll || pcs->poll) - mod_timer(&pl->link_poll, jiffies + HZ); - else - del_timer(&pl->link_poll); - } -} -EXPORT_SYMBOL_GPL(phylink_set_pcs); - /** * phylink_destroy() - cleanup and destroy the phylink instance * @pl: a pointer to a &struct phylink returned from phylink_create() @@ -1403,11 +1379,11 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, ret = phylink_validate(pl, supported, &config); if (ret) { - phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %d\n", + phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %pe\n", phy_modes(config.interface), __ETHTOOL_LINK_MODE_MASK_NBITS, phy->supported, __ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising, - ret); + ERR_PTR(ret)); return ret; } @@ -1684,7 +1660,6 @@ void phylink_start(struct phylink *pl) poll |= pl->config->poll_fixed_state; break; case MLO_AN_INBAND: - poll |= pl->config->pcs_poll; if (pl->pcs) poll |= pl->pcs->poll; break; @@ -2607,8 +2582,9 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode, /* Ignore errors if we're expecting a PHY to attach later */ ret = phylink_validate(pl, support, &config); if (ret) { - phylink_err(pl, "validation with support %*pb failed: %d\n", - __ETHTOOL_LINK_MODE_MASK_NBITS, support, ret); + phylink_err(pl, "validation with support %*pb failed: %pe\n", + __ETHTOOL_LINK_MODE_MASK_NBITS, support, + ERR_PTR(ret)); return ret; } @@ -2624,10 +2600,12 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode, linkmode_copy(support1, support); ret = phylink_validate(pl, support1, &config); if (ret) { - phylink_err(pl, "validation of %s/%s with support %*pb failed: %d\n", + phylink_err(pl, + "validation of %s/%s with support %*pb failed: %pe\n", phylink_an_mode_str(mode), phy_modes(config.interface), - __ETHTOOL_LINK_MODE_MASK_NBITS, support, ret); + __ETHTOOL_LINK_MODE_MASK_NBITS, support, + ERR_PTR(ret)); return ret; } diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index c1512c9925a6..15aa5ac1ff49 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -74,6 +74,12 @@ static const struct sfp_quirk sfp_quirks[] = { .vendor = "HUAWEI", .part = "MA5671A", .modes = sfp_quirk_2500basex, + }, { + // Lantech 8330-262D-E can operate at 2500base-X, but + // incorrectly report 2500MBd NRZ in their EEPROM + .vendor = "Lantech", + .part = "8330-262D-E", + .modes = sfp_quirk_2500basex, }, { .vendor = "UBNT", .part = "UF-INSTANT", diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 4720b24ca51b..4dfb79807823 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -471,8 +471,8 @@ static unsigned int sfp_soft_get_state(struct sfp *sfp) state |= SFP_F_TX_FAULT; } else { dev_err_ratelimited(sfp->dev, - "failed to read SFP soft status: %d\n", - ret); + "failed to read SFP soft status: %pe\n", + ERR_PTR(ret)); /* Preserve the current state */ state = sfp->state; } @@ -1311,7 +1311,8 @@ static void sfp_hwmon_probe(struct work_struct *work) mod_delayed_work(system_wq, &sfp->hwmon_probe, T_PROBE_RETRY_SLOW); } else { - dev_warn(sfp->dev, "hwmon probe failed: %d\n", err); + dev_warn(sfp->dev, "hwmon probe failed: %pe\n", + ERR_PTR(err)); } return; } @@ -1516,14 +1517,15 @@ static int sfp_sm_probe_phy(struct sfp *sfp, bool is_c45) if (phy == ERR_PTR(-ENODEV)) return PTR_ERR(phy); if (IS_ERR(phy)) { - dev_err(sfp->dev, "mdiobus scan returned %ld\n", PTR_ERR(phy)); + dev_err(sfp->dev, "mdiobus scan returned %pe\n", phy); return PTR_ERR(phy); } err = phy_device_register(phy); if (err) { phy_device_free(phy); - dev_err(sfp->dev, "phy_device_register failed: %d\n", err); + dev_err(sfp->dev, "phy_device_register failed: %pe\n", + ERR_PTR(err)); return err; } @@ -1531,7 +1533,7 @@ static int sfp_sm_probe_phy(struct sfp *sfp, bool is_c45) if (err) { phy_device_remove(phy); phy_device_free(phy); - dev_err(sfp->dev, "sfp_add_phy failed: %d\n", err); + dev_err(sfp->dev, "sfp_add_phy failed: %pe\n", ERR_PTR(err)); return err; } @@ -1708,7 +1710,7 @@ static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable) err = sfp_read(sfp, true, SFP_EXT_STATUS, &val, sizeof(val)); if (err != sizeof(val)) { - dev_err(sfp->dev, "Failed to read EEPROM: %d\n", err); + dev_err(sfp->dev, "Failed to read EEPROM: %pe\n", ERR_PTR(err)); return -EAGAIN; } @@ -1726,7 +1728,8 @@ static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable) err = sfp_write(sfp, true, SFP_EXT_STATUS, &val, sizeof(val)); if (err != sizeof(val)) { - dev_err(sfp->dev, "Failed to write EEPROM: %d\n", err); + dev_err(sfp->dev, "Failed to write EEPROM: %pe\n", + ERR_PTR(err)); return -EAGAIN; } @@ -1778,7 +1781,9 @@ static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id) id->base.connector = SFF8024_CONNECTOR_LC; err = sfp_write(sfp, false, SFP_PHYS_ID, &id->base, 3); if (err != 3) { - dev_err(sfp->dev, "Failed to rewrite module EEPROM: %d\n", err); + dev_err(sfp->dev, + "Failed to rewrite module EEPROM: %pe\n", + ERR_PTR(err)); return err; } @@ -1789,7 +1794,9 @@ static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id) check = sfp_check(&id->base, sizeof(id->base) - 1); err = sfp_write(sfp, false, SFP_CC_BASE, &check, 1); if (err != 1) { - dev_err(sfp->dev, "Failed to update base structure checksum in fiber module EEPROM: %d\n", err); + dev_err(sfp->dev, + "Failed to update base structure checksum in fiber module EEPROM: %pe\n", + ERR_PTR(err)); return err; } } @@ -1814,12 +1821,13 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) ret = sfp_read(sfp, false, 0, &id.base, sizeof(id.base)); if (ret < 0) { if (report) - dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret); + dev_err(sfp->dev, "failed to read EEPROM: %pe\n", + ERR_PTR(ret)); return -EAGAIN; } if (ret != sizeof(id.base)) { - dev_err(sfp->dev, "EEPROM short read: %d\n", ret); + dev_err(sfp->dev, "EEPROM short read: %pe\n", ERR_PTR(ret)); return -EAGAIN; } @@ -1839,13 +1847,15 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) ret = sfp_read(sfp, false, 0, &id.base, sizeof(id.base)); if (ret < 0) { if (report) - dev_err(sfp->dev, "failed to read EEPROM: %d\n", - ret); + dev_err(sfp->dev, + "failed to read EEPROM: %pe\n", + ERR_PTR(ret)); return -EAGAIN; } if (ret != sizeof(id.base)) { - dev_err(sfp->dev, "EEPROM short read: %d\n", ret); + dev_err(sfp->dev, "EEPROM short read: %pe\n", + ERR_PTR(ret)); return -EAGAIN; } } @@ -1887,12 +1897,13 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) ret = sfp_read(sfp, false, SFP_CC_BASE + 1, &id.ext, sizeof(id.ext)); if (ret < 0) { if (report) - dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret); + dev_err(sfp->dev, "failed to read EEPROM: %pe\n", + ERR_PTR(ret)); return -EAGAIN; } if (ret != sizeof(id.ext)) { - dev_err(sfp->dev, "EEPROM short read: %d\n", ret); + dev_err(sfp->dev, "EEPROM short read: %pe\n", ERR_PTR(ret)); return -EAGAIN; } @@ -2046,7 +2057,8 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event) err = sfp_hwmon_insert(sfp); if (err) - dev_warn(sfp->dev, "hwmon probe failed: %d\n", err); + dev_warn(sfp->dev, "hwmon probe failed: %pe\n", + ERR_PTR(err)); sfp_sm_mod_next(sfp, SFP_MOD_WAITDEV, 0); fallthrough; diff --git a/drivers/net/plip/plip.c b/drivers/net/plip/plip.c index 0d491b4d6667..dafd3e9ebbf8 100644 --- a/drivers/net/plip/plip.c +++ b/drivers/net/plip/plip.c @@ -676,7 +676,7 @@ plip_receive_packet(struct net_device *dev, struct net_local *nl, case PLIP_PK_DONE: /* Inform the upper layer for the arrival of a packet. */ rcv->skb->protocol=plip_type_trans(rcv->skb, dev); - netif_rx_ni(rcv->skb); + netif_rx(rcv->skb); dev->stats.rx_bytes += rcv->length.h; dev->stats.rx_packets++; rcv->skb = NULL; diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index 1a95f3beb784..39e61e07e489 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -109,7 +109,7 @@ static int rionet_rx_clean(struct net_device *ndev) skb_put(rnet->rx_skb[i], RIO_MAX_MSG_SIZE); rnet->rx_skb[i]->protocol = eth_type_trans(rnet->rx_skb[i], ndev); - error = netif_rx(rnet->rx_skb[i]); + error = __netif_rx(rnet->rx_skb[i]); if (error == NET_RX_DROP) { ndev->stats.rx_dropped++; diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index 57a6d598467b..c3f8020571ad 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -872,7 +872,7 @@ printk("cm0: IP identification: %02x%02x fragment offset: %02x%02x\n", buffer[3 /* datagram completed: send to upper level */ skb_trim(skb, dlen); - netif_rx(skb); + __netif_rx(skb); stats->rx_bytes+=dlen; stats->rx_packets++; lp->rx_skb[ns] = NULL; diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index 98f586f910fb..88396ff99f03 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -368,7 +368,7 @@ static void sl_bump(struct slip *sl) skb_put_data(skb, sl->rbuff, count); skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IP); - netif_rx_ni(skb); + netif_rx(skb); dev->stats.rx_packets++; } diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 8e3a28ba6b28..c3d42062559d 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -322,6 +322,7 @@ rx_handler_result_t tap_handle_frame(struct sk_buff **pskb) struct tap_dev *tap; struct tap_queue *q; netdev_features_t features = TAP_FEATURES; + enum skb_drop_reason drop_reason; tap = tap_dev_get_rcu(dev); if (!tap) @@ -343,12 +344,16 @@ rx_handler_result_t tap_handle_frame(struct sk_buff **pskb) struct sk_buff *segs = __skb_gso_segment(skb, features, false); struct sk_buff *next; - if (IS_ERR(segs)) + if (IS_ERR(segs)) { + drop_reason = SKB_DROP_REASON_SKB_GSO_SEG; goto drop; + } if (!segs) { - if (ptr_ring_produce(&q->ring, skb)) + if (ptr_ring_produce(&q->ring, skb)) { + drop_reason = SKB_DROP_REASON_FULL_RING; goto drop; + } goto wake_up; } @@ -356,8 +361,9 @@ rx_handler_result_t tap_handle_frame(struct sk_buff **pskb) skb_list_walk_safe(segs, skb, next) { skb_mark_not_on_list(skb); if (ptr_ring_produce(&q->ring, skb)) { - kfree_skb(skb); - kfree_skb_list(next); + drop_reason = SKB_DROP_REASON_FULL_RING; + kfree_skb_reason(skb, drop_reason); + kfree_skb_list_reason(next, drop_reason); break; } } @@ -369,10 +375,14 @@ rx_handler_result_t tap_handle_frame(struct sk_buff **pskb) */ if (skb->ip_summed == CHECKSUM_PARTIAL && !(features & NETIF_F_CSUM_MASK) && - skb_checksum_help(skb)) + skb_checksum_help(skb)) { + drop_reason = SKB_DROP_REASON_SKB_CSUM; goto drop; - if (ptr_ring_produce(&q->ring, skb)) + } + if (ptr_ring_produce(&q->ring, skb)) { + drop_reason = SKB_DROP_REASON_FULL_RING; goto drop; + } } wake_up: @@ -383,7 +393,7 @@ rx_handler_result_t tap_handle_frame(struct sk_buff **pskb) /* Count errors/drops only here, thus don't care about args. */ if (tap->count_rx_dropped) tap->count_rx_dropped(tap); - kfree_skb(skb); + kfree_skb_reason(skb, drop_reason); return RX_HANDLER_CONSUMED; } EXPORT_SYMBOL_GPL(tap_handle_frame); @@ -632,6 +642,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, int depth; bool zerocopy = false; size_t linear; + enum skb_drop_reason drop_reason; if (q->flags & IFF_VNET_HDR) { vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz); @@ -696,8 +707,10 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, else err = skb_copy_datagram_from_iter(skb, 0, from, len); - if (err) + if (err) { + drop_reason = SKB_DROP_REASON_SKB_UCOPY_FAULT; goto err_kfree; + } skb_set_network_header(skb, ETH_HLEN); skb_reset_mac_header(skb); @@ -706,8 +719,10 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, if (vnet_hdr_len) { err = virtio_net_hdr_to_skb(skb, &vnet_hdr, tap_is_little_endian(q)); - if (err) + if (err) { + drop_reason = SKB_DROP_REASON_DEV_HDR; goto err_kfree; + } } skb_probe_transport_header(skb); @@ -738,7 +753,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, return total_len; err_kfree: - kfree_skb(skb); + kfree_skb_reason(skb, drop_reason); err: rcu_read_lock(); @@ -1198,7 +1213,8 @@ static int tap_sendmsg(struct socket *sock, struct msghdr *m, struct xdp_buff *xdp; int i; - if (ctl && (ctl->type == TUN_MSG_PTR)) { + if (m->msg_controllen == sizeof(struct tun_msg_ctl) && + ctl && ctl->type == TUN_MSG_PTR) { for (i = 0; i < ctl->num; i++) { xdp = &((struct xdp_buff *)ctl->ptr)[i]; tap_get_user_xdp(q, xdp); diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 8b2adc56b92a..b07dde6f0abf 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -734,6 +734,11 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb) port = team_port_get_rcu(skb->dev); team = port->team; if (!team_port_enabled(port)) { + if (is_link_local_ether_addr(eth_hdr(skb)->h_dest)) + /* link-local packets are mostly useful when stack receives them + * with the link they arrive on. + */ + return RX_HANDLER_PASS; /* allow exact match delivery for disabled ports */ res = RX_HANDLER_EXACT; } else { diff --git a/drivers/net/tun.c b/drivers/net/tun.c index fed85447701a..276a0e42ca8e 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1058,6 +1058,7 @@ static unsigned int run_ebpf_filter(struct tun_struct *tun, static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) { struct tun_struct *tun = netdev_priv(dev); + enum skb_drop_reason drop_reason; int txq = skb->queue_mapping; struct netdev_queue *queue; struct tun_file *tfile; @@ -1067,8 +1068,10 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) tfile = rcu_dereference(tun->tfiles[txq]); /* Drop packet if interface is not attached */ - if (!tfile) + if (!tfile) { + drop_reason = SKB_DROP_REASON_DEV_READY; goto drop; + } if (!rcu_dereference(tun->steering_prog)) tun_automq_xmit(tun, skb); @@ -1078,19 +1081,32 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) /* Drop if the filter does not like it. * This is a noop if the filter is disabled. * Filter can be enabled only for the TAP devices. */ - if (!check_filter(&tun->txflt, skb)) + if (!check_filter(&tun->txflt, skb)) { + drop_reason = SKB_DROP_REASON_TAP_TXFILTER; goto drop; + } if (tfile->socket.sk->sk_filter && - sk_filter(tfile->socket.sk, skb)) + sk_filter(tfile->socket.sk, skb)) { + drop_reason = SKB_DROP_REASON_SOCKET_FILTER; goto drop; + } len = run_ebpf_filter(tun, skb, len); - if (len == 0 || pskb_trim(skb, len)) + if (len == 0) { + drop_reason = SKB_DROP_REASON_TAP_FILTER; goto drop; + } - if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC))) + if (pskb_trim(skb, len)) { + drop_reason = SKB_DROP_REASON_NOMEM; goto drop; + } + + if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC))) { + drop_reason = SKB_DROP_REASON_SKB_UCOPY_FAULT; + goto drop; + } skb_tx_timestamp(skb); @@ -1101,8 +1117,10 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) nf_reset_ct(skb); - if (ptr_ring_produce(&tfile->tx_ring, skb)) + if (ptr_ring_produce(&tfile->tx_ring, skb)) { + drop_reason = SKB_DROP_REASON_FULL_RING; goto drop; + } /* NETIF_F_LLTX requires to do our own update of trans_start */ queue = netdev_get_tx_queue(dev, txq); @@ -1117,9 +1135,9 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; drop: - atomic_long_inc(&dev->tx_dropped); + dev_core_stats_tx_dropped_inc(dev); skb_tx_error(skb); - kfree_skb(skb); + kfree_skb_reason(skb, drop_reason); rcu_read_unlock(); return NET_XMIT_DROP; } @@ -1273,7 +1291,7 @@ static int tun_xdp_xmit(struct net_device *dev, int n, void *frame = tun_xdp_to_ptr(xdp); if (__ptr_ring_produce(&tfile->tx_ring, frame)) { - atomic_long_inc(&dev->tx_dropped); + dev_core_stats_tx_dropped_inc(dev); break; } nxmit++; @@ -1608,7 +1626,7 @@ static int tun_xdp_act(struct tun_struct *tun, struct bpf_prog *xdp_prog, trace_xdp_exception(tun->dev, xdp_prog, act); fallthrough; case XDP_DROP: - atomic_long_inc(&tun->dev->rx_dropped); + dev_core_stats_rx_dropped_inc(tun->dev); break; } @@ -1717,6 +1735,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, u32 rxhash = 0; int skb_xdp = 1; bool frags = tun_napi_frags_enabled(tfile); + enum skb_drop_reason drop_reason; if (!(tun->flags & IFF_NO_PI)) { if (len < sizeof(pi)) @@ -1778,7 +1797,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, */ skb = tun_build_skb(tun, tfile, from, &gso, len, &skb_xdp); if (IS_ERR(skb)) { - atomic_long_inc(&tun->dev->rx_dropped); + dev_core_stats_rx_dropped_inc(tun->dev); return PTR_ERR(skb); } if (!skb) @@ -1807,7 +1826,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (IS_ERR(skb)) { if (PTR_ERR(skb) != -EAGAIN) - atomic_long_inc(&tun->dev->rx_dropped); + dev_core_stats_rx_dropped_inc(tun->dev); if (frags) mutex_unlock(&tfile->napi_mutex); return PTR_ERR(skb); @@ -1820,9 +1839,10 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (err) { err = -EFAULT; + drop_reason = SKB_DROP_REASON_SKB_UCOPY_FAULT; drop: - atomic_long_inc(&tun->dev->rx_dropped); - kfree_skb(skb); + dev_core_stats_rx_dropped_inc(tun->dev); + kfree_skb_reason(skb, drop_reason); if (frags) { tfile->napi.skb = NULL; mutex_unlock(&tfile->napi_mutex); @@ -1856,7 +1876,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, pi.proto = htons(ETH_P_IPV6); break; default: - atomic_long_inc(&tun->dev->rx_dropped); + dev_core_stats_rx_dropped_inc(tun->dev); kfree_skb(skb); return -EINVAL; } @@ -1869,6 +1889,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, case IFF_TAP: if (frags && !pskb_may_pull(skb, ETH_HLEN)) { err = -ENOMEM; + drop_reason = SKB_DROP_REASON_HDR_TRUNC; goto drop; } skb->protocol = eth_type_trans(skb, tun->dev); @@ -1922,6 +1943,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (unlikely(!(tun->dev->flags & IFF_UP))) { err = -EIO; rcu_read_unlock(); + drop_reason = SKB_DROP_REASON_DEV_READY; goto drop; } @@ -1934,7 +1956,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, skb_headlen(skb)); if (unlikely(headlen > skb_headlen(skb))) { - atomic_long_inc(&tun->dev->rx_dropped); + dev_core_stats_rx_dropped_inc(tun->dev); napi_free_frags(&tfile->napi); rcu_read_unlock(); mutex_unlock(&tfile->napi_mutex); @@ -1962,7 +1984,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } else if (!IS_ENABLED(CONFIG_4KSTACKS)) { tun_rx_batched(tun, tfile, skb, more); } else { - netif_rx_ni(skb); + netif_rx(skb); } rcu_read_unlock(); @@ -2388,9 +2410,10 @@ static int tun_xdp_one(struct tun_struct *tun, struct virtio_net_hdr *gso = &hdr->gso; struct bpf_prog *xdp_prog; struct sk_buff *skb = NULL; + struct sk_buff_head *queue; u32 rxhash = 0, act; int buflen = hdr->buflen; - int err = 0; + int ret = 0; bool skb_xdp = false; struct page *page; @@ -2405,13 +2428,13 @@ static int tun_xdp_one(struct tun_struct *tun, xdp_set_data_meta_invalid(xdp); act = bpf_prog_run_xdp(xdp_prog, xdp); - err = tun_xdp_act(tun, xdp_prog, xdp, act); - if (err < 0) { + ret = tun_xdp_act(tun, xdp_prog, xdp, act); + if (ret < 0) { put_page(virt_to_head_page(xdp->data)); - return err; + return ret; } - switch (err) { + switch (ret) { case XDP_REDIRECT: *flush = true; fallthrough; @@ -2435,7 +2458,7 @@ static int tun_xdp_one(struct tun_struct *tun, build: skb = build_skb(xdp->data_hard_start, buflen); if (!skb) { - err = -ENOMEM; + ret = -ENOMEM; goto out; } @@ -2445,7 +2468,7 @@ static int tun_xdp_one(struct tun_struct *tun, if (virtio_net_hdr_to_skb(skb, gso, tun_is_little_endian(tun))) { atomic_long_inc(&tun->rx_frame_errors); kfree_skb(skb); - err = -EINVAL; + ret = -EINVAL; goto out; } @@ -2455,16 +2478,27 @@ static int tun_xdp_one(struct tun_struct *tun, skb_record_rx_queue(skb, tfile->queue_index); if (skb_xdp) { - err = do_xdp_generic(xdp_prog, skb); - if (err != XDP_PASS) + ret = do_xdp_generic(xdp_prog, skb); + if (ret != XDP_PASS) { + ret = 0; goto out; + } } if (!rcu_dereference(tun->steering_prog) && tun->numqueues > 1 && !tfile->detached) rxhash = __skb_get_hash_symmetric(skb); - netif_receive_skb(skb); + if (tfile->napi_enabled) { + queue = &tfile->sk.sk_write_queue; + spin_lock(&queue->lock); + __skb_queue_tail(queue, skb); + spin_unlock(&queue->lock); + ret = 1; + } else { + netif_receive_skb(skb); + ret = 0; + } /* No need to disable preemption here since this function is * always called with bh disabled @@ -2475,7 +2509,7 @@ static int tun_xdp_one(struct tun_struct *tun, tun_flow_update(tun, rxhash, tfile); out: - return err; + return ret; } static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len) @@ -2489,10 +2523,11 @@ static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len) if (!tun) return -EBADFD; - if (ctl && (ctl->type == TUN_MSG_PTR)) { + if (m->msg_controllen == sizeof(struct tun_msg_ctl) && + ctl && ctl->type == TUN_MSG_PTR) { struct tun_page tpage; int n = ctl->num; - int flush = 0; + int flush = 0, queued = 0; memset(&tpage, 0, sizeof(tpage)); @@ -2501,12 +2536,17 @@ static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len) for (i = 0; i < n; i++) { xdp = &((struct xdp_buff *)ctl->ptr)[i]; - tun_xdp_one(tun, tfile, xdp, &flush, &tpage); + ret = tun_xdp_one(tun, tfile, xdp, &flush, &tpage); + if (ret > 0) + queued += ret; } if (flush) xdp_do_flush(); + if (tfile->napi_enabled && queued > 0) + napi_schedule(&tfile->napi); + rcu_read_unlock(); local_bh_enable(); diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index b554054a7560..e62fc4f2aee0 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -358,6 +358,7 @@ config USB_NET_SMSC95XX select BITREVERSE select CRC16 select CRC32 + imply NET_SELFTESTS help This option adds support for SMSC LAN95XX based USB 2.0 10/100 Ethernet adapters. diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h index 2a1e31defe71..2c81236c6c7c 100644 --- a/drivers/net/usb/asix.h +++ b/drivers/net/usb/asix.h @@ -158,6 +158,8 @@ #define AX_EEPROM_MAGIC 0xdeadbeef #define AX_EEPROM_LEN 0x200 +#define AX_EMBD_PHY_ADDR 0x10 + /* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */ struct asix_data { u8 multi_filter[AX_MCAST_FILTER_SIZE]; @@ -177,14 +179,16 @@ struct asix_rx_fixup_info { struct asix_common_private { void (*resume)(struct usbnet *dev); void (*suspend)(struct usbnet *dev); + int (*reset)(struct usbnet *dev, int in_pm); u16 presvd_phy_advertise; u16 presvd_phy_bmcr; struct asix_rx_fixup_info rx_fixup_info; struct mii_bus *mdio; struct phy_device *phydev; + struct phy_device *phydev_int; u16 phy_addr; - char phy_name[20]; bool embd_phy; + u8 chipcode; }; extern const struct driver_info ax88172a_info; @@ -192,8 +196,8 @@ extern const struct driver_info ax88172a_info; /* ASIX specific flags */ #define FLAG_EEPROM_MAC (1UL << 0) /* init device MAC from eeprom */ -int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, - u16 size, void *data, int in_pm); +int __must_check asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data, int in_pm); int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data, int in_pm); diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 71682970be58..632fa6c1d5e3 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -11,8 +11,8 @@ #define AX_HOST_EN_RETRIES 30 -int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, - u16 size, void *data, int in_pm) +int __must_check asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data, int in_pm) { int ret; int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); @@ -27,9 +27,12 @@ int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, data, size); - if (unlikely(ret < 0)) + if (unlikely(ret < size)) { + ret = ret < 0 ? ret : -ENODATA; + netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n", index, ret); + } return ret; } @@ -79,7 +82,7 @@ static int asix_check_host_enable(struct usbnet *dev, int in_pm) 0, 0, 1, &smsr, in_pm); if (ret == -ENODEV) break; - else if (ret < sizeof(smsr)) + else if (ret < 0) continue; else if (smsr & AX_HOST_EN) break; @@ -488,7 +491,8 @@ void asix_set_multicast(struct net_device *net) asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); } -int asix_mdio_read(struct net_device *netdev, int phy_id, int loc) +static int __asix_mdio_read(struct net_device *netdev, int phy_id, int loc, + bool in_pm) { struct usbnet *dev = netdev_priv(netdev); __le16 res; @@ -496,18 +500,18 @@ int asix_mdio_read(struct net_device *netdev, int phy_id, int loc) mutex_lock(&dev->phy_mutex); - ret = asix_check_host_enable(dev, 0); + ret = asix_check_host_enable(dev, in_pm); if (ret == -ENODEV || ret == -ETIMEDOUT) { mutex_unlock(&dev->phy_mutex); return ret; } ret = asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, - &res, 0); + &res, in_pm); if (ret < 0) goto out; - ret = asix_set_hw_mii(dev, 0); + ret = asix_set_hw_mii(dev, in_pm); out: mutex_unlock(&dev->phy_mutex); @@ -517,8 +521,13 @@ int asix_mdio_read(struct net_device *netdev, int phy_id, int loc) return ret < 0 ? ret : le16_to_cpu(res); } +int asix_mdio_read(struct net_device *netdev, int phy_id, int loc) +{ + return __asix_mdio_read(netdev, phy_id, loc, false); +} + static int __asix_mdio_write(struct net_device *netdev, int phy_id, int loc, - int val) + int val, bool in_pm) { struct usbnet *dev = netdev_priv(netdev); __le16 res = cpu_to_le16(val); @@ -529,16 +538,16 @@ static int __asix_mdio_write(struct net_device *netdev, int phy_id, int loc, mutex_lock(&dev->phy_mutex); - ret = asix_check_host_enable(dev, 0); + ret = asix_check_host_enable(dev, in_pm); if (ret == -ENODEV) goto out; ret = asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, - &res, 0); + &res, in_pm); if (ret < 0) goto out; - ret = asix_set_hw_mii(dev, 0); + ret = asix_set_hw_mii(dev, in_pm); out: mutex_unlock(&dev->phy_mutex); @@ -547,7 +556,7 @@ static int __asix_mdio_write(struct net_device *netdev, int phy_id, int loc, void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val) { - __asix_mdio_write(netdev, phy_id, loc, val); + __asix_mdio_write(netdev, phy_id, loc, val, false); } /* MDIO read and write wrappers for phylib */ @@ -555,63 +564,25 @@ int asix_mdio_bus_read(struct mii_bus *bus, int phy_id, int regnum) { struct usbnet *priv = bus->priv; - return asix_mdio_read(priv->net, phy_id, regnum); + return __asix_mdio_read(priv->net, phy_id, regnum, false); } int asix_mdio_bus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val) { struct usbnet *priv = bus->priv; - return __asix_mdio_write(priv->net, phy_id, regnum, val); + return __asix_mdio_write(priv->net, phy_id, regnum, val, false); } int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc) { - struct usbnet *dev = netdev_priv(netdev); - __le16 res; - int ret; - - mutex_lock(&dev->phy_mutex); - - ret = asix_check_host_enable(dev, 1); - if (ret == -ENODEV || ret == -ETIMEDOUT) { - mutex_unlock(&dev->phy_mutex); - return ret; - } - - asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, - (__u16)loc, 2, &res, 1); - asix_set_hw_mii(dev, 1); - mutex_unlock(&dev->phy_mutex); - - netdev_dbg(dev->net, "asix_mdio_read_nopm() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", - phy_id, loc, le16_to_cpu(res)); - - return le16_to_cpu(res); + return __asix_mdio_read(netdev, phy_id, loc, true); } void asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc, int val) { - struct usbnet *dev = netdev_priv(netdev); - __le16 res = cpu_to_le16(val); - int ret; - - netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", - phy_id, loc, val); - - mutex_lock(&dev->phy_mutex); - - ret = asix_check_host_enable(dev, 1); - if (ret == -ENODEV) { - mutex_unlock(&dev->phy_mutex); - return; - } - - asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, - (__u16)loc, 2, &res, 1); - asix_set_hw_mii(dev, 1); - mutex_unlock(&dev->phy_mutex); + __asix_mdio_write(netdev, phy_id, loc, val, true); } void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 4514d35ef4c4..38e47a93fb83 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -450,7 +450,6 @@ static int ax88772a_hw_reset(struct usbnet *dev, int in_pm) struct asix_data *data = (struct asix_data *)&dev->data; struct asix_common_private *priv = dev->driver_priv; u16 rx_ctl, phy14h, phy15h, phy16h; - u8 chipcode = 0; int ret; ret = asix_write_gpio(dev, AX_GPIO_RSE, 5, in_pm); @@ -493,12 +492,7 @@ static int ax88772a_hw_reset(struct usbnet *dev, int in_pm) goto out; } - ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, - 0, 1, &chipcode, in_pm); - if (ret < 0) - goto out; - - if ((chipcode & AX_CHIPCODE_MASK) == AX_AX88772B_CHIPCODE) { + if (priv->chipcode == AX_AX88772B_CHIPCODE) { ret = asix_write_cmd(dev, AX_QCTCTRL, 0x8000, 0x8001, 0, NULL, in_pm); if (ret < 0) { @@ -506,7 +500,7 @@ static int ax88772a_hw_reset(struct usbnet *dev, int in_pm) ret); goto out; } - } else if ((chipcode & AX_CHIPCODE_MASK) == AX_AX88772A_CHIPCODE) { + } else if (priv->chipcode == AX_AX88772A_CHIPCODE) { /* Check if the PHY registers have default settings */ phy14h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id, AX88772A_PHY14H); @@ -625,27 +619,13 @@ static void ax88772_resume(struct usbnet *dev) int i; for (i = 0; i < 3; i++) - if (!ax88772_hw_reset(dev, 1)) + if (!priv->reset(dev, 1)) break; if (netif_running(dev->net)) phy_start(priv->phydev); } -static void ax88772a_resume(struct usbnet *dev) -{ - struct asix_common_private *priv = dev->driver_priv; - int i; - - for (i = 0; i < 3; i++) { - if (!ax88772a_hw_reset(dev, 1)) - break; - } - - if (netif_running(dev->net)) - phy_start(priv->phydev); -} - static int asix_resume(struct usb_interface *intf) { struct usbnet *dev = usb_get_intfdata(intf); @@ -681,15 +661,16 @@ static int ax88772_init_phy(struct usbnet *dev) struct asix_common_private *priv = dev->driver_priv; int ret; - snprintf(priv->phy_name, sizeof(priv->phy_name), PHY_ID_FMT, - priv->mdio->id, priv->phy_addr); + priv->phydev = mdiobus_get_phy(priv->mdio, priv->phy_addr); + if (!priv->phydev) { + netdev_err(dev->net, "Could not find PHY\n"); + return -ENODEV; + } - priv->phydev = phy_connect(dev->net, priv->phy_name, &asix_adjust_link, - PHY_INTERFACE_MODE_INTERNAL); - if (IS_ERR(priv->phydev)) { - netdev_err(dev->net, "Could not connect to PHY device %s\n", - priv->phy_name); - ret = PTR_ERR(priv->phydev); + ret = phy_connect_direct(dev->net, priv->phydev, &asix_adjust_link, + PHY_INTERFACE_MODE_INTERNAL); + if (ret) { + netdev_err(dev->net, "Could not connect PHY\n"); return ret; } @@ -698,13 +679,29 @@ static int ax88772_init_phy(struct usbnet *dev) phy_attached_info(priv->phydev); + if (priv->embd_phy) + return 0; + + /* In case main PHY is not the embedded PHY and MAC is RMII clock + * provider, we need to suspend embedded PHY by keeping PLL enabled + * (AX_SWRESET_IPPD == 0). + */ + priv->phydev_int = mdiobus_get_phy(priv->mdio, AX_EMBD_PHY_ADDR); + if (!priv->phydev_int) { + netdev_err(dev->net, "Could not find internal PHY\n"); + return -ENODEV; + } + + priv->phydev_int->mac_managed_pm = 1; + phy_suspend(priv->phydev_int); + return 0; } static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) { - u8 buf[ETH_ALEN] = {0}, chipcode = 0; struct asix_common_private *priv; + u8 buf[ETH_ALEN] = {0}; int ret, i; priv = devm_kzalloc(&dev->udev->dev, sizeof(*priv), GFP_KERNEL); @@ -753,14 +750,25 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) return ret; priv->phy_addr = ret; - priv->embd_phy = ((priv->phy_addr & 0x1f) == 0x10); + priv->embd_phy = ((priv->phy_addr & 0x1f) == AX_EMBD_PHY_ADDR); - asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &chipcode, 0); - chipcode &= AX_CHIPCODE_MASK; + ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, + &priv->chipcode, 0); + if (ret < 0) { + netdev_dbg(dev->net, "Failed to read STATMNGSTS_REG: %d\n", ret); + return ret; + } - ret = (chipcode == AX_AX88772_CHIPCODE) ? ax88772_hw_reset(dev, 0) : - ax88772a_hw_reset(dev, 0); + priv->chipcode &= AX_CHIPCODE_MASK; + priv->resume = ax88772_resume; + priv->suspend = ax88772_suspend; + if (priv->chipcode == AX_AX88772_CHIPCODE) + priv->reset = ax88772_hw_reset; + else + priv->reset = ax88772a_hw_reset; + + ret = priv->reset(dev, 0); if (ret < 0) { netdev_dbg(dev->net, "Failed to reset AX88772: %d\n", ret); return ret; @@ -775,13 +783,6 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) priv->presvd_phy_bmcr = 0; priv->presvd_phy_advertise = 0; - if (chipcode == AX_AX88772_CHIPCODE) { - priv->resume = ax88772_resume; - priv->suspend = ax88772_suspend; - } else { - priv->resume = ax88772a_resume; - priv->suspend = ax88772_suspend; - } ret = ax88772_init_mdio(dev); if (ret) @@ -858,7 +859,6 @@ static int marvell_phy_init(struct usbnet *dev) reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_MARVELL_LED_CTRL); netdev_dbg(dev->net, "MII_MARVELL_LED_CTRL (2) = 0x%04x\n", reg); - reg &= 0xfc0f; } return 0; @@ -920,11 +920,21 @@ static int ax88178_reset(struct usbnet *dev) int gpio0 = 0; u32 phyid; - asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status, 0); + ret = asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status, 0); + if (ret < 0) { + netdev_dbg(dev->net, "Failed to read GPIOS: %d\n", ret); + return ret; + } + netdev_dbg(dev->net, "GPIO Status: 0x%04x\n", status); asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL, 0); - asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom, 0); + ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom, 0); + if (ret < 0) { + netdev_dbg(dev->net, "Failed to read EEPROM: %d\n", ret); + return ret; + } + asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL, 0); netdev_dbg(dev->net, "EEPROM index 0x17 is 0x%04x\n", eeprom); diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index c0b8b4aa78f3..c89639381eca 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -21,6 +21,7 @@ #include #include #include +#include /* alternative VLAN for IP session 0 if not untagged */ #define MBIM_IPS0_VID 4094 diff --git a/drivers/net/usb/gl620a.c b/drivers/net/usb/gl620a.c index 13a9a83b8538..46af78caf457 100644 --- a/drivers/net/usb/gl620a.c +++ b/drivers/net/usb/gl620a.c @@ -56,7 +56,7 @@ struct gl_packet { __le32 packet_length; - char packet_data [1]; + char packet_data[]; }; struct gl_header { diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index f97813a4e8d1..f8221a7acf62 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2319,7 +2319,7 @@ static struct hso_device *hso_create_device(struct usb_interface *intf, { struct hso_device *hso_dev; - hso_dev = kzalloc(sizeof(*hso_dev), GFP_ATOMIC); + hso_dev = kzalloc(sizeof(*hso_dev), GFP_KERNEL); if (!hso_dev) return NULL; diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index a0f29482294d..4ef61f6b85df 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -20,6 +20,8 @@ #include #include #include +#include + #include "smsc95xx.h" #define SMSC_CHIPNAME "smsc95xx" @@ -739,6 +741,26 @@ static u32 smsc95xx_get_link(struct net_device *net) return net->phydev->link; } +static void smsc95xx_ethtool_get_strings(struct net_device *netdev, u32 sset, + u8 *data) +{ + switch (sset) { + case ETH_SS_TEST: + net_selftest_get_strings(data); + break; + } +} + +static int smsc95xx_ethtool_get_sset_count(struct net_device *ndev, int sset) +{ + switch (sset) { + case ETH_SS_TEST: + return net_selftest_get_count(); + default: + return -EOPNOTSUPP; + } +} + static const struct ethtool_ops smsc95xx_ethtool_ops = { .get_link = smsc95xx_get_link, .nway_reset = phy_ethtool_nway_reset, @@ -755,6 +777,9 @@ static const struct ethtool_ops smsc95xx_ethtool_ops = { .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, .get_ts_info = ethtool_op_get_ts_info, + .self_test = net_selftest, + .get_strings = smsc95xx_ethtool_get_strings, + .get_sset_count = smsc95xx_ethtool_get_sset_count, }; static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index d29fb9759cc9..1b5714926d81 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -287,7 +287,7 @@ static int veth_forward_skb(struct net_device *dev, struct sk_buff *skb, { return __dev_forward_skb(dev, skb) ?: xdp ? veth_xdp_rx(rq, skb) : - netif_rx(skb); + __netif_rx(skb); } /* return true if the specified skb has chances of GRO aggregation @@ -433,21 +433,6 @@ static void veth_set_multicast_list(struct net_device *dev) { } -static struct sk_buff *veth_build_skb(void *head, int headroom, int len, - int buflen) -{ - struct sk_buff *skb; - - skb = build_skb(head, buflen); - if (!skb) - return NULL; - - skb_reserve(skb, headroom); - skb_put(skb, len); - - return skb; -} - static int veth_select_rxq(struct net_device *dev) { return smp_processor_id() % dev->real_num_rx_queues; @@ -494,7 +479,7 @@ static int veth_xdp_xmit(struct net_device *dev, int n, struct xdp_frame *frame = frames[i]; void *ptr = veth_xdp_to_ptr(frame); - if (unlikely(frame->len > max_len || + if (unlikely(xdp_get_frame_len(frame) > max_len || __ptr_ring_produce(&rq->xdp_ring, ptr))) break; nxmit++; @@ -695,16 +680,130 @@ static void veth_xdp_rcv_bulk_skb(struct veth_rq *rq, void **frames, } } +static void veth_xdp_get(struct xdp_buff *xdp) +{ + struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); + int i; + + get_page(virt_to_page(xdp->data)); + if (likely(!xdp_buff_has_frags(xdp))) + return; + + for (i = 0; i < sinfo->nr_frags; i++) + __skb_frag_ref(&sinfo->frags[i]); +} + +static int veth_convert_skb_to_xdp_buff(struct veth_rq *rq, + struct xdp_buff *xdp, + struct sk_buff **pskb) +{ + struct sk_buff *skb = *pskb; + u32 frame_sz; + + if (skb_shared(skb) || skb_head_is_locked(skb) || + skb_shinfo(skb)->nr_frags) { + u32 size, len, max_head_size, off; + struct sk_buff *nskb; + struct page *page; + int i, head_off; + + /* We need a private copy of the skb and data buffers since + * the ebpf program can modify it. We segment the original skb + * into order-0 pages without linearize it. + * + * Make sure we have enough space for linear and paged area + */ + max_head_size = SKB_WITH_OVERHEAD(PAGE_SIZE - + VETH_XDP_HEADROOM); + if (skb->len > PAGE_SIZE * MAX_SKB_FRAGS + max_head_size) + goto drop; + + /* Allocate skb head */ + page = alloc_page(GFP_ATOMIC | __GFP_NOWARN); + if (!page) + goto drop; + + nskb = build_skb(page_address(page), PAGE_SIZE); + if (!nskb) { + put_page(page); + goto drop; + } + + skb_reserve(nskb, VETH_XDP_HEADROOM); + size = min_t(u32, skb->len, max_head_size); + if (skb_copy_bits(skb, 0, nskb->data, size)) { + consume_skb(nskb); + goto drop; + } + skb_put(nskb, size); + + skb_copy_header(nskb, skb); + head_off = skb_headroom(nskb) - skb_headroom(skb); + skb_headers_offset_update(nskb, head_off); + + /* Allocate paged area of new skb */ + off = size; + len = skb->len - off; + + for (i = 0; i < MAX_SKB_FRAGS && off < skb->len; i++) { + page = alloc_page(GFP_ATOMIC | __GFP_NOWARN); + if (!page) { + consume_skb(nskb); + goto drop; + } + + size = min_t(u32, len, PAGE_SIZE); + skb_add_rx_frag(nskb, i, page, 0, size, PAGE_SIZE); + if (skb_copy_bits(skb, off, page_address(page), + size)) { + consume_skb(nskb); + goto drop; + } + + len -= size; + off += size; + } + + consume_skb(skb); + skb = nskb; + } else if (skb_headroom(skb) < XDP_PACKET_HEADROOM && + pskb_expand_head(skb, VETH_XDP_HEADROOM, 0, GFP_ATOMIC)) { + goto drop; + } + + /* SKB "head" area always have tailroom for skb_shared_info */ + frame_sz = skb_end_pointer(skb) - skb->head; + frame_sz += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + xdp_init_buff(xdp, frame_sz, &rq->xdp_rxq); + xdp_prepare_buff(xdp, skb->head, skb_headroom(skb), + skb_headlen(skb), true); + + if (skb_is_nonlinear(skb)) { + skb_shinfo(skb)->xdp_frags_size = skb->data_len; + xdp_buff_set_frags_flag(xdp); + } else { + xdp_buff_clear_frags_flag(xdp); + } + *pskb = skb; + + return 0; +drop: + consume_skb(skb); + *pskb = NULL; + + return -ENOMEM; +} + static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, struct sk_buff *skb, struct veth_xdp_tx_bq *bq, struct veth_stats *stats) { - u32 pktlen, headroom, act, metalen, frame_sz; void *orig_data, *orig_data_end; struct bpf_prog *xdp_prog; - int mac_len, delta, off; struct xdp_buff xdp; + u32 act, metalen; + int off; skb_prepare_for_gro(skb); @@ -715,52 +814,9 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, goto out; } - mac_len = skb->data - skb_mac_header(skb); - pktlen = skb->len + mac_len; - headroom = skb_headroom(skb) - mac_len; - - if (skb_shared(skb) || skb_head_is_locked(skb) || - skb_is_nonlinear(skb) || headroom < XDP_PACKET_HEADROOM) { - struct sk_buff *nskb; - int size, head_off; - void *head, *start; - struct page *page; - - size = SKB_DATA_ALIGN(VETH_XDP_HEADROOM + pktlen) + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - if (size > PAGE_SIZE) - goto drop; - - page = alloc_page(GFP_ATOMIC | __GFP_NOWARN); - if (!page) - goto drop; - - head = page_address(page); - start = head + VETH_XDP_HEADROOM; - if (skb_copy_bits(skb, -mac_len, start, pktlen)) { - page_frag_free(head); - goto drop; - } - - nskb = veth_build_skb(head, VETH_XDP_HEADROOM + mac_len, - skb->len, PAGE_SIZE); - if (!nskb) { - page_frag_free(head); - goto drop; - } - - skb_copy_header(nskb, skb); - head_off = skb_headroom(nskb) - skb_headroom(skb); - skb_headers_offset_update(nskb, head_off); - consume_skb(skb); - skb = nskb; - } - - /* SKB "head" area always have tailroom for skb_shared_info */ - frame_sz = skb_end_pointer(skb) - skb->head; - frame_sz += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - xdp_init_buff(&xdp, frame_sz, &rq->xdp_rxq); - xdp_prepare_buff(&xdp, skb->head, skb->mac_header, pktlen, true); + __skb_push(skb, skb->data - skb_mac_header(skb)); + if (veth_convert_skb_to_xdp_buff(rq, &xdp, &skb)) + goto drop; orig_data = xdp.data; orig_data_end = xdp.data_end; @@ -771,7 +827,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, case XDP_PASS: break; case XDP_TX: - get_page(virt_to_page(xdp.data)); + veth_xdp_get(&xdp); consume_skb(skb); xdp.rxq->mem = rq->xdp_mem; if (unlikely(veth_xdp_tx(rq, &xdp, bq) < 0)) { @@ -783,7 +839,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, rcu_read_unlock(); goto xdp_xmit; case XDP_REDIRECT: - get_page(virt_to_page(xdp.data)); + veth_xdp_get(&xdp); consume_skb(skb); xdp.rxq->mem = rq->xdp_mem; if (xdp_do_redirect(rq->dev, &xdp, xdp_prog)) { @@ -806,18 +862,27 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, rcu_read_unlock(); /* check if bpf_xdp_adjust_head was used */ - delta = orig_data - xdp.data; - off = mac_len + delta; + off = orig_data - xdp.data; if (off > 0) __skb_push(skb, off); else if (off < 0) __skb_pull(skb, -off); - skb->mac_header -= delta; + + skb_reset_mac_header(skb); /* check if bpf_xdp_adjust_tail was used */ off = xdp.data_end - orig_data_end; if (off != 0) __skb_put(skb, off); /* positive on grow, negative on shrink */ + + /* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers + * (e.g. bpf_xdp_adjust_tail), we need to update data_len here. + */ + if (xdp_buff_has_frags(&xdp)) + skb->data_len = skb_shinfo(skb)->xdp_frags_size; + else + skb->data_len = 0; + skb->protocol = eth_type_trans(skb, rq->dev); metalen = xdp.data - xdp.data_meta; @@ -833,7 +898,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, return NULL; err_xdp: rcu_read_unlock(); - page_frag_free(xdp.data); + xdp_return_buff(&xdp); xdp_xmit: return NULL; } @@ -855,7 +920,7 @@ static int veth_xdp_rcv(struct veth_rq *rq, int budget, /* ndo_xdp_xmit */ struct xdp_frame *frame = veth_ptr_to_xdp(ptr); - stats->xdp_bytes += frame->len; + stats->xdp_bytes += xdp_get_frame_len(frame); frame = veth_xdp_rcv_one(rq, frame, bq, stats); if (frame) { /* XDP_PASS */ @@ -1463,9 +1528,14 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog, goto err; } - max_mtu = PAGE_SIZE - VETH_XDP_HEADROOM - - peer->hard_header_len - - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + max_mtu = SKB_WITH_OVERHEAD(PAGE_SIZE - VETH_XDP_HEADROOM) - + peer->hard_header_len; + /* Allow increasing the max_mtu if the program supports + * XDP fragments. + */ + if (prog->aux->xdp_has_frags) + max_mtu += PAGE_SIZE * MAX_SKB_FRAGS; + if (peer->mtu > max_mtu) { NL_SET_ERR_MSG_MOD(extack, "Peer MTU is too large to set XDP"); err = -ERANGE; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index a801ea40908f..11f26b00a226 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -3449,8 +3449,7 @@ static __init int virtio_net_driver_init(void) NULL, virtnet_cpu_dead); if (ret) goto err_dead; - - ret = register_virtio_driver(&virtio_net_driver); + ret = register_virtio_driver(&virtio_net_driver); if (ret) goto err_virtio; return 0; diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index e0b1ab99a359..85e362461d71 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -418,7 +418,7 @@ static int vrf_local_xmit(struct sk_buff *skb, struct net_device *dev, skb->protocol = eth_type_trans(skb, dev); - if (likely(netif_rx(skb) == NET_RX_SUCCESS)) + if (likely(__netif_rx(skb) == NET_RX_SUCCESS)) vrf_rx_stats(dev, len); else this_cpu_inc(dev->dstats->rx_drps); @@ -472,14 +472,13 @@ static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb, memset(&fl6, 0, sizeof(fl6)); /* needed to match OIF rule */ - fl6.flowi6_oif = dev->ifindex; + fl6.flowi6_l3mdev = dev->ifindex; fl6.flowi6_iif = LOOPBACK_IFINDEX; fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; fl6.flowlabel = ip6_flowinfo(iph); fl6.flowi6_mark = skb->mark; fl6.flowi6_proto = iph->nexthdr; - fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF; dst = ip6_dst_lookup_flow(net, NULL, &fl6, NULL); if (IS_ERR(dst) || dst == dst_null) @@ -551,10 +550,10 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb, memset(&fl4, 0, sizeof(fl4)); /* needed to match OIF rule */ - fl4.flowi4_oif = vrf_dev->ifindex; + fl4.flowi4_l3mdev = vrf_dev->ifindex; fl4.flowi4_iif = LOOPBACK_IFINDEX; fl4.flowi4_tos = RT_TOS(ip4h->tos); - fl4.flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_SKIP_NH_OIF; + fl4.flowi4_flags = FLOWI_FLAG_ANYSRC; fl4.flowi4_proto = ip4h->protocol; fl4.daddr = ip4h->daddr; fl4.saddr = ip4h->saddr; diff --git a/drivers/net/vxlan/Makefile b/drivers/net/vxlan/Makefile new file mode 100644 index 000000000000..d4c255499b72 --- /dev/null +++ b/drivers/net/vxlan/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the vxlan driver +# + +obj-$(CONFIG_VXLAN) += vxlan.o + +vxlan-objs := vxlan_core.o vxlan_multicast.o vxlan_vnifilter.o diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan/vxlan_core.c similarity index 93% rename from drivers/net/vxlan.c rename to drivers/net/vxlan/vxlan_core.c index 359d16780dbb..de97ff98d36e 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -34,10 +34,10 @@ #include #endif +#include "vxlan_private.h" + #define VXLAN_VERSION "0.1" -#define PORT_HASH_BITS 8 -#define PORT_HASH_SIZE (1<sa.sa_family != b->sa.sa_family) - return false; - if (a->sa.sa_family == AF_INET6) - return ipv6_addr_equal(&a->sin6.sin6_addr, &b->sin6.sin6_addr); - else - return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr; -} - static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla) { if (nla_len(nla) >= sizeof(struct in6_addr)) { @@ -135,12 +98,6 @@ static int vxlan_nla_put_addr(struct sk_buff *skb, int attr, #else /* !CONFIG_IPV6 */ -static inline -bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b) -{ - return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr; -} - static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla) { if (nla_len(nla) >= sizeof(struct in6_addr)) { @@ -161,37 +118,6 @@ static int vxlan_nla_put_addr(struct sk_buff *skb, int attr, } #endif -/* Virtual Network hash table head */ -static inline struct hlist_head *vni_head(struct vxlan_sock *vs, __be32 vni) -{ - return &vs->vni_list[hash_32((__force u32)vni, VNI_HASH_BITS)]; -} - -/* Socket hash table head */ -static inline struct hlist_head *vs_head(struct net *net, __be16 port) -{ - struct vxlan_net *vn = net_generic(net, vxlan_net_id); - - return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)]; -} - -/* First remote destination for a forwarding entry. - * Guaranteed to be non-NULL because remotes are never deleted. - */ -static inline struct vxlan_rdst *first_remote_rcu(struct vxlan_fdb *fdb) -{ - if (rcu_access_pointer(fdb->nh)) - return NULL; - return list_entry_rcu(fdb->remotes.next, struct vxlan_rdst, list); -} - -static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb) -{ - if (rcu_access_pointer(fdb->nh)) - return NULL; - return list_first_entry(&fdb->remotes, struct vxlan_rdst, list); -} - /* Find VXLAN socket based on network namespace, address family, UDP port, * enabled unshareable flags and socket device binding (see l3mdev with * non-default VRF). @@ -213,18 +139,29 @@ static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family, return NULL; } -static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, int ifindex, - __be32 vni) +static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, + int ifindex, __be32 vni, + struct vxlan_vni_node **vninode) { + struct vxlan_vni_node *vnode; struct vxlan_dev_node *node; /* For flow based devices, map all packets to VNI 0 */ - if (vs->flags & VXLAN_F_COLLECT_METADATA) + if (vs->flags & VXLAN_F_COLLECT_METADATA && + !(vs->flags & VXLAN_F_VNIFILTER)) vni = 0; hlist_for_each_entry_rcu(node, vni_head(vs, vni), hlist) { - if (node->vxlan->default_dst.remote_vni != vni) + if (!node->vxlan) continue; + vnode = NULL; + if (node->vxlan->cfg.flags & VXLAN_F_VNIFILTER) { + vnode = vxlan_vnifilter_lookup(node->vxlan, vni); + if (!vnode) + continue; + } else if (node->vxlan->default_dst.remote_vni != vni) { + continue; + } if (IS_ENABLED(CONFIG_IPV6)) { const struct vxlan_config *cfg = &node->vxlan->cfg; @@ -234,6 +171,8 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, int ifindex, continue; } + if (vninode) + *vninode = vnode; return node->vxlan; } @@ -251,7 +190,7 @@ static struct vxlan_dev *vxlan_find_vni(struct net *net, int ifindex, if (!vs) return NULL; - return vxlan_vs_find_vni(vs, ifindex, vni); + return vxlan_vs_find_vni(vs, ifindex, vni, NULL); } /* Fill in neighbour message in skbuff. */ @@ -493,7 +432,7 @@ static u32 eth_hash(const unsigned char *addr) return hash_64(value, FDB_HASH_BITS); } -static u32 eth_vni_hash(const unsigned char *addr, __be32 vni) +u32 eth_vni_hash(const unsigned char *addr, __be32 vni) { /* use 1 byte of OUI and 3 bytes of NIC */ u32 key = get_unaligned((u32 *)(addr + 2)); @@ -501,7 +440,7 @@ static u32 eth_vni_hash(const unsigned char *addr, __be32 vni) return jhash_2words(key, vni, vxlan_salt) & (FDB_HASH_SIZE - 1); } -static u32 fdb_head_index(struct vxlan_dev *vxlan, const u8 *mac, __be32 vni) +u32 fdb_head_index(struct vxlan_dev *vxlan, const u8 *mac, __be32 vni) { if (vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) return eth_vni_hash(mac, vni); @@ -872,37 +811,35 @@ static int vxlan_fdb_nh_update(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb, goto err_inval; } - if (nh) { - if (!nexthop_get(nh)) { - NL_SET_ERR_MSG(extack, "Nexthop has been deleted"); - nh = NULL; - goto err_inval; - } - if (!nexthop_is_fdb(nh)) { - NL_SET_ERR_MSG(extack, "Nexthop is not a fdb nexthop"); - goto err_inval; - } + if (!nexthop_get(nh)) { + NL_SET_ERR_MSG(extack, "Nexthop has been deleted"); + nh = NULL; + goto err_inval; + } + if (!nexthop_is_fdb(nh)) { + NL_SET_ERR_MSG(extack, "Nexthop is not a fdb nexthop"); + goto err_inval; + } - if (!nexthop_is_multipath(nh)) { - NL_SET_ERR_MSG(extack, "Nexthop is not a multipath group"); + if (!nexthop_is_multipath(nh)) { + NL_SET_ERR_MSG(extack, "Nexthop is not a multipath group"); + goto err_inval; + } + + /* check nexthop group family */ + switch (vxlan->default_dst.remote_ip.sa.sa_family) { + case AF_INET: + if (!nexthop_has_v4(nh)) { + err = -EAFNOSUPPORT; + NL_SET_ERR_MSG(extack, "Nexthop group family not supported"); goto err_inval; } - - /* check nexthop group family */ - switch (vxlan->default_dst.remote_ip.sa.sa_family) { - case AF_INET: - if (!nexthop_has_v4(nh)) { - err = -EAFNOSUPPORT; - NL_SET_ERR_MSG(extack, "Nexthop group family not supported"); - goto err_inval; - } - break; - case AF_INET6: - if (nexthop_has_v4(nh)) { - err = -EAFNOSUPPORT; - NL_SET_ERR_MSG(extack, "Nexthop group family not supported"); - goto err_inval; - } + break; + case AF_INET6: + if (nexthop_has_v4(nh)) { + err = -EAFNOSUPPORT; + NL_SET_ERR_MSG(extack, "Nexthop group family not supported"); + goto err_inval; } } @@ -920,12 +857,12 @@ static int vxlan_fdb_nh_update(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb, return err; } -static int vxlan_fdb_create(struct vxlan_dev *vxlan, - const u8 *mac, union vxlan_addr *ip, - __u16 state, __be16 port, __be32 src_vni, - __be32 vni, __u32 ifindex, __u16 ndm_flags, - u32 nhid, struct vxlan_fdb **fdb, - struct netlink_ext_ack *extack) +int vxlan_fdb_create(struct vxlan_dev *vxlan, + const u8 *mac, union vxlan_addr *ip, + __u16 state, __be16 port, __be32 src_vni, + __be32 vni, __u32 ifindex, __u16 ndm_flags, + u32 nhid, struct vxlan_fdb **fdb, + struct netlink_ext_ack *extack) { struct vxlan_rdst *rd = NULL; struct vxlan_fdb *f; @@ -1150,13 +1087,13 @@ static int vxlan_fdb_update_create(struct vxlan_dev *vxlan, } /* Add new entry to forwarding table -- assumes lock held */ -static int vxlan_fdb_update(struct vxlan_dev *vxlan, - const u8 *mac, union vxlan_addr *ip, - __u16 state, __u16 flags, - __be16 port, __be32 src_vni, __be32 vni, - __u32 ifindex, __u16 ndm_flags, u32 nhid, - bool swdev_notify, - struct netlink_ext_ack *extack) +int vxlan_fdb_update(struct vxlan_dev *vxlan, + const u8 *mac, union vxlan_addr *ip, + __u16 state, __u16 flags, + __be16 port, __be32 src_vni, __be32 vni, + __u32 ifindex, __u16 ndm_flags, u32 nhid, + bool swdev_notify, + struct netlink_ext_ack *extack) { struct vxlan_fdb *f; @@ -1307,10 +1244,10 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], return err; } -static int __vxlan_fdb_delete(struct vxlan_dev *vxlan, - const unsigned char *addr, union vxlan_addr ip, - __be16 port, __be32 src_vni, __be32 vni, - u32 ifindex, bool swdev_notify) +int __vxlan_fdb_delete(struct vxlan_dev *vxlan, + const unsigned char *addr, union vxlan_addr ip, + __be16 port, __be32 src_vni, __be32 vni, + u32 ifindex, bool swdev_notify) { struct vxlan_rdst *rd = NULL; struct vxlan_fdb *f; @@ -1519,56 +1456,6 @@ static bool vxlan_snoop(struct net_device *dev, return false; } -/* See if multicast group is already in use by other ID */ -static bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev) -{ - struct vxlan_dev *vxlan; - struct vxlan_sock *sock4; -#if IS_ENABLED(CONFIG_IPV6) - struct vxlan_sock *sock6; -#endif - unsigned short family = dev->default_dst.remote_ip.sa.sa_family; - - sock4 = rtnl_dereference(dev->vn4_sock); - - /* The vxlan_sock is only used by dev, leaving group has - * no effect on other vxlan devices. - */ - if (family == AF_INET && sock4 && refcount_read(&sock4->refcnt) == 1) - return false; -#if IS_ENABLED(CONFIG_IPV6) - sock6 = rtnl_dereference(dev->vn6_sock); - if (family == AF_INET6 && sock6 && refcount_read(&sock6->refcnt) == 1) - return false; -#endif - - list_for_each_entry(vxlan, &vn->vxlan_list, next) { - if (!netif_running(vxlan->dev) || vxlan == dev) - continue; - - if (family == AF_INET && - rtnl_dereference(vxlan->vn4_sock) != sock4) - continue; -#if IS_ENABLED(CONFIG_IPV6) - if (family == AF_INET6 && - rtnl_dereference(vxlan->vn6_sock) != sock6) - continue; -#endif - - if (!vxlan_addr_equal(&vxlan->default_dst.remote_ip, - &dev->default_dst.remote_ip)) - continue; - - if (vxlan->default_dst.remote_ifindex != - dev->default_dst.remote_ifindex) - continue; - - return true; - } - - return false; -} - static bool __vxlan_sock_release_prep(struct vxlan_sock *vs) { struct vxlan_net *vn; @@ -1602,7 +1489,10 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan) RCU_INIT_POINTER(vxlan->vn4_sock, NULL); synchronize_net(); - vxlan_vs_del_dev(vxlan); + if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) + vxlan_vs_del_vnigrp(vxlan); + else + vxlan_vs_del_dev(vxlan); if (__vxlan_sock_release_prep(sock4)) { udp_tunnel_sock_release(sock4->sock); @@ -1617,76 +1507,6 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan) #endif } -/* Update multicast group membership when first VNI on - * multicast address is brought up - */ -static int vxlan_igmp_join(struct vxlan_dev *vxlan) -{ - struct sock *sk; - union vxlan_addr *ip = &vxlan->default_dst.remote_ip; - int ifindex = vxlan->default_dst.remote_ifindex; - int ret = -EINVAL; - - if (ip->sa.sa_family == AF_INET) { - struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock); - struct ip_mreqn mreq = { - .imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr, - .imr_ifindex = ifindex, - }; - - sk = sock4->sock->sk; - lock_sock(sk); - ret = ip_mc_join_group(sk, &mreq); - release_sock(sk); -#if IS_ENABLED(CONFIG_IPV6) - } else { - struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock); - - sk = sock6->sock->sk; - lock_sock(sk); - ret = ipv6_stub->ipv6_sock_mc_join(sk, ifindex, - &ip->sin6.sin6_addr); - release_sock(sk); -#endif - } - - return ret; -} - -/* Inverse of vxlan_igmp_join when last VNI is brought down */ -static int vxlan_igmp_leave(struct vxlan_dev *vxlan) -{ - struct sock *sk; - union vxlan_addr *ip = &vxlan->default_dst.remote_ip; - int ifindex = vxlan->default_dst.remote_ifindex; - int ret = -EINVAL; - - if (ip->sa.sa_family == AF_INET) { - struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock); - struct ip_mreqn mreq = { - .imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr, - .imr_ifindex = ifindex, - }; - - sk = sock4->sock->sk; - lock_sock(sk); - ret = ip_mc_leave_group(sk, &mreq); - release_sock(sk); -#if IS_ENABLED(CONFIG_IPV6) - } else { - struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock); - - sk = sock6->sock->sk; - lock_sock(sk); - ret = ipv6_stub->ipv6_sock_mc_drop(sk, ifindex, - &ip->sin6.sin6_addr); - release_sock(sk); -#endif - } - - return ret; -} - static bool vxlan_remcsum(struct vxlanhdr *unparsed, struct sk_buff *skb, u32 vxflags) { @@ -1828,6 +1648,7 @@ static bool vxlan_ecn_decapsulate(struct vxlan_sock *vs, void *oiph, /* Callback from net/ipv4/udp.c to receive packets */ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) { + struct vxlan_vni_node *vninode = NULL; struct vxlan_dev *vxlan; struct vxlan_sock *vs; struct vxlanhdr unparsed; @@ -1860,7 +1681,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) vni = vxlan_vni(vxlan_hdr(skb)->vx_vni); - vxlan = vxlan_vs_find_vni(vs, skb->dev->ifindex, vni); + vxlan = vxlan_vs_find_vni(vs, skb->dev->ifindex, vni, &vninode); if (!vxlan) goto drop; @@ -1930,6 +1751,8 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) if (!vxlan_ecn_decapsulate(vs, oiph, skb)) { ++vxlan->dev->stats.rx_frame_errors; ++vxlan->dev->stats.rx_errors; + vxlan_vnifilter_count(vxlan, vni, vninode, + VXLAN_VNI_STATS_RX_ERRORS, 0); goto drop; } @@ -1937,11 +1760,14 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) if (unlikely(!(vxlan->dev->flags & IFF_UP))) { rcu_read_unlock(); - atomic_long_inc(&vxlan->dev->rx_dropped); + dev_core_stats_rx_dropped_inc(vxlan->dev); + vxlan_vnifilter_count(vxlan, vni, vninode, + VXLAN_VNI_STATS_RX_DROPS, 0); goto drop; } dev_sw_netstats_rx_add(vxlan->dev, skb->len); + vxlan_vnifilter_count(vxlan, vni, vninode, VXLAN_VNI_STATS_RX, skb->len); gro_cells_receive(&vxlan->gro_cells, skb); rcu_read_unlock(); @@ -1975,7 +1801,7 @@ static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb) return -ENOENT; vni = vxlan_vni(hdr->vx_vni); - vxlan = vxlan_vs_find_vni(vs, skb->dev->ifindex, vni); + vxlan = vxlan_vs_find_vni(vs, skb->dev->ifindex, vni, NULL); if (!vxlan) return -ENOENT; @@ -2049,8 +1875,12 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) reply->ip_summed = CHECKSUM_UNNECESSARY; reply->pkt_type = PACKET_HOST; - if (netif_rx_ni(reply) == NET_RX_DROP) + if (netif_rx(reply) == NET_RX_DROP) { dev->stats.rx_dropped++; + vxlan_vnifilter_count(vxlan, vni, NULL, + VXLAN_VNI_STATS_RX_DROPS, 0); + } + } else if (vxlan->cfg.flags & VXLAN_F_L3MISS) { union vxlan_addr ipa = { .sin.sin_addr.s_addr = tip, @@ -2204,9 +2034,11 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) if (reply == NULL) goto out; - if (netif_rx_ni(reply) == NET_RX_DROP) + if (netif_rx(reply) == NET_RX_DROP) { dev->stats.rx_dropped++; - + vxlan_vnifilter_count(vxlan, vni, NULL, + VXLAN_VNI_STATS_RX_DROPS, 0); + } } else if (vxlan->cfg.flags & VXLAN_F_L3MISS) { union vxlan_addr ipa = { .sin6.sin6_addr = msg->target, @@ -2540,15 +2372,20 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, tx_stats->tx_packets++; tx_stats->tx_bytes += len; u64_stats_update_end(&tx_stats->syncp); + vxlan_vnifilter_count(src_vxlan, vni, NULL, VXLAN_VNI_STATS_TX, len); - if (netif_rx(skb) == NET_RX_SUCCESS) { + if (__netif_rx(skb) == NET_RX_SUCCESS) { u64_stats_update_begin(&rx_stats->syncp); rx_stats->rx_packets++; rx_stats->rx_bytes += len; u64_stats_update_end(&rx_stats->syncp); + vxlan_vnifilter_count(dst_vxlan, vni, NULL, VXLAN_VNI_STATS_RX, + len); } else { drop: dev->stats.rx_dropped++; + vxlan_vnifilter_count(dst_vxlan, vni, NULL, + VXLAN_VNI_STATS_RX_DROPS, 0); } rcu_read_unlock(); } @@ -2578,6 +2415,8 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev, vxlan->cfg.flags); if (!dst_vxlan) { dev->stats.tx_errors++; + vxlan_vnifilter_count(vxlan, vni, NULL, + VXLAN_VNI_STATS_TX_ERRORS, 0); kfree_skb(skb); return -ENOENT; @@ -2601,15 +2440,19 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, union vxlan_addr remote_ip, local_ip; struct vxlan_metadata _md; struct vxlan_metadata *md = &_md; + unsigned int pkt_len = skb->len; __be16 src_port = 0, dst_port; struct dst_entry *ndst = NULL; - __be32 vni, label; __u8 tos, ttl; int ifindex; int err; u32 flags = vxlan->cfg.flags; bool udp_sum = false; bool xnet = !net_eq(vxlan->net, dev_net(vxlan->dev)); + __be32 vni = 0; +#if IS_ENABLED(CONFIG_IPV6) + __be32 label; +#endif info = skb_tunnel_info(skb); @@ -2647,7 +2490,9 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM_TX); else udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX); +#if IS_ENABLED(CONFIG_IPV6) label = vxlan->cfg.label; +#endif } else { if (!info) { WARN_ONCE(1, "%s: Missing encapsulation instructions\n", @@ -2674,7 +2519,9 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, } ttl = info->key.ttl; tos = info->key.tos; +#if IS_ENABLED(CONFIG_IPV6) label = info->key.label; +#endif udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM); } src_port = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min, @@ -2821,12 +2668,14 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, label, src_port, dst_port, !udp_sum); #endif } + vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX, pkt_len); out_unlock: rcu_read_unlock(); return; drop: dev->stats.tx_dropped++; + vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); dev_kfree_skb(skb); return; @@ -2838,6 +2687,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, dev->stats.tx_carrier_errors++; dst_release(ndst); dev->stats.tx_errors++; + vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_ERRORS, 0); kfree_skb(skb); } @@ -2870,6 +2720,8 @@ static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev, drop: dev->stats.tx_dropped++; + vxlan_vnifilter_count(netdev_priv(dev), vni, NULL, + VXLAN_VNI_STATS_TX_DROPS, 0); dev_kfree_skb(skb); } @@ -2944,6 +2796,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) vxlan_fdb_miss(vxlan, eth->h_dest); dev->stats.tx_dropped++; + vxlan_vnifilter_count(vxlan, vni, NULL, + VXLAN_VNI_STATS_TX_DROPS, 0); kfree_skb(skb); return NETDEV_TX_OK; } @@ -3044,6 +2898,9 @@ static int vxlan_init(struct net_device *dev) struct vxlan_dev *vxlan = netdev_priv(dev); int err; + if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) + vxlan_vnigroup_init(vxlan); + dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; @@ -3073,6 +2930,9 @@ static void vxlan_uninit(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); + if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) + vxlan_vnigroup_uninit(vxlan); + gro_cells_destroy(&vxlan->gro_cells); vxlan_fdb_delete_default(vxlan, vxlan->cfg.vni); @@ -3090,14 +2950,10 @@ static int vxlan_open(struct net_device *dev) if (ret < 0) return ret; - if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) { - ret = vxlan_igmp_join(vxlan); - if (ret == -EADDRINUSE) - ret = 0; - if (ret) { - vxlan_sock_release(vxlan); - return ret; - } + ret = vxlan_multicast_join(vxlan); + if (ret) { + vxlan_sock_release(vxlan); + return ret; } if (vxlan->cfg.age_interval) @@ -3134,19 +2990,15 @@ static void vxlan_flush(struct vxlan_dev *vxlan, bool do_all) static int vxlan_stop(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); - int ret = 0; - if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip) && - !vxlan_group_used(vn, vxlan)) - ret = vxlan_igmp_leave(vxlan); + vxlan_multicast_leave(vxlan); del_timer_sync(&vxlan->age_timer); vxlan_flush(vxlan, false); vxlan_sock_release(vxlan); - return ret; + return 0; } /* Stub, nothing needs to be done. */ @@ -3369,6 +3221,7 @@ static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = { [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG }, [IFLA_VXLAN_TTL_INHERIT] = { .type = NLA_FLAG }, [IFLA_VXLAN_DF] = { .type = NLA_U8 }, + [IFLA_VXLAN_VNIFILTER] = { .type = NLA_U8 }, }; static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[], @@ -3554,6 +3407,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6, static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6) { struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); + bool metadata = vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA; struct vxlan_sock *vs = NULL; struct vxlan_dev_node *node; int l3mdev_index = 0; @@ -3589,7 +3443,12 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6) rcu_assign_pointer(vxlan->vn4_sock, vs); node = &vxlan->hlist4; } - vxlan_vs_add_dev(vs, vxlan, node); + + if (metadata && (vxlan->cfg.flags & VXLAN_F_VNIFILTER)) + vxlan_vs_add_vnigrp(vxlan, vs, ipv6); + else + vxlan_vs_add_dev(vs, vxlan, node); + return 0; } @@ -3616,13 +3475,42 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan) return ret; } +int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan, + struct vxlan_config *conf, __be32 vni) +{ + struct vxlan_net *vn = net_generic(src_net, vxlan_net_id); + struct vxlan_dev *tmp; + + list_for_each_entry(tmp, &vn->vxlan_list, next) { + if (tmp == vxlan) + continue; + if (tmp->cfg.flags & VXLAN_F_VNIFILTER) { + if (!vxlan_vnifilter_lookup(tmp, vni)) + continue; + } else if (tmp->cfg.vni != vni) { + continue; + } + if (tmp->cfg.dst_port != conf->dst_port) + continue; + if ((tmp->cfg.flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) != + (conf->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6))) + continue; + + if ((conf->flags & VXLAN_F_IPV6_LINKLOCAL) && + tmp->cfg.remote_ifindex != conf->remote_ifindex) + continue; + + return -EEXIST; + } + + return 0; +} + static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf, struct net_device **lower, struct vxlan_dev *old, struct netlink_ext_ack *extack) { - struct vxlan_net *vn = net_generic(src_net, vxlan_net_id); - struct vxlan_dev *tmp; bool use_ipv6 = false; if (conf->flags & VXLAN_F_GPE) { @@ -3755,22 +3643,7 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf, if (!conf->age_interval) conf->age_interval = FDB_AGE_DEFAULT; - list_for_each_entry(tmp, &vn->vxlan_list, next) { - if (tmp == old) - continue; - - if (tmp->cfg.vni != conf->vni) - continue; - if (tmp->cfg.dst_port != conf->dst_port) - continue; - if ((tmp->cfg.flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) != - (conf->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6))) - continue; - - if ((conf->flags & VXLAN_F_IPV6_LINKLOCAL) && - tmp->cfg.remote_ifindex != conf->remote_ifindex) - continue; - + if (vxlan_vni_in_use(src_net, old, conf, conf->vni)) { NL_SET_ERR_MSG(extack, "A VXLAN device with the specified VNI already exists"); return -EEXIST; @@ -4226,6 +4099,21 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[], if (data[IFLA_VXLAN_DF]) conf->df = nla_get_u8(data[IFLA_VXLAN_DF]); + if (data[IFLA_VXLAN_VNIFILTER]) { + err = vxlan_nl2flag(conf, data, IFLA_VXLAN_VNIFILTER, + VXLAN_F_VNIFILTER, changelink, false, + extack); + if (err) + return err; + + if ((conf->flags & VXLAN_F_VNIFILTER) && + !(conf->flags & VXLAN_F_COLLECT_METADATA)) { + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_VXLAN_VNIFILTER], + "vxlan vnifilter only valid in collect metadata mode"); + return -EINVAL; + } + } + return 0; } @@ -4301,6 +4189,19 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], dst->remote_ifindex, true); spin_unlock_bh(&vxlan->hash_lock[hash_index]); + + /* If vni filtering device, also update fdb entries of + * all vnis that were using default remote ip + */ + if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) { + err = vxlan_vnilist_update_group(vxlan, &dst->remote_ip, + &conf.remote_ip, extack); + if (err) { + netdev_adjacent_change_abort(dst->remote_dev, + lowerdev, dev); + return err; + } + } } if (conf.age_interval != vxlan->cfg.age_interval) @@ -4446,6 +4347,11 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_flag(skb, IFLA_VXLAN_REMCSUM_NOPARTIAL)) goto nla_put_failure; + if (vxlan->cfg.flags & VXLAN_F_VNIFILTER && + nla_put_u8(skb, IFLA_VXLAN_VNIFILTER, + !!(vxlan->cfg.flags & VXLAN_F_VNIFILTER))) + goto nla_put_failure; + return 0; nla_put_failure: @@ -4805,6 +4711,8 @@ static int __init vxlan_init_module(void) if (rc) goto out4; + vxlan_vnifilter_init(); + return 0; out4: unregister_switchdev_notifier(&vxlan_switchdev_notifier_block); @@ -4819,6 +4727,7 @@ late_initcall(vxlan_init_module); static void __exit vxlan_cleanup_module(void) { + vxlan_vnifilter_uninit(); rtnl_link_unregister(&vxlan_link_ops); unregister_switchdev_notifier(&vxlan_switchdev_notifier_block); unregister_netdevice_notifier(&vxlan_notifier_block); diff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c new file mode 100644 index 000000000000..a7f2d67dc61b --- /dev/null +++ b/drivers/net/vxlan/vxlan_multicast.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Vxlan multicast group handling + * + */ +#include +#include +#include +#include +#include + +#include "vxlan_private.h" + +/* Update multicast group membership when first VNI on + * multicast address is brought up + */ +int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip, + int rifindex) +{ + union vxlan_addr *ip = (rip ? : &vxlan->default_dst.remote_ip); + int ifindex = (rifindex ? : vxlan->default_dst.remote_ifindex); + int ret = -EINVAL; + struct sock *sk; + + if (ip->sa.sa_family == AF_INET) { + struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock); + struct ip_mreqn mreq = { + .imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr, + .imr_ifindex = ifindex, + }; + + sk = sock4->sock->sk; + lock_sock(sk); + ret = ip_mc_join_group(sk, &mreq); + release_sock(sk); +#if IS_ENABLED(CONFIG_IPV6) + } else { + struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock); + + sk = sock6->sock->sk; + lock_sock(sk); + ret = ipv6_stub->ipv6_sock_mc_join(sk, ifindex, + &ip->sin6.sin6_addr); + release_sock(sk); +#endif + } + + return ret; +} + +int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip, + int rifindex) +{ + union vxlan_addr *ip = (rip ? : &vxlan->default_dst.remote_ip); + int ifindex = (rifindex ? : vxlan->default_dst.remote_ifindex); + int ret = -EINVAL; + struct sock *sk; + + if (ip->sa.sa_family == AF_INET) { + struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock); + struct ip_mreqn mreq = { + .imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr, + .imr_ifindex = ifindex, + }; + + sk = sock4->sock->sk; + lock_sock(sk); + ret = ip_mc_leave_group(sk, &mreq); + release_sock(sk); +#if IS_ENABLED(CONFIG_IPV6) + } else { + struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock); + + sk = sock6->sock->sk; + lock_sock(sk); + ret = ipv6_stub->ipv6_sock_mc_drop(sk, ifindex, + &ip->sin6.sin6_addr); + release_sock(sk); +#endif + } + + return ret; +} + +static bool vxlan_group_used_match(union vxlan_addr *ip, int ifindex, + union vxlan_addr *rip, int rifindex) +{ + if (!vxlan_addr_multicast(rip)) + return false; + + if (!vxlan_addr_equal(rip, ip)) + return false; + + if (rifindex != ifindex) + return false; + + return true; +} + +static bool vxlan_group_used_by_vnifilter(struct vxlan_dev *vxlan, + union vxlan_addr *ip, int ifindex) +{ + struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp); + struct vxlan_vni_node *v, *tmp; + + if (vxlan_group_used_match(ip, ifindex, + &vxlan->default_dst.remote_ip, + vxlan->default_dst.remote_ifindex)) + return true; + + list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { + if (!vxlan_addr_multicast(&v->remote_ip)) + continue; + + if (vxlan_group_used_match(ip, ifindex, + &v->remote_ip, + vxlan->default_dst.remote_ifindex)) + return true; + } + + return false; +} + +/* See if multicast group is already in use by other ID */ +bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev, + __be32 vni, union vxlan_addr *rip, int rifindex) +{ + union vxlan_addr *ip = (rip ? : &dev->default_dst.remote_ip); + int ifindex = (rifindex ? : dev->default_dst.remote_ifindex); + struct vxlan_dev *vxlan; + struct vxlan_sock *sock4; +#if IS_ENABLED(CONFIG_IPV6) + struct vxlan_sock *sock6; +#endif + unsigned short family = dev->default_dst.remote_ip.sa.sa_family; + + sock4 = rtnl_dereference(dev->vn4_sock); + + /* The vxlan_sock is only used by dev, leaving group has + * no effect on other vxlan devices. + */ + if (family == AF_INET && sock4 && refcount_read(&sock4->refcnt) == 1) + return false; + +#if IS_ENABLED(CONFIG_IPV6) + sock6 = rtnl_dereference(dev->vn6_sock); + if (family == AF_INET6 && sock6 && refcount_read(&sock6->refcnt) == 1) + return false; +#endif + + list_for_each_entry(vxlan, &vn->vxlan_list, next) { + if (!netif_running(vxlan->dev) || vxlan == dev) + continue; + + if (family == AF_INET && + rtnl_dereference(vxlan->vn4_sock) != sock4) + continue; +#if IS_ENABLED(CONFIG_IPV6) + if (family == AF_INET6 && + rtnl_dereference(vxlan->vn6_sock) != sock6) + continue; +#endif + if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) { + if (!vxlan_group_used_by_vnifilter(vxlan, ip, ifindex)) + continue; + } else { + if (!vxlan_group_used_match(ip, ifindex, + &vxlan->default_dst.remote_ip, + vxlan->default_dst.remote_ifindex)) + continue; + } + + return true; + } + + return false; +} + +static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan) +{ + struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp); + struct vxlan_vni_node *v, *tmp, *vgood = NULL; + int ret = 0; + + list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { + if (!vxlan_addr_multicast(&v->remote_ip)) + continue; + /* skip if address is same as default address */ + if (vxlan_addr_equal(&v->remote_ip, + &vxlan->default_dst.remote_ip)) + continue; + ret = vxlan_igmp_join(vxlan, &v->remote_ip, 0); + if (ret == -EADDRINUSE) + ret = 0; + if (ret) + goto out; + vgood = v; + } +out: + if (ret) { + list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { + if (!vxlan_addr_multicast(&v->remote_ip)) + continue; + if (vxlan_addr_equal(&v->remote_ip, + &vxlan->default_dst.remote_ip)) + continue; + vxlan_igmp_leave(vxlan, &v->remote_ip, 0); + if (v == vgood) + break; + } + } + + return ret; +} + +static int vxlan_multicast_leave_vnigrp(struct vxlan_dev *vxlan) +{ + struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); + struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp); + struct vxlan_vni_node *v, *tmp; + int last_err = 0, ret; + + list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { + if (vxlan_addr_multicast(&v->remote_ip) && + !vxlan_group_used(vn, vxlan, v->vni, &v->remote_ip, + 0)) { + ret = vxlan_igmp_leave(vxlan, &v->remote_ip, 0); + if (ret) + last_err = ret; + } + } + + return last_err; +} + +int vxlan_multicast_join(struct vxlan_dev *vxlan) +{ + int ret = 0; + + if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) { + ret = vxlan_igmp_join(vxlan, &vxlan->default_dst.remote_ip, + vxlan->default_dst.remote_ifindex); + if (ret == -EADDRINUSE) + ret = 0; + if (ret) + return ret; + } + + if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) + return vxlan_multicast_join_vnigrp(vxlan); + + return 0; +} + +int vxlan_multicast_leave(struct vxlan_dev *vxlan) +{ + struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); + int ret = 0; + + if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip) && + !vxlan_group_used(vn, vxlan, 0, NULL, 0)) { + ret = vxlan_igmp_leave(vxlan, &vxlan->default_dst.remote_ip, + vxlan->default_dst.remote_ifindex); + if (ret) + return ret; + } + + if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) + return vxlan_multicast_leave_vnigrp(vxlan); + + return 0; +} diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h new file mode 100644 index 000000000000..599c3b4fdd5e --- /dev/null +++ b/drivers/net/vxlan/vxlan_private.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Vxlan private header file + * + */ + +#ifndef _VXLAN_PRIVATE_H +#define _VXLAN_PRIVATE_H + +#include + +extern unsigned int vxlan_net_id; +extern const u8 all_zeros_mac[ETH_ALEN + 2]; +extern const struct rhashtable_params vxlan_vni_rht_params; + +#define PORT_HASH_BITS 8 +#define PORT_HASH_SIZE (1 << PORT_HASH_BITS) + +/* per-network namespace private data for this module */ +struct vxlan_net { + struct list_head vxlan_list; + struct hlist_head sock_list[PORT_HASH_SIZE]; + spinlock_t sock_lock; + struct notifier_block nexthop_notifier_block; +}; + +/* Forwarding table entry */ +struct vxlan_fdb { + struct hlist_node hlist; /* linked list of entries */ + struct rcu_head rcu; + unsigned long updated; /* jiffies */ + unsigned long used; + struct list_head remotes; + u8 eth_addr[ETH_ALEN]; + u16 state; /* see ndm_state */ + __be32 vni; + u16 flags; /* see ndm_flags and below */ + struct list_head nh_list; + struct nexthop __rcu *nh; + struct vxlan_dev __rcu *vdev; +}; + +#define NTF_VXLAN_ADDED_BY_USER 0x100 + +/* Virtual Network hash table head */ +static inline struct hlist_head *vni_head(struct vxlan_sock *vs, __be32 vni) +{ + return &vs->vni_list[hash_32((__force u32)vni, VNI_HASH_BITS)]; +} + +/* Socket hash table head */ +static inline struct hlist_head *vs_head(struct net *net, __be16 port) +{ + struct vxlan_net *vn = net_generic(net, vxlan_net_id); + + return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)]; +} + +/* First remote destination for a forwarding entry. + * Guaranteed to be non-NULL because remotes are never deleted. + */ +static inline struct vxlan_rdst *first_remote_rcu(struct vxlan_fdb *fdb) +{ + if (rcu_access_pointer(fdb->nh)) + return NULL; + return list_entry_rcu(fdb->remotes.next, struct vxlan_rdst, list); +} + +static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb) +{ + if (rcu_access_pointer(fdb->nh)) + return NULL; + return list_first_entry(&fdb->remotes, struct vxlan_rdst, list); +} + +#if IS_ENABLED(CONFIG_IPV6) +static inline +bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b) +{ + if (a->sa.sa_family != b->sa.sa_family) + return false; + if (a->sa.sa_family == AF_INET6) + return ipv6_addr_equal(&a->sin6.sin6_addr, &b->sin6.sin6_addr); + else + return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr; +} + +#else /* !CONFIG_IPV6 */ + +static inline +bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b) +{ + return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr; +} + +#endif + +static inline struct vxlan_vni_node * +vxlan_vnifilter_lookup(struct vxlan_dev *vxlan, __be32 vni) +{ + struct vxlan_vni_group *vg; + + vg = rcu_dereference_rtnl(vxlan->vnigrp); + if (!vg) + return NULL; + + return rhashtable_lookup_fast(&vg->vni_hash, &vni, + vxlan_vni_rht_params); +} + +/* vxlan_core.c */ +int vxlan_fdb_create(struct vxlan_dev *vxlan, + const u8 *mac, union vxlan_addr *ip, + __u16 state, __be16 port, __be32 src_vni, + __be32 vni, __u32 ifindex, __u16 ndm_flags, + u32 nhid, struct vxlan_fdb **fdb, + struct netlink_ext_ack *extack); +int __vxlan_fdb_delete(struct vxlan_dev *vxlan, + const unsigned char *addr, union vxlan_addr ip, + __be16 port, __be32 src_vni, __be32 vni, + u32 ifindex, bool swdev_notify); +u32 eth_vni_hash(const unsigned char *addr, __be32 vni); +u32 fdb_head_index(struct vxlan_dev *vxlan, const u8 *mac, __be32 vni); +int vxlan_fdb_update(struct vxlan_dev *vxlan, + const u8 *mac, union vxlan_addr *ip, + __u16 state, __u16 flags, + __be16 port, __be32 src_vni, __be32 vni, + __u32 ifindex, __u16 ndm_flags, u32 nhid, + bool swdev_notify, struct netlink_ext_ack *extack); +int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan, + struct vxlan_config *conf, __be32 vni); + +/* vxlan_vnifilter.c */ +int vxlan_vnigroup_init(struct vxlan_dev *vxlan); +void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan); + +void vxlan_vnifilter_init(void); +void vxlan_vnifilter_uninit(void); +void vxlan_vnifilter_count(struct vxlan_dev *vxlan, __be32 vni, + struct vxlan_vni_node *vninode, + int type, unsigned int len); + +void vxlan_vs_add_vnigrp(struct vxlan_dev *vxlan, + struct vxlan_sock *vs, + bool ipv6); +void vxlan_vs_del_vnigrp(struct vxlan_dev *vxlan); +int vxlan_vnilist_update_group(struct vxlan_dev *vxlan, + union vxlan_addr *old_remote_ip, + union vxlan_addr *new_remote_ip, + struct netlink_ext_ack *extack); + + +/* vxlan_multicast.c */ +int vxlan_multicast_join(struct vxlan_dev *vxlan); +int vxlan_multicast_leave(struct vxlan_dev *vxlan); +bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev, + __be32 vni, union vxlan_addr *rip, int rifindex); +int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip, + int rifindex); +int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip, + int rifindex); +#endif diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c new file mode 100644 index 000000000000..9f28d0b6a6b2 --- /dev/null +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -0,0 +1,999 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Vxlan vni filter for collect metadata mode + * + * Authors: Roopa Prabhu + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vxlan_private.h" + +static inline int vxlan_vni_cmp(struct rhashtable_compare_arg *arg, + const void *ptr) +{ + const struct vxlan_vni_node *vnode = ptr; + __be32 vni = *(__be32 *)arg->key; + + return vnode->vni != vni; +} + +const struct rhashtable_params vxlan_vni_rht_params = { + .head_offset = offsetof(struct vxlan_vni_node, vnode), + .key_offset = offsetof(struct vxlan_vni_node, vni), + .key_len = sizeof(__be32), + .nelem_hint = 3, + .max_size = VXLAN_N_VID, + .obj_cmpfn = vxlan_vni_cmp, + .automatic_shrinking = true, +}; + +static void vxlan_vs_add_del_vninode(struct vxlan_dev *vxlan, + struct vxlan_vni_node *v, + bool del) +{ + struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); + struct vxlan_dev_node *node; + struct vxlan_sock *vs; + + spin_lock(&vn->sock_lock); + if (del) { + if (!hlist_unhashed(&v->hlist4.hlist)) + hlist_del_init_rcu(&v->hlist4.hlist); +#if IS_ENABLED(CONFIG_IPV6) + if (!hlist_unhashed(&v->hlist6.hlist)) + hlist_del_init_rcu(&v->hlist6.hlist); +#endif + goto out; + } + +#if IS_ENABLED(CONFIG_IPV6) + vs = rtnl_dereference(vxlan->vn6_sock); + if (vs && v) { + node = &v->hlist6; + hlist_add_head_rcu(&node->hlist, vni_head(vs, v->vni)); + } +#endif + vs = rtnl_dereference(vxlan->vn4_sock); + if (vs && v) { + node = &v->hlist4; + hlist_add_head_rcu(&node->hlist, vni_head(vs, v->vni)); + } +out: + spin_unlock(&vn->sock_lock); +} + +void vxlan_vs_add_vnigrp(struct vxlan_dev *vxlan, + struct vxlan_sock *vs, + bool ipv6) +{ + struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); + struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp); + struct vxlan_vni_node *v, *tmp; + struct vxlan_dev_node *node; + + if (!vg) + return; + + spin_lock(&vn->sock_lock); + list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { +#if IS_ENABLED(CONFIG_IPV6) + if (ipv6) + node = &v->hlist6; + else +#endif + node = &v->hlist4; + node->vxlan = vxlan; + hlist_add_head_rcu(&node->hlist, vni_head(vs, v->vni)); + } + spin_unlock(&vn->sock_lock); +} + +void vxlan_vs_del_vnigrp(struct vxlan_dev *vxlan) +{ + struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp); + struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); + struct vxlan_vni_node *v, *tmp; + + if (!vg) + return; + + spin_lock(&vn->sock_lock); + list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { + hlist_del_init_rcu(&v->hlist4.hlist); +#if IS_ENABLED(CONFIG_IPV6) + hlist_del_init_rcu(&v->hlist6.hlist); +#endif + } + spin_unlock(&vn->sock_lock); +} + +static void vxlan_vnifilter_stats_get(const struct vxlan_vni_node *vninode, + struct vxlan_vni_stats *dest) +{ + int i; + + memset(dest, 0, sizeof(*dest)); + for_each_possible_cpu(i) { + struct vxlan_vni_stats_pcpu *pstats; + struct vxlan_vni_stats temp; + unsigned int start; + + pstats = per_cpu_ptr(vninode->stats, i); + do { + start = u64_stats_fetch_begin_irq(&pstats->syncp); + memcpy(&temp, &pstats->stats, sizeof(temp)); + } while (u64_stats_fetch_retry_irq(&pstats->syncp, start)); + + dest->rx_packets += temp.rx_packets; + dest->rx_bytes += temp.rx_bytes; + dest->rx_drops += temp.rx_drops; + dest->rx_errors += temp.rx_errors; + dest->tx_packets += temp.tx_packets; + dest->tx_bytes += temp.tx_bytes; + dest->tx_drops += temp.tx_drops; + dest->tx_errors += temp.tx_errors; + } +} + +static void vxlan_vnifilter_stats_add(struct vxlan_vni_node *vninode, + int type, unsigned int len) +{ + struct vxlan_vni_stats_pcpu *pstats = this_cpu_ptr(vninode->stats); + + u64_stats_update_begin(&pstats->syncp); + switch (type) { + case VXLAN_VNI_STATS_RX: + pstats->stats.rx_bytes += len; + pstats->stats.rx_packets++; + break; + case VXLAN_VNI_STATS_RX_DROPS: + pstats->stats.rx_drops++; + break; + case VXLAN_VNI_STATS_RX_ERRORS: + pstats->stats.rx_errors++; + break; + case VXLAN_VNI_STATS_TX: + pstats->stats.tx_bytes += len; + pstats->stats.tx_packets++; + break; + case VXLAN_VNI_STATS_TX_DROPS: + pstats->stats.tx_drops++; + break; + case VXLAN_VNI_STATS_TX_ERRORS: + pstats->stats.tx_errors++; + break; + } + u64_stats_update_end(&pstats->syncp); +} + +void vxlan_vnifilter_count(struct vxlan_dev *vxlan, __be32 vni, + struct vxlan_vni_node *vninode, + int type, unsigned int len) +{ + struct vxlan_vni_node *vnode; + + if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) + return; + + if (vninode) { + vnode = vninode; + } else { + vnode = vxlan_vnifilter_lookup(vxlan, vni); + if (!vnode) + return; + } + + vxlan_vnifilter_stats_add(vnode, type, len); +} + +static u32 vnirange(struct vxlan_vni_node *vbegin, + struct vxlan_vni_node *vend) +{ + return (be32_to_cpu(vend->vni) - be32_to_cpu(vbegin->vni)); +} + +static size_t vxlan_vnifilter_entry_nlmsg_size(void) +{ + return NLMSG_ALIGN(sizeof(struct tunnel_msg)) + + nla_total_size(0) /* VXLAN_VNIFILTER_ENTRY */ + + nla_total_size(sizeof(u32)) /* VXLAN_VNIFILTER_ENTRY_START */ + + nla_total_size(sizeof(u32)) /* VXLAN_VNIFILTER_ENTRY_END */ + + nla_total_size(sizeof(struct in6_addr));/* VXLAN_VNIFILTER_ENTRY_GROUP{6} */ +} + +static int __vnifilter_entry_fill_stats(struct sk_buff *skb, + const struct vxlan_vni_node *vbegin) +{ + struct vxlan_vni_stats vstats; + struct nlattr *vstats_attr; + + vstats_attr = nla_nest_start(skb, VXLAN_VNIFILTER_ENTRY_STATS); + if (!vstats_attr) + goto out_stats_err; + + vxlan_vnifilter_stats_get(vbegin, &vstats); + if (nla_put_u64_64bit(skb, VNIFILTER_ENTRY_STATS_RX_BYTES, + vstats.rx_bytes, VNIFILTER_ENTRY_STATS_PAD) || + nla_put_u64_64bit(skb, VNIFILTER_ENTRY_STATS_RX_PKTS, + vstats.rx_packets, VNIFILTER_ENTRY_STATS_PAD) || + nla_put_u64_64bit(skb, VNIFILTER_ENTRY_STATS_RX_DROPS, + vstats.rx_drops, VNIFILTER_ENTRY_STATS_PAD) || + nla_put_u64_64bit(skb, VNIFILTER_ENTRY_STATS_RX_ERRORS, + vstats.rx_errors, VNIFILTER_ENTRY_STATS_PAD) || + nla_put_u64_64bit(skb, VNIFILTER_ENTRY_STATS_TX_BYTES, + vstats.tx_bytes, VNIFILTER_ENTRY_STATS_PAD) || + nla_put_u64_64bit(skb, VNIFILTER_ENTRY_STATS_TX_PKTS, + vstats.tx_packets, VNIFILTER_ENTRY_STATS_PAD) || + nla_put_u64_64bit(skb, VNIFILTER_ENTRY_STATS_TX_DROPS, + vstats.tx_drops, VNIFILTER_ENTRY_STATS_PAD) || + nla_put_u64_64bit(skb, VNIFILTER_ENTRY_STATS_TX_ERRORS, + vstats.tx_errors, VNIFILTER_ENTRY_STATS_PAD)) + goto out_stats_err; + + nla_nest_end(skb, vstats_attr); + + return 0; + +out_stats_err: + nla_nest_cancel(skb, vstats_attr); + return -EMSGSIZE; +} + +static bool vxlan_fill_vni_filter_entry(struct sk_buff *skb, + struct vxlan_vni_node *vbegin, + struct vxlan_vni_node *vend, + bool fill_stats) +{ + struct nlattr *ventry; + u32 vs = be32_to_cpu(vbegin->vni); + u32 ve = 0; + + if (vbegin != vend) + ve = be32_to_cpu(vend->vni); + + ventry = nla_nest_start(skb, VXLAN_VNIFILTER_ENTRY); + if (!ventry) + return false; + + if (nla_put_u32(skb, VXLAN_VNIFILTER_ENTRY_START, vs)) + goto out_err; + + if (ve && nla_put_u32(skb, VXLAN_VNIFILTER_ENTRY_END, ve)) + goto out_err; + + if (!vxlan_addr_any(&vbegin->remote_ip)) { + if (vbegin->remote_ip.sa.sa_family == AF_INET) { + if (nla_put_in_addr(skb, VXLAN_VNIFILTER_ENTRY_GROUP, + vbegin->remote_ip.sin.sin_addr.s_addr)) + goto out_err; +#if IS_ENABLED(CONFIG_IPV6) + } else { + if (nla_put_in6_addr(skb, VXLAN_VNIFILTER_ENTRY_GROUP6, + &vbegin->remote_ip.sin6.sin6_addr)) + goto out_err; +#endif + } + } + + if (fill_stats && __vnifilter_entry_fill_stats(skb, vbegin)) + goto out_err; + + nla_nest_end(skb, ventry); + + return true; + +out_err: + nla_nest_cancel(skb, ventry); + + return false; +} + +static void vxlan_vnifilter_notify(const struct vxlan_dev *vxlan, + struct vxlan_vni_node *vninode, int cmd) +{ + struct tunnel_msg *tmsg; + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct net *net = dev_net(vxlan->dev); + int err = -ENOBUFS; + + skb = nlmsg_new(vxlan_vnifilter_entry_nlmsg_size(), GFP_KERNEL); + if (!skb) + goto out_err; + + err = -EMSGSIZE; + nlh = nlmsg_put(skb, 0, 0, cmd, sizeof(*tmsg), 0); + if (!nlh) + goto out_err; + tmsg = nlmsg_data(nlh); + memset(tmsg, 0, sizeof(*tmsg)); + tmsg->family = AF_BRIDGE; + tmsg->ifindex = vxlan->dev->ifindex; + + if (!vxlan_fill_vni_filter_entry(skb, vninode, vninode, false)) + goto out_err; + + nlmsg_end(skb, nlh); + rtnl_notify(skb, net, 0, RTNLGRP_TUNNEL, NULL, GFP_KERNEL); + + return; + +out_err: + rtnl_set_sk_err(net, RTNLGRP_TUNNEL, err); + + kfree_skb(skb); +} + +static int vxlan_vnifilter_dump_dev(const struct net_device *dev, + struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct vxlan_vni_node *tmp, *v, *vbegin = NULL, *vend = NULL; + struct vxlan_dev *vxlan = netdev_priv(dev); + struct tunnel_msg *new_tmsg, *tmsg; + int idx = 0, s_idx = cb->args[1]; + struct vxlan_vni_group *vg; + struct nlmsghdr *nlh; + bool dump_stats; + int err = 0; + + if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) + return -EINVAL; + + /* RCU needed because of the vni locking rules (rcu || rtnl) */ + vg = rcu_dereference(vxlan->vnigrp); + if (!vg || !vg->num_vnis) + return 0; + + tmsg = nlmsg_data(cb->nlh); + dump_stats = !!(tmsg->flags & TUNNEL_MSG_FLAG_STATS); + + nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + RTM_NEWTUNNEL, sizeof(*new_tmsg), NLM_F_MULTI); + if (!nlh) + return -EMSGSIZE; + new_tmsg = nlmsg_data(nlh); + memset(new_tmsg, 0, sizeof(*new_tmsg)); + new_tmsg->family = PF_BRIDGE; + new_tmsg->ifindex = dev->ifindex; + + list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { + if (idx < s_idx) { + idx++; + continue; + } + if (!vbegin) { + vbegin = v; + vend = v; + continue; + } + if (!dump_stats && vnirange(vend, v) == 1 && + vxlan_addr_equal(&v->remote_ip, &vend->remote_ip)) { + goto update_end; + } else { + if (!vxlan_fill_vni_filter_entry(skb, vbegin, vend, + dump_stats)) { + err = -EMSGSIZE; + break; + } + idx += vnirange(vbegin, vend) + 1; + vbegin = v; + } +update_end: + vend = v; + } + + if (!err && vbegin) { + if (!vxlan_fill_vni_filter_entry(skb, vbegin, vend, dump_stats)) + err = -EMSGSIZE; + } + + cb->args[1] = err ? idx : 0; + + nlmsg_end(skb, nlh); + + return err; +} + +static int vxlan_vnifilter_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int idx = 0, err = 0, s_idx = cb->args[0]; + struct net *net = sock_net(skb->sk); + struct tunnel_msg *tmsg; + struct net_device *dev; + + tmsg = nlmsg_data(cb->nlh); + + if (tmsg->flags & ~TUNNEL_MSG_VALID_USER_FLAGS) { + NL_SET_ERR_MSG(cb->extack, "Invalid tunnelmsg flags in ancillary header"); + return -EINVAL; + } + + rcu_read_lock(); + if (tmsg->ifindex) { + dev = dev_get_by_index_rcu(net, tmsg->ifindex); + if (!dev) { + err = -ENODEV; + goto out_err; + } + err = vxlan_vnifilter_dump_dev(dev, skb, cb); + /* if the dump completed without an error we return 0 here */ + if (err != -EMSGSIZE) + goto out_err; + } else { + for_each_netdev_rcu(net, dev) { + if (!netif_is_vxlan(dev)) + continue; + if (idx < s_idx) + goto skip; + err = vxlan_vnifilter_dump_dev(dev, skb, cb); + if (err == -EMSGSIZE) + break; +skip: + idx++; + } + } + cb->args[0] = idx; + rcu_read_unlock(); + + return skb->len; + +out_err: + rcu_read_unlock(); + + return err; +} + +static const struct nla_policy vni_filter_entry_policy[VXLAN_VNIFILTER_ENTRY_MAX + 1] = { + [VXLAN_VNIFILTER_ENTRY_START] = { .type = NLA_U32 }, + [VXLAN_VNIFILTER_ENTRY_END] = { .type = NLA_U32 }, + [VXLAN_VNIFILTER_ENTRY_GROUP] = { .type = NLA_BINARY, + .len = sizeof_field(struct iphdr, daddr) }, + [VXLAN_VNIFILTER_ENTRY_GROUP6] = { .type = NLA_BINARY, + .len = sizeof(struct in6_addr) }, +}; + +static const struct nla_policy vni_filter_policy[VXLAN_VNIFILTER_MAX + 1] = { + [VXLAN_VNIFILTER_ENTRY] = { .type = NLA_NESTED }, +}; + +static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni, + union vxlan_addr *old_remote_ip, + union vxlan_addr *remote_ip, + struct netlink_ext_ack *extack) +{ + struct vxlan_rdst *dst = &vxlan->default_dst; + u32 hash_index; + int err = 0; + + hash_index = fdb_head_index(vxlan, all_zeros_mac, vni); + spin_lock_bh(&vxlan->hash_lock[hash_index]); + if (remote_ip && !vxlan_addr_any(remote_ip)) { + err = vxlan_fdb_update(vxlan, all_zeros_mac, + remote_ip, + NUD_REACHABLE | NUD_PERMANENT, + NLM_F_APPEND | NLM_F_CREATE, + vxlan->cfg.dst_port, + vni, + vni, + dst->remote_ifindex, + NTF_SELF, 0, true, extack); + if (err) { + spin_unlock_bh(&vxlan->hash_lock[hash_index]); + return err; + } + } + + if (old_remote_ip && !vxlan_addr_any(old_remote_ip)) { + __vxlan_fdb_delete(vxlan, all_zeros_mac, + *old_remote_ip, + vxlan->cfg.dst_port, + vni, vni, + dst->remote_ifindex, + true); + } + spin_unlock_bh(&vxlan->hash_lock[hash_index]); + + return err; +} + +static int vxlan_vni_update_group(struct vxlan_dev *vxlan, + struct vxlan_vni_node *vninode, + union vxlan_addr *group, + bool create, bool *changed, + struct netlink_ext_ack *extack) +{ + struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); + struct vxlan_rdst *dst = &vxlan->default_dst; + union vxlan_addr *newrip = NULL, *oldrip = NULL; + union vxlan_addr old_remote_ip; + int ret = 0; + + memcpy(&old_remote_ip, &vninode->remote_ip, sizeof(old_remote_ip)); + + /* if per vni remote ip is not present use vxlan dev + * default dst remote ip for fdb entry + */ + if (group && !vxlan_addr_any(group)) { + newrip = group; + } else { + if (!vxlan_addr_any(&dst->remote_ip)) + newrip = &dst->remote_ip; + } + + /* if old rip exists, and no newrip, + * explicitly delete old rip + */ + if (!newrip && !vxlan_addr_any(&old_remote_ip)) + oldrip = &old_remote_ip; + + if (!newrip && !oldrip) + return 0; + + if (!create && oldrip && newrip && vxlan_addr_equal(oldrip, newrip)) + return 0; + + ret = vxlan_update_default_fdb_entry(vxlan, vninode->vni, + oldrip, newrip, + extack); + if (ret) + goto out; + + if (group) + memcpy(&vninode->remote_ip, group, sizeof(vninode->remote_ip)); + + if (vxlan->dev->flags & IFF_UP) { + if (vxlan_addr_multicast(&old_remote_ip) && + !vxlan_group_used(vn, vxlan, vninode->vni, + &old_remote_ip, + vxlan->default_dst.remote_ifindex)) { + ret = vxlan_igmp_leave(vxlan, &old_remote_ip, + 0); + if (ret) + goto out; + } + + if (vxlan_addr_multicast(&vninode->remote_ip)) { + ret = vxlan_igmp_join(vxlan, &vninode->remote_ip, 0); + if (ret == -EADDRINUSE) + ret = 0; + if (ret) + goto out; + } + } + + *changed = true; + + return 0; +out: + return ret; +} + +int vxlan_vnilist_update_group(struct vxlan_dev *vxlan, + union vxlan_addr *old_remote_ip, + union vxlan_addr *new_remote_ip, + struct netlink_ext_ack *extack) +{ + struct list_head *headp, *hpos; + struct vxlan_vni_group *vg; + struct vxlan_vni_node *vent; + int ret; + + vg = rtnl_dereference(vxlan->vnigrp); + + headp = &vg->vni_list; + list_for_each_prev(hpos, headp) { + vent = list_entry(hpos, struct vxlan_vni_node, vlist); + if (vxlan_addr_any(&vent->remote_ip)) { + ret = vxlan_update_default_fdb_entry(vxlan, vent->vni, + old_remote_ip, + new_remote_ip, + extack); + if (ret) + return ret; + } + } + + return 0; +} + +static void vxlan_vni_delete_group(struct vxlan_dev *vxlan, + struct vxlan_vni_node *vninode) +{ + struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); + struct vxlan_rdst *dst = &vxlan->default_dst; + + /* if per vni remote_ip not present, delete the + * default dst remote_ip previously added for this vni + */ + if (!vxlan_addr_any(&vninode->remote_ip) || + !vxlan_addr_any(&dst->remote_ip)) + __vxlan_fdb_delete(vxlan, all_zeros_mac, + (vxlan_addr_any(&vninode->remote_ip) ? + dst->remote_ip : vninode->remote_ip), + vxlan->cfg.dst_port, + vninode->vni, vninode->vni, + dst->remote_ifindex, + true); + + if (vxlan->dev->flags & IFF_UP) { + if (vxlan_addr_multicast(&vninode->remote_ip) && + !vxlan_group_used(vn, vxlan, vninode->vni, + &vninode->remote_ip, + dst->remote_ifindex)) { + vxlan_igmp_leave(vxlan, &vninode->remote_ip, 0); + } + } +} + +static int vxlan_vni_update(struct vxlan_dev *vxlan, + struct vxlan_vni_group *vg, + __be32 vni, union vxlan_addr *group, + bool *changed, + struct netlink_ext_ack *extack) +{ + struct vxlan_vni_node *vninode; + int ret; + + vninode = rhashtable_lookup_fast(&vg->vni_hash, &vni, + vxlan_vni_rht_params); + if (!vninode) + return 0; + + ret = vxlan_vni_update_group(vxlan, vninode, group, false, changed, + extack); + if (ret) + return ret; + + if (changed) + vxlan_vnifilter_notify(vxlan, vninode, RTM_NEWTUNNEL); + + return 0; +} + +static void __vxlan_vni_add_list(struct vxlan_vni_group *vg, + struct vxlan_vni_node *v) +{ + struct list_head *headp, *hpos; + struct vxlan_vni_node *vent; + + headp = &vg->vni_list; + list_for_each_prev(hpos, headp) { + vent = list_entry(hpos, struct vxlan_vni_node, vlist); + if (be32_to_cpu(v->vni) < be32_to_cpu(vent->vni)) + continue; + else + break; + } + list_add_rcu(&v->vlist, hpos); + vg->num_vnis++; +} + +static void __vxlan_vni_del_list(struct vxlan_vni_group *vg, + struct vxlan_vni_node *v) +{ + list_del_rcu(&v->vlist); + vg->num_vnis--; +} + +static struct vxlan_vni_node *vxlan_vni_alloc(struct vxlan_dev *vxlan, + __be32 vni) +{ + struct vxlan_vni_node *vninode; + + vninode = kzalloc(sizeof(*vninode), GFP_ATOMIC); + if (!vninode) + return NULL; + vninode->stats = netdev_alloc_pcpu_stats(struct vxlan_vni_stats_pcpu); + if (!vninode->stats) { + kfree(vninode); + return NULL; + } + vninode->vni = vni; + vninode->hlist4.vxlan = vxlan; +#if IS_ENABLED(CONFIG_IPV6) + vninode->hlist6.vxlan = vxlan; +#endif + + return vninode; +} + +static int vxlan_vni_add(struct vxlan_dev *vxlan, + struct vxlan_vni_group *vg, + u32 vni, union vxlan_addr *group, + struct netlink_ext_ack *extack) +{ + struct vxlan_vni_node *vninode; + __be32 v = cpu_to_be32(vni); + bool changed = false; + int err = 0; + + if (vxlan_vnifilter_lookup(vxlan, v)) + return vxlan_vni_update(vxlan, vg, v, group, &changed, extack); + + err = vxlan_vni_in_use(vxlan->net, vxlan, &vxlan->cfg, v); + if (err) { + NL_SET_ERR_MSG(extack, "VNI in use"); + return err; + } + + vninode = vxlan_vni_alloc(vxlan, v); + if (!vninode) + return -ENOMEM; + + err = rhashtable_lookup_insert_fast(&vg->vni_hash, + &vninode->vnode, + vxlan_vni_rht_params); + if (err) { + kfree(vninode); + return err; + } + + __vxlan_vni_add_list(vg, vninode); + + if (vxlan->dev->flags & IFF_UP) + vxlan_vs_add_del_vninode(vxlan, vninode, false); + + err = vxlan_vni_update_group(vxlan, vninode, group, true, &changed, + extack); + + if (changed) + vxlan_vnifilter_notify(vxlan, vninode, RTM_NEWTUNNEL); + + return err; +} + +static void vxlan_vni_node_rcu_free(struct rcu_head *rcu) +{ + struct vxlan_vni_node *v; + + v = container_of(rcu, struct vxlan_vni_node, rcu); + free_percpu(v->stats); + kfree(v); +} + +static int vxlan_vni_del(struct vxlan_dev *vxlan, + struct vxlan_vni_group *vg, + u32 vni, struct netlink_ext_ack *extack) +{ + struct vxlan_vni_node *vninode; + __be32 v = cpu_to_be32(vni); + int err = 0; + + vg = rtnl_dereference(vxlan->vnigrp); + + vninode = rhashtable_lookup_fast(&vg->vni_hash, &v, + vxlan_vni_rht_params); + if (!vninode) { + err = -ENOENT; + goto out; + } + + vxlan_vni_delete_group(vxlan, vninode); + + err = rhashtable_remove_fast(&vg->vni_hash, + &vninode->vnode, + vxlan_vni_rht_params); + if (err) + goto out; + + __vxlan_vni_del_list(vg, vninode); + + vxlan_vnifilter_notify(vxlan, vninode, RTM_DELTUNNEL); + + if (vxlan->dev->flags & IFF_UP) + vxlan_vs_add_del_vninode(vxlan, vninode, true); + + call_rcu(&vninode->rcu, vxlan_vni_node_rcu_free); + + return 0; +out: + return err; +} + +static int vxlan_vni_add_del(struct vxlan_dev *vxlan, __u32 start_vni, + __u32 end_vni, union vxlan_addr *group, + int cmd, struct netlink_ext_ack *extack) +{ + struct vxlan_vni_group *vg; + int v, err = 0; + + vg = rtnl_dereference(vxlan->vnigrp); + + for (v = start_vni; v <= end_vni; v++) { + switch (cmd) { + case RTM_NEWTUNNEL: + err = vxlan_vni_add(vxlan, vg, v, group, extack); + break; + case RTM_DELTUNNEL: + err = vxlan_vni_del(vxlan, vg, v, extack); + break; + default: + err = -EOPNOTSUPP; + break; + } + if (err) + goto out; + } + + return 0; +out: + return err; +} + +static int vxlan_process_vni_filter(struct vxlan_dev *vxlan, + struct nlattr *nlvnifilter, + int cmd, struct netlink_ext_ack *extack) +{ + struct nlattr *vattrs[VXLAN_VNIFILTER_ENTRY_MAX + 1]; + u32 vni_start = 0, vni_end = 0; + union vxlan_addr group; + int err; + + err = nla_parse_nested(vattrs, + VXLAN_VNIFILTER_ENTRY_MAX, + nlvnifilter, vni_filter_entry_policy, + extack); + if (err) + return err; + + if (vattrs[VXLAN_VNIFILTER_ENTRY_START]) { + vni_start = nla_get_u32(vattrs[VXLAN_VNIFILTER_ENTRY_START]); + vni_end = vni_start; + } + + if (vattrs[VXLAN_VNIFILTER_ENTRY_END]) + vni_end = nla_get_u32(vattrs[VXLAN_VNIFILTER_ENTRY_END]); + + if (!vni_start && !vni_end) { + NL_SET_ERR_MSG_ATTR(extack, nlvnifilter, + "vni start nor end found in vni entry"); + return -EINVAL; + } + + if (vattrs[VXLAN_VNIFILTER_ENTRY_GROUP]) { + group.sin.sin_addr.s_addr = + nla_get_in_addr(vattrs[VXLAN_VNIFILTER_ENTRY_GROUP]); + group.sa.sa_family = AF_INET; + } else if (vattrs[VXLAN_VNIFILTER_ENTRY_GROUP6]) { + group.sin6.sin6_addr = + nla_get_in6_addr(vattrs[VXLAN_VNIFILTER_ENTRY_GROUP6]); + group.sa.sa_family = AF_INET6; + } else { + memset(&group, 0, sizeof(group)); + } + + if (vxlan_addr_multicast(&group) && !vxlan->default_dst.remote_ifindex) { + NL_SET_ERR_MSG(extack, + "Local interface required for multicast remote group"); + + return -EINVAL; + } + + err = vxlan_vni_add_del(vxlan, vni_start, vni_end, &group, cmd, + extack); + if (err) + return err; + + return 0; +} + +void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan) +{ + struct vxlan_vni_node *v, *tmp; + struct vxlan_vni_group *vg; + + vg = rtnl_dereference(vxlan->vnigrp); + list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { + rhashtable_remove_fast(&vg->vni_hash, &v->vnode, + vxlan_vni_rht_params); + hlist_del_init_rcu(&v->hlist4.hlist); +#if IS_ENABLED(CONFIG_IPV6) + hlist_del_init_rcu(&v->hlist6.hlist); +#endif + __vxlan_vni_del_list(vg, v); + vxlan_vnifilter_notify(vxlan, v, RTM_DELTUNNEL); + call_rcu(&v->rcu, vxlan_vni_node_rcu_free); + } + rhashtable_destroy(&vg->vni_hash); + kfree(vg); +} + +int vxlan_vnigroup_init(struct vxlan_dev *vxlan) +{ + struct vxlan_vni_group *vg; + int ret; + + vg = kzalloc(sizeof(*vg), GFP_KERNEL); + if (!vg) + return -ENOMEM; + ret = rhashtable_init(&vg->vni_hash, &vxlan_vni_rht_params); + if (ret) { + kfree(vg); + return ret; + } + INIT_LIST_HEAD(&vg->vni_list); + rcu_assign_pointer(vxlan->vnigrp, vg); + + return 0; +} + +static int vxlan_vnifilter_process(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) +{ + struct net *net = sock_net(skb->sk); + struct tunnel_msg *tmsg; + struct vxlan_dev *vxlan; + struct net_device *dev; + struct nlattr *attr; + int err, vnis = 0; + int rem; + + /* this should validate the header and check for remaining bytes */ + err = nlmsg_parse(nlh, sizeof(*tmsg), NULL, VXLAN_VNIFILTER_MAX, + vni_filter_policy, extack); + if (err < 0) + return err; + + tmsg = nlmsg_data(nlh); + dev = __dev_get_by_index(net, tmsg->ifindex); + if (!dev) + return -ENODEV; + + if (!netif_is_vxlan(dev)) { + NL_SET_ERR_MSG_MOD(extack, "The device is not a vxlan device"); + return -EINVAL; + } + + vxlan = netdev_priv(dev); + + if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) + return -EOPNOTSUPP; + + nlmsg_for_each_attr(attr, nlh, sizeof(*tmsg), rem) { + switch (nla_type(attr)) { + case VXLAN_VNIFILTER_ENTRY: + err = vxlan_process_vni_filter(vxlan, attr, + nlh->nlmsg_type, extack); + break; + default: + continue; + } + vnis++; + if (err) + break; + } + + if (!vnis) { + NL_SET_ERR_MSG_MOD(extack, "No vnis found to process"); + err = -EINVAL; + } + + return err; +} + +void vxlan_vnifilter_init(void) +{ + rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_GETTUNNEL, NULL, + vxlan_vnifilter_dump, 0); + rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_NEWTUNNEL, + vxlan_vnifilter_process, NULL, 0); + rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_DELTUNNEL, + vxlan_vnifilter_process, NULL, 0); +} + +void vxlan_vnifilter_uninit(void) +{ + rtnl_unregister(PF_BRIDGE, RTM_GETTUNNEL); + rtnl_unregister(PF_BRIDGE, RTM_NEWTUNNEL); + rtnl_unregister(PF_BRIDGE, RTM_DELTUNNEL); +} diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c index 6a142dc85c37..76c6b4f89890 100644 --- a/drivers/net/wan/lmc/lmc_main.c +++ b/drivers/net/wan/lmc/lmc_main.c @@ -57,6 +57,7 @@ #include #include #include +#include //#include #define DRIVER_MAJOR_VERSION 1 @@ -1968,7 +1969,7 @@ static void lmc_driver_timeout(struct net_device *dev, unsigned int txqueue) printk("%s: Xmitter busy|\n", dev->name); sc->extra_stats.tx_tbusy_calls++; - if (jiffies - dev_trans_start(dev) < TX_TIMEOUT) + if (time_is_before_jiffies(dev_trans_start(dev) + TX_TIMEOUT)) goto bug_out; /* diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 141c1b5a7b1f..9cabd342d156 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -104,7 +104,7 @@ static void ar5523_cmd_rx_cb(struct urb *urb) } if (urb->actual_length < sizeof(struct ar5523_cmd_hdr)) { - ar5523_err(ar, "RX USB to short.\n"); + ar5523_err(ar, "RX USB too short.\n"); goto skip; } diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 8f5b8eb368fa..9e1f483e1362 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -75,6 +75,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA988X_BOARD_DATA_SZ, .board_ext_size = QCA988X_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -111,6 +112,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA988X_BOARD_DATA_SZ, .board_ext_size = QCA988X_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -148,6 +150,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA9887_BOARD_DATA_SZ, .board_ext_size = QCA9887_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -184,6 +187,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca6174_sdio_ops, .hw_clk = qca6174_clk, .target_cpu_freq = 176000000, @@ -216,6 +220,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -252,6 +257,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -288,6 +294,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -325,6 +332,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca6174_ops, .hw_clk = qca6174_clk, .target_cpu_freq = 176000000, @@ -370,6 +378,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ, }, .sw_decrypt_mcast_mgmt = true, + .rx_desc_ops = &qca99x0_rx_desc_ops, .hw_ops = &qca99x0_ops, .decap_align_bytes = 1, .spectral_bin_discard = 4, @@ -415,6 +424,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .ext_board_size = QCA99X0_EXT_BOARD_DATA_SZ, }, .sw_decrypt_mcast_mgmt = true, + .rx_desc_ops = &qca99x0_rx_desc_ops, .hw_ops = &qca99x0_ops, .decap_align_bytes = 1, .spectral_bin_discard = 12, @@ -461,6 +471,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ, }, .sw_decrypt_mcast_mgmt = true, + .rx_desc_ops = &qca99x0_rx_desc_ops, .hw_ops = &qca99x0_ops, .decap_align_bytes = 1, .spectral_bin_discard = 12, @@ -501,6 +512,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA9377_BOARD_DATA_SZ, .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -537,6 +549,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA9377_BOARD_DATA_SZ, .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca6174_ops, .hw_clk = qca6174_clk, .target_cpu_freq = 176000000, @@ -575,6 +588,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA9377_BOARD_DATA_SZ, .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca6174_ops, .hw_clk = qca6174_clk, .target_cpu_freq = 176000000, @@ -611,6 +625,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_ext_size = QCA4019_BOARD_EXT_DATA_SZ, }, .sw_decrypt_mcast_mgmt = true, + .rx_desc_ops = &qca99x0_rx_desc_ops, .hw_ops = &qca99x0_ops, .decap_align_bytes = 1, .spectral_bin_discard = 4, @@ -643,6 +658,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dir = WCN3990_HW_1_0_FW_DIR, }, .sw_decrypt_mcast_mgmt = true, + .rx_desc_ops = &wcn3990_rx_desc_ops, .hw_ops = &wcn3990_ops, .decap_align_bytes = 1, .num_peers = TARGET_HL_TLV_NUM_PEERS, diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 127b4e4980ef..907e1e13871a 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -131,6 +131,159 @@ static const enum htt_t2h_msg_type htt_10_4_t2h_msg_types[] = { HTT_T2H_MSG_TYPE_PEER_STATS, }; +const struct ath10k_htt_rx_desc_ops qca988x_rx_desc_ops = { + .rx_desc_size = sizeof(struct htt_rx_desc_v1), + .rx_desc_msdu_payload_offset = offsetof(struct htt_rx_desc_v1, msdu_payload) +}; + +static int ath10k_qca99x0_rx_desc_get_l3_pad_bytes(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc = container_of(rxd, + struct htt_rx_desc_v1, + base); + + return MS(__le32_to_cpu(rx_desc->msdu_end.qca99x0.info1), + RX_MSDU_END_INFO1_L3_HDR_PAD); +} + +static bool ath10k_qca99x0_rx_desc_msdu_limit_error(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc = container_of(rxd, + struct htt_rx_desc_v1, + base); + + return !!(rx_desc->msdu_end.common.info0 & + __cpu_to_le32(RX_MSDU_END_INFO0_MSDU_LIMIT_ERR)); +} + +const struct ath10k_htt_rx_desc_ops qca99x0_rx_desc_ops = { + .rx_desc_size = sizeof(struct htt_rx_desc_v1), + .rx_desc_msdu_payload_offset = offsetof(struct htt_rx_desc_v1, msdu_payload), + + .rx_desc_get_l3_pad_bytes = ath10k_qca99x0_rx_desc_get_l3_pad_bytes, + .rx_desc_get_msdu_limit_error = ath10k_qca99x0_rx_desc_msdu_limit_error, +}; + +static void ath10k_rx_desc_wcn3990_get_offsets(struct htt_rx_ring_rx_desc_offsets *off) +{ +#define desc_offset(x) (offsetof(struct htt_rx_desc_v2, x) / 4) + off->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status)); + off->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload)); + off->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start)); + off->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end)); + off->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start)); + off->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end)); + off->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start)); + off->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end)); + off->rx_attention_offset = __cpu_to_le16(desc_offset(attention)); + off->frag_info_offset = __cpu_to_le16(desc_offset(frag_info)); +#undef desc_offset +} + +static struct htt_rx_desc * +ath10k_rx_desc_wcn3990_from_raw_buffer(void *buff) +{ + return &((struct htt_rx_desc_v2 *)buff)->base; +} + +static struct rx_attention * +ath10k_rx_desc_wcn3990_get_attention(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->attention; +} + +static struct rx_frag_info_common * +ath10k_rx_desc_wcn3990_get_frag_info(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->frag_info.common; +} + +static struct rx_mpdu_start * +ath10k_rx_desc_wcn3990_get_mpdu_start(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->mpdu_start; +} + +static struct rx_mpdu_end * +ath10k_rx_desc_wcn3990_get_mpdu_end(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->mpdu_end; +} + +static struct rx_msdu_start_common * +ath10k_rx_desc_wcn3990_get_msdu_start(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->msdu_start.common; +} + +static struct rx_msdu_end_common * +ath10k_rx_desc_wcn3990_get_msdu_end(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->msdu_end.common; +} + +static struct rx_ppdu_start * +ath10k_rx_desc_wcn3990_get_ppdu_start(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->ppdu_start; +} + +static struct rx_ppdu_end_common * +ath10k_rx_desc_wcn3990_get_ppdu_end(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->ppdu_end.common; +} + +static u8 * +ath10k_rx_desc_wcn3990_get_rx_hdr_status(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return rx_desc->rx_hdr_status; +} + +static u8 * +ath10k_rx_desc_wcn3990_get_msdu_payload(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return rx_desc->msdu_payload; +} + +const struct ath10k_htt_rx_desc_ops wcn3990_rx_desc_ops = { + .rx_desc_size = sizeof(struct htt_rx_desc_v2), + .rx_desc_msdu_payload_offset = offsetof(struct htt_rx_desc_v2, msdu_payload), + + .rx_desc_from_raw_buffer = ath10k_rx_desc_wcn3990_from_raw_buffer, + .rx_desc_get_offsets = ath10k_rx_desc_wcn3990_get_offsets, + .rx_desc_get_attention = ath10k_rx_desc_wcn3990_get_attention, + .rx_desc_get_frag_info = ath10k_rx_desc_wcn3990_get_frag_info, + .rx_desc_get_mpdu_start = ath10k_rx_desc_wcn3990_get_mpdu_start, + .rx_desc_get_mpdu_end = ath10k_rx_desc_wcn3990_get_mpdu_end, + .rx_desc_get_msdu_start = ath10k_rx_desc_wcn3990_get_msdu_start, + .rx_desc_get_msdu_end = ath10k_rx_desc_wcn3990_get_msdu_end, + .rx_desc_get_ppdu_start = ath10k_rx_desc_wcn3990_get_ppdu_start, + .rx_desc_get_ppdu_end = ath10k_rx_desc_wcn3990_get_ppdu_end, + .rx_desc_get_rx_hdr_status = ath10k_rx_desc_wcn3990_get_rx_hdr_status, + .rx_desc_get_msdu_payload = ath10k_rx_desc_wcn3990_get_msdu_payload, +}; + int ath10k_htt_connect(struct ath10k_htt *htt) { struct ath10k_htc_svc_conn_req conn_req; diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 9a3a8907389b..f06cf39204e2 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -240,14 +240,7 @@ enum htt_rx_ring_flags { #define HTT_RX_RING_FILL_LEVEL (((HTT_RX_RING_SIZE) / 2) - 1) #define HTT_RX_RING_FILL_LEVEL_DUAL_MAC (HTT_RX_RING_SIZE - 1) -struct htt_rx_ring_setup_ring32 { - __le32 fw_idx_shadow_reg_paddr; - __le32 rx_ring_base_paddr; - __le16 rx_ring_len; /* in 4-byte words */ - __le16 rx_ring_bufsize; /* rx skb size - in bytes */ - __le16 flags; /* %HTT_RX_RING_FLAGS_ */ - __le16 fw_idx_init_val; - +struct htt_rx_ring_rx_desc_offsets { /* the following offsets are in 4-byte units */ __le16 mac80211_hdr_offset; __le16 msdu_payload_offset; @@ -261,6 +254,17 @@ struct htt_rx_ring_setup_ring32 { __le16 frag_info_offset; } __packed; +struct htt_rx_ring_setup_ring32 { + __le32 fw_idx_shadow_reg_paddr; + __le32 rx_ring_base_paddr; + __le16 rx_ring_len; /* in 4-byte words */ + __le16 rx_ring_bufsize; /* rx skb size - in bytes */ + __le16 flags; /* %HTT_RX_RING_FLAGS_ */ + __le16 fw_idx_init_val; + + struct htt_rx_ring_rx_desc_offsets offsets; +} __packed; + struct htt_rx_ring_setup_ring64 { __le64 fw_idx_shadow_reg_paddr; __le64 rx_ring_base_paddr; @@ -269,17 +273,7 @@ struct htt_rx_ring_setup_ring64 { __le16 flags; /* %HTT_RX_RING_FLAGS_ */ __le16 fw_idx_init_val; - /* the following offsets are in 4-byte units */ - __le16 mac80211_hdr_offset; - __le16 msdu_payload_offset; - __le16 ppdu_start_offset; - __le16 ppdu_end_offset; - __le16 mpdu_start_offset; - __le16 mpdu_end_offset; - __le16 msdu_start_offset; - __le16 msdu_end_offset; - __le16 rx_attention_offset; - __le16 frag_info_offset; + struct htt_rx_ring_rx_desc_offsets offsets; } __packed; struct htt_rx_ring_setup_hdr { @@ -2075,12 +2069,22 @@ static inline bool ath10k_htt_rx_proc_rx_frag_ind(struct ath10k_htt *htt, return htt->rx_ops->htt_rx_proc_rx_frag_ind(htt, rx, skb); } +/* the driver strongly assumes that the rx header status be 64 bytes long, + * so all possible rx_desc structures must respect this assumption. + */ #define RX_HTT_HDR_STATUS_LEN 64 -/* This structure layout is programmed via rx ring setup +/* The rx descriptor structure layout is programmed via rx ring setup * so that FW knows how to transfer the rx descriptor to the host. - * Buffers like this are placed on the rx ring. + * Unfortunately, though, QCA6174's firmware doesn't currently behave correctly + * when modifying the structure layout of the rx descriptor beyond what it expects + * (even if it correctly programmed during the rx ring setup). + * Therefore we must keep two different memory layouts, abstract the rx descriptor + * representation and use ath10k_rx_desc_ops + * for correctly accessing rx descriptor data. */ + +/* base struct used for abstracting the rx descritor representation */ struct htt_rx_desc { union { /* This field is filled on the host using the msdu buffer @@ -2089,6 +2093,13 @@ struct htt_rx_desc { struct fw_rx_desc_base fw_desc; u32 pad; } __packed; +} __packed; + +/* rx descriptor for wcn3990 and possibly extensible for newer cards + * Buffers like this are placed on the rx ring. + */ +struct htt_rx_desc_v2 { + struct htt_rx_desc base; struct { struct rx_attention attention; struct rx_frag_info frag_info; @@ -2103,6 +2114,240 @@ struct htt_rx_desc { u8 msdu_payload[]; }; +/* QCA6174, QCA988x, QCA99x0 dedicated rx descriptor to make sure their firmware + * works correctly. We keep a single rx descriptor for all these three + * families of cards because from tests it seems to be the most stable solution, + * e.g. having a rx descriptor only for QCA6174 seldom caused firmware crashes + * during some tests. + * Buffers like this are placed on the rx ring. + */ +struct htt_rx_desc_v1 { + struct htt_rx_desc base; + struct { + struct rx_attention attention; + struct rx_frag_info_v1 frag_info; + struct rx_mpdu_start mpdu_start; + struct rx_msdu_start_v1 msdu_start; + struct rx_msdu_end_v1 msdu_end; + struct rx_mpdu_end mpdu_end; + struct rx_ppdu_start ppdu_start; + struct rx_ppdu_end_v1 ppdu_end; + } __packed; + u8 rx_hdr_status[RX_HTT_HDR_STATUS_LEN]; + u8 msdu_payload[]; +}; + +/* rx_desc abstraction */ +struct ath10k_htt_rx_desc_ops { + /* These fields are mandatory, they must be specified in any instance */ + + /* sizeof() of the rx_desc structure used by this hw */ + size_t rx_desc_size; + + /* offset of msdu_payload inside the rx_desc structure used by this hw */ + size_t rx_desc_msdu_payload_offset; + + /* These fields are options. + * When a field is not provided the default implementation gets used + * (see the ath10k_rx_desc_* operations below for more info about the defaults) + */ + bool (*rx_desc_get_msdu_limit_error)(struct htt_rx_desc *rxd); + int (*rx_desc_get_l3_pad_bytes)(struct htt_rx_desc *rxd); + + /* Safely cast from a void* buffer containing an rx descriptor + * to the proper rx_desc structure + */ + struct htt_rx_desc *(*rx_desc_from_raw_buffer)(void *buff); + + void (*rx_desc_get_offsets)(struct htt_rx_ring_rx_desc_offsets *offs); + struct rx_attention *(*rx_desc_get_attention)(struct htt_rx_desc *rxd); + struct rx_frag_info_common *(*rx_desc_get_frag_info)(struct htt_rx_desc *rxd); + struct rx_mpdu_start *(*rx_desc_get_mpdu_start)(struct htt_rx_desc *rxd); + struct rx_mpdu_end *(*rx_desc_get_mpdu_end)(struct htt_rx_desc *rxd); + struct rx_msdu_start_common *(*rx_desc_get_msdu_start)(struct htt_rx_desc *rxd); + struct rx_msdu_end_common *(*rx_desc_get_msdu_end)(struct htt_rx_desc *rxd); + struct rx_ppdu_start *(*rx_desc_get_ppdu_start)(struct htt_rx_desc *rxd); + struct rx_ppdu_end_common *(*rx_desc_get_ppdu_end)(struct htt_rx_desc *rxd); + u8 *(*rx_desc_get_rx_hdr_status)(struct htt_rx_desc *rxd); + u8 *(*rx_desc_get_msdu_payload)(struct htt_rx_desc *rxd); +}; + +extern const struct ath10k_htt_rx_desc_ops qca988x_rx_desc_ops; +extern const struct ath10k_htt_rx_desc_ops qca99x0_rx_desc_ops; +extern const struct ath10k_htt_rx_desc_ops wcn3990_rx_desc_ops; + +static inline int +ath10k_htt_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + if (hw->rx_desc_ops->rx_desc_get_l3_pad_bytes) + return hw->rx_desc_ops->rx_desc_get_l3_pad_bytes(rxd); + return 0; +} + +static inline bool +ath10k_htt_rx_desc_msdu_limit_error(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + if (hw->rx_desc_ops->rx_desc_get_msdu_limit_error) + return hw->rx_desc_ops->rx_desc_get_msdu_limit_error(rxd); + return false; +} + +/* The default implementation of all these getters is using the old rx_desc, + * so that it is easier to define the ath10k_htt_rx_desc_ops instances. + * But probably, if new wireless cards must be supported, it would be better + * to switch the default implementation to the new rx_desc, since this would + * make the extension easier . + */ +static inline struct htt_rx_desc * +ath10k_htt_rx_desc_from_raw_buffer(struct ath10k_hw_params *hw, void *buff) +{ + if (hw->rx_desc_ops->rx_desc_from_raw_buffer) + return hw->rx_desc_ops->rx_desc_from_raw_buffer(buff); + return &((struct htt_rx_desc_v1 *)buff)->base; +} + +static inline void +ath10k_htt_rx_desc_get_offsets(struct ath10k_hw_params *hw, + struct htt_rx_ring_rx_desc_offsets *off) +{ + if (hw->rx_desc_ops->rx_desc_get_offsets) { + hw->rx_desc_ops->rx_desc_get_offsets(off); + } else { +#define desc_offset(x) (offsetof(struct htt_rx_desc_v1, x) / 4) + off->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status)); + off->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload)); + off->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start)); + off->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end)); + off->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start)); + off->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end)); + off->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start)); + off->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end)); + off->rx_attention_offset = __cpu_to_le16(desc_offset(attention)); + off->frag_info_offset = __cpu_to_le16(desc_offset(frag_info)); +#undef desc_offset + } +} + +static inline struct rx_attention * +ath10k_htt_rx_desc_get_attention(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_attention) + return hw->rx_desc_ops->rx_desc_get_attention(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->attention; +} + +static inline struct rx_frag_info_common * +ath10k_htt_rx_desc_get_frag_info(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_frag_info) + return hw->rx_desc_ops->rx_desc_get_frag_info(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->frag_info.common; +} + +static inline struct rx_mpdu_start * +ath10k_htt_rx_desc_get_mpdu_start(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_mpdu_start) + return hw->rx_desc_ops->rx_desc_get_mpdu_start(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->mpdu_start; +} + +static inline struct rx_mpdu_end * +ath10k_htt_rx_desc_get_mpdu_end(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_mpdu_end) + return hw->rx_desc_ops->rx_desc_get_mpdu_end(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->mpdu_end; +} + +static inline struct rx_msdu_start_common * +ath10k_htt_rx_desc_get_msdu_start(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_msdu_start) + return hw->rx_desc_ops->rx_desc_get_msdu_start(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->msdu_start.common; +} + +static inline struct rx_msdu_end_common * +ath10k_htt_rx_desc_get_msdu_end(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_msdu_end) + return hw->rx_desc_ops->rx_desc_get_msdu_end(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->msdu_end.common; +} + +static inline struct rx_ppdu_start * +ath10k_htt_rx_desc_get_ppdu_start(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_ppdu_start) + return hw->rx_desc_ops->rx_desc_get_ppdu_start(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->ppdu_start; +} + +static inline struct rx_ppdu_end_common * +ath10k_htt_rx_desc_get_ppdu_end(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_ppdu_end) + return hw->rx_desc_ops->rx_desc_get_ppdu_end(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->ppdu_end.common; +} + +static inline u8 * +ath10k_htt_rx_desc_get_rx_hdr_status(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_rx_hdr_status) + return hw->rx_desc_ops->rx_desc_get_rx_hdr_status(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return rx_desc->rx_hdr_status; +} + +static inline u8 * +ath10k_htt_rx_desc_get_msdu_payload(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_msdu_payload) + return hw->rx_desc_ops->rx_desc_get_msdu_payload(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return rx_desc->msdu_payload; +} + #define HTT_RX_DESC_HL_INFO_SEQ_NUM_MASK 0x00000fff #define HTT_RX_DESC_HL_INFO_SEQ_NUM_LSB 0 #define HTT_RX_DESC_HL_INFO_ENCRYPTED_MASK 0x00001000 @@ -2136,7 +2381,14 @@ struct htt_rx_chan_info { * rounded up to a cache line size. */ #define HTT_RX_BUF_SIZE 2048 -#define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc)) + +/* The HTT_RX_MSDU_SIZE can't be statically computed anymore, + * because it depends on the underlying device rx_desc representation + */ +static inline int ath10k_htt_rx_msdu_size(struct ath10k_hw_params *hw) +{ + return HTT_RX_BUF_SIZE - (int)hw->rx_desc_ops->rx_desc_size; +} /* Refill a bunch of RX buffers for each refill round so that FW/HW can handle * aggregated traffic more nicely. diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index adbaeb67eedf..771252dd6d4e 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -21,7 +21,10 @@ #define HTT_RX_RING_REFILL_RESCHED_MS 5 -static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb); +/* shortcut to interpret a raw memory buffer as a rx descriptor */ +#define HTT_RX_BUF_TO_RX_DESC(hw, buf) ath10k_htt_rx_desc_from_raw_buffer(hw, buf) + +static int ath10k_htt_rx_get_csum_state(struct ath10k_hw_params *hw, struct sk_buff *skb); static struct sk_buff * ath10k_htt_rx_find_skb_paddr(struct ath10k *ar, u64 paddr) @@ -128,6 +131,7 @@ static void *ath10k_htt_get_vaddr_ring_64(struct ath10k_htt *htt) static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) { + struct ath10k_hw_params *hw = &htt->ar->hw_params; struct htt_rx_desc *rx_desc; struct ath10k_skb_rxcb *rxcb; struct sk_buff *skb; @@ -163,8 +167,8 @@ static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) skb->data); /* Clear rx_desc attention word before posting to Rx ring */ - rx_desc = (struct htt_rx_desc *)skb->data; - rx_desc->attention.flags = __cpu_to_le32(0); + rx_desc = HTT_RX_BUF_TO_RX_DESC(hw, skb->data); + ath10k_htt_rx_desc_get_attention(hw, rx_desc)->flags = __cpu_to_le32(0); paddr = dma_map_single(htt->ar->dev, skb->data, skb->len + skb_tailroom(skb), @@ -343,9 +347,14 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, struct sk_buff_head *amsdu) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; int msdu_len, msdu_chaining = 0; struct sk_buff *msdu; struct htt_rx_desc *rx_desc; + struct rx_attention *rx_desc_attention; + struct rx_frag_info_common *rx_desc_frag_info_common; + struct rx_msdu_start_common *rx_desc_msdu_start_common; + struct rx_msdu_end_common *rx_desc_msdu_end_common; lockdep_assert_held(&htt->rx_ring.lock); @@ -360,13 +369,18 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, __skb_queue_tail(amsdu, msdu); - rx_desc = (struct htt_rx_desc *)msdu->data; + rx_desc = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data); + rx_desc_attention = ath10k_htt_rx_desc_get_attention(hw, rx_desc); + rx_desc_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, + rx_desc); + rx_desc_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rx_desc); + rx_desc_frag_info_common = ath10k_htt_rx_desc_get_frag_info(hw, rx_desc); /* FIXME: we must report msdu payload since this is what caller * expects now */ - skb_put(msdu, offsetof(struct htt_rx_desc, msdu_payload)); - skb_pull(msdu, offsetof(struct htt_rx_desc, msdu_payload)); + skb_put(msdu, hw->rx_desc_ops->rx_desc_msdu_payload_offset); + skb_pull(msdu, hw->rx_desc_ops->rx_desc_msdu_payload_offset); /* * Sanity check - confirm the HW is finished filling in the @@ -376,24 +390,24 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, * To prevent the case that we handle a stale Rx descriptor, * just assert for now until we have a way to recover. */ - if (!(__le32_to_cpu(rx_desc->attention.flags) + if (!(__le32_to_cpu(rx_desc_attention->flags) & RX_ATTENTION_FLAGS_MSDU_DONE)) { __skb_queue_purge(amsdu); return -EIO; } - msdu_len_invalid = !!(__le32_to_cpu(rx_desc->attention.flags) + msdu_len_invalid = !!(__le32_to_cpu(rx_desc_attention->flags) & (RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR | RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR)); - msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.common.info0), + msdu_len = MS(__le32_to_cpu(rx_desc_msdu_start_common->info0), RX_MSDU_START_INFO0_MSDU_LENGTH); - msdu_chained = rx_desc->frag_info.ring2_more_count; + msdu_chained = rx_desc_frag_info_common->ring2_more_count; if (msdu_len_invalid) msdu_len = 0; skb_trim(msdu, 0); - skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE)); + skb_put(msdu, min(msdu_len, ath10k_htt_rx_msdu_size(hw))); msdu_len -= msdu->len; /* Note: Chained buffers do not contain rx descriptor */ @@ -411,11 +425,12 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu_chaining = 1; } - last_msdu = __le32_to_cpu(rx_desc->msdu_end.common.info0) & + last_msdu = __le32_to_cpu(rx_desc_msdu_end_common->info0) & RX_MSDU_END_INFO0_LAST_MSDU; - trace_ath10k_htt_rx_desc(ar, &rx_desc->attention, - sizeof(*rx_desc) - sizeof(u32)); + /* FIXME: why are we skipping the first part of the rx_desc? */ + trace_ath10k_htt_rx_desc(ar, (void *)rx_desc + sizeof(u32), + hw->rx_desc_ops->rx_desc_size - sizeof(u32)); if (last_msdu) break; @@ -480,6 +495,7 @@ static int ath10k_htt_rx_handle_amsdu_mon_32(struct ath10k_htt *htt, struct htt_rx_in_ord_msdu_desc **msdu_desc) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; u32 paddr; struct sk_buff *frag_buf; struct sk_buff *prev_frag_buf; @@ -488,12 +504,12 @@ static int ath10k_htt_rx_handle_amsdu_mon_32(struct ath10k_htt *htt, struct htt_rx_desc *rxd; int amsdu_len = __le16_to_cpu(ind_desc->msdu_len); - rxd = (void *)msdu->data; - trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd)); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data); + trace_ath10k_htt_rx_desc(ar, rxd, hw->rx_desc_ops->rx_desc_size); - skb_put(msdu, sizeof(struct htt_rx_desc)); - skb_pull(msdu, sizeof(struct htt_rx_desc)); - skb_put(msdu, min(amsdu_len, HTT_RX_MSDU_SIZE)); + skb_put(msdu, hw->rx_desc_ops->rx_desc_size); + skb_pull(msdu, hw->rx_desc_ops->rx_desc_size); + skb_put(msdu, min(amsdu_len, ath10k_htt_rx_msdu_size(hw))); amsdu_len -= msdu->len; last_frag = ind_desc->reserved; @@ -556,6 +572,7 @@ ath10k_htt_rx_handle_amsdu_mon_64(struct ath10k_htt *htt, struct htt_rx_in_ord_msdu_desc_ext **msdu_desc) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; u64 paddr; struct sk_buff *frag_buf; struct sk_buff *prev_frag_buf; @@ -564,12 +581,12 @@ ath10k_htt_rx_handle_amsdu_mon_64(struct ath10k_htt *htt, struct htt_rx_desc *rxd; int amsdu_len = __le16_to_cpu(ind_desc->msdu_len); - rxd = (void *)msdu->data; - trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd)); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data); + trace_ath10k_htt_rx_desc(ar, rxd, hw->rx_desc_ops->rx_desc_size); - skb_put(msdu, sizeof(struct htt_rx_desc)); - skb_pull(msdu, sizeof(struct htt_rx_desc)); - skb_put(msdu, min(amsdu_len, HTT_RX_MSDU_SIZE)); + skb_put(msdu, hw->rx_desc_ops->rx_desc_size); + skb_pull(msdu, hw->rx_desc_ops->rx_desc_size); + skb_put(msdu, min(amsdu_len, ath10k_htt_rx_msdu_size(hw))); amsdu_len -= msdu->len; last_frag = ind_desc->reserved; @@ -631,8 +648,10 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt, struct sk_buff_head *list) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_in_ord_msdu_desc *msdu_desc = ev->msdu_descs32; struct htt_rx_desc *rxd; + struct rx_attention *rxd_attention; struct sk_buff *msdu; int msdu_count, ret; bool is_offload; @@ -667,15 +686,16 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt, __skb_queue_tail(list, msdu); if (!is_offload) { - rxd = (void *)msdu->data; + rxd = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data); + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); - trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd)); + trace_ath10k_htt_rx_desc(ar, rxd, hw->rx_desc_ops->rx_desc_size); - skb_put(msdu, sizeof(*rxd)); - skb_pull(msdu, sizeof(*rxd)); + skb_put(msdu, hw->rx_desc_ops->rx_desc_size); + skb_pull(msdu, hw->rx_desc_ops->rx_desc_size); skb_put(msdu, __le16_to_cpu(msdu_desc->msdu_len)); - if (!(__le32_to_cpu(rxd->attention.flags) & + if (!(__le32_to_cpu(rxd_attention->flags) & RX_ATTENTION_FLAGS_MSDU_DONE)) { ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n"); return -EIO; @@ -693,8 +713,10 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt, struct sk_buff_head *list) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_in_ord_msdu_desc_ext *msdu_desc = ev->msdu_descs64; struct htt_rx_desc *rxd; + struct rx_attention *rxd_attention; struct sk_buff *msdu; int msdu_count, ret; bool is_offload; @@ -728,15 +750,16 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt, __skb_queue_tail(list, msdu); if (!is_offload) { - rxd = (void *)msdu->data; + rxd = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data); + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); - trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd)); + trace_ath10k_htt_rx_desc(ar, rxd, hw->rx_desc_ops->rx_desc_size); - skb_put(msdu, sizeof(*rxd)); - skb_pull(msdu, sizeof(*rxd)); + skb_put(msdu, hw->rx_desc_ops->rx_desc_size); + skb_pull(msdu, hw->rx_desc_ops->rx_desc_size); skb_put(msdu, __le16_to_cpu(msdu_desc->msdu_len)); - if (!(__le32_to_cpu(rxd->attention.flags) & + if (!(__le32_to_cpu(rxd_attention->flags) & RX_ATTENTION_FLAGS_MSDU_DONE)) { ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n"); return -EIO; @@ -944,16 +967,32 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, struct ieee80211_rx_status *status, struct htt_rx_desc *rxd) { + struct ath10k_hw_params *hw = &ar->hw_params; + struct rx_attention *rxd_attention; + struct rx_mpdu_start *rxd_mpdu_start; + struct rx_mpdu_end *rxd_mpdu_end; + struct rx_msdu_start_common *rxd_msdu_start_common; + struct rx_msdu_end_common *rxd_msdu_end_common; + struct rx_ppdu_start *rxd_ppdu_start; struct ieee80211_supported_band *sband; u8 cck, rate, bw, sgi, mcs, nss; + u8 *rxd_msdu_payload; u8 preamble = 0; u8 group_id; u32 info1, info2, info3; u32 stbc, nsts_su; - info1 = __le32_to_cpu(rxd->ppdu_start.info1); - info2 = __le32_to_cpu(rxd->ppdu_start.info2); - info3 = __le32_to_cpu(rxd->ppdu_start.info3); + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); + rxd_mpdu_start = ath10k_htt_rx_desc_get_mpdu_start(hw, rxd); + rxd_mpdu_end = ath10k_htt_rx_desc_get_mpdu_end(hw, rxd); + rxd_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, rxd); + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + rxd_ppdu_start = ath10k_htt_rx_desc_get_ppdu_start(hw, rxd); + rxd_msdu_payload = ath10k_htt_rx_desc_get_msdu_payload(hw, rxd); + + info1 = __le32_to_cpu(rxd_ppdu_start->info1); + info2 = __le32_to_cpu(rxd_ppdu_start->info2); + info3 = __le32_to_cpu(rxd_ppdu_start->info3); preamble = MS(info1, RX_PPDU_START_INFO1_PREAMBLE_TYPE); @@ -1022,24 +1061,24 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, if (mcs > 0x09) { ath10k_warn(ar, "invalid MCS received %u\n", mcs); ath10k_warn(ar, "rxd %08x mpdu start %08x %08x msdu start %08x %08x ppdu start %08x %08x %08x %08x %08x\n", - __le32_to_cpu(rxd->attention.flags), - __le32_to_cpu(rxd->mpdu_start.info0), - __le32_to_cpu(rxd->mpdu_start.info1), - __le32_to_cpu(rxd->msdu_start.common.info0), - __le32_to_cpu(rxd->msdu_start.common.info1), - rxd->ppdu_start.info0, - __le32_to_cpu(rxd->ppdu_start.info1), - __le32_to_cpu(rxd->ppdu_start.info2), - __le32_to_cpu(rxd->ppdu_start.info3), - __le32_to_cpu(rxd->ppdu_start.info4)); + __le32_to_cpu(rxd_attention->flags), + __le32_to_cpu(rxd_mpdu_start->info0), + __le32_to_cpu(rxd_mpdu_start->info1), + __le32_to_cpu(rxd_msdu_start_common->info0), + __le32_to_cpu(rxd_msdu_start_common->info1), + rxd_ppdu_start->info0, + __le32_to_cpu(rxd_ppdu_start->info1), + __le32_to_cpu(rxd_ppdu_start->info2), + __le32_to_cpu(rxd_ppdu_start->info3), + __le32_to_cpu(rxd_ppdu_start->info4)); ath10k_warn(ar, "msdu end %08x mpdu end %08x\n", - __le32_to_cpu(rxd->msdu_end.common.info0), - __le32_to_cpu(rxd->mpdu_end.info0)); + __le32_to_cpu(rxd_msdu_end_common->info0), + __le32_to_cpu(rxd_mpdu_end->info0)); ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "rx desc msdu payload: ", - rxd->msdu_payload, 50); + rxd_msdu_payload, 50); } status->rate_idx = mcs; @@ -1059,6 +1098,10 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, static struct ieee80211_channel * ath10k_htt_rx_h_peer_channel(struct ath10k *ar, struct htt_rx_desc *rxd) { + struct ath10k_hw_params *hw = &ar->hw_params; + struct rx_attention *rxd_attention; + struct rx_msdu_end_common *rxd_msdu_end_common; + struct rx_mpdu_start *rxd_mpdu_start; struct ath10k_peer *peer; struct ath10k_vif *arvif; struct cfg80211_chan_def def; @@ -1069,15 +1112,19 @@ ath10k_htt_rx_h_peer_channel(struct ath10k *ar, struct htt_rx_desc *rxd) if (!rxd) return NULL; - if (rxd->attention.flags & + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + rxd_mpdu_start = ath10k_htt_rx_desc_get_mpdu_start(hw, rxd); + + if (rxd_attention->flags & __cpu_to_le32(RX_ATTENTION_FLAGS_PEER_IDX_INVALID)) return NULL; - if (!(rxd->msdu_end.common.info0 & + if (!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU))) return NULL; - peer_id = MS(__le32_to_cpu(rxd->mpdu_start.info0), + peer_id = MS(__le32_to_cpu(rxd_mpdu_start->info0), RX_MPDU_START_INFO0_PEER_IDX); peer = ath10k_peer_find_by_id(ar, peer_id); @@ -1167,14 +1214,16 @@ static void ath10k_htt_rx_h_signal(struct ath10k *ar, struct ieee80211_rx_status *status, struct htt_rx_desc *rxd) { + struct ath10k_hw_params *hw = &ar->hw_params; + struct rx_ppdu_start *rxd_ppdu_start = ath10k_htt_rx_desc_get_ppdu_start(hw, rxd); int i; for (i = 0; i < IEEE80211_MAX_CHAINS ; i++) { status->chains &= ~BIT(i); - if (rxd->ppdu_start.rssi_chains[i].pri20_mhz != 0x80) { + if (rxd_ppdu_start->rssi_chains[i].pri20_mhz != 0x80) { status->chain_signal[i] = ATH10K_DEFAULT_NOISE_FLOOR + - rxd->ppdu_start.rssi_chains[i].pri20_mhz; + rxd_ppdu_start->rssi_chains[i].pri20_mhz; status->chains |= BIT(i); } @@ -1182,7 +1231,7 @@ static void ath10k_htt_rx_h_signal(struct ath10k *ar, /* FIXME: Get real NF */ status->signal = ATH10K_DEFAULT_NOISE_FLOOR + - rxd->ppdu_start.rssi_comb; + rxd_ppdu_start->rssi_comb; status->flag &= ~RX_FLAG_NO_SIGNAL_VAL; } @@ -1190,13 +1239,18 @@ static void ath10k_htt_rx_h_mactime(struct ath10k *ar, struct ieee80211_rx_status *status, struct htt_rx_desc *rxd) { + struct ath10k_hw_params *hw = &ar->hw_params; + struct rx_ppdu_end_common *rxd_ppdu_end_common; + + rxd_ppdu_end_common = ath10k_htt_rx_desc_get_ppdu_end(hw, rxd); + /* FIXME: TSF is known only at the end of PPDU, in the last MPDU. This * means all prior MSDUs in a PPDU are reported to mac80211 without the * TSF. Is it worth holding frames until end of PPDU is known? * * FIXME: Can we get/compute 64bit TSF? */ - status->mactime = __le32_to_cpu(rxd->ppdu_end.common.tsf_timestamp); + status->mactime = __le32_to_cpu(rxd_ppdu_end_common->tsf_timestamp); status->flag |= RX_FLAG_MACTIME_END; } @@ -1206,7 +1260,9 @@ static void ath10k_htt_rx_h_ppdu(struct ath10k *ar, u32 vdev_id) { struct sk_buff *first; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_attention *rxd_attention; bool is_first_ppdu; bool is_last_ppdu; @@ -1214,11 +1270,14 @@ static void ath10k_htt_rx_h_ppdu(struct ath10k *ar, return; first = skb_peek(amsdu); - rxd = (void *)first->data - sizeof(*rxd); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)first->data - hw->rx_desc_ops->rx_desc_size); - is_first_ppdu = !!(rxd->attention.flags & + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); + + is_first_ppdu = !!(rxd_attention->flags & __cpu_to_le32(RX_ATTENTION_FLAGS_FIRST_MPDU)); - is_last_ppdu = !!(rxd->attention.flags & + is_last_ppdu = !!(rxd_attention->flags & __cpu_to_le32(RX_ATTENTION_FLAGS_LAST_MPDU)); if (is_first_ppdu) { @@ -1357,7 +1416,9 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar, const u8 first_hdr[64]) { struct ieee80211_hdr *hdr; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_msdu_end_common *rxd_msdu_end_common; size_t hdr_len; size_t crypto_len; bool is_first; @@ -1366,10 +1427,13 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar, int bytes_aligned = ar->hw_params.decap_align_bytes; u8 *qos; - rxd = (void *)msdu->data - sizeof(*rxd); - is_first = !!(rxd->msdu_end.common.info0 & + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - hw->rx_desc_ops->rx_desc_size); + + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + is_first = !!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU)); - is_last = !!(rxd->msdu_end.common.info0 & + is_last = !!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU)); /* Delivered decapped frame: @@ -1387,7 +1451,7 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar, * error packets. If limit exceeds, hw sends all remaining MSDUs as * a single last MSDU with this msdu limit error set. */ - msdu_limit_err = ath10k_rx_desc_msdu_limit_error(&ar->hw_params, rxd); + msdu_limit_err = ath10k_htt_rx_desc_msdu_limit_error(hw, rxd); /* If MSDU limit error happens, then don't warn on, the partial raw MSDU * without first MSDU is expected in that case, and handled later here. @@ -1479,6 +1543,7 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar, const u8 first_hdr[64], enum htt_rx_mpdu_encrypt_type enctype) { + struct ath10k_hw_params *hw = &ar->hw_params; struct ieee80211_hdr *hdr; struct htt_rx_desc *rxd; size_t hdr_len; @@ -1499,9 +1564,10 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar, */ /* pull decapped header and copy SA & DA */ - rxd = (void *)msdu->data - sizeof(*rxd); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, (void *)msdu->data - + hw->rx_desc_ops->rx_desc_size); - l3_pad_bytes = ath10k_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd); + l3_pad_bytes = ath10k_htt_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd); skb_put(msdu, l3_pad_bytes); hdr = (struct ieee80211_hdr *)(msdu->data + l3_pad_bytes); @@ -1537,18 +1603,25 @@ static void *ath10k_htt_rx_h_find_rfc1042(struct ath10k *ar, enum htt_rx_mpdu_encrypt_type enctype) { struct ieee80211_hdr *hdr; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_msdu_end_common *rxd_msdu_end_common; + u8 *rxd_rx_hdr_status; size_t hdr_len, crypto_len; void *rfc1042; bool is_first, is_last, is_amsdu; int bytes_aligned = ar->hw_params.decap_align_bytes; - rxd = (void *)msdu->data - sizeof(*rxd); - hdr = (void *)rxd->rx_hdr_status; + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - hw->rx_desc_ops->rx_desc_size); - is_first = !!(rxd->msdu_end.common.info0 & + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + rxd_rx_hdr_status = ath10k_htt_rx_desc_get_rx_hdr_status(hw, rxd); + hdr = (void *)rxd_rx_hdr_status; + + is_first = !!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU)); - is_last = !!(rxd->msdu_end.common.info0 & + is_last = !!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU)); is_amsdu = !(is_first && is_last); @@ -1574,6 +1647,7 @@ static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar, const u8 first_hdr[64], enum htt_rx_mpdu_encrypt_type enctype) { + struct ath10k_hw_params *hw = &ar->hw_params; struct ieee80211_hdr *hdr; struct ethhdr *eth; size_t hdr_len; @@ -1593,8 +1667,10 @@ static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar, if (WARN_ON_ONCE(!rfc1042)) return; - rxd = (void *)msdu->data - sizeof(*rxd); - l3_pad_bytes = ath10k_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - hw->rx_desc_ops->rx_desc_size); + + l3_pad_bytes = ath10k_htt_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd); skb_put(msdu, l3_pad_bytes); skb_pull(msdu, l3_pad_bytes); @@ -1635,6 +1711,7 @@ static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar, const u8 first_hdr[64], enum htt_rx_mpdu_encrypt_type enctype) { + struct ath10k_hw_params *hw = &ar->hw_params; struct ieee80211_hdr *hdr; size_t hdr_len; int l3_pad_bytes; @@ -1647,8 +1724,10 @@ static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar, * [payload] */ - rxd = (void *)msdu->data - sizeof(*rxd); - l3_pad_bytes = ath10k_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - hw->rx_desc_ops->rx_desc_size); + + l3_pad_bytes = ath10k_htt_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd); skb_put(msdu, l3_pad_bytes); skb_pull(msdu, sizeof(struct amsdu_subframe_hdr) + l3_pad_bytes); @@ -1673,7 +1752,9 @@ static void ath10k_htt_rx_h_undecap(struct ath10k *ar, enum htt_rx_mpdu_encrypt_type enctype, bool is_decrypted) { + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_msdu_start_common *rxd_msdu_start_common; enum rx_msdu_decap_format decap; /* First msdu's decapped header: @@ -1687,8 +1768,11 @@ static void ath10k_htt_rx_h_undecap(struct ath10k *ar, * [rfc1042/llc] */ - rxd = (void *)msdu->data - sizeof(*rxd); - decap = MS(__le32_to_cpu(rxd->msdu_start.common.info1), + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - hw->rx_desc_ops->rx_desc_size); + + rxd_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, rxd); + decap = MS(__le32_to_cpu(rxd_msdu_start_common->info1), RX_MSDU_START_INFO1_DECAP_FORMAT); switch (decap) { @@ -1710,17 +1794,23 @@ static void ath10k_htt_rx_h_undecap(struct ath10k *ar, } } -static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb) +static int ath10k_htt_rx_get_csum_state(struct ath10k_hw_params *hw, struct sk_buff *skb) { struct htt_rx_desc *rxd; + struct rx_attention *rxd_attention; + struct rx_msdu_start_common *rxd_msdu_start_common; u32 flags, info; bool is_ip4, is_ip6; bool is_tcp, is_udp; bool ip_csum_ok, tcpudp_csum_ok; - rxd = (void *)skb->data - sizeof(*rxd); - flags = __le32_to_cpu(rxd->attention.flags); - info = __le32_to_cpu(rxd->msdu_start.common.info1); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)skb->data - hw->rx_desc_ops->rx_desc_size); + + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); + rxd_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, rxd); + flags = __le32_to_cpu(rxd_attention->flags); + info = __le32_to_cpu(rxd_msdu_start_common->info1); is_ip4 = !!(info & RX_MSDU_START_INFO1_IPV4_PROTO); is_ip6 = !!(info & RX_MSDU_START_INFO1_IPV6_PROTO); @@ -1741,9 +1831,10 @@ static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb) return CHECKSUM_UNNECESSARY; } -static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu) +static void ath10k_htt_rx_h_csum_offload(struct ath10k_hw_params *hw, + struct sk_buff *msdu) { - msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu); + msdu->ip_summed = ath10k_htt_rx_get_csum_state(hw, msdu); } static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb, @@ -1835,7 +1926,11 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, struct sk_buff *first; struct sk_buff *last; struct sk_buff *msdu, *temp; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_attention *rxd_attention; + struct rx_mpdu_start *rxd_mpdu_start; + struct ieee80211_hdr *hdr; enum htt_rx_mpdu_encrypt_type enctype; u8 first_hdr[64]; @@ -1853,18 +1948,22 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, return; first = skb_peek(amsdu); - rxd = (void *)first->data - sizeof(*rxd); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)first->data - hw->rx_desc_ops->rx_desc_size); - is_mgmt = !!(rxd->attention.flags & + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); + rxd_mpdu_start = ath10k_htt_rx_desc_get_mpdu_start(hw, rxd); + + is_mgmt = !!(rxd_attention->flags & __cpu_to_le32(RX_ATTENTION_FLAGS_MGMT_TYPE)); - enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), + enctype = MS(__le32_to_cpu(rxd_mpdu_start->info0), RX_MPDU_START_INFO0_ENCRYPT_TYPE); /* First MSDU's Rx descriptor in an A-MSDU contains full 802.11 * decapped header. It'll be used for undecapping of each MSDU. */ - hdr = (void *)rxd->rx_hdr_status; + hdr = (void *)ath10k_htt_rx_desc_get_rx_hdr_status(hw, rxd); memcpy(first_hdr, hdr, RX_HTT_HDR_STATUS_LEN); if (rx_hdr) @@ -1882,8 +1981,11 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, /* Some attention flags are valid only in the last MSDU. */ last = skb_peek_tail(amsdu); - rxd = (void *)last->data - sizeof(*rxd); - attention = __le32_to_cpu(rxd->attention.flags); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)last->data - hw->rx_desc_ops->rx_desc_size); + + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); + attention = __le32_to_cpu(rxd_attention->flags); has_fcs_err = !!(attention & RX_ATTENTION_FLAGS_FCS_ERR); has_crypto_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR); @@ -1971,7 +2073,7 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, continue; } - ath10k_htt_rx_h_csum_offload(msdu); + ath10k_htt_rx_h_csum_offload(&ar->hw_params, msdu); if (frag && !fill_crypt_header && enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA) @@ -2083,12 +2185,19 @@ static void ath10k_htt_rx_h_unchain(struct ath10k *ar, unsigned long *unchain_cnt) { struct sk_buff *first; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_msdu_start_common *rxd_msdu_start_common; + struct rx_frag_info_common *rxd_frag_info; enum rx_msdu_decap_format decap; first = skb_peek(amsdu); - rxd = (void *)first->data - sizeof(*rxd); - decap = MS(__le32_to_cpu(rxd->msdu_start.common.info1), + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)first->data - hw->rx_desc_ops->rx_desc_size); + + rxd_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, rxd); + rxd_frag_info = ath10k_htt_rx_desc_get_frag_info(hw, rxd); + decap = MS(__le32_to_cpu(rxd_msdu_start_common->info1), RX_MSDU_START_INFO1_DECAP_FORMAT); /* FIXME: Current unchaining logic can only handle simple case of raw @@ -2097,7 +2206,7 @@ static void ath10k_htt_rx_h_unchain(struct ath10k *ar, * try re-constructing such frames - it'll be pretty much garbage. */ if (decap != RX_MSDU_DECAP_RAW || - skb_queue_len(amsdu) != 1 + rxd->frag_info.ring2_more_count) { + skb_queue_len(amsdu) != 1 + rxd_frag_info->ring2_more_count) { *drop_cnt += skb_queue_len(amsdu); __skb_queue_purge(amsdu); return; @@ -2112,7 +2221,10 @@ static bool ath10k_htt_rx_validate_amsdu(struct ath10k *ar, u8 *subframe_hdr; struct sk_buff *first; bool is_first, is_last; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_msdu_end_common *rxd_msdu_end_common; + struct rx_mpdu_start *rxd_mpdu_start; struct ieee80211_hdr *hdr; size_t hdr_len, crypto_len; enum htt_rx_mpdu_encrypt_type enctype; @@ -2120,12 +2232,16 @@ static bool ath10k_htt_rx_validate_amsdu(struct ath10k *ar, first = skb_peek(amsdu); - rxd = (void *)first->data - sizeof(*rxd); - hdr = (void *)rxd->rx_hdr_status; + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)first->data - hw->rx_desc_ops->rx_desc_size); - is_first = !!(rxd->msdu_end.common.info0 & + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + rxd_mpdu_start = ath10k_htt_rx_desc_get_mpdu_start(hw, rxd); + hdr = (void *)ath10k_htt_rx_desc_get_rx_hdr_status(hw, rxd); + + is_first = !!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU)); - is_last = !!(rxd->msdu_end.common.info0 & + is_last = !!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU)); /* Return in case of non-aggregated msdu */ @@ -2136,7 +2252,7 @@ static bool ath10k_htt_rx_validate_amsdu(struct ath10k *ar, if (!is_first) return false; - enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), + enctype = MS(__le32_to_cpu(rxd_mpdu_start->info0), RX_MPDU_START_INFO0_ENCRYPT_TYPE); hdr_len = ieee80211_hdrlen(hdr->frame_control); @@ -3028,11 +3144,13 @@ static void ath10k_htt_rx_delba(struct ath10k *ar, struct htt_resp *resp) spin_unlock_bh(&ar->data_lock); } -static int ath10k_htt_rx_extract_amsdu(struct sk_buff_head *list, +static int ath10k_htt_rx_extract_amsdu(struct ath10k_hw_params *hw, + struct sk_buff_head *list, struct sk_buff_head *amsdu) { struct sk_buff *msdu; struct htt_rx_desc *rxd; + struct rx_msdu_end_common *rxd_msdu_end_common; if (skb_queue_empty(list)) return -ENOBUFS; @@ -3043,15 +3161,22 @@ static int ath10k_htt_rx_extract_amsdu(struct sk_buff_head *list, while ((msdu = __skb_dequeue(list))) { __skb_queue_tail(amsdu, msdu); - rxd = (void *)msdu->data - sizeof(*rxd); - if (rxd->msdu_end.common.info0 & + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - + hw->rx_desc_ops->rx_desc_size); + + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + if (rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU)) break; } msdu = skb_peek_tail(amsdu); - rxd = (void *)msdu->data - sizeof(*rxd); - if (!(rxd->msdu_end.common.info0 & + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - hw->rx_desc_ops->rx_desc_size); + + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + if (!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU))) { skb_queue_splice_init(amsdu, list); return -EAGAIN; @@ -3194,7 +3319,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb) while (!skb_queue_empty(&list)) { __skb_queue_head_init(&amsdu); - ret = ath10k_htt_rx_extract_amsdu(&list, &amsdu); + ret = ath10k_htt_rx_extract_amsdu(&ar->hw_params, &list, &amsdu); switch (ret) { case 0: /* Note: The in-order indication may report interleaved diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index b793eac2cfac..9842a4b2f78f 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -796,47 +796,26 @@ static int ath10k_htt_send_frag_desc_bank_cfg_64(struct ath10k_htt *htt) return 0; } -static void ath10k_htt_fill_rx_desc_offset_32(void *rx_ring) +static void ath10k_htt_fill_rx_desc_offset_32(struct ath10k_hw_params *hw, void *rx_ring) { struct htt_rx_ring_setup_ring32 *ring = (struct htt_rx_ring_setup_ring32 *)rx_ring; -#define desc_offset(x) (offsetof(struct htt_rx_desc, x) / 4) - ring->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status)); - ring->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload)); - ring->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start)); - ring->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end)); - ring->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start)); - ring->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end)); - ring->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start)); - ring->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end)); - ring->rx_attention_offset = __cpu_to_le16(desc_offset(attention)); - ring->frag_info_offset = __cpu_to_le16(desc_offset(frag_info)); -#undef desc_offset + ath10k_htt_rx_desc_get_offsets(hw, &ring->offsets); } -static void ath10k_htt_fill_rx_desc_offset_64(void *rx_ring) +static void ath10k_htt_fill_rx_desc_offset_64(struct ath10k_hw_params *hw, void *rx_ring) { struct htt_rx_ring_setup_ring64 *ring = (struct htt_rx_ring_setup_ring64 *)rx_ring; -#define desc_offset(x) (offsetof(struct htt_rx_desc, x) / 4) - ring->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status)); - ring->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload)); - ring->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start)); - ring->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end)); - ring->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start)); - ring->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end)); - ring->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start)); - ring->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end)); - ring->rx_attention_offset = __cpu_to_le16(desc_offset(attention)); - ring->frag_info_offset = __cpu_to_le16(desc_offset(frag_info)); -#undef desc_offset + ath10k_htt_rx_desc_get_offsets(hw, &ring->offsets); } static int ath10k_htt_send_rx_ring_cfg_32(struct ath10k_htt *htt) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; struct sk_buff *skb; struct htt_cmd *cmd; struct htt_rx_ring_setup_ring32 *ring; @@ -896,7 +875,7 @@ static int ath10k_htt_send_rx_ring_cfg_32(struct ath10k_htt *htt) ring->flags = __cpu_to_le16(flags); ring->fw_idx_init_val = __cpu_to_le16(fw_idx); - ath10k_htt_fill_rx_desc_offset_32(ring); + ath10k_htt_fill_rx_desc_offset_32(hw, ring); ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { dev_kfree_skb_any(skb); @@ -909,6 +888,7 @@ static int ath10k_htt_send_rx_ring_cfg_32(struct ath10k_htt *htt) static int ath10k_htt_send_rx_ring_cfg_64(struct ath10k_htt *htt) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; struct sk_buff *skb; struct htt_cmd *cmd; struct htt_rx_ring_setup_ring64 *ring; @@ -965,7 +945,7 @@ static int ath10k_htt_send_rx_ring_cfg_64(struct ath10k_htt *htt) ring->flags = __cpu_to_le16(flags); ring->fw_idx_init_val = __cpu_to_le16(fw_idx); - ath10k_htt_fill_rx_desc_offset_64(ring); + ath10k_htt_fill_rx_desc_offset_64(hw, ring); ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { dev_kfree_skb_any(skb); diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 57c58af64a57..e52e41a70321 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -11,6 +11,7 @@ #include "hif.h" #include "wmi-ops.h" #include "bmi.h" +#include "rx_desc.h" const struct ath10k_hw_regs qca988x_regs = { .rtc_soc_base_address = 0x00004000, @@ -1134,21 +1135,7 @@ const struct ath10k_hw_ops qca988x_ops = { .is_rssi_enable = ath10k_htt_tx_rssi_enable, }; -static int ath10k_qca99x0_rx_desc_get_l3_pad_bytes(struct htt_rx_desc *rxd) -{ - return MS(__le32_to_cpu(rxd->msdu_end.qca99x0.info1), - RX_MSDU_END_INFO1_L3_HDR_PAD); -} - -static bool ath10k_qca99x0_rx_desc_msdu_limit_error(struct htt_rx_desc *rxd) -{ - return !!(rxd->msdu_end.common.info0 & - __cpu_to_le32(RX_MSDU_END_INFO0_MSDU_LIMIT_ERR)); -} - const struct ath10k_hw_ops qca99x0_ops = { - .rx_desc_get_l3_pad_bytes = ath10k_qca99x0_rx_desc_get_l3_pad_bytes, - .rx_desc_get_msdu_limit_error = ath10k_qca99x0_rx_desc_msdu_limit_error, .is_rssi_enable = ath10k_htt_tx_rssi_enable, }; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 591ef7416b61..5215a6816d71 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -510,6 +510,8 @@ struct ath10k_hw_clk_params { u32 outdiv; }; +struct htt_rx_desc_ops; + struct ath10k_hw_params { u32 id; u16 dev_id; @@ -562,6 +564,9 @@ struct ath10k_hw_params { */ bool sw_decrypt_mcast_mgmt; + /* Rx descriptor abstraction */ + const struct ath10k_htt_rx_desc_ops *rx_desc_ops; + const struct ath10k_hw_ops *hw_ops; /* Number of bytes used for alignment in rx_hdr_status of rx desc. */ @@ -630,16 +635,14 @@ struct ath10k_hw_params { bool dynamic_sar_support; }; -struct htt_rx_desc; struct htt_resp; struct htt_data_tx_completion_ext; +struct htt_rx_ring_rx_desc_offsets; /* Defines needed for Rx descriptor abstraction */ struct ath10k_hw_ops { - int (*rx_desc_get_l3_pad_bytes)(struct htt_rx_desc *rxd); void (*set_coverage_class)(struct ath10k *ar, s16 value); int (*enable_pll_clk)(struct ath10k *ar); - bool (*rx_desc_get_msdu_limit_error)(struct htt_rx_desc *rxd); int (*tx_data_rssi_pad_bytes)(struct htt_resp *htt); int (*is_rssi_enable)(struct htt_resp *resp); }; @@ -652,24 +655,6 @@ extern const struct ath10k_hw_ops wcn3990_ops; extern const struct ath10k_hw_clk_params qca6174_clk[]; -static inline int -ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, - struct htt_rx_desc *rxd) -{ - if (hw->hw_ops->rx_desc_get_l3_pad_bytes) - return hw->hw_ops->rx_desc_get_l3_pad_bytes(rxd); - return 0; -} - -static inline bool -ath10k_rx_desc_msdu_limit_error(struct ath10k_hw_params *hw, - struct htt_rx_desc *rxd) -{ - if (hw->hw_ops->rx_desc_get_msdu_limit_error) - return hw->hw_ops->rx_desc_get_msdu_limit_error(rxd); - return false; -} - static inline int ath10k_tx_data_rssi_get_pad_bytes(struct ath10k_hw_params *hw, struct htt_resp *htt) diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h index 705b6295e466..6ce2a8b1060d 100644 --- a/drivers/net/wireless/ath/ath10k/rx_desc.h +++ b/drivers/net/wireless/ath/ath10k/rx_desc.h @@ -196,17 +196,31 @@ struct rx_attention { * descriptor. */ -struct rx_frag_info { +struct rx_frag_info_common { u8 ring0_more_count; u8 ring1_more_count; u8 ring2_more_count; u8 ring3_more_count; +} __packed; + +struct rx_frag_info_wcn3990 { u8 ring4_more_count; u8 ring5_more_count; u8 ring6_more_count; u8 ring7_more_count; } __packed; +struct rx_frag_info { + struct rx_frag_info_common common; + union { + struct rx_frag_info_wcn3990 wcn3990; + } __packed; +} __packed; + +struct rx_frag_info_v1 { + struct rx_frag_info_common common; +} __packed; + /* * ring0_more_count * Indicates the number of more buffers associated with RX DMA @@ -474,11 +488,17 @@ struct rx_msdu_start_wcn3990 { struct rx_msdu_start { struct rx_msdu_start_common common; union { - struct rx_msdu_start_qca99x0 qca99x0; struct rx_msdu_start_wcn3990 wcn3990; } __packed; } __packed; +struct rx_msdu_start_v1 { + struct rx_msdu_start_common common; + union { + struct rx_msdu_start_qca99x0 qca99x0; + } __packed; +} __packed; + /* * msdu_length * MSDU length in bytes after decapsulation. This field is @@ -612,11 +632,17 @@ struct rx_msdu_end_wcn3990 { struct rx_msdu_end { struct rx_msdu_end_common common; union { - struct rx_msdu_end_qca99x0 qca99x0; struct rx_msdu_end_wcn3990 wcn3990; } __packed; } __packed; +struct rx_msdu_end_v1 { + struct rx_msdu_end_common common; + union { + struct rx_msdu_end_qca99x0 qca99x0; + } __packed; +} __packed; + /* *ip_hdr_chksum * This can include the IP header checksum or the pseudo header @@ -1134,13 +1160,19 @@ struct rx_ppdu_end_wcn3990 { } __packed; struct rx_ppdu_end { + struct rx_ppdu_end_common common; + union { + struct rx_ppdu_end_wcn3990 wcn3990; + } __packed; +} __packed; + +struct rx_ppdu_end_v1 { struct rx_ppdu_end_common common; union { struct rx_ppdu_end_qca988x qca988x; struct rx_ppdu_end_qca6174 qca6174; struct rx_ppdu_end_qca99x0 qca99x0; struct rx_ppdu_end_qca9984 qca9984; - struct rx_ppdu_end_wcn3990 wcn3990; } __packed; } __packed; diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 9513ab696fff..8328966a0471 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -1306,13 +1306,10 @@ static int ath10k_snoc_resource_init(struct ath10k *ar) } for (i = 0; i < CE_COUNT; i++) { - res = platform_get_resource(ar_snoc->dev, IORESOURCE_IRQ, i); - if (!res) { - ath10k_err(ar, "failed to get IRQ%d\n", i); - ret = -ENODEV; - goto out; - } - ar_snoc->ce_irqs[i].irq_line = res->start; + ret = platform_get_irq(ar_snoc->dev, i); + if (ret < 0) + return ret; + ar_snoc->ce_irqs[i].irq_line = ret; } ret = device_property_read_u32(&pdev->dev, "qcom,xo-cal-data", @@ -1323,10 +1320,8 @@ static int ath10k_snoc_resource_init(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_SNOC, "xo cal data %x\n", ar_snoc->xo_cal_data); } - ret = 0; -out: - return ret; + return 0; } static void ath10k_snoc_quirks_init(struct ath10k *ar) @@ -1556,11 +1551,11 @@ static int ath10k_setup_msa_resources(struct ath10k *ar, u32 msa_size) node = of_parse_phandle(dev->of_node, "memory-region", 0); if (node) { ret = of_address_to_resource(node, 0, &r); + of_node_put(node); if (ret) { dev_err(dev, "failed to resolve msa fixed region\n"); return ret; } - of_node_put(node); ar->msa.paddr = r.start; ar->msa.mem_size = resource_size(&r); diff --git a/drivers/net/wireless/ath/ath10k/swap.h b/drivers/net/wireless/ath/ath10k/swap.h index 25e0ad36ddb1..b4733b5ded34 100644 --- a/drivers/net/wireless/ath/ath10k/swap.h +++ b/drivers/net/wireless/ath/ath10k/swap.h @@ -17,7 +17,7 @@ struct ath10k_fw_file; struct ath10k_swap_code_seg_tlv { __le32 address; __le32 length; - u8 data[0]; + u8 data[]; } __packed; struct ath10k_swap_code_seg_tail { diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 6f8b64218894..10123974c3da 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -125,7 +125,7 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt, tx_done->ack_rssi != ATH10K_INVALID_RSSI) { info->status.ack_signal = ATH10K_DEFAULT_NOISE_FLOOR + tx_done->ack_rssi; - info->status.is_valid_ack_signal = true; + info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; } ieee80211_tx_status(htt->ar->hw, msdu); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 7c1c2658cb5f..cd438f76f284 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2427,7 +2427,7 @@ wmi_process_mgmt_tx_comp(struct ath10k *ar, struct mgmt_tx_compl_params *param) info->flags |= IEEE80211_TX_STAT_ACK; info->status.ack_signal = ATH10K_DEFAULT_NOISE_FLOOR + param->ack_rssi; - info->status.is_valid_ack_signal = true; + info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; } ieee80211_tx_status_irqsafe(ar->hw, msdu); diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c index 7d65c115669f..20b9aa8ddf7d 100644 --- a/drivers/net/wireless/ath/ath10k/wow.c +++ b/drivers/net/wireless/ath/ath10k/wow.c @@ -337,14 +337,15 @@ static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif, if (patterns[i].mask[j / 8] & BIT(j % 8)) bitmask[j] = 0xff; old_pattern.mask = bitmask; - new_pattern = old_pattern; if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) { - if (patterns[i].pkt_offset < ETH_HLEN) + if (patterns[i].pkt_offset < ETH_HLEN) { ath10k_wow_convert_8023_to_80211(&new_pattern, &old_pattern); - else + } else { + new_pattern = old_pattern; new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN; + } } if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE)) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 3fb0aa000825..f407d4af2074 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -391,6 +391,8 @@ static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab) for (j = 0; j < irq_grp->num_irq; j++) free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); + + netif_napi_del(&irq_grp->napi); } } @@ -466,7 +468,7 @@ static irqreturn_t ath11k_ahb_ext_interrupt_handler(int irq, void *arg) return IRQ_HANDLED; } -static int ath11k_ahb_ext_irq_config(struct ath11k_base *ab) +static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab) { struct ath11k_hw_params *hw = &ab->hw_params; int i, j; @@ -574,7 +576,7 @@ static int ath11k_ahb_config_irq(struct ath11k_base *ab) } /* Configure external interrupts */ - ret = ath11k_ahb_ext_irq_config(ab); + ret = ath11k_ahb_config_ext_irq(ab); return ret; } diff --git a/drivers/net/wireless/ath/ath11k/ce.h b/drivers/net/wireless/ath/ath11k/ce.h index 8255b6cfab0c..9644ff909502 100644 --- a/drivers/net/wireless/ath/ath11k/ce.h +++ b/drivers/net/wireless/ath/ath11k/ce.h @@ -145,7 +145,7 @@ struct ath11k_ce_ring { u32 hal_ring_id; /* keep last */ - struct sk_buff *skb[0]; + struct sk_buff *skb[]; }; struct ath11k_ce_pipe { diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 293563b3f784..71eb7d04c3bf 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -97,6 +98,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .wakeup_mhi = false, .supports_rssi_stats = false, .fw_wmi_diag_event = false, + .current_cc_support = false, + .dbr_debug_support = true, }, { .hw_rev = ATH11K_HW_IPQ6018_HW10, @@ -161,6 +164,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .wakeup_mhi = false, .supports_rssi_stats = false, .fw_wmi_diag_event = false, + .current_cc_support = false, + .dbr_debug_support = true, }, { .name = "qca6390 hw2.0", @@ -224,6 +229,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .wakeup_mhi = true, .supports_rssi_stats = true, .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, }, { .name = "qcn9074 hw1.0", @@ -287,6 +294,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .wakeup_mhi = false, .supports_rssi_stats = false, .fw_wmi_diag_event = false, + .current_cc_support = false, + .dbr_debug_support = true, }, { .name = "wcn6855 hw2.0", @@ -350,6 +359,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .wakeup_mhi = true, .supports_rssi_stats = true, .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, }, { .name = "wcn6855 hw2.1", @@ -412,6 +423,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .wakeup_mhi = true, .supports_rssi_stats = true, .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, }, }; @@ -1404,6 +1417,8 @@ EXPORT_SYMBOL(ath11k_core_deinit); void ath11k_core_free(struct ath11k_base *ab) { + destroy_workqueue(ab->workqueue); + kfree(ab); } EXPORT_SYMBOL(ath11k_core_free); diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 9e88ccca5ca7..c0228e91a596 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -263,6 +263,9 @@ struct ath11k_vif { bool bcca_zero_sent; bool do_not_send_tmpl; struct ieee80211_chanctx_conf chanctx; +#ifdef CONFIG_ATH11K_DEBUGFS + struct dentry *debugfs_twt; +#endif /* CONFIG_ATH11K_DEBUGFS */ }; struct ath11k_vif_iter { @@ -441,6 +444,8 @@ struct ath11k_dbg_htt_stats { spinlock_t lock; }; +#define MAX_MODULE_ID_BITMAP_WORDS 16 + struct ath11k_debug { struct dentry *debugfs_pdev; struct ath11k_dbg_htt_stats htt_stats; @@ -454,6 +459,9 @@ struct ath11k_debug { u32 pktlog_peer_valid; u8 pktlog_peer_addr[ETH_ALEN]; u32 rx_filter; + u32 mem_offset; + u32 module_id_bitmap[MAX_MODULE_ID_BITMAP_WORDS]; + struct ath11k_debug_dbr *dbr_debug[WMI_DIRECT_BUF_MAX]; }; struct ath11k_per_peer_tx_stats { @@ -603,6 +611,8 @@ struct ath11k { struct completion finish_11d_ch_list; bool pending_11d; bool regdom_set_by_user; + int hw_rate_code; + u8 twt_enabled; }; struct ath11k_band_cap { @@ -806,7 +816,7 @@ struct ath11k_base { } id; /* must be last */ - u8 drv_priv[0] __aligned(sizeof(void *)); + u8 drv_priv[] __aligned(sizeof(void *)); }; struct ath11k_fw_stats_pdev { diff --git a/drivers/net/wireless/ath/ath11k/dbring.c b/drivers/net/wireless/ath/ath11k/dbring.c index eda67ebfc4c2..2107ec05d14f 100644 --- a/drivers/net/wireless/ath/ath11k/dbring.c +++ b/drivers/net/wireless/ath/ath11k/dbring.c @@ -37,7 +37,8 @@ static void ath11k_dbring_fill_magic_value(struct ath11k *ar, static int ath11k_dbring_bufs_replenish(struct ath11k *ar, struct ath11k_dbring *ring, - struct ath11k_dbring_element *buff) + struct ath11k_dbring_element *buff, + enum wmi_direct_buffer_module id) { struct ath11k_base *ab = ar->ab; struct hal_srng *srng; @@ -84,6 +85,7 @@ static int ath11k_dbring_bufs_replenish(struct ath11k *ar, ath11k_hal_rx_buf_addr_info_set(desc, paddr, cookie, 0); + ath11k_debugfs_add_dbring_entry(ar, id, ATH11K_DBG_DBR_EVENT_REPLENISH, srng); ath11k_hal_srng_access_end(ab, srng); return 0; @@ -101,7 +103,8 @@ static int ath11k_dbring_bufs_replenish(struct ath11k *ar, } static int ath11k_dbring_fill_bufs(struct ath11k *ar, - struct ath11k_dbring *ring) + struct ath11k_dbring *ring, + enum wmi_direct_buffer_module id) { struct ath11k_dbring_element *buff; struct hal_srng *srng; @@ -129,7 +132,7 @@ static int ath11k_dbring_fill_bufs(struct ath11k *ar, kfree(buff); break; } - ret = ath11k_dbring_bufs_replenish(ar, ring, buff); + ret = ath11k_dbring_bufs_replenish(ar, ring, buff, id); if (ret) { ath11k_warn(ar->ab, "failed to replenish db ring num_remain %d req_ent %d\n", num_remain, req_entries); @@ -210,7 +213,7 @@ int ath11k_dbring_buf_setup(struct ath11k *ar, ring->hp_addr = ath11k_hal_srng_get_hp_addr(ar->ab, srng); ring->tp_addr = ath11k_hal_srng_get_tp_addr(ar->ab, srng); - ret = ath11k_dbring_fill_bufs(ar, ring); + ret = ath11k_dbring_fill_bufs(ar, ring, db_cap->id); return ret; } @@ -270,7 +273,7 @@ int ath11k_dbring_buffer_release_event(struct ath11k_base *ab, struct ath11k_buffer_addr desc; u8 *vaddr_unalign; u32 num_entry, num_buff_reaped; - u8 pdev_idx, rbm; + u8 pdev_idx, rbm, module_id; u32 cookie; int buf_id; int size; @@ -278,6 +281,7 @@ int ath11k_dbring_buffer_release_event(struct ath11k_base *ab, int ret = 0; pdev_idx = ev->fixed.pdev_id; + module_id = ev->fixed.module_id; if (pdev_idx >= ab->num_radios) { ath11k_warn(ab, "Invalid pdev id %d\n", pdev_idx); @@ -346,6 +350,9 @@ int ath11k_dbring_buffer_release_event(struct ath11k_base *ab, dma_unmap_single(ab->dev, buff->paddr, ring->buf_sz, DMA_FROM_DEVICE); + ath11k_debugfs_add_dbring_entry(ar, module_id, + ATH11K_DBG_DBR_EVENT_RX, srng); + if (ring->handler) { vaddr_unalign = buff->payload; handler_data.data = PTR_ALIGN(vaddr_unalign, @@ -357,7 +364,7 @@ int ath11k_dbring_buffer_release_event(struct ath11k_base *ab, buff->paddr = 0; memset(buff->payload, 0, size); - ath11k_dbring_bufs_replenish(ar, ring, buff); + ath11k_dbring_bufs_replenish(ar, ring, buff, module_id); } spin_unlock_bh(&srng->lock); diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index 198ade90b725..a82266c8befc 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -52,6 +52,45 @@ static const char *htt_bp_lmac_ring[HTT_SW_LMAC_RING_IDX_MAX] = { "MONITOR_DEST_RING", }; +void ath11k_debugfs_add_dbring_entry(struct ath11k *ar, + enum wmi_direct_buffer_module id, + enum ath11k_dbg_dbr_event event, + struct hal_srng *srng) +{ + struct ath11k_debug_dbr *dbr_debug; + struct ath11k_dbg_dbr_data *dbr_data; + struct ath11k_dbg_dbr_entry *entry; + + if (id >= WMI_DIRECT_BUF_MAX || event >= ATH11K_DBG_DBR_EVENT_MAX) + return; + + dbr_debug = ar->debug.dbr_debug[id]; + if (!dbr_debug) + return; + + if (!dbr_debug->dbr_debug_enabled) + return; + + dbr_data = &dbr_debug->dbr_dbg_data; + + spin_lock_bh(&dbr_data->lock); + + if (dbr_data->entries) { + entry = &dbr_data->entries[dbr_data->dbr_debug_idx]; + entry->hp = srng->u.src_ring.hp; + entry->tp = *srng->u.src_ring.tp_addr; + entry->timestamp = jiffies; + entry->event = event; + + dbr_data->dbr_debug_idx++; + if (dbr_data->dbr_debug_idx == + dbr_data->num_ring_debug_entries) + dbr_data->dbr_debug_idx = 0; + } + + spin_unlock_bh(&dbr_data->lock); +} + static void ath11k_fw_stats_pdevs_free(struct list_head *head) { struct ath11k_fw_stats_pdev *i, *tmp; @@ -666,6 +705,12 @@ static ssize_t ath11k_write_extd_rx_stats(struct file *file, goto exit; } + if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) { + ar->debug.extd_rx_stats = enable; + ret = count; + goto exit; + } + if (enable) { rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START; rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START; @@ -870,6 +915,69 @@ static const struct file_operations fops_soc_dp_stats = { .llseek = default_llseek, }; +static ssize_t ath11k_write_fw_dbglog(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[128] = {0}; + struct ath11k_fw_dbglog dbglog; + unsigned int param, mod_id_index, is_end; + u64 value; + int ret, num; + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, + user_buf, count); + if (ret <= 0) + return ret; + + num = sscanf(buf, "%u %llx %u %u", ¶m, &value, &mod_id_index, &is_end); + + if (num < 2) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + if (param == WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP || + param == WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP) { + if (num != 4 || mod_id_index > (MAX_MODULE_ID_BITMAP_WORDS - 1)) { + ret = -EINVAL; + goto out; + } + ar->debug.module_id_bitmap[mod_id_index] = upper_32_bits(value); + if (!is_end) { + ret = count; + goto out; + } + } else { + if (num != 2) { + ret = -EINVAL; + goto out; + } + } + + dbglog.param = param; + dbglog.value = lower_32_bits(value); + ret = ath11k_wmi_fw_dbglog_cfg(ar, ar->debug.module_id_bitmap, &dbglog); + if (ret) { + ath11k_warn(ar->ab, "fw dbglog config failed from debugfs: %d\n", + ret); + goto out; + } + + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_fw_dbglog = { + .write = ath11k_write_fw_dbglog, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath11k_debugfs_pdev_create(struct ath11k_base *ab) { if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) @@ -1107,6 +1215,169 @@ static const struct file_operations fops_simulate_radar = { .open = simple_open }; +static ssize_t ath11k_debug_dump_dbr_entries(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k_dbg_dbr_data *dbr_dbg_data = file->private_data; + static const char * const event_id_to_string[] = {"empty", "Rx", "Replenish"}; + int size = ATH11K_DEBUG_DBR_ENTRIES_MAX * 100; + char *buf; + int i, ret; + int len = 0; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, size - len, + "-----------------------------------------\n"); + len += scnprintf(buf + len, size - len, + "| idx | hp | tp | timestamp | event |\n"); + len += scnprintf(buf + len, size - len, + "-----------------------------------------\n"); + + spin_lock_bh(&dbr_dbg_data->lock); + + for (i = 0; i < dbr_dbg_data->num_ring_debug_entries; i++) { + len += scnprintf(buf + len, size - len, + "|%4u|%8u|%8u|%11llu|%8s|\n", i, + dbr_dbg_data->entries[i].hp, + dbr_dbg_data->entries[i].tp, + dbr_dbg_data->entries[i].timestamp, + event_id_to_string[dbr_dbg_data->entries[i].event]); + } + + spin_unlock_bh(&dbr_dbg_data->lock); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return ret; +} + +static const struct file_operations fops_debug_dump_dbr_entries = { + .read = ath11k_debug_dump_dbr_entries, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static void ath11k_debugfs_dbr_dbg_destroy(struct ath11k *ar, int dbr_id) +{ + struct ath11k_debug_dbr *dbr_debug; + struct ath11k_dbg_dbr_data *dbr_dbg_data; + + if (!ar->debug.dbr_debug[dbr_id]) + return; + + dbr_debug = ar->debug.dbr_debug[dbr_id]; + dbr_dbg_data = &dbr_debug->dbr_dbg_data; + + debugfs_remove_recursive(dbr_debug->dbr_debugfs); + kfree(dbr_dbg_data->entries); + kfree(dbr_debug); + ar->debug.dbr_debug[dbr_id] = NULL; +} + +static int ath11k_debugfs_dbr_dbg_init(struct ath11k *ar, int dbr_id) +{ + struct ath11k_debug_dbr *dbr_debug; + struct ath11k_dbg_dbr_data *dbr_dbg_data; + static const char * const dbr_id_to_str[] = {"spectral", "CFR"}; + + if (ar->debug.dbr_debug[dbr_id]) + return 0; + + ar->debug.dbr_debug[dbr_id] = kzalloc(sizeof(*dbr_debug), + GFP_KERNEL); + + if (!ar->debug.dbr_debug[dbr_id]) + return -ENOMEM; + + dbr_debug = ar->debug.dbr_debug[dbr_id]; + dbr_dbg_data = &dbr_debug->dbr_dbg_data; + + if (dbr_debug->dbr_debugfs) + return 0; + + dbr_debug->dbr_debugfs = debugfs_create_dir(dbr_id_to_str[dbr_id], + ar->debug.debugfs_pdev); + if (IS_ERR_OR_NULL(dbr_debug->dbr_debugfs)) { + if (IS_ERR(dbr_debug->dbr_debugfs)) + return PTR_ERR(dbr_debug->dbr_debugfs); + return -ENOMEM; + } + + dbr_debug->dbr_debug_enabled = true; + dbr_dbg_data->num_ring_debug_entries = ATH11K_DEBUG_DBR_ENTRIES_MAX; + dbr_dbg_data->dbr_debug_idx = 0; + dbr_dbg_data->entries = kcalloc(ATH11K_DEBUG_DBR_ENTRIES_MAX, + sizeof(struct ath11k_dbg_dbr_entry), + GFP_KERNEL); + if (!dbr_dbg_data->entries) + return -ENOMEM; + + spin_lock_init(&dbr_dbg_data->lock); + + debugfs_create_file("dump_dbr_debug", 0444, dbr_debug->dbr_debugfs, + dbr_dbg_data, &fops_debug_dump_dbr_entries); + + return 0; +} + +static ssize_t ath11k_debugfs_write_enable_dbr_dbg(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[32] = {0}; + u32 dbr_id, enable; + int ret; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); + if (ret < 0) + goto out; + + buf[ret] = '\0'; + ret = sscanf(buf, "%u %u", &dbr_id, &enable); + if (ret != 2 || dbr_id > 1 || enable > 1) { + ret = -EINVAL; + ath11k_warn(ar->ab, "usage: echo dbr_id:0-Spectral 1-CFR val:0-disable 1-enable\n"); + goto out; + } + + if (enable) { + ret = ath11k_debugfs_dbr_dbg_init(ar, dbr_id); + if (ret) { + ath11k_warn(ar->ab, "db ring module debugfs init failed: %d\n", + ret); + goto out; + } + } else { + ath11k_debugfs_dbr_dbg_destroy(ar, dbr_id); + } + + ret = count; +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_dbr_debug = { + .write = ath11k_debugfs_write_enable_dbr_dbg, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath11k_debugfs_register(struct ath11k *ar) { struct ath11k_base *ab = ar->ab; @@ -1136,6 +1407,9 @@ int ath11k_debugfs_register(struct ath11k *ar) debugfs_create_file("pktlog_filter", 0644, ar->debug.debugfs_pdev, ar, &fops_pktlog_filter); + debugfs_create_file("fw_dbglog_config", 0600, + ar->debug.debugfs_pdev, ar, + &fops_fw_dbglog); if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) { debugfs_create_file("dfs_simulate_radar", 0200, @@ -1146,9 +1420,250 @@ int ath11k_debugfs_register(struct ath11k *ar) &ar->dfs_block_radar_events); } + if (ab->hw_params.dbr_debug_support) + debugfs_create_file("enable_dbr_debug", 0200, ar->debug.debugfs_pdev, + ar, &fops_dbr_debug); + return 0; } void ath11k_debugfs_unregister(struct ath11k *ar) { + struct ath11k_debug_dbr *dbr_debug; + struct ath11k_dbg_dbr_data *dbr_dbg_data; + int i; + + for (i = 0; i < WMI_DIRECT_BUF_MAX; i++) { + dbr_debug = ar->debug.dbr_debug[i]; + if (!dbr_debug) + continue; + + dbr_dbg_data = &dbr_debug->dbr_dbg_data; + kfree(dbr_dbg_data->entries); + debugfs_remove_recursive(dbr_debug->dbr_debugfs); + kfree(dbr_debug); + ar->debug.dbr_debug[i] = NULL; + } +} + +static ssize_t ath11k_write_twt_add_dialog(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k_vif *arvif = file->private_data; + struct wmi_twt_add_dialog_params params = { 0 }; + u8 buf[128] = {0}; + int ret; + + if (arvif->ar->twt_enabled == 0) { + ath11k_err(arvif->ar->ab, "twt support is not enabled\n"); + return -EOPNOTSUPP; + } + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); + if (ret < 0) + return ret; + + buf[ret] = '\0'; + ret = sscanf(buf, + "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u %u %u %hhu %hhu %hhu %hhu %hhu", + ¶ms.peer_macaddr[0], + ¶ms.peer_macaddr[1], + ¶ms.peer_macaddr[2], + ¶ms.peer_macaddr[3], + ¶ms.peer_macaddr[4], + ¶ms.peer_macaddr[5], + ¶ms.dialog_id, + ¶ms.wake_intvl_us, + ¶ms.wake_intvl_mantis, + ¶ms.wake_dura_us, + ¶ms.sp_offset_us, + ¶ms.twt_cmd, + ¶ms.flag_bcast, + ¶ms.flag_trigger, + ¶ms.flag_flow_type, + ¶ms.flag_protection); + if (ret != 16) + return -EINVAL; + + params.vdev_id = arvif->vdev_id; + + ret = ath11k_wmi_send_twt_add_dialog_cmd(arvif->ar, ¶ms); + if (ret) + return ret; + + return count; +} + +static ssize_t ath11k_write_twt_del_dialog(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k_vif *arvif = file->private_data; + struct wmi_twt_del_dialog_params params = { 0 }; + u8 buf[64] = {0}; + int ret; + + if (arvif->ar->twt_enabled == 0) { + ath11k_err(arvif->ar->ab, "twt support is not enabled\n"); + return -EOPNOTSUPP; + } + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); + if (ret < 0) + return ret; + + buf[ret] = '\0'; + ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u", + ¶ms.peer_macaddr[0], + ¶ms.peer_macaddr[1], + ¶ms.peer_macaddr[2], + ¶ms.peer_macaddr[3], + ¶ms.peer_macaddr[4], + ¶ms.peer_macaddr[5], + ¶ms.dialog_id); + if (ret != 7) + return -EINVAL; + + params.vdev_id = arvif->vdev_id; + + ret = ath11k_wmi_send_twt_del_dialog_cmd(arvif->ar, ¶ms); + if (ret) + return ret; + + return count; +} + +static ssize_t ath11k_write_twt_pause_dialog(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k_vif *arvif = file->private_data; + struct wmi_twt_pause_dialog_params params = { 0 }; + u8 buf[64] = {0}; + int ret; + + if (arvif->ar->twt_enabled == 0) { + ath11k_err(arvif->ar->ab, "twt support is not enabled\n"); + return -EOPNOTSUPP; + } + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); + if (ret < 0) + return ret; + + buf[ret] = '\0'; + ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u", + ¶ms.peer_macaddr[0], + ¶ms.peer_macaddr[1], + ¶ms.peer_macaddr[2], + ¶ms.peer_macaddr[3], + ¶ms.peer_macaddr[4], + ¶ms.peer_macaddr[5], + ¶ms.dialog_id); + if (ret != 7) + return -EINVAL; + + params.vdev_id = arvif->vdev_id; + + ret = ath11k_wmi_send_twt_pause_dialog_cmd(arvif->ar, ¶ms); + if (ret) + return ret; + + return count; +} + +static ssize_t ath11k_write_twt_resume_dialog(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k_vif *arvif = file->private_data; + struct wmi_twt_resume_dialog_params params = { 0 }; + u8 buf[64] = {0}; + int ret; + + if (arvif->ar->twt_enabled == 0) { + ath11k_err(arvif->ar->ab, "twt support is not enabled\n"); + return -EOPNOTSUPP; + } + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); + if (ret < 0) + return ret; + + buf[ret] = '\0'; + ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u", + ¶ms.peer_macaddr[0], + ¶ms.peer_macaddr[1], + ¶ms.peer_macaddr[2], + ¶ms.peer_macaddr[3], + ¶ms.peer_macaddr[4], + ¶ms.peer_macaddr[5], + ¶ms.dialog_id, + ¶ms.sp_offset_us, + ¶ms.next_twt_size); + if (ret != 9) + return -EINVAL; + + params.vdev_id = arvif->vdev_id; + + ret = ath11k_wmi_send_twt_resume_dialog_cmd(arvif->ar, ¶ms); + if (ret) + return ret; + + return count; +} + +static const struct file_operations ath11k_fops_twt_add_dialog = { + .write = ath11k_write_twt_add_dialog, + .open = simple_open +}; + +static const struct file_operations ath11k_fops_twt_del_dialog = { + .write = ath11k_write_twt_del_dialog, + .open = simple_open +}; + +static const struct file_operations ath11k_fops_twt_pause_dialog = { + .write = ath11k_write_twt_pause_dialog, + .open = simple_open +}; + +static const struct file_operations ath11k_fops_twt_resume_dialog = { + .write = ath11k_write_twt_resume_dialog, + .open = simple_open +}; + +int ath11k_debugfs_add_interface(struct ath11k_vif *arvif) +{ + if (arvif->vif->type == NL80211_IFTYPE_AP && !arvif->debugfs_twt) { + arvif->debugfs_twt = debugfs_create_dir("twt", + arvif->vif->debugfs_dir); + if (!arvif->debugfs_twt || IS_ERR(arvif->debugfs_twt)) { + ath11k_warn(arvif->ar->ab, + "failed to create directory %p\n", + arvif->debugfs_twt); + arvif->debugfs_twt = NULL; + return -1; + } + + debugfs_create_file("add_dialog", 0200, arvif->debugfs_twt, + arvif, &ath11k_fops_twt_add_dialog); + + debugfs_create_file("del_dialog", 0200, arvif->debugfs_twt, + arvif, &ath11k_fops_twt_del_dialog); + + debugfs_create_file("pause_dialog", 0200, arvif->debugfs_twt, + arvif, &ath11k_fops_twt_pause_dialog); + + debugfs_create_file("resume_dialog", 0200, arvif->debugfs_twt, + arvif, &ath11k_fops_twt_resume_dialog); + } + return 0; +} + +void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif) +{ + debugfs_remove_recursive(arvif->debugfs_twt); + arvif->debugfs_twt = NULL; } diff --git a/drivers/net/wireless/ath/ath11k/debugfs.h b/drivers/net/wireless/ath/ath11k/debugfs.h index 4c0740394c95..30c00cb28311 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.h +++ b/drivers/net/wireless/ath/ath11k/debugfs.h @@ -47,6 +47,36 @@ enum ath11k_dbg_htt_ext_stats_type { ATH11K_DBG_HTT_NUM_EXT_STATS, }; +#define ATH11K_DEBUG_DBR_ENTRIES_MAX 512 + +enum ath11k_dbg_dbr_event { + ATH11K_DBG_DBR_EVENT_INVALID, + ATH11K_DBG_DBR_EVENT_RX, + ATH11K_DBG_DBR_EVENT_REPLENISH, + ATH11K_DBG_DBR_EVENT_MAX, +}; + +struct ath11k_dbg_dbr_entry { + u32 hp; + u32 tp; + u64 timestamp; + enum ath11k_dbg_dbr_event event; +}; + +struct ath11k_dbg_dbr_data { + /* protects ath11k_db_ring_debug data */ + spinlock_t lock; + struct ath11k_dbg_dbr_entry *entries; + u32 dbr_debug_idx; + u32 num_ring_debug_entries; +}; + +struct ath11k_debug_dbr { + struct ath11k_dbg_dbr_data dbr_dbg_data; + struct dentry *dbr_debugfs; + bool dbr_debug_enabled; +}; + struct debug_htt_stats_req { bool done; u8 pdev_id; @@ -88,6 +118,7 @@ enum ath11k_pktlog_mode { }; enum ath11k_pktlog_enum { + ATH11K_PKTLOG_TYPE_INVALID = 0, ATH11K_PKTLOG_TYPE_TX_CTRL = 1, ATH11K_PKTLOG_TYPE_TX_STAT = 2, ATH11K_PKTLOG_TYPE_TX_MSDU_ID = 3, @@ -107,6 +138,130 @@ enum ath11k_dbg_aggr_mode { ATH11K_DBG_AGGR_MODE_MAX, }; +enum fw_dbglog_wlan_module_id { + WLAN_MODULE_ID_MIN = 0, + WLAN_MODULE_INF = WLAN_MODULE_ID_MIN, + WLAN_MODULE_WMI, + WLAN_MODULE_STA_PWRSAVE, + WLAN_MODULE_WHAL, + WLAN_MODULE_COEX, + WLAN_MODULE_ROAM, + WLAN_MODULE_RESMGR_CHAN_MANAGER, + WLAN_MODULE_RESMGR, + WLAN_MODULE_VDEV_MGR, + WLAN_MODULE_SCAN, + WLAN_MODULE_RATECTRL, + WLAN_MODULE_AP_PWRSAVE, + WLAN_MODULE_BLOCKACK, + WLAN_MODULE_MGMT_TXRX, + WLAN_MODULE_DATA_TXRX, + WLAN_MODULE_HTT, + WLAN_MODULE_HOST, + WLAN_MODULE_BEACON, + WLAN_MODULE_OFFLOAD, + WLAN_MODULE_WAL, + WLAN_WAL_MODULE_DE, + WLAN_MODULE_PCIELP, + WLAN_MODULE_RTT, + WLAN_MODULE_RESOURCE, + WLAN_MODULE_DCS, + WLAN_MODULE_CACHEMGR, + WLAN_MODULE_ANI, + WLAN_MODULE_P2P, + WLAN_MODULE_CSA, + WLAN_MODULE_NLO, + WLAN_MODULE_CHATTER, + WLAN_MODULE_WOW, + WLAN_MODULE_WAL_VDEV, + WLAN_MODULE_WAL_PDEV, + WLAN_MODULE_TEST, + WLAN_MODULE_STA_SMPS, + WLAN_MODULE_SWBMISS, + WLAN_MODULE_WMMAC, + WLAN_MODULE_TDLS, + WLAN_MODULE_HB, + WLAN_MODULE_TXBF, + WLAN_MODULE_BATCH_SCAN, + WLAN_MODULE_THERMAL_MGR, + WLAN_MODULE_PHYERR_DFS, + WLAN_MODULE_RMC, + WLAN_MODULE_STATS, + WLAN_MODULE_NAN, + WLAN_MODULE_IBSS_PWRSAVE, + WLAN_MODULE_HIF_UART, + WLAN_MODULE_LPI, + WLAN_MODULE_EXTSCAN, + WLAN_MODULE_UNIT_TEST, + WLAN_MODULE_MLME, + WLAN_MODULE_SUPPL, + WLAN_MODULE_ERE, + WLAN_MODULE_OCB, + WLAN_MODULE_RSSI_MONITOR, + WLAN_MODULE_WPM, + WLAN_MODULE_CSS, + WLAN_MODULE_PPS, + WLAN_MODULE_SCAN_CH_PREDICT, + WLAN_MODULE_MAWC, + WLAN_MODULE_CMC_QMIC, + WLAN_MODULE_EGAP, + WLAN_MODULE_NAN20, + WLAN_MODULE_QBOOST, + WLAN_MODULE_P2P_LISTEN_OFFLOAD, + WLAN_MODULE_HALPHY, + WLAN_WAL_MODULE_ENQ, + WLAN_MODULE_GNSS, + WLAN_MODULE_WAL_MEM, + WLAN_MODULE_SCHED_ALGO, + WLAN_MODULE_TX, + WLAN_MODULE_RX, + WLAN_MODULE_WLM, + WLAN_MODULE_RU_ALLOCATOR, + WLAN_MODULE_11K_OFFLOAD, + WLAN_MODULE_STA_TWT, + WLAN_MODULE_AP_TWT, + WLAN_MODULE_UL_OFDMA, + WLAN_MODULE_HPCS_PULSE, + WLAN_MODULE_DTF, + WLAN_MODULE_QUIET_IE, + WLAN_MODULE_SHMEM_MGR, + WLAN_MODULE_CFIR, + WLAN_MODULE_CODE_COVER, + WLAN_MODULE_SHO, + WLAN_MODULE_MLO_MGR, + WLAN_MODULE_PEER_INIT, + WLAN_MODULE_STA_MLO_PS, + + WLAN_MODULE_ID_MAX, + WLAN_MODULE_ID_INVALID = WLAN_MODULE_ID_MAX, +}; + +enum fw_dbglog_log_level { + ATH11K_FW_DBGLOG_ML = 0, + ATH11K_FW_DBGLOG_VERBOSE = 0, + ATH11K_FW_DBGLOG_INFO, + ATH11K_FW_DBGLOG_INFO_LVL_1, + ATH11K_FW_DBGLOG_INFO_LVL_2, + ATH11K_FW_DBGLOG_WARN, + ATH11K_FW_DBGLOG_ERR, + ATH11K_FW_DBGLOG_LVL_MAX +}; + +struct ath11k_fw_dbglog { + enum wmi_debug_log_param param; + union { + struct { + /* log_level values are given in enum fw_dbglog_log_level */ + u16 log_level; + /* module_id values are given in enum fw_dbglog_wlan_module_id */ + u16 module_id; + }; + /* value is either log_level&module_id/vdev_id/vdev_id_bitmap/log_level + * according to param + */ + u32 value; + }; +}; + #ifdef CONFIG_ATH11K_DEBUGFS int ath11k_debugfs_soc_create(struct ath11k_base *ab); void ath11k_debugfs_soc_destroy(struct ath11k_base *ab); @@ -151,6 +306,13 @@ static inline int ath11k_debugfs_rx_filter(struct ath11k *ar) return ar->debug.rx_filter; } +int ath11k_debugfs_add_interface(struct ath11k_vif *arvif); +void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif); +void ath11k_debugfs_add_dbring_entry(struct ath11k *ar, + enum wmi_direct_buffer_module id, + enum ath11k_dbg_dbr_event event, + struct hal_srng *srng); + #else static inline int ath11k_debugfs_soc_create(struct ath11k_base *ab) { @@ -224,6 +386,22 @@ static inline int ath11k_debugfs_get_fw_stats(struct ath11k *ar, return 0; } -#endif /* CONFIG_MAC80211_DEBUGFS*/ +static inline int ath11k_debugfs_add_interface(struct ath11k_vif *arvif) +{ + return 0; +} + +static inline void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif) +{ +} + +static inline void +ath11k_debugfs_add_dbring_entry(struct ath11k *ar, + enum wmi_direct_buffer_module id, + enum ath11k_dbg_dbr_event event, + struct hal_srng *srng) +{ +} +#endif /* CONFIG_ATH11K_DEBUGFS*/ #endif /* _ATH11K_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index 409d6cc5a1d5..e9dfa209098b 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -115,6 +115,8 @@ struct ath11k_pdev_mon_stats { u32 dest_mpdu_drop; u32 dup_mon_linkdesc_cnt; u32 dup_mon_buf_cnt; + u32 dest_mon_stuck; + u32 dest_mon_not_reaped; }; struct dp_full_mon_mpdu { @@ -167,6 +169,7 @@ struct ath11k_mon_data { struct ath11k_pdev_dp { u32 mac_id; + u32 mon_dest_ring_stuck_cnt; atomic_t num_tx_pending; wait_queue_head_t tx_empty_waitq; struct dp_rxdma_ring rx_refill_buf_ring; @@ -1170,12 +1173,12 @@ struct ath11k_htt_ppdu_stats_msg { u32 ppdu_id; u32 timestamp; u32 rsvd; - u8 data[0]; + u8 data[]; } __packed; struct htt_tlv { u32 header; - u8 value[0]; + u8 value[]; } __packed; #define HTT_TLV_TAG GENMASK(11, 0) @@ -1362,7 +1365,7 @@ struct htt_ppdu_stats_usr_cmn_array { * tx_ppdu_stats_info is variable length, with length = * number_of_ppdu_stats * sizeof (struct htt_tx_ppdu_stats_info) */ - struct htt_tx_ppdu_stats_info tx_ppdu_info[0]; + struct htt_tx_ppdu_stats_info tx_ppdu_info[]; } __packed; struct htt_ppdu_user_stats { @@ -1424,7 +1427,7 @@ struct htt_ppdu_stats_info { */ struct htt_pktlog_msg { u32 hdr; - u8 payload[0]; + u8 payload[]; }; /** @@ -1645,7 +1648,7 @@ struct ath11k_htt_extd_stats_msg { u32 info0; u64 cookie; u32 info1; - u8 data[0]; + u8 data[]; } __packed; #define HTT_MAC_ADDR_L32_0 GENMASK(7, 0) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index c212a789421e..049774cc158c 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -42,6 +42,13 @@ static inline u8 ath11k_dp_rx_h_msdu_start_decap_type(struct ath11k_base *ab, return ab->hw_params.hw_ops->rx_desc_get_decap_type(desc); } +static inline +bool ath11k_dp_rx_h_msdu_start_ldpc_support(struct ath11k_base *ab, + struct hal_rx_desc *desc) +{ + return ab->hw_params.hw_ops->rx_desc_get_ldpc_support(desc); +} + static inline u8 ath11k_dp_rx_h_msdu_start_mesh_ctl_present(struct ath11k_base *ab, struct hal_rx_desc *desc) @@ -2313,7 +2320,7 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, u8 bw; u8 rate_mcs, nss; u8 sgi; - bool is_cck; + bool is_cck, is_ldpc; pkt_type = ath11k_dp_rx_h_msdu_start_pkt_type(ar->ab, rx_desc); bw = ath11k_dp_rx_h_msdu_start_rx_bw(ar->ab, rx_desc); @@ -2355,6 +2362,9 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, if (sgi) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); + is_ldpc = ath11k_dp_rx_h_msdu_start_ldpc_support(ar->ab, rx_desc); + if (is_ldpc) + rx_status->enc_flags |= RX_ENC_FLAG_LDPC; break; case RX_MSDU_START_PKT_TYPE_11AX: rx_status->rate_idx = rate_mcs; @@ -2642,9 +2652,9 @@ int ath11k_dp_process_rx(struct ath11k_base *ab, int ring_id, spin_lock_bh(&srng->lock); +try_again: ath11k_hal_srng_access_begin(ab, srng); -try_again: while (likely(desc = (struct hal_reo_dest_ring *)ath11k_hal_srng_dst_get_next_entry(ab, srng))) { @@ -3080,79 +3090,6 @@ static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id, return num_buffs_reaped; } -int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, - struct napi_struct *napi, int budget) -{ - struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); - enum hal_rx_mon_status hal_status; - struct sk_buff *skb; - struct sk_buff_head skb_list; - struct hal_rx_mon_ppdu_info ppdu_info; - struct ath11k_peer *peer; - struct ath11k_sta *arsta; - int num_buffs_reaped = 0; - u32 rx_buf_sz; - u16 log_type = 0; - - __skb_queue_head_init(&skb_list); - - num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ab, mac_id, &budget, - &skb_list); - if (!num_buffs_reaped) - goto exit; - - memset(&ppdu_info, 0, sizeof(ppdu_info)); - ppdu_info.peer_id = HAL_INVALID_PEERID; - - while ((skb = __skb_dequeue(&skb_list))) { - if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar)) { - log_type = ATH11K_PKTLOG_TYPE_LITE_RX; - rx_buf_sz = DP_RX_BUFFER_SIZE_LITE; - } else if (ath11k_debugfs_is_pktlog_rx_stats_enabled(ar)) { - log_type = ATH11K_PKTLOG_TYPE_RX_STATBUF; - rx_buf_sz = DP_RX_BUFFER_SIZE; - } - - if (log_type) - trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); - - hal_status = ath11k_hal_rx_parse_mon_status(ab, &ppdu_info, skb); - - if (ppdu_info.peer_id == HAL_INVALID_PEERID || - hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { - dev_kfree_skb_any(skb); - continue; - } - - rcu_read_lock(); - spin_lock_bh(&ab->base_lock); - peer = ath11k_peer_find_by_id(ab, ppdu_info.peer_id); - - if (!peer || !peer->sta) { - ath11k_dbg(ab, ATH11K_DBG_DATA, - "failed to find the peer with peer_id %d\n", - ppdu_info.peer_id); - goto next_skb; - } - - arsta = (struct ath11k_sta *)peer->sta->drv_priv; - ath11k_dp_rx_update_peer_stats(arsta, &ppdu_info); - - if (ath11k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) - trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); - -next_skb: - spin_unlock_bh(&ab->base_lock); - rcu_read_unlock(); - - dev_kfree_skb_any(skb); - memset(&ppdu_info, 0, sizeof(ppdu_info)); - ppdu_info.peer_id = HAL_INVALID_PEERID; - } -exit: - return num_buffs_reaped; -} - static void ath11k_dp_rx_frag_timer(struct timer_list *timer) { struct dp_rx_tid *rx_tid = from_timer(rx_tid, timer, frag_timer); @@ -4870,7 +4807,6 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11k *ar, { struct ath11k_base *ab = ar->ab; struct sk_buff *msdu, *prev_buf; - u32 wifi_hdr_len; struct hal_rx_desc *rx_desc; char *hdr_desc; u8 *dest, decap_format; @@ -4912,38 +4848,27 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11k *ar, skb_trim(prev_buf, prev_buf->len - HAL_RX_FCS_LEN); } else if (decap_format == DP_RX_DECAP_TYPE_NATIVE_WIFI) { - __le16 qos_field; u8 qos_pkt = 0; rx_desc = (struct hal_rx_desc *)head_msdu->data; hdr_desc = ath11k_dp_rxdesc_get_80211hdr(ab, rx_desc); /* Base size */ - wifi_hdr_len = sizeof(struct ieee80211_hdr_3addr); wh = (struct ieee80211_hdr_3addr *)hdr_desc; - if (ieee80211_is_data_qos(wh->frame_control)) { - struct ieee80211_qos_hdr *qwh = - (struct ieee80211_qos_hdr *)hdr_desc; - - qos_field = qwh->qos_ctrl; + if (ieee80211_is_data_qos(wh->frame_control)) qos_pkt = 1; - } + msdu = head_msdu; while (msdu) { - rx_desc = (struct hal_rx_desc *)msdu->data; - hdr_desc = ath11k_dp_rxdesc_get_80211hdr(ab, rx_desc); - + ath11k_dp_rx_msdus_set_payload(ar, msdu); if (qos_pkt) { dest = skb_push(msdu, sizeof(__le16)); if (!dest) goto err_merge_fail; - memcpy(dest, hdr_desc, wifi_hdr_len); - memcpy(dest + wifi_hdr_len, - (u8 *)&qos_field, sizeof(__le16)); + memcpy(dest, hdr_desc, sizeof(struct ieee80211_qos_hdr)); } - ath11k_dp_rx_msdus_set_payload(ar, msdu); prev_buf = msdu; msdu = msdu->next; } @@ -4967,8 +4892,98 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11k *ar, return NULL; } +static void +ath11k_dp_rx_update_radiotap_he(struct hal_rx_mon_ppdu_info *rx_status, + u8 *rtap_buf) +{ + u32 rtap_len = 0; + + put_unaligned_le16(rx_status->he_data1, &rtap_buf[rtap_len]); + rtap_len += 2; + + put_unaligned_le16(rx_status->he_data2, &rtap_buf[rtap_len]); + rtap_len += 2; + + put_unaligned_le16(rx_status->he_data3, &rtap_buf[rtap_len]); + rtap_len += 2; + + put_unaligned_le16(rx_status->he_data4, &rtap_buf[rtap_len]); + rtap_len += 2; + + put_unaligned_le16(rx_status->he_data5, &rtap_buf[rtap_len]); + rtap_len += 2; + + put_unaligned_le16(rx_status->he_data6, &rtap_buf[rtap_len]); +} + +static void +ath11k_dp_rx_update_radiotap_he_mu(struct hal_rx_mon_ppdu_info *rx_status, + u8 *rtap_buf) +{ + u32 rtap_len = 0; + + put_unaligned_le16(rx_status->he_flags1, &rtap_buf[rtap_len]); + rtap_len += 2; + + put_unaligned_le16(rx_status->he_flags2, &rtap_buf[rtap_len]); + rtap_len += 2; + + rtap_buf[rtap_len] = rx_status->he_RU[0]; + rtap_len += 1; + + rtap_buf[rtap_len] = rx_status->he_RU[1]; + rtap_len += 1; + + rtap_buf[rtap_len] = rx_status->he_RU[2]; + rtap_len += 1; + + rtap_buf[rtap_len] = rx_status->he_RU[3]; +} + +static void ath11k_update_radiotap(struct ath11k *ar, + struct hal_rx_mon_ppdu_info *ppduinfo, + struct sk_buff *mon_skb, + struct ieee80211_rx_status *rxs) +{ + struct ieee80211_supported_band *sband; + u8 *ptr = NULL; + + rxs->flag |= RX_FLAG_MACTIME_START; + rxs->signal = ppduinfo->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR; + + if (ppduinfo->nss) + rxs->nss = ppduinfo->nss; + + if (ppduinfo->he_mu_flags) { + rxs->flag |= RX_FLAG_RADIOTAP_HE_MU; + rxs->encoding = RX_ENC_HE; + ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he_mu)); + ath11k_dp_rx_update_radiotap_he_mu(ppduinfo, ptr); + } else if (ppduinfo->he_flags) { + rxs->flag |= RX_FLAG_RADIOTAP_HE; + rxs->encoding = RX_ENC_HE; + ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he)); + ath11k_dp_rx_update_radiotap_he(ppduinfo, ptr); + rxs->rate_idx = ppduinfo->rate; + } else if (ppduinfo->vht_flags) { + rxs->encoding = RX_ENC_VHT; + rxs->rate_idx = ppduinfo->rate; + } else if (ppduinfo->ht_flags) { + rxs->encoding = RX_ENC_HT; + rxs->rate_idx = ppduinfo->rate; + } else { + rxs->encoding = RX_ENC_LEGACY; + sband = &ar->mac.sbands[rxs->band]; + rxs->rate_idx = ath11k_mac_hw_rate_to_idx(sband, ppduinfo->rate, + ppduinfo->cck_flag); + } + + rxs->mactime = ppduinfo->tsft; +} + static int ath11k_dp_rx_mon_deliver(struct ath11k *ar, u32 mac_id, struct sk_buff *head_msdu, + struct hal_rx_mon_ppdu_info *ppduinfo, struct sk_buff *tail_msdu, struct napi_struct *napi) { @@ -5003,7 +5018,7 @@ static int ath11k_dp_rx_mon_deliver(struct ath11k *ar, u32 mac_id, } else { rxs->flag |= RX_FLAG_ALLOW_SAME_PN; } - rxs->flag |= RX_FLAG_ONLY_MONITOR; + ath11k_update_radiotap(ar, ppduinfo, mon_skb, rxs); ath11k_dp_rx_deliver_msdu(ar, napi, mon_skb, rxs); mon_skb = skb_next; @@ -5022,6 +5037,12 @@ static int ath11k_dp_rx_mon_deliver(struct ath11k *ar, u32 mac_id, return -EINVAL; } +/* The destination ring processing is stuck if the destination is not + * moving while status ring moves 16 PPDU. The destination ring processing + * skips this destination ring PPDU as a workaround. + */ +#define MON_DEST_RING_STUCK_MAX_CNT 16 + static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, u32 quota, struct napi_struct *napi) { @@ -5035,6 +5056,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, u32 ring_id; struct ath11k_pdev_mon_stats *rx_mon_stats; u32 npackets = 0; + u32 mpdu_rx_bufs_used; if (ar->ab->hw_params.rxdma1_enable) ring_id = dp->rxdma_mon_dst_ring.ring_id; @@ -5064,20 +5086,44 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, head_msdu = NULL; tail_msdu = NULL; - rx_bufs_used += ath11k_dp_rx_mon_mpdu_pop(ar, mac_id, ring_entry, - &head_msdu, - &tail_msdu, - &npackets, &ppdu_id); + mpdu_rx_bufs_used = ath11k_dp_rx_mon_mpdu_pop(ar, mac_id, ring_entry, + &head_msdu, + &tail_msdu, + &npackets, &ppdu_id); + + rx_bufs_used += mpdu_rx_bufs_used; + + if (mpdu_rx_bufs_used) { + dp->mon_dest_ring_stuck_cnt = 0; + } else { + dp->mon_dest_ring_stuck_cnt++; + rx_mon_stats->dest_mon_not_reaped++; + } + + if (dp->mon_dest_ring_stuck_cnt > MON_DEST_RING_STUCK_MAX_CNT) { + rx_mon_stats->dest_mon_stuck++; + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "status ring ppdu_id=%d dest ring ppdu_id=%d mon_dest_ring_stuck_cnt=%d dest_mon_not_reaped=%u dest_mon_stuck=%u\n", + pmon->mon_ppdu_info.ppdu_id, ppdu_id, + dp->mon_dest_ring_stuck_cnt, + rx_mon_stats->dest_mon_not_reaped, + rx_mon_stats->dest_mon_stuck); + pmon->mon_ppdu_info.ppdu_id = ppdu_id; + continue; + } if (ppdu_id != pmon->mon_ppdu_info.ppdu_id) { pmon->mon_ppdu_status = DP_PPDU_STATUS_START; ath11k_dbg(ar->ab, ATH11K_DBG_DATA, - "dest_rx: new ppdu_id %x != status ppdu_id %x", - ppdu_id, pmon->mon_ppdu_info.ppdu_id); + "dest_rx: new ppdu_id %x != status ppdu_id %x dest_mon_not_reaped = %u dest_mon_stuck = %u\n", + ppdu_id, pmon->mon_ppdu_info.ppdu_id, + rx_mon_stats->dest_mon_not_reaped, + rx_mon_stats->dest_mon_stuck); break; } if (head_msdu && tail_msdu) { ath11k_dp_rx_mon_deliver(ar, dp->mac_id, head_msdu, + &pmon->mon_ppdu_info, tail_msdu, napi); rx_mon_stats->dest_mpdu_done++; } @@ -5106,36 +5152,92 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, } } -static void ath11k_dp_rx_mon_status_process_tlv(struct ath11k *ar, - int mac_id, u32 quota, - struct napi_struct *napi) +int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget) { - struct ath11k_pdev_dp *dp = &ar->dp; - struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; - struct hal_rx_mon_ppdu_info *ppdu_info; - struct sk_buff *status_skb; - u32 tlv_status = HAL_TLV_STATUS_BUF_DONE; - struct ath11k_pdev_mon_stats *rx_mon_stats; + struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); + enum hal_rx_mon_status hal_status; + struct sk_buff *skb; + struct sk_buff_head skb_list; + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + int num_buffs_reaped = 0; + u32 rx_buf_sz; + u16 log_type; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&ar->dp.mon_data; + struct ath11k_pdev_mon_stats *rx_mon_stats = &pmon->rx_mon_stats; + struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; - ppdu_info = &pmon->mon_ppdu_info; - rx_mon_stats = &pmon->rx_mon_stats; + __skb_queue_head_init(&skb_list); - if (pmon->mon_ppdu_status != DP_PPDU_STATUS_START) - return; + num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ab, mac_id, &budget, + &skb_list); + if (!num_buffs_reaped) + goto exit; - while (!skb_queue_empty(&pmon->rx_status_q)) { - status_skb = skb_dequeue(&pmon->rx_status_q); + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; - tlv_status = ath11k_hal_rx_parse_mon_status(ar->ab, ppdu_info, - status_skb); - if (tlv_status == HAL_TLV_STATUS_PPDU_DONE) { + while ((skb = __skb_dequeue(&skb_list))) { + if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar)) { + log_type = ATH11K_PKTLOG_TYPE_LITE_RX; + rx_buf_sz = DP_RX_BUFFER_SIZE_LITE; + } else if (ath11k_debugfs_is_pktlog_rx_stats_enabled(ar)) { + log_type = ATH11K_PKTLOG_TYPE_RX_STATBUF; + rx_buf_sz = DP_RX_BUFFER_SIZE; + } else { + log_type = ATH11K_PKTLOG_TYPE_INVALID; + rx_buf_sz = 0; + } + + if (log_type != ATH11K_PKTLOG_TYPE_INVALID) + trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + + memset(ppdu_info, 0, sizeof(struct hal_rx_mon_ppdu_info)); + hal_status = ath11k_hal_rx_parse_mon_status(ab, ppdu_info, skb); + + if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && + pmon->mon_ppdu_status == DP_PPDU_STATUS_START && + hal_status == HAL_TLV_STATUS_PPDU_DONE) { rx_mon_stats->status_ppdu_done++; pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; - ath11k_dp_rx_mon_dest_process(ar, mac_id, quota, napi); + ath11k_dp_rx_mon_dest_process(ar, mac_id, budget, napi); pmon->mon_ppdu_status = DP_PPDU_STATUS_START; } - dev_kfree_skb_any(status_skb); + + if (ppdu_info->peer_id == HAL_INVALID_PEERID || + hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { + dev_kfree_skb_any(skb); + continue; + } + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, ppdu_info->peer_id); + + if (!peer || !peer->sta) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "failed to find the peer with peer_id %d\n", + ppdu_info->peer_id); + goto next_skb; + } + + arsta = (struct ath11k_sta *)peer->sta->drv_priv; + ath11k_dp_rx_update_peer_stats(arsta, ppdu_info); + + if (ath11k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) + trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + +next_skb: + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + + dev_kfree_skb_any(skb); + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; } +exit: + return num_buffs_reaped; } static u32 @@ -5352,6 +5454,7 @@ static int ath11k_dp_rx_full_mon_deliver_ppdu(struct ath11k *ar, tail_msdu = mon_mpdu->tail; if (head_msdu && tail_msdu) { ret = ath11k_dp_rx_mon_deliver(ar, mac_id, head_msdu, + &pmon->mon_ppdu_info, tail_msdu, napi); rx_mon_stats->dest_mpdu_done++; ath11k_dbg(ar->ab, ATH11K_DBG_DATA, "full mon: deliver ppdu\n"); @@ -5489,22 +5592,6 @@ static int ath11k_dp_full_mon_process_rx(struct ath11k_base *ab, int mac_id, return quota; } -static int ath11k_dp_mon_process_rx(struct ath11k_base *ab, int mac_id, - struct napi_struct *napi, int budget) -{ - struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); - struct ath11k_pdev_dp *dp = &ar->dp; - struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; - int num_buffs_reaped = 0; - - num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ar->ab, mac_id, &budget, - &pmon->rx_status_q); - if (num_buffs_reaped) - ath11k_dp_rx_mon_status_process_tlv(ar, mac_id, budget, napi); - - return num_buffs_reaped; -} - int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id, struct napi_struct *napi, int budget) { @@ -5514,8 +5601,6 @@ int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id, if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && ab->hw_params.full_monitor_mode) ret = ath11k_dp_full_mon_process_rx(ab, mac_id, napi, budget); - else if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) - ret = ath11k_dp_mon_process_rx(ab, mac_id, napi, budget); else ret = ath11k_dp_rx_process_mon_status(ab, mac_id, napi, budget); diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 91d6244b6543..00a45819907e 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -351,7 +351,8 @@ ath11k_dp_tx_htt_tx_complete_buf(struct ath11k_base *ab, info->flags |= IEEE80211_TX_STAT_ACK; info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR + ts->ack_rssi; - info->status.is_valid_ack_signal = true; + info->status.flags |= + IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; } else { info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; } @@ -426,7 +427,7 @@ void ath11k_dp_tx_update_txcompl(struct ath11k *ar, struct hal_tx_status *ts) struct ath11k_sta *arsta; struct ieee80211_sta *sta; u16 rate, ru_tones; - u8 mcs, rate_idx, ofdma; + u8 mcs, rate_idx = 0, ofdma; int ret; spin_lock_bh(&ab->base_lock); @@ -518,9 +519,13 @@ static void ath11k_dp_tx_complete_msdu(struct ath11k *ar, struct sk_buff *msdu, struct hal_tx_status *ts) { + struct ieee80211_tx_status status = { 0 }; struct ath11k_base *ab = ar->ab; struct ieee80211_tx_info *info; struct ath11k_skb_cb *skb_cb; + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + struct rate_info rate; if (WARN_ON_ONCE(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) { /* Must not happen */ @@ -552,7 +557,7 @@ static void ath11k_dp_tx_complete_msdu(struct ath11k *ar, info->flags |= IEEE80211_TX_STAT_ACK; info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR + ts->ack_rssi; - info->status.is_valid_ack_signal = true; + info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; } if (ts->status == HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX && @@ -583,12 +588,26 @@ static void ath11k_dp_tx_complete_msdu(struct ath11k *ar, ath11k_dp_tx_cache_peer_stats(ar, msdu, ts); } - /* NOTE: Tx rate status reporting. Tx completion status does not have - * necessary information (for example nss) to build the tx rate. - * Might end up reporting it out-of-band from HTT stats. - */ + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, ts->peer_id); + if (!peer || !peer->sta) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "dp_tx: failed to find the peer with peer_id %d\n", + ts->peer_id); + spin_unlock_bh(&ab->base_lock); + dev_kfree_skb_any(msdu); + return; + } + arsta = (struct ath11k_sta *)peer->sta->drv_priv; + status.sta = peer->sta; + status.skb = msdu; + status.info = info; + rate = arsta->last_txrate; + status.rate = &rate; - ieee80211_tx_status(ar->hw, msdu); + spin_unlock_bh(&ab->base_lock); + + ieee80211_tx_status_ext(ar->hw, &status); } static inline void ath11k_dp_tx_status_parse(struct ath11k_base *ab, diff --git a/drivers/net/wireless/ath/ath11k/hal_desc.h b/drivers/net/wireless/ath/ath11k/hal_desc.h index 406767672844..24e72e75a8c7 100644 --- a/drivers/net/wireless/ath/ath11k/hal_desc.h +++ b/drivers/net/wireless/ath/ath11k/hal_desc.h @@ -474,6 +474,7 @@ enum hal_tlv_tag { #define HAL_TLV_HDR_TAG GENMASK(9, 1) #define HAL_TLV_HDR_LEN GENMASK(25, 10) +#define HAL_TLV_USR_ID GENMASK(31, 26) #define HAL_TLV_ALIGN 4 diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c index a3b353a4b5f7..4bb1fbaed0c9 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.c +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -453,10 +453,12 @@ void ath11k_hal_reo_status_queue_stats(struct ath11k_base *ab, u32 *reo_desc, desc->info0)); ath11k_dbg(ab, ATH11k_DBG_HAL, "pn = [%08x, %08x, %08x, %08x]\n", desc->pn[0], desc->pn[1], desc->pn[2], desc->pn[3]); - ath11k_dbg(ab, ATH11k_DBG_HAL, "last_rx: enqueue_tstamp %08x dequeue_tstamp %08x\n", + ath11k_dbg(ab, ATH11k_DBG_HAL, + "last_rx: enqueue_tstamp %08x dequeue_tstamp %08x\n", desc->last_rx_enqueue_timestamp, desc->last_rx_dequeue_timestamp); - ath11k_dbg(ab, ATH11k_DBG_HAL, "rx_bitmap [%08x %08x %08x %08x %08x %08x %08x %08x]\n", + ath11k_dbg(ab, ATH11k_DBG_HAL, + "rx_bitmap [%08x %08x %08x %08x %08x %08x %08x %08x]\n", desc->rx_bitmap[0], desc->rx_bitmap[1], desc->rx_bitmap[2], desc->rx_bitmap[3], desc->rx_bitmap[4], desc->rx_bitmap[5], desc->rx_bitmap[6], desc->rx_bitmap[7]); @@ -802,12 +804,75 @@ void ath11k_hal_reo_init_cmd_ring(struct ath11k_base *ab, } } +#define HAL_MAX_UL_MU_USERS 37 +static inline void +ath11k_hal_rx_handle_ofdma_info(void *rx_tlv, + struct hal_rx_user_status *rx_user_status) +{ + struct hal_rx_ppdu_end_user_stats *ppdu_end_user = + (struct hal_rx_ppdu_end_user_stats *)rx_tlv; + + rx_user_status->ul_ofdma_user_v0_word0 = __le32_to_cpu(ppdu_end_user->info6); + + rx_user_status->ul_ofdma_user_v0_word1 = __le32_to_cpu(ppdu_end_user->rsvd2[10]); +} + +static inline void +ath11k_hal_rx_populate_byte_count(void *rx_tlv, void *ppduinfo, + struct hal_rx_user_status *rx_user_status) +{ + struct hal_rx_ppdu_end_user_stats *ppdu_end_user = + (struct hal_rx_ppdu_end_user_stats *)rx_tlv; + + rx_user_status->mpdu_ok_byte_count = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_RSVD2_6_MPDU_OK_BYTE_COUNT, + __le32_to_cpu(ppdu_end_user->rsvd2[6])); + rx_user_status->mpdu_err_byte_count = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_RSVD2_8_MPDU_ERR_BYTE_COUNT, + __le32_to_cpu(ppdu_end_user->rsvd2[8])); +} + +static inline void +ath11k_hal_rx_populate_mu_user_info(void *rx_tlv, struct hal_rx_mon_ppdu_info *ppdu_info, + struct hal_rx_user_status *rx_user_status) +{ + rx_user_status->ast_index = ppdu_info->ast_index; + rx_user_status->tid = ppdu_info->tid; + rx_user_status->tcp_msdu_count = + ppdu_info->tcp_msdu_count; + rx_user_status->udp_msdu_count = + ppdu_info->udp_msdu_count; + rx_user_status->other_msdu_count = + ppdu_info->other_msdu_count; + rx_user_status->frame_control = ppdu_info->frame_control; + rx_user_status->frame_control_info_valid = + ppdu_info->frame_control_info_valid; + rx_user_status->data_sequence_control_info_valid = + ppdu_info->data_sequence_control_info_valid; + rx_user_status->first_data_seq_ctrl = + ppdu_info->first_data_seq_ctrl; + rx_user_status->preamble_type = ppdu_info->preamble_type; + rx_user_status->ht_flags = ppdu_info->ht_flags; + rx_user_status->vht_flags = ppdu_info->vht_flags; + rx_user_status->he_flags = ppdu_info->he_flags; + rx_user_status->rs_flags = ppdu_info->rs_flags; + + rx_user_status->mpdu_cnt_fcs_ok = + ppdu_info->num_mpdu_fcs_ok; + rx_user_status->mpdu_cnt_fcs_err = + ppdu_info->num_mpdu_fcs_err; + + ath11k_hal_rx_populate_byte_count(rx_tlv, ppdu_info, rx_user_status); +} + static enum hal_rx_mon_status ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, struct hal_rx_mon_ppdu_info *ppdu_info, - u32 tlv_tag, u8 *tlv_data) + u32 tlv_tag, u8 *tlv_data, u32 userid) { - u32 info0, info1; + u32 info0, info1, value; + u8 he_dcm = 0, he_stbc = 0; + u16 he_gi = 0, he_ltf = 0; switch (tlv_tag) { case HAL_RX_PPDU_START: { @@ -828,6 +893,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, info0 = __le32_to_cpu(eu_stats->info0); info1 = __le32_to_cpu(eu_stats->info1); + ppdu_info->ast_index = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO2_AST_INDEX, + __le32_to_cpu(eu_stats->info2)); ppdu_info->tid = ffs(FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO6_TID_BITMAP, __le32_to_cpu(eu_stats->info6))) - 1; @@ -851,6 +919,44 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, ppdu_info->num_mpdu_fcs_err = FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR, info0); + switch (ppdu_info->preamble_type) { + case HAL_RX_PREAMBLE_11N: + ppdu_info->ht_flags = 1; + break; + case HAL_RX_PREAMBLE_11AC: + ppdu_info->vht_flags = 1; + break; + case HAL_RX_PREAMBLE_11AX: + ppdu_info->he_flags = 1; + break; + default: + break; + } + + if (userid < HAL_MAX_UL_MU_USERS) { + struct hal_rx_user_status *rxuser_stats = + &ppdu_info->userstats; + + ath11k_hal_rx_handle_ofdma_info(tlv_data, rxuser_stats); + ath11k_hal_rx_populate_mu_user_info(tlv_data, ppdu_info, + rxuser_stats); + } + ppdu_info->userstats.mpdu_fcs_ok_bitmap[0] = + __le32_to_cpu(eu_stats->rsvd1[0]); + ppdu_info->userstats.mpdu_fcs_ok_bitmap[1] = + __le32_to_cpu(eu_stats->rsvd1[1]); + + break; + } + case HAL_RX_PPDU_END_USER_STATS_EXT: { + struct hal_rx_ppdu_end_user_stats_ext *eu_stats = + (struct hal_rx_ppdu_end_user_stats_ext *)tlv_data; + ppdu_info->userstats.mpdu_fcs_ok_bitmap[2] = eu_stats->info1; + ppdu_info->userstats.mpdu_fcs_ok_bitmap[3] = eu_stats->info2; + ppdu_info->userstats.mpdu_fcs_ok_bitmap[4] = eu_stats->info3; + ppdu_info->userstats.mpdu_fcs_ok_bitmap[5] = eu_stats->info4; + ppdu_info->userstats.mpdu_fcs_ok_bitmap[6] = eu_stats->info5; + ppdu_info->userstats.mpdu_fcs_ok_bitmap[7] = eu_stats->info6; break; } case HAL_PHYRX_HT_SIG: { @@ -949,50 +1055,151 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, else ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; + ppdu_info->vht_flag_values5 = group_id; + ppdu_info->vht_flag_values3[0] = (((ppdu_info->mcs) << 4) | + ppdu_info->nss); + ppdu_info->vht_flag_values2 = ppdu_info->bw; + ppdu_info->vht_flag_values4 = + FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO1_SU_MU_CODING, info1); break; } case HAL_PHYRX_HE_SIG_A_SU: { struct hal_rx_he_sig_a_su_info *he_sig_a = (struct hal_rx_he_sig_a_su_info *)tlv_data; - u32 nsts, cp_ltf, dcm; + ppdu_info->he_flags = 1; info0 = __le32_to_cpu(he_sig_a->info0); info1 = __le32_to_cpu(he_sig_a->info1); - ppdu_info->mcs = - FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_MCS, - info0); - ppdu_info->bw = - FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_BW, - info0); - ppdu_info->ldpc = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_CODING, info0); - ppdu_info->is_stbc = info1 & - HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC; - ppdu_info->beamformed = info1 & - HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF; - dcm = info0 & HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM; - cp_ltf = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_CP_LTF_SIZE, - info0); - nsts = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS, info0); + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_FORMAT_IND, info0); - switch (cp_ltf) { + if (value == 0) + ppdu_info->he_data1 = IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG; + else + ppdu_info->he_data1 = IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU; + + ppdu_info->he_data1 |= + IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN; + + ppdu_info->he_data2 |= + IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN; + + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_BSS_COLOR, info0); + ppdu_info->he_data3 = + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR, value); + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_BEAM_CHANGE, info0); + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE, value); + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_DL_UL_FLAG, info0); + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_UL_DL, value); + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_MCS, info0); + ppdu_info->mcs = value; + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS, value); + + he_dcm = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM, info0); + ppdu_info->dcm = he_dcm; + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_DATA_DCM, he_dcm); + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_CODING, info1); + ppdu_info->ldpc = (value == HAL_RX_SU_MU_CODING_LDPC) ? 1 : 0; + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_CODING, value); + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_LDPC_EXTRA, info1); + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG, value); + he_stbc = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC, info1); + ppdu_info->is_stbc = he_stbc; + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_STBC, he_stbc); + + /* data4 */ + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_SPATIAL_REUSE, info0); + ppdu_info->he_data4 = + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE, value); + + /* data5 */ + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_BW, info0); + ppdu_info->bw = value; + ppdu_info->he_data5 = + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC, value); + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_CP_LTF_SIZE, info0); + switch (value) { case 0: + he_gi = HE_GI_0_8; + he_ltf = HE_LTF_1_X; + break; case 1: - ppdu_info->gi = HAL_RX_GI_0_8_US; - break; + he_gi = HE_GI_0_8; + he_ltf = HE_LTF_2_X; + break; case 2: - ppdu_info->gi = HAL_RX_GI_1_6_US; - break; + he_gi = HE_GI_1_6; + he_ltf = HE_LTF_2_X; + break; case 3: - if (dcm && ppdu_info->is_stbc) - ppdu_info->gi = HAL_RX_GI_0_8_US; - else - ppdu_info->gi = HAL_RX_GI_3_2_US; - break; + if (he_dcm && he_stbc) { + he_gi = HE_GI_0_8; + he_ltf = HE_LTF_4_X; + } else { + he_gi = HE_GI_3_2; + he_ltf = HE_LTF_4_X; + } + break; } + ppdu_info->gi = he_gi; + he_gi = (he_gi != 0) ? he_gi - 1 : 0; + ppdu_info->he_data5 |= FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_GI, he_gi); + ppdu_info->ltf_size = he_ltf; + ppdu_info->he_data5 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE, + (he_ltf == HE_LTF_4_X) ? he_ltf - 1 : he_ltf); + + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS, info0); + ppdu_info->he_data5 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS, value); + + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_PKT_EXT_FACTOR, info1); + ppdu_info->he_data5 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD, value); + + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF, info1); + ppdu_info->beamformed = value; + ppdu_info->he_data5 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_TXBF, value); + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_PKT_EXT_PE_DISAM, info1); + ppdu_info->he_data5 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG, value); + + /* data6 */ + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS, info0); + value++; + ppdu_info->nss = value; + ppdu_info->he_data6 = + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA6_NSTS, value); + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_DOPPLER_IND, info1); + ppdu_info->he_data6 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA6_DOPPLER, value); + value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXOP_DURATION, info1); + ppdu_info->he_data6 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA6_TXOP, value); - ppdu_info->nss = nsts + 1; - ppdu_info->dcm = dcm; ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; break; } @@ -1000,29 +1207,142 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, struct hal_rx_he_sig_a_mu_dl_info *he_sig_a_mu_dl = (struct hal_rx_he_sig_a_mu_dl_info *)tlv_data; - u32 cp_ltf; - info0 = __le32_to_cpu(he_sig_a_mu_dl->info0); info1 = __le32_to_cpu(he_sig_a_mu_dl->info1); - ppdu_info->bw = - FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW, - info0); - cp_ltf = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE, - info0); + ppdu_info->he_mu_flags = 1; - switch (cp_ltf) { + ppdu_info->he_data1 = IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU; + ppdu_info->he_data1 |= + IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN; + + ppdu_info->he_data2 = + IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN; + + /*data3*/ + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_BSS_COLOR, info0); + ppdu_info->he_data3 = + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR, value); + + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_UL_FLAG, info0); + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_UL_DL, value); + + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_LDPC_EXTRA, info1); + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG, value); + + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_STBC, info1); + he_stbc = value; + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_STBC, value); + + /*data4*/ + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_SPATIAL_REUSE, info0); + ppdu_info->he_data4 = + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE, value); + + /*data5*/ + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW, info0); + ppdu_info->bw = value; + ppdu_info->he_data5 = + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC, value); + + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE, info0); + switch (value) { case 0: + he_gi = HE_GI_0_8; + he_ltf = HE_LTF_4_X; + break; case 1: - ppdu_info->gi = HAL_RX_GI_0_8_US; + he_gi = HE_GI_0_8; + he_ltf = HE_LTF_2_X; break; case 2: - ppdu_info->gi = HAL_RX_GI_1_6_US; + he_gi = HE_GI_1_6; + he_ltf = HE_LTF_2_X; break; case 3: - ppdu_info->gi = HAL_RX_GI_3_2_US; + he_gi = HE_GI_3_2; + he_ltf = HE_LTF_4_X; break; } + ppdu_info->gi = he_gi; + he_gi = (he_gi != 0) ? he_gi - 1 : 0; + ppdu_info->he_data5 |= FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_GI, he_gi); + ppdu_info->ltf_size = he_ltf; + ppdu_info->he_data5 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE, + (he_ltf == HE_LTF_4_X) ? he_ltf - 1 : he_ltf); + + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_NUM_LTF_SYMB, info1); + ppdu_info->he_data5 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS, value); + + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_PKT_EXT_FACTOR, + info1); + ppdu_info->he_data5 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD, value); + + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_PKT_EXT_PE_DISAM, + info1); + ppdu_info->he_data5 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG, value); + + /*data6*/ + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_DOPPLER_INDICATION, + info0); + ppdu_info->he_data6 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA6_DOPPLER, value); + + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_TXOP_DURATION, info1); + ppdu_info->he_data6 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA6_TXOP, value); + + /* HE-MU Flags */ + /* HE-MU-flags1 */ + ppdu_info->he_flags1 = + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN; + + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_MCS_OF_SIGB, info0); + ppdu_info->he_flags1 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN, + value); + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_DCM_OF_SIGB, info0); + ppdu_info->he_flags1 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN, + value); + + /* HE-MU-flags2 */ + ppdu_info->he_flags2 = + IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN; + + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW, info0); + ppdu_info->he_flags2 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW, + value); + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_COMP_MODE_SIGB, info0); + ppdu_info->he_flags2 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP, value); + value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_NUM_SIGB_SYMB, info0); + value = value - 1; + ppdu_info->he_flags2 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS, + value); ppdu_info->is_stbc = info1 & HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_STBC; @@ -1040,7 +1360,7 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, info0); ppdu_info->ru_alloc = ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(ru_tones); - + ppdu_info->he_RU[0] = ru_tones; ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; break; } @@ -1050,14 +1370,25 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, info0 = __le32_to_cpu(he_sig_b2_mu->info0); + ppdu_info->he_data1 |= IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN; + ppdu_info->mcs = - FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_MCS, - info0); + FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_MCS, info0); + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS, ppdu_info->mcs); + + value = FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_CODING, info0); + ppdu_info->ldpc = value; + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_CODING, value); + + value = FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_ID, info0); + ppdu_info->he_data4 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA4_MU_STA_ID, value); + ppdu_info->nss = - FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS, - info0) + 1; - ppdu_info->ldpc = FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_CODING, - info0); + FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS, info0) + 1; break; } case HAL_PHYRX_HE_SIG_B2_OFDMA: { @@ -1066,17 +1397,40 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, info0 = __le32_to_cpu(he_sig_b2_ofdma->info0); + ppdu_info->he_data1 |= + IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN; + + /* HE-data2 */ + ppdu_info->he_data2 |= IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN; + ppdu_info->mcs = FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_MCS, info0); + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS, ppdu_info->mcs); + + value = FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_DCM, info0); + he_dcm = value; + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_DATA_DCM, value); + + value = FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_CODING, info0); + ppdu_info->ldpc = value; + ppdu_info->he_data3 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_CODING, value); + + /* HE-data4 */ + value = FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_ID, info0); + ppdu_info->he_data4 |= + FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA4_MU_STA_ID, value); + ppdu_info->nss = FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS, info0) + 1; ppdu_info->beamformed = - info0 & - HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF; - ppdu_info->ldpc = FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_CODING, - info0); + info0 & HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF; ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA; break; } @@ -1118,6 +1472,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, ppdu_info->rx_duration = FIELD_GET(HAL_RX_PPDU_END_DURATION, __le32_to_cpu(ppdu_rx_duration->info0)); + ppdu_info->tsft = __le32_to_cpu(ppdu_rx_duration->rsvd0[1]); + ppdu_info->tsft = (ppdu_info->tsft << 32) | + __le32_to_cpu(ppdu_rx_duration->rsvd0[0]); break; } case HAL_DUMMY: @@ -1141,12 +1498,14 @@ ath11k_hal_rx_parse_mon_status(struct ath11k_base *ab, enum hal_rx_mon_status hal_status = HAL_RX_MON_STATUS_BUF_DONE; u16 tlv_tag; u16 tlv_len; + u32 tlv_userid = 0; u8 *ptr = skb->data; do { tlv = (struct hal_tlv_hdr *)ptr; tlv_tag = FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl); tlv_len = FIELD_GET(HAL_TLV_HDR_LEN, tlv->tl); + tlv_userid = FIELD_GET(HAL_TLV_USR_ID, tlv->tl); ptr += sizeof(*tlv); /* The actual length of PPDU_END is the combined length of many PHY @@ -1158,7 +1517,7 @@ ath11k_hal_rx_parse_mon_status(struct ath11k_base *ab, tlv_len = sizeof(struct hal_rx_rxpcu_classification_overview); hal_status = ath11k_hal_rx_parse_mon_status_tlv(ab, ppdu_info, - tlv_tag, ptr); + tlv_tag, ptr, tlv_userid); ptr += tlv_len; ptr = PTR_ALIGN(ptr, HAL_TLV_ALIGN); diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.h b/drivers/net/wireless/ath/ath11k/hal_rx.h index 571054c6d7f8..f6bae07abfd3 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.h +++ b/drivers/net/wireless/ath/ath11k/hal_rx.h @@ -65,10 +65,6 @@ enum hal_rx_reception_type { HAL_RX_RECEPTION_TYPE_MAX, }; -#define HAL_TLV_STATUS_PPDU_NOT_DONE 0 -#define HAL_TLV_STATUS_PPDU_DONE 1 -#define HAL_TLV_STATUS_BUF_DONE 2 -#define HAL_TLV_STATUS_PPDU_NON_STD_DONE 3 #define HAL_RX_FCS_LEN 4 enum hal_rx_mon_status { @@ -77,6 +73,40 @@ enum hal_rx_mon_status { HAL_RX_MON_STATUS_BUF_DONE, }; +struct hal_rx_user_status { + u32 mcs:4, + nss:3, + ofdma_info_valid:1, + dl_ofdma_ru_start_index:7, + dl_ofdma_ru_width:7, + dl_ofdma_ru_size:8; + u32 ul_ofdma_user_v0_word0; + u32 ul_ofdma_user_v0_word1; + u32 ast_index; + u32 tid; + u16 tcp_msdu_count; + u16 udp_msdu_count; + u16 other_msdu_count; + u16 frame_control; + u8 frame_control_info_valid; + u8 data_sequence_control_info_valid; + u16 first_data_seq_ctrl; + u32 preamble_type; + u16 ht_flags; + u16 vht_flags; + u16 he_flags; + u8 rs_flags; + u32 mpdu_cnt_fcs_ok; + u32 mpdu_cnt_fcs_err; + u32 mpdu_fcs_ok_bitmap[8]; + u32 mpdu_ok_byte_count; + u32 mpdu_err_byte_count; +}; + +#define HAL_TLV_STATUS_PPDU_NOT_DONE HAL_RX_MON_STATUS_PPDU_NOT_DONE +#define HAL_TLV_STATUS_PPDU_DONE HAL_RX_MON_STATUS_PPDU_DONE +#define HAL_TLV_STATUS_BUF_DONE HAL_RX_MON_STATUS_BUF_DONE + struct hal_sw_mon_ring_entries { dma_addr_t mon_dst_paddr; dma_addr_t mon_status_paddr; @@ -107,6 +137,12 @@ struct hal_rx_mon_ppdu_info { u8 mcs; u8 nss; u8 bw; + u8 vht_flag_values1; + u8 vht_flag_values2; + u8 vht_flag_values3[4]; + u8 vht_flag_values4; + u8 vht_flag_values5; + u16 vht_flag_values6; u8 is_stbc; u8 gi; u8 ldpc; @@ -114,10 +150,46 @@ struct hal_rx_mon_ppdu_info { u8 rssi_comb; u8 rssi_chain_pri20[HAL_RX_MAX_NSS]; u8 tid; + u16 ht_flags; + u16 vht_flags; + u16 he_flags; + u16 he_mu_flags; u8 dcm; u8 ru_alloc; u8 reception_type; + u64 tsft; u64 rx_duration; + u16 frame_control; + u32 ast_index; + u8 rs_fcs_err; + u8 rs_flags; + u8 cck_flag; + u8 ofdm_flag; + u8 ulofdma_flag; + u8 frame_control_info_valid; + u16 he_per_user_1; + u16 he_per_user_2; + u8 he_per_user_position; + u8 he_per_user_known; + u16 he_flags1; + u16 he_flags2; + u8 he_RU[4]; + u16 he_data1; + u16 he_data2; + u16 he_data3; + u16 he_data4; + u16 he_data5; + u16 he_data6; + u32 ppdu_len; + u32 prev_ppdu_id; + u32 device_id; + u16 first_data_seq_ctrl; + u8 monitor_direct_used; + u8 data_sequence_control_info_valid; + u8 ltf_size; + u8 rxpcu_filter_pass; + char rssi_chain[8][8]; + struct hal_rx_user_status userstats; }; #define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0) @@ -150,6 +222,9 @@ struct hal_rx_ppdu_start { #define HAL_RX_PPDU_END_USER_STATS_INFO6_TID_BITMAP GENMASK(15, 0) #define HAL_RX_PPDU_END_USER_STATS_INFO6_TID_EOSP_BITMAP GENMASK(31, 16) +#define HAL_RX_PPDU_END_USER_STATS_RSVD2_6_MPDU_OK_BYTE_COUNT GENMASK(24, 0) +#define HAL_RX_PPDU_END_USER_STATS_RSVD2_8_MPDU_ERR_BYTE_COUNT GENMASK(24, 0) + struct hal_rx_ppdu_end_user_stats { __le32 rsvd0[2]; __le32 info0; @@ -164,6 +239,16 @@ struct hal_rx_ppdu_end_user_stats { __le32 rsvd2[11]; } __packed; +struct hal_rx_ppdu_end_user_stats_ext { + u32 info0; + u32 info1; + u32 info2; + u32 info3; + u32 info4; + u32 info5; + u32 info6; +} __packed; + #define HAL_RX_HT_SIG_INFO_INFO0_MCS GENMASK(6, 0) #define HAL_RX_HT_SIG_INFO_INFO0_BW BIT(7) @@ -212,25 +297,62 @@ enum hal_rx_vht_sig_a_gi_setting { HAL_RX_VHT_SIG_A_SHORT_GI_AMBIGUITY = 3, }; +#define HAL_RX_SU_MU_CODING_LDPC 0x01 + +#define HE_GI_0_8 0 +#define HE_GI_0_4 1 +#define HE_GI_1_6 2 +#define HE_GI_3_2 3 + +#define HE_LTF_1_X 0 +#define HE_LTF_2_X 1 +#define HE_LTF_4_X 2 +#define HE_LTF_UNKNOWN 3 + #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_MCS GENMASK(6, 3) #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM BIT(7) #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_BW GENMASK(20, 19) #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_CP_LTF_SIZE GENMASK(22, 21) #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS GENMASK(25, 23) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_BSS_COLOR GENMASK(13, 8) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_SPATIAL_REUSE GENMASK(18, 15) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_FORMAT_IND BIT(0) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_BEAM_CHANGE BIT(1) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_DL_UL_FLAG BIT(2) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXOP_DURATION GENMASK(6, 0) #define HAL_RX_HE_SIG_A_SU_INFO_INFO1_CODING BIT(7) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_LDPC_EXTRA BIT(8) #define HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC BIT(9) #define HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF BIT(10) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_PKT_EXT_FACTOR GENMASK(12, 11) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_PKT_EXT_PE_DISAM BIT(13) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_DOPPLER_IND BIT(15) struct hal_rx_he_sig_a_su_info { __le32 info0; __le32 info1; } __packed; -#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW GENMASK(17, 15) -#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE GENMASK(24, 23) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_UL_FLAG BIT(1) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_MCS_OF_SIGB GENMASK(3, 1) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_DCM_OF_SIGB BIT(4) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_BSS_COLOR GENMASK(10, 5) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_SPATIAL_REUSE GENMASK(14, 11) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW GENMASK(17, 15) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_NUM_SIGB_SYMB GENMASK(21, 18) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_COMP_MODE_SIGB BIT(22) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE GENMASK(24, 23) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_DOPPLER_INDICATION BIT(25) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_TXOP_DURATION GENMASK(6, 0) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_CODING BIT(7) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_NUM_LTF_SYMB GENMASK(10, 8) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_LDPC_EXTRA BIT(11) #define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_STBC BIT(12) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_TXBF BIT(10) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_PKT_EXT_FACTOR GENMASK(14, 13) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_PKT_EXT_PE_DISAM BIT(15) struct hal_rx_he_sig_a_mu_dl_info { __le32 info0; @@ -243,6 +365,7 @@ struct hal_rx_he_sig_b1_mu_info { __le32 info0; } __packed; +#define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_ID GENMASK(10, 0) #define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_MCS GENMASK(18, 15) #define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_CODING BIT(20) #define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS GENMASK(31, 29) @@ -251,6 +374,7 @@ struct hal_rx_he_sig_b2_mu_info { __le32 info0; } __packed; +#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_ID GENMASK(10, 0) #define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS GENMASK(13, 11) #define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF BIT(19) #define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_MCS GENMASK(18, 15) @@ -279,11 +403,14 @@ struct hal_rx_phyrx_rssi_legacy_info { #define HAL_RX_MPDU_INFO_INFO0_PEERID GENMASK(31, 16) #define HAL_RX_MPDU_INFO_INFO0_PEERID_WCN6855 GENMASK(15, 0) +#define HAL_RX_MPDU_INFO_INFO1_MPDU_LEN GENMASK(13, 0) struct hal_rx_mpdu_info { __le32 rsvd0; __le32 info0; - __le32 rsvd1[21]; + __le32 rsvd1[11]; + __le32 info1; + __le32 rsvd2[9]; } __packed; struct hal_rx_mpdu_info_wcn6855 { diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c index 3b0fdc1a6b3f..d1b0e76d9ec2 100644 --- a/drivers/net/wireless/ath/ath11k/hw.c +++ b/drivers/net/wireless/ath/ath11k/hw.c @@ -273,6 +273,12 @@ static u8 ath11k_hw_ipq8074_rx_desc_get_mesh_ctl(struct hal_rx_desc *desc) __le32_to_cpu(desc->u.ipq8074.msdu_start.info2)); } +static bool ath11k_hw_ipq8074_rx_desc_get_ldpc_support(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_LDPC, + __le32_to_cpu(desc->u.ipq8074.msdu_start.info2)); +} + static bool ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld(struct hal_rx_desc *desc) { return !!FIELD_GET(RX_MPDU_START_INFO1_MPDU_SEQ_CTRL_VALID, @@ -444,6 +450,12 @@ static u8 ath11k_hw_qcn9074_rx_desc_get_mesh_ctl(struct hal_rx_desc *desc) __le32_to_cpu(desc->u.qcn9074.msdu_start.info2)); } +static bool ath11k_hw_qcn9074_rx_desc_get_ldpc_support(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_LDPC, + __le32_to_cpu(desc->u.qcn9074.msdu_start.info2)); +} + static bool ath11k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld(struct hal_rx_desc *desc) { return !!FIELD_GET(RX_MPDU_START_INFO11_MPDU_SEQ_CTRL_VALID, @@ -801,6 +813,12 @@ static u16 ath11k_hw_wcn6855_mpdu_info_get_peerid(u8 *tlv_data) return peer_id; } +static bool ath11k_hw_wcn6855_rx_desc_get_ldpc_support(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_LDPC, + __le32_to_cpu(desc->u.wcn6855.msdu_start.info2)); +} + const struct ath11k_hw_ops ipq8074_ops = { .get_hw_mac_from_pdev_id = ath11k_hw_ipq8074_mac_from_pdev_id, .wmi_init_config = ath11k_init_wmi_config_ipq8074, @@ -815,6 +833,7 @@ const struct ath11k_hw_ops ipq8074_ops = { .rx_desc_get_encrypt_type = ath11k_hw_ipq8074_rx_desc_get_encrypt_type, .rx_desc_get_decap_type = ath11k_hw_ipq8074_rx_desc_get_decap_type, .rx_desc_get_mesh_ctl = ath11k_hw_ipq8074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath11k_hw_ipq8074_rx_desc_get_ldpc_support, .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, .rx_desc_get_mpdu_fc_valid = ath11k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, .rx_desc_get_mpdu_start_seq_no = ath11k_hw_ipq8074_rx_desc_get_mpdu_start_seq_no, @@ -853,6 +872,7 @@ const struct ath11k_hw_ops ipq6018_ops = { .rx_desc_get_encrypt_type = ath11k_hw_ipq8074_rx_desc_get_encrypt_type, .rx_desc_get_decap_type = ath11k_hw_ipq8074_rx_desc_get_decap_type, .rx_desc_get_mesh_ctl = ath11k_hw_ipq8074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath11k_hw_ipq8074_rx_desc_get_ldpc_support, .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, .rx_desc_get_mpdu_fc_valid = ath11k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, .rx_desc_get_mpdu_start_seq_no = ath11k_hw_ipq8074_rx_desc_get_mpdu_start_seq_no, @@ -891,6 +911,7 @@ const struct ath11k_hw_ops qca6390_ops = { .rx_desc_get_encrypt_type = ath11k_hw_ipq8074_rx_desc_get_encrypt_type, .rx_desc_get_decap_type = ath11k_hw_ipq8074_rx_desc_get_decap_type, .rx_desc_get_mesh_ctl = ath11k_hw_ipq8074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath11k_hw_ipq8074_rx_desc_get_ldpc_support, .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, .rx_desc_get_mpdu_fc_valid = ath11k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, .rx_desc_get_mpdu_start_seq_no = ath11k_hw_ipq8074_rx_desc_get_mpdu_start_seq_no, @@ -929,6 +950,7 @@ const struct ath11k_hw_ops qcn9074_ops = { .rx_desc_get_encrypt_type = ath11k_hw_qcn9074_rx_desc_get_encrypt_type, .rx_desc_get_decap_type = ath11k_hw_qcn9074_rx_desc_get_decap_type, .rx_desc_get_mesh_ctl = ath11k_hw_qcn9074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath11k_hw_qcn9074_rx_desc_get_ldpc_support, .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld, .rx_desc_get_mpdu_fc_valid = ath11k_hw_qcn9074_rx_desc_get_mpdu_fc_valid, .rx_desc_get_mpdu_start_seq_no = ath11k_hw_qcn9074_rx_desc_get_mpdu_start_seq_no, @@ -967,6 +989,7 @@ const struct ath11k_hw_ops wcn6855_ops = { .rx_desc_get_encrypt_type = ath11k_hw_wcn6855_rx_desc_get_encrypt_type, .rx_desc_get_decap_type = ath11k_hw_wcn6855_rx_desc_get_decap_type, .rx_desc_get_mesh_ctl = ath11k_hw_wcn6855_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath11k_hw_wcn6855_rx_desc_get_ldpc_support, .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_wcn6855_rx_desc_get_mpdu_seq_ctl_vld, .rx_desc_get_mpdu_fc_valid = ath11k_hw_wcn6855_rx_desc_get_mpdu_fc_valid, .rx_desc_get_mpdu_start_seq_no = ath11k_hw_wcn6855_rx_desc_get_mpdu_start_seq_no, diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h index 29934b36c14e..27ca4a9c20fc 100644 --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -192,6 +192,8 @@ struct ath11k_hw_params { bool wakeup_mhi; bool supports_rssi_stats; bool fw_wmi_diag_event; + bool current_cc_support; + bool dbr_debug_support; }; struct ath11k_hw_ops { @@ -210,6 +212,7 @@ struct ath11k_hw_ops { u32 (*rx_desc_get_encrypt_type)(struct hal_rx_desc *desc); u8 (*rx_desc_get_decap_type)(struct hal_rx_desc *desc); u8 (*rx_desc_get_mesh_ctl)(struct hal_rx_desc *desc); + bool (*rx_desc_get_ldpc_support)(struct hal_rx_desc *desc); bool (*rx_desc_get_mpdu_seq_ctl_vld)(struct hal_rx_desc *desc); bool (*rx_desc_get_mpdu_fc_valid)(struct hal_rx_desc *desc); u16 (*rx_desc_get_mpdu_start_seq_no)(struct hal_rx_desc *desc); diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 07f499d5ec92..d5b83f90d27a 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -2319,6 +2319,9 @@ static void ath11k_peer_assoc_h_he_6ghz(struct ath11k *ar, if (!arg->he_flag || band != NL80211_BAND_6GHZ || !sta->he_6ghz_capa.capa) return; + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + arg->bw_40 = true; + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) arg->bw_80 = true; @@ -2862,6 +2865,11 @@ static void ath11k_recalculate_mgmt_rate(struct ath11k *ar, if (ret) ath11k_warn(ar->ab, "failed to set mgmt tx rate %d\n", ret); + /* For WCN6855, firmware will clear this param when vdev starts, hence + * cache it here so that we can reconfigure it once vdev starts. + */ + ar->hw_rate_code = hw_rate_code; + vdev_param = WMI_VDEV_PARAM_BEACON_RATE; ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, vdev_param, hw_rate_code); @@ -4504,24 +4512,30 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, sta->addr, arvif->vdev_id); } else if ((old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST)) { + bool skip_peer_delete = ar->ab->hw_params.vdev_start_delay && + vif->type == NL80211_IFTYPE_STATION; + ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); - if (ar->ab->hw_params.vdev_start_delay && - vif->type == NL80211_IFTYPE_STATION) - goto free; - - ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); - if (ret) - ath11k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - else - ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); + if (!skip_peer_delete) { + ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); + if (ret) + ath11k_warn(ar->ab, + "Failed to delete peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + else + ath11k_dbg(ar->ab, + ATH11K_DBG_MAC, + "Removed peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + } ath11k_mac_dec_num_stations(arvif, sta); spin_lock_bh(&ar->ab->base_lock); peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); - if (peer && peer->sta == sta) { + if (skip_peer_delete && peer) { + peer->sta = NULL; + } else if (peer && peer->sta == sta) { ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", vif->addr, arvif->vdev_id); peer->sta = NULL; @@ -4531,7 +4545,6 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, } spin_unlock_bh(&ar->ab->base_lock); -free: kfree(arsta->tx_stats); arsta->tx_stats = NULL; @@ -5566,7 +5579,7 @@ static int ath11k_mac_mgmt_tx(struct ath11k *ar, struct sk_buff *skb, skb_queue_tail(q, skb); atomic_inc(&ar->num_pending_mgmt_tx); - ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work); + queue_work(ar->ab->workqueue, &ar->wmi_mgmt_tx_work); return 0; } @@ -6341,6 +6354,10 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, } } + ret = ath11k_debugfs_add_interface(arvif); + if (ret) + goto err_peer_del; + mutex_unlock(&ar->conf_mutex); return 0; @@ -6375,6 +6392,7 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, spin_unlock_bh(&ar->data_lock); err: + ath11k_debugfs_remove_interface(arvif); mutex_unlock(&ar->conf_mutex); return ret; @@ -6473,6 +6491,8 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw, /* Recalc txpower for remaining vdev */ ath11k_mac_txpower_recalc(ar); + ath11k_debugfs_remove_interface(arvif); + /* TODO: recal traffic pause state based on the available vdevs */ mutex_unlock(&ar->conf_mutex); @@ -6610,12 +6630,13 @@ static void ath11k_mac_op_remove_chanctx(struct ieee80211_hw *hw, static int ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif, - const struct cfg80211_chan_def *chandef, + struct ieee80211_chanctx_conf *ctx, bool restart) { struct ath11k *ar = arvif->ar; struct ath11k_base *ab = ar->ab; struct wmi_vdev_start_req_arg arg = {}; + const struct cfg80211_chan_def *chandef = &ctx->def; int he_support = arvif->vif->bss_conf.he_support; int ret = 0; @@ -6650,8 +6671,7 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif, arg.channel.chan_radar = !!(chandef->chan->flags & IEEE80211_CHAN_RADAR); - arg.channel.freq2_radar = - !!(chandef->chan->flags & IEEE80211_CHAN_RADAR); + arg.channel.freq2_radar = ctx->radar_enabled; arg.channel.passive = arg.channel.chan_radar; @@ -6761,15 +6781,15 @@ static int ath11k_mac_vdev_stop(struct ath11k_vif *arvif) } static int ath11k_mac_vdev_start(struct ath11k_vif *arvif, - const struct cfg80211_chan_def *chandef) + struct ieee80211_chanctx_conf *ctx) { - return ath11k_mac_vdev_start_restart(arvif, chandef, false); + return ath11k_mac_vdev_start_restart(arvif, ctx, false); } static int ath11k_mac_vdev_restart(struct ath11k_vif *arvif, - const struct cfg80211_chan_def *chandef) + struct ieee80211_chanctx_conf *ctx) { - return ath11k_mac_vdev_start_restart(arvif, chandef, true); + return ath11k_mac_vdev_start_restart(arvif, ctx, true); } struct ath11k_mac_change_chanctx_arg { @@ -6836,13 +6856,33 @@ ath11k_mac_update_vif_chan(struct ath11k *ar, if (WARN_ON(!arvif->is_started)) continue; - if (WARN_ON(!arvif->is_up)) - continue; + /* change_chanctx can be called even before vdev_up from + * ieee80211_start_ap->ieee80211_vif_use_channel-> + * ieee80211_recalc_radar_chanctx. + * + * Firmware expect vdev_restart only if vdev is up. + * If vdev is down then it expect vdev_stop->vdev_start. + */ + if (arvif->is_up) { + ret = ath11k_mac_vdev_restart(arvif, vifs[i].new_ctx); + if (ret) { + ath11k_warn(ab, "failed to restart vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } else { + ret = ath11k_mac_vdev_stop(arvif); + if (ret) { + ath11k_warn(ab, "failed to stop vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + + ret = ath11k_mac_vdev_start(arvif, vifs[i].new_ctx); + if (ret) + ath11k_warn(ab, "failed to start vdev %d: %d\n", + arvif->vdev_id, ret); - ret = ath11k_mac_vdev_restart(arvif, &vifs[i].new_ctx->def); - if (ret) { - ath11k_warn(ab, "failed to restart vdev %d: %d\n", - arvif->vdev_id, ret); continue; } @@ -6927,7 +6967,8 @@ static void ath11k_mac_op_change_chanctx(struct ieee80211_hw *hw, if (WARN_ON(changed & IEEE80211_CHANCTX_CHANGE_CHANNEL)) goto unlock; - if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) + if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH || + changed & IEEE80211_CHANCTX_CHANGE_RADAR) ath11k_mac_update_active_vif_chan(ar, ctx); /* TODO: Recalc radar detection */ @@ -6947,7 +6988,7 @@ static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, if (WARN_ON(arvif->is_started)) return -EBUSY; - ret = ath11k_mac_vdev_start(arvif, &arvif->chanctx.def); + ret = ath11k_mac_vdev_start(arvif, &arvif->chanctx); if (ret) { ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", arvif->vdev_id, vif->addr, @@ -6955,6 +6996,19 @@ static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, return ret; } + /* Reconfigure hardware rate code since it is cleared by firmware. + */ + if (ar->hw_rate_code > 0) { + u32 vdev_param = WMI_VDEV_PARAM_MGMT_RATE; + + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, vdev_param, + ar->hw_rate_code); + if (ret) { + ath11k_warn(ar->ab, "failed to set mgmt tx rate %d\n", ret); + return ret; + } + } + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { ret = ath11k_wmi_vdev_up(ar, arvif->vdev_id, 0, ar->mac_addr); if (ret) { @@ -7028,7 +7082,7 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, goto out; } - ret = ath11k_mac_vdev_start(arvif, &ctx->def); + ret = ath11k_mac_vdev_start(arvif, ctx); if (ret) { ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", arvif->vdev_id, vif->addr, @@ -8422,7 +8476,7 @@ static int __ath11k_mac_register(struct ath11k *ar) ar->hw->queues = ATH11K_HW_MAX_QUEUES; ar->hw->wiphy->tx_queue_len = ATH11K_QUEUE_LEN; ar->hw->offchannel_tx_hw_queue = ATH11K_HW_MAX_QUEUES - 1; - ar->hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; + ar->hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; ar->hw->vif_data_size = sizeof(struct ath11k_vif); ar->hw->sta_data_size = sizeof(struct ath11k_sta); diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c index e4250ba8dfee..fc3524e83e52 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.c +++ b/drivers/net/wireless/ath/ath11k/mhi.c @@ -13,6 +13,7 @@ #include "pci.h" #define MHI_TIMEOUT_DEFAULT_MS 90000 +#define RDDM_DUMP_SIZE 0x420000 static struct mhi_channel_config ath11k_mhi_channels_qca6390[] = { { @@ -332,6 +333,7 @@ static int ath11k_mhi_read_addr_from_dt(struct mhi_controller *mhi_ctrl) return -ENOENT; ret = of_address_to_resource(np, 0, &res); + of_node_put(np); if (ret) return ret; @@ -381,6 +383,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci) mhi_ctrl->iova_stop = 0xFFFFFFFF; } + mhi_ctrl->rddm_size = RDDM_DUMP_SIZE; mhi_ctrl->sbl_size = SZ_512K; mhi_ctrl->seg_len = SZ_512K; mhi_ctrl->fbc_download = true; @@ -560,7 +563,7 @@ static int ath11k_mhi_set_state(struct ath11k_pci *ab_pci, ret = 0; break; case ATH11K_MHI_POWER_ON: - ret = mhi_async_power_up(ab_pci->mhi_ctrl); + ret = mhi_sync_power_up(ab_pci->mhi_ctrl); break; case ATH11K_MHI_POWER_OFF: mhi_power_down(ab_pci->mhi_ctrl, true); diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index de71ad594f34..903758751c99 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -1571,6 +1571,11 @@ static __maybe_unused int ath11k_pci_pm_suspend(struct device *dev) struct ath11k_base *ab = dev_get_drvdata(dev); int ret; + if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { + ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot skipping pci suspend as qmi is not initialised\n"); + return 0; + } + ret = ath11k_core_suspend(ab); if (ret) ath11k_warn(ab, "failed to suspend core: %d\n", ret); @@ -1583,6 +1588,11 @@ static __maybe_unused int ath11k_pci_pm_resume(struct device *dev) struct ath11k_base *ab = dev_get_drvdata(dev); int ret; + if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { + ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot skipping pci resume as qmi is not initialised\n"); + return 0; + } + ret = ath11k_core_resume(ab); if (ret) ath11k_warn(ab, "failed to resume core: %d\n", ret); diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c index 85471f8b3563..332886bc6b33 100644 --- a/drivers/net/wireless/ath/ath11k/peer.c +++ b/drivers/net/wireless/ath/ath11k/peer.c @@ -252,7 +252,7 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, { struct ath11k_peer *peer; struct ath11k_sta *arsta; - int ret; + int ret, fbret; lockdep_assert_held(&ar->conf_mutex); @@ -291,22 +291,8 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n", param->peer_addr, param->vdev_id); - reinit_completion(&ar->peer_delete_done); - - ret = ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, - param->vdev_id); - if (ret) { - ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", - param->vdev_id, param->peer_addr); - return ret; - } - - ret = ath11k_wait_for_peer_delete_done(ar, param->vdev_id, - param->peer_addr); - if (ret) - return ret; - - return -ENOENT; + ret = -ENOENT; + goto cleanup; } peer->pdev_idx = ar->pdev_idx; @@ -335,4 +321,24 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, spin_unlock_bh(&ar->ab->base_lock); return 0; + +cleanup: + reinit_completion(&ar->peer_delete_done); + + fbret = ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, + param->vdev_id); + if (fbret) { + ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", + param->vdev_id, param->peer_addr); + goto exit; + } + + fbret = ath11k_wait_for_peer_delete_done(ar, param->vdev_id, + param->peer_addr); + if (fbret) + ath11k_warn(ar->ab, "failed wait for peer %pM delete done id %d fallback ret %d\n", + param->peer_addr, param->vdev_id, fbret); + +exit: + return ret; } diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 65d3c6ba35ae..04e966830c18 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -1932,10 +1932,11 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) if (!hremote_node) { ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi fail to get hremote_node\n"); - return ret; + return -ENODEV; } ret = of_address_to_resource(hremote_node, 0, &res); + of_node_put(hremote_node); if (ret) { ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi fail to get reg from hremote\n"); @@ -2341,6 +2342,7 @@ static void ath11k_qmi_m3_free(struct ath11k_base *ab) dma_free_coherent(ab->dev, m3_mem->size, m3_mem->vaddr, m3_mem->paddr); m3_mem->vaddr = NULL; + m3_mem->size = 0; } static int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab) @@ -2958,7 +2960,11 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work) clear_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); - ath11k_core_qmi_firmware_ready(ab); + ret = ath11k_core_qmi_firmware_ready(ab); + if (ret) { + set_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags); + break; + } set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags); } @@ -3024,3 +3030,8 @@ void ath11k_qmi_deinit_service(struct ath11k_base *ab) } EXPORT_SYMBOL(ath11k_qmi_deinit_service); +void ath11k_qmi_free_resource(struct ath11k_base *ab) +{ + ath11k_qmi_free_target_mem_chunk(ab); + ath11k_qmi_m3_free(ab); +} diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index ba2eff4d59cb..61678de56ac7 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -492,5 +492,6 @@ void ath11k_qmi_event_work(struct work_struct *work); void ath11k_qmi_msg_recv_work(struct work_struct *work); void ath11k_qmi_deinit_service(struct ath11k_base *ab); int ath11k_qmi_init_service(struct ath11k_base *ab); +void ath11k_qmi_free_resource(struct ath11k_base *ab); #endif diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index d6575feca5a2..81e11cde31d7 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -48,6 +48,7 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct wmi_init_country_params init_country_param; + struct wmi_set_current_country_params set_current_param = {}; struct ath11k *ar = hw->priv; int ret; @@ -76,18 +77,26 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) return; } - /* Set the country code to the firmware and wait for + /* Set the country code to the firmware and will receive * the WMI_REG_CHAN_LIST_CC EVENT for updating the * reg info */ - init_country_param.flags = ALPHA_IS_SET; - memcpy(&init_country_param.cc_info.alpha2, request->alpha2, 2); - init_country_param.cc_info.alpha2[2] = 0; + if (ar->ab->hw_params.current_cc_support) { + memcpy(&set_current_param.alpha2, request->alpha2, 2); + ret = ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); + if (ret) + ath11k_warn(ar->ab, + "failed set current country code: %d\n", ret); + } else { + init_country_param.flags = ALPHA_IS_SET; + memcpy(&init_country_param.cc_info.alpha2, request->alpha2, 2); + init_country_param.cc_info.alpha2[2] = 0; - ret = ath11k_wmi_send_init_country_cmd(ar, init_country_param); - if (ret) - ath11k_warn(ar->ab, - "INIT Country code set to fw failed : %d\n", ret); + ret = ath11k_wmi_send_init_country_cmd(ar, init_country_param); + if (ret) + ath11k_warn(ar->ab, + "INIT Country code set to fw failed : %d\n", ret); + } ath11k_mac_11d_scan_stop(ar); ar->regdom_set_by_user = true; diff --git a/drivers/net/wireless/ath/ath11k/rx_desc.h b/drivers/net/wireless/ath/ath11k/rx_desc.h index 79c50804d7dc..26ecc1bcd9d5 100644 --- a/drivers/net/wireless/ath/ath11k/rx_desc.h +++ b/drivers/net/wireless/ath/ath11k/rx_desc.h @@ -1445,7 +1445,7 @@ struct hal_rx_desc_ipq8074 { __le32 hdr_status_tag; __le32 phy_ppdu_id; u8 hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; - u8 msdu_payload[0]; + u8 msdu_payload[]; } __packed; struct hal_rx_desc_qcn9074 { @@ -1464,7 +1464,7 @@ struct hal_rx_desc_qcn9074 { __le32 hdr_status_tag; __le32 phy_ppdu_id; u8 hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; - u8 msdu_payload[0]; + u8 msdu_payload[]; } __packed; struct hal_rx_desc_wcn6855 { @@ -1483,7 +1483,7 @@ struct hal_rx_desc_wcn6855 { __le32 hdr_status_tag; __le32 phy_ppdu_id; u8 hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; - u8 msdu_payload[0]; + u8 msdu_payload[]; } __packed; struct hal_rx_desc { diff --git a/drivers/net/wireless/ath/ath11k/spectral.c b/drivers/net/wireless/ath/ath11k/spectral.c index 4100cc1449a2..2b18871d5f7c 100644 --- a/drivers/net/wireless/ath/ath11k/spectral.c +++ b/drivers/net/wireless/ath/ath11k/spectral.c @@ -107,7 +107,7 @@ struct spectral_search_fft_report { __le32 info1; __le32 info2; __le32 reserve0; - u8 bins[0]; + u8 bins[]; } __packed; struct ath11k_spectral_search_report { diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 6b68ccf65e39..b4f86c45d81f 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -144,6 +144,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = { .min_len = sizeof(struct wmi_11d_new_cc_ev) }, [WMI_TAG_PER_CHAIN_RSSI_STATS] = { .min_len = sizeof(struct wmi_per_chain_rssi_stats) }, + [WMI_TAG_TWT_ADD_DIALOG_COMPLETE_EVENT] = { + .min_len = sizeof(struct wmi_twt_add_dialog_event) }, }; #define PRIMAP(_hw_mode_) \ @@ -3085,11 +3087,12 @@ ath11k_wmi_send_twt_enable_cmd(struct ath11k *ar, u32 pdev_id) /* TODO add MBSSID support */ cmd->mbss_support = 0; - ret = ath11k_wmi_cmd_send(wmi, skb, - WMI_TWT_ENABLE_CMDID); + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_ENABLE_CMDID); if (ret) { ath11k_warn(ab, "Failed to send WMI_TWT_ENABLE_CMDID"); dev_kfree_skb(skb); + } else { + ar->twt_enabled = 1; } return ret; } @@ -3114,11 +3117,181 @@ ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id) FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); cmd->pdev_id = pdev_id; - ret = ath11k_wmi_cmd_send(wmi, skb, - WMI_TWT_DISABLE_CMDID); + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_DISABLE_CMDID); if (ret) { ath11k_warn(ab, "Failed to send WMI_TWT_DISABLE_CMDID"); dev_kfree_skb(skb); + } else { + ar->twt_enabled = 0; + } + return ret; +} + +int ath11k_wmi_send_twt_add_dialog_cmd(struct ath11k *ar, + struct wmi_twt_add_dialog_params *params) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct ath11k_base *ab = wmi->wmi_ab->ab; + struct wmi_twt_add_dialog_params_cmd *cmd; + struct sk_buff *skb; + int ret, len; + + len = sizeof(*cmd); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_twt_add_dialog_params_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TWT_ADD_DIALOG_CMD) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + + cmd->vdev_id = params->vdev_id; + ether_addr_copy(cmd->peer_macaddr.addr, params->peer_macaddr); + cmd->dialog_id = params->dialog_id; + cmd->wake_intvl_us = params->wake_intvl_us; + cmd->wake_intvl_mantis = params->wake_intvl_mantis; + cmd->wake_dura_us = params->wake_dura_us; + cmd->sp_offset_us = params->sp_offset_us; + cmd->flags = params->twt_cmd; + if (params->flag_bcast) + cmd->flags |= WMI_TWT_ADD_DIALOG_FLAG_BCAST; + if (params->flag_trigger) + cmd->flags |= WMI_TWT_ADD_DIALOG_FLAG_TRIGGER; + if (params->flag_flow_type) + cmd->flags |= WMI_TWT_ADD_DIALOG_FLAG_FLOW_TYPE; + if (params->flag_protection) + cmd->flags |= WMI_TWT_ADD_DIALOG_FLAG_PROTECTION; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi add twt dialog vdev %u dialog id %u wake interval %u mantissa %u wake duration %u service period offset %u flags 0x%x\n", + cmd->vdev_id, cmd->dialog_id, cmd->wake_intvl_us, + cmd->wake_intvl_mantis, cmd->wake_dura_us, cmd->sp_offset_us, + cmd->flags); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_ADD_DIALOG_CMDID); + + if (ret) { + ath11k_warn(ab, + "failed to send wmi command to add twt dialog: %d", + ret); + dev_kfree_skb(skb); + } + return ret; +} + +int ath11k_wmi_send_twt_del_dialog_cmd(struct ath11k *ar, + struct wmi_twt_del_dialog_params *params) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct ath11k_base *ab = wmi->wmi_ab->ab; + struct wmi_twt_del_dialog_params_cmd *cmd; + struct sk_buff *skb; + int ret, len; + + len = sizeof(*cmd); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_twt_del_dialog_params_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TWT_DEL_DIALOG_CMD) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + + cmd->vdev_id = params->vdev_id; + ether_addr_copy(cmd->peer_macaddr.addr, params->peer_macaddr); + cmd->dialog_id = params->dialog_id; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi delete twt dialog vdev %u dialog id %u\n", + cmd->vdev_id, cmd->dialog_id); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_DEL_DIALOG_CMDID); + if (ret) { + ath11k_warn(ab, + "failed to send wmi command to delete twt dialog: %d", + ret); + dev_kfree_skb(skb); + } + return ret; +} + +int ath11k_wmi_send_twt_pause_dialog_cmd(struct ath11k *ar, + struct wmi_twt_pause_dialog_params *params) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct ath11k_base *ab = wmi->wmi_ab->ab; + struct wmi_twt_pause_dialog_params_cmd *cmd; + struct sk_buff *skb; + int ret, len; + + len = sizeof(*cmd); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_twt_pause_dialog_params_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_TWT_PAUSE_DIALOG_CMD) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + + cmd->vdev_id = params->vdev_id; + ether_addr_copy(cmd->peer_macaddr.addr, params->peer_macaddr); + cmd->dialog_id = params->dialog_id; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi pause twt dialog vdev %u dialog id %u\n", + cmd->vdev_id, cmd->dialog_id); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_PAUSE_DIALOG_CMDID); + if (ret) { + ath11k_warn(ab, + "failed to send wmi command to pause twt dialog: %d", + ret); + dev_kfree_skb(skb); + } + return ret; +} + +int ath11k_wmi_send_twt_resume_dialog_cmd(struct ath11k *ar, + struct wmi_twt_resume_dialog_params *params) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct ath11k_base *ab = wmi->wmi_ab->ab; + struct wmi_twt_resume_dialog_params_cmd *cmd; + struct sk_buff *skb; + int ret, len; + + len = sizeof(*cmd); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_twt_resume_dialog_params_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_TWT_RESUME_DIALOG_CMD) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + + cmd->vdev_id = params->vdev_id; + ether_addr_copy(cmd->peer_macaddr.addr, params->peer_macaddr); + cmd->dialog_id = params->dialog_id; + cmd->sp_offset_us = params->sp_offset_us; + cmd->next_twt_size = params->next_twt_size; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi resume twt dialog vdev %u dialog id %u service period offset %u next twt subfield size %u\n", + cmd->vdev_id, cmd->dialog_id, cmd->sp_offset_us, + cmd->next_twt_size); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_RESUME_DIALOG_CMDID); + if (ret) { + ath11k_warn(ab, + "failed to send wmi command to resume twt dialog: %d", + ret); + dev_kfree_skb(skb); } return ret; } @@ -7532,6 +7705,66 @@ ath11k_wmi_diag_event(struct ath11k_base *ab, trace_ath11k_wmi_diag(ab, skb->data, skb->len); } +static const char *ath11k_wmi_twt_add_dialog_event_status(u32 status) +{ + switch (status) { + case WMI_ADD_TWT_STATUS_OK: + return "ok"; + case WMI_ADD_TWT_STATUS_TWT_NOT_ENABLED: + return "twt disabled"; + case WMI_ADD_TWT_STATUS_USED_DIALOG_ID: + return "dialog id in use"; + case WMI_ADD_TWT_STATUS_INVALID_PARAM: + return "invalid parameters"; + case WMI_ADD_TWT_STATUS_NOT_READY: + return "not ready"; + case WMI_ADD_TWT_STATUS_NO_RESOURCE: + return "resource unavailable"; + case WMI_ADD_TWT_STATUS_NO_ACK: + return "no ack"; + case WMI_ADD_TWT_STATUS_NO_RESPONSE: + return "no response"; + case WMI_ADD_TWT_STATUS_DENIED: + return "denied"; + case WMI_ADD_TWT_STATUS_UNKNOWN_ERROR: + fallthrough; + default: + return "unknown error"; + } +} + +static void ath11k_wmi_twt_add_dialog_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + const void **tb; + const struct wmi_twt_add_dialog_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, + "failed to parse wmi twt add dialog status event tlv: %d\n", + ret); + return; + } + + ev = tb[WMI_TAG_TWT_ADD_DIALOG_COMPLETE_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch twt add dialog wmi event\n"); + goto exit; + } + + if (ev->status) + ath11k_warn(ab, + "wmi add twt dialog event vdev %d dialog id %d status %s\n", + ev->vdev_id, ev->dialog_id, + ath11k_wmi_twt_add_dialog_event_status(ev->status)); + +exit: + kfree(tb); +} + static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -7629,11 +7862,17 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID: ath11k_wmi_obss_color_collision_event(ab, skb); break; + case WMI_TWT_ADD_DIALOG_EVENTID: + ath11k_wmi_twt_add_dialog_event(ab, skb); + break; /* add Unsupported events here */ case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: case WMI_PEER_OPER_MODE_CHANGE_EVENTID: case WMI_TWT_ENABLE_EVENTID: case WMI_TWT_DISABLE_EVENTID: + case WMI_TWT_DEL_DIALOG_EVENTID: + case WMI_TWT_PAUSE_DIALOG_EVENTID: + case WMI_TWT_RESUME_DIALOG_EVENTID: case WMI_PDEV_DMA_RING_CFG_RSP_EVENTID: case WMI_PEER_CREATE_CONF_EVENTID: ath11k_dbg(ab, ATH11K_DBG_WMI, @@ -7798,6 +8037,59 @@ int ath11k_wmi_simulate_radar(struct ath11k *ar) return ath11k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args); } +int ath11k_wmi_fw_dbglog_cfg(struct ath11k *ar, u32 *module_id_bitmap, + struct ath11k_fw_dbglog *dbglog) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_debug_log_config_cmd_fixed_param *cmd; + struct sk_buff *skb; + struct wmi_tlv *tlv; + int ret, len; + + len = sizeof(*cmd) + TLV_HDR_SIZE + (MAX_MODULE_ID_BITMAP_WORDS * sizeof(u32)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_debug_log_config_cmd_fixed_param *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_DEBUG_LOG_CONFIG_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->dbg_log_param = dbglog->param; + + tlv = (struct wmi_tlv *)((u8 *)cmd + sizeof(*cmd)); + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_UINT32) | + FIELD_PREP(WMI_TLV_LEN, MAX_MODULE_ID_BITMAP_WORDS * sizeof(u32)); + + switch (dbglog->param) { + case WMI_DEBUG_LOG_PARAM_LOG_LEVEL: + case WMI_DEBUG_LOG_PARAM_VDEV_ENABLE: + case WMI_DEBUG_LOG_PARAM_VDEV_DISABLE: + case WMI_DEBUG_LOG_PARAM_VDEV_ENABLE_BITMAP: + cmd->value = dbglog->value; + break; + case WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP: + case WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP: + cmd->value = dbglog->value; + memcpy(tlv->value, module_id_bitmap, + MAX_MODULE_ID_BITMAP_WORDS * sizeof(u32)); + /* clear current config to be used for next user config */ + memset(module_id_bitmap, 0, + MAX_MODULE_ID_BITMAP_WORDS * sizeof(u32)); + break; + default: + dev_kfree_skb(skb); + return -EINVAL; + } + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_DBGLOG_CFG_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_DBGLOG_CFG_CMDID\n"); + dev_kfree_skb(skb); + } + return ret; +} + int ath11k_wmi_connect(struct ath11k_base *ab) { u32 i; diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 2f26ec1a8aa3..587f42307250 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -12,6 +12,7 @@ struct ath11k_base; struct ath11k; struct ath11k_fw_stats; +struct ath11k_fw_dbglog; #define PSOC_HOST_MAX_NUM_SS (8) @@ -4952,6 +4953,112 @@ struct wmi_twt_disable_params_cmd { u32 pdev_id; } __packed; +enum WMI_HOST_TWT_COMMAND { + WMI_HOST_TWT_COMMAND_REQUEST_TWT = 0, + WMI_HOST_TWT_COMMAND_SUGGEST_TWT, + WMI_HOST_TWT_COMMAND_DEMAND_TWT, + WMI_HOST_TWT_COMMAND_TWT_GROUPING, + WMI_HOST_TWT_COMMAND_ACCEPT_TWT, + WMI_HOST_TWT_COMMAND_ALTERNATE_TWT, + WMI_HOST_TWT_COMMAND_DICTATE_TWT, + WMI_HOST_TWT_COMMAND_REJECT_TWT, +}; + +#define WMI_TWT_ADD_DIALOG_FLAG_BCAST BIT(8) +#define WMI_TWT_ADD_DIALOG_FLAG_TRIGGER BIT(9) +#define WMI_TWT_ADD_DIALOG_FLAG_FLOW_TYPE BIT(10) +#define WMI_TWT_ADD_DIALOG_FLAG_PROTECTION BIT(11) + +struct wmi_twt_add_dialog_params_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 dialog_id; + u32 wake_intvl_us; + u32 wake_intvl_mantis; + u32 wake_dura_us; + u32 sp_offset_us; + u32 flags; +} __packed; + +struct wmi_twt_add_dialog_params { + u32 vdev_id; + u8 peer_macaddr[ETH_ALEN]; + u32 dialog_id; + u32 wake_intvl_us; + u32 wake_intvl_mantis; + u32 wake_dura_us; + u32 sp_offset_us; + u8 twt_cmd; + u8 flag_bcast; + u8 flag_trigger; + u8 flag_flow_type; + u8 flag_protection; +} __packed; + +enum wmi_twt_add_dialog_status { + WMI_ADD_TWT_STATUS_OK, + WMI_ADD_TWT_STATUS_TWT_NOT_ENABLED, + WMI_ADD_TWT_STATUS_USED_DIALOG_ID, + WMI_ADD_TWT_STATUS_INVALID_PARAM, + WMI_ADD_TWT_STATUS_NOT_READY, + WMI_ADD_TWT_STATUS_NO_RESOURCE, + WMI_ADD_TWT_STATUS_NO_ACK, + WMI_ADD_TWT_STATUS_NO_RESPONSE, + WMI_ADD_TWT_STATUS_DENIED, + WMI_ADD_TWT_STATUS_UNKNOWN_ERROR, +}; + +struct wmi_twt_add_dialog_event { + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 dialog_id; + u32 status; +} __packed; + +struct wmi_twt_del_dialog_params { + u32 vdev_id; + u8 peer_macaddr[ETH_ALEN]; + u32 dialog_id; +} __packed; + +struct wmi_twt_del_dialog_params_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 dialog_id; +} __packed; + +struct wmi_twt_pause_dialog_params { + u32 vdev_id; + u8 peer_macaddr[ETH_ALEN]; + u32 dialog_id; +} __packed; + +struct wmi_twt_pause_dialog_params_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 dialog_id; +} __packed; + +struct wmi_twt_resume_dialog_params { + u32 vdev_id; + u8 peer_macaddr[ETH_ALEN]; + u32 dialog_id; + u32 sp_offset_us; + u32 next_twt_size; +} __packed; + +struct wmi_twt_resume_dialog_params_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 dialog_id; + u32 sp_offset_us; + u32 next_twt_size; +} __packed; + struct wmi_obss_spatial_reuse_params_cmd { u32 tlv_header; u32 pdev_id; @@ -5240,6 +5347,21 @@ struct wmi_rfkill_state_change_ev { u32 radio_state; } __packed; +enum wmi_debug_log_param { + WMI_DEBUG_LOG_PARAM_LOG_LEVEL = 0x1, + WMI_DEBUG_LOG_PARAM_VDEV_ENABLE, + WMI_DEBUG_LOG_PARAM_VDEV_DISABLE, + WMI_DEBUG_LOG_PARAM_VDEV_ENABLE_BITMAP, + WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP, + WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP, +}; + +struct wmi_debug_log_config_cmd_fixed_param { + u32 tlv_header; + u32 dbg_log_param; + u32 value; +} __packed; + #define WMI_MAX_MEM_REQS 32 #define MAX_RADIOS 3 @@ -5546,6 +5668,14 @@ void ath11k_wmi_fw_stats_fill(struct ath11k *ar, int ath11k_wmi_simulate_radar(struct ath11k *ar); int ath11k_wmi_send_twt_enable_cmd(struct ath11k *ar, u32 pdev_id); int ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id); +int ath11k_wmi_send_twt_add_dialog_cmd(struct ath11k *ar, + struct wmi_twt_add_dialog_params *params); +int ath11k_wmi_send_twt_del_dialog_cmd(struct ath11k *ar, + struct wmi_twt_del_dialog_params *params); +int ath11k_wmi_send_twt_pause_dialog_cmd(struct ath11k *ar, + struct wmi_twt_pause_dialog_params *params); +int ath11k_wmi_send_twt_resume_dialog_cmd(struct ath11k *ar, + struct wmi_twt_resume_dialog_params *params); int ath11k_wmi_send_obss_spr_cmd(struct ath11k *ar, u32 vdev_id, struct ieee80211_he_obss_pd *he_obss_pd); int ath11k_wmi_pdev_set_srg_bss_color_bitmap(struct ath11k *ar, u32 *bitmap); @@ -5582,4 +5712,6 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar); int ath11k_wmi_wow_enable(struct ath11k *ar); int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar, const u8 mac_addr[ETH_ALEN]); +int ath11k_wmi_fw_dbglog_cfg(struct ath11k *ar, u32 *module_id_bitmap, + struct ath11k_fw_dbglog *dbglog); #endif diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 234ea939d316..f595204f493d 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1395,10 +1395,6 @@ struct ath5k_hw { u32 ah_txq_imr_nofrm; u32 ah_txq_isr_txok_all; - u32 ah_txq_isr_txurn; - u32 ah_txq_isr_qcborn; - u32 ah_txq_isr_qcburn; - u32 ah_txq_isr_qtrig; u32 *ah_rf_banks; size_t ah_rf_banks_size; diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c index e6c52f7c26e7..d9e376eb040e 100644 --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -650,6 +650,7 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) */ *interrupt_mask = (pisr & AR5K_INT_COMMON) & ah->ah_imr; + ah->ah_txq_isr_txok_all = 0; /* We treat TXOK,TXDESC, TXERR and TXEOL * the same way (schedule the tx tasklet) @@ -670,13 +671,6 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1, AR5K_SISR1_QCU_TXEOL); - /* Currently this is not much useful since we treat - * all queues the same way if we get a TXURN (update - * tx trigger level) but we might need it later on*/ - if (pisr & AR5K_ISR_TXURN) - ah->ah_txq_isr_txurn |= AR5K_REG_MS(sisr2, - AR5K_SISR2_QCU_TXURN); - /* Misc Beacon related interrupts */ /* For AR5211 */ @@ -709,25 +703,16 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) *interrupt_mask |= AR5K_INT_BNR; /* A queue got CBR overrun */ - if (unlikely(pisr & (AR5K_ISR_QCBRORN))) { + if (unlikely(pisr & (AR5K_ISR_QCBRORN))) *interrupt_mask |= AR5K_INT_QCBRORN; - ah->ah_txq_isr_qcborn |= AR5K_REG_MS(sisr3, - AR5K_SISR3_QCBRORN); - } /* A queue got CBR underrun */ - if (unlikely(pisr & (AR5K_ISR_QCBRURN))) { + if (unlikely(pisr & (AR5K_ISR_QCBRURN))) *interrupt_mask |= AR5K_INT_QCBRURN; - ah->ah_txq_isr_qcburn |= AR5K_REG_MS(sisr3, - AR5K_SISR3_QCBRURN); - } /* A queue got triggered */ - if (unlikely(pisr & (AR5K_ISR_QTRIG))) { + if (unlikely(pisr & (AR5K_ISR_QTRIG))) *interrupt_mask |= AR5K_INT_QTRIG; - ah->ah_txq_isr_qtrig |= AR5K_REG_MS(sisr4, - AR5K_SISR4_QTRIG); - } data = pisr; } diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c index 1fbc2c19848f..d444b3d70ba2 100644 --- a/drivers/net/wireless/ath/ath5k/eeprom.c +++ b/drivers/net/wireless/ath/ath5k/eeprom.c @@ -746,6 +746,9 @@ ath5k_eeprom_convert_pcal_info_5111(struct ath5k_hw *ah, int mode, } } + if (idx == AR5K_EEPROM_N_PD_CURVES) + goto err_out; + ee->ee_pd_gains[mode] = 1; pd = &chinfo[pier].pd_curves[idx]; diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index b22ed499f7ba..a56fab6232a9 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -839,7 +839,7 @@ static void ath6kl_deliver_frames_to_nw_stack(struct net_device *dev, skb->protocol = eth_type_trans(skb, skb->dev); - netif_rx_ni(skb); + netif_rx(skb); } static void ath6kl_alloc_netbufs(struct sk_buff_head *q, u16 num) diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index aba70f35e574..65e683effdcb 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -1217,6 +1217,7 @@ static int ath6kl_usb_pm_resume(struct usb_interface *interface) static const struct usb_device_id ath6kl_usb_ids[] = { {USB_DEVICE(0x0cf3, 0x9375)}, {USB_DEVICE(0x0cf3, 0x9374)}, + {USB_DEVICE(0x04da, 0x390d)}, { /* Terminating entry */ }, }; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index bd1ef6334997..3787b9fb0075 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1750,7 +1750,6 @@ static int ath6kl_wmi_snr_threshold_event_rx(struct wmi *wmi, u8 *datap, static int ath6kl_wmi_aplist_event_rx(struct wmi *wmi, u8 *datap, int len) { - u16 ap_info_entry_size; struct wmi_aplist_event *ev = (struct wmi_aplist_event *) datap; struct wmi_ap_info_v1 *ap_info_v1; u8 index; @@ -1759,14 +1758,12 @@ static int ath6kl_wmi_aplist_event_rx(struct wmi *wmi, u8 *datap, int len) ev->ap_list_ver != APLIST_VER1) return -EINVAL; - ap_info_entry_size = sizeof(struct wmi_ap_info_v1); ap_info_v1 = (struct wmi_ap_info_v1 *) ev->ap_list; ath6kl_dbg(ATH6KL_DBG_WMI, "number of APs in aplist event: %d\n", ev->num_ap); - if (len < (int) (sizeof(struct wmi_aplist_event) + - (ev->num_ap - 1) * ap_info_entry_size)) + if (len < struct_size(ev, ap_list, ev->num_ap)) return -EINVAL; /* AP list version 1 contents */ @@ -1959,21 +1956,15 @@ static int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, { struct sk_buff *skb; struct wmi_start_scan_cmd *sc; - s8 size; int i, ret; - size = sizeof(struct wmi_start_scan_cmd); - if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN)) return -EINVAL; if (num_chan > WMI_MAX_CHANNELS) return -EINVAL; - if (num_chan) - size += sizeof(u16) * (num_chan - 1); - - skb = ath6kl_wmi_get_new_buf(size); + skb = ath6kl_wmi_get_new_buf(struct_size(sc, ch_list, num_chan)); if (!skb) return -ENOMEM; @@ -2008,7 +1999,7 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, struct ieee80211_supported_band *sband; struct sk_buff *skb; struct wmi_begin_scan_cmd *sc; - s8 size, *supp_rates; + s8 *supp_rates; int i, band, ret; struct ath6kl *ar = wmi->parent_dev; int num_rates; @@ -2023,18 +2014,13 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, num_chan, ch_list); } - size = sizeof(struct wmi_begin_scan_cmd); - if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN)) return -EINVAL; if (num_chan > WMI_MAX_CHANNELS) return -EINVAL; - if (num_chan) - size += sizeof(u16) * (num_chan - 1); - - skb = ath6kl_wmi_get_new_buf(size); + skb = ath6kl_wmi_get_new_buf(struct_size(sc, ch_list, num_chan)); if (!skb) return -ENOMEM; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 784940ba4c90..672014973cee 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -863,7 +863,7 @@ struct wmi_begin_scan_cmd { u8 num_ch; /* channels in Mhz */ - __le16 ch_list[1]; + __le16 ch_list[]; } __packed; /* wmi_start_scan_cmd is to be deprecated. Use @@ -889,7 +889,7 @@ struct wmi_start_scan_cmd { u8 num_ch; /* channels in Mhz */ - __le16 ch_list[1]; + __le16 ch_list[]; } __packed; /* @@ -1373,7 +1373,7 @@ struct wmi_channel_list_reply { u8 num_ch; /* channel in Mhz */ - __le16 ch_list[1]; + __le16 ch_list[]; } __packed; /* List of Events (target to host) */ @@ -1545,7 +1545,7 @@ struct wmi_connect_event { u8 beacon_ie_len; u8 assoc_req_len; u8 assoc_resp_len; - u8 assoc_info[1]; + u8 assoc_info[]; } __packed; /* Disconnect Event */ @@ -1596,7 +1596,7 @@ struct wmi_disconnect_event { u8 disconn_reason; u8 assoc_resp_len; - u8 assoc_info[1]; + u8 assoc_info[]; } __packed; /* @@ -1637,7 +1637,7 @@ struct bss_bias { struct bss_bias_info { u8 num_bss; - struct bss_bias bss_bias[0]; + struct bss_bias bss_bias[]; } __packed; struct low_rssi_scan_params { @@ -1720,7 +1720,7 @@ struct wmi_neighbor_info { struct wmi_neighbor_report_event { u8 num_neighbors; - struct wmi_neighbor_info neighbor[0]; + struct wmi_neighbor_info neighbor[]; } __packed; /* TKIP MIC Error Event */ @@ -1957,7 +1957,7 @@ union wmi_ap_info { struct wmi_aplist_event { u8 ap_list_ver; u8 num_ap; - union wmi_ap_info ap_list[1]; + union wmi_ap_info ap_list[]; } __packed; /* Developer Commands */ @@ -2051,7 +2051,7 @@ struct wmi_get_keepalive_cmd { struct wmi_set_appie_cmd { u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */ u8 ie_len; - u8 ie_info[0]; + u8 ie_info[]; } __packed; struct wmi_set_ie_cmd { @@ -2059,7 +2059,7 @@ struct wmi_set_ie_cmd { u8 ie_field; /* enum wmi_ie_field_type */ u8 ie_len; u8 reserved; - u8 ie_info[0]; + u8 ie_info[]; } __packed; /* Notify the WSC registration status to the target */ @@ -2127,7 +2127,7 @@ struct wmi_add_wow_pattern_cmd { u8 filter_list_id; u8 filter_size; u8 filter_offset; - u8 filter[0]; + u8 filter[]; } __packed; struct wmi_del_wow_pattern_cmd { @@ -2360,7 +2360,7 @@ struct wmi_send_action_cmd { __le32 freq; __le32 wait; __le16 len; - u8 data[0]; + u8 data[]; } __packed; struct wmi_send_mgmt_cmd { @@ -2369,7 +2369,7 @@ struct wmi_send_mgmt_cmd { __le32 wait; __le32 no_cck; __le16 len; - u8 data[0]; + u8 data[]; } __packed; struct wmi_tx_status_event { @@ -2389,7 +2389,7 @@ struct wmi_set_appie_extended_cmd { u8 role_id; u8 mgmt_frm_type; u8 ie_len; - u8 ie_info[0]; + u8 ie_info[]; } __packed; struct wmi_remain_on_chnl_event { @@ -2406,18 +2406,18 @@ struct wmi_cancel_remain_on_chnl_event { struct wmi_rx_action_event { __le32 freq; __le16 len; - u8 data[0]; + u8 data[]; } __packed; struct wmi_p2p_capabilities_event { __le16 len; - u8 data[0]; + u8 data[]; } __packed; struct wmi_p2p_rx_probe_req_event { __le32 freq; __le16 len; - u8 data[0]; + u8 data[]; } __packed; #define P2P_FLAG_CAPABILITIES_REQ (0x00000001) @@ -2431,7 +2431,7 @@ struct wmi_get_p2p_info { struct wmi_p2p_info_event { __le32 info_req_flags; __le16 len; - u8 data[0]; + u8 data[]; } __packed; struct wmi_p2p_capabilities { @@ -2450,7 +2450,7 @@ struct wmi_p2p_probe_response_cmd { __le32 freq; u8 destination_addr[ETH_ALEN]; __le16 len; - u8 data[0]; + u8 data[]; } __packed; /* Extended WMI (WMIX) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index ef6f5ea06c1f..3ccf8cfc6b63 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -1071,8 +1071,9 @@ struct ath_softc { #endif #ifdef CONFIG_ATH9K_HWRNG + struct hwrng rng_ops; u32 rng_last; - struct task_struct *rng_task; + char rng_name[sizeof("ath9k_65535")]; #endif }; diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index e6b3cd49ea18..efb7889142d4 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -670,8 +670,6 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, int ath9k_hw_eeprom_init(struct ath_hw *ah) { - int status; - if (AR_SREV_9300_20_OR_LATER(ah)) ah->eep_ops = &eep_ar9300_ops; else if (AR_SREV_9287(ah)) { @@ -685,7 +683,5 @@ int ath9k_hw_eeprom_init(struct ath_hw *ah) if (!ah->eep_ops->fill_eeprom(ah)) return -EIO; - status = ah->eep_ops->check_eeprom(ah); - - return status; + return ah->eep_ops->check_eeprom(ah); } diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index 510e61e97dbc..994ec48b2f66 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -30,6 +30,7 @@ static int htc_issue_send(struct htc_target *target, struct sk_buff* skb, hdr->endpoint_id = epid; hdr->flags = flags; hdr->payload_len = cpu_to_be16(len); + memset(hdr->control, 0, sizeof(hdr->control)); status = target->hif->send(target->hif_dev, endpoint->ul_pipeid, skb); @@ -272,6 +273,10 @@ int htc_connect_service(struct htc_target *target, conn_msg->dl_pipeid = endpoint->dl_pipeid; conn_msg->ul_pipeid = endpoint->ul_pipeid; + /* To prevent infoleak */ + conn_msg->svc_meta_len = 0; + conn_msg->pad = 0; + ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0); if (ret) goto err; diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 39d46c203f6b..039bf0c35fbe 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -43,7 +43,7 @@ static bool ath_mci_add_profile(struct ath_common *common, struct ath_mci_profile_info *info) { struct ath_mci_profile_info *entry; - u8 voice_priority[] = { 110, 110, 110, 112, 110, 110, 114, 116, 118 }; + static const u8 voice_priority[] = { 110, 110, 110, 112, 110, 110, 114, 116, 118 }; if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) && (info->type == MCI_GPM_COEX_PROFILE_VOICE)) diff --git a/drivers/net/wireless/ath/ath9k/rng.c b/drivers/net/wireless/ath/ath9k/rng.c index f9d3d6eedd3c..cb5414265a9b 100644 --- a/drivers/net/wireless/ath/ath9k/rng.c +++ b/drivers/net/wireless/ath/ath9k/rng.c @@ -21,11 +21,6 @@ #include "hw.h" #include "ar9003_phy.h" -#define ATH9K_RNG_BUF_SIZE 320 -#define ATH9K_RNG_ENTROPY(x) (((x) * 8 * 10) >> 5) /* quality: 10/32 */ - -static DECLARE_WAIT_QUEUE_HEAD(rng_queue); - static int ath9k_rng_data_read(struct ath_softc *sc, u32 *buf, u32 buf_size) { int i, j; @@ -71,61 +66,56 @@ static u32 ath9k_rng_delay_get(u32 fail_stats) return delay; } -static int ath9k_rng_kthread(void *data) +static int ath9k_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) { - int bytes_read; - struct ath_softc *sc = data; - u32 *rng_buf; - u32 delay, fail_stats = 0; + struct ath_softc *sc = container_of(rng, struct ath_softc, rng_ops); + u32 fail_stats = 0, word; + int bytes_read = 0; - rng_buf = kmalloc_array(ATH9K_RNG_BUF_SIZE, sizeof(u32), GFP_KERNEL); - if (!rng_buf) - goto out; - - while (!kthread_should_stop()) { - bytes_read = ath9k_rng_data_read(sc, rng_buf, - ATH9K_RNG_BUF_SIZE); - if (unlikely(!bytes_read)) { - delay = ath9k_rng_delay_get(++fail_stats); - wait_event_interruptible_timeout(rng_queue, - kthread_should_stop(), - msecs_to_jiffies(delay)); - continue; + for (;;) { + if (max & ~3UL) + bytes_read = ath9k_rng_data_read(sc, buf, max >> 2); + if ((max & 3UL) && ath9k_rng_data_read(sc, &word, 1)) { + memcpy(buf + bytes_read, &word, max & 3UL); + bytes_read += max & 3UL; + memzero_explicit(&word, sizeof(word)); } + if (!wait || !max || likely(bytes_read) || fail_stats > 110) + break; - fail_stats = 0; - - /* sleep until entropy bits under write_wakeup_threshold */ - add_hwgenerator_randomness((void *)rng_buf, bytes_read, - ATH9K_RNG_ENTROPY(bytes_read)); + msleep_interruptible(ath9k_rng_delay_get(++fail_stats)); } - kfree(rng_buf); -out: - sc->rng_task = NULL; - - return 0; + if (wait && !bytes_read && max) + bytes_read = -EIO; + return bytes_read; } void ath9k_rng_start(struct ath_softc *sc) { + static atomic_t serial = ATOMIC_INIT(0); struct ath_hw *ah = sc->sc_ah; - if (sc->rng_task) + if (sc->rng_ops.read) return; if (!AR_SREV_9300_20_OR_LATER(ah)) return; - sc->rng_task = kthread_run(ath9k_rng_kthread, sc, "ath9k-hwrng"); - if (IS_ERR(sc->rng_task)) - sc->rng_task = NULL; + snprintf(sc->rng_name, sizeof(sc->rng_name), "ath9k_%u", + (atomic_inc_return(&serial) - 1) & U16_MAX); + sc->rng_ops.name = sc->rng_name; + sc->rng_ops.read = ath9k_rng_read; + sc->rng_ops.quality = 320; + + if (devm_hwrng_register(sc->dev, &sc->rng_ops)) + sc->rng_ops.read = NULL; } void ath9k_rng_stop(struct ath_softc *sc) { - if (sc->rng_task) { - kthread_stop(sc->rng_task); - sc->rng_task = NULL; + if (sc->rng_ops.read) { + devm_hwrng_unregister(sc->dev, &sc->rng_ops); + sc->rng_ops.read = NULL; } } diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index 84a8ce0784b1..ba29b4aebe9f 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -458,7 +458,6 @@ struct ar9170 { # define CARL9170_HWRNG_CACHE_SIZE CARL9170_MAX_CMD_PAYLOAD_LEN struct { struct hwrng rng; - bool initialized; char name[30 + 1]; u16 cache[CARL9170_HWRNG_CACHE_SIZE / sizeof(u16)]; unsigned int cache_idx; diff --git a/drivers/net/wireless/ath/carl9170/fwdesc.h b/drivers/net/wireless/ath/carl9170/fwdesc.h index 503b21abbba5..10acb6ad30d0 100644 --- a/drivers/net/wireless/ath/carl9170/fwdesc.h +++ b/drivers/net/wireless/ath/carl9170/fwdesc.h @@ -149,7 +149,7 @@ struct carl9170fw_fix_entry { struct carl9170fw_fix_desc { struct carl9170fw_desc_head head; - struct carl9170fw_fix_entry data[0]; + struct carl9170fw_fix_entry data[]; } __packed; #define CARL9170FW_FIX_DESC_SIZE \ (sizeof(struct carl9170fw_fix_desc)) diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 49f7ee1c912b..76e84adf57c1 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1412,7 +1412,7 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, return -EOPNOTSUPP; tid_info = kzalloc(sizeof(struct carl9170_sta_tid), - GFP_ATOMIC); + GFP_KERNEL); if (!tid_info) return -ENOMEM; @@ -1494,7 +1494,7 @@ static int carl9170_register_wps_button(struct ar9170 *ar) if (!(ar->features & CARL9170_WPS_BUTTON)) return 0; - input = input_allocate_device(); + input = devm_input_allocate_device(&ar->udev->dev); if (!input) return -ENOMEM; @@ -1512,10 +1512,8 @@ static int carl9170_register_wps_button(struct ar9170 *ar) input_set_capability(input, EV_KEY, KEY_WPS_BUTTON); err = input_register_device(input); - if (err) { - input_free_device(input); + if (err) return err; - } ar->wps.pbc = input; return 0; @@ -1539,7 +1537,7 @@ static int carl9170_rng_get(struct ar9170 *ar) BUILD_BUG_ON(RB > CARL9170_MAX_CMD_PAYLOAD_LEN); - if (!IS_ACCEPTING_CMD(ar) || !ar->rng.initialized) + if (!IS_ACCEPTING_CMD(ar)) return -EAGAIN; count = ARRAY_SIZE(ar->rng.cache); @@ -1585,14 +1583,6 @@ static int carl9170_rng_read(struct hwrng *rng, u32 *data) return sizeof(u16); } -static void carl9170_unregister_hwrng(struct ar9170 *ar) -{ - if (ar->rng.initialized) { - hwrng_unregister(&ar->rng.rng); - ar->rng.initialized = false; - } -} - static int carl9170_register_hwrng(struct ar9170 *ar) { int err; @@ -1603,25 +1593,14 @@ static int carl9170_register_hwrng(struct ar9170 *ar) ar->rng.rng.data_read = carl9170_rng_read; ar->rng.rng.priv = (unsigned long)ar; - if (WARN_ON(ar->rng.initialized)) - return -EALREADY; - - err = hwrng_register(&ar->rng.rng); + err = devm_hwrng_register(&ar->udev->dev, &ar->rng.rng); if (err) { dev_err(&ar->udev->dev, "Failed to register the random " "number generator (%d)\n", err); return err; } - ar->rng.initialized = true; - - err = carl9170_rng_get(ar); - if (err) { - carl9170_unregister_hwrng(ar); - return err; - } - - return 0; + return carl9170_rng_get(ar); } #endif /* CONFIG_CARL9170_HWRNG */ @@ -1914,7 +1893,7 @@ static int carl9170_parse_eeprom(struct ar9170 *ar) WARN_ON(!(tx_streams >= 1 && tx_streams <= IEEE80211_HT_MCS_TX_MAX_STREAMS)); - tx_params = (tx_streams - 1) << + tx_params |= (tx_streams - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; carl9170_band_2GHz.ht_cap.mcs.tx_params |= tx_params; @@ -1937,7 +1916,8 @@ static int carl9170_parse_eeprom(struct ar9170 *ar) if (!bands) return -EINVAL; - ar->survey = kcalloc(chans, sizeof(struct survey_info), GFP_KERNEL); + ar->survey = devm_kcalloc(&ar->udev->dev, chans, + sizeof(struct survey_info), GFP_KERNEL); if (!ar->survey) return -ENOMEM; ar->num_channels = chans; @@ -1964,11 +1944,7 @@ int carl9170_register(struct ar9170 *ar) struct ath_regulatory *regulatory = &ar->common.regulatory; int err = 0, i; - if (WARN_ON(ar->mem_bitmap)) - return -EINVAL; - - ar->mem_bitmap = bitmap_zalloc(ar->fw.mem_blocks, GFP_KERNEL); - + ar->mem_bitmap = devm_bitmap_zalloc(&ar->udev->dev, ar->fw.mem_blocks, GFP_KERNEL); if (!ar->mem_bitmap) return -ENOMEM; @@ -2057,17 +2033,6 @@ void carl9170_unregister(struct ar9170 *ar) carl9170_debugfs_unregister(ar); #endif /* CONFIG_CARL9170_DEBUGFS */ -#ifdef CONFIG_CARL9170_WPC - if (ar->wps.pbc) { - input_unregister_device(ar->wps.pbc); - ar->wps.pbc = NULL; - } -#endif /* CONFIG_CARL9170_WPC */ - -#ifdef CONFIG_CARL9170_HWRNG - carl9170_unregister_hwrng(ar); -#endif /* CONFIG_CARL9170_HWRNG */ - carl9170_cancel_worker(ar); cancel_work_sync(&ar->restart_work); @@ -2082,12 +2047,6 @@ void carl9170_free(struct ar9170 *ar) kfree_skb(ar->rx_failover); ar->rx_failover = NULL; - bitmap_free(ar->mem_bitmap); - ar->mem_bitmap = NULL; - - kfree(ar->survey); - ar->survey = NULL; - mutex_destroy(&ar->mutex); ieee80211_free_hw(ar->hw); diff --git a/drivers/net/wireless/ath/carl9170/wlan.h b/drivers/net/wireless/ath/carl9170/wlan.h index bb73553fd7c2..0a4e42e806b9 100644 --- a/drivers/net/wireless/ath/carl9170/wlan.h +++ b/drivers/net/wireless/ath/carl9170/wlan.h @@ -327,7 +327,7 @@ struct _carl9170_tx_superdesc { struct _carl9170_tx_superframe { struct _carl9170_tx_superdesc s; struct _ar9170_tx_hwdesc f; - u8 frame_data[0]; + u8 frame_data[]; } __packed __aligned(4); #define CARL9170_TX_SUPERDESC_LEN 24 diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index 75cb53a3ec15..27f4d74a41c8 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -197,7 +197,7 @@ static void channel_detector_exit(struct dfs_pattern_detector *dpd, static struct channel_detector * channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) { - u32 sz, i; + u32 i; struct channel_detector *cd; cd = kmalloc(sizeof(*cd), GFP_ATOMIC); @@ -206,8 +206,8 @@ channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) INIT_LIST_HEAD(&cd->head); cd->freq = freq; - sz = sizeof(cd->detectors) * dpd->num_radar_types; - cd->detectors = kzalloc(sz, GFP_ATOMIC); + cd->detectors = kmalloc_array(dpd->num_radar_types, + sizeof(*cd->detectors), GFP_ATOMIC); if (cd->detectors == NULL) goto fail; diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index b2400e2417a5..f15e7bd690b5 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -667,14 +667,14 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, /* * Some users have reported their EEPROM programmed with - * 0x8000 or 0x0 set, this is not a supported regulatory - * domain but since we have more than one user with it we - * need a solution for them. We default to 0x64, which is - * the default Atheros world regulatory domain. + * 0x8000 set, this is not a supported regulatory domain + * but since we have more than one user with it we need + * a solution for them. We default to 0x64, which is the + * default Atheros world regulatory domain. */ static void ath_regd_sanitize(struct ath_regulatory *reg) { - if (reg->current_rd != COUNTRY_ERD_FLAG && reg->current_rd != 0) + if (reg->current_rd != COUNTRY_ERD_FLAG) return; printk(KERN_DEBUG "ath: EEPROM regdomain sanitized\n"); reg->current_rd = 0x64; diff --git a/drivers/net/wireless/ath/spectral_common.h b/drivers/net/wireless/ath/spectral_common.h index e14f374f97d4..fe187c1fbeb0 100644 --- a/drivers/net/wireless/ath/spectral_common.h +++ b/drivers/net/wireless/ath/spectral_common.h @@ -108,7 +108,7 @@ struct fft_sample_ath10k { u8 avgpwr_db; u8 max_exp; - u8 data[0]; + u8 data[]; } __packed; struct fft_sample_ath11k { @@ -123,7 +123,7 @@ struct fft_sample_ath11k { __be32 tsf; __be32 noise; - u8 data[0]; + u8 data[]; } __packed; #endif /* SPECTRAL_COMMON_H */ diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 9575d7373bf2..95ea7d040d8c 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -331,6 +331,7 @@ static int wcn36xx_start(struct ieee80211_hw *hw) INIT_LIST_HEAD(&wcn->vif_list); spin_lock_init(&wcn->dxe_lock); + spin_lock_init(&wcn->survey_lock); return 0; @@ -392,11 +393,41 @@ static void wcn36xx_change_opchannel(struct wcn36xx *wcn, int ch) { struct ieee80211_vif *vif = NULL; struct wcn36xx_vif *tmp; + struct ieee80211_supported_band *band; + struct ieee80211_channel *channel = NULL; + unsigned long flags; + int i, j; + + for (i = 0; i < ARRAY_SIZE(wcn->hw->wiphy->bands); i++) { + band = wcn->hw->wiphy->bands[i]; + if (!band) + break; + for (j = 0; j < band->n_channels; j++) { + if (HW_VALUE_CHANNEL(band->channels[j].hw_value) == ch) { + channel = &band->channels[j]; + break; + } + } + if (channel) + break; + } + + if (!channel) { + wcn36xx_err("Cannot tune to channel %d\n", ch); + return; + } + + spin_lock_irqsave(&wcn->survey_lock, flags); + wcn->band = band; + wcn->channel = channel; + spin_unlock_irqrestore(&wcn->survey_lock, flags); list_for_each_entry(tmp, &wcn->vif_list, list) { vif = wcn36xx_priv_to_vif(tmp); wcn36xx_smd_switch_channel(wcn, vif, ch); } + + return; } static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) @@ -1326,6 +1357,49 @@ static void wcn36xx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } } +static int wcn36xx_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct wcn36xx *wcn = hw->priv; + struct ieee80211_supported_band *sband; + struct wcn36xx_chan_survey *chan_survey; + int band_idx; + unsigned long flags; + + sband = wcn->hw->wiphy->bands[NL80211_BAND_2GHZ]; + band_idx = idx; + if (band_idx >= sband->n_channels) { + band_idx -= sband->n_channels; + sband = wcn->hw->wiphy->bands[NL80211_BAND_5GHZ]; + } + + if (!sband || band_idx >= sband->n_channels) + return -ENOENT; + + spin_lock_irqsave(&wcn->survey_lock, flags); + + chan_survey = &wcn->chan_survey[idx]; + survey->channel = &sband->channels[band_idx]; + survey->noise = chan_survey->rssi - chan_survey->snr; + survey->filled = 0; + + if (chan_survey->rssi > -100 && chan_survey->rssi < 0) + survey->filled |= SURVEY_INFO_NOISE_DBM; + + if (survey->channel == wcn->channel) + survey->filled |= SURVEY_INFO_IN_USE; + + spin_unlock_irqrestore(&wcn->survey_lock, flags); + + wcn36xx_dbg(WCN36XX_DBG_MAC, + "ch %d rssi %d snr %d noise %d filled %x freq %d\n", + HW_VALUE_CHANNEL(survey->channel->hw_value), + chan_survey->rssi, chan_survey->snr, survey->noise, + survey->filled, survey->channel->center_freq); + + return 0; +} + static const struct ieee80211_ops wcn36xx_ops = { .start = wcn36xx_start, .stop = wcn36xx_stop, @@ -1354,6 +1428,7 @@ static const struct ieee80211_ops wcn36xx_ops = { .ipv6_addr_change = wcn36xx_ipv6_addr_change, #endif .flush = wcn36xx_flush, + .get_survey = wcn36xx_get_survey, CFG80211_TESTMODE_CMD(wcn36xx_tm_cmd) }; @@ -1446,25 +1521,20 @@ static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, { struct device_node *mmio_node; struct device_node *iris_node; - struct resource *res; int index; int ret; /* Set TX IRQ */ - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tx"); - if (!res) { - wcn36xx_err("failed to get tx_irq\n"); - return -ENOENT; - } - wcn->tx_irq = res->start; + ret = platform_get_irq_byname(pdev, "tx"); + if (ret < 0) + return ret; + wcn->tx_irq = ret; /* Set RX IRQ */ - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "rx"); - if (!res) { - wcn36xx_err("failed to get rx_irq\n"); - return -ENOENT; - } - wcn->rx_irq = res->start; + ret = platform_get_irq_byname(pdev, "rx"); + if (ret < 0) + return ret; + wcn->rx_irq = ret; /* Acquire SMSM tx enable handle */ wcn->tx_enable_state = qcom_smem_state_get(&pdev->dev, @@ -1513,6 +1583,9 @@ static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, if (iris_node) { if (of_device_is_compatible(iris_node, "qcom,wcn3620")) wcn->rf_id = RF_IRIS_WCN3620; + if (of_device_is_compatible(iris_node, "qcom,wcn3660") || + of_device_is_compatible(iris_node, "qcom,wcn3660b")) + wcn->rf_id = RF_IRIS_WCN3660; if (of_device_is_compatible(iris_node, "qcom,wcn3680")) wcn->rf_id = RF_IRIS_WCN3680; of_node_put(iris_node); @@ -1535,6 +1608,7 @@ static int wcn36xx_probe(struct platform_device *pdev) void *wcnss; int ret; const u8 *addr; + int n_channels; wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n"); @@ -1562,6 +1636,13 @@ static int wcn36xx_probe(struct platform_device *pdev) goto out_wq; } + n_channels = wcn_band_2ghz.n_channels + wcn_band_5ghz.n_channels; + wcn->chan_survey = devm_kmalloc(wcn->dev, n_channels, GFP_KERNEL); + if (!wcn->chan_survey) { + ret = -ENOMEM; + goto out_wq; + } + ret = dma_set_mask_and_coherent(wcn->dev, DMA_BIT_MASK(32)); if (ret < 0) { wcn36xx_err("failed to set DMA mask: %d\n", ret); diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index caeb68901326..59ad332156ae 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -3347,7 +3347,7 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev, case WCN36XX_HAL_DELETE_STA_CONTEXT_IND: case WCN36XX_HAL_PRINT_REG_INFO_IND: case WCN36XX_HAL_SCAN_OFFLOAD_IND: - msg_ind = kmalloc(sizeof(*msg_ind) + len, GFP_ATOMIC); + msg_ind = kmalloc(struct_size(msg_ind, msg, len), GFP_ATOMIC); if (!msg_ind) { wcn36xx_err("Run out of memory while handling SMD_EVENT (%d)\n", msg_header->msg_type); diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c index dd58dde8c836..df749b114568 100644 --- a/drivers/net/wireless/ath/wcn36xx/txrx.c +++ b/drivers/net/wireless/ath/wcn36xx/txrx.c @@ -23,6 +23,11 @@ static inline int get_rssi0(struct wcn36xx_rx_bd *bd) return 100 - ((bd->phy_stat0 >> 24) & 0xff); } +static inline int get_snr(struct wcn36xx_rx_bd *bd) +{ + return ((bd->phy_stat1 >> 24) & 0xff); +} + struct wcn36xx_rate { u16 bitrate; u16 mcs_or_legacy_index; @@ -266,6 +271,34 @@ static void __skb_queue_purge_irq(struct sk_buff_head *list) dev_kfree_skb_irq(skb); } +static void wcn36xx_update_survey(struct wcn36xx *wcn, int rssi, int snr, + int band, int freq) +{ + static struct ieee80211_channel *channel; + struct ieee80211_supported_band *sband; + int idx; + int i; + + idx = 0; + if (band == NL80211_BAND_5GHZ) + idx = wcn->hw->wiphy->bands[NL80211_BAND_2GHZ]->n_channels; + + sband = wcn->hw->wiphy->bands[band]; + channel = sband->channels; + + for (i = 0; i < sband->n_channels; i++, channel++) { + if (channel->center_freq == freq) { + idx += i; + break; + } + } + + spin_lock(&wcn->survey_lock); + wcn->chan_survey[idx].rssi = rssi; + wcn->chan_survey[idx].snr = snr; + spin_unlock(&wcn->survey_lock); +} + int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) { struct ieee80211_rx_status status; @@ -343,6 +376,9 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) status.freq = WCN36XX_CENTER_FREQ(wcn); } + wcn36xx_update_survey(wcn, status.signal, get_snr(bd), + status.band, status.freq); + if (bd->rate_id < ARRAY_SIZE(wcn36xx_rate_table)) { rate = &wcn36xx_rate_table[bd->rate_id]; status.encoding = rate->encoding; diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index fbd0558c2c19..9aa08b636d08 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -97,6 +97,7 @@ enum wcn36xx_ampdu_state { #define RF_UNKNOWN 0x0000 #define RF_IRIS_WCN3620 0x3620 +#define RF_IRIS_WCN3660 0x3660 #define RF_IRIS_WCN3680 0x3680 static inline void buff_to_be(u32 *buf, size_t len) @@ -194,7 +195,14 @@ struct wcn36xx_sta { enum wcn36xx_ampdu_state ampdu_state[16]; int non_agg_frame_ct; }; + struct wcn36xx_dxe_ch; + +struct wcn36xx_chan_survey { + s8 rssi; + u8 snr; +}; + struct wcn36xx { struct ieee80211_hw *hw; struct device *dev; @@ -281,6 +289,12 @@ struct wcn36xx { /* Debug file system entry */ struct wcn36xx_dfs_entry dfs; #endif /* CONFIG_WCN36XX_DEBUGFS */ + + struct ieee80211_supported_band *band; + struct ieee80211_channel *channel; + + spinlock_t survey_lock; /* protects chan_survey */ + struct wcn36xx_chan_survey *chan_survey; }; static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn, diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index cc830c795b33..5704defd7be1 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -958,7 +958,7 @@ void wil_netif_rx(struct sk_buff *skb, struct net_device *ndev, int cid, if (gro) napi_gro_receive(&wil->napi_rx, skb); else - netif_rx_ni(skb); + netif_rx(skb); } ndev->stats.rx_packets++; stats->rx_packets++; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index dd8abbb28849..98b4c189eecc 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1199,7 +1199,7 @@ static void wmi_evt_eapol_rx(struct wil6210_vif *vif, int id, void *d, int len) eth->h_proto = cpu_to_be16(ETH_P_PAE); skb_put_data(skb, evt->eapol, eapol_len); skb->protocol = eth_type_trans(skb, ndev); - if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { + if (likely(netif_rx(skb) == NET_RX_SUCCESS)) { ndev->stats.rx_packets++; ndev->stats.rx_bytes += sz; if (stats) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index 3984fd7d918e..2c95a08a5871 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -397,9 +397,9 @@ brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, } static void brcmf_proto_bcdc_rxreorder(struct brcmf_if *ifp, - struct sk_buff *skb, bool inirq) + struct sk_buff *skb) { - brcmf_fws_rxreorder(ifp, skb, inirq); + brcmf_fws_rxreorder(ifp, skb); } static void diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index ba52318615ae..f0ad1e23f3c8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "core.h" #include "debug.h" #include "tracepoint.h" @@ -4622,7 +4623,7 @@ s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif) { - s32 pktflags[] = { + static const s32 pktflags[] = { BRCMF_VNDR_IE_PRBREQ_FLAG, BRCMF_VNDR_IE_PRBRSP_FLAG, BRCMF_VNDR_IE_BEACON_FLAG @@ -7476,6 +7477,16 @@ int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg, vif_event_equals(event, action), timeout); } +static bool brmcf_use_iso3166_ccode_fallback(struct brcmf_pub *drvr) +{ + switch (drvr->bus_if->chip) { + case BRCM_CC_4345_CHIP_ID: + return true; + default: + return false; + } +} + static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2], struct brcmf_fil_country_le *ccreq) { @@ -7484,18 +7495,28 @@ static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2], s32 found_index; int i; - country_codes = drvr->settings->country_codes; - if (!country_codes) { - brcmf_dbg(TRACE, "No country codes configured for device\n"); - return -EINVAL; - } - if ((alpha2[0] == ccreq->country_abbrev[0]) && (alpha2[1] == ccreq->country_abbrev[1])) { brcmf_dbg(TRACE, "Country code already set\n"); return -EAGAIN; } + country_codes = drvr->settings->country_codes; + if (!country_codes) { + if (brmcf_use_iso3166_ccode_fallback(drvr)) { + brcmf_dbg(TRACE, "No country codes configured for device, using ISO3166 code and 0 rev\n"); + memset(ccreq, 0, sizeof(*ccreq)); + ccreq->country_abbrev[0] = alpha2[0]; + ccreq->country_abbrev[1] = alpha2[1]; + ccreq->ccode[0] = alpha2[0]; + ccreq->ccode[1] = alpha2[1]; + return 0; + } + + brcmf_dbg(TRACE, "No country codes configured for device\n"); + return -EINVAL; + } + found_index = -1; for (i = 0; i < country_codes->table_size; i++) { cc = &country_codes->table[i]; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index 1ee49f9e325d..4ec7773b6906 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -704,6 +704,7 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) { switch (ci->pub.chip) { case BRCM_CC_4345_CHIP_ID: + case BRCM_CC_43454_CHIP_ID: return 0x198000; case BRCM_CC_4335_CHIP_ID: case BRCM_CC_4339_CHIP_ID: @@ -1401,6 +1402,7 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) case BRCM_CC_4354_CHIP_ID: case BRCM_CC_4356_CHIP_ID: case BRCM_CC_4345_CHIP_ID: + case BRCM_CC_43454_CHIP_ID: /* explicitly check SR engine enable bit */ pmu_cc3_mask = BIT(2); fallthrough; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index fed9cd5f29a2..26fab4bee22c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -400,7 +400,7 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp, spin_unlock_irqrestore(&ifp->netif_stop_lock, flags); } -void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb, bool inirq) +void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) { /* Most of Broadcom's firmwares send 802.11f ADD frame every time a new * STA connects to the AP interface. This is an obsoleted standard most @@ -423,15 +423,7 @@ void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb, bool inirq) ifp->ndev->stats.rx_packets++; brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol)); - if (inirq) { - netif_rx(skb); - } else { - /* If the receive is not processed inside an ISR, - * the softirqd must be woken explicitly to service - * the NET_RX_SOFTIRQ. This is handled by netif_rx_ni(). - */ - netif_rx_ni(skb); - } + netif_rx(skb); } void brcmf_netif_mon_rx(struct brcmf_if *ifp, struct sk_buff *skb) @@ -480,7 +472,7 @@ void brcmf_netif_mon_rx(struct brcmf_if *ifp, struct sk_buff *skb) skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); - brcmf_netif_rx(ifp, skb, false); + brcmf_netif_rx(ifp, skb); } static int brcmf_rx_hdrpull(struct brcmf_pub *drvr, struct sk_buff *skb, @@ -515,7 +507,7 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_event, return; if (brcmf_proto_is_reorder_skb(skb)) { - brcmf_proto_rxreorder(ifp, skb, inirq); + brcmf_proto_rxreorder(ifp, skb); } else { /* Process special event packets */ if (handle_event) { @@ -524,7 +516,7 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_event, brcmf_fweh_process_skb(ifp->drvr, skb, BCMILCP_SUBTYPE_VENDOR_LONG, gfp); } - brcmf_netif_rx(ifp, skb, inirq); + brcmf_netif_rx(ifp, skb); } } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 8212c9de14f1..340346c122d3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -208,7 +208,7 @@ void brcmf_remove_interface(struct brcmf_if *ifp, bool locked); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success); -void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb, bool inirq); +void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb); void brcmf_netif_mon_rx(struct brcmf_if *ifp, struct sk_buff *skb); void brcmf_net_detach(struct net_device *ndev, bool locked); int brcmf_net_mon_attach(struct brcmf_if *ifp); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index 7c68d9849324..d2ac844e1e9f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -248,7 +248,8 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_firmware_capabilities(ifp); memset(&gscan_cfg, 0, sizeof(gscan_cfg)); if (drvr->bus_if->chip != BRCM_CC_43430_CHIP_ID && - drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID) + drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID && + drvr->bus_if->chip != BRCM_CC_43454_CHIP_ID) brcmf_feat_iovar_data_set(ifp, BRCMF_FEAT_GSCAN, "pfn_gscan_cfg", &gscan_cfg, sizeof(gscan_cfg)); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index d99140960a82..dcbe55b56e43 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -207,6 +207,8 @@ static int brcmf_init_nvram_parser(struct nvram_parser *nvp, size = BRCMF_FW_MAX_NVRAM_SIZE; else size = data_len; + /* Add space for properties we may add */ + size += strlen(BRCMF_FW_DEFAULT_BOARDREV) + 1; /* Alloc for extra 0 byte + roundup by 4 + length field */ size += 1 + 3 + sizeof(u32); nvp->nvram = kzalloc(size, GFP_KERNEL); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c index d5578ca681bb..72fe8bce6eaf 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c @@ -192,7 +192,7 @@ brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data) } static u32 -brcmf_create_iovar(char *name, const char *data, u32 datalen, +brcmf_create_iovar(const char *name, const char *data, u32 datalen, char *buf, u32 buflen) { u32 len; @@ -213,7 +213,7 @@ brcmf_create_iovar(char *name, const char *data, u32 datalen, s32 -brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, +brcmf_fil_iovar_data_set(struct brcmf_if *ifp, const char *name, const void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; @@ -241,7 +241,7 @@ brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, } s32 -brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, +brcmf_fil_iovar_data_get(struct brcmf_if *ifp, const char *name, void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; @@ -272,7 +272,7 @@ brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, } s32 -brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data) +brcmf_fil_iovar_int_set(struct brcmf_if *ifp, const char *name, u32 data) { __le32 data_le = cpu_to_le32(data); @@ -280,7 +280,7 @@ brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data) } s32 -brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data) +brcmf_fil_iovar_int_get(struct brcmf_if *ifp, const char *name, u32 *data) { __le32 data_le = cpu_to_le32(*data); s32 err; @@ -292,7 +292,7 @@ brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data) } static u32 -brcmf_create_bsscfg(s32 bsscfgidx, char *name, char *data, u32 datalen, +brcmf_create_bsscfg(s32 bsscfgidx, const char *name, char *data, u32 datalen, char *buf, u32 buflen) { const s8 *prefix = "bsscfg:"; @@ -337,7 +337,7 @@ brcmf_create_bsscfg(s32 bsscfgidx, char *name, char *data, u32 datalen, } s32 -brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, +brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, const char *name, void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; @@ -366,7 +366,7 @@ brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, } s32 -brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, +brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, const char *name, void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; @@ -396,7 +396,7 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, } s32 -brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data) +brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, const char *name, u32 data) { __le32 data_le = cpu_to_le32(data); @@ -405,7 +405,7 @@ brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data) } s32 -brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data) +brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, const char *name, u32 *data) { __le32 data_le = cpu_to_le32(*data); s32 err; @@ -417,7 +417,7 @@ brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data) return err; } -static u32 brcmf_create_xtlv(char *name, u16 id, char *data, u32 len, +static u32 brcmf_create_xtlv(const char *name, u16 id, char *data, u32 len, char *buf, u32 buflen) { u32 iolen; @@ -438,7 +438,7 @@ static u32 brcmf_create_xtlv(char *name, u16 id, char *data, u32 len, return iolen; } -s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, char *name, u16 id, +s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, const char *name, u16 id, void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; @@ -466,7 +466,7 @@ s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, char *name, u16 id, return err; } -s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, char *name, u16 id, +s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, const char *name, u16 id, void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; @@ -495,7 +495,7 @@ s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, char *name, u16 id, return err; } -s32 brcmf_fil_xtlv_int_set(struct brcmf_if *ifp, char *name, u16 id, u32 data) +s32 brcmf_fil_xtlv_int_set(struct brcmf_if *ifp, const char *name, u16 id, u32 data) { __le32 data_le = cpu_to_le32(data); @@ -503,7 +503,7 @@ s32 brcmf_fil_xtlv_int_set(struct brcmf_if *ifp, char *name, u16 id, u32 data) sizeof(data_le)); } -s32 brcmf_fil_xtlv_int_get(struct brcmf_if *ifp, char *name, u16 id, u32 *data) +s32 brcmf_fil_xtlv_int_get(struct brcmf_if *ifp, const char *name, u16 id, u32 *data) { __le32 data_le = cpu_to_le32(*data); s32 err; @@ -514,12 +514,12 @@ s32 brcmf_fil_xtlv_int_get(struct brcmf_if *ifp, char *name, u16 id, u32 *data) return err; } -s32 brcmf_fil_xtlv_int8_get(struct brcmf_if *ifp, char *name, u16 id, u8 *data) +s32 brcmf_fil_xtlv_int8_get(struct brcmf_if *ifp, const char *name, u16 id, u8 *data) { return brcmf_fil_xtlv_data_get(ifp, name, id, data, sizeof(*data)); } -s32 brcmf_fil_xtlv_int16_get(struct brcmf_if *ifp, char *name, u16 id, u16 *data) +s32 brcmf_fil_xtlv_int16_get(struct brcmf_if *ifp, const char *name, u16 id, u16 *data) { __le16 data_le = cpu_to_le16(*data); s32 err; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h index cb26f8c59c21..bc693157c4b1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h @@ -84,26 +84,26 @@ s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data); s32 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data); -s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, +s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, const char *name, const void *data, u32 len); -s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, +s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, const char *name, void *data, u32 len); -s32 brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data); -s32 brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data); +s32 brcmf_fil_iovar_int_set(struct brcmf_if *ifp, const char *name, u32 data); +s32 brcmf_fil_iovar_int_get(struct brcmf_if *ifp, const char *name, u32 *data); -s32 brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, void *data, +s32 brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, const char *name, void *data, u32 len); -s32 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, void *data, +s32 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, const char *name, void *data, u32 len); -s32 brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data); -s32 brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data); -s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, char *name, u16 id, +s32 brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, const char *name, u32 data); +s32 brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, const char *name, u32 *data); +s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, const char *name, u16 id, void *data, u32 len); -s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, char *name, u16 id, +s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, const char *name, u16 id, void *data, u32 len); -s32 brcmf_fil_xtlv_int_set(struct brcmf_if *ifp, char *name, u16 id, u32 data); -s32 brcmf_fil_xtlv_int_get(struct brcmf_if *ifp, char *name, u16 id, u32 *data); -s32 brcmf_fil_xtlv_int8_get(struct brcmf_if *ifp, char *name, u16 id, u8 *data); -s32 brcmf_fil_xtlv_int16_get(struct brcmf_if *ifp, char *name, u16 id, u16 *data); +s32 brcmf_fil_xtlv_int_set(struct brcmf_if *ifp, const char *name, u16 id, u32 data); +s32 brcmf_fil_xtlv_int_get(struct brcmf_if *ifp, const char *name, u16 id, u32 *data); +s32 brcmf_fil_xtlv_int8_get(struct brcmf_if *ifp, const char *name, u16 id, u8 *data); +s32 brcmf_fil_xtlv_int16_get(struct brcmf_if *ifp, const char *name, u16 id, u16 *data); #endif /* _fwil_h_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index e69d1e56996f..c87b829adb0d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -1068,7 +1068,7 @@ struct brcmf_mkeep_alive_pkt_le { __le32 period_msec; __le16 len_bytes; u8 keep_alive_id; - u8 data[0]; + u8 data[]; } __packed; #endif /* FWIL_TYPES_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c index 19b0f318f93e..d58525ebe618 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -1664,7 +1664,7 @@ static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi, rfi->pend_pkts -= skb_queue_len(skb_list); } -void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt, bool inirq) +void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt) { struct brcmf_pub *drvr = ifp->drvr; u8 *reorder_data; @@ -1682,7 +1682,7 @@ void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt, bool inirq) /* validate flags and flow id */ if (flags == 0xFF) { bphy_err(drvr, "invalid flags...so ignore this packet\n"); - brcmf_netif_rx(ifp, pkt, inirq); + brcmf_netif_rx(ifp, pkt); return; } @@ -1694,7 +1694,7 @@ void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt, bool inirq) if (rfi == NULL) { brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n", flow_id); - brcmf_netif_rx(ifp, pkt, inirq); + brcmf_netif_rx(ifp, pkt); return; } @@ -1719,7 +1719,7 @@ void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt, bool inirq) rfi = kzalloc(buf_size, GFP_ATOMIC); if (rfi == NULL) { bphy_err(drvr, "failed to alloc buffer\n"); - brcmf_netif_rx(ifp, pkt, inirq); + brcmf_netif_rx(ifp, pkt); return; } @@ -1833,7 +1833,7 @@ void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt, bool inirq) netif_rx: skb_queue_walk_safe(&reorder_list, pkt, pnext) { __skb_unlink(pkt, &reorder_list); - brcmf_netif_rx(ifp, pkt, inirq); + brcmf_netif_rx(ifp, pkt); } } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h index 50e424b5880d..b16a9d1c0508 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h @@ -42,6 +42,6 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp); void brcmf_fws_del_interface(struct brcmf_if *ifp); void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb); void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked); -void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb, bool inirq); +void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb); #endif /* FWSIGNAL_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index 7c8e08ee8f0f..b2d0f7570aa9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -536,8 +536,7 @@ static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws, return -ENODEV; } -static void brcmf_msgbuf_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb, - bool inirq) +static void brcmf_msgbuf_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb) { } @@ -1191,7 +1190,7 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) } skb->protocol = eth_type_trans(skb, ifp->ndev); - brcmf_netif_rx(ifp, skb, false); + brcmf_netif_rx(ifp, skb); } static void brcmf_msgbuf_process_gen_status(struct brcmf_msgbuf *msgbuf, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c index 513c7e6421b2..8623bde5eb70 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c @@ -71,16 +71,18 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, /* Set board-type to the first string of the machine compatible prop */ root = of_find_node_by_path("/"); if (root) { - int i, len; + int i; char *board_type; const char *tmp; of_property_read_string_index(root, "compatible", 0, &tmp); /* get rid of '/' in the compatible string to be able to find the FW */ - len = strlen(tmp) + 1; - board_type = devm_kzalloc(dev, len, GFP_KERNEL); - strscpy(board_type, tmp, len); + board_type = devm_kstrdup(dev, tmp, GFP_KERNEL); + if (!board_type) { + of_node_put(root); + return; + } for (i = 0; i < board_type[i]; i++) { if (board_type[i] == '/') board_type[i] = '-'; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index 4735063e4c03..479041f070f9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -90,8 +90,8 @@ #define P2PSD_ACTION_CATEGORY 0x04 /* Public action frame */ #define P2PSD_ACTION_ID_GAS_IREQ 0x0a /* GAS Initial Request AF */ #define P2PSD_ACTION_ID_GAS_IRESP 0x0b /* GAS Initial Response AF */ -#define P2PSD_ACTION_ID_GAS_CREQ 0x0c /* GAS Comback Request AF */ -#define P2PSD_ACTION_ID_GAS_CRESP 0x0d /* GAS Comback Response AF */ +#define P2PSD_ACTION_ID_GAS_CREQ 0x0c /* GAS Comeback Request AF */ +#define P2PSD_ACTION_ID_GAS_CRESP 0x0d /* GAS Comeback Response AF */ #define BRCMF_P2P_DISABLE_TIMEOUT msecs_to_jiffies(500) @@ -158,7 +158,7 @@ struct brcmf_p2p_pub_act_frame { u8 oui_type; u8 subtype; u8 dialog_token; - u8 elts[1]; + u8 elts[]; }; /** @@ -177,7 +177,7 @@ struct brcmf_p2p_action_frame { u8 type; u8 subtype; u8 dialog_token; - u8 elts[1]; + u8 elts[]; }; /** @@ -192,7 +192,7 @@ struct brcmf_p2psd_gas_pub_act_frame { u8 category; u8 action; u8 dialog_token; - u8 query_data[1]; + u8 query_data[]; }; /** @@ -225,7 +225,7 @@ static bool brcmf_p2p_is_pub_action(void *frame, u32 frame_len) return false; pact_frm = (struct brcmf_p2p_pub_act_frame *)frame; - if (frame_len < sizeof(struct brcmf_p2p_pub_act_frame) - 1) + if (frame_len < sizeof(*pact_frm)) return false; if (pact_frm->category == P2P_PUB_AF_CATEGORY && @@ -253,7 +253,7 @@ static bool brcmf_p2p_is_p2p_action(void *frame, u32 frame_len) return false; act_frm = (struct brcmf_p2p_action_frame *)frame; - if (frame_len < sizeof(struct brcmf_p2p_action_frame) - 1) + if (frame_len < sizeof(*act_frm)) return false; if (act_frm->category == P2P_AF_CATEGORY && @@ -280,7 +280,7 @@ static bool brcmf_p2p_is_gas_action(void *frame, u32 frame_len) return false; sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame; - if (frame_len < sizeof(struct brcmf_p2psd_gas_pub_act_frame) - 1) + if (frame_len < sizeof(*sd_act_frm)) return false; if (sd_act_frm->category != P2PSD_ACTION_CATEGORY) @@ -396,11 +396,11 @@ static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len) (tx) ? "TX" : "RX"); break; case P2PSD_ACTION_ID_GAS_CREQ: - brcmf_dbg(TRACE, "%s P2P GAS Comback Request\n", + brcmf_dbg(TRACE, "%s P2P GAS Comeback Request\n", (tx) ? "TX" : "RX"); break; case P2PSD_ACTION_ID_GAS_CRESP: - brcmf_dbg(TRACE, "%s P2P GAS Comback Response\n", + brcmf_dbg(TRACE, "%s P2P GAS Comeback Response\n", (tx) ? "TX" : "RX"); break; default: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 8b149996fc00..97f0f13dfe50 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,13 @@ BRCMF_FW_DEF(4366B, "brcmfmac4366b-pcie"); BRCMF_FW_DEF(4366C, "brcmfmac4366c-pcie"); BRCMF_FW_DEF(4371, "brcmfmac4371-pcie"); +/* firmware config files */ +MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.txt"); +MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.*.txt"); + +/* per-board firmware binaries */ +MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.*.bin"); + static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602), BRCMF_FW_ENTRY(BRCM_CC_43465_CHIP_ID, 0xFFFFFFF0, 4366C), @@ -447,47 +455,6 @@ brcmf_pcie_write_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, } -static void -brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset, - void *srcaddr, u32 len) -{ - void __iomem *address = devinfo->tcm + mem_offset; - __le32 *src32; - __le16 *src16; - u8 *src8; - - if (((ulong)address & 4) || ((ulong)srcaddr & 4) || (len & 4)) { - if (((ulong)address & 2) || ((ulong)srcaddr & 2) || (len & 2)) { - src8 = (u8 *)srcaddr; - while (len) { - iowrite8(*src8, address); - address++; - src8++; - len--; - } - } else { - len = len / 2; - src16 = (__le16 *)srcaddr; - while (len) { - iowrite16(le16_to_cpu(*src16), address); - address += 2; - src16++; - len--; - } - } - } else { - len = len / 4; - src32 = (__le32 *)srcaddr; - while (len) { - iowrite32(le32_to_cpu(*src32), address); - address += 4; - src32++; - len--; - } - } -} - - static void brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset, void *dstaddr, u32 len) @@ -777,6 +744,8 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo, return; console = &devinfo->shared.console; + if (!console->base_addr) + return; addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET; newidx = brcmf_pcie_read_tcm32(devinfo, addr); while (newidx != console->read_idx) { @@ -1348,6 +1317,18 @@ static void brcmf_pcie_down(struct device *dev) { } +static int brcmf_pcie_preinit(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; + + brcmf_dbg(PCIE, "Enter\n"); + + brcmf_pcie_intr_enable(buspub->devinfo); + brcmf_pcie_hostready(buspub->devinfo); + + return 0; +} static int brcmf_pcie_tx(struct device *dev, struct sk_buff *skb) { @@ -1456,6 +1437,7 @@ static int brcmf_pcie_reset(struct device *dev) } static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { + .preinit = brcmf_pcie_preinit, .txdata = brcmf_pcie_tx, .stop = brcmf_pcie_down, .txctl = brcmf_pcie_tx_ctlpkt, @@ -1540,6 +1522,7 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, shared->max_rxbufpost, shared->rx_dataoffset); brcmf_pcie_bus_console_init(devinfo); + brcmf_pcie_bus_console_read(devinfo, false); return 0; } @@ -1563,8 +1546,8 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, return err; brcmf_dbg(PCIE, "Download FW %s\n", devinfo->fw_name); - brcmf_pcie_copy_mem_todev(devinfo, devinfo->ci->rambase, - (void *)fw->data, fw->size); + memcpy_toio(devinfo->tcm + devinfo->ci->rambase, + (void *)fw->data, fw->size); resetintr = get_unaligned_le32(fw->data); release_firmware(fw); @@ -1578,7 +1561,7 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, brcmf_dbg(PCIE, "Download NVRAM %s\n", devinfo->nvram_name); address = devinfo->ci->rambase + devinfo->ci->ramsize - nvram_len; - brcmf_pcie_copy_mem_todev(devinfo, address, nvram, nvram_len); + memcpy_toio(devinfo->tcm + address, nvram, nvram_len); brcmf_fw_nvram_free(nvram); } else { brcmf_dbg(PCIE, "No matching NVRAM file found %s\n", @@ -1777,6 +1760,8 @@ static void brcmf_pcie_setup(struct device *dev, int ret, ret = brcmf_chip_get_raminfo(devinfo->ci); if (ret) { brcmf_err(bus, "Failed to get RAM info\n"); + release_firmware(fw); + brcmf_fw_nvram_free(nvram); goto fail; } @@ -1826,9 +1811,6 @@ static void brcmf_pcie_setup(struct device *dev, int ret, init_waitqueue_head(&devinfo->mbdata_resp_wait); - brcmf_pcie_intr_enable(devinfo); - brcmf_pcie_hostready(devinfo); - ret = brcmf_attach(&devinfo->pdev->dev); if (ret) goto fail; @@ -1980,6 +1962,7 @@ brcmf_pcie_remove(struct pci_dev *pdev) return; devinfo = bus->bus_priv.pcie->devinfo; + brcmf_pcie_bus_console_read(devinfo, false); devinfo->state = BRCMFMAC_PCIE_STATE_DOWN; if (devinfo->ci) @@ -2106,6 +2089,7 @@ static const struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_RAW_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4359_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h index f4a79e217da5..bd08d3aaa8f4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h @@ -32,7 +32,7 @@ struct brcmf_proto { u8 peer[ETH_ALEN]); void (*add_tdls_peer)(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]); - void (*rxreorder)(struct brcmf_if *ifp, struct sk_buff *skb, bool inirq); + void (*rxreorder)(struct brcmf_if *ifp, struct sk_buff *skb); void (*add_if)(struct brcmf_if *ifp); void (*del_if)(struct brcmf_if *ifp); void (*reset_if)(struct brcmf_if *ifp); @@ -109,9 +109,9 @@ static inline bool brcmf_proto_is_reorder_skb(struct sk_buff *skb) } static inline void -brcmf_proto_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb, bool inirq) +brcmf_proto_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb) { - ifp->drvr->proto->rxreorder(ifp, skb, inirq); + ifp->drvr->proto->rxreorder(ifp, skb); } static inline void diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 8effeb7a7269..ba3c159111d3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -629,7 +629,6 @@ BRCMF_FW_CLM_DEF(43752, "brcmfmac43752-sdio"); /* firmware config files */ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-sdio.*.txt"); -MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.*.txt"); /* per-board firmware binaries */ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-sdio.*.bin"); @@ -652,6 +651,7 @@ static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFC, 43430B0), BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0x00000200, 43456), BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFDC0, 43455), + BRCMF_FW_ENTRY(BRCM_CC_43454_CHIP_ID, 0x00000040, 43455), BRCMF_FW_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354), BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356), BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359), diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/xtlv.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/xtlv.h index e1930ce1b642..b2c7ae8966a1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/xtlv.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/xtlv.h @@ -15,7 +15,7 @@ struct brcmf_xtlv { u16 id; u16 len; - u8 data[0]; + u8 data[]; }; enum brcmf_xtlv_option { diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h index 9d81320164ce..ed0b707f0cdf 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h @@ -32,6 +32,7 @@ #define BRCM_CC_4339_CHIP_ID 0x4339 #define BRCM_CC_43430_CHIP_ID 43430 #define BRCM_CC_4345_CHIP_ID 0x4345 +#define BRCM_CC_43454_CHIP_ID 43454 #define BRCM_CC_43465_CHIP_ID 43465 #define BRCM_CC_4350_CHIP_ID 0x4350 #define BRCM_CC_43525_CHIP_ID 43525 @@ -71,6 +72,7 @@ #define BRCM_PCIE_4356_DEVICE_ID 0x43ec #define BRCM_PCIE_43567_DEVICE_ID 0x43d3 #define BRCM_PCIE_43570_DEVICE_ID 0x43d9 +#define BRCM_PCIE_43570_RAW_DEVICE_ID 0xaa31 #define BRCM_PCIE_4358_DEVICE_ID 0x43e9 #define BRCM_PCIE_4359_DEVICE_ID 0x43ef #define BRCM_PCIE_43602_DEVICE_ID 0x43ba diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c index 452d08545d31..10daef81c355 100644 --- a/drivers/net/wireless/cisco/airo.c +++ b/drivers/net/wireless/cisco/airo.c @@ -545,7 +545,7 @@ struct ConfigRid { #define MODE_CFG_MASK cpu_to_le16(0xff) #define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */ #define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */ -#define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extenstions */ +#define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extensions */ #define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */ #define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */ #define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */ diff --git a/drivers/net/wireless/intel/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig index 85e704283755..a647a406b87b 100644 --- a/drivers/net/wireless/intel/iwlwifi/Kconfig +++ b/drivers/net/wireless/intel/iwlwifi/Kconfig @@ -139,6 +139,7 @@ config IWLMEI tristate "Intel Management Engine communication over WLAN" depends on INTEL_MEI depends on PM + depends on CFG80211 help Enables the iwlmei kernel module. diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index 330ef04ca51a..8ff967edc8f0 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -1,15 +1,16 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2022 Intel Corporation */ #include #include #include "iwl-config.h" #include "iwl-prph.h" +#include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_22000_UCODE_API_MAX 69 +#define IWL_22000_UCODE_API_MAX 72 /* Lowest firmware API version supported */ #define IWL_22000_UCODE_API_MIN 39 @@ -39,6 +40,7 @@ #define IWL_SO_A_GF_A_FW_PRE "iwlwifi-so-a0-gf-a0-" #define IWL_TY_A_GF_A_FW_PRE "iwlwifi-ty-a0-gf-a0-" #define IWL_SO_A_GF4_A_FW_PRE "iwlwifi-so-a0-gf4-a0-" +#define IWL_SO_A_MR_A_FW_PRE "iwlwifi-so-a0-mr-a0-" #define IWL_SNJ_A_GF4_A_FW_PRE "iwlwifi-SoSnj-a0-gf4-a0-" #define IWL_SNJ_A_GF_A_FW_PRE "iwlwifi-SoSnj-a0-gf-a0-" #define IWL_SNJ_A_HR_B_FW_PRE "iwlwifi-SoSnj-a0-hr-b0-" @@ -119,8 +121,6 @@ IWL_BZ_A_FM_A_FW_PRE __stringify(api) ".ucode" #define IWL_GL_A_FM_A_MODULE_FIRMWARE(api) \ IWL_GL_A_FM_A_FW_PRE __stringify(api) ".ucode" -#define IWL_BZ_Z_GF_A_MODULE_FIRMWARE(api) \ - IWL_BZ_Z_GF_A_FW_PRE __stringify(api) ".ucode" #define IWL_BNJ_A_FM_A_MODULE_FIRMWARE(api) \ IWL_BNJ_A_FM_A_FW_PRE __stringify(api) ".ucode" #define IWL_BNJ_A_FM4_A_MODULE_FIRMWARE(api) \ @@ -224,7 +224,7 @@ static const struct iwl_ht_params iwl_22000_ht_params = { .trans.base_params = &iwl_ax210_base_params, \ .min_txq_size = 128, \ .gp2_reg_addr = 0xd02c68, \ - .min_256_ba_txq_size = 1024, \ + .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_HE, \ .mon_dram_regs = { \ .write_ptr = { \ .addr = DBGC_CUR_DBGBUF_STATUS, \ @@ -285,7 +285,7 @@ static const struct iwl_ht_params iwl_22000_ht_params = { .trans.base_params = &iwl_ax210_base_params, \ .min_txq_size = 128, \ .gp2_reg_addr = 0xd02c68, \ - .min_256_ba_txq_size = 1024, \ + .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT, \ .mon_dram_regs = { \ .write_ptr = { \ .addr = DBGC_CUR_DBGBUF_STATUS, \ @@ -299,6 +299,12 @@ static const struct iwl_ht_params iwl_22000_ht_params = { .addr = DBGC_CUR_DBGBUF_STATUS, \ .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, \ }, \ + }, \ + .mon_dbgi_regs = { \ + .write_ptr = { \ + .addr = DBGI_SRAM_FIFO_POINTERS, \ + .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK, \ + }, \ } const struct iwl_cfg_trans_params iwl_qnj_trans_cfg = { @@ -385,6 +391,21 @@ const struct iwl_cfg_trans_params iwl_so_long_latency_trans_cfg = { .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, }; +const struct iwl_cfg_trans_params iwl_so_long_latency_imr_trans_cfg = { + .mq_rx_supported = true, + .use_tfh = true, + .rf_id = true, + .gen2 = true, + .device_family = IWL_DEVICE_FAMILY_AX210, + .base_params = &iwl_ax210_base_params, + .umac_prph_offset = 0x300000, + .integrated = true, + .low_latency_xtal = true, + .xtal_latency = 12000, + .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, + .imr_enabled = true, +}; + /* * If the device doesn't support HE, no need to have that many buffers. * 22000 devices can split multiple frames into a single RB, so fewer are @@ -476,6 +497,7 @@ const char iwl_ax101_name[] = "Intel(R) Wi-Fi 6 AX101"; const char iwl_ax200_name[] = "Intel(R) Wi-Fi 6 AX200 160MHz"; const char iwl_ax201_name[] = "Intel(R) Wi-Fi 6 AX201 160MHz"; const char iwl_ax203_name[] = "Intel(R) Wi-Fi 6 AX203"; +const char iwl_ax204_name[] = "Intel(R) Wi-Fi 6 AX204 160MHz"; const char iwl_ax211_name[] = "Intel(R) Wi-Fi 6E AX211 160MHz"; const char iwl_ax221_name[] = "Intel(R) Wi-Fi 6E AX221 160MHz"; const char iwl_ax231_name[] = "Intel(R) Wi-Fi 6E AX231 160MHz"; @@ -816,6 +838,20 @@ const struct iwl_cfg iwl_cfg_ma_a0_mr_a0 = { .num_rbds = IWL_NUM_RBDS_AX210_HE, }; +const struct iwl_cfg iwl_cfg_ma_a0_ms_a0 = { + .fw_name_pre = IWL_MA_A_MR_A_FW_PRE, + .uhb_supported = false, + IWL_DEVICE_AX210, + .num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + +const struct iwl_cfg iwl_cfg_so_a0_ms_a0 = { + .fw_name_pre = IWL_SO_A_MR_A_FW_PRE, + .uhb_supported = false, + IWL_DEVICE_AX210, + .num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + const struct iwl_cfg iwl_cfg_ma_a0_fm_a0 = { .fw_name_pre = IWL_MA_A_FM_A_FW_PRE, .uhb_supported = true, @@ -830,6 +866,13 @@ const struct iwl_cfg iwl_cfg_snj_a0_mr_a0 = { .num_rbds = IWL_NUM_RBDS_AX210_HE, }; +const struct iwl_cfg iwl_cfg_snj_a0_ms_a0 = { + .fw_name_pre = IWL_SNJ_A_MR_A_FW_PRE, + .uhb_supported = false, + IWL_DEVICE_AX210, + .num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + const struct iwl_cfg iwl_cfg_so_a0_hr_a0 = { .fw_name_pre = IWL_SO_A_HR_B_FW_PRE, IWL_DEVICE_AX210, diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index 754876cd27ce..e8bd4f0e3d2d 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -299,7 +299,7 @@ static int iwlagn_mac_start(struct ieee80211_hw *hw) priv->is_open = 1; IWL_DEBUG_MAC80211(priv, "leave\n"); - return 0; + return ret; } static void iwlagn_mac_stop(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index 90b9becd1673..caf452922dbd 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -48,6 +48,7 @@ #define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux" MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IWLWIFI); /* Please keep this array *SORTED* by hex value. * Access is done through binary search. diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c index db0c41bbeb0e..e9d2717362cf 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c @@ -3,7 +3,7 @@ * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Deutschland GmbH - * Copyright(c) 2018, 2020 Intel Corporation + * Copyright(c) 2018, 2020-2021 Intel Corporation * * Portions of this file are derived from the ipw3945 project, as well * as portionhelp of the ieee80211 subsystem header files. @@ -915,7 +915,7 @@ static void iwlagn_rx_noa_notification(struct iwl_priv *priv, len += 1 + 2; copylen += 1 + 2; - new_data = kmalloc(sizeof(*new_data) + len, GFP_ATOMIC); + new_data = kmalloc(struct_size(new_data, data, len), GFP_ATOMIC); if (new_data) { new_data->length = len; new_data->data[0] = WLAN_EID_VENDOR_SPECIFIC; @@ -1015,8 +1015,7 @@ void iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct napi_struct *napi, /* No handling needed */ IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n", iwl_get_cmd_string(priv->trans, - iwl_cmd_id(pkt->hdr.cmd, - 0, 0)), + WIDE_ID(0, pkt->hdr.cmd)), pkt->hdr.cmd); } } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index c17ab53fcd8f..33aae639ad37 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -4,6 +4,7 @@ * Copyright (C) 2019-2022 Intel Corporation */ #include +#include #include "iwl-drv.h" #include "iwl-debug.h" #include "acpi.h" @@ -19,6 +20,30 @@ const guid_t iwl_rfi_guid = GUID_INIT(0x7266172C, 0x220B, 0x4B29, 0xDD, 0x26, 0xB5, 0xFD); IWL_EXPORT_SYMBOL(iwl_rfi_guid); +static const struct dmi_system_id dmi_ppag_approved_list[] = { + { .ident = "HP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + }, + }, + { .ident = "SAMSUNG", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), + }, + }, + { .ident = "MSFT", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + }, + }, + { .ident = "ASUS", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTek COMPUTER INC."), + }, + }, + {} +}; + static int iwl_acpi_get_handle(struct device *dev, acpi_string method, acpi_handle *ret_handle) { @@ -242,7 +267,7 @@ union acpi_object *iwl_acpi_get_wifi_pkg_range(struct device *dev, IWL_EXPORT_SYMBOL(iwl_acpi_get_wifi_pkg_range); int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, - struct iwl_tas_config_cmd_v3 *cmd) + union iwl_tas_config_cmd *cmd, int fw_ver) { union acpi_object *wifi_pkg, *data; int ret, tbl_rev, i, block_list_size, enabled; @@ -268,10 +293,18 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, (tas_selection & ACPI_WTAS_OVERRIDE_IEC_MSK) >> ACPI_WTAS_OVERRIDE_IEC_POS; u16 enabled_iec = (tas_selection & ACPI_WTAS_ENABLE_IEC_MSK) >> ACPI_WTAS_ENABLE_IEC_POS; + u8 usa_tas_uhb = (tas_selection & ACPI_WTAS_USA_UHB_MSK) >> ACPI_WTAS_USA_UHB_POS; + enabled = tas_selection & ACPI_WTAS_ENABLED_MSK; - cmd->override_tas_iec = cpu_to_le16(override_iec); - cmd->enable_tas_iec = cpu_to_le16(enabled_iec); + if (fw_ver <= 3) { + cmd->v3.override_tas_iec = cpu_to_le16(override_iec); + cmd->v3.enable_tas_iec = cpu_to_le16(enabled_iec); + } else { + cmd->v4.usa_tas_uhb_allowed = usa_tas_uhb; + cmd->v4.override_tas_iec = (u8)override_iec; + cmd->v4.enable_tas_iec = (u8)enabled_iec; + } } else if (tbl_rev == 0 && wifi_pkg->package.elements[1].type == ACPI_TYPE_INTEGER) { @@ -297,7 +330,7 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, goto out_free; } block_list_size = wifi_pkg->package.elements[2].integer.value; - cmd->block_list_size = cpu_to_le32(block_list_size); + cmd->v4.block_list_size = cpu_to_le32(block_list_size); IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size); if (block_list_size > APCI_WTAS_BLACK_LIST_MAX) { @@ -319,7 +352,7 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, } country = wifi_pkg->package.elements[3 + i].integer.value; - cmd->block_list_array[i] = cpu_to_le32(country); + cmd->v4.block_list_array[i] = cpu_to_le32(country); IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country); } @@ -529,8 +562,8 @@ IWL_EXPORT_SYMBOL(iwl_sar_select_profile); int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) { union acpi_object *wifi_pkg, *table, *data; - bool enabled; int ret, tbl_rev; + u32 flags; u8 num_chains, num_sub_bands; data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD); @@ -596,7 +629,8 @@ int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev); - enabled = !!(wifi_pkg->package.elements[1].integer.value); + flags = wifi_pkg->package.elements[1].integer.value; + fwrt->reduced_power_flags = flags >> IWL_REDUCE_POWER_FLAGS_POS; /* position of the actual table */ table = &wifi_pkg->package.elements[2]; @@ -604,7 +638,8 @@ int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) /* The profile from WRDS is officially profile 1, but goes * into sar_profiles[0] (because we don't have a profile 0). */ - ret = iwl_sar_set_profile(table, &fwrt->sar_profiles[0], enabled, + ret = iwl_sar_set_profile(table, &fwrt->sar_profiles[0], + flags & IWL_SAR_ENABLE_MSK, num_chains, num_sub_bands); out_free: kfree(data); @@ -962,3 +997,181 @@ __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) return config_bitmap; } IWL_EXPORT_SYMBOL(iwl_acpi_get_lari_config_bitmap); + +int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) +{ + union acpi_object *wifi_pkg, *data, *flags; + int i, j, ret, tbl_rev, num_sub_bands = 0; + int idx = 2; + + fwrt->ppag_flags = 0; + + data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD); + if (IS_ERR(data)) + return PTR_ERR(data); + + /* try to read ppag table rev 2 or 1 (both have the same data size) */ + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev); + + if (!IS_ERR(wifi_pkg)) { + if (tbl_rev == 1 || tbl_rev == 2) { + num_sub_bands = IWL_NUM_SUB_BANDS_V2; + IWL_DEBUG_RADIO(fwrt, + "Reading PPAG table v2 (tbl_rev=%d)\n", + tbl_rev); + goto read_table; + } else { + ret = -EINVAL; + goto out_free; + } + } + + /* try to read ppag table revision 0 */ + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_PPAG_WIFI_DATA_SIZE_V1, &tbl_rev); + + if (!IS_ERR(wifi_pkg)) { + if (tbl_rev != 0) { + ret = -EINVAL; + goto out_free; + } + num_sub_bands = IWL_NUM_SUB_BANDS_V1; + IWL_DEBUG_RADIO(fwrt, "Reading PPAG table v1 (tbl_rev=0)\n"); + goto read_table; + } + +read_table: + fwrt->ppag_ver = tbl_rev; + flags = &wifi_pkg->package.elements[1]; + + if (flags->type != ACPI_TYPE_INTEGER) { + ret = -EINVAL; + goto out_free; + } + + fwrt->ppag_flags = flags->integer.value & ACPI_PPAG_MASK; + + if (!fwrt->ppag_flags) { + ret = 0; + goto out_free; + } + + /* + * read, verify gain values and save them into the PPAG table. + * first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the + * following sub-bands to High-Band (5GHz). + */ + for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { + for (j = 0; j < num_sub_bands; j++) { + union acpi_object *ent; + + ent = &wifi_pkg->package.elements[idx++]; + if (ent->type != ACPI_TYPE_INTEGER) { + ret = -EINVAL; + goto out_free; + } + + fwrt->ppag_chains[i].subbands[j] = ent->integer.value; + + if ((j == 0 && + (fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_LB || + fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_LB)) || + (j != 0 && + (fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_HB || + fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_HB))) { + fwrt->ppag_flags = 0; + ret = -EINVAL; + goto out_free; + } + } + } + + + ret = 0; + +out_free: + kfree(data); + return ret; +} +IWL_EXPORT_SYMBOL(iwl_acpi_get_ppag_table); + +int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *cmd, + int *cmd_size) +{ + u8 cmd_ver; + int i, j, num_sub_bands; + s8 *gain; + + if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) { + IWL_DEBUG_RADIO(fwrt, + "PPAG capability not supported by FW, command not sent.\n"); + return -EINVAL; + } + if (!fwrt->ppag_flags) { + IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n"); + return -EINVAL; + } + + /* The 'flags' field is the same in v1 and in v2 so we can just + * use v1 to access it. + */ + cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags); + cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, + WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD), + IWL_FW_CMD_VER_UNKNOWN); + if (cmd_ver == 1) { + num_sub_bands = IWL_NUM_SUB_BANDS_V1; + gain = cmd->v1.gain[0]; + *cmd_size = sizeof(cmd->v1); + if (fwrt->ppag_ver == 1 || fwrt->ppag_ver == 2) { + IWL_DEBUG_RADIO(fwrt, + "PPAG table rev is %d but FW supports v1, sending truncated table\n", + fwrt->ppag_ver); + cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK); + } + } else if (cmd_ver == 2 || cmd_ver == 3) { + num_sub_bands = IWL_NUM_SUB_BANDS_V2; + gain = cmd->v2.gain[0]; + *cmd_size = sizeof(cmd->v2); + if (fwrt->ppag_ver == 0) { + IWL_DEBUG_RADIO(fwrt, + "PPAG table is v1 but FW supports v2, sending padded table\n"); + } else if (cmd_ver == 2 && fwrt->ppag_ver == 2) { + IWL_DEBUG_RADIO(fwrt, + "PPAG table is v3 but FW supports v2, sending partial bitmap.\n"); + cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK); + } + } else { + IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n"); + return -EINVAL; + } + + for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { + for (j = 0; j < num_sub_bands; j++) { + gain[i * num_sub_bands + j] = + fwrt->ppag_chains[i].subbands[j]; + IWL_DEBUG_RADIO(fwrt, + "PPAG table: chain[%d] band[%d]: gain = %d\n", + i, j, gain[i * num_sub_bands + j]); + } + } + + return 0; +} +IWL_EXPORT_SYMBOL(iwl_read_ppag_table); + +bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt) +{ + + if (!dmi_check_system(dmi_ppag_approved_list)) { + IWL_DEBUG_RADIO(fwrt, + "System vendor '%s' is not in the approved list, disabling PPAG.\n", + dmi_get_system_info(DMI_SYS_VENDOR)); + fwrt->ppag_flags = 0; + return false; + } + + return true; +} +IWL_EXPORT_SYMBOL(iwl_acpi_is_ppag_approved); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 22b3c665f91a..6f361c59106f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2022 Intel Corporation */ #ifndef __iwl_fw_acpi__ #define __iwl_fw_acpi__ @@ -77,6 +77,8 @@ #define ACPI_WTAS_ENABLE_IEC_MSK 0x4 #define ACPI_WTAS_OVERRIDE_IEC_POS 0x1 #define ACPI_WTAS_ENABLE_IEC_POS 0x2 +#define ACPI_WTAS_USA_UHB_MSK BIT(16) +#define ACPI_WTAS_USA_UHB_POS 16 #define ACPI_PPAG_WIFI_DATA_SIZE_V1 ((IWL_NUM_CHAIN_LIMITS * \ @@ -89,6 +91,11 @@ #define ACPI_PPAG_MAX_LB 24 #define ACPI_PPAG_MIN_HB -16 #define ACPI_PPAG_MAX_HB 40 +#define ACPI_PPAG_MASK 3 +#define IWL_PPAG_ETSI_MASK BIT(0) + +#define IWL_SAR_ENABLE_MSK BIT(0) +#define IWL_REDUCE_POWER_FLAGS_POS 1 /* * The profile for revision 2 is a superset of revision 1, which is in @@ -126,7 +133,8 @@ enum iwl_dsm_funcs_rev_0 { DSM_FUNC_ENABLE_6E = 3, DSM_FUNC_11AX_ENABLEMENT = 6, DSM_FUNC_ENABLE_UNII4_CHAN = 7, - DSM_FUNC_ACTIVATE_CHANNEL = 8 + DSM_FUNC_ACTIVATE_CHANNEL = 8, + DSM_FUNC_FORCE_DISABLE_CHANNELS = 9 }; enum iwl_dsm_values_srd { @@ -213,10 +221,17 @@ int iwl_sar_geo_init(struct iwl_fw_runtime *fwrt, u32 n_bands, u32 n_profiles); int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, - struct iwl_tas_config_cmd_v3 *cmd); + union iwl_tas_config_cmd *cmd, int fw_ver); __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); +int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt); + +int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *cmd, + int *cmd_size); + +bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt); + #else /* CONFIG_ACPI */ static inline void *iwl_acpi_get_object(struct device *dev, acpi_string method) @@ -294,7 +309,7 @@ static inline bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) } static inline int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, - struct iwl_tas_config_cmd_v3 *cmd) + union iwl_tas_config_cmd *cmd, int fw_ver) { return -ENOENT; } @@ -304,6 +319,22 @@ static inline __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt return 0; } +static inline int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) +{ + return -ENOENT; +} + +static inline int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, + union iwl_ppag_table_cmd *cmd, int *cmd_size) +{ + return -ENOENT; +} + +static inline bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt) +{ + return false; +} + #endif /* CONFIG_ACPI */ static inline union acpi_object * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index 35b8856e511f..c78d2f1c722c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -322,14 +322,6 @@ enum iwl_legacy_cmds { */ REPLY_THERMAL_MNG_BACKOFF = 0x7e, - /** - * @DC2DC_CONFIG_CMD: - * Set/Get DC2DC frequency tune - * Command is &struct iwl_dc2dc_config_cmd, - * response is &struct iwl_dc2dc_config_resp - */ - DC2DC_CONFIG_CMD = 0x83, - /** * @NVM_ACCESS_CMD: using &struct iwl_nvm_access_cmd */ @@ -608,6 +600,11 @@ enum iwl_system_subcmd_ids { * @SYSTEM_FEATURES_CONTROL_CMD: &struct iwl_system_features_control_cmd */ SYSTEM_FEATURES_CONTROL_CMD = 0xd, + + /** + * @RFI_DEACTIVATE_NOTIF: &struct iwl_rfi_deactivate_notif + */ + RFI_DEACTIVATE_NOTIF = 0xff, }; #endif /* __iwl_fw_api_commands_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/config.h b/drivers/net/wireless/intel/iwlwifi/fw/api/config.h index 1ab92f62c414..087354b3c308 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/config.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/config.h @@ -114,37 +114,4 @@ enum iwl_dc2dc_config_id { DCDC_FREQ_TUNE_SET = 0x2, }; /* MARKER_ID_API_E_VER_1 */ -/** - * struct iwl_dc2dc_config_cmd - configure dc2dc values - * - * (DC2DC_CONFIG_CMD = 0x83) - * - * Set/Get & configure dc2dc values. - * The command always returns the current dc2dc values. - * - * @flags: set/get dc2dc - * @enable_low_power_mode: not used. - * @dc2dc_freq_tune0: frequency divider - digital domain - * @dc2dc_freq_tune1: frequency divider - analog domain - */ -struct iwl_dc2dc_config_cmd { - __le32 flags; - __le32 enable_low_power_mode; /* not used */ - __le32 dc2dc_freq_tune0; - __le32 dc2dc_freq_tune1; -} __packed; /* DC2DC_CONFIG_CMD_API_S_VER_1 */ - -/** - * struct iwl_dc2dc_config_resp - response for iwl_dc2dc_config_cmd - * - * Current dc2dc values returned by the FW. - * - * @dc2dc_freq_tune0: frequency divider - digital domain - * @dc2dc_freq_tune1: frequency divider - analog domain - */ -struct iwl_dc2dc_config_resp { - __le32 dc2dc_freq_tune0; - __le32 dc2dc_freq_tune1; -} __packed; /* DC2DC_CONFIG_RESP_API_S_VER_1 */ - #endif /* __iwl_fw_api_config_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index 89236f42c5a4..43619acc29fd 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -42,7 +42,7 @@ enum iwl_data_path_subcmd_ids { RFH_QUEUE_CONFIG_CMD = 0xD, /** - * @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd + * @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd_v4 */ TLC_MNG_CONFIG_CMD = 0xF, @@ -57,6 +57,20 @@ enum iwl_data_path_subcmd_ids { */ CHEST_COLLECTOR_FILTER_CONFIG_CMD = 0x14, + /** + * @RX_BAID_ALLOCATION_CONFIG_CMD: Allocate/deallocate a BAID for an RX + * blockack session, uses &struct iwl_rx_baid_cfg_cmd for the + * command, and &struct iwl_rx_baid_cfg_resp as a response. + */ + RX_BAID_ALLOCATION_CONFIG_CMD = 0x16, + + /** + * @SCD_QUEUE_CONFIG_CMD: new scheduler queue allocation/config/removal + * command, uses &struct iwl_scd_queue_cfg_cmd and the response + * is (same as before) &struct iwl_tx_queue_cfg_rsp. + */ + SCD_QUEUE_CONFIG_CMD = 0x17, + /** * @MONITOR_NOTIF: Datapath monitoring notification, using * &struct iwl_datapath_monitor_notif @@ -257,4 +271,136 @@ struct iwl_rlc_config_cmd { u8 reserved[3]; } __packed; /* RLC_CONFIG_CMD_API_S_VER_2 */ +#define IWL_MAX_BAID_OLD 16 /* MAX_IMMEDIATE_BA_API_D_VER_2 */ +#define IWL_MAX_BAID 32 /* MAX_IMMEDIATE_BA_API_D_VER_3 */ + +/** + * enum iwl_rx_baid_action - BAID allocation/config action + * @IWL_RX_BAID_ACTION_ADD: add a new BAID session + * @IWL_RX_BAID_ACTION_MODIFY: modify the BAID session + * @IWL_RX_BAID_ACTION_REMOVE: remove the BAID session + */ +enum iwl_rx_baid_action { + IWL_RX_BAID_ACTION_ADD, + IWL_RX_BAID_ACTION_MODIFY, + IWL_RX_BAID_ACTION_REMOVE, +}; /* RX_BAID_ALLOCATION_ACTION_E_VER_1 */ + +/** + * struct iwl_rx_baid_cfg_cmd_alloc - BAID allocation data + * @sta_id_mask: station ID mask + * @tid: the TID for this session + * @reserved: reserved + * @ssn: the starting sequence number + * @win_size: RX BA session window size + */ +struct iwl_rx_baid_cfg_cmd_alloc { + __le32 sta_id_mask; + u8 tid; + u8 reserved[3]; + __le16 ssn; + __le16 win_size; +} __packed; /* RX_BAID_ALLOCATION_ADD_CMD_API_S_VER_1 */ + +/** + * struct iwl_rx_baid_cfg_cmd_modify - BAID modification data + * @old_sta_id_mask: old station ID mask + * @new_sta_id_mask: new station ID mask + * @tid: TID of the BAID + */ +struct iwl_rx_baid_cfg_cmd_modify { + __le32 old_sta_id_mask; + __le32 new_sta_id_mask; + __le32 tid; +} __packed; /* RX_BAID_ALLOCATION_MODIFY_CMD_API_S_VER_2 */ + +/** + * struct iwl_rx_baid_cfg_cmd_remove_v1 - BAID removal data + * @baid: the BAID to remove + */ +struct iwl_rx_baid_cfg_cmd_remove_v1 { + __le32 baid; +} __packed; /* RX_BAID_ALLOCATION_REMOVE_CMD_API_S_VER_1 */ + +/** + * struct iwl_rx_baid_cfg_cmd_remove - BAID removal data + * @sta_id_mask: the station mask of the BAID to remove + * @tid: the TID of the BAID to remove + */ +struct iwl_rx_baid_cfg_cmd_remove { + __le32 sta_id_mask; + __le32 tid; +} __packed; /* RX_BAID_ALLOCATION_REMOVE_CMD_API_S_VER_2 */ + +/** + * struct iwl_rx_baid_cfg_cmd - BAID allocation/config command + * @action: the action, from &enum iwl_rx_baid_action + */ +struct iwl_rx_baid_cfg_cmd { + __le32 action; + union { + struct iwl_rx_baid_cfg_cmd_alloc alloc; + struct iwl_rx_baid_cfg_cmd_modify modify; + struct iwl_rx_baid_cfg_cmd_remove_v1 remove_v1; + struct iwl_rx_baid_cfg_cmd_remove remove; + }; /* RX_BAID_ALLOCATION_OPERATION_API_U_VER_2 */ +} __packed; /* RX_BAID_ALLOCATION_CONFIG_CMD_API_S_VER_2 */ + +/** + * struct iwl_rx_baid_cfg_resp - BAID allocation response + * @baid: the allocated BAID + */ +struct iwl_rx_baid_cfg_resp { + __le32 baid; +}; /* RX_BAID_ALLOCATION_RESPONSE_API_S_VER_1 */ + +/** + * enum iwl_scd_queue_cfg_operation - scheduler queue operation + * @IWL_SCD_QUEUE_ADD: allocate a new queue + * @IWL_SCD_QUEUE_REMOVE: remove a queue + * @IWL_SCD_QUEUE_MODIFY: modify a queue + */ +enum iwl_scd_queue_cfg_operation { + IWL_SCD_QUEUE_ADD = 0, + IWL_SCD_QUEUE_REMOVE = 1, + IWL_SCD_QUEUE_MODIFY = 2, +}; + +/** + * struct iwl_scd_queue_cfg_cmd - scheduler queue allocation command + * @operation: the operation, see &enum iwl_scd_queue_cfg_operation + * @u.add.sta_mask: station mask + * @u.add.tid: TID + * @u.add.reserved: reserved + * @u.add.flags: flags from &enum iwl_tx_queue_cfg_actions, except + * %TX_QUEUE_CFG_ENABLE_QUEUE is not valid + * @u.add.cb_size: size code + * @u.add.bc_dram_addr: byte-count table IOVA + * @u.add.tfdq_dram_addr: TFD queue IOVA + * @u.remove.queue: queue ID for removal + * @u.modify.sta_mask: new station mask for modify + * @u.modify.queue: queue ID to modify + */ +struct iwl_scd_queue_cfg_cmd { + __le32 operation; + union { + struct { + __le32 sta_mask; + u8 tid; + u8 reserved[3]; + __le32 flags; + __le32 cb_size; + __le64 bc_dram_addr; + __le64 tfdq_dram_addr; + } __packed add; /* TX_QUEUE_CFG_CMD_ADD_API_S_VER_1 */ + struct { + __le32 queue; + } __packed remove; /* TX_QUEUE_CFG_CMD_REMOVE_API_S_VER_1 */ + struct { + __le32 sta_mask; + __le32 queue; + } __packed modify; /* TX_QUEUE_CFG_CMD_MODIFY_API_S_VER_1 */ + } __packed u; /* TX_QUEUE_CFG_CMD_OPERATION_API_U_VER_1 */ +} __packed; /* TX_QUEUE_CFG_CMD_API_S_VER_3 */ + #endif /* __iwl_fw_api_datapath_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index 456b7eaac570..52bf96585fc6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2022 Intel Corporation */ #ifndef __iwl_fw_dbg_tlv_h__ #define __iwl_fw_dbg_tlv_h__ @@ -11,7 +11,8 @@ #define IWL_FW_INI_MAX_NAME 32 #define IWL_FW_INI_MAX_CFG_NAME 64 #define IWL_FW_INI_DOMAIN_ALWAYS_ON 0 -#define IWL_FW_INI_REGION_V2_MASK 0x0000FFFF +#define IWL_FW_INI_REGION_ID_MASK GENMASK(15, 0) +#define IWL_FW_INI_REGION_DUMP_POLICY_MASK GENMASK(31, 16) /** * struct iwl_fw_ini_hcmd @@ -249,11 +250,10 @@ struct iwl_fw_ini_hcmd_tlv { } __packed; /* FW_TLV_DEBUG_HCMD_API_S_VER_1 */ /** -* struct iwl_fw_ini_conf_tlv - preset configuration TLV +* struct iwl_fw_ini_addr_val - Address and value to set it to * * @address: the base address * @value: value to set at address - */ struct iwl_fw_ini_addr_val { __le32 address; @@ -475,6 +475,7 @@ enum iwl_fw_ini_time_point { * @IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG: override trigger configuration * @IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA: override trigger data. * Append otherwise + * @IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD: send cmd once dump collected */ enum iwl_fw_ini_trigger_apply_policy { IWL_FW_INI_APPLY_POLICY_MATCH_TIME_POINT = BIT(0), @@ -482,6 +483,7 @@ enum iwl_fw_ini_trigger_apply_policy { IWL_FW_INI_APPLY_POLICY_OVERRIDE_REGIONS = BIT(8), IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG = BIT(9), IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA = BIT(10), + IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD = BIT(16), }; /** @@ -496,4 +498,31 @@ enum iwl_fw_ini_trigger_reset_fw_policy { IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY, IWL_FW_INI_RESET_FW_MODE_STOP_AND_RELOAD_FW }; + +/** + * enum iwl_fw_ini_dump_policy - Determines how to handle dump based on enabled flags + * + * @IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT: OS has no limit of dump size + * @IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB: mini dump only 600KB region dump + * @IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB: mini dump 5MB size dump + */ +enum iwl_fw_ini_dump_policy { + IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT = BIT(0), + IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB = BIT(1), + IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB = BIT(2), + +}; + +/** + * enum iwl_fw_ini_dump_type - Determines dump type based on size defined by FW. + * + * @IWL_FW_INI_DUMP_BRIEF : only dump the most important regions + * @IWL_FW_INI_DEBUG_MEDIUM: dump more regions than "brief", but not all regions + * @IWL_FW_INI_DUMP_VERBOSE : dump all regions + */ +enum iwl_fw_ini_dump_type { + IWL_FW_INI_DUMP_BRIEF, + IWL_FW_INI_DUMP_MEDIUM, + IWL_FW_INI_DUMP_VERBOSE, +}; #endif diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h index 029ae64bf2b2..6255257ddebe 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2020 Intel Corporation + * Copyright (C) 2005-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -42,6 +42,12 @@ enum iwl_debug_cmds { * &struct iwl_buf_alloc_cmd */ BUFFER_ALLOCATION = 0x8, + /** + * @FW_DUMP_COMPLETE_CMD: + * sends command to fw once dump collection completed + * &struct iwl_dbg_dump_complete_cmd + */ + FW_DUMP_COMPLETE_CMD = 0xB, /** * @MFU_ASSERT_DUMP_NTF: * &struct iwl_mfu_assert_dump_notif @@ -404,4 +410,15 @@ struct iwl_dbg_host_event_cfg_cmd { __le32 enabled_severities; } __packed; /* DEBUG_HOST_EVENT_CFG_CMD_API_S_VER_1 */ +/** + * struct iwl_dbg_dump_complete_cmd - dump complete cmd + * + * @tp: timepoint whose dump has completed + * @tp_data: timepoint data + */ +struct iwl_dbg_dump_complete_cmd { + __le32 tp; + __le32 tp_data; +} __packed; /* FW_DUMP_COMPLETE_CMD_API_S_VER_1 */ + #endif /* __iwl_fw_api_debug_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index d088c820b1a9..712532f17630 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -27,6 +27,10 @@ enum iwl_mac_conf_subcmd_ids { * @SESSION_PROTECTION_CMD: &struct iwl_mvm_session_prot_cmd */ SESSION_PROTECTION_CMD = 0x5, + /** + * @CANCEL_CHANNEL_SWITCH_CMD: &struct iwl_cancel_channel_switch_cmd + */ + CANCEL_CHANNEL_SWITCH_CMD = 0x6, /** * @SESSION_PROTECTION_NOTIF: &struct iwl_mvm_session_prot_notif @@ -42,6 +46,11 @@ enum iwl_mac_conf_subcmd_ids { * @CHANNEL_SWITCH_START_NOTIF: &struct iwl_channel_switch_start_notif */ CHANNEL_SWITCH_START_NOTIF = 0xFF, + + /** + *@CHANNEL_SWITCH_ERROR_NOTIF: &struct iwl_channel_switch_error_notif + */ + CHANNEL_SWITCH_ERROR_NOTIF = 0xF9, }; #define IWL_P2P_NOA_DESC_COUNT (2) @@ -110,6 +119,31 @@ struct iwl_channel_switch_start_notif { __le32 id_and_color; } __packed; /* CHANNEL_SWITCH_START_NTFY_API_S_VER_1 */ +#define CS_ERR_COUNT_ERROR BIT(0) +#define CS_ERR_LONG_DELAY_AFTER_CS BIT(1) +#define CS_ERR_LONG_TX_BLOCK BIT(2) +#define CS_ERR_TX_BLOCK_TIMER_EXPIRED BIT(3) + +/** + * struct iwl_channel_switch_error_notif - Channel switch error notification + * + * @mac_id: the mac for which the ucode sends the notification for + * @csa_err_mask: mask of channel switch error that can occur + */ +struct iwl_channel_switch_error_notif { + __le32 mac_id; + __le32 csa_err_mask; +} __packed; /* CHANNEL_SWITCH_ERROR_NTFY_API_S_VER_1 */ + +/** + * struct iwl_cancel_channel_switch_cmd - Cancel Channel Switch command + * + * @mac_id: the mac that should cancel the channel switch + */ +struct iwl_cancel_channel_switch_cmd { + __le32 mac_id; +} __packed; /* MAC_CANCEL_CHANNEL_SWITCH_S_VER_1 */ + /** * struct iwl_chan_switch_te_cmd - Channel Switch Time Event command * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h index 11f0bd283e49..9b7caf968346 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h @@ -413,10 +413,11 @@ enum iwl_he_pkt_ext_constellations { }; #define MAX_HE_SUPP_NSS 2 -#define MAX_HE_CHANNEL_BW_INDX 4 +#define MAX_CHANNEL_BW_INDX_API_D_VER_2 4 +#define MAX_CHANNEL_BW_INDX_API_D_VER_3 5 /** - * struct iwl_he_pkt_ext - QAM thresholds + * struct iwl_he_pkt_ext_v1 - QAM thresholds * The required PPE is set via HE Capabilities IE, per Nss x BW x MCS * The IE is organized in the following way: * Support for Nss x BW (or RU) matrix: @@ -435,9 +436,34 @@ enum iwl_he_pkt_ext_constellations { * Nss (0-siso, 1-mimo2) x BW (0-20MHz, 1-40MHz, 2-80MHz, 3-160MHz) x * (0-low_th, 1-high_th) */ -struct iwl_he_pkt_ext { - u8 pkt_ext_qam_th[MAX_HE_SUPP_NSS][MAX_HE_CHANNEL_BW_INDX][2]; -} __packed; /* PKT_EXT_DOT11AX_API_S */ +struct iwl_he_pkt_ext_v1 { + u8 pkt_ext_qam_th[MAX_HE_SUPP_NSS][MAX_CHANNEL_BW_INDX_API_D_VER_2][2]; +} __packed; /* PKT_EXT_DOT11AX_API_S_VER_1 */ + +/** + * struct iwl_he_pkt_ext_v2 - QAM thresholds + * The required PPE is set via HE Capabilities IE, per Nss x BW x MCS + * The IE is organized in the following way: + * Support for Nss x BW (or RU) matrix: + * (0=SISO, 1=MIMO2) x (0-20MHz, 1-40MHz, 2-80MHz, 3-160MHz) + * Each entry contains 2 QAM thresholds for 8us and 16us: + * 0=BPSK, 1=QPSK, 2=16QAM, 3=64QAM, 4=256QAM, 5=1024QAM, 6=RES, 7=NONE + * i.e. QAM_th1 < QAM_th2 such if TX uses QAM_tx: + * QAM_tx < QAM_th1 --> PPE=0us + * QAM_th1 <= QAM_tx < QAM_th2 --> PPE=8us + * QAM_th2 <= QAM_tx --> PPE=16us + * @pkt_ext_qam_th: QAM thresholds + * For each Nss/Bw define 2 QAM thrsholds (0..5) + * For rates below the low_th, no need for PPE + * For rates between low_th and high_th, need 8us PPE + * For rates equal or higher then the high_th, need 16us PPE + * Nss (0-siso, 1-mimo2) x + * BW (0-20MHz, 1-40MHz, 2-80MHz, 3-160MHz, 4-320MHz) x + * (0-low_th, 1-high_th) + */ +struct iwl_he_pkt_ext_v2 { + u8 pkt_ext_qam_th[MAX_HE_SUPP_NSS][MAX_CHANNEL_BW_INDX_API_D_VER_3][2]; +} __packed; /* PKT_EXT_DOT11AX_API_S_VER_2 */ /** * enum iwl_he_sta_ctxt_flags - HE STA context flags @@ -464,6 +490,11 @@ struct iwl_he_pkt_ext { * @STA_CTXT_HE_RU_2MHZ_BLOCK: indicates that 26-tone RU OFDMA transmission are * not allowed (as there are OBSS that might classify such transmissions as * radar pulses). + * @STA_CTXT_HE_NDP_FEEDBACK_ENABLED: mark support for NDP feedback and change + * of threshold + * @STA_CTXT_EHT_PUNCTURE_MASK_VALID: indicates the puncture_mask field is valid + * @STA_CTXT_EHT_LONG_PPE_ENABLED: indicates the PPE requirement should be + * extended to 20us for BW > 160Mhz or for MCS w/ 4096-QAM. */ enum iwl_he_sta_ctxt_flags { STA_CTXT_HE_REF_BSSID_VALID = BIT(4), @@ -477,6 +508,9 @@ enum iwl_he_sta_ctxt_flags { STA_CTXT_HE_MU_EDCA_CW = BIT(12), STA_CTXT_HE_NIC_NOT_ACK_ENABLED = BIT(13), STA_CTXT_HE_RU_2MHZ_BLOCK = BIT(14), + STA_CTXT_HE_NDP_FEEDBACK_ENABLED = BIT(15), + STA_CTXT_EHT_PUNCTURE_MASK_VALID = BIT(16), + STA_CTXT_EHT_LONG_PPE_ENABLED = BIT(17), }; /** @@ -551,7 +585,7 @@ struct iwl_he_sta_context_cmd_v1 { u8 frag_min_size; /* The below fields are set via PPE thresholds element */ - struct iwl_he_pkt_ext pkt_ext; + struct iwl_he_pkt_ext_v1 pkt_ext; /* The below fields are set via HE-Operation IE */ u8 bss_color; @@ -568,7 +602,7 @@ struct iwl_he_sta_context_cmd_v1 { } __packed; /* STA_CONTEXT_DOT11AX_API_S_VER_1 */ /** - * struct iwl_he_sta_context_cmd - configure FW to work with HE AP + * struct iwl_he_sta_context_cmd_v2 - configure FW to work with HE AP * @sta_id: STA id * @tid_limit: max num of TIDs in TX HE-SU multi-TID agg * 0 - bad value, 1 - multi-tid not supported, 2..8 - tid limit @@ -599,7 +633,7 @@ struct iwl_he_sta_context_cmd_v1 { * @bssid_count: actual number of VAPs in the MultiBSS Set * @reserved4: alignment */ -struct iwl_he_sta_context_cmd { +struct iwl_he_sta_context_cmd_v2 { u8 sta_id; u8 tid_limit; u8 reserved1; @@ -619,7 +653,7 @@ struct iwl_he_sta_context_cmd { u8 frag_min_size; /* The below fields are set via PPE thresholds element */ - struct iwl_he_pkt_ext pkt_ext; + struct iwl_he_pkt_ext_v1 pkt_ext; /* The below fields are set via HE-Operation IE */ u8 bss_color; @@ -642,6 +676,81 @@ struct iwl_he_sta_context_cmd { u8 reserved4[3]; } __packed; /* STA_CONTEXT_DOT11AX_API_S_VER_2 */ +/** + * struct iwl_he_sta_context_cmd_v3 - configure FW to work with HE AP + * @sta_id: STA id + * @tid_limit: max num of TIDs in TX HE-SU multi-TID agg + * 0 - bad value, 1 - multi-tid not supported, 2..8 - tid limit + * @reserved1: reserved byte for future use + * @reserved2: reserved byte for future use + * @flags: see %iwl_11ax_sta_ctxt_flags + * @ref_bssid_addr: reference BSSID used by the AP + * @reserved0: reserved 2 bytes for aligning the ref_bssid_addr field to 8 bytes + * @htc_flags: which features are supported in HTC + * @frag_flags: frag support in A-MSDU + * @frag_level: frag support level + * @frag_max_num: max num of "open" MSDUs in the receiver (in power of 2) + * @frag_min_size: min frag size (except last frag) + * @pkt_ext: optional, exists according to PPE-present bit in the HE-PHY capa + * @bss_color: 11ax AP ID that is used in the HE SIG-A to mark inter BSS frame + * @htc_trig_based_pkt_ext: default PE in 4us units + * @frame_time_rts_th: HE duration RTS threshold, in units of 32us + * @rand_alloc_ecwmin: random CWmin = 2**ECWmin-1 + * @rand_alloc_ecwmax: random CWmax = 2**ECWmax-1 + * @puncture_mask: puncture mask for EHT + * @trig_based_txf: MU EDCA Parameter set for the trigger based traffic queues + * @max_bssid_indicator: indicator of the max bssid supported on the associated + * bss + * @bssid_index: index of the associated VAP + * @ema_ap: AP supports enhanced Multi BSSID advertisement + * @profile_periodicity: number of Beacon periods that are needed to receive the + * complete VAPs info + * @bssid_count: actual number of VAPs in the MultiBSS Set + * @reserved4: alignment + */ +struct iwl_he_sta_context_cmd_v3 { + u8 sta_id; + u8 tid_limit; + u8 reserved1; + u8 reserved2; + __le32 flags; + + /* The below fields are set via Multiple BSSID IE */ + u8 ref_bssid_addr[6]; + __le16 reserved0; + + /* The below fields are set via HE-capabilities IE */ + __le32 htc_flags; + + u8 frag_flags; + u8 frag_level; + u8 frag_max_num; + u8 frag_min_size; + + /* The below fields are set via PPE thresholds element */ + struct iwl_he_pkt_ext_v2 pkt_ext; + + /* The below fields are set via HE-Operation IE */ + u8 bss_color; + u8 htc_trig_based_pkt_ext; + __le16 frame_time_rts_th; + + /* Random access parameter set (i.e. RAPS) */ + u8 rand_alloc_ecwmin; + u8 rand_alloc_ecwmax; + __le16 puncture_mask; + + /* The below fields are set via MU EDCA parameter set element */ + struct iwl_he_backoff_conf trig_based_txf[AC_NUM]; + + u8 max_bssid_indicator; + u8 bssid_index; + u8 ema_ap; + u8 profile_periodicity; + u8 bssid_count; + u8 reserved4[3]; +} __packed; /* STA_CONTEXT_DOT11AX_API_S_VER_2 */ + /** * struct iwl_he_monitor_cmd - configure air sniffer for HE * @bssid: the BSSID to sniff for diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index 4949fcf85257..91bfde6d5367 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -419,6 +419,30 @@ struct iwl_tas_config_cmd_v3 { __le16 enable_tas_iec; } __packed; /* TAS_CONFIG_CMD_API_S_VER_3 */ +/** + * struct iwl_tas_config_cmd_v3 - configures the TAS + * @block_list_size: size of relevant field in block_list_array + * @block_list_array: list of countries where TAS must be disabled + * @override_tas_iec: indicates whether to override default value of IEC regulatory + * @enable_tas_iec: in case override_tas_iec is set - + * indicates whether IEC regulatory is enabled or disabled + * @usa_tas_uhb_allowed: if set, allow TAS UHB in the USA + * @reserved: reserved +*/ +struct iwl_tas_config_cmd_v4 { + __le32 block_list_size; + __le32 block_list_array[IWL_TAS_BLOCK_LIST_MAX]; + u8 override_tas_iec; + u8 enable_tas_iec; + u8 usa_tas_uhb_allowed; + u8 reserved; +} __packed; /* TAS_CONFIG_CMD_API_S_VER_4 */ + +union iwl_tas_config_cmd { + struct iwl_tas_config_cmd_v2 v2; + struct iwl_tas_config_cmd_v3 v3; + struct iwl_tas_config_cmd_v4 v4; +}; /** * enum iwl_lari_configs - bit masks for the various LARI config operations * @LARI_CONFIG_DISABLE_11AC_UKRAINE_MSK: disable 11ac in ukraine @@ -514,6 +538,32 @@ struct iwl_lari_config_change_cmd_v5 { __le32 chan_state_active_bitmap; } __packed; /* LARI_CHANGE_CONF_CMD_S_VER_5 */ +/** + * struct iwl_lari_config_change_cmd_v6 - change LARI configuration + * @config_bitmap: Bitmap of the config commands. Each bit will trigger a + * different predefined FW config operation. + * @oem_uhb_allow_bitmap: Bitmap of UHB enabled MCC sets. + * @oem_11ax_allow_bitmap: Bitmap of 11ax allowed MCCs. There are two bits + * per country, one to indicate whether to override and the other to + * indicate the value to use. + * @oem_unii4_allow_bitmap: Bitmap of unii4 allowed MCCs.There are two bits + * per country, one to indicate whether to override and the other to + * indicate allow/disallow unii4 channels. + * @chan_state_active_bitmap: Bitmap for overriding channel state to active. + * Each bit represents a country or region to activate, according to the BIOS + * definitions. + * @force_disable_channels_bitmap: Bitmap of disabled bands/channels. + * Each bit represents a set of channels in a specific band that should be disabled + */ +struct iwl_lari_config_change_cmd_v6 { + __le32 config_bitmap; + __le32 oem_uhb_allow_bitmap; + __le32 oem_11ax_allow_bitmap; + __le32 oem_unii4_allow_bitmap; + __le32 chan_state_active_bitmap; + __le32 force_disable_channels_bitmap; +} __packed; /* LARI_CHANGE_CONF_CMD_S_VER_6 */ + /** * struct iwl_pnvm_init_complete_ntfy - PNVM initialization complete * @status: PNVM image loading status diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h index c04f2521fcb3..b1b9c29859c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h @@ -166,14 +166,24 @@ struct iwl_dts_measurement_resp { /** * struct ct_kill_notif - CT-kill entry notification + * This structure represent both versions of this notification. * * @temperature: the current temperature in celsius - * @reserved: reserved + * @dts: only in v2: DTS that trigger the CT Kill bitmap: + * bit 0: ToP master + * bit 1: PA chain A master + * bit 2: PA chain B master + * bit 3: ToP slave + * bit 4: PA chain A slave + * bit 5: PA chain B slave) + * bits 6,7: reserved (set to 0) + * @scheme: only for v2: scheme that trigger the CT Kill (0-SW, 1-HW) */ struct ct_kill_notif { __le16 temperature; - __le16 reserved; -} __packed; /* GRP_PHY_CT_KILL_NTF */ + u8 dts; + u8 scheme; +} __packed; /* CT_KILL_NOTIFICATION_API_S_VER_1, CT_KILL_NOTIFICATION_API_S_VER_2 */ /** * enum ctdp_cmd_operation - CTDP command operations diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h index 81318208f2f6..f92cac1da764 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -340,7 +340,7 @@ struct iwl_dev_tx_power_cmd_v5 { } __packed; /* TX_REDUCED_POWER_API_S_VER_5 */ /** - * struct iwl_dev_tx_power_cmd_v5 - TX power reduction command version 5 + * struct iwl_dev_tx_power_cmd_v6 - TX power reduction command version 6 * @per_chain: per chain restrictions * @enable_ack_reduction: enable or disable close range ack TX power * reduction. @@ -360,6 +360,28 @@ struct iwl_dev_tx_power_cmd_v6 { __le32 timer_period; } __packed; /* TX_REDUCED_POWER_API_S_VER_6 */ +/** + * struct iwl_dev_tx_power_cmd_v7 - TX power reduction command version 7 + * @per_chain: per chain restrictions + * @enable_ack_reduction: enable or disable close range ack TX power + * reduction. + * @per_chain_restriction_changed: is per_chain_restriction has changed + * from last command. used if set_mode is + * IWL_TX_POWER_MODE_SET_SAR_TIMER. + * note: if not changed, the command is used for keep alive only. + * @reserved: reserved (padding) + * @timer_period: timer in milliseconds. if expires FW will change to default + * BIOS values. relevant if setMode is IWL_TX_POWER_MODE_SET_SAR_TIMER + * @flags: reduce power flags. + */ +struct iwl_dev_tx_power_cmd_v7 { + __le16 per_chain[IWL_NUM_CHAIN_TABLES_V2][IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V2]; + u8 enable_ack_reduction; + u8 per_chain_restriction_changed; + u8 reserved[2]; + __le32 timer_period; + __le32 flags; +} __packed; /* TX_REDUCED_POWER_API_S_VER_7 */ /** * struct iwl_dev_tx_power_cmd - TX power reduction command (multiversion) * @common: common part of the command @@ -375,6 +397,7 @@ struct iwl_dev_tx_power_cmd { struct iwl_dev_tx_power_cmd_v4 v4; struct iwl_dev_tx_power_cmd_v5 v5; struct iwl_dev_tx_power_cmd_v6 v6; + struct iwl_dev_tx_power_cmd_v7 v7; }; }; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rfi.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rfi.h index c678b9aa9b55..1a84a4081e7c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rfi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rfi.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2020 Intel Corporation + * Copyright (C) 2020-2021 Intel Corporation */ #ifndef __iwl_fw_api_rfi_h__ #define __iwl_fw_api_rfi_h__ @@ -57,4 +57,12 @@ struct iwl_rfi_freq_table_resp_cmd { __le32 status; } __packed; /* RFI_CONFIG_CMD_API_S_VER_1 */ +/** + * struct iwl_rfi_deactivate_notif - notifcation that FW disaled RFIm + * + * @reason: used only for a log message + */ +struct iwl_rfi_deactivate_notif { + __le32 reason; +} __packed; /* RFI_DEACTIVATE_NTF_S_VER_1 */ #endif /* __iwl_fw_api_rfi_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index 4a7723eb8c1d..687f804c46b7 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2020 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_rs_h__ @@ -133,7 +133,7 @@ enum IWL_TLC_MCS_PER_BW { }; /** - * struct tlc_config_cmd - TLC configuration + * struct iwl_tlc_config_cmd_v3 - TLC configuration * @sta_id: station id * @reserved1: reserved * @max_ch_width: max supported channel width from @enum iwl_tlc_mng_cfg_cw @@ -168,7 +168,7 @@ struct iwl_tlc_config_cmd_v3 { } __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_3 */ /** - * struct tlc_config_cmd - TLC configuration + * struct iwl_tlc_config_cmd_v4 - TLC configuration * @sta_id: station id * @reserved1: reserved * @max_ch_width: max supported channel width from &enum iwl_tlc_mng_cfg_cw diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h index e73cc7380a26..ecc6706f66ed 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2016-2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_tx_h__ @@ -296,8 +296,7 @@ struct iwl_tx_cmd_gen2 { * @dram_info: FW internal DRAM storage * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is * cleared. Combination of RATE_MCS_* - * @ttl: time to live - packet lifetime limit. The FW should drop if - * passed. + * @reserved: reserved * @hdr: 802.11 header */ struct iwl_tx_cmd_gen3 { @@ -306,7 +305,7 @@ struct iwl_tx_cmd_gen3 { __le32 offload_assist; struct iwl_dram_sec_info dram_info; __le32 rate_n_flags; - __le64 ttl; + u8 reserved[8]; struct ieee80211_hdr hdr[]; } __packed; /* TX_CMD_API_S_VER_8, TX_CMD_API_S_VER_10 */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h index 8b3a00df41da..e018946310d1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2019-2020 Intel Corporation + * Copyright (C) 2005-2014, 2019-2021 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -76,6 +76,8 @@ enum iwl_tx_queue_cfg_actions { TX_QUEUE_CFG_TFD_SHORT_FORMAT = BIT(1), }; +#define IWL_DEFAULT_QUEUE_SIZE_EHT (1024 * 4) +#define IWL_DEFAULT_QUEUE_SIZE_HE 1024 #define IWL_DEFAULT_QUEUE_SIZE 256 #define IWL_MGMT_QUEUE_SIZE 16 #define IWL_CMD_QUEUE_SIZE 32 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 7ad9cee925da..abf49022edbe 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -12,7 +12,7 @@ #include "iwl-io.h" #include "iwl-prph.h" #include "iwl-csr.h" - +#include "iwl-fh.h" /** * struct iwl_fw_dump_ptrs - set of pointers needed for the fw-error-dump * @@ -303,9 +303,6 @@ static void iwl_fw_dump_txf(struct iwl_fw_runtime *fwrt, iwl_trans_release_nic_access(fwrt->trans); } -#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ -#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ - struct iwl_prph_range { u32 start, end; }; @@ -1027,7 +1024,7 @@ struct iwl_dump_ini_region_data { static int iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1052,7 +1049,7 @@ iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_prph_phy_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1102,7 +1099,7 @@ iwl_dump_ini_prph_phy_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_csr_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1121,7 +1118,7 @@ static int iwl_dump_ini_csr_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_config_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_trans *trans = fwrt->trans; struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; @@ -1153,7 +1150,7 @@ static int iwl_dump_ini_config_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1175,7 +1172,7 @@ static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt, } static int _iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct page *page = fwrt->fw_paging_db[idx].fw_paging_block; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1195,7 +1192,7 @@ static int _iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_error_dump_range *range; u32 page_size; @@ -1204,7 +1201,7 @@ static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, idx++; if (!fwrt->trans->trans_cfg->gen2) - return _iwl_dump_ini_paging_iter(fwrt, range_ptr, idx); + return _iwl_dump_ini_paging_iter(fwrt, range_ptr, range_len, idx); range = range_ptr; page_size = fwrt->trans->init_dram.paging[idx].size; @@ -1220,7 +1217,7 @@ static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_mon_dram_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1239,7 +1236,7 @@ iwl_dump_ini_mon_dram_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_mon_smem_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1307,7 +1304,7 @@ static bool iwl_ini_txf_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1442,7 +1439,7 @@ static void iwl_ini_get_rxf_data(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1509,7 +1506,7 @@ static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_err_table_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_region_err_table *err_table = ®->err_table; @@ -1528,7 +1525,7 @@ iwl_dump_ini_err_table_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_special_mem_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_region_special_device_memory *special_mem = @@ -1549,7 +1546,7 @@ iwl_dump_ini_special_mem_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1561,8 +1558,6 @@ iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt, return -EBUSY; range->range_data_size = reg->dev_addr.size; - iwl_write_prph_no_grab(fwrt->trans, DBGI_SRAM_TARGET_ACCESS_CFG, - DBGI_SRAM_TARGET_ACCESS_CFG_RESET_ADDRESS_MSK); for (i = 0; i < (le32_to_cpu(reg->dev_addr.size) / 4); i++) { prph_data = iwl_read_prph_no_grab(fwrt->trans, (i % 2) ? DBGI_SRAM_TARGET_ACCESS_RDATA_MSB : @@ -1579,7 +1574,7 @@ iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_fw_pkt_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, int idx) + void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_error_dump_range *range = range_ptr; struct iwl_rx_packet *pkt = reg_data->dump_data->fw_pkt; @@ -1598,10 +1593,37 @@ static int iwl_dump_ini_fw_pkt_iter(struct iwl_fw_runtime *fwrt, return sizeof(*range) + le32_to_cpu(range->range_data_size); } +static int iwl_dump_ini_imr_iter(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data, + void *range_ptr, u32 range_len, int idx) +{ + /* read the IMR memory and DMA it to SRAM */ + struct iwl_fw_ini_error_dump_range *range = range_ptr; + u64 imr_curr_addr = fwrt->trans->dbg.imr_data.imr_curr_addr; + u32 imr_rem_bytes = fwrt->trans->dbg.imr_data.imr2sram_remainbyte; + u32 sram_addr = fwrt->trans->dbg.imr_data.sram_addr; + u32 sram_size = fwrt->trans->dbg.imr_data.sram_size; + u32 size_to_dump = (imr_rem_bytes > sram_size) ? sram_size : imr_rem_bytes; + + range->range_data_size = cpu_to_le32(size_to_dump); + if (iwl_trans_write_imr_mem(fwrt->trans, sram_addr, + imr_curr_addr, size_to_dump)) { + IWL_ERR(fwrt, "WRT_DEBUG: IMR Memory transfer failed\n"); + return -1; + } + + fwrt->trans->dbg.imr_data.imr_curr_addr = imr_curr_addr + size_to_dump; + fwrt->trans->dbg.imr_data.imr2sram_remainbyte -= size_to_dump; + + iwl_trans_read_mem_bytes(fwrt->trans, sram_addr, range->data, + size_to_dump); + return sizeof(*range) + le32_to_cpu(range->range_data_size); +} + static void * iwl_dump_ini_mem_fill_header(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *data) + void *data, u32 data_len) { struct iwl_fw_ini_error_dump *dump = data; @@ -1677,7 +1699,7 @@ iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, static void * iwl_dump_ini_mon_dram_fill_header(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *data) + void *data, u32 data_len) { struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; @@ -1688,7 +1710,7 @@ iwl_dump_ini_mon_dram_fill_header(struct iwl_fw_runtime *fwrt, static void * iwl_dump_ini_mon_smem_fill_header(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *data) + void *data, u32 data_len) { struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; @@ -1696,10 +1718,21 @@ iwl_dump_ini_mon_smem_fill_header(struct iwl_fw_runtime *fwrt, &fwrt->trans->cfg->mon_smem_regs); } +static void * +iwl_dump_ini_mon_dbgi_fill_header(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data, + void *data, u32 data_len) +{ + struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; + + return iwl_dump_ini_mon_fill_header(fwrt, reg_data, mon_dump, + &fwrt->trans->cfg->mon_dbgi_regs); +} + static void * iwl_dump_ini_err_table_fill_header(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *data) + void *data, u32 data_len) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_err_table_dump *dump = data; @@ -1713,7 +1746,7 @@ iwl_dump_ini_err_table_fill_header(struct iwl_fw_runtime *fwrt, static void * iwl_dump_ini_special_mem_fill_header(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *data) + void *data, u32 data_len) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_special_device_memory *dump = data; @@ -1725,6 +1758,18 @@ iwl_dump_ini_special_mem_fill_header(struct iwl_fw_runtime *fwrt, return dump->data; } +static void * +iwl_dump_ini_imr_fill_header(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data, + void *data, u32 data_len) +{ + struct iwl_fw_ini_error_dump *dump = data; + + dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER); + + return dump->data; +} + static u32 iwl_dump_ini_mem_ranges(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data) { @@ -1784,6 +1829,26 @@ static u32 iwl_dump_ini_single_range(struct iwl_fw_runtime *fwrt, return 1; } +static u32 iwl_dump_ini_imr_ranges(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data) +{ + /* range is total number of pages need to copied from + *IMR memory to SRAM and later from SRAM to DRAM + */ + u32 imr_enable = fwrt->trans->dbg.imr_data.imr_enable; + u32 imr_size = fwrt->trans->dbg.imr_data.imr_size; + u32 sram_size = fwrt->trans->dbg.imr_data.sram_size; + + if (imr_enable == 0 || imr_size == 0 || sram_size == 0) { + IWL_DEBUG_INFO(fwrt, + "WRT: Invalid imr data enable: %d, imr_size: %d, sram_size: %d\n", + imr_enable, imr_size, sram_size); + return 0; + } + + return((imr_size % sram_size) ? (imr_size / sram_size + 1) : (imr_size / sram_size)); +} + static u32 iwl_dump_ini_mem_get_size(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data) { @@ -1861,6 +1926,20 @@ iwl_dump_ini_mon_smem_get_size(struct iwl_fw_runtime *fwrt, return size; } +static u32 iwl_dump_ini_mon_dbgi_get_size(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data) +{ + struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; + u32 size = le32_to_cpu(reg->dev_addr.size); + u32 ranges = iwl_dump_ini_mem_ranges(fwrt, reg_data); + + if (!size || !ranges) + return 0; + + return sizeof(struct iwl_fw_ini_monitor_dump) + ranges * + (size + sizeof(struct iwl_fw_ini_error_dump_range)); +} + static u32 iwl_dump_ini_txf_get_size(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data) { @@ -1948,6 +2027,33 @@ iwl_dump_ini_fw_pkt_get_size(struct iwl_fw_runtime *fwrt, return size; } +static u32 +iwl_dump_ini_imr_get_size(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data) +{ + u32 size = 0; + u32 ranges = 0; + u32 imr_enable = fwrt->trans->dbg.imr_data.imr_enable; + u32 imr_size = fwrt->trans->dbg.imr_data.imr_size; + u32 sram_size = fwrt->trans->dbg.imr_data.sram_size; + + if (imr_enable == 0 || imr_size == 0 || sram_size == 0) { + IWL_DEBUG_INFO(fwrt, + "WRT: Invalid imr data enable: %d, imr_size: %d, sram_size: %d\n", + imr_enable, imr_size, sram_size); + return size; + } + size = imr_size; + ranges = iwl_dump_ini_imr_ranges(fwrt, reg_data); + if (!size && !ranges) { + IWL_ERR(fwrt, "WRT: imr_size :=%d, ranges :=%d\n", size, ranges); + return 0; + } + size += sizeof(struct iwl_fw_ini_error_dump) + + ranges * sizeof(struct iwl_fw_ini_error_dump_range); + return size; +} + /** * struct iwl_dump_ini_mem_ops - ini memory dump operations * @get_num_of_ranges: returns the number of memory ranges in the region. @@ -1964,10 +2070,10 @@ struct iwl_dump_ini_mem_ops { struct iwl_dump_ini_region_data *reg_data); void *(*fill_mem_hdr)(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *data); + void *data, u32 data_len); int (*fill_range)(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range, int idx); + void *range, u32 range_len, int idx); }; /** @@ -1990,24 +2096,53 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, struct iwl_fw_ini_error_dump_data *tlv; struct iwl_fw_ini_error_dump_header *header; u32 type = reg->type; - u32 id = le32_to_cpu(reg->id); + u32 id = le32_get_bits(reg->id, IWL_FW_INI_REGION_ID_MASK); u32 num_of_ranges, i, size; - void *range; + u8 *range; + u32 free_size; + u64 header_size; + u32 dump_policy = IWL_FW_INI_DUMP_VERBOSE; - /* - * The higher part of the ID from 2 is irrelevant for - * us, so mask it out. - */ - if (le32_to_cpu(reg->hdr.version) >= 2) - id &= IWL_FW_INI_REGION_V2_MASK; + IWL_DEBUG_FW(fwrt, "WRT: Collecting region: dump type=%d, id=%d, type=%d\n", + dump_policy, id, type); + + if (le32_to_cpu(reg->hdr.version) >= 2) { + u32 dp = le32_get_bits(reg->id, + IWL_FW_INI_REGION_DUMP_POLICY_MASK); + + if (dump_policy == IWL_FW_INI_DUMP_VERBOSE && + !(dp & IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT)) { + IWL_DEBUG_FW(fwrt, + "WRT: no dump - type %d and policy mismatch=%d\n", + dump_policy, dp); + return 0; + } else if (dump_policy == IWL_FW_INI_DUMP_MEDIUM && + !(dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB)) { + IWL_DEBUG_FW(fwrt, + "WRT: no dump - type %d and policy mismatch=%d\n", + dump_policy, dp); + return 0; + } else if (dump_policy == IWL_FW_INI_DUMP_BRIEF && + !(dp & IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB)) { + IWL_DEBUG_FW(fwrt, + "WRT: no dump - type %d and policy mismatch=%d\n", + dump_policy, dp); + return 0; + } + } if (!ops->get_num_of_ranges || !ops->get_size || !ops->fill_mem_hdr || - !ops->fill_range) + !ops->fill_range) { + IWL_DEBUG_FW(fwrt, "WRT: no ops for collecting data\n"); return 0; + } size = ops->get_size(fwrt, reg_data); - if (!size) + + if (size < sizeof(*header)) { + IWL_DEBUG_FW(fwrt, "WRT: size didn't include space for header\n"); return 0; + } entry = vzalloc(sizeof(*entry) + sizeof(*tlv) + size); if (!entry) @@ -2022,9 +2157,6 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, tlv->reserved = reg->reserved; tlv->len = cpu_to_le32(size); - IWL_DEBUG_FW(fwrt, "WRT: Collecting region: id=%d, type=%d\n", id, - type); - num_of_ranges = ops->get_num_of_ranges(fwrt, reg_data); header = (void *)tlv->data; @@ -2033,7 +2165,8 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, header->name_len = cpu_to_le32(IWL_FW_INI_MAX_NAME); memcpy(header->name, reg->name, IWL_FW_INI_MAX_NAME); - range = ops->fill_mem_hdr(fwrt, reg_data, header); + free_size = size; + range = ops->fill_mem_hdr(fwrt, reg_data, header, free_size); if (!range) { IWL_ERR(fwrt, "WRT: Failed to fill region header: id=%d, type=%d\n", @@ -2041,8 +2174,21 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, goto out_err; } + header_size = range - (u8 *)header; + + if (WARN(header_size > free_size, + "header size %llu > free_size %d", + header_size, free_size)) { + IWL_ERR(fwrt, + "WRT: fill_mem_hdr used more than given free_size\n"); + goto out_err; + } + + free_size -= header_size; + for (i = 0; i < num_of_ranges; i++) { - int range_size = ops->fill_range(fwrt, reg_data, range, i); + int range_size = ops->fill_range(fwrt, reg_data, range, + free_size, i); if (range_size < 0) { IWL_ERR(fwrt, @@ -2050,6 +2196,15 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, id, type); goto out_err; } + + if (WARN(range_size > free_size, "range_size %d > free_size %d", + range_size, free_size)) { + IWL_ERR(fwrt, + "WRT: fill_raged used more than given free_size\n"); + goto out_err; + } + + free_size -= range_size; range = range + range_size; } @@ -2240,7 +2395,12 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_csr_iter, }, - [IWL_FW_INI_REGION_DRAM_IMR] = {}, + [IWL_FW_INI_REGION_DRAM_IMR] = { + .get_num_of_ranges = iwl_dump_ini_imr_ranges, + .get_size = iwl_dump_ini_imr_get_size, + .fill_mem_hdr = iwl_dump_ini_imr_fill_header, + .fill_range = iwl_dump_ini_imr_iter, + }, [IWL_FW_INI_REGION_PCI_IOSF_CONFIG] = { .get_num_of_ranges = iwl_dump_ini_mem_ranges, .get_size = iwl_dump_ini_mem_get_size, @@ -2255,8 +2415,8 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { }, [IWL_FW_INI_REGION_DBGI_SRAM] = { .get_num_of_ranges = iwl_dump_ini_mem_ranges, - .get_size = iwl_dump_ini_mem_get_size, - .fill_mem_hdr = iwl_dump_ini_mem_fill_header, + .get_size = iwl_dump_ini_mon_dbgi_get_size, + .fill_mem_hdr = iwl_dump_ini_mon_dbgi_fill_header, .fill_range = iwl_dump_ini_dbgi_sram_iter, }, }; @@ -2270,6 +2430,9 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data reg_data = { .dump_data = dump_data, }; + struct iwl_dump_ini_region_data imr_reg_data = { + .dump_data = dump_data, + }; int i; u32 size = 0; u64 regions_mask = le64_to_cpu(trigger->regions_mask) & @@ -2305,10 +2468,32 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, tp_id); continue; } + /* + * DRAM_IMR can be collected only for FW/HW error timepoint + * when fw is not alive. In addition, it must be collected + * lastly as it overwrites SRAM that can possibly contain + * debug data which also need to be collected. + */ + if (reg_type == IWL_FW_INI_REGION_DRAM_IMR) { + if (tp_id == IWL_FW_INI_TIME_POINT_FW_ASSERT || + tp_id == IWL_FW_INI_TIME_POINT_FW_HW_ERROR) + imr_reg_data.reg_tlv = fwrt->trans->dbg.active_regions[i]; + else + IWL_INFO(fwrt, + "WRT: trying to collect DRAM_IMR at time point: %d, skipping\n", + tp_id); + /* continue to next region */ + continue; + } + size += iwl_dump_ini_mem(fwrt, list, ®_data, &iwl_dump_ini_region_ops[reg_type]); } + /* collect DRAM_IMR region in the last */ + if (imr_reg_data.reg_tlv) + size += iwl_dump_ini_mem(fwrt, list, ®_data, + &iwl_dump_ini_region_ops[IWL_FW_INI_REGION_DRAM_IMR]); if (size) size += iwl_dump_ini_info(fwrt, trigger, list); @@ -2444,7 +2629,7 @@ static void iwl_fw_error_dump_data_free(struct iwl_fwrt_dump_data *dump_data) static void iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt, struct iwl_fwrt_dump_data *dump_data) { - struct list_head dump_list = LIST_HEAD_INIT(dump_list); + LIST_HEAD(dump_list); struct scatterlist *sg_dump_data; u32 file_len = iwl_dump_ini_file_gen(fwrt, dump_data, &dump_list); @@ -2589,7 +2774,7 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt, delay = le32_to_cpu(trigger->stop_delay) * USEC_PER_MSEC; } - desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); + desc = kzalloc(struct_size(desc, trig_desc.data, len), GFP_ATOMIC); if (!desc) return -ENOMEM; @@ -2685,6 +2870,28 @@ int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 conf_id) } IWL_EXPORT_SYMBOL(iwl_fw_start_dbg_conf); +void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt, + u32 timepoint, + u32 timepoint_data) +{ + struct iwl_dbg_dump_complete_cmd hcmd_data; + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(DEBUG_GROUP, FW_DUMP_COMPLETE_CMD), + .data[0] = &hcmd_data, + .len[0] = sizeof(hcmd_data), + }; + + if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) + return; + + if (fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_DUMP_COMPLETE_SUPPORT)) { + hcmd_data.tp = cpu_to_le32(timepoint); + hcmd_data.tp_data = cpu_to_le32(timepoint_data); + iwl_trans_send_cmd(fwrt->trans, &hcmd); + } +} + /* this function assumes dump_start was called beforehand and dump_end will be * called afterwards */ @@ -2693,10 +2900,16 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx) struct iwl_fw_dbg_params params = {0}; struct iwl_fwrt_dump_data *dump_data = &fwrt->dump.wks[wk_idx].dump_data; - + u32 policy; + u32 time_point; if (!test_bit(wk_idx, &fwrt->dump.active_wks)) return; + if (!dump_data->trig) { + IWL_ERR(fwrt, "dump trigger data is not set\n"); + goto out; + } + if (!test_bit(STATUS_DEVICE_ENABLED, &fwrt->trans->status)) { IWL_ERR(fwrt, "Device is not enabled - cannot dump error\n"); goto out; @@ -2719,6 +2932,13 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx) iwl_fw_dbg_stop_restart_recording(fwrt, ¶ms, false); + policy = le32_to_cpu(dump_data->trig->apply_policy); + time_point = le32_to_cpu(dump_data->trig->time_point); + + if (policy & IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD) { + IWL_DEBUG_FW_INFO(fwrt, "WRT: sending dump complete\n"); + iwl_send_dbg_dump_complete_cmd(fwrt, time_point, 0); + } if (fwrt->trans->dbg.last_tp_resetfw == IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) iwl_force_nmi(fwrt->trans); @@ -2777,10 +2997,10 @@ int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt, "WRT: Collecting data: ini trigger %d fired (delay=%dms).\n", tp_id, (u32)(delay / USEC_PER_MSEC)); - schedule_delayed_work(&fwrt->dump.wks[idx].wk, usecs_to_jiffies(delay)); - if (sync) iwl_fw_dbg_collect_sync(fwrt, idx); + else + schedule_delayed_work(&fwrt->dump.wks[idx].wk, usecs_to_jiffies(delay)); return 0; } @@ -2795,9 +3015,8 @@ void iwl_fw_error_dump_wk(struct work_struct *work) /* assumes the op mode mutex is locked in dump_start since * iwl_fw_dbg_collect_sync can't run in parallel */ - if (fwrt->ops && fwrt->ops->dump_start && - fwrt->ops->dump_start(fwrt->ops_ctx)) - return; + if (fwrt->ops && fwrt->ops->dump_start) + fwrt->ops->dump_start(fwrt->ops_ctx); iwl_fw_dbg_collect_sync(fwrt, wks->idx); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h index 8c3c890066b0..be7806407de8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2019, 2021 Intel Corporation + * Copyright (C) 2005-2014, 2018-2019, 2021-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -324,4 +324,7 @@ static inline void iwl_fwrt_update_fw_versions(struct iwl_fw_runtime *fwrt, } void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt); +void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt, + u32 timepoint, + u32 timepoint_data); #endif /* __iwl_fw_dbg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c index a152ce306475..43e997283db0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c @@ -150,7 +150,7 @@ static int iwl_dbgfs_enabled_severities_write(struct iwl_fw_runtime *fwrt, { struct iwl_dbg_host_event_cfg_cmd event_cfg; struct iwl_host_cmd hcmd = { - .id = iwl_cmd_id(HOST_EVENT_CFG, DEBUG_GROUP, 0), + .id = WIDE_ID(DEBUG_GROUP, HOST_EVENT_CFG), .flags = CMD_ASYNC, .data[0] = &event_cfg, .len[0] = sizeof(event_cfg), @@ -358,7 +358,7 @@ static int iwl_dbgfs_fw_info_seq_show(struct seq_file *seq, void *v) ver = &fw->ucode_capa.cmd_versions[state->pos]; - cmd_id = iwl_cmd_id(ver->cmd, ver->group, 0); + cmd_id = WIDE_ID(ver->group, ver->cmd); seq_printf(seq, " 0x%04x:\n", cmd_id); seq_printf(seq, " name: %s\n", diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index efc6540d31af..5679a78758be 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -119,7 +119,7 @@ enum iwl_ucode_tlv_type { struct iwl_ucode_tlv { __le32 type; /* see above */ __le32 length; /* not including type/length fields */ - u8 data[0]; + u8 data[]; }; #define IWL_TLV_UCODE_MAGIC 0x0a4c5749 @@ -310,7 +310,6 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching * @IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG: Consolidated D3-D0 image * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command - * @IWL_UCODE_TLV_CAPA_DC2DC_SUPPORT: supports DC2DC Command * @IWL_UCODE_TLV_CAPA_CSUM_SUPPORT: supports TCP Checksum Offload * @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics * @IWL_UCODE_TLV_CAPA_P2P_SCM_UAPSD: supports U-APSD on p2p interface when it @@ -368,6 +367,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * reset flow * @IWL_UCODE_TLV_CAPA_PASSIVE_6GHZ_SCAN: Support for passive scan on 6GHz PSC * channels even when these are not enabled. + * @IWL_UCODE_TLV_CAPA_DUMP_COMPLETE_SUPPORT: Support for indicating dump collection + * complete to FW. * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -386,7 +387,6 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = (__force iwl_ucode_tlv_capa_t)13, IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG = (__force iwl_ucode_tlv_capa_t)17, IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = (__force iwl_ucode_tlv_capa_t)18, - IWL_UCODE_TLV_CAPA_DC2DC_CONFIG_SUPPORT = (__force iwl_ucode_tlv_capa_t)19, IWL_UCODE_TLV_CAPA_CSUM_SUPPORT = (__force iwl_ucode_tlv_capa_t)21, IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS = (__force iwl_ucode_tlv_capa_t)22, IWL_UCODE_TLV_CAPA_P2P_SCM_UAPSD = (__force iwl_ucode_tlv_capa_t)26, @@ -419,6 +419,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_BROADCAST_TWT = (__force iwl_ucode_tlv_capa_t)60, IWL_UCODE_TLV_CAPA_COEX_HIGH_PRIO = (__force iwl_ucode_tlv_capa_t)61, IWL_UCODE_TLV_CAPA_RFIM_SUPPORT = (__force iwl_ucode_tlv_capa_t)62, + IWL_UCODE_TLV_CAPA_BAID_ML_SUPPORT = (__force iwl_ucode_tlv_capa_t)63, /* set 2 */ IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64, @@ -453,6 +454,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_BIGTK_SUPPORT = (__force iwl_ucode_tlv_capa_t)100, IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT = (__force iwl_ucode_tlv_capa_t)104, + IWL_UCODE_TLV_CAPA_DUMP_COMPLETE_SUPPORT = (__force iwl_ucode_tlv_capa_t)105, #ifdef __CHECKER__ /* sparse says it cannot increment the previous enum member */ @@ -514,34 +516,6 @@ enum iwl_fw_phy_cfg { FW_PHY_CFG_SHARED_CLK = BIT(31), }; -#define IWL_UCODE_MAX_CS 1 - -/** - * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW. - * @cipher: a cipher suite selector - * @flags: cipher scheme flags (currently reserved for a future use) - * @hdr_len: a size of MPDU security header - * @pn_len: a size of PN - * @pn_off: an offset of pn from the beginning of the security header - * @key_idx_off: an offset of key index byte in the security header - * @key_idx_mask: a bit mask of key_idx bits - * @key_idx_shift: bit shift needed to get key_idx - * @mic_len: mic length in bytes - * @hw_cipher: a HW cipher index used in host commands - */ -struct iwl_fw_cipher_scheme { - __le32 cipher; - u8 flags; - u8 hdr_len; - u8 pn_len; - u8 pn_off; - u8 key_idx_off; - u8 key_idx_mask; - u8 key_idx_shift; - u8 mic_len; - u8 hw_cipher; -} __packed; - enum iwl_fw_dbg_reg_operator { CSR_ASSIGN, CSR_SETBIT, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.c b/drivers/net/wireless/intel/iwlwifi/fw/img.c index 530674a35eeb..b7deca05a953 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/img.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/img.c @@ -2,13 +2,16 @@ /* * Copyright(c) 2019 - 2021 Intel Corporation */ - +#include #include "img.h" -u8 iwl_fw_lookup_cmd_ver(const struct iwl_fw *fw, u8 grp, u8 cmd, u8 def) +u8 iwl_fw_lookup_cmd_ver(const struct iwl_fw *fw, u32 cmd_id, u8 def) { const struct iwl_fw_cmd_version *entry; unsigned int i; + /* prior to LONG_GROUP, we never used this CMD version API */ + u8 grp = iwl_cmd_groupid(cmd_id) ?: LONG_GROUP; + u8 cmd = iwl_cmd_opcode(cmd_id); if (!fw->ucode_capa.cmd_versions || !fw->ucode_capa.n_cmd_versions) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h index fa7b1780064c..f878ac508801 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/img.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h @@ -133,16 +133,6 @@ struct iwl_fw_paging { u32 fw_offs; }; -/** - * struct iwl_fw_cscheme_list - a cipher scheme list - * @size: a number of entries - * @cs: cipher scheme entries - */ -struct iwl_fw_cscheme_list { - u8 size; - struct iwl_fw_cipher_scheme cs[]; -} __packed; - /** * enum iwl_fw_type - iwlwifi firmware type * @IWL_FW_DVM: DVM firmware @@ -197,7 +187,6 @@ struct iwl_dump_exclude { * @inst_evtlog_size: event log size for runtime ucode. * @inst_errlog_ptr: error log offfset for runtime ucode. * @type: firmware type (&enum iwl_fw_type) - * @cipher_scheme: optional external cipher scheme. * @human_readable: human readable version * we get the ALIVE from the uCode * @phy_integration_ver: PHY integration version string @@ -228,7 +217,6 @@ struct iwl_fw { enum iwl_fw_type type; - struct iwl_fw_cipher_scheme cs[IWL_UCODE_MAX_CS]; u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; struct iwl_fw_dbg dbg; @@ -275,7 +263,7 @@ iwl_get_ucode_image(const struct iwl_fw *fw, enum iwl_ucode_type ucode_type) return &fw->img[ucode_type]; } -u8 iwl_fw_lookup_cmd_ver(const struct iwl_fw *fw, u8 grp, u8 cmd, u8 def); +u8 iwl_fw_lookup_cmd_ver(const struct iwl_fw *fw, u32 cmd_id, u8 def); u8 iwl_fw_lookup_notif_ver(const struct iwl_fw *fw, u8 grp, u8 cmd, u8 def); const char *iwl_fw_lookup_assert_desc(u32 num); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/init.c b/drivers/net/wireless/intel/iwlwifi/fw/init.c index 139ece879fab..135bd48bfe9f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/init.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/init.c @@ -58,7 +58,7 @@ int iwl_set_soc_latency(struct iwl_fw_runtime *fwrt) { struct iwl_soc_configuration_cmd cmd = {}; struct iwl_host_cmd hcmd = { - .id = iwl_cmd_id(SOC_CONFIGURATION_CMD, SYSTEM_GROUP, 0), + .id = WIDE_ID(SYSTEM_GROUP, SOC_CONFIGURATION_CMD), .data[0] = &cmd, .len[0] = sizeof(cmd), }; @@ -87,8 +87,7 @@ int iwl_set_soc_latency(struct iwl_fw_runtime *fwrt) cmd.flags |= le32_encode_bits(fwrt->trans->trans_cfg->ltr_delay, SOC_FLAGS_LTR_APPLY_DELAY_MASK); - if (iwl_fw_lookup_cmd_ver(fwrt->fw, IWL_ALWAYS_LONG_GROUP, - SCAN_REQ_UMAC, + if (iwl_fw_lookup_cmd_ver(fwrt->fw, SCAN_REQ_UMAC, IWL_FW_CMD_VER_UNKNOWN) >= 2 && fwrt->trans->trans_cfg->low_latency_xtal) cmd.flags |= cpu_to_le32(SOC_CONFIG_CMD_FLAGS_LOW_LATENCY); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/paging.c b/drivers/net/wireless/intel/iwlwifi/fw/paging.c index 58ca3849d1f3..945bc4160cc9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/paging.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/paging.c @@ -197,7 +197,7 @@ static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt, } memcpy(page_address(block->fw_paging_block), - image->sec[sec_idx].data + offset, len); + (const u8 *)image->sec[sec_idx].data + offset, len); block->fw_offs = image->sec[sec_idx].offset + offset; dma_sync_single_for_device(fwrt->trans->dev, block->fw_paging_phys, @@ -243,7 +243,7 @@ static int iwl_send_paging_cmd(struct iwl_fw_runtime *fwrt, .block_num = cpu_to_le32(fwrt->num_of_paging_blk), }; struct iwl_host_cmd hcmd = { - .id = iwl_cmd_id(FW_PAGING_BLOCK_CMD, IWL_ALWAYS_LONG_GROUP, 0), + .id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, FW_PAGING_BLOCK_CMD), .len = { sizeof(paging_cmd), }, .data = { &paging_cmd, }, }; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index 7d4aa398729a..b6d3ac6ed440 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -33,7 +33,7 @@ static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait, static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data, size_t len) { - struct iwl_ucode_tlv *tlv; + const struct iwl_ucode_tlv *tlv; u32 sha1 = 0; u16 mac_type = 0, rf_id = 0; u8 *pnvm_data = NULL, *tmp; @@ -47,7 +47,7 @@ static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data, u32 tlv_len, tlv_type; len -= sizeof(*tlv); - tlv = (void *)data; + tlv = (const void *)data; tlv_len = le32_to_cpu(tlv->length); tlv_type = le32_to_cpu(tlv->type); @@ -70,7 +70,7 @@ static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data, break; } - sha1 = le32_to_cpup((__le32 *)data); + sha1 = le32_to_cpup((const __le32 *)data); IWL_DEBUG_FW(trans, "Got IWL_UCODE_TLV_PNVM_VERSION %0x\n", @@ -87,8 +87,8 @@ static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data, if (hw_match) break; - mac_type = le16_to_cpup((__le16 *)data); - rf_id = le16_to_cpup((__le16 *)(data + sizeof(__le16))); + mac_type = le16_to_cpup((const __le16 *)data); + rf_id = le16_to_cpup((const __le16 *)(data + sizeof(__le16))); IWL_DEBUG_FW(trans, "Got IWL_UCODE_TLV_HW_TYPE mac_type 0x%0x rf_id 0x%0x\n", @@ -99,7 +99,7 @@ static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data, hw_match = true; break; case IWL_UCODE_TLV_SEC_RT: { - struct iwl_pnvm_section *section = (void *)data; + const struct iwl_pnvm_section *section = (const void *)data; u32 data_len = tlv_len - sizeof(*section); IWL_DEBUG_FW(trans, @@ -107,7 +107,7 @@ static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data, tlv_len); /* TODO: remove, this is a deprecated separator */ - if (le32_to_cpup((__le32 *)data) == 0xddddeeee) { + if (le32_to_cpup((const __le32 *)data) == 0xddddeeee) { IWL_DEBUG_FW(trans, "Ignoring separator.\n"); break; } @@ -173,7 +173,7 @@ static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data, static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, size_t len) { - struct iwl_ucode_tlv *tlv; + const struct iwl_ucode_tlv *tlv; IWL_DEBUG_FW(trans, "Parsing PNVM file\n"); @@ -181,7 +181,7 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, u32 tlv_len, tlv_type; len -= sizeof(*tlv); - tlv = (void *)data; + tlv = (const void *)data; tlv_len = le32_to_cpu(tlv->length); tlv_type = le32_to_cpu(tlv->type); @@ -193,8 +193,8 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, } if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) { - struct iwl_sku_id *sku_id = - (void *)(data + sizeof(*tlv)); + const struct iwl_sku_id *sku_id = + (const void *)(data + sizeof(*tlv)); IWL_DEBUG_FW(trans, "Got IWL_UCODE_TLV_PNVM_SKU len %d\n", diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 3cb0ddbe3ab2..d3cb1ae68a96 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2020 Intel Corporation + * Copyright (C) 2018-2022 Intel Corporation */ #ifndef __iwl_fw_runtime_h__ #define __iwl_fw_runtime_h__ @@ -16,7 +16,7 @@ #include "fw/acpi.h" struct iwl_fw_runtime_ops { - int (*dump_start)(void *ctx); + void (*dump_start)(void *ctx); void (*dump_end)(void *ctx); bool (*fw_running)(void *ctx); int (*send_hcmd)(void *ctx, struct iwl_host_cmd *host_cmd); @@ -163,6 +163,7 @@ struct iwl_fw_runtime { u32 ppag_ver; struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; + u8 reduced_power_flags; #endif }; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/smem.c b/drivers/net/wireless/intel/iwlwifi/fw/smem.c index f2f1789f470d..3f1272014daf 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/smem.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/smem.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2020 Intel Corporation + * Copyright (C) 2012-2014, 2018-2021 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -89,7 +89,7 @@ void iwl_get_shared_mem_conf(struct iwl_fw_runtime *fwrt) if (fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) - cmd.id = iwl_cmd_id(SHARED_MEM_CFG_CMD, SYSTEM_GROUP, 0); + cmd.id = WIDE_ID(SYSTEM_GROUP, SHARED_MEM_CFG_CMD); else cmd.id = SHARED_MEM_CFG; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index bd82c24811c8..23b1d689ba7b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -69,7 +69,7 @@ void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans, const u8 *data, size_t len) { - struct iwl_ucode_tlv *tlv; + const struct iwl_ucode_tlv *tlv; u8 *reduce_power_data = NULL, *tmp; u32 size = 0; @@ -79,7 +79,7 @@ static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans, u32 tlv_len, tlv_type; len -= sizeof(*tlv); - tlv = (void *)data; + tlv = (const void *)data; tlv_len = le32_to_cpu(tlv->length); tlv_type = le32_to_cpu(tlv->type); @@ -154,7 +154,7 @@ static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans, static void *iwl_uefi_reduce_power_parse(struct iwl_trans *trans, const u8 *data, size_t len) { - struct iwl_ucode_tlv *tlv; + const struct iwl_ucode_tlv *tlv; void *sec_data; IWL_DEBUG_FW(trans, "Parsing REDUCE_POWER data\n"); @@ -163,7 +163,7 @@ static void *iwl_uefi_reduce_power_parse(struct iwl_trans *trans, u32 tlv_len, tlv_type; len -= sizeof(*tlv); - tlv = (void *)data; + tlv = (const void *)data; tlv_len = le32_to_cpu(tlv->length); tlv_type = le32_to_cpu(tlv->type); @@ -175,8 +175,8 @@ static void *iwl_uefi_reduce_power_parse(struct iwl_trans *trans, } if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) { - struct iwl_sku_id *sku_id = - (void *)(data + sizeof(*tlv)); + const struct iwl_sku_id *sku_id = + (const void *)(data + sizeof(*tlv)); IWL_DEBUG_FW(trans, "Got IWL_UCODE_TLV_PNVM_SKU len %d\n", diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index e122b8b4e1fc..f5b556a103e8 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -260,6 +260,7 @@ enum iwl_cfg_trans_ltr_delay { * @integrated: discrete or integrated * @low_latency_xtal: use the low latency xtal if supported * @ltr_delay: LTR delay parameter, &enum iwl_cfg_trans_ltr_delay. + * @imr_enabled: use the IMR if supported. */ struct iwl_cfg_trans_params { const struct iwl_base_params *base_params; @@ -274,7 +275,8 @@ struct iwl_cfg_trans_params { integrated:1, low_latency_xtal:1, bisr_workaround:1, - ltr_delay:2; + ltr_delay:2, + imr_enabled:1; }; /** @@ -343,8 +345,8 @@ struct iwl_fw_mon_regs { * @bisr_workaround: BISR hardware workaround (for 22260 series devices) * @min_txq_size: minimum number of slots required in a TX queue * @uhb_supported: ultra high band channels supported - * @min_256_ba_txq_size: minimum number of slots required in a TX queue which - * supports 256 BA aggregation + * @min_ba_txq_size: minimum number of slots required in a TX queue which + * based on hardware support (HE - 256, EHT - 1K). * @num_rbds: number of receive buffer descriptors to use * (only used for multi-queue capable devices) * @mac_addr_csr_base: CSR base register for MAC address access, if not set @@ -405,9 +407,10 @@ struct iwl_cfg { u32 d3_debug_data_length; u32 min_txq_size; u32 gp2_reg_addr; - u32 min_256_ba_txq_size; + u32 min_ba_txq_size; const struct iwl_fw_mon_regs mon_dram_regs; const struct iwl_fw_mon_regs mon_smem_regs; + const struct iwl_fw_mon_regs mon_dbgi_regs; }; #define IWL_CFG_ANY (~0) @@ -433,6 +436,7 @@ struct iwl_cfg { #define IWL_CFG_RF_TYPE_HR1 0x10C #define IWL_CFG_RF_TYPE_GF 0x10D #define IWL_CFG_RF_TYPE_MR 0x110 +#define IWL_CFG_RF_TYPE_MS 0x111 #define IWL_CFG_RF_TYPE_FM 0x112 #define IWL_CFG_RF_ID_TH 0x1 @@ -489,6 +493,7 @@ extern const struct iwl_cfg_trans_params iwl_ax200_trans_cfg; extern const struct iwl_cfg_trans_params iwl_snj_trans_cfg; extern const struct iwl_cfg_trans_params iwl_so_trans_cfg; extern const struct iwl_cfg_trans_params iwl_so_long_latency_trans_cfg; +extern const struct iwl_cfg_trans_params iwl_so_long_latency_imr_trans_cfg; extern const struct iwl_cfg_trans_params iwl_ma_trans_cfg; extern const struct iwl_cfg_trans_params iwl_bz_trans_cfg; extern const char iwl9162_name[]; @@ -509,6 +514,7 @@ extern const char iwl9560_killer_1550i_name[]; extern const char iwl9560_killer_1550s_name[]; extern const char iwl_ax200_name[]; extern const char iwl_ax203_name[]; +extern const char iwl_ax204_name[]; extern const char iwl_ax201_name[]; extern const char iwl_ax101_name[]; extern const char iwl_ax200_killer_1650w_name[]; @@ -631,9 +637,12 @@ extern const struct iwl_cfg iwl_cfg_ma_a0_hr_b0; extern const struct iwl_cfg iwl_cfg_ma_a0_gf_a0; extern const struct iwl_cfg iwl_cfg_ma_a0_gf4_a0; extern const struct iwl_cfg iwl_cfg_ma_a0_mr_a0; +extern const struct iwl_cfg iwl_cfg_ma_a0_ms_a0; extern const struct iwl_cfg iwl_cfg_ma_a0_fm_a0; extern const struct iwl_cfg iwl_cfg_snj_a0_mr_a0; +extern const struct iwl_cfg iwl_cfg_snj_a0_ms_a0; extern const struct iwl_cfg iwl_cfg_so_a0_hr_a0; +extern const struct iwl_cfg iwl_cfg_so_a0_ms_a0; extern const struct iwl_cfg iwl_cfg_quz_a0_hr_b0; extern const struct iwl_cfg iwl_cfg_bz_a0_hr_b0; extern const struct iwl_cfg iwl_cfg_bz_a0_gf_a0; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h b/drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h index 5adf485db38e..b84884034c74 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2018, 2020-2021 Intel Corporation + * Copyright (C) 2018, 2020-2022 Intel Corporation */ #ifndef __iwl_context_info_file_gen3_h__ #define __iwl_context_info_file_gen3_h__ @@ -34,6 +34,7 @@ enum iwl_prph_scratch_mtr_format { /** * enum iwl_prph_scratch_flags - PRPH scratch control flags + * @IWL_PRPH_SCRATCH_IMR_DEBUG_EN: IMR support for debug * @IWL_PRPH_SCRATCH_EARLY_DEBUG_EN: enable early debug conf * @IWL_PRPH_SCRATCH_EDBG_DEST_DRAM: use DRAM, with size allocated * in hwm config. @@ -55,6 +56,7 @@ enum iwl_prph_scratch_mtr_format { * @IWL_PRPH_SCRATCH_RB_SIZE_EXT_16K: 16kB RB size */ enum iwl_prph_scratch_flags { + IWL_PRPH_SCRATCH_IMR_DEBUG_EN = BIT(1), IWL_PRPH_SCRATCH_EARLY_DEBUG_EN = BIT(4), IWL_PRPH_SCRATCH_EDBG_DEST_DRAM = BIT(8), IWL_PRPH_SCRATCH_EDBG_DEST_INTERNAL = BIT(9), diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index 8e10ba88afb3..3e1f011e93aa 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -534,6 +534,9 @@ enum { * 11-8: queue selector */ #define HBUS_TARG_WRPTR (HBUS_BASE+0x060) +/* This register is common for Tx and Rx, Rx queues start from 512 */ +#define HBUS_TARG_WRPTR_Q_SHIFT (16) +#define HBUS_TARG_WRPTR_RX_Q(q) (((q) + 512) << HBUS_TARG_WRPTR_Q_SHIFT) /********************************************************** * CSR values diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index c73672d61356..866a33f49915 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2022 Intel Corporation */ #include #include "iwl-drv.h" @@ -74,7 +74,8 @@ static int iwl_dbg_tlv_add(const struct iwl_ucode_tlv *tlv, if (!node) return -ENOMEM; - memcpy(&node->tlv, tlv, sizeof(node->tlv) + len); + memcpy(&node->tlv, tlv, sizeof(node->tlv)); + memcpy(node->tlv.data, tlv->data, len); list_add_tail(&node->list, list); return 0; @@ -181,11 +182,11 @@ static int iwl_dbg_tlv_alloc_region(struct iwl_trans *trans, u32 tlv_len = sizeof(*tlv) + le32_to_cpu(tlv->length); /* - * The higher part of the ID in from version 2 is irrelevant for - * us, so mask it out. + * The higher part of the ID from version 2 is debug policy. + * The id will be only lsb 16 bits, so mask it out. */ if (le32_to_cpu(reg->hdr.version) >= 2) - id &= IWL_FW_INI_REGION_V2_MASK; + id &= IWL_FW_INI_REGION_ID_MASK; if (le32_to_cpu(tlv->length) < sizeof(*reg)) return -EINVAL; @@ -211,6 +212,14 @@ static int iwl_dbg_tlv_alloc_region(struct iwl_trans *trans, return -EOPNOTSUPP; } + if (type == IWL_FW_INI_REGION_INTERNAL_BUFFER) { + trans->dbg.imr_data.sram_addr = + le32_to_cpu(reg->internal_buffer.base_addr); + trans->dbg.imr_data.sram_size = + le32_to_cpu(reg->internal_buffer.size); + } + + active_reg = &trans->dbg.active_regions[id]; if (*active_reg) { IWL_WARN(trans, "WRT: Overriding region id %u\n", id); @@ -271,7 +280,7 @@ static int iwl_dbg_tlv_alloc_trigger(struct iwl_trans *trans, static int iwl_dbg_tlv_config_set(struct iwl_trans *trans, const struct iwl_ucode_tlv *tlv) { - struct iwl_fw_ini_conf_set_tlv *conf_set = (void *)tlv->data; + const struct iwl_fw_ini_conf_set_tlv *conf_set = (const void *)tlv->data; u32 tp = le32_to_cpu(conf_set->time_point); u32 type = le32_to_cpu(conf_set->set_type); @@ -460,7 +469,7 @@ static int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data, while (len >= sizeof(*tlv)) { len -= sizeof(*tlv); - tlv = (void *)data; + tlv = (const void *)data; tlv_len = le32_to_cpu(tlv->length); @@ -577,8 +586,7 @@ static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt, return 0; num_frags = le32_to_cpu(fw_mon_cfg->max_frags_num); - if (!fw_has_capa(&fwrt->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_DBG_BUF_ALLOC_CMD_SUPP)) { + if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) { if (alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1) return -EIO; num_frags = 1; @@ -762,33 +770,40 @@ static int iwl_dbg_tlv_update_dram(struct iwl_fw_runtime *fwrt, static void iwl_dbg_tlv_update_drams(struct iwl_fw_runtime *fwrt) { - int ret, i, dram_alloc = 0; - struct iwl_dram_info dram_info; + int ret, i; + bool dram_alloc = false; struct iwl_dram_data *frags = &fwrt->trans->dbg.fw_mon_ini[IWL_FW_INI_ALLOCATION_ID_DBGC1].frags[0]; + struct iwl_dram_info *dram_info; + + if (!frags || !frags->block) + return; + + dram_info = frags->block; if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT)) return; - dram_info.first_word = cpu_to_le32(DRAM_INFO_FIRST_MAGIC_WORD); - dram_info.second_word = cpu_to_le32(DRAM_INFO_SECOND_MAGIC_WORD); + dram_info->first_word = cpu_to_le32(DRAM_INFO_FIRST_MAGIC_WORD); + dram_info->second_word = cpu_to_le32(DRAM_INFO_SECOND_MAGIC_WORD); for (i = IWL_FW_INI_ALLOCATION_ID_DBGC1; i <= IWL_FW_INI_ALLOCATION_ID_DBGC3; i++) { - ret = iwl_dbg_tlv_update_dram(fwrt, i, &dram_info); + ret = iwl_dbg_tlv_update_dram(fwrt, i, dram_info); if (!ret) - dram_alloc++; + dram_alloc = true; else IWL_WARN(fwrt, "WRT: Failed to set DRAM buffer for alloc id %d, ret=%d\n", i, ret); } - if (dram_alloc) { - memcpy(frags->block, &dram_info, sizeof(dram_info)); - IWL_DEBUG_FW(fwrt, "block data after %016x\n", - *((int *)fwrt->trans->dbg.fw_mon_ini[1].frags[0].block)); - } + + if (dram_alloc) + IWL_DEBUG_FW(fwrt, "block data after %08x\n", + dram_info->first_word); + else + memset(frags->block, 0, sizeof(*dram_info)); } static void iwl_dbg_tlv_send_hcmds(struct iwl_fw_runtime *fwrt, @@ -811,11 +826,11 @@ static void iwl_dbg_tlv_send_hcmds(struct iwl_fw_runtime *fwrt, } static void iwl_dbg_tlv_apply_config(struct iwl_fw_runtime *fwrt, - struct list_head *config_list) + struct list_head *conf_list) { struct iwl_dbg_tlv_node *node; - list_for_each_entry(node, config_list, list) { + list_for_each_entry(node, conf_list, list) { struct iwl_fw_ini_conf_set_tlv *config_list = (void *)node->tlv.data; u32 count, address, value; u32 len = (le32_to_cpu(node->tlv.length) - sizeof(*config_list)) / 8; @@ -861,11 +876,18 @@ static void iwl_dbg_tlv_apply_config(struct iwl_fw_runtime *fwrt, case IWL_FW_INI_CONFIG_SET_TYPE_DBGC_DRAM_ADDR: { struct iwl_dbgc1_info dram_info = {}; struct iwl_dram_data *frags = &fwrt->trans->dbg.fw_mon_ini[1].frags[0]; - __le64 dram_base_addr = cpu_to_le64(frags->physical); - __le32 dram_size = cpu_to_le32(frags->size); - u64 dram_addr = le64_to_cpu(dram_base_addr); + __le64 dram_base_addr; + __le32 dram_size; + u64 dram_addr; u32 ret; + if (!frags) + break; + + dram_base_addr = cpu_to_le64(frags->physical); + dram_size = cpu_to_le32(frags->size); + dram_addr = le64_to_cpu(dram_base_addr); + IWL_DEBUG_FW(fwrt, "WRT: dram_base_addr 0x%016llx, dram_size 0x%x\n", dram_base_addr, dram_size); IWL_DEBUG_FW(fwrt, "WRT: config_list->addr_offset: %u\n", diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h index 79287708bd6e..128059ca77e6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2022 Intel Corporation */ #ifndef __iwl_dbg_tlv_h__ #define __iwl_dbg_tlv_h__ @@ -10,6 +10,8 @@ #include #include +#define IWL_DBG_TLV_MAX_PRESET 15 + /** * struct iwl_dbg_tlv_node - debug TLV node * @list: list of &struct iwl_dbg_tlv_node diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 6651e78b39ec..a2203f661321 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -243,14 +243,14 @@ struct iwl_firmware_pieces { /* FW debug data parsed for driver usage */ bool dbg_dest_tlv_init; - u8 *dbg_dest_ver; + const u8 *dbg_dest_ver; union { - struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; - struct iwl_fw_dbg_dest_tlv_v1 *dbg_dest_tlv_v1; + const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; + const struct iwl_fw_dbg_dest_tlv_v1 *dbg_dest_tlv_v1; }; - struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX]; + const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX]; size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX]; - struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX]; + const struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX]; size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX]; struct iwl_fw_dbg_mem_seg_tlv *dbg_mem_tlv; size_t n_mem_tlv; @@ -324,29 +324,6 @@ static void set_sec_offset(struct iwl_firmware_pieces *pieces, pieces->img[type].sec[sec].offset = offset; } -static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len) -{ - int i, j; - struct iwl_fw_cscheme_list *l = (struct iwl_fw_cscheme_list *)data; - struct iwl_fw_cipher_scheme *fwcs; - - if (len < sizeof(*l) || - len < sizeof(l->size) + l->size * sizeof(l->cs[0])) - return -EINVAL; - - for (i = 0, j = 0; i < IWL_UCODE_MAX_CS && i < l->size; i++) { - fwcs = &l->cs[j]; - - /* we skip schemes with zero cipher suite selector */ - if (!fwcs->cipher) - continue; - - fw->cs[j++] = *fwcs; - } - - return 0; -} - /* * Gets uCode section from tlv. */ @@ -356,13 +333,13 @@ static int iwl_store_ucode_sec(struct iwl_firmware_pieces *pieces, { struct fw_img_parsing *img; struct fw_sec *sec; - struct fw_sec_parsing *sec_parse; + const struct fw_sec_parsing *sec_parse; size_t alloc_size; if (WARN_ON(!pieces || !data || type >= IWL_UCODE_TYPE_MAX)) return -1; - sec_parse = (struct fw_sec_parsing *)data; + sec_parse = (const struct fw_sec_parsing *)data; img = &pieces->img[type]; @@ -385,8 +362,8 @@ static int iwl_store_ucode_sec(struct iwl_firmware_pieces *pieces, static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data) { - struct iwl_tlv_calib_data *def_calib = - (struct iwl_tlv_calib_data *)data; + const struct iwl_tlv_calib_data *def_calib = + (const struct iwl_tlv_calib_data *)data; u32 ucode_type = le32_to_cpu(def_calib->ucode_type); if (ucode_type >= IWL_UCODE_TYPE_MAX) { IWL_ERR(drv, "Wrong ucode_type %u for default calibration.\n", @@ -404,7 +381,7 @@ static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data) static void iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data, struct iwl_ucode_capabilities *capa) { - const struct iwl_ucode_api *ucode_api = (void *)data; + const struct iwl_ucode_api *ucode_api = (const void *)data; u32 api_index = le32_to_cpu(ucode_api->api_index); u32 api_flags = le32_to_cpu(ucode_api->api_flags); int i; @@ -425,7 +402,7 @@ static void iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data, static void iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data, struct iwl_ucode_capabilities *capa) { - const struct iwl_ucode_capa *ucode_capa = (void *)data; + const struct iwl_ucode_capa *ucode_capa = (const void *)data; u32 api_index = le32_to_cpu(ucode_capa->api_index); u32 api_flags = le32_to_cpu(ucode_capa->api_capa); int i; @@ -457,7 +434,7 @@ static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv, const struct firmware *ucode_raw, struct iwl_firmware_pieces *pieces) { - struct iwl_ucode_header *ucode = (void *)ucode_raw->data; + const struct iwl_ucode_header *ucode = (const void *)ucode_raw->data; u32 api_ver, hdr_size, build; char buildstr[25]; const u8 *src; @@ -600,7 +577,7 @@ static void iwl_parse_dbg_tlv_assert_tables(struct iwl_drv *drv, sizeof(region->special_mem)) return; - region = (void *)tlv->data; + region = (const void *)tlv->data; addr = le32_to_cpu(region->special_mem.base_addr); addr += le32_to_cpu(region->special_mem.offset); addr &= ~FW_ADDR_CACHE_CONTROL; @@ -655,7 +632,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, struct iwl_ucode_capabilities *capa, bool *usniffer_images) { - struct iwl_tlv_ucode_header *ucode = (void *)ucode_raw->data; + const struct iwl_tlv_ucode_header *ucode = (const void *)ucode_raw->data; const struct iwl_ucode_tlv *tlv; size_t len = ucode_raw->size; const u8 *data; @@ -704,8 +681,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, while (len >= sizeof(*tlv)) { len -= sizeof(*tlv); - tlv = (void *)data; + tlv = (const void *)data; tlv_len = le32_to_cpu(tlv->length); tlv_type = le32_to_cpu(tlv->type); tlv_data = tlv->data; @@ -762,7 +739,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (tlv_len != sizeof(u32)) goto invalid_tlv_len; capa->max_probe_length = - le32_to_cpup((__le32 *)tlv_data); + le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_PAN: if (tlv_len) @@ -783,7 +760,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, * will not work with the new firmware, or * it'll not take advantage of new features. */ - capa->flags = le32_to_cpup((__le32 *)tlv_data); + capa->flags = le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_API_CHANGES_SET: if (tlv_len != sizeof(struct iwl_ucode_api)) @@ -799,37 +776,37 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (tlv_len != sizeof(u32)) goto invalid_tlv_len; pieces->init_evtlog_ptr = - le32_to_cpup((__le32 *)tlv_data); + le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_INIT_EVTLOG_SIZE: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; pieces->init_evtlog_size = - le32_to_cpup((__le32 *)tlv_data); + le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_INIT_ERRLOG_PTR: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; pieces->init_errlog_ptr = - le32_to_cpup((__le32 *)tlv_data); + le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_RUNT_EVTLOG_PTR: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; pieces->inst_evtlog_ptr = - le32_to_cpup((__le32 *)tlv_data); + le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_RUNT_EVTLOG_SIZE: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; pieces->inst_evtlog_size = - le32_to_cpup((__le32 *)tlv_data); + le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_RUNT_ERRLOG_PTR: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; pieces->inst_errlog_ptr = - le32_to_cpup((__le32 *)tlv_data); + le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_ENHANCE_SENS_TBL: if (tlv_len) @@ -858,7 +835,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (tlv_len != sizeof(u32)) goto invalid_tlv_len; capa->standard_phy_calibration_size = - le32_to_cpup((__le32 *)tlv_data); + le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_SEC_RT: iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, @@ -884,7 +861,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, case IWL_UCODE_TLV_PHY_SKU: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; - drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data); + drv->fw.phy_config = le32_to_cpup((const __le32 *)tlv_data); drv->fw.valid_tx_ant = (drv->fw.phy_config & FW_PHY_CFG_TX_CHAIN) >> FW_PHY_CFG_TX_CHAIN_POS; @@ -911,7 +888,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (tlv_len != sizeof(u32)) goto invalid_tlv_len; num_of_cpus = - le32_to_cpup((__le32 *)tlv_data); + le32_to_cpup((const __le32 *)tlv_data); if (num_of_cpus == 2) { drv->fw.img[IWL_UCODE_REGULAR].is_dual_cpus = @@ -925,18 +902,14 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, return -EINVAL; } break; - case IWL_UCODE_TLV_CSCHEME: - if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len)) - goto invalid_tlv_len; - break; case IWL_UCODE_TLV_N_SCAN_CHANNELS: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; capa->n_scan_channels = - le32_to_cpup((__le32 *)tlv_data); + le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_FW_VERSION: { - __le32 *ptr = (void *)tlv_data; + const __le32 *ptr = (const void *)tlv_data; u32 major, minor; u8 local_comp; @@ -960,15 +933,15 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, break; } case IWL_UCODE_TLV_FW_DBG_DEST: { - struct iwl_fw_dbg_dest_tlv *dest = NULL; - struct iwl_fw_dbg_dest_tlv_v1 *dest_v1 = NULL; + const struct iwl_fw_dbg_dest_tlv *dest = NULL; + const struct iwl_fw_dbg_dest_tlv_v1 *dest_v1 = NULL; u8 mon_mode; - pieces->dbg_dest_ver = (u8 *)tlv_data; + pieces->dbg_dest_ver = (const u8 *)tlv_data; if (*pieces->dbg_dest_ver == 1) { - dest = (void *)tlv_data; + dest = (const void *)tlv_data; } else if (*pieces->dbg_dest_ver == 0) { - dest_v1 = (void *)tlv_data; + dest_v1 = (const void *)tlv_data; } else { IWL_ERR(drv, "The version is %d, and it is invalid\n", @@ -1009,7 +982,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, break; } case IWL_UCODE_TLV_FW_DBG_CONF: { - struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data; + const struct iwl_fw_dbg_conf_tlv *conf = + (const void *)tlv_data; if (!pieces->dbg_dest_tlv_init) { IWL_ERR(drv, @@ -1043,8 +1017,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, break; } case IWL_UCODE_TLV_FW_DBG_TRIGGER: { - struct iwl_fw_dbg_trigger_tlv *trigger = - (void *)tlv_data; + const struct iwl_fw_dbg_trigger_tlv *trigger = + (const void *)tlv_data; u32 trigger_id = le32_to_cpu(trigger->id); if (trigger_id >= ARRAY_SIZE(drv->fw.dbg.trigger_tlv)) { @@ -1075,7 +1049,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, } drv->fw.dbg.dump_mask = - le32_to_cpup((__le32 *)tlv_data); + le32_to_cpup((const __le32 *)tlv_data); break; } case IWL_UCODE_TLV_SEC_RT_USNIFFER: @@ -1087,7 +1061,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, case IWL_UCODE_TLV_PAGING: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; - paging_mem_size = le32_to_cpup((__le32 *)tlv_data); + paging_mem_size = le32_to_cpup((const __le32 *)tlv_data); IWL_DEBUG_FW(drv, "Paging: paging enabled (size = %u bytes)\n", @@ -1117,8 +1091,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, /* ignored */ break; case IWL_UCODE_TLV_FW_MEM_SEG: { - struct iwl_fw_dbg_mem_seg_tlv *dbg_mem = - (void *)tlv_data; + const struct iwl_fw_dbg_mem_seg_tlv *dbg_mem = + (const void *)tlv_data; size_t size; struct iwl_fw_dbg_mem_seg_tlv *n; @@ -1146,10 +1120,10 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, break; } case IWL_UCODE_TLV_FW_RECOVERY_INFO: { - struct { + const struct { __le32 buf_addr; __le32 buf_size; - } *recov_info = (void *)tlv_data; + } *recov_info = (const void *)tlv_data; if (tlv_len != sizeof(*recov_info)) goto invalid_tlv_len; @@ -1160,10 +1134,10 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, } break; case IWL_UCODE_TLV_FW_FSEQ_VERSION: { - struct { + const struct { u8 version[32]; u8 sha1[20]; - } *fseq_ver = (void *)tlv_data; + } *fseq_ver = (const void *)tlv_data; if (tlv_len != sizeof(*fseq_ver)) goto invalid_tlv_len; @@ -1174,19 +1148,19 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, case IWL_UCODE_TLV_FW_NUM_STATIONS: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; - if (le32_to_cpup((__le32 *)tlv_data) > + if (le32_to_cpup((const __le32 *)tlv_data) > IWL_MVM_STATION_COUNT_MAX) { IWL_ERR(drv, "%d is an invalid number of station\n", - le32_to_cpup((__le32 *)tlv_data)); + le32_to_cpup((const __le32 *)tlv_data)); goto tlv_error; } capa->num_stations = - le32_to_cpup((__le32 *)tlv_data); + le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_UMAC_DEBUG_ADDRS: { - struct iwl_umac_debug_addrs *dbg_ptrs = - (void *)tlv_data; + const struct iwl_umac_debug_addrs *dbg_ptrs = + (const void *)tlv_data; if (tlv_len != sizeof(*dbg_ptrs)) goto invalid_tlv_len; @@ -1201,8 +1175,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, break; } case IWL_UCODE_TLV_LMAC_DEBUG_ADDRS: { - struct iwl_lmac_debug_addrs *dbg_ptrs = - (void *)tlv_data; + const struct iwl_lmac_debug_addrs *dbg_ptrs = + (const void *)tlv_data; if (tlv_len != sizeof(*dbg_ptrs)) goto invalid_tlv_len; @@ -1277,7 +1251,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (len) { IWL_ERR(drv, "invalid TLV after parsing: %zd\n", len); - iwl_print_hex_dump(drv, IWL_DL_FW, (u8 *)data, len); + iwl_print_hex_dump(drv, IWL_DL_FW, data, len); return -EINVAL; } @@ -1418,7 +1392,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) { struct iwl_drv *drv = context; struct iwl_fw *fw = &drv->fw; - struct iwl_ucode_header *ucode; + const struct iwl_ucode_header *ucode; struct iwlwifi_opmode_table *op; int err; struct iwl_firmware_pieces *pieces; @@ -1456,7 +1430,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) } /* Data from ucode file: header followed by uCode images */ - ucode = (struct iwl_ucode_header *)ucode_raw->data; + ucode = (const struct iwl_ucode_header *)ucode_raw->data; if (ucode->ver) err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces); @@ -1645,6 +1619,8 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) /* We have our copies now, allow OS release its copies */ release_firmware(ucode_raw); + iwl_dbg_tlv_load_bin(drv->trans->dev, drv->trans); + mutex_lock(&iwlwifi_opmode_table_mtx); switch (fw->type) { case IWL_FW_DVM: @@ -1661,8 +1637,6 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) IWL_INFO(drv, "loaded firmware version %s op_mode %s\n", drv->fw.fw_version, op->name); - iwl_dbg_tlv_load_bin(drv->trans->dev, drv->trans); - /* add this device to the list of devices using this op_mode */ list_add_tail(&drv->list, &op->drv); @@ -1796,6 +1770,7 @@ void iwl_drv_stop(struct iwl_drv *drv) kfree(drv); } +#define ENABLE_INI (IWL_DBG_TLV_MAX_PRESET + 1) /* shared module parameters */ struct iwl_mod_params iwlwifi_mod_params = { @@ -1803,7 +1778,7 @@ struct iwl_mod_params iwlwifi_mod_params = { .bt_coex_active = true, .power_level = IWL_POWER_INDEX_1, .uapsd_disable = IWL_DISABLE_UAPSD_BSS | IWL_DISABLE_UAPSD_P2P_CLIENT, - .enable_ini = true, + .enable_ini = ENABLE_INI, /* the rest are 0 by default */ }; IWL_EXPORT_SYMBOL(iwlwifi_mod_params); @@ -1915,10 +1890,42 @@ MODULE_PARM_DESC(nvm_file, "NVM file name"); module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, uint, 0644); MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality bitmap 1: BSS 2: P2P Client (default: 3)"); -module_param_named(enable_ini, iwlwifi_mod_params.enable_ini, - bool, S_IRUGO | S_IWUSR); + +static int enable_ini_set(const char *arg, const struct kernel_param *kp) +{ + int ret = 0; + bool res; + __u32 new_enable_ini; + + /* in case the argument type is a number */ + ret = kstrtou32(arg, 0, &new_enable_ini); + if (!ret) { + if (new_enable_ini > ENABLE_INI) { + pr_err("enable_ini cannot be %d, in range 0-16\n", new_enable_ini); + return -EINVAL; + } + goto out; + } + + /* in case the argument type is boolean */ + ret = kstrtobool(arg, &res); + if (ret) + return ret; + new_enable_ini = (res ? ENABLE_INI : 0); + +out: + iwlwifi_mod_params.enable_ini = new_enable_ini; + return 0; +} + +static const struct kernel_param_ops enable_ini_ops = { + .set = enable_ini_set +}; + +module_param_cb(enable_ini, &enable_ini_ops, &iwlwifi_mod_params.enable_ini, 0644); MODULE_PARM_DESC(enable_ini, - "Enable debug INI TLV FW debug infrastructure (default: true"); + "0:disable, 1-15:FW_DBG_PRESET Values, 16:enabled without preset value defined," + "Debug INI TLV FW debug infrastructure (default: 16)"); /* * set bt_coex_active to true, uCode will do kill/defer diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h index 0fd009e6d685..80073f973334 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h @@ -84,7 +84,7 @@ void iwl_drv_stop(struct iwl_drv *drv); * everything is built-in, then we can avoid that. */ #ifdef CONFIG_IWLWIFI_OPMODE_MODULAR -#define IWL_EXPORT_SYMBOL(sym) EXPORT_SYMBOL_GPL(sym) +#define IWL_EXPORT_SYMBOL(sym) EXPORT_SYMBOL_NS_GPL(sym, IWLWIFI) #else #define IWL_EXPORT_SYMBOL(sym) #endif diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c index b9e86bf972e5..5f386bb1a353 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c @@ -23,26 +23,22 @@ */ #define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ -#define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ -#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ - - /* * The device's EEPROM semaphore prevents conflicts between driver and uCode * when accessing the EEPROM; each access is a series of pulses to/from the * EEPROM chip, not a single event, so even reads could conflict if they * weren't arbitrated by the semaphore. */ +#define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ +#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ -#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ -#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ static int iwl_eeprom_acquire_semaphore(struct iwl_trans *trans) { u16 count; int ret; - for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { + for (count = 0; count < IWL_EEPROM_SEM_RETRY_LIMIT; count++) { /* Request semaphore */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); @@ -51,7 +47,7 @@ static int iwl_eeprom_acquire_semaphore(struct iwl_trans *trans) ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, - EEPROM_SEM_TIMEOUT); + IWL_EEPROM_SEM_TIMEOUT); if (ret >= 0) { IWL_DEBUG_EEPROM(trans->dev, "Acquired semaphore after %d tries.\n", diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h index e6fd4941a4cb..bedd78a47f67 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2020 Intel Corporation + * Copyright (C) 2005-2014, 2018-2021 Intel Corporation * Copyright (C) 2015-2017 Intel Deutschland GmbH */ #ifndef __iwl_fh_h__ @@ -590,11 +590,31 @@ struct iwl_rb_status { #define TFD_QUEUE_CB_SIZE(x) (ilog2(x) - 3) #define TFD_QUEUE_SIZE_BC_DUP (64) #define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP) -#define TFD_QUEUE_BC_SIZE_GEN3 1024 +#define TFD_QUEUE_BC_SIZE_GEN3_AX210 1024 +#define TFD_QUEUE_BC_SIZE_GEN3_BZ (1024 * 4) #define IWL_TX_DMA_MASK DMA_BIT_MASK(36) #define IWL_NUM_OF_TBS 20 #define IWL_TFH_NUM_TBS 25 +/* IMR DMA registers */ +#define IMR_TFH_SRV_DMA_CHNL0_CTRL 0x00a0a51c +#define IMR_TFH_SRV_DMA_CHNL0_SRAM_ADDR 0x00a0a520 +#define IMR_TFH_SRV_DMA_CHNL0_DRAM_ADDR_LSB 0x00a0a524 +#define IMR_TFH_SRV_DMA_CHNL0_DRAM_ADDR_MSB 0x00a0a528 +#define IMR_TFH_SRV_DMA_CHNL0_BC 0x00a0a52c +#define TFH_SRV_DMA_CHNL0_LEFT_BC 0x00a0a530 + +/* RFH S2D DMA registers */ +#define IMR_RFH_GEN_CFG_SERVICE_DMA_RS_MSK 0x0000000c +#define IMR_RFH_GEN_CFG_SERVICE_DMA_SNOOP_MSK 0x00000002 + +/* TFH D2S DMA registers */ +#define IMR_UREG_CHICK_HALT_UMAC_PERMANENTLY_MSK 0x80000000 +#define IMR_UREG_CHICK 0x00d05c00 +#define IMR_TFH_SRV_DMA_CHNL0_CTRL_D2S_IRQ_TARGET_POS 0x00800000 +#define IMR_TFH_SRV_DMA_CHNL0_CTRL_D2S_RS_MSK 0x00000030 +#define IMR_TFH_SRV_DMA_CHNL0_CTRL_D2S_DMA_EN_POS 0x80000000 + static inline u8 iwl_get_dma_hi_addr(dma_addr_t addr) { return (sizeof(addr) > sizeof(u32) ? upper_32_bits(addr) : 0) & 0xF; @@ -707,14 +727,14 @@ struct iwlagn_scd_bc_tbl { } __packed; /** - * struct iwl_gen3_bc_tbl scheduler byte count table gen3 + * struct iwl_gen3_bc_tbl_entry scheduler byte count table entry gen3 * For AX210 and on: * @tfd_offset: 0-12 - tx command byte count * 12-13 - number of 64 byte chunks * 14-16 - reserved */ -struct iwl_gen3_bc_tbl { - __le16 tfd_offset[TFD_QUEUE_BC_SIZE_GEN3]; +struct iwl_gen3_bc_tbl_entry { + __le16 tfd_offset; } __packed; #endif /* !__iwl_fh_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index 253eac4cbf59..396f2c997da6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -65,14 +65,14 @@ IWL_EXPORT_SYMBOL(iwl_poll_bit); u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg) { - u32 value = 0x5a5a5a5a; - if (iwl_trans_grab_nic_access(trans)) { - value = iwl_read32(trans, reg); + u32 value = iwl_read32(trans, reg); + iwl_trans_release_nic_access(trans); + return value; } - return value; + return 0x5a5a5a5a; } IWL_EXPORT_SYMBOL(iwl_read_direct32); @@ -135,13 +135,15 @@ IWL_EXPORT_SYMBOL(iwl_write_prph64_no_grab); u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs) { - u32 val = 0x5a5a5a5a; - if (iwl_trans_grab_nic_access(trans)) { - val = iwl_read_prph_no_grab(trans, ofs); + u32 val = iwl_read_prph_no_grab(trans, ofs); + iwl_trans_release_nic_access(trans); + + return val; } - return val; + + return 0x5a5a5a5a; } IWL_EXPORT_SYMBOL(iwl_read_prph); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h index 004ebdac4535..d0b4d02bdab9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2020 Intel Corporation + * Copyright (C) 2005-2014, 2018-2022 Intel Corporation */ #ifndef __iwl_modparams_h__ #define __iwl_modparams_h__ @@ -83,7 +83,8 @@ struct iwl_mod_params { */ bool disable_11ax; bool remove_when_gone; - bool enable_ini; + u32 enable_ini; + bool disable_11be; }; static inline bool iwl_enable_rx_ampdu(void) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 04addf964d83..9040da3dcce3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -375,10 +375,10 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, if (v4) ch_flags = - __le32_to_cpup((__le32 *)nvm_ch_flags + ch_idx); + __le32_to_cpup((const __le32 *)nvm_ch_flags + ch_idx); else ch_flags = - __le16_to_cpup((__le16 *)nvm_ch_flags + ch_idx); + __le16_to_cpup((const __le16 *)nvm_ch_flags + ch_idx); if (band == NL80211_BAND_5GHZ && !data->sku_cap_band_52ghz_enable) @@ -583,9 +583,9 @@ static const struct ieee80211_sband_iftype_data iwl_he_capa[] = { IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ, .phy_cap_info[3] = - IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_NO_DCM | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK | IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 | - IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_NO_DCM | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK | IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1, .phy_cap_info[4] = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | @@ -653,9 +653,9 @@ static const struct ieee80211_sband_iftype_data iwl_he_capa[] = { IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US, .phy_cap_info[3] = - IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_NO_DCM | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK | IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 | - IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_NO_DCM | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK | IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1, .phy_cap_info[6] = IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT, @@ -731,7 +731,7 @@ static void iwl_init_he_6ghz_capa(struct iwl_trans *trans, IWL_DEBUG_EEPROM(trans->dev, "he_6ghz_capa=0x%x\n", he_6ghz_capa); /* we know it's writable - we set it before ourselves */ - iftype_data = (void *)sband->iftype_data; + iftype_data = (void *)(uintptr_t)sband->iftype_data; for (i = 0; i < sband->n_iftype_data; i++) iftype_data[i].he_6ghz_capa.capa = cpu_to_le16(he_6ghz_capa); } @@ -783,6 +783,7 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { case IWL_CFG_RF_TYPE_GF: case IWL_CFG_RF_TYPE_MR: + case IWL_CFG_RF_TYPE_MS: iftype_data->he_cap.he_cap_elem.phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU; if (!is_ap) @@ -911,7 +912,7 @@ static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw, if (cfg->nvm_type != IWL_NVM_EXT) return le16_to_cpup(nvm_sw + SKU); - return le32_to_cpup((__le32 *)(phy_sku + SKU_FAMILY_8000)); + return le32_to_cpup((const __le32 *)(phy_sku + SKU_FAMILY_8000)); } static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw) @@ -919,8 +920,8 @@ static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw) if (cfg->nvm_type != IWL_NVM_EXT) return le16_to_cpup(nvm_sw + NVM_VERSION); else - return le32_to_cpup((__le32 *)(nvm_sw + - NVM_VERSION_EXT_NVM)); + return le32_to_cpup((const __le32 *)(nvm_sw + + NVM_VERSION_EXT_NVM)); } static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw, @@ -929,7 +930,7 @@ static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw, if (cfg->nvm_type != IWL_NVM_EXT) return le16_to_cpup(nvm_sw + RADIO_CFG); - return le32_to_cpup((__le32 *)(phy_sku + RADIO_CFG_FAMILY_EXT_NVM)); + return le32_to_cpup((const __le32 *)(phy_sku + RADIO_CFG_FAMILY_EXT_NVM)); } @@ -940,7 +941,7 @@ static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw) if (cfg->nvm_type != IWL_NVM_EXT) return le16_to_cpup(nvm_sw + N_HW_ADDRS); - n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000)); + n_hw_addr = le32_to_cpup((const __le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000)); return n_hw_addr & N_HW_ADDR_MASK; } @@ -1079,7 +1080,9 @@ static int iwl_set_hw_address(struct iwl_trans *trans, return -EINVAL; } - IWL_INFO(trans, "base HW address: %pM\n", data->hw_addr); + if (!trans->csme_own) + IWL_INFO(trans, "base HW address: %pM, OTP minor version: 0x%x\n", + data->hw_addr, iwl_read_prph(trans, REG_OTP_MINOR)); return 0; } @@ -1384,8 +1387,12 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, nvm_chan = iwl_nvm_channels; } - if (WARN_ON(num_of_ch > max_num_ch)) + if (num_of_ch > max_num_ch) { + IWL_DEBUG_DEV(dev, IWL_DL_LAR, + "Num of channels (%d) is greater than expected. Truncating to %d\n", + num_of_ch, max_num_ch); num_of_ch = max_num_ch; + } if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES)) return ERR_PTR(-EINVAL); @@ -1591,7 +1598,7 @@ int iwl_read_external_nvm(struct iwl_trans *trans, } eof = fw_entry->data + fw_entry->size; - dword_buff = (__le32 *)fw_entry->data; + dword_buff = (const __le32 *)fw_entry->data; /* some NVM file will contain a header. * The header is identified by 2 dwords header as follow: @@ -1603,7 +1610,7 @@ int iwl_read_external_nvm(struct iwl_trans *trans, if (fw_entry->size > NVM_HEADER_SIZE && dword_buff[0] == cpu_to_le32(NVM_HEADER_0) && dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) { - file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE); + file_sec = (const void *)(fw_entry->data + NVM_HEADER_SIZE); IWL_INFO(trans, "NVM Version %08X\n", le32_to_cpu(dword_buff[2])); IWL_INFO(trans, "NVM Manufacturing date %08X\n", le32_to_cpu(dword_buff[3])); @@ -1616,7 +1623,7 @@ int iwl_read_external_nvm(struct iwl_trans *trans, goto out; } } else { - file_sec = (void *)fw_entry->data; + file_sec = (const void *)fw_entry->data; } while (true) { @@ -1684,7 +1691,7 @@ int iwl_read_external_nvm(struct iwl_trans *trans, nvm_sections[section_id].length = section_size; /* advance to the next section */ - file_sec = (void *)(file_sec->data + section_size); + file_sec = (const void *)(file_sec->data + section_size); } out: release_firmware(fw_entry); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c index 5378315d0179..0a93ac769f66 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2020 Intel Corporation + * Copyright (C) 2005-2014, 2020-2021 Intel Corporation * Copyright (C) 2016 Intel Deutschland GmbH */ #include @@ -13,8 +13,6 @@ #include "iwl-op-mode.h" #include "iwl-trans.h" -#define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */ - struct iwl_phy_db_entry { u16 size; u8 *data; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 95b3dae7b504..a22788a68168 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2021 Intel Corporation + * Copyright (C) 2005-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016 Intel Deutschland GmbH */ @@ -354,10 +354,10 @@ #define WFPM_GP2 0xA030B4 /* DBGI SRAM Register details */ -#define DBGI_SRAM_TARGET_ACCESS_CFG 0x00A2E14C -#define DBGI_SRAM_TARGET_ACCESS_CFG_RESET_ADDRESS_MSK 0x10000 #define DBGI_SRAM_TARGET_ACCESS_RDATA_LSB 0x00A2E154 #define DBGI_SRAM_TARGET_ACCESS_RDATA_MSB 0x00A2E158 +#define DBGI_SRAM_FIFO_POINTERS 0x00A2E148 +#define DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK 0x00000FFF enum { ENABLE_WFPM = BIT(31), @@ -386,6 +386,11 @@ enum { #define UREG_LMAC1_CURRENT_PC 0xa05c1c #define UREG_LMAC2_CURRENT_PC 0xa05c20 +#define WFPM_LMAC1_PD_NOTIFICATION 0xa0338c +#define WFPM_ARC1_PD_NOTIFICATION 0xa03044 +#define HPM_SECONDARY_DEVICE_STATE 0xa03404 + + /* For UMAG_GEN_HW_STATUS reg check */ enum { UMAG_GEN_HW_IS_FPGA = BIT(1), @@ -491,4 +496,6 @@ enum { #define HBUS_TIMEOUT 0xA5A5A5A1 #define WFPM_DPHY_OFF 0xDF10FF +#define REG_OTP_MINOR 0xA0333C + #endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 9236f9106826..b1af9359cea5 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -78,8 +78,12 @@ int iwl_trans_init(struct iwl_trans *trans) if (WARN_ON(trans->trans_cfg->gen2 && txcmd_size >= txcmd_align)) return -EINVAL; - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) - trans->txqs.bc_tbl_size = sizeof(struct iwl_gen3_bc_tbl); + if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + trans->txqs.bc_tbl_size = + sizeof(struct iwl_gen3_bc_tbl_entry) * TFD_QUEUE_BC_SIZE_GEN3_BZ; + else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + trans->txqs.bc_tbl_size = + sizeof(struct iwl_gen3_bc_tbl_entry) * TFD_QUEUE_BC_SIZE_GEN3_AX210; else trans->txqs.bc_tbl_size = sizeof(struct iwlagn_scd_bc_tbl); /* @@ -203,10 +207,10 @@ IWL_EXPORT_SYMBOL(iwl_trans_send_cmd); static int iwl_hcmd_names_cmp(const void *key, const void *elt) { const struct iwl_hcmd_names *name = elt; - u8 cmd1 = *(u8 *)key; + const u8 *cmd1 = key; u8 cmd2 = name->cmd_id; - return (cmd1 - cmd2); + return (*cmd1 - cmd2); } const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 1bcaa3598785..d659ccd065f7 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2021 Intel Corporation + * Copyright (C) 2005-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -406,6 +406,9 @@ struct iwl_dump_sanitize_ops { * @cb_data_offs: offset inside skb->cb to store transport data at, must have * space for at least two pointers * @fw_reset_handshake: firmware supports reset flow handshake + * @queue_alloc_cmd_ver: queue allocation command version, set to 0 + * for using the older SCD_QUEUE_CFG, set to the version of + * SCD_QUEUE_CONFIG_CMD otherwise. */ struct iwl_trans_config { struct iwl_op_mode *op_mode; @@ -424,6 +427,7 @@ struct iwl_trans_config { u8 cb_data_offs; bool fw_reset_handshake; + u8 queue_alloc_cmd_ver; }; struct iwl_trans_dump_data { @@ -569,10 +573,9 @@ struct iwl_trans_ops { void (*txq_disable)(struct iwl_trans *trans, int queue, bool configure_scd); /* 22000 functions */ - int (*txq_alloc)(struct iwl_trans *trans, - __le16 flags, u8 sta_id, u8 tid, - int cmd_id, int size, - unsigned int queue_wdg_timeout); + int (*txq_alloc)(struct iwl_trans *trans, u32 flags, + u32 sta_mask, u8 tid, + int size, unsigned int queue_wdg_timeout); void (*txq_free)(struct iwl_trans *trans, int queue); int (*rxq_dma_data)(struct iwl_trans *trans, int queue, struct iwl_trans_rxq_dma_data *data); @@ -615,6 +618,10 @@ struct iwl_trans_ops { int (*set_reduce_power)(struct iwl_trans *trans, const void *data, u32 len); void (*interrupts)(struct iwl_trans *trans, bool enable); + int (*imr_dma_data)(struct iwl_trans *trans, + u32 dst_addr, u64 src_addr, + u32 byte_cnt); + }; /** @@ -721,6 +728,26 @@ struct iwl_self_init_dram { int paging_cnt; }; +/** + * struct iwl_imr_data - imr dram data used during debug process + * @imr_enable: imr enable status received from fw + * @imr_size: imr dram size received from fw + * @sram_addr: sram address from debug tlv + * @sram_size: sram size from debug tlv + * @imr2sram_remainbyte`: size remained after each dma transfer + * @imr_curr_addr: current dst address used during dma transfer + * @imr_base_addr: imr address received from fw + */ +struct iwl_imr_data { + u32 imr_enable; + u32 imr_size; + u32 sram_addr; + u32 sram_size; + u32 imr2sram_remainbyte; + u64 imr_curr_addr; + __le64 imr_base_addr; +}; + /** * struct iwl_trans_debug - transport debug related data * @@ -785,6 +812,7 @@ struct iwl_trans_debug { u32 ucode_preset; bool restart_required; u32 last_tp_resetfw; + struct iwl_imr_data imr_data; }; struct iwl_dma_ptr { @@ -904,6 +932,7 @@ struct iwl_txq { * @queue_used - bit mask of used queues * @queue_stopped - bit mask of stopped queues * @scd_bc_tbls: gen1 pointer to the byte count table of the scheduler + * @queue_alloc_cmd_ver: queue allocation command version */ struct iwl_trans_txqs { unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_TVQM_QUEUES)]; @@ -929,6 +958,8 @@ struct iwl_trans_txqs { } tfd; struct iwl_dma_ptr scd_bc_tbls; + + u8 queue_alloc_cmd_ver; }; /** @@ -1220,9 +1251,8 @@ iwl_trans_txq_free(struct iwl_trans *trans, int queue) static inline int iwl_trans_txq_alloc(struct iwl_trans *trans, - __le16 flags, u8 sta_id, u8 tid, - int cmd_id, int size, - unsigned int wdg_timeout) + u32 flags, u32 sta_mask, u8 tid, + int size, unsigned int wdg_timeout) { might_sleep(); @@ -1234,8 +1264,8 @@ iwl_trans_txq_alloc(struct iwl_trans *trans, return -EIO; } - return trans->ops->txq_alloc(trans, flags, sta_id, tid, - cmd_id, size, wdg_timeout); + return trans->ops->txq_alloc(trans, flags, sta_mask, tid, + size, wdg_timeout); } static inline void iwl_trans_txq_set_shared_mode(struct iwl_trans *trans, @@ -1368,6 +1398,15 @@ static inline int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr, iwl_trans_read_mem(trans, addr, buf, (bufsize) / sizeof(u32));\ } while (0) +static inline int iwl_trans_write_imr_mem(struct iwl_trans *trans, + u32 dst_addr, u64 src_addr, + u32 byte_cnt) +{ + if (trans->ops->imr_dma_data) + return trans->ops->imr_dma_data(trans, dst_addr, src_addr, byte_cnt); + return 0; +} + static inline u32 iwl_trans_read_mem32(struct iwl_trans *trans, u32 addr) { u32 value; diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index 2f7f0f994ca3..b4f45234cfc8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -312,7 +312,7 @@ static ssize_t iwl_mei_write_cyclic_buf(struct mei_cl_device *cldev, memcpy(q_head + wr, hdr, tx_sz); } else { memcpy(q_head + wr, hdr, q_sz - wr); - memcpy(q_head, (u8 *)hdr + q_sz - wr, tx_sz - (q_sz - wr)); + memcpy(q_head, (const u8 *)hdr + q_sz - wr, tx_sz - (q_sz - wr)); } WRITE_ONCE(notif_q->wr_ptr, cpu_to_le32((wr + tx_sz) % q_sz)); @@ -432,7 +432,7 @@ void iwl_mei_add_data_to_ring(struct sk_buff *skb, bool cb_tx) u32 q_sz; u32 rd; u32 wr; - void *q_head; + u8 *q_head; if (!iwl_mei_global_cldev) return; @@ -2003,7 +2003,11 @@ static void iwl_mei_remove(struct mei_cl_device *cldev) } static const struct mei_cl_device_id iwl_mei_tbl[] = { - { KBUILD_MODNAME, MEI_WLAN_UUID, MEI_CL_VERSION_ANY}, + { + .name = KBUILD_MODNAME, + .uuid = MEI_WLAN_UUID, + .version = MEI_CL_VERSION_ANY, + }, /* required last entry */ { } diff --git a/drivers/net/wireless/intel/iwlwifi/mei/net.c b/drivers/net/wireless/intel/iwlwifi/mei/net.c index 468102a95e1b..3472167c8370 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/net.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/net.c @@ -102,8 +102,8 @@ static bool iwl_mei_rx_filter_arp(struct sk_buff *skb, * src IP address - 4 bytes * target MAC addess - 6 bytes */ - target_ip = (void *)((u8 *)(arp + 1) + - ETH_ALEN + sizeof(__be32) + ETH_ALEN); + target_ip = (const void *)((const u8 *)(arp + 1) + + ETH_ALEN + sizeof(__be32) + ETH_ALEN); /* * ARP request is forwarded to ME only if IP address match in the diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index b400867e94f0..a995bba0ba81 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -31,7 +31,7 @@ void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw, memcpy(mvmvif->rekey_data.kck, data->kck, data->kck_len); mvmvif->rekey_data.akm = data->akm & 0xFF; mvmvif->rekey_data.replay_ctr = - cpu_to_le64(be64_to_cpup((__be64 *)data->replay_ctr)); + cpu_to_le64(be64_to_cpup((const __be64 *)data->replay_ctr)); mvmvif->rekey_data.valid = true; mutex_unlock(&mvm->mutex); @@ -453,8 +453,7 @@ static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ver = iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - WOWLAN_TSC_RSC_PARAM, + int ver = iwl_fw_lookup_cmd_ver(mvm->fw, WOWLAN_TSC_RSC_PARAM, IWL_FW_CMD_VER_UNKNOWN); int ret; @@ -672,8 +671,7 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, }; int i, err; - int ver = iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - WOWLAN_PATTERNS, + int ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id, IWL_FW_CMD_VER_UNKNOWN); if (!wowlan->n_patterns) @@ -921,8 +919,7 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, wowlan_config_cmd->flags = ENABLE_L3_FILTERING | ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING; - if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - WOWLAN_CONFIGURATION, 0) < 6) { + if (iwl_fw_lookup_cmd_ver(mvm->fw, WOWLAN_CONFIGURATION, 0) < 6) { /* Query the last used seqno and set it */ int ret = iwl_mvm_get_last_nonqos_seq(mvm, vif); @@ -1017,8 +1014,7 @@ static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_TKIP_MIC_KEYS)) { - int ver = iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - WOWLAN_TKIP_PARAM, + int ver = iwl_fw_lookup_cmd_ver(mvm->fw, WOWLAN_TKIP_PARAM, IWL_FW_CMD_VER_UNKNOWN); struct wowlan_key_tkip_data tkip_data = {}; int size; @@ -1058,7 +1054,6 @@ static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, }; cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, - IWL_ALWAYS_LONG_GROUP, WOWLAN_KEK_KCK_MATERIAL, IWL_FW_CMD_VER_UNKNOWN); if (WARN_ON(cmd_ver != 2 && cmd_ver != 3 && cmd_ver != 4 && @@ -1089,7 +1084,7 @@ static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, sizeof(struct iwl_wowlan_kek_kck_material_cmd_v2); /* skip the sta_id at the beginning */ _kek_kck_cmd = (void *) - ((u8 *)_kek_kck_cmd) + sizeof(kek_kck_cmd.sta_id); + ((u8 *)_kek_kck_cmd + sizeof(kek_kck_cmd.sta_id)); } IWL_DEBUG_WOWLAN(mvm, "setting akm %d\n", @@ -1489,7 +1484,7 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, int pktsize = status->wake_packet_bufsize; int pktlen = status->wake_packet_length; const u8 *pktdata = status->wake_packet; - struct ieee80211_hdr *hdr = (void *)pktdata; + const struct ieee80211_hdr *hdr = (const void *)pktdata; int truncated = pktlen - pktsize; /* this would be a firmware bug */ @@ -2074,8 +2069,7 @@ iwl_mvm_send_wowlan_get_status(struct iwl_mvm *mvm, u8 sta_id) }; int ret, len; u8 notif_ver; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - WOWLAN_GET_STATUSES, + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id, IWL_FW_CMD_VER_UNKNOWN); if (cmd_ver == IWL_FW_CMD_VER_UNKNOWN) @@ -2182,8 +2176,7 @@ iwl_mvm_send_wowlan_get_status(struct iwl_mvm *mvm, u8 sta_id) static struct iwl_wowlan_status_data * iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, u8 sta_id) { - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - OFFLOADS_QUERY_CMD, + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, OFFLOADS_QUERY_CMD, IWL_FW_CMD_VER_UNKNOWN); __le32 station_id = cpu_to_le32(sta_id); u32 cmd_size = cmd_ver != IWL_FW_CMD_VER_UNKNOWN ? sizeof(station_id) : 0; @@ -2704,7 +2697,9 @@ static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file) /* start pseudo D3 */ rtnl_lock(); + wiphy_lock(mvm->hw->wiphy); err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true); + wiphy_unlock(mvm->hw->wiphy); rtnl_unlock(); if (err > 0) err = -EINVAL; @@ -2760,7 +2755,9 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt); rtnl_lock(); + wiphy_lock(mvm->hw->wiphy); __iwl_mvm_resume(mvm, true); + wiphy_unlock(mvm->hw->wiphy); rtnl_unlock(); iwl_mvm_resume_tcm(mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 445c94adb076..49898fd99594 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -426,8 +426,7 @@ static ssize_t iwl_dbgfs_amsdu_len_write(struct ieee80211_sta *sta, return -EINVAL; /* only change from debug set <-> debug unset */ - if ((amsdu_len && mvmsta->orig_amsdu_len) || - (!!amsdu_len && mvmsta->orig_amsdu_len)) + if (amsdu_len && mvmsta->orig_amsdu_len) return -EBUSY; if (amsdu_len) { @@ -1479,7 +1478,7 @@ iwl_dbgfs_he_sniffer_params_write(struct iwl_mvm *mvm, char *buf, .mvm = mvm, }; u16 wait_cmds[] = { - iwl_cmd_id(HE_AIR_SNIFFER_CONFIG_CMD, DATA_PATH_GROUP, 0), + WIDE_ID(DATA_PATH_GROUP, HE_AIR_SNIFFER_CONFIG_CMD), }; u32 aid; int ret; @@ -1514,8 +1513,9 @@ iwl_dbgfs_he_sniffer_params_write(struct iwl_mvm *mvm, char *buf, wait_cmds, ARRAY_SIZE(wait_cmds), iwl_mvm_sniffer_apply, &apply); - ret = iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(HE_AIR_SNIFFER_CONFIG_CMD, - DATA_PATH_GROUP, 0), 0, + ret = iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(DATA_PATH_GROUP, HE_AIR_SNIFFER_CONFIG_CMD), + 0, sizeof(he_mon_cmd), &he_mon_cmd); /* no need to really wait, we already did anyway */ @@ -1727,8 +1727,7 @@ static ssize_t iwl_dbgfs_mem_read(struct file *file, char __user *user_buf, if (!iwl_mvm_firmware_running(mvm)) return -EIO; - hcmd.id = iwl_cmd_id(*ppos >> 24 ? UMAC_RD_WR : LMAC_RD_WR, - DEBUG_GROUP, 0); + hcmd.id = WIDE_ID(DEBUG_GROUP, *ppos >> 24 ? UMAC_RD_WR : LMAC_RD_WR); cmd.op = cpu_to_le32(DEBUG_MEM_OP_READ); /* Take care of alignment of both the position and the length */ @@ -1758,7 +1757,7 @@ static ssize_t iwl_dbgfs_mem_read(struct file *file, char __user *user_buf, goto out; } - ret = len - copy_to_user(user_buf, (void *)rsp->data + delta, len); + ret = len - copy_to_user(user_buf, (u8 *)rsp->data + delta, len); *ppos += ret; out: @@ -1782,8 +1781,7 @@ static ssize_t iwl_dbgfs_mem_write(struct file *file, if (!iwl_mvm_firmware_running(mvm)) return -EIO; - hcmd.id = iwl_cmd_id(*ppos >> 24 ? UMAC_RD_WR : LMAC_RD_WR, - DEBUG_GROUP, 0); + hcmd.id = WIDE_ID(DEBUG_GROUP, *ppos >> 24 ? UMAC_RD_WR : LMAC_RD_WR); if (*ppos & 0x3 || count < 4) { op = DEBUG_MEM_OP_WRITE_BYTES; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c index 628aee634b2a..430044bc4755 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c @@ -346,8 +346,8 @@ iwl_mvm_ftm_target_chandef_v2(struct iwl_mvm *mvm, *format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS; break; case NL80211_CHAN_WIDTH_160: - cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP, - TOF_RANGE_REQ_CMD, + cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), IWL_FW_CMD_VER_UNKNOWN); if (cmd_ver >= 13) { @@ -548,7 +548,7 @@ static int iwl_mvm_ftm_start_v5(struct iwl_mvm *mvm, struct ieee80211_vif *vif, { struct iwl_tof_range_req_cmd_v5 cmd_v5; struct iwl_host_cmd hcmd = { - .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0), + .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), .dataflags[0] = IWL_HCMD_DFL_DUP, .data[0] = &cmd_v5, .len[0] = sizeof(cmd_v5), @@ -574,7 +574,7 @@ static int iwl_mvm_ftm_start_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif, { struct iwl_tof_range_req_cmd_v7 cmd_v7; struct iwl_host_cmd hcmd = { - .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0), + .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), .dataflags[0] = IWL_HCMD_DFL_DUP, .data[0] = &cmd_v7, .len[0] = sizeof(cmd_v7), @@ -604,7 +604,7 @@ static int iwl_mvm_ftm_start_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif, { struct iwl_tof_range_req_cmd_v8 cmd; struct iwl_host_cmd hcmd = { - .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0), + .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), .dataflags[0] = IWL_HCMD_DFL_DUP, .data[0] = &cmd, .len[0] = sizeof(cmd), @@ -630,7 +630,7 @@ static int iwl_mvm_ftm_start_v9(struct iwl_mvm *mvm, struct ieee80211_vif *vif, { struct iwl_tof_range_req_cmd_v9 cmd; struct iwl_host_cmd hcmd = { - .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0), + .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), .dataflags[0] = IWL_HCMD_DFL_DUP, .data[0] = &cmd, .len[0] = sizeof(cmd), @@ -728,7 +728,7 @@ static int iwl_mvm_ftm_start_v11(struct iwl_mvm *mvm, { struct iwl_tof_range_req_cmd_v11 cmd; struct iwl_host_cmd hcmd = { - .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0), + .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), .dataflags[0] = IWL_HCMD_DFL_DUP, .data[0] = &cmd, .len[0] = sizeof(cmd), @@ -799,7 +799,7 @@ static int iwl_mvm_ftm_start_v12(struct iwl_mvm *mvm, { struct iwl_tof_range_req_cmd_v12 cmd; struct iwl_host_cmd hcmd = { - .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0), + .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), .dataflags[0] = IWL_HCMD_DFL_DUP, .data[0] = &cmd, .len[0] = sizeof(cmd), @@ -827,7 +827,7 @@ static int iwl_mvm_ftm_start_v13(struct iwl_mvm *mvm, { struct iwl_tof_range_req_cmd_v13 cmd; struct iwl_host_cmd hcmd = { - .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0), + .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), .dataflags[0] = IWL_HCMD_DFL_DUP, .data[0] = &cmd, .len[0] = sizeof(cmd), @@ -877,8 +877,8 @@ int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return -EBUSY; if (new_api) { - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP, - TOF_RANGE_REQ_CMD, + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), IWL_FW_CMD_VER_UNKNOWN); switch (cmd_ver) { @@ -927,8 +927,7 @@ void iwl_mvm_ftm_abort(struct iwl_mvm *mvm, struct cfg80211_pmsr_request *req) iwl_mvm_ftm_reset(mvm); - if (iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_RANGE_ABORT_CMD, - LOCATION_GROUP, 0), + if (iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(LOCATION_GROUP, TOF_RANGE_ABORT_CMD), 0, sizeof(cmd), &cmd)) IWL_ERR(mvm, "failed to abort FTM process\n"); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c index bda6da7d988e..9729680476fd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c @@ -106,6 +106,7 @@ iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_chan_def *chandef) { + u32 cmd_id = WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_CONFIG_CMD); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); /* * The command structure is the same for versions 6, 7 and 8 (only the @@ -120,8 +121,7 @@ iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm, IWL_TOF_RESPONDER_CMD_VALID_STA_ID), .sta_id = mvmvif->bcast_sta.sta_id, }; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP, - TOF_RESPONDER_CONFIG_CMD, 6); + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 6); int err; int cmd_size; @@ -161,9 +161,7 @@ iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm, memcpy(cmd.bssid, vif->addr, ETH_ALEN); - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_RESPONDER_CONFIG_CMD, - LOCATION_GROUP, 0), - 0, cmd_size, &cmd); + return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, cmd_size, &cmd); } static int @@ -177,8 +175,7 @@ iwl_mvm_ftm_responder_dyn_cfg_v2(struct iwl_mvm *mvm, }; u8 data[IWL_LCI_CIVIC_IE_MAX_SIZE] = {0}; struct iwl_host_cmd hcmd = { - .id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD, - LOCATION_GROUP, 0), + .id = WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD), .data[0] = &cmd, .len[0] = sizeof(cmd), .data[1] = &data, @@ -220,8 +217,7 @@ iwl_mvm_ftm_responder_dyn_cfg_v3(struct iwl_mvm *mvm, { struct iwl_tof_responder_dyn_config_cmd cmd; struct iwl_host_cmd hcmd = { - .id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD, - LOCATION_GROUP, 0), + .id = WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD), .data[0] = &cmd, .len[0] = sizeof(cmd), /* may not be able to DMA from stack */ @@ -278,8 +274,9 @@ iwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm *mvm, struct ieee80211_ftm_responder_params *params) { int ret; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP, - TOF_RESPONDER_DYN_CONFIG_CMD, 2); + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD), + 2); switch (cmd_ver) { case 2: @@ -320,8 +317,9 @@ int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm, .addr = addr, .hltk = hltk, }; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP, - TOF_RESPONDER_DYN_CONFIG_CMD, 2); + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD), + 2); lockdep_assert_held(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index ae589b3b8c46..e842816134f1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -25,11 +25,6 @@ #define MVM_UCODE_ALIVE_TIMEOUT (HZ) #define MVM_UCODE_CALIB_TIMEOUT (2 * HZ) -#define UCODE_VALID_OK cpu_to_le32(0x1) - -#define IWL_PPAG_MASK 3 -#define IWL_PPAG_ETSI_MASK BIT(0) - #define IWL_TAS_US_MCC 0x5553 #define IWL_TAS_CANADA_MCC 0x4341 @@ -79,7 +74,7 @@ static int iwl_mvm_send_dqa_cmd(struct iwl_mvm *mvm) struct iwl_dqa_enable_cmd dqa_cmd = { .cmd_queue = cpu_to_le32(IWL_MVM_DQA_CMD_QUEUE), }; - u32 cmd_id = iwl_cmd_id(DQA_ENABLE_CMD, DATA_PATH_GROUP, 0); + u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, DQA_ENABLE_CMD); int ret; ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(dqa_cmd), &dqa_cmd); @@ -126,13 +121,54 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, u32 lmac_error_event_table, umac_error_table; u32 version = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, UCODE_ALIVE_NTFY, 0); + u32 i; - /* - * For v5 and above, we can check the version, for older - * versions we need to check the size. - */ - if (version == 5 || version == 6) { - /* v5 and v6 are compatible (only IMR addition) */ + if (version == 6) { + struct iwl_alive_ntf_v6 *palive; + + if (pkt_len < sizeof(*palive)) + return false; + + palive = (void *)pkt->data; + mvm->trans->dbg.imr_data.imr_enable = + le32_to_cpu(palive->imr.enabled); + mvm->trans->dbg.imr_data.imr_size = + le32_to_cpu(palive->imr.size); + mvm->trans->dbg.imr_data.imr2sram_remainbyte = + mvm->trans->dbg.imr_data.imr_size; + mvm->trans->dbg.imr_data.imr_base_addr = + palive->imr.base_addr; + mvm->trans->dbg.imr_data.imr_curr_addr = + le64_to_cpu(mvm->trans->dbg.imr_data.imr_base_addr); + IWL_DEBUG_FW(mvm, "IMR Enabled: 0x0%x size 0x0%x Address 0x%016llx\n", + mvm->trans->dbg.imr_data.imr_enable, + mvm->trans->dbg.imr_data.imr_size, + le64_to_cpu(mvm->trans->dbg.imr_data.imr_base_addr)); + + if (!mvm->trans->dbg.imr_data.imr_enable) { + for (i = 0; i < ARRAY_SIZE(mvm->trans->dbg.active_regions); i++) { + struct iwl_ucode_tlv *reg_tlv; + struct iwl_fw_ini_region_tlv *reg; + + reg_tlv = mvm->trans->dbg.active_regions[i]; + if (!reg_tlv) + continue; + + reg = (void *)reg_tlv->data; + /* + * We have only one DRAM IMR region, so we + * can break as soon as we find the first + * one. + */ + if (reg->type == IWL_FW_INI_REGION_DRAM_IMR) { + mvm->trans->dbg.unsupported_region_msk |= BIT(i); + break; + } + } + } + } + + if (version >= 5) { struct iwl_alive_ntf_v5 *palive; if (pkt_len < sizeof(*palive)) @@ -249,6 +285,26 @@ static bool iwl_wait_phy_db_entry(struct iwl_notif_wait_data *notif_wait, return false; } +static void iwl_mvm_print_pd_notification(struct iwl_mvm *mvm) +{ + struct iwl_trans *trans = mvm->trans; + enum iwl_device_family device_family = trans->trans_cfg->device_family; + + if (device_family < IWL_DEVICE_FAMILY_8000) + return; + + if (device_family <= IWL_DEVICE_FAMILY_9000) + IWL_ERR(mvm, "WFPM_ARC1_PD_NOTIFICATION: 0x%x\n", + iwl_read_umac_prph(trans, WFPM_ARC1_PD_NOTIFICATION)); + else + IWL_ERR(mvm, "WFPM_LMAC1_PD_NOTIFICATION: 0x%x\n", + iwl_read_umac_prph(trans, WFPM_LMAC1_PD_NOTIFICATION)); + + IWL_ERR(mvm, "HPM_SECONDARY_DEVICE_STATE: 0x%x\n", + iwl_read_umac_prph(trans, HPM_SECONDARY_DEVICE_STATE)); + +} + static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, enum iwl_ucode_type ucode_type) { @@ -314,6 +370,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, iwl_read_prph(trans, SB_CPU_2_STATUS)); } + iwl_mvm_print_pd_notification(mvm); + /* LMAC/UMAC PC info */ if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_9000) { @@ -546,8 +604,7 @@ static int iwl_mvm_sgom_init(struct iwl_mvm *mvm) return 0; } - cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, REGULATORY_AND_NVM_GROUP, - SAR_OFFSET_MAPPING_TABLE_CMD, + cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id, IWL_FW_CMD_VER_UNKNOWN); if (cmd_ver != 2) { @@ -572,6 +629,7 @@ static int iwl_mvm_sgom_init(struct iwl_mvm *mvm) static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) { + u32 cmd_id = PHY_CONFIGURATION_CMD; struct iwl_phy_cfg_cmd_v3 phy_cfg_cmd; enum iwl_ucode_type ucode_type = mvm->fwrt.cur_fw_img; struct iwl_phy_specific_cfg phy_filters = {}; @@ -603,8 +661,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) phy_cfg_cmd.calib_control.flow_trigger = mvm->fw->default_calib[ucode_type].flow_trigger; - cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP, - PHY_CONFIGURATION_CMD, + cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, IWL_FW_CMD_VER_UNKNOWN); if (cmd_ver == 3) { iwl_mvm_phy_filter_init(mvm, &phy_filters); @@ -616,8 +673,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) phy_cfg_cmd.phy_cfg); cmd_size = (cmd_ver == 3) ? sizeof(struct iwl_phy_cfg_cmd_v3) : sizeof(struct iwl_phy_cfg_cmd_v1); - return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, 0, - cmd_size, &phy_cfg_cmd); + return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, cmd_size, &phy_cfg_cmd); } int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm) @@ -737,7 +793,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm) mvm->nvm_data->bands[0].n_channels = 1; mvm->nvm_data->bands[0].n_bitrates = 1; mvm->nvm_data->bands[0].bitrates = - (void *)mvm->nvm_data->channels + 1; + (void *)((u8 *)mvm->nvm_data->channels + 1); mvm->nvm_data->bands[0].bitrates->hw_value = 10; } @@ -760,6 +816,7 @@ static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) #ifdef CONFIG_ACPI int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) { + u32 cmd_id = REDUCE_TX_POWER_CMD; struct iwl_dev_tx_power_cmd cmd = { .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), }; @@ -767,11 +824,14 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) int ret; u16 len = 0; u32 n_subbands; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - REDUCE_TX_POWER_CMD, + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, IWL_FW_CMD_VER_UNKNOWN); - - if (cmd_ver == 6) { + if (cmd_ver == 7) { + len = sizeof(cmd.v7); + n_subbands = IWL_NUM_SUB_BANDS_V2; + per_chain = cmd.v7.per_chain[0][0]; + cmd.v7.flags = cpu_to_le32(mvm->fwrt.reduced_power_flags); + } else if (cmd_ver == 6) { len = sizeof(cmd.v6); n_subbands = IWL_NUM_SUB_BANDS_V2; per_chain = cmd.v6.per_chain[0][0]; @@ -805,7 +865,7 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) iwl_mei_set_power_limit(per_chain); IWL_DEBUG_RADIO(mvm, "Sending REDUCE_TX_POWER_CMD per chain\n"); - return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); + return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd); } int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) @@ -814,9 +874,12 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) struct iwl_geo_tx_power_profiles_resp *resp; u16 len; int ret; - struct iwl_host_cmd cmd; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_OPS_GROUP, - PER_CHAIN_LIMIT_OFFSET_CMD, + struct iwl_host_cmd cmd = { + .id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD), + .flags = CMD_WANT_SKB, + .data = { &geo_tx_cmd }, + }; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id, IWL_FW_CMD_VER_UNKNOWN); /* the ops field is at the same spot for all versions, so set in v1 */ @@ -838,12 +901,7 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) if (!iwl_sar_geo_support(&mvm->fwrt)) return -EOPNOTSUPP; - cmd = (struct iwl_host_cmd){ - .id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD), - .len = { len, }, - .flags = CMD_WANT_SKB, - .data = { &geo_tx_cmd }, - }; + cmd.len[0] = len; ret = iwl_mvm_send_cmd(mvm, &cmd); if (ret) { @@ -863,14 +921,14 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) { + u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD); union iwl_geo_tx_power_profiles_cmd cmd; u16 len; u32 n_bands; u32 n_profiles; u32 sk = 0; int ret; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_OPS_GROUP, - PER_CHAIN_LIMIT_OFFSET_CMD, + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, IWL_FW_CMD_VER_UNKNOWN); BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v1, ops) != @@ -948,167 +1006,19 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) IWL_UCODE_TLV_API_SAR_TABLE_VER)) cmd.v2.table_revision = cpu_to_le32(sk); - return iwl_mvm_send_cmd_pdu(mvm, - WIDE_ID(PHY_OPS_GROUP, - PER_CHAIN_LIMIT_OFFSET_CMD), - 0, len, &cmd); -} - -static int iwl_mvm_get_ppag_table(struct iwl_mvm *mvm) -{ - union acpi_object *wifi_pkg, *data, *flags; - int i, j, ret, tbl_rev, num_sub_bands; - int idx = 2; - - mvm->fwrt.ppag_flags = 0; - - data = iwl_acpi_get_object(mvm->dev, ACPI_PPAG_METHOD); - if (IS_ERR(data)) - return PTR_ERR(data); - - /* try to read ppag table rev 2 or 1 (both have the same data size) */ - wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data, - ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev); - if (!IS_ERR(wifi_pkg)) { - if (tbl_rev == 1 || tbl_rev == 2) { - num_sub_bands = IWL_NUM_SUB_BANDS_V2; - IWL_DEBUG_RADIO(mvm, - "Reading PPAG table v2 (tbl_rev=%d)\n", - tbl_rev); - goto read_table; - } else { - ret = -EINVAL; - goto out_free; - } - } - - /* try to read ppag table revision 0 */ - wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data, - ACPI_PPAG_WIFI_DATA_SIZE_V1, &tbl_rev); - if (!IS_ERR(wifi_pkg)) { - if (tbl_rev != 0) { - ret = -EINVAL; - goto out_free; - } - num_sub_bands = IWL_NUM_SUB_BANDS_V1; - IWL_DEBUG_RADIO(mvm, "Reading PPAG table v1 (tbl_rev=0)\n"); - goto read_table; - } - ret = PTR_ERR(wifi_pkg); - goto out_free; - -read_table: - mvm->fwrt.ppag_ver = tbl_rev; - flags = &wifi_pkg->package.elements[1]; - - if (flags->type != ACPI_TYPE_INTEGER) { - ret = -EINVAL; - goto out_free; - } - - mvm->fwrt.ppag_flags = flags->integer.value & IWL_PPAG_MASK; - - if (!mvm->fwrt.ppag_flags) { - ret = 0; - goto out_free; - } - - /* - * read, verify gain values and save them into the PPAG table. - * first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the - * following sub-bands to High-Band (5GHz). - */ - for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { - for (j = 0; j < num_sub_bands; j++) { - union acpi_object *ent; - - ent = &wifi_pkg->package.elements[idx++]; - if (ent->type != ACPI_TYPE_INTEGER) { - ret = -EINVAL; - goto out_free; - } - - mvm->fwrt.ppag_chains[i].subbands[j] = ent->integer.value; - - if ((j == 0 && - (mvm->fwrt.ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_LB || - mvm->fwrt.ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_LB)) || - (j != 0 && - (mvm->fwrt.ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_HB || - mvm->fwrt.ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_HB))) { - mvm->fwrt.ppag_flags = 0; - ret = -EINVAL; - goto out_free; - } - } - } - - ret = 0; -out_free: - kfree(data); - return ret; + return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd); } int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) { union iwl_ppag_table_cmd cmd; - u8 cmd_ver; - int i, j, ret, num_sub_bands, cmd_size; - s8 *gain; + int ret, cmd_size; - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) { - IWL_DEBUG_RADIO(mvm, - "PPAG capability not supported by FW, command not sent.\n"); + ret = iwl_read_ppag_table(&mvm->fwrt, &cmd, &cmd_size); + /* Not supporting PPAG table is a valid scenario */ + if(ret < 0) return 0; - } - if (!mvm->fwrt.ppag_flags) { - IWL_DEBUG_RADIO(mvm, "PPAG not enabled, command not sent.\n"); - return 0; - } - /* The 'flags' field is the same in v1 and in v2 so we can just - * use v1 to access it. - */ - cmd.v1.flags = cpu_to_le32(mvm->fwrt.ppag_flags); - cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_OPS_GROUP, - PER_PLATFORM_ANT_GAIN_CMD, - IWL_FW_CMD_VER_UNKNOWN); - if (cmd_ver == 1) { - num_sub_bands = IWL_NUM_SUB_BANDS_V1; - gain = cmd.v1.gain[0]; - cmd_size = sizeof(cmd.v1); - if (mvm->fwrt.ppag_ver == 1 || mvm->fwrt.ppag_ver == 2) { - IWL_DEBUG_RADIO(mvm, - "PPAG table rev is %d but FW supports v1, sending truncated table\n", - mvm->fwrt.ppag_ver); - cmd.v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK); - } - } else if (cmd_ver == 2 || cmd_ver == 3) { - num_sub_bands = IWL_NUM_SUB_BANDS_V2; - gain = cmd.v2.gain[0]; - cmd_size = sizeof(cmd.v2); - if (mvm->fwrt.ppag_ver == 0) { - IWL_DEBUG_RADIO(mvm, - "PPAG table is v1 but FW supports v2, sending padded table\n"); - } else if (cmd_ver == 2 && mvm->fwrt.ppag_ver == 2) { - IWL_DEBUG_RADIO(mvm, - "PPAG table is v3 but FW supports v2, sending partial bitmap.\n"); - cmd.v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK); - } - } else { - IWL_DEBUG_RADIO(mvm, "Unsupported PPAG command version\n"); - return 0; - } - - for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { - for (j = 0; j < num_sub_bands; j++) { - gain[i * num_sub_bands + j] = - mvm->fwrt.ppag_chains[i].subbands[j]; - IWL_DEBUG_RADIO(mvm, - "PPAG table: chain[%d] band[%d]: gain = %d\n", - i, j, gain[i * num_sub_bands + j]); - } - } IWL_DEBUG_RADIO(mvm, "Sending PER_PLATFORM_ANT_GAIN_CMD\n"); ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD), @@ -1120,40 +1030,11 @@ int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) return ret; } -static const struct dmi_system_id dmi_ppag_approved_list[] = { - { .ident = "HP", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "HP"), - }, - }, - { .ident = "SAMSUNG", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), - }, - }, - { .ident = "MSFT", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - }, - }, - { .ident = "ASUS", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTek COMPUTER INC."), - }, - }, - {} -}; - static int iwl_mvm_ppag_init(struct iwl_mvm *mvm) { /* no need to read the table, done in INIT stage */ - if (!dmi_check_system(dmi_ppag_approved_list)) { - IWL_DEBUG_RADIO(mvm, - "System vendor '%s' is not in the approved list, disabling PPAG.\n", - dmi_get_system_info(DMI_SYS_VENDOR)); - mvm->fwrt.ppag_flags = 0; + if (!(iwl_acpi_is_ppag_approved(&mvm->fwrt))) return 0; - } return iwl_mvm_ppag_send_cmd(mvm); } @@ -1205,11 +1086,12 @@ static bool iwl_mvm_add_to_tas_block_list(__le32 *list, __le32 *le_size, unsigne static void iwl_mvm_tas_init(struct iwl_mvm *mvm) { + u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG); int ret; - struct iwl_tas_config_cmd_v3 cmd = {}; - int cmd_size; + union iwl_tas_config_cmd cmd = {}; + int cmd_size, fw_ver; - BUILD_BUG_ON(ARRAY_SIZE(cmd.block_list_array) < + BUILD_BUG_ON(ARRAY_SIZE(cmd.v3.block_list_array) < APCI_WTAS_BLACK_LIST_MAX); if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) { @@ -1217,7 +1099,10 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) return; } - ret = iwl_acpi_get_tas(&mvm->fwrt, &cmd); + fw_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, + IWL_FW_CMD_VER_UNKNOWN); + + ret = iwl_acpi_get_tas(&mvm->fwrt, &cmd, fw_ver); if (ret < 0) { IWL_DEBUG_RADIO(mvm, "TAS table invalid or unavailable. (%d)\n", @@ -1232,25 +1117,24 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) IWL_DEBUG_RADIO(mvm, "System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n", dmi_get_system_info(DMI_SYS_VENDOR)); - if ((!iwl_mvm_add_to_tas_block_list(cmd.block_list_array, - &cmd.block_list_size, IWL_TAS_US_MCC)) || - (!iwl_mvm_add_to_tas_block_list(cmd.block_list_array, - &cmd.block_list_size, IWL_TAS_CANADA_MCC))) { + if ((!iwl_mvm_add_to_tas_block_list(cmd.v4.block_list_array, + &cmd.v4.block_list_size, + IWL_TAS_US_MCC)) || + (!iwl_mvm_add_to_tas_block_list(cmd.v4.block_list_array, + &cmd.v4.block_list_size, + IWL_TAS_CANADA_MCC))) { IWL_DEBUG_RADIO(mvm, "Unable to add US/Canada to TAS block list, disabling TAS\n"); return; } } - cmd_size = iwl_fw_lookup_cmd_ver(mvm->fw, REGULATORY_AND_NVM_GROUP, - TAS_CONFIG, - IWL_FW_CMD_VER_UNKNOWN) < 3 ? + /* v4 is the same size as v3, so no need to differentiate here */ + cmd_size = fw_ver < 3 ? sizeof(struct iwl_tas_config_cmd_v2) : sizeof(struct iwl_tas_config_cmd_v3); - ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(REGULATORY_AND_NVM_GROUP, - TAS_CONFIG), - 0, cmd_size, &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, cmd_size, &cmd); if (ret < 0) IWL_DEBUG_RADIO(mvm, "failed to send TAS_CONFIG (%d)\n", ret); } @@ -1283,7 +1167,7 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) { int ret; u32 value; - struct iwl_lari_config_change_cmd_v5 cmd = {}; + struct iwl_lari_config_change_cmd_v6 cmd = {}; cmd.config_bitmap = iwl_acpi_get_lari_config_bitmap(&mvm->fwrt); @@ -1310,25 +1194,43 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) if (!ret) cmd.oem_uhb_allow_bitmap = cpu_to_le32(value); + ret = iwl_acpi_get_dsm_u32(mvm->fwrt.dev, 0, + DSM_FUNC_FORCE_DISABLE_CHANNELS, + &iwl_guid, &value); + if (!ret) + cmd.force_disable_channels_bitmap = cpu_to_le32(value); + if (cmd.config_bitmap || cmd.oem_uhb_allow_bitmap || cmd.oem_11ax_allow_bitmap || cmd.oem_unii4_allow_bitmap || - cmd.chan_state_active_bitmap) { + cmd.chan_state_active_bitmap || + cmd.force_disable_channels_bitmap) { size_t cmd_size; u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, - REGULATORY_AND_NVM_GROUP, - LARI_CONFIG_CHANGE, 1); - if (cmd_ver == 5) + WIDE_ID(REGULATORY_AND_NVM_GROUP, + LARI_CONFIG_CHANGE), + 1); + switch (cmd_ver) { + case 6: + cmd_size = sizeof(struct iwl_lari_config_change_cmd_v6); + break; + case 5: cmd_size = sizeof(struct iwl_lari_config_change_cmd_v5); - else if (cmd_ver == 4) + break; + case 4: cmd_size = sizeof(struct iwl_lari_config_change_cmd_v4); - else if (cmd_ver == 3) + break; + case 3: cmd_size = sizeof(struct iwl_lari_config_change_cmd_v3); - else if (cmd_ver == 2) + break; + case 2: cmd_size = sizeof(struct iwl_lari_config_change_cmd_v2); - else + break; + default: cmd_size = sizeof(struct iwl_lari_config_change_cmd_v1); + break; + } IWL_DEBUG_RADIO(mvm, "sending LARI_CONFIG_CHANGE, config_bitmap=0x%x, oem_11ax_allow_bitmap=0x%x\n", @@ -1340,8 +1242,9 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) le32_to_cpu(cmd.chan_state_active_bitmap), cmd_ver); IWL_DEBUG_RADIO(mvm, - "sending LARI_CONFIG_CHANGE, oem_uhb_allow_bitmap=0x%x\n", - le32_to_cpu(cmd.oem_uhb_allow_bitmap)); + "sending LARI_CONFIG_CHANGE, oem_uhb_allow_bitmap=0x%x, force_disable_channels_bitmap=0x%x\n", + le32_to_cpu(cmd.oem_uhb_allow_bitmap), + le32_to_cpu(cmd.force_disable_channels_bitmap)); ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(REGULATORY_AND_NVM_GROUP, LARI_CONFIG_CHANGE), @@ -1358,7 +1261,7 @@ void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm) int ret; /* read PPAG table */ - ret = iwl_mvm_get_ppag_table(mvm); + ret = iwl_acpi_get_ppag_table(&mvm->fwrt); if (ret < 0) { IWL_DEBUG_RADIO(mvm, "PPAG BIOS table invalid or unavailable. (%d)\n", @@ -1641,9 +1544,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) * internal aux station for all aux activities that don't * requires a dedicated data queue. */ - if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - ADD_STA, - 0) < 12) { + if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) < 12) { /* * In old version the aux station uses mac id like other * station and not lmac id @@ -1658,8 +1559,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm) while (!sband && i < NUM_NL80211_BANDS) sband = mvm->hw->wiphy->bands[i++]; - if (WARN_ON_ONCE(!sband)) + if (WARN_ON_ONCE(!sband)) { + ret = -ENODEV; goto error; + } chan = &sband->channels[0]; @@ -1800,9 +1703,7 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm) for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); - if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - ADD_STA, - 0) < 12) { + if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) < 12) { /* * Add auxiliary station for scanning. * Newer versions of this command implies that the fw uses diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index fd7d4abfb454..5aa4520b70ac 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -821,10 +821,7 @@ u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct ieee80211_tx_info *info, u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw, u8 rate_idx) { u16 flags = iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx); - bool is_new_rate = iwl_fw_lookup_cmd_ver(fw, - LONG_GROUP, - BEACON_TEMPLATE_CMD, - 0) > 10; + bool is_new_rate = iwl_fw_lookup_cmd_ver(fw, BEACON_TEMPLATE_CMD, 0) > 10; if (rate_idx <= IWL_FIRST_CCK_RATE) flags |= is_new_rate ? IWL_MAC_BEACON_CCK @@ -960,8 +957,7 @@ static int iwl_mvm_mac_ctxt_send_beacon_v9(struct iwl_mvm *mvm, WARN_ON(channel == 0); if (cfg80211_channel_is_psc(ctx->def.chan) && !IWL_MVM_DISABLE_AP_FILS) { - flags |= iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - BEACON_TEMPLATE_CMD, + flags |= iwl_fw_lookup_cmd_ver(mvm->fw, BEACON_TEMPLATE_CMD, 0) > 10 ? IWL_MAC_BEACON_FILS : IWL_MAC_BEACON_FILS_V1; @@ -1458,8 +1454,9 @@ void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm, struct sk_buff *skb; u8 *data; u32 size = le32_to_cpu(sb->byte_count); - int ver = iwl_fw_lookup_cmd_ver(mvm->fw, PROT_OFFLOAD_GROUP, - STORED_BEACON_NTF, 0); + int ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(PROT_OFFLOAD_GROUP, STORED_BEACON_NTF), + 0); if (size == 0) return; @@ -1602,6 +1599,18 @@ void iwl_mvm_channel_switch_start_notif(struct iwl_mvm *mvm, RCU_INIT_POINTER(mvm->csa_vif, NULL); return; case NL80211_IFTYPE_STATION: + /* + * if we don't know about an ongoing channel switch, + * make sure FW cancels it + */ + if (iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP, + CHANNEL_SWITCH_ERROR_NOTIF, + 0) && !vif->csa_active) { + IWL_DEBUG_INFO(mvm, "Channel Switch was canceled\n"); + iwl_mvm_cancel_channel_switch(mvm, vif, mac_id); + break; + } + iwl_mvm_csa_client_absent(mvm, vif); cancel_delayed_work(&mvmvif->csa_work); ieee80211_chswitch_done(vif, true); @@ -1615,6 +1624,31 @@ void iwl_mvm_channel_switch_start_notif(struct iwl_mvm *mvm, rcu_read_unlock(); } +void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_channel_switch_error_notif *notif = (void *)pkt->data; + struct ieee80211_vif *vif; + u32 id = le32_to_cpu(notif->mac_id); + u32 csa_err_mask = le32_to_cpu(notif->csa_err_mask); + + rcu_read_lock(); + vif = iwl_mvm_rcu_dereference_vif_id(mvm, id, true); + if (!vif) { + rcu_read_unlock(); + return; + } + + IWL_DEBUG_INFO(mvm, "FW reports CSA error: mac_id=%u, csa_err_mask=%u\n", + id, csa_err_mask); + if (csa_err_mask & (CS_ERR_COUNT_ERROR | + CS_ERR_LONG_DELAY_AFTER_CS | + CS_ERR_TX_BLOCK_TIMER_EXPIRED)) + ieee80211_channel_switch_disconnect(vif, true); + rcu_read_unlock(); +} + void iwl_mvm_rx_missed_vap_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 709a3df57b10..784d91281c02 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -374,28 +374,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->n_cipher_suites++; } - /* currently FW API supports only one optional cipher scheme */ - if (mvm->fw->cs[0].cipher) { - const struct iwl_fw_cipher_scheme *fwcs = &mvm->fw->cs[0]; - struct ieee80211_cipher_scheme *cs = &mvm->cs[0]; - - mvm->hw->n_cipher_schemes = 1; - - cs->cipher = le32_to_cpu(fwcs->cipher); - cs->iftype = BIT(NL80211_IFTYPE_STATION); - cs->hdr_len = fwcs->hdr_len; - cs->pn_len = fwcs->pn_len; - cs->pn_off = fwcs->pn_off; - cs->key_idx_off = fwcs->key_idx_off; - cs->key_idx_mask = fwcs->key_idx_mask; - cs->key_idx_shift = fwcs->key_idx_shift; - cs->mic_len = fwcs->mic_len; - - mvm->hw->cipher_schemes = mvm->cs; - mvm->ciphers[hw->wiphy->n_cipher_suites] = cs->cipher; - hw->wiphy->n_cipher_suites++; - } - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_FTM_CALIBRATED)) { wiphy_ext_feature_set(hw->wiphy, @@ -553,8 +531,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT)) hw->wiphy->features |= NL80211_FEATURE_WFA_TPC_IE_IN_PROBES; - if (iwl_fw_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP, - WOWLAN_KEK_KCK_MATERIAL, + if (iwl_fw_lookup_cmd_ver(mvm->fw, WOWLAN_KEK_KCK_MATERIAL, IWL_FW_CMD_VER_UNKNOWN) == 3) hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK; @@ -567,9 +544,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) } if (iwl_mvm_is_oce_supported(mvm)) { - u8 scan_ver = iwl_fw_lookup_cmd_ver(mvm->fw, - IWL_ALWAYS_LONG_GROUP, - SCAN_REQ_UMAC, 0); + u8 scan_ver = iwl_fw_lookup_cmd_ver(mvm->fw, SCAN_REQ_UMAC, 0); wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP); @@ -1154,7 +1129,7 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm) /* async_handlers_wk is now blocked */ - if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, ADD_STA, 0) < 12) + if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) < 12) iwl_mvm_rm_aux_sta(mvm); iwl_mvm_stop_device(mvm); @@ -1246,6 +1221,7 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, s16 tx_power) { + u32 cmd_id = REDUCE_TX_POWER_CMD; int len; struct iwl_dev_tx_power_cmd cmd = { .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC), @@ -1253,14 +1229,15 @@ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id), .common.pwr_restriction = cpu_to_le16(8 * tx_power), }; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - REDUCE_TX_POWER_CMD, + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, IWL_FW_CMD_VER_UNKNOWN); if (tx_power == IWL_DEFAULT_MAX_TX_POWER) cmd.common.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER); - if (cmd_ver == 6) + if (cmd_ver == 7) + len = sizeof(cmd.v7); + else if (cmd_ver == 6) len = sizeof(cmd.v6); else if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_REDUCE_TX_POWER)) @@ -1274,7 +1251,7 @@ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, /* all structs have the same common part, add it */ len += sizeof(cmd.common); - return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); + return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd); } static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw, @@ -1335,6 +1312,15 @@ static void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw, .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), }; + /* + * In the new flow since FW is in charge of the timing, + * if driver has canceled the channel switch he will receive the + * CHANNEL_SWITCH_START_NOTIF notification from FW and then cancel it + */ + if (iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP, + CHANNEL_SWITCH_ERROR_NOTIF, 0)) + return; + IWL_DEBUG_MAC80211(mvm, "Abort CSA on mac %d\n", mvmvif->id); mutex_lock(&mvm->mutex); @@ -1845,11 +1831,108 @@ static u8 iwl_mvm_he_get_ppe_val(u8 *ppe, u8 ppe_pos_bit) return res; } +static void iwl_mvm_parse_ppe(struct iwl_mvm *mvm, + struct iwl_he_pkt_ext_v2 *pkt_ext, u8 nss, + u8 ru_index_bitmap, u8 *ppe, u8 ppe_pos_bit) +{ + int i; + + /* + * FW currently supports only nss == MAX_HE_SUPP_NSS + * + * If nss > MAX: we can ignore values we don't support + * If nss < MAX: we can set zeros in other streams + */ + if (nss > MAX_HE_SUPP_NSS) { + IWL_INFO(mvm, "Got NSS = %d - trimming to %d\n", nss, + MAX_HE_SUPP_NSS); + nss = MAX_HE_SUPP_NSS; + } + + for (i = 0; i < nss; i++) { + u8 ru_index_tmp = ru_index_bitmap << 1; + u8 low_th = IWL_HE_PKT_EXT_NONE, high_th = IWL_HE_PKT_EXT_NONE; + u8 bw; + + for (bw = 0; + bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); + bw++) { + ru_index_tmp >>= 1; + + if (!(ru_index_tmp & 1)) + continue; + + high_th = iwl_mvm_he_get_ppe_val(ppe, ppe_pos_bit); + ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE; + low_th = iwl_mvm_he_get_ppe_val(ppe, ppe_pos_bit); + ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE; + + pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th; + pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th; + } + } +} + +static void iwl_mvm_set_pkt_ext_from_he_ppe(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_he_pkt_ext_v2 *pkt_ext) +{ + u8 nss = (sta->he_cap.ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) + 1; + u8 *ppe = &sta->he_cap.ppe_thres[0]; + u8 ru_index_bitmap = + u8_get_bits(*ppe, + IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK); + /* Starting after PPE header */ + u8 ppe_pos_bit = IEEE80211_HE_PPE_THRES_INFO_HEADER_SIZE; + + iwl_mvm_parse_ppe(mvm, pkt_ext, nss, ru_index_bitmap, ppe, ppe_pos_bit); +} + +static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext, + u8 nominal_padding, + u32 *flags) +{ + int low_th = -1; + int high_th = -1; + int i; + + switch (nominal_padding) { + case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_0US: + low_th = IWL_HE_PKT_EXT_NONE; + high_th = IWL_HE_PKT_EXT_NONE; + break; + case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_8US: + low_th = IWL_HE_PKT_EXT_BPSK; + high_th = IWL_HE_PKT_EXT_NONE; + break; + case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US: + low_th = IWL_HE_PKT_EXT_NONE; + high_th = IWL_HE_PKT_EXT_BPSK; + break; + } + + /* Set the PPE thresholds accordingly */ + if (low_th >= 0 && high_th >= 0) { + for (i = 0; i < MAX_HE_SUPP_NSS; i++) { + u8 bw; + + for (bw = 0; + bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); + bw++) { + pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th; + pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th; + } + } + + *flags |= STA_CTXT_HE_PACKET_EXT; + } +} + static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u8 sta_id) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_he_sta_context_cmd sta_ctxt_cmd = { + struct iwl_he_sta_context_cmd_v3 sta_ctxt_cmd = { .sta_id = sta_id, .tid_limit = IWL_MAX_TID_COUNT, .bss_color = vif->bss_conf.he_bss_color.color, @@ -1857,16 +1940,39 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, .frame_time_rts_th = cpu_to_le16(vif->bss_conf.frame_time_rts_th), }; - int size = fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_MBSSID_HE) ? - sizeof(sta_ctxt_cmd) : - sizeof(struct iwl_he_sta_context_cmd_v1); + struct iwl_he_sta_context_cmd_v2 sta_ctxt_cmd_v2 = {}; + u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, STA_HE_CTXT_CMD); + u8 ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 2); + int size; struct ieee80211_sta *sta; u32 flags; int i; const struct ieee80211_sta_he_cap *own_he_cap = NULL; struct ieee80211_chanctx_conf *chanctx_conf; const struct ieee80211_supported_band *sband; + void *cmd; + + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_MBSSID_HE)) + ver = 1; + + switch (ver) { + case 1: + /* same layout as v2 except some data at the end */ + cmd = &sta_ctxt_cmd_v2; + size = sizeof(struct iwl_he_sta_context_cmd_v1); + break; + case 2: + cmd = &sta_ctxt_cmd_v2; + size = sizeof(struct iwl_he_sta_context_cmd_v2); + break; + case 3: + cmd = &sta_ctxt_cmd; + size = sizeof(struct iwl_he_sta_context_cmd_v3); + break; + default: + IWL_ERR(mvm, "bad STA_HE_CTXT_CMD version %d\n", ver); + return; + } rcu_read_lock(); @@ -1931,97 +2037,25 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, * Initialize the PPE thresholds to "None" (7), as described in Table * 9-262ac of 80211.ax/D3.0. */ - memset(&sta_ctxt_cmd.pkt_ext, 7, sizeof(sta_ctxt_cmd.pkt_ext)); + memset(&sta_ctxt_cmd.pkt_ext, IWL_HE_PKT_EXT_NONE, + sizeof(sta_ctxt_cmd.pkt_ext)); /* If PPE Thresholds exist, parse them into a FW-familiar format. */ if (sta->he_cap.he_cap_elem.phy_cap_info[6] & - IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { - u8 nss = (sta->he_cap.ppe_thres[0] & - IEEE80211_PPE_THRES_NSS_MASK) + 1; - u8 ru_index_bitmap = - (sta->he_cap.ppe_thres[0] & - IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK) >> - IEEE80211_PPE_THRES_RU_INDEX_BITMASK_POS; - u8 *ppe = &sta->he_cap.ppe_thres[0]; - u8 ppe_pos_bit = 7; /* Starting after PPE header */ - - /* - * FW currently supports only nss == MAX_HE_SUPP_NSS - * - * If nss > MAX: we can ignore values we don't support - * If nss < MAX: we can set zeros in other streams - */ - if (nss > MAX_HE_SUPP_NSS) { - IWL_INFO(mvm, "Got NSS = %d - trimming to %d\n", nss, - MAX_HE_SUPP_NSS); - nss = MAX_HE_SUPP_NSS; - } - - for (i = 0; i < nss; i++) { - u8 ru_index_tmp = ru_index_bitmap << 1; - u8 bw; - - for (bw = 0; bw < MAX_HE_CHANNEL_BW_INDX; bw++) { - ru_index_tmp >>= 1; - if (!(ru_index_tmp & 1)) - continue; - - sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i][bw][1] = - iwl_mvm_he_get_ppe_val(ppe, - ppe_pos_bit); - ppe_pos_bit += - IEEE80211_PPE_THRES_INFO_PPET_SIZE; - sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i][bw][0] = - iwl_mvm_he_get_ppe_val(ppe, - ppe_pos_bit); - ppe_pos_bit += - IEEE80211_PPE_THRES_INFO_PPET_SIZE; - } - } - + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta, + &sta_ctxt_cmd.pkt_ext); flags |= STA_CTXT_HE_PACKET_EXT; - } else if (u8_get_bits(sta->he_cap.he_cap_elem.phy_cap_info[9], - IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK) - != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED) { - int low_th = -1; - int high_th = -1; - - /* Take the PPE thresholds from the nominal padding info */ - switch (u8_get_bits(sta->he_cap.he_cap_elem.phy_cap_info[9], - IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK)) { - case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_0US: - low_th = IWL_HE_PKT_EXT_NONE; - high_th = IWL_HE_PKT_EXT_NONE; - break; - case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_8US: - low_th = IWL_HE_PKT_EXT_BPSK; - high_th = IWL_HE_PKT_EXT_NONE; - break; - case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US: - low_th = IWL_HE_PKT_EXT_NONE; - high_th = IWL_HE_PKT_EXT_BPSK; - break; - } - - /* Set the PPE thresholds accordingly */ - if (low_th >= 0 && high_th >= 0) { - struct iwl_he_pkt_ext *pkt_ext = - (struct iwl_he_pkt_ext *)&sta_ctxt_cmd.pkt_ext; - - for (i = 0; i < MAX_HE_SUPP_NSS; i++) { - u8 bw; - - for (bw = 0; bw < MAX_HE_CHANNEL_BW_INDX; - bw++) { - pkt_ext->pkt_ext_qam_th[i][bw][0] = - low_th; - pkt_ext->pkt_ext_qam_th[i][bw][1] = - high_th; - } - } - - flags |= STA_CTXT_HE_PACKET_EXT; - } + /* PPE Thresholds doesn't exist - set the API PPE values + * according to Common Nominal Packet Padding fiels. */ + } else { + u8 nominal_padding = + u8_get_bits(sta->he_cap.he_cap_elem.phy_cap_info[9], + IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); + if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED) + iwl_mvm_set_pkt_ext_from_nominal_padding(&sta_ctxt_cmd.pkt_ext, + nominal_padding, + &flags); } if (sta->he_cap.he_cap_elem.mac_cap_info[2] & @@ -2084,9 +2118,46 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, sta_ctxt_cmd.flags = cpu_to_le32(flags); - if (iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(STA_HE_CTXT_CMD, - DATA_PATH_GROUP, 0), - 0, size, &sta_ctxt_cmd)) + if (ver < 3) { + /* fields before pkt_ext */ + BUILD_BUG_ON(offsetof(typeof(sta_ctxt_cmd), pkt_ext) != + offsetof(typeof(sta_ctxt_cmd_v2), pkt_ext)); + memcpy(&sta_ctxt_cmd_v2, &sta_ctxt_cmd, + offsetof(typeof(sta_ctxt_cmd), pkt_ext)); + + /* pkt_ext */ + for (i = 0; + i < ARRAY_SIZE(sta_ctxt_cmd_v2.pkt_ext.pkt_ext_qam_th); + i++) { + u8 bw; + + for (bw = 0; + bw < ARRAY_SIZE(sta_ctxt_cmd_v2.pkt_ext.pkt_ext_qam_th[i]); + bw++) { + BUILD_BUG_ON(sizeof(sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i][bw]) != + sizeof(sta_ctxt_cmd_v2.pkt_ext.pkt_ext_qam_th[i][bw])); + + memcpy(&sta_ctxt_cmd_v2.pkt_ext.pkt_ext_qam_th[i][bw], + &sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i][bw], + sizeof(sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i][bw])); + } + } + + /* fields after pkt_ext */ + BUILD_BUG_ON(sizeof(sta_ctxt_cmd) - + offsetofend(typeof(sta_ctxt_cmd), pkt_ext) != + sizeof(sta_ctxt_cmd_v2) - + offsetofend(typeof(sta_ctxt_cmd_v2), pkt_ext)); + memcpy((u8 *)&sta_ctxt_cmd_v2 + + offsetofend(typeof(sta_ctxt_cmd_v2), pkt_ext), + (u8 *)&sta_ctxt_cmd + + offsetofend(typeof(sta_ctxt_cmd), pkt_ext), + sizeof(sta_ctxt_cmd) - + offsetofend(typeof(sta_ctxt_cmd), pkt_ext)); + sta_ctxt_cmd_v2.reserved3 = 0; + } + + if (iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, size, cmd)) IWL_ERR(mvm, "Failed to config FW to work HE!\n"); } @@ -2301,11 +2372,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, /* * We received a beacon from the associated AP so * remove the session protection. - * A firmware with the new API will remove it automatically. */ - if (!fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) - iwl_mvm_stop_session_protection(mvm, vif); + iwl_mvm_stop_session_protection(mvm, vif); iwl_mvm_sf_update(mvm, vif, false); WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); @@ -2949,7 +3017,7 @@ static void iwl_mvm_reset_cca_40mhz_workaround(struct iwl_mvm *mvm, if (he_cap) { /* we know that ours is writable */ - struct ieee80211_sta_he_cap *he = (void *)he_cap; + struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap; he->he_cap_elem.phy_cap_info[0] |= IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; @@ -3413,12 +3481,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw, /* support HW crypto on TX */ return 0; default: - /* currently FW supports only one optional cipher scheme */ - if (hw->n_cipher_schemes && - hw->cipher_schemes->cipher == key->cipher) - key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; - else - return -EOPNOTSUPP; + return -EOPNOTSUPP; } switch (cmd) { @@ -3801,8 +3864,7 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw, if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT)) { /* Use aux roc framework (HS20) */ - if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - ADD_STA, 0) >= 12) { + if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) >= 12) { u32 lmac_id; lmac_id = iwl_mvm_get_lmac_id(mvm->fw, @@ -4605,6 +4667,15 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, break; case NL80211_IFTYPE_STATION: + /* + * In the new flow FW is in charge of timing the switch so there + * is no need for all of this + */ + if (iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP, + CHANNEL_SWITCH_ERROR_NOTIF, + 0)) + break; + /* * We haven't configured the firmware to be associated yet since * we don't know the dtim period. In this case, the firmware can't @@ -4676,6 +4747,14 @@ static void iwl_mvm_channel_switch_rx_beacon(struct ieee80211_hw *hw, .cs_mode = chsw->block_tx, }; + /* + * In the new flow FW is in charge of timing the switch so there is no + * need for all of this + */ + if (iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP, + CHANNEL_SWITCH_ERROR_NOTIF, 0)) + return; + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CS_MODIFY)) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index d78f40730594..c6bc85d4600a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2020 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1064,7 +1064,6 @@ struct iwl_mvm { u32 ciphers[IWL_MVM_NUM_CIPHERS]; - struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; struct cfg80211_ftm_responder_stats ftm_resp_stats; struct { @@ -1086,7 +1085,6 @@ struct iwl_mvm { } cmd_ver; struct ieee80211_vif *nan_vif; -#define IWL_MAX_BAID 32 struct iwl_mvm_baid_data __rcu *baid_map[IWL_MAX_BAID]; /* @@ -1106,6 +1104,8 @@ struct iwl_mvm { unsigned long last_6ghz_passive_scan_jiffies; unsigned long last_reset_or_resume_time_jiffies; + + bool sta_remove_requires_queue_remove; }; /* Extract MVM priv from op_mode and _hw */ @@ -1671,6 +1671,8 @@ void iwl_mvm_rx_missed_vap_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_channel_switch_start_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); /* Bindings */ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -1932,10 +1934,6 @@ static inline u32 iwl_mvm_flushable_queues(struct iwl_mvm *mvm) void iwl_mvm_stop_device(struct iwl_mvm *mvm); -/* Re-configure the SCD for a queue that has already been configured */ -int iwl_mvm_reconfig_scd(struct iwl_mvm *mvm, int queue, int fifo, int sta_id, - int tid, int frame_limit, u16 ssn); - /* Thermal management and CT-kill */ void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff); void iwl_mvm_temp_notif(struct iwl_mvm *mvm, @@ -2085,6 +2083,8 @@ void iwl_mvm_sta_add_debugfs(struct ieee80211_hw *hw, int iwl_rfi_send_config_cmd(struct iwl_mvm *mvm, struct iwl_rfi_lut_entry *rfi_table); struct iwl_rfi_freq_table_resp_cmd *iwl_rfi_get_freq_table(struct iwl_mvm *mvm); +void iwl_rfi_deactivate_notif_handler(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); static inline u8 iwl_mvm_phy_band_from_nl80211(enum nl80211_band band) { @@ -2159,8 +2159,7 @@ iwl_mvm_set_chan_info_chandef(struct iwl_mvm *mvm, static inline int iwl_umac_scan_get_max_profiles(const struct iwl_fw *fw) { - u8 ver = iwl_fw_lookup_cmd_ver(fw, IWL_ALWAYS_LONG_GROUP, - SCAN_OFFLOAD_UPDATE_PROFILES_CMD, + u8 ver = iwl_fw_lookup_cmd_ver(fw, SCAN_OFFLOAD_UPDATE_PROFILES_CMD, IWL_FW_CMD_VER_UNKNOWN); return (ver == IWL_FW_CMD_VER_UNKNOWN || ver < 3) ? IWL_SCAN_MAX_PROFILES : IWL_SCAN_MAX_PROFILES_V2; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c index 41880517e8bb..c7dabc6b3765 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c @@ -47,8 +47,7 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, struct iwl_proto_offload_cmd_common *common; u32 enabled = 0, size; u32 capa_flags = mvm->fw->ucode_capa.flags; - int ver = iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - PROT_OFFLOAD_CONFIG_CMD, 0); + int ver = iwl_fw_lookup_cmd_ver(mvm->fw, hcmd.id, 0); #if IS_ENABLED(CONFIG_IPV6) struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 1f8b97995b94..b2f33ebdf485 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -24,6 +24,7 @@ #include "iwl-prph.h" #include "rs.h" #include "fw/api/scan.h" +#include "fw/api/rfi.h" #include "time-event.h" #include "fw-api.h" #include "fw/acpi.h" @@ -32,6 +33,7 @@ #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IWLWIFI); static const struct iwl_op_mode_ops iwl_mvm_ops; static const struct iwl_op_mode_ops iwl_mvm_ops_mq; @@ -191,7 +193,7 @@ static void iwl_mvm_rx_monitor_notif(struct iwl_mvm *mvm, if (he_cap) { /* we know that ours is writable */ - struct ieee80211_sta_he_cap *he = (void *)he_cap; + struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap; WARN_ON(!he->has_he); WARN_ON(!(he->he_cap_elem.phy_cap_info[0] & @@ -235,7 +237,8 @@ static void iwl_mvm_rx_thermal_dual_chain_req(struct iwl_mvm *mvm, */ mvm->fw_static_smps_request = req->event == cpu_to_le32(THERMAL_DUAL_CHAIN_REQ_DISABLE); - ieee80211_iterate_interfaces(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + ieee80211_iterate_interfaces(mvm->hw, + IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER, iwl_mvm_intf_dual_chain_req, NULL); } @@ -382,6 +385,10 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER_GRP(MAC_CONF_GROUP, CHANNEL_SWITCH_START_NOTIF, iwl_mvm_channel_switch_start_notif, RX_HANDLER_SYNC, struct iwl_channel_switch_start_notif), + RX_HANDLER_GRP(MAC_CONF_GROUP, CHANNEL_SWITCH_ERROR_NOTIF, + iwl_mvm_channel_switch_error_notif, + RX_HANDLER_ASYNC_UNLOCKED, + struct iwl_channel_switch_error_notif), RX_HANDLER_GRP(DATA_PATH_GROUP, MONITOR_NOTIF, iwl_mvm_rx_monitor_notif, RX_HANDLER_ASYNC_LOCKED, struct iwl_datapath_monitor_notif), @@ -390,6 +397,10 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { iwl_mvm_rx_thermal_dual_chain_req, RX_HANDLER_ASYNC_LOCKED, struct iwl_thermal_dual_chain_request), + + RX_HANDLER_GRP(SYSTEM_GROUP, RFI_DEACTIVATE_NOTIF, + iwl_rfi_deactivate_notif_handler, RX_HANDLER_ASYNC_UNLOCKED, + struct iwl_rfi_deactivate_notif), }; #undef RX_HANDLER #undef RX_HANDLER_GRP @@ -443,7 +454,6 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = { HCMD_NAME(POWER_TABLE_CMD), HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), HCMD_NAME(REPLY_THERMAL_MNG_BACKOFF), - HCMD_NAME(DC2DC_CONFIG_CMD), HCMD_NAME(NVM_ACCESS_CMD), HCMD_NAME(BEACON_NOTIFICATION), HCMD_NAME(BEACON_TEMPLATE_CMD), @@ -499,6 +509,7 @@ static const struct iwl_hcmd_names iwl_mvm_system_names[] = { HCMD_NAME(RFI_CONFIG_CMD), HCMD_NAME(RFI_GET_FREQ_TABLE_CMD), HCMD_NAME(SYSTEM_FEATURES_CONTROL_CMD), + HCMD_NAME(RFI_DEACTIVATE_NOTIF), }; /* Please keep this array *SORTED* by hex value. @@ -535,6 +546,7 @@ static const struct iwl_hcmd_names iwl_mvm_data_path_names[] = { HCMD_NAME(RFH_QUEUE_CONFIG_CMD), HCMD_NAME(TLC_MNG_CONFIG_CMD), HCMD_NAME(CHEST_COLLECTOR_FILTER_CONFIG_CMD), + HCMD_NAME(SCD_QUEUE_CONFIG_CMD), HCMD_NAME(MONITOR_NOTIF), HCMD_NAME(THERMAL_DUAL_CHAIN_REQUEST), HCMD_NAME(STA_PM_NOTIF), @@ -633,13 +645,11 @@ static void iwl_mvm_tx_unblock_dwork(struct work_struct *work) mutex_unlock(&mvm->mutex); } -static int iwl_mvm_fwrt_dump_start(void *ctx) +static void iwl_mvm_fwrt_dump_start(void *ctx) { struct iwl_mvm *mvm = ctx; mutex_lock(&mvm->mutex); - - return 0; } static void iwl_mvm_fwrt_dump_end(void *ctx) @@ -1076,12 +1086,12 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (!hw) return NULL; - hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; if (cfg->max_tx_agg_size) hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size; else - hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; + hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; op_mode = hw->priv; @@ -1244,6 +1254,14 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans_cfg.fw_reset_handshake = fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_FW_RESET_HANDSHAKE); + trans_cfg.queue_alloc_cmd_ver = + iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(DATA_PATH_GROUP, + SCD_QUEUE_CONFIG_CMD), + 0); + mvm->sta_remove_requires_queue_remove = + trans_cfg.queue_alloc_cmd_ver > 0; + /* Configure transport layer */ iwl_trans_configure(mvm->trans, &trans_cfg); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c index 9af40b0fa37a..a3cefbc43e80 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2017 Intel Deutschland GmbH */ @@ -158,8 +158,7 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm, iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef); /* we only support RLC command version 2 */ - if (iwl_fw_lookup_cmd_ver(mvm->fw, DATA_PATH_GROUP, - RLC_CONFIG_CMD, 0) < 2) + if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), 0) < 2) iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &cmd->rxchain_info, chains_static, chains_dynamic); } @@ -172,8 +171,7 @@ static int iwl_mvm_phy_send_rlc(struct iwl_mvm *mvm, .phy_id = cpu_to_le32(ctxt->id), }; - if (iwl_fw_lookup_cmd_ver(mvm->fw, DATA_PATH_GROUP, - RLC_CONFIG_CMD, 0) < 2) + if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), 0) < 2) return 0; BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_DRIVER_FORCE != @@ -209,8 +207,7 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, u32 action) { int ret; - int ver = iwl_fw_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP, - PHY_CONTEXT_CMD, 1); + int ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1); if (ver == 3 || ver == 4) { struct iwl_phy_context_cmd cmd = {}; @@ -301,8 +298,7 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, lockdep_assert_held(&mvm->mutex); - if (iwl_fw_lookup_cmd_ver(mvm->fw, DATA_PATH_GROUP, - RLC_CONFIG_CMD, 0) >= 2 && + if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), 0) >= 2 && ctxt->channel == chandef->chan && ctxt->width == chandef->width && ctxt->center_freq1 == chandef->center_freq1) @@ -349,19 +345,32 @@ void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) * otherwise we might not be able to reuse this phy. */ if (ctxt->ref == 0) { - struct ieee80211_channel *chan; + struct ieee80211_channel *chan = NULL; struct cfg80211_chan_def chandef; - struct ieee80211_supported_band *sband = NULL; - enum nl80211_band band = NL80211_BAND_2GHZ; + struct ieee80211_supported_band *sband; + enum nl80211_band band; + int channel; - while (!sband && band < NUM_NL80211_BANDS) - sband = mvm->hw->wiphy->bands[band++]; + for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { + sband = mvm->hw->wiphy->bands[band]; - if (WARN_ON(!sband)) + if (!sband) + continue; + + for (channel = 0; channel < sband->n_channels; channel++) + if (!(sband->channels[channel].flags & + IEEE80211_CHAN_DISABLED)) { + chan = &sband->channels[channel]; + break; + } + + if (chan) + break; + } + + if (WARN_ON(!chan)) return; - chan = &sband->channels[0]; - cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); iwl_mvm_phy_ctxt_changed(mvm, ctxt, &chandef, 1, 1); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c index 3d0166df2002..c862bd243b55 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018 Intel Corporation + * Copyright (C) 2012-2014, 2018, 2021 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rfi.c b/drivers/net/wireless/intel/iwlwifi/mvm/rfi.c index f054ce76bed5..bb77bc9aa821 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rfi.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rfi.c @@ -125,12 +125,19 @@ struct iwl_rfi_freq_table_resp_cmd *iwl_rfi_get_freq_table(struct iwl_mvm *mvm) if (WARN_ON_ONCE(iwl_rx_packet_payload_len(cmd.resp_pkt) != resp_size)) return ERR_PTR(-EIO); - resp = kzalloc(resp_size, GFP_KERNEL); + resp = kmemdup(cmd.resp_pkt->data, resp_size, GFP_KERNEL); if (!resp) return ERR_PTR(-ENOMEM); - memcpy(resp, cmd.resp_pkt->data, resp_size); - iwl_free_resp(&cmd); return resp; } + +void iwl_rfi_deactivate_notif_handler(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_rfi_deactivate_notif *notif = (void *)pkt->data; + + IWL_INFO(mvm, "RFIm is deactivated, reason = %d\n", notif->reason); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index 66808c55aa0e..9830d2663689 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2022 Intel Corporation */ #include "rs.h" #include "fw-api.h" @@ -97,7 +97,10 @@ static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm, if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[3] & - IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK)) + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK && + sband->iftype_data && + sband->iftype_data->he_cap.he_cap_elem.phy_cap_info[3] & + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK)) flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK; return flags; @@ -420,7 +423,7 @@ void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct ieee80211_hw *hw = mvm->hw; struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw; - u32 cmd_id = iwl_cmd_id(TLC_MNG_CONFIG_CMD, DATA_PATH_GROUP, 0); + u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD); struct ieee80211_supported_band *sband = hw->wiphy->bands[band]; u16 max_amsdu_len = rs_fw_get_max_amsdu_len(sta); struct iwl_tlc_config_cmd_v4 cfg_cmd = { @@ -449,8 +452,22 @@ void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, */ sta->max_amsdu_len = max_amsdu_len; - cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, DATA_PATH_GROUP, - TLC_MNG_CONFIG_CMD, 0); + cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(DATA_PATH_GROUP, + TLC_MNG_CONFIG_CMD), + 0); + IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, sta_id=%d, max_ch_width=%d, mode=%d\n", + cfg_cmd.sta_id, cfg_cmd.max_ch_width, cfg_cmd.mode); + IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, chains=0x%X, ch_wid_supp=%d, flags=0x%X\n", + cfg_cmd.chains, cfg_cmd.sgi_ch_width_supp, cfg_cmd.flags); + IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, mpdu_len=%d, no_ht_rate=0x%X, tx_op=%d\n", + cfg_cmd.max_mpdu_len, cfg_cmd.non_ht_rates, cfg_cmd.max_tx_op); + IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, ht_rate[0][0]=0x%X, ht_rate[1][0]=0x%X\n", + cfg_cmd.ht_rates[0][0], cfg_cmd.ht_rates[1][0]); + IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, ht_rate[0][1]=0x%X, ht_rate[1][1]=0x%X\n", + cfg_cmd.ht_rates[0][1], cfg_cmd.ht_rates[1][1]); + IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, ht_rate[0][2]=0x%X, ht_rate[1][2]=0x%X\n", + cfg_cmd.ht_rates[0][2], cfg_cmd.ht_rates[1][2]); if (cmd_ver == 4) { ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_ASYNC, sizeof(cfg_cmd), &cfg_cmd); @@ -474,8 +491,9 @@ void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u16 cmd_size = sizeof(cfg_cmd_v3); /* In old versions of the API the struct is 4 bytes smaller */ - if (iwl_fw_lookup_cmd_ver(mvm->fw, DATA_PATH_GROUP, - TLC_MNG_CONFIG_CMD, 0) < 3) + if (iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(DATA_PATH_GROUP, + TLC_MNG_CONFIG_CMD), 0) < 3) cmd_size -= 4; ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_ASYNC, cmd_size, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index f4d02f9fe16d..62114616317c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -454,8 +454,6 @@ static const u16 expected_tpt_mimo2_160MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 971, 0, 1925, 2861, 3779, 5574, 7304, 8147, 8976, 10592, 11640}, }; -#define MCS_INDEX_PER_STREAM (8) - static const char *rs_pretty_lq_type(enum iwl_table_type type) { static const char * const lq_types[] = { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 64446a11ef98..78198da7e55b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -83,8 +83,8 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, fraglen = len - hdrlen; if (fraglen) { - int offset = (void *)hdr + hdrlen - - rxb_addr(rxb) + rxb_offset(rxb); + int offset = (u8 *)hdr + hdrlen - + (u8 *)rxb_addr(rxb) + rxb_offset(rxb); skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, fraglen, rxb->truesize); @@ -640,7 +640,7 @@ static void iwl_mvm_stat_iterator_all_macs(void *_data, u8 *mac, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); u16 vif_id = mvmvif->id; - if (WARN_ONCE(vif_id > MAC_INDEX_AUX, "invalid vif id: %d", vif_id)) + if (WARN_ONCE(vif_id >= MAC_INDEX_AUX, "invalid vif id: %d", vif_id)) return; if (vif->type != NL80211_IFTYPE_STATION) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 295629c5c035..2c43a9989783 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -209,13 +209,16 @@ static int iwl_mvm_create_skb(struct iwl_mvm *mvm, struct sk_buff *skb, shdr->type != htons(ETH_P_PAE) && shdr->type != htons(ETH_P_TDLS)))) skb->ip_summed = CHECKSUM_NONE; + else + /* mac80211 assumes full CSUM including SNAP header */ + skb_postpush_rcsum(skb, shdr, sizeof(*shdr)); } fraglen = len - headlen; if (fraglen) { - int offset = (void *)hdr + headlen + pad_len - - rxb_addr(rxb) + rxb_offset(rxb); + int offset = (u8 *)hdr + headlen + pad_len - + (u8 *)rxb_addr(rxb) + rxb_offset(rxb); skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, fraglen, rxb->truesize); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 5f92a09db374..a4077053e374 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -20,7 +20,6 @@ #define IWL_SCAN_DWELL_FRAGMENTED 44 #define IWL_SCAN_DWELL_EXTENDED 90 #define IWL_SCAN_NUM_OF_FRAGS 3 -#define IWL_SCAN_LAST_2_4_CHN 14 /* adaptive dwell max budget time [TU] for full scan */ #define IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN 300 @@ -98,6 +97,7 @@ struct iwl_mvm_scan_params { u32 n_6ghz_params; bool scan_6ghz; bool enable_6ghz_passive; + bool respect_p2p_go, respect_p2p_go_hb; }; static inline void *iwl_mvm_get_scan_req_umac_data(struct iwl_mvm *mvm) @@ -169,17 +169,6 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum nl80211_band band, return cpu_to_le32(IWL_RATE_6M_PLCP | tx_ant); } -static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int *global_cnt = data; - - if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt && - mvmvif->phy_ctxt->id < NUM_PHY_CTX) - *global_cnt += 1; -} - static enum iwl_mvm_traffic_load iwl_mvm_get_traffic_load(struct iwl_mvm *mvm) { return mvm->tcm.result.global_load; @@ -191,26 +180,31 @@ iwl_mvm_get_traffic_load_band(struct iwl_mvm *mvm, enum nl80211_band band) return mvm->tcm.result.band_load[band]; } -struct iwl_is_dcm_with_go_iterator_data { +struct iwl_mvm_scan_iter_data { + u32 global_cnt; struct ieee80211_vif *current_vif; bool is_dcm_with_p2p_go; }; -static void iwl_mvm_is_dcm_with_go_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) +static void iwl_mvm_scan_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) { - struct iwl_is_dcm_with_go_iterator_data *data = _data; - struct iwl_mvm_vif *other_mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_vif *curr_mvmvif = - iwl_mvm_vif_from_mac80211(data->current_vif); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_scan_iter_data *data = _data; + struct iwl_mvm_vif *curr_mvmvif; - /* exclude the given vif */ - if (vif == data->current_vif) + if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt && + mvmvif->phy_ctxt->id < NUM_PHY_CTX) + data->global_cnt += 1; + + if (!data->current_vif || vif == data->current_vif) return; + curr_mvmvif = iwl_mvm_vif_from_mac80211(data->current_vif); + if (vif->type == NL80211_IFTYPE_AP && vif->p2p && - other_mvmvif->phy_ctxt && curr_mvmvif->phy_ctxt && - other_mvmvif->phy_ctxt->id != curr_mvmvif->phy_ctxt->id) + mvmvif->phy_ctxt && curr_mvmvif->phy_ctxt && + mvmvif->phy_ctxt->id != curr_mvmvif->phy_ctxt->id) data->is_dcm_with_p2p_go = true; } @@ -220,13 +214,18 @@ iwl_mvm_scan_type _iwl_mvm_get_scan_type(struct iwl_mvm *mvm, enum iwl_mvm_traffic_load load, bool low_latency) { - int global_cnt = 0; + struct iwl_mvm_scan_iter_data data = { + .current_vif = vif, + .is_dcm_with_p2p_go = false, + .global_cnt = 0, + }; ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_scan_condition_iterator, - &global_cnt); - if (!global_cnt) + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_scan_iterator, + &data); + + if (!data.global_cnt) return IWL_SCAN_TYPE_UNASSOC; if (fw_has_api(&mvm->fw->ucode_capa, @@ -235,23 +234,14 @@ iwl_mvm_scan_type _iwl_mvm_get_scan_type(struct iwl_mvm *mvm, (!vif || vif->type != NL80211_IFTYPE_P2P_DEVICE)) return IWL_SCAN_TYPE_FRAGMENTED; - /* in case of DCM with GO where BSS DTIM interval < 220msec + /* + * in case of DCM with GO where BSS DTIM interval < 220msec * set all scan requests as fast-balance scan - * */ + */ if (vif && vif->type == NL80211_IFTYPE_STATION && - vif->bss_conf.dtim_period < 220) { - struct iwl_is_dcm_with_go_iterator_data data = { - .current_vif = vif, - .is_dcm_with_p2p_go = false, - }; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_is_dcm_with_go_iterator, - &data); - if (data.is_dcm_with_p2p_go) - return IWL_SCAN_TYPE_FAST_BALANCE; - } + vif->bss_conf.dtim_period < 220 && + data.is_dcm_with_p2p_go) + return IWL_SCAN_TYPE_FAST_BALANCE; } if (load >= IWL_MVM_TRAFFIC_MEDIUM || low_latency) @@ -651,9 +641,7 @@ static void iwl_mvm_scan_fill_tx_cmd(struct iwl_mvm *mvm, NL80211_BAND_2GHZ, no_cck); - if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - ADD_STA, - 0) < 12) { + if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) < 12) { tx_cmd[0].sta_id = mvm->aux_sta.sta_id; tx_cmd[1].sta_id = mvm->aux_sta.sta_id; @@ -1090,8 +1078,7 @@ static void iwl_mvm_fill_scan_config_v1(struct iwl_mvm *mvm, void *config, memcpy(&cfg->mac_addr, &mvm->addresses[0].addr, ETH_ALEN); /* This function should not be called when using ADD_STA ver >=12 */ - WARN_ON_ONCE(iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - ADD_STA, 0) >= 12); + WARN_ON_ONCE(iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) >= 12); cfg->bcast_sta_id = mvm->aux_sta.sta_id; cfg->channel_flags = channel_flags; @@ -1142,8 +1129,7 @@ static void iwl_mvm_fill_scan_config_v2(struct iwl_mvm *mvm, void *config, memcpy(&cfg->mac_addr, &mvm->addresses[0].addr, ETH_ALEN); /* This function should not be called when using ADD_STA ver >=12 */ - WARN_ON_ONCE(iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - ADD_STA, 0) >= 12); + WARN_ON_ONCE(iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) >= 12); cfg->bcast_sta_id = mvm->aux_sta.sta_id; cfg->channel_flags = channel_flags; @@ -1156,7 +1142,7 @@ static int iwl_mvm_legacy_config_scan(struct iwl_mvm *mvm) void *cfg; int ret, cmd_size; struct iwl_host_cmd cmd = { - .id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0), + .id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, SCAN_CFG_CMD), }; enum iwl_mvm_scan_type type; enum iwl_mvm_scan_type hb_type = IWL_SCAN_TYPE_NOT_SET; @@ -1247,7 +1233,7 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) { struct iwl_scan_config cfg; struct iwl_host_cmd cmd = { - .id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0), + .id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, SCAN_CFG_CMD), .len[0] = sizeof(cfg), .data[0] = &cfg, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, @@ -1258,11 +1244,9 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) memset(&cfg, 0, sizeof(cfg)); - if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - ADD_STA, 0) < 12) { + if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) < 12) { cfg.bcast_sta_id = mvm->aux_sta.sta_id; - } else if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - SCAN_CFG_CMD, 0) < 5) { + } else if (iwl_fw_lookup_cmd_ver(mvm->fw, SCAN_CFG_CMD, 0) < 5) { /* * Fw doesn't use this sta anymore. Deprecated on SCAN_CFG_CMD * version 5. @@ -1662,7 +1646,7 @@ iwl_mvm_umac_scan_cfg_channels_v6(struct iwl_mvm *mvm, } } -static int +static void iwl_mvm_umac_scan_fill_6g_chan_list(struct iwl_mvm *mvm, struct iwl_mvm_scan_params *params, struct iwl_scan_probe_params_v4 *pp) @@ -1731,31 +1715,40 @@ iwl_mvm_umac_scan_fill_6g_chan_list(struct iwl_mvm *mvm, pp->short_ssid_num = idex_s; pp->bssid_num = idex_b; - return 0; } /* TODO: this function can be merged with iwl_mvm_scan_umac_fill_ch_p_v6 */ -static void -iwl_mvm_umac_scan_cfg_channels_v6_6g(struct iwl_mvm_scan_params *params, +static u32 +iwl_mvm_umac_scan_cfg_channels_v6_6g(struct iwl_mvm *mvm, + struct iwl_mvm_scan_params *params, u32 n_channels, struct iwl_scan_probe_params_v4 *pp, struct iwl_scan_channel_params_v6 *cp, enum nl80211_iftype vif_type) { - struct iwl_scan_channel_cfg_umac *channel_cfg = cp->channel_config; int i; struct cfg80211_scan_6ghz_params *scan_6ghz_params = params->scan_6ghz_params; + u32 ch_cnt; - for (i = 0; i < params->n_channels; i++) { + for (i = 0, ch_cnt = 0; i < params->n_channels; i++) { struct iwl_scan_channel_cfg_umac *cfg = - &cp->channel_config[i]; + &cp->channel_config[ch_cnt]; u32 s_ssid_bitmap = 0, bssid_bitmap = 0, flags = 0; u8 j, k, s_max = 0, b_max = 0, n_used_bssid_entries; bool force_passive, found = false, allow_passive = true, unsolicited_probe_on_chan = false, psc_no_listen = false; + /* + * Avoid performing passive scan on non PSC channels unless the + * scan is specifically a passive scan, i.e., no SSIDs + * configured in the scan command. + */ + if (!cfg80211_channel_is_psc(params->channels[i]) && + !params->n_6ghz_params && params->n_ssids) + continue; + cfg->v1.channel_num = params->channels[i]->hw_value; cfg->v2.band = 2; cfg->v2.iter_count = 1; @@ -1875,8 +1868,16 @@ iwl_mvm_umac_scan_cfg_channels_v6_6g(struct iwl_mvm_scan_params *params, else flags |= bssid_bitmap | (s_ssid_bitmap << 16); - channel_cfg[i].flags |= cpu_to_le32(flags); + cfg->flags |= cpu_to_le32(flags); + ch_cnt++; } + + if (params->n_channels > ch_cnt) + IWL_DEBUG_SCAN(mvm, + "6GHz: reducing number channels: (%u->%u)\n", + params->n_channels, ch_cnt); + + return ch_cnt; } static u8 iwl_mvm_scan_umac_chan_flags_v2(struct iwl_mvm *mvm, @@ -1893,9 +1894,25 @@ static u8 iwl_mvm_scan_umac_chan_flags_v2(struct iwl_mvm *mvm, IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; /* set fragmented ebs for fragmented scan on HB channels */ - if (iwl_mvm_is_scan_fragmented(params->hb_type)) + if ((!iwl_mvm_is_cdb_supported(mvm) && + iwl_mvm_is_scan_fragmented(params->type)) || + (iwl_mvm_is_cdb_supported(mvm) && + iwl_mvm_is_scan_fragmented(params->hb_type))) flags |= IWL_SCAN_CHANNEL_FLAG_EBS_FRAG; + /* + * force EBS in case the scan is a fragmented and there is a need to take P2P + * GO operation into consideration during scan operation. + */ + if ((!iwl_mvm_is_cdb_supported(mvm) && + iwl_mvm_is_scan_fragmented(params->type) && params->respect_p2p_go) || + (iwl_mvm_is_cdb_supported(mvm) && + iwl_mvm_is_scan_fragmented(params->hb_type) && + params->respect_p2p_go_hb)) { + IWL_DEBUG_SCAN(mvm, "Respect P2P GO. Force EBS\n"); + flags |= IWL_SCAN_CHANNEL_FLAG_FORCE_EBS; + } + return flags; } @@ -2046,6 +2063,26 @@ static u16 iwl_mvm_scan_umac_flags_v2(struct iwl_mvm *mvm, return flags; } +static u8 iwl_mvm_scan_umac_flags2(struct iwl_mvm *mvm, + struct iwl_mvm_scan_params *params, + struct ieee80211_vif *vif, int type) +{ + u8 flags = 0; + + if (iwl_mvm_is_cdb_supported(mvm)) { + if (params->respect_p2p_go) + flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_LB; + if (params->respect_p2p_go_hb) + flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_HB; + } else { + if (params->respect_p2p_go) + flags = IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_LB | + IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_HB; + } + + return flags; +} + static u16 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm, struct iwl_mvm_scan_params *params, struct ieee80211_vif *vif) @@ -2164,7 +2201,7 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_scan_req_umac *cmd = mvm->scan_cmd; struct iwl_scan_umac_chan_param *chan_param; void *cmd_data = iwl_mvm_get_scan_req_umac_data(mvm); - void *sec_part = cmd_data + sizeof(struct iwl_scan_channel_cfg_umac) * + void *sec_part = (u8 *)cmd_data + sizeof(struct iwl_scan_channel_cfg_umac) * mvm->fw->ucode_capa.n_scan_channels; struct iwl_scan_req_umac_tail_v2 *tail_v2 = (struct iwl_scan_req_umac_tail_v2 *)sec_part; @@ -2248,13 +2285,17 @@ iwl_mvm_scan_umac_fill_general_p_v11(struct iwl_mvm *mvm, struct iwl_mvm_scan_params *params, struct ieee80211_vif *vif, struct iwl_scan_general_params_v11 *gp, - u16 gen_flags) + u16 gen_flags, u8 gen_flags2) { struct iwl_mvm_vif *scan_vif = iwl_mvm_vif_from_mac80211(vif); iwl_mvm_scan_umac_dwell_v11(mvm, gp, params); + IWL_DEBUG_SCAN(mvm, "Gerenal: flags=0x%x, flags2=0x%x\n", + gen_flags, gen_flags2); + gp->flags = cpu_to_le16(gen_flags); + gp->flags2 = gen_flags2; if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1) gp->num_of_fragments[SCAN_LB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS; @@ -2358,7 +2399,7 @@ static int iwl_mvm_scan_umac_v12(struct iwl_mvm *mvm, struct ieee80211_vif *vif, gen_flags = iwl_mvm_scan_umac_flags_v2(mvm, params, vif, type); iwl_mvm_scan_umac_fill_general_p_v11(mvm, params, vif, &scan_p->general_params, - gen_flags); + gen_flags, 0); ret = iwl_mvm_fill_scan_sched_params(params, scan_p->periodic_params.schedule, @@ -2384,6 +2425,7 @@ static int iwl_mvm_scan_umac_v14_and_above(struct iwl_mvm *mvm, struct iwl_scan_probe_params_v4 *pb = &scan_p->probe_params; int ret; u16 gen_flags; + u8 gen_flags2; u32 bitmap_ssid = 0; mvm->scan_uid_status[uid] = type; @@ -2392,9 +2434,15 @@ static int iwl_mvm_scan_umac_v14_and_above(struct iwl_mvm *mvm, cmd->uid = cpu_to_le32(uid); gen_flags = iwl_mvm_scan_umac_flags_v2(mvm, params, vif, type); + + if (version >= 15) + gen_flags2 = iwl_mvm_scan_umac_flags2(mvm, params, vif, type); + else + gen_flags2 = 0; + iwl_mvm_scan_umac_fill_general_p_v11(mvm, params, vif, &scan_p->general_params, - gen_flags); + gen_flags, gen_flags2); ret = iwl_mvm_fill_scan_sched_params(params, scan_p->periodic_params.schedule, @@ -2417,14 +2465,16 @@ static int iwl_mvm_scan_umac_v14_and_above(struct iwl_mvm *mvm, cp->n_aps_override[0] = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY; cp->n_aps_override[1] = IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS; - ret = iwl_mvm_umac_scan_fill_6g_chan_list(mvm, params, pb); - if (ret) - return ret; + iwl_mvm_umac_scan_fill_6g_chan_list(mvm, params, pb); + + cp->count = iwl_mvm_umac_scan_cfg_channels_v6_6g(mvm, params, + params->n_channels, + pb, cp, vif->type); + if (!cp->count) { + mvm->scan_uid_status[uid] = 0; + return -EINVAL; + } - iwl_mvm_umac_scan_cfg_channels_v6_6g(params, - params->n_channels, - pb, cp, vif->type); - cp->count = params->n_channels; if (!params->n_ssids || (params->n_ssids == 1 && !params->ssids[0].ssid_len)) cp->flags |= IWL_SCAN_CHANNEL_FLAG_6G_PSC_NO_FILTER; @@ -2588,10 +2638,9 @@ static int iwl_mvm_build_scan_cmd(struct iwl_mvm *mvm, if (uid < 0) return uid; - hcmd->id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); + hcmd->id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, SCAN_REQ_UMAC); - scan_ver = iwl_fw_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP, - SCAN_REQ_UMAC, + scan_ver = iwl_fw_lookup_cmd_ver(mvm->fw, SCAN_REQ_UMAC, IWL_FW_CMD_VER_UNKNOWN); for (i = 0; i < ARRAY_SIZE(iwl_scan_umac_handlers); i++) { @@ -2611,6 +2660,85 @@ static int iwl_mvm_build_scan_cmd(struct iwl_mvm *mvm, return uid; } +struct iwl_mvm_scan_respect_p2p_go_iter_data { + struct ieee80211_vif *current_vif; + bool p2p_go; + enum nl80211_band band; +}; + +static void iwl_mvm_scan_respect_p2p_go_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_scan_respect_p2p_go_iter_data *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + /* exclude the given vif */ + if (vif == data->current_vif) + return; + + if (vif->type == NL80211_IFTYPE_AP && vif->p2p && + mvmvif->phy_ctxt->id < NUM_PHY_CTX && + (data->band == NUM_NL80211_BANDS || + mvmvif->phy_ctxt->channel->band == data->band)) + data->p2p_go = true; +} + +static bool _iwl_mvm_get_respect_p2p_go(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool low_latency, + enum nl80211_band band) +{ + struct iwl_mvm_scan_respect_p2p_go_iter_data data = { + .current_vif = vif, + .p2p_go = false, + .band = band, + }; + + if (!low_latency) + return false; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_scan_respect_p2p_go_iter, + &data); + + return data.p2p_go; +} + +static bool iwl_mvm_get_respect_p2p_go_band(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + enum nl80211_band band) +{ + bool low_latency = iwl_mvm_low_latency_band(mvm, band); + + return _iwl_mvm_get_respect_p2p_go(mvm, vif, low_latency, band); +} + +static bool iwl_mvm_get_respect_p2p_go(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + bool low_latency = iwl_mvm_low_latency(mvm); + + return _iwl_mvm_get_respect_p2p_go(mvm, vif, low_latency, + NUM_NL80211_BANDS); +} + +static void iwl_mvm_fill_respect_p2p_go(struct iwl_mvm *mvm, + struct iwl_mvm_scan_params *params, + struct ieee80211_vif *vif) +{ + if (iwl_mvm_is_cdb_supported(mvm)) { + params->respect_p2p_go = + iwl_mvm_get_respect_p2p_go_band(mvm, vif, + NL80211_BAND_2GHZ); + params->respect_p2p_go_hb = + iwl_mvm_get_respect_p2p_go_band(mvm, vif, + NL80211_BAND_5GHZ); + } else { + params->respect_p2p_go = iwl_mvm_get_respect_p2p_go(mvm, vif); + } +} + int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_scan_request *req, struct ieee80211_scan_ies *ies) @@ -2662,6 +2790,7 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, params.scan_6ghz_params = req->scan_6ghz_params; params.scan_6ghz = req->scan_6ghz; iwl_mvm_fill_scan_type(mvm, ¶ms, vif); + iwl_mvm_fill_respect_p2p_go(mvm, ¶ms, vif); if (req->duration) params.iter_notif = true; @@ -2753,6 +2882,7 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, params.scan_plans = req->scan_plans; iwl_mvm_fill_scan_type(mvm, ¶ms, vif); + iwl_mvm_fill_respect_p2p_go(mvm, ¶ms, vif); /* In theory, LMAC scans can handle a 32-bit delay, but since * waiting for over 18 hours to start the scan is a bit silly @@ -2922,8 +3052,7 @@ static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type) IWL_DEBUG_SCAN(mvm, "Sending scan abort, uid %u\n", uid); ret = iwl_mvm_send_cmd_pdu(mvm, - iwl_cmd_id(SCAN_ABORT_UMAC, - IWL_ALWAYS_LONG_GROUP, 0), + WIDE_ID(IWL_ALWAYS_LONG_GROUP, SCAN_ABORT_UMAC), 0, sizeof(cmd), &cmd); if (!ret) mvm->scan_uid_status[uid] = type << IWL_MVM_SCAN_STOPPING_SHIFT; @@ -2978,8 +3107,7 @@ static int iwl_scan_req_umac_get_size(u8 scan_ver) int iwl_mvm_scan_size(struct iwl_mvm *mvm) { int base_size, tail_size; - u8 scan_ver = iwl_fw_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP, - SCAN_REQ_UMAC, + u8 scan_ver = iwl_fw_lookup_cmd_ver(mvm->fw, SCAN_REQ_UMAC, IWL_FW_CMD_VER_UNKNOWN); base_size = iwl_scan_req_umac_get_size(scan_ver); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index feab0bfcd7a2..c7f9d3870f21 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2015, 2018-2021 Intel Corporation + * Copyright (C) 2012-2015, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -87,6 +87,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, } switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_320: case IEEE80211_STA_RX_BW_160: add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_160MHZ); fallthrough; @@ -316,7 +317,7 @@ static int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue, } static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - u16 *queueptr, u8 tid, u8 flags) + u16 *queueptr, u8 tid) { int queue = *queueptr; struct iwl_scd_txq_cfg_cmd cmd = { @@ -325,11 +326,28 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, }; int ret; + lockdep_assert_held(&mvm->mutex); + if (iwl_mvm_has_new_tx_api(mvm)) { + if (mvm->sta_remove_requires_queue_remove) { + u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, + SCD_QUEUE_CONFIG_CMD); + struct iwl_scd_queue_cfg_cmd remove_cmd = { + .operation = cpu_to_le32(IWL_SCD_QUEUE_REMOVE), + .u.remove.queue = cpu_to_le32(queue), + }; + + ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, + sizeof(remove_cmd), + &remove_cmd); + } else { + ret = 0; + } + iwl_trans_txq_free(mvm->trans, queue); *queueptr = IWL_MVM_INVALID_QUEUE; - return 0; + return ret; } if (WARN_ON(mvm->queue_info[queue].tid_bitmap == 0)) @@ -373,7 +391,7 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, mvm->queue_info[queue].reserved = false; iwl_trans_txq_disable(mvm->trans, queue, false); - ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, + ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(struct iwl_scd_txq_cfg_cmd), &cmd); if (ret) @@ -512,7 +530,7 @@ static int iwl_mvm_free_inactive_queue(struct iwl_mvm *mvm, int queue, iwl_mvm_invalidate_sta_queue(mvm, queue, disable_agg_tids, false); - ret = iwl_mvm_disable_txq(mvm, old_sta, &queue_tmp, tid, 0); + ret = iwl_mvm_disable_txq(mvm, old_sta, &queue_tmp, tid); if (ret) { IWL_ERR(mvm, "Failed to free inactive queue %d (ret=%d)\n", @@ -596,6 +614,39 @@ static int iwl_mvm_get_shared_queue(struct iwl_mvm *mvm, return queue; } +/* Re-configure the SCD for a queue that has already been configured */ +static int iwl_mvm_reconfig_scd(struct iwl_mvm *mvm, int queue, int fifo, + int sta_id, int tid, int frame_limit, u16 ssn) +{ + struct iwl_scd_txq_cfg_cmd cmd = { + .scd_queue = queue, + .action = SCD_CFG_ENABLE_QUEUE, + .window = frame_limit, + .sta_id = sta_id, + .ssn = cpu_to_le16(ssn), + .tx_fifo = fifo, + .aggregate = (queue >= IWL_MVM_DQA_MIN_DATA_QUEUE || + queue == IWL_MVM_DQA_BSS_CLIENT_QUEUE), + .tid = tid, + }; + int ret; + + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return -EINVAL; + + if (WARN(mvm->queue_info[queue].tid_bitmap == 0, + "Trying to reconfig unallocated queue %d\n", queue)) + return -ENXIO; + + IWL_DEBUG_TX_QUEUES(mvm, "Reconfig SCD for TXQ #%d\n", queue); + + ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd); + WARN_ONCE(ret, "Failed to re-configure queue %d on FIFO %d, ret=%d\n", + queue, fifo, ret); + + return ret; +} + /* * If a given queue has a higher AC than the TID stream that is being compared * to, the queue needs to be redirected to the lower AC. This function does that @@ -716,21 +767,40 @@ static int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, static int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, u8 sta_id, u8 tid, unsigned int timeout) { - int queue, size = max_t(u32, IWL_DEFAULT_QUEUE_SIZE, - mvm->trans->cfg->min_256_ba_txq_size); + int queue, size; if (tid == IWL_MAX_TID_COUNT) { tid = IWL_MGMT_TID; size = max_t(u32, IWL_MGMT_QUEUE_SIZE, mvm->trans->cfg->min_txq_size); + } else { + struct ieee80211_sta *sta; + + rcu_read_lock(); + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + + /* this queue isn't used for traffic (cab_queue) */ + if (IS_ERR_OR_NULL(sta)) { + size = IWL_MGMT_QUEUE_SIZE; + } else if (sta->he_cap.has_he) { + /* support for 256 ba size */ + size = IWL_DEFAULT_QUEUE_SIZE_HE; + } else { + size = IWL_DEFAULT_QUEUE_SIZE; + } + + rcu_read_unlock(); } - do { - __le16 enable = cpu_to_le16(TX_QUEUE_CFG_ENABLE_QUEUE); + /* take the min with bc tbl entries allowed */ + size = min_t(u32, size, mvm->trans->txqs.bc_tbl_size / sizeof(u16)); - queue = iwl_trans_txq_alloc(mvm->trans, enable, - sta_id, tid, SCD_QUEUE_CFG, - size, timeout); + /* size needs to be power of 2 values for calculating read/write pointers */ + size = rounddown_pow_of_two(size); + + do { + queue = iwl_trans_txq_alloc(mvm->trans, 0, BIT(sta_id), + tid, size, timeout); if (queue < 0) IWL_DEBUG_TX_QUEUES(mvm, @@ -1019,12 +1089,12 @@ static bool iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm, * Remove the ones that did. */ for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { - u16 tid_bitmap; + u16 q_tid_bitmap; mvmsta->tid_data[tid].txq_id = IWL_MVM_INVALID_QUEUE; mvm->queue_info[queue].tid_bitmap &= ~BIT(tid); - tid_bitmap = mvm->queue_info[queue].tid_bitmap; + q_tid_bitmap = mvm->queue_info[queue].tid_bitmap; /* * We need to take into account a situation in which a TXQ was @@ -1037,7 +1107,7 @@ static bool iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm, * Mark this queue in the right bitmap, we'll send the command * to the firmware later. */ - if (!(tid_bitmap & BIT(mvm->queue_info[queue].txq_tid))) + if (!(q_tid_bitmap & BIT(mvm->queue_info[queue].txq_tid))) set_bit(queue, changetid_queues); IWL_DEBUG_TX_QUEUES(mvm, @@ -1337,7 +1407,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, out_err: queue_tmp = queue; - iwl_mvm_disable_txq(mvm, sta, &queue_tmp, tid, 0); + iwl_mvm_disable_txq(mvm, sta, &queue_tmp, tid); return ret; } @@ -1516,8 +1586,7 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, memset(&cmd, 0, sizeof(cmd)); cmd.sta_id = sta->sta_id; - if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, ADD_STA, - 0) >= 12 && + if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) >= 12 && sta->type == IWL_STA_AUX_ACTIVITY) cmd.mac_id_n_color = cpu_to_le32(mac_id); else @@ -1784,8 +1853,7 @@ static void iwl_mvm_disable_sta_queues(struct iwl_mvm *mvm, if (mvm_sta->tid_data[i].txq_id == IWL_MVM_INVALID_QUEUE) continue; - iwl_mvm_disable_txq(mvm, sta, &mvm_sta->tid_data[i].txq_id, i, - 0); + iwl_mvm_disable_txq(mvm, sta, &mvm_sta->tid_data[i].txq_id, i); mvm_sta->tid_data[i].txq_id = IWL_MVM_INVALID_QUEUE; } @@ -1993,7 +2061,7 @@ static int iwl_mvm_add_int_sta_with_queue(struct iwl_mvm *mvm, int macidx, if (ret) { if (!iwl_mvm_has_new_tx_api(mvm)) iwl_mvm_disable_txq(mvm, NULL, queue, - IWL_MAX_TID_COUNT, 0); + IWL_MAX_TID_COUNT); return ret; } @@ -2065,7 +2133,7 @@ int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (WARN_ON_ONCE(mvm->snif_sta.sta_id == IWL_MVM_INVALID_STA)) return -EINVAL; - iwl_mvm_disable_txq(mvm, NULL, &mvm->snif_queue, IWL_MAX_TID_COUNT, 0); + iwl_mvm_disable_txq(mvm, NULL, &mvm->snif_queue, IWL_MAX_TID_COUNT); ret = iwl_mvm_rm_sta_common(mvm, mvm->snif_sta.sta_id); if (ret) IWL_WARN(mvm, "Failed sending remove station\n"); @@ -2082,7 +2150,7 @@ int iwl_mvm_rm_aux_sta(struct iwl_mvm *mvm) if (WARN_ON_ONCE(mvm->aux_sta.sta_id == IWL_MVM_INVALID_STA)) return -EINVAL; - iwl_mvm_disable_txq(mvm, NULL, &mvm->aux_queue, IWL_MAX_TID_COUNT, 0); + iwl_mvm_disable_txq(mvm, NULL, &mvm->aux_queue, IWL_MAX_TID_COUNT); ret = iwl_mvm_rm_sta_common(mvm, mvm->aux_sta.sta_id); if (ret) IWL_WARN(mvm, "Failed sending remove station\n"); @@ -2199,7 +2267,7 @@ static void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, } queue = *queueptr; - iwl_mvm_disable_txq(mvm, NULL, queueptr, IWL_MAX_TID_COUNT, 0); + iwl_mvm_disable_txq(mvm, NULL, queueptr, IWL_MAX_TID_COUNT); if (iwl_mvm_has_new_tx_api(mvm)) return; @@ -2434,7 +2502,7 @@ int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) iwl_mvm_flush_sta(mvm, &mvmvif->mcast_sta, true); - iwl_mvm_disable_txq(mvm, NULL, &mvmvif->cab_queue, 0, 0); + iwl_mvm_disable_txq(mvm, NULL, &mvmvif->cab_queue, 0); ret = iwl_mvm_rm_sta_common(mvm, mvmvif->mcast_sta.sta_id); if (ret) @@ -2443,8 +2511,6 @@ int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return ret; } -#define IWL_MAX_RX_BA_SESSIONS 16 - static void iwl_mvm_sync_rxq_del_ba(struct iwl_mvm *mvm, u8 baid) { struct iwl_mvm_delba_data notif = { @@ -2526,18 +2592,126 @@ static void iwl_mvm_init_reorder_buffer(struct iwl_mvm *mvm, } } +static int iwl_mvm_fw_baid_op_sta(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvm_sta, + bool start, int tid, u16 ssn, + u16 buf_size) +{ + struct iwl_mvm_add_sta_cmd cmd = { + .mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color), + .sta_id = mvm_sta->sta_id, + .add_modify = STA_MODE_MODIFY, + }; + u32 status; + int ret; + + if (start) { + cmd.add_immediate_ba_tid = tid; + cmd.add_immediate_ba_ssn = cpu_to_le16(ssn); + cmd.rx_ba_window = cpu_to_le16(buf_size); + cmd.modify_mask = STA_MODIFY_ADD_BA_TID; + } else { + cmd.remove_immediate_ba_tid = tid; + cmd.modify_mask = STA_MODIFY_REMOVE_BA_TID; + } + + status = ADD_STA_SUCCESS; + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, + iwl_mvm_add_sta_cmd_size(mvm), + &cmd, &status); + if (ret) + return ret; + + switch (status & IWL_ADD_STA_STATUS_MASK) { + case ADD_STA_SUCCESS: + IWL_DEBUG_HT(mvm, "RX BA Session %sed in fw\n", + start ? "start" : "stopp"); + if (WARN_ON(start && iwl_mvm_has_new_rx_api(mvm) && + !(status & IWL_ADD_STA_BAID_VALID_MASK))) + return -EINVAL; + return u32_get_bits(status, IWL_ADD_STA_BAID_MASK); + case ADD_STA_IMMEDIATE_BA_FAILURE: + IWL_WARN(mvm, "RX BA Session refused by fw\n"); + return -ENOSPC; + default: + IWL_ERR(mvm, "RX BA Session failed %sing, status 0x%x\n", + start ? "start" : "stopp", status); + return -EIO; + } +} + +static int iwl_mvm_fw_baid_op_cmd(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvm_sta, + bool start, int tid, u16 ssn, + u16 buf_size, int baid) +{ + struct iwl_rx_baid_cfg_cmd cmd = { + .action = start ? cpu_to_le32(IWL_RX_BAID_ACTION_ADD) : + cpu_to_le32(IWL_RX_BAID_ACTION_REMOVE), + }; + u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD); + int ret; + + BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid)); + + if (start) { + cmd.alloc.sta_id_mask = cpu_to_le32(BIT(mvm_sta->sta_id)); + cmd.alloc.tid = tid; + cmd.alloc.ssn = cpu_to_le16(ssn); + cmd.alloc.win_size = cpu_to_le16(buf_size); + baid = -EIO; + } else if (iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1) == 1) { + cmd.remove_v1.baid = cpu_to_le32(baid); + BUILD_BUG_ON(sizeof(cmd.remove_v1) > sizeof(cmd.remove)); + } else { + cmd.remove.sta_id_mask = cpu_to_le32(BIT(mvm_sta->sta_id)); + cmd.remove.tid = cpu_to_le32(tid); + } + + ret = iwl_mvm_send_cmd_pdu_status(mvm, cmd_id, sizeof(cmd), + &cmd, &baid); + if (ret) + return ret; + + if (!start) { + /* ignore firmware baid on remove */ + baid = 0; + } + + IWL_DEBUG_HT(mvm, "RX BA Session %sed in fw\n", + start ? "start" : "stopp"); + + if (baid < 0 || baid >= ARRAY_SIZE(mvm->baid_map)) + return -EINVAL; + + return baid; +} + +static int iwl_mvm_fw_baid_op(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvm_sta, + bool start, int tid, u16 ssn, u16 buf_size, + int baid) +{ + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BAID_ML_SUPPORT)) + return iwl_mvm_fw_baid_op_cmd(mvm, mvm_sta, start, + tid, ssn, buf_size, baid); + + return iwl_mvm_fw_baid_op_sta(mvm, mvm_sta, start, + tid, ssn, buf_size); +} + int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, int tid, u16 ssn, bool start, u16 buf_size, u16 timeout) { struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_add_sta_cmd cmd = {}; struct iwl_mvm_baid_data *baid_data = NULL; - int ret; - u32 status; + int ret, baid; + u32 max_ba_id_sessions = iwl_mvm_has_new_tx_api(mvm) ? IWL_MAX_BAID : + IWL_MAX_BAID_OLD; lockdep_assert_held(&mvm->mutex); - if (start && mvm->rx_ba_sessions >= IWL_MAX_RX_BA_SESSIONS) { + if (start && mvm->rx_ba_sessions >= max_ba_id_sessions) { IWL_WARN(mvm, "Not enough RX BA SESSIONS\n"); return -ENOSPC; } @@ -2583,59 +2757,29 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, reorder_buf_size / sizeof(baid_data->entries[0]); } - cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); - cmd.sta_id = mvm_sta->sta_id; - cmd.add_modify = STA_MODE_MODIFY; - if (start) { - cmd.add_immediate_ba_tid = (u8) tid; - cmd.add_immediate_ba_ssn = cpu_to_le16(ssn); - cmd.rx_ba_window = cpu_to_le16(buf_size); + if (iwl_mvm_has_new_rx_api(mvm) && !start) { + baid = mvm_sta->tid_to_baid[tid]; } else { - cmd.remove_immediate_ba_tid = (u8) tid; - } - cmd.modify_mask = start ? STA_MODIFY_ADD_BA_TID : - STA_MODIFY_REMOVE_BA_TID; - - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, - iwl_mvm_add_sta_cmd_size(mvm), - &cmd, &status); - if (ret) - goto out_free; - - switch (status & IWL_ADD_STA_STATUS_MASK) { - case ADD_STA_SUCCESS: - IWL_DEBUG_HT(mvm, "RX BA Session %sed in fw\n", - start ? "start" : "stopp"); - break; - case ADD_STA_IMMEDIATE_BA_FAILURE: - IWL_WARN(mvm, "RX BA Session refused by fw\n"); - ret = -ENOSPC; - break; - default: - ret = -EIO; - IWL_ERR(mvm, "RX BA Session failed %sing, status 0x%x\n", - start ? "start" : "stopp", status); - break; + /* we don't really need it in this case */ + baid = -1; } - if (ret) + /* Don't send command to remove (start=0) BAID during restart */ + if (start || !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + baid = iwl_mvm_fw_baid_op(mvm, mvm_sta, start, tid, ssn, buf_size, + baid); + + if (baid < 0) { + ret = baid; goto out_free; + } if (start) { - u8 baid; - mvm->rx_ba_sessions++; if (!iwl_mvm_has_new_rx_api(mvm)) return 0; - if (WARN_ON(!(status & IWL_ADD_STA_BAID_VALID_MASK))) { - ret = -EINVAL; - goto out_free; - } - baid = (u8)((status & IWL_ADD_STA_BAID_MASK) >> - IWL_ADD_STA_BAID_SHIFT); baid_data->baid = baid; baid_data->timeout = timeout; baid_data->last_rx = jiffies; @@ -2663,7 +2807,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, WARN_ON(rcu_access_pointer(mvm->baid_map[baid])); rcu_assign_pointer(mvm->baid_map[baid], baid_data); } else { - u8 baid = mvm_sta->tid_to_baid[tid]; + baid = mvm_sta->tid_to_baid[tid]; if (mvm->rx_ba_sessions > 0) /* check that restart flow didn't zero the counter */ @@ -3238,8 +3382,7 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, int i, size; bool new_api = fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_TKIP_MIC_KEYS); - int api_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - ADD_STA_KEY, + int api_ver = iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA_KEY, new_api ? 2 : 1); if (sta_id == IWL_MVM_INVALID_STA) @@ -3939,7 +4082,7 @@ void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif) mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id); - if (!WARN_ON(!mvmsta)) + if (mvmsta) iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true); rcu_read_unlock(); @@ -3998,3 +4141,21 @@ int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, iwl_mvm_dealloc_int_sta(mvm, sta); return ret; } + +void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 mac_id) +{ + struct iwl_cancel_channel_switch_cmd cancel_channel_switch_cmd = { + .mac_id = cpu_to_le32(mac_id), + }; + int ret; + + ret = iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(MAC_CONF_GROUP, CANCEL_CHANNEL_SWITCH_CMD), + CMD_ASYNC, + sizeof(cancel_channel_switch_cmd), + &cancel_channel_switch_cmd); + if (ret) + IWL_ERR(mvm, "Failed to cancel the channel switch\n"); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index e34b82b2a288..f1a4fc3e4038 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -548,4 +548,7 @@ void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk); int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher, u8 *key, u32 key_len); +void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 mac_id); #endif /* __sta_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index ab06dcda1462..6edf2b79db43 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -97,8 +97,7 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk) /* In newer version of this command an aux station is added only * in cases of dedicated tx queue and need to be removed in end * of use */ - if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, - ADD_STA, 0) >= 12) + if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) >= 12) iwl_mvm_rm_aux_sta(mvm); } @@ -658,8 +657,8 @@ static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm, }; int ret; - ret = iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(SESSION_PROTECTION_CMD, - MAC_CONF_GROUP, 0), + ret = iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 0, sizeof(cmd), &cmd); if (ret) IWL_ERR(mvm, @@ -923,8 +922,8 @@ iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm, } cmd.conf_id = cpu_to_le32(mvmvif->time_event_data.id); - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(SESSION_PROTECTION_CMD, - MAC_CONF_GROUP, 0), + return iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 0, sizeof(cmd), &cmd); } @@ -1162,8 +1161,7 @@ void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; - const u16 notif[] = { iwl_cmd_id(SESSION_PROTECTION_NOTIF, - MAC_CONF_GROUP, 0) }; + const u16 notif[] = { WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF) }; struct iwl_notification_wait wait_notif; struct iwl_mvm_session_prot_cmd cmd = { .id_and_color = @@ -1201,8 +1199,7 @@ void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, if (!wait_for_notif) { if (iwl_mvm_send_cmd_pdu(mvm, - iwl_cmd_id(SESSION_PROTECTION_CMD, - MAC_CONF_GROUP, 0), + WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 0, sizeof(cmd), &cmd)) { IWL_ERR(mvm, "Couldn't send the SESSION_PROTECTION_CMD\n"); @@ -1219,8 +1216,7 @@ void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, iwl_mvm_session_prot_notif, NULL); if (iwl_mvm_send_cmd_pdu(mvm, - iwl_cmd_id(SESSION_PROTECTION_CMD, - MAC_CONF_GROUP, 0), + WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 0, sizeof(cmd), &cmd)) { IWL_ERR(mvm, "Couldn't send the SESSION_PROTECTION_CMD\n"); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index 398390c59344..69cf3a372759 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2019-2020 Intel Corporation + * Copyright (C) 2012-2014, 2019-2021 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2016 Intel Deutschland GmbH */ @@ -160,6 +160,11 @@ void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) notif = (struct ct_kill_notif *)pkt->data; IWL_DEBUG_TEMP(mvm, "CT Kill notification temperature = %d\n", notif->temperature); + if (iwl_fw_lookup_notif_ver(mvm->fw, PHY_OPS_GROUP, + CT_KILL_NOTIFICATION, 0) > 1) + IWL_DEBUG_TEMP(mvm, + "CT kill notification DTS bitmap = 0x%x, Scheme = %d\n", + notif->dts, notif->scheme); iwl_mvm_enter_ctkill(mvm); } @@ -240,8 +245,8 @@ int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp) * a response. For older versions we send the command and wait for a * notification (no command TLV for previous versions). */ - cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_OPS_GROUP, - CMD_DTS_MEASUREMENT_TRIGGER_WIDE, + cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE), IWL_FW_CMD_VER_UNKNOWN); if (cmd_ver == 1) return iwl_mvm_send_temp_cmd(mvm, true, temp); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 9213f8518f10..7763037b93ed 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -318,15 +318,14 @@ static u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm, /* info->control is only relevant for non HW rate control */ if (!ieee80211_hw_check(mvm->hw, HAS_RATE_CONTROL)) { - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - /* HT rate doesn't make sense for a non data frame */ WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS && !ieee80211_is_data(fc), "Got a HT rate (flags:0x%x/mcs:%d/fc:0x%x/state:%d) for a non data frame\n", info->control.rates[0].flags, info->control.rates[0].idx, - le16_to_cpu(fc), sta ? mvmsta->sta_state : -1); + le16_to_cpu(fc), + sta ? iwl_mvm_sta_from_mac80211(sta)->sta_state : -1); rate_idx = info->control.rates[0].idx; } @@ -351,7 +350,7 @@ static u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm, is_cck = (rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE); /* Set CCK or OFDM flag */ - if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, TX_CMD, 0) > 8) { + if (iwl_fw_lookup_cmd_ver(mvm->fw, TX_CMD, 0) > 8) { if (!is_cck) rate_flags |= RATE_MCS_LEGACY_OFDM_MSK; else @@ -654,7 +653,8 @@ static void iwl_mvm_probe_resp_set_noa(struct iwl_mvm *mvm, struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; int base_len = (u8 *)mgmt->u.probe_resp.variable - (u8 *)mgmt; struct iwl_probe_resp_data *resp_data; - u8 *ie, *pos; + const u8 *ie; + u8 *pos; u8 match[] = { (WLAN_OUI_WFA >> 16) & 0xff, (WLAN_OUI_WFA >> 8) & 0xff, @@ -671,10 +671,10 @@ static void iwl_mvm_probe_resp_set_noa(struct iwl_mvm *mvm, if (!resp_data->notif.noa_active) goto out; - ie = (u8 *)cfg80211_find_ie_match(WLAN_EID_VENDOR_SPECIFIC, - mgmt->u.probe_resp.variable, - skb->len - base_len, - match, 4, 2); + ie = cfg80211_find_ie_match(WLAN_EID_VENDOR_SPECIFIC, + mgmt->u.probe_resp.variable, + skb->len - base_len, + match, 4, 2); if (!ie) { IWL_DEBUG_TX(mvm, "probe resp doesn't have P2P IE\n"); goto out; @@ -1602,8 +1602,6 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, seq_ctl = le16_to_cpu(hdr->seq_ctrl); if (unlikely(!seq_ctl)) { - struct ieee80211_hdr *hdr = (void *)skb->data; - /* * If it is an NDP, we can't update next_reclaim since * its sequence control is 0. Note that for that same diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 1f3e90e5dbd4..bc947733d982 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -169,8 +169,7 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx) { - if (iwl_fw_lookup_cmd_ver(fw, LONG_GROUP, - TX_CMD, 0) > 8) + if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8) /* In the new rate legacy rates are indexed: * 0 - 3 for CCK and 0 - 7 for OFDM. */ @@ -241,38 +240,6 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx) return last_idx; } -int iwl_mvm_reconfig_scd(struct iwl_mvm *mvm, int queue, int fifo, int sta_id, - int tid, int frame_limit, u16 ssn) -{ - struct iwl_scd_txq_cfg_cmd cmd = { - .scd_queue = queue, - .action = SCD_CFG_ENABLE_QUEUE, - .window = frame_limit, - .sta_id = sta_id, - .ssn = cpu_to_le16(ssn), - .tx_fifo = fifo, - .aggregate = (queue >= IWL_MVM_DQA_MIN_DATA_QUEUE || - queue == IWL_MVM_DQA_BSS_CLIENT_QUEUE), - .tid = tid, - }; - int ret; - - if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) - return -EINVAL; - - if (WARN(mvm->queue_info[queue].tid_bitmap == 0, - "Trying to reconfig unallocated queue %d\n", queue)) - return -ENXIO; - - IWL_DEBUG_TX_QUEUES(mvm, "Reconfig SCD for TXQ #%d\n", queue); - - ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd); - WARN_ONCE(ret, "Failed to re-configure queue %d on FIFO %d, ret=%d\n", - queue, fifo, ret); - - return ret; -} - /** * iwl_mvm_send_lq_cmd() - Send link quality command * @mvm: Driver data. @@ -480,8 +447,7 @@ void iwl_mvm_send_low_latency_cmd(struct iwl_mvm *mvm, cmd.low_latency_tx = 1; } - if (iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(LOW_LATENCY_CMD, - MAC_CONF_GROUP, 0), + if (iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, LOW_LATENCY_CMD), 0, sizeof(cmd), &cmd)) IWL_ERR(mvm, "Failed to send low latency command\n"); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c index 85a6da70ca78..75fd386b048e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2022 Intel Corporation */ #include "iwl-trans.h" #include "iwl-fh.h" @@ -125,6 +125,9 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, control_flags |= IWL_PRPH_SCRATCH_MTR_MODE; control_flags |= IWL_PRPH_MTR_FORMAT_256B & IWL_PRPH_SCRATCH_MTR_FORMAT; + if (trans->trans_cfg->imr_enabled) + control_flags |= IWL_PRPH_SCRATCH_IMR_DEBUG_EN; + /* initialize RX default queue */ prph_sc_ctrl->rbd_cfg.free_rbd_addr = cpu_to_le64(trans_pcie->rxq->bd_dma); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 5178e852c5d3..b16d4ae182d1 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -491,18 +491,21 @@ static const struct pci_device_id iwl_hw_card_ids[] = { /* So devices */ {IWL_PCI_DEVICE(0x2725, PCI_ANY_ID, iwl_so_trans_cfg)}, {IWL_PCI_DEVICE(0x2726, PCI_ANY_ID, iwl_snj_trans_cfg)}, - {IWL_PCI_DEVICE(0x7A70, PCI_ANY_ID, iwl_so_long_latency_trans_cfg)}, + {IWL_PCI_DEVICE(0x7A70, PCI_ANY_ID, iwl_so_long_latency_imr_trans_cfg)}, {IWL_PCI_DEVICE(0x7AF0, PCI_ANY_ID, iwl_so_trans_cfg)}, {IWL_PCI_DEVICE(0x51F0, PCI_ANY_ID, iwl_so_long_latency_trans_cfg)}, + {IWL_PCI_DEVICE(0x51F1, PCI_ANY_ID, iwl_so_long_latency_imr_trans_cfg)}, {IWL_PCI_DEVICE(0x54F0, PCI_ANY_ID, iwl_so_long_latency_trans_cfg)}, + {IWL_PCI_DEVICE(0x7F70, PCI_ANY_ID, iwl_so_trans_cfg)}, /* Ma devices */ {IWL_PCI_DEVICE(0x2729, PCI_ANY_ID, iwl_ma_trans_cfg)}, {IWL_PCI_DEVICE(0x7E40, PCI_ANY_ID, iwl_ma_trans_cfg)}, - {IWL_PCI_DEVICE(0x7F70, PCI_ANY_ID, iwl_ma_trans_cfg)}, /* Bz devices */ {IWL_PCI_DEVICE(0x2727, PCI_ANY_ID, iwl_bz_trans_cfg)}, + {IWL_PCI_DEVICE(0xA840, PCI_ANY_ID, iwl_bz_trans_cfg)}, + {IWL_PCI_DEVICE(0x7740, PCI_ANY_ID, iwl_bz_trans_cfg)}, #endif /* CONFIG_IWLMVM */ {0} @@ -668,8 +671,8 @@ static const struct iwl_dev_info iwl_dev_info_table[] = { IWL_DEV_INFO(0x2726, 0x1652, iwl_cfg_snj_hr_b0, iwl_ax201_killer_1650i_name), IWL_DEV_INFO(0x2726, 0x1691, iwlax411_2ax_cfg_sosnj_gf4_a0, iwl_ax411_killer_1690s_name), IWL_DEV_INFO(0x2726, 0x1692, iwlax411_2ax_cfg_sosnj_gf4_a0, iwl_ax411_killer_1690i_name), - IWL_DEV_INFO(0x7F70, 0x1691, iwlax411_2ax_cfg_sosnj_gf4_a0, iwl_ax411_killer_1690s_name), - IWL_DEV_INFO(0x7F70, 0x1692, iwlax411_2ax_cfg_sosnj_gf4_a0, iwl_ax411_killer_1690i_name), + IWL_DEV_INFO(0x7F70, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name), + IWL_DEV_INFO(0x7F70, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), /* SO with GF2 */ IWL_DEV_INFO(0x2726, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name), @@ -682,6 +685,8 @@ static const struct iwl_dev_info iwl_dev_info_table[] = { IWL_DEV_INFO(0x7A70, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name), IWL_DEV_INFO(0x7AF0, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name), IWL_DEV_INFO(0x7AF0, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name), + IWL_DEV_INFO(0x7F70, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name), + IWL_DEV_INFO(0x7F70, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name), /* MA with GF2 */ IWL_DEV_INFO(0x7E40, 0x1671, iwl_cfg_ma_a0_gf_a0, iwl_ax211_killer_1675s_name), @@ -1301,7 +1306,30 @@ static const struct iwl_dev_info iwl_dev_info_table[] = { IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY, - iwlax210_2ax_cfg_so_jf_b0, iwl9462_name) + iwlax210_2ax_cfg_so_jf_b0, iwl9462_name), + +/* MsP */ +/* For now we use the same FW as MR, but this will change in the future. */ + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_MS, IWL_CFG_ANY, + IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, + iwl_cfg_so_a0_ms_a0, iwl_ax204_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_MS, IWL_CFG_ANY, + IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, + iwl_cfg_so_a0_ms_a0, iwl_ax204_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_MS, IWL_CFG_ANY, + IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, + iwl_cfg_ma_a0_ms_a0, iwl_ax204_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SNJ, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_MS, IWL_CFG_ANY, + IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, + iwl_cfg_snj_a0_ms_a0, iwl_ax204_name) #endif /* CONFIG_IWLMVM */ }; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index a43e56c7689f..f7e4f868363d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2003-2015, 2018-2021 Intel Corporation + * Copyright (C) 2003-2015, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -103,6 +103,18 @@ struct iwl_rx_completion_desc { u8 reserved2[25]; } __packed; +/** + * struct iwl_rx_completion_desc_bz - Bz completion descriptor + * @rbid: unique tag of the received buffer + * @flags: flags (0: fragmented, all others: reserved) + * @reserved: reserved + */ +struct iwl_rx_completion_desc_bz { + __le16 rbid; + u8 flags; + u8 reserved[1]; +} __packed; + /** * struct iwl_rxq - Rx queue * @id: queue index @@ -133,11 +145,7 @@ struct iwl_rxq { int id; void *bd; dma_addr_t bd_dma; - union { - void *used_bd; - __le32 *bd_32; - struct iwl_rx_completion_desc *cd; - }; + void *used_bd; dma_addr_t used_bd_dma; u32 read; u32 write; @@ -261,6 +269,20 @@ enum iwl_pcie_fw_reset_state { FW_RESET_ERROR, }; +/** + * enum wl_pcie_imr_status - imr dma transfer state + * @IMR_D2S_IDLE: default value of the dma transfer + * @IMR_D2S_REQUESTED: dma transfer requested + * @IMR_D2S_COMPLETED: dma transfer completed + * @IMR_D2S_ERROR: dma transfer error + */ +enum iwl_pcie_imr_status { + IMR_D2S_IDLE, + IMR_D2S_REQUESTED, + IMR_D2S_COMPLETED, + IMR_D2S_ERROR, +}; + /** * struct iwl_trans_pcie - PCIe transport specific data * @rxq: all the RX queue data @@ -319,6 +341,8 @@ enum iwl_pcie_fw_reset_state { * @alloc_page_lock: spinlock for the page allocator * @alloc_page: allocated page to still use parts of * @alloc_page_used: how much of the allocated page was already used (bytes) + * @imr_status: imr dma state machine + * @wait_queue_head_t: imr wait queue for dma completion * @rf_name: name/version of the CRF, if any */ struct iwl_trans_pcie { @@ -363,7 +387,7 @@ struct iwl_trans_pcie { /* PCI bus related data */ struct pci_dev *pci_dev; - void __iomem *hw_base; + u8 __iomem *hw_base; bool ucode_write_complete; bool sx_complete; @@ -414,7 +438,8 @@ struct iwl_trans_pcie { bool fw_reset_handshake; enum iwl_pcie_fw_reset_state fw_reset_state; wait_queue_head_t fw_reset_waitq; - + enum iwl_pcie_imr_status imr_status; + wait_queue_head_t imr_waitq; char rf_name[32]; }; @@ -809,4 +834,9 @@ int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); +void iwl_trans_pcie_copy_imr_fh(struct iwl_trans *trans, + u32 dst_addr, u64 src_addr, u32 byte_cnt); +int iwl_trans_pcie_copy_imr(struct iwl_trans *trans, + u32 dst_addr, u64 src_addr, u32 byte_cnt); + #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 8247014278f3..68a4572cee53 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2003-2014, 2018-2021 Intel Corporation + * Copyright (C) 2003-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -190,11 +190,14 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, } rxq->write_actual = round_down(rxq->write, 8); - if (trans->trans_cfg->mq_rx_supported) + if (!trans->trans_cfg->mq_rx_supported) + iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); + else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + iwl_write32(trans, HBUS_TARG_WRPTR, rxq->write_actual | + HBUS_TARG_WRPTR_RX_Q(rxq->id)); + else iwl_write32(trans, RFH_Q_FRBDCB_WIDX_TRG(rxq->id), rxq->write_actual); - else - iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); } static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans) @@ -652,23 +655,30 @@ void iwl_pcie_rx_allocator_work(struct work_struct *data) iwl_pcie_rx_allocator(trans_pcie->trans); } -static int iwl_pcie_free_bd_size(struct iwl_trans *trans, bool use_rx_td) +static int iwl_pcie_free_bd_size(struct iwl_trans *trans) { - struct iwl_rx_transfer_desc *rx_td; + if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + return sizeof(struct iwl_rx_transfer_desc); - if (use_rx_td) - return sizeof(*rx_td); - else - return trans->trans_cfg->mq_rx_supported ? sizeof(__le64) : - sizeof(__le32); + return trans->trans_cfg->mq_rx_supported ? + sizeof(__le64) : sizeof(__le32); +} + +static int iwl_pcie_used_bd_size(struct iwl_trans *trans) +{ + if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + return sizeof(struct iwl_rx_completion_desc_bz); + + if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + return sizeof(struct iwl_rx_completion_desc); + + return sizeof(__le32); } static void iwl_pcie_free_rxq_dma(struct iwl_trans *trans, struct iwl_rxq *rxq) { - bool use_rx_td = (trans->trans_cfg->device_family >= - IWL_DEVICE_FAMILY_AX210); - int free_size = iwl_pcie_free_bd_size(trans, use_rx_td); + int free_size = iwl_pcie_free_bd_size(trans); if (rxq->bd) dma_free_coherent(trans->dev, @@ -682,8 +692,8 @@ static void iwl_pcie_free_rxq_dma(struct iwl_trans *trans, if (rxq->used_bd) dma_free_coherent(trans->dev, - (use_rx_td ? sizeof(*rxq->cd) : - sizeof(__le32)) * rxq->queue_size, + iwl_pcie_used_bd_size(trans) * + rxq->queue_size, rxq->used_bd, rxq->used_bd_dma); rxq->used_bd_dma = 0; rxq->used_bd = NULL; @@ -707,7 +717,7 @@ static int iwl_pcie_alloc_rxq_dma(struct iwl_trans *trans, else rxq->queue_size = RX_QUEUE_SIZE; - free_size = iwl_pcie_free_bd_size(trans, use_rx_td); + free_size = iwl_pcie_free_bd_size(trans); /* * Allocate the circular buffer of Read Buffer Descriptors @@ -720,14 +730,15 @@ static int iwl_pcie_alloc_rxq_dma(struct iwl_trans *trans, if (trans->trans_cfg->mq_rx_supported) { rxq->used_bd = dma_alloc_coherent(dev, - (use_rx_td ? sizeof(*rxq->cd) : sizeof(__le32)) * rxq->queue_size, + iwl_pcie_used_bd_size(trans) * + rxq->queue_size, &rxq->used_bd_dma, GFP_KERNEL); if (!rxq->used_bd) goto err; } - rxq->rb_stts = trans_pcie->base_rb_stts + rxq->id * rb_stts_size; + rxq->rb_stts = (u8 *)trans_pcie->base_rb_stts + rxq->id * rb_stts_size; rxq->rb_stts_dma = trans_pcie->base_rb_stts_dma + rxq->id * rb_stts_size; @@ -1307,9 +1318,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, "Q %d: cmd at offset %d: %s (%.2x.%2x, seq 0x%x)\n", rxq->id, offset, iwl_get_cmd_string(trans, - iwl_cmd_id(pkt->hdr.cmd, - pkt->hdr.group_id, - 0)), + WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)), pkt->hdr.group_id, pkt->hdr.cmd, le16_to_cpu(pkt->hdr.sequence)); @@ -1319,7 +1328,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, offset += ALIGN(len, FH_RSCSR_FRAME_ALIGN); /* check that what the device tells us made sense */ - if (offset > max_len) + if (len < sizeof(*pkt) || offset > max_len) break; trace_iwlwifi_dev_rx(trans->dev, trans, pkt, len); @@ -1419,6 +1428,7 @@ static struct iwl_rx_mem_buffer *iwl_pcie_get_rxb(struct iwl_trans *trans, u16 vid; BUILD_BUG_ON(sizeof(struct iwl_rx_completion_desc) != 32); + BUILD_BUG_ON(sizeof(struct iwl_rx_completion_desc_bz) != 4); if (!trans->trans_cfg->mq_rx_supported) { rxb = rxq->queue[i]; @@ -1426,11 +1436,20 @@ static struct iwl_rx_mem_buffer *iwl_pcie_get_rxb(struct iwl_trans *trans, return rxb; } - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { - vid = le16_to_cpu(rxq->cd[i].rbid); - *join = rxq->cd[i].flags & IWL_RX_CD_FLAGS_FRAGMENTED; + if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + struct iwl_rx_completion_desc_bz *cd = rxq->used_bd; + + vid = le16_to_cpu(cd[i].rbid); + *join = cd[i].flags & IWL_RX_CD_FLAGS_FRAGMENTED; + } else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + struct iwl_rx_completion_desc *cd = rxq->used_bd; + + vid = le16_to_cpu(cd[i].rbid); + *join = cd[i].flags & IWL_RX_CD_FLAGS_FRAGMENTED; } else { - vid = le32_to_cpu(rxq->bd_32[i]) & 0x0FFF; /* 12-bit VID */ + __le32 *cd = rxq->used_bd; + + vid = le32_to_cpu(cd[i]) & 0x0FFF; /* 12-bit VID */ } if (!vid || vid > RX_POOL_SIZE(trans_pcie->num_rx_bufs)) @@ -1608,10 +1627,13 @@ irqreturn_t iwl_pcie_irq_rx_msix_handler(int irq, void *dev_id) if (WARN_ON(entry->entry >= trans->num_rx_queues)) return IRQ_NONE; - if (WARN_ONCE(!rxq, - "[%d] Got MSI-X interrupt before we have Rx queues", - entry->entry)) + if (!rxq) { + if (net_ratelimit()) + IWL_ERR(trans, + "[%d] Got MSI-X interrupt before we have Rx queues\n", + entry->entry); return IRQ_NONE; + } lock_map_acquire(&trans->sync_cmd_lockdep_map); IWL_DEBUG_ISR(trans, "[%d] Got interrupt\n", entry->entry); @@ -1954,7 +1976,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) CSR_INT, CSR_INT_BIT_RX_PERIODIC); } /* Sending RX interrupt require many steps to be done in the - * the device: + * device: * 1- write interrupt to current index in ICT table. * 2- dma RX frame. * 3- update RX shared data to indicate last write index. @@ -1998,6 +2020,11 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) /* Wake up uCode load routine, now that load is complete */ trans_pcie->ucode_write_complete = true; wake_up(&trans_pcie->ucode_write_waitq); + /* Wake up IMR write routine, now that write to SRAM is complete */ + if (trans_pcie->imr_status == IMR_D2S_REQUESTED) { + trans_pcie->imr_status = IMR_D2S_COMPLETED; + wake_up(&trans_pcie->ucode_write_waitq); + } } if (inta & ~handled) { @@ -2211,7 +2238,17 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) } /* This "Tx" DMA channel is used only for loading uCode */ - if (inta_fh & MSIX_FH_INT_CAUSES_D2S_CH0_NUM) { + if (inta_fh & MSIX_FH_INT_CAUSES_D2S_CH0_NUM && + trans_pcie->imr_status == IMR_D2S_REQUESTED) { + IWL_DEBUG_ISR(trans, "IMR Complete interrupt\n"); + isr_stats->tx++; + + /* Wake up IMR routine once write to SRAM is complete */ + if (trans_pcie->imr_status == IMR_D2S_REQUESTED) { + trans_pcie->imr_status = IMR_D2S_COMPLETED; + wake_up(&trans_pcie->ucode_write_waitq); + } + } else if (inta_fh & MSIX_FH_INT_CAUSES_D2S_CH0_NUM) { IWL_DEBUG_ISR(trans, "uCode load interrupt\n"); isr_stats->tx++; /* @@ -2220,6 +2257,12 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) */ trans_pcie->ucode_write_complete = true; wake_up(&trans_pcie->ucode_write_waitq); + + /* Wake up IMR routine once write to SRAM is complete */ + if (trans_pcie->imr_status == IMR_D2S_REQUESTED) { + trans_pcie->imr_status = IMR_D2S_COMPLETED; + wake_up(&trans_pcie->ucode_write_waitq); + } } if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) @@ -2234,7 +2277,10 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) inta_fh); isr_stats->sw++; /* during FW reset flow report errors from there */ - if (trans_pcie->fw_reset_state == FW_RESET_REQUESTED) { + if (trans_pcie->imr_status == IMR_D2S_REQUESTED) { + trans_pcie->imr_status = IMR_D2S_ERROR; + wake_up(&trans_pcie->imr_waitq); + } else if (trans_pcie->fw_reset_state == FW_RESET_REQUESTED) { trans_pcie->fw_reset_state = FW_RESET_ERROR; wake_up(&trans_pcie->fw_reset_waitq); } else { diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index ef14584fc0a1..8be3c3c8c68b 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2007-2015, 2018-2020 Intel Corporation + * Copyright (C) 2007-2015, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -745,7 +745,7 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, iwl_set_bits_prph(trans, LMPM_CHICK, LMPM_CHICK_EXTENDED_ADDR_SPACE); - memcpy(v_addr, (u8 *)section->data + offset, copy_size); + memcpy(v_addr, (const u8 *)section->data + offset, copy_size); ret = iwl_pcie_load_firmware_chunk(trans, dst_addr, p_addr, copy_size); @@ -1112,7 +1112,7 @@ static const struct iwl_causes_list causes_list_pre_bz[] = { }; static const struct iwl_causes_list causes_list_bz[] = { - {MSIX_HW_INT_CAUSES_REG_SW_ERR_BZ, CSR_MSIX_HW_INT_MASK_AD, 0x29}, + {MSIX_HW_INT_CAUSES_REG_SW_ERR_BZ, CSR_MSIX_HW_INT_MASK_AD, 0x15}, }; static void iwl_pcie_map_list(struct iwl_trans *trans, @@ -1948,6 +1948,7 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, trans->txqs.cmd.wdg_timeout = trans_cfg->cmd_q_wdg_timeout; trans->txqs.page_offs = trans_cfg->cb_data_offs; trans->txqs.dev_cmd_offs = trans_cfg->cb_data_offs + sizeof(void *); + trans->txqs.queue_alloc_cmd_ver = trans_cfg->queue_alloc_cmd_ver; if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS)) trans_pcie->n_no_reclaim_cmds = 0; @@ -2863,7 +2864,7 @@ static ssize_t iwl_dbgfs_monitor_data_read(struct file *file, { struct iwl_trans *trans = file->private_data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - void *cpu_addr = (void *)trans->dbg.fw_mon.block, *curr_buf; + u8 *cpu_addr = (void *)trans->dbg.fw_mon.block, *curr_buf; struct cont_rec *data = &trans_pcie->fw_mon_data; u32 write_ptr_addr, wrap_cnt_addr, write_ptr, wrap_cnt; ssize_t size, bytes_copied = 0; @@ -3468,7 +3469,8 @@ static void iwl_trans_pcie_sync_nmi(struct iwl_trans *trans) .d3_suspend = iwl_trans_pcie_d3_suspend, \ .d3_resume = iwl_trans_pcie_d3_resume, \ .interrupts = iwl_trans_pci_interrupts, \ - .sync_nmi = iwl_trans_pcie_sync_nmi \ + .sync_nmi = iwl_trans_pcie_sync_nmi, \ + .imr_dma_data = iwl_trans_pcie_copy_imr \ static const struct iwl_trans_ops trans_ops_pcie = { IWL_TRANS_COMMON_OPS, @@ -3553,6 +3555,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, mutex_init(&trans_pcie->mutex); init_waitqueue_head(&trans_pcie->ucode_write_waitq); init_waitqueue_head(&trans_pcie->fw_reset_waitq); + init_waitqueue_head(&trans_pcie->imr_waitq); trans_pcie->rba.alloc_wq = alloc_workqueue("rb_allocator", WQ_HIGHPRI | WQ_UNBOUND, 1); @@ -3681,3 +3684,41 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, iwl_trans_free(trans); return ERR_PTR(ret); } + +void iwl_trans_pcie_copy_imr_fh(struct iwl_trans *trans, + u32 dst_addr, u64 src_addr, u32 byte_cnt) +{ + iwl_write_prph(trans, IMR_UREG_CHICK, + iwl_read_prph(trans, IMR_UREG_CHICK) | + IMR_UREG_CHICK_HALT_UMAC_PERMANENTLY_MSK); + iwl_write_prph(trans, IMR_TFH_SRV_DMA_CHNL0_SRAM_ADDR, dst_addr); + iwl_write_prph(trans, IMR_TFH_SRV_DMA_CHNL0_DRAM_ADDR_LSB, + (u32)(src_addr & 0xFFFFFFFF)); + iwl_write_prph(trans, IMR_TFH_SRV_DMA_CHNL0_DRAM_ADDR_MSB, + iwl_get_dma_hi_addr(src_addr)); + iwl_write_prph(trans, IMR_TFH_SRV_DMA_CHNL0_BC, byte_cnt); + iwl_write_prph(trans, IMR_TFH_SRV_DMA_CHNL0_CTRL, + IMR_TFH_SRV_DMA_CHNL0_CTRL_D2S_IRQ_TARGET_POS | + IMR_TFH_SRV_DMA_CHNL0_CTRL_D2S_DMA_EN_POS | + IMR_TFH_SRV_DMA_CHNL0_CTRL_D2S_RS_MSK); +} + +int iwl_trans_pcie_copy_imr(struct iwl_trans *trans, + u32 dst_addr, u64 src_addr, u32 byte_cnt) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret = -1; + + trans_pcie->imr_status = IMR_D2S_REQUESTED; + iwl_trans_pcie_copy_imr_fh(trans, dst_addr, src_addr, byte_cnt); + ret = wait_event_timeout(trans_pcie->imr_waitq, + trans_pcie->imr_status != + IMR_D2S_REQUESTED, 5 * HZ); + if (!ret || trans_pcie->imr_status == IMR_D2S_ERROR) { + IWL_ERR(trans, "Failed to copy IMR Memory chunk!\n"); + iwl_trans_pcie_dump_regs(trans); + return -ETIMEDOUT; + } + trans_pcie->imr_status = IMR_D2S_IDLE; + return 0; +} diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 34bde8c87324..c72a84d8bb4f 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -213,7 +213,7 @@ int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, /* map the remaining (adjusted) nocopy/dup fragments */ for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { - const void *data = cmddata[i]; + void *data = (void *)(uintptr_t)cmddata[i]; if (!cmdlen[i]) continue; @@ -222,7 +222,7 @@ int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) data = dup_buf; - phys_addr = dma_map_single(trans->dev, (void *)data, + phys_addr = dma_map_single(trans->dev, data, cmdlen[i], DMA_TO_DEVICE); if (dma_mapping_error(trans->dev, phys_addr)) { idx = -ENOMEM; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 4f6c187eed69..3546c5269c3b 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -154,7 +154,7 @@ static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, void *tfd; u32 num_tbs; - tfd = txq->tfds + trans->txqs.tfd.size * txq->write_ptr; + tfd = (u8 *)txq->tfds + trans->txqs.tfd.size * txq->write_ptr; if (reset) memset(tfd, 0, trans->txqs.tfd.size); @@ -540,7 +540,7 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans) trans->cfg->min_txq_size); else slots_num = max_t(u32, IWL_DEFAULT_QUEUE_SIZE, - trans->cfg->min_256_ba_txq_size); + trans->cfg->min_ba_txq_size); trans->txqs.txq[txq_id] = &trans_pcie->txq_memory[txq_id]; ret = iwl_txq_alloc(trans, trans->txqs.txq[txq_id], slots_num, cmd_queue); @@ -594,7 +594,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) trans->cfg->min_txq_size); else slots_num = max_t(u32, IWL_DEFAULT_QUEUE_SIZE, - trans->cfg->min_256_ba_txq_size); + trans->cfg->min_ba_txq_size); ret = iwl_txq_init(trans, trans->txqs.txq[txq_id], slots_num, cmd_queue); if (ret) { @@ -877,7 +877,7 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, if (configure_scd) { iwl_scd_txq_set_inactive(trans, txq_id); - iwl_trans_write_mem(trans, stts_addr, (void *)zero_val, + iwl_trans_write_mem(trans, stts_addr, (const void *)zero_val, ARRAY_SIZE(zero_val)); } @@ -1114,7 +1114,7 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, /* map the remaining (adjusted) nocopy/dup fragments */ for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { - const void *data = cmddata[i]; + void *data = (void *)(uintptr_t)cmddata[i]; if (!cmdlen[i]) continue; @@ -1123,7 +1123,7 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) data = dup_buf; - phys_addr = dma_map_single(trans->dev, (void *)data, + phys_addr = dma_map_single(trans->dev, data, cmdlen[i], DMA_TO_DEVICE); if (dma_mapping_error(trans->dev, phys_addr)) { iwl_txq_gen1_tfd_unmap(trans, out_meta, txq, @@ -1201,7 +1201,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, cmd = txq->entries[cmd_index].cmd; meta = &txq->entries[cmd_index].meta; group_id = cmd->hdr.group_id; - cmd_id = iwl_cmd_id(cmd->hdr.cmd, group_id, 0); + cmd_id = WIDE_ID(group_id, cmd->hdr.cmd); iwl_txq_gen1_tfd_unmap(trans, meta, txq, index); diff --git a/drivers/net/wireless/intel/iwlwifi/queue/tx.c b/drivers/net/wireless/intel/iwlwifi/queue/tx.c index 0730657d54bf..726185d6fab8 100644 --- a/drivers/net/wireless/intel/iwlwifi/queue/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/queue/tx.c @@ -1,13 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2020-2021 Intel Corporation + * Copyright (C) 2020-2022 Intel Corporation */ #include #include #include "iwl-debug.h" #include "iwl-io.h" +#include "fw/api/commands.h" #include "fw/api/tx.h" +#include "fw/api/datapath.h" #include "queue/tx.h" #include "iwl-fh.h" #include "iwl-scd.h" @@ -41,13 +43,13 @@ static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans *trans, num_fetch_chunks = DIV_ROUND_UP(filled_tfd_size, 64) - 1; if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { - struct iwl_gen3_bc_tbl *scd_bc_tbl_gen3 = txq->bc_tbl.addr; + struct iwl_gen3_bc_tbl_entry *scd_bc_tbl_gen3 = txq->bc_tbl.addr; /* Starting from AX210, the HW expects bytes */ WARN_ON(trans->txqs.bc_table_dword); WARN_ON(len > 0x3FFF); bc_ent = cpu_to_le16(len | (num_fetch_chunks << 14)); - scd_bc_tbl_gen3->tfd_offset[idx] = bc_ent; + scd_bc_tbl_gen3[idx].tfd_offset = bc_ent; } else { struct iwlagn_scd_bc_tbl *scd_bc_tbl = txq->bc_tbl.addr; @@ -189,7 +191,7 @@ static struct page *get_workaround_page(struct iwl_trans *trans, return NULL; /* set the chaining pointer to the previous page if there */ - *(void **)(page_address(ret) + PAGE_SIZE - sizeof(void *)) = *page_ptr; + *(void **)((u8 *)page_address(ret) + PAGE_SIZE - sizeof(void *)) = *page_ptr; *page_ptr = ret; return ret; @@ -314,7 +316,7 @@ struct iwl_tso_hdr_page *get_page_hdr(struct iwl_trans *trans, size_t len, return NULL; p->pos = page_address(p->page); /* set the chaining pointer to NULL */ - *(void **)(page_address(p->page) + PAGE_SIZE - sizeof(void *)) = NULL; + *(void **)((u8 *)page_address(p->page) + PAGE_SIZE - sizeof(void *)) = NULL; out: *page_ptr = p->page; get_page(p->page); @@ -963,7 +965,7 @@ void iwl_txq_free_tso_page(struct iwl_trans *trans, struct sk_buff *skb) while (next) { struct page *tmp = next; - next = *(void **)(page_address(next) + PAGE_SIZE - + next = *(void **)((u8 *)page_address(next) + PAGE_SIZE - sizeof(void *)); __free_page(tmp); } @@ -1083,9 +1085,8 @@ int iwl_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num, return -ENOMEM; } -static int iwl_txq_dyn_alloc_dma(struct iwl_trans *trans, - struct iwl_txq **intxq, int size, - unsigned int timeout) +static struct iwl_txq * +iwl_txq_dyn_alloc_dma(struct iwl_trans *trans, int size, unsigned int timeout) { size_t bc_tbl_size, bc_tbl_entries; struct iwl_txq *txq; @@ -1097,18 +1098,18 @@ static int iwl_txq_dyn_alloc_dma(struct iwl_trans *trans, bc_tbl_entries = bc_tbl_size / sizeof(u16); if (WARN_ON(size > bc_tbl_entries)) - return -EINVAL; + return ERR_PTR(-EINVAL); txq = kzalloc(sizeof(*txq), GFP_KERNEL); if (!txq) - return -ENOMEM; + return ERR_PTR(-ENOMEM); txq->bc_tbl.addr = dma_pool_alloc(trans->txqs.bc_pool, GFP_KERNEL, &txq->bc_tbl.dma); if (!txq->bc_tbl.addr) { IWL_ERR(trans, "Scheduler BC Table allocation failed\n"); kfree(txq); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } ret = iwl_txq_alloc(trans, txq, size, false); @@ -1124,12 +1125,11 @@ static int iwl_txq_dyn_alloc_dma(struct iwl_trans *trans, txq->wd_timeout = msecs_to_jiffies(timeout); - *intxq = txq; - return 0; + return txq; error: iwl_txq_gen2_free_memory(trans, txq); - return ret; + return ERR_PTR(ret); } static int iwl_txq_alloc_response(struct iwl_trans *trans, struct iwl_txq *txq, @@ -1186,30 +1186,61 @@ static int iwl_txq_alloc_response(struct iwl_trans *trans, struct iwl_txq *txq, return ret; } -int iwl_txq_dyn_alloc(struct iwl_trans *trans, __le16 flags, u8 sta_id, u8 tid, - int cmd_id, int size, unsigned int timeout) +int iwl_txq_dyn_alloc(struct iwl_trans *trans, u32 flags, u32 sta_mask, + u8 tid, int size, unsigned int timeout) { - struct iwl_txq *txq = NULL; - struct iwl_tx_queue_cfg_cmd cmd = { - .flags = flags, - .sta_id = sta_id, - .tid = tid, - }; + struct iwl_txq *txq; + union { + struct iwl_tx_queue_cfg_cmd old; + struct iwl_scd_queue_cfg_cmd new; + } cmd; struct iwl_host_cmd hcmd = { - .id = cmd_id, - .len = { sizeof(cmd) }, - .data = { &cmd, }, .flags = CMD_WANT_SKB, }; int ret; - ret = iwl_txq_dyn_alloc_dma(trans, &txq, size, timeout); - if (ret) - return ret; + if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_BZ && + trans->hw_rev_step == SILICON_A_STEP) + size = 4096; - cmd.tfdq_addr = cpu_to_le64(txq->dma_addr); - cmd.byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma); - cmd.cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(size)); + txq = iwl_txq_dyn_alloc_dma(trans, size, timeout); + if (IS_ERR(txq)) + return PTR_ERR(txq); + + if (trans->txqs.queue_alloc_cmd_ver == 0) { + memset(&cmd.old, 0, sizeof(cmd.old)); + cmd.old.tfdq_addr = cpu_to_le64(txq->dma_addr); + cmd.old.byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma); + cmd.old.cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(size)); + cmd.old.flags = cpu_to_le16(flags | TX_QUEUE_CFG_ENABLE_QUEUE); + cmd.old.tid = tid; + + if (hweight32(sta_mask) != 1) { + ret = -EINVAL; + goto error; + } + cmd.old.sta_id = ffs(sta_mask) - 1; + + hcmd.id = SCD_QUEUE_CFG; + hcmd.len[0] = sizeof(cmd.old); + hcmd.data[0] = &cmd.old; + } else if (trans->txqs.queue_alloc_cmd_ver == 3) { + memset(&cmd.new, 0, sizeof(cmd.new)); + cmd.new.operation = cpu_to_le32(IWL_SCD_QUEUE_ADD); + cmd.new.u.add.tfdq_dram_addr = cpu_to_le64(txq->dma_addr); + cmd.new.u.add.bc_dram_addr = cpu_to_le64(txq->bc_tbl.dma); + cmd.new.u.add.cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(size)); + cmd.new.u.add.flags = cpu_to_le32(flags); + cmd.new.u.add.sta_mask = cpu_to_le32(sta_mask); + cmd.new.u.add.tid = tid; + + hcmd.id = WIDE_ID(DATA_PATH_GROUP, SCD_QUEUE_CONFIG_CMD); + hcmd.len[0] = sizeof(cmd.new); + hcmd.data[0] = &cmd.new; + } else { + ret = -EOPNOTSUPP; + goto error; + } ret = iwl_trans_send_cmd(trans, &hcmd); if (ret) @@ -1307,10 +1338,10 @@ static inline dma_addr_t iwl_txq_gen1_tfd_tb_get_addr(struct iwl_trans *trans, dma_addr_t hi_len; if (trans->trans_cfg->use_tfh) { - struct iwl_tfh_tfd *tfd = _tfd; - struct iwl_tfh_tb *tb = &tfd->tbs[idx]; + struct iwl_tfh_tfd *tfh_tfd = _tfd; + struct iwl_tfh_tb *tfh_tb = &tfh_tfd->tbs[idx]; - return (dma_addr_t)(le64_to_cpu(tb->addr)); + return (dma_addr_t)(le64_to_cpu(tfh_tb->addr)); } tfd = _tfd; diff --git a/drivers/net/wireless/intel/iwlwifi/queue/tx.h b/drivers/net/wireless/intel/iwlwifi/queue/tx.h index 20efc62acf13..eca53bfd326d 100644 --- a/drivers/net/wireless/intel/iwlwifi/queue/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/queue/tx.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2020-2021 Intel Corporation + * Copyright (C) 2020-2022 Intel Corporation */ #ifndef __iwl_trans_queue_tx_h__ #define __iwl_trans_queue_tx_h__ @@ -41,7 +41,7 @@ static inline void *iwl_txq_get_tfd(struct iwl_trans *trans, if (trans->trans_cfg->use_tfh) idx = iwl_txq_get_cmd_index(txq, idx); - return txq->tfds + trans->txqs.tfd.size * idx; + return (u8 *)txq->tfds + trans->txqs.tfd.size * idx; } int iwl_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num, @@ -112,10 +112,9 @@ void iwl_txq_gen2_tfd_unmap(struct iwl_trans *trans, struct iwl_cmd_meta *meta, struct iwl_tfh_tfd *tfd); -int iwl_txq_dyn_alloc(struct iwl_trans *trans, - __le16 flags, u8 sta_id, u8 tid, - int cmd_id, int size, - unsigned int timeout); +int iwl_txq_dyn_alloc(struct iwl_trans *trans, u32 flags, + u32 sta_mask, u8 tid, + int size, unsigned int timeout); int iwl_txq_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_tx_cmd *dev_cmd, int txq_id); @@ -137,9 +136,9 @@ static inline u8 iwl_txq_gen1_tfd_get_num_tbs(struct iwl_trans *trans, struct iwl_tfd *tfd; if (trans->trans_cfg->use_tfh) { - struct iwl_tfh_tfd *tfd = _tfd; + struct iwl_tfh_tfd *tfh_tfd = _tfd; - return le16_to_cpu(tfd->num_tbs) & 0x1f; + return le16_to_cpu(tfh_tfd->num_tbs) & 0x1f; } tfd = (struct iwl_tfd *)_tfd; @@ -153,10 +152,10 @@ static inline u16 iwl_txq_gen1_tfd_tb_get_len(struct iwl_trans *trans, struct iwl_tfd_tb *tb; if (trans->trans_cfg->use_tfh) { - struct iwl_tfh_tfd *tfd = _tfd; - struct iwl_tfh_tb *tb = &tfd->tbs[idx]; + struct iwl_tfh_tfd *tfh_tfd = _tfd; + struct iwl_tfh_tb *tfh_tb = &tfh_tfd->tbs[idx]; - return le16_to_cpu(tb->tb_len); + return le16_to_cpu(tfh_tb->tb_len); } tfd = (struct iwl_tfd *)_tfd; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index fc5725f6daee..28bfa7b7b73c 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -4,7 +4,7 @@ * Copyright (c) 2008, Jouni Malinen * Copyright (c) 2011, Javier Lopez * Copyright (c) 2016 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2020 Intel Corporation + * Copyright (C) 2018 - 2022 Intel Corporation */ /* @@ -173,9 +173,23 @@ static const struct ieee80211_regdomain hwsim_world_regdom_custom_02 = { } }; +static const struct ieee80211_regdomain hwsim_world_regdom_custom_03 = { + .n_reg_rules = 6, + .alpha2 = "99", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0), + REG_RULE(2484 - 10, 2484 + 10, 40, 0, 20, 0), + REG_RULE(5150 - 10, 5240 + 10, 40, 0, 30, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 0, 30, 0), + REG_RULE(5855 - 10, 5925 + 10, 40, 0, 33, 0), + REG_RULE(5955 - 10, 7125 + 10, 320, 0, 33, 0), + } +}; + static const struct ieee80211_regdomain *hwsim_world_regdom_custom[] = { &hwsim_world_regdom_custom_01, &hwsim_world_regdom_custom_02, + &hwsim_world_regdom_custom_03, }; struct hwsim_vif_priv { @@ -475,16 +489,16 @@ static const struct ieee80211_sta_s1g_cap hwsim_s1g_cap = { 0 }, }; -static void hwsim_init_s1g_channels(struct ieee80211_channel *channels) +static void hwsim_init_s1g_channels(struct ieee80211_channel *chans) { int ch, freq; for (ch = 0; ch < NUM_S1G_CHANS_US; ch++) { freq = 902000 + (ch + 1) * 500; - channels[ch].band = NL80211_BAND_S1GHZ; - channels[ch].center_freq = KHZ_TO_MHZ(freq); - channels[ch].freq_offset = freq % 1000; - channels[ch].hw_value = ch + 1; + chans[ch].band = NL80211_BAND_S1GHZ; + chans[ch].center_freq = KHZ_TO_MHZ(freq); + chans[ch].freq_offset = freq % 1000; + chans[ch].hw_value = ch + 1; } } @@ -503,6 +517,8 @@ static const struct ieee80211_rate hwsim_rates[] = { { .bitrate = 540 } }; +#define DEFAULT_RX_RSSI -50 + static const u32 hwsim_ciphers[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, @@ -652,6 +668,7 @@ struct mac80211_hwsim_data { ARRAY_SIZE(hwsim_channels_6ghz)]; struct ieee80211_channel *channel; + enum nl80211_chan_width bw; u64 beacon_int /* beacon interval in us */; unsigned int rx_filter; bool started, idle, scanning; @@ -690,6 +707,9 @@ struct mac80211_hwsim_data { u64 rx_bytes; u64 tx_dropped; u64 tx_failed; + + /* RSSI in rx status of the receiver */ + int rx_rssi; }; static const struct rhashtable_params hwsim_rht_params = { @@ -803,6 +823,40 @@ extern int hwsim_tx_virtio(struct mac80211_hwsim_data *data, #define hwsim_virtio_enabled false #endif +static int hwsim_get_chanwidth(enum nl80211_chan_width bw) +{ + switch (bw) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + return 20; + case NL80211_CHAN_WIDTH_40: + return 40; + case NL80211_CHAN_WIDTH_80: + return 80; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + return 160; + case NL80211_CHAN_WIDTH_320: + return 320; + case NL80211_CHAN_WIDTH_5: + return 5; + case NL80211_CHAN_WIDTH_10: + return 10; + case NL80211_CHAN_WIDTH_1: + return 1; + case NL80211_CHAN_WIDTH_2: + return 2; + case NL80211_CHAN_WIDTH_4: + return 4; + case NL80211_CHAN_WIDTH_8: + return 8; + case NL80211_CHAN_WIDTH_16: + return 16; + } + + return INT_MAX; +} + static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_channel *chan); @@ -964,6 +1018,29 @@ DEFINE_DEBUGFS_ATTRIBUTE(hwsim_fops_group, hwsim_fops_group_read, hwsim_fops_group_write, "%llx\n"); +static int hwsim_fops_rx_rssi_read(void *dat, u64 *val) +{ + struct mac80211_hwsim_data *data = dat; + *val = data->rx_rssi; + return 0; +} + +static int hwsim_fops_rx_rssi_write(void *dat, u64 val) +{ + struct mac80211_hwsim_data *data = dat; + int rssi = (int)val; + + if (rssi >= 0 || rssi < -100) + return -EINVAL; + + data->rx_rssi = rssi; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(hwsim_fops_rx_rssi, + hwsim_fops_rx_rssi_read, hwsim_fops_rx_rssi_write, + "%lld\n"); + static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -1482,8 +1559,8 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, rx_status.bw = RATE_INFO_BW_20; if (info->control.rates[0].flags & IEEE80211_TX_RC_SHORT_GI) rx_status.enc_flags |= RX_ENC_FLAG_SHORT_GI; - /* TODO: simulate real signal strength (and optional packet loss) */ - rx_status.signal = -50; + /* TODO: simulate optional packet loss */ + rx_status.signal = data->rx_rssi; if (info->control.vif) rx_status.signal += info->control.vif->bss_conf.txpower; @@ -1595,7 +1672,8 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_channel *channel; bool ack; - u32 _portid; + enum nl80211_chan_width confbw = NL80211_CHAN_WIDTH_20_NOHT; + u32 _portid, i; if (WARN_ON(skb->len < 10)) { /* Should not happen; just a sanity check for addr1 use */ @@ -1605,14 +1683,17 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, if (!data->use_chanctx) { channel = data->channel; + confbw = data->bw; } else if (txi->hw_queue == 4) { channel = data->tmp_chan; } else { chanctx_conf = rcu_dereference(txi->control.vif->chanctx_conf); - if (chanctx_conf) + if (chanctx_conf) { channel = chanctx_conf->def.chan; - else + confbw = chanctx_conf->def.width; + } else { channel = NULL; + } } if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) { @@ -1636,6 +1717,25 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, txi->control.rates, ARRAY_SIZE(txi->control.rates)); + for (i = 0; i < ARRAY_SIZE(txi->control.rates); i++) { + u16 rflags = txi->control.rates[i].flags; + /* initialize to data->bw for 5/10 MHz handling */ + enum nl80211_chan_width bw = data->bw; + + if (txi->control.rates[i].idx == -1) + break; + + if (rflags & IEEE80211_TX_RC_40_MHZ_WIDTH) + bw = NL80211_CHAN_WIDTH_40; + else if (rflags & IEEE80211_TX_RC_80_MHZ_WIDTH) + bw = NL80211_CHAN_WIDTH_80; + else if (rflags & IEEE80211_TX_RC_160_MHZ_WIDTH) + bw = NL80211_CHAN_WIDTH_160; + + if (WARN_ON(hwsim_get_chanwidth(bw) > hwsim_get_chanwidth(confbw))) + return; + } + if (skb->len >= 24 + 8 && ieee80211_is_probe_resp(hdr->frame_control)) { /* fake header transmission time */ @@ -1935,6 +2035,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) } data->channel = conf->chandef.chan; + data->bw = conf->chandef.width; for (idx = 0; idx < ARRAY_SIZE(data->survey_data); idx++) { if (data->survey_data[idx].channel && @@ -1946,6 +2047,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) } } else { data->channel = conf->chandef.chan; + data->bw = conf->chandef.width; } mutex_unlock(&data->mutex); @@ -2077,12 +2179,49 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, wiphy_dbg(hw->wiphy, " TX Power: %d dBm\n", info->txpower); } +static void +mac80211_hwsim_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 changed) +{ + struct mac80211_hwsim_data *data = hw->priv; + u32 bw = U32_MAX; + enum nl80211_chan_width confbw = NL80211_CHAN_WIDTH_20_NOHT; + + switch (sta->bandwidth) { +#define C(_bw) case IEEE80211_STA_RX_BW_##_bw: bw = _bw; break + C(20); + C(40); + C(80); + C(160); + C(320); +#undef C + } + + if (!data->use_chanctx) { + confbw = data->bw; + } else { + struct ieee80211_chanctx_conf *chanctx_conf = + rcu_dereference(vif->chanctx_conf); + + if (!WARN_ON(!chanctx_conf)) + confbw = chanctx_conf->def.width; + } + + WARN(bw > hwsim_get_chanwidth(confbw), + "intf %pM: bad STA %pM bandwidth %d MHz (%d) > channel config %d MHz (%d)\n", + vif->addr, sta->addr, bw, sta->bandwidth, + hwsim_get_chanwidth(data->bw), data->bw); +} + static int mac80211_hwsim_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { hwsim_check_magic(vif); hwsim_set_sta_magic(sta); + mac80211_hwsim_sta_rc_update(hw, vif, sta, 0); return 0; } @@ -2658,6 +2797,7 @@ static int mac80211_hwsim_tx_last_beacon(struct ieee80211_hw *hw) .sta_add = mac80211_hwsim_sta_add, \ .sta_remove = mac80211_hwsim_sta_remove, \ .sta_notify = mac80211_hwsim_sta_notify, \ + .sta_rc_update = mac80211_hwsim_sta_rc_update, \ .set_tim = mac80211_hwsim_set_tim, \ .conf_tx = mac80211_hwsim_conf_tx, \ .get_survey = mac80211_hwsim_get_survey, \ @@ -2809,7 +2949,7 @@ static void hwsim_mcast_new_radio(int id, struct genl_info *info, nlmsg_free(mcast_skb); } -static const struct ieee80211_sband_iftype_data he_capa_2ghz[] = { +static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = { { .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP), @@ -2855,6 +2995,66 @@ static const struct ieee80211_sband_iftype_data he_capa_2ghz[] = { .tx_mcs_80p80 = cpu_to_le16(0xffff), }, }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_NSEP_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE, + .phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, + .phy_cap_info[4] = + IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | + IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | + IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | + IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | + IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, + .phy_cap_info[6] = + IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK, + .phy_cap_info[7] = + IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW, + }, + + /* For all MCS and bandwidth, set 8 NSS for both Tx and + * Rx + */ + .eht_mcs_nss_supp = { + /* + * Since B0, B1, B2 and B3 are not set in + * the supported channel width set field in the + * HE PHY capabilities information field the + * device is a 20MHz only device on 2.4GHz band. + */ + .only_20mhz = { + .rx_tx_mcs7_max_nss = 0x88, + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + }, + /* PPE threshold information is not supported */ + }, }, #ifdef CONFIG_MAC80211_MESH { @@ -2897,7 +3097,7 @@ static const struct ieee80211_sband_iftype_data he_capa_2ghz[] = { #endif }; -static const struct ieee80211_sband_iftype_data he_capa_5ghz[] = { +static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = { { /* TODO: should we support other types, e.g., P2P?*/ .types_mask = BIT(NL80211_IFTYPE_STATION) | @@ -2948,6 +3148,81 @@ static const struct ieee80211_sband_iftype_data he_capa_5ghz[] = { .tx_mcs_80p80 = cpu_to_le16(0xfffa), }, }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_NSEP_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | + IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, + .phy_cap_info[1] = + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK, + .phy_cap_info[2] = + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK, + .phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, + .phy_cap_info[4] = + IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | + IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | + IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | + IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | + IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, + .phy_cap_info[6] = + IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK, + .phy_cap_info[7] = + IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ, + }, + + /* For all MCS and bandwidth, set 8 NSS for both Tx and + * Rx + */ + .eht_mcs_nss_supp = { + /* + * As B1 and B2 are set in the supported + * channel width set field in the HE PHY + * capabilities information field include all + * the following MCS/NSS. + */ + .bw._80 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + .bw._160 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + }, + /* PPE threshold information is not supported */ + }, }, #ifdef CONFIG_MAC80211_MESH { @@ -2995,7 +3270,7 @@ static const struct ieee80211_sband_iftype_data he_capa_5ghz[] = { #endif }; -static const struct ieee80211_sband_iftype_data he_capa_6ghz[] = { +static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { { /* TODO: should we support other types, e.g., P2P?*/ .types_mask = BIT(NL80211_IFTYPE_STATION) | @@ -3055,6 +3330,93 @@ static const struct ieee80211_sband_iftype_data he_capa_6ghz[] = { .tx_mcs_80p80 = cpu_to_le16(0xfffa), }, }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_NSEP_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ | + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | + IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, + .phy_cap_info[1] = + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK, + .phy_cap_info[2] = + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK | + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK, + .phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, + .phy_cap_info[4] = + IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | + IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | + IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | + IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | + IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, + .phy_cap_info[6] = + IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK | + IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP, + .phy_cap_info[7] = + IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ, + }, + + /* For all MCS and bandwidth, set 8 NSS for both Tx and + * Rx + */ + .eht_mcs_nss_supp = { + /* + * As B1 and B2 are set in the supported + * channel width set field in the HE PHY + * capabilities information field and 320MHz in + * 6GHz is supported include all the following + * MCS/NSS. + */ + .bw._80 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + .bw._160 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + .bw._320 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + }, + /* PPE threshold information is not supported */ + }, }, #ifdef CONFIG_MAC80211_MESH { @@ -3111,22 +3473,22 @@ static const struct ieee80211_sband_iftype_data he_capa_6ghz[] = { #endif }; -static void mac80211_hwsim_he_capab(struct ieee80211_supported_band *sband) +static void mac80211_hwsim_sband_capab(struct ieee80211_supported_band *sband) { u16 n_iftype_data; if (sband->band == NL80211_BAND_2GHZ) { - n_iftype_data = ARRAY_SIZE(he_capa_2ghz); + n_iftype_data = ARRAY_SIZE(sband_capa_2ghz); sband->iftype_data = - (struct ieee80211_sband_iftype_data *)he_capa_2ghz; + (struct ieee80211_sband_iftype_data *)sband_capa_2ghz; } else if (sband->band == NL80211_BAND_5GHZ) { - n_iftype_data = ARRAY_SIZE(he_capa_5ghz); + n_iftype_data = ARRAY_SIZE(sband_capa_5ghz); sband->iftype_data = - (struct ieee80211_sband_iftype_data *)he_capa_5ghz; + (struct ieee80211_sband_iftype_data *)sband_capa_5ghz; } else if (sband->band == NL80211_BAND_6GHZ) { - n_iftype_data = ARRAY_SIZE(he_capa_6ghz); + n_iftype_data = ARRAY_SIZE(sband_capa_6ghz); sband->iftype_data = - (struct ieee80211_sband_iftype_data *)he_capa_6ghz; + (struct ieee80211_sband_iftype_data *)sband_capa_6ghz; } else { return; } @@ -3318,6 +3680,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hw->wiphy->n_cipher_suites = param->n_ciphers; } + data->rx_rssi = DEFAULT_RX_RSSI; + INIT_DELAYED_WORK(&data->roc_start, hw_roc_start); INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); @@ -3449,7 +3813,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; } - mac80211_hwsim_he_capab(sband); + mac80211_hwsim_sband_capab(sband); hw->wiphy->bands[band] = sband; } @@ -3509,6 +3873,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps); debugfs_create_file("group", 0666, data->debugfs, data, &hwsim_fops_group); + debugfs_create_file("rx_rssi", 0666, data->debugfs, data, + &hwsim_fops_rx_rssi); if (!data->use_chanctx) debugfs_create_file("dfs_simulate_radar", 0222, data->debugfs, diff --git a/drivers/net/wireless/marvell/libertas/rx.c b/drivers/net/wireless/marvell/libertas/rx.c index 9f24b0760e1f..c34d30f7cbe0 100644 --- a/drivers/net/wireless/marvell/libertas/rx.c +++ b/drivers/net/wireless/marvell/libertas/rx.c @@ -147,7 +147,7 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) dev->stats.rx_packets++; skb->protocol = eth_type_trans(skb, dev); - netif_rx_any_context(skb); + netif_rx(skb); ret = 0; done: @@ -262,7 +262,7 @@ static int process_rxed_802_11_packet(struct lbs_private *priv, dev->stats.rx_packets++; skb->protocol = eth_type_trans(skb, priv->dev); - netif_rx_any_context(skb); + netif_rx(skb); ret = 0; diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c index 18e89777b784..630e1679c3f9 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c @@ -389,7 +389,7 @@ mwifiex_set_wmm_params(struct mwifiex_private *priv, { const u8 *vendor_ie; const u8 *wmm_ie; - u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02}; + static const u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02}; vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WMM, diff --git a/drivers/net/wireless/marvell/mwifiex/uap_txrx.c b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c index 245ff644f81e..4e49ed21c5ce 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c @@ -350,7 +350,7 @@ int mwifiex_uap_recv_packet(struct mwifiex_private *priv, skb->truesize += (skb->len - MWIFIEX_RX_DATA_BUF_SIZE); /* Forward multicast/broadcast packet to upper layer*/ - netif_rx_any_context(skb); + netif_rx(skb); return 0; } diff --git a/drivers/net/wireless/marvell/mwifiex/util.c b/drivers/net/wireless/marvell/mwifiex/util.c index d583fa600a29..d5edb1e89f5b 100644 --- a/drivers/net/wireless/marvell/mwifiex/util.c +++ b/drivers/net/wireless/marvell/mwifiex/util.c @@ -488,7 +488,7 @@ int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb) (skb->truesize > MWIFIEX_RX_DATA_BUF_SIZE)) skb->truesize += (skb->len - MWIFIEX_RX_DATA_BUF_SIZE); - netif_rx_any_context(skb); + netif_rx(skb); return 0; } diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 3a9af8931c35..02daeefb0761 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -93,7 +93,7 @@ mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q) { int i; - if (!q) + if (!q || !q->ndesc) return; /* clear descriptors */ @@ -233,7 +233,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush) struct mt76_queue_entry entry; int last; - if (!q) + if (!q || !q->ndesc) return; spin_lock_bh(&q->cleanup_lock); @@ -448,6 +448,9 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q) int len = SKB_WITH_OVERHEAD(q->buf_size); int offset = q->buf_offset; + if (!q->ndesc) + return 0; + spin_lock_bh(&q->lock); while (q->queued < q->ndesc - 1) { @@ -465,6 +468,7 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q) qbuf.addr = addr + offset; qbuf.len = len - offset; + qbuf.skip_unmap = false; mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, NULL); frames++; } @@ -484,6 +488,9 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q) void *buf; bool more; + if (!q->ndesc) + return; + spin_lock_bh(&q->lock); do { buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more); @@ -508,6 +515,9 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid) struct mt76_queue *q = &dev->q_rx[qid]; int i; + if (!q->ndesc) + return; + for (i = 0; i < q->ndesc; i++) q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE); diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 8bb1c7ab5b50..5b53d008eb66 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -180,7 +180,7 @@ static const struct cfg80211_sar_freq_ranges mt76_sar_freq_ranges[] = { { .start_freq = 5725, .end_freq = 5950, }, }; -const struct cfg80211_sar_capa mt76_sar_capa = { +static const struct cfg80211_sar_capa mt76_sar_capa = { .type = NL80211_SAR_TYPE_POWER, .num_freq_ranges = ARRAY_SIZE(mt76_sar_freq_ranges), .freq_ranges = &mt76_sar_freq_ranges[0], @@ -823,6 +823,10 @@ void mt76_set_channel(struct mt76_phy *phy) wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout); mt76_update_survey(phy); + if (phy->chandef.chan->center_freq != chandef->chan->center_freq || + phy->chandef.width != chandef->width) + phy->dfs_state = MT_DFS_STATE_UNKNOWN; + phy->chandef = *chandef; phy->chan_state = mt76_channel_state(phy, chandef->chan); @@ -928,6 +932,36 @@ void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid, } EXPORT_SYMBOL(mt76_wcid_key_setup); +static int +mt76_rx_signal(struct mt76_rx_status *status) +{ + s8 *chain_signal = status->chain_signal; + int signal = -128; + u8 chains; + + for (chains = status->chains; chains; chains >>= 1, chain_signal++) { + int cur, diff; + + cur = *chain_signal; + if (!(chains & BIT(0)) || + cur > 0) + continue; + + if (cur > signal) + swap(cur, signal); + + diff = signal - cur; + if (diff == 0) + signal += 3; + else if (diff <= 2) + signal += 2; + else if (diff <= 6) + signal += 1; + } + + return signal; +} + static void mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb, struct ieee80211_hw **hw, @@ -956,6 +990,9 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb, status->ampdu_reference = mstat.ampdu_ref; status->device_timestamp = mstat.timestamp; status->mactime = mstat.timestamp; + status->signal = mt76_rx_signal(&mstat); + if (status->signal <= -128) + status->flag |= RX_FLAG_NO_SIGNAL_VAL; if (ieee80211_is_beacon(hdr->frame_control) || ieee80211_is_probe_resp(hdr->frame_control)) @@ -1604,3 +1641,27 @@ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi, wi->worker_stat_count = ei - wi->initial_stat_idx; } EXPORT_SYMBOL_GPL(mt76_ethtool_worker); + +enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy) +{ + struct ieee80211_hw *hw = phy->hw; + struct mt76_dev *dev = phy->dev; + + if (dev->region == NL80211_DFS_UNSET || + test_bit(MT76_SCANNING, &phy->state)) + return MT_DFS_STATE_DISABLED; + + if (!hw->conf.radar_enabled) { + if ((hw->conf.flags & IEEE80211_CONF_MONITOR) && + (phy->chandef.chan->flags & IEEE80211_CHAN_RADAR)) + return MT_DFS_STATE_ACTIVE; + + return MT_DFS_STATE_DISABLED; + } + + if (!cfg80211_reg_can_beacon(hw->wiphy, &phy->chandef, NL80211_IFTYPE_AP)) + return MT_DFS_STATE_CAC; + + return MT_DFS_STATE_ACTIVE; +} +EXPORT_SYMBOL_GPL(mt76_phy_dfs_state); diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 404c3d1a70d6..882fb5d2517f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -19,7 +19,7 @@ #define MT_MCU_RING_SIZE 32 #define MT_RX_BUF_SIZE 2048 -#define MT_SKB_HEAD_LEN 128 +#define MT_SKB_HEAD_LEN 256 #define MT_MAX_NON_AQL_PKT 16 #define MT_TXQ_FREE_THR 32 @@ -85,6 +85,7 @@ enum mt76_rxq_id { MT_RXQ_MCU_WA, MT_RXQ_EXT, MT_RXQ_EXT_WA, + MT_RXQ_MAIN_WA, __MT_RXQ_MAX }; @@ -104,6 +105,13 @@ enum mt76_cipher_type { MT_CIPHER_GCMP_256, }; +enum mt76_dfs_state { + MT_DFS_STATE_UNKNOWN, + MT_DFS_STATE_DISABLED, + MT_DFS_STATE_CAC, + MT_DFS_STATE_ACTIVE, +}; + struct mt76_queue_buf { dma_addr_t addr; u16 len; @@ -224,7 +232,7 @@ enum mt76_wcid_flags { MT_WCID_FLAG_HDR_TRANS, }; -#define MT76_N_WCIDS 288 +#define MT76_N_WCIDS 544 /* stored in ieee80211_tx_info::hw_queue */ #define MT_TX_HW_QUEUE_EXT_PHY BIT(3) @@ -496,7 +504,7 @@ struct mt76_usb { } mcu; }; -#define MT76S_XMIT_BUF_SZ (16 * PAGE_SIZE) +#define MT76S_XMIT_BUF_SZ 0x3fe00 #define MT76S_NUM_TX_ENTRIES 256 #define MT76S_NUM_RX_ENTRIES 512 struct mt76_sdio { @@ -506,7 +514,8 @@ struct mt76_sdio { struct work_struct stat_work; - u8 *xmit_buf[IEEE80211_NUM_ACS + 2]; + u8 *xmit_buf; + u32 xmit_buf_sz; struct sdio_func *func; void *intr_data; @@ -621,6 +630,7 @@ struct mt76_vif { u8 band_idx; u8 wmm_idx; u8 scan_seq_num; + u8 cipher; }; struct mt76_phy { @@ -636,6 +646,7 @@ struct mt76_phy { struct ieee80211_channel *main_chan; struct mt76_channel_state *chan_state; + enum mt76_dfs_state dfs_state; ktime_t survey_time; struct mt76_hw_cap cap; @@ -897,8 +908,8 @@ static inline u16 mt76_rev(struct mt76_dev *dev) #define mt76_queue_reset(dev, ...) (dev)->mt76.queue_ops->reset_q(&((dev)->mt76), __VA_ARGS__) #define mt76_for_each_q_rx(dev, i) \ - for (i = 0; i < ARRAY_SIZE((dev)->q_rx) && \ - (dev)->q_rx[i].ndesc; i++) + for (i = 0; i < ARRAY_SIZE((dev)->q_rx); i++) \ + if ((dev)->q_rx[i].ndesc) struct mt76_dev *mt76_alloc_device(struct device *pdev, unsigned int size, const struct ieee80211_ops *ops, @@ -1181,6 +1192,7 @@ void mt76_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const u8 *mac); void mt76_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy); int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len); int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, @@ -1262,13 +1274,21 @@ mt76u_bulk_msg(struct mt76_dev *dev, void *data, int len, int *actual_len, void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi, struct mt76_sta_stats *stats); int mt76_skb_adjust_pad(struct sk_buff *skb, int pad); +int __mt76u_vendor_request(struct mt76_dev *dev, u8 req, u8 req_type, + u16 val, u16 offset, void *buf, size_t len); int mt76u_vendor_request(struct mt76_dev *dev, u8 req, u8 req_type, u16 val, u16 offset, void *buf, size_t len); void mt76u_single_wr(struct mt76_dev *dev, const u8 req, const u16 offset, const u32 val); -int mt76u_init(struct mt76_dev *dev, struct usb_interface *intf, - bool ext); +void mt76u_read_copy(struct mt76_dev *dev, u32 offset, + void *data, int len); +u32 ___mt76u_rr(struct mt76_dev *dev, u8 req, u8 req_type, u32 addr); +void ___mt76u_wr(struct mt76_dev *dev, u8 req, u8 req_type, + u32 addr, u32 val); +int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf, + struct mt76_bus_ops *ops); +int mt76u_init(struct mt76_dev *dev, struct usb_interface *intf); int mt76u_alloc_mcu_queue(struct mt76_dev *dev); int mt76u_alloc_queues(struct mt76_dev *dev); void mt76u_stop_tx(struct mt76_dev *dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c index 415ea17b9be6..37b092e3ea51 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c @@ -76,7 +76,7 @@ void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, __le32 *end = (__le32 *)&skb->data[skb->len]; enum rx_pkt_type type; - type = FIELD_GET(MT_RXD0_PKT_TYPE, le32_to_cpu(rxd[0])); + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); if (q == MT_RXQ_MCU) { if (type == PKT_TYPE_RX_EVENT) diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c index a272d64808c3..17713c821d80 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c @@ -643,11 +643,6 @@ mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb) status->chain_signal[1] = FIELD_GET(MT_RXV4_IB_RSSI1, rxdg3) + dev->rssi_offset[1]; - status->signal = status->chain_signal[0]; - if (status->chains & BIT(1)) - status->signal = max(status->signal, - status->chain_signal[1]); - if (FIELD_GET(MT_RXV1_FRAME_MODE, rxdg0) == 1) status->bw = RATE_INFO_BW_40; @@ -1135,7 +1130,7 @@ mt7603_fill_txs(struct mt7603_dev *dev, struct mt7603_sta *sta, } rate_set_tsf = READ_ONCE(sta->rate_set_tsf); - rs_idx = !((u32)(FIELD_GET(MT_TXS1_F0_TIMESTAMP, le32_to_cpu(txs_data[1])) - + rs_idx = !((u32)(le32_get_bits(txs_data[1], MT_TXS1_F0_TIMESTAMP) - rate_set_tsf) < 1000000); rs_idx ^= rate_set_tsf & BIT(0); rs = &sta->rateset[rs_idx]; @@ -1249,14 +1244,11 @@ void mt7603_mac_add_txs(struct mt7603_dev *dev, void *data) struct mt7603_sta *msta = NULL; struct mt76_wcid *wcid; __le32 *txs_data = data; - u32 txs; u8 wcidx; u8 pid; - txs = le32_to_cpu(txs_data[4]); - pid = FIELD_GET(MT_TXS4_PID, txs); - txs = le32_to_cpu(txs_data[3]); - wcidx = FIELD_GET(MT_TXS3_WCID, txs); + pid = le32_get_bits(txs_data[4], MT_TXS4_PID); + wcidx = le32_get_bits(txs_data[3], MT_TXS3_WCID); if (pid == MT_PACKET_ID_NO_ACK) return; diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c index 2b546bc05d82..83c5eec5b163 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c @@ -641,6 +641,9 @@ mt7603_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta_rates *sta_rates = rcu_dereference(sta->rates); int i; + if (!sta_rates) + return; + spin_lock_bh(&dev->mt76.lock); for (i = 0; i < ARRAY_SIZE(msta->rates); i++) { msta->rates[i].idx = sta_rates->rate[i].idx; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c index b53528014fbc..c26b45a09923 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c @@ -105,10 +105,10 @@ mt7615_pm_set(void *data, u64 val) if (!mt7615_firmware_offload(dev) || mt76_is_usb(&dev->mt76)) return -EOPNOTSUPP; - if (val == pm->enable) - return 0; + mutex_lock(&dev->mt76.mutex); - mt7615_mutex_acquire(dev); + if (val == pm->enable) + goto out; if (dev->phy.n_beacon_vif) { ret = -EBUSY; @@ -119,9 +119,16 @@ mt7615_pm_set(void *data, u64 val) pm->stats.last_wake_event = jiffies; pm->stats.last_doze_event = jiffies; } + /* make sure the chip is awake here and ps_work is scheduled + * just at end of the this routine. + */ + pm->enable = false; + mt76_connac_pm_wake(&dev->mphy, pm); + pm->enable = val; + mt76_connac_power_save_sched(&dev->mphy, pm); out: - mt7615_mutex_release(dev); + mutex_unlock(&dev->mt76.mutex); return ret; } @@ -436,11 +443,16 @@ mt7615_ext_mac_addr_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct mt7615_dev *dev = file->private_data; - char buf[32 * ((ETH_ALEN * 3) + 4) + 1]; + u32 len = 32 * ((ETH_ALEN * 3) + 4) + 1; u8 addr[ETH_ALEN]; + char *buf; int ofs = 0; int i; + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + for (i = 0; i < 32; i++) { if (!(dev->muar_mask & BIT(i))) continue; @@ -451,10 +463,13 @@ mt7615_ext_mac_addr_read(struct file *file, char __user *userbuf, put_unaligned_le32(mt76_rr(dev, MT_WF_RMAC_MAR0), addr); put_unaligned_le16((mt76_rr(dev, MT_WF_RMAC_MAR1) & MT_WF_RMAC_MAR1_ADDR), addr + 4); - ofs += snprintf(buf + ofs, sizeof(buf) - ofs, "%d=%pM\n", i, addr); + ofs += snprintf(buf + ofs, len - ofs, "%d=%pM\n", i, addr); } - return simple_read_from_buffer(userbuf, count, ppos, buf, ofs); + ofs = simple_read_from_buffer(userbuf, count, ppos, buf, ofs); + + kfree(buf); + return ofs; } static ssize_t diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c index a753c7476d31..a06dcbb8c673 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c @@ -552,7 +552,6 @@ void mt7615_init_device(struct mt7615_dev *dev) dev->pm.stats.last_wake_event = jiffies; dev->pm.stats.last_doze_event = jiffies; mt7615_cap_dbdc_disable(dev); - dev->phy.dfs_state = -1; #ifdef CONFIG_NL80211_TESTMODE dev->mt76.test_ops = &mt7615_testmode_ops; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index ec25e5a95d44..bd687f7de628 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -253,15 +253,15 @@ static void mt7615_mac_fill_tm_rx(struct mt7615_phy *phy, __le32 *rxv) static int mt7615_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) { struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap); struct mt7615_sta *msta = (struct mt7615_sta *)status->wcid; + __le32 *rxd = (__le32 *)skb->data; struct ieee80211_sta *sta; struct ieee80211_vif *vif; struct ieee80211_hdr hdr; - struct ethhdr eth_hdr; - __le32 *rxd = (__le32 *)skb->data; - __le32 qos_ctrl, ht_ctrl; + u16 frame_control; - if (FIELD_GET(MT_RXD1_NORMAL_ADDR_TYPE, le32_to_cpu(rxd[1])) != + if (le32_get_bits(rxd[1], MT_RXD1_NORMAL_ADDR_TYPE) != MT_RXD1_NORMAL_U2M) return -EINVAL; @@ -275,47 +275,53 @@ static int mt7615_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); /* store the info from RXD and ethhdr to avoid being overridden */ - memcpy(ð_hdr, skb->data + hdr_gap, sizeof(eth_hdr)); - hdr.frame_control = FIELD_GET(MT_RXD4_FRAME_CONTROL, rxd[4]); - hdr.seq_ctrl = FIELD_GET(MT_RXD6_SEQ_CTRL, rxd[6]); - qos_ctrl = FIELD_GET(MT_RXD6_QOS_CTL, rxd[6]); - ht_ctrl = FIELD_GET(MT_RXD7_HT_CONTROL, rxd[7]); - + frame_control = le32_get_bits(rxd[4], MT_RXD4_FRAME_CONTROL); + hdr.frame_control = cpu_to_le16(frame_control); + hdr.seq_ctrl = cpu_to_le16(le32_get_bits(rxd[6], MT_RXD6_SEQ_CTRL)); hdr.duration_id = 0; + ether_addr_copy(hdr.addr1, vif->addr); ether_addr_copy(hdr.addr2, sta->addr); - switch (le16_to_cpu(hdr.frame_control) & - (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { + switch (frame_control & (IEEE80211_FCTL_TODS | + IEEE80211_FCTL_FROMDS)) { case 0: ether_addr_copy(hdr.addr3, vif->bss_conf.bssid); break; case IEEE80211_FCTL_FROMDS: - ether_addr_copy(hdr.addr3, eth_hdr.h_source); + ether_addr_copy(hdr.addr3, eth_hdr->h_source); break; case IEEE80211_FCTL_TODS: - ether_addr_copy(hdr.addr3, eth_hdr.h_dest); + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); break; case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS: - ether_addr_copy(hdr.addr3, eth_hdr.h_dest); - ether_addr_copy(hdr.addr4, eth_hdr.h_source); + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + ether_addr_copy(hdr.addr4, eth_hdr->h_source); break; default: break; } skb_pull(skb, hdr_gap + sizeof(struct ethhdr) - 2); - if (eth_hdr.h_proto == htons(ETH_P_AARP) || - eth_hdr.h_proto == htons(ETH_P_IPX)) + if (eth_hdr->h_proto == cpu_to_be16(ETH_P_AARP) || + eth_hdr->h_proto == cpu_to_be16(ETH_P_IPX)) ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header); - else if (eth_hdr.h_proto >= htons(ETH_P_802_3_MIN)) + else if (be16_to_cpu(eth_hdr->h_proto) >= ETH_P_802_3_MIN) ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header); else skb_pull(skb, 2); if (ieee80211_has_order(hdr.frame_control)) - memcpy(skb_push(skb, 2), &ht_ctrl, 2); - if (ieee80211_is_data_qos(hdr.frame_control)) - memcpy(skb_push(skb, 2), &qos_ctrl, 2); + memcpy(skb_push(skb, IEEE80211_HT_CTL_LEN), &rxd[7], + IEEE80211_HT_CTL_LEN); + + if (ieee80211_is_data_qos(hdr.frame_control)) { + __le16 qos_ctrl; + + qos_ctrl = cpu_to_le16(le32_get_bits(rxd[6], MT_RXD6_QOS_CTL)); + memcpy(skb_push(skb, IEEE80211_QOS_CTL_LEN), &qos_ctrl, + IEEE80211_QOS_CTL_LEN); + } + if (ieee80211_has_a4(hdr.frame_control)) memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); else @@ -570,15 +576,6 @@ static int mt7615_mac_fill_rx(struct mt7615_dev *dev, struct sk_buff *skb) status->chain_signal[1] = to_rssi(MT_RXV4_RCPI1, rxdg3); status->chain_signal[2] = to_rssi(MT_RXV4_RCPI2, rxdg3); status->chain_signal[3] = to_rssi(MT_RXV4_RCPI3, rxdg3); - status->signal = status->chain_signal[0]; - - for (i = 1; i < hweight8(mphy->antenna_mask); i++) { - if (!(status->chains & BIT(i))) - continue; - - status->signal = max(status->signal, - status->chain_signal[i]); - } mt7615_mac_fill_tm_rx(mphy->priv, rxd); @@ -1430,7 +1427,7 @@ static bool mt7615_fill_txs(struct mt7615_dev *dev, struct mt7615_sta *sta, } rate_set_tsf = READ_ONCE(sta->rate_set_tsf); - rs_idx = !((u32)(FIELD_GET(MT_TXS4_F0_TIMESTAMP, le32_to_cpu(txs_data[4])) - + rs_idx = !((u32)(le32_get_bits(txs_data[4], MT_TXS4_F0_TIMESTAMP) - rate_set_tsf) < 1000000); rs_idx ^= rate_set_tsf & BIT(0); rs = &sta->rateset[rs_idx]; @@ -1561,14 +1558,11 @@ static void mt7615_mac_add_txs(struct mt7615_dev *dev, void *data) struct mt76_wcid *wcid; struct mt76_phy *mphy = &dev->mt76.phy; __le32 *txs_data = data; - u32 txs; u8 wcidx; u8 pid; - txs = le32_to_cpu(txs_data[0]); - pid = FIELD_GET(MT_TXS0_PID, txs); - txs = le32_to_cpu(txs_data[2]); - wcidx = FIELD_GET(MT_TXS2_WCID, txs); + pid = le32_get_bits(txs_data[0], MT_TXS0_PID); + wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID); if (pid == MT_PACKET_ID_NO_ACK) return; @@ -1642,9 +1636,10 @@ mt7615_mac_tx_free_token(struct mt7615_dev *dev, u16 token) mt7615_txwi_free(dev, txwi); } -static void mt7615_mac_tx_free(struct mt7615_dev *dev, struct sk_buff *skb) +static void mt7615_mac_tx_free(struct mt7615_dev *dev, void *data, int len) { - struct mt7615_tx_free *free = (struct mt7615_tx_free *)skb->data; + struct mt7615_tx_free *free = (struct mt7615_tx_free *)data; + void *end = data + len; u8 i, count; mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false); @@ -1655,21 +1650,25 @@ static void mt7615_mac_tx_free(struct mt7615_dev *dev, struct sk_buff *skb) mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], false); } - count = FIELD_GET(MT_TX_FREE_MSDU_ID_CNT, le16_to_cpu(free->ctrl)); + count = le16_get_bits(free->ctrl, MT_TX_FREE_MSDU_ID_CNT); if (is_mt7615(&dev->mt76)) { __le16 *token = &free->token[0]; + if (WARN_ON_ONCE((void *)&token[count] > end)) + return; + for (i = 0; i < count; i++) mt7615_mac_tx_free_token(dev, le16_to_cpu(token[i])); } else { __le32 *token = (__le32 *)&free->token[0]; + if (WARN_ON_ONCE((void *)&token[count] > end)) + return; + for (i = 0; i < count; i++) mt7615_mac_tx_free_token(dev, le32_to_cpu(token[i])); } - dev_kfree_skb(skb); - rcu_read_lock(); mt7615_mac_sta_poll(dev); rcu_read_unlock(); @@ -1677,6 +1676,29 @@ static void mt7615_mac_tx_free(struct mt7615_dev *dev, struct sk_buff *skb) mt76_worker_schedule(&dev->mt76.tx_worker); } +bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + __le32 *rxd = (__le32 *)data; + __le32 *end = (__le32 *)&rxd[len / 4]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + mt7615_mac_tx_free(dev, data, len); + return false; + case PKT_TYPE_TXS: + for (rxd++; rxd + 7 <= end; rxd += 7) + mt7615_mac_add_txs(dev, rxd); + return false; + default: + return true; + } +} +EXPORT_SYMBOL_GPL(mt7615_rx_check); + void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, struct sk_buff *skb) { @@ -1686,8 +1708,8 @@ void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, enum rx_pkt_type type; u16 flag; - type = FIELD_GET(MT_RXD0_PKT_TYPE, le32_to_cpu(rxd[0])); - flag = FIELD_GET(MT_RXD0_PKT_FLAG, le32_to_cpu(rxd[0])); + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + flag = le32_get_bits(rxd[0], MT_RXD0_PKT_FLAG); if (type == PKT_TYPE_RX_EVENT && flag == 0x1) type = PKT_TYPE_NORMAL_MCU; @@ -1698,7 +1720,8 @@ void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, dev_kfree_skb(skb); break; case PKT_TYPE_TXRX_NOTIFY: - mt7615_mac_tx_free(dev, skb); + mt7615_mac_tx_free(dev, skb->data, skb->len); + dev_kfree_skb(skb); break; case PKT_TYPE_RX_EVENT: mt7615_mcu_rx_event(dev, skb); @@ -1835,7 +1858,7 @@ mt7615_mac_adjust_sensitivity(struct mt7615_phy *phy, struct mt7615_dev *dev = phy->dev; int false_cca = ofdm ? phy->false_cca_ofdm : phy->false_cca_cck; bool ext_phy = phy != &dev->phy; - u16 def_th = ofdm ? -98 : -110; + s16 def_th = ofdm ? -98 : -110; bool update = false; s8 *sensitivity; int signal; @@ -2068,6 +2091,7 @@ void mt7615_pm_wake_work(struct work_struct *work) int i; if (mt76_is_sdio(mdev)) { + mt76_connac_pm_dequeue_skbs(mphy, &dev->pm); mt76_worker_schedule(&mdev->sdio.txrx_worker); } else { mt76_for_each_q_rx(mdev, i) @@ -2103,6 +2127,14 @@ void mt7615_pm_power_save_work(struct work_struct *work) test_bit(MT76_HW_SCHED_SCANNING, &dev->mphy.state)) goto out; + if (mutex_is_locked(&dev->mt76.mutex)) + /* if mt76 mutex is held we should not put the device + * to sleep since we are currently accessing device + * register map. We need to wait for the next power_save + * trigger. + */ + goto out; + if (time_is_after_jiffies(dev->pm.last_activity + delta)) { delta = dev->pm.last_activity + delta - jiffies; goto out; @@ -2160,21 +2192,24 @@ static void mt7615_dfs_stop_radar_detector(struct mt7615_phy *phy) struct mt7615_dev *dev = phy->dev; if (phy->rdd_state & BIT(0)) - mt7615_mcu_rdd_cmd(dev, RDD_STOP, 0, MT_RX_SEL0, 0); + mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, 0, + MT_RX_SEL0, 0); if (phy->rdd_state & BIT(1)) - mt7615_mcu_rdd_cmd(dev, RDD_STOP, 1, MT_RX_SEL0, 0); + mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, 1, + MT_RX_SEL0, 0); } static int mt7615_dfs_start_rdd(struct mt7615_dev *dev, int chain) { int err; - err = mt7615_mcu_rdd_cmd(dev, RDD_START, chain, MT_RX_SEL0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, chain, + MT_RX_SEL0, 0); if (err < 0) return err; - return mt7615_mcu_rdd_cmd(dev, RDD_DET_MODE, chain, - MT_RX_SEL0, 1); + return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_DET_MODE, chain, + MT_RX_SEL0, 1); } static int mt7615_dfs_start_radar_detector(struct mt7615_phy *phy) @@ -2185,7 +2220,8 @@ static int mt7615_dfs_start_radar_detector(struct mt7615_phy *phy) int err; /* start CAC */ - err = mt7615_mcu_rdd_cmd(dev, RDD_CAC_START, ext_phy, MT_RX_SEL0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START, ext_phy, + MT_RX_SEL0, 0); if (err < 0) return err; @@ -2246,50 +2282,60 @@ mt7615_dfs_init_radar_specs(struct mt7615_phy *phy) int mt7615_dfs_init_radar_detector(struct mt7615_phy *phy) { - struct cfg80211_chan_def *chandef = &phy->mt76->chandef; struct mt7615_dev *dev = phy->dev; bool ext_phy = phy != &dev->phy; + enum mt76_dfs_state dfs_state, prev_state; int err; if (is_mt7663(&dev->mt76)) return 0; - if (dev->mt76.region == NL80211_DFS_UNSET) { - phy->dfs_state = -1; - if (phy->rdd_state) - goto stop; + prev_state = phy->mt76->dfs_state; + dfs_state = mt76_phy_dfs_state(phy->mt76); - return 0; - } - - if (test_bit(MT76_SCANNING, &phy->mt76->state)) + if (prev_state == dfs_state) return 0; - if (phy->dfs_state == chandef->chan->dfs_state) - return 0; + if (prev_state == MT_DFS_STATE_UNKNOWN) + mt7615_dfs_stop_radar_detector(phy); - err = mt7615_dfs_init_radar_specs(phy); - if (err < 0) { - phy->dfs_state = -1; + if (dfs_state == MT_DFS_STATE_DISABLED) goto stop; + + if (prev_state <= MT_DFS_STATE_DISABLED) { + err = mt7615_dfs_init_radar_specs(phy); + if (err < 0) + return err; + + err = mt7615_dfs_start_radar_detector(phy); + if (err < 0) + return err; + + phy->mt76->dfs_state = MT_DFS_STATE_CAC; } - phy->dfs_state = chandef->chan->dfs_state; + if (dfs_state == MT_DFS_STATE_CAC) + return 0; - if (chandef->chan->flags & IEEE80211_CHAN_RADAR) { - if (chandef->chan->dfs_state != NL80211_DFS_AVAILABLE) - return mt7615_dfs_start_radar_detector(phy); - - return mt7615_mcu_rdd_cmd(dev, RDD_CAC_END, ext_phy, - MT_RX_SEL0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_END, + ext_phy, MT_RX_SEL0, 0); + if (err < 0) { + phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; + return err; } + phy->mt76->dfs_state = MT_DFS_STATE_ACTIVE; + return 0; + stop: - err = mt7615_mcu_rdd_cmd(dev, RDD_NORMAL_START, ext_phy, MT_RX_SEL0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_NORMAL_START, ext_phy, + MT_RX_SEL0, 0); if (err < 0) return err; mt7615_dfs_stop_radar_detector(phy); + phy->mt76->dfs_state = MT_DFS_STATE_DISABLED; + return 0; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index 82d625a16a62..d79cbdbd5a05 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -291,7 +291,8 @@ static void mt7615_init_dfs_state(struct mt7615_phy *phy) if (hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) return; - if (!(chandef->chan->flags & IEEE80211_CHAN_RADAR)) + if (!(chandef->chan->flags & IEEE80211_CHAN_RADAR) && + !(mphy->chandef.chan->flags & IEEE80211_CHAN_RADAR)) return; if (mphy->chandef.chan->center_freq == chandef->chan->center_freq && @@ -365,6 +366,7 @@ static int mt7615_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_key_conf *key) { struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_phy *phy = mt7615_hw_phy(hw); struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; struct mt7615_sta *msta = sta ? (struct mt7615_sta *)sta->drv_priv : &mvif->sta; @@ -403,6 +405,11 @@ static int mt7615_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, mt7615_mutex_acquire(dev); + if (cmd == SET_KEY && !sta && !mvif->mt76.cipher) { + mvif->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher); + mt7615_mcu_add_bss_info(phy, vif, NULL, true); + } + if (cmd == SET_KEY) *wcid_keyidx = idx; else if (idx == *wcid_keyidx) @@ -424,6 +431,29 @@ static int mt7615_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, return err; } +static int mt7615_set_sar_specs(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar) +{ + struct mt7615_phy *phy = mt7615_hw_phy(hw); + int err; + + if (!cfg80211_chandef_valid(&phy->mt76->chandef)) + return -EINVAL; + + err = mt76_init_sar_power(hw, sar); + if (err) + return err; + + if (mt7615_firmware_offload(phy->dev)) + return mt76_connac_mcu_set_rate_txpower(phy->mt76); + + ieee80211_stop_queues(hw); + err = mt7615_set_channel(phy); + ieee80211_wake_queues(hw); + + return err; +} + static int mt7615_config(struct ieee80211_hw *hw, u32 changed) { struct mt7615_dev *dev = mt7615_hw_dev(hw); @@ -683,6 +713,9 @@ static void mt7615_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_sta_rates *sta_rates = rcu_dereference(sta->rates); int i; + if (!sta_rates) + return; + spin_lock_bh(&dev->mt76.lock); for (i = 0; i < ARRAY_SIZE(msta->rates); i++) { msta->rates[i].idx = sta_rates->rate[i].idx; @@ -1323,6 +1356,7 @@ const struct ieee80211_ops mt7615_ops = { .set_wakeup = mt7615_set_wakeup, .set_rekey_data = mt7615_set_rekey_data, #endif /* CONFIG_PM */ + .set_sar_specs = mt7615_set_sar_specs, }; EXPORT_SYMBOL_GPL(mt7615_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c index 759dcf0e6783..97e2a85cb728 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c @@ -71,19 +71,6 @@ struct mt7663_fw_buf { #define IMG_CRC_LEN 4 -#define FW_FEATURE_SET_ENCRYPT BIT(0) -#define FW_FEATURE_SET_KEY_IDX GENMASK(2, 1) - -#define DL_MODE_ENCRYPT BIT(0) -#define DL_MODE_KEY_IDX GENMASK(2, 1) -#define DL_MODE_RESET_SEC_IV BIT(3) -#define DL_MODE_WORKING_PDA_CR4 BIT(4) -#define DL_MODE_VALID_RAM_ENTRY BIT(5) -#define DL_MODE_NEED_RSP BIT(31) - -#define FW_START_OVERRIDE BIT(0) -#define FW_START_WORKING_PDA_CR4 BIT(2) - void mt7615_mcu_fill_msg(struct mt7615_dev *dev, struct sk_buff *skb, int cmd, int *wait_seq) { @@ -756,145 +743,7 @@ mt7615_mcu_add_beacon_offload(struct mt7615_dev *dev, static int mt7615_mcu_ctrl_pm_state(struct mt7615_dev *dev, int band, int state) { -#define ENTER_PM_STATE 1 -#define EXIT_PM_STATE 2 - struct { - u8 pm_number; - u8 pm_state; - u8 bssid[ETH_ALEN]; - u8 dtim_period; - u8 wlan_idx; - __le16 bcn_interval; - __le32 aid; - __le32 rx_filter; - u8 band_idx; - u8 rsv[3]; - __le32 feature; - u8 omac_idx; - u8 wmm_idx; - u8 bcn_loss_cnt; - u8 bcn_sp_duration; - } __packed req = { - .pm_number = 5, - .pm_state = state ? ENTER_PM_STATE : EXIT_PM_STATE, - .band_idx = band, - }; - - return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(PM_STATE_CTRL), - &req, sizeof(req), true); -} - -static int -mt7615_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, struct mt7615_phy *phy, - bool enable) -{ - struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; - u32 type = vif->p2p ? NETWORK_P2P : NETWORK_INFRA; - struct bss_info_basic *bss; - u8 wlan_idx = mvif->sta.wcid.idx; - struct tlv *tlv; - - tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_BASIC, sizeof(*bss)); - - switch (vif->type) { - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_MONITOR: - break; - case NL80211_IFTYPE_STATION: - /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */ - if (enable && sta) { - struct mt7615_sta *msta; - - msta = (struct mt7615_sta *)sta->drv_priv; - wlan_idx = msta->wcid.idx; - } - break; - case NL80211_IFTYPE_ADHOC: - type = NETWORK_IBSS; - break; - default: - WARN_ON(1); - break; - } - - bss = (struct bss_info_basic *)tlv; - bss->network_type = cpu_to_le32(type); - bss->bmc_wcid_lo = wlan_idx; - bss->wmm_idx = mvif->mt76.wmm_idx; - bss->active = enable; - - if (vif->type != NL80211_IFTYPE_MONITOR) { - memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN); - bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int); - bss->dtim_period = vif->bss_conf.dtim_period; - } else { - memcpy(bss->bssid, phy->mt76->macaddr, ETH_ALEN); - } - - return 0; -} - -static void -mt7615_mcu_bss_omac_tlv(struct sk_buff *skb, struct ieee80211_vif *vif) -{ - struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; - u8 omac_idx = mvif->mt76.omac_idx; - struct bss_info_omac *omac; - struct tlv *tlv; - u32 type = 0; - - tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_OMAC, sizeof(*omac)); - - switch (vif->type) { - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_AP: - if (vif->p2p) - type = CONNECTION_P2P_GO; - else - type = CONNECTION_INFRA_AP; - break; - case NL80211_IFTYPE_STATION: - if (vif->p2p) - type = CONNECTION_P2P_GC; - else - type = CONNECTION_INFRA_STA; - break; - case NL80211_IFTYPE_ADHOC: - type = CONNECTION_IBSS_ADHOC; - break; - default: - WARN_ON(1); - break; - } - - omac = (struct bss_info_omac *)tlv; - omac->conn_type = cpu_to_le32(type); - omac->omac_idx = mvif->mt76.omac_idx; - omac->band_idx = mvif->mt76.band_idx; - omac->hw_bss_idx = omac_idx > EXT_BSSID_START ? HW_BSSID_0 : omac_idx; -} - -/* SIFS 20us + 512 byte beacon tranmitted by 1Mbps (3906us) */ -#define BCN_TX_ESTIMATE_TIME (4096 + 20) -static void -mt7615_mcu_bss_ext_tlv(struct sk_buff *skb, struct mt7615_vif *mvif) -{ - struct bss_info_ext_bss *ext; - int ext_bss_idx, tsf_offset; - struct tlv *tlv; - - ext_bss_idx = mvif->mt76.omac_idx - EXT_BSSID_START; - if (ext_bss_idx < 0) - return; - - tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_EXT_BSS, sizeof(*ext)); - - ext = (struct bss_info_ext_bss *)tlv; - tsf_offset = ext_bss_idx * BCN_TX_ESTIMATE_TIME; - ext->mbss_tsf_offset = cpu_to_le32(tsf_offset); + return mt76_connac_mcu_set_pm(&dev->mt76, band, state); } static int @@ -913,13 +762,14 @@ mt7615_mcu_add_bss(struct mt7615_phy *phy, struct ieee80211_vif *vif, return PTR_ERR(skb); if (enable) - mt7615_mcu_bss_omac_tlv(skb, vif); + mt76_connac_mcu_bss_omac_tlv(skb, vif); - mt7615_mcu_bss_basic_tlv(skb, vif, sta, phy, enable); + mt76_connac_mcu_bss_basic_tlv(skb, vif, sta, phy->mt76, + mvif->sta.wcid.idx, enable); if (enable && mvif->mt76.omac_idx >= EXT_BSSID_START && mvif->mt76.omac_idx < REPEATER_BSSID_START) - mt7615_mcu_bss_ext_tlv(skb, mvif); + mt76_connac_mcu_bss_ext_tlv(skb, &mvif->mt76); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(BSS_INFO_UPDATE), true); @@ -1030,7 +880,7 @@ mt7615_mcu_wtbl_sta_add(struct mt7615_phy *phy, struct ieee80211_vif *vif, NULL, wtbl_hdr); if (sta) mt76_connac_mcu_wtbl_ht_tlv(&dev->mt76, wskb, sta, - NULL, wtbl_hdr); + NULL, wtbl_hdr, true, true); mt76_connac_mcu_wtbl_hdr_trans_tlv(wskb, vif, &msta->wcid, NULL, wtbl_hdr); } @@ -1057,19 +907,7 @@ mt7615_mcu_wtbl_update_hdr_trans(struct mt7615_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { - struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; - struct wtbl_req_hdr *wtbl_hdr; - struct sk_buff *skb = NULL; - - wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid, - WTBL_SET, NULL, &skb); - if (IS_ERR(wtbl_hdr)) - return PTR_ERR(wtbl_hdr); - - mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, vif, &msta->wcid, NULL, - wtbl_hdr); - return mt76_mcu_skb_send_msg(&dev->mt76, skb, - MCU_EXT_CMD(WTBL_UPDATE), true); + return mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta); } static const struct mt7615_mcu_ops wtbl_update_ops = { @@ -1303,7 +1141,8 @@ mt7615_mcu_uni_tx_ba(struct mt7615_dev *dev, struct mt7615_sta *sta = (struct mt7615_sta *)params->sta->drv_priv; return mt76_connac_mcu_sta_ba(&dev->mt76, &sta->vif->mt76, params, - enable, true); + MCU_UNI_CMD(STA_REC_UPDATE), enable, + true); } static int @@ -1451,20 +1290,6 @@ static int mt7615_load_patch(struct mt7615_dev *dev, u32 addr, const char *name) return ret; } -static u32 mt7615_mcu_gen_dl_mode(u8 feature_set, bool is_cr4) -{ - u32 ret = 0; - - ret |= (feature_set & FW_FEATURE_SET_ENCRYPT) ? - (DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV) : 0; - ret |= FIELD_PREP(DL_MODE_KEY_IDX, - FIELD_GET(FW_FEATURE_SET_KEY_IDX, feature_set)); - ret |= DL_MODE_NEED_RSP; - ret |= is_cr4 ? DL_MODE_WORKING_PDA_CR4 : 0; - - return ret; -} - static int mt7615_mcu_send_ram_firmware(struct mt7615_dev *dev, const struct mt7615_fw_trailer *hdr, @@ -1475,7 +1300,8 @@ mt7615_mcu_send_ram_firmware(struct mt7615_dev *dev, u32 len, addr, mode; for (i = 0; i < n_region; i++) { - mode = mt7615_mcu_gen_dl_mode(hdr[i].feature_set, is_cr4); + mode = mt76_connac_mcu_gen_dl_mode(&dev->mt76, + hdr[i].feature_set, is_cr4); len = le32_to_cpu(hdr[i].len) + IMG_CRC_LEN; addr = le32_to_cpu(hdr[i].addr); @@ -1723,7 +1549,8 @@ static int mt7663_load_n9(struct mt7615_dev *dev, const char *name) dev_info(dev->mt76.dev, "Parsing tailer Region: %d\n", i); buf = (const struct mt7663_fw_buf *)(base_addr - shift); - mode = mt7615_mcu_gen_dl_mode(buf->feature_set, false); + mode = mt76_connac_mcu_gen_dl_mode(&dev->mt76, + buf->feature_set, false); addr = le32_to_cpu(buf->img_dest_addr); len = le32_to_cpu(buf->img_size); @@ -2064,27 +1891,6 @@ int mt7615_mcu_del_wtbl_all(struct mt7615_dev *dev) &req, sizeof(req), true); } -int mt7615_mcu_rdd_cmd(struct mt7615_dev *dev, - enum mt7615_rdd_cmd cmd, u8 index, - u8 rx_sel, u8 val) -{ - struct { - u8 ctrl; - u8 rdd_idx; - u8 rdd_rx_sel; - u8 val; - u8 rsv[4]; - } req = { - .ctrl = cmd, - .rdd_idx = index, - .rdd_rx_sel = rx_sel, - .val = val, - }; - - return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_RDD_CTRL), - &req, sizeof(req), true); -} - int mt7615_mcu_set_fcc5_lpn(struct mt7615_dev *dev, int val) { struct { @@ -2214,7 +2020,7 @@ static void mt7615_mcu_set_txpower_sku(struct mt7615_phy *phy, u8 *sku) struct mt76_power_limits limits; s8 *limits_array = (s8 *)&limits; int n_chains = hweight8(mphy->antenna_mask); - int tx_power; + int tx_power = hw->conf.power_level * 2; int i; static const u8 sku_mapping[] = { #define SKU_FIELD(_type, _field) \ @@ -2271,9 +2077,8 @@ static void mt7615_mcu_set_txpower_sku(struct mt7615_phy *phy, u8 *sku) #undef SKU_FIELD }; - tx_power = hw->conf.power_level * 2 - - mt76_tx_power_nss_delta(n_chains); - + tx_power = mt76_get_sar_power(mphy, mphy->chandef.chan, tx_power); + tx_power -= mt76_tx_power_nss_delta(n_chains); tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &limits, tx_power); mphy->txpower_cur = tx_power; @@ -2346,10 +2151,13 @@ int mt7615_mcu_set_chan_info(struct mt7615_phy *phy, int cmd) .center_chan2 = ieee80211_frequency_to_channel(freq2), }; - if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) + if (cmd == MCU_EXT_CMD(SET_RX_PATH) || + dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR) + req.switch_reason = CH_SWITCH_NORMAL; + else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; - else if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) && - chandef->chan->dfs_state != NL80211_DFS_AVAILABLE) + else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef, + NL80211_IFTYPE_AP)) req.switch_reason = CH_SWITCH_DFS; else req.switch_reason = CH_SWITCH_NORMAL; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c index 33f72f3657d0..ce45c3bfc443 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c @@ -194,6 +194,7 @@ int mt7615_mmio_probe(struct device *pdev, void __iomem *mem_base, .token_size = MT7615_TOKEN_SIZE, .tx_prepare_skb = mt7615_tx_prepare_skb, .tx_complete_skb = mt7615_tx_complete_skb, + .rx_check = mt7615_rx_check, .rx_skb = mt7615_queue_rx_skb, .rx_poll_complete = mt7615_rx_poll_complete, .sta_ps = mt7615_sta_ps, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h index 6ff6d5800918..2e91f6a27d0f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h @@ -403,30 +403,9 @@ int mt7615_mcu_set_chan_info(struct mt7615_phy *phy, int cmd); int mt7615_mcu_set_wmm(struct mt7615_dev *dev, u8 queue, const struct ieee80211_tx_queue_params *params); void mt7615_mcu_rx_event(struct mt7615_dev *dev, struct sk_buff *skb); -int mt7615_mcu_rdd_cmd(struct mt7615_dev *dev, - enum mt7615_rdd_cmd cmd, u8 index, - u8 rx_sel, u8 val); int mt7615_mcu_rdd_send_pattern(struct mt7615_dev *dev); int mt7615_mcu_fw_log_2_host(struct mt7615_dev *dev, u8 ctrl); -static inline bool is_mt7622(struct mt76_dev *dev) -{ - if (!IS_ENABLED(CONFIG_MT7622_WMAC)) - return false; - - return mt76_chip(dev) == 0x7622; -} - -static inline bool is_mt7615(struct mt76_dev *dev) -{ - return mt76_chip(dev) == 0x7615 || mt76_chip(dev) == 0x7611; -} - -static inline bool is_mt7611(struct mt76_dev *dev) -{ - return mt76_chip(dev) == 0x7611; -} - static inline void mt7615_irq_enable(struct mt7615_dev *dev, u32 mask) { mt76_set_irq_mask(&dev->mt76, 0, 0, mask); @@ -530,6 +509,7 @@ int mt7615_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, void mt7615_tx_worker(struct mt76_worker *w); void mt7615_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e); void mt7615_tx_token_put(struct mt7615_dev *dev); +bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len); void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, struct sk_buff *skb); void mt7615_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); @@ -579,6 +559,7 @@ void mt7663_usb_sdio_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e); int mt7663_usb_sdio_register_device(struct mt7615_dev *dev); int mt7663u_mcu_init(struct mt7615_dev *dev); +int mt7663u_mcu_power_on(struct mt7615_dev *dev); /* sdio */ int mt7663s_mcu_init(struct mt7615_dev *dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c index 31c4a76b7f91..49ab3a1f3b9b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c @@ -56,7 +56,10 @@ static int mt7663s_parse_intr(struct mt76_dev *dev, struct mt76s_intr *intr) struct mt7663s_intr *irq_data = sdio->intr_data; int i, err; + sdio_claim_host(sdio->func); err = sdio_readsb(sdio->func, irq_data, MCR_WHISR, sizeof(*irq_data)); + sdio_release_host(sdio->func); + if (err) return err; @@ -98,7 +101,7 @@ static int mt7663s_probe(struct sdio_func *func, struct ieee80211_ops *ops; struct mt7615_dev *dev; struct mt76_dev *mdev; - int i, ret; + int ret; ops = devm_kmemdup(&func->dev, &mt7615_ops, sizeof(mt7615_ops), GFP_KERNEL); @@ -137,16 +140,6 @@ static int mt7663s_probe(struct sdio_func *func, goto error; } - for (i = 0; i < ARRAY_SIZE(mdev->sdio.xmit_buf); i++) { - mdev->sdio.xmit_buf[i] = devm_kmalloc(mdev->dev, - MT76S_XMIT_BUF_SZ, - GFP_KERNEL); - if (!mdev->sdio.xmit_buf[i]) { - ret = -ENOMEM; - goto error; - } - } - ret = mt76s_alloc_rx_queue(mdev, MT_RXQ_MAIN); if (ret) goto error; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c index 0396ad532ba6..967641aebf5f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c @@ -17,9 +17,68 @@ static const struct usb_device_id mt7615_device_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7663, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x043e, 0x310c, 0xff, 0xff, 0xff) }, { }, }; +static u32 mt7663u_rr(struct mt76_dev *dev, u32 addr) +{ + u32 ret; + + mutex_lock(&dev->usb.usb_ctrl_mtx); + ret = ___mt76u_rr(dev, MT_VEND_READ_EXT, + USB_DIR_IN | USB_TYPE_VENDOR, addr); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return ret; +} + +static void mt7663u_wr(struct mt76_dev *dev, u32 addr, u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + ___mt76u_wr(dev, MT_VEND_WRITE_EXT, + USB_DIR_OUT | USB_TYPE_VENDOR, addr, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); +} + +static u32 mt7663u_rmw(struct mt76_dev *dev, u32 addr, + u32 mask, u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + val |= ___mt76u_rr(dev, MT_VEND_READ_EXT, + USB_DIR_IN | USB_TYPE_VENDOR, addr) & ~mask; + ___mt76u_wr(dev, MT_VEND_WRITE_EXT, + USB_DIR_OUT | USB_TYPE_VENDOR, addr, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return val; +} + +static void mt7663u_copy(struct mt76_dev *dev, u32 offset, + const void *data, int len) +{ + struct mt76_usb *usb = &dev->usb; + int ret, i = 0, batch_len; + const u8 *val = data; + + len = round_up(len, 4); + + mutex_lock(&usb->usb_ctrl_mtx); + while (i < len) { + batch_len = min_t(int, usb->data_len, len - i); + memcpy(usb->data, val + i, batch_len); + ret = __mt76u_vendor_request(dev, MT_VEND_WRITE_EXT, + USB_DIR_OUT | USB_TYPE_VENDOR, + (offset + i) >> 16, offset + i, + usb->data, batch_len); + if (ret < 0) + break; + + i += batch_len; + } + mutex_unlock(&usb->usb_ctrl_mtx); +} + static void mt7663u_stop(struct ieee80211_hw *hw) { struct mt7615_phy *phy = mt7615_hw_phy(hw); @@ -65,6 +124,14 @@ static int mt7663u_probe(struct usb_interface *usb_intf, .sta_remove = mt7615_mac_sta_remove, .update_survey = mt7615_update_channel, }; + static struct mt76_bus_ops bus_ops = { + .rr = mt7663u_rr, + .wr = mt7663u_wr, + .rmw = mt7663u_rmw, + .read_copy = mt76u_read_copy, + .write_copy = mt7663u_copy, + .type = MT76_BUS_USB, + }; struct usb_device *udev = interface_to_usbdev(usb_intf); struct ieee80211_ops *ops; struct mt7615_dev *dev; @@ -91,7 +158,7 @@ static int mt7663u_probe(struct usb_interface *usb_intf, INIT_WORK(&dev->mcu_work, mt7663u_init_work); dev->reg_map = mt7663_usb_sdio_reg_map; dev->ops = ops; - ret = mt76u_init(mdev, usb_intf, true); + ret = __mt76u_init(mdev, usb_intf, &bus_ops); if (ret < 0) goto error; @@ -99,27 +166,15 @@ static int mt7663u_probe(struct usb_interface *usb_intf, (mt76_rr(dev, MT_HW_REV) & 0xff); dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); - if (mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_PWR_ON, - FW_STATE_PWR_ON << 1, 500)) { - dev_dbg(dev->mt76.dev, "Usb device already powered on\n"); - set_bit(MT76_STATE_POWER_OFF, &dev->mphy.state); - goto alloc_queues; - } - - ret = mt76u_vendor_request(&dev->mt76, MT_VEND_POWER_ON, - USB_DIR_OUT | USB_TYPE_VENDOR, - 0x0, 0x1, NULL, 0); - if (ret) - goto error; - if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_PWR_ON, FW_STATE_PWR_ON << 1, 500)) { - dev_err(dev->mt76.dev, "Timeout for power on\n"); - ret = -EIO; - goto error; + ret = mt7663u_mcu_power_on(dev); + if (ret) + goto error; + } else { + set_bit(MT76_STATE_POWER_OFF, &dev->mphy.state); } -alloc_queues: ret = mt76u_alloc_mcu_queue(&dev->mt76); if (ret) goto error; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb_mcu.c index 0ebb4c3c336a..98bf2f6ae936 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/usb_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb_mcu.c @@ -42,6 +42,26 @@ mt7663u_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, return ret; } +int mt7663u_mcu_power_on(struct mt7615_dev *dev) +{ + int ret; + + ret = mt76u_vendor_request(&dev->mt76, MT_VEND_POWER_ON, + USB_DIR_OUT | USB_TYPE_VENDOR, + 0x0, 0x1, NULL, 0); + if (ret) + return ret; + + if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, + MT_TOP_MISC2_FW_PWR_ON, + FW_STATE_PWR_ON << 1, 500)) { + dev_err(dev->mt76.dev, "Timeout for power on\n"); + ret = -EIO; + } + + return 0; +} + int mt7663u_mcu_init(struct mt7615_dev *dev) { static const struct mt76_mcu_ops mt7663u_mcu_ops = { @@ -57,23 +77,17 @@ int mt7663u_mcu_init(struct mt7615_dev *dev) mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); if (test_and_clear_bit(MT76_STATE_POWER_OFF, &dev->mphy.state)) { - mt7615_mcu_restart(&dev->mt76); - if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, - MT_TOP_MISC2_FW_PWR_ON, 0, 500)) - return -EIO; - - ret = mt76u_vendor_request(&dev->mt76, MT_VEND_POWER_ON, - USB_DIR_OUT | USB_TYPE_VENDOR, - 0x0, 0x1, NULL, 0); + ret = mt7615_mcu_restart(&dev->mt76); if (ret) return ret; if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, - MT_TOP_MISC2_FW_PWR_ON, - FW_STATE_PWR_ON << 1, 500)) { - dev_err(dev->mt76.dev, "Timeout for power on\n"); + MT_TOP_MISC2_FW_PWR_ON, 0, 500)) return -EIO; - } + + ret = mt7663u_mcu_power_on(dev); + if (ret) + return ret; } ret = __mt7663_load_firmware(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index e7f01c2978a2..400ba514460e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -45,9 +45,11 @@ enum { }; struct mt76_connac_pm { - bool enable; - bool ds_enable; - bool suspended; + bool enable:1; + bool enable_user:1; + bool ds_enable:1; + bool ds_enable_user:1; + bool suspended:1; spinlock_t txq_lock; struct { @@ -83,6 +85,11 @@ struct mt76_connac_coredump { unsigned long last_activity; }; +struct mt76_connac_sta_key_conf { + s8 keyidx; + u8 key[16]; +}; + extern const struct wiphy_wowlan_support mt76_connac_wowlan_support; static inline bool is_mt7922(struct mt76_dev *dev) @@ -100,6 +107,69 @@ static inline bool is_mt7663(struct mt76_dev *dev) return mt76_chip(dev) == 0x7663; } +static inline bool is_mt7915(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7915; +} + +static inline bool is_mt7916(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7906; +} + +static inline bool is_mt7986(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7986; +} + +static inline bool is_mt7622(struct mt76_dev *dev) +{ + if (!IS_ENABLED(CONFIG_MT7622_WMAC)) + return false; + + return mt76_chip(dev) == 0x7622; +} + +static inline bool is_mt7615(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7615 || mt76_chip(dev) == 0x7611; +} + +static inline bool is_mt7611(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7611; +} + +static inline bool is_connac_v1(struct mt76_dev *dev) +{ + return is_mt7615(dev) || is_mt7663(dev) || is_mt7622(dev); +} + +static inline u8 mt76_connac_chan_bw(struct cfg80211_chan_def *chandef) +{ + static const u8 width_to_bw[] = { + [NL80211_CHAN_WIDTH_40] = CMD_CBW_40MHZ, + [NL80211_CHAN_WIDTH_80] = CMD_CBW_80MHZ, + [NL80211_CHAN_WIDTH_80P80] = CMD_CBW_8080MHZ, + [NL80211_CHAN_WIDTH_160] = CMD_CBW_160MHZ, + [NL80211_CHAN_WIDTH_5] = CMD_CBW_5MHZ, + [NL80211_CHAN_WIDTH_10] = CMD_CBW_10MHZ, + [NL80211_CHAN_WIDTH_20] = CMD_CBW_20MHZ, + [NL80211_CHAN_WIDTH_20_NOHT] = CMD_CBW_20MHZ, + }; + + if (chandef->width >= ARRAY_SIZE(width_to_bw)) + return 0; + + return width_to_bw[chandef->width]; +} + +static inline u8 mt76_connac_lmac_mapping(u8 ac) +{ + /* LMAC uses the reverse order of mac80211 AC indexes */ + return 3 - ac; +} + int mt76_connac_pm_wake(struct mt76_phy *phy, struct mt76_connac_pm *pm); void mt76_connac_power_save_sched(struct mt76_phy *phy, struct mt76_connac_pm *pm); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index f79e3d5084f3..7cb17bf40e35 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -62,8 +62,8 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len, }; int cmd; - if (is_mt7921(dev) && - (req.addr == cpu_to_le32(MCU_PATCH_ADDRESS) || addr == 0x900000)) + if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) || + (is_mt7921(dev) && addr == 0x900000)) cmd = MCU_CMD(PATCH_START_REQ); else cmd = MCU_CMD(TARGET_ADDRESS_LEN_REQ); @@ -266,8 +266,8 @@ mt76_connac_mcu_add_nested_tlv(struct sk_buff *skb, int tag, int len, EXPORT_SYMBOL_GPL(mt76_connac_mcu_add_nested_tlv); struct sk_buff * -mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif *mvif, - struct mt76_wcid *wcid) +__mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif *mvif, + struct mt76_wcid *wcid, int len) { struct sta_req_hdr hdr = { .bss_idx = mvif->idx, @@ -278,7 +278,7 @@ mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif *mvif, mt76_connac_mcu_get_wlan_idx(dev, wcid, &hdr.wlan_idx_lo, &hdr.wlan_idx_hi); - skb = mt76_mcu_msg_alloc(dev, NULL, MT76_CONNAC_STA_UPDATE_MAX_SIZE); + skb = mt76_mcu_msg_alloc(dev, NULL, len); if (!skb) return ERR_PTR(-ENOMEM); @@ -286,7 +286,7 @@ mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif *mvif, return skb; } -EXPORT_SYMBOL_GPL(mt76_connac_mcu_alloc_sta_req); +EXPORT_SYMBOL_GPL(__mt76_connac_mcu_alloc_sta_req); struct wtbl_req_hdr * mt76_connac_mcu_alloc_wtbl_req(struct mt76_dev *dev, struct mt76_wcid *wcid, @@ -310,12 +310,54 @@ mt76_connac_mcu_alloc_wtbl_req(struct mt76_dev *dev, struct mt76_wcid *wcid, } if (sta_hdr) - sta_hdr->len = cpu_to_le16(sizeof(hdr)); + le16_add_cpu(&sta_hdr->len, sizeof(hdr)); return skb_put_data(nskb, &hdr, sizeof(hdr)); } EXPORT_SYMBOL_GPL(mt76_connac_mcu_alloc_wtbl_req); +void mt76_connac_mcu_bss_omac_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif) +{ + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + u8 omac_idx = mvif->omac_idx; + struct bss_info_omac *omac; + struct tlv *tlv; + u32 type = 0; + + switch (vif->type) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + if (vif->p2p) + type = CONNECTION_P2P_GO; + else + type = CONNECTION_INFRA_AP; + break; + case NL80211_IFTYPE_STATION: + if (vif->p2p) + type = CONNECTION_P2P_GC; + else + type = CONNECTION_INFRA_STA; + break; + case NL80211_IFTYPE_ADHOC: + type = CONNECTION_IBSS_ADHOC; + break; + default: + WARN_ON(1); + break; + } + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_OMAC, sizeof(*omac)); + + omac = (struct bss_info_omac *)tlv; + omac->conn_type = cpu_to_le32(type); + omac->omac_idx = mvif->omac_idx; + omac->band_idx = mvif->band_idx; + omac->hw_bss_idx = omac_idx > EXT_BSSID_START ? HW_BSSID_0 : omac_idx; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_bss_omac_tlv); + void mt76_connac_mcu_sta_basic_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -376,9 +418,8 @@ void mt76_connac_mcu_sta_basic_tlv(struct sk_buff *skb, } EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_basic_tlv); -static void -mt76_connac_mcu_sta_uapsd(struct sk_buff *skb, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) +void mt76_connac_mcu_sta_uapsd(struct sk_buff *skb, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) { struct sta_rec_uapsd *uapsd; struct tlv *tlv; @@ -407,6 +448,7 @@ mt76_connac_mcu_sta_uapsd(struct sk_buff *skb, struct ieee80211_vif *vif, } uapsd->max_sp = sta->max_sp; } +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_uapsd); void mt76_connac_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, @@ -420,13 +462,17 @@ void mt76_connac_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb, sizeof(*htr), wtbl_tlv, sta_wtbl); htr = (struct wtbl_hdr_trans *)tlv; - htr->no_rx_trans = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags); + htr->no_rx_trans = true; if (vif->type == NL80211_IFTYPE_STATION) htr->to_ds = true; else htr->from_ds = true; + if (!wcid) + return; + + htr->no_rx_trans = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags); if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) { htr->to_ds = true; htr->from_ds = true; @@ -461,6 +507,25 @@ int mt76_connac_mcu_sta_update_hdr_trans(struct mt76_dev *dev, } EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_update_hdr_trans); +int mt76_connac_mcu_wtbl_update_hdr_trans(struct mt76_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; + struct wtbl_req_hdr *wtbl_hdr; + struct sk_buff *skb = NULL; + + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(dev, wcid, WTBL_SET, NULL, + &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, vif, wcid, NULL, wtbl_hdr); + + return mt76_mcu_skb_send_msg(dev, skb, MCU_EXT_CMD(WTBL_UPDATE), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_update_hdr_trans); + void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev, struct sk_buff *skb, struct ieee80211_vif *vif, @@ -488,8 +553,7 @@ void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev, generic->muar_idx = mvif->omac_idx; generic->qos = sta->wme; } else { - if (is_mt7921(dev) && - vif->type == NL80211_IFTYPE_STATION) + if (!is_connac_v1(dev) && vif->type == NL80211_IFTYPE_STATION) memcpy(generic->peer_addr, vif->bss_conf.bssid, ETH_ALEN); else @@ -506,7 +570,7 @@ void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev, rx->rca2 = 1; rx->rv = 1; - if (is_mt7921(dev)) + if (!is_connac_v1(dev)) return; tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_SPE, sizeof(*spe), @@ -819,9 +883,9 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb, } EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_tlv); -static void -mt76_connac_mcu_wtbl_smps_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, - void *sta_wtbl, void *wtbl_tlv) +void mt76_connac_mcu_wtbl_smps_tlv(struct sk_buff *skb, + struct ieee80211_sta *sta, + void *sta_wtbl, void *wtbl_tlv) { struct wtbl_smps *smps; struct tlv *tlv; @@ -829,30 +893,39 @@ mt76_connac_mcu_wtbl_smps_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_SMPS, sizeof(*smps), wtbl_tlv, sta_wtbl); smps = (struct wtbl_smps *)tlv; - - if (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) - smps->smps = true; + smps->smps = (sta->smps_mode == IEEE80211_SMPS_DYNAMIC); } +EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_smps_tlv); void mt76_connac_mcu_wtbl_ht_tlv(struct mt76_dev *dev, struct sk_buff *skb, struct ieee80211_sta *sta, void *sta_wtbl, - void *wtbl_tlv) + void *wtbl_tlv, bool ht_ldpc, bool vht_ldpc) { struct wtbl_ht *ht = NULL; struct tlv *tlv; u32 flags = 0; - if (sta->ht_cap.ht_supported) { + if (sta->ht_cap.ht_supported || sta->he_6ghz_capa.capa) { tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_HT, sizeof(*ht), wtbl_tlv, sta_wtbl); ht = (struct wtbl_ht *)tlv; - ht->ldpc = !!(sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING); - ht->af = sta->ht_cap.ampdu_factor; - ht->mm = sta->ht_cap.ampdu_density; + ht->ldpc = ht_ldpc && + !!(sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING); + + if (sta->ht_cap.ht_supported) { + ht->af = sta->ht_cap.ampdu_factor; + ht->mm = sta->ht_cap.ampdu_density; + } else { + ht->af = le16_get_bits(sta->he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + ht->mm = le16_get_bits(sta->he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); + } + ht->ht = true; } - if (sta->vht_cap.vht_supported) { + if (sta->vht_cap.vht_supported || sta->he_6ghz_capa.capa) { struct wtbl_vht *vht; u8 af; @@ -860,7 +933,8 @@ void mt76_connac_mcu_wtbl_ht_tlv(struct mt76_dev *dev, struct sk_buff *skb, sizeof(*vht), wtbl_tlv, sta_wtbl); vht = (struct wtbl_vht *)tlv; - vht->ldpc = !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC); + vht->ldpc = vht_ldpc && + !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC); vht->vht = true; af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, @@ -871,7 +945,7 @@ void mt76_connac_mcu_wtbl_ht_tlv(struct mt76_dev *dev, struct sk_buff *skb, mt76_connac_mcu_wtbl_smps_tlv(skb, sta, sta_wtbl, wtbl_tlv); - if (!is_mt7921(dev) && sta->ht_cap.ht_supported) { + if (is_connac_v1(dev) && sta->ht_cap.ht_supported) { /* sgi */ u32 msk = MT_WTBL_W5_SHORT_GI_20 | MT_WTBL_W5_SHORT_GI_40 | MT_WTBL_W5_SHORT_GI_80 | MT_WTBL_W5_SHORT_GI_160; @@ -939,7 +1013,8 @@ int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy, sta_wtbl, wtbl_hdr); if (info->sta) mt76_connac_mcu_wtbl_ht_tlv(dev, skb, info->sta, - sta_wtbl, wtbl_hdr); + sta_wtbl, wtbl_hdr, + true, true); } return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true); @@ -973,13 +1048,13 @@ void mt76_connac_mcu_wtbl_ba_tlv(struct mt76_dev *dev, struct sk_buff *skb, ba->rst_ba_sb = 1; } - if (is_mt7921(dev)) { + if (!is_connac_v1(dev)) { ba->ba_winsize = enable ? cpu_to_le16(params->buf_size) : 0; return; } if (enable && tx) { - u8 ba_range[] = { 4, 8, 12, 24, 36, 48, 54, 64 }; + static const u8 ba_range[] = { 4, 8, 12, 24, 36, 48, 54, 64 }; int i; for (i = 7; i > 0; i--) { @@ -1106,7 +1181,7 @@ EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_ba_tlv); int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, struct ieee80211_ampdu_params *params, - bool enable, bool tx) + int cmd, bool enable, bool tx) { struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv; struct wtbl_req_hdr *wtbl_hdr; @@ -1129,8 +1204,7 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, mt76_connac_mcu_wtbl_ba_tlv(dev, skb, params, enable, tx, sta_wtbl, wtbl_hdr); - ret = mt76_mcu_skb_send_msg(dev, skb, - MCU_UNI_CMD(STA_REC_UPDATE), true); + ret = mt76_mcu_skb_send_msg(dev, skb, cmd, true); if (ret) return ret; @@ -1140,15 +1214,12 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, mt76_connac_mcu_sta_ba_tlv(skb, params, enable, tx); - return mt76_mcu_skb_send_msg(dev, skb, - MCU_UNI_CMD(STA_REC_UPDATE), true); + return mt76_mcu_skb_send_msg(dev, skb, cmd, true); } EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_ba); -static u8 -mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif, - enum nl80211_band band, - struct ieee80211_sta *sta) +u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif, + enum nl80211_band band, struct ieee80211_sta *sta) { struct mt76_dev *dev = phy->dev; const struct ieee80211_sta_he_cap *he_cap; @@ -1156,7 +1227,7 @@ mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif, struct ieee80211_sta_ht_cap *ht_cap; u8 mode = 0; - if (!is_mt7921(dev)) + if (is_connac_v1(dev)) return 0x38; if (sta) { @@ -1180,7 +1251,7 @@ mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif, if (he_cap && he_cap->has_he) mode |= PHY_MODE_AX_24G; - } else if (band == NL80211_BAND_5GHZ || band == NL80211_BAND_6GHZ) { + } else if (band == NL80211_BAND_5GHZ) { mode |= PHY_MODE_A; if (ht_cap->ht_supported) @@ -1189,14 +1260,18 @@ mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif, if (vht_cap->vht_supported) mode |= PHY_MODE_AC; - if (he_cap && he_cap->has_he && band == NL80211_BAND_5GHZ) + if (he_cap && he_cap->has_he) mode |= PHY_MODE_AX_5G; + } else if (band == NL80211_BAND_6GHZ) { + mode |= PHY_MODE_A | PHY_MODE_AN | + PHY_MODE_AC | PHY_MODE_AX_5G; } return mode; } +EXPORT_SYMBOL_GPL(mt76_connac_get_phy_mode); -static const struct ieee80211_sta_he_cap * +const struct ieee80211_sta_he_cap * mt76_connac_get_he_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif) { enum nl80211_band band = phy->chandef.chan->band; @@ -1206,6 +1281,7 @@ mt76_connac_get_he_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif) return ieee80211_get_he_iftype_cap(sband, vif->type); } +EXPORT_SYMBOL_GPL(mt76_connac_get_he_phy_cap); #define DEFAULT_HE_PE_DURATION 4 #define DEFAULT_HE_DURATION_RTS_THRES 1023 @@ -1438,7 +1514,6 @@ int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, int ext_channels_num = max_t(int, sreq->n_channels - 32, 0); struct ieee80211_channel **scan_list = sreq->channels; struct mt76_dev *mdev = phy->dev; - bool ext_phy = phy == mdev->phy2; struct mt76_connac_mcu_scan_channel *chan; struct mt76_connac_hw_scan_req *req; struct sk_buff *skb; @@ -1452,7 +1527,7 @@ int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, req = (struct mt76_connac_hw_scan_req *)skb_put(skb, sizeof(*req)); - req->seq_num = mvif->scan_seq_num | ext_phy << 7; + req->seq_num = mvif->scan_seq_num | mvif->band_idx << 7; req->bss_idx = mvif->idx; req->scan_type = sreq->n_ssids ? 1 : 0; req->probe_req_num = sreq->n_ssids ? 2 : 0; @@ -1560,7 +1635,6 @@ int mt76_connac_mcu_sched_scan_req(struct mt76_phy *phy, struct mt76_connac_mcu_scan_channel *chan; struct mt76_connac_sched_scan_req *req; struct mt76_dev *mdev = phy->dev; - bool ext_phy = phy == mdev->phy2; struct cfg80211_match_set *match; struct cfg80211_ssid *ssid; struct sk_buff *skb; @@ -1574,7 +1648,7 @@ int mt76_connac_mcu_sched_scan_req(struct mt76_phy *phy, req = (struct mt76_connac_sched_scan_req *)skb_put(skb, sizeof(*req)); req->version = 1; - req->seq_num = mvif->scan_seq_num | ext_phy << 7; + req->seq_num = mvif->scan_seq_num | mvif->band_idx << 7; if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { u8 *addr = is_mt7663(phy->dev) ? req->mt7663.random_mac @@ -2482,5 +2556,257 @@ void mt76_connac_mcu_reg_wr(struct mt76_dev *dev, u32 offset, u32 val) } EXPORT_SYMBOL_GPL(mt76_connac_mcu_reg_wr); +static int +mt76_connac_mcu_sta_key_tlv(struct mt76_connac_sta_key_conf *sta_key_conf, + struct sk_buff *skb, + struct ieee80211_key_conf *key, + enum set_key_cmd cmd) +{ + struct sta_rec_sec *sec; + u32 len = sizeof(*sec); + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V2, sizeof(*sec)); + sec = (struct sta_rec_sec *)tlv; + sec->add = cmd; + + if (cmd == SET_KEY) { + struct sec_key *sec_key; + u8 cipher; + + cipher = mt76_connac_mcu_get_cipher(key->cipher); + if (cipher == MCU_CIPHER_NONE) + return -EOPNOTSUPP; + + sec_key = &sec->key[0]; + sec_key->cipher_len = sizeof(*sec_key); + + if (cipher == MCU_CIPHER_BIP_CMAC_128) { + sec_key->cipher_id = MCU_CIPHER_AES_CCMP; + sec_key->key_id = sta_key_conf->keyidx; + sec_key->key_len = 16; + memcpy(sec_key->key, sta_key_conf->key, 16); + + sec_key = &sec->key[1]; + sec_key->cipher_id = MCU_CIPHER_BIP_CMAC_128; + sec_key->cipher_len = sizeof(*sec_key); + sec_key->key_len = 16; + memcpy(sec_key->key, key->key, 16); + sec->n_cipher = 2; + } else { + sec_key->cipher_id = cipher; + sec_key->key_id = key->keyidx; + sec_key->key_len = key->keylen; + memcpy(sec_key->key, key->key, key->keylen); + + if (cipher == MCU_CIPHER_TKIP) { + /* Rx/Tx MIC keys are swapped */ + memcpy(sec_key->key + 16, key->key + 24, 8); + memcpy(sec_key->key + 24, key->key + 16, 8); + } + + /* store key_conf for BIP batch update */ + if (cipher == MCU_CIPHER_AES_CCMP) { + memcpy(sta_key_conf->key, key->key, key->keylen); + sta_key_conf->keyidx = key->keyidx; + } + + len -= sizeof(*sec_key); + sec->n_cipher = 1; + } + } else { + len -= sizeof(sec->key); + sec->n_cipher = 0; + } + sec->len = cpu_to_le16(len); + + return 0; +} + +int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct ieee80211_key_conf *key, int mcu_cmd, + struct mt76_wcid *wcid, enum set_key_cmd cmd) +{ + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + struct sk_buff *skb; + int ret; + + skb = mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ret = mt76_connac_mcu_sta_key_tlv(sta_key_conf, skb, key, cmd); + if (ret) + return ret; + + return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_add_key); + +/* SIFS 20us + 512 byte beacon tranmitted by 1Mbps (3906us) */ +#define BCN_TX_ESTIMATE_TIME (4096 + 20) +void mt76_connac_mcu_bss_ext_tlv(struct sk_buff *skb, struct mt76_vif *mvif) +{ + struct bss_info_ext_bss *ext; + int ext_bss_idx, tsf_offset; + struct tlv *tlv; + + ext_bss_idx = mvif->omac_idx - EXT_BSSID_START; + if (ext_bss_idx < 0) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_EXT_BSS, sizeof(*ext)); + + ext = (struct bss_info_ext_bss *)tlv; + tsf_offset = ext_bss_idx * BCN_TX_ESTIMATE_TIME; + ext->mbss_tsf_offset = cpu_to_le32(tsf_offset); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_bss_ext_tlv); + +int mt76_connac_mcu_bss_basic_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct mt76_phy *phy, u16 wlan_idx, + bool enable) +{ + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + u32 type = vif->p2p ? NETWORK_P2P : NETWORK_INFRA; + struct bss_info_basic *bss; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_BASIC, sizeof(*bss)); + bss = (struct bss_info_basic *)tlv; + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_MONITOR: + break; + case NL80211_IFTYPE_AP: + if (ieee80211_hw_check(phy->hw, SUPPORTS_MULTI_BSSID)) { + u8 bssid_id = vif->bss_conf.bssid_indicator; + struct wiphy *wiphy = phy->hw->wiphy; + + if (bssid_id > ilog2(wiphy->mbssid_max_interfaces)) + return -EINVAL; + + bss->non_tx_bssid = vif->bss_conf.bssid_index; + bss->max_bssid = bssid_id; + } + break; + case NL80211_IFTYPE_STATION: + if (enable) { + rcu_read_lock(); + if (!sta) + sta = ieee80211_find_sta(vif, + vif->bss_conf.bssid); + /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */ + if (sta) { + struct mt76_wcid *wcid; + + wcid = (struct mt76_wcid *)sta->drv_priv; + wlan_idx = wcid->idx; + } + rcu_read_unlock(); + } + break; + case NL80211_IFTYPE_ADHOC: + type = NETWORK_IBSS; + break; + default: + WARN_ON(1); + break; + } + + bss->network_type = cpu_to_le32(type); + bss->bmc_wcid_lo = to_wcid_lo(wlan_idx); + bss->bmc_wcid_hi = to_wcid_hi(wlan_idx); + bss->wmm_idx = mvif->wmm_idx; + bss->active = enable; + bss->cipher = mvif->cipher; + + if (vif->type != NL80211_IFTYPE_MONITOR) { + struct cfg80211_chan_def *chandef = &phy->chandef; + + memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN); + bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int); + bss->dtim_period = vif->bss_conf.dtim_period; + bss->phy_mode = mt76_connac_get_phy_mode(phy, vif, + chandef->chan->band, NULL); + } else { + memcpy(bss->bssid, phy->macaddr, ETH_ALEN); + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_bss_basic_tlv); + +#define ENTER_PM_STATE 1 +#define EXIT_PM_STATE 2 +int mt76_connac_mcu_set_pm(struct mt76_dev *dev, int band, int enter) +{ + struct { + u8 pm_number; + u8 pm_state; + u8 bssid[ETH_ALEN]; + u8 dtim_period; + u8 wlan_idx_lo; + __le16 bcn_interval; + __le32 aid; + __le32 rx_filter; + u8 band_idx; + u8 wlan_idx_hi; + u8 rsv[2]; + __le32 feature; + u8 omac_idx; + u8 wmm_idx; + u8 bcn_loss_cnt; + u8 bcn_sp_duration; + } __packed req = { + .pm_number = 5, + .pm_state = enter ? ENTER_PM_STATE : EXIT_PM_STATE, + .band_idx = band, + }; + + return mt76_mcu_send_msg(dev, MCU_EXT_CMD(PM_STATE_CTRL), &req, + sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_pm); + +int mt76_connac_mcu_restart(struct mt76_dev *dev) +{ + struct { + u8 power_mode; + u8 rsv[3]; + } req = { + .power_mode = 1, + }; + + return mt76_mcu_send_msg(dev, MCU_CMD(NIC_POWER_CTRL), &req, + sizeof(req), false); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_restart); + +int mt76_connac_mcu_rdd_cmd(struct mt76_dev *dev, int cmd, u8 index, + u8 rx_sel, u8 val) +{ + struct { + u8 ctrl; + u8 rdd_idx; + u8 rdd_rx_sel; + u8 val; + u8 rsv[4]; + } __packed req = { + .ctrl = cmd, + .rdd_idx = index, + .rdd_rx_sel = rx_sel, + .val = val, + }; + + return mt76_mcu_send_msg(dev, MCU_EXT_CMD(SET_RDD_CTRL), &req, + sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_rdd_cmd); + MODULE_AUTHOR("Lorenzo Bianconi "); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 5baf8370b7bd..c3c93338d56a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -6,6 +6,26 @@ #include "mt76_connac.h" +#define FW_FEATURE_SET_ENCRYPT BIT(0) +#define FW_FEATURE_SET_KEY_IDX GENMASK(2, 1) +#define FW_FEATURE_ENCRY_MODE BIT(4) +#define FW_FEATURE_OVERRIDE_ADDR BIT(5) + +#define DL_MODE_ENCRYPT BIT(0) +#define DL_MODE_KEY_IDX GENMASK(2, 1) +#define DL_MODE_RESET_SEC_IV BIT(3) +#define DL_MODE_WORKING_PDA_CR4 BIT(4) +#define DL_MODE_VALID_RAM_ENTRY BIT(5) +#define DL_CONFIG_ENCRY_MODE_SEL BIT(6) +#define DL_MODE_NEED_RSP BIT(31) + +#define FW_START_OVERRIDE BIT(0) +#define FW_START_WORKING_PDA_CR4 BIT(2) + +#define PATCH_SEC_NOT_SUPPORT GENMASK(31, 0) +#define PATCH_SEC_TYPE_MASK GENMASK(15, 0) +#define PATCH_SEC_TYPE_INFO 0x2 + struct tlv { __le16 tag; __le16 len; @@ -570,6 +590,7 @@ struct wtbl_raw { sizeof(struct sta_rec_muru) + \ sizeof(struct sta_rec_bfee) + \ sizeof(struct sta_rec_ra) + \ + sizeof(struct sta_rec_sec) + \ sizeof(struct sta_rec_ra_fixed) + \ sizeof(struct sta_rec_he_6g_capa) + \ sizeof(struct tlv) + \ @@ -956,6 +977,7 @@ enum { MCU_EXT_CMD_SCS_CTRL = 0x82, MCU_EXT_CMD_TWT_AGRT_UPDATE = 0x94, MCU_EXT_CMD_FW_DBG_CTRL = 0x95, + MCU_EXT_CMD_OFFCH_SCAN_CTRL = 0x9a, MCU_EXT_CMD_SET_RDD_TH = 0x9d, MCU_EXT_CMD_MURU_CTRL = 0x9f, MCU_EXT_CMD_SET_SPR = 0xa8, @@ -971,6 +993,7 @@ enum { MCU_UNI_CMD_SUSPEND = 0x05, MCU_UNI_CMD_OFFLOAD = 0x06, MCU_UNI_CMD_HIF_CTRL = 0x07, + MCU_UNI_CMD_SNIFFER = 0x24, }; enum { @@ -996,7 +1019,8 @@ enum { MCU_CE_CMD_SET_BSS_CONNECTED = 0x16, MCU_CE_CMD_SET_BSS_ABORT = 0x17, MCU_CE_CMD_CANCEL_HW_SCAN = 0x1b, - MCU_CE_CMD_SET_ROC = 0x1d, + MCU_CE_CMD_SET_ROC = 0x1c, + MCU_CE_CMD_SET_EDCA_PARMS = 0x1d, MCU_CE_CMD_SET_P2P_OPPPS = 0x33, MCU_CE_CMD_SET_RATE_TX_POWER = 0x5d, MCU_CE_CMD_SCHED_SCAN_ENABLE = 0x61, @@ -1427,6 +1451,51 @@ struct mt76_connac_config { u8 data[320]; } __packed; +static inline enum mcu_cipher_type +mt76_connac_mcu_get_cipher(int cipher) +{ + switch (cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return MCU_CIPHER_WEP40; + case WLAN_CIPHER_SUITE_WEP104: + return MCU_CIPHER_WEP104; + case WLAN_CIPHER_SUITE_TKIP: + return MCU_CIPHER_TKIP; + case WLAN_CIPHER_SUITE_AES_CMAC: + return MCU_CIPHER_BIP_CMAC_128; + case WLAN_CIPHER_SUITE_CCMP: + return MCU_CIPHER_AES_CCMP; + case WLAN_CIPHER_SUITE_CCMP_256: + return MCU_CIPHER_CCMP_256; + case WLAN_CIPHER_SUITE_GCMP: + return MCU_CIPHER_GCMP; + case WLAN_CIPHER_SUITE_GCMP_256: + return MCU_CIPHER_GCMP_256; + case WLAN_CIPHER_SUITE_SMS4: + return MCU_CIPHER_WAPI; + default: + return MCU_CIPHER_NONE; + } +} + +static inline u32 +mt76_connac_mcu_gen_dl_mode(struct mt76_dev *dev, u8 feature_set, bool is_wa) +{ + u32 ret = 0; + + ret |= feature_set & FW_FEATURE_SET_ENCRYPT ? + DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV : 0; + if (is_mt7921(dev)) + ret |= feature_set & FW_FEATURE_ENCRY_MODE ? + DL_CONFIG_ENCRY_MODE_SEL : 0; + ret |= FIELD_PREP(DL_MODE_KEY_IDX, + FIELD_GET(FW_FEATURE_SET_KEY_IDX, feature_set)); + ret |= DL_MODE_NEED_RSP; + ret |= is_wa ? DL_MODE_WORKING_PDA_CR4 : 0; + + return ret; +} + #define to_wcid_lo(id) FIELD_GET(GENMASK(7, 0), (u16)id) #define to_wcid_hi(id) FIELD_GET(GENMASK(9, 8), (u16)id) @@ -1436,7 +1505,7 @@ mt76_connac_mcu_get_wlan_idx(struct mt76_dev *dev, struct mt76_wcid *wcid, { *wlan_idx_hi = 0; - if (is_mt7921(dev)) { + if (!is_connac_v1(dev)) { *wlan_idx_lo = wcid ? to_wcid_lo(wcid->idx) : 0; *wlan_idx_hi = wcid ? to_wcid_hi(wcid->idx) : 0; } else { @@ -1445,8 +1514,16 @@ mt76_connac_mcu_get_wlan_idx(struct mt76_dev *dev, struct mt76_wcid *wcid, } struct sk_buff * +__mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif *mvif, + struct mt76_wcid *wcid, int len); +static inline struct sk_buff * mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif *mvif, - struct mt76_wcid *wcid); + struct mt76_wcid *wcid) +{ + return __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid, + MT76_CONNAC_STA_UPDATE_MAX_SIZE); +} + struct wtbl_req_hdr * mt76_connac_mcu_alloc_wtbl_req(struct mt76_dev *dev, struct mt76_wcid *wcid, int cmd, void *sta_wtbl, struct sk_buff **skb); @@ -1476,13 +1553,16 @@ void mt76_connac_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb, int mt76_connac_mcu_sta_update_hdr_trans(struct mt76_dev *dev, struct ieee80211_vif *vif, struct mt76_wcid *wcid, int cmd); +int mt76_connac_mcu_wtbl_update_hdr_trans(struct mt76_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb, struct ieee80211_sta *sta, struct ieee80211_vif *vif, u8 rcpi, u8 state); void mt76_connac_mcu_wtbl_ht_tlv(struct mt76_dev *dev, struct sk_buff *skb, struct ieee80211_sta *sta, void *sta_wtbl, - void *wtbl_tlv); + void *wtbl_tlv, bool ht_ldpc, bool vht_ldpc); void mt76_connac_mcu_wtbl_ba_tlv(struct mt76_dev *dev, struct sk_buff *skb, struct ieee80211_ampdu_params *params, bool enable, bool tx, void *sta_wtbl, @@ -1496,7 +1576,7 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, bool enable); int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, struct ieee80211_ampdu_params *params, - bool enable, bool tx); + int cmd, bool enable, bool tx); int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_wcid *wcid, @@ -1546,4 +1626,32 @@ int mt76_connac_mcu_set_p2p_oppps(struct ieee80211_hw *hw, struct ieee80211_vif *vif); u32 mt76_connac_mcu_reg_rr(struct mt76_dev *dev, u32 offset); void mt76_connac_mcu_reg_wr(struct mt76_dev *dev, u32 offset, u32 val); + +const struct ieee80211_sta_he_cap * +mt76_connac_get_he_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif); +u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif, + enum nl80211_band band, struct ieee80211_sta *sta); + +int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct ieee80211_key_conf *key, int mcu_cmd, + struct mt76_wcid *wcid, enum set_key_cmd cmd); + +void mt76_connac_mcu_bss_ext_tlv(struct sk_buff *skb, struct mt76_vif *mvif); +void mt76_connac_mcu_bss_omac_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif); +int mt76_connac_mcu_bss_basic_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct mt76_phy *phy, u16 wlan_idx, + bool enable); +void mt76_connac_mcu_sta_uapsd(struct sk_buff *skb, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt76_connac_mcu_wtbl_smps_tlv(struct sk_buff *skb, + struct ieee80211_sta *sta, + void *sta_wtbl, void *wtbl_tlv); +int mt76_connac_mcu_set_pm(struct mt76_dev *dev, int band, int enter); +int mt76_connac_mcu_restart(struct mt76_dev *dev); +int mt76_connac_mcu_rdd_cmd(struct mt76_dev *dev, int cmd, u8 index, + u8 rx_sel, u8 val); #endif /* __MT76_CONNAC_MCU_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c index 436daf6d6d86..0422c332354a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c @@ -245,7 +245,7 @@ static int mt76x0u_probe(struct usb_interface *usb_intf, usb_set_intfdata(usb_intf, dev); mt76x02u_init_mcu(mdev); - ret = mt76u_init(mdev, usb_intf, false); + ret = mt76u_init(mdev, usb_intf); if (ret) goto err; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h index 44d1a92d9a90..f76fd22ee035 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h @@ -103,7 +103,8 @@ struct mt76x02_dev { u8 tbtt_count; u32 tx_hang_reset; - u8 tx_hang_check; + u8 tx_hang_check[4]; + u8 beacon_hang_check; u8 mcu_timeout; struct mt76x02_calibration cal; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c index a601350531cd..024a5c0a5a57 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c @@ -823,10 +823,7 @@ EXPORT_SYMBOL_GPL(mt76x02_phy_dfs_adjust_agc); void mt76x02_dfs_init_params(struct mt76x02_dev *dev) { - struct cfg80211_chan_def *chandef = &dev->mphy.chandef; - - if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) && - dev->mt76.region != NL80211_DFS_UNSET) { + if (mt76_phy_dfs_state(&dev->mphy) > MT_DFS_STATE_DISABLED) { mt76x02_dfs_init_sw_detector(dev); mt76x02_dfs_set_bbp_params(dev); /* enable debug mode */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c index a404fd7ea968..2afad8c76ca6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c @@ -860,9 +860,7 @@ int mt76x02_mac_process_rx(struct mt76x02_dev *dev, struct sk_buff *skb, status->chain_signal[1] = mt76x02_mac_get_rssi(dev, rxwi->rssi[1], 1); - signal = max_t(s8, signal, status->chain_signal[1]); } - status->signal = signal; status->freq = dev->mphy.chandef.chan->center_freq; status->band = dev->mphy.chandef.chan->band; @@ -1040,12 +1038,26 @@ EXPORT_SYMBOL_GPL(mt76x02_update_channel); static void mt76x02_check_mac_err(struct mt76x02_dev *dev) { - u32 val = mt76_rr(dev, 0x10f4); + if (dev->mt76.beacon_mask) { + if (mt76_rr(dev, MT_TX_STA_0) & MT_TX_STA_0_BEACONS) { + dev->beacon_hang_check = 0; + return; + } - if (!(val & BIT(29)) || !(val & (BIT(7) | BIT(5)))) - return; + if (++dev->beacon_hang_check < 10) + return; - dev_err(dev->mt76.dev, "mac specific condition occurred\n"); + dev->beacon_hang_check = 0; + } else { + u32 val = mt76_rr(dev, 0x10f4); + if (!(val & BIT(29)) || !(val & (BIT(7) | BIT(5)))) + return; + } + + dev_err(dev->mt76.dev, "MAC error detected\n"); + + mt76_wr(dev, MT_MAC_SYS_CTRL, 0); + mt76x02_wait_for_txrx_idle(&dev->mt76); mt76_set(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_RESET_CSR); udelay(10); @@ -1178,8 +1190,7 @@ void mt76x02_mac_work(struct work_struct *work) dev->mt76.aggr_stats[idx++] += val >> 16; } - if (!dev->mt76.beacon_mask) - mt76x02_check_mac_err(dev); + mt76x02_check_mac_err(dev); if (dev->ed_monitor) mt76x02_edcca_check(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c index ec0de691129a..8bcd8afa0d3a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c @@ -348,18 +348,20 @@ static bool mt76x02_tx_hang(struct mt76x02_dev *dev) for (i = 0; i < 4; i++) { q = dev->mphy.q_tx[i]; - if (!q->queued) - continue; - prev_dma_idx = dev->mt76.tx_dma_idx[i]; dma_idx = readl(&q->regs->dma_idx); dev->mt76.tx_dma_idx[i] = dma_idx; - if (prev_dma_idx == dma_idx) - break; + if (!q->queued || prev_dma_idx != dma_idx) { + dev->tx_hang_check[i] = 0; + continue; + } + + if (++dev->tx_hang_check[i] >= MT_TX_HANG_TH) + return true; } - return i < 4; + return false; } static void mt76x02_key_sync(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -530,23 +532,13 @@ static void mt76x02_check_tx_hang(struct mt76x02_dev *dev) if (test_bit(MT76_RESTART, &dev->mphy.state)) return; - if (mt76x02_tx_hang(dev)) { - if (++dev->tx_hang_check >= MT_TX_HANG_TH) - goto restart; - } else { - dev->tx_hang_check = 0; - } + if (!mt76x02_tx_hang(dev) && !dev->mcu_timeout) + return; - if (dev->mcu_timeout) - goto restart; - - return; - -restart: mt76x02_watchdog_reset(dev); dev->tx_hang_reset++; - dev->tx_hang_check = 0; + memset(dev->tx_hang_check, 0, sizeof(dev->tx_hang_check)); memset(dev->mt76.tx_dma_idx, 0xff, sizeof(dev->mt76.tx_dma_idx)); } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h b/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h index fa7872ac22bf..fe0c5e3298bc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h @@ -571,6 +571,8 @@ #define MT_RX_STAT_2_OVERFLOW_ERRORS GENMASK(31, 16) #define MT_TX_STA_0 0x170c +#define MT_TX_STA_0_BEACONS GENMASK(31, 16) + #define MT_TX_STA_1 0x1710 #define MT_TX_STA_2 0x1714 diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c index 2575369e44e2..55068f3252ef 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c @@ -57,7 +57,7 @@ static int mt76x2u_probe(struct usb_interface *intf, usb_set_intfdata(intf, dev); mt76x02u_init_mcu(mdev); - err = mt76u_init(mdev, intf, false); + err = mt76u_init(mdev, intf); if (err < 0) goto err; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig index d98225da694c..f21282cea845 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig @@ -1,9 +1,10 @@ # SPDX-License-Identifier: ISC config MT7915E tristate "MediaTek MT7915E (PCIe) support" - select MT76_CORE + select MT76_CONNAC_LIB depends on MAC80211 depends on PCI + select RELAY help This adds support for MT7915-based wireless PCIe devices, which support concurrent dual-band operation at both 5GHz @@ -11,3 +12,13 @@ config MT7915E OFDMA, spatial reuse and dual carrier modulation. To compile this driver as a module, choose M here. + +config MT7986_WMAC + bool "MT7986 (SoC) WMAC support" + depends on MT7915E + depends on ARCH_MEDIATEK || COMPILE_TEST + select REGMAP + help + This adds support for the built-in WMAC on MT7986 SoC device + which has the same feature set as a MT7915, but enables 6E + support. diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile index 80e49244348e..b794ceb79c37 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile +++ b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile @@ -6,3 +6,4 @@ mt7915e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \ debugfs.o mmio.o mt7915e-$(CONFIG_NL80211_TESTMODE) += testmode.o +mt7915e-$(CONFIG_MT7986_WMAC) += soc.o \ No newline at end of file diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index e96d1c31dd36..4e1ecaec8f4f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -1,9 +1,13 @@ // SPDX-License-Identifier: ISC /* Copyright (C) 2020 MediaTek Inc. */ +#include #include "mt7915.h" #include "eeprom.h" #include "mcu.h" +#include "mac.h" + +#define FW_BIN_LOG_MAGIC 0x44e98caf /** global debugfs **/ @@ -75,7 +79,11 @@ mt7915_radar_trigger(void *data, u64 val) { struct mt7915_dev *dev = data; - return mt7915_mcu_rdd_cmd(dev, RDD_RADAR_EMULATE, 1, 0, 0); + if (val > MT_RX_SEL2) + return -EINVAL; + + return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_RADAR_EMULATE, + val, 0, 0); } DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_trigger, NULL, @@ -300,6 +308,53 @@ static int mt7915_muru_stats_show(struct seq_file *file, void *data) } DEFINE_SHOW_ATTRIBUTE(mt7915_muru_stats); +static int +mt7915_rdd_monitor(struct seq_file *s, void *data) +{ + struct mt7915_dev *dev = dev_get_drvdata(s->private); + struct cfg80211_chan_def *chandef = &dev->rdd2_chandef; + const char *bw; + int ret = 0; + + mutex_lock(&dev->mt76.mutex); + + if (!cfg80211_chandef_valid(chandef)) { + ret = -EINVAL; + goto out; + } + + if (!dev->rdd2_phy) { + seq_puts(s, "not running\n"); + goto out; + } + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + bw = "40"; + break; + case NL80211_CHAN_WIDTH_80: + bw = "80"; + break; + case NL80211_CHAN_WIDTH_160: + bw = "160"; + break; + case NL80211_CHAN_WIDTH_80P80: + bw = "80P80"; + break; + default: + bw = "20"; + break; + } + + seq_printf(s, "channel %d (%d MHz) width %s MHz center1: %d MHz\n", + chandef->chan->hw_value, chandef->chan->center_freq, + bw, chandef->center_freq1); +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + static int mt7915_fw_debug_wm_set(void *data, u64 val) { @@ -311,16 +366,31 @@ mt7915_fw_debug_wm_set(void *data, u64 val) DEBUG_SPL, DEBUG_RPT_RX, } debug; + bool tx, rx, en; int ret; dev->fw_debug_wm = val ? MCU_FW_LOG_TO_HOST : 0; - ret = mt7915_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, dev->fw_debug_wm); + if (dev->fw_debug_bin) + val = 16; + else + val = dev->fw_debug_wm; + + tx = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(1)); + rx = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(2)); + en = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(0)); + + ret = mt7915_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, val); if (ret) return ret; for (debug = DEBUG_TXCMD; debug <= DEBUG_RPT_RX; debug++) { - ret = mt7915_mcu_fw_dbg_ctrl(dev, debug, !!dev->fw_debug_wm); + if (debug == DEBUG_RPT_RX) + val = en && rx; + else + val = en && tx; + + ret = mt7915_mcu_fw_dbg_ctrl(dev, debug, val); if (ret) return ret; } @@ -376,6 +446,65 @@ mt7915_fw_debug_wa_get(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wa, mt7915_fw_debug_wa_get, mt7915_fw_debug_wa_set, "%lld\n"); +static struct dentry * +create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode, + struct rchan_buf *buf, int *is_global) +{ + struct dentry *f; + + f = debugfs_create_file("fwlog_data", mode, parent, buf, + &relay_file_operations); + if (IS_ERR(f)) + return NULL; + + *is_global = 1; + + return f; +} + +static int +remove_buf_file_cb(struct dentry *f) +{ + debugfs_remove(f); + + return 0; +} + +static int +mt7915_fw_debug_bin_set(void *data, u64 val) +{ + static struct rchan_callbacks relay_cb = { + .create_buf_file = create_buf_file_cb, + .remove_buf_file = remove_buf_file_cb, + }; + struct mt7915_dev *dev = data; + + if (!dev->relay_fwlog) + dev->relay_fwlog = relay_open("fwlog_data", dev->debugfs_dir, + 1500, 512, &relay_cb, NULL); + if (!dev->relay_fwlog) + return -ENOMEM; + + dev->fw_debug_bin = val; + + relay_reset(dev->relay_fwlog); + + return mt7915_fw_debug_wm_set(dev, dev->fw_debug_wm); +} + +static int +mt7915_fw_debug_bin_get(void *data, u64 *val) +{ + struct mt7915_dev *dev = data; + + *val = dev->fw_debug_bin; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_bin, mt7915_fw_debug_bin_get, + mt7915_fw_debug_bin_set, "%lld\n"); + static int mt7915_fw_util_wm_show(struct seq_file *file, void *data) { @@ -419,12 +548,12 @@ mt7915_ampdu_stat_read_phy(struct mt7915_phy *phy, /* Tx ampdu stat */ for (i = 0; i < ARRAY_SIZE(range); i++) - range[i] = mt76_rr(dev, MT_MIB_ARNG(ext_phy, i)); + range[i] = mt76_rr(dev, MT_MIB_ARNG(phy->band_idx, i)); for (i = 0; i < ARRAY_SIZE(bound); i++) bound[i] = MT_MIB_ARNCR_RANGE(range[i / 4], i % 4) + 1; - seq_printf(file, "\nPhy %d\n", ext_phy); + seq_printf(file, "\nPhy %d, Phy band %d\n", ext_phy, phy->band_idx); seq_printf(file, "Length: %8d | ", bound[0]); for (i = 0; i < ARRAY_SIZE(bound) - 1; i++) @@ -432,7 +561,7 @@ mt7915_ampdu_stat_read_phy(struct mt7915_phy *phy, bound[i] + 1, bound[i + 1]); seq_puts(file, "\nCount: "); - n = ext_phy ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; + n = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; for (i = 0; i < ARRAY_SIZE(bound); i++) seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i + n]); seq_puts(file, "\n"); @@ -521,14 +650,14 @@ mt7915_tx_stats_show(struct seq_file *file, void *data) DEFINE_SHOW_ATTRIBUTE(mt7915_tx_stats); static void -mt7915_hw_queue_read(struct seq_file *s, u32 base, u32 size, +mt7915_hw_queue_read(struct seq_file *s, u32 size, const struct hw_queue_map *map) { struct mt7915_phy *phy = s->private; struct mt7915_dev *dev = phy->dev; u32 i, val; - val = mt76_rr(dev, base + MT_FL_Q_EMPTY); + val = mt76_rr(dev, MT_FL_Q_EMPTY); for (i = 0; i < size; i++) { u32 ctrl, head, tail, queued; @@ -536,13 +665,13 @@ mt7915_hw_queue_read(struct seq_file *s, u32 base, u32 size, continue; ctrl = BIT(31) | (map[i].pid << 10) | (map[i].qid << 24); - mt76_wr(dev, base + MT_FL_Q0_CTRL, ctrl); + mt76_wr(dev, MT_FL_Q0_CTRL, ctrl); - head = mt76_get_field(dev, base + MT_FL_Q2_CTRL, + head = mt76_get_field(dev, MT_FL_Q2_CTRL, GENMASK(11, 0)); - tail = mt76_get_field(dev, base + MT_FL_Q2_CTRL, + tail = mt76_get_field(dev, MT_FL_Q2_CTRL, GENMASK(27, 16)); - queued = mt76_get_field(dev, base + MT_FL_Q3_CTRL, + queued = mt76_get_field(dev, MT_FL_Q3_CTRL, GENMASK(11, 0)); seq_printf(s, "\t%s: ", map[i].name); @@ -570,8 +699,8 @@ mt7915_sta_hw_queue_read(void *data, struct ieee80211_sta *sta) if (val & BIT(offs)) continue; - mt76_wr(dev, MT_PLE_BASE + MT_FL_Q0_CTRL, ctrl | msta->wcid.idx); - qlen = mt76_get_field(dev, MT_PLE_BASE + MT_FL_Q3_CTRL, + mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | msta->wcid.idx); + qlen = mt76_get_field(dev, MT_FL_Q3_CTRL, GENMASK(11, 0)); seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n", sta->addr, msta->wcid.idx, @@ -633,7 +762,7 @@ mt7915_hw_queues_show(struct seq_file *file, void *data) val, head, tail); seq_puts(file, "PLE non-empty queue info:\n"); - mt7915_hw_queue_read(file, MT_PLE_BASE, ARRAY_SIZE(ple_queue_map), + mt7915_hw_queue_read(file, ARRAY_SIZE(ple_queue_map), &ple_queue_map[0]); /* iterate per-sta ple queue */ @@ -641,7 +770,7 @@ mt7915_hw_queues_show(struct seq_file *file, void *data) mt7915_sta_hw_queue_read, file); /* pse queue */ seq_puts(file, "PSE non-empty queue info:\n"); - mt7915_hw_queue_read(file, MT_PSE_BASE, ARRAY_SIZE(pse_queue_map), + mt7915_hw_queue_read(file, ARRAY_SIZE(pse_queue_map), &pse_queue_map[0]); return 0; @@ -757,6 +886,7 @@ int mt7915_init_debugfs(struct mt7915_phy *phy) debugfs_create_file("tx_stats", 0400, dir, phy, &mt7915_tx_stats_fops); debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm); debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa); + debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin); debugfs_create_file("fw_util_wm", 0400, dir, dev, &mt7915_fw_util_wm_fops); debugfs_create_file("fw_util_wa", 0400, dir, dev, @@ -768,16 +898,77 @@ int mt7915_init_debugfs(struct mt7915_phy *phy) debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir, mt7915_twt_stats); debugfs_create_file("ser_trigger", 0200, dir, dev, &fops_ser_trigger); - if (!dev->dbdc_support || ext_phy) { + if (!dev->dbdc_support || phy->band_idx) { debugfs_create_u32("dfs_hw_pattern", 0400, dir, &dev->hw_pattern); debugfs_create_file("radar_trigger", 0200, dir, dev, &fops_radar_trigger); + debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir, + mt7915_rdd_monitor); } + if (!ext_phy) + dev->debugfs_dir = dir; + return 0; } +static void +mt7915_debugfs_write_fwlog(struct mt7915_dev *dev, const void *hdr, int hdrlen, + const void *data, int len) +{ + static DEFINE_SPINLOCK(lock); + unsigned long flags; + void *dest; + + spin_lock_irqsave(&lock, flags); + dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4); + if (dest) { + *(u32 *)dest = hdrlen + len; + dest += 4; + + if (hdrlen) { + memcpy(dest, hdr, hdrlen); + dest += hdrlen; + } + + memcpy(dest, data, len); + relay_flush(dev->relay_fwlog); + } + spin_unlock_irqrestore(&lock, flags); +} + +void mt7915_debugfs_rx_fw_monitor(struct mt7915_dev *dev, const void *data, int len) +{ + struct { + __le32 magic; + __le32 timestamp; + __le16 msg_type; + __le16 len; + } hdr = { + .magic = cpu_to_le32(FW_BIN_LOG_MAGIC), + .msg_type = cpu_to_le16(PKT_TYPE_RX_FW_MONITOR), + }; + + if (!dev->relay_fwlog) + return; + + hdr.timestamp = cpu_to_le32(mt76_rr(dev, MT_LPON_FRCR(0))); + hdr.len = *(__le16 *)data; + mt7915_debugfs_write_fwlog(dev, &hdr, sizeof(hdr), data, len); +} + +bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len) +{ + if (get_unaligned_le32(data) != FW_BIN_LOG_MAGIC) + return false; + + if (dev->relay_fwlog) + mt7915_debugfs_write_fwlog(dev, NULL, 0, data, len); + + return true; +} + #ifdef CONFIG_MAC80211_DEBUGFS /** per-station debugfs **/ diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index 9182568f95c7..49b4d8ade16b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -5,11 +5,11 @@ #include "../dma.h" #include "mac.h" -int mt7915_init_tx_queues(struct mt7915_phy *phy, int idx, int n_desc) +int mt7915_init_tx_queues(struct mt7915_phy *phy, int idx, int n_desc, int ring_base) { int i, err; - err = mt76_init_tx_queue(phy->mt76, 0, idx, n_desc, MT_TX_RING_BASE); + err = mt76_init_tx_queue(phy->mt76, 0, idx, n_desc, ring_base); if (err < 0) return err; @@ -40,140 +40,392 @@ static int mt7915_poll_tx(struct napi_struct *napi, int budget) return 0; } +static void mt7915_dma_config(struct mt7915_dev *dev) +{ +#define Q_CONFIG(q, wfdma, int, id) do { \ + if (wfdma) \ + dev->wfdma_mask |= (1 << (q)); \ + dev->q_int_mask[(q)] = int; \ + dev->q_id[(q)] = id; \ + } while (0) + +#define MCUQ_CONFIG(q, wfdma, int, id) Q_CONFIG(q, (wfdma), (int), (id)) +#define RXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__RXQ(q), (wfdma), (int), (id)) +#define TXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__TXQ(q), (wfdma), (int), (id)) + + if (is_mt7915(&dev->mt76)) { + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, MT7915_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU, WFDMA1, MT_INT_RX_DONE_WM, MT7915_RXQ_MCU_WM); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA1, MT_INT_RX_DONE_WA, MT7915_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_EXT, WFDMA0, MT_INT_RX_DONE_BAND1, MT7915_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_EXT_WA, WFDMA1, MT_INT_RX_DONE_WA_EXT, MT7915_RXQ_MCU_WA_EXT); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA1, MT_INT_RX_DONE_WA_MAIN, MT7915_RXQ_MCU_WA); + TXQ_CONFIG(0, WFDMA1, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA1, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1); + MCUQ_CONFIG(MT_MCUQ_WM, WFDMA1, MT_INT_TX_DONE_MCU_WM, MT7915_TXQ_MCU_WM); + MCUQ_CONFIG(MT_MCUQ_WA, WFDMA1, MT_INT_TX_DONE_MCU_WA, MT7915_TXQ_MCU_WA); + MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA1, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL); + } else { + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916, MT7916_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7916_RXQ_MCU_WM); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7916_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_EXT, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916, MT7916_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_EXT_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT_MT7916, MT7916_RXQ_MCU_WA_EXT); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN_MT7916, MT7916_RXQ_MCU_WA_MAIN); + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1); + MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7915_TXQ_MCU_WM); + MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA_MT7916, MT7915_TXQ_MCU_WA); + MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL); + } +} + static void __mt7915_dma_prefetch(struct mt7915_dev *dev, u32 ofs) { -#define PREFETCH(base, depth) ((base) << 16 | (depth)) +#define PREFETCH(_base, _depth) ((_base) << 16 | (_depth)) + u32 base = 0; - mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL + ofs, PREFETCH(0x0, 0x4)); - mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL + ofs, PREFETCH(0x40, 0x4)); - mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL + ofs, PREFETCH(0x80, 0x0)); + /* prefetch SRAM wrapping boundary for tx/rx ring. */ + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_FWDL) + ofs, PREFETCH(0x0, 0x4)); + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WM) + ofs, PREFETCH(0x40, 0x4)); + mt76_wr(dev, MT_TXQ_EXT_CTRL(0) + ofs, PREFETCH(0x80, 0x4)); + mt76_wr(dev, MT_TXQ_EXT_CTRL(1) + ofs, PREFETCH(0xc0, 0x4)); + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WA) + ofs, PREFETCH(0x100, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING0_EXT_CTRL + ofs, PREFETCH(0x80, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING1_EXT_CTRL + ofs, PREFETCH(0xc0, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING2_EXT_CTRL + ofs, PREFETCH(0x100, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING3_EXT_CTRL + ofs, PREFETCH(0x140, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING4_EXT_CTRL + ofs, PREFETCH(0x180, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING5_EXT_CTRL + ofs, PREFETCH(0x1c0, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING6_EXT_CTRL + ofs, PREFETCH(0x200, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING7_EXT_CTRL + ofs, PREFETCH(0x240, 0x4)); + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MCU) + ofs, PREFETCH(0x140, 0x4)); + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MCU_WA) + ofs, PREFETCH(0x180, 0x4)); + if (!is_mt7915(&dev->mt76)) { + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MAIN_WA) + ofs, PREFETCH(0x1c0, 0x4)); + base = 0x40; + } + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_EXT_WA) + ofs, PREFETCH(0x1c0 + base, 0x4)); + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MAIN) + ofs, PREFETCH(0x200 + base, 0x4)); + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_EXT) + ofs, PREFETCH(0x240 + base, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING16_EXT_CTRL + ofs, PREFETCH(0x280, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING17_EXT_CTRL + ofs, PREFETCH(0x2c0, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING18_EXT_CTRL + ofs, PREFETCH(0x300, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING19_EXT_CTRL + ofs, PREFETCH(0x340, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING20_EXT_CTRL + ofs, PREFETCH(0x380, 0x4)); - mt76_wr(dev, MT_WFDMA1_TX_RING21_EXT_CTRL + ofs, PREFETCH(0x3c0, 0x0)); - - mt76_wr(dev, MT_WFDMA1_RX_RING0_EXT_CTRL + ofs, PREFETCH(0x3c0, 0x4)); - mt76_wr(dev, MT_WFDMA1_RX_RING1_EXT_CTRL + ofs, PREFETCH(0x400, 0x4)); - mt76_wr(dev, MT_WFDMA1_RX_RING2_EXT_CTRL + ofs, PREFETCH(0x440, 0x4)); - mt76_wr(dev, MT_WFDMA1_RX_RING3_EXT_CTRL + ofs, PREFETCH(0x480, 0x0)); + /* for mt7915, the ring which is next the last + * used ring must be initialized. + */ + if (is_mt7915(&dev->mt76)) { + ofs += 0x4; + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WA) + ofs, PREFETCH(0x140, 0x0)); + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_EXT_WA) + ofs, PREFETCH(0x200 + base, 0x0)); + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_EXT) + ofs, PREFETCH(0x280 + base, 0x0)); + } } void mt7915_dma_prefetch(struct mt7915_dev *dev) { __mt7915_dma_prefetch(dev, 0); if (dev->hif2) - __mt7915_dma_prefetch(dev, MT_WFDMA1_PCIE1_BASE - MT_WFDMA1_BASE); + __mt7915_dma_prefetch(dev, MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0)); } -int mt7915_dma_init(struct mt7915_dev *dev) +static void mt7915_dma_disable(struct mt7915_dev *dev, bool rst) { + struct mt76_dev *mdev = &dev->mt76; u32 hif1_ofs = 0; - int ret; - - mt76_dma_attach(&dev->mt76); if (dev->hif2) - hif1_ofs = MT_WFDMA1_PCIE1_BASE - MT_WFDMA1_BASE; + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); - /* configure global setting */ - mt76_set(dev, MT_WFDMA1_GLO_CFG, - MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | - MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); + /* reset */ + if (rst) { + mt76_clear(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + if (is_mt7915(mdev)) { + mt76_clear(dev, MT_WFDMA1_RST, + MT_WFDMA1_RST_DMASHDL_ALL_RST | + MT_WFDMA1_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA1_RST, + MT_WFDMA1_RST_DMASHDL_ALL_RST | + MT_WFDMA1_RST_LOGIC_RST); + } + + if (dev->hif2) { + mt76_clear(dev, MT_WFDMA0_RST + hif1_ofs, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA0_RST + hif1_ofs, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + if (is_mt7915(mdev)) { + mt76_clear(dev, MT_WFDMA1_RST + hif1_ofs, + MT_WFDMA1_RST_DMASHDL_ALL_RST | + MT_WFDMA1_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA1_RST + hif1_ofs, + MT_WFDMA1_RST_DMASHDL_ALL_RST | + MT_WFDMA1_RST_LOGIC_RST); + } + } + } + + /* disable */ + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (is_mt7915(mdev)) + mt76_clear(dev, MT_WFDMA1_GLO_CFG, + MT_WFDMA1_GLO_CFG_TX_DMA_EN | + MT_WFDMA1_GLO_CFG_RX_DMA_EN | + MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (dev->hif2) { + mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (is_mt7915(mdev)) + mt76_clear(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, + MT_WFDMA1_GLO_CFG_TX_DMA_EN | + MT_WFDMA1_GLO_CFG_RX_DMA_EN | + MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO_PFET2); + } +} + +static int mt7915_dma_enable(struct mt7915_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + u32 hif1_ofs = 0; + u32 irq_mask; + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); /* reset dma idx */ mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0); - mt76_wr(dev, MT_WFDMA1_RST_DTX_PTR, ~0); + if (is_mt7915(mdev)) + mt76_wr(dev, MT_WFDMA1_RST_DTX_PTR, ~0); + if (dev->hif2) { + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR + hif1_ofs, ~0); + if (is_mt7915(mdev)) + mt76_wr(dev, MT_WFDMA1_RST_DTX_PTR + hif1_ofs, ~0); + } - /* configure delay interrupt */ + /* configure delay interrupt off */ mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0, 0); - mt76_wr(dev, MT_WFDMA1_PRI_DLY_INT_CFG0, 0); + if (is_mt7915(mdev)) { + mt76_wr(dev, MT_WFDMA1_PRI_DLY_INT_CFG0, 0); + } else { + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG1, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG2, 0); + } if (dev->hif2) { - mt76_set(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, - MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | - MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); - - mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR + hif1_ofs, ~0); - mt76_wr(dev, MT_WFDMA1_RST_DTX_PTR + hif1_ofs, ~0); - mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0 + hif1_ofs, 0); - mt76_wr(dev, MT_WFDMA1_PRI_DLY_INT_CFG0 + hif1_ofs, 0); + if (is_mt7915(mdev)) { + mt76_wr(dev, MT_WFDMA1_PRI_DLY_INT_CFG0 + + hif1_ofs, 0); + } else { + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG1 + + hif1_ofs, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG2 + + hif1_ofs, 0); + } } /* configure perfetch settings */ mt7915_dma_prefetch(dev); + /* hif wait WFDMA idle */ + mt76_set(dev, MT_WFDMA0_BUSY_ENA, + MT_WFDMA0_BUSY_ENA_TX_FIFO0 | + MT_WFDMA0_BUSY_ENA_TX_FIFO1 | + MT_WFDMA0_BUSY_ENA_RX_FIFO); + + if (is_mt7915(mdev)) + mt76_set(dev, MT_WFDMA1_BUSY_ENA, + MT_WFDMA1_BUSY_ENA_TX_FIFO0 | + MT_WFDMA1_BUSY_ENA_TX_FIFO1 | + MT_WFDMA1_BUSY_ENA_RX_FIFO); + + if (dev->hif2) { + mt76_set(dev, MT_WFDMA0_BUSY_ENA + hif1_ofs, + MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 | + MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 | + MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO); + + if (is_mt7915(mdev)) + mt76_set(dev, MT_WFDMA1_BUSY_ENA + hif1_ofs, + MT_WFDMA1_PCIE1_BUSY_ENA_TX_FIFO0 | + MT_WFDMA1_PCIE1_BUSY_ENA_TX_FIFO1 | + MT_WFDMA1_PCIE1_BUSY_ENA_RX_FIFO); + } + + mt76_poll(dev, MT_WFDMA_EXT_CSR_HIF_MISC, + MT_WFDMA_EXT_CSR_HIF_MISC_BUSY, 0, 1000); + + /* set WFDMA Tx/Rx */ + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (is_mt7915(mdev)) + mt76_set(dev, MT_WFDMA1_GLO_CFG, + MT_WFDMA1_GLO_CFG_TX_DMA_EN | + MT_WFDMA1_GLO_CFG_RX_DMA_EN | + MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); + + if (dev->hif2) { + mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (is_mt7915(mdev)) + mt76_set(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, + MT_WFDMA1_GLO_CFG_TX_DMA_EN | + MT_WFDMA1_GLO_CFG_RX_DMA_EN | + MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); + + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_PDMA_BAND); + } + + /* enable interrupts for TX/RX rings */ + irq_mask = MT_INT_RX_DONE_MCU | + MT_INT_TX_DONE_MCU | + MT_INT_MCU_CMD; + + if (!dev->phy.band_idx) + irq_mask |= MT_INT_BAND0_RX_DONE; + + if (dev->dbdc_support || dev->phy.band_idx) + irq_mask |= MT_INT_BAND1_RX_DONE; + + mt7915_irq_enable(dev, irq_mask); + + return 0; +} + +int mt7915_dma_init(struct mt7915_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + u32 hif1_ofs = 0; + int ret; + + mt7915_dma_config(dev); + + mt76_dma_attach(&dev->mt76); + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + mt7915_dma_disable(dev, true); + /* init tx queue */ - ret = mt7915_init_tx_queues(&dev->phy, MT7915_TXQ_BAND0, - MT7915_TX_RING_SIZE); + ret = mt7915_init_tx_queues(&dev->phy, + MT_TXQ_ID(dev->phy.band_idx), + MT7915_TX_RING_SIZE, + MT_TXQ_RING_BASE(0)); if (ret) return ret; /* command to WM */ - ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, MT7915_TXQ_MCU_WM, - MT7915_TX_MCU_RING_SIZE, MT_TX_RING_BASE); + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, + MT_MCUQ_ID(MT_MCUQ_WM), + MT7915_TX_MCU_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_WM)); if (ret) return ret; /* command to WA */ - ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WA, MT7915_TXQ_MCU_WA, - MT7915_TX_MCU_RING_SIZE, MT_TX_RING_BASE); + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WA, + MT_MCUQ_ID(MT_MCUQ_WA), + MT7915_TX_MCU_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_WA)); if (ret) return ret; /* firmware download */ - ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, MT7915_TXQ_FWDL, - MT7915_TX_FWDL_RING_SIZE, MT_TX_RING_BASE); + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, + MT_MCUQ_ID(MT_MCUQ_FWDL), + MT7915_TX_FWDL_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_FWDL)); if (ret) return ret; /* event from WM */ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], - MT7915_RXQ_MCU_WM, MT7915_RX_MCU_RING_SIZE, - MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE); + MT_RXQ_ID(MT_RXQ_MCU), + MT7915_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MCU)); if (ret) return ret; /* event from WA */ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], - MT7915_RXQ_MCU_WA, MT7915_RX_MCU_RING_SIZE, - MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE); + MT_RXQ_ID(MT_RXQ_MCU_WA), + MT7915_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MCU_WA)); if (ret) return ret; - /* rx data queue */ - ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], - MT7915_RXQ_BAND0, MT7915_RX_RING_SIZE, - MT_RX_BUF_SIZE, MT_RX_DATA_RING_BASE); - if (ret) - return ret; - - if (dev->dbdc_support) { - ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_EXT], - MT7915_RXQ_BAND1, MT7915_RX_RING_SIZE, + /* rx data queue for band0 */ + if (!dev->phy.band_idx) { + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], + MT_RXQ_ID(MT_RXQ_MAIN), + MT7915_RX_RING_SIZE, MT_RX_BUF_SIZE, - MT_RX_DATA_RING_BASE + hif1_ofs); + MT_RXQ_RING_BASE(MT_RXQ_MAIN)); + if (ret) + return ret; + } + + /* tx free notify event from WA for band0 */ + if (!is_mt7915(mdev)) { + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA], + MT_RXQ_ID(MT_RXQ_MAIN_WA), + MT7915_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA)); + if (ret) + return ret; + } + + if (dev->dbdc_support || dev->phy.band_idx) { + /* rx data queue for band1 */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_EXT], + MT_RXQ_ID(MT_RXQ_EXT), + MT7915_RX_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_EXT) + hif1_ofs); if (ret) return ret; - /* event from WA */ + /* tx free notify event from WA for band1 */ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_EXT_WA], - MT7915_RXQ_MCU_WA_EXT, + MT_RXQ_ID(MT_RXQ_EXT_WA), MT7915_RX_MCU_RING_SIZE, MT_RX_BUF_SIZE, - MT_RX_EVENT_RING_BASE + hif1_ofs); + MT_RXQ_RING_BASE(MT_RXQ_EXT_WA) + hif1_ofs); if (ret) return ret; } @@ -186,80 +438,14 @@ int mt7915_dma_init(struct mt7915_dev *dev) mt7915_poll_tx, NAPI_POLL_WEIGHT); napi_enable(&dev->mt76.tx_napi); - /* hif wait WFDMA idle */ - mt76_set(dev, MT_WFDMA0_BUSY_ENA, - MT_WFDMA0_BUSY_ENA_TX_FIFO0 | - MT_WFDMA0_BUSY_ENA_TX_FIFO1 | - MT_WFDMA0_BUSY_ENA_RX_FIFO); - - mt76_set(dev, MT_WFDMA1_BUSY_ENA, - MT_WFDMA1_BUSY_ENA_TX_FIFO0 | - MT_WFDMA1_BUSY_ENA_TX_FIFO1 | - MT_WFDMA1_BUSY_ENA_RX_FIFO); - - mt76_set(dev, MT_WFDMA0_PCIE1_BUSY_ENA, - MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 | - MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 | - MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO); - - mt76_set(dev, MT_WFDMA1_PCIE1_BUSY_ENA, - MT_WFDMA1_PCIE1_BUSY_ENA_TX_FIFO0 | - MT_WFDMA1_PCIE1_BUSY_ENA_TX_FIFO1 | - MT_WFDMA1_PCIE1_BUSY_ENA_RX_FIFO); - - mt76_poll(dev, MT_WFDMA_EXT_CSR_HIF_MISC, - MT_WFDMA_EXT_CSR_HIF_MISC_BUSY, 0, 1000); - - /* set WFDMA Tx/Rx */ - mt76_set(dev, MT_WFDMA0_GLO_CFG, - MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); - mt76_set(dev, MT_WFDMA1_GLO_CFG, - MT_WFDMA1_GLO_CFG_TX_DMA_EN | MT_WFDMA1_GLO_CFG_RX_DMA_EN); - - if (dev->hif2) { - mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, - (MT_WFDMA0_GLO_CFG_TX_DMA_EN | - MT_WFDMA0_GLO_CFG_RX_DMA_EN)); - mt76_set(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, - (MT_WFDMA1_GLO_CFG_TX_DMA_EN | - MT_WFDMA1_GLO_CFG_RX_DMA_EN)); - mt76_set(dev, MT_WFDMA_HOST_CONFIG, - MT_WFDMA_HOST_CONFIG_PDMA_BAND); - } - - /* enable interrupts for TX/RX rings */ - mt7915_irq_enable(dev, MT_INT_RX_DONE_ALL | MT_INT_TX_DONE_MCU | - MT_INT_MCU_CMD); + mt7915_dma_enable(dev); return 0; } void mt7915_dma_cleanup(struct mt7915_dev *dev) { - /* disable */ - mt76_clear(dev, MT_WFDMA0_GLO_CFG, - MT_WFDMA0_GLO_CFG_TX_DMA_EN | - MT_WFDMA0_GLO_CFG_RX_DMA_EN); - mt76_clear(dev, MT_WFDMA1_GLO_CFG, - MT_WFDMA1_GLO_CFG_TX_DMA_EN | - MT_WFDMA1_GLO_CFG_RX_DMA_EN); - - /* reset */ - mt76_clear(dev, MT_WFDMA1_RST, - MT_WFDMA1_RST_DMASHDL_ALL_RST | - MT_WFDMA1_RST_LOGIC_RST); - - mt76_set(dev, MT_WFDMA1_RST, - MT_WFDMA1_RST_DMASHDL_ALL_RST | - MT_WFDMA1_RST_LOGIC_RST); - - mt76_clear(dev, MT_WFDMA0_RST, - MT_WFDMA0_RST_DMASHDL_ALL_RST | - MT_WFDMA0_RST_LOGIC_RST); - - mt76_set(dev, MT_WFDMA0_RST, - MT_WFDMA0_RST_DMASHDL_ALL_RST | - MT_WFDMA0_RST_LOGIC_RST); + mt7915_dma_disable(dev, true); mt76_dma_cleanup(&dev->mt76); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c index edd74d0de157..5b133bcdab17 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c @@ -10,6 +10,7 @@ static int mt7915_eeprom_load_precal(struct mt7915_dev *dev) struct mt76_dev *mdev = &dev->mt76; u8 *eeprom = mdev->eeprom.data; u32 val = eeprom[MT_EE_DO_PRE_CAL]; + u32 offs; if (!dev->flash_mode) return 0; @@ -22,7 +23,9 @@ static int mt7915_eeprom_load_precal(struct mt7915_dev *dev) if (!dev->cal) return -ENOMEM; - return mt76_get_of_eeprom(mdev, dev->cal, MT_EE_PRECAL, val); + offs = is_mt7915(&dev->mt76) ? MT_EE_PRECAL : MT_EE_PRECAL_V2; + + return mt76_get_of_eeprom(mdev, dev->cal, offs, val); } static int mt7915_check_eeprom(struct mt7915_dev *dev) @@ -32,24 +35,49 @@ static int mt7915_check_eeprom(struct mt7915_dev *dev) switch (val) { case 0x7915: + case 0x7916: + case 0x7986: return 0; default: return -EINVAL; } } +static char *mt7915_eeprom_name(struct mt7915_dev *dev) +{ + switch (mt76_chip(&dev->mt76)) { + case 0x7915: + return dev->dbdc_support ? + MT7915_EEPROM_DEFAULT_DBDC : MT7915_EEPROM_DEFAULT; + case 0x7986: + switch (mt7915_check_adie(dev, true)) { + case MT7976_ONE_ADIE_DBDC: + return MT7986_EEPROM_MT7976_DEFAULT_DBDC; + case MT7975_ONE_ADIE: + return MT7986_EEPROM_MT7975_DEFAULT; + case MT7976_ONE_ADIE: + return MT7986_EEPROM_MT7976_DEFAULT; + case MT7975_DUAL_ADIE: + return MT7986_EEPROM_MT7975_DUAL_DEFAULT; + case MT7976_DUAL_ADIE: + return MT7986_EEPROM_MT7976_DUAL_DEFAULT; + default: + break; + } + return NULL; + default: + return MT7916_EEPROM_DEFAULT; + } +} + static int mt7915_eeprom_load_default(struct mt7915_dev *dev) { - char *default_bin = MT7915_EEPROM_DEFAULT; u8 *eeprom = dev->mt76.eeprom.data; const struct firmware *fw = NULL; int ret; - if (dev->dbdc_support) - default_bin = MT7915_EEPROM_DEFAULT_DBDC; - - ret = request_firmware(&fw, default_bin, dev->mt76.dev); + ret = request_firmware(&fw, mt7915_eeprom_name(dev), dev->mt76.dev); if (ret) return ret; @@ -59,7 +87,7 @@ mt7915_eeprom_load_default(struct mt7915_dev *dev) goto out; } - memcpy(eeprom, fw->data, MT7915_EEPROM_SIZE); + memcpy(eeprom, fw->data, mt7915_eeprom_size(dev)); dev->flash_mode = true; out: @@ -71,8 +99,9 @@ mt7915_eeprom_load_default(struct mt7915_dev *dev) static int mt7915_eeprom_load(struct mt7915_dev *dev) { int ret; + u16 eeprom_size = mt7915_eeprom_size(dev); - ret = mt76_eeprom_init(&dev->mt76, MT7915_EEPROM_SIZE); + ret = mt76_eeprom_init(&dev->mt76, eeprom_size); if (ret < 0) return ret; @@ -88,7 +117,7 @@ static int mt7915_eeprom_load(struct mt7915_dev *dev) return -EINVAL; /* read eeprom data from efuse */ - block_num = DIV_ROUND_UP(MT7915_EEPROM_SIZE, + block_num = DIV_ROUND_UP(eeprom_size, MT7915_EEPROM_BLOCK_SIZE); for (i = 0; i < block_num; i++) mt7915_mcu_get_eeprom(dev, @@ -98,17 +127,32 @@ static int mt7915_eeprom_load(struct mt7915_dev *dev) return mt7915_check_eeprom(dev); } -void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy) +static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; - bool ext_phy = phy != &dev->phy; u8 *eeprom = dev->mt76.eeprom.data; u32 val; - val = eeprom[MT_EE_WIFI_CONF + ext_phy]; + val = eeprom[MT_EE_WIFI_CONF + phy->band_idx]; val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val); - if (val == MT_EE_BAND_SEL_DEFAULT && dev->dbdc_support) - val = ext_phy ? MT_EE_BAND_SEL_5GHZ : MT_EE_BAND_SEL_2GHZ; + + if (!is_mt7915(&dev->mt76)) { + switch (val) { + case MT_EE_V2_BAND_SEL_5GHZ: + phy->mt76->cap.has_5ghz = true; + return; + case MT_EE_V2_BAND_SEL_6GHZ: + phy->mt76->cap.has_6ghz = true; + return; + case MT_EE_V2_BAND_SEL_5GHZ_6GHZ: + phy->mt76->cap.has_5ghz = true; + phy->mt76->cap.has_6ghz = true; + return; + default: + phy->mt76->cap.has_2ghz = true; + return; + } + } switch (val) { case MT_EE_BAND_SEL_5GHZ: @@ -124,32 +168,65 @@ void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy) } } -static void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev) +void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, + struct mt7915_phy *phy) { - u8 nss, nss_band, *eeprom = dev->mt76.eeprom.data; + u8 nss, nss_band, nss_band_max, *eeprom = dev->mt76.eeprom.data; + struct mt76_phy *mphy = phy->mt76; + bool ext_phy = phy != &dev->phy; - mt7915_eeprom_parse_band_config(&dev->phy); + mt7915_eeprom_parse_band_config(phy); + + /* read tx/rx mask from eeprom */ + if (is_mt7915(&dev->mt76)) { + nss = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, + eeprom[MT_EE_WIFI_CONF]); + } else { + nss = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, + eeprom[MT_EE_WIFI_CONF + phy->band_idx]); + } - /* read tx mask from eeprom */ - nss = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, eeprom[MT_EE_WIFI_CONF]); if (!nss || nss > 4) nss = 4; + /* read tx/rx stream */ nss_band = nss; if (dev->dbdc_support) { - nss_band = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0, - eeprom[MT_EE_WIFI_CONF + 3]); - if (!nss_band || nss_band > 2) - nss_band = 2; + if (is_mt7915(&dev->mt76)) { + nss_band = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0, + eeprom[MT_EE_WIFI_CONF + 3]); + if (phy->band_idx) + nss_band = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B1, + eeprom[MT_EE_WIFI_CONF + 3]); + } else { + nss_band = FIELD_GET(MT_EE_WIFI_CONF_STREAM_NUM, + eeprom[MT_EE_WIFI_CONF + 2 + phy->band_idx]); + } - if (nss_band >= nss) - nss = 4; + nss_band_max = is_mt7986(&dev->mt76) ? + MT_EE_NSS_MAX_DBDC_MA7986 : MT_EE_NSS_MAX_DBDC_MA7915; + } else { + nss_band_max = is_mt7986(&dev->mt76) ? + MT_EE_NSS_MAX_MA7986 : MT_EE_NSS_MAX_MA7915; } - dev->chainmask = BIT(nss) - 1; - dev->mphy.antenna_mask = BIT(nss_band) - 1; - dev->mphy.chainmask = dev->mphy.antenna_mask; + if (!nss_band || nss_band > nss_band_max) + nss_band = nss_band_max; + + if (nss_band > nss) { + dev_warn(dev->mt76.dev, + "nss mismatch, nss(%d) nss_band(%d) band(%d) ext_phy(%d)\n", + nss, nss_band, phy->band_idx, ext_phy); + nss = nss_band; + } + + mphy->chainmask = BIT(nss) - 1; + if (ext_phy) + mphy->chainmask <<= dev->chainshift; + mphy->antenna_mask = BIT(nss_band) - 1; + dev->chainmask |= mphy->chainmask; + dev->chainshift = hweight8(dev->mphy.chainmask); } int mt7915_eeprom_init(struct mt7915_dev *dev) @@ -171,7 +248,7 @@ int mt7915_eeprom_init(struct mt7915_dev *dev) if (ret) return ret; - mt7915_eeprom_parse_hw_cap(dev); + mt7915_eeprom_parse_hw_cap(dev, &dev->phy); memcpy(dev->mphy.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, ETH_ALEN); @@ -186,27 +263,43 @@ int mt7915_eeprom_get_target_power(struct mt7915_dev *dev, { u8 *eeprom = dev->mt76.eeprom.data; int index, target_power; - bool tssi_on; + bool tssi_on, is_7976; if (chain_idx > 3) return -EINVAL; tssi_on = mt7915_tssi_enabled(dev, chan->band); + is_7976 = mt7915_check_adie(dev, false) || is_mt7916(&dev->mt76); if (chan->band == NL80211_BAND_2GHZ) { - index = MT_EE_TX0_POWER_2G + chain_idx * 3; - target_power = eeprom[index]; + if (is_7976) { + index = MT_EE_TX0_POWER_2G_V2 + chain_idx; + target_power = eeprom[index]; + } else { + index = MT_EE_TX0_POWER_2G + chain_idx * 3; + target_power = eeprom[index]; - if (!tssi_on) - target_power += eeprom[index + 1]; + if (!tssi_on) + target_power += eeprom[index + 1]; + } + } else if (chan->band == NL80211_BAND_5GHZ) { + int group = mt7915_get_channel_group_5g(chan->hw_value, is_7976); + + if (is_7976) { + index = MT_EE_TX0_POWER_5G_V2 + chain_idx * 5; + target_power = eeprom[index + group]; + } else { + index = MT_EE_TX0_POWER_5G + chain_idx * 12; + target_power = eeprom[index + group]; + + if (!tssi_on) + target_power += eeprom[index + 8]; + } } else { - int group = mt7915_get_channel_group(chan->hw_value); + int group = mt7915_get_channel_group_6g(chan->hw_value); - index = MT_EE_TX0_POWER_5G + chain_idx * 12; - target_power = eeprom[index + group]; - - if (!tssi_on) - target_power += eeprom[index + 8]; + index = MT_EE_TX0_POWER_6G_V2 + chain_idx * 8; + target_power = is_7976 ? eeprom[index + group] : 0; } return target_power; @@ -215,15 +308,20 @@ int mt7915_eeprom_get_target_power(struct mt7915_dev *dev, s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band) { u8 *eeprom = dev->mt76.eeprom.data; - u32 val; + u32 val, offs; s8 delta; + bool is_7976 = mt7915_check_adie(dev, false) || is_mt7916(&dev->mt76); if (band == NL80211_BAND_2GHZ) - val = eeprom[MT_EE_RATE_DELTA_2G]; + offs = is_7976 ? MT_EE_RATE_DELTA_2G_V2 : MT_EE_RATE_DELTA_2G; + else if (band == NL80211_BAND_5GHZ) + offs = is_7976 ? MT_EE_RATE_DELTA_5G_V2 : MT_EE_RATE_DELTA_5G; else - val = eeprom[MT_EE_RATE_DELTA_5G]; + offs = is_7976 ? MT_EE_RATE_DELTA_6G_V2 : 0; - if (!(val & MT_EE_RATE_DELTA_EN)) + val = eeprom[offs]; + + if (!offs || !(val & MT_EE_RATE_DELTA_EN)) return 0; delta = FIELD_GET(MT_EE_RATE_DELTA_MASK, val); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h index a43389a41800..7578ac6d0be6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h @@ -23,11 +23,19 @@ enum mt7915_eeprom_field { MT_EE_RATE_DELTA_5G = 0x29d, MT_EE_TX0_POWER_2G = 0x2fc, MT_EE_TX0_POWER_5G = 0x34b, + MT_EE_RATE_DELTA_2G_V2 = 0x7d3, + MT_EE_RATE_DELTA_5G_V2 = 0x81e, + MT_EE_RATE_DELTA_6G_V2 = 0x884, /* 6g fields only appear in eeprom v2 */ + MT_EE_TX0_POWER_2G_V2 = 0x441, + MT_EE_TX0_POWER_5G_V2 = 0x445, + MT_EE_TX0_POWER_6G_V2 = 0x465, MT_EE_ADIE_FT_VERSION = 0x9a0, __MT_EE_MAX = 0xe00, + __MT_EE_MAX_V2 = 0x1000, /* 0xe10 ~ 0x5780 used to save group cal data */ - MT_EE_PRECAL = 0xe10 + MT_EE_PRECAL = 0xe10, + MT_EE_PRECAL_V2 = 0x1010 }; #define MT_EE_WIFI_CAL_GROUP BIT(0) @@ -39,6 +47,7 @@ enum mt7915_eeprom_field { #define MT_EE_WIFI_CONF0_TX_PATH GENMASK(2, 0) #define MT_EE_WIFI_CONF0_BAND_SEL GENMASK(7, 6) #define MT_EE_WIFI_CONF1_BAND_SEL GENMASK(7, 6) +#define MT_EE_WIFI_CONF_STREAM_NUM GENMASK(7, 5) #define MT_EE_WIFI_CONF3_TX_PATH_B0 GENMASK(1, 0) #define MT_EE_WIFI_CONF3_TX_PATH_B1 GENMASK(5, 4) #define MT_EE_WIFI_CONF7_TSSI0_2G BIT(0) @@ -49,6 +58,19 @@ enum mt7915_eeprom_field { #define MT_EE_RATE_DELTA_SIGN BIT(6) #define MT_EE_RATE_DELTA_EN BIT(7) +#define MT_EE_NSS_MAX_MA7915 4 +#define MT_EE_NSS_MAX_DBDC_MA7915 2 +#define MT_EE_NSS_MAX_MA7986 4 +#define MT_EE_NSS_MAX_DBDC_MA7986 4 + +enum mt7915_adie_sku { + MT7976_ONE_ADIE_DBDC = 0x7, + MT7975_ONE_ADIE = 0x8, + MT7976_ONE_ADIE = 0xa, + MT7975_DUAL_ADIE = 0xd, + MT7976_DUAL_ADIE = 0xf, +}; + enum mt7915_eeprom_band { MT_EE_BAND_SEL_DEFAULT, MT_EE_BAND_SEL_5GHZ, @@ -56,6 +78,13 @@ enum mt7915_eeprom_band { MT_EE_BAND_SEL_DUAL, }; +enum { + MT_EE_V2_BAND_SEL_2GHZ, + MT_EE_V2_BAND_SEL_5GHZ, + MT_EE_V2_BAND_SEL_6GHZ, + MT_EE_V2_BAND_SEL_5GHZ_6GHZ, +}; + enum mt7915_sku_rate_group { SKU_CCK, SKU_OFDM, @@ -76,8 +105,20 @@ enum mt7915_sku_rate_group { }; static inline int -mt7915_get_channel_group(int channel) +mt7915_get_channel_group_5g(int channel, bool is_7976) { + if (is_7976) { + if (channel <= 64) + return 0; + if (channel <= 96) + return 1; + if (channel <= 128) + return 2; + if (channel <= 144) + return 3; + return 4; + } + if (channel >= 184 && channel <= 196) return 0; if (channel <= 48) @@ -95,6 +136,15 @@ mt7915_get_channel_group(int channel) return 7; } +static inline int +mt7915_get_channel_group_6g(int channel) +{ + if (channel <= 29) + return 0; + + return DIV_ROUND_UP(channel - 29, 32); +} + static inline bool mt7915_tssi_enabled(struct mt7915_dev *dev, enum nl80211_band band) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index d054cdecd5f7..6d29366c5139 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -50,15 +50,22 @@ static ssize_t mt7915_thermal_temp_show(struct device *dev, int i = to_sensor_dev_attr(attr)->index; int temperature; - if (i) - return sprintf(buf, "%u\n", phy->throttle_temp[i - 1] * 1000); - - temperature = mt7915_mcu_get_temperature(phy); - if (temperature < 0) - return temperature; - - /* display in millidegree celcius */ - return sprintf(buf, "%u\n", temperature * 1000); + switch (i) { + case 0: + temperature = mt7915_mcu_get_temperature(phy); + if (temperature < 0) + return temperature; + /* display in millidegree celcius */ + return sprintf(buf, "%u\n", temperature * 1000); + case 1: + case 2: + return sprintf(buf, "%u\n", + phy->throttle_temp[i - 1] * 1000); + case 3: + return sprintf(buf, "%hhu\n", phy->throttle_state); + default: + return -EINVAL; + } } static ssize_t mt7915_thermal_temp_store(struct device *dev, @@ -84,11 +91,13 @@ static ssize_t mt7915_thermal_temp_store(struct device *dev, static SENSOR_DEVICE_ATTR_RO(temp1_input, mt7915_thermal_temp, 0); static SENSOR_DEVICE_ATTR_RW(temp1_crit, mt7915_thermal_temp, 1); static SENSOR_DEVICE_ATTR_RW(temp1_max, mt7915_thermal_temp, 2); +static SENSOR_DEVICE_ATTR_RO(throttle1, mt7915_thermal_temp, 3); static struct attribute *mt7915_hwmon_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_crit.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_throttle1.dev_attr.attr, NULL, }; ATTRIBUTE_GROUPS(mt7915_hwmon); @@ -97,7 +106,7 @@ static int mt7915_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev, unsigned long *state) { - *state = MT7915_THERMAL_THROTTLE_MAX; + *state = MT7915_CDEV_THROTTLE_MAX; return 0; } @@ -108,7 +117,7 @@ mt7915_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev, { struct mt7915_phy *phy = cdev->devdata; - *state = phy->throttle_state; + *state = phy->cdev_state; return 0; } @@ -118,22 +127,27 @@ mt7915_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev, unsigned long state) { struct mt7915_phy *phy = cdev->devdata; + u8 throttling = MT7915_THERMAL_THROTTLE_MAX - state; int ret; - if (state > MT7915_THERMAL_THROTTLE_MAX) + if (state > MT7915_CDEV_THROTTLE_MAX) return -EINVAL; if (phy->throttle_temp[0] > phy->throttle_temp[1]) return 0; - if (state == phy->throttle_state) + if (state == phy->cdev_state) return 0; - ret = mt7915_mcu_set_thermal_throttling(phy, state); + /* + * cooling_device convention: 0 = no cooling, more = more cooling + * mcu convention: 1 = max cooling, more = less cooling + */ + ret = mt7915_mcu_set_thermal_throttling(phy, throttling); if (ret) return ret; - phy->throttle_state = state; + phy->cdev_state = state; return 0; } @@ -186,7 +200,8 @@ static int mt7915_thermal_init(struct mt7915_phy *phy) phy->throttle_temp[0] = 110; phy->throttle_temp[1] = 120; - return 0; + return mt7915_mcu_set_thermal_throttling(phy, + MT7915_THERMAL_THROTTLE_MAX); } static void mt7915_led_set_config(struct led_classdev *led_cdev, @@ -288,17 +303,18 @@ mt7915_regd_notifier(struct wiphy *wiphy, struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt76_phy *mphy = hw->priv; struct mt7915_phy *phy = mphy->priv; - struct cfg80211_chan_def *chandef = &mphy->chandef; memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2)); dev->mt76.region = request->dfs_region; + if (dev->mt76.region == NL80211_DFS_UNSET) + mt7915_mcu_rdd_background_enable(phy, NULL); + mt7915_init_txpower(dev, &mphy->sband_2g.sband); mt7915_init_txpower(dev, &mphy->sband_5g.sband); + mt7915_init_txpower(dev, &mphy->sband_6g.sband); - if (!(chandef->chan->flags & IEEE80211_CHAN_RADAR)) - return; - + mphy->dfs_state = MT_DFS_STATE_UNKNOWN; mt7915_dfs_init_radar_detector(phy); } @@ -306,11 +322,13 @@ static void mt7915_init_wiphy(struct ieee80211_hw *hw) { struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt76_dev *mdev = &phy->dev->mt76; struct wiphy *wiphy = hw->wiphy; + struct mt7915_dev *dev = phy->dev; hw->queues = 4; - hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; - hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; hw->netdev_features = NETIF_F_RXCSUM; hw->radiotap_timestamp.units_pos = @@ -325,6 +343,7 @@ mt7915_init_wiphy(struct ieee80211_hw *hw) wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); wiphy->reg_notifier = mt7915_regd_notifier; wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; + wiphy->mbssid_max_interfaces = 16; wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_COLOR); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); @@ -333,9 +352,16 @@ mt7915_init_wiphy(struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_VHT); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE); + if (!mdev->dev->of_node || + !of_property_read_bool(mdev->dev->of_node, + "mediatek,disable-radar-background")) + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_RADAR_BACKGROUND); + ieee80211_hw_set(hw, HAS_RATE_CONTROL); ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD); ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD); + ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); ieee80211_hw_set(hw, WANT_MONITOR_VIF); hw->max_tx_fragments = 4; @@ -349,14 +375,34 @@ mt7915_init_wiphy(struct ieee80211_hw *hw) phy->mt76->sband_5g.sband.ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING | IEEE80211_HT_CAP_MAX_AMSDU; - phy->mt76->sband_5g.sband.vht_cap.cap |= - IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 | - IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; + + if (is_mt7915(&dev->mt76)) { + phy->mt76->sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; + + if (!dev->dbdc_support) + phy->mt76->sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + } else { + phy->mt76->sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; + + /* mt7916 dbdc with 2g 2x2 bw40 and 5g 2x2 bw160c */ + phy->mt76->sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + } } mt76_set_stream_caps(phy->mt76, true); mt7915_set_stream_vht_txbf_caps(phy); mt7915_set_stream_he_caps(phy); + + wiphy->available_antennas_rx = phy->mt76->antenna_mask; + wiphy->available_antennas_tx = phy->mt76->antenna_mask; } static void @@ -387,19 +433,27 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band) mt76_rmw(dev, MT_MDP_BNRCFR1(band), mask, set); mt76_rmw_field(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_MAX_RX_LEN, 0x680); - /* disable rx rate report by default due to hw issues */ + + /* mt7915: disable rx rate report by default due to hw issues */ mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN); } static void mt7915_mac_init(struct mt7915_dev *dev) { int i; + u32 rx_len = is_mt7915(&dev->mt76) ? 0x400 : 0x680; + + /* config pse qid6 wfdma port selection */ + if (!is_mt7915(&dev->mt76) && dev->hif2) + mt76_rmw(dev, MT_WF_PP_TOP_RXQ_WFDMA_CF_5, 0, + MT_WF_PP_TOP_RXQ_QID6_WFDMA_HIF_SEL_MASK); + + mt76_rmw_field(dev, MT_MDP_DCR1, MT_MDP_DCR1_MAX_RX_LEN, rx_len); - mt76_rmw_field(dev, MT_MDP_DCR1, MT_MDP_DCR1_MAX_RX_LEN, 0x400); /* enable hardware de-agg */ mt76_set(dev, MT_MDP_DCR0, MT_MDP_DCR0_DAMSDU_EN); - for (i = 0; i < MT7915_WTBL_SIZE; i++) + for (i = 0; i < mt7915_wtbl_size(dev); i++) mt7915_mac_wtbl_update(dev, i, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); for (i = 0; i < 2; i++) @@ -449,20 +503,32 @@ static int mt7915_register_ext_phy(struct mt7915_dev *dev) phy = mphy->priv; phy->dev = dev; phy->mt76 = mphy; - mphy->chainmask = dev->chainmask & ~dev->mphy.chainmask; - mphy->antenna_mask = BIT(hweight8(mphy->chainmask)) - 1; + + /* Bind main phy to band0 and ext_phy to band1 for dbdc case */ + phy->band_idx = 1; INIT_DELAYED_WORK(&mphy->mac_work, mt7915_mac_work); - mt7915_eeprom_parse_band_config(phy); - mt7915_init_wiphy(mphy->hw); + mt7915_eeprom_parse_hw_cap(dev, phy); memcpy(mphy->macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR2, ETH_ALEN); + /* Make the secondary PHY MAC address local without overlapping with + * the usual MAC address allocation scheme on multiple virtual interfaces + */ + if (!is_valid_ether_addr(mphy->macaddr)) { + memcpy(mphy->macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, + ETH_ALEN); + mphy->macaddr[0] |= 2; + mphy->macaddr[0] ^= BIT(7); + } mt76_eeprom_override(mphy); - ret = mt7915_init_tx_queues(phy, MT7915_TXQ_BAND1, - MT7915_TX_RING_SIZE); + /* init wiphy according to mphy and phy */ + mt7915_init_wiphy(mphy->hw); + ret = mt7915_init_tx_queues(phy, MT_TXQ_ID(phy->band_idx), + MT7915_TX_RING_SIZE, + MT_TXQ_RING_BASE(1)); if (ret) goto error; @@ -495,46 +561,88 @@ static void mt7915_init_work(struct work_struct *work) mt7915_mac_init(dev); mt7915_init_txpower(dev, &dev->mphy.sband_2g.sband); mt7915_init_txpower(dev, &dev->mphy.sband_5g.sband); + mt7915_init_txpower(dev, &dev->mphy.sband_6g.sband); mt7915_txbf_init(dev); } static void mt7915_wfsys_reset(struct mt7915_dev *dev) { - u32 val = MT_TOP_PWR_KEY | MT_TOP_PWR_SW_PWR_ON | MT_TOP_PWR_PWR_ON; - #define MT_MCU_DUMMY_RANDOM GENMASK(15, 0) #define MT_MCU_DUMMY_DEFAULT GENMASK(31, 16) - mt76_wr(dev, MT_MCU_WFDMA0_DUMMY_CR, MT_MCU_DUMMY_RANDOM); + if (is_mt7915(&dev->mt76)) { + u32 val = MT_TOP_PWR_KEY | MT_TOP_PWR_SW_PWR_ON | MT_TOP_PWR_PWR_ON; - /* change to software control */ - val |= MT_TOP_PWR_SW_RST; - mt76_wr(dev, MT_TOP_PWR_CTRL, val); + mt76_wr(dev, MT_MCU_WFDMA0_DUMMY_CR, MT_MCU_DUMMY_RANDOM); - /* reset wfsys */ - val &= ~MT_TOP_PWR_SW_RST; - mt76_wr(dev, MT_TOP_PWR_CTRL, val); + /* change to software control */ + val |= MT_TOP_PWR_SW_RST; + mt76_wr(dev, MT_TOP_PWR_CTRL, val); - /* release wfsys then mcu re-excutes romcode */ - val |= MT_TOP_PWR_SW_RST; - mt76_wr(dev, MT_TOP_PWR_CTRL, val); + /* reset wfsys */ + val &= ~MT_TOP_PWR_SW_RST; + mt76_wr(dev, MT_TOP_PWR_CTRL, val); - /* switch to hw control */ - val &= ~MT_TOP_PWR_SW_RST; - val |= MT_TOP_PWR_HW_CTRL; - mt76_wr(dev, MT_TOP_PWR_CTRL, val); + /* release wfsys then mcu re-executes romcode */ + val |= MT_TOP_PWR_SW_RST; + mt76_wr(dev, MT_TOP_PWR_CTRL, val); - /* check whether mcu resets to default */ - if (!mt76_poll_msec(dev, MT_MCU_WFDMA0_DUMMY_CR, MT_MCU_DUMMY_DEFAULT, - MT_MCU_DUMMY_DEFAULT, 1000)) { - dev_err(dev->mt76.dev, "wifi subsystem reset failure\n"); - return; + /* switch to hw control */ + val &= ~MT_TOP_PWR_SW_RST; + val |= MT_TOP_PWR_HW_CTRL; + mt76_wr(dev, MT_TOP_PWR_CTRL, val); + + /* check whether mcu resets to default */ + if (!mt76_poll_msec(dev, MT_MCU_WFDMA0_DUMMY_CR, + MT_MCU_DUMMY_DEFAULT, MT_MCU_DUMMY_DEFAULT, + 1000)) { + dev_err(dev->mt76.dev, "wifi subsystem reset failure\n"); + return; + } + + /* wfsys reset won't clear host registers */ + mt76_clear(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE); + + msleep(100); + } else if (is_mt7986(&dev->mt76)) { + mt7986_wmac_disable(dev); + msleep(20); + + mt7986_wmac_enable(dev); + msleep(20); + } else { + mt76_set(dev, MT_WF_SUBSYS_RST, 0x1); + msleep(20); + + mt76_clear(dev, MT_WF_SUBSYS_RST, 0x1); + msleep(20); + } +} + +static bool mt7915_band_config(struct mt7915_dev *dev) +{ + bool ret = true; + + dev->phy.band_idx = 0; + + if (is_mt7986(&dev->mt76)) { + u32 sku = mt7915_check_adie(dev, true); + + /* + * for mt7986, dbdc support is determined by the number + * of adie chips and the main phy is bound to band1 when + * dbdc is disabled. + */ + if (sku == MT7975_ONE_ADIE || sku == MT7976_ONE_ADIE) { + dev->phy.band_idx = 1; + ret = false; + } + } else { + ret = is_mt7915(&dev->mt76) ? + !!(mt76_rr(dev, MT_HW_BOUND) & BIT(5)) : true; } - /* wfsys reset won't clear host registers */ - mt76_clear(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE); - - msleep(100); + return ret; } static int mt7915_init_hardware(struct mt7915_dev *dev) @@ -544,7 +652,8 @@ static int mt7915_init_hardware(struct mt7915_dev *dev) mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); INIT_WORK(&dev->init_work, mt7915_init_work); - dev->dbdc_support = !!(mt76_rr(dev, MT_HW_BOUND) & BIT(5)); + + dev->dbdc_support = mt7915_band_config(dev); /* If MCU was already running, it is likely in a bad state */ if (mt76_get_field(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE) > @@ -557,12 +666,6 @@ static int mt7915_init_hardware(struct mt7915_dev *dev) set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); - /* - * force firmware operation mode into normal state, - * which should be set before firmware download stage. - */ - mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); - ret = mt7915_mcu_init(dev); if (ret) { /* Reset and try again */ @@ -577,7 +680,6 @@ static int mt7915_init_hardware(struct mt7915_dev *dev) if (ret < 0) return ret; - if (dev->flash_mode) { ret = mt7915_mcu_apply_group_cal(dev); if (ret) @@ -626,11 +728,18 @@ void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy) } static void -mt7915_set_stream_he_txbf_caps(struct ieee80211_sta_he_cap *he_cap, +mt7915_set_stream_he_txbf_caps(struct mt7915_dev *dev, + struct ieee80211_sta_he_cap *he_cap, int vif, int nss) { struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem; - u8 c; + u8 c, nss_160; + + /* Can do 1/2 of NSS streams in 160Mhz mode for mt7915 */ + if (is_mt7915(&dev->mt76) && !dev->dbdc_support) + nss_160 = nss / 2; + else + nss_160 = nss; #ifdef CONFIG_MAC80211_MESH if (vif == NL80211_IFTYPE_MESH_POINT) @@ -684,13 +793,21 @@ mt7915_set_stream_he_txbf_caps(struct ieee80211_sta_he_cap *he_cap, /* num_snd_dim * for mt7915, max supported nss is 2 for bw > 80MHz */ - c = (nss - 1) | - IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2; + c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + nss - 1) | + FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, + nss_160 - 1); elem->phy_cap_info[5] |= c; c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB; elem->phy_cap_info[6] |= c; + + if (!is_mt7915(&dev->mt76)) { + c = IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ | + IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ; + elem->phy_cap_info[7] |= c; + } } static void @@ -718,9 +835,17 @@ static int mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, struct ieee80211_sband_iftype_data *data) { + struct mt7915_dev *dev = phy->dev; int i, idx = 0, nss = hweight8(phy->mt76->chainmask); u16 mcs_map = 0; u16 mcs_map_160 = 0; + u8 nss_160; + + /* Can do 1/2 of NSS streams in 160Mhz mode for mt7915 */ + if (is_mt7915(&dev->mt76) && !dev->dbdc_support) + nss_160 = nss / 2; + else + nss_160 = nss; for (i = 0; i < 8; i++) { if (i < nss) @@ -728,8 +853,7 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, else mcs_map |= (IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2)); - /* Can do 1/2 of NSS streams in 160Mhz mode. */ - if (i < nss / 2) + if (i < nss_160) mcs_map_160 |= (IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2)); else mcs_map_160 |= (IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2)); @@ -767,7 +891,7 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, if (band == NL80211_BAND_2GHZ) he_cap_elem->phy_cap_info[0] = IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; - else if (band == NL80211_BAND_5GHZ) + else he_cap_elem->phy_cap_info[0] = IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | @@ -806,7 +930,7 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, if (band == NL80211_BAND_2GHZ) he_cap_elem->phy_cap_info[0] |= IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G; - else if (band == NL80211_BAND_5GHZ) + else he_cap_elem->phy_cap_info[0] |= IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G; @@ -845,7 +969,7 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, he_mcs->rx_mcs_80p80 = cpu_to_le16(mcs_map_160); he_mcs->tx_mcs_80p80 = cpu_to_le16(mcs_map_160); - mt7915_set_stream_he_txbf_caps(he_cap, i, nss); + mt7915_set_stream_he_txbf_caps(dev, he_cap, i, nss); memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); if (he_cap_elem->phy_cap_info[6] & @@ -856,6 +980,21 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, u8_encode_bits(IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US, IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); } + + if (band == NL80211_BAND_6GHZ) { + u16 cap = IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS | + IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS; + + cap |= u16_encode_bits(IEEE80211_HT_MPDU_DENSITY_8, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START) | + u16_encode_bits(IEEE80211_VHT_MAX_AMPDU_1024K, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP) | + u16_encode_bits(IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454, + IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN); + + data[idx].he_6ghz_capa.capa = cpu_to_le16(cap); + } + idx++; } @@ -885,6 +1024,15 @@ void mt7915_set_stream_he_caps(struct mt7915_phy *phy) band->iftype_data = data; band->n_iftype_data = n; } + + if (phy->mt76->cap.has_6ghz) { + data = phy->iftype[NL80211_BAND_6GHZ]; + n = mt7915_init_he_caps(phy, NL80211_BAND_6GHZ, data); + + band = &phy->mt76->sband_6g.sband; + band->iftype_data = data; + band->n_iftype_data = n; + } } static void mt7915_unregister_ext_phy(struct mt7915_dev *dev) @@ -924,15 +1072,6 @@ int mt7915_register_device(struct mt7915_dev *dev) mt7915_init_wiphy(hw); - if (!dev->dbdc_support) - dev->mphy.sband_5g.sband.vht_cap.cap |= - IEEE80211_VHT_CAP_SHORT_GI_160 | - IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; - - dev->mphy.hw->wiphy->available_antennas_rx = dev->mphy.chainmask; - dev->mphy.hw->wiphy->available_antennas_tx = dev->mphy.chainmask; - dev->phy.dfs_state = -1; - #ifdef CONFIG_NL80211_TESTMODE dev->mt76.test_ops = &mt7915_testmode_ops; #endif @@ -971,5 +1110,8 @@ void mt7915_unregister_device(struct mt7915_dev *dev) mt7915_dma_cleanup(dev); tasklet_disable(&dev->irq_tasklet); + if (is_mt7986(&dev->mt76)) + mt7986_wmac_disable(dev); + mt76_free_device(&dev->mt76); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 48f115502282..e9e7efbf350d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -165,7 +165,7 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); for (i = 0; i < IEEE80211_NUM_ACS; i++) { - u8 q = mt7915_lmac_mapping(dev, i); + u8 q = mt76_connac_lmac_mapping(i); u32 tx_cur = tx_time[q]; u32 rx_cur = rx_time[q]; u8 tid = ac_to_tid[i]; @@ -226,8 +226,8 @@ mt7915_mac_decode_he_radiotap_ru(struct mt76_rx_status *status, u32 ru_h, ru_l; u8 ru, offs = 0; - ru_l = FIELD_GET(MT_PRXV_HE_RU_ALLOC_L, le32_to_cpu(rxv[0])); - ru_h = FIELD_GET(MT_PRXV_HE_RU_ALLOC_H, le32_to_cpu(rxv[1])); + ru_l = le32_get_bits(rxv[0], MT_PRXV_HE_RU_ALLOC_L); + ru_h = le32_get_bits(rxv[1], MT_PRXV_HE_RU_ALLOC_H); ru = (u8)(ru_l | ru_h << 4); status->bw = RATE_INFO_BW_HE_RU; @@ -349,14 +349,16 @@ mt7915_mac_decode_he_radiotap(struct sk_buff *skb, __le32 *rxv, u32 mode) case MT_PHY_TYPE_HE_SU: he->data1 |= HE_BITS(DATA1_FORMAT_SU) | HE_BITS(DATA1_UL_DL_KNOWN) | - HE_BITS(DATA1_BEAM_CHANGE_KNOWN); + HE_BITS(DATA1_BEAM_CHANGE_KNOWN) | + HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); he->data3 |= HE_PREP(DATA3_BEAM_CHANGE, BEAM_CHNG, rxv[14]) | HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); break; case MT_PHY_TYPE_HE_EXT_SU: he->data1 |= HE_BITS(DATA1_FORMAT_EXT_SU) | - HE_BITS(DATA1_UL_DL_KNOWN); + HE_BITS(DATA1_UL_DL_KNOWN) | + HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); break; @@ -376,7 +378,8 @@ mt7915_mac_decode_he_radiotap(struct sk_buff *skb, __le32 *rxv, u32 mode) HE_BITS(DATA1_SPTL_REUSE3_KNOWN) | HE_BITS(DATA1_SPTL_REUSE4_KNOWN); - he->data4 |= HE_PREP(DATA4_TB_SPTL_REUSE2, SR1_MASK, rxv[11]) | + he->data4 |= HE_PREP(DATA4_TB_SPTL_REUSE1, SR_MASK, rxv[11]) | + HE_PREP(DATA4_TB_SPTL_REUSE2, SR1_MASK, rxv[11]) | HE_PREP(DATA4_TB_SPTL_REUSE3, SR2_MASK, rxv[11]) | HE_PREP(DATA4_TB_SPTL_REUSE4, SR3_MASK, rxv[11]); @@ -391,15 +394,15 @@ mt7915_mac_decode_he_radiotap(struct sk_buff *skb, __le32 *rxv, u32 mode) static int mt7915_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) { struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap); struct mt7915_sta *msta = (struct mt7915_sta *)status->wcid; + __le32 *rxd = (__le32 *)skb->data; struct ieee80211_sta *sta; struct ieee80211_vif *vif; struct ieee80211_hdr hdr; - struct ethhdr eth_hdr; - __le32 *rxd = (__le32 *)skb->data; - __le32 qos_ctrl, ht_ctrl; + u16 frame_control; - if (FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, le32_to_cpu(rxd[3])) != + if (le32_get_bits(rxd[3], MT_RXD3_NORMAL_ADDR_TYPE) != MT_RXD3_NORMAL_U2M) return -EINVAL; @@ -413,47 +416,52 @@ static int mt7915_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); /* store the info from RXD and ethhdr to avoid being overridden */ - memcpy(ð_hdr, skb->data + hdr_gap, sizeof(eth_hdr)); - hdr.frame_control = FIELD_GET(MT_RXD6_FRAME_CONTROL, rxd[6]); - hdr.seq_ctrl = FIELD_GET(MT_RXD8_SEQ_CTRL, rxd[8]); - qos_ctrl = FIELD_GET(MT_RXD8_QOS_CTL, rxd[8]); - ht_ctrl = FIELD_GET(MT_RXD9_HT_CONTROL, rxd[9]); - + frame_control = le32_get_bits(rxd[6], MT_RXD6_FRAME_CONTROL); + hdr.frame_control = cpu_to_le16(frame_control); + hdr.seq_ctrl = cpu_to_le16(le32_get_bits(rxd[8], MT_RXD8_SEQ_CTRL)); hdr.duration_id = 0; + ether_addr_copy(hdr.addr1, vif->addr); ether_addr_copy(hdr.addr2, sta->addr); - switch (le16_to_cpu(hdr.frame_control) & - (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { + switch (frame_control & (IEEE80211_FCTL_TODS | + IEEE80211_FCTL_FROMDS)) { case 0: ether_addr_copy(hdr.addr3, vif->bss_conf.bssid); break; case IEEE80211_FCTL_FROMDS: - ether_addr_copy(hdr.addr3, eth_hdr.h_source); + ether_addr_copy(hdr.addr3, eth_hdr->h_source); break; case IEEE80211_FCTL_TODS: - ether_addr_copy(hdr.addr3, eth_hdr.h_dest); + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); break; case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS: - ether_addr_copy(hdr.addr3, eth_hdr.h_dest); - ether_addr_copy(hdr.addr4, eth_hdr.h_source); + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + ether_addr_copy(hdr.addr4, eth_hdr->h_source); break; default: break; } skb_pull(skb, hdr_gap + sizeof(struct ethhdr) - 2); - if (eth_hdr.h_proto == htons(ETH_P_AARP) || - eth_hdr.h_proto == htons(ETH_P_IPX)) + if (eth_hdr->h_proto == cpu_to_be16(ETH_P_AARP) || + eth_hdr->h_proto == cpu_to_be16(ETH_P_IPX)) ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header); - else if (eth_hdr.h_proto >= htons(ETH_P_802_3_MIN)) + else if (be16_to_cpu(eth_hdr->h_proto) >= ETH_P_802_3_MIN) ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header); else skb_pull(skb, 2); if (ieee80211_has_order(hdr.frame_control)) - memcpy(skb_push(skb, 2), &ht_ctrl, 2); - if (ieee80211_is_data_qos(hdr.frame_control)) - memcpy(skb_push(skb, 2), &qos_ctrl, 2); + memcpy(skb_push(skb, IEEE80211_HT_CTL_LEN), &rxd[9], + IEEE80211_HT_CTL_LEN); + if (ieee80211_is_data_qos(hdr.frame_control)) { + __le16 qos_ctrl; + + qos_ctrl = cpu_to_le16(le32_get_bits(rxd[8], MT_RXD8_QOS_CTL)); + memcpy(skb_push(skb, IEEE80211_QOS_CTL_LEN), &qos_ctrl, + IEEE80211_QOS_CTL_LEN); + } + if (ieee80211_has_a4(hdr.frame_control)) memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); else @@ -462,6 +470,108 @@ static int mt7915_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) return 0; } +static int +mt7915_mac_fill_rx_rate(struct mt7915_dev *dev, + struct mt76_rx_status *status, + struct ieee80211_supported_band *sband, + __le32 *rxv) +{ + u32 v0, v2; + u8 stbc, gi, bw, dcm, mode, nss; + int i, idx; + bool cck = false; + + v0 = le32_to_cpu(rxv[0]); + v2 = le32_to_cpu(rxv[2]); + + idx = i = FIELD_GET(MT_PRXV_TX_RATE, v0); + nss = FIELD_GET(MT_PRXV_NSTS, v0) + 1; + + if (!is_mt7915(&dev->mt76)) { + stbc = FIELD_GET(MT_PRXV_HT_STBC, v0); + gi = FIELD_GET(MT_PRXV_HT_SHORT_GI, v0); + mode = FIELD_GET(MT_PRXV_TX_MODE, v0); + dcm = FIELD_GET(MT_PRXV_DCM, v0); + bw = FIELD_GET(MT_PRXV_FRAME_MODE, v0); + } else { + stbc = FIELD_GET(MT_CRXV_HT_STBC, v2); + gi = FIELD_GET(MT_CRXV_HT_SHORT_GI, v2); + mode = FIELD_GET(MT_CRXV_TX_MODE, v2); + dcm = !!(idx & GENMASK(3, 0) & MT_PRXV_TX_DCM); + bw = FIELD_GET(MT_CRXV_FRAME_MODE, v2); + } + + switch (mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + i = mt76_get_rate(&dev->mt76, sband, i, cck); + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + status->encoding = RX_ENC_HT; + if (gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (i > 31) + return -EINVAL; + break; + case MT_PHY_TYPE_VHT: + status->nss = nss; + status->encoding = RX_ENC_VHT; + if (gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (i > 9) + return -EINVAL; + break; + case MT_PHY_TYPE_HE_MU: + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + status->nss = nss; + status->encoding = RX_ENC_HE; + i &= GENMASK(3, 0); + + if (gi <= NL80211_RATE_INFO_HE_GI_3_2) + status->he_gi = gi; + + status->he_dcm = dcm; + break; + default: + return -EINVAL; + } + status->rate_idx = i; + + switch (bw) { + case IEEE80211_STA_RX_BW_20: + break; + case IEEE80211_STA_RX_BW_40: + if (mode & MT_PHY_TYPE_HE_EXT_SU && + (idx & MT_PRXV_TX_ER_SU_106T)) { + status->bw = RATE_INFO_BW_HE_RU; + status->he_ru = + NL80211_RATE_INFO_HE_RU_ALLOC_106; + } else { + status->bw = RATE_INFO_BW_40; + } + break; + case IEEE80211_STA_RX_BW_80: + status->bw = RATE_INFO_BW_80; + break; + case IEEE80211_STA_RX_BW_160: + status->bw = RATE_INFO_BW_160; + break; + default: + return -EINVAL; + } + + status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc; + if (mode < MT_PHY_TYPE_HE_SU && gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + + return 0; +} + static int mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) { @@ -485,11 +595,11 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) u16 seq_ctrl = 0; u8 qos_ctl = 0; __le16 fc = 0; - int i, idx; + int idx; memset(status, 0, sizeof(*status)); - if (rxd1 & MT_RXD1_NORMAL_BAND_IDX) { + if ((rxd1 & MT_RXD1_NORMAL_BAND_IDX) && !phy->band_idx) { mphy = dev->mt76.phy2; if (!mphy) return -EINVAL; @@ -530,6 +640,8 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) status->band = mphy->chandef.chan->band; if (status->band == NL80211_BAND_5GHZ) sband = &mphy->sband_5g.sband; + else if (status->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; else sband = &mphy->sband_2g.sband; @@ -626,7 +738,8 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) /* RXD Group 3 - P-RXV */ if (rxd1 & MT_RXD1_NORMAL_GROUP_3) { - u32 v0, v1, v2; + u32 v0, v1; + int ret; rxv = rxd; rxd += 2; @@ -635,7 +748,6 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) v0 = le32_to_cpu(rxv[0]); v1 = le32_to_cpu(rxv[1]); - v2 = le32_to_cpu(rxv[2]); if (v0 & MT_PRXV_HT_AD_CODE) status->enc_flags |= RX_ENC_FLAG_LDPC; @@ -645,94 +757,18 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v1); status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v1); status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v1); - status->signal = status->chain_signal[0]; - - for (i = 1; i < hweight8(mphy->antenna_mask); i++) { - if (!(status->chains & BIT(i))) - continue; - - status->signal = max(status->signal, - status->chain_signal[i]); - } /* RXD Group 5 - C-RXV */ if (rxd1 & MT_RXD1_NORMAL_GROUP_5) { - u8 stbc = FIELD_GET(MT_CRXV_HT_STBC, v2); - u8 gi = FIELD_GET(MT_CRXV_HT_SHORT_GI, v2); - bool cck = false; - rxd += 18; if ((u8 *)rxd - skb->data >= skb->len) return -EINVAL; + } - idx = i = FIELD_GET(MT_PRXV_TX_RATE, v0); - mode = FIELD_GET(MT_CRXV_TX_MODE, v2); - - switch (mode) { - case MT_PHY_TYPE_CCK: - cck = true; - fallthrough; - case MT_PHY_TYPE_OFDM: - i = mt76_get_rate(&dev->mt76, sband, i, cck); - break; - case MT_PHY_TYPE_HT_GF: - case MT_PHY_TYPE_HT: - status->encoding = RX_ENC_HT; - if (i > 31) - return -EINVAL; - break; - case MT_PHY_TYPE_VHT: - status->nss = - FIELD_GET(MT_PRXV_NSTS, v0) + 1; - status->encoding = RX_ENC_VHT; - if (i > 9) - return -EINVAL; - break; - case MT_PHY_TYPE_HE_MU: - case MT_PHY_TYPE_HE_SU: - case MT_PHY_TYPE_HE_EXT_SU: - case MT_PHY_TYPE_HE_TB: - status->nss = - FIELD_GET(MT_PRXV_NSTS, v0) + 1; - status->encoding = RX_ENC_HE; - i &= GENMASK(3, 0); - - if (gi <= NL80211_RATE_INFO_HE_GI_3_2) - status->he_gi = gi; - - status->he_dcm = !!(idx & MT_PRXV_TX_DCM); - break; - default: - return -EINVAL; - } - status->rate_idx = i; - - switch (FIELD_GET(MT_CRXV_FRAME_MODE, v2)) { - case IEEE80211_STA_RX_BW_20: - break; - case IEEE80211_STA_RX_BW_40: - if (mode & MT_PHY_TYPE_HE_EXT_SU && - (idx & MT_PRXV_TX_ER_SU_106T)) { - status->bw = RATE_INFO_BW_HE_RU; - status->he_ru = - NL80211_RATE_INFO_HE_RU_ALLOC_106; - } else { - status->bw = RATE_INFO_BW_40; - } - break; - case IEEE80211_STA_RX_BW_80: - status->bw = RATE_INFO_BW_80; - break; - case IEEE80211_STA_RX_BW_160: - status->bw = RATE_INFO_BW_160; - break; - default: - return -EINVAL; - } - - status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc; - if (mode < MT_PHY_TYPE_HE_SU && gi) - status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (!is_mt7915(&dev->mt76) || (rxd1 & MT_RXD1_NORMAL_GROUP_5)) { + ret = mt7915_mac_fill_rx_rate(dev, status, sband, rxv); + if (ret < 0) + return ret; } } @@ -801,6 +837,10 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) if (!status->wcid || !ieee80211_is_data_qos(fc)) return 0; + /* drop no data frame */ + if (fc & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)) + return -EINVAL; + status->aggr = unicast && !ieee80211_is_qos_nullfunc(fc); status->qos_ctl = qos_ctl; @@ -818,13 +858,13 @@ mt7915_mac_fill_rx_vector(struct mt7915_dev *dev, struct sk_buff *skb) __le32 *rxv_hdr = rxd + 2; __le32 *rxv = rxd + 4; u32 rcpi, ib_rssi, wb_rssi, v20, v21; - bool ext_phy; + u8 band_idx; s32 foe; u8 snr; int i; - ext_phy = FIELD_GET(MT_RXV_HDR_BAND_IDX, le32_to_cpu(rxv_hdr[1])); - if (ext_phy) + band_idx = le32_get_bits(rxv_hdr[1], MT_RXV_HDR_BAND_IDX); + if (band_idx && !phy->band_idx) phy = mt7915_ext_phy(dev); rcpi = le32_to_cpu(rxv[6]); @@ -1065,6 +1105,7 @@ mt7915_mac_write_txwi_80211(struct mt7915_dev *dev, __le32 *txwi, if (ieee80211_is_beacon(fc)) { txwi[3] &= ~cpu_to_le32(MT_TXD3_SW_POWER_MGMT); txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); + txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX, 0x18)); } if (info->flags & IEEE80211_TX_CTL_INJECTED) { @@ -1080,6 +1121,7 @@ mt7915_mac_write_txwi_80211(struct mt7915_dev *dev, __le32 *txwi, val = MT_TXD3_SN_VALID | FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); txwi[3] |= cpu_to_le32(val); + txwi[7] &= ~cpu_to_le32(MT_TXD7_HW_AMSDU); } val = FIELD_PREP(MT_TXD7_TYPE, fc_type) | @@ -1140,7 +1182,7 @@ void mt7915_mac_write_txwi(struct mt7915_dev *dev, __le32 *txwi, struct ieee80211_vif *vif = info->control.vif; struct mt76_phy *mphy = &dev->mphy; bool ext_phy = info->hw_queue & MT_TX_HW_QUEUE_EXT_PHY; - u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0; + u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0, band_idx = 0; bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; bool mcast = false; u16 tx_count = 15; @@ -1151,6 +1193,7 @@ void mt7915_mac_write_txwi(struct mt7915_dev *dev, __le32 *txwi, omac_idx = mvif->mt76.omac_idx; wmm_idx = mvif->mt76.wmm_idx; + band_idx = mvif->mt76.band_idx; } if (ext_phy && dev->mt76.phy2) @@ -1165,7 +1208,7 @@ void mt7915_mac_write_txwi(struct mt7915_dev *dev, __le32 *txwi, } else { p_fmt = MT_TX_TYPE_CT; q_idx = wmm_idx * MT7915_MAX_WMM_SETS + - mt7915_lmac_mapping(dev, skb_get_queue_mapping(skb)); + mt76_connac_lmac_mapping(skb_get_queue_mapping(skb)); } val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + MT_TXD_SIZE) | @@ -1177,7 +1220,7 @@ void mt7915_mac_write_txwi(struct mt7915_dev *dev, __le32 *txwi, FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) | FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx); - if (ext_phy && q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0) + if (ext_phy || band_idx) val |= MT_TXD1_TGID; txwi[1] = cpu_to_le32(val); @@ -1311,10 +1354,10 @@ mt7915_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi) u16 fc, tid; u32 val; - if (!sta || !sta->ht_cap.ht_supported) + if (!sta || !(sta->ht_cap.ht_supported || sta->he_cap.has_he)) return; - tid = FIELD_GET(MT_TXD1_TID, le32_to_cpu(txwi[1])); + tid = le32_get_bits(txwi[1], MT_TXD1_TID); if (tid >= 6) /* skip VO queue */ return; @@ -1362,7 +1405,7 @@ mt7915_txwi_free(struct mt7915_dev *dev, struct mt76_txwi_cache *t, if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE))) mt7915_tx_check_aggr(sta, txwi); } else { - wcid_idx = FIELD_GET(MT_TXD1_WLAN_IDX, le32_to_cpu(txwi[1])); + wcid_idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX); } __mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list); @@ -1383,8 +1426,10 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len) LIST_HEAD(free_list); struct sk_buff *skb, *tmp; void *end = data + len; - u8 i, count; - bool wake = false; + bool v3, wake = false; + u16 total, count = 0; + u32 txd = le32_to_cpu(free->txd); + __le32 *cur_info; /* clean DMA queues and unmap buffers first */ mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false); @@ -1394,17 +1439,14 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len) mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[MT_TXQ_BE], false); } - /* - * TODO: MT_TX_FREE_LATENCY is msdu time from the TXD is queued into PLE, - * to the time ack is received or dropped by hw (air + hw queue time). - * Should avoid accessing WTBL to get Tx airtime, and use it instead. - */ - count = FIELD_GET(MT_TX_FREE_MSDU_CNT, le16_to_cpu(free->ctrl)); - if (WARN_ON_ONCE((void *)&free->info[count] > end)) + total = le16_get_bits(free->ctrl, MT_TX_FREE_MSDU_CNT); + v3 = (FIELD_GET(MT_TX_FREE_VER, txd) == 0x4); + if (WARN_ON_ONCE((void *)&free->info[total >> v3] > end)) return; - for (i = 0; i < count; i++) { - u32 msdu, info = le32_to_cpu(free->info[i]); + for (cur_info = &free->info[0]; count < total; cur_info++) { + u32 msdu, info = le32_to_cpu(*cur_info); + u8 i; /* * 1'b1: new wcid pair. @@ -1415,7 +1457,6 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len) struct mt76_wcid *wcid; u16 idx; - count++; idx = FIELD_GET(MT_TX_FREE_WLAN_ID, info); wcid = rcu_dereference(dev->mt76.wcid[idx]); sta = wcid_to_sta(wcid); @@ -1430,12 +1471,24 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len) continue; } - msdu = FIELD_GET(MT_TX_FREE_MSDU_ID, info); - txwi = mt76_token_release(mdev, msdu, &wake); - if (!txwi) + if (v3 && (info & MT_TX_FREE_MPDU_HEADER)) continue; - mt7915_txwi_free(dev, txwi, sta, &free_list); + for (i = 0; i < 1 + v3; i++) { + if (v3) { + msdu = (info >> (15 * i)) & MT_TX_FREE_MSDU_ID_V3; + if (msdu == MT_TX_FREE_MSDU_ID_V3) + continue; + } else { + msdu = FIELD_GET(MT_TX_FREE_MSDU_ID, info); + } + count++; + txwi = mt76_token_release(mdev, msdu, &wake); + if (!txwi) + continue; + + mt7915_txwi_free(dev, txwi, sta, &free_list); + } } mt7915_mac_sta_poll(dev); @@ -1504,6 +1557,8 @@ mt7915_mac_add_txs_skb(struct mt7915_dev *dev, struct mt76_wcid *wcid, int pid, if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) sband = &mphy->sband_5g.sband; + else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; else sband = &mphy->sband_2g.sband; @@ -1512,7 +1567,6 @@ mt7915_mac_add_txs_skb(struct mt7915_dev *dev, struct mt76_wcid *wcid, int pid, break; case MT_PHY_TYPE_HT: case MT_PHY_TYPE_HT_GF: - rate.mcs += (rate.nss - 1) * 8; if (rate.mcs > 31) goto out; @@ -1578,23 +1632,18 @@ static void mt7915_mac_add_txs(struct mt7915_dev *dev, void *data) struct mt76_wcid *wcid; __le32 *txs_data = data; u16 wcidx; - u32 txs; u8 pid; - txs = le32_to_cpu(txs_data[0]); - if (FIELD_GET(MT_TXS0_TXS_FORMAT, txs) > 1) + if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) > 1) return; - txs = le32_to_cpu(txs_data[2]); - wcidx = FIELD_GET(MT_TXS2_WCID, txs); - - txs = le32_to_cpu(txs_data[3]); - pid = FIELD_GET(MT_TXS3_PID, txs); + wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID); + pid = le32_get_bits(txs_data[3], MT_TXS3_PID); if (pid < MT_PACKET_ID_FIRST) return; - if (wcidx >= MT7915_WTBL_SIZE) + if (wcidx >= mt7915_wtbl_size(dev)) return; rcu_read_lock(); @@ -1626,7 +1675,8 @@ bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len) __le32 *end = (__le32 *)&rxd[len / 4]; enum rx_pkt_type type; - type = FIELD_GET(MT_RXD0_PKT_TYPE, le32_to_cpu(rxd[0])); + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + switch (type) { case PKT_TYPE_TXRX_NOTIFY: mt7915_mac_tx_free(dev, data, len); @@ -1635,6 +1685,9 @@ bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len) for (rxd += 2; rxd + 8 <= end; rxd += 8) mt7915_mac_add_txs(dev, rxd); return false; + case PKT_TYPE_RX_FW_MONITOR: + mt7915_debugfs_rx_fw_monitor(dev, data, len); + return false; default: return true; } @@ -1648,7 +1701,7 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, __le32 *end = (__le32 *)&skb->data[skb->len]; enum rx_pkt_type type; - type = FIELD_GET(MT_RXD0_PKT_TYPE, le32_to_cpu(rxd[0])); + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); switch (type) { case PKT_TYPE_TXRX_NOTIFY: @@ -1666,6 +1719,10 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, mt7915_mac_add_txs(dev, rxd); dev_kfree_skb(skb); break; + case PKT_TYPE_RX_FW_MONITOR: + mt7915_debugfs_rx_fw_monitor(dev, skb->data, skb->len); + dev_kfree_skb(skb); + break; case PKT_TYPE_NORMAL: if (!mt7915_mac_fill_rx(dev, skb)) { mt76_rx(&dev->mt76, q, skb); @@ -1702,8 +1759,7 @@ void mt7915_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e) void mt7915_mac_cca_stats_reset(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; - bool ext_phy = phy != &dev->phy; - u32 reg = MT_WF_PHY_RX_CTRL1(ext_phy); + u32 reg = MT_WF_PHY_RX_CTRL1(phy->band_idx); mt76_clear(dev, reg, MT_WF_PHY_RX_CTRL1_STSCNT_EN); mt76_set(dev, reg, BIT(11) | BIT(9)); @@ -1712,25 +1768,22 @@ void mt7915_mac_cca_stats_reset(struct mt7915_phy *phy) void mt7915_mac_reset_counters(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; - bool ext_phy = phy != &dev->phy; int i; for (i = 0; i < 4; i++) { - mt76_rr(dev, MT_TX_AGG_CNT(ext_phy, i)); - mt76_rr(dev, MT_TX_AGG_CNT2(ext_phy, i)); + mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); + mt76_rr(dev, MT_TX_AGG_CNT2(phy->band_idx, i)); } - if (ext_phy) { - dev->mt76.phy2->survey_time = ktime_get_boottime(); + i = 0; + phy->mt76->survey_time = ktime_get_boottime(); + if (phy->band_idx) i = ARRAY_SIZE(dev->mt76.aggr_stats) / 2; - } else { - dev->mt76.phy.survey_time = ktime_get_boottime(); - i = 0; - } + memset(&dev->mt76.aggr_stats[i], 0, sizeof(dev->mt76.aggr_stats) / 2); /* reset airtime counters */ - mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(ext_phy), + mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(phy->band_idx), MT_WF_RMAC_MIB_RXTIME_CLR); mt7915_mcu_get_chan_mib_info(phy, true); @@ -1740,29 +1793,23 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) { s16 coverage_class = phy->coverage_class; struct mt7915_dev *dev = phy->dev; - bool ext_phy = phy != &dev->phy; + struct mt7915_phy *ext_phy = mt7915_ext_phy(dev); u32 val, reg_offset; u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28); int offset; - bool is_5ghz = phy->mt76->chandef.chan->band == NL80211_BAND_5GHZ; + bool a_band = !(phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ); if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) return; - if (ext_phy) { + if (ext_phy) coverage_class = max_t(s16, dev->phy.coverage_class, - coverage_class); - } else { - struct mt7915_phy *phy_ext = mt7915_ext_phy(dev); + ext_phy->coverage_class); - if (phy_ext) - coverage_class = max_t(s16, phy_ext->coverage_class, - coverage_class); - } - mt76_set(dev, MT_ARB_SCR(ext_phy), + mt76_set(dev, MT_ARB_SCR(phy->band_idx), MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); udelay(1); @@ -1770,35 +1817,40 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); - mt76_wr(dev, MT_TMAC_CDTR(ext_phy), cck + reg_offset); - mt76_wr(dev, MT_TMAC_ODTR(ext_phy), ofdm + reg_offset); - mt76_wr(dev, MT_TMAC_ICR0(ext_phy), - FIELD_PREP(MT_IFS_EIFS_OFDM, is_5ghz ? 84 : 78) | + mt76_wr(dev, MT_TMAC_CDTR(phy->band_idx), cck + reg_offset); + mt76_wr(dev, MT_TMAC_ODTR(phy->band_idx), ofdm + reg_offset); + mt76_wr(dev, MT_TMAC_ICR0(phy->band_idx), + FIELD_PREP(MT_IFS_EIFS_OFDM, a_band ? 84 : 78) | FIELD_PREP(MT_IFS_RIFS, 2) | FIELD_PREP(MT_IFS_SIFS, 10) | FIELD_PREP(MT_IFS_SLOT, phy->slottime)); - mt76_wr(dev, MT_TMAC_ICR1(ext_phy), + mt76_wr(dev, MT_TMAC_ICR1(phy->band_idx), FIELD_PREP(MT_IFS_EIFS_CCK, 314)); - if (phy->slottime < 20 || is_5ghz) + if (phy->slottime < 20 || a_band) val = MT7915_CFEND_RATE_DEFAULT; else val = MT7915_CFEND_RATE_11B; - mt76_rmw_field(dev, MT_AGG_ACR0(ext_phy), MT_AGG_ACR_CFEND_RATE, val); - mt76_clear(dev, MT_ARB_SCR(ext_phy), + mt76_rmw_field(dev, MT_AGG_ACR0(phy->band_idx), MT_AGG_ACR_CFEND_RATE, val); + mt76_clear(dev, MT_ARB_SCR(phy->band_idx), MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); } void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool ext_phy) { - mt76_set(dev, MT_WF_PHY_RXTD12(ext_phy), + u32 reg; + + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RXTD12(ext_phy) : + MT_WF_PHY_RXTD12_MT7916(ext_phy); + mt76_set(dev, reg, MT_WF_PHY_RXTD12_IRPI_SW_CLR_ONLY | MT_WF_PHY_RXTD12_IRPI_SW_CLR); - mt76_set(dev, MT_WF_PHY_RX_CTRL1(ext_phy), - FIELD_PREP(MT_WF_PHY_RX_CTRL1_IPI_EN, 0x5)); + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RX_CTRL1(ext_phy) : + MT_WF_PHY_RX_CTRL1_MT7916(ext_phy); + mt76_set(dev, reg, FIELD_PREP(MT_WF_PHY_RX_CTRL1_IPI_EN, 0x5)); } static u8 @@ -1810,7 +1862,9 @@ mt7915_phy_get_nf(struct mt7915_phy *phy, int idx) int nss, i; for (nss = 0; nss < hweight8(phy->mt76->chainmask); nss++) { - u32 reg = MT_WF_IRPI(nss + (idx << dev->dbdc_support)); + u32 reg = is_mt7915(&dev->mt76) ? + MT_WF_IRPI_NSS(0, nss + (idx << dev->dbdc_support)) : + MT_WF_IRPI_NSS_MT7916(idx, nss); for (i = 0; i < ARRAY_SIZE(nf_power); i++, reg += 4) { val = mt76_rr(dev, reg); @@ -1829,12 +1883,11 @@ void mt7915_update_channel(struct mt76_phy *mphy) { struct mt7915_phy *phy = (struct mt7915_phy *)mphy->priv; struct mt76_channel_state *state = mphy->chan_state; - bool ext_phy = phy != &phy->dev->phy; int nf; mt7915_mcu_get_chan_mib_info(phy, false); - nf = mt7915_phy_get_nf(phy, ext_phy); + nf = mt7915_phy_get_nf(phy, phy->band_idx); if (!phy->noise) phy->noise = nf << 4; else if (nf) @@ -1891,20 +1944,26 @@ static void mt7915_dma_reset(struct mt7915_dev *dev) { struct mt76_phy *mphy_ext = dev->mt76.phy2; - u32 hif1_ofs = MT_WFDMA1_PCIE1_BASE - MT_WFDMA1_BASE; + u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); int i; mt76_clear(dev, MT_WFDMA0_GLO_CFG, - MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); - mt76_clear(dev, MT_WFDMA1_GLO_CFG, - MT_WFDMA1_GLO_CFG_TX_DMA_EN | MT_WFDMA1_GLO_CFG_RX_DMA_EN); + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + if (is_mt7915(&dev->mt76)) + mt76_clear(dev, MT_WFDMA1_GLO_CFG, + MT_WFDMA1_GLO_CFG_TX_DMA_EN | + MT_WFDMA1_GLO_CFG_RX_DMA_EN); if (dev->hif2) { mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, - (MT_WFDMA0_GLO_CFG_TX_DMA_EN | - MT_WFDMA0_GLO_CFG_RX_DMA_EN)); - mt76_clear(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, - (MT_WFDMA1_GLO_CFG_TX_DMA_EN | - MT_WFDMA1_GLO_CFG_RX_DMA_EN)); + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + if (is_mt7915(&dev->mt76)) + mt76_clear(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, + MT_WFDMA1_GLO_CFG_TX_DMA_EN | + MT_WFDMA1_GLO_CFG_RX_DMA_EN); } usleep_range(1000, 2000); @@ -1928,19 +1987,23 @@ mt7915_dma_reset(struct mt7915_dev *dev) mt76_set(dev, MT_WFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); - mt76_set(dev, MT_WFDMA1_GLO_CFG, - MT_WFDMA1_GLO_CFG_TX_DMA_EN | MT_WFDMA1_GLO_CFG_RX_DMA_EN | - MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | - MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); - if (dev->hif2) { - mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, - (MT_WFDMA0_GLO_CFG_TX_DMA_EN | - MT_WFDMA0_GLO_CFG_RX_DMA_EN)); - mt76_set(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, - (MT_WFDMA1_GLO_CFG_TX_DMA_EN | + if (is_mt7915(&dev->mt76)) + mt76_set(dev, MT_WFDMA1_GLO_CFG, + MT_WFDMA1_GLO_CFG_TX_DMA_EN | MT_WFDMA1_GLO_CFG_RX_DMA_EN | MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | - MT_WFDMA1_GLO_CFG_OMIT_RX_INFO)); + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); + if (dev->hif2) { + mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + if (is_mt7915(&dev->mt76)) + mt76_set(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, + MT_WFDMA1_GLO_CFG_TX_DMA_EN | + MT_WFDMA1_GLO_CFG_RX_DMA_EN | + MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); } } @@ -2050,106 +2113,96 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; struct mib_stats *mib = &phy->mib; - bool ext_phy = phy != &dev->phy; int i, aggr0, aggr1, cnt; + u32 val; - mib->fcs_err_cnt += mt76_get_field(dev, MT_MIB_SDR3(ext_phy), - MT_MIB_SDR3_FCS_ERR_MASK); + cnt = mt76_rr(dev, MT_MIB_SDR3(phy->band_idx)); + mib->fcs_err_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK, cnt) : + FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR4(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR4(phy->band_idx)); mib->rx_fifo_full_cnt += FIELD_GET(MT_MIB_SDR4_RX_FIFO_FULL_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR5(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR5(phy->band_idx)); mib->rx_mpdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR6(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR6(phy->band_idx)); mib->channel_idle_cnt += FIELD_GET(MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR7(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR7(phy->band_idx)); mib->rx_vector_mismatch_cnt += FIELD_GET(MT_MIB_SDR7_RX_VECTOR_MISMATCH_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR8(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR8(phy->band_idx)); mib->rx_delimiter_fail_cnt += FIELD_GET(MT_MIB_SDR8_RX_DELIMITER_FAIL_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR11(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR11(phy->band_idx)); mib->rx_len_mismatch_cnt += FIELD_GET(MT_MIB_SDR11_RX_LEN_MISMATCH_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR12(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR12(phy->band_idx)); mib->tx_ampdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR13(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR13(phy->band_idx)); mib->tx_stop_q_empty_cnt += FIELD_GET(MT_MIB_SDR13_TX_STOP_Q_EMPTY_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR14(ext_phy)); - mib->tx_mpdu_attempts_cnt += FIELD_GET(MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK, cnt); + cnt = mt76_rr(dev, MT_MIB_SDR14(phy->band_idx)); + mib->tx_mpdu_attempts_cnt += is_mt7915(&dev->mt76) ? + FIELD_GET(MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK, cnt) : + FIELD_GET(MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR15(ext_phy)); - mib->tx_mpdu_success_cnt += FIELD_GET(MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK, cnt); + cnt = mt76_rr(dev, MT_MIB_SDR15(phy->band_idx)); + mib->tx_mpdu_success_cnt += is_mt7915(&dev->mt76) ? + FIELD_GET(MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK, cnt) : + FIELD_GET(MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR22(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR22(phy->band_idx)); mib->rx_ampdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR23(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR23(phy->band_idx)); mib->rx_ampdu_bytes_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR24(ext_phy)); - mib->rx_ampdu_valid_subframe_cnt += FIELD_GET(MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK, cnt); + cnt = mt76_rr(dev, MT_MIB_SDR24(phy->band_idx)); + mib->rx_ampdu_valid_subframe_cnt += is_mt7915(&dev->mt76) ? + FIELD_GET(MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK, cnt) : + FIELD_GET(MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR25(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR25(phy->band_idx)); mib->rx_ampdu_valid_subframe_bytes_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR27(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR27(phy->band_idx)); mib->tx_rwp_fail_cnt += FIELD_GET(MT_MIB_SDR27_TX_RWP_FAIL_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR28(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR28(phy->band_idx)); mib->tx_rwp_need_cnt += FIELD_GET(MT_MIB_SDR28_TX_RWP_NEED_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR29(ext_phy)); - mib->rx_pfdrop_cnt += FIELD_GET(MT_MIB_SDR29_RX_PFDROP_CNT_MASK, cnt); + cnt = mt76_rr(dev, MT_MIB_SDR29(phy->band_idx)); + mib->rx_pfdrop_cnt += is_mt7915(&dev->mt76) ? + FIELD_GET(MT_MIB_SDR29_RX_PFDROP_CNT_MASK, cnt) : + FIELD_GET(MT_MIB_SDR29_RX_PFDROP_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR30(ext_phy)); - mib->rx_vec_queue_overflow_drop_cnt += - FIELD_GET(MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK, cnt); + cnt = mt76_rr(dev, MT_MIB_SDRVEC(phy->band_idx)); + mib->rx_vec_queue_overflow_drop_cnt += is_mt7915(&dev->mt76) ? + FIELD_GET(MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK, cnt) : + FIELD_GET(MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR31(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDR31(phy->band_idx)); mib->rx_ba_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR32(ext_phy)); - mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT_MASK, cnt); - - cnt = mt76_rr(dev, MT_MIB_SDR33(ext_phy)); - mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR33_TX_PKT_IBF_CNT_MASK, cnt); - - cnt = mt76_rr(dev, MT_MIB_SDR34(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_SDRMUBF(phy->band_idx)); mib->tx_bf_cnt += FIELD_GET(MT_MIB_MU_BF_TX_CNT, cnt); - cnt = mt76_rr(dev, MT_MIB_DR8(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_DR8(phy->band_idx)); mib->tx_mu_mpdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_DR9(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_DR9(phy->band_idx)); mib->tx_mu_acked_mpdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_DR11(ext_phy)); + cnt = mt76_rr(dev, MT_MIB_DR11(phy->band_idx)); mib->tx_su_acked_mpdu_cnt += cnt; - cnt = mt76_rr(dev, MT_ETBF_TX_APP_CNT(ext_phy)); - mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_IBF_CNT, cnt); - mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_EBF_CNT, cnt); - - cnt = mt76_rr(dev, MT_ETBF_RX_FB_CNT(ext_phy)); - mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_ETBF_RX_FB_ALL, cnt); - mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_ETBF_RX_FB_HE, cnt); - mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_ETBF_RX_FB_VHT, cnt); - mib->tx_bf_rx_fb_ht_cnt += FIELD_GET(MT_ETBF_RX_FB_HT, cnt); - - cnt = mt76_rr(dev, MT_ETBF_RX_FB_CONT(ext_phy)); - mib->tx_bf_rx_fb_bw = FIELD_GET(MT_ETBF_RX_FB_BW, cnt); - mib->tx_bf_rx_fb_nc_cnt += FIELD_GET(MT_ETBF_RX_FB_NC, cnt); - mib->tx_bf_rx_fb_nr_cnt += FIELD_GET(MT_ETBF_RX_FB_NR, cnt); - - cnt = mt76_rr(dev, MT_ETBF_TX_NDP_BFRP(ext_phy)); - mib->tx_bf_fb_cpl_cnt += FIELD_GET(MT_ETBF_TX_FB_CPL, cnt); - mib->tx_bf_fb_trig_cnt += FIELD_GET(MT_ETBF_TX_FB_TRI, cnt); + cnt = mt76_rr(dev, MT_ETBF_PAR_RPT0(phy->band_idx)); + mib->tx_bf_rx_fb_bw = FIELD_GET(MT_ETBF_PAR_RPT0_FB_BW, cnt); + mib->tx_bf_rx_fb_nc_cnt += FIELD_GET(MT_ETBF_PAR_RPT0_FB_NC, cnt); + mib->tx_bf_rx_fb_nr_cnt += FIELD_GET(MT_ETBF_PAR_RPT0_FB_NR, cnt); for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) { cnt = mt76_rr(dev, MT_PLE_AMSDU_PACK_MSDU_CNT(i)); @@ -2157,27 +2210,97 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) mib->tx_amsdu_cnt += cnt; } - aggr0 = ext_phy ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; - for (i = 0, aggr1 = aggr0 + 4; i < 4; i++) { - u32 val; + aggr0 = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; + if (is_mt7915(&dev->mt76)) { + for (i = 0, aggr1 = aggr0 + 4; i < 4; i++) { + val = mt76_rr(dev, MT_MIB_MB_SDR1(phy->band_idx, (i << 4))); + mib->ba_miss_cnt += FIELD_GET(MT_MIB_BA_MISS_COUNT_MASK, val); + mib->ack_fail_cnt += + FIELD_GET(MT_MIB_ACK_FAIL_COUNT_MASK, val); - val = mt76_rr(dev, MT_MIB_MB_SDR1(ext_phy, i)); - mib->ba_miss_cnt += FIELD_GET(MT_MIB_BA_MISS_COUNT_MASK, val); - mib->ack_fail_cnt += - FIELD_GET(MT_MIB_ACK_FAIL_COUNT_MASK, val); + val = mt76_rr(dev, MT_MIB_MB_SDR0(phy->band_idx, (i << 4))); + mib->rts_cnt += FIELD_GET(MT_MIB_RTS_COUNT_MASK, val); + mib->rts_retries_cnt += + FIELD_GET(MT_MIB_RTS_RETRIES_COUNT_MASK, val); - val = mt76_rr(dev, MT_MIB_MB_SDR0(ext_phy, i)); - mib->rts_cnt += FIELD_GET(MT_MIB_RTS_COUNT_MASK, val); - mib->rts_retries_cnt += - FIELD_GET(MT_MIB_RTS_RETRIES_COUNT_MASK, val); + val = mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); + dev->mt76.aggr_stats[aggr0++] += val & 0xffff; + dev->mt76.aggr_stats[aggr0++] += val >> 16; - val = mt76_rr(dev, MT_TX_AGG_CNT(ext_phy, i)); - dev->mt76.aggr_stats[aggr0++] += val & 0xffff; - dev->mt76.aggr_stats[aggr0++] += val >> 16; + val = mt76_rr(dev, MT_TX_AGG_CNT2(phy->band_idx, i)); + dev->mt76.aggr_stats[aggr1++] += val & 0xffff; + dev->mt76.aggr_stats[aggr1++] += val >> 16; + } - val = mt76_rr(dev, MT_TX_AGG_CNT2(ext_phy, i)); - dev->mt76.aggr_stats[aggr1++] += val & 0xffff; - dev->mt76.aggr_stats[aggr1++] += val >> 16; + cnt = mt76_rr(dev, MT_MIB_SDR32(phy->band_idx)); + mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR33(phy->band_idx)); + mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR33_TX_PKT_IBF_CNT, cnt); + + cnt = mt76_rr(dev, MT_ETBF_TX_APP_CNT(phy->band_idx)); + mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_IBF_CNT, cnt); + mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_EBF_CNT, cnt); + + cnt = mt76_rr(dev, MT_ETBF_TX_NDP_BFRP(phy->band_idx)); + mib->tx_bf_fb_cpl_cnt += FIELD_GET(MT_ETBF_TX_FB_CPL, cnt); + mib->tx_bf_fb_trig_cnt += FIELD_GET(MT_ETBF_TX_FB_TRI, cnt); + + cnt = mt76_rr(dev, MT_ETBF_RX_FB_CNT(phy->band_idx)); + mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_ETBF_RX_FB_ALL, cnt); + mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_ETBF_RX_FB_HE, cnt); + mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_ETBF_RX_FB_VHT, cnt); + mib->tx_bf_rx_fb_ht_cnt += FIELD_GET(MT_ETBF_RX_FB_HT, cnt); + } else { + for (i = 0; i < 2; i++) { + /* rts count */ + val = mt76_rr(dev, MT_MIB_MB_SDR0(phy->band_idx, (i << 2))); + mib->rts_cnt += FIELD_GET(GENMASK(15, 0), val); + mib->rts_cnt += FIELD_GET(GENMASK(31, 16), val); + + /* rts retry count */ + val = mt76_rr(dev, MT_MIB_MB_SDR1(phy->band_idx, (i << 2))); + mib->rts_retries_cnt += FIELD_GET(GENMASK(15, 0), val); + mib->rts_retries_cnt += FIELD_GET(GENMASK(31, 16), val); + + /* ba miss count */ + val = mt76_rr(dev, MT_MIB_MB_SDR2(phy->band_idx, (i << 2))); + mib->ba_miss_cnt += FIELD_GET(GENMASK(15, 0), val); + mib->ba_miss_cnt += FIELD_GET(GENMASK(31, 16), val); + + /* ack fail count */ + val = mt76_rr(dev, MT_MIB_MB_BFTF(phy->band_idx, (i << 2))); + mib->ack_fail_cnt += FIELD_GET(GENMASK(15, 0), val); + mib->ack_fail_cnt += FIELD_GET(GENMASK(31, 16), val); + } + + for (i = 0; i < 8; i++) { + val = mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); + dev->mt76.aggr_stats[aggr0++] += FIELD_GET(GENMASK(15, 0), val); + dev->mt76.aggr_stats[aggr0++] += FIELD_GET(GENMASK(31, 16), val); + } + + cnt = mt76_rr(dev, MT_MIB_SDR32(phy->band_idx)); + mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_IBF_CNT, cnt); + mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_IBF_CNT, cnt); + mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt); + mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt); + + cnt = mt76_rr(dev, MT_MIB_BFCR7(phy->band_idx)); + mib->tx_bf_fb_cpl_cnt += FIELD_GET(MT_MIB_BFCR7_BFEE_TX_FB_CPL, cnt); + + cnt = mt76_rr(dev, MT_MIB_BFCR2(phy->band_idx)); + mib->tx_bf_fb_trig_cnt += FIELD_GET(MT_MIB_BFCR2_BFEE_TX_FB_TRIG, cnt); + + cnt = mt76_rr(dev, MT_MIB_BFCR0(phy->band_idx)); + mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_VHT, cnt); + mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_VHT, cnt); + mib->tx_bf_rx_fb_ht_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_HT, cnt); + mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_HT, cnt); + + cnt = mt76_rr(dev, MT_MIB_BFCR1(phy->band_idx)); + mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_MIB_BFCR1_RX_FB_HE, cnt); + mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR1_RX_FB_HE, cnt); } } @@ -2248,39 +2371,59 @@ static void mt7915_dfs_stop_radar_detector(struct mt7915_phy *phy) struct mt7915_dev *dev = phy->dev; if (phy->rdd_state & BIT(0)) - mt7915_mcu_rdd_cmd(dev, RDD_STOP, 0, MT_RX_SEL0, 0); + mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, 0, + MT_RX_SEL0, 0); if (phy->rdd_state & BIT(1)) - mt7915_mcu_rdd_cmd(dev, RDD_STOP, 1, MT_RX_SEL0, 0); + mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, 1, + MT_RX_SEL0, 0); } static int mt7915_dfs_start_rdd(struct mt7915_dev *dev, int chain) { - int err; + int err, region; - err = mt7915_mcu_rdd_cmd(dev, RDD_START, chain, MT_RX_SEL0, 0); + switch (dev->mt76.region) { + case NL80211_DFS_ETSI: + region = 0; + break; + case NL80211_DFS_JP: + region = 2; + break; + case NL80211_DFS_FCC: + default: + region = 1; + break; + } + + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, chain, + MT_RX_SEL0, region); if (err < 0) return err; - return mt7915_mcu_rdd_cmd(dev, RDD_DET_MODE, chain, MT_RX_SEL0, 1); + return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_DET_MODE, chain, + MT_RX_SEL0, 1); } static int mt7915_dfs_start_radar_detector(struct mt7915_phy *phy) { struct cfg80211_chan_def *chandef = &phy->mt76->chandef; struct mt7915_dev *dev = phy->dev; - bool ext_phy = phy != &dev->phy; int err; /* start CAC */ - err = mt7915_mcu_rdd_cmd(dev, RDD_CAC_START, ext_phy, MT_RX_SEL0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START, phy->band_idx, + MT_RX_SEL0, 0); if (err < 0) return err; - err = mt7915_dfs_start_rdd(dev, ext_phy); + err = mt7915_dfs_start_rdd(dev, phy->band_idx); if (err < 0) return err; - phy->rdd_state |= BIT(ext_phy); + phy->rdd_state |= BIT(phy->band_idx); + + if (!is_mt7915(&dev->mt76)) + return 0; if (chandef->width == NL80211_CHAN_WIDTH_160 || chandef->width == NL80211_CHAN_WIDTH_80P80) { @@ -2330,48 +2473,56 @@ mt7915_dfs_init_radar_specs(struct mt7915_phy *phy) int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy) { - struct cfg80211_chan_def *chandef = &phy->mt76->chandef; struct mt7915_dev *dev = phy->dev; - bool ext_phy = phy != &dev->phy; + enum mt76_dfs_state dfs_state, prev_state; int err; - if (dev->mt76.region == NL80211_DFS_UNSET) { - phy->dfs_state = -1; - if (phy->rdd_state) - goto stop; + prev_state = phy->mt76->dfs_state; + dfs_state = mt76_phy_dfs_state(phy->mt76); - return 0; - } - - if (test_bit(MT76_SCANNING, &phy->mt76->state)) + if (prev_state == dfs_state) return 0; - if (phy->dfs_state == chandef->chan->dfs_state) - return 0; + if (prev_state == MT_DFS_STATE_UNKNOWN) + mt7915_dfs_stop_radar_detector(phy); - err = mt7915_dfs_init_radar_specs(phy); - if (err < 0) { - phy->dfs_state = -1; + if (dfs_state == MT_DFS_STATE_DISABLED) goto stop; + + if (prev_state <= MT_DFS_STATE_DISABLED) { + err = mt7915_dfs_init_radar_specs(phy); + if (err < 0) + return err; + + err = mt7915_dfs_start_radar_detector(phy); + if (err < 0) + return err; + + phy->mt76->dfs_state = MT_DFS_STATE_CAC; } - phy->dfs_state = chandef->chan->dfs_state; + if (dfs_state == MT_DFS_STATE_CAC) + return 0; - if (chandef->chan->flags & IEEE80211_CHAN_RADAR) { - if (chandef->chan->dfs_state != NL80211_DFS_AVAILABLE) - return mt7915_dfs_start_radar_detector(phy); - - return mt7915_mcu_rdd_cmd(dev, RDD_CAC_END, ext_phy, - MT_RX_SEL0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_END, + phy->band_idx, MT_RX_SEL0, 0); + if (err < 0) { + phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; + return err; } + phy->mt76->dfs_state = MT_DFS_STATE_ACTIVE; + return 0; + stop: - err = mt7915_mcu_rdd_cmd(dev, RDD_NORMAL_START, ext_phy, - MT_RX_SEL0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_NORMAL_START, + phy->band_idx, MT_RX_SEL0, 0); if (err < 0) return err; mt7915_dfs_stop_radar_detector(phy); + phy->mt76->dfs_state = MT_DFS_STATE_DISABLED; + return 0; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h index 7a2c740d1464..5add1dd36dbe 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h @@ -23,6 +23,7 @@ enum rx_pkt_type { PKT_TYPE_RETRIEVE, PKT_TYPE_TXRX_NOTIFY, PKT_TYPE_RX_EVENT, + PKT_TYPE_RX_FW_MONITOR = 0x0c, }; /* RXD DW1 */ @@ -125,6 +126,12 @@ enum rx_pkt_type { #define MT_PRXV_RCPI2 GENMASK(23, 16) #define MT_PRXV_RCPI1 GENMASK(15, 8) #define MT_PRXV_RCPI0 GENMASK(7, 0) +#define MT_PRXV_HT_SHORT_GI GENMASK(16, 15) +#define MT_PRXV_HT_STBC GENMASK(23, 22) +#define MT_PRXV_TX_MODE GENMASK(27, 24) +#define MT_PRXV_FRAME_MODE GENMASK(14, 12) +#define MT_PRXV_DCM BIT(17) +#define MT_PRXV_NUM_RX BIT(20, 18) /* C-RXV */ #define MT_CRXV_HT_STBC GENMASK(1, 0) @@ -298,18 +305,20 @@ struct mt7915_txp { struct mt7915_tx_free { __le16 rx_byte_cnt; __le16 ctrl; - u8 txd_cnt; - u8 rsv[3]; + __le32 txd; __le32 info[]; } __packed __aligned(4); +#define MT_TX_FREE_VER GENMASK(18, 16) #define MT_TX_FREE_MSDU_CNT GENMASK(9, 0) #define MT_TX_FREE_WLAN_ID GENMASK(23, 14) #define MT_TX_FREE_LATENCY GENMASK(12, 0) /* 0: success, others: dropped */ -#define MT_TX_FREE_STATUS GENMASK(14, 13) #define MT_TX_FREE_MSDU_ID GENMASK(30, 16) #define MT_TX_FREE_PAIR BIT(31) +#define MT_TX_FREE_MPDU_HEADER BIT(30) +#define MT_TX_FREE_MSDU_ID_V3 GENMASK(14, 0) + /* will support this field in further revision */ #define MT_TX_FREE_RATE GENMASK(13, 0) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 8ac6f59af174..c3f44d801e7f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -34,7 +34,7 @@ static int mt7915_start(struct ieee80211_hw *hw) running = mt7915_dev_running(dev); if (!running) { - ret = mt7915_mcu_set_pm(dev, 0, 0); + ret = mt76_connac_mcu_set_pm(&dev->mt76, 0, 0); if (ret) goto out; @@ -49,8 +49,8 @@ static int mt7915_start(struct ieee80211_hw *hw) mt7915_mac_enable_nf(dev, 0); } - if (phy != &dev->phy) { - ret = mt7915_mcu_set_pm(dev, 1, 0); + if (phy != &dev->phy || phy->band_idx) { + ret = mt76_connac_mcu_set_pm(&dev->mt76, 1, 0); if (ret) goto out; @@ -65,7 +65,8 @@ static int mt7915_start(struct ieee80211_hw *hw) mt7915_mac_enable_nf(dev, 1); } - ret = mt7915_mcu_set_rts_thresh(phy, 0x92b); + ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b, + phy != &dev->phy); if (ret) goto out; @@ -106,12 +107,12 @@ static void mt7915_stop(struct ieee80211_hw *hw) clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); if (phy != &dev->phy) { - mt7915_mcu_set_pm(dev, 1, 1); + mt76_connac_mcu_set_pm(&dev->mt76, 1, 1); mt7915_mcu_set_mac(dev, 1, false, false); } if (!mt7915_dev_running(dev)) { - mt7915_mcu_set_pm(dev, 0, 1); + mt76_connac_mcu_set_pm(&dev->mt76, 0, 1); mt7915_mcu_set_mac(dev, 0, false, false); } @@ -216,7 +217,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw, } mvif->mt76.omac_idx = idx; mvif->phy = phy; - mvif->mt76.band_idx = ext_phy; + mvif->mt76.band_idx = phy->band_idx; mvif->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP; if (ext_phy) @@ -234,7 +235,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw, INIT_LIST_HEAD(&mvif->sta.rc_list); INIT_LIST_HEAD(&mvif->sta.poll_list); mvif->sta.wcid.idx = idx; - mvif->sta.wcid.ext_phy = mvif->mt76.band_idx; + mvif->sta.wcid.ext_phy = ext_phy; mvif->sta.wcid.hw_key_idx = -1; mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET; mt76_packet_id_init(&mvif->sta.wcid); @@ -256,6 +257,9 @@ static int mt7915_add_interface(struct ieee80211_hw *hw, mt7915_init_bitrate_mask(vif); memset(&mvif->cap, -1, sizeof(mvif->cap)); + mt7915_mcu_add_bss_info(phy, vif, true); + mt7915_mcu_add_sta(dev, vif, NULL, true); + out: mutex_unlock(&dev->mt76.mutex); @@ -298,25 +302,6 @@ static void mt7915_remove_interface(struct ieee80211_hw *hw, mt76_packet_id_flush(&dev->mt76, &msta->wcid); } -static void mt7915_init_dfs_state(struct mt7915_phy *phy) -{ - struct mt76_phy *mphy = phy->mt76; - struct ieee80211_hw *hw = mphy->hw; - struct cfg80211_chan_def *chandef = &hw->conf.chandef; - - if (hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) - return; - - if (!(chandef->chan->flags & IEEE80211_CHAN_RADAR)) - return; - - if (mphy->chandef.chan->center_freq == chandef->chan->center_freq && - mphy->chandef.width == chandef->width) - return; - - phy->dfs_state = -1; -} - int mt7915_set_channel(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; @@ -327,7 +312,6 @@ int mt7915_set_channel(struct mt7915_phy *phy) mutex_lock(&dev->mt76.mutex); set_bit(MT76_RESET, &phy->mt76->state); - mt7915_init_dfs_state(phy); mt76_set_channel(phy->mt76); if (dev->flash_mode) { @@ -366,6 +350,7 @@ static int mt7915_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_key_conf *key) { struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_sta *msta = sta ? (struct mt7915_sta *)sta->drv_priv : &mvif->sta; @@ -405,6 +390,11 @@ static int mt7915_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, mutex_lock(&dev->mt76.mutex); + if (cmd == SET_KEY && !sta && !mvif->mt76.cipher) { + mvif->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher); + mt7915_mcu_add_bss_info(phy, vif, true); + } + if (cmd == SET_KEY) *wcid_keyidx = idx; else if (idx == *wcid_keyidx) @@ -415,8 +405,9 @@ static int mt7915_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, mt76_wcid_key_setup(&dev->mt76, wcid, cmd == SET_KEY ? key : NULL); - err = mt7915_mcu_add_key(dev, vif, msta, key, cmd); - + err = mt76_connac_mcu_add_key(&dev->mt76, vif, &msta->bip, + key, MCU_EXT_CMD(STA_REC_UPDATE), + &msta->wcid, cmd); out: mutex_unlock(&dev->mt76.mutex); @@ -498,11 +489,10 @@ static int mt7915_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { - struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; /* no need to update right away, we'll get BSS_CHANGED_QOS */ - queue = mt7915_lmac_mapping(dev, queue); + queue = mt76_connac_lmac_mapping(queue); mvif->queue_params[queue] = *params; return 0; @@ -664,6 +654,7 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + bool ext_phy = mvif->phy != &dev->phy; int ret, idx; idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7915_WTBL_STA); @@ -675,7 +666,7 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, msta->vif = mvif; msta->wcid.sta = 1; msta->wcid.idx = idx; - msta->wcid.ext_phy = mvif->mt76.band_idx; + msta->wcid.ext_phy = ext_phy; msta->wcid.tx_info |= MT_WCID_TX_INFO_SET; msta->jiffies = jiffies; @@ -746,7 +737,7 @@ static int mt7915_set_rts_threshold(struct ieee80211_hw *hw, u32 val) int ret; mutex_lock(&dev->mt76.mutex); - ret = mt7915_mcu_set_rts_thresh(phy, val); + ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, phy != &dev->phy); mutex_unlock(&dev->mt76.mutex); return ret; @@ -861,8 +852,12 @@ u64 __mt7915_get_tsf(struct ieee80211_hw *hw, struct mt7915_vif *mvif) n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 : mvif->mt76.omac_idx; /* TSF software read */ - mt76_rmw(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE, - MT_LPON_TCR_SW_READ); + if (is_mt7915(&dev->mt76)) + mt76_rmw(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_READ); + else + mt76_rmw(dev, MT_LPON_TCR_MT7916(band, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_READ); tsf.t32[0] = mt76_rr(dev, MT_LPON_UTTR0(band)); tsf.t32[1] = mt76_rr(dev, MT_LPON_UTTR1(band)); @@ -904,8 +899,12 @@ mt7915_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt76_wr(dev, MT_LPON_UTTR0(band), tsf.t32[0]); mt76_wr(dev, MT_LPON_UTTR1(band), tsf.t32[1]); /* TSF software overwrite */ - mt76_rmw(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE, - MT_LPON_TCR_SW_WRITE); + if (is_mt7915(&dev->mt76)) + mt76_rmw(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_WRITE); + else + mt76_rmw(dev, MT_LPON_TCR_MT7916(band, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_WRITE); mutex_unlock(&dev->mt76.mutex); } @@ -931,8 +930,12 @@ mt7915_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt76_wr(dev, MT_LPON_UTTR0(band), tsf.t32[0]); mt76_wr(dev, MT_LPON_UTTR1(band), tsf.t32[1]); /* TSF software adjust*/ - mt76_rmw(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE, - MT_LPON_TCR_SW_ADJUST); + if (is_mt7915(&dev->mt76)) + mt76_rmw(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_ADJUST); + else + mt76_rmw(dev, MT_LPON_TCR_MT7916(band, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_ADJUST); mutex_unlock(&dev->mt76.mutex); } @@ -967,12 +970,9 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) phy->mt76->antenna_mask = tx_ant; - if (ext_phy) { - if (dev->chainmask == 0xf) - tx_ant <<= 2; - else - tx_ant <<= 1; - } + if (ext_phy) + tx_ant <<= dev->chainshift; + phy->mt76->chainmask = tx_ant; mt76_set_stream_caps(phy->mt76, true); @@ -994,7 +994,8 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw, struct rate_info *txrate = &msta->wcid.rate; struct rate_info rxrate = {}; - if (!mt7915_mcu_get_rx_rate(phy, vif, sta, &rxrate)) { + if (is_mt7915(&phy->dev->mt76) && + !mt7915_mcu_get_rx_rate(phy, vif, sta, &rxrate)) { sinfo->rxrate = rxrate; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE); } @@ -1079,7 +1080,7 @@ static void mt7915_sta_set_4addr(struct ieee80211_hw *hw, else clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags); - mt7915_mcu_sta_update_hdr_trans(dev, vif, sta); + mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta); } static void mt7915_sta_set_decap_offload(struct ieee80211_hw *hw, @@ -1095,7 +1096,7 @@ static void mt7915_sta_set_decap_offload(struct ieee80211_hw *hw, else clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); - mt7915_mcu_sta_update_hdr_trans(dev, vif, sta); + mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta); } static const char mt7915_gstrings_stats[][ETH_GSTRING_LEN] = { @@ -1238,7 +1239,6 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw, }; struct mib_stats *mib = &phy->mib; /* See mt7915_ampdu_stat_read_phy, etc */ - bool ext_phy = phy != &dev->phy; int i, n, ei = 0; mutex_lock(&dev->mt76.mutex); @@ -1255,7 +1255,7 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw, data[ei++] = mib->tx_pkt_ibf_cnt; /* Tx ampdu stat */ - n = ext_phy ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; + n = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; for (i = 0; i < 15 /*ARRAY_SIZE(bound)*/; i++) data[ei++] = dev->mt76.aggr_stats[i + n]; @@ -1332,6 +1332,55 @@ mt7915_twt_teardown_request(struct ieee80211_hw *hw, mutex_unlock(&dev->mt76.mutex); } +static int +mt7915_set_radar_background(struct ieee80211_hw *hw, + struct cfg80211_chan_def *chandef) +{ + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_dev *dev = phy->dev; + int ret = -EINVAL; + bool running; + + mutex_lock(&dev->mt76.mutex); + + if (dev->mt76.region == NL80211_DFS_UNSET) + goto out; + + if (dev->rdd2_phy && dev->rdd2_phy != phy) { + /* rdd2 is already locked */ + ret = -EBUSY; + goto out; + } + + /* rdd2 already configured on a radar channel */ + running = dev->rdd2_phy && + cfg80211_chandef_valid(&dev->rdd2_chandef) && + !!(dev->rdd2_chandef.chan->flags & IEEE80211_CHAN_RADAR); + + if (!chandef || running || + !(chandef->chan->flags & IEEE80211_CHAN_RADAR)) { + ret = mt7915_mcu_rdd_background_enable(phy, NULL); + if (ret) + goto out; + + if (!running) + goto update_phy; + } + + ret = mt7915_mcu_rdd_background_enable(phy, chandef); + if (ret) + goto out; + +update_phy: + dev->rdd2_phy = chandef ? phy : NULL; + if (chandef) + dev->rdd2_chandef = *chandef; +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + const struct ieee80211_ops mt7915_ops = { .tx = mt7915_tx, .start = mt7915_start, @@ -1378,4 +1427,5 @@ const struct ieee80211_ops mt7915_ops = { #ifdef CONFIG_MAC80211_DEBUGFS .sta_add_debugfs = mt7915_sta_add_debugfs, #endif + .set_radar_background = mt7915_set_radar_background, }; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 0911b6f973b5..e7a6f80e7755 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -64,136 +64,31 @@ struct mt7915_fw_region { u8 reserved1[15]; } __packed; +#define fw_name(_dev, name, ...) ({ \ + char *_fw; \ + switch (mt76_chip(&(_dev)->mt76)) { \ + case 0x7915: \ + _fw = MT7915_##name; \ + break; \ + case 0x7986: \ + _fw = MT7986_##name##__VA_ARGS__; \ + break; \ + default: \ + _fw = MT7916_##name; \ + break; \ + } \ + _fw; \ +}) + +#define fw_name_var(_dev, name) (mt7915_check_adie(dev, false) ? \ + fw_name(_dev, name) : \ + fw_name(_dev, name, _MT7975)) + #define MCU_PATCH_ADDRESS 0x200000 -#define FW_FEATURE_SET_ENCRYPT BIT(0) -#define FW_FEATURE_SET_KEY_IDX GENMASK(2, 1) -#define FW_FEATURE_OVERRIDE_ADDR BIT(5) - -#define DL_MODE_ENCRYPT BIT(0) -#define DL_MODE_KEY_IDX GENMASK(2, 1) -#define DL_MODE_RESET_SEC_IV BIT(3) -#define DL_MODE_WORKING_PDA_CR4 BIT(4) -#define DL_MODE_NEED_RSP BIT(31) - -#define FW_START_OVERRIDE BIT(0) -#define FW_START_WORKING_PDA_CR4 BIT(2) - -#define PATCH_SEC_TYPE_MASK GENMASK(15, 0) -#define PATCH_SEC_TYPE_INFO 0x2 - -#define to_wcid_lo(id) FIELD_GET(GENMASK(7, 0), (u16)id) -#define to_wcid_hi(id) FIELD_GET(GENMASK(9, 8), (u16)id) - #define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p) #define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m) -static enum mcu_cipher_type -mt7915_mcu_get_cipher(int cipher) -{ - switch (cipher) { - case WLAN_CIPHER_SUITE_WEP40: - return MCU_CIPHER_WEP40; - case WLAN_CIPHER_SUITE_WEP104: - return MCU_CIPHER_WEP104; - case WLAN_CIPHER_SUITE_TKIP: - return MCU_CIPHER_TKIP; - case WLAN_CIPHER_SUITE_AES_CMAC: - return MCU_CIPHER_BIP_CMAC_128; - case WLAN_CIPHER_SUITE_CCMP: - return MCU_CIPHER_AES_CCMP; - case WLAN_CIPHER_SUITE_CCMP_256: - return MCU_CIPHER_CCMP_256; - case WLAN_CIPHER_SUITE_GCMP: - return MCU_CIPHER_GCMP; - case WLAN_CIPHER_SUITE_GCMP_256: - return MCU_CIPHER_GCMP_256; - case WLAN_CIPHER_SUITE_SMS4: - return MCU_CIPHER_WAPI; - default: - return MCU_CIPHER_NONE; - } -} - -static u8 mt7915_mcu_chan_bw(struct cfg80211_chan_def *chandef) -{ - static const u8 width_to_bw[] = { - [NL80211_CHAN_WIDTH_40] = CMD_CBW_40MHZ, - [NL80211_CHAN_WIDTH_80] = CMD_CBW_80MHZ, - [NL80211_CHAN_WIDTH_80P80] = CMD_CBW_8080MHZ, - [NL80211_CHAN_WIDTH_160] = CMD_CBW_160MHZ, - [NL80211_CHAN_WIDTH_5] = CMD_CBW_5MHZ, - [NL80211_CHAN_WIDTH_10] = CMD_CBW_10MHZ, - [NL80211_CHAN_WIDTH_20] = CMD_CBW_20MHZ, - [NL80211_CHAN_WIDTH_20_NOHT] = CMD_CBW_20MHZ, - }; - - if (chandef->width >= ARRAY_SIZE(width_to_bw)) - return 0; - - return width_to_bw[chandef->width]; -} - -static const struct ieee80211_sta_he_cap * -mt7915_get_he_phy_cap(struct mt7915_phy *phy, struct ieee80211_vif *vif) -{ - struct ieee80211_supported_band *sband; - enum nl80211_band band; - - band = phy->mt76->chandef.chan->band; - sband = phy->mt76->hw->wiphy->bands[band]; - - return ieee80211_get_he_iftype_cap(sband, vif->type); -} - -static u8 -mt7915_get_phy_mode(struct ieee80211_vif *vif, struct ieee80211_sta *sta) -{ - struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - enum nl80211_band band = mvif->phy->mt76->chandef.chan->band; - struct ieee80211_sta_ht_cap *ht_cap; - struct ieee80211_sta_vht_cap *vht_cap; - const struct ieee80211_sta_he_cap *he_cap; - u8 mode = 0; - - if (sta) { - ht_cap = &sta->ht_cap; - vht_cap = &sta->vht_cap; - he_cap = &sta->he_cap; - } else { - struct ieee80211_supported_band *sband; - - sband = mvif->phy->mt76->hw->wiphy->bands[band]; - - ht_cap = &sband->ht_cap; - vht_cap = &sband->vht_cap; - he_cap = ieee80211_get_he_iftype_cap(sband, vif->type); - } - - if (band == NL80211_BAND_2GHZ) { - mode |= PHY_MODE_B | PHY_MODE_G; - - if (ht_cap->ht_supported) - mode |= PHY_MODE_GN; - - if (he_cap && he_cap->has_he) - mode |= PHY_MODE_AX_24G; - } else if (band == NL80211_BAND_5GHZ) { - mode |= PHY_MODE_A; - - if (ht_cap->ht_supported) - mode |= PHY_MODE_AN; - - if (vht_cap->vht_supported) - mode |= PHY_MODE_AC; - - if (he_cap && he_cap->has_he) - mode |= PHY_MODE_AX_5G; - } - - return mode; -} - static u8 mt7915_mcu_get_sta_nss(u16 mcs_map) { @@ -211,24 +106,13 @@ mt7915_mcu_get_sta_nss(u16 mcs_map) static void mt7915_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs, - const u16 *mask) + u16 mcs_map) { struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; - struct cfg80211_chan_def *chandef = &msta->vif->phy->mt76->chandef; + struct mt7915_dev *dev = msta->vif->phy->dev; + enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band; + const u16 *mask = msta->vif->bitrate_mask.control[band].he_mcs; int nss, max_nss = sta->rx_nss > 3 ? 4 : sta->rx_nss; - u16 mcs_map; - - switch (chandef->width) { - case NL80211_CHAN_WIDTH_80P80: - mcs_map = le16_to_cpu(sta->he_cap.he_mcs_nss_supp.rx_mcs_80p80); - break; - case NL80211_CHAN_WIDTH_160: - mcs_map = le16_to_cpu(sta->he_cap.he_mcs_nss_supp.rx_mcs_160); - break; - default: - mcs_map = le16_to_cpu(sta->he_cap.he_mcs_nss_supp.rx_mcs_80); - break; - } for (nss = 0; nss < max_nss; nss++) { int mcs; @@ -266,8 +150,9 @@ mt7915_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs, mcs_map &= ~(0x3 << (nss * 2)); mcs_map |= mcs << (nss * 2); - /* only support 2ss on 160MHz */ - if (nss > 1 && (sta->bandwidth == IEEE80211_STA_RX_BW_160)) + /* only support 2ss on 160MHz for mt7915 */ + if (is_mt7915(&dev->mt76) && nss > 1 && + sta->bandwidth == IEEE80211_STA_RX_BW_160) break; } @@ -278,6 +163,8 @@ static void mt7915_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs, const u16 *mask) { + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_dev *dev = msta->vif->phy->dev; u16 mcs_map = le16_to_cpu(sta->vht_cap.vht_mcs.rx_mcs_map); int nss, max_nss = sta->rx_nss > 3 ? 4 : sta->rx_nss; u16 mcs; @@ -299,8 +186,9 @@ mt7915_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs, vht_mcs[nss] = cpu_to_le16(mcs & mask[nss]); - /* only support 2ss on 160MHz */ - if (nss > 1 && (sta->bandwidth == IEEE80211_STA_RX_BW_160)) + /* only support 2ss on 160MHz for mt7915 */ + if (is_mt7915(&dev->mt76) && nss > 1 && + sta->bandwidth == IEEE80211_STA_RX_BW_160) break; } } @@ -446,7 +334,7 @@ mt7915_mcu_rx_csa_notify(struct mt7915_dev *dev, struct sk_buff *skb) c = (struct mt7915_mcu_csa_notify *)skb->data; - if (c->band_idx && dev->mt76.phy2) + if ((c->band_idx && !dev->phy.band_idx) && dev->mt76.phy2) mphy = dev->mt76.phy2; ieee80211_iterate_active_interfaces_atomic(mphy->hw, @@ -465,7 +353,7 @@ mt7915_mcu_rx_thermal_notify(struct mt7915_dev *dev, struct sk_buff *skb) if (t->ctrl.ctrl_id != THERMAL_PROTECT_ENABLE) return; - if (t->ctrl.band_idx && dev->mt76.phy2) + if ((t->ctrl.band_idx && !dev->phy.band_idx) && dev->mt76.phy2) mphy = dev->mt76.phy2; phy = (struct mt7915_phy *)mphy->priv; @@ -480,10 +368,15 @@ mt7915_mcu_rx_radar_detected(struct mt7915_dev *dev, struct sk_buff *skb) r = (struct mt7915_mcu_rdd_report *)skb->data; - if (r->band_idx && dev->mt76.phy2) + if ((r->band_idx && !dev->phy.band_idx) && dev->mt76.phy2) mphy = dev->mt76.phy2; - ieee80211_radar_detected(mphy->hw); + if (r->band_idx == MT_RX_SEL2) + cfg80211_background_radar_event(mphy->hw->wiphy, + &dev->rdd2_chandef, + GFP_ATOMIC); + else + ieee80211_radar_detected(mphy->hw); dev->hw_pattern++; } @@ -493,9 +386,13 @@ mt7915_mcu_rx_log_message(struct mt7915_dev *dev, struct sk_buff *skb) struct mt7915_mcu_rxd *rxd = (struct mt7915_mcu_rxd *)skb->data; const char *data = (char *)&rxd[1]; const char *type; + int len = skb->len - sizeof(*rxd); switch (rxd->s2d_index) { case 0: + if (mt7915_debugfs_rx_log(dev, data, len)) + return; + type = "WM"; break; case 2: @@ -506,8 +403,7 @@ mt7915_mcu_rx_log_message(struct mt7915_dev *dev, struct sk_buff *skb) break; } - wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, - (int)(skb->len - sizeof(*rxd)), data); + wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, len, data); } static void @@ -519,6 +415,22 @@ mt7915_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) ieee80211_color_change_finish(vif); } +static void +mt7915_mcu_rx_bcc_notify(struct mt7915_dev *dev, struct sk_buff *skb) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7915_mcu_bcc_notify *b; + + b = (struct mt7915_mcu_bcc_notify *)skb->data; + + if ((b->band_idx && !dev->phy.band_idx) && dev->mt76.phy2) + mphy = dev->mt76.phy2; + + ieee80211_iterate_active_interfaces_atomic(mphy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7915_mcu_cca_finish, mphy->hw); +} + static void mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb) { @@ -538,9 +450,7 @@ mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb) mt7915_mcu_rx_log_message(dev, skb); break; case MCU_EXT_EVENT_BCC_NOTIFY: - ieee80211_iterate_active_interfaces_atomic(dev->mt76.hw, - IEEE80211_IFACE_ITER_RESUME_ALL, - mt7915_mcu_cca_finish, dev); + mt7915_mcu_rx_bcc_notify(dev, skb); break; default: break; @@ -577,88 +487,6 @@ void mt7915_mcu_rx_event(struct mt7915_dev *dev, struct sk_buff *skb) mt76_mcu_rx_event(&dev->mt76, skb); } -static struct sk_buff * -mt7915_mcu_alloc_sta_req(struct mt7915_dev *dev, struct mt7915_vif *mvif, - struct mt7915_sta *msta, int len) -{ - struct sta_req_hdr hdr = { - .bss_idx = mvif->mt76.idx, - .wlan_idx_lo = msta ? to_wcid_lo(msta->wcid.idx) : 0, - .wlan_idx_hi = msta ? to_wcid_hi(msta->wcid.idx) : 0, - .muar_idx = msta && msta->wcid.sta ? mvif->mt76.omac_idx : 0xe, - .is_tlv_append = 1, - }; - struct sk_buff *skb; - - skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); - if (!skb) - return ERR_PTR(-ENOMEM); - - skb_put_data(skb, &hdr, sizeof(hdr)); - - return skb; -} - -static struct wtbl_req_hdr * -mt7915_mcu_alloc_wtbl_req(struct mt7915_dev *dev, struct mt7915_sta *msta, - int cmd, void *sta_wtbl, struct sk_buff **skb) -{ - struct tlv *sta_hdr = sta_wtbl; - struct wtbl_req_hdr hdr = { - .wlan_idx_lo = to_wcid_lo(msta->wcid.idx), - .wlan_idx_hi = to_wcid_hi(msta->wcid.idx), - .operation = cmd, - }; - struct sk_buff *nskb = *skb; - - if (!nskb) { - nskb = mt76_mcu_msg_alloc(&dev->mt76, NULL, - MT76_CONNAC_WTBL_UPDATE_MAX_SIZE); - if (!nskb) - return ERR_PTR(-ENOMEM); - - *skb = nskb; - } - - if (sta_hdr) - le16_add_cpu(&sta_hdr->len, sizeof(hdr)); - - return skb_put_data(nskb, &hdr, sizeof(hdr)); -} - -static struct tlv * -mt7915_mcu_add_nested_tlv(struct sk_buff *skb, int tag, int len, - void *sta_ntlv, void *sta_wtbl) -{ - struct sta_ntlv_hdr *ntlv_hdr = sta_ntlv; - struct tlv *sta_hdr = sta_wtbl; - struct tlv *ptlv, tlv = { - .tag = cpu_to_le16(tag), - .len = cpu_to_le16(len), - }; - u16 ntlv; - - ptlv = skb_put(skb, len); - memcpy(ptlv, &tlv, sizeof(tlv)); - - ntlv = le16_to_cpu(ntlv_hdr->tlv_num); - ntlv_hdr->tlv_num = cpu_to_le16(ntlv + 1); - - if (sta_hdr) { - u16 size = le16_to_cpu(sta_hdr->len); - - sta_hdr->len = cpu_to_le16(size + len); - } - - return ptlv; -} - -static struct tlv * -mt7915_mcu_add_tlv(struct sk_buff *skb, int tag, int len) -{ - return mt7915_mcu_add_nested_tlv(skb, tag, len, skb->data, NULL); -} - static struct tlv * mt7915_mcu_add_nested_subtlv(struct sk_buff *skb, int sub_tag, int sub_len, __le16 *sub_ntlv, __le16 *len) @@ -678,105 +506,6 @@ mt7915_mcu_add_nested_subtlv(struct sk_buff *skb, int sub_tag, int sub_len, } /** bss info **/ -static int -mt7915_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, - struct mt7915_phy *phy, bool enable) -{ - struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - struct bss_info_basic *bss; - u16 wlan_idx = mvif->sta.wcid.idx; - u32 type = NETWORK_INFRA; - struct tlv *tlv; - - tlv = mt7915_mcu_add_tlv(skb, BSS_INFO_BASIC, sizeof(*bss)); - - switch (vif->type) { - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_MONITOR: - break; - case NL80211_IFTYPE_STATION: - /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */ - if (enable) { - struct ieee80211_sta *sta; - struct mt7915_sta *msta; - - rcu_read_lock(); - sta = ieee80211_find_sta(vif, vif->bss_conf.bssid); - if (!sta) { - rcu_read_unlock(); - return -EINVAL; - } - - msta = (struct mt7915_sta *)sta->drv_priv; - wlan_idx = msta->wcid.idx; - rcu_read_unlock(); - } - break; - case NL80211_IFTYPE_ADHOC: - type = NETWORK_IBSS; - break; - default: - WARN_ON(1); - break; - } - - bss = (struct bss_info_basic *)tlv; - bss->network_type = cpu_to_le32(type); - bss->bmc_wcid_lo = to_wcid_lo(wlan_idx); - bss->bmc_wcid_hi = to_wcid_hi(wlan_idx); - bss->wmm_idx = mvif->mt76.wmm_idx; - bss->active = enable; - - if (vif->type != NL80211_IFTYPE_MONITOR) { - memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN); - bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int); - bss->dtim_period = vif->bss_conf.dtim_period; - bss->phy_mode = mt7915_get_phy_mode(vif, NULL); - } else { - memcpy(bss->bssid, phy->mt76->macaddr, ETH_ALEN); - } - - return 0; -} - -static void -mt7915_mcu_bss_omac_tlv(struct sk_buff *skb, struct ieee80211_vif *vif) -{ - struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - struct bss_info_omac *omac; - struct tlv *tlv; - u32 type = 0; - u8 idx; - - tlv = mt7915_mcu_add_tlv(skb, BSS_INFO_OMAC, sizeof(*omac)); - - switch (vif->type) { - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_AP: - type = CONNECTION_INFRA_AP; - break; - case NL80211_IFTYPE_STATION: - type = CONNECTION_INFRA_STA; - break; - case NL80211_IFTYPE_ADHOC: - type = CONNECTION_IBSS_ADHOC; - break; - default: - WARN_ON(1); - break; - } - - omac = (struct bss_info_omac *)tlv; - idx = mvif->mt76.omac_idx > EXT_BSSID_START ? HW_BSSID_0 - : mvif->mt76.omac_idx; - omac->conn_type = cpu_to_le32(type); - omac->omac_idx = mvif->mt76.omac_idx; - omac->band_idx = mvif->mt76.band_idx; - omac->hw_bss_idx = idx; -} - struct mt7915_he_obss_narrow_bw_ru_data { bool tolerated; }; @@ -829,12 +558,12 @@ mt7915_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct tlv *tlv; int freq1 = chandef->center_freq1; - tlv = mt7915_mcu_add_tlv(skb, BSS_INFO_RF_CH, sizeof(*ch)); + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_RF_CH, sizeof(*ch)); ch = (struct bss_info_rf_ch *)tlv; ch->pri_ch = chandef->chan->hw_value; ch->center_ch0 = ieee80211_frequency_to_channel(freq1); - ch->bw = mt7915_mcu_chan_bw(chandef); + ch->bw = mt76_connac_chan_bw(chandef); if (chandef->width == NL80211_CHAN_WIDTH_80P80) { int freq2 = chandef->center_freq2; @@ -843,12 +572,7 @@ mt7915_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, } if (vif->bss_conf.he_support && vif->type == NL80211_IFTYPE_STATION) { - struct mt7915_dev *dev = phy->dev; - struct mt76_phy *mphy = &dev->mt76.phy; - bool ext_phy = phy != &dev->phy; - - if (ext_phy && dev->mt76.phy2) - mphy = dev->mt76.phy2; + struct mt76_phy *mphy = phy->mt76; ch->he_ru26_block = mt7915_check_he_obss_narrow_bw_ru(mphy->hw, vif); @@ -866,7 +590,7 @@ mt7915_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct bss_info_ra *ra; struct tlv *tlv; - tlv = mt7915_mcu_add_tlv(skb, BSS_INFO_RA, sizeof(*ra)); + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_RA, sizeof(*ra)); ra = (struct bss_info_ra *)tlv; ra->op_mode = vif->type == NL80211_IFTYPE_AP; @@ -894,9 +618,9 @@ mt7915_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct bss_info_he *he; struct tlv *tlv; - cap = mt7915_get_he_phy_cap(phy, vif); + cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); - tlv = mt7915_mcu_add_tlv(skb, BSS_INFO_HE_BASIC, sizeof(*he)); + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HE_BASIC, sizeof(*he)); he = (struct bss_info_he *)tlv; he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext; @@ -920,7 +644,7 @@ mt7915_mcu_bss_hw_amsdu_tlv(struct sk_buff *skb) struct bss_info_hw_amsdu *amsdu; struct tlv *tlv; - tlv = mt7915_mcu_add_tlv(skb, BSS_INFO_HW_AMSDU, sizeof(*amsdu)); + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HW_AMSDU, sizeof(*amsdu)); amsdu = (struct bss_info_hw_amsdu *)tlv; amsdu->cmp_bitmap_0 = cpu_to_le32(TXD_CMP_MAP1); @@ -929,26 +653,6 @@ mt7915_mcu_bss_hw_amsdu_tlv(struct sk_buff *skb) amsdu->enable = true; } -static void -mt7915_mcu_bss_ext_tlv(struct sk_buff *skb, struct mt7915_vif *mvif) -{ -/* SIFS 20us + 512 byte beacon tranmitted by 1Mbps (3906us) */ -#define BCN_TX_ESTIMATE_TIME (4096 + 20) - struct bss_info_ext_bss *ext; - int ext_bss_idx, tsf_offset; - struct tlv *tlv; - - ext_bss_idx = mvif->mt76.omac_idx - EXT_BSSID_START; - if (ext_bss_idx < 0) - return; - - tlv = mt7915_mcu_add_tlv(skb, BSS_INFO_EXT_BSS, sizeof(*ext)); - - ext = (struct bss_info_ext_bss *)tlv; - tsf_offset = ext_bss_idx * BCN_TX_ESTIMATE_TIME; - ext->mbss_tsf_offset = cpu_to_le32(tsf_offset); -} - static void mt7915_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt7915_phy *phy) { @@ -957,7 +661,7 @@ mt7915_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt7915_phy *phy) enum nl80211_band band = chandef->chan->band; struct tlv *tlv; - tlv = mt7915_mcu_add_tlv(skb, BSS_INFO_BMC_RATE, sizeof(*bmc)); + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_BMC_RATE, sizeof(*bmc)); bmc = (struct bss_info_bmc_rate *)tlv; if (band == NL80211_BAND_2GHZ) { @@ -1010,6 +714,7 @@ int mt7915_mcu_add_bss_info(struct mt7915_phy *phy, struct ieee80211_vif *vif, int enable) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_dev *dev = phy->dev; struct sk_buff *skb; if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) { @@ -1017,16 +722,17 @@ int mt7915_mcu_add_bss_info(struct mt7915_phy *phy, mt7915_mcu_muar_config(phy, vif, true, enable); } - skb = mt7915_mcu_alloc_sta_req(phy->dev, mvif, NULL, - MT7915_BSS_UPDATE_MAX_SIZE); + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, NULL, + MT7915_BSS_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); /* bss_omac must be first */ if (enable) - mt7915_mcu_bss_omac_tlv(skb, vif); + mt76_connac_mcu_bss_omac_tlv(skb, vif); - mt7915_mcu_bss_basic_tlv(skb, vif, phy, enable); + mt76_connac_mcu_bss_basic_tlv(skb, vif, NULL, phy->mt76, + mvif->sta.wcid.idx, enable); if (vif->type == NL80211_IFTYPE_MONITOR) goto out; @@ -1042,309 +748,48 @@ int mt7915_mcu_add_bss_info(struct mt7915_phy *phy, if (mvif->mt76.omac_idx >= EXT_BSSID_START && mvif->mt76.omac_idx < REPEATER_BSSID_START) - mt7915_mcu_bss_ext_tlv(skb, mvif); + mt76_connac_mcu_bss_ext_tlv(skb, &mvif->mt76); } out: - return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, + return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(BSS_INFO_UPDATE), true); } /** starec & wtbl **/ -static int -mt7915_mcu_sta_key_tlv(struct mt7915_sta *msta, struct sk_buff *skb, - struct ieee80211_key_conf *key, enum set_key_cmd cmd) -{ - struct mt7915_sta_key_conf *bip = &msta->bip; - struct sta_rec_sec *sec; - struct tlv *tlv; - u32 len = sizeof(*sec); - - tlv = mt7915_mcu_add_tlv(skb, STA_REC_KEY_V2, sizeof(*sec)); - - sec = (struct sta_rec_sec *)tlv; - sec->add = cmd; - - if (cmd == SET_KEY) { - struct sec_key *sec_key; - u8 cipher; - - cipher = mt7915_mcu_get_cipher(key->cipher); - if (cipher == MCU_CIPHER_NONE) - return -EOPNOTSUPP; - - sec_key = &sec->key[0]; - sec_key->cipher_len = sizeof(*sec_key); - - if (cipher == MCU_CIPHER_BIP_CMAC_128) { - sec_key->cipher_id = MCU_CIPHER_AES_CCMP; - sec_key->key_id = bip->keyidx; - sec_key->key_len = 16; - memcpy(sec_key->key, bip->key, 16); - - sec_key = &sec->key[1]; - sec_key->cipher_id = MCU_CIPHER_BIP_CMAC_128; - sec_key->cipher_len = sizeof(*sec_key); - sec_key->key_len = 16; - memcpy(sec_key->key, key->key, 16); - - sec->n_cipher = 2; - } else { - sec_key->cipher_id = cipher; - sec_key->key_id = key->keyidx; - sec_key->key_len = key->keylen; - memcpy(sec_key->key, key->key, key->keylen); - - if (cipher == MCU_CIPHER_TKIP) { - /* Rx/Tx MIC keys are swapped */ - memcpy(sec_key->key + 16, key->key + 24, 8); - memcpy(sec_key->key + 24, key->key + 16, 8); - } - - /* store key_conf for BIP batch update */ - if (cipher == MCU_CIPHER_AES_CCMP) { - memcpy(bip->key, key->key, key->keylen); - bip->keyidx = key->keyidx; - } - - len -= sizeof(*sec_key); - sec->n_cipher = 1; - } - } else { - len -= sizeof(sec->key); - sec->n_cipher = 0; - } - sec->len = cpu_to_le16(len); - - return 0; -} - -int mt7915_mcu_add_key(struct mt7915_dev *dev, struct ieee80211_vif *vif, - struct mt7915_sta *msta, struct ieee80211_key_conf *key, - enum set_key_cmd cmd) -{ - struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - struct sk_buff *skb; - int len = sizeof(struct sta_req_hdr) + sizeof(struct sta_rec_sec); - int ret; - - skb = mt7915_mcu_alloc_sta_req(dev, mvif, msta, len); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - ret = mt7915_mcu_sta_key_tlv(msta, skb, key, cmd); - if (ret) - return ret; - - return mt76_mcu_skb_send_msg(&dev->mt76, skb, - MCU_EXT_CMD(STA_REC_UPDATE), true); -} - -static void -mt7915_mcu_sta_ba_tlv(struct sk_buff *skb, - struct ieee80211_ampdu_params *params, - bool enable, bool tx) -{ - struct sta_rec_ba *ba; - struct tlv *tlv; - - tlv = mt7915_mcu_add_tlv(skb, STA_REC_BA, sizeof(*ba)); - - ba = (struct sta_rec_ba *)tlv; - ba->ba_type = tx ? MT_BA_TYPE_ORIGINATOR : MT_BA_TYPE_RECIPIENT; - ba->winsize = cpu_to_le16(params->buf_size); - ba->ssn = cpu_to_le16(params->ssn); - ba->ba_en = enable << params->tid; - ba->amsdu = params->amsdu; - ba->tid = params->tid; -} - -static void -mt7915_mcu_wtbl_ba_tlv(struct sk_buff *skb, - struct ieee80211_ampdu_params *params, - bool enable, bool tx, void *sta_wtbl, - void *wtbl_tlv) -{ - struct wtbl_ba *ba; - struct tlv *tlv; - - tlv = mt7915_mcu_add_nested_tlv(skb, WTBL_BA, sizeof(*ba), - wtbl_tlv, sta_wtbl); - - ba = (struct wtbl_ba *)tlv; - ba->tid = params->tid; - - if (tx) { - ba->ba_type = MT_BA_TYPE_ORIGINATOR; - ba->sn = enable ? cpu_to_le16(params->ssn) : 0; - ba->ba_en = enable; - } else { - memcpy(ba->peer_addr, params->sta->addr, ETH_ALEN); - ba->ba_type = MT_BA_TYPE_RECIPIENT; - ba->rst_ba_tid = params->tid; - ba->rst_ba_sel = RST_BA_MAC_TID_MATCH; - ba->rst_ba_sb = 1; - } - - if (enable) - ba->ba_winsize = cpu_to_le16(params->buf_size); -} - -static int -mt7915_mcu_sta_ba(struct mt7915_dev *dev, - struct ieee80211_ampdu_params *params, - bool enable, bool tx) -{ - struct mt7915_sta *msta = (struct mt7915_sta *)params->sta->drv_priv; - struct mt7915_vif *mvif = msta->vif; - struct wtbl_req_hdr *wtbl_hdr; - struct tlv *sta_wtbl; - struct sk_buff *skb; - int ret; - - if (enable && tx && !params->amsdu) - msta->wcid.amsdu = false; - - skb = mt7915_mcu_alloc_sta_req(dev, mvif, msta, - MT76_CONNAC_STA_UPDATE_MAX_SIZE); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - sta_wtbl = mt7915_mcu_add_tlv(skb, STA_REC_WTBL, sizeof(struct tlv)); - - wtbl_hdr = mt7915_mcu_alloc_wtbl_req(dev, msta, WTBL_SET, sta_wtbl, - &skb); - if (IS_ERR(wtbl_hdr)) - return PTR_ERR(wtbl_hdr); - - mt7915_mcu_wtbl_ba_tlv(skb, params, enable, tx, sta_wtbl, wtbl_hdr); - - ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, - MCU_EXT_CMD(STA_REC_UPDATE), true); - if (ret) - return ret; - - skb = mt7915_mcu_alloc_sta_req(dev, mvif, msta, - MT76_CONNAC_STA_UPDATE_MAX_SIZE); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - mt7915_mcu_sta_ba_tlv(skb, params, enable, tx); - - return mt76_mcu_skb_send_msg(&dev->mt76, skb, - MCU_EXT_CMD(STA_REC_UPDATE), true); -} - int mt7915_mcu_add_tx_ba(struct mt7915_dev *dev, struct ieee80211_ampdu_params *params, bool enable) { - return mt7915_mcu_sta_ba(dev, params, enable, true); + struct mt7915_sta *msta = (struct mt7915_sta *)params->sta->drv_priv; + struct mt7915_vif *mvif = msta->vif; + + if (enable && !params->amsdu) + msta->wcid.amsdu = false; + + return mt76_connac_mcu_sta_ba(&dev->mt76, &mvif->mt76, params, + MCU_EXT_CMD(STA_REC_UPDATE), + enable, true); } int mt7915_mcu_add_rx_ba(struct mt7915_dev *dev, struct ieee80211_ampdu_params *params, bool enable) { - return mt7915_mcu_sta_ba(dev, params, enable, false); -} + struct mt7915_sta *msta = (struct mt7915_sta *)params->sta->drv_priv; + struct mt7915_vif *mvif = msta->vif; -static void -mt7915_mcu_wtbl_generic_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, void *sta_wtbl, - void *wtbl_tlv) -{ - struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - struct wtbl_generic *generic; - struct wtbl_rx *rx; - struct tlv *tlv; - - tlv = mt7915_mcu_add_nested_tlv(skb, WTBL_GENERIC, sizeof(*generic), - wtbl_tlv, sta_wtbl); - - generic = (struct wtbl_generic *)tlv; - - if (sta) { - memcpy(generic->peer_addr, sta->addr, ETH_ALEN); - generic->partial_aid = cpu_to_le16(sta->aid); - generic->muar_idx = mvif->mt76.omac_idx; - generic->qos = sta->wme; - } else { - /* use BSSID in station mode */ - if (vif->type == NL80211_IFTYPE_STATION) - memcpy(generic->peer_addr, vif->bss_conf.bssid, - ETH_ALEN); - else - eth_broadcast_addr(generic->peer_addr); - - generic->muar_idx = 0xe; - } - - tlv = mt7915_mcu_add_nested_tlv(skb, WTBL_RX, sizeof(*rx), - wtbl_tlv, sta_wtbl); - - rx = (struct wtbl_rx *)tlv; - rx->rca1 = sta ? vif->type != NL80211_IFTYPE_AP : 1; - rx->rca2 = 1; - rx->rv = 1; -} - -static void -mt7915_mcu_sta_basic_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, bool enable) -{ -#define EXTRA_INFO_VER BIT(0) -#define EXTRA_INFO_NEW BIT(1) - struct sta_rec_basic *basic; - struct tlv *tlv; - - tlv = mt7915_mcu_add_tlv(skb, STA_REC_BASIC, sizeof(*basic)); - - basic = (struct sta_rec_basic *)tlv; - basic->extra_info = cpu_to_le16(EXTRA_INFO_VER); - - if (enable) { - basic->extra_info |= cpu_to_le16(EXTRA_INFO_NEW); - basic->conn_state = CONN_STATE_PORT_SECURE; - } else { - basic->conn_state = CONN_STATE_DISCONNECT; - } - - if (!sta) { - basic->conn_type = cpu_to_le32(CONNECTION_INFRA_BC); - eth_broadcast_addr(basic->peer_addr); - return; - } - - switch (vif->type) { - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_AP: - basic->conn_type = cpu_to_le32(CONNECTION_INFRA_STA); - break; - case NL80211_IFTYPE_STATION: - basic->conn_type = cpu_to_le32(CONNECTION_INFRA_AP); - break; - case NL80211_IFTYPE_ADHOC: - basic->conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC); - break; - default: - WARN_ON(1); - break; - } - - memcpy(basic->peer_addr, sta->addr, ETH_ALEN); - basic->aid = cpu_to_le16(sta->aid); - basic->qos = sta->wme; + return mt76_connac_mcu_sta_ba(&dev->mt76, &mvif->mt76, params, + MCU_EXT_CMD(STA_REC_UPDATE), + enable, false); } static void mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, struct ieee80211_vif *vif) { - struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct ieee80211_he_cap_elem *elem = &sta->he_cap.he_cap_elem; - enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band; - const u16 *mcs_mask = msta->vif->bitrate_mask.control[band].he_mcs; + struct ieee80211_he_mcs_nss_supp mcs_map; struct sta_rec_he *he; struct tlv *tlv; u32 cap = 0; @@ -1352,7 +797,7 @@ mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, if (!sta->he_cap.has_he) return; - tlv = mt7915_mcu_add_tlv(skb, STA_REC_HE, sizeof(*he)); + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE, sizeof(*he)); he = (struct sta_rec_he *)tlv; @@ -1376,8 +821,9 @@ mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G)) cap |= STA_REC_HE_CAP_BW20_RU242_SUPPORT; - if (mvif->cap.ldpc && (elem->phy_cap_info[1] & - IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) + if (mvif->cap.he_ldpc && + (elem->phy_cap_info[1] & + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) cap |= STA_REC_HE_CAP_LDPC; if (elem->phy_cap_info[1] & @@ -1434,22 +880,23 @@ mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, he->he_cap = cpu_to_le32(cap); + mcs_map = sta->he_cap.he_mcs_nss_supp; switch (sta->bandwidth) { case IEEE80211_STA_RX_BW_160: if (elem->phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) mt7915_mcu_set_sta_he_mcs(sta, &he->max_nss_mcs[CMD_HE_MCS_BW8080], - mcs_mask); + le16_to_cpu(mcs_map.rx_mcs_80p80)); mt7915_mcu_set_sta_he_mcs(sta, &he->max_nss_mcs[CMD_HE_MCS_BW160], - mcs_mask); + le16_to_cpu(mcs_map.rx_mcs_160)); fallthrough; default: mt7915_mcu_set_sta_he_mcs(sta, &he->max_nss_mcs[CMD_HE_MCS_BW80], - mcs_mask); + le16_to_cpu(mcs_map.rx_mcs_80)); break; } @@ -1479,38 +926,6 @@ mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, he->pkt_ext = 2; } -static void -mt7915_mcu_sta_uapsd_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, - struct ieee80211_vif *vif) -{ - struct sta_rec_uapsd *uapsd; - struct tlv *tlv; - - if (vif->type != NL80211_IFTYPE_AP || !sta->wme) - return; - - tlv = mt7915_mcu_add_tlv(skb, STA_REC_APPS, sizeof(*uapsd)); - uapsd = (struct sta_rec_uapsd *)tlv; - - if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) { - uapsd->dac_map |= BIT(3); - uapsd->tac_map |= BIT(3); - } - if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) { - uapsd->dac_map |= BIT(2); - uapsd->tac_map |= BIT(2); - } - if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) { - uapsd->dac_map |= BIT(1); - uapsd->tac_map |= BIT(1); - } - if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) { - uapsd->dac_map |= BIT(0); - uapsd->tac_map |= BIT(0); - } - uapsd->max_sp = sta->max_sp; -} - static void mt7915_mcu_sta_muru_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, struct ieee80211_vif *vif) @@ -1524,19 +939,19 @@ mt7915_mcu_sta_muru_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, vif->type != NL80211_IFTYPE_AP) return; - if (!sta->vht_cap.vht_supported) - return; - - tlv = mt7915_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru)); + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru)); muru = (struct sta_rec_muru *)tlv; muru->cfg.mimo_dl_en = mvif->cap.he_mu_ebfer || mvif->cap.vht_mu_ebfer || mvif->cap.vht_mu_ebfee; + muru->cfg.mimo_ul_en = true; + muru->cfg.ofdma_dl_en = true; - muru->mimo_dl.vht_mu_bfee = - !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); + if (sta->vht_cap.vht_supported) + muru->mimo_dl.vht_mu_bfee = + !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); if (!sta->he_cap.has_he) return; @@ -1544,13 +959,11 @@ mt7915_mcu_sta_muru_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, muru->mimo_dl.partial_bw_dl_mimo = HE_PHY(CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO, elem->phy_cap_info[6]); - muru->cfg.mimo_ul_en = true; muru->mimo_ul.full_ul_mimo = HE_PHY(CAP2_UL_MU_FULL_MU_MIMO, elem->phy_cap_info[2]); muru->mimo_ul.partial_ul_mimo = HE_PHY(CAP2_UL_MU_PARTIAL_MU_MIMO, elem->phy_cap_info[2]); - muru->cfg.ofdma_dl_en = true; muru->ofdma_dl.punc_pream_rx = HE_PHY(CAP1_PREAMBLE_PUNC_RX_MASK, elem->phy_cap_info[1]); muru->ofdma_dl.he_20m_in_40m_2g = @@ -1574,7 +987,10 @@ mt7915_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) struct sta_rec_ht *ht; struct tlv *tlv; - tlv = mt7915_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); + if (!sta->ht_cap.ht_supported) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); ht = (struct sta_rec_ht *)tlv; ht->ht_cap = cpu_to_le16(sta->ht_cap.cap); @@ -1589,7 +1005,7 @@ mt7915_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) if (!sta->vht_cap.vht_supported) return; - tlv = mt7915_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); vht = (struct sta_rec_vht *)tlv; vht->vht_cap = cpu_to_le32(sta->vht_cap.cap); @@ -1598,8 +1014,8 @@ mt7915_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) } static void -mt7915_mcu_sta_amsdu_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) +mt7915_mcu_sta_amsdu_tlv(struct mt7915_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; struct sta_rec_amsdu *amsdu; @@ -1612,96 +1028,27 @@ mt7915_mcu_sta_amsdu_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, if (!sta->max_amsdu_len) return; - tlv = mt7915_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); amsdu = (struct sta_rec_amsdu *)tlv; amsdu->max_amsdu_num = 8; amsdu->amsdu_en = true; - amsdu->max_mpdu_size = sta->max_amsdu_len >= - IEEE80211_MAX_MPDU_LEN_VHT_7991; msta->wcid.amsdu = true; -} -static void -mt7915_mcu_wtbl_smps_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, - void *sta_wtbl, void *wtbl_tlv) -{ - struct wtbl_smps *smps; - struct tlv *tlv; - - tlv = mt7915_mcu_add_nested_tlv(skb, WTBL_SMPS, sizeof(*smps), - wtbl_tlv, sta_wtbl); - smps = (struct wtbl_smps *)tlv; - smps->smps = (sta->smps_mode == IEEE80211_SMPS_DYNAMIC); -} - -static void -mt7915_mcu_wtbl_ht_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, void *sta_wtbl, - void *wtbl_tlv) -{ - struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - struct wtbl_ht *ht = NULL; - struct tlv *tlv; - - /* wtbl ht */ - if (sta->ht_cap.ht_supported) { - tlv = mt7915_mcu_add_nested_tlv(skb, WTBL_HT, sizeof(*ht), - wtbl_tlv, sta_wtbl); - ht = (struct wtbl_ht *)tlv; - ht->ldpc = mvif->cap.ldpc && - (sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING); - ht->af = sta->ht_cap.ampdu_factor; - ht->mm = sta->ht_cap.ampdu_density; - ht->ht = true; - } - - /* wtbl vht */ - if (sta->vht_cap.vht_supported) { - struct wtbl_vht *vht; - u8 af; - - tlv = mt7915_mcu_add_nested_tlv(skb, WTBL_VHT, sizeof(*vht), - wtbl_tlv, sta_wtbl); - vht = (struct wtbl_vht *)tlv; - vht->ldpc = mvif->cap.ldpc && - (sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC); - vht->vht = true; - - af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, - sta->vht_cap.cap); - if (ht) - ht->af = max_t(u8, ht->af, af); - } - - mt7915_mcu_wtbl_smps_tlv(skb, sta, sta_wtbl, wtbl_tlv); -} - -static void -mt7915_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - void *sta_wtbl, void *wtbl_tlv) -{ - struct mt7915_sta *msta; - struct wtbl_hdr_trans *htr = NULL; - struct tlv *tlv; - - tlv = mt7915_mcu_add_nested_tlv(skb, WTBL_HDR_TRANS, sizeof(*htr), - wtbl_tlv, sta_wtbl); - htr = (struct wtbl_hdr_trans *)tlv; - htr->no_rx_trans = true; - if (vif->type == NL80211_IFTYPE_STATION) - htr->to_ds = true; - else - htr->from_ds = true; - - if (!sta) + switch (sta->max_amsdu_len) { + case IEEE80211_MAX_MPDU_LEN_VHT_11454: + if (!is_mt7915(&dev->mt76)) { + amsdu->max_mpdu_size = + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; + return; + } + fallthrough; + case IEEE80211_MAX_MPDU_LEN_HT_7935: + case IEEE80211_MAX_MPDU_LEN_VHT_7991: + amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; + return; + default: + amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; return; - - msta = (struct mt7915_sta *)sta->drv_priv; - htr->no_rx_trans = !test_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); - if (test_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags)) { - htr->to_ds = true; - htr->from_ds = true; } } @@ -1712,48 +1059,30 @@ mt7915_mcu_sta_wtbl_tlv(struct mt7915_dev *dev, struct sk_buff *skb, struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_sta *msta; struct wtbl_req_hdr *wtbl_hdr; + struct mt76_wcid *wcid; struct tlv *tlv; msta = sta ? (struct mt7915_sta *)sta->drv_priv : &mvif->sta; + wcid = sta ? &msta->wcid : NULL; - tlv = mt7915_mcu_add_tlv(skb, STA_REC_WTBL, sizeof(struct tlv)); - wtbl_hdr = mt7915_mcu_alloc_wtbl_req(dev, msta, WTBL_RESET_AND_SET, - tlv, &skb); + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL, sizeof(struct tlv)); + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid, + WTBL_RESET_AND_SET, tlv, + &skb); if (IS_ERR(wtbl_hdr)) return PTR_ERR(wtbl_hdr); - mt7915_mcu_wtbl_generic_tlv(skb, vif, sta, tlv, wtbl_hdr); - mt7915_mcu_wtbl_hdr_trans_tlv(skb, vif, sta, tlv, wtbl_hdr); - + mt76_connac_mcu_wtbl_generic_tlv(&dev->mt76, skb, vif, sta, tlv, + wtbl_hdr); + mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, vif, wcid, tlv, wtbl_hdr); if (sta) - mt7915_mcu_wtbl_ht_tlv(skb, vif, sta, tlv, wtbl_hdr); + mt76_connac_mcu_wtbl_ht_tlv(&dev->mt76, skb, sta, tlv, + wtbl_hdr, mvif->cap.ht_ldpc, + mvif->cap.vht_ldpc); return 0; } -int mt7915_mcu_sta_update_hdr_trans(struct mt7915_dev *dev, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; - struct wtbl_req_hdr *wtbl_hdr; - struct sk_buff *skb; - - skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, - MT76_CONNAC_WTBL_UPDATE_MAX_SIZE); - if (!skb) - return -ENOMEM; - - wtbl_hdr = mt7915_mcu_alloc_wtbl_req(dev, msta, WTBL_SET, NULL, &skb); - if (IS_ERR(wtbl_hdr)) - return PTR_ERR(wtbl_hdr); - - mt7915_mcu_wtbl_hdr_trans_tlv(skb, vif, sta, NULL, wtbl_hdr); - - return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(WTBL_UPDATE), - true); -} - static inline bool mt7915_is_ebf_supported(struct mt7915_phy *phy, struct ieee80211_vif *vif, struct ieee80211_sta *sta, bool bfee) @@ -1870,7 +1199,8 @@ mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, { struct ieee80211_sta_he_cap *pc = &sta->he_cap; struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; - const struct ieee80211_sta_he_cap *vc = mt7915_get_he_phy_cap(phy, vif); + const struct ieee80211_sta_he_cap *vc = + mt76_connac_get_he_phy_cap(phy->mt76, vif); const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem; u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); @@ -1928,8 +1258,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - struct mt7915_phy *phy = - mvif->mt76.band_idx ? mt7915_ext_phy(dev) : &dev->phy; + struct mt7915_phy *phy = mvif->phy; int tx_ant = hweight8(phy->mt76->chainmask) - 1; struct sta_rec_bf *bf; struct tlv *tlv; @@ -1941,11 +1270,14 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, }; bool ebf; + if (!(sta->ht_cap.ht_supported || sta->he_cap.has_he)) + return; + ebf = mt7915_is_ebf_supported(phy, vif, sta, false); if (!ebf && !dev->ibf) return; - tlv = mt7915_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf)); + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf)); bf = (struct sta_rec_bf *)tlv; /* he: eBF only, in accordance with spec @@ -1995,17 +1327,19 @@ mt7915_mcu_sta_bfee_tlv(struct mt7915_dev *dev, struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - struct mt7915_phy *phy = - mvif->mt76.band_idx ? mt7915_ext_phy(dev) : &dev->phy; + struct mt7915_phy *phy = mvif->phy; int tx_ant = hweight8(phy->mt76->chainmask) - 1; struct sta_rec_bfee *bfee; struct tlv *tlv; u8 nrow = 0; + if (!(sta->vht_cap.vht_supported || sta->he_cap.has_he)) + return; + if (!mt7915_is_ebf_supported(phy, vif, sta, true)) return; - tlv = mt7915_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee)); + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee)); bfee = (struct sta_rec_bfee *)tlv; if (sta->he_cap.has_he) { @@ -2050,13 +1384,13 @@ int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev, struct sta_rec_ra_fixed *ra; struct sk_buff *skb; struct tlv *tlv; - int len = sizeof(struct sta_req_hdr) + sizeof(*ra); - skb = mt7915_mcu_alloc_sta_req(dev, mvif, msta, len); + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); if (IS_ERR(skb)) return PTR_ERR(skb); - tlv = mt7915_mcu_add_tlv(skb, STA_REC_RA_UPDATE, sizeof(*ra)); + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA_UPDATE, sizeof(*ra)); ra = (struct sta_rec_ra_fixed *)tlv; switch (field) { @@ -2091,19 +1425,19 @@ int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct sk_buff *skb; int ret; - skb = mt7915_mcu_alloc_sta_req(dev, mvif, msta, - MT76_CONNAC_STA_UPDATE_MAX_SIZE); + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); if (IS_ERR(skb)) return PTR_ERR(skb); - sta_wtbl = mt7915_mcu_add_tlv(skb, STA_REC_WTBL, sizeof(struct tlv)); - - wtbl_hdr = mt7915_mcu_alloc_wtbl_req(dev, msta, WTBL_SET, sta_wtbl, - &skb); + sta_wtbl = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL, + sizeof(struct tlv)); + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid, + WTBL_SET, sta_wtbl, &skb); if (IS_ERR(wtbl_hdr)) return PTR_ERR(wtbl_hdr); - mt7915_mcu_wtbl_smps_tlv(skb, sta, sta_wtbl, wtbl_hdr); + mt76_connac_mcu_wtbl_smps_tlv(skb, sta, sta_wtbl, wtbl_hdr); ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(STA_REC_UPDATE), true); @@ -2134,9 +1468,12 @@ mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev, phy.sgi |= gi << (i << (_he)); \ phy.he_ltf |= mask->control[band].he_ltf << (i << (_he));\ } \ - for (i = 0; i < ARRAY_SIZE(mask->control[band]._mcs); i++) \ - nrates += hweight16(mask->control[band]._mcs[i]); \ - phy.mcs = ffs(mask->control[band]._mcs[0]) - 1; \ + for (i = 0; i < ARRAY_SIZE(mask->control[band]._mcs); i++) { \ + if (!mask->control[band]._mcs[i]) \ + continue; \ + nrates += hweight16(mask->control[band]._mcs[i]); \ + phy.mcs = ffs(mask->control[band]._mcs[i]) - 1; \ + } \ } while (0) if (sta->he_cap.has_he) { @@ -2204,7 +1541,8 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef; + struct mt76_phy *mphy = mvif->phy->mt76; + struct cfg80211_chan_def *chandef = &mphy->chandef; struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask; enum nl80211_band band = chandef->chan->band; struct sta_rec_ra *ra; @@ -2212,15 +1550,16 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev, u32 supp_rate = sta->supp_rates[band]; u32 cap = sta->wme ? STA_CAP_WMM : 0; - tlv = mt7915_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra)); + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra)); ra = (struct sta_rec_ra *)tlv; ra->valid = true; ra->auto_rate = true; - ra->phy_mode = mt7915_get_phy_mode(vif, sta); + ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, sta); ra->channel = chandef->chan->hw_value; ra->bw = sta->bandwidth; ra->phy.bw = sta->bandwidth; + ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->smps_mode); if (supp_rate) { supp_rate &= mask->control[band].legacy; @@ -2254,7 +1593,7 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev, cap |= STA_CAP_TX_STBC; if (sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) cap |= STA_CAP_RX_STBC; - if (mvif->cap.ldpc && + if (mvif->cap.ht_ldpc && (sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) cap |= STA_CAP_LDPC; @@ -2280,7 +1619,7 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev, cap |= STA_CAP_VHT_TX_STBC; if (sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1) cap |= STA_CAP_VHT_RX_STBC; - if (mvif->cap.ldpc && + if (mvif->cap.vht_ldpc && (sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) cap |= STA_CAP_VHT_LDPC; @@ -2291,6 +1630,10 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev, if (sta->he_cap.has_he) { ra->supp_mode |= MODE_HE; cap |= STA_CAP_HE; + + if (sta->he_6ghz_capa.capa) + ra->af = le16_get_bits(sta->he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); } ra->sta_cap = cpu_to_le32(cap); @@ -2304,8 +1647,8 @@ int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct sk_buff *skb; int ret; - skb = mt7915_mcu_alloc_sta_req(dev, mvif, msta, - MT76_CONNAC_STA_UPDATE_MAX_SIZE); + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -2313,7 +1656,7 @@ int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif, * once dev->rc_work changes the settings driver should also * update sta_rec_he here. */ - if (sta->he_cap.has_he && changed) + if (changed) mt7915_mcu_sta_he_tlv(skb, sta, vif); /* sta_rec_ra accommodates BW, NSS and only MCS range format @@ -2371,18 +1714,18 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif, msta = sta ? (struct mt7915_sta *)sta->drv_priv : &mvif->sta; - skb = mt7915_mcu_alloc_sta_req(dev, mvif, msta, - MT76_CONNAC_STA_UPDATE_MAX_SIZE); + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); if (IS_ERR(skb)) return PTR_ERR(skb); /* starec basic */ - mt7915_mcu_sta_basic_tlv(skb, vif, sta, enable); + mt76_connac_mcu_sta_basic_tlv(skb, vif, sta, enable, true); if (!enable) goto out; /* tag order is in accordance with firmware dependency. */ - if (sta && sta->ht_cap.ht_supported) { + if (sta) { /* starec bfer */ mt7915_mcu_sta_bfer_tlv(dev, skb, vif, sta); /* starec ht */ @@ -2390,16 +1733,18 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif, /* starec vht */ mt7915_mcu_sta_vht_tlv(skb, sta); /* starec uapsd */ - mt7915_mcu_sta_uapsd_tlv(skb, sta, vif); + mt76_connac_mcu_sta_uapsd(skb, vif, sta); } ret = mt7915_mcu_sta_wtbl_tlv(dev, skb, vif, sta); - if (ret) + if (ret) { + dev_kfree_skb(skb); return ret; + } - if (sta && sta->ht_cap.ht_supported) { + if (sta) { /* starec amsdu */ - mt7915_mcu_sta_amsdu_tlv(skb, vif, sta); + mt7915_mcu_sta_amsdu_tlv(dev, skb, vif, sta); /* starec he */ mt7915_mcu_sta_he_tlv(skb, sta, vif); /* starec muru */ @@ -2409,8 +1754,10 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif, } ret = mt7915_mcu_add_group(dev, vif, sta); - if (ret) + if (ret) { + dev_kfree_skb(skb); return ret; + } out: return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(STA_REC_UPDATE), true); @@ -2478,6 +1825,55 @@ mt7915_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb, info->cnt = skb->data[offs->cntdwn_counter_offs[0]]; } +static void +mt7915_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb, + struct ieee80211_vif *vif, struct bss_info_bcn *bcn, + struct ieee80211_mutable_offsets *offs) +{ + struct bss_info_bcn_mbss *mbss; + const struct element *elem; + struct tlv *tlv; + + if (!vif->bss_conf.bssid_indicator) + return; + + tlv = mt7915_mcu_add_nested_subtlv(rskb, BSS_INFO_BCN_MBSSID, + sizeof(*mbss), &bcn->sub_ntlv, + &bcn->len); + + mbss = (struct bss_info_bcn_mbss *)tlv; + mbss->offset[0] = cpu_to_le16(offs->tim_offset); + mbss->bitmap = cpu_to_le32(1); + + for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, + &skb->data[offs->mbssid_off], + skb->len - offs->mbssid_off) { + const struct element *sub_elem; + + if (elem->datalen < 2) + continue; + + for_each_element(sub_elem, elem->data + 1, elem->datalen - 1) { + const u8 *data; + + if (sub_elem->id || sub_elem->datalen < 4) + continue; /* not a valid BSS profile */ + + /* Find WLAN_EID_MULTI_BSSID_IDX + * in the merged nontransmitted profile + */ + data = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, + sub_elem->data, + sub_elem->datalen); + if (!data || data[1] < 1 || !data[2]) + continue; + + mbss->offset[data[2]] = cpu_to_le16(data - skb->data); + mbss->bitmap |= cpu_to_le32(BIT(data[2])); + } + } +} + static void mt7915_mcu_beacon_cont(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct sk_buff *rskb, struct sk_buff *skb, @@ -2540,8 +1936,8 @@ mt7915_mcu_beacon_check_caps(struct mt7915_phy *phy, struct ieee80211_vif *vif, len); if (ie && ie[1] >= sizeof(*ht)) { ht = (void *)(ie + 2); - vc->ldpc |= !!(le16_to_cpu(ht->cap_info) & - IEEE80211_HT_CAP_LDPC_CODING); + vc->ht_ldpc = !!(le16_to_cpu(ht->cap_info) & + IEEE80211_HT_CAP_LDPC_CODING); } ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, mgmt->u.beacon.variable, @@ -2552,7 +1948,7 @@ mt7915_mcu_beacon_check_caps(struct mt7915_phy *phy, struct ieee80211_vif *vif, vht = (void *)(ie + 2); bc = le32_to_cpu(vht->vht_cap_info); - vc->ldpc |= !!(bc & IEEE80211_VHT_CAP_RXLDPC); + vc->vht_ldpc = !!(bc & IEEE80211_VHT_CAP_RXLDPC); vc->vht_su_ebfer = (bc & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) && (pc & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); @@ -2571,11 +1967,13 @@ mt7915_mcu_beacon_check_caps(struct mt7915_phy *phy, struct ieee80211_vif *vif, mgmt->u.beacon.variable, len); if (ie && ie[1] >= sizeof(*he) + 1) { const struct ieee80211_sta_he_cap *pc = - mt7915_get_he_phy_cap(phy, vif); + mt76_connac_get_he_phy_cap(phy->mt76, vif); const struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; he = (void *)(ie + 3); + vc->he_ldpc = + HE_PHY(CAP1_LDPC_CODING_IN_PAYLOAD, pe->phy_cap_info[1]); vc->he_su_ebfer = HE_PHY(CAP3_SU_BEAMFORMER, he->phy_cap_info[3]) && HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]); @@ -2601,12 +1999,17 @@ int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct tlv *tlv; struct bss_info_bcn *bcn; int len = MT7915_BEACON_UPDATE_SIZE + MAX_BEACON_SIZE; + bool ext_phy = phy != &dev->phy; - rskb = mt7915_mcu_alloc_sta_req(dev, mvif, NULL, len); + if (vif->bss_conf.nontransmitted) + return 0; + + rskb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + NULL, len); if (IS_ERR(rskb)) return PTR_ERR(rskb); - tlv = mt7915_mcu_add_tlv(rskb, BSS_INFO_OFFLOAD, sizeof(*bcn)); + tlv = mt76_connac_mcu_add_tlv(rskb, BSS_INFO_OFFLOAD, sizeof(*bcn)); bcn = (struct bss_info_bcn *)tlv; bcn->enable = en; @@ -2623,15 +2026,15 @@ int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, return -EINVAL; } - if (mvif->mt76.band_idx) { + if (ext_phy) { info = IEEE80211_SKB_CB(skb); info->hw_queue |= MT_TX_HW_QUEUE_EXT_PHY; } mt7915_mcu_beacon_check_caps(phy, vif, skb); - /* TODO: subtag - 11v MBSSID */ mt7915_mcu_beacon_cntdwn(vif, rskb, skb, bcn, &offs); + mt7915_mcu_beacon_mbss(rskb, skb, vif, bcn, &offs); mt7915_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs); dev_kfree_skb(skb); @@ -2640,100 +2043,29 @@ int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, MCU_EXT_CMD(BSS_INFO_UPDATE), true); } -static int mt7915_mcu_start_firmware(struct mt7915_dev *dev, u32 addr, - u32 option) +static int mt7915_driver_own(struct mt7915_dev *dev, u8 band) { - struct { - __le32 option; - __le32 addr; - } req = { - .option = cpu_to_le32(option), - .addr = cpu_to_le32(addr), - }; - - return mt76_mcu_send_msg(&dev->mt76, MCU_CMD(FW_START_REQ), &req, - sizeof(req), true); -} - -static int mt7915_mcu_restart(struct mt76_dev *dev) -{ - struct { - u8 power_mode; - u8 rsv[3]; - } req = { - .power_mode = 1, - }; - - return mt76_mcu_send_msg(dev, MCU_CMD(NIC_POWER_CTRL), &req, - sizeof(req), false); -} - -static int mt7915_mcu_patch_sem_ctrl(struct mt7915_dev *dev, bool get) -{ - struct { - __le32 op; - } req = { - .op = cpu_to_le32(get ? PATCH_SEM_GET : PATCH_SEM_RELEASE), - }; - - return mt76_mcu_send_msg(&dev->mt76, MCU_CMD(PATCH_SEM_CONTROL), &req, - sizeof(req), true); -} - -static int mt7915_mcu_start_patch(struct mt7915_dev *dev) -{ - struct { - u8 check_crc; - u8 reserved[3]; - } req = { - .check_crc = 0, - }; - - return mt76_mcu_send_msg(&dev->mt76, MCU_CMD(PATCH_FINISH_REQ), &req, - sizeof(req), true); -} - -static int mt7915_driver_own(struct mt7915_dev *dev) -{ - mt76_wr(dev, MT_TOP_LPCR_HOST_BAND0, MT_TOP_LPCR_HOST_DRV_OWN); - if (!mt76_poll_msec(dev, MT_TOP_LPCR_HOST_BAND0, - MT_TOP_LPCR_HOST_FW_OWN, 0, 500)) { + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(band), MT_TOP_LPCR_HOST_DRV_OWN); + if (!mt76_poll_msec(dev, MT_TOP_LPCR_HOST_BAND(band), + MT_TOP_LPCR_HOST_FW_OWN_STAT, 0, 500)) { dev_err(dev->mt76.dev, "Timeout for driver own\n"); return -EIO; } + /* clear irq when the driver own success */ + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND_IRQ_STAT(band), + MT_TOP_LPCR_HOST_BAND_STAT); + return 0; } -static int mt7915_mcu_init_download(struct mt7915_dev *dev, u32 addr, - u32 len, u32 mode) -{ - struct { - __le32 addr; - __le32 len; - __le32 mode; - } req = { - .addr = cpu_to_le32(addr), - .len = cpu_to_le32(len), - .mode = cpu_to_le32(mode), - }; - int attr; - - if (req.addr == cpu_to_le32(MCU_PATCH_ADDRESS)) - attr = MCU_CMD(PATCH_START_REQ); - else - attr = MCU_CMD(TARGET_ADDRESS_LEN_REQ); - - return mt76_mcu_send_msg(&dev->mt76, attr, &req, sizeof(req), true); -} - static int mt7915_load_patch(struct mt7915_dev *dev) { const struct mt7915_patch_hdr *hdr; const struct firmware *fw = NULL; int i, ret, sem; - sem = mt7915_mcu_patch_sem_ctrl(dev, 1); + sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, 1); switch (sem) { case PATCH_IS_DL: return 0; @@ -2744,7 +2076,8 @@ static int mt7915_load_patch(struct mt7915_dev *dev) return -EAGAIN; } - ret = request_firmware(&fw, MT7915_ROM_PATCH, dev->mt76.dev); + ret = request_firmware(&fw, fw_name_var(dev, ROM_PATCH), + dev->mt76.dev); if (ret) goto out; @@ -2776,8 +2109,8 @@ static int mt7915_load_patch(struct mt7915_dev *dev) len = be32_to_cpu(sec->info.len); dl = fw->data + be32_to_cpu(sec->offs); - ret = mt7915_mcu_init_download(dev, addr, len, - DL_MODE_NEED_RSP); + ret = mt76_connac_mcu_init_download(&dev->mt76, addr, len, + DL_MODE_NEED_RSP); if (ret) { dev_err(dev->mt76.dev, "Download request failed\n"); goto out; @@ -2791,12 +2124,12 @@ static int mt7915_load_patch(struct mt7915_dev *dev) } } - ret = mt7915_mcu_start_patch(dev); + ret = mt76_connac_mcu_start_patch(&dev->mt76); if (ret) dev_err(dev->mt76.dev, "Failed to start patch\n"); out: - sem = mt7915_mcu_patch_sem_ctrl(dev, 0); + sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, 0); switch (sem) { case PATCH_REL_SEM_SUCCESS: break; @@ -2810,20 +2143,6 @@ static int mt7915_load_patch(struct mt7915_dev *dev) return ret; } -static u32 mt7915_mcu_gen_dl_mode(u8 feature_set, bool is_wa) -{ - u32 ret = 0; - - ret |= (feature_set & FW_FEATURE_SET_ENCRYPT) ? - (DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV) : 0; - ret |= FIELD_PREP(DL_MODE_KEY_IDX, - FIELD_GET(FW_FEATURE_SET_KEY_IDX, feature_set)); - ret |= DL_MODE_NEED_RSP; - ret |= is_wa ? DL_MODE_WORKING_PDA_CR4 : 0; - - return ret; -} - static int mt7915_mcu_send_ram_firmware(struct mt7915_dev *dev, const struct mt7915_fw_trailer *hdr, @@ -2839,14 +2158,16 @@ mt7915_mcu_send_ram_firmware(struct mt7915_dev *dev, region = (const struct mt7915_fw_region *)((const u8 *)hdr - (hdr->n_region - i) * sizeof(*region)); - mode = mt7915_mcu_gen_dl_mode(region->feature_set, is_wa); + mode = mt76_connac_mcu_gen_dl_mode(&dev->mt76, + region->feature_set, is_wa); len = le32_to_cpu(region->len); addr = le32_to_cpu(region->addr); if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR) override = addr; - err = mt7915_mcu_init_download(dev, addr, len, mode); + err = mt76_connac_mcu_init_download(&dev->mt76, addr, len, + mode); if (err) { dev_err(dev->mt76.dev, "Download request failed\n"); return err; @@ -2868,7 +2189,7 @@ mt7915_mcu_send_ram_firmware(struct mt7915_dev *dev, if (is_wa) option |= FW_START_WORKING_PDA_CR4; - return mt7915_mcu_start_firmware(dev, override, option); + return mt76_connac_mcu_start_firmware(&dev->mt76, override, option); } static int mt7915_load_ram(struct mt7915_dev *dev) @@ -2877,7 +2198,8 @@ static int mt7915_load_ram(struct mt7915_dev *dev) const struct firmware *fw; int ret; - ret = request_firmware(&fw, MT7915_FIRMWARE_WM, dev->mt76.dev); + ret = request_firmware(&fw, fw_name_var(dev, FIRMWARE_WM), + dev->mt76.dev); if (ret) return ret; @@ -2901,7 +2223,8 @@ static int mt7915_load_ram(struct mt7915_dev *dev) release_firmware(fw); - ret = request_firmware(&fw, MT7915_FIRMWARE_WA, dev->mt76.dev); + ret = request_firmware(&fw, fw_name(dev, FIRMWARE_WA), + dev->mt76.dev); if (ret) return ret; @@ -2933,10 +2256,36 @@ static int mt7915_load_ram(struct mt7915_dev *dev) return ret; } +static int +mt7915_firmware_state(struct mt7915_dev *dev, bool wa) +{ + u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE, + wa ? FW_STATE_RDY : FW_STATE_FW_DOWNLOAD); + + if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE, + state, 1000)) { + dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); + return -EIO; + } + return 0; +} + static int mt7915_load_firmware(struct mt7915_dev *dev) { int ret; + /* make sure fw is download state */ + if (mt7915_firmware_state(dev, false)) { + /* restart firmware once */ + __mt76_mcu_restart(&dev->mt76); + ret = mt7915_firmware_state(dev, false); + if (ret) { + dev_err(dev->mt76.dev, + "Firmware is not ready for download\n"); + return ret; + } + } + ret = mt7915_load_patch(dev); if (ret) return ret; @@ -2945,12 +2294,9 @@ static int mt7915_load_firmware(struct mt7915_dev *dev) if (ret) return ret; - if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE, - FIELD_PREP(MT_TOP_MISC_FW_STATE, - FW_STATE_RDY), 1000)) { - dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); - return -EIO; - } + ret = mt7915_firmware_state(dev, true); + if (ret) + return ret; mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false); @@ -3021,7 +2367,7 @@ int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy, void *ms) u8 band_idx; } req = { .cmd = cpu_to_le32(MURU_GET_TXC_TX_STATS), - .band_idx = phy != &dev->phy, + .band_idx = phy->band_idx, }; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), @@ -3110,15 +2456,29 @@ int mt7915_mcu_init(struct mt7915_dev *dev) .headroom = sizeof(struct mt7915_mcu_txd), .mcu_skb_send_msg = mt7915_mcu_send_message, .mcu_parse_response = mt7915_mcu_parse_response, - .mcu_restart = mt7915_mcu_restart, + .mcu_restart = mt76_connac_mcu_restart, }; int ret; dev->mt76.mcu_ops = &mt7915_mcu_ops; - ret = mt7915_driver_own(dev); + /* force firmware operation mode into normal state, + * which should be set before firmware download stage. + */ + if (is_mt7915(&dev->mt76)) + mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); + else + mt76_wr(dev, MT_SWDEF_MODE_MT7916, MT_SWDEF_NORMAL_MODE); + + ret = mt7915_driver_own(dev, 0); if (ret) return ret; + /* set driver own for band1 when two hif exist */ + if (dev->hif2) { + ret = mt7915_driver_own(dev, 1); + if (ret) + return ret; + } ret = mt7915_load_firmware(dev); if (ret) @@ -3153,14 +2513,15 @@ int mt7915_mcu_init(struct mt7915_dev *dev) void mt7915_mcu_exit(struct mt7915_dev *dev) { __mt76_mcu_restart(&dev->mt76); - if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE, - FIELD_PREP(MT_TOP_MISC_FW_STATE, - FW_STATE_FW_DOWNLOAD), 1000)) { + if (mt7915_firmware_state(dev, false)) { dev_err(dev->mt76.dev, "Failed to exit mcu\n"); return; } - mt76_wr(dev, MT_TOP_LPCR_HOST_BAND0, MT_TOP_LPCR_HOST_FW_OWN); + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(0), MT_TOP_LPCR_HOST_FW_OWN); + if (dev->hif2) + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(1), + MT_TOP_LPCR_HOST_FW_OWN); skb_queue_purge(&dev->mt76.mcu.res_q); } @@ -3238,26 +2599,6 @@ int mt7915_mcu_set_scs(struct mt7915_dev *dev, u8 band, bool enable) sizeof(req), false); } -int mt7915_mcu_set_rts_thresh(struct mt7915_phy *phy, u32 val) -{ - struct mt7915_dev *dev = phy->dev; - struct { - u8 prot_idx; - u8 band; - u8 rsv[2]; - __le32 len_thresh; - __le32 pkt_thresh; - } __packed req = { - .prot_idx = 1, - .band = phy != &dev->phy, - .len_thresh = cpu_to_le32(val), - .pkt_thresh = cpu_to_le32(0x2), - }; - - return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(PROTECT_CTRL), &req, - sizeof(req), true); -} - int mt7915_mcu_update_edca(struct mt7915_dev *dev, void *param) { struct mt7915_mcu_tx *req = (struct mt7915_mcu_tx *)param; @@ -3303,58 +2644,6 @@ int mt7915_mcu_set_tx(struct mt7915_dev *dev, struct ieee80211_vif *vif) return mt7915_mcu_update_edca(dev, &req); } -int mt7915_mcu_set_pm(struct mt7915_dev *dev, int band, int enter) -{ -#define ENTER_PM_STATE 1 -#define EXIT_PM_STATE 2 - struct { - u8 pm_number; - u8 pm_state; - u8 bssid[ETH_ALEN]; - u8 dtim_period; - u8 wlan_idx_lo; - __le16 bcn_interval; - __le32 aid; - __le32 rx_filter; - u8 band_idx; - u8 wlan_idx_hi; - u8 rsv[2]; - __le32 feature; - u8 omac_idx; - u8 wmm_idx; - u8 bcn_loss_cnt; - u8 bcn_sp_duration; - } __packed req = { - .pm_number = 5, - .pm_state = (enter) ? ENTER_PM_STATE : EXIT_PM_STATE, - .band_idx = band, - }; - - return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(PM_STATE_CTRL), &req, - sizeof(req), true); -} - -int mt7915_mcu_rdd_cmd(struct mt7915_dev *dev, - enum mt7915_rdd_cmd cmd, u8 index, - u8 rx_sel, u8 val) -{ - struct { - u8 ctrl; - u8 rdd_idx; - u8 rdd_rx_sel; - u8 val; - u8 rsv[4]; - } __packed req = { - .ctrl = cmd, - .rdd_idx = index, - .rdd_rx_sel = rx_sel, - .val = val, - }; - - return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_RDD_CTRL), &req, - sizeof(req), true); -} - int mt7915_mcu_set_fcc5_lpn(struct mt7915_dev *dev, int val) { struct { @@ -3453,12 +2742,109 @@ int mt7915_mcu_set_radar_th(struct mt7915_dev *dev, int index, sizeof(req), true); } +static int +mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy, + struct cfg80211_chan_def *chandef, + int cmd) +{ + struct mt7915_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct ieee80211_channel *chan = mphy->chandef.chan; + int freq = mphy->chandef.center_freq1; + struct mt7915_mcu_background_chain_ctrl req = { + .monitor_scan_type = 2, /* simple rx */ + }; + + if (!chandef && cmd != CH_SWITCH_BACKGROUND_SCAN_STOP) + return -EINVAL; + + if (!cfg80211_chandef_valid(&mphy->chandef)) + return -EINVAL; + + switch (cmd) { + case CH_SWITCH_BACKGROUND_SCAN_START: { + req.chan = chan->hw_value; + req.central_chan = ieee80211_frequency_to_channel(freq); + req.bw = mt76_connac_chan_bw(&mphy->chandef); + req.monitor_chan = chandef->chan->hw_value; + req.monitor_central_chan = + ieee80211_frequency_to_channel(chandef->center_freq1); + req.monitor_bw = mt76_connac_chan_bw(chandef); + req.band_idx = phy != &dev->phy; + req.scan_mode = 1; + break; + } + case CH_SWITCH_BACKGROUND_SCAN_RUNNING: + req.monitor_chan = chandef->chan->hw_value; + req.monitor_central_chan = + ieee80211_frequency_to_channel(chandef->center_freq1); + req.band_idx = phy != &dev->phy; + req.scan_mode = 2; + break; + case CH_SWITCH_BACKGROUND_SCAN_STOP: + req.chan = chan->hw_value; + req.central_chan = ieee80211_frequency_to_channel(freq); + req.bw = mt76_connac_chan_bw(&mphy->chandef); + req.tx_stream = hweight8(mphy->antenna_mask); + req.rx_stream = mphy->antenna_mask; + break; + default: + return -EINVAL; + } + req.band = chandef ? chandef->chan->band == NL80211_BAND_5GHZ : 1; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(OFFCH_SCAN_CTRL), + &req, sizeof(req), false); +} + +int mt7915_mcu_rdd_background_enable(struct mt7915_phy *phy, + struct cfg80211_chan_def *chandef) +{ + struct mt7915_dev *dev = phy->dev; + int err, region; + + if (!chandef) { /* disable offchain */ + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, MT_RX_SEL2, + 0, 0); + if (err) + return err; + + return mt7915_mcu_background_chain_ctrl(phy, NULL, + CH_SWITCH_BACKGROUND_SCAN_STOP); + } + + err = mt7915_mcu_background_chain_ctrl(phy, chandef, + CH_SWITCH_BACKGROUND_SCAN_START); + if (err) + return err; + + switch (dev->mt76.region) { + case NL80211_DFS_ETSI: + region = 0; + break; + case NL80211_DFS_JP: + region = 2; + break; + case NL80211_DFS_FCC: + default: + region = 1; + break; + } + + return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, MT_RX_SEL2, + 0, region); +} + int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) { + static const u8 ch_band[] = { + [NL80211_BAND_2GHZ] = 0, + [NL80211_BAND_5GHZ] = 1, + [NL80211_BAND_6GHZ] = 2, + }; struct mt7915_dev *dev = phy->dev; struct cfg80211_chan_def *chandef = &phy->mt76->chandef; int freq1 = chandef->center_freq1; - bool ext_phy = phy != &dev->phy; struct { u8 control_ch; u8 center_ch; @@ -3479,11 +2865,11 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) } __packed req = { .control_ch = chandef->chan->hw_value, .center_ch = ieee80211_frequency_to_channel(freq1), - .bw = mt7915_mcu_chan_bw(chandef), + .bw = mt76_connac_chan_bw(chandef), .tx_streams_num = hweight8(phy->mt76->antenna_mask), .rx_streams = phy->mt76->antenna_mask, - .band_idx = ext_phy, - .channel_band = chandef->chan->band, + .band_idx = phy->band_idx, + .channel_band = ch_band[chandef->chan->band], }; #ifdef CONFIG_NL80211_TESTMODE @@ -3494,17 +2880,18 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) req.tx_streams_num = fls(phy->mt76->test.tx_antenna_mask); req.rx_streams = phy->mt76->test.tx_antenna_mask; - if (ext_phy) { - req.tx_streams_num = 2; - req.rx_streams >>= 2; - } + if (phy != &dev->phy) + req.rx_streams >>= dev->chainshift; } #endif - if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) + if (cmd == MCU_EXT_CMD(SET_RX_PATH) || + dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR) + req.switch_reason = CH_SWITCH_NORMAL; + else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; - else if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) && - chandef->chan->dfs_state != NL80211_DFS_AVAILABLE) + else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef, + NL80211_IFTYPE_AP)) req.switch_reason = CH_SWITCH_DFS; else req.switch_reason = CH_SWITCH_NORMAL; @@ -3527,7 +2914,8 @@ static int mt7915_mcu_set_eeprom_flash(struct mt7915_dev *dev) #define PAGE_IDX_MASK GENMASK(4, 2) #define PER_PAGE_SIZE 0x400 struct mt7915_mcu_eeprom req = { .buffer_mode = EE_MODE_BUFFER }; - u8 total = DIV_ROUND_UP(MT7915_EEPROM_SIZE, PER_PAGE_SIZE); + u16 eeprom_size = mt7915_eeprom_size(dev); + u8 total = DIV_ROUND_UP(eeprom_size, PER_PAGE_SIZE); u8 *eep = (u8 *)dev->mt76.eeprom.data; int eep_len; int i; @@ -3536,8 +2924,8 @@ static int mt7915_mcu_set_eeprom_flash(struct mt7915_dev *dev) struct sk_buff *skb; int ret; - if (i == total - 1 && !!(MT7915_EEPROM_SIZE % PER_PAGE_SIZE)) - eep_len = MT7915_EEPROM_SIZE % PER_PAGE_SIZE; + if (i == total - 1 && !!(eeprom_size % PER_PAGE_SIZE)) + eep_len = eeprom_size % PER_PAGE_SIZE; else eep_len = PER_PAGE_SIZE; @@ -3770,19 +3158,26 @@ int mt7915_mcu_apply_tx_dpd(struct mt7915_phy *phy) int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch) { /* strict order */ - static const enum mt7915_chan_mib_offs offs[] = { - MIB_BUSY_TIME, MIB_TX_TIME, MIB_RX_TIME, MIB_OBSS_AIRTIME + static const u32 offs[] = { + MIB_BUSY_TIME, MIB_TX_TIME, MIB_RX_TIME, MIB_OBSS_AIRTIME, + MIB_BUSY_TIME_V2, MIB_TX_TIME_V2, MIB_RX_TIME_V2, + MIB_OBSS_AIRTIME_V2 }; struct mt76_channel_state *state = phy->mt76->chan_state; struct mt76_channel_state *state_ts = &phy->state_ts; struct mt7915_dev *dev = phy->dev; struct mt7915_mcu_mib *res, req[4]; struct sk_buff *skb; - int i, ret; + int i, ret, start = 0, ofs = 20; + + if (!is_mt7915(&dev->mt76)) { + start = 4; + ofs = 0; + } for (i = 0; i < 4; i++) { req[i].band = cpu_to_le32(phy != &dev->phy); - req[i].offs = cpu_to_le32(offs[i]); + req[i].offs = cpu_to_le32(offs[i + start]); } ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(GET_MIB_INFO), @@ -3790,7 +3185,7 @@ int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch) if (ret) return ret; - res = (struct mt7915_mcu_mib *)(skb->data + 20); + res = (struct mt7915_mcu_mib *)(skb->data + ofs); if (chan_switch) goto out; @@ -3842,7 +3237,7 @@ int mt7915_mcu_set_thermal_throttling(struct mt7915_phy *phy, u8 state) u8 rsv[2]; } __packed req = { .ctrl = { - .band_idx = phy != &dev->phy, + .band_idx = phy->band_idx, }, }; int level; @@ -4137,6 +3532,8 @@ int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif, case MT_PHY_TYPE_OFDM: if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) sband = &mphy->sband_5g.sband; + else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; else sband = &mphy->sband_2g.sband; @@ -4210,11 +3607,13 @@ int mt7915_mcu_update_bss_color(struct mt7915_dev *dev, struct ieee80211_vif *vi struct sk_buff *skb; struct tlv *tlv; - skb = mt7915_mcu_alloc_sta_req(dev, mvif, NULL, len); + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + NULL, len); if (IS_ERR(skb)) return PTR_ERR(skb); - tlv = mt7915_mcu_add_tlv(skb, BSS_INFO_BSS_COLOR, sizeof(*bss_color)); + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_BSS_COLOR, + sizeof(*bss_color)); bss_color = (struct bss_info_color *)tlv; bss_color->disable = !he_bss_color->enabled; bss_color->color = he_bss_color->color; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 92268e696931..960072a44222 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -79,6 +79,15 @@ struct mt7915_mcu_csa_notify { u8 rsv; } __packed; +struct mt7915_mcu_bcc_notify { + struct mt7915_mcu_rxd rxd; + + u8 band_idx; + u8 omac_idx; + u8 cca_count; + u8 rsv; +} __packed; + struct mt7915_mcu_rdd_report { struct mt7915_mcu_rxd rxd; @@ -131,6 +140,29 @@ struct mt7915_mcu_rdd_report { } hw_pulse[32]; } __packed; +struct mt7915_mcu_background_chain_ctrl { + u8 chan; /* primary channel */ + u8 central_chan; /* central channel */ + u8 bw; + u8 tx_stream; + u8 rx_stream; + + u8 monitor_chan; /* monitor channel */ + u8 monitor_central_chan;/* monitor central channel */ + u8 monitor_bw; + u8 monitor_tx_stream; + u8 monitor_rx_stream; + + u8 scan_mode; /* 0: ScanStop + * 1: ScanStart + * 2: ScanRunning + */ + u8 band_idx; /* DBDC */ + u8 monitor_scan_type; + u8 band; /* 0: 2.4GHz, 1: 5GHz */ + u8 rsv[2]; +} __packed; + struct mt7915_mcu_eeprom { u8 buffer_mode; u8 format; @@ -161,10 +193,16 @@ struct mt7915_mcu_mib { } __packed; enum mt7915_chan_mib_offs { + /* mt7915 */ MIB_BUSY_TIME = 14, MIB_TX_TIME = 81, MIB_RX_TIME, - MIB_OBSS_AIRTIME = 86 + MIB_OBSS_AIRTIME = 86, + /* mt7916 */ + MIB_BUSY_TIME_V2 = 0, + MIB_TX_TIME_V2 = 6, + MIB_RX_TIME_V2 = 8, + MIB_OBSS_AIRTIME_V2 = 490 }; struct edca { @@ -266,29 +304,6 @@ enum mcu_mmps_mode { MCU_MMPS_DISABLE, }; -#define STA_TYPE_STA BIT(0) -#define STA_TYPE_AP BIT(1) -#define STA_TYPE_ADHOC BIT(2) -#define STA_TYPE_WDS BIT(4) -#define STA_TYPE_BC BIT(5) - -#define NETWORK_INFRA BIT(16) -#define NETWORK_P2P BIT(17) -#define NETWORK_IBSS BIT(18) -#define NETWORK_WDS BIT(21) - -#define CONNECTION_INFRA_STA (STA_TYPE_STA | NETWORK_INFRA) -#define CONNECTION_INFRA_AP (STA_TYPE_AP | NETWORK_INFRA) -#define CONNECTION_P2P_GC (STA_TYPE_STA | NETWORK_P2P) -#define CONNECTION_P2P_GO (STA_TYPE_AP | NETWORK_P2P) -#define CONNECTION_IBSS_ADHOC (STA_TYPE_ADHOC | NETWORK_IBSS) -#define CONNECTION_WDS (STA_TYPE_WDS | NETWORK_WDS) -#define CONNECTION_INFRA_BC (STA_TYPE_BC | NETWORK_INFRA) - -#define CONN_STATE_DISCONNECT 0 -#define CONN_STATE_CONNECT 1 -#define CONN_STATE_PORT_SECURE 2 - enum { SCS_SEND_DATA, SCS_SET_MANUAL_PD_TH, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 1f6ba306c850..5062e0d8cae4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -1,103 +1,442 @@ // SPDX-License-Identifier: ISC /* Copyright (C) 2020 MediaTek Inc. */ +#include +#include +#include +#include + #include "mt7915.h" +#include "mac.h" +#include "../trace.h" + +static const u32 mt7915_reg[] = { + [INT_SOURCE_CSR] = 0xd7010, + [INT_MASK_CSR] = 0xd7014, + [INT1_SOURCE_CSR] = 0xd7088, + [INT1_MASK_CSR] = 0xd708c, + [INT_MCU_CMD_SOURCE] = 0xd51f0, + [INT_MCU_CMD_EVENT] = 0x3108, + [WFDMA0_ADDR] = 0xd4000, + [WFDMA0_PCIE1_ADDR] = 0xd8000, + [WFDMA_EXT_CSR_ADDR] = 0xd7000, + [CBTOP1_PHY_END] = 0x77ffffff, + [INFRA_MCU_ADDR_END] = 0x7c3fffff, +}; + +static const u32 mt7916_reg[] = { + [INT_SOURCE_CSR] = 0xd4200, + [INT_MASK_CSR] = 0xd4204, + [INT1_SOURCE_CSR] = 0xd8200, + [INT1_MASK_CSR] = 0xd8204, + [INT_MCU_CMD_SOURCE] = 0xd41f0, + [INT_MCU_CMD_EVENT] = 0x2108, + [WFDMA0_ADDR] = 0xd4000, + [WFDMA0_PCIE1_ADDR] = 0xd8000, + [WFDMA_EXT_CSR_ADDR] = 0xd7000, + [CBTOP1_PHY_END] = 0x7fffffff, + [INFRA_MCU_ADDR_END] = 0x7c085fff, +}; + +static const u32 mt7986_reg[] = { + [INT_SOURCE_CSR] = 0x24200, + [INT_MASK_CSR] = 0x24204, + [INT1_SOURCE_CSR] = 0x28200, + [INT1_MASK_CSR] = 0x28204, + [INT_MCU_CMD_SOURCE] = 0x241f0, + [INT_MCU_CMD_EVENT] = 0x54000108, + [WFDMA0_ADDR] = 0x24000, + [WFDMA0_PCIE1_ADDR] = 0x28000, + [WFDMA_EXT_CSR_ADDR] = 0x27000, + [CBTOP1_PHY_END] = 0x7fffffff, + [INFRA_MCU_ADDR_END] = 0x7c085fff, +}; + +static const u32 mt7915_offs[] = { + [TMAC_CDTR] = 0x090, + [TMAC_ODTR] = 0x094, + [TMAC_ATCR] = 0x098, + [TMAC_TRCR0] = 0x09c, + [TMAC_ICR0] = 0x0a4, + [TMAC_ICR1] = 0x0b4, + [TMAC_CTCR0] = 0x0f4, + [TMAC_TFCR0] = 0x1e0, + [MDP_BNRCFR0] = 0x070, + [MDP_BNRCFR1] = 0x074, + [ARB_DRNGR0] = 0x194, + [ARB_SCR] = 0x080, + [RMAC_MIB_AIRTIME14] = 0x3b8, + [AGG_AWSCR0] = 0x05c, + [AGG_PCR0] = 0x06c, + [AGG_ACR0] = 0x084, + [AGG_MRCR] = 0x098, + [AGG_ATCR1] = 0x0f0, + [AGG_ATCR3] = 0x0f4, + [LPON_UTTR0] = 0x080, + [LPON_UTTR1] = 0x084, + [LPON_FRCR] = 0x314, + [MIB_SDR3] = 0x014, + [MIB_SDR4] = 0x018, + [MIB_SDR5] = 0x01c, + [MIB_SDR7] = 0x024, + [MIB_SDR8] = 0x028, + [MIB_SDR9] = 0x02c, + [MIB_SDR10] = 0x030, + [MIB_SDR11] = 0x034, + [MIB_SDR12] = 0x038, + [MIB_SDR13] = 0x03c, + [MIB_SDR14] = 0x040, + [MIB_SDR15] = 0x044, + [MIB_SDR16] = 0x048, + [MIB_SDR17] = 0x04c, + [MIB_SDR18] = 0x050, + [MIB_SDR19] = 0x054, + [MIB_SDR20] = 0x058, + [MIB_SDR21] = 0x05c, + [MIB_SDR22] = 0x060, + [MIB_SDR23] = 0x064, + [MIB_SDR24] = 0x068, + [MIB_SDR25] = 0x06c, + [MIB_SDR27] = 0x074, + [MIB_SDR28] = 0x078, + [MIB_SDR29] = 0x07c, + [MIB_SDRVEC] = 0x080, + [MIB_SDR31] = 0x084, + [MIB_SDR32] = 0x088, + [MIB_SDRMUBF] = 0x090, + [MIB_DR8] = 0x0c0, + [MIB_DR9] = 0x0c4, + [MIB_DR11] = 0x0cc, + [MIB_MB_SDR0] = 0x100, + [MIB_MB_SDR1] = 0x104, + [TX_AGG_CNT] = 0x0a8, + [TX_AGG_CNT2] = 0x164, + [MIB_ARNG] = 0x4b8, + [WTBLON_TOP_WDUCR] = 0x0, + [WTBL_UPDATE] = 0x030, + [PLE_FL_Q_EMPTY] = 0x0b0, + [PLE_FL_Q_CTRL] = 0x1b0, + [PLE_AC_QEMPTY] = 0x500, + [PLE_FREEPG_CNT] = 0x100, + [PLE_FREEPG_HEAD_TAIL] = 0x104, + [PLE_PG_HIF_GROUP] = 0x110, + [PLE_HIF_PG_INFO] = 0x114, + [AC_OFFSET] = 0x040, + [ETBF_PAR_RPT0] = 0x068, +}; + +static const u32 mt7916_offs[] = { + [TMAC_CDTR] = 0x0c8, + [TMAC_ODTR] = 0x0cc, + [TMAC_ATCR] = 0x00c, + [TMAC_TRCR0] = 0x010, + [TMAC_ICR0] = 0x014, + [TMAC_ICR1] = 0x018, + [TMAC_CTCR0] = 0x114, + [TMAC_TFCR0] = 0x0e4, + [MDP_BNRCFR0] = 0x090, + [MDP_BNRCFR1] = 0x094, + [ARB_DRNGR0] = 0x1e0, + [ARB_SCR] = 0x000, + [RMAC_MIB_AIRTIME14] = 0x0398, + [AGG_AWSCR0] = 0x030, + [AGG_PCR0] = 0x040, + [AGG_ACR0] = 0x054, + [AGG_MRCR] = 0x068, + [AGG_ATCR1] = 0x1a8, + [AGG_ATCR3] = 0x080, + [LPON_UTTR0] = 0x360, + [LPON_UTTR1] = 0x364, + [LPON_FRCR] = 0x37c, + [MIB_SDR3] = 0x698, + [MIB_SDR4] = 0x788, + [MIB_SDR5] = 0x780, + [MIB_SDR7] = 0x5a8, + [MIB_SDR8] = 0x78c, + [MIB_SDR9] = 0x024, + [MIB_SDR10] = 0x76c, + [MIB_SDR11] = 0x790, + [MIB_SDR12] = 0x558, + [MIB_SDR13] = 0x560, + [MIB_SDR14] = 0x564, + [MIB_SDR15] = 0x568, + [MIB_SDR16] = 0x7fc, + [MIB_SDR17] = 0x800, + [MIB_SDR18] = 0x030, + [MIB_SDR19] = 0x5ac, + [MIB_SDR20] = 0x5b0, + [MIB_SDR21] = 0x5b4, + [MIB_SDR22] = 0x770, + [MIB_SDR23] = 0x774, + [MIB_SDR24] = 0x778, + [MIB_SDR25] = 0x77c, + [MIB_SDR27] = 0x080, + [MIB_SDR28] = 0x084, + [MIB_SDR29] = 0x650, + [MIB_SDRVEC] = 0x5a8, + [MIB_SDR31] = 0x55c, + [MIB_SDR32] = 0x7a8, + [MIB_SDRMUBF] = 0x7ac, + [MIB_DR8] = 0x56c, + [MIB_DR9] = 0x570, + [MIB_DR11] = 0x574, + [MIB_MB_SDR0] = 0x688, + [MIB_MB_SDR1] = 0x690, + [TX_AGG_CNT] = 0x7dc, + [TX_AGG_CNT2] = 0x7ec, + [MIB_ARNG] = 0x0b0, + [WTBLON_TOP_WDUCR] = 0x200, + [WTBL_UPDATE] = 0x230, + [PLE_FL_Q_EMPTY] = 0x360, + [PLE_FL_Q_CTRL] = 0x3e0, + [PLE_AC_QEMPTY] = 0x600, + [PLE_FREEPG_CNT] = 0x380, + [PLE_FREEPG_HEAD_TAIL] = 0x384, + [PLE_PG_HIF_GROUP] = 0x00c, + [PLE_HIF_PG_INFO] = 0x388, + [AC_OFFSET] = 0x080, + [ETBF_PAR_RPT0] = 0x100, +}; + +static const struct __map mt7915_reg_map[] = { + { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ + { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure regs) */ + { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */ + { 0x54000000, 0x02000, 0x1000 }, /* WFDMA PCIE0 MCU DMA0 */ + { 0x55000000, 0x03000, 0x1000 }, /* WFDMA PCIE0 MCU DMA1 */ + { 0x58000000, 0x06000, 0x1000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ + { 0x59000000, 0x07000, 0x1000 }, /* WFDMA PCIE1 MCU DMA1 */ + { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */ + { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, WFDMA */ + { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ + { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ + { 0x820c0000, 0x08000, 0x4000 }, /* WF_UMAC_TOP (PLE) */ + { 0x820c8000, 0x0c000, 0x2000 }, /* WF_UMAC_TOP (PSE) */ + { 0x820cc000, 0x0e000, 0x2000 }, /* WF_UMAC_TOP (PP) */ + { 0x820ce000, 0x21c00, 0x0200 }, /* WF_LMAC_TOP (WF_SEC) */ + { 0x820cf000, 0x22000, 0x1000 }, /* WF_LMAC_TOP (WF_PF) */ + { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ + { 0x820e0000, 0x20000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ + { 0x820e1000, 0x20400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ + { 0x820e2000, 0x20800, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ + { 0x820e3000, 0x20c00, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ + { 0x820e4000, 0x21000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + { 0x820e5000, 0x21400, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + { 0x820e7000, 0x21e00, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ + { 0x820e9000, 0x23400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + { 0x820ea000, 0x24000, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + { 0x820eb000, 0x24200, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ + { 0x820ec000, 0x24600, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ + { 0x820ed000, 0x24800, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ + { 0x820f0000, 0xa0000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ + { 0x820f1000, 0xa0600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ + { 0x820f2000, 0xa0800, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ + { 0x820f3000, 0xa0c00, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ + { 0x820f4000, 0xa1000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + { 0x820f5000, 0xa1400, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + { 0x820f7000, 0xa1e00, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ + { 0x820f9000, 0xa3400, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + { 0x820fa000, 0xa4000, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + { 0x820fb000, 0xa4200, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ + { 0x820fc000, 0xa4600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ + { 0x820fd000, 0xa4800, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ + { 0x0, 0x0, 0x0 }, /* imply end of search */ +}; + +static const struct __map mt7916_reg_map[] = { + { 0x54000000, 0x02000, 0x1000 }, /* WFDMA_0 (PCIE0 MCU DMA0) */ + { 0x55000000, 0x03000, 0x1000 }, /* WFDMA_1 (PCIE0 MCU DMA1) */ + { 0x56000000, 0x04000, 0x1000 }, /* WFDMA_2 (Reserved) */ + { 0x57000000, 0x05000, 0x1000 }, /* WFDMA_3 (MCU wrap CR) */ + { 0x58000000, 0x06000, 0x1000 }, /* WFDMA_4 (PCIE1 MCU DMA0) */ + { 0x59000000, 0x07000, 0x1000 }, /* WFDMA_5 (PCIE1 MCU DMA1) */ + { 0x820c0000, 0x08000, 0x4000 }, /* WF_UMAC_TOP (PLE) */ + { 0x820c8000, 0x0c000, 0x2000 }, /* WF_UMAC_TOP (PSE) */ + { 0x820cc000, 0x0e000, 0x2000 }, /* WF_UMAC_TOP (PP) */ + { 0x820e0000, 0x20000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ + { 0x820e1000, 0x20400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ + { 0x820e2000, 0x20800, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ + { 0x820e3000, 0x20c00, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ + { 0x820e4000, 0x21000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + { 0x820e5000, 0x21400, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + { 0x820ce000, 0x21c00, 0x0200 }, /* WF_LMAC_TOP (WF_SEC) */ + { 0x820e7000, 0x21e00, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ + { 0x820cf000, 0x22000, 0x1000 }, /* WF_LMAC_TOP (WF_PF) */ + { 0x820e9000, 0x23400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + { 0x820ea000, 0x24000, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + { 0x820eb000, 0x24200, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ + { 0x820ec000, 0x24600, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ + { 0x820ed000, 0x24800, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ + { 0x820ca000, 0x26000, 0x2000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ + { 0x820d0000, 0x30000, 0x10000}, /* WF_LMAC_TOP (WF_WTBLON) */ + { 0x00400000, 0x80000, 0x10000}, /* WF_MCU_SYSRAM */ + { 0x00410000, 0x90000, 0x10000}, /* WF_MCU_SYSRAM (configure cr) */ + { 0x820f0000, 0xa0000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ + { 0x820f1000, 0xa0600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ + { 0x820f2000, 0xa0800, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ + { 0x820f3000, 0xa0c00, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ + { 0x820f4000, 0xa1000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + { 0x820f5000, 0xa1400, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + { 0x820f7000, 0xa1e00, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ + { 0x820f9000, 0xa3400, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + { 0x820fa000, 0xa4000, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + { 0x820fb000, 0xa4200, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ + { 0x820fc000, 0xa4600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ + { 0x820fd000, 0xa4800, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ + { 0x820c4000, 0xa8000, 0x1000 }, /* WF_LMAC_TOP (WF_UWTBL ) */ + { 0x820b0000, 0xae000, 0x1000 }, /* [APB2] WFSYS_ON */ + { 0x80020000, 0xb0000, 0x10000}, /* WF_TOP_MISC_OFF */ + { 0x81020000, 0xc0000, 0x10000}, /* WF_TOP_MISC_ON */ + { 0x0, 0x0, 0x0 }, /* imply end of search */ +}; + +static const struct __map mt7986_reg_map[] = { + { 0x54000000, 0x402000, 0x1000 }, /* WFDMA_0 (PCIE0 MCU DMA0) */ + { 0x55000000, 0x403000, 0x1000 }, /* WFDMA_1 (PCIE0 MCU DMA1) */ + { 0x56000000, 0x404000, 0x1000 }, /* WFDMA_2 (Reserved) */ + { 0x57000000, 0x405000, 0x1000 }, /* WFDMA_3 (MCU wrap CR) */ + { 0x58000000, 0x406000, 0x1000 }, /* WFDMA_4 (PCIE1 MCU DMA0) */ + { 0x59000000, 0x407000, 0x1000 }, /* WFDMA_5 (PCIE1 MCU DMA1) */ + { 0x820c0000, 0x408000, 0x4000 }, /* WF_UMAC_TOP (PLE) */ + { 0x820c8000, 0x40c000, 0x2000 }, /* WF_UMAC_TOP (PSE) */ + { 0x820cc000, 0x40e000, 0x2000 }, /* WF_UMAC_TOP (PP) */ + { 0x820e0000, 0x420000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ + { 0x820e1000, 0x420400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ + { 0x820e2000, 0x420800, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ + { 0x820e3000, 0x420c00, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ + { 0x820e4000, 0x421000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + { 0x820e5000, 0x421400, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + { 0x820ce000, 0x421c00, 0x0200 }, /* WF_LMAC_TOP (WF_SEC) */ + { 0x820e7000, 0x421e00, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ + { 0x820cf000, 0x422000, 0x1000 }, /* WF_LMAC_TOP (WF_PF) */ + { 0x820e9000, 0x423400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + { 0x820ea000, 0x424000, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + { 0x820eb000, 0x424200, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ + { 0x820ec000, 0x424600, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ + { 0x820ed000, 0x424800, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ + { 0x820ca000, 0x426000, 0x2000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ + { 0x820d0000, 0x430000, 0x10000}, /* WF_LMAC_TOP (WF_WTBLON) */ + { 0x00400000, 0x480000, 0x10000}, /* WF_MCU_SYSRAM */ + { 0x00410000, 0x490000, 0x10000}, /* WF_MCU_SYSRAM */ + { 0x820f0000, 0x4a0000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ + { 0x820f1000, 0x4a0600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ + { 0x820f2000, 0x4a0800, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ + { 0x820f3000, 0x4a0c00, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ + { 0x820f4000, 0x4a1000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + { 0x820f5000, 0x4a1400, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + { 0x820f7000, 0x4a1e00, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ + { 0x820f9000, 0x4a3400, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + { 0x820fa000, 0x4a4000, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + { 0x820fb000, 0x4a4200, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ + { 0x820fc000, 0x4a4600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ + { 0x820fd000, 0x4a4800, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ + { 0x820c4000, 0x4a8000, 0x1000 }, /* WF_LMAC_TOP (WF_UWTBL ) */ + { 0x820b0000, 0x4ae000, 0x1000 }, /* [APB2] WFSYS_ON */ + { 0x80020000, 0x4b0000, 0x10000}, /* WF_TOP_MISC_OFF */ + { 0x81020000, 0x4c0000, 0x10000}, /* WF_TOP_MISC_ON */ + { 0x89000000, 0x4d0000, 0x1000 }, /* WF_MCU_CFG_ON */ + { 0x89010000, 0x4d1000, 0x1000 }, /* WF_MCU_CIRQ */ + { 0x89020000, 0x4d2000, 0x1000 }, /* WF_MCU_GPT */ + { 0x89030000, 0x4d3000, 0x1000 }, /* WF_MCU_WDT */ + { 0x80010000, 0x4d4000, 0x1000 }, /* WF_AXIDMA */ + { 0x0, 0x0, 0x0 }, /* imply end of search */ +}; static u32 mt7915_reg_map_l1(struct mt7915_dev *dev, u32 addr) { u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr); u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr); + u32 l1_remap; - mt76_rmw_field(dev, MT_HIF_REMAP_L1, MT_HIF_REMAP_L1_MASK, base); + if (is_mt7986(&dev->mt76)) + return MT_CONN_INFRA_OFFSET(addr); + + l1_remap = is_mt7915(&dev->mt76) ? + MT_HIF_REMAP_L1 : MT_HIF_REMAP_L1_MT7916; + + dev->bus_ops->rmw(&dev->mt76, l1_remap, + MT_HIF_REMAP_L1_MASK, + FIELD_PREP(MT_HIF_REMAP_L1_MASK, base)); /* use read to push write */ - mt76_rr(dev, MT_HIF_REMAP_L1); + dev->bus_ops->rr(&dev->mt76, l1_remap); return MT_HIF_REMAP_BASE_L1 + offset; } static u32 mt7915_reg_map_l2(struct mt7915_dev *dev, u32 addr) { - u32 offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr); - u32 base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr); + u32 offset, base; - mt76_rmw_field(dev, MT_HIF_REMAP_L2, MT_HIF_REMAP_L2_MASK, base); - /* use read to push write */ - mt76_rr(dev, MT_HIF_REMAP_L2); + if (is_mt7915(&dev->mt76)) { + offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr); + base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr); - return MT_HIF_REMAP_BASE_L2 + offset; + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2, + MT_HIF_REMAP_L2_MASK, + FIELD_PREP(MT_HIF_REMAP_L2_MASK, base)); + + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2); + } else { + u32 ofs = is_mt7986(&dev->mt76) ? 0x400000 : 0; + + offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET_MT7916, addr); + base = FIELD_GET(MT_HIF_REMAP_L2_BASE_MT7916, addr); + + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2_MT7916 + ofs, + MT_HIF_REMAP_L2_MASK_MT7916, + FIELD_PREP(MT_HIF_REMAP_L2_MASK_MT7916, base)); + + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2_MT7916 + ofs); + + offset += (MT_HIF_REMAP_BASE_L2_MT7916 + ofs); + } + + return offset; } static u32 __mt7915_reg_addr(struct mt7915_dev *dev, u32 addr) { - static const struct { - u32 phys; - u32 mapped; - u32 size; - } fixed_map[] = { - { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ - { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure regs) */ - { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */ - { 0x54000000, 0x02000, 0x1000 }, /* WFDMA PCIE0 MCU DMA0 */ - { 0x55000000, 0x03000, 0x1000 }, /* WFDMA PCIE0 MCU DMA1 */ - { 0x58000000, 0x06000, 0x1000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ - { 0x59000000, 0x07000, 0x1000 }, /* WFDMA PCIE1 MCU DMA1 */ - { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */ - { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, WFDMA */ - { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ - { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ - { 0x820c0000, 0x08000, 0x4000 }, /* WF_UMAC_TOP (PLE) */ - { 0x820c8000, 0x0c000, 0x2000 }, /* WF_UMAC_TOP (PSE) */ - { 0x820cc000, 0x0e000, 0x2000 }, /* WF_UMAC_TOP (PP) */ - { 0x820ce000, 0x21c00, 0x0200 }, /* WF_LMAC_TOP (WF_SEC) */ - { 0x820cf000, 0x22000, 0x1000 }, /* WF_LMAC_TOP (WF_PF) */ - { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ - { 0x820e0000, 0x20000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ - { 0x820e1000, 0x20400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ - { 0x820e2000, 0x20800, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ - { 0x820e3000, 0x20c00, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ - { 0x820e4000, 0x21000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ - { 0x820e5000, 0x21400, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ - { 0x820e7000, 0x21e00, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ - { 0x820e9000, 0x23400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ - { 0x820ea000, 0x24000, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ - { 0x820eb000, 0x24200, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ - { 0x820ec000, 0x24600, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ - { 0x820ed000, 0x24800, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ - { 0x820f0000, 0xa0000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ - { 0x820f1000, 0xa0600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ - { 0x820f2000, 0xa0800, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ - { 0x820f3000, 0xa0c00, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ - { 0x820f4000, 0xa1000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ - { 0x820f5000, 0xa1400, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ - { 0x820f7000, 0xa1e00, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ - { 0x820f9000, 0xa3400, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ - { 0x820fa000, 0xa4000, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ - { 0x820fb000, 0xa4200, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ - { 0x820fc000, 0xa4600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ - { 0x820fd000, 0xa4800, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ - }; int i; if (addr < 0x100000) return addr; - for (i = 0; i < ARRAY_SIZE(fixed_map); i++) { - u32 ofs; - - if (addr < fixed_map[i].phys) - continue; - - ofs = addr - fixed_map[i].phys; - if (ofs > fixed_map[i].size) - continue; - - return fixed_map[i].mapped + ofs; + if (!dev->reg.map) { + dev_err(dev->mt76.dev, "err: reg_map is null\n"); + return addr; } - if ((addr >= 0x18000000 && addr < 0x18c00000) || - (addr >= 0x70000000 && addr < 0x78000000)) + for (i = 0; i < dev->reg.map_size; i++) { + u32 ofs; + + if (addr < dev->reg.map[i].phys) + continue; + + ofs = addr - dev->reg.map[i].phys; + if (ofs > dev->reg.map[i].size) + continue; + + return dev->reg.map[i].maps + ofs; + } + + if ((addr >= MT_INFRA_BASE && addr < MT_WFSYS0_PHY_START) || + (addr >= MT_WFSYS0_PHY_START && addr < MT_WFSYS1_PHY_START) || + (addr >= MT_WFSYS1_PHY_START && addr <= MT_WFSYS1_PHY_END)) return mt7915_reg_map_l1(dev, addr); + if (dev_is_pci(dev->mt76.dev) && + ((addr >= MT_CBTOP1_PHY_START && addr <= MT_CBTOP1_PHY_END) || + (addr >= MT_CBTOP2_PHY_START && addr <= MT_CBTOP2_PHY_END))) + return mt7915_reg_map_l1(dev, addr); + + /* CONN_INFRA: covert to phyiscal addr and use layer 1 remap */ + if (addr >= MT_INFRA_MCU_START && addr <= MT_INFRA_MCU_END) { + addr = addr - MT_INFRA_MCU_START + MT_INFRA_BASE; + return mt7915_reg_map_l1(dev, addr); + } + return mt7915_reg_map_l2(dev, addr); } @@ -125,7 +464,9 @@ static u32 mt7915_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) return dev->bus_ops->rmw(mdev, addr, mask, val); } -int mt7915_mmio_init(struct mt76_dev *mdev, void __iomem *mem_base, int irq) +static int mt7915_mmio_init(struct mt76_dev *mdev, + void __iomem *mem_base, + u32 device_id) { struct mt76_bus_ops *bus_ops; struct mt7915_dev *dev; @@ -133,6 +474,29 @@ int mt7915_mmio_init(struct mt76_dev *mdev, void __iomem *mem_base, int irq) dev = container_of(mdev, struct mt7915_dev, mt76); mt76_mmio_init(&dev->mt76, mem_base); + switch (device_id) { + case 0x7915: + dev->reg.reg_rev = mt7915_reg; + dev->reg.offs_rev = mt7915_offs; + dev->reg.map = mt7915_reg_map; + dev->reg.map_size = ARRAY_SIZE(mt7915_reg_map); + break; + case 0x7906: + dev->reg.reg_rev = mt7916_reg; + dev->reg.offs_rev = mt7916_offs; + dev->reg.map = mt7916_reg_map; + dev->reg.map_size = ARRAY_SIZE(mt7916_reg_map); + break; + case 0x7986: + dev->reg.reg_rev = mt7986_reg; + dev->reg.offs_rev = mt7916_offs; + dev->reg.map = mt7986_reg_map; + dev->reg.map_size = ARRAY_SIZE(mt7986_reg_map); + break; + default: + return -EINVAL; + } + dev->bus_ops = dev->mt76.bus; bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), GFP_KERNEL); @@ -144,11 +508,210 @@ int mt7915_mmio_init(struct mt76_dev *mdev, void __iomem *mem_base, int irq) bus_ops->rmw = mt7915_rmw; dev->mt76.bus = bus_ops; - mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + mdev->rev = (device_id << 16) | (mt76_rr(dev, MT_HW_REV) & 0xff); dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); - mt76_wr(dev, MT_INT_MASK_CSR, 0); - return 0; } + +void mt7915_dual_hif_set_irq_mask(struct mt7915_dev *dev, + bool write_reg, + u32 clear, u32 set) +{ + struct mt76_dev *mdev = &dev->mt76; + unsigned long flags; + + spin_lock_irqsave(&mdev->mmio.irq_lock, flags); + + mdev->mmio.irqmask &= ~clear; + mdev->mmio.irqmask |= set; + + if (write_reg) { + mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); + mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask); + } + + spin_unlock_irqrestore(&mdev->mmio.irq_lock, flags); +} + +static void mt7915_rx_poll_complete(struct mt76_dev *mdev, + enum mt76_rxq_id q) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + + mt7915_irq_enable(dev, MT_INT_RX(q)); +} + +/* TODO: support 2/4/6/8 MSI-X vectors */ +static void mt7915_irq_tasklet(struct tasklet_struct *t) +{ + struct mt7915_dev *dev = from_tasklet(dev, t, irq_tasklet); + u32 intr, intr1, mask; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + if (dev->hif2) + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + + intr = mt76_rr(dev, MT_INT_SOURCE_CSR); + intr &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_INT_SOURCE_CSR, intr); + + if (dev->hif2) { + intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR); + intr1 &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1); + + intr |= intr1; + } + + trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); + + mask = intr & MT_INT_RX_DONE_ALL; + if (intr & MT_INT_TX_DONE_MCU) + mask |= MT_INT_TX_DONE_MCU; + + mt7915_irq_disable(dev, mask); + + if (intr & MT_INT_TX_DONE_MCU) + napi_schedule(&dev->mt76.tx_napi); + + if (intr & MT_INT_RX(MT_RXQ_MAIN)) + napi_schedule(&dev->mt76.napi[MT_RXQ_MAIN]); + + if (intr & MT_INT_RX(MT_RXQ_EXT)) + napi_schedule(&dev->mt76.napi[MT_RXQ_EXT]); + + if (intr & MT_INT_RX(MT_RXQ_MCU)) + napi_schedule(&dev->mt76.napi[MT_RXQ_MCU]); + + if (intr & MT_INT_RX(MT_RXQ_MCU_WA)) + napi_schedule(&dev->mt76.napi[MT_RXQ_MCU_WA]); + + if (!is_mt7915(&dev->mt76) && + (intr & MT_INT_RX(MT_RXQ_MAIN_WA))) + napi_schedule(&dev->mt76.napi[MT_RXQ_MAIN_WA]); + + if (intr & MT_INT_RX(MT_RXQ_EXT_WA)) + napi_schedule(&dev->mt76.napi[MT_RXQ_EXT_WA]); + + if (intr & MT_INT_MCU_CMD) { + u32 val = mt76_rr(dev, MT_MCU_CMD); + + mt76_wr(dev, MT_MCU_CMD, val); + if (val & MT_MCU_CMD_ERROR_MASK) { + dev->reset_state = val; + ieee80211_queue_work(mt76_hw(dev), &dev->reset_work); + wake_up(&dev->reset_wait); + } + } +} + +irqreturn_t mt7915_irq_handler(int irq, void *dev_instance) +{ + struct mt7915_dev *dev = dev_instance; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + if (dev->hif2) + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + return IRQ_NONE; + + tasklet_schedule(&dev->irq_tasklet); + + return IRQ_HANDLED; +} + +struct mt7915_dev *mt7915_mmio_probe(struct device *pdev, + void __iomem *mem_base, u32 device_id) +{ + static const struct mt76_driver_ops drv_ops = { + /* txwi_size = txd size + txp size */ + .txwi_size = MT_TXD_SIZE + sizeof(struct mt7915_txp), + .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ, + .survey_flags = SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BSS_RX, + .token_size = MT7915_TOKEN_SIZE, + .tx_prepare_skb = mt7915_tx_prepare_skb, + .tx_complete_skb = mt7915_tx_complete_skb, + .rx_skb = mt7915_queue_rx_skb, + .rx_check = mt7915_rx_check, + .rx_poll_complete = mt7915_rx_poll_complete, + .sta_ps = mt7915_sta_ps, + .sta_add = mt7915_mac_sta_add, + .sta_remove = mt7915_mac_sta_remove, + .update_survey = mt7915_update_channel, + }; + struct ieee80211_ops *ops; + struct mt7915_dev *dev; + struct mt76_dev *mdev; + int ret; + + ops = devm_kmemdup(pdev, &mt7915_ops, sizeof(mt7915_ops), GFP_KERNEL); + if (!ops) + return ERR_PTR(-ENOMEM); + + mdev = mt76_alloc_device(pdev, sizeof(*dev), ops, &drv_ops); + if (!mdev) + return ERR_PTR(-ENOMEM); + + dev = container_of(mdev, struct mt7915_dev, mt76); + + ret = mt7915_mmio_init(mdev, mem_base, device_id); + if (ret) + goto error; + + tasklet_setup(&dev->irq_tasklet, mt7915_irq_tasklet); + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + return dev; + +error: + mt76_free_device(&dev->mt76); + + return ERR_PTR(ret); +} + +static int __init mt7915_init(void) +{ + int ret; + + ret = pci_register_driver(&mt7915_hif_driver); + if (ret) + return ret; + + ret = pci_register_driver(&mt7915_pci_driver); + if (ret) + goto error_pci; + + if (IS_ENABLED(CONFIG_MT7986_WMAC)) { + ret = platform_driver_register(&mt7986_wmac_driver); + if (ret) + goto error_wmac; + } + + return 0; + +error_wmac: + pci_unregister_driver(&mt7915_pci_driver); +error_pci: + pci_unregister_driver(&mt7915_hif_driver); + + return ret; +} + +static void __exit mt7915_exit(void) +{ + if (IS_ENABLED(CONFIG_MT7986_WMAC)) + platform_driver_unregister(&mt7986_wmac_driver); + + pci_unregister_driver(&mt7915_pci_driver); + pci_unregister_driver(&mt7915_hif_driver); +} + +module_init(mt7915_init); +module_exit(mt7915_exit); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 42d887383e8d..6efa0a2e2345 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -6,13 +6,14 @@ #include #include -#include "../mt76.h" +#include "../mt76_connac.h" #include "regs.h" #define MT7915_MAX_INTERFACES 19 #define MT7915_MAX_WMM_SETS 4 #define MT7915_WTBL_SIZE 288 -#define MT7915_WTBL_RESERVED (MT7915_WTBL_SIZE - 1) +#define MT7916_WTBL_SIZE 544 +#define MT7915_WTBL_RESERVED (mt7915_wtbl_size(dev) - 1) #define MT7915_WTBL_STA (MT7915_WTBL_RESERVED - \ MT7915_MAX_INTERFACES) @@ -30,10 +31,28 @@ #define MT7915_FIRMWARE_WM "mediatek/mt7915_wm.bin" #define MT7915_ROM_PATCH "mediatek/mt7915_rom_patch.bin" +#define MT7916_FIRMWARE_WA "mediatek/mt7916_wa.bin" +#define MT7916_FIRMWARE_WM "mediatek/mt7916_wm.bin" +#define MT7916_ROM_PATCH "mediatek/mt7916_rom_patch.bin" + +#define MT7986_FIRMWARE_WA "mediatek/mt7986_wa.bin" +#define MT7986_FIRMWARE_WM "mediatek/mt7986_wm.bin" +#define MT7986_FIRMWARE_WM_MT7975 "mediatek/mt7986_wm_mt7975.bin" +#define MT7986_ROM_PATCH "mediatek/mt7986_rom_patch.bin" +#define MT7986_ROM_PATCH_MT7975 "mediatek/mt7986_rom_patch_mt7975.bin" + #define MT7915_EEPROM_DEFAULT "mediatek/mt7915_eeprom.bin" #define MT7915_EEPROM_DEFAULT_DBDC "mediatek/mt7915_eeprom_dbdc.bin" +#define MT7916_EEPROM_DEFAULT "mediatek/mt7916_eeprom.bin" +#define MT7986_EEPROM_MT7975_DEFAULT "mediatek/mt7986_eeprom_mt7975.bin" +#define MT7986_EEPROM_MT7975_DUAL_DEFAULT "mediatek/mt7986_eeprom_mt7975_dual.bin" +#define MT7986_EEPROM_MT7976_DEFAULT "mediatek/mt7986_eeprom_mt7976.bin" +#define MT7986_EEPROM_MT7976_DEFAULT_DBDC "mediatek/mt7986_eeprom_mt7976_dbdc.bin" +#define MT7986_EEPROM_MT7976_DUAL_DEFAULT "mediatek/mt7986_eeprom_mt7976_dual.bin" #define MT7915_EEPROM_SIZE 3584 +#define MT7916_EEPROM_SIZE 4096 + #define MT7915_EEPROM_BLOCK_SIZE 16 #define MT7915_TOKEN_SIZE 8192 @@ -41,11 +60,13 @@ #define MT7915_CFEND_RATE_11B 0x03 /* 11B LP, 11M */ #define MT7915_THERMAL_THROTTLE_MAX 100 +#define MT7915_CDEV_THROTTLE_MAX 99 #define MT7915_SKU_RATE_NUM 161 #define MT7915_MAX_TWT_AGRT 16 #define MT7915_MAX_STA_TWT_AGRT 8 +#define MT7915_MAX_QUEUE (__MT_RXQ_MAX + __MT_MCUQ_MAX + 2) struct mt7915_vif; struct mt7915_sta; @@ -68,9 +89,13 @@ enum mt7915_rxq_id { MT7915_RXQ_MCU_WA_EXT, }; -struct mt7915_sta_key_conf { - s8 keyidx; - u8 key[16]; +enum mt7916_rxq_id { + MT7916_RXQ_MCU_WM = 0, + MT7916_RXQ_MCU_WA, + MT7916_RXQ_MCU_WA_MAIN, + MT7916_RXQ_MCU_WA_EXT, + MT7916_RXQ_BAND0, + MT7916_RXQ_BAND1, }; struct mt7915_twt_flow { @@ -104,7 +129,7 @@ struct mt7915_sta { struct mt76_sta_stats stats; - struct mt7915_sta_key_conf bip; + struct mt76_connac_sta_key_conf bip; struct { u8 flowid_mask; @@ -113,7 +138,9 @@ struct mt7915_sta { }; struct mt7915_vif_cap { - bool ldpc:1; + bool ht_ldpc:1; + bool vht_ldpc:1; + bool he_ldpc:1; bool vht_su_ebfer:1; bool vht_su_ebfee:1; bool vht_mu_ebfer:1; @@ -200,16 +227,18 @@ struct mt7915_phy { struct mt76_phy *mt76; struct mt7915_dev *dev; - struct ieee80211_sband_iftype_data iftype[2][NUM_NL80211_IFTYPES]; + struct ieee80211_sband_iftype_data iftype[NUM_NL80211_BANDS][NUM_NL80211_IFTYPES]; struct ieee80211_vif *monitor_vif; struct thermal_cooling_device *cdev; + u8 cdev_state; u8 throttle_state; u32 throttle_temp[2]; /* 0: critical high, 1: maximum */ u32 rxfilter; u64 omac_mask; + u8 band_idx; u16 noise; @@ -217,7 +246,6 @@ struct mt7915_phy { u8 slottime; u8 rdd_state; - int dfs_state; u32 rx_ampdu_ts; u32 ampdu_ref; @@ -247,12 +275,21 @@ struct mt7915_dev { }; struct mt7915_hif *hif2; + struct mt7915_reg_desc reg; + u8 q_id[MT7915_MAX_QUEUE]; + u32 q_int_mask[MT7915_MAX_QUEUE]; + u32 wfdma_mask; const struct mt76_bus_ops *bus_ops; struct tasklet_struct irq_tasklet; struct mt7915_phy phy; + /* monitor rx chain configured channel */ + struct cfg80211_chan_def rdd2_chandef; + struct mt7915_phy *rdd2_phy; + u16 chainmask; + u16 chainshift; u32 hif_idx; struct work_struct init_work; @@ -274,6 +311,10 @@ struct mt7915_dev { bool ibf; u8 fw_debug_wm; u8 fw_debug_wa; + u8 fw_debug_bin; + + struct dentry *debugfs_dir; + struct rchan *relay_fwlog; void *cal; @@ -281,6 +322,17 @@ struct mt7915_dev { u8 table_mask; u8 n_agrt; } twt; + + struct reset_control *rstc; + void __iomem *dcm; + void __iomem *sku; +}; + +enum { + WFDMA0 = 0x0, + WFDMA1, + WFDMA_EXT, + __MT_WFDMA_MAX, }; enum { @@ -300,6 +352,7 @@ enum { enum { MT_RX_SEL0, MT_RX_SEL1, + MT_RX_SEL2, /* monitor chain */ }; enum mt7915_rdd_cmd { @@ -345,21 +398,44 @@ mt7915_ext_phy(struct mt7915_dev *dev) return phy->priv; } -static inline u8 mt7915_lmac_mapping(struct mt7915_dev *dev, u8 ac) +static inline u32 mt7915_check_adie(struct mt7915_dev *dev, bool sku) { - /* LMAC uses the reverse order of mac80211 AC indexes */ - return 3 - ac; + u32 mask = sku ? MT_CONNINFRA_SKU_MASK : MT_ADIE_TYPE_MASK; + + if (!is_mt7986(&dev->mt76)) + return 0; + + return mt76_rr(dev, MT_CONNINFRA_SKU_DEC_ADDR) & mask; } extern const struct ieee80211_ops mt7915_ops; extern const struct mt76_testmode_ops mt7915_testmode_ops; +extern struct pci_driver mt7915_pci_driver; +extern struct pci_driver mt7915_hif_driver; +extern struct platform_driver mt7986_wmac_driver; -u32 mt7915_reg_map(struct mt7915_dev *dev, u32 addr); +#ifdef CONFIG_MT7986_WMAC +int mt7986_wmac_enable(struct mt7915_dev *dev); +void mt7986_wmac_disable(struct mt7915_dev *dev); +#else +static inline int mt7986_wmac_enable(struct mt7915_dev *dev) +{ + return 0; +} + +static inline void mt7986_wmac_disable(struct mt7915_dev *dev) +{ +} +#endif +struct mt7915_dev *mt7915_mmio_probe(struct device *pdev, + void __iomem *mem_base, u32 device_id); +irqreturn_t mt7915_irq_handler(int irq, void *dev_instance); u64 __mt7915_get_tsf(struct ieee80211_hw *hw, struct mt7915_vif *mvif); int mt7915_register_device(struct mt7915_dev *dev); void mt7915_unregister_device(struct mt7915_dev *dev); int mt7915_eeprom_init(struct mt7915_dev *dev); -void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy); +void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, + struct mt7915_phy *phy); int mt7915_eeprom_get_target_power(struct mt7915_dev *dev, struct ieee80211_channel *chan, u8 chain_idx); @@ -378,18 +454,12 @@ int mt7915_mcu_add_bss_info(struct mt7915_phy *phy, struct ieee80211_vif *vif, int enable); int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, bool enable); -int mt7915_mcu_sta_update_hdr_trans(struct mt7915_dev *dev, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta); int mt7915_mcu_add_tx_ba(struct mt7915_dev *dev, struct ieee80211_ampdu_params *params, bool add); int mt7915_mcu_add_rx_ba(struct mt7915_dev *dev, struct ieee80211_ampdu_params *params, bool add); -int mt7915_mcu_add_key(struct mt7915_dev *dev, struct ieee80211_vif *vif, - struct mt7915_sta *msta, struct ieee80211_key_conf *key, - enum set_key_cmd cmd); int mt7915_mcu_update_bss_color(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct cfg80211_he_bss_color *he_bss_color); int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -417,8 +487,6 @@ int mt7915_mcu_set_test_param(struct mt7915_dev *dev, u8 param, bool test_mode, u8 en); int mt7915_mcu_set_scs(struct mt7915_dev *dev, u8 band, bool enable); int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band); -int mt7915_mcu_set_rts_thresh(struct mt7915_phy *phy, u32 val); -int mt7915_mcu_set_pm(struct mt7915_dev *dev, int band, int enter); int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable); int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy); int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len); @@ -436,17 +504,22 @@ int mt7915_mcu_get_temperature(struct mt7915_phy *phy); int mt7915_mcu_set_thermal_throttling(struct mt7915_phy *phy, u8 state); int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct rate_info *rate); -int mt7915_mcu_rdd_cmd(struct mt7915_dev *dev, enum mt7915_rdd_cmd cmd, - u8 index, u8 rx_sel, u8 val); +int mt7915_mcu_rdd_background_enable(struct mt7915_phy *phy, + struct cfg80211_chan_def *chandef); int mt7915_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3); int mt7915_mcu_fw_log_2_host(struct mt7915_dev *dev, u8 type, u8 ctrl); int mt7915_mcu_fw_dbg_ctrl(struct mt7915_dev *dev, u32 module, u8 level); void mt7915_mcu_rx_event(struct mt7915_dev *dev, struct sk_buff *skb); void mt7915_mcu_exit(struct mt7915_dev *dev); -static inline bool is_mt7915(struct mt76_dev *dev) +static inline u16 mt7915_wtbl_size(struct mt7915_dev *dev) { - return mt76_chip(dev) == 0x7915; + return is_mt7915(&dev->mt76) ? MT7915_WTBL_SIZE : MT7916_WTBL_SIZE; +} + +static inline u16 mt7915_eeprom_size(struct mt7915_dev *dev) +{ + return is_mt7915(&dev->mt76) ? MT7915_EEPROM_SIZE : MT7916_EEPROM_SIZE; } void mt7915_dual_hif_set_irq_mask(struct mt7915_dev *dev, bool write_reg, @@ -487,7 +560,6 @@ void mt7915_mac_work(struct work_struct *work); void mt7915_mac_reset_work(struct work_struct *work); void mt7915_mac_sta_rc_work(struct work_struct *work); void mt7915_mac_update_stats(struct mt7915_phy *phy); -int mt7915_mmio_init(struct mt76_dev *mdev, void __iomem *mem_base, int irq); void mt7915_mac_twt_teardown_flow(struct mt7915_dev *dev, struct mt7915_sta *msta, u8 flowid); @@ -500,7 +572,7 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, struct mt76_tx_info *tx_info); void mt7915_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e); void mt7915_tx_token_put(struct mt7915_dev *dev); -int mt7915_init_tx_queues(struct mt7915_phy *phy, int idx, int n_desc); +int mt7915_init_tx_queues(struct mt7915_phy *phy, int idx, int n_desc, int ring_base); void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, struct sk_buff *skb); bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len); @@ -514,6 +586,8 @@ void mt7915_update_channel(struct mt76_phy *mphy); int mt7915_mcu_muru_debug_set(struct mt7915_dev *dev, bool enable); int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy, void *ms); int mt7915_init_debugfs(struct mt7915_phy *phy); +void mt7915_debugfs_rx_fw_monitor(struct mt7915_dev *dev, const void *data, int len); +bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len); #ifdef CONFIG_MAC80211_DEBUGFS void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c index 8130ea43971f..6f819c41a4c4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c @@ -18,35 +18,17 @@ static u32 hif_idx; static const struct pci_device_id mt7915_pci_device_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7915) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7906) }, { }, }; static const struct pci_device_id mt7915_hif_device_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7916) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x790a) }, { }, }; -void mt7915_dual_hif_set_irq_mask(struct mt7915_dev *dev, bool write_reg, - u32 clear, u32 set) -{ - struct mt76_dev *mdev = &dev->mt76; - unsigned long flags; - - spin_lock_irqsave(&mdev->mmio.irq_lock, flags); - - mdev->mmio.irqmask &= ~clear; - mdev->mmio.irqmask |= set; - - if (write_reg) { - mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); - mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask); - } - - spin_unlock_irqrestore(&mdev->mmio.irq_lock, flags); -} - -static struct mt7915_hif * -mt7915_pci_get_hif2(struct mt7915_dev *dev) +static struct mt7915_hif *mt7915_pci_get_hif2(u32 idx) { struct mt7915_hif *hif; u32 val; @@ -56,7 +38,7 @@ mt7915_pci_get_hif2(struct mt7915_dev *dev) list_for_each_entry(hif, &hif_list, list) { val = readl(hif->regs + MT_PCIE_RECOG_ID); val &= MT_PCIE_RECOG_ID_MASK; - if (val != dev->hif_idx) + if (val != idx) continue; get_device(hif->dev); @@ -78,123 +60,17 @@ static void mt7915_put_hif2(struct mt7915_hif *hif) put_device(hif->dev); } -static void -mt7915_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) +static struct mt7915_hif *mt7915_pci_init_hif2(struct pci_dev *pdev) { - struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); - static const u32 rx_irq_mask[] = { - [MT_RXQ_MAIN] = MT_INT_RX_DONE_DATA0, - [MT_RXQ_EXT] = MT_INT_RX_DONE_DATA1, - [MT_RXQ_MCU] = MT_INT_RX_DONE_WM, - [MT_RXQ_MCU_WA] = MT_INT_RX_DONE_WA, - [MT_RXQ_EXT_WA] = MT_INT_RX_DONE_WA_EXT, - }; + hif_idx++; + if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7916, NULL) && + !pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x790a, NULL)) + return NULL; - mt7915_irq_enable(dev, rx_irq_mask[q]); -} + writel(hif_idx | MT_PCIE_RECOG_ID_SEM, + pcim_iomap_table(pdev)[0] + MT_PCIE_RECOG_ID); -/* TODO: support 2/4/6/8 MSI-X vectors */ -static void mt7915_irq_tasklet(struct tasklet_struct *t) -{ - struct mt7915_dev *dev = from_tasklet(dev, t, irq_tasklet); - u32 intr, intr1, mask; - - mt76_wr(dev, MT_INT_MASK_CSR, 0); - if (dev->hif2) - mt76_wr(dev, MT_INT1_MASK_CSR, 0); - - intr = mt76_rr(dev, MT_INT_SOURCE_CSR); - intr &= dev->mt76.mmio.irqmask; - mt76_wr(dev, MT_INT_SOURCE_CSR, intr); - - if (dev->hif2) { - intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR); - intr1 &= dev->mt76.mmio.irqmask; - mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1); - - intr |= intr1; - } - - trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); - - mask = intr & MT_INT_RX_DONE_ALL; - if (intr & MT_INT_TX_DONE_MCU) - mask |= MT_INT_TX_DONE_MCU; - - mt7915_irq_disable(dev, mask); - - if (intr & MT_INT_TX_DONE_MCU) - napi_schedule(&dev->mt76.tx_napi); - - if (intr & MT_INT_RX_DONE_DATA0) - napi_schedule(&dev->mt76.napi[MT_RXQ_MAIN]); - - if (intr & MT_INT_RX_DONE_DATA1) - napi_schedule(&dev->mt76.napi[MT_RXQ_EXT]); - - if (intr & MT_INT_RX_DONE_WM) - napi_schedule(&dev->mt76.napi[MT_RXQ_MCU]); - - if (intr & MT_INT_RX_DONE_WA) - napi_schedule(&dev->mt76.napi[MT_RXQ_MCU_WA]); - - if (intr & MT_INT_RX_DONE_WA_EXT) - napi_schedule(&dev->mt76.napi[MT_RXQ_EXT_WA]); - - if (intr & MT_INT_MCU_CMD) { - u32 val = mt76_rr(dev, MT_MCU_CMD); - - mt76_wr(dev, MT_MCU_CMD, val); - if (val & MT_MCU_CMD_ERROR_MASK) { - dev->reset_state = val; - ieee80211_queue_work(mt76_hw(dev), &dev->reset_work); - wake_up(&dev->reset_wait); - } - } -} - -static irqreturn_t mt7915_irq_handler(int irq, void *dev_instance) -{ - struct mt7915_dev *dev = dev_instance; - - mt76_wr(dev, MT_INT_MASK_CSR, 0); - if (dev->hif2) - mt76_wr(dev, MT_INT1_MASK_CSR, 0); - - if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) - return IRQ_NONE; - - tasklet_schedule(&dev->irq_tasklet); - - return IRQ_HANDLED; -} - -static void mt7915_pci_init_hif2(struct mt7915_dev *dev) -{ - struct mt7915_hif *hif; - - dev->hif_idx = ++hif_idx; - if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7916, NULL)) - return; - - mt76_wr(dev, MT_PCIE_RECOG_ID, dev->hif_idx | MT_PCIE_RECOG_ID_SEM); - - hif = mt7915_pci_get_hif2(dev); - if (!hif) - return; - - dev->hif2 = hif; - - mt76_wr(dev, MT_INT1_MASK_CSR, 0); - - if (devm_request_irq(dev->mt76.dev, hif->irq, mt7915_irq_handler, - IRQF_SHARED, KBUILD_MODNAME "-hif", dev)) { - mt7915_put_hif2(hif); - hif = NULL; - } - - /* master switch of PCIe tnterrupt enable */ - mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff); + return mt7915_pci_get_hif2(hif_idx); } static int mt7915_pci_hif2_probe(struct pci_dev *pdev) @@ -219,26 +95,10 @@ static int mt7915_pci_hif2_probe(struct pci_dev *pdev) static int mt7915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - static const struct mt76_driver_ops drv_ops = { - /* txwi_size = txd size + txp size */ - .txwi_size = MT_TXD_SIZE + sizeof(struct mt7915_txp), - .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ, - .survey_flags = SURVEY_INFO_TIME_TX | - SURVEY_INFO_TIME_RX | - SURVEY_INFO_TIME_BSS_RX, - .token_size = MT7915_TOKEN_SIZE, - .tx_prepare_skb = mt7915_tx_prepare_skb, - .tx_complete_skb = mt7915_tx_complete_skb, - .rx_skb = mt7915_queue_rx_skb, - .rx_check = mt7915_rx_check, - .rx_poll_complete = mt7915_rx_poll_complete, - .sta_ps = mt7915_sta_ps, - .sta_add = mt7915_mac_sta_add, - .sta_remove = mt7915_mac_sta_remove, - .update_survey = mt7915_update_channel, - }; struct mt7915_dev *dev; struct mt76_dev *mdev; + struct mt7915_hif *hif2; + int irq; int ret; ret = pcim_enable_device(pdev); @@ -257,48 +117,65 @@ static int mt7915_pci_probe(struct pci_dev *pdev, mt76_pci_disable_aspm(pdev); - if (id->device == 0x7916) + if (id->device == 0x7916 || id->device == 0x790a) return mt7915_pci_hif2_probe(pdev); - mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), &mt7915_ops, - &drv_ops); - if (!mdev) - return -ENOMEM; + dev = mt7915_mmio_probe(&pdev->dev, pcim_iomap_table(pdev)[0], + id->device); + if (IS_ERR(dev)) + return PTR_ERR(dev); - dev = container_of(mdev, struct mt7915_dev, mt76); + mdev = &dev->mt76; + hif2 = mt7915_pci_init_hif2(pdev); ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); if (ret < 0) - goto free; + goto free_device; - ret = mt7915_mmio_init(mdev, pcim_iomap_table(pdev)[0], pdev->irq); + irq = pdev->irq; + ret = devm_request_irq(mdev->dev, irq, mt7915_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); if (ret) - goto error; - - tasklet_setup(&dev->irq_tasklet, mt7915_irq_tasklet); + goto free_irq_vector; mt76_wr(dev, MT_INT_MASK_CSR, 0); /* master switch of PCIe tnterrupt enable */ mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); - ret = devm_request_irq(mdev->dev, pdev->irq, mt7915_irq_handler, - IRQF_SHARED, KBUILD_MODNAME, dev); - if (ret) - goto error; + if (hif2) { + dev->hif2 = hif2; - mt7915_pci_init_hif2(dev); + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + /* master switch of PCIe tnterrupt enable */ + if (is_mt7915(mdev)) + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff); + else + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE_MT7916, 0xff); + + ret = devm_request_irq(mdev->dev, dev->hif2->irq, + mt7915_irq_handler, IRQF_SHARED, + KBUILD_MODNAME "-hif", dev); + if (ret) + goto free_hif2; + } ret = mt7915_register_device(dev); if (ret) - goto free_irq; + goto free_hif2_irq; return 0; -free_irq: - devm_free_irq(mdev->dev, pdev->irq, dev); -error: + +free_hif2_irq: + if (dev->hif2) + devm_free_irq(mdev->dev, dev->hif2->irq, dev); +free_hif2: + if (dev->hif2) + put_device(dev->hif2->dev); + devm_free_irq(mdev->dev, irq, dev); +free_irq_vector: pci_free_irq_vectors(pdev); -free: +free_device: mt76_free_device(&dev->mt76); return ret; @@ -322,47 +199,25 @@ static void mt7915_pci_remove(struct pci_dev *pdev) mt7915_unregister_device(dev); } -static struct pci_driver mt7915_hif_driver = { +struct pci_driver mt7915_hif_driver = { .name = KBUILD_MODNAME "_hif", .id_table = mt7915_hif_device_table, .probe = mt7915_pci_probe, .remove = mt7915_hif_remove, }; -static struct pci_driver mt7915_pci_driver = { +struct pci_driver mt7915_pci_driver = { .name = KBUILD_MODNAME, .id_table = mt7915_pci_device_table, .probe = mt7915_pci_probe, .remove = mt7915_pci_remove, }; -static int __init mt7915_init(void) -{ - int ret; - - ret = pci_register_driver(&mt7915_hif_driver); - if (ret) - return ret; - - ret = pci_register_driver(&mt7915_pci_driver); - if (ret) - pci_unregister_driver(&mt7915_hif_driver); - - return ret; -} - -static void __exit mt7915_exit(void) -{ - pci_unregister_driver(&mt7915_pci_driver); - pci_unregister_driver(&mt7915_hif_driver); -} - -module_init(mt7915_init); -module_exit(mt7915_exit); - MODULE_DEVICE_TABLE(pci, mt7915_pci_device_table); MODULE_DEVICE_TABLE(pci, mt7915_hif_device_table); MODULE_FIRMWARE(MT7915_FIRMWARE_WA); MODULE_FIRMWARE(MT7915_FIRMWARE_WM); MODULE_FIRMWARE(MT7915_ROM_PATCH); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_FIRMWARE(MT7916_FIRMWARE_WA); +MODULE_FIRMWARE(MT7916_FIRMWARE_WM); +MODULE_FIRMWARE(MT7916_ROM_PATCH); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 59693535b098..e5f93c40591c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -4,41 +4,152 @@ #ifndef __MT7915_REGS_H #define __MT7915_REGS_H +struct __map { + u32 phys; + u32 maps; + u32 size; +}; + +/* used to differentiate between generations */ +struct mt7915_reg_desc { + const u32 *reg_rev; + const u32 *offs_rev; + const struct __map *map; + u32 map_size; +}; + +enum reg_rev { + INT_SOURCE_CSR, + INT_MASK_CSR, + INT1_SOURCE_CSR, + INT1_MASK_CSR, + INT_MCU_CMD_SOURCE, + INT_MCU_CMD_EVENT, + WFDMA0_ADDR, + WFDMA0_PCIE1_ADDR, + WFDMA_EXT_CSR_ADDR, + CBTOP1_PHY_END, + INFRA_MCU_ADDR_END, + __MT_REG_MAX, +}; + +enum offs_rev { + TMAC_CDTR, + TMAC_ODTR, + TMAC_ATCR, + TMAC_TRCR0, + TMAC_ICR0, + TMAC_ICR1, + TMAC_CTCR0, + TMAC_TFCR0, + MDP_BNRCFR0, + MDP_BNRCFR1, + ARB_DRNGR0, + ARB_SCR, + RMAC_MIB_AIRTIME14, + AGG_AWSCR0, + AGG_PCR0, + AGG_ACR0, + AGG_MRCR, + AGG_ATCR1, + AGG_ATCR3, + LPON_UTTR0, + LPON_UTTR1, + LPON_FRCR, + MIB_SDR3, + MIB_SDR4, + MIB_SDR5, + MIB_SDR7, + MIB_SDR8, + MIB_SDR9, + MIB_SDR10, + MIB_SDR11, + MIB_SDR12, + MIB_SDR13, + MIB_SDR14, + MIB_SDR15, + MIB_SDR16, + MIB_SDR17, + MIB_SDR18, + MIB_SDR19, + MIB_SDR20, + MIB_SDR21, + MIB_SDR22, + MIB_SDR23, + MIB_SDR24, + MIB_SDR25, + MIB_SDR27, + MIB_SDR28, + MIB_SDR29, + MIB_SDRVEC, + MIB_SDR31, + MIB_SDR32, + MIB_SDRMUBF, + MIB_DR8, + MIB_DR9, + MIB_DR11, + MIB_MB_SDR0, + MIB_MB_SDR1, + TX_AGG_CNT, + TX_AGG_CNT2, + MIB_ARNG, + WTBLON_TOP_WDUCR, + WTBL_UPDATE, + PLE_FL_Q_EMPTY, + PLE_FL_Q_CTRL, + PLE_AC_QEMPTY, + PLE_FREEPG_CNT, + PLE_FREEPG_HEAD_TAIL, + PLE_PG_HIF_GROUP, + PLE_HIF_PG_INFO, + AC_OFFSET, + ETBF_PAR_RPT0, + __MT_OFFS_MAX, +}; + +#define __REG(id) (dev->reg.reg_rev[(id)]) +#define __OFFS(id) (dev->reg.offs_rev[(id)]) + /* MCU WFDMA0 */ #define MT_MCU_WFDMA0_BASE 0x2000 #define MT_MCU_WFDMA0(ofs) (MT_MCU_WFDMA0_BASE + (ofs)) + #define MT_MCU_WFDMA0_DUMMY_CR MT_MCU_WFDMA0(0x120) /* MCU WFDMA1 */ #define MT_MCU_WFDMA1_BASE 0x3000 #define MT_MCU_WFDMA1(ofs) (MT_MCU_WFDMA1_BASE + (ofs)) -#define MT_MCU_INT_EVENT MT_MCU_WFDMA1(0x108) +#define MT_MCU_INT_EVENT __REG(INT_MCU_CMD_EVENT) #define MT_MCU_INT_EVENT_DMA_STOPPED BIT(0) #define MT_MCU_INT_EVENT_DMA_INIT BIT(1) #define MT_MCU_INT_EVENT_SER_TRIGGER BIT(2) #define MT_MCU_INT_EVENT_RESET_DONE BIT(3) -#define MT_PLE_BASE 0x8000 +/* PLE */ +#define MT_PLE_BASE 0x820c0000 #define MT_PLE(ofs) (MT_PLE_BASE + (ofs)) -#define MT_FL_Q_EMPTY 0x0b0 -#define MT_FL_Q0_CTRL 0x1b0 -#define MT_FL_Q2_CTRL 0x1b8 -#define MT_FL_Q3_CTRL 0x1bc +#define MT_FL_Q_EMPTY MT_PLE(__OFFS(PLE_FL_Q_EMPTY)) +#define MT_FL_Q0_CTRL MT_PLE(__OFFS(PLE_FL_Q_CTRL)) +#define MT_FL_Q2_CTRL MT_PLE(__OFFS(PLE_FL_Q_CTRL) + 0x8) +#define MT_FL_Q3_CTRL MT_PLE(__OFFS(PLE_FL_Q_CTRL) + 0xc) -#define MT_PLE_FREEPG_CNT MT_PLE(0x100) -#define MT_PLE_FREEPG_HEAD_TAIL MT_PLE(0x104) -#define MT_PLE_PG_HIF_GROUP MT_PLE(0x110) -#define MT_PLE_HIF_PG_INFO MT_PLE(0x114) -#define MT_PLE_AC_QEMPTY(ac, n) MT_PLE(0x500 + 0x40 * (ac) + \ - ((n) << 2)) +#define MT_PLE_FREEPG_CNT MT_PLE(__OFFS(PLE_FREEPG_CNT)) +#define MT_PLE_FREEPG_HEAD_TAIL MT_PLE(__OFFS(PLE_FREEPG_HEAD_TAIL)) +#define MT_PLE_PG_HIF_GROUP MT_PLE(__OFFS(PLE_PG_HIF_GROUP)) +#define MT_PLE_HIF_PG_INFO MT_PLE(__OFFS(PLE_HIF_PG_INFO)) + +#define MT_PLE_AC_QEMPTY(ac, n) MT_PLE(__OFFS(PLE_AC_QEMPTY) + \ + __OFFS(AC_OFFSET) * \ + (ac) + ((n) << 2)) #define MT_PLE_AMSDU_PACK_MSDU_CNT(n) MT_PLE(0x10e0 + ((n) << 2)) -#define MT_PSE_BASE 0xc000 +#define MT_PSE_BASE 0x820c8000 #define MT_PSE(ofs) (MT_PSE_BASE + (ofs)) -#define MT_MDP_BASE 0xf000 +/* WF MDP TOP */ +#define MT_MDP_BASE 0x820cd000 #define MT_MDP(ofs) (MT_MDP_BASE + (ofs)) #define MT_MDP_DCR0 MT_MDP(0x000) @@ -47,73 +158,76 @@ #define MT_MDP_DCR1 MT_MDP(0x004) #define MT_MDP_DCR1_MAX_RX_LEN GENMASK(15, 3) -#define MT_MDP_BNRCFR0(_band) MT_MDP(0x070 + ((_band) << 8)) +#define MT_MDP_BNRCFR0(_band) MT_MDP(__OFFS(MDP_BNRCFR0) + \ + ((_band) << 8)) #define MT_MDP_RCFR0_MCU_RX_MGMT GENMASK(5, 4) #define MT_MDP_RCFR0_MCU_RX_CTL_NON_BAR GENMASK(7, 6) #define MT_MDP_RCFR0_MCU_RX_CTL_BAR GENMASK(9, 8) -#define MT_MDP_BNRCFR1(_band) MT_MDP(0x074 + ((_band) << 8)) +#define MT_MDP_BNRCFR1(_band) MT_MDP(__OFFS(MDP_BNRCFR1) + \ + ((_band) << 8)) #define MT_MDP_RCFR1_MCU_RX_BYPASS GENMASK(23, 22) #define MT_MDP_RCFR1_RX_DROPPED_UCAST GENMASK(28, 27) #define MT_MDP_RCFR1_RX_DROPPED_MCAST GENMASK(30, 29) #define MT_MDP_TO_HIF 0 #define MT_MDP_TO_WM 1 -/* TMAC: band 0(0x21000), band 1(0xa1000) */ -#define MT_WF_TMAC_BASE(_band) ((_band) ? 0xa1000 : 0x21000) +/* TMAC: band 0(0x820e4000), band 1(0x820f4000) */ +#define MT_WF_TMAC_BASE(_band) ((_band) ? 0x820f4000 : 0x820e4000) #define MT_WF_TMAC(_band, ofs) (MT_WF_TMAC_BASE(_band) + (ofs)) #define MT_TMAC_TCR0(_band) MT_WF_TMAC(_band, 0) #define MT_TMAC_TCR0_TX_BLINK GENMASK(7, 6) #define MT_TMAC_TCR0_TBTT_STOP_CTRL BIT(25) -#define MT_TMAC_CDTR(_band) MT_WF_TMAC(_band, 0x090) -#define MT_TMAC_ODTR(_band) MT_WF_TMAC(_band, 0x094) +#define MT_TMAC_CDTR(_band) MT_WF_TMAC(_band, __OFFS(TMAC_CDTR)) + #define MT_TMAC_ODTR(_band) MT_WF_TMAC(_band, __OFFS(TMAC_ODTR)) #define MT_TIMEOUT_VAL_PLCP GENMASK(15, 0) #define MT_TIMEOUT_VAL_CCA GENMASK(31, 16) -#define MT_TMAC_ATCR(_band) MT_WF_TMAC(_band, 0x098) +#define MT_TMAC_ATCR(_band) MT_WF_TMAC(_band, __OFFS(TMAC_ATCR)) #define MT_TMAC_ATCR_TXV_TOUT GENMASK(7, 0) -#define MT_TMAC_TRCR0(_band) MT_WF_TMAC(_band, 0x09c) +#define MT_TMAC_TRCR0(_band) MT_WF_TMAC(_band, __OFFS(TMAC_TRCR0)) #define MT_TMAC_TRCR0_TR2T_CHK GENMASK(8, 0) #define MT_TMAC_TRCR0_I2T_CHK GENMASK(24, 16) -#define MT_TMAC_ICR0(_band) MT_WF_TMAC(_band, 0x0a4) -#define MT_IFS_EIFS_OFDM GENMASK(8, 0) +#define MT_TMAC_ICR0(_band) MT_WF_TMAC(_band, __OFFS(TMAC_ICR0)) +#define MT_IFS_EIFS_OFDM GENMASK(8, 0) #define MT_IFS_RIFS GENMASK(14, 10) #define MT_IFS_SIFS GENMASK(22, 16) #define MT_IFS_SLOT GENMASK(30, 24) -#define MT_TMAC_ICR1(_band) MT_WF_TMAC(_band, 0x0b4) +#define MT_TMAC_ICR1(_band) MT_WF_TMAC(_band, __OFFS(TMAC_ICR1)) #define MT_IFS_EIFS_CCK GENMASK(8, 0) -#define MT_TMAC_CTCR0(_band) MT_WF_TMAC(_band, 0x0f4) +#define MT_TMAC_CTCR0(_band) MT_WF_TMAC(_band, __OFFS(TMAC_CTCR0)) #define MT_TMAC_CTCR0_INS_DDLMT_REFTIME GENMASK(5, 0) #define MT_TMAC_CTCR0_INS_DDLMT_EN BIT(17) #define MT_TMAC_CTCR0_INS_DDLMT_VHT_SMPDU_EN BIT(18) -#define MT_TMAC_TFCR0(_band) MT_WF_TMAC(_band, 0x1e0) +#define MT_TMAC_TFCR0(_band) MT_WF_TMAC(_band, __OFFS(TMAC_TFCR0)) -#define MT_WF_DMA_BASE(_band) ((_band) ? 0xa1e00 : 0x21e00) +/* WF DMA TOP: band 0(0x820e7000),band 1(0x820f7000) */ +#define MT_WF_DMA_BASE(_band) ((_band) ? 0x820f7000 : 0x820e7000) #define MT_WF_DMA(_band, ofs) (MT_WF_DMA_BASE(_band) + (ofs)) #define MT_DMA_DCR0(_band) MT_WF_DMA(_band, 0x000) #define MT_DMA_DCR0_MAX_RX_LEN GENMASK(15, 3) #define MT_DMA_DCR0_RXD_G5_EN BIT(23) -/* ETBF: band 0(0x24000), band 1(0xa4000) */ -#define MT_WF_ETBF_BASE(_band) ((_band) ? 0xa4000 : 0x24000) +/* ETBF: band 0(0x820ea000), band 1(0x820fa000) */ +#define MT_WF_ETBF_BASE(_band) ((_band) ? 0x820fa000 : 0x820ea000) #define MT_WF_ETBF(_band, ofs) (MT_WF_ETBF_BASE(_band) + (ofs)) #define MT_ETBF_TX_NDP_BFRP(_band) MT_WF_ETBF(_band, 0x040) #define MT_ETBF_TX_FB_CPL GENMASK(31, 16) #define MT_ETBF_TX_FB_TRI GENMASK(15, 0) -#define MT_ETBF_RX_FB_CONT(_band) MT_WF_ETBF(_band, 0x068) -#define MT_ETBF_RX_FB_BW GENMASK(7, 6) -#define MT_ETBF_RX_FB_NC GENMASK(5, 3) -#define MT_ETBF_RX_FB_NR GENMASK(2, 0) +#define MT_ETBF_PAR_RPT0(_band) MT_WF_ETBF(_band, __OFFS(ETBF_PAR_RPT0)) +#define MT_ETBF_PAR_RPT0_FB_BW GENMASK(7, 6) +#define MT_ETBF_PAR_RPT0_FB_NC GENMASK(5, 3) +#define MT_ETBF_PAR_RPT0_FB_NR GENMASK(2, 0) #define MT_ETBF_TX_APP_CNT(_band) MT_WF_ETBF(_band, 0x0f0) #define MT_ETBF_TX_IBF_CNT GENMASK(31, 16) @@ -125,174 +239,209 @@ #define MT_ETBF_RX_FB_VHT GENMASK(15, 8) #define MT_ETBF_RX_FB_HT GENMASK(7, 0) -/* LPON: band 0(0x24200), band 1(0xa4200) */ -#define MT_WF_LPON_BASE(_band) ((_band) ? 0xa4200 : 0x24200) +/* LPON: band 0(0x820eb000), band 1(0x820fb000) */ +#define MT_WF_LPON_BASE(_band) ((_band) ? 0x820fb000 : 0x820eb000) #define MT_WF_LPON(_band, ofs) (MT_WF_LPON_BASE(_band) + (ofs)) -#define MT_LPON_UTTR0(_band) MT_WF_LPON(_band, 0x080) -#define MT_LPON_UTTR1(_band) MT_WF_LPON(_band, 0x084) +#define MT_LPON_UTTR0(_band) MT_WF_LPON(_band, __OFFS(LPON_UTTR0)) +#define MT_LPON_UTTR1(_band) MT_WF_LPON(_band, __OFFS(LPON_UTTR1)) +#define MT_LPON_FRCR(_band) MT_WF_LPON(_band, __OFFS(LPON_FRCR)) -#define MT_LPON_TCR(_band, n) MT_WF_LPON(_band, 0x0a8 + (n) * 4) +#define MT_LPON_TCR(_band, n) MT_WF_LPON(_band, 0x0a8 + \ + (((n) * 4) << 1)) +#define MT_LPON_TCR_MT7916(_band, n) MT_WF_LPON(_band, 0x0a8 + \ + (((n) * 4) << 4)) #define MT_LPON_TCR_SW_MODE GENMASK(1, 0) #define MT_LPON_TCR_SW_WRITE BIT(0) #define MT_LPON_TCR_SW_ADJUST BIT(1) #define MT_LPON_TCR_SW_READ GENMASK(1, 0) -/* MIB: band 0(0x24800), band 1(0xa4800) */ +/* MIB: band 0(0x820ed000), band 1(0x820fd000) */ /* These counters are (mostly?) clear-on-read. So, some should not * be read at all in case firmware is already reading them. These * are commented with 'DNR' below. The DNR stats will be read by querying * the firmware API for the appropriate message. For counters the driver * does read, the driver should accumulate the counters. */ -#define MT_WF_MIB_BASE(_band) ((_band) ? 0xa4800 : 0x24800) +#define MT_WF_MIB_BASE(_band) ((_band) ? 0x820fd000 : 0x820ed000) #define MT_WF_MIB(_band, ofs) (MT_WF_MIB_BASE(_band) + (ofs)) #define MT_MIB_SDR0(_band) MT_WF_MIB(_band, 0x010) #define MT_MIB_SDR0_BERACON_TX_CNT_MASK GENMASK(15, 0) -#define MT_MIB_SDR3(_band) MT_WF_MIB(_band, 0x014) +#define MT_MIB_SDR3(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR3)) #define MT_MIB_SDR3_FCS_ERR_MASK GENMASK(15, 0) +#define MT_MIB_SDR3_FCS_ERR_MASK_MT7916 GENMASK(31, 16) -#define MT_MIB_SDR4(_band) MT_WF_MIB(_band, 0x018) +#define MT_MIB_SDR4(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR4)) #define MT_MIB_SDR4_RX_FIFO_FULL_MASK GENMASK(15, 0) /* rx mpdu counter, full 32 bits */ -#define MT_MIB_SDR5(_band) MT_WF_MIB(_band, 0x01c) +#define MT_MIB_SDR5(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR5)) #define MT_MIB_SDR6(_band) MT_WF_MIB(_band, 0x020) #define MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK GENMASK(15, 0) -#define MT_MIB_SDR7(_band) MT_WF_MIB(_band, 0x024) +#define MT_MIB_SDR7(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR7)) #define MT_MIB_SDR7_RX_VECTOR_MISMATCH_CNT_MASK GENMASK(15, 0) -#define MT_MIB_SDR8(_band) MT_WF_MIB(_band, 0x028) +#define MT_MIB_SDR8(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR8)) #define MT_MIB_SDR8_RX_DELIMITER_FAIL_CNT_MASK GENMASK(15, 0) /* aka CCA_NAV_TX_TIME */ -#define MT_MIB_SDR9_DNR(_band) MT_WF_MIB(_band, 0x02c) -#define MT_MIB_SDR9_CCA_BUSY_TIME_MASK GENMASK(23, 0) +#define MT_MIB_SDR9_DNR(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR9)) +#define MT_MIB_SDR9_CCA_BUSY_TIME_MASK GENMASK(23, 0) -#define MT_MIB_SDR10_DNR(_band) MT_WF_MIB(_band, 0x030) -#define MT_MIB_SDR10_MRDY_COUNT_MASK GENMASK(25, 0) +#define MT_MIB_SDR10_DNR(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR10)) +#define MT_MIB_SDR10_MRDY_COUNT_MASK GENMASK(25, 0) +#define MT_MIB_SDR10_MRDY_COUNT_MASK_MT7916 GENMASK(31, 0) -#define MT_MIB_SDR11(_band) MT_WF_MIB(_band, 0x034) +#define MT_MIB_SDR11(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR11)) #define MT_MIB_SDR11_RX_LEN_MISMATCH_CNT_MASK GENMASK(15, 0) /* tx ampdu cnt, full 32 bits */ -#define MT_MIB_SDR12(_band) MT_WF_MIB(_band, 0x038) +#define MT_MIB_SDR12(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR12)) -#define MT_MIB_SDR13(_band) MT_WF_MIB(_band, 0x03c) +#define MT_MIB_SDR13(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR13)) #define MT_MIB_SDR13_TX_STOP_Q_EMPTY_CNT_MASK GENMASK(15, 0) /* counts all mpdus in ampdu, regardless of success */ -#define MT_MIB_SDR14(_band) MT_WF_MIB(_band, 0x040) +#define MT_MIB_SDR14(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR14)) #define MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK GENMASK(23, 0) +#define MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK_MT7916 GENMASK(31, 0) /* counts all successfully tx'd mpdus in ampdu */ -#define MT_MIB_SDR15(_band) MT_WF_MIB(_band, 0x044) +#define MT_MIB_SDR15(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR15)) #define MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK GENMASK(23, 0) +#define MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK_MT7916 GENMASK(31, 0) /* in units of 'us' */ -#define MT_MIB_SDR16_DNR(_band) MT_WF_MIB(_band, 0x048) +#define MT_MIB_SDR16_DNR(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR16)) #define MT_MIB_SDR16_PRIMARY_CCA_BUSY_TIME_MASK GENMASK(23, 0) -#define MT_MIB_SDR17_DNR(_band) MT_WF_MIB(_band, 0x04c) +#define MT_MIB_SDR17_DNR(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR17)) #define MT_MIB_SDR17_SECONDARY_CCA_BUSY_TIME_MASK GENMASK(23, 0) -#define MT_MIB_SDR18(_band) MT_WF_MIB(_band, 0x050) +#define MT_MIB_SDR18(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR18)) #define MT_MIB_SDR18_PRIMARY_ENERGY_DETECT_TIME_MASK GENMASK(23, 0) /* units are us */ -#define MT_MIB_SDR19_DNR(_band) MT_WF_MIB(_band, 0x054) +#define MT_MIB_SDR19_DNR(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR19)) #define MT_MIB_SDR19_CCK_MDRDY_TIME_MASK GENMASK(23, 0) -#define MT_MIB_SDR20_DNR(_band) MT_WF_MIB(_band, 0x058) +#define MT_MIB_SDR20_DNR(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR20)) #define MT_MIB_SDR20_OFDM_VHT_MDRDY_TIME_MASK GENMASK(23, 0) -#define MT_MIB_SDR21_DNR(_band) MT_WF_MIB(_band, 0x05c) +#define MT_MIB_SDR21_DNR(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR21)) #define MT_MIB_SDR20_GREEN_MDRDY_TIME_MASK GENMASK(23, 0) /* rx ampdu count, 32-bit */ -#define MT_MIB_SDR22(_band) MT_WF_MIB(_band, 0x060) +#define MT_MIB_SDR22(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR22)) /* rx ampdu bytes count, 32-bit */ -#define MT_MIB_SDR23(_band) MT_WF_MIB(_band, 0x064) +#define MT_MIB_SDR23(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR23)) /* rx ampdu valid subframe count */ -#define MT_MIB_SDR24(_band) MT_WF_MIB(_band, 0x068) +#define MT_MIB_SDR24(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR24)) #define MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK GENMASK(23, 0) +#define MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK_MT7916 GENMASK(31, 0) /* rx ampdu valid subframe bytes count, 32bits */ -#define MT_MIB_SDR25(_band) MT_WF_MIB(_band, 0x06c) +#define MT_MIB_SDR25(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR25)) /* remaining windows protected stats */ -#define MT_MIB_SDR27(_band) MT_WF_MIB(_band, 0x074) +#define MT_MIB_SDR27(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR27)) #define MT_MIB_SDR27_TX_RWP_FAIL_CNT_MASK GENMASK(15, 0) -#define MT_MIB_SDR28(_band) MT_WF_MIB(_band, 0x078) +#define MT_MIB_SDR28(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR28)) #define MT_MIB_SDR28_TX_RWP_NEED_CNT_MASK GENMASK(15, 0) -#define MT_MIB_SDR29(_band) MT_WF_MIB(_band, 0x07c) -#define MT_MIB_SDR29_RX_PFDROP_CNT_MASK GENMASK(7, 0) +#define MT_MIB_SDR29(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR29)) +#define MT_MIB_SDR29_RX_PFDROP_CNT_MASK GENMASK(7, 0) +#define MT_MIB_SDR29_RX_PFDROP_CNT_MASK_MT7916 GENMASK(15, 0) -#define MT_MIB_SDR30(_band) MT_WF_MIB(_band, 0x080) +#define MT_MIB_SDRVEC(_band) MT_WF_MIB(_band, __OFFS(MIB_SDRVEC)) #define MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK GENMASK(15, 0) +#define MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK_MT7916 GENMASK(31, 16) /* rx blockack count, 32 bits */ -#define MT_MIB_SDR31(_band) MT_WF_MIB(_band, 0x084) +#define MT_MIB_SDR31(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR31)) -#define MT_MIB_SDR32(_band) MT_WF_MIB(_band, 0x088) -#define MT_MIB_SDR32_TX_PKT_EBF_CNT_MASK GENMASK(15, 0) +#define MT_MIB_SDR32(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR32)) +#define MT_MIB_SDR32_TX_PKT_EBF_CNT GENMASK(15, 0) +#define MT_MIB_SDR32_TX_PKT_IBF_CNT GENMASK(31, 16) -#define MT_MIB_SDR33(_band) MT_WF_MIB(_band, 0x08c) -#define MT_MIB_SDR33_TX_PKT_IBF_CNT_MASK GENMASK(15, 0) +#define MT_MIB_SDR33(_band) MT_WF_MIB(_band, 0x088) +#define MT_MIB_SDR33_TX_PKT_IBF_CNT GENMASK(15, 0) -#define MT_MIB_SDR34(_band) MT_WF_MIB(_band, 0x090) +#define MT_MIB_SDRMUBF(_band) MT_WF_MIB(_band, __OFFS(MIB_SDRMUBF)) #define MT_MIB_MU_BF_TX_CNT GENMASK(15, 0) /* 36, 37 both DNR */ -#define MT_MIB_DR8(_band) MT_WF_MIB(_band, 0x0c0) -#define MT_MIB_DR9(_band) MT_WF_MIB(_band, 0x0c4) -#define MT_MIB_DR11(_band) MT_WF_MIB(_band, 0x0cc) +#define MT_MIB_DR8(_band) MT_WF_MIB(_band, __OFFS(MIB_DR8)) +#define MT_MIB_DR9(_band) MT_WF_MIB(_band, __OFFS(MIB_DR9)) +#define MT_MIB_DR11(_band) MT_WF_MIB(_band, __OFFS(MIB_DR11)) -#define MT_MIB_MB_SDR0(_band, n) MT_WF_MIB(_band, 0x100 + ((n) << 4)) +#define MT_MIB_MB_SDR0(_band, n) MT_WF_MIB(_band, __OFFS(MIB_MB_SDR0) + (n)) #define MT_MIB_RTS_RETRIES_COUNT_MASK GENMASK(31, 16) #define MT_MIB_RTS_COUNT_MASK GENMASK(15, 0) -#define MT_MIB_MB_SDR1(_band, n) MT_WF_MIB(_band, 0x104 + ((n) << 4)) +#define MT_MIB_MB_SDR1(_band, n) MT_WF_MIB(_band, __OFFS(MIB_MB_SDR1) + (n)) #define MT_MIB_BA_MISS_COUNT_MASK GENMASK(15, 0) #define MT_MIB_ACK_FAIL_COUNT_MASK GENMASK(31, 16) -#define MT_TX_AGG_CNT(_band, n) MT_WF_MIB(_band, 0x0a8 + ((n) << 2)) -#define MT_TX_AGG_CNT2(_band, n) MT_WF_MIB(_band, 0x164 + ((n) << 2)) -#define MT_MIB_ARNG(_band, n) MT_WF_MIB(_band, 0x4b8 + ((n) << 2)) +#define MT_MIB_MB_SDR2(_band, n) MT_WF_MIB(_band, 0x518 + (n)) +#define MT_MIB_MB_BFTF(_band, n) MT_WF_MIB(_band, 0x510 + (n)) + +#define MT_TX_AGG_CNT(_band, n) MT_WF_MIB(_band, __OFFS(TX_AGG_CNT) + \ + ((n) << 2)) +#define MT_TX_AGG_CNT2(_band, n) MT_WF_MIB(_band, __OFFS(TX_AGG_CNT2) + \ + ((n) << 2)) +#define MT_MIB_ARNG(_band, n) MT_WF_MIB(_band, __OFFS(MIB_ARNG) + \ + ((n) << 2)) #define MT_MIB_ARNCR_RANGE(val, n) (((val) >> ((n) << 3)) & GENMASK(7, 0)) -#define MT_WTBLON_TOP_BASE 0x34000 +#define MT_MIB_BFCR0(_band) MT_WF_MIB(_band, 0x7b0) +#define MT_MIB_BFCR0_RX_FB_HT GENMASK(15, 0) +#define MT_MIB_BFCR0_RX_FB_VHT GENMASK(31, 16) + +#define MT_MIB_BFCR1(_band) MT_WF_MIB(_band, 0x7b4) +#define MT_MIB_BFCR1_RX_FB_HE GENMASK(15, 0) + +#define MT_MIB_BFCR2(_band) MT_WF_MIB(_band, 0x7b8) +#define MT_MIB_BFCR2_BFEE_TX_FB_TRIG GENMASK(15, 0) + +#define MT_MIB_BFCR7(_band) MT_WF_MIB(_band, 0x7cc) +#define MT_MIB_BFCR7_BFEE_TX_FB_CPL GENMASK(15, 0) + +/* WTBLON TOP */ +#define MT_WTBLON_TOP_BASE 0x820d4000 #define MT_WTBLON_TOP(ofs) (MT_WTBLON_TOP_BASE + (ofs)) -#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x0) +#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(__OFFS(WTBLON_TOP_WDUCR)) #define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(2, 0) -#define MT_WTBL_UPDATE MT_WTBLON_TOP(0x030) +#define MT_WTBL_UPDATE MT_WTBLON_TOP(__OFFS(WTBL_UPDATE)) #define MT_WTBL_UPDATE_WLAN_IDX GENMASK(9, 0) #define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(12) #define MT_WTBL_UPDATE_BUSY BIT(31) -#define MT_WTBL_BASE 0x38000 +/* WTBL */ +#define MT_WTBL_BASE 0x820d8000 #define MT_WTBL_LMAC_ID GENMASK(14, 8) #define MT_WTBL_LMAC_DW GENMASK(7, 2) #define MT_WTBL_LMAC_OFFS(_id, _dw) (MT_WTBL_BASE | \ - FIELD_PREP(MT_WTBL_LMAC_ID, _id) | \ - FIELD_PREP(MT_WTBL_LMAC_DW, _dw)) + FIELD_PREP(MT_WTBL_LMAC_ID, _id) | \ + FIELD_PREP(MT_WTBL_LMAC_DW, _dw)) -/* AGG: band 0(0x20800), band 1(0xa0800) */ -#define MT_WF_AGG_BASE(_band) ((_band) ? 0xa0800 : 0x20800) +/* AGG: band 0(0x820e2000), band 1(0x820f2000) */ +#define MT_WF_AGG_BASE(_band) ((_band) ? 0x820f2000 : 0x820e2000) #define MT_WF_AGG(_band, ofs) (MT_WF_AGG_BASE(_band) + (ofs)) -#define MT_AGG_AWSCR0(_band, _n) MT_WF_AGG(_band, 0x05c + (_n) * 4) -#define MT_AGG_PCR0(_band, _n) MT_WF_AGG(_band, 0x06c + (_n) * 4) +#define MT_AGG_AWSCR0(_band, _n) MT_WF_AGG(_band, (__OFFS(AGG_AWSCR0) + \ + (_n) * 4)) +#define MT_AGG_PCR0(_band, _n) MT_WF_AGG(_band, (__OFFS(AGG_PCR0) + \ + (_n) * 4)) #define MT_AGG_PCR0_MM_PROT BIT(0) #define MT_AGG_PCR0_GF_PROT BIT(1) #define MT_AGG_PCR0_BW20_PROT BIT(2) @@ -305,31 +454,32 @@ #define MT_AGG_PCR1_RTS0_NUM_THRES GENMASK(31, 23) #define MT_AGG_PCR1_RTS0_LEN_THRES GENMASK(19, 0) -#define MT_AGG_ACR0(_band) MT_WF_AGG(_band, 0x084) +#define MT_AGG_ACR0(_band) MT_WF_AGG(_band, __OFFS(AGG_ACR0)) #define MT_AGG_ACR_CFEND_RATE GENMASK(13, 0) #define MT_AGG_ACR_BAR_RATE GENMASK(29, 16) -#define MT_AGG_MRCR(_band) MT_WF_AGG(_band, 0x098) -#define MT_AGG_MRCR_BAR_CNT_LIMIT GENMASK(15, 12) -#define MT_AGG_MRCR_LAST_RTS_CTS_RN BIT(6) -#define MT_AGG_MRCR_RTS_FAIL_LIMIT GENMASK(11, 7) +#define MT_AGG_MRCR(_band) MT_WF_AGG(_band, __OFFS(AGG_MRCR)) +#define MT_AGG_MRCR_BAR_CNT_LIMIT GENMASK(15, 12) +#define MT_AGG_MRCR_LAST_RTS_CTS_RN BIT(6) +#define MT_AGG_MRCR_RTS_FAIL_LIMIT GENMASK(11, 7) #define MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT GENMASK(28, 24) -#define MT_AGG_ATCR1(_band) MT_WF_AGG(_band, 0x0f0) -#define MT_AGG_ATCR3(_band) MT_WF_AGG(_band, 0x0f4) +#define MT_AGG_ATCR1(_band) MT_WF_AGG(_band, __OFFS(AGG_ATCR1)) +#define MT_AGG_ATCR3(_band) MT_WF_AGG(_band, __OFFS(AGG_ATCR3)) -/* ARB: band 0(0x20c00), band 1(0xa0c00) */ -#define MT_WF_ARB_BASE(_band) ((_band) ? 0xa0c00 : 0x20c00) +/* ARB: band 0(0x820e3000), band 1(0x820f3000) */ +#define MT_WF_ARB_BASE(_band) ((_band) ? 0x820f3000 : 0x820e3000) #define MT_WF_ARB(_band, ofs) (MT_WF_ARB_BASE(_band) + (ofs)) -#define MT_ARB_SCR(_band) MT_WF_ARB(_band, 0x080) +#define MT_ARB_SCR(_band) MT_WF_ARB(_band, __OFFS(ARB_SCR)) #define MT_ARB_SCR_TX_DISABLE BIT(8) #define MT_ARB_SCR_RX_DISABLE BIT(9) -#define MT_ARB_DRNGR0(_band, _n) MT_WF_ARB(_band, 0x194 + (_n) * 4) +#define MT_ARB_DRNGR0(_band, _n) MT_WF_ARB(_band, (__OFFS(ARB_DRNGR0) + \ + (_n) * 4)) -/* RMAC: band 0(0x21400), band 1(0xa1400) */ -#define MT_WF_RMAC_BASE(_band) ((_band) ? 0xa1400 : 0x21400) +/* RMAC: band 0(0x820e5000), band 1(0x820f5000) */ +#define MT_WF_RMAC_BASE(_band) ((_band) ? 0x820f5000 : 0x820e5000) #define MT_WF_RMAC(_band, ofs) (MT_WF_RMAC_BASE(_band) + (ofs)) #define MT_WF_RFCR(_band) MT_WF_RMAC(_band, 0x000) @@ -366,7 +516,7 @@ #define MT_WF_RMAC_MIB_RXTIME_CLR BIT(31) /* WFDMA0 */ -#define MT_WFDMA0_BASE 0xd4000 +#define MT_WFDMA0_BASE __REG(WFDMA0_ADDR) #define MT_WFDMA0(ofs) (MT_WFDMA0_BASE + (ofs)) #define MT_WFDMA0_RST MT_WFDMA0(0x100) @@ -381,15 +531,14 @@ #define MT_WFDMA0_GLO_CFG MT_WFDMA0(0x208) #define MT_WFDMA0_GLO_CFG_TX_DMA_EN BIT(0) #define MT_WFDMA0_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WFDMA0_GLO_CFG_OMIT_TX_INFO BIT(28) +#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO BIT(27) +#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) #define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c) #define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0) - -#define MT_RX_DATA_RING_BASE MT_WFDMA0(0x500) - -#define MT_WFDMA0_RX_RING0_EXT_CTRL MT_WFDMA0(0x680) -#define MT_WFDMA0_RX_RING1_EXT_CTRL MT_WFDMA0(0x684) -#define MT_WFDMA0_RX_RING2_EXT_CTRL MT_WFDMA0(0x688) +#define MT_WFDMA0_PRI_DLY_INT_CFG1 MT_WFDMA0(0x2f4) +#define MT_WFDMA0_PRI_DLY_INT_CFG2 MT_WFDMA0(0x2f8) /* WFDMA1 */ #define MT_WFDMA1_BASE 0xd5000 @@ -404,129 +553,371 @@ #define MT_WFDMA1_BUSY_ENA_TX_FIFO1 BIT(1) #define MT_WFDMA1_BUSY_ENA_RX_FIFO BIT(2) -#define MT_MCU_CMD MT_WFDMA1(0x1f0) -#define MT_MCU_CMD_STOP_DMA_FW_RELOAD BIT(1) -#define MT_MCU_CMD_STOP_DMA BIT(2) -#define MT_MCU_CMD_RESET_DONE BIT(3) -#define MT_MCU_CMD_RECOVERY_DONE BIT(4) -#define MT_MCU_CMD_NORMAL_STATE BIT(5) -#define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1) - #define MT_WFDMA1_GLO_CFG MT_WFDMA1(0x208) #define MT_WFDMA1_GLO_CFG_TX_DMA_EN BIT(0) #define MT_WFDMA1_GLO_CFG_RX_DMA_EN BIT(2) #define MT_WFDMA1_GLO_CFG_OMIT_TX_INFO BIT(28) #define MT_WFDMA1_GLO_CFG_OMIT_RX_INFO BIT(27) +#define MT_WFDMA1_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) #define MT_WFDMA1_RST_DTX_PTR MT_WFDMA1(0x20c) #define MT_WFDMA1_PRI_DLY_INT_CFG0 MT_WFDMA1(0x2f0) -#define MT_TX_RING_BASE MT_WFDMA1(0x300) -#define MT_RX_EVENT_RING_BASE MT_WFDMA1(0x500) - -#define MT_WFDMA1_TX_RING0_EXT_CTRL MT_WFDMA1(0x600) -#define MT_WFDMA1_TX_RING1_EXT_CTRL MT_WFDMA1(0x604) -#define MT_WFDMA1_TX_RING2_EXT_CTRL MT_WFDMA1(0x608) -#define MT_WFDMA1_TX_RING3_EXT_CTRL MT_WFDMA1(0x60c) -#define MT_WFDMA1_TX_RING4_EXT_CTRL MT_WFDMA1(0x610) -#define MT_WFDMA1_TX_RING5_EXT_CTRL MT_WFDMA1(0x614) -#define MT_WFDMA1_TX_RING6_EXT_CTRL MT_WFDMA1(0x618) -#define MT_WFDMA1_TX_RING7_EXT_CTRL MT_WFDMA1(0x61c) - -#define MT_WFDMA1_TX_RING16_EXT_CTRL MT_WFDMA1(0x640) -#define MT_WFDMA1_TX_RING17_EXT_CTRL MT_WFDMA1(0x644) -#define MT_WFDMA1_TX_RING18_EXT_CTRL MT_WFDMA1(0x648) -#define MT_WFDMA1_TX_RING19_EXT_CTRL MT_WFDMA1(0x64c) -#define MT_WFDMA1_TX_RING20_EXT_CTRL MT_WFDMA1(0x650) -#define MT_WFDMA1_TX_RING21_EXT_CTRL MT_WFDMA1(0x654) -#define MT_WFDMA1_TX_RING22_EXT_CTRL MT_WFDMA1(0x658) -#define MT_WFDMA1_TX_RING23_EXT_CTRL MT_WFDMA1(0x65c) - -#define MT_WFDMA1_RX_RING0_EXT_CTRL MT_WFDMA1(0x680) -#define MT_WFDMA1_RX_RING1_EXT_CTRL MT_WFDMA1(0x684) -#define MT_WFDMA1_RX_RING2_EXT_CTRL MT_WFDMA1(0x688) -#define MT_WFDMA1_RX_RING3_EXT_CTRL MT_WFDMA1(0x68c) - /* WFDMA CSR */ -#define MT_WFDMA_EXT_CSR_BASE 0xd7000 +#define MT_WFDMA_EXT_CSR_BASE __REG(WFDMA_EXT_CSR_ADDR) #define MT_WFDMA_EXT_CSR(ofs) (MT_WFDMA_EXT_CSR_BASE + (ofs)) -#define MT_INT_SOURCE_CSR MT_WFDMA_EXT_CSR(0x10) -#define MT_INT_MASK_CSR MT_WFDMA_EXT_CSR(0x14) -#define MT_INT_RX_DONE_DATA0 BIT(16) -#define MT_INT_RX_DONE_DATA1 BIT(17) -#define MT_INT_RX_DONE_WM BIT(0) -#define MT_INT_RX_DONE_WA BIT(1) -#define MT_INT_RX_DONE_WA_EXT BIT(2) -#define MT_INT_RX_DONE_ALL (GENMASK(2, 0) | GENMASK(17, 16)) -#define MT_INT_TX_DONE_MCU_WA BIT(15) -#define MT_INT_TX_DONE_FWDL BIT(26) -#define MT_INT_TX_DONE_MCU_WM BIT(27) -#define MT_INT_TX_DONE_BAND0 BIT(30) -#define MT_INT_TX_DONE_BAND1 BIT(31) - -#define MT_INT_BAND1_MASK (MT_INT_RX_DONE_WA_EXT | \ - MT_INT_TX_DONE_BAND1) - -#define MT_INT_MCU_CMD BIT(29) - -#define MT_INT_TX_DONE_MCU (MT_INT_TX_DONE_MCU_WA | \ - MT_INT_TX_DONE_MCU_WM | \ - MT_INT_TX_DONE_FWDL) - #define MT_WFDMA_HOST_CONFIG MT_WFDMA_EXT_CSR(0x30) #define MT_WFDMA_HOST_CONFIG_PDMA_BAND BIT(0) #define MT_WFDMA_EXT_CSR_HIF_MISC MT_WFDMA_EXT_CSR(0x44) #define MT_WFDMA_EXT_CSR_HIF_MISC_BUSY BIT(0) -#define MT_INT1_SOURCE_CSR MT_WFDMA_EXT_CSR(0x88) -#define MT_INT1_MASK_CSR MT_WFDMA_EXT_CSR(0x8c) - -#define MT_PCIE_RECOG_ID MT_WFDMA_EXT_CSR(0x90) +#define MT_PCIE_RECOG_ID 0xd7090 #define MT_PCIE_RECOG_ID_MASK GENMASK(30, 0) #define MT_PCIE_RECOG_ID_SEM BIT(31) /* WFDMA0 PCIE1 */ -#define MT_WFDMA0_PCIE1_BASE 0xd8000 -#define MT_WFDMA0_PCIE1(ofs) (MT_WFDMA0_PCIE1_BASE + (ofs)) +#define MT_WFDMA0_PCIE1_BASE __REG(WFDMA0_PCIE1_ADDR) +#define MT_WFDMA0_PCIE1(ofs) (MT_WFDMA0_PCIE1_BASE + (ofs)) -#define MT_WFDMA0_PCIE1_BUSY_ENA MT_WFDMA0_PCIE1(0x13c) +#define MT_WFDMA0_PCIE1_BUSY_ENA MT_WFDMA0_PCIE1(0x13c) #define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 BIT(0) #define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 BIT(1) #define MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO BIT(2) /* WFDMA1 PCIE1 */ -#define MT_WFDMA1_PCIE1_BASE 0xd9000 -#define MT_WFDMA1_PCIE1(ofs) (MT_WFDMA0_PCIE1_BASE + (ofs)) +#define MT_WFDMA1_PCIE1_BASE 0xd9000 +#define MT_WFDMA1_PCIE1(ofs) (MT_WFDMA1_PCIE1_BASE + (ofs)) -#define MT_WFDMA1_PCIE1_BUSY_ENA MT_WFDMA1_PCIE1(0x13c) +#define MT_WFDMA1_PCIE1_BUSY_ENA MT_WFDMA1_PCIE1(0x13c) #define MT_WFDMA1_PCIE1_BUSY_ENA_TX_FIFO0 BIT(0) #define MT_WFDMA1_PCIE1_BUSY_ENA_TX_FIFO1 BIT(1) #define MT_WFDMA1_PCIE1_BUSY_ENA_RX_FIFO BIT(2) -#define MT_TOP_RGU_BASE 0xf0000 -#define MT_TOP_PWR_CTRL (MT_TOP_RGU_BASE + (0x0)) -#define MT_TOP_PWR_KEY (0x5746 << 16) -#define MT_TOP_PWR_SW_RST BIT(0) -#define MT_TOP_PWR_SW_PWR_ON GENMASK(3, 2) -#define MT_TOP_PWR_HW_CTRL BIT(4) -#define MT_TOP_PWR_PWR_ON BIT(7) +/* WFDMA COMMON */ +#define __RXQ(q) ((q) + __MT_MCUQ_MAX) +#define __TXQ(q) (__RXQ(q) + __MT_RXQ_MAX) -#define MT_INFRA_CFG_BASE 0xf1000 -#define MT_INFRA(ofs) (MT_INFRA_CFG_BASE + (ofs)) +#define MT_Q_ID(q) (dev->q_id[(q)]) +#define MT_Q_BASE(q) ((dev->wfdma_mask >> (q)) & 0x1 ? \ + MT_WFDMA1_BASE : MT_WFDMA0_BASE) -#define MT_HIF_REMAP_L1 MT_INFRA(0x1ac) +#define MT_MCUQ_ID(q) MT_Q_ID(q) +#define MT_TXQ_ID(q) MT_Q_ID(__TXQ(q)) +#define MT_RXQ_ID(q) MT_Q_ID(__RXQ(q)) + +#define MT_MCUQ_RING_BASE(q) (MT_Q_BASE(q) + 0x300) +#define MT_TXQ_RING_BASE(q) (MT_Q_BASE(__TXQ(q)) + 0x300) +#define MT_RXQ_RING_BASE(q) (MT_Q_BASE(__RXQ(q)) + 0x500) + +#define MT_MCUQ_EXT_CTRL(q) (MT_Q_BASE(q) + 0x600 + \ + MT_MCUQ_ID(q)* 0x4) +#define MT_RXQ_EXT_CTRL(q) (MT_Q_BASE(__RXQ(q)) + 0x680 + \ + MT_RXQ_ID(q)* 0x4) +#define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \ + MT_TXQ_ID(q)* 0x4) + +#define MT_INT_SOURCE_CSR __REG(INT_SOURCE_CSR) +#define MT_INT_MASK_CSR __REG(INT_MASK_CSR) + +#define MT_INT1_SOURCE_CSR __REG(INT1_SOURCE_CSR) +#define MT_INT1_MASK_CSR __REG(INT1_MASK_CSR) + +#define MT_INT_RX_DONE_BAND0 BIT(16) +#define MT_INT_RX_DONE_BAND1 BIT(17) +#define MT_INT_RX_DONE_WM BIT(0) +#define MT_INT_RX_DONE_WA BIT(1) +#define MT_INT_RX_DONE_WA_MAIN BIT(1) +#define MT_INT_RX_DONE_WA_EXT BIT(2) +#define MT_INT_MCU_CMD BIT(29) +#define MT_INT_RX_DONE_BAND0_MT7916 BIT(22) +#define MT_INT_RX_DONE_BAND1_MT7916 BIT(23) +#define MT_INT_RX_DONE_WA_MAIN_MT7916 BIT(2) +#define MT_INT_RX_DONE_WA_EXT_MT7916 BIT(3) + +#define MT_INT_RX(q) (dev->q_int_mask[__RXQ(q)]) +#define MT_INT_TX_MCU(q) (dev->q_int_mask[(q)]) + +#define MT_INT_RX_DONE_MCU (MT_INT_RX(MT_RXQ_MCU) | \ + MT_INT_RX(MT_RXQ_MCU_WA)) + +#define MT_INT_BAND0_RX_DONE (MT_INT_RX(MT_RXQ_MAIN) | \ + MT_INT_RX(MT_RXQ_MAIN_WA)) + +#define MT_INT_BAND1_RX_DONE (MT_INT_RX(MT_RXQ_EXT) | \ + MT_INT_RX(MT_RXQ_EXT_WA) | \ + MT_INT_RX(MT_RXQ_MAIN_WA)) + +#define MT_INT_RX_DONE_ALL (MT_INT_RX_DONE_MCU | \ + MT_INT_BAND0_RX_DONE | \ + MT_INT_BAND1_RX_DONE) + +#define MT_INT_TX_DONE_FWDL BIT(26) +#define MT_INT_TX_DONE_MCU_WM BIT(27) +#define MT_INT_TX_DONE_MCU_WA BIT(15) +#define MT_INT_TX_DONE_BAND0 BIT(30) +#define MT_INT_TX_DONE_BAND1 BIT(31) +#define MT_INT_TX_DONE_MCU_WA_MT7916 BIT(25) + +#define MT_INT_TX_DONE_MCU (MT_INT_TX_MCU(MT_MCUQ_WA) | \ + MT_INT_TX_MCU(MT_MCUQ_WM) | \ + MT_INT_TX_MCU(MT_MCUQ_FWDL)) + +#define MT_MCU_CMD __REG(INT_MCU_CMD_SOURCE) +#define MT_MCU_CMD_STOP_DMA_FW_RELOAD BIT(1) +#define MT_MCU_CMD_STOP_DMA BIT(2) +#define MT_MCU_CMD_RESET_DONE BIT(3) +#define MT_MCU_CMD_RECOVERY_DONE BIT(4) +#define MT_MCU_CMD_NORMAL_STATE BIT(5) +#define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1) + +/* TOP RGU */ +#define MT_TOP_RGU_BASE 0x18000000 +#define MT_TOP_PWR_CTRL (MT_TOP_RGU_BASE + (0x0)) +#define MT_TOP_PWR_KEY (0x5746 << 16) +#define MT_TOP_PWR_SW_RST BIT(0) +#define MT_TOP_PWR_SW_PWR_ON GENMASK(3, 2) +#define MT_TOP_PWR_HW_CTRL BIT(4) +#define MT_TOP_PWR_PWR_ON BIT(7) + +#define MT_TOP_RGU_SYSRAM_PDN (MT_TOP_RGU_BASE + 0x050) +#define MT_TOP_RGU_SYSRAM_SLP (MT_TOP_RGU_BASE + 0x054) +#define MT_TOP_WFSYS_PWR (MT_TOP_RGU_BASE + 0x010) +#define MT_TOP_PWR_EN_MASK BIT(7) +#define MT_TOP_PWR_ACK_MASK BIT(6) +#define MT_TOP_PWR_KEY_MASK GENMASK(31, 16) + +#define MT7986_TOP_WM_RESET (MT_TOP_RGU_BASE + 0x120) +#define MT7986_TOP_WM_RESET_MASK BIT(0) + +/* l1/l2 remap */ +#define MT_HIF_REMAP_L1 0xf11ac +#define MT_HIF_REMAP_L1_MT7916 0xfe260 #define MT_HIF_REMAP_L1_MASK GENMASK(15, 0) #define MT_HIF_REMAP_L1_OFFSET GENMASK(15, 0) #define MT_HIF_REMAP_L1_BASE GENMASK(31, 16) #define MT_HIF_REMAP_BASE_L1 0xe0000 -#define MT_HIF_REMAP_L2 MT_INFRA(0x1b0) +#define MT_HIF_REMAP_L2 0xf11b0 #define MT_HIF_REMAP_L2_MASK GENMASK(19, 0) #define MT_HIF_REMAP_L2_OFFSET GENMASK(11, 0) #define MT_HIF_REMAP_L2_BASE GENMASK(31, 12) -#define MT_HIF_REMAP_BASE_L2 0x00000 +#define MT_HIF_REMAP_L2_MT7916 0x1b8 +#define MT_HIF_REMAP_L2_MASK_MT7916 GENMASK(31, 16) +#define MT_HIF_REMAP_L2_OFFSET_MT7916 GENMASK(15, 0) +#define MT_HIF_REMAP_L2_BASE_MT7916 GENMASK(31, 16) +#define MT_HIF_REMAP_BASE_L2_MT7916 0x40000 + +#define MT_INFRA_BASE 0x18000000 +#define MT_WFSYS0_PHY_START 0x18400000 +#define MT_WFSYS1_PHY_START 0x18800000 +#define MT_WFSYS1_PHY_END 0x18bfffff +#define MT_CBTOP1_PHY_START 0x70000000 +#define MT_CBTOP1_PHY_END __REG(CBTOP1_PHY_END) +#define MT_CBTOP2_PHY_START 0xf0000000 +#define MT_CBTOP2_PHY_END 0xffffffff +#define MT_INFRA_MCU_START 0x7c000000 +#define MT_INFRA_MCU_END __REG(INFRA_MCU_ADDR_END) +#define MT_CONN_INFRA_OFFSET(p) ((p) - MT_INFRA_BASE) + +/* CONN INFRA CFG */ +#define MT_CONN_INFRA_BASE 0x18001000 +#define MT_CONN_INFRA(ofs) (MT_CONN_INFRA_BASE + (ofs)) + +#define MT_CONN_INFRA_EFUSE MT_CONN_INFRA(0x020) + +#define MT_CONN_INFRA_ADIE_RESET MT_CONN_INFRA(0x030) +#define MT_CONN_INFRA_ADIE1_RESET_MASK BIT(0) +#define MT_CONN_INFRA_ADIE2_RESET_MASK BIT(2) + +#define MT_CONN_INFRA_OSC_RC_EN MT_CONN_INFRA(0x380) + +#define MT_CONN_INFRA_OSC_CTRL MT_CONN_INFRA(0x300) +#define MT_CONN_INFRA_OSC_RC_EN_MASK BIT(7) +#define MT_CONN_INFRA_OSC_STB_TIME_MASK GENMASK(23, 0) + +#define MT_CONN_INFRA_HW_CTRL MT_CONN_INFRA(0x200) +#define MT_CONN_INFRA_HW_CTRL_MASK BIT(0) + +#define MT_CONN_INFRA_WF_SLP_PROT MT_CONN_INFRA(0x540) +#define MT_CONN_INFRA_WF_SLP_PROT_MASK BIT(0) + +#define MT_CONN_INFRA_WF_SLP_PROT_RDY MT_CONN_INFRA(0x544) +#define MT_CONN_INFRA_CONN_WF_MASK (BIT(29) | BIT(31)) +#define MT_CONN_INFRA_CONN (BIT(25) | BIT(29) | BIT(31)) + +#define MT_CONN_INFRA_EMI_REQ MT_CONN_INFRA(0x414) +#define MT_CONN_INFRA_EMI_REQ_MASK BIT(0) +#define MT_CONN_INFRA_INFRA_REQ_MASK BIT(5) + +/* AFE */ +#define MT_AFE_CTRL_BASE(_band) (0x18003000 + ((_band) << 19)) +#define MT_AFE_CTRL(_band, ofs) (MT_AFE_CTRL_BASE(_band) + (ofs)) + +#define MT_AFE_DIG_EN_01(_band) MT_AFE_CTRL(_band, 0x00) +#define MT_AFE_DIG_EN_02(_band) MT_AFE_CTRL(_band, 0x04) +#define MT_AFE_DIG_EN_03(_band) MT_AFE_CTRL(_band, 0x08) +#define MT_AFE_DIG_TOP_01(_band) MT_AFE_CTRL(_band, 0x0c) + +#define MT_AFE_PLL_STB_TIME(_band) MT_AFE_CTRL(_band, 0xf4) +#define MT_AFE_PLL_STB_TIME_MASK (GENMASK(30, 16) | GENMASK(14, 0)) +#define MT_AFE_PLL_STB_TIME_VAL (FIELD_PREP(GENMASK(30, 16), 0x4bc) | \ + FIELD_PREP(GENMASK(14, 0), 0x7e4)) +#define MT_AFE_BPLL_CFG_MASK GENMASK(7, 6) +#define MT_AFE_WPLL_CFG_MASK GENMASK(1, 0) +#define MT_AFE_MCU_WPLL_CFG_MASK GENMASK(3, 2) +#define MT_AFE_MCU_BPLL_CFG_MASK GENMASK(17, 16) +#define MT_AFE_PLL_CFG_MASK (MT_AFE_BPLL_CFG_MASK | \ + MT_AFE_WPLL_CFG_MASK | \ + MT_AFE_MCU_WPLL_CFG_MASK | \ + MT_AFE_MCU_BPLL_CFG_MASK) +#define MT_AFE_PLL_CFG_VAL (FIELD_PREP(MT_AFE_BPLL_CFG_MASK, 0x1) | \ + FIELD_PREP(MT_AFE_WPLL_CFG_MASK, 0x2) | \ + FIELD_PREP(MT_AFE_MCU_WPLL_CFG_MASK, 0x1) | \ + FIELD_PREP(MT_AFE_MCU_BPLL_CFG_MASK, 0x2)) + +#define MT_AFE_DIG_TOP_01_MASK GENMASK(18, 15) +#define MT_AFE_DIG_TOP_01_VAL FIELD_PREP(MT_AFE_DIG_TOP_01_MASK, 0x9) + +#define MT_AFE_RG_WBG_EN_RCK_MASK BIT(0) +#define MT_AFE_RG_WBG_EN_BPLL_UP_MASK BIT(21) +#define MT_AFE_RG_WBG_EN_WPLL_UP_MASK BIT(20) +#define MT_AFE_RG_WBG_EN_PLL_UP_MASK (MT_AFE_RG_WBG_EN_BPLL_UP_MASK | \ + MT_AFE_RG_WBG_EN_WPLL_UP_MASK) +#define MT_AFE_RG_WBG_EN_TXCAL_MASK GENMASK(21, 17) + +#define MT_ADIE_SLP_CTRL_BASE(_band) (0x18005000 + ((_band) << 19)) +#define MT_ADIE_SLP_CTRL(_band, ofs) (MT_ADIE_SLP_CTRL_BASE(_band) + (ofs)) + +#define MT_ADIE_SLP_CTRL_CK0(_band) MT_ADIE_SLP_CTRL(_band, 0x120) + +/* ADIE */ +#define MT_ADIE_CHIP_ID 0x02c +#define MT_ADIE_CHIP_ID_MASK GENMASK(31, 16) +#define MT_ADIE_IDX0 GENMASK(15, 0) +#define MT_ADIE_IDX1 GENMASK(31, 16) + +#define MT_ADIE_RG_TOP_THADC_BG 0x034 +#define MT_ADIE_VRPI_SEL_CR_MASK GENMASK(15, 12) +#define MT_ADIE_VRPI_SEL_EFUSE_MASK GENMASK(6, 3) + +#define MT_ADIE_RG_TOP_THADC 0x038 +#define MT_ADIE_PGA_GAIN_MASK GENMASK(25, 23) +#define MT_ADIE_PGA_GAIN_EFUSE_MASK GENMASK(2, 0) +#define MT_ADIE_LDO_CTRL_MASK GENMASK(27, 26) +#define MT_ADIE_LDO_CTRL_EFUSE_MASK GENMASK(6, 5) + +#define MT_AFE_RG_ENCAL_WBTAC_IF_SW 0x070 +#define MT_ADIE_EFUSE_RDATA0 0x130 + +#define MT_ADIE_EFUSE2_CTRL 0x148 +#define MT_ADIE_EFUSE_CTRL_MASK BIT(1) + +#define MT_ADIE_EFUSE_CFG 0x144 +#define MT_ADIE_EFUSE_MODE_MASK GENMASK(7, 6) +#define MT_ADIE_EFUSE_ADDR_MASK GENMASK(25, 16) +#define MT_ADIE_EFUSE_VALID_MASK BIT(29) +#define MT_ADIE_EFUSE_KICK_MASK BIT(30) + +#define MT_ADIE_THADC_ANALOG 0x3a6 + +#define MT_ADIE_THADC_SLOP 0x3a7 +#define MT_ADIE_ANA_EN_MASK BIT(7) + +#define MT_ADIE_7975_XTAL_CAL 0x3a1 +#define MT_ADIE_TRIM_MASK GENMASK(6, 0) +#define MT_ADIE_EFUSE_TRIM_MASK GENMASK(5, 0) +#define MT_ADIE_XO_TRIM_EN_MASK BIT(7) +#define MT_ADIE_XTAL_DECREASE_MASK BIT(6) + +#define MT_ADIE_7975_XO_TRIM2 0x3a2 +#define MT_ADIE_7975_XO_TRIM3 0x3a3 +#define MT_ADIE_7975_XO_TRIM4 0x3a4 +#define MT_ADIE_7975_XTAL_EN 0x3a5 + +#define MT_ADIE_XO_TRIM_FLOW 0x3ac +#define MT_ADIE_XTAL_AXM_80M_OSC 0x390 +#define MT_ADIE_XTAL_AXM_40M_OSC 0x391 +#define MT_ADIE_XTAL_TRIM1_80M_OSC 0x398 +#define MT_ADIE_XTAL_TRIM1_40M_OSC 0x399 +#define MT_ADIE_WRI_CK_SEL 0x4ac +#define MT_ADIE_RG_STRAP_PIN_IN 0x4fc +#define MT_ADIE_XTAL_C1 0x654 +#define MT_ADIE_XTAL_C2 0x658 +#define MT_ADIE_RG_XO_01 0x65c +#define MT_ADIE_RG_XO_03 0x664 + +#define MT_ADIE_CLK_EN 0xa00 + +#define MT_ADIE_7975_XTAL 0xa18 +#define MT_ADIE_7975_XTAL_EN_MASK BIT(29) + +#define MT_ADIE_7975_COCLK 0xa1c +#define MT_ADIE_7975_XO_2 0xa84 +#define MT_ADIE_7975_XO_2_FIX_EN BIT(31) + +#define MT_ADIE_7975_XO_CTRL2 0xa94 +#define MT_ADIE_7975_XO_CTRL2_C1_MASK GENMASK(26, 20) +#define MT_ADIE_7975_XO_CTRL2_C2_MASK GENMASK(18, 12) +#define MT_ADIE_7975_XO_CTRL2_MASK (MT_ADIE_7975_XO_CTRL2_C1_MASK | \ + MT_ADIE_7975_XO_CTRL2_C2_MASK) + +#define MT_ADIE_7975_XO_CTRL6 0xaa4 +#define MT_ADIE_7975_XO_CTRL6_MASK BIT(16) + +/* TOP SPI */ +#define MT_TOP_SPI_ADIE_BASE(_band) (0x18004000 + ((_band) << 19)) +#define MT_TOP_SPI_ADIE(_band, ofs) (MT_TOP_SPI_ADIE_BASE(_band) + (ofs)) + +#define MT_TOP_SPI_BUSY_CR(_band) MT_TOP_SPI_ADIE(_band, 0) +#define MT_TOP_SPI_POLLING_BIT BIT(5) + +#define MT_TOP_SPI_ADDR_CR(_band) MT_TOP_SPI_ADIE(_band, 0x50) +#define MT_TOP_SPI_READ_ADDR_FORMAT (BIT(12) | BIT(13) | BIT(15)) +#define MT_TOP_SPI_WRITE_ADDR_FORMAT (BIT(13) | BIT(15)) + +#define MT_TOP_SPI_WRITE_DATA_CR(_band) MT_TOP_SPI_ADIE(_band, 0x54) +#define MT_TOP_SPI_READ_DATA_CR(_band) MT_TOP_SPI_ADIE(_band, 0x58) + +/* CONN INFRA CKGEN */ +#define MT_INFRA_CKGEN_BASE 0x18009000 +#define MT_INFRA_CKGEN(ofs) (MT_INFRA_CKGEN_BASE + (ofs)) + +#define MT_INFRA_CKGEN_BUS MT_INFRA_CKGEN(0xa00) +#define MT_INFRA_CKGEN_BUS_CLK_SEL_MASK BIT(23) +#define MT_INFRA_CKGEN_BUS_RDY_SEL_MASK BIT(29) + +#define MT_INFRA_CKGEN_BUS_WPLL_DIV_1 MT_INFRA_CKGEN(0x008) +#define MT_INFRA_CKGEN_BUS_WPLL_DIV_2 MT_INFRA_CKGEN(0x00c) + +#define MT_INFRA_CKGEN_RFSPI_WPLL_DIV MT_INFRA_CKGEN(0x040) +#define MT_INFRA_CKGEN_DIV_SEL_MASK GENMASK(7, 2) +#define MT_INFRA_CKGEN_DIV_EN_MASK BIT(0) + +/* CONN INFRA BUS */ +#define MT_INFRA_BUS_BASE 0x1800e000 +#define MT_INFRA_BUS(ofs) (MT_INFRA_BUS_BASE + (ofs)) + +#define MT_INFRA_BUS_OFF_TIMEOUT MT_INFRA_BUS(0x300) +#define MT_INFRA_BUS_TIMEOUT_LIMIT_MASK GENMASK(14, 7) +#define MT_INFRA_BUS_TIMEOUT_EN_MASK GENMASK(3, 0) + +#define MT_INFRA_BUS_ON_TIMEOUT MT_INFRA_BUS(0x31c) +#define MT_INFRA_BUS_EMI_START MT_INFRA_BUS(0x360) +#define MT_INFRA_BUS_EMI_END MT_INFRA_BUS(0x364) + +/* CONN_INFRA_SKU */ +#define MT_CONNINFRA_SKU_DEC_ADDR 0x18050000 +#define MT_CONNINFRA_SKU_MASK GENMASK(15, 0) +#define MT_ADIE_TYPE_MASK BIT(1) + +/* FW MODE SYNC */ +#define MT_SWDEF_MODE 0x41f23c +#define MT_SWDEF_MODE_MT7916 0x41143c +#define MT_SWDEF_NORMAL_MODE 0 +#define MT_SWDEF_ICAP_MODE 1 +#define MT_SWDEF_SPECTRUM_MODE 2 #define MT_DIC_CMD_REG_BASE 0x41f000 #define MT_DIC_CMD_REG(ofs) (MT_DIC_CMD_REG_BASE + (ofs)) @@ -540,13 +931,7 @@ #define MT_CPU_UTIL_PEAK_IDLE_CNT MT_CPU_UTIL(0x0c) #define MT_CPU_UTIL_CTRL MT_CPU_UTIL(0x1c) -#define MT_SWDEF_BASE 0x41f200 -#define MT_SWDEF(ofs) (MT_SWDEF_BASE + (ofs)) -#define MT_SWDEF_MODE MT_SWDEF(0x3c) -#define MT_SWDEF_NORMAL_MODE 0 -#define MT_SWDEF_ICAP_MODE 1 -#define MT_SWDEF_SPECTRUM_MODE 2 - +/* LED */ #define MT_LED_TOP_BASE 0x18013000 #define MT_LED_PHYS(_n) (MT_LED_TOP_BASE + (_n)) @@ -561,46 +946,124 @@ #define MT_LED_EN(_n) MT_LED_PHYS(0x40 + ((_n) * 4)) -#define MT_TOP_BASE 0x18060000 -#define MT_TOP(ofs) (MT_TOP_BASE + (ofs)) - -#define MT_TOP_LPCR_HOST_BAND0 MT_TOP(0x10) -#define MT_TOP_LPCR_HOST_FW_OWN BIT(0) -#define MT_TOP_LPCR_HOST_DRV_OWN BIT(1) - -#define MT_TOP_MISC MT_TOP(0xf0) -#define MT_TOP_MISC_FW_STATE GENMASK(2, 0) - #define MT_LED_GPIO_MUX2 0x70005058 /* GPIO 18 */ #define MT_LED_GPIO_MUX3 0x7000505C /* GPIO 26 */ #define MT_LED_GPIO_SEL_MASK GENMASK(11, 8) +/* MT TOP */ +#define MT_TOP_BASE 0x18060000 +#define MT_TOP(ofs) (MT_TOP_BASE + (ofs)) + +#define MT_TOP_LPCR_HOST_BAND(_band) MT_TOP(0x10 + ((_band) * 0x10)) +#define MT_TOP_LPCR_HOST_FW_OWN BIT(0) +#define MT_TOP_LPCR_HOST_DRV_OWN BIT(1) +#define MT_TOP_LPCR_HOST_FW_OWN_STAT BIT(2) + +#define MT_TOP_LPCR_HOST_BAND_IRQ_STAT(_band) MT_TOP(0x14 + ((_band) * 0x10)) +#define MT_TOP_LPCR_HOST_BAND_STAT BIT(0) + +#define MT_TOP_MISC MT_TOP(0xf0) +#define MT_TOP_MISC_FW_STATE GENMASK(2, 0) + #define MT_HW_BOUND 0x70010020 -#define MT_HW_CHIPID 0x70010200 #define MT_HW_REV 0x70010204 +#define MT_WF_SUBSYS_RST 0x70002600 -#define MT_PCIE1_MAC_BASE 0x74020000 -#define MT_PCIE1_MAC(ofs) (MT_PCIE1_MAC_BASE + (ofs)) -#define MT_PCIE1_MAC_INT_ENABLE MT_PCIE1_MAC(0x188) +#define MT_TOP_WFSYS_WAKEUP MT_TOP(0x1a4) +#define MT_TOP_WFSYS_WAKEUP_MASK BIT(0) +#define MT_TOP_MCU_EMI_BASE MT_TOP(0x1c4) +#define MT_TOP_MCU_EMI_BASE_MASK GENMASK(19, 0) + +#define MT_TOP_CONN_INFRA_WAKEUP MT_TOP(0x1a0) +#define MT_TOP_CONN_INFRA_WAKEUP_MASK BIT(0) + +#define MT_TOP_WFSYS_RESET_STATUS MT_TOP(0x2cc) +#define MT_TOP_WFSYS_RESET_STATUS_MASK BIT(30) + +/* SEMA */ +#define MT_SEMA_BASE 0x18070000 +#define MT_SEMA(ofs) (MT_SEMA_BASE + (ofs)) + +#define MT_SEMA_RFSPI_STATUS (MT_SEMA(0x2000) + (11 * 4)) +#define MT_SEMA_RFSPI_RELEASE (MT_SEMA(0x2200) + (11 * 4)) +#define MT_SEMA_RFSPI_STATUS_MASK BIT(1) + +/* MCU BUS */ +#define MT_MCU_BUS_BASE 0x18400000 +#define MT_MCU_BUS(ofs) (MT_MCU_BUS_BASE + (ofs)) + +#define MT_MCU_BUS_TIMEOUT MT_MCU_BUS(0xf0440) +#define MT_MCU_BUS_TIMEOUT_SET_MASK GENMASK(7, 0) +#define MT_MCU_BUS_TIMEOUT_CG_EN_MASK BIT(28) +#define MT_MCU_BUS_TIMEOUT_EN_MASK BIT(31) + +#define MT_MCU_BUS_REMAP MT_MCU_BUS(0x120) + +/* TOP CFG */ +#define MT_TOP_CFG_BASE 0x184b0000 +#define MT_TOP_CFG(ofs) (MT_TOP_CFG_BASE + (ofs)) + +#define MT_TOP_CFG_IP_VERSION_ADDR MT_TOP_CFG(0x010) + +/* TOP CFG ON */ +#define MT_TOP_CFG_ON_BASE 0x184c1000 +#define MT_TOP_CFG_ON(ofs) (MT_TOP_CFG_ON_BASE + (ofs)) + +#define MT_TOP_CFG_ON_ROM_IDX MT_TOP_CFG_ON(0x604) + +/* SLP CTRL */ +#define MT_SLP_BASE 0x184c3000 +#define MT_SLP(ofs) (MT_SLP_BASE + (ofs)) + +#define MT_SLP_STATUS MT_SLP(0x00c) +#define MT_SLP_WFDMA2CONN_MASK (BIT(21) | BIT(23)) +#define MT_SLP_CTRL_EN_MASK BIT(0) +#define MT_SLP_CTRL_BSY_MASK BIT(1) + +/* MCU BUS DBG */ +#define MT_MCU_BUS_DBG_BASE 0x18500000 +#define MT_MCU_BUS_DBG(ofs) (MT_MCU_BUS_DBG_BASE + (ofs)) + +#define MT_MCU_BUS_DBG_TIMEOUT MT_MCU_BUS_DBG(0x0) +#define MT_MCU_BUS_DBG_TIMEOUT_SET_MASK GENMASK(31, 16) +#define MT_MCU_BUS_DBG_TIMEOUT_CK_EN_MASK BIT(3) +#define MT_MCU_BUS_DBG_TIMEOUT_EN_MASK BIT(2) + +/* PCIE MAC */ #define MT_PCIE_MAC_BASE 0x74030000 #define MT_PCIE_MAC(ofs) (MT_PCIE_MAC_BASE + (ofs)) #define MT_PCIE_MAC_INT_ENABLE MT_PCIE_MAC(0x188) -#define MT_WF_IRPI_BASE 0x83006000 -#define MT_WF_IRPI(ofs) (MT_WF_IRPI_BASE + ((ofs) << 16)) +#define MT_PCIE1_MAC_INT_ENABLE 0x74020188 +#define MT_PCIE1_MAC_INT_ENABLE_MT7916 0x74090188 -/* PHY: band 0(0x83080000), band 1(0x83090000) */ +/* PP TOP */ +#define MT_WF_PP_TOP_BASE 0x820cc000 +#define MT_WF_PP_TOP(ofs) (MT_WF_PP_TOP_BASE + (ofs)) + +#define MT_WF_PP_TOP_RXQ_WFDMA_CF_5 MT_WF_PP_TOP(0x0e8) +#define MT_WF_PP_TOP_RXQ_QID6_WFDMA_HIF_SEL_MASK BIT(6) + +#define MT_WF_IRPI_BASE 0x83000000 +#define MT_WF_IRPI(ofs) (MT_WF_IRPI_BASE + (ofs)) + +#define MT_WF_IRPI_NSS(phy, nss) MT_WF_IRPI(0x6000 + ((phy) << 20) + ((nss) << 16)) +#define MT_WF_IRPI_NSS_MT7916(phy, nss) MT_WF_IRPI(0x1000 + ((phy) << 20) + ((nss) << 16)) + +/* PHY */ #define MT_WF_PHY_BASE 0x83080000 #define MT_WF_PHY(ofs) (MT_WF_PHY_BASE + (ofs)) #define MT_WF_PHY_RX_CTRL1(_phy) MT_WF_PHY(0x2004 + ((_phy) << 16)) +#define MT_WF_PHY_RX_CTRL1_MT7916(_phy) MT_WF_PHY(0x2004 + ((_phy) << 20)) #define MT_WF_PHY_RX_CTRL1_IPI_EN GENMASK(2, 0) #define MT_WF_PHY_RX_CTRL1_STSCNT_EN GENMASK(11, 9) #define MT_WF_PHY_RXTD12(_phy) MT_WF_PHY(0x8230 + ((_phy) << 16)) +#define MT_WF_PHY_RXTD12_MT7916(_phy) MT_WF_PHY(0x8230 + ((_phy) << 20)) #define MT_WF_PHY_RXTD12_IRPI_SW_CLR_ONLY BIT(18) -#define MT_WF_PHY_RXTD12_IRPI_SW_CLR BIT(29) +#define MT_WF_PHY_RXTD12_IRPI_SW_CLR BIT(29) #define MT_MCU_WM_CIRQ_BASE 0x89010000 #define MT_MCU_WM_CIRQ(ofs) (MT_MCU_WM_CIRQ_BASE + (ofs)) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c new file mode 100644 index 000000000000..3028c02cb840 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c @@ -0,0 +1,1212 @@ +// SPDX-License-Identifier: ISC +/* Copyright (C) 2022 MediaTek Inc. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt7915.h" + +/* INFRACFG */ +#define MT_INFRACFG_CONN2AP_SLPPROT 0x0d0 +#define MT_INFRACFG_AP2CONN_SLPPROT 0x0d4 + +#define MT_INFRACFG_RX_EN_MASK BIT(16) +#define MT_INFRACFG_TX_RDY_MASK BIT(4) +#define MT_INFRACFG_TX_EN_MASK BIT(0) + +/* TOP POS */ +#define MT_TOP_POS_FAST_CTRL 0x114 +#define MT_TOP_POS_FAST_EN_MASK BIT(3) + +#define MT_TOP_POS_SKU 0x21c +#define MT_TOP_POS_SKU_MASK GENMASK(31, 28) +#define MT_TOP_POS_SKU_ADIE_DBDC_MASK BIT(2) + +enum { + ADIE_SB, + ADIE_DBDC +}; + +static int +mt76_wmac_spi_read(struct mt7915_dev *dev, u8 adie, u32 addr, u32 *val) +{ + int ret; + u32 cur; + + ret = read_poll_timeout(mt76_rr, cur, !(cur & MT_TOP_SPI_POLLING_BIT), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_TOP_SPI_BUSY_CR(adie)); + if (ret) + return ret; + + mt76_wr(dev, MT_TOP_SPI_ADDR_CR(adie), + MT_TOP_SPI_READ_ADDR_FORMAT | addr); + mt76_wr(dev, MT_TOP_SPI_WRITE_DATA_CR(adie), 0); + + ret = read_poll_timeout(mt76_rr, cur, !(cur & MT_TOP_SPI_POLLING_BIT), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_TOP_SPI_BUSY_CR(adie)); + if (ret) + return ret; + + *val = mt76_rr(dev, MT_TOP_SPI_READ_DATA_CR(adie)); + + return 0; +} + +static int +mt76_wmac_spi_write(struct mt7915_dev *dev, u8 adie, u32 addr, u32 val) +{ + int ret; + u32 cur; + + ret = read_poll_timeout(mt76_rr, cur, !(cur & MT_TOP_SPI_POLLING_BIT), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_TOP_SPI_BUSY_CR(adie)); + if (ret) + return ret; + + mt76_wr(dev, MT_TOP_SPI_ADDR_CR(adie), + MT_TOP_SPI_WRITE_ADDR_FORMAT | addr); + mt76_wr(dev, MT_TOP_SPI_WRITE_DATA_CR(adie), val); + + return read_poll_timeout(mt76_rr, cur, !(cur & MT_TOP_SPI_POLLING_BIT), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_TOP_SPI_BUSY_CR(adie)); +} + +static int +mt76_wmac_spi_rmw(struct mt7915_dev *dev, u8 adie, + u32 addr, u32 mask, u32 val) +{ + u32 cur, ret; + + ret = mt76_wmac_spi_read(dev, adie, addr, &cur); + if (ret) + return ret; + + cur &= ~mask; + cur |= val; + + return mt76_wmac_spi_write(dev, adie, addr, cur); +} + +static int +mt7986_wmac_adie_efuse_read(struct mt7915_dev *dev, u8 adie, + u32 addr, u32 *data) +{ + int ret, temp; + u32 val, mask; + + ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_EFUSE_CFG, + MT_ADIE_EFUSE_CTRL_MASK); + if (ret) + return ret; + + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_EFUSE2_CTRL, BIT(30), 0x0); + if (ret) + return ret; + + mask = (MT_ADIE_EFUSE_MODE_MASK | MT_ADIE_EFUSE_ADDR_MASK | + MT_ADIE_EFUSE_KICK_MASK); + val = FIELD_PREP(MT_ADIE_EFUSE_MODE_MASK, 0) | + FIELD_PREP(MT_ADIE_EFUSE_ADDR_MASK, addr) | + FIELD_PREP(MT_ADIE_EFUSE_KICK_MASK, 1); + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_EFUSE2_CTRL, mask, val); + if (ret) + return ret; + + ret = read_poll_timeout(mt76_wmac_spi_read, temp, + !temp && !FIELD_GET(MT_ADIE_EFUSE_KICK_MASK, val), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, adie, MT_ADIE_EFUSE2_CTRL, &val); + if (ret) + return ret; + + ret = mt76_wmac_spi_read(dev, adie, MT_ADIE_EFUSE2_CTRL, &val); + if (ret) + return ret; + + if (FIELD_GET(MT_ADIE_EFUSE_VALID_MASK, val) == 1) + ret = mt76_wmac_spi_read(dev, adie, MT_ADIE_EFUSE_RDATA0, + data); + + return ret; +} + +static inline void mt76_wmac_spi_lock(struct mt7915_dev *dev) +{ + u32 cur; + + read_poll_timeout(mt76_rr, cur, + FIELD_GET(MT_SEMA_RFSPI_STATUS_MASK, cur), + 1000, 1000 * MSEC_PER_SEC, false, dev, + MT_SEMA_RFSPI_STATUS); +} + +static inline void mt76_wmac_spi_unlock(struct mt7915_dev *dev) +{ + mt76_wr(dev, MT_SEMA_RFSPI_RELEASE, 1); +} + +static u32 mt76_wmac_rmw(void __iomem *base, u32 offset, u32 mask, u32 val) +{ + val |= readl(base + offset) & ~mask; + writel(val, base + offset); + + return val; +} + +static u8 mt7986_wmac_check_adie_type(struct mt7915_dev *dev) +{ + u32 val; + + val = readl(dev->sku + MT_TOP_POS_SKU); + + return FIELD_GET(MT_TOP_POS_SKU_ADIE_DBDC_MASK, val); +} + +static int mt7986_wmac_consys_reset(struct mt7915_dev *dev, bool enable) +{ + if (!enable) + return reset_control_assert(dev->rstc); + + mt76_wmac_rmw(dev->sku, MT_TOP_POS_FAST_CTRL, + MT_TOP_POS_FAST_EN_MASK, + FIELD_PREP(MT_TOP_POS_FAST_EN_MASK, 0x1)); + + return reset_control_deassert(dev->rstc); +} + +static int mt7986_wmac_gpio_setup(struct mt7915_dev *dev) +{ + struct pinctrl_state *state; + struct pinctrl *pinctrl; + int ret; + u8 type; + + type = mt7986_wmac_check_adie_type(dev); + pinctrl = devm_pinctrl_get(dev->mt76.dev); + if (IS_ERR(pinctrl)) + return PTR_ERR(pinctrl); + + switch (type) { + case ADIE_SB: + state = pinctrl_lookup_state(pinctrl, "default"); + if (IS_ERR_OR_NULL(state)) + return -EINVAL; + break; + case ADIE_DBDC: + state = pinctrl_lookup_state(pinctrl, "dbdc"); + if (IS_ERR_OR_NULL(state)) + return -EINVAL; + break; + } + + ret = pinctrl_select_state(pinctrl, state); + if (ret) + return ret; + + usleep_range(500, 1000); + + return 0; +} + +static int mt7986_wmac_consys_lockup(struct mt7915_dev *dev, bool enable) +{ + int ret; + u32 cur; + + mt76_wmac_rmw(dev->dcm, MT_INFRACFG_AP2CONN_SLPPROT, + MT_INFRACFG_RX_EN_MASK, + FIELD_PREP(MT_INFRACFG_RX_EN_MASK, enable)); + ret = read_poll_timeout(readl, cur, !(cur & MT_INFRACFG_RX_EN_MASK), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev->dcm + MT_INFRACFG_AP2CONN_SLPPROT); + if (ret) + return ret; + + mt76_wmac_rmw(dev->dcm, MT_INFRACFG_AP2CONN_SLPPROT, + MT_INFRACFG_TX_EN_MASK, + FIELD_PREP(MT_INFRACFG_TX_EN_MASK, enable)); + ret = read_poll_timeout(readl, cur, !(cur & MT_INFRACFG_TX_RDY_MASK), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev->dcm + MT_INFRACFG_AP2CONN_SLPPROT); + if (ret) + return ret; + + mt76_wmac_rmw(dev->dcm, MT_INFRACFG_CONN2AP_SLPPROT, + MT_INFRACFG_RX_EN_MASK, + FIELD_PREP(MT_INFRACFG_RX_EN_MASK, enable)); + mt76_wmac_rmw(dev->dcm, MT_INFRACFG_CONN2AP_SLPPROT, + MT_INFRACFG_TX_EN_MASK, + FIELD_PREP(MT_INFRACFG_TX_EN_MASK, enable)); + + return 0; +} + +static int mt7986_wmac_coninfra_check(struct mt7915_dev *dev) +{ + u32 cur; + + return read_poll_timeout(mt76_rr, cur, (cur == 0x02070000), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, + false, dev, MT_CONN_INFRA_BASE); +} + +static int mt7986_wmac_coninfra_setup(struct mt7915_dev *dev) +{ + struct device *pdev = dev->mt76.dev; + struct reserved_mem *rmem; + struct device_node *np; + u32 val; + + np = of_parse_phandle(pdev->of_node, "memory-region", 0); + if (!np) + return -EINVAL; + + rmem = of_reserved_mem_lookup(np); + if (!rmem) + return -EINVAL; + + val = (rmem->base >> 16) & MT_TOP_MCU_EMI_BASE_MASK; + + /* Set conninfra subsys PLL check */ + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS, + MT_INFRA_CKGEN_BUS_RDY_SEL_MASK, 0x1); + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS, + MT_INFRA_CKGEN_BUS_RDY_SEL_MASK, 0x1); + + mt76_rmw_field(dev, MT_TOP_MCU_EMI_BASE, + MT_TOP_MCU_EMI_BASE_MASK, val); + + mt76_wr(dev, MT_INFRA_BUS_EMI_START, rmem->base); + mt76_wr(dev, MT_INFRA_BUS_EMI_END, rmem->size); + + mt76_rr(dev, MT_CONN_INFRA_EFUSE); + + /* Set conninfra sysram */ + mt76_wr(dev, MT_TOP_RGU_SYSRAM_PDN, 0); + mt76_wr(dev, MT_TOP_RGU_SYSRAM_SLP, 1); + + return 0; +} + +static int mt7986_wmac_sku_setup(struct mt7915_dev *dev, u32 *adie_type) +{ + int ret; + u32 adie_main, adie_ext; + + mt76_rmw_field(dev, MT_CONN_INFRA_ADIE_RESET, + MT_CONN_INFRA_ADIE1_RESET_MASK, 0x1); + mt76_rmw_field(dev, MT_CONN_INFRA_ADIE_RESET, + MT_CONN_INFRA_ADIE2_RESET_MASK, 0x1); + + mt76_wmac_spi_lock(dev); + + ret = mt76_wmac_spi_read(dev, 0, MT_ADIE_CHIP_ID, &adie_main); + if (ret) + goto out; + + ret = mt76_wmac_spi_read(dev, 1, MT_ADIE_CHIP_ID, &adie_ext); + if (ret) + goto out; + + *adie_type = FIELD_GET(MT_ADIE_CHIP_ID_MASK, adie_main) | + (MT_ADIE_CHIP_ID_MASK & adie_ext); + +out: + mt76_wmac_spi_unlock(dev); + + return 0; +} + +static inline u16 mt7986_adie_idx(u8 adie, u32 adie_type) +{ + if (adie == 0) + return u32_get_bits(adie_type, MT_ADIE_IDX0); + else + return u32_get_bits(adie_type, MT_ADIE_IDX1); +} + +static inline bool is_7975(struct mt7915_dev *dev, u8 adie, u32 adie_type) +{ + return mt7986_adie_idx(adie, adie_type) == 0x7975; +} + +static inline bool is_7976(struct mt7915_dev *dev, u8 adie, u32 adie_type) +{ + return mt7986_adie_idx(adie, adie_type) == 0x7976; +} + +static int mt7986_wmac_adie_thermal_cal(struct mt7915_dev *dev, u8 adie) +{ + int ret; + u32 data, val; + + ret = mt7986_wmac_adie_efuse_read(dev, adie, MT_ADIE_THADC_ANALOG, + &data); + if (ret || FIELD_GET(MT_ADIE_ANA_EN_MASK, data)) { + val = FIELD_GET(MT_ADIE_VRPI_SEL_EFUSE_MASK, data); + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_RG_TOP_THADC_BG, + MT_ADIE_VRPI_SEL_CR_MASK, + FIELD_PREP(MT_ADIE_VRPI_SEL_CR_MASK, val)); + if (ret) + return ret; + + val = FIELD_GET(MT_ADIE_PGA_GAIN_EFUSE_MASK, data); + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_RG_TOP_THADC, + MT_ADIE_PGA_GAIN_MASK, + FIELD_PREP(MT_ADIE_PGA_GAIN_MASK, val)); + if (ret) + return ret; + } + + ret = mt7986_wmac_adie_efuse_read(dev, adie, MT_ADIE_THADC_SLOP, + &data); + if (ret || FIELD_GET(MT_ADIE_ANA_EN_MASK, data)) { + val = FIELD_GET(MT_ADIE_LDO_CTRL_EFUSE_MASK, data); + + return mt76_wmac_spi_rmw(dev, adie, MT_ADIE_RG_TOP_THADC, + MT_ADIE_LDO_CTRL_MASK, + FIELD_PREP(MT_ADIE_LDO_CTRL_MASK, val)); + } + + return 0; +} + +static int +mt7986_read_efuse_xo_trim_7976(struct mt7915_dev *dev, u8 adie, + bool is_40m, int *result) +{ + int ret; + u32 data, addr; + + addr = is_40m ? MT_ADIE_XTAL_AXM_40M_OSC : MT_ADIE_XTAL_AXM_80M_OSC; + ret = mt7986_wmac_adie_efuse_read(dev, adie, addr, &data); + if (ret) + return ret; + + if (!FIELD_GET(MT_ADIE_XO_TRIM_EN_MASK, data)) { + *result = 64; + } else { + *result = FIELD_GET(MT_ADIE_TRIM_MASK, data); + addr = is_40m ? MT_ADIE_XTAL_TRIM1_40M_OSC : + MT_ADIE_XTAL_TRIM1_80M_OSC; + ret = mt7986_wmac_adie_efuse_read(dev, adie, addr, &data); + if (ret) + return ret; + + if (FIELD_GET(MT_ADIE_XO_TRIM_EN_MASK, data) && + FIELD_GET(MT_ADIE_XTAL_DECREASE_MASK, data)) + *result -= FIELD_GET(MT_ADIE_EFUSE_TRIM_MASK, data); + else if (FIELD_GET(MT_ADIE_XO_TRIM_EN_MASK, data)) + *result += FIELD_GET(MT_ADIE_EFUSE_TRIM_MASK, data); + + *result = max(0, min(127, *result)); + } + + return 0; +} + +static int mt7986_wmac_adie_xtal_trim_7976(struct mt7915_dev *dev, u8 adie) +{ + int ret, trim_80m, trim_40m; + u32 data, val, mode; + + ret = mt7986_wmac_adie_efuse_read(dev, adie, MT_ADIE_XO_TRIM_FLOW, + &data); + if (ret || !FIELD_GET(BIT(1), data)) + return 0; + + ret = mt7986_read_efuse_xo_trim_7976(dev, adie, false, &trim_80m); + if (ret) + return ret; + + ret = mt7986_read_efuse_xo_trim_7976(dev, adie, true, &trim_40m); + if (ret) + return ret; + + ret = mt76_wmac_spi_read(dev, adie, MT_ADIE_RG_STRAP_PIN_IN, &val); + if (ret) + return ret; + + mode = FIELD_PREP(GENMASK(6, 4), val); + if (!mode || mode == 0x2) { + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_XTAL_C1, + GENMASK(31, 24), + FIELD_PREP(GENMASK(31, 24), trim_80m)); + if (ret) + return ret; + + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_XTAL_C2, + GENMASK(31, 24), + FIELD_PREP(GENMASK(31, 24), trim_80m)); + } else if (mode == 0x3 || mode == 0x4 || mode == 0x6) { + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_XTAL_C1, + GENMASK(23, 16), + FIELD_PREP(GENMASK(23, 16), trim_40m)); + if (ret) + return ret; + + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_XTAL_C2, + GENMASK(23, 16), + FIELD_PREP(GENMASK(23, 16), trim_40m)); + } + + return ret; +} + +static int mt7986_wmac_adie_patch_7976(struct mt7915_dev *dev, u8 adie) +{ + int ret; + + ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_TOP_THADC, 0x4a563b00); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_XO_01, 0x1d59080f); + if (ret) + return ret; + + return mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_XO_03, 0x34c00fe0); +} + +static int +mt7986_read_efuse_xo_trim_7975(struct mt7915_dev *dev, u8 adie, + u32 addr, u32 *result) +{ + int ret; + u32 data; + + ret = mt7986_wmac_adie_efuse_read(dev, adie, addr, &data); + if (ret) + return ret; + + if ((data & MT_ADIE_XO_TRIM_EN_MASK)) { + if ((data & MT_ADIE_XTAL_DECREASE_MASK)) + *result -= (data & MT_ADIE_EFUSE_TRIM_MASK); + else + *result += (data & MT_ADIE_EFUSE_TRIM_MASK); + + *result = (*result & MT_ADIE_TRIM_MASK); + } + + return 0; +} + +static int mt7986_wmac_adie_xtal_trim_7975(struct mt7915_dev *dev, u8 adie) +{ + int ret; + u32 data, result = 0, value; + + ret = mt7986_wmac_adie_efuse_read(dev, adie, MT_ADIE_7975_XTAL_EN, + &data); + if (ret || !(data & BIT(1))) + return 0; + + ret = mt7986_wmac_adie_efuse_read(dev, adie, MT_ADIE_7975_XTAL_CAL, + &data); + if (ret) + return ret; + + if (data & MT_ADIE_XO_TRIM_EN_MASK) + result = (data & MT_ADIE_TRIM_MASK); + + ret = mt7986_read_efuse_xo_trim_7975(dev, adie, MT_ADIE_7975_XO_TRIM2, + &result); + if (ret) + return ret; + + ret = mt7986_read_efuse_xo_trim_7975(dev, adie, MT_ADIE_7975_XO_TRIM3, + &result); + if (ret) + return ret; + + ret = mt7986_read_efuse_xo_trim_7975(dev, adie, MT_ADIE_7975_XO_TRIM4, + &result); + if (ret) + return ret; + + /* Update trim value to C1 and C2*/ + value = FIELD_GET(MT_ADIE_7975_XO_CTRL2_C1_MASK, result) | + FIELD_GET(MT_ADIE_7975_XO_CTRL2_C2_MASK, result); + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_7975_XO_CTRL2, + MT_ADIE_7975_XO_CTRL2_MASK, value); + if (ret) + return ret; + + ret = mt76_wmac_spi_read(dev, adie, MT_ADIE_7975_XTAL, &value); + if (ret) + return ret; + + if (value & MT_ADIE_7975_XTAL_EN_MASK) { + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_7975_XO_2, + MT_ADIE_7975_XO_2_FIX_EN, 0x0); + if (ret) + return ret; + } + + return mt76_wmac_spi_rmw(dev, adie, MT_ADIE_7975_XO_CTRL6, + MT_ADIE_7975_XO_CTRL6_MASK, 0x1); +} + +static int mt7986_wmac_adie_patch_7975(struct mt7915_dev *dev, u8 adie) +{ + int ret; + + /* disable CAL LDO and fine tune RFDIG LDO */ + ret = mt76_wmac_spi_write(dev, adie, 0x348, 0x00000002); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, 0x378, 0x00000002); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, 0x3a8, 0x00000002); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, 0x3d8, 0x00000002); + if (ret) + return ret; + + /* set CKA driving and filter */ + ret = mt76_wmac_spi_write(dev, adie, 0xa1c, 0x30000aaa); + if (ret) + return ret; + + /* set CKB LDO to 1.4V */ + ret = mt76_wmac_spi_write(dev, adie, 0xa84, 0x8470008a); + if (ret) + return ret; + + /* turn on SX0 LTBUF */ + ret = mt76_wmac_spi_write(dev, adie, 0x074, 0x00000002); + if (ret) + return ret; + + /* CK_BUF_SW_EN = 1 (all buf in manual mode.) */ + ret = mt76_wmac_spi_write(dev, adie, 0xaa4, 0x01001fc0); + if (ret) + return ret; + + /* BT mode/WF normal mode 00000005 */ + ret = mt76_wmac_spi_write(dev, adie, 0x070, 0x00000005); + if (ret) + return ret; + + /* BG thermal sensor offset update */ + ret = mt76_wmac_spi_write(dev, adie, 0x344, 0x00000088); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, 0x374, 0x00000088); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, 0x3a4, 0x00000088); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, 0x3d4, 0x00000088); + if (ret) + return ret; + + /* set WCON VDD IPTAT to "0000" */ + ret = mt76_wmac_spi_write(dev, adie, 0xa80, 0x44d07000); + if (ret) + return ret; + + /* change back LTBUF SX3 drving to default value */ + ret = mt76_wmac_spi_write(dev, adie, 0xa88, 0x3900aaaa); + if (ret) + return ret; + + /* SM input cap off */ + ret = mt76_wmac_spi_write(dev, adie, 0x2c4, 0x00000000); + if (ret) + return ret; + + /* set CKB driving and filter */ + return mt76_wmac_spi_write(dev, adie, 0x2c8, 0x00000072); +} + +static int mt7986_wmac_adie_cfg(struct mt7915_dev *dev, u8 adie, u32 adie_type) +{ + int ret; + + mt76_wmac_spi_lock(dev); + ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_CLK_EN, ~0); + if (ret) + goto out; + + if (is_7975(dev, adie, adie_type)) { + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_7975_COCLK, + BIT(1), 0x1); + if (ret) + goto out; + + ret = mt7986_wmac_adie_thermal_cal(dev, adie); + if (ret) + goto out; + + ret = mt7986_wmac_adie_xtal_trim_7975(dev, adie); + if (ret) + goto out; + + ret = mt7986_wmac_adie_patch_7975(dev, adie); + } else if (is_7976(dev, adie, adie_type)) { + if (mt7986_wmac_check_adie_type(dev) == ADIE_DBDC) { + ret = mt76_wmac_spi_write(dev, adie, + MT_ADIE_WRI_CK_SEL, 0x1c); + if (ret) + goto out; + } + + ret = mt7986_wmac_adie_thermal_cal(dev, adie); + if (ret) + goto out; + + ret = mt7986_wmac_adie_xtal_trim_7976(dev, adie); + if (ret) + goto out; + + ret = mt7986_wmac_adie_patch_7976(dev, adie); + } +out: + mt76_wmac_spi_unlock(dev); + + return ret; +} + +static int +mt7986_wmac_afe_cal(struct mt7915_dev *dev, u8 adie, bool dbdc, u32 adie_type) +{ + int ret; + u8 idx; + + mt76_wmac_spi_lock(dev); + if (is_7975(dev, adie, adie_type)) + ret = mt76_wmac_spi_write(dev, adie, + MT_AFE_RG_ENCAL_WBTAC_IF_SW, + 0x80000000); + else + ret = mt76_wmac_spi_write(dev, adie, + MT_AFE_RG_ENCAL_WBTAC_IF_SW, + 0x88888005); + if (ret) + goto out; + + idx = dbdc ? ADIE_DBDC : adie; + + mt76_rmw_field(dev, MT_AFE_DIG_EN_01(idx), + MT_AFE_RG_WBG_EN_RCK_MASK, 0x1); + usleep_range(60, 100); + + mt76_rmw(dev, MT_AFE_DIG_EN_01(idx), + MT_AFE_RG_WBG_EN_RCK_MASK, 0x0); + + mt76_rmw_field(dev, MT_AFE_DIG_EN_03(idx), + MT_AFE_RG_WBG_EN_BPLL_UP_MASK, 0x1); + usleep_range(30, 100); + + mt76_rmw_field(dev, MT_AFE_DIG_EN_03(idx), + MT_AFE_RG_WBG_EN_WPLL_UP_MASK, 0x1); + usleep_range(60, 100); + + mt76_rmw_field(dev, MT_AFE_DIG_EN_01(idx), + MT_AFE_RG_WBG_EN_TXCAL_MASK, 0x1f); + usleep_range(800, 1000); + + mt76_rmw(dev, MT_AFE_DIG_EN_01(idx), + MT_AFE_RG_WBG_EN_TXCAL_MASK, 0x0); + mt76_rmw(dev, MT_AFE_DIG_EN_03(idx), + MT_AFE_RG_WBG_EN_PLL_UP_MASK, 0x0); + + ret = mt76_wmac_spi_write(dev, adie, MT_AFE_RG_ENCAL_WBTAC_IF_SW, + 0x5); + +out: + mt76_wmac_spi_unlock(dev); + + return ret; +} + +static void mt7986_wmac_subsys_pll_initial(struct mt7915_dev *dev, u8 band) +{ + mt76_rmw(dev, MT_AFE_PLL_STB_TIME(band), + MT_AFE_PLL_STB_TIME_MASK, MT_AFE_PLL_STB_TIME_VAL); + + mt76_rmw(dev, MT_AFE_DIG_EN_02(band), + MT_AFE_PLL_CFG_MASK, MT_AFE_PLL_CFG_VAL); + + mt76_rmw(dev, MT_AFE_DIG_TOP_01(band), + MT_AFE_DIG_TOP_01_MASK, MT_AFE_DIG_TOP_01_VAL); +} + +static void mt7986_wmac_subsys_setting(struct mt7915_dev *dev) +{ + /* Subsys pll init */ + mt7986_wmac_subsys_pll_initial(dev, 0); + mt7986_wmac_subsys_pll_initial(dev, 1); + + /* Set legacy OSC control stable time*/ + mt76_rmw(dev, MT_CONN_INFRA_OSC_RC_EN, + MT_CONN_INFRA_OSC_RC_EN_MASK, 0x0); + mt76_rmw(dev, MT_CONN_INFRA_OSC_CTRL, + MT_CONN_INFRA_OSC_STB_TIME_MASK, 0x80706); + + /* prevent subsys from power on/of in a short time interval */ + mt76_rmw(dev, MT_TOP_WFSYS_PWR, + MT_TOP_PWR_ACK_MASK | MT_TOP_PWR_KEY_MASK, + MT_TOP_PWR_KEY); +} + +static int mt7986_wmac_bus_timeout(struct mt7915_dev *dev) +{ + mt76_rmw_field(dev, MT_INFRA_BUS_OFF_TIMEOUT, + MT_INFRA_BUS_TIMEOUT_LIMIT_MASK, 0x2); + + mt76_rmw_field(dev, MT_INFRA_BUS_OFF_TIMEOUT, + MT_INFRA_BUS_TIMEOUT_EN_MASK, 0xf); + + mt76_rmw_field(dev, MT_INFRA_BUS_ON_TIMEOUT, + MT_INFRA_BUS_TIMEOUT_LIMIT_MASK, 0xc); + + mt76_rmw_field(dev, MT_INFRA_BUS_ON_TIMEOUT, + MT_INFRA_BUS_TIMEOUT_EN_MASK, 0xf); + + return mt7986_wmac_coninfra_check(dev); +} + +static void mt7986_wmac_clock_enable(struct mt7915_dev *dev, u32 adie_type) +{ + u32 cur; + + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS_WPLL_DIV_1, + MT_INFRA_CKGEN_DIV_SEL_MASK, 0x1); + + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS_WPLL_DIV_2, + MT_INFRA_CKGEN_DIV_SEL_MASK, 0x1); + + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS_WPLL_DIV_1, + MT_INFRA_CKGEN_DIV_EN_MASK, 0x1); + + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS_WPLL_DIV_2, + MT_INFRA_CKGEN_DIV_EN_MASK, 0x1); + + mt76_rmw_field(dev, MT_INFRA_CKGEN_RFSPI_WPLL_DIV, + MT_INFRA_CKGEN_DIV_SEL_MASK, 0x8); + + mt76_rmw_field(dev, MT_INFRA_CKGEN_RFSPI_WPLL_DIV, + MT_INFRA_CKGEN_DIV_EN_MASK, 0x1); + + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS, + MT_INFRA_CKGEN_BUS_CLK_SEL_MASK, 0x0); + + mt76_rmw_field(dev, MT_CONN_INFRA_HW_CTRL, + MT_CONN_INFRA_HW_CTRL_MASK, 0x1); + + mt76_rmw(dev, MT_TOP_CONN_INFRA_WAKEUP, + MT_TOP_CONN_INFRA_WAKEUP_MASK, 0x1); + + usleep_range(900, 1000); + + mt76_wmac_spi_lock(dev); + if (is_7975(dev, 0, adie_type) || is_7976(dev, 0, adie_type)) { + mt76_rmw_field(dev, MT_ADIE_SLP_CTRL_CK0(0), + MT_SLP_CTRL_EN_MASK, 0x1); + + read_poll_timeout(mt76_rr, cur, !(cur & MT_SLP_CTRL_BSY_MASK), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_ADIE_SLP_CTRL_CK0(0)); + } + if (is_7975(dev, 1, adie_type) || is_7976(dev, 1, adie_type)) { + mt76_rmw_field(dev, MT_ADIE_SLP_CTRL_CK0(1), + MT_SLP_CTRL_EN_MASK, 0x1); + + read_poll_timeout(mt76_rr, cur, !(cur & MT_SLP_CTRL_BSY_MASK), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_ADIE_SLP_CTRL_CK0(0)); + } + mt76_wmac_spi_unlock(dev); + + mt76_rmw(dev, MT_TOP_CONN_INFRA_WAKEUP, + MT_TOP_CONN_INFRA_WAKEUP_MASK, 0x0); + usleep_range(900, 1000); +} + +static int mt7986_wmac_top_wfsys_wakeup(struct mt7915_dev *dev, bool enable) +{ + mt76_rmw_field(dev, MT_TOP_WFSYS_WAKEUP, + MT_TOP_WFSYS_WAKEUP_MASK, enable); + + usleep_range(900, 1000); + + if (!enable) + return 0; + + return mt7986_wmac_coninfra_check(dev); +} + +static int mt7986_wmac_wm_enable(struct mt7915_dev *dev, bool enable) +{ + u32 cur; + + mt76_rmw_field(dev, MT7986_TOP_WM_RESET, + MT7986_TOP_WM_RESET_MASK, enable); + if (!enable) + return 0; + + return read_poll_timeout(mt76_rr, cur, (cur == 0x1d1e), + USEC_PER_MSEC, 5000 * USEC_PER_MSEC, false, + dev, MT_TOP_CFG_ON_ROM_IDX); +} + +static int mt7986_wmac_wfsys_poweron(struct mt7915_dev *dev, bool enable) +{ + u32 mask = MT_TOP_PWR_EN_MASK | MT_TOP_PWR_KEY_MASK; + u32 cur; + + mt76_rmw(dev, MT_TOP_WFSYS_PWR, mask, + MT_TOP_PWR_KEY | FIELD_PREP(MT_TOP_PWR_EN_MASK, enable)); + + return read_poll_timeout(mt76_rr, cur, + (FIELD_GET(MT_TOP_WFSYS_RESET_STATUS_MASK, cur) == enable), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_TOP_WFSYS_RESET_STATUS); +} + +static int mt7986_wmac_wfsys_setting(struct mt7915_dev *dev) +{ + int ret; + u32 cur; + + /* Turn off wfsys2conn bus sleep protect */ + mt76_rmw(dev, MT_CONN_INFRA_WF_SLP_PROT, + MT_CONN_INFRA_WF_SLP_PROT_MASK, 0x0); + + ret = mt7986_wmac_wfsys_poweron(dev, true); + if (ret) + return ret; + + /* Check bus sleep protect */ + + ret = read_poll_timeout(mt76_rr, cur, + !(cur & MT_CONN_INFRA_CONN_WF_MASK), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_CONN_INFRA_WF_SLP_PROT_RDY); + if (ret) + return ret; + + ret = read_poll_timeout(mt76_rr, cur, !(cur & MT_SLP_WFDMA2CONN_MASK), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_SLP_STATUS); + if (ret) + return ret; + + return read_poll_timeout(mt76_rr, cur, (cur == 0x02060000), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_TOP_CFG_IP_VERSION_ADDR); +} + +static void mt7986_wmac_wfsys_set_timeout(struct mt7915_dev *dev) +{ + u32 mask = MT_MCU_BUS_TIMEOUT_SET_MASK | + MT_MCU_BUS_TIMEOUT_CG_EN_MASK | + MT_MCU_BUS_TIMEOUT_EN_MASK; + u32 val = FIELD_PREP(MT_MCU_BUS_TIMEOUT_SET_MASK, 1) | + FIELD_PREP(MT_MCU_BUS_TIMEOUT_CG_EN_MASK, 1) | + FIELD_PREP(MT_MCU_BUS_TIMEOUT_EN_MASK, 1); + + mt76_rmw(dev, MT_MCU_BUS_TIMEOUT, mask, val); + + mt76_wr(dev, MT_MCU_BUS_REMAP, 0x810f0000); + + mask = MT_MCU_BUS_DBG_TIMEOUT_SET_MASK | + MT_MCU_BUS_DBG_TIMEOUT_CK_EN_MASK | + MT_MCU_BUS_DBG_TIMEOUT_EN_MASK; + val = FIELD_PREP(MT_MCU_BUS_DBG_TIMEOUT_SET_MASK, 0x3aa) | + FIELD_PREP(MT_MCU_BUS_DBG_TIMEOUT_CK_EN_MASK, 1) | + FIELD_PREP(MT_MCU_BUS_DBG_TIMEOUT_EN_MASK, 1); + + mt76_rmw(dev, MT_MCU_BUS_DBG_TIMEOUT, mask, val); +} + +static int mt7986_wmac_sku_update(struct mt7915_dev *dev, u32 adie_type) +{ + u32 val; + + if (is_7976(dev, 0, adie_type) && is_7976(dev, 1, adie_type)) + val = 0xf; + else if (is_7975(dev, 0, adie_type) && is_7975(dev, 1, adie_type)) + val = 0xd; + else if (is_7976(dev, 0, adie_type)) + val = 0x7; + else if (is_7975(dev, 1, adie_type)) + val = 0x8; + else if (is_7976(dev, 1, adie_type)) + val = 0xa; + else + return -EINVAL; + + mt76_wmac_rmw(dev->sku, MT_TOP_POS_SKU, MT_TOP_POS_SKU_MASK, + FIELD_PREP(MT_TOP_POS_SKU_MASK, val)); + + mt76_wr(dev, MT_CONNINFRA_SKU_DEC_ADDR, val); + + return 0; +} + +static int +mt7986_wmac_adie_setup(struct mt7915_dev *dev, u8 adie, u32 adie_type) +{ + int ret; + + if (!(is_7975(dev, adie, adie_type) || is_7976(dev, adie, adie_type))) + return 0; + + ret = mt7986_wmac_adie_cfg(dev, adie, adie_type); + if (ret) + return ret; + + ret = mt7986_wmac_afe_cal(dev, adie, false, adie_type); + if (ret) + return ret; + + if (!adie && (mt7986_wmac_check_adie_type(dev) == ADIE_DBDC)) + ret = mt7986_wmac_afe_cal(dev, adie, true, adie_type); + + return ret; +} + +static int mt7986_wmac_subsys_powerup(struct mt7915_dev *dev, u32 adie_type) +{ + int ret; + + mt7986_wmac_subsys_setting(dev); + + ret = mt7986_wmac_bus_timeout(dev); + if (ret) + return ret; + + mt7986_wmac_clock_enable(dev, adie_type); + + return 0; +} + +static int mt7986_wmac_wfsys_powerup(struct mt7915_dev *dev) +{ + int ret; + + ret = mt7986_wmac_wm_enable(dev, false); + if (ret) + return ret; + + ret = mt7986_wmac_wfsys_setting(dev); + if (ret) + return ret; + + mt7986_wmac_wfsys_set_timeout(dev); + + return mt7986_wmac_wm_enable(dev, true); +} + +int mt7986_wmac_enable(struct mt7915_dev *dev) +{ + int ret; + u32 adie_type; + + ret = mt7986_wmac_consys_reset(dev, true); + if (ret) + return ret; + + ret = mt7986_wmac_gpio_setup(dev); + if (ret) + return ret; + + ret = mt7986_wmac_consys_lockup(dev, false); + if (ret) + return ret; + + ret = mt7986_wmac_coninfra_check(dev); + if (ret) + return ret; + + ret = mt7986_wmac_coninfra_setup(dev); + if (ret) + return ret; + + ret = mt7986_wmac_sku_setup(dev, &adie_type); + if (ret) + return ret; + + ret = mt7986_wmac_adie_setup(dev, 0, adie_type); + if (ret) + return ret; + + ret = mt7986_wmac_adie_setup(dev, 1, adie_type); + if (ret) + return ret; + + ret = mt7986_wmac_subsys_powerup(dev, adie_type); + if (ret) + return ret; + + ret = mt7986_wmac_top_wfsys_wakeup(dev, true); + if (ret) + return ret; + + ret = mt7986_wmac_wfsys_powerup(dev); + if (ret) + return ret; + + return mt7986_wmac_sku_update(dev, adie_type); +} + +void mt7986_wmac_disable(struct mt7915_dev *dev) +{ + u32 cur; + + mt7986_wmac_top_wfsys_wakeup(dev, true); + + /* Turn on wfsys2conn bus sleep protect */ + mt76_rmw_field(dev, MT_CONN_INFRA_WF_SLP_PROT, + MT_CONN_INFRA_WF_SLP_PROT_MASK, 0x1); + + /* Check wfsys2conn bus sleep protect */ + read_poll_timeout(mt76_rr, cur, !(cur ^ MT_CONN_INFRA_CONN), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_CONN_INFRA_WF_SLP_PROT_RDY); + + mt7986_wmac_wfsys_poweron(dev, false); + + /* Turn back wpll setting */ + mt76_rmw_field(dev, MT_AFE_DIG_EN_02(0), MT_AFE_MCU_BPLL_CFG_MASK, 0x2); + mt76_rmw_field(dev, MT_AFE_DIG_EN_02(0), MT_AFE_WPLL_CFG_MASK, 0x2); + + /* Reset EMI */ + mt76_rmw_field(dev, MT_CONN_INFRA_EMI_REQ, + MT_CONN_INFRA_EMI_REQ_MASK, 0x1); + mt76_rmw_field(dev, MT_CONN_INFRA_EMI_REQ, + MT_CONN_INFRA_EMI_REQ_MASK, 0x0); + mt76_rmw_field(dev, MT_CONN_INFRA_EMI_REQ, + MT_CONN_INFRA_INFRA_REQ_MASK, 0x1); + mt76_rmw_field(dev, MT_CONN_INFRA_EMI_REQ, + MT_CONN_INFRA_INFRA_REQ_MASK, 0x0); + + mt7986_wmac_top_wfsys_wakeup(dev, false); + mt7986_wmac_consys_lockup(dev, true); + mt7986_wmac_consys_reset(dev, false); +} + +static int mt7986_wmac_init(struct mt7915_dev *dev) +{ + struct device *pdev = dev->mt76.dev; + struct platform_device *pfdev = to_platform_device(pdev); + + dev->dcm = devm_platform_ioremap_resource(pfdev, 1); + if (IS_ERR(dev->dcm)) + return PTR_ERR(dev->dcm); + + dev->sku = devm_platform_ioremap_resource(pfdev, 2); + if (IS_ERR(dev->sku)) + return PTR_ERR(dev->sku); + + dev->rstc = devm_reset_control_get(pdev, "consys"); + if (IS_ERR(dev->rstc)) + return PTR_ERR(dev->rstc); + + return mt7986_wmac_enable(dev); +} + +static int mt7986_wmac_probe(struct platform_device *pdev) +{ + void __iomem *mem_base; + struct mt7915_dev *dev; + struct mt76_dev *mdev; + int irq, ret; + u32 chip_id; + + chip_id = (uintptr_t)of_device_get_match_data(&pdev->dev); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + mem_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mem_base)) { + dev_err(&pdev->dev, "Failed to get memory resource\n"); + return PTR_ERR(mem_base); + } + + dev = mt7915_mmio_probe(&pdev->dev, mem_base, chip_id); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + mdev = &dev->mt76; + ret = devm_request_irq(mdev->dev, irq, mt7915_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto free_device; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + ret = mt7986_wmac_init(dev); + if (ret) + goto free_irq; + + ret = mt7915_register_device(dev); + if (ret) + goto free_irq; + + return 0; + +free_irq: + devm_free_irq(mdev->dev, irq, dev); + +free_device: + mt76_free_device(&dev->mt76); + + return ret; +} + +static int mt7986_wmac_remove(struct platform_device *pdev) +{ + struct mt7915_dev *dev = platform_get_drvdata(pdev); + + mt7915_unregister_device(dev); + + return 0; +} + +static const struct of_device_id mt7986_wmac_of_match[] = { + { .compatible = "mediatek,mt7986-wmac", .data = (u32 *)0x7986 }, + {}, +}; + +struct platform_driver mt7986_wmac_driver = { + .driver = { + .name = "mt7986-wmac", + .of_match_table = mt7986_wmac_of_match, + }, + .probe = mt7986_wmac_probe, + .remove = mt7986_wmac_remove, +}; + +MODULE_FIRMWARE(MT7986_FIRMWARE_WA); +MODULE_FIRMWARE(MT7986_FIRMWARE_WM); +MODULE_FIRMWARE(MT7986_FIRMWARE_WM_MT7975); +MODULE_FIRMWARE(MT7986_ROM_PATCH); +MODULE_FIRMWARE(MT7986_ROM_PATCH_MT7975); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c index af80c2cf8c83..20f63644e929 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c @@ -23,30 +23,16 @@ struct reg_band { u32 band[2]; }; -#define REG_BAND(_reg) \ - { .band[0] = MT_##_reg(0), .band[1] = MT_##_reg(1) } -#define REG_BAND_IDX(_reg, _idx) \ - { .band[0] = MT_##_reg(0, _idx), .band[1] = MT_##_reg(1, _idx) } +#define REG_BAND(_list, _reg) \ + { _list.band[0] = MT_##_reg(0); \ + _list.band[1] = MT_##_reg(1); } +#define REG_BAND_IDX(_list, _reg, _idx) \ + { _list.band[0] = MT_##_reg(0, _idx); \ + _list.band[1] = MT_##_reg(1, _idx); } + +#define TM_REG_MAX_ID 17 +static struct reg_band reg_backup_list[TM_REG_MAX_ID]; -static const struct reg_band reg_backup_list[] = { - REG_BAND_IDX(AGG_PCR0, 0), - REG_BAND_IDX(AGG_PCR0, 1), - REG_BAND_IDX(AGG_AWSCR0, 0), - REG_BAND_IDX(AGG_AWSCR0, 1), - REG_BAND_IDX(AGG_AWSCR0, 2), - REG_BAND_IDX(AGG_AWSCR0, 3), - REG_BAND(AGG_MRCR), - REG_BAND(TMAC_TFCR0), - REG_BAND(TMAC_TCR0), - REG_BAND(AGG_ATCR1), - REG_BAND(AGG_ATCR3), - REG_BAND(TMAC_TRCR0), - REG_BAND(TMAC_ICR0), - REG_BAND_IDX(ARB_DRNGR0, 0), - REG_BAND_IDX(ARB_DRNGR0, 1), - REG_BAND(WF_RFCR), - REG_BAND(WF_RFCR1), -}; static int mt7915_tm_set_tx_power(struct mt7915_phy *phy) @@ -212,7 +198,6 @@ mt7915_tm_set_ipg_params(struct mt7915_phy *phy, u32 ipg, u8 mode) u8 slot_time = 9, sifs = TM_DEFAULT_SIFS; u8 aifsn = TM_MIN_AIFSN; u32 i2t_time, tr2t_time, txv_time; - bool ext_phy = phy != &dev->phy; u16 cw = 0; if (ipg < sig_ext + slot_time + sifs) @@ -242,29 +227,25 @@ mt7915_tm_set_ipg_params(struct mt7915_phy *phy, u32 ipg, u8 mode) ipg -= aifsn * slot_time; - if (ipg > TM_DEFAULT_SIFS) { - if (ipg < TM_MAX_SIFS) - sifs = ipg; - else - sifs = TM_MAX_SIFS; - } + if (ipg > TM_DEFAULT_SIFS) + sifs = min_t(u32, ipg, TM_MAX_SIFS); } done: - txv_time = mt76_get_field(dev, MT_TMAC_ATCR(ext_phy), + txv_time = mt76_get_field(dev, MT_TMAC_ATCR(phy->band_idx), MT_TMAC_ATCR_TXV_TOUT); txv_time *= 50; /* normal clock time */ i2t_time = (slot_time * 1000 - txv_time - BBP_PROC_TIME) / 50; tr2t_time = (sifs * 1000 - txv_time - BBP_PROC_TIME) / 50; - mt76_set(dev, MT_TMAC_TRCR0(ext_phy), + mt76_set(dev, MT_TMAC_TRCR0(phy->band_idx), FIELD_PREP(MT_TMAC_TRCR0_TR2T_CHK, tr2t_time) | FIELD_PREP(MT_TMAC_TRCR0_I2T_CHK, i2t_time)); mt7915_tm_set_slot_time(phy, slot_time, sifs); return mt7915_tm_set_wmm_qid(dev, - mt7915_lmac_mapping(dev, IEEE80211_AC_BE), + mt76_connac_lmac_mapping(IEEE80211_AC_BE), aifsn, cw, cw, 0); } @@ -290,6 +271,8 @@ mt7915_tm_set_tx_len(struct mt7915_phy *phy, u32 tx_time) case MT76_TM_TX_MODE_OFDM: if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) sband = &mphy->sband_5g.sband; + else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; else sband = &mphy->sband_2g.sband; @@ -351,13 +334,30 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy) { int n_regs = ARRAY_SIZE(reg_backup_list); struct mt7915_dev *dev = phy->dev; - bool ext_phy = phy != &dev->phy; u32 *b = phy->test.reg_backup; int i; + REG_BAND_IDX(reg_backup_list[0], AGG_PCR0, 0); + REG_BAND_IDX(reg_backup_list[1], AGG_PCR0, 1); + REG_BAND_IDX(reg_backup_list[2], AGG_AWSCR0, 0); + REG_BAND_IDX(reg_backup_list[3], AGG_AWSCR0, 1); + REG_BAND_IDX(reg_backup_list[4], AGG_AWSCR0, 2); + REG_BAND_IDX(reg_backup_list[5], AGG_AWSCR0, 3); + REG_BAND(reg_backup_list[6], AGG_MRCR); + REG_BAND(reg_backup_list[7], TMAC_TFCR0); + REG_BAND(reg_backup_list[8], TMAC_TCR0); + REG_BAND(reg_backup_list[9], AGG_ATCR1); + REG_BAND(reg_backup_list[10], AGG_ATCR3); + REG_BAND(reg_backup_list[11], TMAC_TRCR0); + REG_BAND(reg_backup_list[12], TMAC_ICR0); + REG_BAND_IDX(reg_backup_list[13], ARB_DRNGR0, 0); + REG_BAND_IDX(reg_backup_list[14], ARB_DRNGR0, 1); + REG_BAND(reg_backup_list[15], WF_RFCR); + REG_BAND(reg_backup_list[16], WF_RFCR1); + if (phy->mt76->test.state == MT76_TM_STATE_OFF) { for (i = 0; i < n_regs; i++) - mt76_wr(dev, reg_backup_list[i].band[ext_phy], b[i]); + mt76_wr(dev, reg_backup_list[i].band[phy->band_idx], b[i]); return; } @@ -368,33 +368,33 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy) phy->test.reg_backup = b; for (i = 0; i < n_regs; i++) - b[i] = mt76_rr(dev, reg_backup_list[i].band[ext_phy]); + b[i] = mt76_rr(dev, reg_backup_list[i].band[phy->band_idx]); } - mt76_clear(dev, MT_AGG_PCR0(ext_phy, 0), MT_AGG_PCR0_MM_PROT | + mt76_clear(dev, MT_AGG_PCR0(phy->band_idx, 0), MT_AGG_PCR0_MM_PROT | MT_AGG_PCR0_GF_PROT | MT_AGG_PCR0_ERP_PROT | MT_AGG_PCR0_VHT_PROT | MT_AGG_PCR0_BW20_PROT | MT_AGG_PCR0_BW40_PROT | MT_AGG_PCR0_BW80_PROT); - mt76_set(dev, MT_AGG_PCR0(ext_phy, 0), MT_AGG_PCR0_PTA_WIN_DIS); + mt76_set(dev, MT_AGG_PCR0(phy->band_idx, 0), MT_AGG_PCR0_PTA_WIN_DIS); - mt76_wr(dev, MT_AGG_PCR0(ext_phy, 1), MT_AGG_PCR1_RTS0_NUM_THRES | + mt76_wr(dev, MT_AGG_PCR0(phy->band_idx, 1), MT_AGG_PCR1_RTS0_NUM_THRES | MT_AGG_PCR1_RTS0_LEN_THRES); - mt76_clear(dev, MT_AGG_MRCR(ext_phy), MT_AGG_MRCR_BAR_CNT_LIMIT | + mt76_clear(dev, MT_AGG_MRCR(phy->band_idx), MT_AGG_MRCR_BAR_CNT_LIMIT | MT_AGG_MRCR_LAST_RTS_CTS_RN | MT_AGG_MRCR_RTS_FAIL_LIMIT | MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT); - mt76_rmw(dev, MT_AGG_MRCR(ext_phy), MT_AGG_MRCR_RTS_FAIL_LIMIT | + mt76_rmw(dev, MT_AGG_MRCR(phy->band_idx), MT_AGG_MRCR_RTS_FAIL_LIMIT | MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT, FIELD_PREP(MT_AGG_MRCR_RTS_FAIL_LIMIT, 1) | FIELD_PREP(MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT, 1)); - mt76_wr(dev, MT_TMAC_TFCR0(ext_phy), 0); - mt76_clear(dev, MT_TMAC_TCR0(ext_phy), MT_TMAC_TCR0_TBTT_STOP_CTRL); + mt76_wr(dev, MT_TMAC_TFCR0(phy->band_idx), 0); + mt76_clear(dev, MT_TMAC_TCR0(phy->band_idx), MT_TMAC_TCR0_TBTT_STOP_CTRL); /* config rx filter for testmode rx */ - mt76_wr(dev, MT_WF_RFCR(ext_phy), 0xcf70a); - mt76_wr(dev, MT_WF_RFCR1(ext_phy), 0); + mt76_wr(dev, MT_WF_RFCR(phy->band_idx), 0xcf70a); + mt76_wr(dev, MT_WF_RFCR1(phy->band_idx), 0); } static void @@ -452,7 +452,7 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en) u8 tx_ant = td->tx_antenna_mask; if (phy != &dev->phy) - tx_ant >>= 2; + tx_ant >>= dev->chainshift; phy->test.spe_idx = spe_idx_map[tx_ant]; } } @@ -574,6 +574,8 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en) if (chandef->chan->band == NL80211_BAND_5GHZ) sband = &phy->mt76->sband_5g.sband; + else if (chandef->chan->band == NL80211_BAND_6GHZ) + sband = &phy->mt76->sband_6g.sband; else sband = &phy->mt76->sband_2g.sband; @@ -720,11 +722,11 @@ mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg) { struct mt7915_phy *phy = mphy->priv; struct mt7915_dev *dev = phy->dev; - bool ext_phy = phy != &dev->phy; enum mt76_rxq_id q; void *rx, *rssi; u16 fcs_err; int i; + u32 cnt; rx = nla_nest_start(msg, MT76_TM_STATS_ATTR_LAST_RX); if (!rx) @@ -768,9 +770,11 @@ mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg) nla_nest_end(msg, rx); - fcs_err = mt76_get_field(dev, MT_MIB_SDR3(ext_phy), - MT_MIB_SDR3_FCS_ERR_MASK); - q = ext_phy ? MT_RXQ_EXT : MT_RXQ_MAIN; + cnt = mt76_rr(dev, MT_MIB_SDR3(phy->band_idx)); + fcs_err = is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK, cnt) : + FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK_MT7916, cnt); + + q = phy->band_idx ? MT_RXQ_EXT : MT_RXQ_MAIN; mphy->test.rx_stats.packets[q] += fcs_err; mphy->test.rx_stats.fcs_error[q] += fcs_err; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7921/Kconfig index 71154fc2a87c..adff2d7350b5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/mt7921/Kconfig @@ -24,3 +24,14 @@ config MT7921S This adds support for MT7921S 802.11ax 2x2:2SS wireless devices. To compile this driver as a module, choose M here. + +config MT7921U + tristate "MediaTek MT7921U (USB) support" + select MT76_USB + select MT7921_COMMON + depends on MAC80211 + depends on USB + help + This adds support for MT7921U 802.11ax 2x2:2SS wireless devices. + + To compile this driver as a module, choose M here. diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile index 1187acedfeda..0a146818c623 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile +++ b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_MT7921_COMMON) += mt7921-common.o obj-$(CONFIG_MT7921E) += mt7921e.o obj-$(CONFIG_MT7921S) += mt7921s.o +obj-$(CONFIG_MT7921U) += mt7921u.o CFLAGS_trace.o := -I$(src) @@ -10,3 +11,4 @@ mt7921-common-y := mac.o mcu.o main.o init.o debugfs.o trace.o mt7921-common-$(CONFIG_NL80211_TESTMODE) += testmode.o mt7921e-y := pci.o pci_mac.o pci_mcu.o dma.o mt7921s-y := sdio.o sdio_mac.o sdio_mcu.o +mt7921u-y := usb.o usb_mac.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c index 86fd7292b229..bce76417f95d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c @@ -129,23 +129,22 @@ mt7921_queues_acq(struct seq_file *s, void *data) mt7921_mutex_acquire(dev); - for (i = 0; i < 16; i++) { - int j, acs = i / 4, index = i % 4; + for (i = 0; i < 4; i++) { u32 ctrl, val, qlen = 0; + int j; - val = mt76_rr(dev, MT_PLE_AC_QEMPTY(acs, index)); - ctrl = BIT(31) | BIT(15) | (acs << 8); + val = mt76_rr(dev, MT_PLE_AC_QEMPTY(i)); + ctrl = BIT(31) | BIT(11) | (i << 24); for (j = 0; j < 32; j++) { if (val & BIT(j)) continue; - mt76_wr(dev, MT_PLE_FL_Q0_CTRL, - ctrl | (j + (index << 5))); + mt76_wr(dev, MT_PLE_FL_Q0_CTRL, ctrl | j); qlen += mt76_get_field(dev, MT_PLE_FL_Q3_CTRL, GENMASK(11, 0)); } - seq_printf(s, "AC%d%d: queued=%d\n", acs, index, qlen); + seq_printf(s, "AC%d: queued=%d\n", i, qlen); } mt7921_mutex_release(dev); @@ -262,26 +261,21 @@ mt7921_txpwr(struct seq_file *s, void *data) return 0; } -static void -mt7921_pm_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) -{ - struct mt7921_dev *dev = priv; - - mt7921_mcu_set_beacon_filter(dev, vif, dev->pm.enable); -} - static int mt7921_pm_set(void *data, u64 val) { struct mt7921_dev *dev = data; struct mt76_connac_pm *pm = &dev->pm; + if (mt76_is_usb(&dev->mt76)) + return -EOPNOTSUPP; + mutex_lock(&dev->mt76.mutex); - if (val == pm->enable) + if (val == pm->enable_user) goto out; - if (!pm->enable) { + if (!pm->enable_user) { pm->stats.last_wake_event = jiffies; pm->stats.last_doze_event = jiffies; } @@ -291,13 +285,8 @@ mt7921_pm_set(void *data, u64 val) pm->enable = false; mt76_connac_pm_wake(&dev->mphy, pm); - ieee80211_iterate_active_interfaces(mt76_hw(dev), - IEEE80211_IFACE_ITER_RESUME_ALL, - mt7921_pm_interface_iter, dev); - - mt76_connac_mcu_set_deep_sleep(&dev->mt76, pm->ds_enable); - - pm->enable = val; + pm->enable_user = val; + mt7921_set_runtime_pm(dev); mt76_connac_power_save_sched(&dev->mphy, pm); out: mutex_unlock(&dev->mt76.mutex); @@ -310,7 +299,7 @@ mt7921_pm_get(void *data, u64 *val) { struct mt7921_dev *dev = data; - *val = dev->pm.enable; + *val = dev->pm.enable_user; return 0; } @@ -322,13 +311,20 @@ mt7921_deep_sleep_set(void *data, u64 val) { struct mt7921_dev *dev = data; struct mt76_connac_pm *pm = &dev->pm; + bool monitor = !!(dev->mphy.hw->conf.flags & IEEE80211_CONF_MONITOR); bool enable = !!val; + if (mt76_is_usb(&dev->mt76)) + return -EOPNOTSUPP; + mt7921_mutex_acquire(dev); - if (pm->ds_enable != enable) { - mt76_connac_mcu_set_deep_sleep(&dev->mt76, enable); - pm->ds_enable = enable; - } + if (pm->ds_enable_user == enable) + goto out; + + pm->ds_enable_user = enable; + pm->ds_enable = enable && !monitor; + mt76_connac_mcu_set_deep_sleep(&dev->mt76, pm->ds_enable); +out: mt7921_mutex_release(dev); return 0; @@ -339,7 +335,7 @@ mt7921_deep_sleep_get(void *data, u64 *val) { struct mt7921_dev *dev = data; - *val = dev->pm.ds_enable; + *val = dev->pm.ds_enable_user; return 0; } @@ -438,8 +434,13 @@ int mt7921_init_debugfs(struct mt7921_dev *dev) if (!dir) return -ENOMEM; - debugfs_create_devm_seqfile(dev->mt76.dev, "queues", dir, - mt7921_queues_read); + if (mt76_is_mmio(&dev->mt76)) + debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", + dir, mt7921_queues_read); + else + debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", + dir, mt76_queues_read); + debugfs_create_devm_seqfile(dev->mt76.dev, "acq", dir, mt7921_queues_acq); debugfs_create_devm_seqfile(dev->mt76.dev, "txpower_sku", dir, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/dma.c b/drivers/net/wireless/mediatek/mt76/mt7921/dma.c index cdff1fd52d93..ca7e20fb5fc0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/dma.c @@ -5,7 +5,7 @@ #include "../dma.h" #include "mac.h" -int mt7921_init_tx_queues(struct mt7921_phy *phy, int idx, int n_desc) +static int mt7921_init_tx_queues(struct mt7921_phy *phy, int idx, int n_desc) { int i, err; @@ -78,110 +78,6 @@ static void mt7921_dma_prefetch(struct mt7921_dev *dev) mt76_wr(dev, MT_WFDMA0_TX_RING17_EXT_CTRL, PREFETCH(0x380, 0x4)); } -static u32 __mt7921_reg_addr(struct mt7921_dev *dev, u32 addr) -{ - static const struct { - u32 phys; - u32 mapped; - u32 size; - } fixed_map[] = { - { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ - { 0x820ed000, 0x24800, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ - { 0x820e4000, 0x21000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ - { 0x820e7000, 0x21e00, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ - { 0x820eb000, 0x24200, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ - { 0x820e2000, 0x20800, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ - { 0x820e3000, 0x20c00, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ - { 0x820e5000, 0x21400, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ - { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ - { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure register) */ - { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */ - { 0x54000000, 0x02000, 0x1000 }, /* WFDMA PCIE0 MCU DMA0 */ - { 0x55000000, 0x03000, 0x1000 }, /* WFDMA PCIE0 MCU DMA1 */ - { 0x58000000, 0x06000, 0x1000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ - { 0x59000000, 0x07000, 0x1000 }, /* WFDMA PCIE1 MCU DMA1 */ - { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */ - { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, WFDMA */ - { 0x7c060000, 0xe0000, 0x10000 }, /* CONN_INFRA, conn_host_csr_top */ - { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ - { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ - { 0x820c0000, 0x08000, 0x4000 }, /* WF_UMAC_TOP (PLE) */ - { 0x820c8000, 0x0c000, 0x2000 }, /* WF_UMAC_TOP (PSE) */ - { 0x820cc000, 0x0e000, 0x1000 }, /* WF_UMAC_TOP (PP) */ - { 0x820cd000, 0x0f000, 0x1000 }, /* WF_MDP_TOP */ - { 0x820ce000, 0x21c00, 0x0200 }, /* WF_LMAC_TOP (WF_SEC) */ - { 0x820cf000, 0x22000, 0x1000 }, /* WF_LMAC_TOP (WF_PF) */ - { 0x820e0000, 0x20000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ - { 0x820e1000, 0x20400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ - { 0x820e9000, 0x23400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ - { 0x820ea000, 0x24000, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ - { 0x820ec000, 0x24600, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ - { 0x820f0000, 0xa0000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ - { 0x820f1000, 0xa0600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ - { 0x820f2000, 0xa0800, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ - { 0x820f3000, 0xa0c00, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ - { 0x820f4000, 0xa1000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ - { 0x820f5000, 0xa1400, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ - { 0x820f7000, 0xa1e00, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ - { 0x820f9000, 0xa3400, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ - { 0x820fa000, 0xa4000, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ - { 0x820fb000, 0xa4200, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ - { 0x820fc000, 0xa4600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ - { 0x820fd000, 0xa4800, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ - }; - int i; - - if (addr < 0x100000) - return addr; - - for (i = 0; i < ARRAY_SIZE(fixed_map); i++) { - u32 ofs; - - if (addr < fixed_map[i].phys) - continue; - - ofs = addr - fixed_map[i].phys; - if (ofs > fixed_map[i].size) - continue; - - return fixed_map[i].mapped + ofs; - } - - if ((addr >= 0x18000000 && addr < 0x18c00000) || - (addr >= 0x70000000 && addr < 0x78000000) || - (addr >= 0x7c000000 && addr < 0x7c400000)) - return mt7921_reg_map_l1(dev, addr); - - dev_err(dev->mt76.dev, "Access currently unsupported address %08x\n", - addr); - - return 0; -} - -static u32 mt7921_rr(struct mt76_dev *mdev, u32 offset) -{ - struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); - u32 addr = __mt7921_reg_addr(dev, offset); - - return dev->bus_ops->rr(mdev, addr); -} - -static void mt7921_wr(struct mt76_dev *mdev, u32 offset, u32 val) -{ - struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); - u32 addr = __mt7921_reg_addr(dev, offset); - - dev->bus_ops->wr(mdev, addr, val); -} - -static u32 mt7921_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) -{ - struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); - u32 addr = __mt7921_reg_addr(dev, offset); - - return dev->bus_ops->rmw(mdev, addr, mask, val); -} - static int mt7921_dma_disable(struct mt7921_dev *dev, bool force) { if (force) { @@ -341,23 +237,8 @@ int mt7921_wpdma_reinit_cond(struct mt7921_dev *dev) int mt7921_dma_init(struct mt7921_dev *dev) { - struct mt76_bus_ops *bus_ops; int ret; - dev->phy.dev = dev; - dev->phy.mt76 = &dev->mt76.phy; - dev->mt76.phy.priv = &dev->phy; - dev->bus_ops = dev->mt76.bus; - bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), - GFP_KERNEL); - if (!bus_ops) - return -ENOMEM; - - bus_ops->rr = mt7921_rr; - bus_ops->wr = mt7921_wr; - bus_ops->rmw = mt7921_rmw; - dev->mt76.bus = bus_ops; - mt76_dma_attach(&dev->mt76); ret = mt7921_dma_disable(dev, true); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c index ad59ef9839dc..91fc41922d95 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c @@ -165,7 +165,7 @@ static int __mt7921_init_hardware(struct mt7921_dev *dev) static int mt7921_init_hardware(struct mt7921_dev *dev) { - int ret, idx, i; + int ret, i; set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); @@ -182,6 +182,13 @@ static int mt7921_init_hardware(struct mt7921_dev *dev) return ret; } + return 0; +} + +static int mt7921_init_wcid(struct mt7921_dev *dev) +{ + int idx; + /* Beacon and mgmt frames should occupy wcid 0 */ idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7921_WTBL_STA - 1); if (idx) @@ -195,6 +202,38 @@ static int mt7921_init_hardware(struct mt7921_dev *dev) return 0; } +static void mt7921_init_work(struct work_struct *work) +{ + struct mt7921_dev *dev = container_of(work, struct mt7921_dev, + init_work); + int ret; + + ret = mt7921_init_hardware(dev); + if (ret) + return; + + mt76_set_stream_caps(&dev->mphy, true); + mt7921_set_stream_he_caps(&dev->phy); + + ret = mt76_register_device(&dev->mt76, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) { + dev_err(dev->mt76.dev, "register device failed\n"); + return; + } + + ret = mt7921_init_debugfs(dev); + if (ret) { + dev_err(dev->mt76.dev, "register debugfs failed\n"); + return; + } + + /* we support chip reset now */ + dev->hw_init_done = true; + + mt76_connac_mcu_set_deep_sleep(&dev->mt76, dev->pm.ds_enable); +} + int mt7921_register_device(struct mt7921_dev *dev) { struct ieee80211_hw *hw = mt76_hw(dev); @@ -222,21 +261,22 @@ int mt7921_register_device(struct mt7921_dev *dev) spin_lock_init(&dev->sta_poll_lock); INIT_WORK(&dev->reset_work, mt7921_mac_reset_work); + INIT_WORK(&dev->init_work, mt7921_init_work); dev->pm.idle_timeout = MT7921_PM_TIMEOUT; dev->pm.stats.last_wake_event = jiffies; dev->pm.stats.last_doze_event = jiffies; - - /* TODO: mt7921s run sleep mode on default */ - if (mt76_is_mmio(&dev->mt76)) { + if (!mt76_is_usb(&dev->mt76)) { + dev->pm.enable_user = true; dev->pm.enable = true; + dev->pm.ds_enable_user = true; dev->pm.ds_enable = true; } - if (mt76_is_sdio(&dev->mt76)) + if (!mt76_is_mmio(&dev->mt76)) hw->extra_tx_headroom += MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE; - ret = mt7921_init_hardware(dev); + ret = mt7921_init_wcid(dev); if (ret) return ret; @@ -264,23 +304,7 @@ int mt7921_register_device(struct mt7921_dev *dev) dev->mphy.hw->wiphy->available_antennas_rx = dev->mphy.chainmask; dev->mphy.hw->wiphy->available_antennas_tx = dev->mphy.chainmask; - mt76_set_stream_caps(&dev->mphy, true); - mt7921_set_stream_he_caps(&dev->phy); - - ret = mt76_register_device(&dev->mt76, true, mt76_rates, - ARRAY_SIZE(mt76_rates)); - if (ret) - return ret; - - ret = mt7921_init_debugfs(dev); - if (ret) - return ret; - - ret = mt76_connac_mcu_set_deep_sleep(&dev->mt76, dev->pm.ds_enable); - if (ret) - return ret; - - dev->hw_init_done = true; + queue_work(system_wq, &dev->init_work); return 0; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index ec10f95a4649..233998ca4857 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -116,7 +116,7 @@ void mt7921_mac_sta_poll(struct mt7921_dev *dev) sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); for (i = 0; i < IEEE80211_NUM_ACS; i++) { - u8 q = mt7921_lmac_mapping(dev, i); + u8 q = mt76_connac_lmac_mapping(i); u32 tx_cur = tx_time[q]; u32 rx_cur = rx_time[q]; u8 tid = ac_to_tid[i]; @@ -176,8 +176,8 @@ mt7921_mac_decode_he_radiotap_ru(struct mt76_rx_status *status, u32 ru_h, ru_l; u8 ru, offs = 0; - ru_l = FIELD_GET(MT_PRXV_HE_RU_ALLOC_L, le32_to_cpu(rxv[0])); - ru_h = FIELD_GET(MT_PRXV_HE_RU_ALLOC_H, le32_to_cpu(rxv[1])); + ru_l = le32_get_bits(rxv[0], MT_PRXV_HE_RU_ALLOC_L); + ru_h = le32_get_bits(rxv[1], MT_PRXV_HE_RU_ALLOC_H); ru = (u8)(ru_l | ru_h << 4); status->bw = RATE_INFO_BW_HE_RU; @@ -247,19 +247,19 @@ mt7921_mac_decode_he_mu_radiotap(struct sk_buff *skb, __le32 *rxv) MU_PREP(FLAGS2_SIG_B_SYMS_USERS, le32_get_bits(rxv[2], MT_CRXV_HE_NUM_USER)); - he_mu->ru_ch1[0] = FIELD_GET(MT_CRXV_HE_RU0, le32_to_cpu(rxv[3])); + he_mu->ru_ch1[0] = le32_get_bits(rxv[3], MT_CRXV_HE_RU0); if (status->bw >= RATE_INFO_BW_40) { he_mu->flags1 |= HE_BITS(MU_FLAGS1_CH2_RU_KNOWN); he_mu->ru_ch2[0] = - FIELD_GET(MT_CRXV_HE_RU1, le32_to_cpu(rxv[3])); + le32_get_bits(rxv[3], MT_CRXV_HE_RU1); } if (status->bw >= RATE_INFO_BW_80) { he_mu->ru_ch1[1] = - FIELD_GET(MT_CRXV_HE_RU2, le32_to_cpu(rxv[3])); + le32_get_bits(rxv[3], MT_CRXV_HE_RU2); he_mu->ru_ch2[1] = - FIELD_GET(MT_CRXV_HE_RU3, le32_to_cpu(rxv[3])); + le32_get_bits(rxv[3], MT_CRXV_HE_RU3); } } @@ -304,15 +304,16 @@ mt7921_mac_decode_he_radiotap(struct sk_buff *skb, __le32 *rxv, u32 mode) case MT_PHY_TYPE_HE_SU: he->data1 |= HE_BITS(DATA1_FORMAT_SU) | HE_BITS(DATA1_UL_DL_KNOWN) | - HE_BITS(DATA1_BEAM_CHANGE_KNOWN); + HE_BITS(DATA1_BEAM_CHANGE_KNOWN) | + HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); he->data3 |= HE_PREP(DATA3_BEAM_CHANGE, BEAM_CHNG, rxv[14]) | HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); - he->data4 |= HE_PREP(DATA4_SU_MU_SPTL_REUSE, SR_MASK, rxv[11]); break; case MT_PHY_TYPE_HE_EXT_SU: he->data1 |= HE_BITS(DATA1_FORMAT_EXT_SU) | - HE_BITS(DATA1_UL_DL_KNOWN); + HE_BITS(DATA1_UL_DL_KNOWN) | + HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); break; @@ -402,15 +403,15 @@ mt7921_mac_assoc_rssi(struct mt7921_dev *dev, struct sk_buff *skb) static int mt7921_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) { struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap); struct mt7921_sta *msta = (struct mt7921_sta *)status->wcid; + __le32 *rxd = (__le32 *)skb->data; struct ieee80211_sta *sta; struct ieee80211_vif *vif; struct ieee80211_hdr hdr; - struct ethhdr eth_hdr; - __le32 *rxd = (__le32 *)skb->data; - __le32 qos_ctrl, ht_ctrl; + u16 frame_control; - if (FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, le32_to_cpu(rxd[3])) != + if (le32_get_bits(rxd[3], MT_RXD3_NORMAL_ADDR_TYPE) != MT_RXD3_NORMAL_U2M) return -EINVAL; @@ -424,47 +425,52 @@ static int mt7921_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); /* store the info from RXD and ethhdr to avoid being overridden */ - memcpy(ð_hdr, skb->data + hdr_gap, sizeof(eth_hdr)); - hdr.frame_control = FIELD_GET(MT_RXD6_FRAME_CONTROL, rxd[6]); - hdr.seq_ctrl = FIELD_GET(MT_RXD8_SEQ_CTRL, rxd[8]); - qos_ctrl = FIELD_GET(MT_RXD8_QOS_CTL, rxd[8]); - ht_ctrl = FIELD_GET(MT_RXD9_HT_CONTROL, rxd[9]); - + frame_control = le32_get_bits(rxd[6], MT_RXD6_FRAME_CONTROL); + hdr.frame_control = cpu_to_le16(frame_control); + hdr.seq_ctrl = cpu_to_le16(le32_get_bits(rxd[8], MT_RXD8_SEQ_CTRL)); hdr.duration_id = 0; + ether_addr_copy(hdr.addr1, vif->addr); ether_addr_copy(hdr.addr2, sta->addr); - switch (le16_to_cpu(hdr.frame_control) & - (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { + switch (frame_control & (IEEE80211_FCTL_TODS | + IEEE80211_FCTL_FROMDS)) { case 0: ether_addr_copy(hdr.addr3, vif->bss_conf.bssid); break; case IEEE80211_FCTL_FROMDS: - ether_addr_copy(hdr.addr3, eth_hdr.h_source); + ether_addr_copy(hdr.addr3, eth_hdr->h_source); break; case IEEE80211_FCTL_TODS: - ether_addr_copy(hdr.addr3, eth_hdr.h_dest); + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); break; case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS: - ether_addr_copy(hdr.addr3, eth_hdr.h_dest); - ether_addr_copy(hdr.addr4, eth_hdr.h_source); + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + ether_addr_copy(hdr.addr4, eth_hdr->h_source); break; default: break; } skb_pull(skb, hdr_gap + sizeof(struct ethhdr) - 2); - if (eth_hdr.h_proto == htons(ETH_P_AARP) || - eth_hdr.h_proto == htons(ETH_P_IPX)) + if (eth_hdr->h_proto == cpu_to_be16(ETH_P_AARP) || + eth_hdr->h_proto == cpu_to_be16(ETH_P_IPX)) ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header); - else if (eth_hdr.h_proto >= htons(ETH_P_802_3_MIN)) + else if (be16_to_cpu(eth_hdr->h_proto) >= ETH_P_802_3_MIN) ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header); else skb_pull(skb, 2); if (ieee80211_has_order(hdr.frame_control)) - memcpy(skb_push(skb, 2), &ht_ctrl, 2); - if (ieee80211_is_data_qos(hdr.frame_control)) - memcpy(skb_push(skb, 2), &qos_ctrl, 2); + memcpy(skb_push(skb, IEEE80211_HT_CTL_LEN), &rxd[9], + IEEE80211_HT_CTL_LEN); + if (ieee80211_is_data_qos(hdr.frame_control)) { + __le16 qos_ctrl; + + qos_ctrl = cpu_to_le16(le32_get_bits(rxd[8], MT_RXD8_QOS_CTL)); + memcpy(skb_push(skb, IEEE80211_QOS_CTL_LEN), &qos_ctrl, + IEEE80211_QOS_CTL_LEN); + } + if (ieee80211_has_a4(hdr.frame_control)) memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); else @@ -666,9 +672,6 @@ mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb) status->chain_signal[i]); } - if (status->signal == -128) - status->flag |= RX_FLAG_NO_SIGNAL_VAL; - stbc = FIELD_GET(MT_PRXV_STBC, v0); gi = FIELD_GET(MT_PRXV_SGI, v0); cck = false; @@ -912,11 +915,18 @@ mt7921_mac_write_txwi_80211(struct mt7921_dev *dev, __le32 *txwi, val = MT_TXD3_SN_VALID | FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); txwi[3] |= cpu_to_le32(val); + txwi[7] &= ~cpu_to_le32(MT_TXD7_HW_AMSDU); } - val = FIELD_PREP(MT_TXD7_TYPE, fc_type) | - FIELD_PREP(MT_TXD7_SUB_TYPE, fc_stype); - txwi[7] |= cpu_to_le32(val); + if (mt76_is_mmio(&dev->mt76)) { + val = FIELD_PREP(MT_TXD7_TYPE, fc_type) | + FIELD_PREP(MT_TXD7_SUB_TYPE, fc_stype); + txwi[7] |= cpu_to_le32(val); + } else { + val = FIELD_PREP(MT_TXD8_L_TYPE, fc_type) | + FIELD_PREP(MT_TXD8_L_SUB_TYPE, fc_stype); + txwi[8] |= cpu_to_le32(val); + } } void mt7921_mac_write_txwi(struct mt7921_dev *dev, __le32 *txwi, @@ -950,7 +960,7 @@ void mt7921_mac_write_txwi(struct mt7921_dev *dev, __le32 *txwi, } else { p_fmt = is_mmio ? MT_TX_TYPE_CT : MT_TX_TYPE_SF; q_idx = wmm_idx * MT7921_MAX_WMM_SETS + - mt7921_lmac_mapping(dev, skb_get_queue_mapping(skb)); + mt76_connac_lmac_mapping(skb_get_queue_mapping(skb)); } val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + sz_txd) | @@ -1016,7 +1026,7 @@ void mt7921_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi) if (!sta || !(sta->ht_cap.ht_supported || sta->he_cap.has_he)) return; - tid = FIELD_GET(MT_TXD1_TID, le32_to_cpu(txwi[1])); + tid = le32_get_bits(txwi[1], MT_TXD1_TID); if (tid >= 6) /* skip VO queue */ return; @@ -1092,7 +1102,6 @@ mt7921_mac_add_txs_skb(struct mt7921_dev *dev, struct mt76_wcid *wcid, int pid, break; case MT_PHY_TYPE_HT: case MT_PHY_TYPE_HT_GF: - rate.mcs += (rate.nss - 1) * 8; if (rate.mcs > 31) goto out; @@ -1156,18 +1165,13 @@ void mt7921_mac_add_txs(struct mt7921_dev *dev, void *data) struct mt76_wcid *wcid; __le32 *txs_data = data; u16 wcidx; - u32 txs; u8 pid; - txs = le32_to_cpu(txs_data[0]); - if (FIELD_GET(MT_TXS0_TXS_FORMAT, txs) > 1) + if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) > 1) return; - txs = le32_to_cpu(txs_data[2]); - wcidx = FIELD_GET(MT_TXS2_WCID, txs); - - txs = le32_to_cpu(txs_data[3]); - pid = FIELD_GET(MT_TXS3_PID, txs); + wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID); + pid = le32_get_bits(txs_data[3], MT_TXS3_PID); if (pid < MT_PACKET_ID_FIRST) return; @@ -1195,6 +1199,7 @@ void mt7921_mac_add_txs(struct mt7921_dev *dev, void *data) out: rcu_read_unlock(); } +EXPORT_SYMBOL_GPL(mt7921_mac_add_txs); void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, struct sk_buff *skb) @@ -1205,8 +1210,8 @@ void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, enum rx_pkt_type type; u16 flag; - type = FIELD_GET(MT_RXD0_PKT_TYPE, le32_to_cpu(rxd[0])); - flag = FIELD_GET(MT_RXD0_PKT_FLAG, le32_to_cpu(rxd[0])); + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + flag = le32_get_bits(rxd[0], MT_RXD0_PKT_FLAG); if (type == PKT_TYPE_RX_EVENT && flag == 0x1) type = PKT_TYPE_NORMAL_MCU; @@ -1548,7 +1553,16 @@ void mt7921_pm_power_save_work(struct work_struct *work) delta = dev->pm.idle_timeout; if (test_bit(MT76_HW_SCANNING, &mphy->state) || - test_bit(MT76_HW_SCHED_SCANNING, &mphy->state)) + test_bit(MT76_HW_SCHED_SCANNING, &mphy->state) || + dev->fw_assert) + goto out; + + if (mutex_is_locked(&dev->mt76.mutex)) + /* if mt76 mutex is held we should not put the device + * to sleep since we are currently accessing device + * register map. We need to wait for the next power_save + * trigger. + */ goto out; if (time_is_after_jiffies(dev->pm.last_activity + delta)) { @@ -1610,3 +1624,94 @@ void mt7921_coredump_work(struct work_struct *work) mt7921_reset(&dev->mt76); } + +/* usb_sdio */ +static void +mt7921_usb_sdio_write_txwi(struct mt7921_dev *dev, struct mt76_wcid *wcid, + enum mt76_txq_id qid, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, int pid, + struct sk_buff *skb) +{ + __le32 *txwi = (__le32 *)(skb->data - MT_SDIO_TXD_SIZE); + + memset(txwi, 0, MT_SDIO_TXD_SIZE); + mt7921_mac_write_txwi(dev, txwi, skb, wcid, key, pid, false); + skb_push(skb, MT_SDIO_TXD_SIZE); +} + +int mt7921_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); + struct ieee80211_key_conf *key = info->control.hw_key; + struct sk_buff *skb = tx_info->skb; + int err, pad, pktid, type; + + if (unlikely(tx_info->skb->len <= ETH_HLEN)) + return -EINVAL; + + if (!wcid) + wcid = &dev->mt76.global_wcid; + + if (sta) { + struct mt7921_sta *msta = (struct mt7921_sta *)sta->drv_priv; + + if (time_after(jiffies, msta->last_txs + HZ / 4)) { + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + msta->last_txs = jiffies; + } + } + + pktid = mt76_tx_status_skb_add(&dev->mt76, wcid, skb); + mt7921_usb_sdio_write_txwi(dev, wcid, qid, sta, key, pktid, skb); + + type = mt76_is_sdio(mdev) ? MT7921_SDIO_DATA : 0; + mt7921_skb_add_usb_sdio_hdr(dev, skb, type); + pad = round_up(skb->len, 4) - skb->len; + if (mt76_is_usb(mdev)) + pad += 4; + + err = mt76_skb_adjust_pad(skb, pad); + if (err) + /* Release pktid in case of error. */ + idr_remove(&wcid->pktid, pktid); + + return err; +} +EXPORT_SYMBOL_GPL(mt7921_usb_sdio_tx_prepare_skb); + +void mt7921_usb_sdio_tx_complete_skb(struct mt76_dev *mdev, + struct mt76_queue_entry *e) +{ + __le32 *txwi = (__le32 *)(e->skb->data + MT_SDIO_HDR_SIZE); + unsigned int headroom = MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE; + struct ieee80211_sta *sta; + struct mt76_wcid *wcid; + u16 idx; + + idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX); + wcid = rcu_dereference(mdev->wcid[idx]); + sta = wcid_to_sta(wcid); + + if (sta && likely(e->skb->protocol != cpu_to_be16(ETH_P_PAE))) + mt7921_tx_check_aggr(sta, txwi); + + skb_pull(e->skb, headroom); + mt76_tx_complete_skb(mdev, e->wcid, e->skb); +} +EXPORT_SYMBOL_GPL(mt7921_usb_sdio_tx_complete_skb); + +bool mt7921_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update) +{ + struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); + + mt7921_mutex_acquire(dev); + mt7921_mac_sta_poll(dev); + mt7921_mutex_release(dev); + + return false; +} +EXPORT_SYMBOL_GPL(mt7921_usb_sdio_tx_status_data); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.h b/drivers/net/wireless/mediatek/mt76/mt7921/mac.h index 544a1c33126a..79447e2d0143 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.h @@ -202,6 +202,7 @@ enum tx_mcu_port_q_idx { #define MT_SDIO_TXD_SIZE (MT_TXD_SIZE + 8 * 4) #define MT_SDIO_TAIL_SIZE 8 #define MT_SDIO_HDR_SIZE 4 +#define MT_USB_TAIL_SIZE 4 #define MT_TXD0_Q_IDX GENMASK(31, 25) #define MT_TXD0_PKT_FMT GENMASK(24, 23) @@ -284,6 +285,9 @@ enum tx_mcu_port_q_idx { #define MT_TXD7_HW_AMSDU BIT(10) #define MT_TXD7_TX_TIME GENMASK(9, 0) +#define MT_TXD8_L_TYPE GENMASK(5, 4) +#define MT_TXD8_L_SUB_TYPE GENMASK(3, 0) + #define MT_TX_RATE_STBC BIT(13) #define MT_TX_RATE_NSS GENMASK(12, 10) #define MT_TX_RATE_MODE GENMASK(9, 6) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 7a8d2596c226..fdaf2451bc1d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -264,7 +264,7 @@ static int mt7921_start(struct ieee80211_hw *hw) return err; } -static void mt7921_stop(struct ieee80211_hw *hw) +void mt7921_stop(struct ieee80211_hw *hw) { struct mt7921_dev *dev = mt7921_hw_dev(hw); struct mt7921_phy *phy = mt7921_hw_phy(hw); @@ -273,6 +273,7 @@ static void mt7921_stop(struct ieee80211_hw *hw) cancel_delayed_work_sync(&dev->pm.ps_work); cancel_work_sync(&dev->pm.wake_work); + cancel_work_sync(&dev->reset_work); mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); mt7921_mutex_acquire(dev); @@ -280,6 +281,7 @@ static void mt7921_stop(struct ieee80211_hw *hw) mt76_connac_mcu_set_mac_enable(&dev->mt76, 0, false, false); mt7921_mutex_release(dev); } +EXPORT_SYMBOL_GPL(mt7921_stop); static int mt7921_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) @@ -452,19 +454,64 @@ static int mt7921_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, mt76_wcid_key_setup(&dev->mt76, wcid, cmd == SET_KEY ? key : NULL); - err = mt7921_mcu_add_key(dev, vif, msta, key, cmd); + err = mt76_connac_mcu_add_key(&dev->mt76, vif, &msta->bip, + key, MCU_UNI_CMD(STA_REC_UPDATE), + &msta->wcid, cmd); if (err) goto out; if (key->cipher == WLAN_CIPHER_SUITE_WEP104 || key->cipher == WLAN_CIPHER_SUITE_WEP40) - err = mt7921_mcu_add_key(dev, vif, mvif->wep_sta, key, cmd); + err = mt76_connac_mcu_add_key(&dev->mt76, vif, + &mvif->wep_sta->bip, + key, MCU_UNI_CMD(STA_REC_UPDATE), + &mvif->wep_sta->wcid, cmd); out: mt7921_mutex_release(dev); return err; } +static void +mt7921_pm_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt7921_dev *dev = priv; + + mt7921_mcu_set_beacon_filter(dev, vif, dev->pm.enable); +} + +static void +mt7921_sniffer_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt7921_dev *dev = priv; + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt76_connac_pm *pm = &dev->pm; + bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + + mt7921_mcu_set_sniffer(dev, vif, monitor); + pm->enable = !monitor; + pm->ds_enable = !monitor; + + mt76_connac_mcu_set_deep_sleep(&dev->mt76, pm->ds_enable); + + if (monitor) + mt7921_mcu_set_beacon_filter(dev, vif, false); +} + +void mt7921_set_runtime_pm(struct mt7921_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt76_connac_pm *pm = &dev->pm; + bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + + pm->enable = pm->enable_user && !monitor; + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_pm_interface_iter, dev); + pm->ds_enable = pm->ds_enable_user && !monitor; + mt76_connac_mcu_set_deep_sleep(&dev->mt76, pm->ds_enable); +} + static int mt7921_config(struct ieee80211_hw *hw, u32 changed) { struct mt7921_dev *dev = mt7921_hw_dev(hw); @@ -488,16 +535,10 @@ static int mt7921_config(struct ieee80211_hw *hw, u32 changed) } if (changed & IEEE80211_CONF_CHANGE_MONITOR) { - bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); - - if (!enabled) - phy->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; - else - phy->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC; - - mt76_rmw_field(dev, MT_DMA_DCR0(0), MT_DMA_DCR0_RXD_G5_EN, - enabled); - mt76_wr(dev, MT_WF_RFCR(0), phy->rxfilter); + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_sniffer_interface_iter, dev); + dev->mt76.rxfilter = mt76_rr(dev, MT_WF_RFCR(0)); } out: @@ -510,11 +551,10 @@ static int mt7921_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { - struct mt7921_dev *dev = mt7921_hw_dev(hw); struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; /* no need to update right away, we'll get BSS_CHANGED_QOS */ - queue = mt7921_lmac_mapping(dev, queue); + queue = mt76_connac_lmac_mapping(queue); mvif->queue_params[queue] = *params; return 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index ef1e1ef91611..da2be050ed7c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -67,25 +67,6 @@ struct mt7921_fw_region { #define MT_STA_BFER BIT(0) #define MT_STA_BFEE BIT(1) -#define FW_FEATURE_SET_ENCRYPT BIT(0) -#define FW_FEATURE_SET_KEY_IDX GENMASK(2, 1) -#define FW_FEATURE_ENCRY_MODE BIT(4) -#define FW_FEATURE_OVERRIDE_ADDR BIT(5) - -#define DL_MODE_ENCRYPT BIT(0) -#define DL_MODE_KEY_IDX GENMASK(2, 1) -#define DL_MODE_RESET_SEC_IV BIT(3) -#define DL_MODE_WORKING_PDA_CR4 BIT(4) -#define DL_CONFIG_ENCRY_MODE_SEL BIT(6) -#define DL_MODE_NEED_RSP BIT(31) - -#define FW_START_OVERRIDE BIT(0) -#define FW_START_WORKING_PDA_CR4 BIT(2) - -#define PATCH_SEC_NOT_SUPPORT GENMASK(31, 0) -#define PATCH_SEC_TYPE_MASK GENMASK(15, 0) -#define PATCH_SEC_TYPE_INFO 0x2 - #define PATCH_SEC_ENC_TYPE_MASK GENMASK(31, 24) #define PATCH_SEC_ENC_TYPE_PLAIN 0x00 #define PATCH_SEC_ENC_TYPE_AES 0x01 @@ -93,52 +74,6 @@ struct mt7921_fw_region { #define PATCH_SEC_ENC_SCRAMBLE_INFO_MASK GENMASK(15, 0) #define PATCH_SEC_ENC_AES_KEY_MASK GENMASK(7, 0) -static enum mcu_cipher_type -mt7921_mcu_get_cipher(int cipher) -{ - switch (cipher) { - case WLAN_CIPHER_SUITE_WEP40: - return MCU_CIPHER_WEP40; - case WLAN_CIPHER_SUITE_WEP104: - return MCU_CIPHER_WEP104; - case WLAN_CIPHER_SUITE_TKIP: - return MCU_CIPHER_TKIP; - case WLAN_CIPHER_SUITE_AES_CMAC: - return MCU_CIPHER_BIP_CMAC_128; - case WLAN_CIPHER_SUITE_CCMP: - return MCU_CIPHER_AES_CCMP; - case WLAN_CIPHER_SUITE_CCMP_256: - return MCU_CIPHER_CCMP_256; - case WLAN_CIPHER_SUITE_GCMP: - return MCU_CIPHER_GCMP; - case WLAN_CIPHER_SUITE_GCMP_256: - return MCU_CIPHER_GCMP_256; - case WLAN_CIPHER_SUITE_SMS4: - return MCU_CIPHER_WAPI; - default: - return MCU_CIPHER_NONE; - } -} - -static u8 mt7921_mcu_chan_bw(struct cfg80211_chan_def *chandef) -{ - static const u8 width_to_bw[] = { - [NL80211_CHAN_WIDTH_40] = CMD_CBW_40MHZ, - [NL80211_CHAN_WIDTH_80] = CMD_CBW_80MHZ, - [NL80211_CHAN_WIDTH_80P80] = CMD_CBW_8080MHZ, - [NL80211_CHAN_WIDTH_160] = CMD_CBW_160MHZ, - [NL80211_CHAN_WIDTH_5] = CMD_CBW_5MHZ, - [NL80211_CHAN_WIDTH_10] = CMD_CBW_10MHZ, - [NL80211_CHAN_WIDTH_20] = CMD_CBW_20MHZ, - [NL80211_CHAN_WIDTH_20_NOHT] = CMD_CBW_20MHZ, - }; - - if (chandef->width >= ARRAY_SIZE(width_to_bw)) - return 0; - - return width_to_bw[chandef->width]; -} - static int mt7921_mcu_parse_eeprom(struct mt76_dev *dev, struct sk_buff *skb) { @@ -465,95 +400,6 @@ void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb) } /** starec & wtbl **/ -static int -mt7921_mcu_sta_key_tlv(struct mt7921_sta *msta, struct sk_buff *skb, - struct ieee80211_key_conf *key, enum set_key_cmd cmd) -{ - struct mt7921_sta_key_conf *bip = &msta->bip; - struct sta_rec_sec *sec; - struct tlv *tlv; - u32 len = sizeof(*sec); - - tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V2, sizeof(*sec)); - - sec = (struct sta_rec_sec *)tlv; - sec->add = cmd; - - if (cmd == SET_KEY) { - struct sec_key *sec_key; - u8 cipher; - - cipher = mt7921_mcu_get_cipher(key->cipher); - if (cipher == MCU_CIPHER_NONE) - return -EOPNOTSUPP; - - sec_key = &sec->key[0]; - sec_key->cipher_len = sizeof(*sec_key); - - if (cipher == MCU_CIPHER_BIP_CMAC_128) { - sec_key->cipher_id = MCU_CIPHER_AES_CCMP; - sec_key->key_id = bip->keyidx; - sec_key->key_len = 16; - memcpy(sec_key->key, bip->key, 16); - - sec_key = &sec->key[1]; - sec_key->cipher_id = MCU_CIPHER_BIP_CMAC_128; - sec_key->cipher_len = sizeof(*sec_key); - sec_key->key_len = 16; - memcpy(sec_key->key, key->key, 16); - - sec->n_cipher = 2; - } else { - sec_key->cipher_id = cipher; - sec_key->key_id = key->keyidx; - sec_key->key_len = key->keylen; - memcpy(sec_key->key, key->key, key->keylen); - - if (cipher == MCU_CIPHER_TKIP) { - /* Rx/Tx MIC keys are swapped */ - memcpy(sec_key->key + 16, key->key + 24, 8); - memcpy(sec_key->key + 24, key->key + 16, 8); - } - - /* store key_conf for BIP batch update */ - if (cipher == MCU_CIPHER_AES_CCMP) { - memcpy(bip->key, key->key, key->keylen); - bip->keyidx = key->keyidx; - } - - len -= sizeof(*sec_key); - sec->n_cipher = 1; - } - } else { - len -= sizeof(sec->key); - sec->n_cipher = 0; - } - sec->len = cpu_to_le16(len); - - return 0; -} - -int mt7921_mcu_add_key(struct mt7921_dev *dev, struct ieee80211_vif *vif, - struct mt7921_sta *msta, struct ieee80211_key_conf *key, - enum set_key_cmd cmd) -{ - struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; - struct sk_buff *skb; - int ret; - - skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, - &msta->wcid); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - ret = mt7921_mcu_sta_key_tlv(msta, skb, key, cmd); - if (ret) - return ret; - - return mt76_mcu_skb_send_msg(&dev->mt76, skb, - MCU_UNI_CMD(STA_REC_UPDATE), true); -} - int mt7921_mcu_uni_tx_ba(struct mt7921_dev *dev, struct ieee80211_ampdu_params *params, bool enable) @@ -564,6 +410,7 @@ int mt7921_mcu_uni_tx_ba(struct mt7921_dev *dev, msta->wcid.amsdu = false; return mt76_connac_mcu_sta_ba(&dev->mt76, &msta->vif->mt76, params, + MCU_UNI_CMD(STA_REC_UPDATE), enable, true); } @@ -574,23 +421,10 @@ int mt7921_mcu_uni_rx_ba(struct mt7921_dev *dev, struct mt7921_sta *msta = (struct mt7921_sta *)params->sta->drv_priv; return mt76_connac_mcu_sta_ba(&dev->mt76, &msta->vif->mt76, params, + MCU_UNI_CMD(STA_REC_UPDATE), enable, false); } -int mt7921_mcu_restart(struct mt76_dev *dev) -{ - struct { - u8 power_mode; - u8 rsv[3]; - } req = { - .power_mode = 1, - }; - - return mt76_mcu_send_msg(dev, MCU_CMD(NIC_POWER_CTRL), &req, - sizeof(req), false); -} -EXPORT_SYMBOL_GPL(mt7921_mcu_restart); - static u32 mt7921_get_data_mode(struct mt7921_dev *dev, u32 info) { u32 mode = DL_MODE_NEED_RSP; @@ -707,12 +541,8 @@ static int mt7921_load_patch(struct mt7921_dev *dev) if (mt76_is_sdio(&dev->mt76)) { /* activate again */ ret = __mt7921_mcu_fw_pmctrl(dev); - if (ret) - return ret; - - ret = __mt7921_mcu_drv_pmctrl(dev); - if (ret) - return ret; + if (!ret) + ret = __mt7921_mcu_drv_pmctrl(dev); } out: @@ -730,22 +560,6 @@ static int mt7921_load_patch(struct mt7921_dev *dev) return ret; } -static u32 mt7921_mcu_gen_dl_mode(u8 feature_set, bool is_wa) -{ - u32 ret = 0; - - ret |= (feature_set & FW_FEATURE_SET_ENCRYPT) ? - (DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV) : 0; - ret |= (feature_set & FW_FEATURE_ENCRY_MODE) ? - DL_CONFIG_ENCRY_MODE_SEL : 0; - ret |= FIELD_PREP(DL_MODE_KEY_IDX, - FIELD_GET(FW_FEATURE_SET_KEY_IDX, feature_set)); - ret |= DL_MODE_NEED_RSP; - ret |= is_wa ? DL_MODE_WORKING_PDA_CR4 : 0; - - return ret; -} - static int mt7921_mcu_send_ram_firmware(struct mt7921_dev *dev, const struct mt7921_fw_trailer *hdr, @@ -763,7 +577,8 @@ mt7921_mcu_send_ram_firmware(struct mt7921_dev *dev, region = (const struct mt7921_fw_region *)((const u8 *)hdr - (hdr->n_region - i) * sizeof(*region)); - mode = mt7921_mcu_gen_dl_mode(region->feature_set, is_wa); + mode = mt76_connac_mcu_gen_dl_mode(&dev->mt76, + region->feature_set, is_wa); len = le32_to_cpu(region->len); addr = le32_to_cpu(region->addr); @@ -920,33 +735,26 @@ EXPORT_SYMBOL_GPL(mt7921_mcu_exit); int mt7921_mcu_set_tx(struct mt7921_dev *dev, struct ieee80211_vif *vif) { -#define WMM_AIFS_SET BIT(0) -#define WMM_CW_MIN_SET BIT(1) -#define WMM_CW_MAX_SET BIT(2) -#define WMM_TXOP_SET BIT(3) -#define WMM_PARAM_SET GENMASK(3, 0) -#define TX_CMD_MODE 1 + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; struct edca { - u8 queue; - u8 set; - u8 aifs; - u8 cw_min; + __le16 cw_min; __le16 cw_max; __le16 txop; - }; + __le16 aifs; + u8 guardtime; + u8 acm; + } __packed; struct mt7921_mcu_tx { - u8 total; - u8 action; - u8 valid; - u8 mode; - struct edca edca[IEEE80211_NUM_ACS]; + u8 bss_idx; + u8 qos; + u8 wmm_idx; + u8 pad; } __packed req = { - .valid = true, - .mode = TX_CMD_MODE, - .total = IEEE80211_NUM_ACS, + .bss_idx = mvif->mt76.idx, + .qos = vif->bss_conf.qos, + .wmm_idx = mvif->mt76.wmm_idx, }; - struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; struct mu_edca { u8 cw_min; u8 cw_max; @@ -970,30 +778,29 @@ int mt7921_mcu_set_tx(struct mt7921_dev *dev, struct ieee80211_vif *vif) .qos = vif->bss_conf.qos, .wmm_idx = mvif->mt76.wmm_idx, }; + static const int to_aci[] = { 1, 0, 2, 3 }; int ac, ret; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { struct ieee80211_tx_queue_params *q = &mvif->queue_params[ac]; - struct edca *e = &req.edca[ac]; + struct edca *e = &req.edca[to_aci[ac]]; - e->set = WMM_PARAM_SET; - e->queue = ac + mvif->mt76.wmm_idx * MT7921_MAX_WMM_SETS; - e->aifs = q->aifs; + e->aifs = cpu_to_le16(q->aifs); e->txop = cpu_to_le16(q->txop); if (q->cw_min) - e->cw_min = fls(q->cw_min); + e->cw_min = cpu_to_le16(q->cw_min); else - e->cw_min = 5; + e->cw_min = cpu_to_le16(5); if (q->cw_max) - e->cw_max = cpu_to_le16(fls(q->cw_max)); + e->cw_max = cpu_to_le16(q->cw_max); else e->cw_max = cpu_to_le16(10); } - ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EDCA_UPDATE), - &req, sizeof(req), true); + ret = mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_EDCA_PARMS), &req, + sizeof(req), false); if (ret) return ret; @@ -1003,7 +810,6 @@ int mt7921_mcu_set_tx(struct mt7921_dev *dev, struct ieee80211_vif *vif) for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { struct ieee80211_he_mu_edca_param_ac_rec *q; struct mu_edca *e; - int to_aci[] = {1, 0, 2, 3}; if (!mvif->queue_params[ac].mu_edca) break; @@ -1046,7 +852,7 @@ int mt7921_mcu_set_chan_info(struct mt7921_phy *phy, int cmd) } __packed req = { .control_ch = chandef->chan->hw_value, .center_ch = ieee80211_frequency_to_channel(freq1), - .bw = mt7921_mcu_chan_bw(chandef), + .bw = mt76_connac_chan_bw(chandef), .tx_streams_num = hweight8(phy->mt76->antenna_mask), .rx_streams = phy->mt76->antenna_mask, .band_idx = phy != &dev->phy, @@ -1057,10 +863,13 @@ int mt7921_mcu_set_chan_info(struct mt7921_phy *phy, int cmd) else req.channel_band = chandef->chan->band; - if (dev->mt76.hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) + if (cmd == MCU_EXT_CMD(SET_RX_PATH) || + dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR) + req.switch_reason = CH_SWITCH_NORMAL; + else if (dev->mt76.hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; - else if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) && - chandef->chan->dfs_state != NL80211_DFS_AVAILABLE) + else if (!cfg80211_reg_can_beacon(dev->mt76.hw->wiphy, chandef, + NL80211_IFTYPE_AP)) req.switch_reason = CH_SWITCH_DFS; else req.switch_reason = CH_SWITCH_NORMAL; @@ -1093,30 +902,6 @@ int mt7921_mcu_set_eeprom(struct mt7921_dev *dev) } EXPORT_SYMBOL_GPL(mt7921_mcu_set_eeprom); -int mt7921_mcu_get_eeprom(struct mt7921_dev *dev, u32 offset) -{ - struct mt7921_mcu_eeprom_info req = { - .addr = cpu_to_le32(round_down(offset, 16)), - }; - struct mt7921_mcu_eeprom_info *res; - struct sk_buff *skb; - int ret; - u8 *buf; - - ret = mt76_mcu_send_and_get_msg(&dev->mt76, - MCU_EXT_QUERY(EFUSE_ACCESS), - &req, sizeof(req), true, &skb); - if (ret) - return ret; - - res = (struct mt7921_mcu_eeprom_info *)skb->data; - buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr); - memcpy(buf, res->data, 16); - dev_kfree_skb(skb); - - return 0; -} - int mt7921_mcu_uni_bss_ps(struct mt7921_dev *dev, struct ieee80211_vif *vif) { struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; @@ -1351,3 +1136,33 @@ int mt7921_get_txpwr_info(struct mt7921_dev *dev, struct mt7921_txpwr *txpwr) return 0; } + +int mt7921_mcu_set_sniffer(struct mt7921_dev *dev, struct ieee80211_vif *vif, + bool enable) +{ + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + struct { + struct { + u8 band_idx; + u8 pad[3]; + } __packed hdr; + struct sniffer_enable_tlv { + __le16 tag; + __le16 len; + u8 enable; + u8 pad[3]; + } __packed enable; + } req = { + .hdr = { + .band_idx = mvif->band_idx, + }, + .enable = { + .tag = cpu_to_le16(0), + .len = cpu_to_le16(sizeof(struct sniffer_enable_tlv)), + .enable = enable, + }, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), + true); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index 96647801850a..7690364bc079 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -30,6 +30,7 @@ #define MT7921_DRV_OWN_RETRY_COUNT 10 #define MT7921_MCU_INIT_RETRY_COUNT 10 +#define MT7921_WFSYS_INIT_RETRY_COUNT 2 #define MT7921_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1.bin" #define MT7921_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1_2_hdr.bin" @@ -89,11 +90,6 @@ enum mt7921_rxq_id { MT7921_RXQ_MCU_WM = 0, }; -struct mt7921_sta_key_conf { - s8 keyidx; - u8 key[16]; -}; - struct mt7921_sta { struct mt76_wcid wcid; /* must be first */ @@ -106,7 +102,7 @@ struct mt7921_sta { unsigned long ampdu_state; struct mt76_sta_stats stats; - struct mt7921_sta_key_conf bip; + struct mt76_connac_sta_key_conf bip; }; DECLARE_EWMA(rssi, 10, 8); @@ -209,6 +205,8 @@ struct mt7921_dev { struct list_head sta_poll_list; spinlock_t sta_poll_lock; + struct work_struct init_work; + u8 fw_debug; struct mt76_connac_pm pm; @@ -277,12 +275,6 @@ mt7921_hw_dev(struct ieee80211_hw *hw) #define mt7921_mutex_release(dev) \ mt76_connac_mutex_release(&(dev)->mt76, &(dev)->pm) -static inline u8 mt7921_lmac_mapping(struct mt7921_dev *dev, u8 ac) -{ - /* LMAC uses the reverse order of mac80211 AC indexes */ - return 3 - ac; -} - extern const struct ieee80211_ops mt7921_ops; extern struct pci_driver mt7921_pci_driver; @@ -296,16 +288,12 @@ int mt7921_wpdma_reset(struct mt7921_dev *dev, bool force); int mt7921_wpdma_reinit_cond(struct mt7921_dev *dev); void mt7921_dma_cleanup(struct mt7921_dev *dev); int mt7921_run_firmware(struct mt7921_dev *dev); -int mt7921_mcu_add_key(struct mt7921_dev *dev, struct ieee80211_vif *vif, - struct mt7921_sta *msta, struct ieee80211_key_conf *key, - enum set_key_cmd cmd); int mt7921_mcu_sta_update(struct mt7921_dev *dev, struct ieee80211_sta *sta, struct ieee80211_vif *vif, bool enable, enum mt76_sta_info_state state); int mt7921_mcu_set_chan_info(struct mt7921_phy *phy, int cmd); int mt7921_mcu_set_tx(struct mt7921_dev *dev, struct ieee80211_vif *vif); int mt7921_mcu_set_eeprom(struct mt7921_dev *dev); -int mt7921_mcu_get_eeprom(struct mt7921_dev *dev, u32 offset); int mt7921_mcu_get_rx_rate(struct mt7921_phy *phy, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct rate_info *rate); int mt7921_mcu_fw_log_2_host(struct mt7921_dev *dev, u8 ctrl); @@ -367,17 +355,20 @@ static inline void mt7921_mcu_tx_cleanup(struct mt7921_dev *dev) mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WA], false); } -static inline void mt7921_skb_add_sdio_hdr(struct sk_buff *skb, - enum mt7921_sdio_pkt_type type) +static inline void +mt7921_skb_add_usb_sdio_hdr(struct mt7921_dev *dev, struct sk_buff *skb, + int type) { - u32 hdr; + u32 hdr, len; - hdr = FIELD_PREP(MT7921_SDIO_HDR_TX_BYTES, skb->len + sizeof(hdr)) | + len = mt76_is_usb(&dev->mt76) ? skb->len : skb->len + sizeof(hdr); + hdr = FIELD_PREP(MT7921_SDIO_HDR_TX_BYTES, len) | FIELD_PREP(MT7921_SDIO_HDR_PKT_TYPE, type); put_unaligned_le32(hdr, skb_push(skb, sizeof(hdr))); } +void mt7921_stop(struct ieee80211_hw *hw); int mt7921_mac_init(struct mt7921_dev *dev); bool mt7921_mac_wtbl_update(struct mt7921_dev *dev, int idx, u32 mask); void mt7921_mac_reset_counters(struct mt7921_phy *phy); @@ -399,7 +390,6 @@ int mt7921e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, void mt7921_tx_worker(struct mt76_worker *w); void mt7921e_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e); -int mt7921_init_tx_queues(struct mt7921_phy *phy, int idx, int n_desc); void mt7921_tx_token_put(struct mt7921_dev *dev); void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, struct sk_buff *skb); @@ -424,7 +414,6 @@ int mt7921_mcu_drv_pmctrl(struct mt7921_dev *dev); int mt7921_mcu_fw_pmctrl(struct mt7921_dev *dev); void mt7921_pm_wake_work(struct work_struct *work); void mt7921_pm_power_save_work(struct work_struct *work); -bool mt7921_wait_for_mcu_init(struct mt7921_dev *dev); void mt7921_coredump_work(struct work_struct *work); int mt7921_wfsys_reset(struct mt7921_dev *dev); int mt7921_get_txpwr_info(struct mt7921_dev *dev, struct mt7921_txpwr *txpwr); @@ -442,8 +431,8 @@ int mt7921_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb, int cmd, int *wait_seq); int mt7921_mcu_parse_response(struct mt76_dev *mdev, int cmd, struct sk_buff *skb, int seq); -int mt7921_mcu_restart(struct mt76_dev *dev); +bool mt7921e_rx_check(struct mt76_dev *mdev, void *data, int len); void mt7921e_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, struct sk_buff *skb); int mt7921e_driver_own(struct mt7921_dev *dev); @@ -452,17 +441,33 @@ int mt7921e_mcu_init(struct mt7921_dev *dev); int mt7921s_wfsys_reset(struct mt7921_dev *dev); int mt7921s_mac_reset(struct mt7921_dev *dev); int mt7921s_init_reset(struct mt7921_dev *dev); +int __mt7921e_mcu_drv_pmctrl(struct mt7921_dev *dev); int mt7921e_mcu_drv_pmctrl(struct mt7921_dev *dev); int mt7921e_mcu_fw_pmctrl(struct mt7921_dev *dev); int mt7921s_mcu_init(struct mt7921_dev *dev); int mt7921s_mcu_drv_pmctrl(struct mt7921_dev *dev); int mt7921s_mcu_fw_pmctrl(struct mt7921_dev *dev); -int mt7921s_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, - enum mt76_txq_id qid, struct mt76_wcid *wcid, - struct ieee80211_sta *sta, - struct mt76_tx_info *tx_info); -void mt7921s_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e); -bool mt7921s_tx_status_data(struct mt76_dev *mdev, u8 *update); void mt7921_mac_add_txs(struct mt7921_dev *dev, void *data); +void mt7921_set_runtime_pm(struct mt7921_dev *dev); +int mt7921_mcu_set_sniffer(struct mt7921_dev *dev, struct ieee80211_vif *vif, + bool enable); + +int mt7921_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); +void mt7921_usb_sdio_tx_complete_skb(struct mt76_dev *mdev, + struct mt76_queue_entry *e); +bool mt7921_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update); + +/* usb */ +#define MT_USB_TYPE_VENDOR (USB_TYPE_VENDOR | 0x1f) +#define MT_USB_TYPE_UHW_VENDOR (USB_TYPE_VENDOR | 0x1e) + +int mt7921u_mcu_power_on(struct mt7921_dev *dev); +int mt7921u_wfsys_reset(struct mt7921_dev *dev); +int mt7921u_dma_init(struct mt7921_dev *dev); +int mt7921u_init_reset(struct mt7921_dev *dev); +int mt7921u_mac_reset(struct mt7921_dev *dev); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 9dae2f5972bf..1a01d025bbe5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -105,6 +105,7 @@ static void mt7921e_unregister_device(struct mt7921_dev *dev) int i; struct mt76_connac_pm *pm = &dev->pm; + cancel_work_sync(&dev->init_work); mt76_unregister_device(&dev->mt76); mt76_for_each_q_rx(&dev->mt76, i) napi_disable(&dev->mt76.napi[i]); @@ -121,6 +122,110 @@ static void mt7921e_unregister_device(struct mt7921_dev *dev) mt76_free_device(&dev->mt76); } +static u32 __mt7921_reg_addr(struct mt7921_dev *dev, u32 addr) +{ + static const struct { + u32 phys; + u32 mapped; + u32 size; + } fixed_map[] = { + { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ + { 0x820ed000, 0x24800, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ + { 0x820e4000, 0x21000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + { 0x820e7000, 0x21e00, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ + { 0x820eb000, 0x24200, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ + { 0x820e2000, 0x20800, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ + { 0x820e3000, 0x20c00, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ + { 0x820e5000, 0x21400, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ + { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure register) */ + { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */ + { 0x54000000, 0x02000, 0x1000 }, /* WFDMA PCIE0 MCU DMA0 */ + { 0x55000000, 0x03000, 0x1000 }, /* WFDMA PCIE0 MCU DMA1 */ + { 0x58000000, 0x06000, 0x1000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ + { 0x59000000, 0x07000, 0x1000 }, /* WFDMA PCIE1 MCU DMA1 */ + { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */ + { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, WFDMA */ + { 0x7c060000, 0xe0000, 0x10000 }, /* CONN_INFRA, conn_host_csr_top */ + { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ + { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ + { 0x820c0000, 0x08000, 0x4000 }, /* WF_UMAC_TOP (PLE) */ + { 0x820c8000, 0x0c000, 0x2000 }, /* WF_UMAC_TOP (PSE) */ + { 0x820cc000, 0x0e000, 0x1000 }, /* WF_UMAC_TOP (PP) */ + { 0x820cd000, 0x0f000, 0x1000 }, /* WF_MDP_TOP */ + { 0x820ce000, 0x21c00, 0x0200 }, /* WF_LMAC_TOP (WF_SEC) */ + { 0x820cf000, 0x22000, 0x1000 }, /* WF_LMAC_TOP (WF_PF) */ + { 0x820e0000, 0x20000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ + { 0x820e1000, 0x20400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ + { 0x820e9000, 0x23400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + { 0x820ea000, 0x24000, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + { 0x820ec000, 0x24600, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ + { 0x820f0000, 0xa0000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ + { 0x820f1000, 0xa0600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ + { 0x820f2000, 0xa0800, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ + { 0x820f3000, 0xa0c00, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ + { 0x820f4000, 0xa1000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + { 0x820f5000, 0xa1400, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + { 0x820f7000, 0xa1e00, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ + { 0x820f9000, 0xa3400, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + { 0x820fa000, 0xa4000, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + { 0x820fb000, 0xa4200, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ + { 0x820fc000, 0xa4600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ + { 0x820fd000, 0xa4800, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ + }; + int i; + + if (addr < 0x100000) + return addr; + + for (i = 0; i < ARRAY_SIZE(fixed_map); i++) { + u32 ofs; + + if (addr < fixed_map[i].phys) + continue; + + ofs = addr - fixed_map[i].phys; + if (ofs > fixed_map[i].size) + continue; + + return fixed_map[i].mapped + ofs; + } + + if ((addr >= 0x18000000 && addr < 0x18c00000) || + (addr >= 0x70000000 && addr < 0x78000000) || + (addr >= 0x7c000000 && addr < 0x7c400000)) + return mt7921_reg_map_l1(dev, addr); + + dev_err(dev->mt76.dev, "Access currently unsupported address %08x\n", + addr); + + return 0; +} + +static u32 mt7921_rr(struct mt76_dev *mdev, u32 offset) +{ + struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); + u32 addr = __mt7921_reg_addr(dev, offset); + + return dev->bus_ops->rr(mdev, addr); +} + +static void mt7921_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ + struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); + u32 addr = __mt7921_reg_addr(dev, offset); + + dev->bus_ops->wr(mdev, addr, val); +} + +static u32 mt7921_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) +{ + struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); + u32 addr = __mt7921_reg_addr(dev, offset); + + return dev->bus_ops->rmw(mdev, addr, mask, val); +} + static int mt7921_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -134,6 +239,7 @@ static int mt7921_pci_probe(struct pci_dev *pdev, .token_size = MT7921_TOKEN_SIZE, .tx_prepare_skb = mt7921e_tx_prepare_skb, .tx_complete_skb = mt7921e_tx_complete_skb, + .rx_check = mt7921e_rx_check, .rx_skb = mt7921e_queue_rx_skb, .rx_poll_complete = mt7921_rx_poll_complete, .sta_ps = mt7921_sta_ps, @@ -151,6 +257,7 @@ static int mt7921_pci_probe(struct pci_dev *pdev, .fw_own = mt7921e_mcu_fw_pmctrl, }; + struct mt76_bus_ops *bus_ops; struct mt7921_dev *dev; struct mt76_dev *mdev; int ret; @@ -188,6 +295,25 @@ static int mt7921_pci_probe(struct pci_dev *pdev, mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); tasklet_init(&dev->irq_tasklet, mt7921_irq_tasklet, (unsigned long)dev); + + dev->phy.dev = dev; + dev->phy.mt76 = &dev->mt76.phy; + dev->mt76.phy.priv = &dev->phy; + dev->bus_ops = dev->mt76.bus; + bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), + GFP_KERNEL); + if (!bus_ops) + return -ENOMEM; + + bus_ops->rr = mt7921_rr; + bus_ops->wr = mt7921_wr; + bus_ops->rmw = mt7921_rmw; + dev->mt76.bus = bus_ops; + + ret = __mt7921e_mcu_drv_pmctrl(dev); + if (ret) + return ret; + mdev->rev = (mt7921_l1_rr(dev, MT_HW_CHIPID) << 16) | (mt7921_l1_rr(dev, MT_HW_REV) & 0xff); dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c index 85286cc9add1..5ca14dbbdd26 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c @@ -137,7 +137,7 @@ mt7921_txwi_free(struct mt7921_dev *dev, struct mt76_txwi_cache *t, wcid_idx = wcid->idx; } else { - wcid_idx = FIELD_GET(MT_TXD1_WLAN_IDX, le32_to_cpu(txwi[1])); + wcid_idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX); } __mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list); @@ -148,14 +148,15 @@ mt7921_txwi_free(struct mt7921_dev *dev, struct mt76_txwi_cache *t, } static void -mt7921_mac_tx_free(struct mt7921_dev *dev, struct sk_buff *skb) +mt7921e_mac_tx_free(struct mt7921_dev *dev, void *data, int len) { - struct mt7921_tx_free *free = (struct mt7921_tx_free *)skb->data; + struct mt7921_tx_free *free = (struct mt7921_tx_free *)data; struct mt76_dev *mdev = &dev->mt76; struct mt76_txwi_cache *txwi; struct ieee80211_sta *sta = NULL; + struct sk_buff *skb, *tmp; + void *end = data + len; LIST_HEAD(free_list); - struct sk_buff *tmp; bool wake = false; u8 i, count; @@ -163,11 +164,10 @@ mt7921_mac_tx_free(struct mt7921_dev *dev, struct sk_buff *skb) mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false); mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BE], false); - /* TODO: MT_TX_FREE_LATENCY is msdu time from the TXD is queued into PLE, - * to the time ack is received or dropped by hw (air + hw queue time). - * Should avoid accessing WTBL to get Tx airtime, and use it instead. - */ - count = FIELD_GET(MT_TX_FREE_MSDU_CNT, le16_to_cpu(free->ctrl)); + count = le16_get_bits(free->ctrl, MT_TX_FREE_MSDU_CNT); + if (WARN_ON_ONCE((void *)&free->info[count] > end)) + return; + for (i = 0; i < count; i++) { u32 msdu, info = le32_to_cpu(free->info[i]); u8 stat; @@ -208,8 +208,6 @@ mt7921_mac_tx_free(struct mt7921_dev *dev, struct sk_buff *skb) if (wake) mt76_set_tx_blocked(&dev->mt76, false); - napi_consume_skb(skb, 1); - list_for_each_entry_safe(skb, tmp, &free_list, list) { skb_list_del_init(skb); napi_consume_skb(skb, 1); @@ -222,6 +220,28 @@ mt7921_mac_tx_free(struct mt7921_dev *dev, struct sk_buff *skb) mt76_worker_schedule(&dev->mt76.tx_worker); } +bool mt7921e_rx_check(struct mt76_dev *mdev, void *data, int len) +{ + struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); + __le32 *rxd = (__le32 *)data; + __le32 *end = (__le32 *)&rxd[len / 4]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + mt7921e_mac_tx_free(dev, data, len); + return false; + case PKT_TYPE_TXS: + for (rxd += 2; rxd + 8 <= end; rxd += 8) + mt7921_mac_add_txs(dev, rxd); + return false; + default: + return true; + } +} + void mt7921e_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, struct sk_buff *skb) { @@ -229,11 +249,12 @@ void mt7921e_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, __le32 *rxd = (__le32 *)skb->data; enum rx_pkt_type type; - type = FIELD_GET(MT_RXD0_PKT_TYPE, le32_to_cpu(rxd[0])); + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); switch (type) { case PKT_TYPE_TXRX_NOTIFY: - mt7921_mac_tx_free(dev, skb); + mt7921e_mac_tx_free(dev, skb->data, skb->len); + napi_consume_skb(skb, 1); break; default: mt7921_queue_rx_skb(mdev, q, skb); @@ -314,6 +335,7 @@ int mt7921e_mac_reset(struct mt7921_dev *dev) } local_bh_enable(); + dev->fw_assert = false; clear_bit(MT76_MCU_RESET, &dev->mphy.state); mt76_wr(dev, MT_WFDMA0_HOST_INT_ENA, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c index a020352122a1..36669e5aeef3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c @@ -42,7 +42,7 @@ int mt7921e_mcu_init(struct mt7921_dev *dev) .headroom = sizeof(struct mt7921_mcu_txd), .mcu_skb_send_msg = mt7921_mcu_send_message, .mcu_parse_response = mt7921_mcu_parse_response, - .mcu_restart = mt7921_mcu_restart, + .mcu_restart = mt76_connac_mcu_restart, }; int err; @@ -59,10 +59,8 @@ int mt7921e_mcu_init(struct mt7921_dev *dev) return err; } -int mt7921e_mcu_drv_pmctrl(struct mt7921_dev *dev) +int __mt7921e_mcu_drv_pmctrl(struct mt7921_dev *dev) { - struct mt76_phy *mphy = &dev->mt76.phy; - struct mt76_connac_pm *pm = &dev->pm; int i, err = 0; for (i = 0; i < MT7921_DRV_OWN_RETRY_COUNT; i++) { @@ -75,9 +73,21 @@ int mt7921e_mcu_drv_pmctrl(struct mt7921_dev *dev) if (i == MT7921_DRV_OWN_RETRY_COUNT) { dev_err(dev->mt76.dev, "driver own failed\n"); err = -EIO; - goto out; } + return err; +} + +int mt7921e_mcu_drv_pmctrl(struct mt7921_dev *dev) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_connac_pm *pm = &dev->pm; + int err; + + err = __mt7921e_mcu_drv_pmctrl(dev); + if (err < 0) + goto out; + mt7921_wpdma_reinit_cond(dev); clear_bit(MT76_STATE_PM, &mphy->state); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regs.h b/drivers/net/wireless/mediatek/mt76/mt7921/regs.h index cbd38122c510..6712ff60c722 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regs.h @@ -17,13 +17,12 @@ #define MT_PLE_BASE 0x820c0000 #define MT_PLE(ofs) (MT_PLE_BASE + (ofs)) -#define MT_PLE_FL_Q0_CTRL MT_PLE(0x1b0) -#define MT_PLE_FL_Q1_CTRL MT_PLE(0x1b4) -#define MT_PLE_FL_Q2_CTRL MT_PLE(0x1b8) -#define MT_PLE_FL_Q3_CTRL MT_PLE(0x1bc) +#define MT_PLE_FL_Q0_CTRL MT_PLE(0x3e0) +#define MT_PLE_FL_Q1_CTRL MT_PLE(0x3e4) +#define MT_PLE_FL_Q2_CTRL MT_PLE(0x3e8) +#define MT_PLE_FL_Q3_CTRL MT_PLE(0x3ec) -#define MT_PLE_AC_QEMPTY(ac, n) MT_PLE(0x300 + 0x10 * (ac) + \ - ((n) << 2)) +#define MT_PLE_AC_QEMPTY(_n) MT_PLE(0x500 + 0x40 * (_n)) #define MT_PLE_AMSDU_PACK_MSDU_CNT(n) MT_PLE(0x10e0 + ((n) << 2)) #define MT_MDP_BASE 0x820cd000 @@ -354,6 +353,7 @@ #define MT_WFDMA0_GLO_CFG_RX_DMA_EN BIT(2) #define MT_WFDMA0_GLO_CFG_RX_DMA_BUSY BIT(3) #define MT_WFDMA0_GLO_CFG_TX_WB_DDONE BIT(6) +#define MT_WFDMA0_GLO_CFG_FW_DWLD_BYPASS_DMASHDL BIT(9) #define MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN BIT(12) #define MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN BIT(15) #define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) @@ -378,6 +378,9 @@ #define MT_WFDMA0_TX_RING16_EXT_CTRL MT_WFDMA0(0x640) #define MT_WFDMA0_TX_RING17_EXT_CTRL MT_WFDMA0(0x644) +#define MT_WPDMA0_MAX_CNT_MASK GENMASK(7, 0) +#define MT_WPDMA0_BASE_PTR_MASK GENMASK(31, 16) + #define MT_WFDMA0_RX_RING0_EXT_CTRL MT_WFDMA0(0x680) #define MT_WFDMA0_RX_RING1_EXT_CTRL MT_WFDMA0(0x684) #define MT_WFDMA0_RX_RING2_EXT_CTRL MT_WFDMA0(0x688) @@ -426,6 +429,10 @@ #define MT_WFDMA_DUMMY_CR MT_MCU_WPDMA0(0x120) #define MT_WFDMA_NEED_REINIT BIT(1) +#define MT_CBTOP_RGU(ofs) (0x70002000 + (ofs)) +#define MT_CBTOP_RGU_WF_SUBSYS_RST MT_CBTOP_RGU(0x600) +#define MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH BIT(0) + #define MT_HW_BOUND 0x70010020 #define MT_HW_CHIPID 0x70010200 #define MT_HW_REV 0x70010204 @@ -434,12 +441,14 @@ #define MT_PCIE_MAC(ofs) (MT_PCIE_MAC_BASE + (ofs)) #define MT_PCIE_MAC_INT_ENABLE MT_PCIE_MAC(0x188) -#define MT_DMA_SHDL(ofs) (0xd6000 + (ofs)) +#define MT_DMA_SHDL(ofs) (0x7c026000 + (ofs)) #define MT_DMASHDL_SW_CONTROL MT_DMA_SHDL(0x004) #define MT_DMASHDL_DMASHDL_BYPASS BIT(28) #define MT_DMASHDL_OPTIONAL MT_DMA_SHDL(0x008) #define MT_DMASHDL_PAGE MT_DMA_SHDL(0x00c) +#define MT_DMASHDL_GROUP_SEQ_ORDER BIT(16) #define MT_DMASHDL_REFILL MT_DMA_SHDL(0x010) +#define MT_DMASHDL_REFILL_MASK GENMASK(31, 16) #define MT_DMASHDL_PKT_MAX_SIZE MT_DMA_SHDL(0x01c) #define MT_DMASHDL_PKT_MAX_SIZE_PLE GENMASK(11, 0) #define MT_DMASHDL_PKT_MAX_SIZE_PSE GENMASK(27, 16) @@ -454,6 +463,46 @@ #define MT_DMASHDL_SCHED_SET(_n) MT_DMA_SHDL(0x070 + ((_n) << 2)) +#define MT_WFDMA_HOST_CONFIG 0x7c027030 +#define MT_WFDMA_HOST_CONFIG_USB_RXEVT_EP4_EN BIT(6) + +#define MT_UMAC(ofs) (0x74000000 + (ofs)) +#define MT_UDMA_TX_QSEL MT_UMAC(0x008) +#define MT_FW_DL_EN BIT(3) + +#define MT_UDMA_WLCFG_1 MT_UMAC(0x00c) +#define MT_WL_RX_AGG_PKT_LMT GENMASK(7, 0) +#define MT_WL_TX_TMOUT_LMT GENMASK(27, 8) + +#define MT_UDMA_WLCFG_0 MT_UMAC(0x18) +#define MT_WL_RX_AGG_TO GENMASK(7, 0) +#define MT_WL_RX_AGG_LMT GENMASK(15, 8) +#define MT_WL_TX_TMOUT_FUNC_EN BIT(16) +#define MT_WL_TX_DPH_CHK_EN BIT(17) +#define MT_WL_RX_MPSZ_PAD0 BIT(18) +#define MT_WL_RX_FLUSH BIT(19) +#define MT_TICK_1US_EN BIT(20) +#define MT_WL_RX_AGG_EN BIT(21) +#define MT_WL_RX_EN BIT(22) +#define MT_WL_TX_EN BIT(23) +#define MT_WL_RX_BUSY BIT(30) +#define MT_WL_TX_BUSY BIT(31) + +#define MT_UDMA_CONN_INFRA_STATUS MT_UMAC(0xa20) +#define MT_UDMA_CONN_WFSYS_INIT_DONE BIT(22) +#define MT_UDMA_CONN_INFRA_STATUS_SEL MT_UMAC(0xa24) + +#define MT_SSUSB_EPCTL_CSR(ofs) (0x74011800 + (ofs)) +#define MT_SSUSB_EPCTL_CSR_EP_RST_OPT MT_SSUSB_EPCTL_CSR(0x090) + +#define MT_UWFDMA0(ofs) (0x7c024000 + (ofs)) +#define MT_UWFDMA0_GLO_CFG MT_UWFDMA0(0x208) +#define MT_UWFDMA0_GLO_CFG_EXT0 MT_UWFDMA0(0x2b0) +#define MT_UWFDMA0_TX_RING_EXT_CTRL(_n) MT_UWFDMA0(0x600 + ((_n) << 2)) + +#define MT_CONN_STATUS 0x7c053c10 +#define MT_WIFI_PATCH_DL_STATE BIT(0) + #define MT_CONN_ON_LPCTL 0x7c060010 #define PCIE_LPCR_HOST_OWN_SYNC BIT(2) #define PCIE_LPCR_HOST_CLR_OWN BIT(1) @@ -464,6 +513,7 @@ #define WFSYS_SW_INIT_DONE BIT(4) #define MT_CONN_ON_MISC 0x7c0600f0 +#define MT_TOP_MISC2_FW_PWR_ON BIT(0) #define MT_TOP_MISC2_FW_N9_RDY GENMASK(1, 0) #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c index 65d693902c22..af26d59fa2f0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c @@ -41,6 +41,7 @@ static void mt7921s_unregister_device(struct mt7921_dev *dev) { struct mt76_connac_pm *pm = &dev->pm; + cancel_work_sync(&dev->init_work); mt76_unregister_device(&dev->mt76); cancel_delayed_work_sync(&pm->ps_work); cancel_work_sync(&pm->wake_work); @@ -58,7 +59,10 @@ static int mt7921s_parse_intr(struct mt76_dev *dev, struct mt76s_intr *intr) struct mt7921_sdio_intr *irq_data = sdio->intr_data; int i, err; + sdio_claim_host(sdio->func); err = sdio_readsb(sdio->func, irq_data, MCR_WHISR, sizeof(*irq_data)); + sdio_release_host(sdio->func); + if (err < 0) return err; @@ -88,9 +92,9 @@ static int mt7921s_probe(struct sdio_func *func, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, - .tx_prepare_skb = mt7921s_tx_prepare_skb, - .tx_complete_skb = mt7921s_tx_complete_skb, - .tx_status_data = mt7921s_tx_status_data, + .tx_prepare_skb = mt7921_usb_sdio_tx_prepare_skb, + .tx_complete_skb = mt7921_usb_sdio_tx_complete_skb, + .tx_status_data = mt7921_usb_sdio_tx_status_data, .rx_skb = mt7921_queue_rx_skb, .sta_ps = mt7921_sta_ps, .sta_add = mt7921_mac_sta_add, @@ -118,7 +122,7 @@ static int mt7921s_probe(struct sdio_func *func, struct mt7921_dev *dev; struct mt76_dev *mdev; - int ret, i; + int ret; mdev = mt76_alloc_device(&func->dev, sizeof(*dev), &mt7921_ops, &drv_ops); @@ -151,16 +155,6 @@ static int mt7921s_probe(struct sdio_func *func, goto error; } - for (i = 0; i < ARRAY_SIZE(mdev->sdio.xmit_buf); i++) { - mdev->sdio.xmit_buf[i] = devm_kmalloc(mdev->dev, - MT76S_XMIT_BUF_SZ, - GFP_KERNEL); - if (!mdev->sdio.xmit_buf[i]) { - ret = -ENOMEM; - goto error; - } - } - ret = mt76s_alloc_rx_queue(mdev, MT_RXQ_MAIN); if (ret) goto error; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c index ccaf8134cec7..1b3adb3d91e8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c @@ -60,7 +60,11 @@ int mt7921s_wfsys_reset(struct mt7921_dev *dev) sdio_release_host(sdio->func); + clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + /* activate mt7921s again */ + mt7921s_mcu_drv_pmctrl(dev); + mt76_clear(dev, MT_CONN_STATUS, MT_WIFI_PATCH_DL_STATE); mt7921s_mcu_fw_pmctrl(dev); mt7921s_mcu_drv_pmctrl(dev); @@ -81,7 +85,6 @@ int mt7921s_init_reset(struct mt7921_dev *dev) mt7921s_wfsys_reset(dev); mt76_worker_enable(&dev->mt76.sdio.txrx_worker); - clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); clear_bit(MT76_MCU_RESET, &dev->mphy.state); mt7921s_enable_irq(&dev->mt76); @@ -114,7 +117,6 @@ int mt7921s_mac_reset(struct mt7921_dev *dev) mt76_worker_enable(&dev->mt76.sdio.net_worker); dev->fw_assert = false; - clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); clear_bit(MT76_MCU_RESET, &dev->mphy.state); mt7921s_enable_irq(&dev->mt76); @@ -138,86 +140,3 @@ int mt7921s_mac_reset(struct mt7921_dev *dev) return err; } - -static void -mt7921s_write_txwi(struct mt7921_dev *dev, struct mt76_wcid *wcid, - enum mt76_txq_id qid, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key, int pid, - struct sk_buff *skb) -{ - __le32 *txwi = (__le32 *)(skb->data - MT_SDIO_TXD_SIZE); - - memset(txwi, 0, MT_SDIO_TXD_SIZE); - mt7921_mac_write_txwi(dev, txwi, skb, wcid, key, pid, false); - skb_push(skb, MT_SDIO_TXD_SIZE); -} - -int mt7921s_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, - enum mt76_txq_id qid, struct mt76_wcid *wcid, - struct ieee80211_sta *sta, - struct mt76_tx_info *tx_info) -{ - struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); - struct ieee80211_key_conf *key = info->control.hw_key; - struct sk_buff *skb = tx_info->skb; - int err, pad, pktid; - - if (unlikely(tx_info->skb->len <= ETH_HLEN)) - return -EINVAL; - - if (!wcid) - wcid = &dev->mt76.global_wcid; - - if (sta) { - struct mt7921_sta *msta = (struct mt7921_sta *)sta->drv_priv; - - if (time_after(jiffies, msta->last_txs + HZ / 4)) { - info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; - msta->last_txs = jiffies; - } - } - - pktid = mt76_tx_status_skb_add(&dev->mt76, wcid, skb); - mt7921s_write_txwi(dev, wcid, qid, sta, key, pktid, skb); - - mt7921_skb_add_sdio_hdr(skb, MT7921_SDIO_DATA); - pad = round_up(skb->len, 4) - skb->len; - - err = mt76_skb_adjust_pad(skb, pad); - if (err) - /* Release pktid in case of error. */ - idr_remove(&wcid->pktid, pktid); - - return err; -} - -void mt7921s_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e) -{ - __le32 *txwi = (__le32 *)(e->skb->data + MT_SDIO_HDR_SIZE); - unsigned int headroom = MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE; - struct ieee80211_sta *sta; - struct mt76_wcid *wcid; - u16 idx; - - idx = FIELD_GET(MT_TXD1_WLAN_IDX, le32_to_cpu(txwi[1])); - wcid = rcu_dereference(mdev->wcid[idx]); - sta = wcid_to_sta(wcid); - - if (sta && likely(e->skb->protocol != cpu_to_be16(ETH_P_PAE))) - mt7921_tx_check_aggr(sta, txwi); - - skb_pull(e->skb, headroom); - mt76_tx_complete_skb(mdev, e->wcid, e->skb); -} - -bool mt7921s_tx_status_data(struct mt76_dev *mdev, u8 *update) -{ - struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); - - mt7921_mutex_acquire(dev); - mt7921_mac_sta_poll(dev); - mt7921_mutex_release(dev); - - return false; -} diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c index d20f2ff01be1..54a5c712a3c3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c @@ -36,7 +36,7 @@ mt7921s_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, if (cmd == MCU_CMD(FW_SCATTER)) type = MT7921_SDIO_FWDL; - mt7921_skb_add_sdio_hdr(skb, type); + mt7921_skb_add_usb_sdio_hdr(dev, skb, type); pad = round_up(skb->len, 4) - skb->len; __skb_put_zero(skb, pad); @@ -49,6 +49,26 @@ mt7921s_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, return ret; } +static u32 mt7921s_read_rm3r(struct mt7921_dev *dev) +{ + struct mt76_sdio *sdio = &dev->mt76.sdio; + + return sdio_readl(sdio->func, MCR_D2HRM3R, NULL); +} + +static u32 mt7921s_clear_rm3r_drv_own(struct mt7921_dev *dev) +{ + struct mt76_sdio *sdio = &dev->mt76.sdio; + u32 val; + + val = sdio_readl(sdio->func, MCR_D2HRM3R, NULL); + if (val) + sdio_writel(sdio->func, H2D_SW_INT_CLEAR_MAILBOX_ACK, + MCR_WSICR, NULL); + + return val; +} + int mt7921s_mcu_init(struct mt7921_dev *dev) { static const struct mt76_mcu_ops mt7921s_mcu_ops = { @@ -88,6 +108,12 @@ int mt7921s_mcu_drv_pmctrl(struct mt7921_dev *dev) err = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status, status & WHLPCR_IS_DRIVER_OWN, 2000, 1000000); + + if (!err && test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) + err = readx_poll_timeout(mt7921s_read_rm3r, dev, status, + status & D2HRM3R_IS_DRIVER_OWN, + 2000, 1000000); + sdio_release_host(func); if (err < 0) { @@ -115,12 +141,24 @@ int mt7921s_mcu_fw_pmctrl(struct mt7921_dev *dev) sdio_claim_host(func); + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) { + err = readx_poll_timeout(mt7921s_clear_rm3r_drv_own, + dev, status, + !(status & D2HRM3R_IS_DRIVER_OWN), + 2000, 1000000); + if (err < 0) { + dev_err(dev->mt76.dev, "mailbox ACK not cleared\n"); + goto err; + } + } + sdio_writel(func, WHLPCR_FW_OWN_REQ_SET, MCR_WHLPCR, NULL); err = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status, !(status & WHLPCR_IS_DRIVER_OWN), 2000, 1000000); sdio_release_host(func); +err: if (err < 0) { dev_err(dev->mt76.dev, "firmware own failed\n"); clear_bit(MT76_STATE_PM, &mphy->state); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c new file mode 100644 index 000000000000..b7771e9f1fcd --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: ISC +/* Copyright (C) 2022 MediaTek Inc. + * + * Author: Lorenzo Bianconi + */ + +#include +#include +#include + +#include "mt7921.h" +#include "mcu.h" +#include "mac.h" + +static const struct usb_device_id mt7921u_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xff, 0xff, 0xff) }, + { }, +}; + +static u32 mt7921u_rr(struct mt76_dev *dev, u32 addr) +{ + u32 ret; + + mutex_lock(&dev->usb.usb_ctrl_mtx); + ret = ___mt76u_rr(dev, MT_VEND_READ_EXT, + USB_DIR_IN | MT_USB_TYPE_VENDOR, addr); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return ret; +} + +static void mt7921u_wr(struct mt76_dev *dev, u32 addr, u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + ___mt76u_wr(dev, MT_VEND_WRITE_EXT, + USB_DIR_OUT | MT_USB_TYPE_VENDOR, addr, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); +} + +static u32 mt7921u_rmw(struct mt76_dev *dev, u32 addr, + u32 mask, u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + val |= ___mt76u_rr(dev, MT_VEND_READ_EXT, + USB_DIR_IN | MT_USB_TYPE_VENDOR, addr) & ~mask; + ___mt76u_wr(dev, MT_VEND_WRITE_EXT, + USB_DIR_OUT | MT_USB_TYPE_VENDOR, addr, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return val; +} + +static void mt7921u_copy(struct mt76_dev *dev, u32 offset, + const void *data, int len) +{ + struct mt76_usb *usb = &dev->usb; + int ret, i = 0, batch_len; + const u8 *val = data; + + len = round_up(len, 4); + + mutex_lock(&usb->usb_ctrl_mtx); + while (i < len) { + batch_len = min_t(int, usb->data_len, len - i); + memcpy(usb->data, val + i, batch_len); + ret = __mt76u_vendor_request(dev, MT_VEND_WRITE_EXT, + USB_DIR_OUT | MT_USB_TYPE_VENDOR, + (offset + i) >> 16, offset + i, + usb->data, batch_len); + if (ret < 0) + break; + + i += batch_len; + } + mutex_unlock(&usb->usb_ctrl_mtx); +} + +int mt7921u_mcu_power_on(struct mt7921_dev *dev) +{ + int ret; + + ret = mt76u_vendor_request(&dev->mt76, MT_VEND_POWER_ON, + USB_DIR_OUT | MT_USB_TYPE_VENDOR, + 0x0, 0x1, NULL, 0); + if (ret) + return ret; + + if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_PWR_ON, + MT_TOP_MISC2_FW_PWR_ON, 500)) { + dev_err(dev->mt76.dev, "Timeout for power on\n"); + ret = -EIO; + } + + return ret; +} + +static int +mt7921u_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *seq) +{ + struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); + u32 pad, ep; + int ret; + + ret = mt7921_mcu_fill_message(mdev, skb, cmd, seq); + if (ret) + return ret; + + if (cmd != MCU_CMD(FW_SCATTER)) + ep = MT_EP_OUT_INBAND_CMD; + else + ep = MT_EP_OUT_AC_BE; + + mt7921_skb_add_usb_sdio_hdr(dev, skb, 0); + pad = round_up(skb->len, 4) + 4 - skb->len; + __skb_put_zero(skb, pad); + + ret = mt76u_bulk_msg(&dev->mt76, skb->data, skb->len, NULL, + 1000, ep); + dev_kfree_skb(skb); + + return ret; +} + +static int mt7921u_mcu_init(struct mt7921_dev *dev) +{ + static const struct mt76_mcu_ops mcu_ops = { + .headroom = MT_SDIO_HDR_SIZE + sizeof(struct mt7921_mcu_txd), + .tailroom = MT_USB_TAIL_SIZE, + .mcu_skb_send_msg = mt7921u_mcu_send_message, + .mcu_parse_response = mt7921_mcu_parse_response, + .mcu_restart = mt76_connac_mcu_restart, + }; + int ret; + + dev->mt76.mcu_ops = &mcu_ops; + + mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + ret = mt7921_run_firmware(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + + return 0; +} + +static void mt7921u_stop(struct ieee80211_hw *hw) +{ + struct mt7921_dev *dev = mt7921_hw_dev(hw); + + mt76u_stop_tx(&dev->mt76); + mt7921_stop(hw); +} + +static void mt7921u_cleanup(struct mt7921_dev *dev) +{ + clear_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + mt7921u_wfsys_reset(dev); + mt7921_mcu_exit(dev); + mt76u_queues_deinit(&dev->mt76); +} + +static int mt7921u_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + static const struct mt76_driver_ops drv_ops = { + .txwi_size = MT_SDIO_TXD_SIZE, + .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ, + .survey_flags = SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BSS_RX, + .tx_prepare_skb = mt7921_usb_sdio_tx_prepare_skb, + .tx_complete_skb = mt7921_usb_sdio_tx_complete_skb, + .tx_status_data = mt7921_usb_sdio_tx_status_data, + .rx_skb = mt7921_queue_rx_skb, + .sta_ps = mt7921_sta_ps, + .sta_add = mt7921_mac_sta_add, + .sta_assoc = mt7921_mac_sta_assoc, + .sta_remove = mt7921_mac_sta_remove, + .update_survey = mt7921_update_channel, + }; + static const struct mt7921_hif_ops hif_ops = { + .mcu_init = mt7921u_mcu_init, + .init_reset = mt7921u_init_reset, + .reset = mt7921u_mac_reset, + }; + static struct mt76_bus_ops bus_ops = { + .rr = mt7921u_rr, + .wr = mt7921u_wr, + .rmw = mt7921u_rmw, + .read_copy = mt76u_read_copy, + .write_copy = mt7921u_copy, + .type = MT76_BUS_USB, + }; + struct usb_device *udev = interface_to_usbdev(usb_intf); + struct ieee80211_ops *ops; + struct ieee80211_hw *hw; + struct mt7921_dev *dev; + struct mt76_dev *mdev; + int ret; + + ops = devm_kmemdup(&usb_intf->dev, &mt7921_ops, sizeof(mt7921_ops), + GFP_KERNEL); + if (!ops) + return -ENOMEM; + + ops->stop = mt7921u_stop; + + mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt7921_dev, mt76); + dev->hif_ops = &hif_ops; + + udev = usb_get_dev(udev); + usb_reset_device(udev); + + usb_set_intfdata(usb_intf, dev); + + ret = __mt76u_init(mdev, usb_intf, &bus_ops); + if (ret < 0) + goto error; + + mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + if (mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY)) { + ret = mt7921u_wfsys_reset(dev); + if (ret) + goto error; + } + + ret = mt7921u_mcu_power_on(dev); + if (ret) + goto error; + + ret = mt76u_alloc_mcu_queue(&dev->mt76); + if (ret) + goto error; + + ret = mt76u_alloc_queues(&dev->mt76); + if (ret) + goto error; + + ret = mt7921u_dma_init(dev); + if (ret) + return ret; + + hw = mt76_hw(dev); + /* check hw sg support in order to enable AMSDU */ + hw->max_tx_fragments = mdev->usb.sg_en ? MT_HW_TXP_MAX_BUF_NUM : 1; + + ret = mt7921_register_device(dev); + if (ret) + goto error; + + return 0; + +error: + mt76u_queues_deinit(&dev->mt76); + + usb_set_intfdata(usb_intf, NULL); + usb_put_dev(interface_to_usbdev(usb_intf)); + + mt76_free_device(&dev->mt76); + + return ret; +} + +static void mt7921u_disconnect(struct usb_interface *usb_intf) +{ + struct mt7921_dev *dev = usb_get_intfdata(usb_intf); + + cancel_work_sync(&dev->init_work); + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + return; + + mt76_unregister_device(&dev->mt76); + mt7921u_cleanup(dev); + + usb_set_intfdata(usb_intf, NULL); + usb_put_dev(interface_to_usbdev(usb_intf)); + + mt76_free_device(&dev->mt76); +} + +MODULE_DEVICE_TABLE(usb, mt7921u_device_table); +MODULE_FIRMWARE(MT7921_FIRMWARE_WM); +MODULE_FIRMWARE(MT7921_ROM_PATCH); + +static struct usb_driver mt7921u_driver = { + .name = KBUILD_MODNAME, + .id_table = mt7921u_device_table, + .probe = mt7921u_probe, + .disconnect = mt7921u_disconnect, + .soft_unbind = 1, + .disable_hub_initiated_lpm = 1, +}; +module_usb_driver(mt7921u_driver); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb_mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb_mac.c new file mode 100644 index 000000000000..99bcbd858b65 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb_mac.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: ISC +/* Copyright (C) 2022 MediaTek Inc. + * + * Author: Lorenzo Bianconi + */ + +#include +#include +#include + +#include "mt7921.h" +#include "mcu.h" +#include "mac.h" + +static u32 mt7921u_uhw_rr(struct mt76_dev *dev, u32 addr) +{ + u32 ret; + + mutex_lock(&dev->usb.usb_ctrl_mtx); + ret = ___mt76u_rr(dev, MT_VEND_DEV_MODE, + USB_DIR_IN | MT_USB_TYPE_UHW_VENDOR, addr); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return ret; +} + +static void mt7921u_uhw_wr(struct mt76_dev *dev, u32 addr, u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + ___mt76u_wr(dev, MT_VEND_WRITE, + USB_DIR_OUT | MT_USB_TYPE_UHW_VENDOR, addr, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); +} + +static void mt7921u_dma_prefetch(struct mt7921_dev *dev) +{ + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(0), + MT_WPDMA0_MAX_CNT_MASK, 4); + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(0), + MT_WPDMA0_BASE_PTR_MASK, 0x80); + + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(1), + MT_WPDMA0_MAX_CNT_MASK, 4); + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(1), + MT_WPDMA0_BASE_PTR_MASK, 0xc0); + + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(2), + MT_WPDMA0_MAX_CNT_MASK, 4); + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(2), + MT_WPDMA0_BASE_PTR_MASK, 0x100); + + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(3), + MT_WPDMA0_MAX_CNT_MASK, 4); + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(3), + MT_WPDMA0_BASE_PTR_MASK, 0x140); + + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(4), + MT_WPDMA0_MAX_CNT_MASK, 4); + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(4), + MT_WPDMA0_BASE_PTR_MASK, 0x180); + + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(16), + MT_WPDMA0_MAX_CNT_MASK, 4); + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(16), + MT_WPDMA0_BASE_PTR_MASK, 0x280); + + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(17), + MT_WPDMA0_MAX_CNT_MASK, 4); + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL(17), + MT_WPDMA0_BASE_PTR_MASK, 0x2c0); +} + +static void mt7921u_wfdma_init(struct mt7921_dev *dev) +{ + mt7921u_dma_prefetch(dev); + + mt76_clear(dev, MT_UWFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_OMIT_RX_INFO); + mt76_set(dev, MT_UWFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 | + MT_WFDMA0_GLO_CFG_FW_DWLD_BYPASS_DMASHDL | + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + /* disable dmashdl */ + mt76_clear(dev, MT_UWFDMA0_GLO_CFG_EXT0, + MT_WFDMA0_CSR_TX_DMASHDL_ENABLE); + mt76_set(dev, MT_DMASHDL_SW_CONTROL, MT_DMASHDL_DMASHDL_BYPASS); + + mt76_set(dev, MT_WFDMA_DUMMY_CR, MT_WFDMA_NEED_REINIT); +} + +static int mt7921u_dma_rx_evt_ep4(struct mt7921_dev *dev) +{ + if (!mt76_poll(dev, MT_UWFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 1000)) + return -ETIMEDOUT; + + mt76_clear(dev, MT_UWFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_RX_DMA_EN); + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_USB_RXEVT_EP4_EN); + mt76_set(dev, MT_UWFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + return 0; +} + +static void mt7921u_epctl_rst_opt(struct mt7921_dev *dev, bool reset) +{ + u32 val; + + /* usb endpoint reset opt + * bits[4,9]: out blk ep 4-9 + * bits[20,21]: in blk ep 4-5 + * bits[22]: in int ep 6 + */ + val = mt7921u_uhw_rr(&dev->mt76, MT_SSUSB_EPCTL_CSR_EP_RST_OPT); + if (reset) + val |= GENMASK(9, 4) | GENMASK(22, 20); + else + val &= ~(GENMASK(9, 4) | GENMASK(22, 20)); + mt7921u_uhw_wr(&dev->mt76, MT_SSUSB_EPCTL_CSR_EP_RST_OPT, val); +} + +int mt7921u_dma_init(struct mt7921_dev *dev) +{ + int err; + + mt7921u_wfdma_init(dev); + + mt76_clear(dev, MT_UDMA_WLCFG_0, MT_WL_RX_FLUSH); + + mt76_set(dev, MT_UDMA_WLCFG_0, + MT_WL_RX_EN | MT_WL_TX_EN | + MT_WL_RX_MPSZ_PAD0 | MT_TICK_1US_EN); + mt76_clear(dev, MT_UDMA_WLCFG_0, + MT_WL_RX_AGG_TO | MT_WL_RX_AGG_LMT); + mt76_clear(dev, MT_UDMA_WLCFG_1, MT_WL_RX_AGG_PKT_LMT); + + err = mt7921u_dma_rx_evt_ep4(dev); + if (err) + return err; + + mt7921u_epctl_rst_opt(dev, false); + + return 0; +} + +int mt7921u_wfsys_reset(struct mt7921_dev *dev) +{ + u32 val; + int i; + + mt7921u_epctl_rst_opt(dev, false); + + val = mt7921u_uhw_rr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST); + val |= MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH; + mt7921u_uhw_wr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST, val); + + usleep_range(10, 20); + + val = mt7921u_uhw_rr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST); + val &= ~MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH; + mt7921u_uhw_wr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST, val); + + mt7921u_uhw_wr(&dev->mt76, MT_UDMA_CONN_INFRA_STATUS_SEL, 0); + for (i = 0; i < MT7921_WFSYS_INIT_RETRY_COUNT; i++) { + val = mt7921u_uhw_rr(&dev->mt76, MT_UDMA_CONN_INFRA_STATUS); + if (val & MT_UDMA_CONN_WFSYS_INIT_DONE) + break; + + msleep(100); + } + + if (i == MT7921_WFSYS_INIT_RETRY_COUNT) + return -ETIMEDOUT; + + return 0; +} + +int mt7921u_init_reset(struct mt7921_dev *dev) +{ + set_bit(MT76_RESET, &dev->mphy.state); + + wake_up(&dev->mt76.mcu.wait); + mt7921_mcu_exit(dev); + + mt76u_stop_rx(&dev->mt76); + mt76u_stop_tx(&dev->mt76); + + mt7921u_wfsys_reset(dev); + + clear_bit(MT76_RESET, &dev->mphy.state); + + return mt76u_resume_rx(&dev->mt76); +} + +int mt7921u_mac_reset(struct mt7921_dev *dev) +{ + int err; + + mt76_txq_schedule_all(&dev->mphy); + mt76_worker_disable(&dev->mt76.tx_worker); + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + + wake_up(&dev->mt76.mcu.wait); + mt7921_mcu_exit(dev); + + mt76u_stop_rx(&dev->mt76); + mt76u_stop_tx(&dev->mt76); + + mt7921u_wfsys_reset(dev); + + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + err = mt76u_resume_rx(&dev->mt76); + if (err) + goto out; + + err = mt7921u_mcu_power_on(dev); + if (err) + goto out; + + err = mt7921u_dma_init(dev); + if (err) + goto out; + + mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); + mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + + err = mt7921_run_firmware(dev); + if (err) + goto out; + + mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + + err = mt7921_mcu_set_eeprom(dev); + if (err) + goto out; + + err = mt7921_mac_init(dev); + if (err) + goto out; + + err = __mt7921_start(&dev->phy); +out: + clear_bit(MT76_RESET, &dev->mphy.state); + + mt76_worker_enable(&dev->mt76.tx_worker); + + return err; +} diff --git a/drivers/net/wireless/mediatek/mt76/sdio.c b/drivers/net/wireless/mediatek/mt76/sdio.c index 54f72d215948..def7f325f5c5 100644 --- a/drivers/net/wireless/mediatek/mt76/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/sdio.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -627,6 +629,7 @@ int mt76s_init(struct mt76_dev *dev, struct sdio_func *func, const struct mt76_bus_ops *bus_ops) { struct mt76_sdio *sdio = &dev->sdio; + u32 host_max_cap; int err; err = mt76_worker_setup(dev->hw, &sdio->status_worker, @@ -648,7 +651,16 @@ int mt76s_init(struct mt76_dev *dev, struct sdio_func *func, dev->bus = bus_ops; dev->sdio.func = func; - return 0; + host_max_cap = min_t(u32, func->card->host->max_req_size, + func->cur_blksize * + func->card->host->max_blk_count); + dev->sdio.xmit_buf_sz = min_t(u32, host_max_cap, MT76S_XMIT_BUF_SZ); + dev->sdio.xmit_buf = devm_kmalloc(dev->dev, dev->sdio.xmit_buf_sz, + GFP_KERNEL); + if (!dev->sdio.xmit_buf) + err = -ENOMEM; + + return err; } EXPORT_SYMBOL_GPL(mt76s_init); diff --git a/drivers/net/wireless/mediatek/mt76/sdio.h b/drivers/net/wireless/mediatek/mt76/sdio.h index 99db4ad93b7c..27d5d2077eba 100644 --- a/drivers/net/wireless/mediatek/mt76/sdio.h +++ b/drivers/net/wireless/mediatek/mt76/sdio.h @@ -65,6 +65,7 @@ #define MCR_H2DSM0R 0x0070 #define H2D_SW_INT_READ BIT(16) #define H2D_SW_INT_WRITE BIT(17) +#define H2D_SW_INT_CLEAR_MAILBOX_ACK BIT(22) #define MCR_H2DSM1R 0x0074 #define MCR_D2HRM0R 0x0078 @@ -109,6 +110,7 @@ #define MCR_H2DSM2R 0x0160 /* supported in CONNAC2 */ #define MCR_H2DSM3R 0x0164 /* supported in CONNAC2 */ #define MCR_D2HRM3R 0x0174 /* supported in CONNAC2 */ +#define D2HRM3R_IS_DRIVER_OWN BIT(0) #define MCR_WTQCR8 0x0190 /* supported in CONNAC2 */ #define MCR_WTQCR9 0x0194 /* supported in CONNAC2 */ #define MCR_WTQCR10 0x0198 /* supported in CONNAC2 */ diff --git a/drivers/net/wireless/mediatek/mt76/sdio_txrx.c b/drivers/net/wireless/mediatek/mt76/sdio_txrx.c index 801590a0a334..a2601aa9e7b1 100644 --- a/drivers/net/wireless/mediatek/mt76/sdio_txrx.c +++ b/drivers/net/wireless/mediatek/mt76/sdio_txrx.c @@ -102,7 +102,10 @@ mt76s_rx_run_queue(struct mt76_dev *dev, enum mt76_rxq_id qid, buf = page_address(page); + sdio_claim_host(sdio->func); err = sdio_readsb(sdio->func, buf, MCR_WRDR(qid), len); + sdio_release_host(sdio->func); + if (err < 0) { dev_err(dev->dev, "sdio read data failed:%d\n", err); put_page(page); @@ -115,7 +118,7 @@ mt76s_rx_run_queue(struct mt76_dev *dev, enum mt76_rxq_id qid, __le32 *rxd = (__le32 *)buf; /* parse rxd to get the actual packet length */ - len = FIELD_GET(GENMASK(15, 0), le32_to_cpu(rxd[0])); + len = le32_get_bits(rxd[0], GENMASK(15, 0)); e->skb = mt76s_build_rx_skb(buf, len, round_up(len + 4, 4)); if (!e->skb) break; @@ -214,7 +217,10 @@ static int __mt76s_xmit_queue(struct mt76_dev *dev, u8 *data, int len) if (len > sdio->func->cur_blksize) len = roundup(len, sdio->func->cur_blksize); + sdio_claim_host(sdio->func); err = sdio_writesb(sdio->func, MCR_WTDR1, data, len); + sdio_release_host(sdio->func); + if (err) dev_err(dev->dev, "sdio write failed: %d\n", err); @@ -223,12 +229,11 @@ static int __mt76s_xmit_queue(struct mt76_dev *dev, u8 *data, int len) static int mt76s_tx_run_queue(struct mt76_dev *dev, struct mt76_queue *q) { - int qid, err, nframes = 0, len = 0, pse_sz = 0, ple_sz = 0; + int err, nframes = 0, len = 0, pse_sz = 0, ple_sz = 0; bool mcu = q == dev->q_mcu[MT_MCUQ_WM]; struct mt76_sdio *sdio = &dev->sdio; u8 pad; - qid = mcu ? ARRAY_SIZE(sdio->xmit_buf) - 1 : q->qid; while (q->first != q->head) { struct mt76_queue_entry *e = &q->entry[q->first]; struct sk_buff *iter; @@ -249,27 +254,25 @@ static int mt76s_tx_run_queue(struct mt76_dev *dev, struct mt76_queue *q) } pad = roundup(e->skb->len, 4) - e->skb->len; - if (len + e->skb->len + pad + 4 > MT76S_XMIT_BUF_SZ) + if (len + e->skb->len + pad + 4 > dev->sdio.xmit_buf_sz) break; if (mt76s_tx_pick_quota(sdio, mcu, e->buf_sz, &pse_sz, &ple_sz)) break; - memcpy(sdio->xmit_buf[qid] + len, e->skb->data, - skb_headlen(e->skb)); + memcpy(sdio->xmit_buf + len, e->skb->data, skb_headlen(e->skb)); len += skb_headlen(e->skb); nframes++; skb_walk_frags(e->skb, iter) { - memcpy(sdio->xmit_buf[qid] + len, iter->data, - iter->len); + memcpy(sdio->xmit_buf + len, iter->data, iter->len); len += iter->len; nframes++; } if (unlikely(pad)) { - memset(sdio->xmit_buf[qid] + len, 0, pad); + memset(sdio->xmit_buf + len, 0, pad); len += pad; } next: @@ -278,8 +281,8 @@ static int mt76s_tx_run_queue(struct mt76_dev *dev, struct mt76_queue *q) } if (nframes) { - memset(sdio->xmit_buf[qid] + len, 0, 4); - err = __mt76s_xmit_queue(dev, sdio->xmit_buf[qid], len + 4); + memset(sdio->xmit_buf + len, 0, 4); + err = __mt76s_xmit_queue(dev, sdio->xmit_buf, len + 4); if (err) return err; } @@ -298,6 +301,7 @@ void mt76s_txrx_worker(struct mt76_sdio *sdio) /* disable interrupt */ sdio_claim_host(sdio->func); sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL); + sdio_release_host(sdio->func); do { nframes = 0; @@ -327,6 +331,7 @@ void mt76s_txrx_worker(struct mt76_sdio *sdio) } while (nframes > 0); /* enable interrupt */ + sdio_claim_host(sdio->func); sdio_writel(sdio->func, WHLPCR_INT_EN_SET, MCR_WHLPCR, NULL); sdio_release_host(sdio->func); } @@ -341,6 +346,7 @@ void mt76s_sdio_irq(struct sdio_func *func) test_bit(MT76_MCU_RESET, &dev->phy.state)) return; + sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL); mt76_worker_schedule(&sdio->txrx_worker); } EXPORT_SYMBOL_GPL(mt76s_sdio_irq); diff --git a/drivers/net/wireless/mediatek/mt76/testmode.c b/drivers/net/wireless/mediatek/mt76/testmode.c index 1a01ad7a4c16..382b45639f26 100644 --- a/drivers/net/wireless/mediatek/mt76/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/testmode.c @@ -409,7 +409,6 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct mt76_dev *dev = phy->dev; struct mt76_testmode_data *td = &phy->test; struct nlattr *tb[NUM_MT76_TM_ATTRS]; - bool ext_phy = phy != &dev->phy; u32 state; int err; int i; @@ -447,8 +446,8 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_LDPC], &td->tx_rate_ldpc, 0, 1) || mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_STBC], &td->tx_rate_stbc, 0, 1) || mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_LTF], &td->tx_ltf, 0, 2) || - mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_ANTENNA], &td->tx_antenna_mask, - 1 << (ext_phy * 2), phy->antenna_mask << (ext_phy * 2)) || + mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_ANTENNA], + &td->tx_antenna_mask, 0, 0xff) || mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_SPE_IDX], &td->tx_spe_idx, 0, 27) || mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_DUTY_CYCLE], &td->tx_duty_cycle, 0, 99) || diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 0a7006c8959b..a85e192c9d59 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -15,9 +15,8 @@ static bool disable_usb_sg; module_param_named(disable_usb_sg, disable_usb_sg, bool, 0644); MODULE_PARM_DESC(disable_usb_sg, "Disable usb scatter-gather support"); -static int __mt76u_vendor_request(struct mt76_dev *dev, u8 req, - u8 req_type, u16 val, u16 offset, - void *buf, size_t len) +int __mt76u_vendor_request(struct mt76_dev *dev, u8 req, u8 req_type, + u16 val, u16 offset, void *buf, size_t len) { struct usb_interface *uintf = to_usb_interface(dev->dev); struct usb_device *udev = interface_to_usbdev(uintf); @@ -45,6 +44,7 @@ static int __mt76u_vendor_request(struct mt76_dev *dev, u8 req, req, offset, ret); return ret; } +EXPORT_SYMBOL_GPL(__mt76u_vendor_request); int mt76u_vendor_request(struct mt76_dev *dev, u8 req, u8 req_type, u16 val, u16 offset, @@ -62,22 +62,21 @@ int mt76u_vendor_request(struct mt76_dev *dev, u8 req, } EXPORT_SYMBOL_GPL(mt76u_vendor_request); -static u32 ___mt76u_rr(struct mt76_dev *dev, u8 req, u32 addr) +u32 ___mt76u_rr(struct mt76_dev *dev, u8 req, u8 req_type, u32 addr) { struct mt76_usb *usb = &dev->usb; u32 data = ~0; int ret; - ret = __mt76u_vendor_request(dev, req, - USB_DIR_IN | USB_TYPE_VENDOR, - addr >> 16, addr, usb->data, - sizeof(__le32)); + ret = __mt76u_vendor_request(dev, req, req_type, addr >> 16, + addr, usb->data, sizeof(__le32)); if (ret == sizeof(__le32)) data = get_unaligned_le32(usb->data); trace_usb_reg_rr(dev, addr, data); return data; } +EXPORT_SYMBOL_GPL(___mt76u_rr); static u32 __mt76u_rr(struct mt76_dev *dev, u32 addr) { @@ -95,7 +94,8 @@ static u32 __mt76u_rr(struct mt76_dev *dev, u32 addr) break; } - return ___mt76u_rr(dev, req, addr & ~MT_VEND_TYPE_MASK); + return ___mt76u_rr(dev, req, USB_DIR_IN | USB_TYPE_VENDOR, + addr & ~MT_VEND_TYPE_MASK); } static u32 mt76u_rr(struct mt76_dev *dev, u32 addr) @@ -109,29 +109,17 @@ static u32 mt76u_rr(struct mt76_dev *dev, u32 addr) return ret; } -static u32 mt76u_rr_ext(struct mt76_dev *dev, u32 addr) -{ - u32 ret; - - mutex_lock(&dev->usb.usb_ctrl_mtx); - ret = ___mt76u_rr(dev, MT_VEND_READ_EXT, addr); - mutex_unlock(&dev->usb.usb_ctrl_mtx); - - return ret; -} - -static void ___mt76u_wr(struct mt76_dev *dev, u8 req, - u32 addr, u32 val) +void ___mt76u_wr(struct mt76_dev *dev, u8 req, u8 req_type, + u32 addr, u32 val) { struct mt76_usb *usb = &dev->usb; put_unaligned_le32(val, usb->data); - __mt76u_vendor_request(dev, req, - USB_DIR_OUT | USB_TYPE_VENDOR, - addr >> 16, addr, usb->data, - sizeof(__le32)); + __mt76u_vendor_request(dev, req, req_type, addr >> 16, + addr, usb->data, sizeof(__le32)); trace_usb_reg_wr(dev, addr, val); } +EXPORT_SYMBOL_GPL(___mt76u_wr); static void __mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val) { @@ -145,7 +133,8 @@ static void __mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val) req = MT_VEND_MULTI_WRITE; break; } - ___mt76u_wr(dev, req, addr & ~MT_VEND_TYPE_MASK, val); + ___mt76u_wr(dev, req, USB_DIR_OUT | USB_TYPE_VENDOR, + addr & ~MT_VEND_TYPE_MASK, val); } static void mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val) @@ -155,13 +144,6 @@ static void mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val) mutex_unlock(&dev->usb.usb_ctrl_mtx); } -static void mt76u_wr_ext(struct mt76_dev *dev, u32 addr, u32 val) -{ - mutex_lock(&dev->usb.usb_ctrl_mtx); - ___mt76u_wr(dev, MT_VEND_WRITE_EXT, addr, val); - mutex_unlock(&dev->usb.usb_ctrl_mtx); -} - static u32 mt76u_rmw(struct mt76_dev *dev, u32 addr, u32 mask, u32 val) { @@ -173,17 +155,6 @@ static u32 mt76u_rmw(struct mt76_dev *dev, u32 addr, return val; } -static u32 mt76u_rmw_ext(struct mt76_dev *dev, u32 addr, - u32 mask, u32 val) -{ - mutex_lock(&dev->usb.usb_ctrl_mtx); - val |= ___mt76u_rr(dev, MT_VEND_READ_EXT, addr) & ~mask; - ___mt76u_wr(dev, MT_VEND_WRITE_EXT, addr, val); - mutex_unlock(&dev->usb.usb_ctrl_mtx); - - return val; -} - static void mt76u_copy(struct mt76_dev *dev, u32 offset, const void *data, int len) { @@ -216,33 +187,8 @@ static void mt76u_copy(struct mt76_dev *dev, u32 offset, mutex_unlock(&usb->usb_ctrl_mtx); } -static void mt76u_copy_ext(struct mt76_dev *dev, u32 offset, - const void *data, int len) -{ - struct mt76_usb *usb = &dev->usb; - int ret, i = 0, batch_len; - const u8 *val = data; - - len = round_up(len, 4); - mutex_lock(&usb->usb_ctrl_mtx); - while (i < len) { - batch_len = min_t(int, usb->data_len, len - i); - memcpy(usb->data, val + i, batch_len); - ret = __mt76u_vendor_request(dev, MT_VEND_WRITE_EXT, - USB_DIR_OUT | USB_TYPE_VENDOR, - (offset + i) >> 16, offset + i, - usb->data, batch_len); - if (ret < 0) - break; - - i += batch_len; - } - mutex_unlock(&usb->usb_ctrl_mtx); -} - -static void -mt76u_read_copy_ext(struct mt76_dev *dev, u32 offset, - void *data, int len) +void mt76u_read_copy(struct mt76_dev *dev, u32 offset, + void *data, int len) { struct mt76_usb *usb = &dev->usb; int i = 0, batch_len, ret; @@ -264,6 +210,7 @@ mt76u_read_copy_ext(struct mt76_dev *dev, u32 offset, } mutex_unlock(&usb->usb_ctrl_mtx); } +EXPORT_SYMBOL_GPL(mt76u_read_copy); void mt76u_single_wr(struct mt76_dev *dev, const u8 req, const u16 offset, const u32 val) @@ -1112,24 +1059,13 @@ static const struct mt76_queue_ops usb_queue_ops = { .kick = mt76u_tx_kick, }; -int mt76u_init(struct mt76_dev *dev, - struct usb_interface *intf, bool ext) +int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf, + struct mt76_bus_ops *ops) { - static struct mt76_bus_ops mt76u_ops = { - .read_copy = mt76u_read_copy_ext, - .wr_rp = mt76u_wr_rp, - .rd_rp = mt76u_rd_rp, - .type = MT76_BUS_USB, - }; struct usb_device *udev = interface_to_usbdev(intf); struct mt76_usb *usb = &dev->usb; int err; - mt76u_ops.rr = ext ? mt76u_rr_ext : mt76u_rr; - mt76u_ops.wr = ext ? mt76u_wr_ext : mt76u_wr; - mt76u_ops.rmw = ext ? mt76u_rmw_ext : mt76u_rmw; - mt76u_ops.write_copy = ext ? mt76u_copy_ext : mt76u_copy; - INIT_WORK(&usb->stat_work, mt76u_tx_status_data); usb->data_len = usb_maxpacket(udev, usb_sndctrlpipe(udev, 0), 1); @@ -1141,7 +1077,7 @@ int mt76u_init(struct mt76_dev *dev, return -ENOMEM; mutex_init(&usb->usb_ctrl_mtx); - dev->bus = &mt76u_ops; + dev->bus = ops; dev->queue_ops = &usb_queue_ops; dev_set_drvdata(&udev->dev, dev); @@ -1167,6 +1103,23 @@ int mt76u_init(struct mt76_dev *dev, return 0; } +EXPORT_SYMBOL_GPL(__mt76u_init); + +int mt76u_init(struct mt76_dev *dev, struct usb_interface *intf) +{ + static struct mt76_bus_ops bus_ops = { + .rr = mt76u_rr, + .wr = mt76u_wr, + .rmw = mt76u_rmw, + .read_copy = mt76u_read_copy, + .write_copy = mt76u_copy, + .wr_rp = mt76u_wr_rp, + .rd_rp = mt76u_rd_rp, + .type = MT76_BUS_USB, + }; + + return __mt76u_init(dev, intf, &bus_ops); +} EXPORT_SYMBOL_GPL(mt76u_init); MODULE_AUTHOR("Lorenzo Bianconi "); diff --git a/drivers/net/wireless/microchip/wilc1000/spi.c b/drivers/net/wireless/microchip/wilc1000/spi.c index d2db52289399..18420e954402 100644 --- a/drivers/net/wireless/microchip/wilc1000/spi.c +++ b/drivers/net/wireless/microchip/wilc1000/spi.c @@ -725,10 +725,7 @@ static int wilc_spi_dma_rw(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz) int nbytes; u8 rsp; - if (sz <= DATA_PKT_SZ) - nbytes = sz; - else - nbytes = DATA_PKT_SZ; + nbytes = min_t(u32, sz, DATA_PKT_SZ); /* * Data Response header diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 2987ad9271f6..87e98ab068ed 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -382,6 +382,8 @@ static int ray_config(struct pcmcia_device *link) goto failed; local->sram = ioremap(link->resource[2]->start, resource_size(link->resource[2])); + if (!local->sram) + goto failed; /*** Set up 16k window for shared memory (receive buffer) ***************/ link->resource[3]->flags |= @@ -396,6 +398,8 @@ static int ray_config(struct pcmcia_device *link) goto failed; local->rmem = ioremap(link->resource[3]->start, resource_size(link->resource[3])); + if (!local->rmem) + goto failed; /*** Set up window for attribute memory ***********************************/ link->resource[4]->flags |= @@ -410,6 +414,8 @@ static int ray_config(struct pcmcia_device *link) goto failed; local->amem = ioremap(link->resource[4]->start, resource_size(link->resource[4])); + if (!local->amem) + goto failed; dev_dbg(&link->dev, "ray_config sram=%p\n", local->sram); dev_dbg(&link->dev, "ray_config rmem=%p\n", local->rmem); diff --git a/drivers/net/wireless/realtek/rtlwifi/cam.c b/drivers/net/wireless/realtek/rtlwifi/cam.c index 7a0355dc6bab..32970ea4b4e7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/cam.c +++ b/drivers/net/wireless/realtek/rtlwifi/cam.c @@ -208,7 +208,7 @@ void rtl_cam_empty_entry(struct ieee80211_hw *hw, u8 uc_index) u32 ul_command; u32 ul_content; - u32 ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_AES]; + u32 ul_encalgo; u8 entry_i; switch (rtlpriv->sec.pairwise_enc_algorithm) { diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.c index 04735da11168..da54e51badd3 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.c @@ -396,36 +396,6 @@ void _rtl92ce_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) } } -static void _rtl92ce_phy_set_rf_sleep(struct ieee80211_hw *hw) -{ - u32 u4b_tmp; - u8 delay = 5; - struct rtl_priv *rtlpriv = rtl_priv(hw); - - rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); - rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); - rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); - u4b_tmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, RFREG_OFFSET_MASK); - while (u4b_tmp != 0 && delay > 0) { - rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x0); - rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); - rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); - u4b_tmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, RFREG_OFFSET_MASK); - delay--; - } - if (delay == 0) { - rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x00); - rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); - rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); - rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); - rtl_dbg(rtlpriv, COMP_POWER, DBG_TRACE, - "Switch RF timeout !!!\n"); - return; - } - rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); - rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x22); -} - static bool _rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state) { @@ -519,7 +489,7 @@ static bool _rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, jiffies_to_msecs(jiffies - ppsc->last_awake_jiffies)); ppsc->last_sleep_jiffies = jiffies; - _rtl92ce_phy_set_rf_sleep(hw); + _rtl92c_phy_set_rf_sleep(hw); break; } default: diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c index b53daf1b29f7..876c14d46c2f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c @@ -334,6 +334,7 @@ static const struct usb_device_id rtl8192c_usb_ids[] = { {RTL_USB_DEVICE(0x04f2, 0xaff7, rtl92cu_hal_cfg)}, /*Xavi*/ {RTL_USB_DEVICE(0x04f2, 0xaff9, rtl92cu_hal_cfg)}, /*Xavi*/ {RTL_USB_DEVICE(0x04f2, 0xaffa, rtl92cu_hal_cfg)}, /*Xavi*/ + {RTL_USB_DEVICE(0x0846, 0x9042, rtl92cu_hal_cfg)}, /*On Netwrks N150MA*/ /****** 8188CUS Slim Combo ********/ {RTL_USB_DEVICE(0x04f2, 0xaff8, rtl92cu_hal_cfg)}, /*Xavi*/ diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c index f6bff0ebd6b0..f3fe16798c59 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c @@ -872,7 +872,7 @@ static void rtl8821ae_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) else falsealm_cnt->cnt_all = falsealm_cnt->cnt_ofdm_fail; - /*reset OFDM FA coutner*/ + /*reset OFDM FA counter*/ rtl_set_bbreg(hw, ODM_REG_OFDM_FA_RST_11AC, BIT(17), 1); rtl_set_bbreg(hw, ODM_REG_OFDM_FA_RST_11AC, BIT(17), 0); /* reset CCK FA counter*/ @@ -1464,7 +1464,7 @@ void rtl8812ae_dm_txpower_tracking_callback_thermalmeter( const u8 *delta_swing_table_idx_tup_b; const u8 *delta_swing_table_idx_tdown_b; - /*2. Initilization ( 7 steps in total )*/ + /*2. Initialization ( 7 steps in total )*/ rtl8812ae_get_delta_swing_table(hw, &delta_swing_table_idx_tup_a, &delta_swing_table_idx_tdown_a, @@ -2502,7 +2502,7 @@ static void rtl8821ae_dm_check_edca_turbo(struct ieee80211_hw *hw) rtlpriv->dm.dbginfo.num_non_be_pkt = 0; /*=============================== - * list paramter for different platform + * list parameter for different platform *=============================== */ pb_is_cur_rdl_state = &rtlpriv->dm.is_cur_rdlstate; diff --git a/drivers/net/wireless/realtek/rtw88/coex.c b/drivers/net/wireless/realtek/rtw88/coex.c index 2551e228b581..cac053f485c3 100644 --- a/drivers/net/wireless/realtek/rtw88/coex.c +++ b/drivers/net/wireless/realtek/rtw88/coex.c @@ -211,6 +211,10 @@ static void rtw_coex_wl_ccklock_detect(struct rtw_dev *rtwdev) bool is_cck_lock_rate = false; + if (coex_stat->wl_coex_mode != COEX_WLINK_2G1PORT && + coex_stat->wl_coex_mode != COEX_WLINK_2GFREE) + return; + if (coex_dm->bt_status == COEX_BTSTATUS_INQ_PAGE || coex_stat->bt_setup_link) { coex_stat->wl_cck_lock = false; @@ -460,6 +464,29 @@ static void rtw_coex_gnt_workaround(struct rtw_dev *rtwdev, bool force, u8 mode) rtw_coex_set_gnt_fix(rtwdev); } +static void rtw_coex_monitor_bt_ctr(struct rtw_dev *rtwdev) +{ + struct rtw_coex *coex = &rtwdev->coex; + struct rtw_coex_stat *coex_stat = &coex->stat; + u32 tmp; + + tmp = rtw_read32(rtwdev, REG_BT_ACT_STATISTICS); + coex_stat->hi_pri_tx = FIELD_GET(MASKLWORD, tmp); + coex_stat->hi_pri_rx = FIELD_GET(MASKHWORD, tmp); + + tmp = rtw_read32(rtwdev, REG_BT_ACT_STATISTICS_1); + coex_stat->lo_pri_tx = FIELD_GET(MASKLWORD, tmp); + coex_stat->lo_pri_rx = FIELD_GET(MASKHWORD, tmp); + + rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, + BIT_R_GRANTALL_WLMASK | BIT_STATIS_BT_EN); + + rtw_dbg(rtwdev, RTW_DBG_COEX, + "[BTCoex], Hi-Pri Rx/Tx: %d/%d, Lo-Pri Rx/Tx: %d/%d\n", + coex_stat->hi_pri_rx, coex_stat->hi_pri_tx, + coex_stat->lo_pri_rx, coex_stat->lo_pri_tx); +} + static void rtw_coex_monitor_bt_enable(struct rtw_dev *rtwdev) { struct rtw_chip_info *chip = rtwdev->chip; @@ -780,7 +807,9 @@ static void rtw_coex_update_bt_link_info(struct rtw_dev *rtwdev) static void rtw_coex_update_wl_ch_info(struct rtw_dev *rtwdev, u8 type) { struct rtw_chip_info *chip = rtwdev->chip; + struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw_coex_dm *coex_dm = &rtwdev->coex.dm; + struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat; u8 link = 0; u8 center_chan = 0; u8 bw; @@ -791,7 +820,9 @@ static void rtw_coex_update_wl_ch_info(struct rtw_dev *rtwdev, u8 type) if (type != COEX_MEDIA_DISCONNECT) center_chan = rtwdev->hal.current_channel; - if (center_chan == 0) { + if (center_chan == 0 || + (efuse->share_ant && center_chan <= 14 && + coex_stat->wl_coex_mode != COEX_WLINK_2GFREE)) { link = 0; center_chan = 0; bw = 0; @@ -930,6 +961,23 @@ static void rtw_coex_set_gnt_wl(struct rtw_dev *rtwdev, u8 state) rtw_coex_write_indirect_reg(rtwdev, LTE_COEX_CTRL, 0x0300, state); } +static void rtw_coex_mimo_ps(struct rtw_dev *rtwdev, bool force, bool state) +{ + struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat; + + if (!force && state == coex_stat->wl_mimo_ps) + return; + + coex_stat->wl_mimo_ps = state; + + rtw_set_txrx_1ss(rtwdev, state); + + rtw_coex_update_wl_ch_info(rtwdev, (u8)coex_stat->wl_connected); + + rtw_dbg(rtwdev, RTW_DBG_COEX, + "[BTCoex], %s(): state = %d\n", __func__, state); +} + static void rtw_btc_wltoggle_table_a(struct rtw_dev *rtwdev, bool force, u8 table_case) { @@ -1106,7 +1154,8 @@ static void rtw_coex_set_tdma(struct rtw_dev *rtwdev, u8 byte1, u8 byte2, ps_type = COEX_PS_WIFI_NATIVE; rtw_coex_power_save_state(rtwdev, ps_type, 0x0, 0x0); - } else if (byte1 & BIT(4) && !(byte1 & BIT(5))) { + } else if ((byte1 & BIT(4) && !(byte1 & BIT(5))) || + coex_stat->wl_coex_mode == COEX_WLINK_2GFREE) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): Force LPS (byte1 = 0x%x)\n", __func__, byte1); @@ -1802,6 +1851,54 @@ static void rtw_coex_action_bt_inquiry(struct rtw_dev *rtwdev) rtw_coex_tdma(rtwdev, false, tdma_case | slot_type); } +static void rtw_coex_action_bt_game_hid(struct rtw_dev *rtwdev) +{ + struct rtw_coex *coex = &rtwdev->coex; + struct rtw_coex_stat *coex_stat = &coex->stat; + struct rtw_efuse *efuse = &rtwdev->efuse; + struct rtw_coex_dm *coex_dm = &coex->dm; + struct rtw_chip_info *chip = rtwdev->chip; + u8 table_case, tdma_case; + + rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__); + rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G); + + if (efuse->share_ant) { + coex_stat->wl_coex_mode = COEX_WLINK_2GFREE; + if (coex_stat->bt_whck_test) + table_case = 2; + else if (coex_stat->wl_linkscan_proc || coex_stat->bt_hid_exist) + table_case = 33; + else if (coex_stat->bt_setup_link || coex_stat->bt_inq_page) + table_case = 0; + else if (coex_stat->bt_a2dp_exist) + table_case = 34; + else + table_case = 33; + + tdma_case = 0; + } else { + if (COEX_RSSI_HIGH(coex_dm->wl_rssi_state[1])) + tdma_case = 112; + else + tdma_case = 113; + + table_case = 121; + } + + if (coex_stat->wl_coex_mode == COEX_WLINK_2GFREE) { + if (coex_stat->wl_tput_dir == COEX_WL_TPUT_TX) + rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_tx[6]); + else + rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[5]); + } else { + rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]); + } + + rtw_coex_table(rtwdev, false, table_case); + rtw_coex_tdma(rtwdev, false, tdma_case); +} + static void rtw_coex_action_bt_hfp(struct rtw_dev *rtwdev) { struct rtw_coex *coex = &rtwdev->coex; @@ -1816,13 +1913,8 @@ static void rtw_coex_action_bt_hfp(struct rtw_dev *rtwdev) if (efuse->share_ant) { /* Shared-Ant */ - if (coex_stat->bt_multi_link) { - table_case = 10; - tdma_case = 17; - } else { - table_case = 10; - tdma_case = 5; - } + table_case = 10; + tdma_case = 5; } else { /* Non-Shared-Ant */ if (coex_stat->bt_multi_link) { @@ -2224,8 +2316,10 @@ static void rtw_coex_action_bt_a2dp_pan_hid(struct rtw_dev *rtwdev) static void rtw_coex_action_wl_under5g(struct rtw_dev *rtwdev) { + struct rtw_coex *coex = &rtwdev->coex; struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw_chip_info *chip = rtwdev->chip; + struct rtw_coex_stat *coex_stat = &coex->stat; u8 table_case, tdma_case; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__); @@ -2235,6 +2329,9 @@ static void rtw_coex_action_wl_under5g(struct rtw_dev *rtwdev) rtw_coex_write_scbd(rtwdev, COEX_SCBD_FIX2M, false); + if (coex_stat->bt_game_hid_exist && coex_stat->wl_linkscan_proc) + coex_stat->wl_coex_mode = COEX_WLINK_2GFREE; + if (efuse->share_ant) { /* Shared-Ant */ table_case = 0; @@ -2278,6 +2375,7 @@ static void rtw_coex_action_wl_native_lps(struct rtw_dev *rtwdev) struct rtw_coex *coex = &rtwdev->coex; struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw_chip_info *chip = rtwdev->chip; + struct rtw_coex_stat *coex_stat = &coex->stat; u8 table_case, tdma_case; if (coex->under_5g) @@ -2286,7 +2384,6 @@ static void rtw_coex_action_wl_native_lps(struct rtw_dev *rtwdev) rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__); rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G); - rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]); if (efuse->share_ant) { /* Shared-Ant */ @@ -2298,6 +2395,16 @@ static void rtw_coex_action_wl_native_lps(struct rtw_dev *rtwdev) tdma_case = 100; } + if (coex_stat->bt_game_hid_exist) { + coex_stat->wl_coex_mode = COEX_WLINK_2GFREE; + if (coex_stat->wl_tput_dir == COEX_WL_TPUT_TX) + rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_tx[6]); + else + rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[5]); + } else { + rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]); + } + rtw_coex_table(rtwdev, false, table_case); rtw_coex_tdma(rtwdev, false, tdma_case); } @@ -2422,6 +2529,7 @@ static void rtw_coex_action_wl_connected(struct rtw_dev *rtwdev) static void rtw_coex_run_coex(struct rtw_dev *rtwdev, u8 reason) { struct rtw_coex *coex = &rtwdev->coex; + struct rtw_chip_info *chip = rtwdev->chip; struct rtw_coex_dm *coex_dm = &coex->dm; struct rtw_coex_stat *coex_stat = &coex->stat; bool rf4ce_en = false; @@ -2494,6 +2602,11 @@ static void rtw_coex_run_coex(struct rtw_dev *rtwdev, u8 reason) goto exit; } + if (coex_stat->bt_game_hid_exist && coex_stat->wl_connected) { + rtw_coex_action_bt_game_hid(rtwdev); + goto exit; + } + if (coex_stat->bt_whck_test) { rtw_coex_action_bt_whql_test(rtwdev); goto exit; @@ -2530,6 +2643,18 @@ static void rtw_coex_run_coex(struct rtw_dev *rtwdev, u8 reason) } exit: + + if (chip->wl_mimo_ps_support) { + if (coex_stat->wl_coex_mode == COEX_WLINK_2GFREE) { + if (coex_dm->reason == COEX_RSN_2GMEDIA) + rtw_coex_mimo_ps(rtwdev, true, true); + else + rtw_coex_mimo_ps(rtwdev, false, true); + } else { + rtw_coex_mimo_ps(rtwdev, false, false); + } + } + rtw_coex_gnt_workaround(rtwdev, false, coex_stat->wl_coex_mode); rtw_coex_limited_wl(rtwdev); } @@ -3139,6 +3264,135 @@ void rtw_coex_bt_info_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length) rtw_coex_run_coex(rtwdev, COEX_RSN_BTINFO); } +#define COEX_BT_HIDINFO_MTK 0x46 +static const u8 coex_bt_hidinfo_ps[] = {0x57, 0x69, 0x72}; +static const u8 coex_bt_hidinfo_xb[] = {0x58, 0x62, 0x6f}; + +void rtw_coex_bt_hid_info_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length) +{ + struct rtw_coex *coex = &rtwdev->coex; + struct rtw_chip_info *chip = rtwdev->chip; + struct rtw_coex_stat *coex_stat = &coex->stat; + struct rtw_coex_hid *hidinfo; + struct rtw_coex_hid_info_a *hida; + struct rtw_coex_hid_handle_list *hl, *bhl; + u8 sub_id = buf[2], gamehid_cnt = 0, handle, i; + bool cur_game_hid_exist, complete; + + if (!chip->wl_mimo_ps_support && + (sub_id == COEX_BT_HIDINFO_LIST || sub_id == COEX_BT_HIDINFO_A)) + return; + + rtw_dbg(rtwdev, RTW_DBG_COEX, + "[BTCoex], HID info notify, sub_id = 0x%x\n", sub_id); + + switch (sub_id) { + case COEX_BT_HIDINFO_LIST: + hl = &coex_stat->hid_handle_list; + bhl = (struct rtw_coex_hid_handle_list *)buf; + if (!memcmp(hl, bhl, sizeof(*hl))) + return; + coex_stat->hid_handle_list = *bhl; + memset(&coex_stat->hid_info, 0, sizeof(coex_stat->hid_info)); + for (i = 0; i < COEX_BT_HIDINFO_HANDLE_NUM; i++) { + hidinfo = &coex_stat->hid_info[i]; + if (hl->handle[i] != COEX_BT_HIDINFO_NOTCON && + hl->handle[i] != 0) + hidinfo->hid_handle = hl->handle[i]; + } + break; + case COEX_BT_HIDINFO_A: + hida = (struct rtw_coex_hid_info_a *)buf; + handle = hida->handle; + for (i = 0; i < COEX_BT_HIDINFO_HANDLE_NUM; i++) { + hidinfo = &coex_stat->hid_info[i]; + if (hidinfo->hid_handle == handle) { + hidinfo->hid_vendor = hida->vendor; + memcpy(hidinfo->hid_name, hida->name, + sizeof(hidinfo->hid_name)); + hidinfo->hid_info_completed = true; + break; + } + } + break; + } + for (i = 0; i < COEX_BT_HIDINFO_HANDLE_NUM; i++) { + hidinfo = &coex_stat->hid_info[i]; + complete = hidinfo->hid_info_completed; + handle = hidinfo->hid_handle; + if (!complete || handle == COEX_BT_HIDINFO_NOTCON || + handle == 0 || handle >= COEX_BT_BLE_HANDLE_THRS) { + hidinfo->is_game_hid = false; + continue; + } + + if (hidinfo->hid_vendor == COEX_BT_HIDINFO_MTK) { + if ((memcmp(hidinfo->hid_name, + coex_bt_hidinfo_ps, + COEX_BT_HIDINFO_NAME)) == 0) + hidinfo->is_game_hid = true; + else if ((memcmp(hidinfo->hid_name, + coex_bt_hidinfo_xb, + COEX_BT_HIDINFO_NAME)) == 0) + hidinfo->is_game_hid = true; + else + hidinfo->is_game_hid = false; + } else { + hidinfo->is_game_hid = false; + } + if (hidinfo->is_game_hid) + gamehid_cnt++; + } + + if (gamehid_cnt > 0) + cur_game_hid_exist = true; + else + cur_game_hid_exist = false; + + if (cur_game_hid_exist != coex_stat->bt_game_hid_exist) { + coex_stat->bt_game_hid_exist = cur_game_hid_exist; + rtw_dbg(rtwdev, RTW_DBG_COEX, + "[BTCoex], HID info changed!bt_game_hid_exist = %d!\n", + coex_stat->bt_game_hid_exist); + rtw_coex_run_coex(rtwdev, COEX_RSN_BTSTATUS); + } +} + +void rtw_coex_query_bt_hid_list(struct rtw_dev *rtwdev) +{ + struct rtw_coex *coex = &rtwdev->coex; + struct rtw_chip_info *chip = rtwdev->chip; + struct rtw_coex_stat *coex_stat = &coex->stat; + struct rtw_coex_hid *hidinfo; + u8 i, handle; + bool complete; + + if (!chip->wl_mimo_ps_support || coex_stat->wl_under_ips || + (coex_stat->wl_under_lps && !coex_stat->wl_force_lps_ctrl)) + return; + + if (!coex_stat->bt_hid_exist && + !((coex_stat->bt_info_lb2 & COEX_INFO_CONNECTION) && + (coex_stat->hi_pri_tx + coex_stat->hi_pri_rx > + COEX_BT_GAMEHID_CNT))) + return; + + rtw_fw_coex_query_hid_info(rtwdev, COEX_BT_HIDINFO_LIST, 0); + + for (i = 0; i < COEX_BT_HIDINFO_HANDLE_NUM; i++) { + hidinfo = &coex_stat->hid_info[i]; + complete = hidinfo->hid_info_completed; + handle = hidinfo->hid_handle; + if (handle == 0 || handle == COEX_BT_HIDINFO_NOTCON || + handle >= COEX_BT_BLE_HANDLE_THRS || complete) + continue; + + rtw_fw_coex_query_hid_info(rtwdev, + COEX_BT_HIDINFO_A, + handle); + } +} + void rtw_coex_wl_fwdbginfo_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length) { struct rtw_coex *coex = &rtwdev->coex; @@ -3175,6 +3429,17 @@ void rtw_coex_wl_status_change_notify(struct rtw_dev *rtwdev, u32 type) rtw_coex_run_coex(rtwdev, COEX_RSN_WLSTATUS); } +void rtw_coex_wl_status_check(struct rtw_dev *rtwdev) +{ + struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat; + + if ((coex_stat->wl_under_lps && !coex_stat->wl_force_lps_ctrl) || + coex_stat->wl_under_ips) + return; + + rtw_coex_monitor_bt_ctr(rtwdev); +} + void rtw_coex_bt_relink_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, @@ -3637,6 +3902,7 @@ static const char *rtw_coex_get_wl_coex_mode(u8 coex_wl_link_mode) switch (coex_wl_link_mode) { case_WLINK(2G1PORT); case_WLINK(5G); + case_WLINK(2GFREE); default: return "Unknown"; } @@ -3658,7 +3924,6 @@ void rtw_coex_display_coex_info(struct rtw_dev *rtwdev, struct seq_file *m) u16 score_board_WB, score_board_BW; u32 wl_reg_6c0, wl_reg_6c4, wl_reg_6c8, wl_reg_778, wl_reg_6cc; u32 lte_coex, bt_coex; - u32 bt_hi_pri, bt_lo_pri; int i; score_board_BW = rtw_coex_read_scbd(rtwdev); @@ -3669,17 +3934,6 @@ void rtw_coex_display_coex_info(struct rtw_dev *rtwdev, struct seq_file *m) wl_reg_6cc = rtw_read32(rtwdev, REG_BT_COEX_TABLE_H); wl_reg_778 = rtw_read8(rtwdev, REG_BT_STAT_CTRL); - bt_hi_pri = rtw_read32(rtwdev, REG_BT_ACT_STATISTICS); - bt_lo_pri = rtw_read32(rtwdev, REG_BT_ACT_STATISTICS_1); - rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, - BIT_R_GRANTALL_WLMASK | BIT_STATIS_BT_EN); - - coex_stat->hi_pri_tx = FIELD_GET(MASKLWORD, bt_hi_pri); - coex_stat->hi_pri_rx = FIELD_GET(MASKHWORD, bt_hi_pri); - - coex_stat->lo_pri_tx = FIELD_GET(MASKLWORD, bt_lo_pri); - coex_stat->lo_pri_rx = FIELD_GET(MASKHWORD, bt_lo_pri); - sys_lte = rtw_read8(rtwdev, 0x73); lte_coex = rtw_coex_read_indirect_reg(rtwdev, 0x38); bt_coex = rtw_coex_read_indirect_reg(rtwdev, 0x54); diff --git a/drivers/net/wireless/realtek/rtw88/coex.h b/drivers/net/wireless/realtek/rtw88/coex.h index fc61a0cab3e4..07fa7aa34d4b 100644 --- a/drivers/net/wireless/realtek/rtw88/coex.h +++ b/drivers/net/wireless/realtek/rtw88/coex.h @@ -11,6 +11,7 @@ #define COEX_MIN_DELAY 10 /* delay unit in ms */ #define COEX_RFK_TIMEOUT 600 /* RFK timeout in ms */ +#define COEX_BT_GAMEHID_CNT 800 #define COEX_RF_OFF 0x0 #define COEX_RF_ON 0x1 @@ -172,6 +173,7 @@ enum coex_bt_profile { enum coex_wl_link_mode { COEX_WLINK_2G1PORT = 0x0, COEX_WLINK_5G = 0x3, + COEX_WLINK_2GFREE = 0x7, COEX_WLINK_MAX }; @@ -401,9 +403,12 @@ void rtw_coex_scan_notify(struct rtw_dev *rtwdev, u8 type); void rtw_coex_connect_notify(struct rtw_dev *rtwdev, u8 type); void rtw_coex_media_status_notify(struct rtw_dev *rtwdev, u8 type); void rtw_coex_bt_info_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length); +void rtw_coex_bt_hid_info_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length); void rtw_coex_wl_fwdbginfo_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length); void rtw_coex_switchband_notify(struct rtw_dev *rtwdev, u8 type); void rtw_coex_wl_status_change_notify(struct rtw_dev *rtwdev, u32 type); +void rtw_coex_wl_status_check(struct rtw_dev *rtwdev); +void rtw_coex_query_bt_hid_list(struct rtw_dev *rtwdev); void rtw_coex_display_coex_info(struct rtw_dev *rtwdev, struct seq_file *m); static inline bool rtw_coex_disabled(struct rtw_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c index e429428232c1..1a52ff585fbc 100644 --- a/drivers/net/wireless/realtek/rtw88/debug.c +++ b/drivers/net/wireless/realtek/rtw88/debug.c @@ -390,7 +390,7 @@ static ssize_t rtw_debugfs_set_h2c(struct file *filp, ¶m[0], ¶m[1], ¶m[2], ¶m[3], ¶m[4], ¶m[5], ¶m[6], ¶m[7]); if (num != 8) { - rtw_info(rtwdev, "invalid H2C command format for debug\n"); + rtw_warn(rtwdev, "invalid H2C command format for debug\n"); return -EINVAL; } @@ -715,8 +715,10 @@ static int rtw_debugfs_get_phy_info(struct seq_file *m, void *v) seq_printf(m, "Current CH(fc) = %u\n", rtwdev->hal.current_channel); seq_printf(m, "Current BW = %u\n", rtwdev->hal.current_band_width); seq_printf(m, "Current IGI = 0x%x\n", dm_info->igi_history[0]); - seq_printf(m, "TP {Tx, Rx} = {%u, %u}Mbps\n\n", + seq_printf(m, "TP {Tx, Rx} = {%u, %u}Mbps\n", stats->tx_throughput, stats->rx_throughput); + seq_printf(m, "1SS for TX and RX = %c\n\n", rtwdev->hal.txrx_1ss ? + 'Y' : 'N'); seq_puts(m, "==========[Tx Phy Info]========\n"); seq_puts(m, "[Tx Rate] = "); diff --git a/drivers/net/wireless/realtek/rtw88/debug.h b/drivers/net/wireless/realtek/rtw88/debug.h index 61f8369fe2d6..066792dd96af 100644 --- a/drivers/net/wireless/realtek/rtw88/debug.h +++ b/drivers/net/wireless/realtek/rtw88/debug.h @@ -23,6 +23,7 @@ enum rtw_debug_mask { RTW_DBG_PATH_DIV = 0x00004000, RTW_DBG_ADAPTIVITY = 0x00008000, RTW_DBG_HW_SCAN = 0x00010000, + RTW_DBG_STATE = 0x00020000, RTW_DBG_ALL = 0xffffffff }; diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c index 2f7c036f9022..aa2aeb5fb2cc 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.c +++ b/drivers/net/wireless/realtek/rtw88/fw.c @@ -233,6 +233,9 @@ void rtw_fw_c2h_cmd_handle(struct rtw_dev *rtwdev, struct sk_buff *skb) case C2H_BT_INFO: rtw_coex_bt_info_notify(rtwdev, c2h->payload, len); break; + case C2H_BT_HID_INFO: + rtw_coex_bt_hid_info_notify(rtwdev, c2h->payload, len); + break; case C2H_WLAN_INFO: rtw_coex_wl_fwdbginfo_notify(rtwdev, c2h->payload, len); break; @@ -538,6 +541,18 @@ void rtw_fw_coex_tdma_type(struct rtw_dev *rtwdev, rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } +void rtw_fw_coex_query_hid_info(struct rtw_dev *rtwdev, u8 sub_id, u8 data) +{ + u8 h2c_pkt[H2C_PKT_SIZE] = {0}; + + SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_HID_INFO); + + SET_COEX_QUERY_HID_INFO_SUBID(h2c_pkt, sub_id); + SET_COEX_QUERY_HID_INFO_DATA1(h2c_pkt, data); + + rtw_fw_send_h2c_command(rtwdev, h2c_pkt); +} + void rtw_fw_bt_wifi_control(struct rtw_dev *rtwdev, u8 op_code, u8 *data) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; @@ -1784,9 +1799,9 @@ void rtw_fw_scan_notify(struct rtw_dev *rtwdev, bool start) rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } -static void rtw_append_probe_req_ie(struct rtw_dev *rtwdev, struct sk_buff *skb, - struct sk_buff_head *list, - struct rtw_vif *rtwvif) +static int rtw_append_probe_req_ie(struct rtw_dev *rtwdev, struct sk_buff *skb, + struct sk_buff_head *list, u8 *bands, + struct rtw_vif *rtwvif) { struct ieee80211_scan_ies *ies = rtwvif->scan_ies; struct rtw_chip_info *chip = rtwdev->chip; @@ -1797,19 +1812,24 @@ static void rtw_append_probe_req_ie(struct rtw_dev *rtwdev, struct sk_buff *skb, if (!(BIT(idx) & chip->band)) continue; new = skb_copy(skb, GFP_KERNEL); + if (!new) + return -ENOMEM; skb_put_data(new, ies->ies[idx], ies->len[idx]); skb_put_data(new, ies->common_ies, ies->common_ie_len); skb_queue_tail(list, new); + (*bands)++; } + + return 0; } -static int _rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev, u8 num_ssids, +static int _rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev, u8 num_probes, struct sk_buff_head *probe_req_list) { struct rtw_chip_info *chip = rtwdev->chip; struct sk_buff *skb, *tmp; u8 page_offset = 1, *buf, page_size = chip->page_size; - u8 pages = page_offset + num_ssids * RTW_PROBE_PG_CNT; + u8 pages = page_offset + num_probes * RTW_PROBE_PG_CNT; u16 pg_addr = rtwdev->fifo.rsvd_h2c_info_addr, loc; u16 buf_offset = page_size * page_offset; u8 tx_desc_sz = chip->tx_pkt_desc_sz; @@ -1848,6 +1868,8 @@ static int _rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev, u8 num_ssids, rtwdev->scan_info.probe_pg_size = page_offset; out: kfree(buf); + skb_queue_walk_safe(probe_req_list, skb, tmp) + kfree_skb(skb); return ret; } @@ -1857,8 +1879,9 @@ static int rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev, { struct cfg80211_scan_request *req = rtwvif->scan_req; struct sk_buff_head list; - struct sk_buff *skb; - u8 num = req->n_ssids, i; + struct sk_buff *skb, *tmp; + u8 num = req->n_ssids, i, bands = 0; + int ret; skb_queue_head_init(&list); for (i = 0; i < num; i++) { @@ -1866,11 +1889,25 @@ static int rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev, req->ssids[i].ssid, req->ssids[i].ssid_len, req->ie_len); - rtw_append_probe_req_ie(rtwdev, skb, &list, rtwvif); + if (!skb) { + ret = -ENOMEM; + goto out; + } + ret = rtw_append_probe_req_ie(rtwdev, skb, &list, &bands, + rtwvif); + if (ret) + goto out; + kfree_skb(skb); } - return _rtw_hw_scan_update_probe_req(rtwdev, num, &list); + return _rtw_hw_scan_update_probe_req(rtwdev, num * bands, &list); + +out: + skb_queue_walk_safe(&list, skb, tmp) + kfree_skb(skb); + + return ret; } static int rtw_add_chan_info(struct rtw_dev *rtwdev, struct rtw_chan_info *info, @@ -2022,7 +2059,7 @@ void rtw_hw_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, rtwdev->hal.rcr |= BIT_CBSSID_BCN; rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr); - rtw_core_scan_complete(rtwdev, vif); + rtw_core_scan_complete(rtwdev, vif, true); ieee80211_wake_queues(rtwdev->hw); ieee80211_scan_completed(rtwdev->hw, &info); @@ -2109,7 +2146,7 @@ void rtw_hw_scan_status_report(struct rtw_dev *rtwdev, struct sk_buff *skb) rtw_hw_scan_complete(rtwdev, vif, aborted); if (aborted) - rtw_info(rtwdev, "HW scan aborted with code: %d\n", rc); + rtw_dbg(rtwdev, RTW_DBG_HW_SCAN, "HW scan aborted with code: %d\n", rc); } void rtw_store_op_chan(struct rtw_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h index 654c3c2e5721..b59d2cbad5d7 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.h +++ b/drivers/net/wireless/realtek/rtw88/fw.h @@ -47,6 +47,7 @@ enum rtw_c2h_cmd_id { C2H_CCX_TX_RPT = 0x03, C2H_BT_INFO = 0x09, C2H_BT_MP_INFO = 0x0b, + C2H_BT_HID_INFO = 0x45, C2H_RA_RPT = 0x0c, C2H_HW_FEATURE_REPORT = 0x19, C2H_WLAN_INFO = 0x27, @@ -529,6 +530,7 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id) #define H2C_CMD_QUERY_BT_MP_INFO 0x67 #define H2C_CMD_BT_WIFI_CONTROL 0x69 #define H2C_CMD_WIFI_CALIBRATION 0x6d +#define H2C_CMD_QUERY_BT_HID_INFO 0x73 #define H2C_CMD_KEEP_ALIVE 0x03 #define H2C_CMD_DISCONNECT_DECISION 0x04 @@ -681,6 +683,11 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id) #define SET_BT_WIFI_CONTROL_DATA5(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(23, 16)) +#define SET_COEX_QUERY_HID_INFO_SUBID(h2c_pkt, value) \ + le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8)) +#define SET_COEX_QUERY_HID_INFO_DATA1(h2c_pkt, value) \ + le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) + #define SET_KEEP_ALIVE_ENABLE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) #define SET_KEEP_ALIVE_ADOPT(h2c_pkt, value) \ @@ -780,6 +787,8 @@ void rtw_fw_force_bt_tx_power(struct rtw_dev *rtwdev, u8 bt_pwr_dec_lvl); void rtw_fw_bt_ignore_wlan_action(struct rtw_dev *rtwdev, bool enable); void rtw_fw_coex_tdma_type(struct rtw_dev *rtwdev, u8 para1, u8 para2, u8 para3, u8 para4, u8 para5); +void rtw_fw_coex_query_hid_info(struct rtw_dev *rtwdev, u8 sub_id, u8 data); + void rtw_fw_bt_wifi_control(struct rtw_dev *rtwdev, u8 op_code, u8 *data); void rtw_fw_send_rssi_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si); void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si); diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c index ae7d97de5fdf..5cdc54c9a9aa 100644 --- a/drivers/net/wireless/realtek/rtw88/mac80211.c +++ b/drivers/net/wireless/realtek/rtw88/mac80211.c @@ -72,6 +72,9 @@ static int rtw_ops_config(struct ieee80211_hw *hw, u32 changed) struct rtw_dev *rtwdev = hw->priv; int ret = 0; + /* let previous ips work finish to ensure we don't leave ips twice */ + cancel_work_sync(&rtwdev->ips_work); + mutex_lock(&rtwdev->mutex); rtw_leave_lps_deep(rtwdev); @@ -205,7 +208,7 @@ static int rtw_ops_add_interface(struct ieee80211_hw *hw, mutex_unlock(&rtwdev->mutex); - rtw_info(rtwdev, "start vif %pM on port %d\n", vif->addr, rtwvif->port); + rtw_dbg(rtwdev, RTW_DBG_STATE, "start vif %pM on port %d\n", vif->addr, rtwvif->port); return 0; } @@ -216,7 +219,7 @@ static void rtw_ops_remove_interface(struct ieee80211_hw *hw, struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; u32 config = 0; - rtw_info(rtwdev, "stop vif %pM on port %d\n", vif->addr, rtwvif->port); + rtw_dbg(rtwdev, RTW_DBG_STATE, "stop vif %pM on port %d\n", vif->addr, rtwvif->port); mutex_lock(&rtwdev->mutex); @@ -242,8 +245,8 @@ static int rtw_ops_change_interface(struct ieee80211_hw *hw, { struct rtw_dev *rtwdev = hw->priv; - rtw_info(rtwdev, "change vif %pM (%d)->(%d), p2p (%d)->(%d)\n", - vif->addr, vif->type, type, vif->p2p, p2p); + rtw_dbg(rtwdev, RTW_DBG_STATE, "change vif %pM (%d)->(%d), p2p (%d)->(%d)\n", + vif->addr, vif->type, type, vif->p2p, p2p); rtw_ops_remove_interface(hw, vif); @@ -614,7 +617,7 @@ static void rtw_ops_sw_scan_complete(struct ieee80211_hw *hw, struct rtw_dev *rtwdev = hw->priv; mutex_lock(&rtwdev->mutex); - rtw_core_scan_complete(rtwdev, vif); + rtw_core_scan_complete(rtwdev, vif, false); mutex_unlock(&rtwdev->mutex); } diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index 38252113c4a8..8b9899e41b0b 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -207,6 +207,9 @@ static void rtw_watch_dog_work(struct work_struct *work) else clear_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags); + rtw_coex_wl_status_check(rtwdev); + rtw_coex_query_bt_hid_list(rtwdev); + if (busy_traffic != test_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags)) rtw_coex_wl_status_change_notify(rtwdev, 0); @@ -272,6 +275,15 @@ static void rtw_c2h_work(struct work_struct *work) } } +static void rtw_ips_work(struct work_struct *work) +{ + struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, ips_work); + + mutex_lock(&rtwdev->mutex); + rtw_enter_ips(rtwdev); + mutex_unlock(&rtwdev->mutex); +} + static u8 rtw_acquire_macid(struct rtw_dev *rtwdev) { unsigned long mac_id; @@ -305,8 +317,8 @@ int rtw_sta_add(struct rtw_dev *rtwdev, struct ieee80211_sta *sta, rtwdev->sta_cnt++; rtwdev->beacon_loss = false; - rtw_info(rtwdev, "sta %pM joined with macid %d\n", - sta->addr, si->mac_id); + rtw_dbg(rtwdev, RTW_DBG_STATE, "sta %pM joined with macid %d\n", + sta->addr, si->mac_id); return 0; } @@ -327,8 +339,8 @@ void rtw_sta_remove(struct rtw_dev *rtwdev, struct ieee80211_sta *sta, kfree(si->mask); rtwdev->sta_cnt--; - rtw_info(rtwdev, "sta %pM with macid %d left\n", - sta->addr, si->mac_id); + rtw_dbg(rtwdev, RTW_DBG_STATE, "sta %pM with macid %d left\n", + sta->addr, si->mac_id); } struct rtw_fwcd_hdr { @@ -1011,37 +1023,52 @@ static u8 get_rate_id(u8 wireless_set, enum rtw_bandwidth bw_mode, u8 tx_num) #define RA_MASK_VHT_RATES (RA_MASK_VHT_RATES_1SS | \ RA_MASK_VHT_RATES_2SS | \ RA_MASK_VHT_RATES_3SS) +#define RA_MASK_CCK_IN_BG 0x00005 #define RA_MASK_CCK_IN_HT 0x00005 #define RA_MASK_CCK_IN_VHT 0x00005 #define RA_MASK_OFDM_IN_VHT 0x00010 #define RA_MASK_OFDM_IN_HT_2G 0x00010 #define RA_MASK_OFDM_IN_HT_5G 0x00030 -static u64 rtw_update_rate_mask(struct rtw_dev *rtwdev, - struct rtw_sta_info *si, - u64 ra_mask, bool is_vht_enable, - u8 wireless_set) +static u64 rtw_rate_mask_rssi(struct rtw_sta_info *si, u8 wireless_set) +{ + u8 rssi_level = si->rssi_level; + + if (wireless_set == WIRELESS_CCK) + return 0xffffffffffffffffULL; + + if (rssi_level == 0) + return 0xffffffffffffffffULL; + else if (rssi_level == 1) + return 0xfffffffffffffff0ULL; + else if (rssi_level == 2) + return 0xffffffffffffefe0ULL; + else if (rssi_level == 3) + return 0xffffffffffffcfc0ULL; + else if (rssi_level == 4) + return 0xffffffffffff8f80ULL; + else + return 0xffffffffffff0f00ULL; +} + +static u64 rtw_rate_mask_recover(u64 ra_mask, u64 ra_mask_bak) +{ + if ((ra_mask & ~(RA_MASK_CCK_RATES | RA_MASK_OFDM_RATES)) == 0) + ra_mask |= (ra_mask_bak & ~(RA_MASK_CCK_RATES | RA_MASK_OFDM_RATES)); + + if (ra_mask == 0) + ra_mask |= (ra_mask_bak & (RA_MASK_CCK_RATES | RA_MASK_OFDM_RATES)); + + return ra_mask; +} + +static u64 rtw_rate_mask_cfg(struct rtw_dev *rtwdev, struct rtw_sta_info *si, + u64 ra_mask, bool is_vht_enable) { struct rtw_hal *hal = &rtwdev->hal; const struct cfg80211_bitrate_mask *mask = si->mask; u64 cfg_mask = GENMASK_ULL(63, 0); - u8 rssi_level, band; - - if (wireless_set != WIRELESS_CCK) { - rssi_level = si->rssi_level; - if (rssi_level == 0) - ra_mask &= 0xffffffffffffffffULL; - else if (rssi_level == 1) - ra_mask &= 0xfffffffffffffff0ULL; - else if (rssi_level == 2) - ra_mask &= 0xffffffffffffefe0ULL; - else if (rssi_level == 3) - ra_mask &= 0xffffffffffffcfc0ULL; - else if (rssi_level == 4) - ra_mask &= 0xffffffffffff8f80ULL; - else if (rssi_level >= 5) - ra_mask &= 0xffffffffffff0f00ULL; - } + u8 band; if (!si->use_cfg_mask) return ra_mask; @@ -1091,6 +1118,7 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si) u8 ldpc_en = 0; u8 tx_num = 1; u64 ra_mask = 0; + u64 ra_mask_bak = 0; bool is_vht_enable = false; bool is_support_sgi = false; @@ -1110,11 +1138,12 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si) ldpc_en = HT_LDPC_EN; } - if (efuse->hw_cap.nss == 1) + if (efuse->hw_cap.nss == 1 || rtwdev->hal.txrx_1ss) ra_mask &= RA_MASK_VHT_RATES_1SS | RA_MASK_HT_RATES_1SS; if (hal->current_band_type == RTW_BAND_5G) { ra_mask |= (u64)sta->supp_rates[NL80211_BAND_5GHZ] << 4; + ra_mask_bak = ra_mask; if (sta->vht_cap.vht_supported) { ra_mask &= RA_MASK_VHT_RATES | RA_MASK_OFDM_IN_VHT; wireless_set = WIRELESS_OFDM | WIRELESS_VHT; @@ -1127,6 +1156,7 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si) dm_info->rrsr_val_init = RRSR_INIT_5G; } else if (hal->current_band_type == RTW_BAND_2G) { ra_mask |= sta->supp_rates[NL80211_BAND_2GHZ]; + ra_mask_bak = ra_mask; if (sta->vht_cap.vht_supported) { ra_mask &= RA_MASK_VHT_RATES | RA_MASK_CCK_IN_VHT | RA_MASK_OFDM_IN_VHT; @@ -1140,11 +1170,13 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si) } else if (sta->supp_rates[0] <= 0xf) { wireless_set = WIRELESS_CCK; } else { + ra_mask &= RA_MASK_OFDM_RATES | RA_MASK_CCK_IN_BG; wireless_set = WIRELESS_CCK | WIRELESS_OFDM; } dm_info->rrsr_val_init = RRSR_INIT_2G; } else { rtw_err(rtwdev, "Unknown band type\n"); + ra_mask_bak = ra_mask; wireless_set = 0; } @@ -1176,8 +1208,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si) rate_id = get_rate_id(wireless_set, bw_mode, tx_num); - ra_mask = rtw_update_rate_mask(rtwdev, si, ra_mask, is_vht_enable, - wireless_set); + ra_mask &= rtw_rate_mask_rssi(si, wireless_set); + ra_mask = rtw_rate_mask_recover(ra_mask, ra_mask_bak); + ra_mask = rtw_rate_mask_cfg(rtwdev, si, ra_mask, is_vht_enable); si->bw_mode = bw_mode; si->stbc_en = stbc_en; @@ -1339,7 +1372,8 @@ void rtw_core_scan_start(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, set_bit(RTW_FLAG_SCANNING, rtwdev->flags); } -void rtw_core_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif) +void rtw_core_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, + bool hw_scan) { struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; u32 config = 0; @@ -1354,6 +1388,9 @@ void rtw_core_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif) rtw_vif_port_config(rtwdev, rtwvif, config); rtw_coex_scan_notify(rtwdev, COEX_SCAN_FINISH); + + if (rtwvif->net_type == RTW_NET_NO_LINK && hw_scan) + ieee80211_queue_work(rtwdev->hw, &rtwdev->ips_work); } int rtw_core_start(struct rtw_dev *rtwdev) @@ -1536,6 +1573,37 @@ static void rtw_unset_supported_band(struct ieee80211_hw *hw, kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]); } +static void rtw_vif_smps_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct rtw_dev *rtwdev = (struct rtw_dev *)data; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) + return; + + if (rtwdev->hal.txrx_1ss) + ieee80211_request_smps(vif, IEEE80211_SMPS_STATIC); + else + ieee80211_request_smps(vif, IEEE80211_SMPS_OFF); +} + +void rtw_set_txrx_1ss(struct rtw_dev *rtwdev, bool txrx_1ss) +{ + struct rtw_chip_info *chip = rtwdev->chip; + struct rtw_hal *hal = &rtwdev->hal; + + if (!chip->ops->config_txrx_mode || rtwdev->hal.txrx_1ss == txrx_1ss) + return; + + rtwdev->hal.txrx_1ss = txrx_1ss; + if (txrx_1ss) + chip->ops->config_txrx_mode(rtwdev, BB_PATH_A, BB_PATH_A, false); + else + chip->ops->config_txrx_mode(rtwdev, hal->antenna_tx, + hal->antenna_rx, false); + rtw_iterate_vifs_atomic(rtwdev, rtw_vif_smps_iter, rtwdev); +} + static void __update_firmware_feature(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) { @@ -1919,6 +1987,7 @@ int rtw_core_init(struct rtw_dev *rtwdev) INIT_DELAYED_WORK(&coex->wl_ccklock_work, rtw_coex_wl_ccklock_work); INIT_WORK(&rtwdev->tx_work, rtw_tx_work); INIT_WORK(&rtwdev->c2h_work, rtw_c2h_work); + INIT_WORK(&rtwdev->ips_work, rtw_ips_work); INIT_WORK(&rtwdev->fw_recovery_work, rtw_fw_recovery_work); INIT_WORK(&rtwdev->ba_work, rtw_txq_ba_work); skb_queue_head_init(&rtwdev->c2h_queue); diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index dc1cd9bd4b8a..17815af9dd4e 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -874,6 +874,8 @@ struct rtw_chip_ops { enum rtw_bb_path tx_path_1ss, enum rtw_bb_path tx_path_cck, bool is_tx2_path); + void (*config_txrx_mode)(struct rtw_dev *rtwdev, u8 tx_path, + u8 rx_path, bool is_tx2_path); /* for coex */ void (*coex_set_init)(struct rtw_dev *rtwdev); @@ -1240,6 +1242,7 @@ struct rtw_chip_info { bool scbd_support; bool new_scbd10_def; /* true: fix 2M(8822c) */ bool ble_hid_profile_support; + bool wl_mimo_ps_support; u8 pstdma_type; /* 0: LPSoff, 1:LPSon */ u8 bt_rssi_type; u8 ant_isolation; @@ -1352,6 +1355,42 @@ struct rtw_coex_dm { #define COEX_BTINFO_LENGTH_MAX 10 #define COEX_BTINFO_LENGTH 7 +#define COEX_BT_HIDINFO_LIST 0x0 +#define COEX_BT_HIDINFO_A 0x1 +#define COEX_BT_HIDINFO_NAME 3 + +#define COEX_BT_HIDINFO_LENGTH 6 +#define COEX_BT_HIDINFO_HANDLE_NUM 4 +#define COEX_BT_HIDINFO_C2H_HANDLE 0 +#define COEX_BT_HIDINFO_C2H_VENDOR 1 +#define COEX_BT_BLE_HANDLE_THRS 0x10 +#define COEX_BT_HIDINFO_NOTCON 0xff + +struct rtw_coex_hid { + u8 hid_handle; + u8 hid_vendor; + u8 hid_name[COEX_BT_HIDINFO_NAME]; + bool hid_info_completed; + bool is_game_hid; +}; + +struct rtw_coex_hid_handle_list { + u8 cmd_id; + u8 len; + u8 subid; + u8 handle_cnt; + u8 handle[COEX_BT_HIDINFO_HANDLE_NUM]; +} __packed; + +struct rtw_coex_hid_info_a { + u8 cmd_id; + u8 len; + u8 subid; + u8 handle; + u8 vendor; + u8 name[COEX_BT_HIDINFO_NAME]; +} __packed; + struct rtw_coex_stat { bool bt_disabled; bool bt_disabled_pre; @@ -1382,6 +1421,8 @@ struct rtw_coex_stat { bool bt_slave; bool bt_418_hid_exist; bool bt_ble_hid_exist; + bool bt_game_hid_exist; + bool bt_hid_handle_cnt; bool bt_mailbox_reply; bool wl_under_lps; @@ -1402,6 +1443,7 @@ struct rtw_coex_stat { bool wl_connecting; bool wl_slot_toggle; bool wl_slot_toggle_change; /* if toggle to no-toggle */ + bool wl_mimo_ps; u32 bt_supported_version; u32 bt_supported_feature; @@ -1459,6 +1501,9 @@ struct rtw_coex_stat { u32 darfrc; u32 darfrch; + + struct rtw_coex_hid hid_info[COEX_BT_HIDINFO_HANDLE_NUM]; + struct rtw_coex_hid_handle_list hid_handle_list; }; struct rtw_coex { @@ -1867,6 +1912,7 @@ struct rtw_hal { u32 antenna_tx; u32 antenna_rx; u8 bfee_sts_cap; + bool txrx_1ss; /* protect tx power section */ struct mutex tx_power_mutex; @@ -1960,6 +2006,7 @@ struct rtw_dev { /* c2h cmd queue & handler work */ struct sk_buff_head c2h_queue; struct work_struct c2h_work; + struct work_struct ips_work; struct work_struct fw_recovery_work; /* used to protect txqs list */ @@ -2101,7 +2148,8 @@ void rtw_tx_report_purge_timer(struct timer_list *t); void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si); void rtw_core_scan_start(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, const u8 *mac_addr, bool hw_scan); -void rtw_core_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif); +void rtw_core_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, + bool hw_scan); int rtw_core_start(struct rtw_dev *rtwdev); void rtw_core_stop(struct rtw_dev *rtwdev); int rtw_chip_info_setup(struct rtw_dev *rtwdev); @@ -2121,5 +2169,5 @@ void rtw_core_fw_scan_notify(struct rtw_dev *rtwdev, bool start); int rtw_dump_fw(struct rtw_dev *rtwdev, const u32 ocp_src, u32 size, u32 fwcd_item); int rtw_dump_reg(struct rtw_dev *rtwdev, const u32 addr, const u32 size); - +void rtw_set_txrx_1ss(struct rtw_dev *rtwdev, bool config_1ss); #endif diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723d.c b/drivers/net/wireless/realtek/rtw88/rtw8723d.c index 3fdbaf7302c5..ad2b323a0423 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723d.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8723d.c @@ -2753,6 +2753,7 @@ struct rtw_chip_info rtw8723d_hw_spec = { .scbd_support = true, .new_scbd10_def = true, .ble_hid_profile_support = false, + .wl_mimo_ps_support = false, .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF, .bt_rssi_type = COEX_BTRSSI_RATIO, .ant_isolation = 15, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c index db078df63f85..99eee128ae94 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c @@ -499,7 +499,7 @@ static s8 get_cck_rx_pwr(struct rtw_dev *rtwdev, u8 lna_idx, u8 vga_idx) } if (lna_idx >= lna_gain_table_size) { - rtw_info(rtwdev, "incorrect lna index (%d)\n", lna_idx); + rtw_warn(rtwdev, "incorrect lna index (%d)\n", lna_idx); return -120; } @@ -1514,6 +1514,7 @@ static const struct rtw_rfe_def rtw8821c_rfe_defs[] = { [0] = RTW_DEF_RFE(8821c, 0, 0), [2] = RTW_DEF_RFE_EXT(8821c, 0, 0, 2), [4] = RTW_DEF_RFE_EXT(8821c, 0, 0, 2), + [6] = RTW_DEF_RFE(8821c, 0, 0), }; static struct rtw_hw_reg rtw8821c_dig[] = { @@ -1924,6 +1925,7 @@ struct rtw_chip_info rtw8821c_hw_spec = { .scbd_support = true, .new_scbd10_def = false, .ble_hid_profile_support = false, + .wl_mimo_ps_support = false, .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF, .bt_rssi_type = COEX_BTRSSI_RATIO, .ant_isolation = 15, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c index dd4fbb82750d..eee7bf035403 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c @@ -1012,12 +1012,12 @@ static int rtw8822b_set_antenna(struct rtw_dev *rtwdev, antenna_tx, antenna_rx); if (!rtw8822b_check_rf_path(antenna_tx)) { - rtw_info(rtwdev, "unsupported tx path 0x%x\n", antenna_tx); + rtw_warn(rtwdev, "unsupported tx path 0x%x\n", antenna_tx); return -EINVAL; } if (!rtw8822b_check_rf_path(antenna_rx)) { - rtw_info(rtwdev, "unsupported rx path 0x%x\n", antenna_rx); + rtw_warn(rtwdev, "unsupported rx path 0x%x\n", antenna_rx); return -EINVAL; } @@ -2554,6 +2554,7 @@ struct rtw_chip_info rtw8822b_hw_spec = { .scbd_support = true, .new_scbd10_def = false, .ble_hid_profile_support = false, + .wl_mimo_ps_support = false, .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF, .bt_rssi_type = COEX_BTRSSI_RATIO, .ant_isolation = 15, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index 35c46e5209de..cd74607a61a2 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -2798,7 +2798,7 @@ static int rtw8822c_set_antenna(struct rtw_dev *rtwdev, case BB_PATH_AB: break; default: - rtw_info(rtwdev, "unsupported tx path 0x%x\n", antenna_tx); + rtw_warn(rtwdev, "unsupported tx path 0x%x\n", antenna_tx); return -EINVAL; } @@ -2808,7 +2808,7 @@ static int rtw8822c_set_antenna(struct rtw_dev *rtwdev, case BB_PATH_AB: break; default: - rtw_info(rtwdev, "unsupported rx path 0x%x\n", antenna_rx); + rtw_warn(rtwdev, "unsupported rx path 0x%x\n", antenna_rx); return -EINVAL; } @@ -2996,19 +2996,34 @@ static void rtw8822c_coex_cfg_gnt_fix(struct rtw_dev *rtwdev) * enable "DAC off if GNT_WL = 0" for non-shared-antenna * disable 0x1c30[22] = 0, * enable: 0x1c30[22] = 1, 0x1c38[12] = 0, 0x1c38[28] = 1 - * - * disable WL-S1 BB chage RF mode if GNT_BT + */ + if (coex_stat->wl_coex_mode == COEX_WLINK_2GFREE) { + rtw_write8_mask(rtwdev, REG_ANAPAR + 2, + BIT_ANAPAR_BTPS >> 16, 0); + } else { + rtw_write8_mask(rtwdev, REG_ANAPAR + 2, + BIT_ANAPAR_BTPS >> 16, 1); + rtw_write8_mask(rtwdev, REG_RSTB_SEL + 1, + BIT_DAC_OFF_ENABLE, 0); + rtw_write8_mask(rtwdev, REG_RSTB_SEL + 3, + BIT_DAC_OFF_ENABLE, 1); + } + + /* disable WL-S1 BB chage RF mode if GNT_BT * since RF TRx mask can do it */ - rtw_write8_mask(rtwdev, REG_ANAPAR + 2, BIT_ANAPAR_BTPS >> 16, 1); - rtw_write8_mask(rtwdev, REG_RSTB_SEL + 1, BIT_DAC_OFF_ENABLE, 0); - rtw_write8_mask(rtwdev, REG_RSTB_SEL + 3, BIT_DAC_OFF_ENABLE, 1); - rtw_write8_mask(rtwdev, REG_IGN_GNTBT4, BIT_PI_IGNORE_GNT_BT, 1); + rtw_write8_mask(rtwdev, REG_IGN_GNTBT4, + BIT_PI_IGNORE_GNT_BT, 1); /* disable WL-S0 BB chage RF mode if wifi is at 5G, * or antenna path is separated */ - if (coex_stat->wl_coex_mode == COEX_WLINK_5G || + if (coex_stat->wl_coex_mode == COEX_WLINK_2GFREE) { + rtw_write8_mask(rtwdev, REG_IGN_GNT_BT1, + BIT_PI_IGNORE_GNT_BT, 1); + rtw_write8_mask(rtwdev, REG_NOMASK_TXBT, + BIT_NOMASK_TXBT_ENABLE, 1); + } else if (coex_stat->wl_coex_mode == COEX_WLINK_5G || coex->under_5g || !efuse->share_ant) { if (coex_stat->kt_ver >= 3) { rtw_write8_mask(rtwdev, REG_IGN_GNT_BT1, @@ -4962,6 +4977,7 @@ static struct rtw_chip_ops rtw8822c_ops = { .cfo_init = rtw8822c_cfo_init, .cfo_track = rtw8822c_cfo_track, .config_tx_path = rtw8822c_config_tx_path, + .config_txrx_mode = rtw8822c_config_trx_mode, .coex_set_init = rtw8822c_coex_cfg_init, .coex_set_ant_switch = NULL, @@ -5007,6 +5023,8 @@ static const struct coex_table_para table_sant_8822c[] = { {0x66556aaa, 0x6a5a6aaa}, /*case-30*/ {0xffffffff, 0x5aaa5aaa}, {0x56555555, 0x5a5a5aaa}, + {0xdaffdaff, 0xdaffdaff}, + {0xddffddff, 0xddffddff}, }; /* Non-Shared-Antenna Coex Table */ @@ -5107,7 +5125,8 @@ static const struct coex_rf_para rf_para_tx_8822c[] = { {8, 17, true, 4}, {7, 18, true, 4}, {6, 19, true, 4}, - {5, 20, true, 4} + {5, 20, true, 4}, + {0, 21, true, 4} /* for gamg hid */ }; static const struct coex_rf_para rf_para_rx_8822c[] = { @@ -5116,7 +5135,8 @@ static const struct coex_rf_para rf_para_rx_8822c[] = { {3, 24, true, 5}, {2, 26, true, 5}, {1, 27, true, 5}, - {0, 28, true, 5} + {0, 28, true, 5}, + {0, 28, true, 5} /* for gamg hid */ }; static_assert(ARRAY_SIZE(rf_para_tx_8822c) == ARRAY_SIZE(rf_para_rx_8822c)); @@ -5354,11 +5374,12 @@ struct rtw_chip_info rtw8822c_hw_spec = { .wowlan_stub = &rtw_wowlan_stub_8822c, .max_sched_scan_ssids = 4, #endif - .coex_para_ver = 0x2103181c, - .bt_desired_ver = 0x1c, + .coex_para_ver = 0x22020720, + .bt_desired_ver = 0x20, .scbd_support = true, .new_scbd10_def = true, .ble_hid_profile_support = true, + .wl_mimo_ps_support = true, .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF, .bt_rssi_type = COEX_BTRSSI_DBM, .ant_isolation = 15, diff --git a/drivers/net/wireless/realtek/rtw88/sar.c b/drivers/net/wireless/realtek/rtw88/sar.c index 3383726c4d90..c472f1502b82 100644 --- a/drivers/net/wireless/realtek/rtw88/sar.c +++ b/drivers/net/wireless/realtek/rtw88/sar.c @@ -91,10 +91,10 @@ int rtw_set_sar_specs(struct rtw_dev *rtwdev, return -EINVAL; power = sar->sub_specs[i].power; - rtw_info(rtwdev, "On freq %u to %u, set SAR %d in 1/%lu dBm\n", - rtw_common_sar_freq_ranges[idx].start_freq, - rtw_common_sar_freq_ranges[idx].end_freq, - power, BIT(RTW_COMMON_SAR_FCT)); + rtw_dbg(rtwdev, RTW_DBG_REGD, "On freq %u to %u, set SAR %d in 1/%lu dBm\n", + rtw_common_sar_freq_ranges[idx].start_freq, + rtw_common_sar_freq_ranges[idx].end_freq, + power, BIT(RTW_COMMON_SAR_FCT)); for (j = 0; j < RTW_RF_PATH_MAX; j++) { for (k = 0; k < RTW_RATE_SECTION_MAX; k++) { diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c index efcc1b0371a8..94d1089f4022 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -353,7 +353,7 @@ static void rtw_tx_data_pkt_info_update(struct rtw_dev *rtwdev, bw = si->bw_mode; rate_id = si->rate_id; - stbc = si->stbc_en; + stbc = rtwdev->hal.txrx_1ss ? false : si->stbc_en; ldpc = si->ldpc_en; out: diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig index 37e5def24d9f..dd02b6a6790e 100644 --- a/drivers/net/wireless/realtek/rtw89/Kconfig +++ b/drivers/net/wireless/realtek/rtw89/Kconfig @@ -16,11 +16,15 @@ config RTW89_CORE config RTW89_PCI tristate +config RTW89_8852A + tristate + config RTW89_8852AE tristate "Realtek 8852AE PCI wireless network adapter" depends on PCI select RTW89_CORE select RTW89_PCI + select RTW89_8852A help Select this option will enable support for 8852AE chipset diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile index 077e8fe23f60..012ae60c0b81 100644 --- a/drivers/net/wireless/realtek/rtw89/Makefile +++ b/drivers/net/wireless/realtek/rtw89/Makefile @@ -6,10 +6,6 @@ rtw89_core-y += core.o \ mac.o \ phy.o \ fw.o \ - rtw8852a.o \ - rtw8852a_table.o \ - rtw8852a_rfk.o \ - rtw8852a_rfk_table.o \ cam.o \ efuse.o \ regd.o \ @@ -18,6 +14,15 @@ rtw89_core-y += core.o \ ps.o \ ser.o +obj-$(CONFIG_RTW89_8852A) += rtw89_8852a.o +rtw89_8852a-objs := rtw8852a.o \ + rtw8852a_table.o \ + rtw8852a_rfk.o \ + rtw8852a_rfk_table.o + +obj-$(CONFIG_RTW89_8852AE) += rtw89_8852ae.o +rtw89_8852ae-objs := rtw8852ae.o + rtw89_core-$(CONFIG_RTW89_DEBUG) += debug.o obj-$(CONFIG_RTW89_PCI) += rtw89_pci.o diff --git a/drivers/net/wireless/realtek/rtw89/cam.c b/drivers/net/wireless/realtek/rtw89/cam.c index bd34e4bbe107..305dbbebff6b 100644 --- a/drivers/net/wireless/realtek/rtw89/cam.c +++ b/drivers/net/wireless/realtek/rtw89/cam.c @@ -231,7 +231,7 @@ static int rtw89_cam_attach_sec_cam(struct rtw89_dev *rtwdev, } rtwvif = (struct rtw89_vif *)vif->drv_priv; - addr_cam = &rtwvif->addr_cam; + addr_cam = rtw89_get_addr_cam_of(rtwvif, rtwsta); ret = rtw89_cam_get_addr_cam_key_idx(addr_cam, sec_cam, key, &key_idx); if (ret) { rtw89_err(rtwdev, "failed to get addr cam key idx %d, %d\n", @@ -387,7 +387,7 @@ int rtw89_cam_sec_key_del(struct rtw89_dev *rtwdev, } rtwvif = (struct rtw89_vif *)vif->drv_priv; - addr_cam = &rtwvif->addr_cam; + addr_cam = rtw89_get_addr_cam_of(rtwvif, rtwsta); sec_cam = addr_cam->sec_entries[key_idx]; if (!sec_cam) return -EINVAL; @@ -427,15 +427,23 @@ static void rtw89_cam_reset_key_iter(struct ieee80211_hw *hw, rtw89_cam_deinit(rtwdev, rtwvif); } +void rtw89_cam_deinit_addr_cam(struct rtw89_dev *rtwdev, + struct rtw89_addr_cam_entry *addr_cam) +{ + struct rtw89_cam_info *cam_info = &rtwdev->cam_info; + + addr_cam->valid = false; + clear_bit(addr_cam->addr_cam_idx, cam_info->addr_cam_map); +} + void rtw89_cam_deinit(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { struct rtw89_cam_info *cam_info = &rtwdev->cam_info; struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam; struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam; - addr_cam->valid = false; + rtw89_cam_deinit_addr_cam(rtwdev, addr_cam); bssid_cam->valid = false; - clear_bit(addr_cam->addr_cam_idx, cam_info->addr_cam_map); clear_bit(bssid_cam->bssid_cam_idx, cam_info->bssid_cam_map); } @@ -464,10 +472,10 @@ static int rtw89_cam_get_avail_addr_cam(struct rtw89_dev *rtwdev, return 0; } -static int rtw89_cam_init_addr_cam(struct rtw89_dev *rtwdev, - struct rtw89_vif *rtwvif) +int rtw89_cam_init_addr_cam(struct rtw89_dev *rtwdev, + struct rtw89_addr_cam_entry *addr_cam, + const struct rtw89_bssid_cam_entry *bssid_cam) { - struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam; u8 addr_cam_idx; int i; int ret; @@ -484,14 +492,17 @@ static int rtw89_cam_init_addr_cam(struct rtw89_dev *rtwdev, addr_cam->valid = true; addr_cam->addr_mask = 0; addr_cam->mask_sel = RTW89_NO_MSK; + addr_cam->sec_ent_mode = RTW89_ADDR_CAM_SEC_NORMAL; bitmap_zero(addr_cam->sec_cam_map, RTW89_SEC_CAM_IN_ADDR_CAM); - ether_addr_copy(addr_cam->sma, rtwvif->mac_addr); for (i = 0; i < RTW89_SEC_CAM_IN_ADDR_CAM; i++) { addr_cam->sec_ent_keyid[i] = 0; addr_cam->sec_ent[i] = 0; } + /* associate addr cam with bssid cam */ + addr_cam->bssid_cam_idx = bssid_cam->bssid_cam_idx; + return 0; } @@ -549,20 +560,17 @@ int rtw89_cam_init(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam; int ret; - ret = rtw89_cam_init_addr_cam(rtwdev, rtwvif); - if (ret) { - rtw89_err(rtwdev, "failed to init addr cam\n"); - return ret; - } - ret = rtw89_cam_init_bssid_cam(rtwdev, rtwvif); if (ret) { rtw89_err(rtwdev, "failed to init bssid cam\n"); return ret; } - /* associate addr cam with bssid cam */ - addr_cam->bssid_cam_idx = bssid_cam->bssid_cam_idx; + ret = rtw89_cam_init_addr_cam(rtwdev, addr_cam, bssid_cam); + if (ret) { + rtw89_err(rtwdev, "failed to init addr cam\n"); + return ret; + } return 0; } @@ -609,7 +617,7 @@ void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev, u8 *cmd) { struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); - struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam; + struct rtw89_addr_cam_entry *addr_cam = rtw89_get_addr_cam_of(rtwvif, rtwsta); struct ieee80211_sta *sta = rtwsta_to_sta_safe(rtwsta); const u8 *sma = scan_mac_addr ? scan_mac_addr : rtwvif->mac_addr; u8 sma_hash, tma_hash, addr_msk_start; diff --git a/drivers/net/wireless/realtek/rtw89/cam.h b/drivers/net/wireless/realtek/rtw89/cam.h index 33a3ad582b81..3a6a786530d1 100644 --- a/drivers/net/wireless/realtek/rtw89/cam.h +++ b/drivers/net/wireless/realtek/rtw89/cam.h @@ -346,6 +346,11 @@ static inline void FWCMD_SET_ADDR_BSSID_BSSID5(void *cmd, u32 value) int rtw89_cam_init(struct rtw89_dev *rtwdev, struct rtw89_vif *vif); void rtw89_cam_deinit(struct rtw89_dev *rtwdev, struct rtw89_vif *vif); +int rtw89_cam_init_addr_cam(struct rtw89_dev *rtwdev, + struct rtw89_addr_cam_entry *addr_cam, + const struct rtw89_bssid_cam_entry *bssid_cam); +void rtw89_cam_deinit_addr_cam(struct rtw89_dev *rtwdev, + struct rtw89_addr_cam_entry *addr_cam); void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev, struct rtw89_vif *vif, struct rtw89_sta *rtwsta, diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 9f7d4f8d0c56..684583955511 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -594,7 +594,7 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type) memset(&btc->dm, 0, sizeof(btc->dm)); memset(bt_linfo->rssi_state, 0, sizeof(bt_linfo->rssi_state)); - for (i = 0; i < RTW89_MAX_HW_PORT_NUM; i++) + for (i = 0; i < RTW89_PORT_NUM; i++) memset(wl_linfo[i].rssi_state, 0, sizeof(wl_linfo[i].rssi_state)); @@ -1478,7 +1478,7 @@ static void _set_gnt_wl(struct rtw89_dev *rtwdev, u8 phy_map, u8 state) } } - rtw89_mac_cfg_gnt(rtwdev, &dm->gnt); + rtw89_chip_mac_cfg_gnt(rtwdev, &dm->gnt); } #define BTC_TDMA_WLROLE_MAX 2 @@ -1698,7 +1698,7 @@ static void _set_bt_afh_info(struct rtw89_dev *rtwdev) wl_rinfo->link_mode == BTC_WLINK_2G_SCC) { en = true; /* get p2p channel */ - for (i = 0; i < RTW89_MAX_HW_PORT_NUM; i++) { + for (i = 0; i < RTW89_PORT_NUM; i++) { if (wl_rinfo->active_role[i].role == RTW89_WIFI_ROLE_P2P_GO || wl_rinfo->active_role[i].role == @@ -1711,7 +1711,7 @@ static void _set_bt_afh_info(struct rtw89_dev *rtwdev) } else { en = true; /* get 2g channel */ - for (i = 0; i < RTW89_MAX_HW_PORT_NUM; i++) { + for (i = 0; i < RTW89_PORT_NUM; i++) { if (wl_rinfo->active_role[i].connected && wl_rinfo->active_role[i].band == RTW89_BAND_2G) { ch = wl_rinfo->active_role[i].ch; @@ -2233,7 +2233,7 @@ static void _set_gnt_bt(struct rtw89_dev *rtwdev, u8 phy_map, u8 state) } } - rtw89_mac_cfg_gnt(rtwdev, &dm->gnt); + rtw89_chip_mac_cfg_gnt(rtwdev, &dm->gnt); } static void _set_bt_plut(struct rtw89_dev *rtwdev, u8 phy_map, @@ -2300,7 +2300,7 @@ static void _set_ant(struct rtw89_dev *rtwdev, bool force_exec, switch (type) { case BTC_ANT_WPOWERON: - rtw89_mac_cfg_ctrl_path(rtwdev, false); + rtw89_chip_cfg_ctrl_path(rtwdev, false); break; case BTC_ANT_WINIT: if (bt->enable.now) { @@ -2310,21 +2310,21 @@ static void _set_ant(struct rtw89_dev *rtwdev, bool force_exec, _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_HI); _set_gnt_bt(rtwdev, phy_map, BTC_GNT_SW_LO); } - rtw89_mac_cfg_ctrl_path(rtwdev, true); + rtw89_chip_cfg_ctrl_path(rtwdev, true); _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_BT, BTC_PLT_BT); break; case BTC_ANT_WONLY: _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_HI); _set_gnt_bt(rtwdev, phy_map, BTC_GNT_SW_LO); - rtw89_mac_cfg_ctrl_path(rtwdev, true); + rtw89_chip_cfg_ctrl_path(rtwdev, true); _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE); break; case BTC_ANT_WOFF: - rtw89_mac_cfg_ctrl_path(rtwdev, false); + rtw89_chip_cfg_ctrl_path(rtwdev, false); _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE); break; case BTC_ANT_W2G: - rtw89_mac_cfg_ctrl_path(rtwdev, true); + rtw89_chip_cfg_ctrl_path(rtwdev, true); if (rtwdev->dbcc_en) { for (i = 0; i < RTW89_PHY_MAX; i++) { b2g = (wl_dinfo->real_band[i] == RTW89_BAND_2G); @@ -2352,32 +2352,32 @@ static void _set_ant(struct rtw89_dev *rtwdev, bool force_exec, } break; case BTC_ANT_W5G: - rtw89_mac_cfg_ctrl_path(rtwdev, true); + rtw89_chip_cfg_ctrl_path(rtwdev, true); _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_HI); _set_gnt_bt(rtwdev, phy_map, BTC_GNT_HW); _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE); break; case BTC_ANT_W25G: - rtw89_mac_cfg_ctrl_path(rtwdev, true); + rtw89_chip_cfg_ctrl_path(rtwdev, true); _set_gnt_wl(rtwdev, phy_map, BTC_GNT_HW); _set_gnt_bt(rtwdev, phy_map, BTC_GNT_HW); _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_GNT_WL, BTC_PLT_GNT_WL); break; case BTC_ANT_FREERUN: - rtw89_mac_cfg_ctrl_path(rtwdev, true); + rtw89_chip_cfg_ctrl_path(rtwdev, true); _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_HI); _set_gnt_bt(rtwdev, phy_map, BTC_GNT_SW_HI); _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE); break; case BTC_ANT_WRFK: - rtw89_mac_cfg_ctrl_path(rtwdev, true); + rtw89_chip_cfg_ctrl_path(rtwdev, true); _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_HI); _set_gnt_bt(rtwdev, phy_map, BTC_GNT_SW_LO); _set_bt_plut(rtwdev, phy_map, BTC_PLT_NONE, BTC_PLT_NONE); break; case BTC_ANT_BRFK: - rtw89_mac_cfg_ctrl_path(rtwdev, false); + rtw89_chip_cfg_ctrl_path(rtwdev, false); _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_LO); _set_gnt_bt(rtwdev, phy_map, BTC_GNT_SW_HI); _set_bt_plut(rtwdev, phy_map, BTC_PLT_NONE, BTC_PLT_NONE); @@ -3287,7 +3287,7 @@ static void _update_wl_info(struct rtw89_dev *rtwdev) memset(wl_rinfo, 0, sizeof(*wl_rinfo)); - for (i = 0; i < RTW89_MAX_HW_PORT_NUM; i++) { + for (i = 0; i < RTW89_PORT_NUM; i++) { /* check if role active? */ if (!wl_linfo[i].active) continue; @@ -4370,6 +4370,7 @@ void rtw89_btc_ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_map, rtwdev->is_bt_iqk_timeout = true; } } +EXPORT_SYMBOL(rtw89_btc_ntfy_wl_rfk); struct rtw89_btc_wl_sta_iter_data { struct rtw89_dev *rtwdev; @@ -4622,12 +4623,12 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m) ver_hotfix = FIELD_GET(GENMASK(15, 8), chip->wlcx_desired); seq_printf(m, "(%s, desired:%d.%d.%d), ", (wl->ver_info.fw_coex >= chip->wlcx_desired ? - "Match" : "Mis-Match"), ver_main, ver_sub, ver_hotfix); + "Match" : "Mismatch"), ver_main, ver_sub, ver_hotfix); seq_printf(m, "BT_FW_coex:%d(%s, desired:%d)\n", bt->ver_info.fw_coex, (bt->ver_info.fw_coex >= chip->btcx_desired ? - "Match" : "Mis-Match"), chip->btcx_desired); + "Match" : "Mismatch"), chip->btcx_desired); if (bt->enable.now && bt->ver_info.fw == 0) rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, true); @@ -4676,7 +4677,7 @@ static void _show_wl_role_info(struct rtw89_dev *rtwdev, struct seq_file *m) wl_dinfo->real_band[RTW89_PHY_1]); } - for (i = 0; i < RTW89_MAX_HW_PORT_NUM; i++) { + for (i = 0; i < RTW89_PORT_NUM; i++) { plink = &btc->cx.wl.link_info[i]; if (!plink->active) @@ -5074,7 +5075,7 @@ static void _show_dm_info(struct rtw89_dev *rtwdev, struct seq_file *m) seq_printf(m, "leak_ap:%d, fw_offload:%s%s\n", dm->leak_ap, (BTC_CX_FW_OFFLOAD ? "Y" : "N"), (dm->wl_fw_cx_offload == BTC_CX_FW_OFFLOAD ? - "" : "(Mis-Match!!)")); + "" : "(Mismatch!!)")); if (dm->rf_trx_para.wl_tx_power == 0xff) seq_printf(m, diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index a0737eea9f81..bcefc968576e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4,6 +4,7 @@ #include #include +#include "cam.h" #include "coex.h" #include "core.h" #include "efuse.h" @@ -21,50 +22,122 @@ static bool rtw89_disable_ps_mode; module_param_named(disable_ps_mode, rtw89_disable_ps_mode, bool, 0644); MODULE_PARM_DESC(disable_ps_mode, "Set Y to disable low power mode"); +#define RTW89_DEF_CHAN(_freq, _hw_val, _flags, _band) \ + { .center_freq = _freq, .hw_value = _hw_val, .flags = _flags, .band = _band, } +#define RTW89_DEF_CHAN_2G(_freq, _hw_val) \ + RTW89_DEF_CHAN(_freq, _hw_val, 0, NL80211_BAND_2GHZ) +#define RTW89_DEF_CHAN_5G(_freq, _hw_val) \ + RTW89_DEF_CHAN(_freq, _hw_val, 0, NL80211_BAND_5GHZ) +#define RTW89_DEF_CHAN_5G_NO_HT40MINUS(_freq, _hw_val) \ + RTW89_DEF_CHAN(_freq, _hw_val, IEEE80211_CHAN_NO_HT40MINUS, NL80211_BAND_5GHZ) +#define RTW89_DEF_CHAN_6G(_freq, _hw_val) \ + RTW89_DEF_CHAN(_freq, _hw_val, 0, NL80211_BAND_6GHZ) + static struct ieee80211_channel rtw89_channels_2ghz[] = { - { .center_freq = 2412, .hw_value = 1, }, - { .center_freq = 2417, .hw_value = 2, }, - { .center_freq = 2422, .hw_value = 3, }, - { .center_freq = 2427, .hw_value = 4, }, - { .center_freq = 2432, .hw_value = 5, }, - { .center_freq = 2437, .hw_value = 6, }, - { .center_freq = 2442, .hw_value = 7, }, - { .center_freq = 2447, .hw_value = 8, }, - { .center_freq = 2452, .hw_value = 9, }, - { .center_freq = 2457, .hw_value = 10, }, - { .center_freq = 2462, .hw_value = 11, }, - { .center_freq = 2467, .hw_value = 12, }, - { .center_freq = 2472, .hw_value = 13, }, - { .center_freq = 2484, .hw_value = 14, }, + RTW89_DEF_CHAN_2G(2412, 1), + RTW89_DEF_CHAN_2G(2417, 2), + RTW89_DEF_CHAN_2G(2422, 3), + RTW89_DEF_CHAN_2G(2427, 4), + RTW89_DEF_CHAN_2G(2432, 5), + RTW89_DEF_CHAN_2G(2437, 6), + RTW89_DEF_CHAN_2G(2442, 7), + RTW89_DEF_CHAN_2G(2447, 8), + RTW89_DEF_CHAN_2G(2452, 9), + RTW89_DEF_CHAN_2G(2457, 10), + RTW89_DEF_CHAN_2G(2462, 11), + RTW89_DEF_CHAN_2G(2467, 12), + RTW89_DEF_CHAN_2G(2472, 13), + RTW89_DEF_CHAN_2G(2484, 14), }; static struct ieee80211_channel rtw89_channels_5ghz[] = { - {.center_freq = 5180, .hw_value = 36,}, - {.center_freq = 5200, .hw_value = 40,}, - {.center_freq = 5220, .hw_value = 44,}, - {.center_freq = 5240, .hw_value = 48,}, - {.center_freq = 5260, .hw_value = 52,}, - {.center_freq = 5280, .hw_value = 56,}, - {.center_freq = 5300, .hw_value = 60,}, - {.center_freq = 5320, .hw_value = 64,}, - {.center_freq = 5500, .hw_value = 100,}, - {.center_freq = 5520, .hw_value = 104,}, - {.center_freq = 5540, .hw_value = 108,}, - {.center_freq = 5560, .hw_value = 112,}, - {.center_freq = 5580, .hw_value = 116,}, - {.center_freq = 5600, .hw_value = 120,}, - {.center_freq = 5620, .hw_value = 124,}, - {.center_freq = 5640, .hw_value = 128,}, - {.center_freq = 5660, .hw_value = 132,}, - {.center_freq = 5680, .hw_value = 136,}, - {.center_freq = 5700, .hw_value = 140,}, - {.center_freq = 5720, .hw_value = 144,}, - {.center_freq = 5745, .hw_value = 149,}, - {.center_freq = 5765, .hw_value = 153,}, - {.center_freq = 5785, .hw_value = 157,}, - {.center_freq = 5805, .hw_value = 161,}, - {.center_freq = 5825, .hw_value = 165, - .flags = IEEE80211_CHAN_NO_HT40MINUS}, + RTW89_DEF_CHAN_5G(5180, 36), + RTW89_DEF_CHAN_5G(5200, 40), + RTW89_DEF_CHAN_5G(5220, 44), + RTW89_DEF_CHAN_5G(5240, 48), + RTW89_DEF_CHAN_5G(5260, 52), + RTW89_DEF_CHAN_5G(5280, 56), + RTW89_DEF_CHAN_5G(5300, 60), + RTW89_DEF_CHAN_5G(5320, 64), + RTW89_DEF_CHAN_5G(5500, 100), + RTW89_DEF_CHAN_5G(5520, 104), + RTW89_DEF_CHAN_5G(5540, 108), + RTW89_DEF_CHAN_5G(5560, 112), + RTW89_DEF_CHAN_5G(5580, 116), + RTW89_DEF_CHAN_5G(5600, 120), + RTW89_DEF_CHAN_5G(5620, 124), + RTW89_DEF_CHAN_5G(5640, 128), + RTW89_DEF_CHAN_5G(5660, 132), + RTW89_DEF_CHAN_5G(5680, 136), + RTW89_DEF_CHAN_5G(5700, 140), + RTW89_DEF_CHAN_5G(5720, 144), + RTW89_DEF_CHAN_5G(5745, 149), + RTW89_DEF_CHAN_5G(5765, 153), + RTW89_DEF_CHAN_5G(5785, 157), + RTW89_DEF_CHAN_5G(5805, 161), + RTW89_DEF_CHAN_5G_NO_HT40MINUS(5825, 165), +}; + +static struct ieee80211_channel rtw89_channels_6ghz[] = { + RTW89_DEF_CHAN_6G(5955, 1), + RTW89_DEF_CHAN_6G(5975, 5), + RTW89_DEF_CHAN_6G(5995, 9), + RTW89_DEF_CHAN_6G(6015, 13), + RTW89_DEF_CHAN_6G(6035, 17), + RTW89_DEF_CHAN_6G(6055, 21), + RTW89_DEF_CHAN_6G(6075, 25), + RTW89_DEF_CHAN_6G(6095, 29), + RTW89_DEF_CHAN_6G(6115, 33), + RTW89_DEF_CHAN_6G(6135, 37), + RTW89_DEF_CHAN_6G(6155, 41), + RTW89_DEF_CHAN_6G(6175, 45), + RTW89_DEF_CHAN_6G(6195, 49), + RTW89_DEF_CHAN_6G(6215, 53), + RTW89_DEF_CHAN_6G(6235, 57), + RTW89_DEF_CHAN_6G(6255, 61), + RTW89_DEF_CHAN_6G(6275, 65), + RTW89_DEF_CHAN_6G(6295, 69), + RTW89_DEF_CHAN_6G(6315, 73), + RTW89_DEF_CHAN_6G(6335, 77), + RTW89_DEF_CHAN_6G(6355, 81), + RTW89_DEF_CHAN_6G(6375, 85), + RTW89_DEF_CHAN_6G(6395, 89), + RTW89_DEF_CHAN_6G(6415, 93), + RTW89_DEF_CHAN_6G(6435, 97), + RTW89_DEF_CHAN_6G(6455, 101), + RTW89_DEF_CHAN_6G(6475, 105), + RTW89_DEF_CHAN_6G(6495, 109), + RTW89_DEF_CHAN_6G(6515, 113), + RTW89_DEF_CHAN_6G(6535, 117), + RTW89_DEF_CHAN_6G(6555, 121), + RTW89_DEF_CHAN_6G(6575, 125), + RTW89_DEF_CHAN_6G(6595, 129), + RTW89_DEF_CHAN_6G(6615, 133), + RTW89_DEF_CHAN_6G(6635, 137), + RTW89_DEF_CHAN_6G(6655, 141), + RTW89_DEF_CHAN_6G(6675, 145), + RTW89_DEF_CHAN_6G(6695, 149), + RTW89_DEF_CHAN_6G(6715, 153), + RTW89_DEF_CHAN_6G(6735, 157), + RTW89_DEF_CHAN_6G(6755, 161), + RTW89_DEF_CHAN_6G(6775, 165), + RTW89_DEF_CHAN_6G(6795, 169), + RTW89_DEF_CHAN_6G(6815, 173), + RTW89_DEF_CHAN_6G(6835, 177), + RTW89_DEF_CHAN_6G(6855, 181), + RTW89_DEF_CHAN_6G(6875, 185), + RTW89_DEF_CHAN_6G(6895, 189), + RTW89_DEF_CHAN_6G(6915, 193), + RTW89_DEF_CHAN_6G(6935, 197), + RTW89_DEF_CHAN_6G(6955, 201), + RTW89_DEF_CHAN_6G(6975, 205), + RTW89_DEF_CHAN_6G(6995, 209), + RTW89_DEF_CHAN_6G(7015, 213), + RTW89_DEF_CHAN_6G(7035, 217), + RTW89_DEF_CHAN_6G(7055, 221), + RTW89_DEF_CHAN_6G(7075, 225), + RTW89_DEF_CHAN_6G(7095, 229), + RTW89_DEF_CHAN_6G(7115, 233), }; static struct ieee80211_rate rtw89_bitrates[] = { @@ -118,6 +191,16 @@ static struct ieee80211_supported_band rtw89_sband_5ghz = { .vht_cap = {0}, }; +static struct ieee80211_supported_band rtw89_sband_6ghz = { + .band = NL80211_BAND_6GHZ, + .channels = rtw89_channels_6ghz, + .n_channels = ARRAY_SIZE(rtw89_channels_6ghz), + + /* 6G has no CCK rates, 1M/2M/5.5M/11M */ + .bitrates = rtw89_bitrates + 4, + .n_bitrates = ARRAY_SIZE(rtw89_bitrates) - 4, +}; + static void rtw89_traffic_stats_accu(struct rtw89_dev *rtwdev, struct rtw89_traffic_stats *stats, struct sk_buff *skb, bool tx) @@ -149,6 +232,9 @@ static void rtw89_get_channel_params(struct cfg80211_chan_def *chandef, u8 center_chan; u8 bandwidth = RTW89_CHANNEL_WIDTH_20; u8 primary_chan_idx = 0; + u32 offset; + u8 band; + u8 subband; center_chan = channel->hw_value; primary_freq = channel->center_freq; @@ -171,23 +257,16 @@ static void rtw89_get_channel_params(struct cfg80211_chan_def *chandef, } break; case NL80211_CHAN_WIDTH_80: - bandwidth = RTW89_CHANNEL_WIDTH_80; + case NL80211_CHAN_WIDTH_160: + bandwidth = nl_to_rtw89_bandwidth(width); if (primary_freq > center_freq) { - if (primary_freq - center_freq == 10) { - primary_chan_idx = RTW89_SC_20_UPPER; - center_chan -= 2; - } else { - primary_chan_idx = RTW89_SC_20_UPMOST; - center_chan -= 6; - } + offset = (primary_freq - center_freq - 10) / 20; + primary_chan_idx = RTW89_SC_20_UPPER + offset * 2; + center_chan -= 2 + offset * 4; } else { - if (center_freq - primary_freq == 10) { - primary_chan_idx = RTW89_SC_20_LOWER; - center_chan += 2; - } else { - primary_chan_idx = RTW89_SC_20_LOWEST; - center_chan += 6; - } + offset = (center_freq - primary_freq - 10) / 20; + primary_chan_idx = RTW89_SC_20_LOWER + offset * 2; + center_chan += 2 + offset * 4; } break; default: @@ -195,10 +274,81 @@ static void rtw89_get_channel_params(struct cfg80211_chan_def *chandef, break; } + switch (channel->band) { + default: + case NL80211_BAND_2GHZ: + band = RTW89_BAND_2G; + break; + case NL80211_BAND_5GHZ: + band = RTW89_BAND_5G; + break; + case NL80211_BAND_6GHZ: + band = RTW89_BAND_6G; + break; + } + + switch (band) { + default: + case RTW89_BAND_2G: + switch (center_chan) { + default: + case 1 ... 14: + subband = RTW89_CH_2G; + break; + } + break; + case RTW89_BAND_5G: + switch (center_chan) { + default: + case 36 ... 64: + subband = RTW89_CH_5G_BAND_1; + break; + case 100 ... 144: + subband = RTW89_CH_5G_BAND_3; + break; + case 149 ... 177: + subband = RTW89_CH_5G_BAND_4; + break; + } + break; + case RTW89_BAND_6G: + switch (center_chan) { + default: + case 1 ... 29: + subband = RTW89_CH_6G_BAND_IDX0; + break; + case 33 ... 61: + subband = RTW89_CH_6G_BAND_IDX1; + break; + case 65 ... 93: + subband = RTW89_CH_6G_BAND_IDX2; + break; + case 97 ... 125: + subband = RTW89_CH_6G_BAND_IDX3; + break; + case 129 ... 157: + subband = RTW89_CH_6G_BAND_IDX4; + break; + case 161 ... 189: + subband = RTW89_CH_6G_BAND_IDX5; + break; + case 193 ... 221: + subband = RTW89_CH_6G_BAND_IDX6; + break; + case 225 ... 253: + subband = RTW89_CH_6G_BAND_IDX7; + break; + } + break; + } + chan_param->center_chan = center_chan; + chan_param->center_freq = center_freq; chan_param->primary_chan = channel->hw_value; chan_param->bandwidth = bandwidth; chan_param->pri_ch_idx = primary_chan_idx; + chan_param->band_type = band; + chan_param->subband_type = subband; } void rtw89_set_channel(struct rtw89_dev *rtwdev) @@ -209,7 +359,6 @@ void rtw89_set_channel(struct rtw89_dev *rtwdev) struct rtw89_channel_params ch_param; struct rtw89_channel_help_params bak; u8 center_chan, bandwidth; - u8 band_type; bool band_changed; rtw89_get_channel_params(&hw->conf.chandef, &ch_param); @@ -218,30 +367,17 @@ void rtw89_set_channel(struct rtw89_dev *rtwdev) center_chan = ch_param.center_chan; bandwidth = ch_param.bandwidth; - band_type = center_chan > 14 ? RTW89_BAND_5G : RTW89_BAND_2G; - band_changed = hal->current_band_type != band_type || + band_changed = hal->current_band_type != ch_param.band_type || hal->current_channel == 0; hal->current_band_width = bandwidth; hal->current_channel = center_chan; + hal->current_freq = ch_param.center_freq; hal->prev_primary_channel = hal->current_primary_channel; + hal->prev_band_type = hal->current_band_type; hal->current_primary_channel = ch_param.primary_chan; - hal->current_band_type = band_type; - - switch (center_chan) { - case 1 ... 14: - hal->current_subband = RTW89_CH_2G; - break; - case 36 ... 64: - hal->current_subband = RTW89_CH_5G_BAND_1; - break; - case 100 ... 144: - hal->current_subband = RTW89_CH_5G_BAND_3; - break; - case 149 ... 177: - hal->current_subband = RTW89_CH_5G_BAND_4; - break; - } + hal->current_band_type = ch_param.band_type; + hal->current_subband = ch_param.subband_type; rtw89_chip_set_channel_prepare(rtwdev, &bak); @@ -300,9 +436,11 @@ rtw89_core_tx_update_sec_key(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req) { struct ieee80211_vif *vif = tx_req->vif; + struct ieee80211_sta *sta = tx_req->sta; struct ieee80211_tx_info *info; struct ieee80211_key_conf *key; struct rtw89_vif *rtwvif; + struct rtw89_sta *rtwsta = sta_to_rtwsta_safe(sta); struct rtw89_addr_cam_entry *addr_cam; struct rtw89_sec_cam_entry *sec_cam; struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; @@ -315,7 +453,7 @@ rtw89_core_tx_update_sec_key(struct rtw89_dev *rtwdev, } rtwvif = (struct rtw89_vif *)vif->drv_priv; - addr_cam = &rtwvif->addr_cam; + addr_cam = rtw89_get_addr_cam_of(rtwvif, rtwsta); info = IEEE80211_SKB_CB(skb); key = info->control.hw_key; @@ -377,14 +515,19 @@ static void rtw89_core_tx_update_mgmt_info(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req) { + struct ieee80211_vif *vif = tx_req->vif; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; u8 qsel, ch_dma; - qsel = RTW89_TX_QSEL_B0_MGMT; + qsel = desc_info->hiq ? RTW89_TX_QSEL_B0_HI : RTW89_TX_QSEL_B0_MGMT; ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel); - desc_info->qsel = RTW89_TX_QSEL_B0_MGMT; + desc_info->qsel = qsel; desc_info->ch_dma = ch_dma; + desc_info->port = desc_info->hiq ? rtwvif->port : 0; + desc_info->hw_ssn_sel = RTW89_MGMT_HW_SSN_SEL; + desc_info->hw_seq_mode = RTW89_MGMT_HW_SEQ_MODE; /* fixed data rate for mgmt frames */ desc_info->en_wd_info = true; @@ -520,6 +663,21 @@ rtw89_core_tx_update_he_qos_htc(struct rtw89_dev *rtwdev, desc_info->bk = true; } +static u8 rtw89_core_tx_get_mac_id(struct rtw89_dev *rtwdev, + struct rtw89_core_tx_request *tx_req) +{ + struct ieee80211_vif *vif = tx_req->vif; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + struct ieee80211_sta *sta = tx_req->sta; + struct rtw89_sta *rtwsta; + + if (!sta) + return rtwvif->mac_id; + + rtwsta = (struct rtw89_sta *)sta->drv_priv; + return rtwsta->mac_id; +} + static void rtw89_core_tx_update_data_info(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req) @@ -535,12 +693,14 @@ rtw89_core_tx_update_data_info(struct rtw89_dev *rtwdev, tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; tid_indicate = rtw89_core_get_tid_indicate(rtwdev, tid); - qsel = rtw89_core_get_qsel(rtwdev, tid); + qsel = desc_info->hiq ? RTW89_TX_QSEL_B0_HI : rtw89_core_get_qsel(rtwdev, tid); ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel); desc_info->ch_dma = ch_dma; desc_info->tid_indicate = tid_indicate; desc_info->qsel = qsel; + desc_info->mac_id = rtw89_core_tx_get_mac_id(rtwdev, tx_req); + desc_info->port = desc_info->hiq ? rtwvif->port : 0; /* enable wd_info for AMPDU */ desc_info->en_wd_info = true; @@ -595,12 +755,29 @@ rtw89_core_tx_btc_spec_pkt_notify(struct rtw89_dev *rtwdev, return PACKET_MAX; } +static void +rtw89_core_tx_wake(struct rtw89_dev *rtwdev, + struct rtw89_core_tx_request *tx_req) +{ + if (!rtwdev->fw.tx_wake) + return; + + if (!test_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags)) + return; + + if (tx_req->tx_type != RTW89_CORE_TX_TYPE_MGMT) + return; + + rtw89_mac_notify_wake(rtwdev); +} + static void rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req) { struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; struct sk_buff *skb = tx_req->skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (void *)skb->data; enum rtw89_core_tx_type tx_type; enum btc_pkt_type pkt_type; @@ -619,6 +796,7 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev, desc_info->pkt_size = skb->len; desc_info->is_bmc = is_bmc; desc_info->wd_page = true; + desc_info->hiq = info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM; switch (tx_req->tx_type) { case RTW89_CORE_TX_TYPE_MGMT: @@ -691,6 +869,8 @@ int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, rtw89_traffic_stats_accu(rtwdev, &rtwdev->stats, skb, true); rtw89_traffic_stats_accu(rtwdev, &rtwvif->stats, skb, true); rtw89_core_tx_update_desc_info(rtwdev, &tx_req); + rtw89_core_tx_wake(rtwdev, &tx_req); + ret = rtw89_hci_tx_write(rtwdev, &tx_req); if (ret) { rtw89_err(rtwdev, "failed to transmit skb to HCI\n"); @@ -710,7 +890,9 @@ static __le32 rtw89_build_txwd_body0(struct rtw89_tx_desc_info *desc_info) FIELD_PREP(RTW89_TXWD_BODY0_CHANNEL_DMA, desc_info->ch_dma) | FIELD_PREP(RTW89_TXWD_BODY0_HDR_LLC_LEN, desc_info->hdr_llc_len) | FIELD_PREP(RTW89_TXWD_BODY0_WD_PAGE, desc_info->wd_page) | - FIELD_PREP(RTW89_TXWD_BODY0_FW_DL, desc_info->fw_dl); + FIELD_PREP(RTW89_TXWD_BODY0_FW_DL, desc_info->fw_dl) | + FIELD_PREP(RTW89_TXWD_BODY0_HW_SSN_SEL, desc_info->hw_ssn_sel) | + FIELD_PREP(RTW89_TXWD_BODY0_HW_SSN_MODE, desc_info->hw_seq_mode); return cpu_to_le32(dword); } @@ -719,7 +901,8 @@ static __le32 rtw89_build_txwd_body2(struct rtw89_tx_desc_info *desc_info) { u32 dword = FIELD_PREP(RTW89_TXWD_BODY2_TID_INDICATE, desc_info->tid_indicate) | FIELD_PREP(RTW89_TXWD_BODY2_QSEL, desc_info->qsel) | - FIELD_PREP(RTW89_TXWD_BODY2_TXPKT_SIZE, desc_info->pkt_size); + FIELD_PREP(RTW89_TXWD_BODY2_TXPKT_SIZE, desc_info->pkt_size) | + FIELD_PREP(RTW89_TXWD_BODY2_MACID, desc_info->mac_id); return cpu_to_le32(dword); } @@ -737,7 +920,8 @@ static __le32 rtw89_build_txwd_info0(struct rtw89_tx_desc_info *desc_info) { u32 dword = FIELD_PREP(RTW89_TXWD_INFO0_USE_RATE, desc_info->use_rate) | FIELD_PREP(RTW89_TXWD_INFO0_DATA_RATE, desc_info->data_rate) | - FIELD_PREP(RTW89_TXWD_INFO0_DISDATAFB, desc_info->dis_data_fb); + FIELD_PREP(RTW89_TXWD_INFO0_DISDATAFB, desc_info->dis_data_fb) | + FIELD_PREP(RTW89_TXWD_INFO0_MULTIPORT_ID, desc_info->port); return cpu_to_le32(dword); } @@ -996,13 +1180,7 @@ static bool rtw89_core_rx_ppdu_match(struct rtw89_dev *rtwdev, rtw89_warn(rtwdev, "invalid RX rate mode %d\n", data_rate_mode); } - if (desc_info->bw == RTW89_CHANNEL_WIDTH_80) - bw = RATE_INFO_BW_80; - else if (desc_info->bw == RTW89_CHANNEL_WIDTH_40) - bw = RATE_INFO_BW_40; - else - bw = RATE_INFO_BW_20; - + bw = rtw89_hw_to_rate_info_bw(desc_info->bw); gi_ltf = rtw89_rxdesc_to_nl_he_gi(rtwdev, desc_info, false); ret = rtwdev->ppdu_sts.curr_rx_ppdu_cnt[band] == desc_info->ppdu_cnt && status->rate_idx == rate_idx && @@ -1083,10 +1261,31 @@ static void rtw89_core_hw_to_sband_rate(struct ieee80211_rx_status *rx_status) if (rx_status->band == NL80211_BAND_2GHZ || rx_status->encoding != RX_ENC_LEGACY) return; + + /* Some control frames' freq(ACKs in this case) are reported wrong due + * to FW notify timing, set to lowest rate to prevent overflow. + */ + if (rx_status->rate_idx < RTW89_HW_RATE_OFDM6) { + rx_status->rate_idx = 0; + return; + } + /* No 4 CCK rates for non-2G */ rx_status->rate_idx -= 4; } +static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, + struct rtw89_rx_phy_ppdu *phy_ppdu, + struct rtw89_rx_desc_info *desc_info, + struct sk_buff *skb_ppdu, + struct ieee80211_rx_status *rx_status) +{ + rtw89_core_hw_to_sband_rate(rx_status); + rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu); + ieee80211_rx_napi(rtwdev->hw, NULL, skb_ppdu, &rtwdev->napi); + rtwdev->napi_budget_countdown--; +} + static void rtw89_core_rx_pending_skb(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu, struct rtw89_rx_desc_info *desc_info, @@ -1106,10 +1305,7 @@ static void rtw89_core_rx_pending_skb(struct rtw89_dev *rtwdev, if (rtw89_core_rx_ppdu_match(rtwdev, desc_info, rx_status)) rtw89_chip_query_ppdu(rtwdev, phy_ppdu, rx_status); rtw89_correct_cck_chan(rtwdev, rx_status); - rtw89_core_hw_to_sband_rate(rx_status); - rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu); - ieee80211_rx_napi(rtwdev->hw, NULL, skb_ppdu, &rtwdev->napi); - rtwdev->napi_budget_countdown--; + rtw89_core_rx_to_mac80211(rtwdev, phy_ppdu, desc_info, skb_ppdu, rx_status); } } @@ -1250,6 +1446,7 @@ static void rtw89_core_update_rx_status(struct rtw89_dev *rtwdev, struct ieee80211_rx_status *rx_status) { struct ieee80211_hw *hw = rtwdev->hw; + struct rtw89_hal *hal = &rtwdev->hal; u16 data_rate; u8 data_rate_mode; @@ -1257,6 +1454,13 @@ static void rtw89_core_update_rx_status(struct rtw89_dev *rtwdev, rx_status->freq = hw->conf.chandef.chan->center_freq; rx_status->band = hw->conf.chandef.chan->band; + if (rtwdev->scanning && rtwdev->fw.scan_offload) { + rx_status->freq = + ieee80211_channel_to_frequency(hal->current_channel, + hal->current_band_type); + rx_status->band = rtwdev->hal.current_band_type; + } + if (desc_info->icv_err || desc_info->crc32_err) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; @@ -1264,12 +1468,7 @@ static void rtw89_core_update_rx_status(struct rtw89_dev *rtwdev, !(desc_info->sw_dec || desc_info->icv_err)) rx_status->flag |= RX_FLAG_DECRYPTED; - if (desc_info->bw == RTW89_CHANNEL_WIDTH_80) - rx_status->bw = RATE_INFO_BW_80; - else if (desc_info->bw == RTW89_CHANNEL_WIDTH_40) - rx_status->bw = RATE_INFO_BW_40; - else - rx_status->bw = RATE_INFO_BW_20; + rx_status->bw = rtw89_hw_to_rate_info_bw(desc_info->bw); data_rate = desc_info->data_rate; data_rate_mode = GET_DATA_RATE_MODE(data_rate); @@ -1334,10 +1533,7 @@ static void rtw89_core_flush_ppdu_rx_queue(struct rtw89_dev *rtwdev, skb_queue_walk_safe(&ppdu_sts->rx_queue[band], skb_ppdu, tmp) { skb_unlink(skb_ppdu, &ppdu_sts->rx_queue[band]); rx_status = IEEE80211_SKB_RXCB(skb_ppdu); - rtw89_core_hw_to_sband_rate(rx_status); - rtw89_core_rx_stats(rtwdev, NULL, desc_info, skb_ppdu); - ieee80211_rx_napi(rtwdev->hw, NULL, skb_ppdu, &rtwdev->napi); - rtwdev->napi_budget_countdown--; + rtw89_core_rx_to_mac80211(rtwdev, NULL, desc_info, skb_ppdu, rx_status); } } @@ -1364,14 +1560,10 @@ void rtw89_core_rx(struct rtw89_dev *rtwdev, memset(rx_status, 0, sizeof(*rx_status)); rtw89_core_update_rx_status(rtwdev, desc_info, rx_status); if (desc_info->long_rxdesc && - BIT(desc_info->frame_type) & PPDU_FILTER_BITMAP) { + BIT(desc_info->frame_type) & PPDU_FILTER_BITMAP) skb_queue_tail(&ppdu_sts->rx_queue[band], skb); - } else { - rtw89_core_hw_to_sband_rate(rx_status); - rtw89_core_rx_stats(rtwdev, NULL, desc_info, skb); - ieee80211_rx_napi(rtwdev->hw, NULL, skb, &rtwdev->napi); - rtwdev->napi_budget_countdown--; - } + else + rtw89_core_rx_to_mac80211(rtwdev, NULL, desc_info, skb, rx_status); } EXPORT_SYMBOL(rtw89_core_rx); @@ -1509,11 +1701,12 @@ static void rtw89_core_txq_push(struct rtw89_dev *rtwdev, unsigned long i; int ret; + rcu_read_lock(); for (i = 0; i < frame_cnt; i++) { skb = ieee80211_tx_dequeue_ni(rtwdev->hw, txq); if (!skb) { rtw89_debug(rtwdev, RTW89_DBG_TXRX, "dequeue a NULL skb\n"); - return; + goto out; } rtw89_core_txq_check_agg(rtwdev, rtwtxq, skb); ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, NULL); @@ -1523,6 +1716,8 @@ static void rtw89_core_txq_push(struct rtw89_dev *rtwdev, break; } } +out: + rcu_read_unlock(); } static u32 rtw89_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, u8 tid) @@ -1598,6 +1793,16 @@ static void rtw89_core_txq_schedule(struct rtw89_dev *rtwdev, u8 ac, bool *reinv ieee80211_txq_schedule_end(hw, ac); } +static void rtw89_ips_work(struct work_struct *work) +{ + struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, + ips_work); + + mutex_lock(&rtwdev->mutex); + rtw89_enter_ips(rtwdev); + mutex_unlock(&rtwdev->mutex); +} + static void rtw89_core_txq_work(struct work_struct *w) { struct rtw89_dev *rtwdev = container_of(w, struct rtw89_dev, txq_work); @@ -1770,6 +1975,51 @@ void rtw89_core_release_all_bits_map(unsigned long *addr, unsigned int nbits) bitmap_zero(addr, nbits); } +int rtw89_core_acquire_sta_ba_entry(struct rtw89_sta *rtwsta, u8 tid, u8 *cam_idx) +{ + struct rtw89_ba_cam_entry *entry; + u8 idx; + + idx = rtw89_core_acquire_bit_map(rtwsta->ba_cam_map, RTW89_BA_CAM_NUM); + if (idx == RTW89_BA_CAM_NUM) { + /* allocate a static BA CAM to tid=0, so replace the existing + * one if BA CAM is full. Hardware will process the original tid + * automatically. + */ + if (tid != 0) + return -ENOSPC; + + idx = 0; + } + + entry = &rtwsta->ba_cam_entry[idx]; + entry->tid = tid; + *cam_idx = idx; + + return 0; +} + +int rtw89_core_release_sta_ba_entry(struct rtw89_sta *rtwsta, u8 tid, u8 *cam_idx) +{ + struct rtw89_ba_cam_entry *entry; + int i; + + for (i = 0; i < RTW89_BA_CAM_NUM; i++) { + if (!test_bit(i, rtwsta->ba_cam_map)) + continue; + + entry = &rtwsta->ba_cam_entry[i]; + if (entry->tid != tid) + continue; + + rtw89_core_release_bit_map(rtwsta->ba_cam_map, i); + *cam_idx = i; + return 0; + } + + return -ENOENT; +} + #define RTW89_TYPE_MAPPING(_type) \ case NL80211_IFTYPE_ ## _type: \ rtwvif->wifi_role = RTW89_WIFI_ROLE_ ## _type; \ @@ -1838,6 +2088,9 @@ int rtw89_core_sta_add(struct rtw89_dev *rtwdev, rtw89_btc_ntfy_role_info(rtwdev, rtwvif, rtwsta, BTC_ROLE_MSTS_STA_CONN_START); rtw89_chip_rfk_channel(rtwdev); + } else if (vif->type == NL80211_IFTYPE_AP) { + rtwsta->mac_id = rtw89_core_acquire_bit_map(rtwdev->mac_id_map, + RTW89_MAX_MAC_ID_NUM); } return 0; @@ -1866,8 +2119,11 @@ int rtw89_core_sta_disconnect(struct rtw89_dev *rtwdev, rtw89_mac_bf_monitor_calc(rtwdev, sta, true); rtw89_mac_bf_disassoc(rtwdev, vif, sta); rtw89_core_free_sta_pending_ba(rtwdev, sta); + if (vif->type == NL80211_IFTYPE_AP) + rtw89_cam_deinit_addr_cam(rtwdev, &rtwsta->addr_cam); - rtw89_vif_type_mapping(vif, false); + if (vif->type == NL80211_IFTYPE_STATION) + rtw89_vif_type_mapping(vif, false); ret = rtw89_fw_h2c_assoc_cmac_tbl(rtwdev, vif, sta); if (ret) { @@ -1875,14 +2131,22 @@ int rtw89_core_sta_disconnect(struct rtw89_dev *rtwdev, return ret; } - ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif, 1); + ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif, rtwsta, true); if (ret) { rtw89_warn(rtwdev, "failed to send h2c join info\n"); return ret; } + if (vif->type == NL80211_IFTYPE_AP) { + ret = rtw89_fw_h2c_role_maintain(rtwdev, rtwvif, rtwsta, RTW89_ROLE_REMOVE); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c role info\n"); + return ret; + } + } + /* update cam aid mac_id net_type */ - rtw89_fw_h2c_cam(rtwdev, rtwvif, rtwsta, NULL); + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif, rtwsta, NULL); if (ret) { rtw89_warn(rtwdev, "failed to send h2c cam\n"); return ret; @@ -1899,7 +2163,25 @@ int rtw89_core_sta_assoc(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; int ret; - rtw89_vif_type_mapping(vif, true); + if (vif->type == NL80211_IFTYPE_AP) { + ret = rtw89_mac_set_macid_pause(rtwdev, rtwsta->mac_id, false); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c macid pause\n"); + return ret; + } + + ret = rtw89_fw_h2c_role_maintain(rtwdev, rtwvif, rtwsta, RTW89_ROLE_CREATE); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c role info\n"); + return ret; + } + + ret = rtw89_cam_init_addr_cam(rtwdev, &rtwsta->addr_cam, &rtwvif->bssid_cam); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c init addr cam\n"); + return ret; + } + } ret = rtw89_fw_h2c_assoc_cmac_tbl(rtwdev, vif, sta); if (ret) { @@ -1907,7 +2189,7 @@ int rtw89_core_sta_assoc(struct rtw89_dev *rtwdev, return ret; } - ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif, 0); + ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif, rtwsta, false); if (ret) { rtw89_warn(rtwdev, "failed to send h2c join info\n"); return ret; @@ -1950,6 +2232,8 @@ int rtw89_core_sta_remove(struct rtw89_dev *rtwdev, if (vif->type == NL80211_IFTYPE_STATION) rtw89_btc_ntfy_role_info(rtwdev, rtwvif, rtwsta, BTC_ROLE_MSTS_STA_DIS_CONN); + else if (vif->type == NL80211_IFTYPE_AP) + rtw89_core_release_bit_map(rtwdev->mac_id_map, rtwsta->mac_id); return 0; } @@ -1986,9 +2270,14 @@ static void rtw89_init_ht_cap(struct rtw89_dev *rtwdev, static void rtw89_init_vht_cap(struct rtw89_dev *rtwdev, struct ieee80211_sta_vht_cap *vht_cap) { - static const __le16 highest[RF_PATH_MAX] = { + static const __le16 highest_bw80[RF_PATH_MAX] = { cpu_to_le16(433), cpu_to_le16(867), cpu_to_le16(1300), cpu_to_le16(1733), }; + static const __le16 highest_bw160[RF_PATH_MAX] = { + cpu_to_le16(867), cpu_to_le16(1733), cpu_to_le16(2600), cpu_to_le16(3467), + }; + const struct rtw89_chip_info *chip = rtwdev->chip; + const __le16 *highest = chip->support_bw160 ? highest_bw160 : highest_bw80; struct rtw89_hal *hal = &rtwdev->hal; u16 tx_mcs_map = 0, rx_mcs_map = 0; u8 sts_cap = 3; @@ -2017,6 +2306,9 @@ static void rtw89_init_vht_cap(struct rtw89_dev *rtwdev, vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; vht_cap->cap |= sts_cap << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; + if (chip->support_bw160) + vht_cap->cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | + IEEE80211_VHT_CAP_SHORT_GI_160; vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(rx_mcs_map); vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(tx_mcs_map); vht_cap->vht_mcs.rx_highest = highest[hal->rx_nss - 1]; @@ -2087,8 +2379,15 @@ static void rtw89_init_he_cap(struct rtw89_dev *rtwdev, IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU; if (i == NL80211_IFTYPE_STATION) mac_cap_info[5] = IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX; - phy_cap_info[0] = IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; + if (band == NL80211_BAND_2GHZ) { + phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + } else { + phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; + if (chip->support_bw160) + phy_cap_info[0] |= IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + } phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US; @@ -2117,6 +2416,9 @@ static void rtw89_init_he_cap(struct rtw89_dev *rtwdev, phy_cap_info[8] = IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI | IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI | IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996; + if (chip->support_bw160) + phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU; phy_cap_info[9] = IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM | IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU | IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | @@ -2127,6 +2429,22 @@ static void rtw89_init_he_cap(struct rtw89_dev *rtwdev, phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU; he_cap->he_mcs_nss_supp.rx_mcs_80 = cpu_to_le16(mcs_map); he_cap->he_mcs_nss_supp.tx_mcs_80 = cpu_to_le16(mcs_map); + if (chip->support_bw160) { + he_cap->he_mcs_nss_supp.rx_mcs_160 = cpu_to_le16(mcs_map); + he_cap->he_mcs_nss_supp.tx_mcs_160 = cpu_to_le16(mcs_map); + } + + if (band == NL80211_BAND_6GHZ) { + __le16 capa; + + capa = le16_encode_bits(IEEE80211_HT_MPDU_DENSITY_NONE, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START) | + le16_encode_bits(IEEE80211_VHT_MAX_AMPDU_1024K, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP) | + le16_encode_bits(IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454, + IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN); + iftype_data[idx].he_6ghz_capa.capa = capa; + } idx++; } @@ -2139,34 +2457,52 @@ static int rtw89_core_set_supported_band(struct rtw89_dev *rtwdev) { struct ieee80211_hw *hw = rtwdev->hw; struct ieee80211_supported_band *sband_2ghz = NULL, *sband_5ghz = NULL; + struct ieee80211_supported_band *sband_6ghz = NULL; u32 size = sizeof(struct ieee80211_supported_band); + u8 support_bands = rtwdev->chip->support_bands; - sband_2ghz = kmemdup(&rtw89_sband_2ghz, size, GFP_KERNEL); - if (!sband_2ghz) - goto err; - rtw89_init_ht_cap(rtwdev, &sband_2ghz->ht_cap); - rtw89_init_he_cap(rtwdev, NL80211_BAND_2GHZ, sband_2ghz); - hw->wiphy->bands[NL80211_BAND_2GHZ] = sband_2ghz; + if (support_bands & BIT(NL80211_BAND_2GHZ)) { + sband_2ghz = kmemdup(&rtw89_sband_2ghz, size, GFP_KERNEL); + if (!sband_2ghz) + goto err; + rtw89_init_ht_cap(rtwdev, &sband_2ghz->ht_cap); + rtw89_init_he_cap(rtwdev, NL80211_BAND_2GHZ, sband_2ghz); + hw->wiphy->bands[NL80211_BAND_2GHZ] = sband_2ghz; + } - sband_5ghz = kmemdup(&rtw89_sband_5ghz, size, GFP_KERNEL); - if (!sband_5ghz) - goto err; - rtw89_init_ht_cap(rtwdev, &sband_5ghz->ht_cap); - rtw89_init_vht_cap(rtwdev, &sband_5ghz->vht_cap); - rtw89_init_he_cap(rtwdev, NL80211_BAND_5GHZ, sband_5ghz); - hw->wiphy->bands[NL80211_BAND_5GHZ] = sband_5ghz; + if (support_bands & BIT(NL80211_BAND_5GHZ)) { + sband_5ghz = kmemdup(&rtw89_sband_5ghz, size, GFP_KERNEL); + if (!sband_5ghz) + goto err; + rtw89_init_ht_cap(rtwdev, &sband_5ghz->ht_cap); + rtw89_init_vht_cap(rtwdev, &sband_5ghz->vht_cap); + rtw89_init_he_cap(rtwdev, NL80211_BAND_5GHZ, sband_5ghz); + hw->wiphy->bands[NL80211_BAND_5GHZ] = sband_5ghz; + } + + if (support_bands & BIT(NL80211_BAND_6GHZ)) { + sband_6ghz = kmemdup(&rtw89_sband_6ghz, size, GFP_KERNEL); + if (!sband_6ghz) + goto err; + rtw89_init_he_cap(rtwdev, NL80211_BAND_6GHZ, sband_6ghz); + hw->wiphy->bands[NL80211_BAND_6GHZ] = sband_6ghz; + } return 0; err: hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL; hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL; + hw->wiphy->bands[NL80211_BAND_6GHZ] = NULL; if (sband_2ghz) kfree(sband_2ghz->iftype_data); if (sband_5ghz) kfree(sband_5ghz->iftype_data); + if (sband_6ghz) + kfree(sband_6ghz->iftype_data); kfree(sband_2ghz); kfree(sband_5ghz); + kfree(sband_6ghz); return -ENOMEM; } @@ -2176,10 +2512,14 @@ static void rtw89_core_clr_supported_band(struct rtw89_dev *rtwdev) kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]->iftype_data); kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]->iftype_data); + if (hw->wiphy->bands[NL80211_BAND_6GHZ]) + kfree(hw->wiphy->bands[NL80211_BAND_6GHZ]->iftype_data); kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]); kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]); + kfree(hw->wiphy->bands[NL80211_BAND_6GHZ]); hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL; hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL; + hw->wiphy->bands[NL80211_BAND_6GHZ] = NULL; } static void rtw89_core_ppdu_sts_init(struct rtw89_dev *rtwdev) @@ -2192,6 +2532,21 @@ static void rtw89_core_ppdu_sts_init(struct rtw89_dev *rtwdev) rtwdev->ppdu_sts.curr_rx_ppdu_cnt[i] = U8_MAX; } +void rtw89_core_update_beacon_work(struct work_struct *work) +{ + struct rtw89_dev *rtwdev; + struct rtw89_vif *rtwvif = container_of(work, struct rtw89_vif, + update_beacon_work); + + if (rtwvif->net_type != RTW89_NET_TYPE_AP_MODE) + return; + + rtwdev = rtwvif->rtwdev; + mutex_lock(&rtwdev->mutex); + rtw89_fw_h2c_update_beacon(rtwdev, rtwvif); + mutex_unlock(&rtwdev->mutex); +} + int rtw89_core_start(struct rtw89_dev *rtwdev) { int ret; @@ -2278,10 +2633,16 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; int ret; + u8 band; INIT_LIST_HEAD(&rtwdev->ba_list); INIT_LIST_HEAD(&rtwdev->rtwvifs_list); INIT_LIST_HEAD(&rtwdev->early_h2c_list); + for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { + if (!(rtwdev->chip->support_bands & BIT(band))) + continue; + INIT_LIST_HEAD(&rtwdev->scan_info.pkt_list[band]); + } INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work); INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work); INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work); @@ -2292,11 +2653,13 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) INIT_DELAYED_WORK(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); rtwdev->txq_wq = alloc_workqueue("rtw89_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0); spin_lock_init(&rtwdev->ba_lock); + spin_lock_init(&rtwdev->rpwm_lock); mutex_init(&rtwdev->mutex); mutex_init(&rtwdev->rf_mutex); rtwdev->total_sta_assoc = 0; INIT_WORK(&rtwdev->c2h_work, rtw89_fw_c2h_work); + INIT_WORK(&rtwdev->ips_work, rtw89_ips_work); skb_queue_head_init(&rtwdev->c2h_queue); rtw89_core_ppdu_sts_init(rtwdev); rtw89_traffic_stats_init(rtwdev, &rtwdev->stats); @@ -2332,12 +2695,48 @@ void rtw89_core_deinit(struct rtw89_dev *rtwdev) } EXPORT_SYMBOL(rtw89_core_deinit); +void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, + const u8 *mac_addr, bool hw_scan) +{ + struct rtw89_hal *hal = &rtwdev->hal; + + rtwdev->scanning = true; + rtw89_leave_lps(rtwdev); + if (hw_scan && rtwvif->net_type == RTW89_NET_TYPE_NO_LINK) + rtw89_leave_ips(rtwdev); + + ether_addr_copy(rtwvif->mac_addr, mac_addr); + rtw89_btc_ntfy_scan_start(rtwdev, RTW89_PHY_0, hal->current_band_type); + rtw89_chip_rfk_scan(rtwdev, true); + rtw89_hci_recalc_int_mit(rtwdev); + + rtw89_fw_h2c_cam(rtwdev, rtwvif, NULL, mac_addr); +} + +void rtw89_core_scan_complete(struct rtw89_dev *rtwdev, + struct ieee80211_vif *vif, bool hw_scan) +{ + struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + + ether_addr_copy(rtwvif->mac_addr, vif->addr); + rtw89_fw_h2c_cam(rtwdev, rtwvif, NULL, NULL); + + rtw89_chip_rfk_scan(rtwdev, false); + rtw89_btc_ntfy_scan_finish(rtwdev, RTW89_PHY_0); + + rtwdev->scanning = false; + rtwdev->dig.bypass_dig = true; + if (hw_scan && rtwvif->net_type == RTW89_NET_TYPE_NO_LINK) + ieee80211_queue_work(rtwdev->hw, &rtwdev->ips_work); +} + static void rtw89_read_chip_ver(struct rtw89_dev *rtwdev) { + const struct rtw89_chip_info *chip = rtwdev->chip; u8 cv; cv = rtw89_read32_mask(rtwdev, R_AX_SYS_CFG1, B_AX_CHIP_VER_MASK); - if (cv <= CHIP_CBV) { + if (chip->chip_id == RTL8852A && cv <= CHIP_CBV) { if (rtw89_read32(rtwdev, R_AX_GPIO0_7_FUNC_SEL) == RTW89_R32_DEAD) cv = CHIP_CAV; else @@ -2347,6 +2746,13 @@ static void rtw89_read_chip_ver(struct rtw89_dev *rtwdev) rtwdev->hal.cv = cv; } +static void rtw89_core_setup_phycap(struct rtw89_dev *rtwdev) +{ + rtwdev->hal.support_cckpd = + !(rtwdev->chip->chip_id == RTL8852A && rtwdev->hal.cv <= CHIP_CBV) && + !(rtwdev->chip->chip_id == RTL8852B && rtwdev->hal.cv <= CHIP_CAV); +} + static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev) { int ret; @@ -2367,6 +2773,8 @@ static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_core_setup_phycap(rtwdev); + rtw89_mac_pwr_off(rtwdev); return 0; @@ -2438,13 +2846,18 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); ieee80211_hw_set(hw, SUPPORTS_PS); ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); - hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP); hw->wiphy->available_antennas_tx = BIT(rtwdev->chip->rf_path_num) - 1; hw->wiphy->available_antennas_rx = BIT(rtwdev->chip->rf_path_num) - 1; hw->wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + hw->wiphy->max_scan_ssids = RTW89_SCANOFLD_MAX_SSID; + hw->wiphy->max_scan_ie_len = RTW89_SCANOFLD_MAX_IE_LEN; + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0); ret = rtw89_core_set_supported_band(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 7c84556ec4ad..771722132c53 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -13,9 +13,9 @@ #include struct rtw89_dev; +struct rtw89_pci_info; extern const struct ieee80211_ops rtw89_ops; -extern const struct rtw89_chip_info rtw8852a_chip_info; #define MASKBYTE0 0xff #define MASKBYTE1 0xff00 @@ -33,7 +33,6 @@ extern const struct rtw89_chip_info rtw8852a_chip_info; #define MAX_RSSI 110 #define RSSI_FACTOR 1 #define RTW89_RSSI_RAW_TO_DBM(rssi) ((s8)((rssi) >> RSSI_FACTOR) - MAX_RSSI) -#define RTW89_MAX_HW_PORT_NUM 5 #define RTW89_HTC_MASK_VARIANT GENMASK(1, 0) #define RTW89_HTC_VARIANT_HE 3 @@ -63,6 +62,15 @@ enum rtw89_subband { RTW89_CH_5G_BAND_3 = 3, RTW89_CH_5G_BAND_4 = 4, + RTW89_CH_6G_BAND_IDX0, /* Low */ + RTW89_CH_6G_BAND_IDX1, /* Low */ + RTW89_CH_6G_BAND_IDX2, /* Mid */ + RTW89_CH_6G_BAND_IDX3, /* Mid */ + RTW89_CH_6G_BAND_IDX4, /* High */ + RTW89_CH_6G_BAND_IDX5, /* High */ + RTW89_CH_6G_BAND_IDX6, /* Ultra-high */ + RTW89_CH_6G_BAND_IDX7, /* Ultra-high */ + RTW89_SUBBAND_NR, }; @@ -140,11 +148,11 @@ enum rtw89_wifi_role { }; enum rtw89_upd_mode { - RTW89_VIF_CREATE, - RTW89_VIF_REMOVE, - RTW89_VIF_TYPE_CHANGE, - RTW89_VIF_INFO_CHANGE, - RTW89_VIF_CON_DISCONN + RTW89_ROLE_CREATE, + RTW89_ROLE_REMOVE, + RTW89_ROLE_TYPE_CHANGE, + RTW89_ROLE_INFO_CHANGE, + RTW89_ROLE_CON_DISCONN }; enum rtw89_self_role { @@ -205,6 +213,7 @@ enum rtw89_port { enum rtw89_band { RTW89_BAND_2G = 0, RTW89_BAND_5G = 1, + RTW89_BAND_6G = 2, RTW89_BAND_MAX, }; @@ -363,6 +372,25 @@ enum rtw89_hw_rate { */ #define RTW89_5G_CH_NUM 53 +/* 6G channels, + * 1, 3, 5, 7, 9, 11, 13, 15, + * 17, 19, 21, 23, 25, 27, 29, 33, + * 35, 37, 39, 41, 43, 45, 47, 49, + * 51, 53, 55, 57, 59, 61, 65, 67, + * 69, 71, 73, 75, 77, 79, 81, 83, + * 85, 87, 89, 91, 93, 97, 99, 101, + * 103, 105, 107, 109, 111, 113, 115, 117, + * 119, 121, 123, 125, 129, 131, 133, 135, + * 137, 139, 141, 143, 145, 147, 149, 151, + * 153, 155, 157, 161, 163, 165, 167, 169, + * 171, 173, 175, 177, 179, 181, 183, 185, + * 187, 189, 193, 195, 197, 199, 201, 203, + * 205, 207, 209, 211, 213, 215, 217, 219, + * 221, 225, 227, 229, 231, 233, 235, 237, + * 239, 241, 243, 245, 247, 249, 251, 253, + */ +#define RTW89_6G_CH_NUM 120 + enum rtw89_rate_section { RTW89_RS_CCK, RTW89_RS_OFDM, @@ -421,9 +449,6 @@ enum rtw89_regulation_type { RTW89_REGD_NUM, }; -extern const u8 rtw89_rs_idx_max[RTW89_RS_MAX]; -extern const u8 rtw89_rs_nss_max[RTW89_RS_MAX]; - struct rtw89_txpwr_byrate { s8 cck[RTW89_RATE_CCK_MAX]; s8 ofdm[RTW89_RATE_OFDM_MAX]; @@ -548,7 +573,8 @@ enum rtw89_ps_mode { }; #define RTW89_2G_BW_NUM (RTW89_CHANNEL_WIDTH_40 + 1) -#define RTW89_5G_BW_NUM (RTW89_CHANNEL_WIDTH_80 + 1) +#define RTW89_5G_BW_NUM (RTW89_CHANNEL_WIDTH_160 + 1) +#define RTW89_6G_BW_NUM (RTW89_CHANNEL_WIDTH_160 + 1) #define RTW89_PPE_BW_NUM (RTW89_CHANNEL_WIDTH_80 + 1) enum rtw89_ru_bandwidth { @@ -564,19 +590,26 @@ enum rtw89_sc_offset { RTW89_SC_20_LOWER = 2, RTW89_SC_20_UPMOST = 3, RTW89_SC_20_LOWEST = 4, + RTW89_SC_20_UP2X = 5, + RTW89_SC_20_LOW2X = 6, + RTW89_SC_20_UP3X = 7, + RTW89_SC_20_LOW3X = 8, RTW89_SC_40_UPPER = 9, RTW89_SC_40_LOWER = 10, }; struct rtw89_channel_params { u8 center_chan; + u32 center_freq; u8 primary_chan; u8 bandwidth; u8 pri_ch_idx; + u8 band_type; + u8 subband_type; }; struct rtw89_channel_help_params { - u16 tx_en; + u32 tx_en; }; struct rtw89_port_reg { @@ -670,6 +703,7 @@ struct rtw89_rxdesc_long { struct rtw89_tx_desc_info { u16 pkt_size; u8 wp_offset; + u8 mac_id; u8 qsel; u8 ch_dma; u8 hdr_llc_len; @@ -691,6 +725,12 @@ struct rtw89_tx_desc_info { bool fw_dl; u16 seq; bool a_ctrl_bsr; + u8 hw_ssn_sel; +#define RTW89_MGMT_HW_SSN_SEL 1 + u8 hw_seq_mode; +#define RTW89_MGMT_HW_SEQ_MODE 1 + bool hiq; + u8 port; }; struct rtw89_core_tx_request { @@ -1048,7 +1088,7 @@ struct rtw89_btc_wl_role_info { /* struct size must be n*4 bytes */ u8 connect_cnt; u8 link_mode; union rtw89_btc_wl_role_info_map role_map; - struct rtw89_btc_wl_active_role active_role[RTW89_MAX_HW_PORT_NUM]; + struct rtw89_btc_wl_active_role active_role[RTW89_PORT_NUM]; }; struct rtw89_btc_wl_ver_info { @@ -1151,7 +1191,7 @@ struct rtw89_btc_rf_para { }; struct rtw89_btc_wl_info { - struct rtw89_btc_wl_link_info link_info[RTW89_MAX_HW_PORT_NUM]; + struct rtw89_btc_wl_link_info link_info[RTW89_PORT_NUM]; struct rtw89_btc_wl_rfk_info rfk_info; struct rtw89_btc_wl_ver_info ver_info; struct rtw89_btc_wl_afh_info afh_info; @@ -1831,27 +1871,10 @@ struct rtw89_ra_report { DECLARE_EWMA(rssi, 10, 16); -struct rtw89_sta { - u8 mac_id; - bool disassoc; - struct rtw89_vif *rtwvif; - struct rtw89_ra_info ra; - struct rtw89_ra_report ra_report; - int max_agg_wait; - u8 prev_rssi; - struct ewma_rssi avg_rssi; - struct rtw89_ampdu_params ampdu_params[IEEE80211_NUM_TIDS]; - struct ieee80211_rx_status rx_status; - u16 rx_hw_rate; - __le32 htc_template; +#define RTW89_BA_CAM_NUM 2 - bool use_cfg_mask; - struct cfg80211_bitrate_mask mask; - - bool cctl_tx_time; - u32 ampdu_max_time:4; - bool cctl_tx_retry_limit; - u32 data_tx_cnt_lmt:6; +struct rtw89_ba_cam_entry { + u8 tid; }; #define RTW89_MAX_ADDR_CAM_NUM 128 @@ -1868,7 +1891,6 @@ struct rtw89_addr_cam_entry { u8 wapi : 1; u8 mask_sel : 2; u8 bssid_cam_idx: 6; - u8 sma[ETH_ALEN]; u8 sec_ent_mode; DECLARE_BITMAP(sec_cam_map, RTW89_SEC_CAM_IN_ADDR_CAM); @@ -1898,6 +1920,33 @@ struct rtw89_sec_cam_entry { u8 key[32]; }; +struct rtw89_sta { + u8 mac_id; + bool disassoc; + struct rtw89_vif *rtwvif; + struct rtw89_ra_info ra; + struct rtw89_ra_report ra_report; + int max_agg_wait; + u8 prev_rssi; + struct ewma_rssi avg_rssi; + struct rtw89_ampdu_params ampdu_params[IEEE80211_NUM_TIDS]; + struct ieee80211_rx_status rx_status; + u16 rx_hw_rate; + __le32 htc_template; + struct rtw89_addr_cam_entry addr_cam; /* AP mode only */ + + bool use_cfg_mask; + struct cfg80211_bitrate_mask mask; + + bool cctl_tx_time; + u32 ampdu_max_time:4; + bool cctl_tx_retry_limit; + u32 data_tx_cnt_lmt:6; + + DECLARE_BITMAP(ba_cam_map, RTW89_BA_CAM_NUM); + struct rtw89_ba_cam_entry ba_cam_entry[RTW89_BA_CAM_NUM]; +}; + struct rtw89_efuse { bool valid; u8 xtal_cap; @@ -1915,6 +1964,7 @@ struct rtw89_phy_rate_pattern { struct rtw89_vif { struct list_head list; + struct rtw89_dev *rtwdev; u8 mac_id; u8 port; u8 mac_addr[ETH_ALEN]; @@ -1936,11 +1986,14 @@ struct rtw89_vif { bool wowlan_magic; bool is_hesta; bool last_a_ctrl; + struct work_struct update_beacon_work; struct rtw89_addr_cam_entry addr_cam; struct rtw89_bssid_cam_entry bssid_cam; struct ieee80211_tx_queue_params tx_params[IEEE80211_NUM_ACS]; struct rtw89_traffic_stats stats; struct rtw89_phy_rate_pattern rate_pattern; + struct cfg80211_scan_request *scan_req; + struct ieee80211_scan_ies *scan_ies; }; enum rtw89_lv1_rcvy_step { @@ -2012,7 +2065,15 @@ struct rtw89_chip_ops { struct ieee80211_rx_status *status); void (*bb_ctrl_btc_preagc)(struct rtw89_dev *rtwdev, bool bt_en); void (*set_txpwr_ul_tb_offset)(struct rtw89_dev *rtwdev, - s16 pw_ofst, enum rtw89_mac_idx mac_idx); + s8 pw_ofst, enum rtw89_mac_idx mac_idx); + int (*pwr_on_func)(struct rtw89_dev *rtwdev); + int (*pwr_off_func)(struct rtw89_dev *rtwdev); + int (*cfg_ctrl_path)(struct rtw89_dev *rtwdev, bool wl); + int (*mac_cfg_gnt)(struct rtw89_dev *rtwdev, + const struct rtw89_mac_ax_coex_gnt *gnt_cfg); + int (*stop_sch_tx)(struct rtw89_dev *rtwdev, u8 mac_idx, + u32 *tx_en, enum rtw89_sch_tx_sel sel); + int (*resume_sch_tx)(struct rtw89_dev *rtwdev, u8 mac_idx, u32 tx_en); void (*btc_set_rfe)(struct rtw89_dev *rtwdev); void (*btc_init_cfg)(struct rtw89_dev *rtwdev); @@ -2133,6 +2194,7 @@ struct rtw89_ple_quota { u16 bb_rpt; u16 wd_rel; u16 cpu_io; + u16 tx_rpt; }; struct rtw89_dle_mem { @@ -2173,6 +2235,8 @@ struct rtw89_phy_table { const struct rtw89_reg2_def *regs; u32 n_regs; enum rtw89_rf_path rf_path; + void (*config)(struct rtw89_dev *rtwdev, const struct rtw89_reg2_def *reg, + enum rtw89_rf_path rf_path, void *data); }; struct rtw89_txpwr_table { @@ -2182,6 +2246,21 @@ struct rtw89_txpwr_table { const struct rtw89_txpwr_table *tbl); }; +struct rtw89_page_regs { + u32 hci_fc_ctrl; + u32 ch_page_ctrl; + u32 ach_page_ctrl; + u32 ach_page_info; + u32 pub_page_info3; + u32 pub_page_ctrl1; + u32 pub_page_ctrl2; + u32 pub_page_info1; + u32 pub_page_info2; + u32 wp_page_ctrl1; + u32 wp_page_ctrl2; + u32 wp_page_info1; +}; + struct rtw89_chip_info { enum rtw89_core_chip_id chip_id; const struct rtw89_chip_ops *ops; @@ -2192,6 +2271,8 @@ struct rtw89_chip_info { const struct rtw89_hfc_param_ini *hfc_param_ini; const struct rtw89_dle_mem *dle_mem; u32 rf_base_addr[2]; + u8 support_bands; + bool support_bw160; u8 rf_path_num; u8 tx_nss; u8 rx_nss; @@ -2203,6 +2284,8 @@ struct rtw89_chip_info { u32 physical_efuse_size; u32 logical_efuse_size; u32 limit_efuse_size; + u32 dav_phy_efuse_size; + u32 dav_log_efuse_size; u32 phycap_addr; u32 phycap_size; @@ -2219,10 +2302,15 @@ struct rtw89_chip_info { const s8 (*txpwr_lmt_5g)[RTW89_5G_BW_NUM][RTW89_NTX_NUM] [RTW89_RS_LMT_NUM][RTW89_BF_NUM] [RTW89_REGD_NUM][RTW89_5G_CH_NUM]; + const s8 (*txpwr_lmt_6g)[RTW89_6G_BW_NUM][RTW89_NTX_NUM] + [RTW89_RS_LMT_NUM][RTW89_BF_NUM] + [RTW89_REGD_NUM][RTW89_6G_CH_NUM]; const s8 (*txpwr_lmt_ru_2g)[RTW89_RU_NUM][RTW89_NTX_NUM] [RTW89_REGD_NUM][RTW89_2G_CH_NUM]; const s8 (*txpwr_lmt_ru_5g)[RTW89_RU_NUM][RTW89_NTX_NUM] [RTW89_REGD_NUM][RTW89_5G_CH_NUM]; + const s8 (*txpwr_lmt_ru_6g)[RTW89_RU_NUM][RTW89_NTX_NUM] + [RTW89_REGD_NUM][RTW89_6G_CH_NUM]; u8 txpwr_factor_rf; u8 txpwr_factor_mac; @@ -2245,6 +2333,24 @@ struct rtw89_chip_info { u8 rf_para_dlink_num; const struct rtw89_btc_rf_trx_para *rf_para_dlink; u8 ps_mode_supported; + + u32 hci_func_en_addr; + u32 h2c_ctrl_reg; + const u32 *h2c_regs; + u32 c2h_ctrl_reg; + const u32 *c2h_regs; + const struct rtw89_page_regs *page_regs; + const struct rtw89_reg_def *dcfo_comp; + u8 dcfo_comp_sft; +}; + +union rtw89_bus_info { + const struct rtw89_pci_info *pci; +}; + +struct rtw89_driver_info { + const struct rtw89_chip_info *chip; + union rtw89_bus_info bus; }; enum rtw89_hcifc_mode { @@ -2312,6 +2418,8 @@ struct rtw89_fw_info { struct rtw89_fw_suit wowlan; bool fw_log_enable; bool old_ht_ra_format; + bool scan_offload; + bool tx_wake; }; struct rtw89_cam_info { @@ -2348,19 +2456,23 @@ struct rtw89_hal { u32 rx_fltr; u8 cv; u8 current_channel; + u32 current_freq; u8 prev_primary_channel; u8 current_primary_channel; enum rtw89_subband current_subband; u8 current_band_width; + u8 prev_band_type; u8 current_band_type; u32 sw_amsdu_max_size; u32 antenna_tx; u32 antenna_rx; u8 tx_nss; u8 rx_nss; + bool support_cckpd; }; #define RTW89_MAX_MAC_ID_NUM 128 +#define RTW89_MAX_PKT_OFLD_NUM 255 enum rtw89_flags { RTW89_FLAG_POWERON, @@ -2556,22 +2668,30 @@ struct rtw89_cfo_tracking_info { s32 residual_cfo_acc; u8 phy_cfotrk_state; u8 phy_cfotrk_cnt; + bool divergence_lock_en; + u8 x_cap_lb; + u8 x_cap_ub; + u8 lock_cnt; }; /* 2GL, 2GH, 5GL1, 5GH1, 5GM1, 5GM2, 5GH1, 5GH2 */ #define TSSI_TRIM_CH_GROUP_NUM 8 +#define TSSI_TRIM_CH_GROUP_NUM_6G 16 #define TSSI_CCK_CH_GROUP_NUM 6 #define TSSI_MCS_2G_CH_GROUP_NUM 5 #define TSSI_MCS_5G_CH_GROUP_NUM 14 +#define TSSI_MCS_6G_CH_GROUP_NUM 32 #define TSSI_MCS_CH_GROUP_NUM \ (TSSI_MCS_2G_CH_GROUP_NUM + TSSI_MCS_5G_CH_GROUP_NUM) struct rtw89_tssi_info { u8 thermal[RF_PATH_MAX]; s8 tssi_trim[RF_PATH_MAX][TSSI_TRIM_CH_GROUP_NUM]; + s8 tssi_trim_6g[RF_PATH_MAX][TSSI_TRIM_CH_GROUP_NUM_6G]; s8 tssi_cck[RF_PATH_MAX][TSSI_CCK_CH_GROUP_NUM]; s8 tssi_mcs[RF_PATH_MAX][TSSI_MCS_CH_GROUP_NUM]; + s8 tssi_6g_mcs[RF_PATH_MAX][TSSI_MCS_6G_CH_GROUP_NUM]; s8 extra_ofst[RF_PATH_MAX]; bool tssi_tracking_check[RF_PATH_MAX]; u8 default_txagc_offset[RF_PATH_MAX]; @@ -2769,12 +2889,23 @@ struct rtw89_early_h2c { u16 h2c_len; }; +struct rtw89_hw_scan_info { + struct ieee80211_vif *scanning_vif; + struct list_head pkt_list[NUM_NL80211_BANDS]; + u8 op_pri_ch; + u8 op_chan; + u8 op_bw; + u8 op_band; +}; + struct rtw89_dev { struct ieee80211_hw *hw; struct device *dev; bool dbcc_en; + struct rtw89_hw_scan_info scan_info; const struct rtw89_chip_info *chip; + const struct rtw89_pci_info *pci_info; struct rtw89_hal hal; struct rtw89_mac_info mac; struct rtw89_fw_info fw; @@ -2795,19 +2926,23 @@ struct rtw89_dev { /* txqs to setup ba session */ struct list_head ba_list; struct work_struct ba_work; + /* used to protect rpwm */ + spinlock_t rpwm_lock; struct rtw89_cam_info cam_info; struct sk_buff_head c2h_queue; struct work_struct c2h_work; + struct work_struct ips_work; struct list_head early_h2c_list; struct rtw89_ser ser; - DECLARE_BITMAP(hw_port, RTW89_MAX_HW_PORT_NUM); + DECLARE_BITMAP(hw_port, RTW89_PORT_NUM); DECLARE_BITMAP(mac_id_map, RTW89_MAX_MAC_ID_NUM); DECLARE_BITMAP(flags, NUM_OF_RTW89_FLAGS); + DECLARE_BITMAP(pkt_offload, RTW89_MAX_PKT_OFLD_NUM); struct rtw89_phy_stat phystat; struct rtw89_dack_info dack; @@ -2847,7 +2982,7 @@ struct rtw89_dev { int napi_budget_countdown; /* HCI related data, keep last */ - u8 priv[0] __aligned(sizeof(void *)); + u8 priv[] __aligned(sizeof(void *)); }; static inline int rtw89_hci_tx_write(struct rtw89_dev *rtwdev, @@ -3128,6 +3263,46 @@ static inline struct rtw89_sta *sta_to_rtwsta_safe(struct ieee80211_sta *sta) return sta ? (struct rtw89_sta *)sta->drv_priv : NULL; } +static inline u8 rtw89_hw_to_rate_info_bw(enum rtw89_bandwidth hw_bw) +{ + if (hw_bw == RTW89_CHANNEL_WIDTH_160) + return RATE_INFO_BW_160; + else if (hw_bw == RTW89_CHANNEL_WIDTH_80) + return RATE_INFO_BW_80; + else if (hw_bw == RTW89_CHANNEL_WIDTH_40) + return RATE_INFO_BW_40; + else + return RATE_INFO_BW_20; +} + +static inline +enum rtw89_bandwidth nl_to_rtw89_bandwidth(enum nl80211_chan_width width) +{ + switch (width) { + default: + WARN(1, "Not support bandwidth %d\n", width); + fallthrough; + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + return RTW89_CHANNEL_WIDTH_20; + case NL80211_CHAN_WIDTH_40: + return RTW89_CHANNEL_WIDTH_40; + case NL80211_CHAN_WIDTH_80: + return RTW89_CHANNEL_WIDTH_80; + case NL80211_CHAN_WIDTH_160: + return RTW89_CHANNEL_WIDTH_160; + } +} + +static inline +struct rtw89_addr_cam_entry *rtw89_get_addr_cam_of(struct rtw89_vif *rtwvif, + struct rtw89_sta *rtwsta) +{ + if (rtwvif->net_type == RTW89_NET_TYPE_AP_MODE && rtwsta) + return &rtwsta->addr_cam; + return &rtwvif->addr_cam; +} + static inline void rtw89_chip_set_channel_prepare(struct rtw89_dev *rtwdev, struct rtw89_channel_help_params *p) @@ -3298,6 +3473,39 @@ static inline void rtw89_ctrl_btg(struct rtw89_dev *rtwdev, bool btg) chip->ops->ctrl_btg(rtwdev, btg); } +static inline +void rtw89_chip_mac_cfg_gnt(struct rtw89_dev *rtwdev, + const struct rtw89_mac_ax_coex_gnt *gnt_cfg) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + chip->ops->mac_cfg_gnt(rtwdev, gnt_cfg); +} + +static inline void rtw89_chip_cfg_ctrl_path(struct rtw89_dev *rtwdev, bool wl) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + chip->ops->cfg_ctrl_path(rtwdev, wl); +} + +static inline +int rtw89_chip_stop_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, + u32 *tx_en, enum rtw89_sch_tx_sel sel) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + return chip->ops->stop_sch_tx(rtwdev, mac_idx, tx_en, sel); +} + +static inline +int rtw89_chip_resume_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, u32 tx_en) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + return chip->ops->resume_sch_tx(rtwdev, mac_idx, tx_en); +} + static inline u8 *get_hdr_bssid(struct ieee80211_hdr *hdr) { __le16 fc = hdr->frame_control; @@ -3371,6 +3579,8 @@ void rtw89_set_channel(struct rtw89_dev *rtwdev); u8 rtw89_core_acquire_bit_map(unsigned long *addr, unsigned long size); void rtw89_core_release_bit_map(unsigned long *addr, u8 bit); void rtw89_core_release_all_bits_map(unsigned long *addr, unsigned int nbits); +int rtw89_core_acquire_sta_ba_entry(struct rtw89_sta *rtwsta, u8 tid, u8 *cam_idx); +int rtw89_core_release_sta_ba_entry(struct rtw89_sta *rtwsta, u8 tid, u8 *cam_idx); void rtw89_vif_type_mapping(struct ieee80211_vif *vif, bool assoc); int rtw89_chip_info_setup(struct rtw89_dev *rtwdev); u16 rtw89_ra_report_to_bitrate(struct rtw89_dev *rtwdev, u8 rpt_rate); @@ -3381,5 +3591,10 @@ void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev, struct rtw89_traffic_stats *stats); int rtw89_core_start(struct rtw89_dev *rtwdev); void rtw89_core_stop(struct rtw89_dev *rtwdev); +void rtw89_core_update_beacon_work(struct work_struct *work); +void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, + const u8 *mac_addr, bool hw_scan); +void rtw89_core_scan_complete(struct rtw89_dev *rtwdev, + struct ieee80211_vif *vif, bool hw_scan); #endif diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 22bd1d03e722..b73cc03cecfd 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -2324,16 +2324,17 @@ rtw89_debug_append_rx_rate(struct seq_file *m, struct rtw89_pkt_stat *pkt_stat, static const struct rtw89_rx_rate_cnt_info { enum rtw89_hw_rate first_rate; int len; + int ext; const char *rate_mode; } rtw89_rx_rate_cnt_infos[] = { - {RTW89_HW_RATE_CCK1, 4, "Legacy:"}, - {RTW89_HW_RATE_OFDM6, 8, "OFDM:"}, - {RTW89_HW_RATE_MCS0, 8, "HT 0:"}, - {RTW89_HW_RATE_MCS8, 8, "HT 1:"}, - {RTW89_HW_RATE_VHT_NSS1_MCS0, 10, "VHT 1SS:"}, - {RTW89_HW_RATE_VHT_NSS2_MCS0, 10, "VHT 2SS:"}, - {RTW89_HW_RATE_HE_NSS1_MCS0, 12, "HE 1SS:"}, - {RTW89_HW_RATE_HE_NSS2_MCS0, 12, "HE 2ss:"}, + {RTW89_HW_RATE_CCK1, 4, 0, "Legacy:"}, + {RTW89_HW_RATE_OFDM6, 8, 0, "OFDM:"}, + {RTW89_HW_RATE_MCS0, 8, 0, "HT 0:"}, + {RTW89_HW_RATE_MCS8, 8, 0, "HT 1:"}, + {RTW89_HW_RATE_VHT_NSS1_MCS0, 10, 2, "VHT 1SS:"}, + {RTW89_HW_RATE_VHT_NSS2_MCS0, 10, 2, "VHT 2SS:"}, + {RTW89_HW_RATE_HE_NSS1_MCS0, 12, 0, "HE 1SS:"}, + {RTW89_HW_RATE_HE_NSS2_MCS0, 12, 0, "HE 2ss:"}, }; static int rtw89_debug_priv_phy_info_get(struct seq_file *m, void *v) @@ -2358,6 +2359,11 @@ static int rtw89_debug_priv_phy_info_get(struct seq_file *m, void *v) seq_printf(m, "%10s [", info->rate_mode); rtw89_debug_append_rx_rate(m, pkt_stat, info->first_rate, info->len); + if (info->ext) { + seq_puts(m, "]["); + rtw89_debug_append_rx_rate(m, pkt_stat, + info->first_rate + info->len, info->ext); + } seq_puts(m, "]\n"); } @@ -2366,6 +2372,72 @@ static int rtw89_debug_priv_phy_info_get(struct seq_file *m, void *v) return 0; } +static void rtw89_dump_addr_cam(struct seq_file *m, + struct rtw89_addr_cam_entry *addr_cam) +{ + struct rtw89_sec_cam_entry *sec_entry; + int i; + + seq_printf(m, "\taddr_cam_idx=%u\n", addr_cam->addr_cam_idx); + seq_printf(m, "\t-> bssid_cam_idx=%u\n", addr_cam->bssid_cam_idx); + seq_printf(m, "\tsec_cam_bitmap=%*ph\n", (int)sizeof(addr_cam->sec_cam_map), + addr_cam->sec_cam_map); + for (i = 0; i < RTW89_SEC_CAM_IN_ADDR_CAM; i++) { + sec_entry = addr_cam->sec_entries[i]; + if (!sec_entry) + continue; + seq_printf(m, "\tsec[%d]: sec_cam_idx %u", i, sec_entry->sec_cam_idx); + if (sec_entry->ext_key) + seq_printf(m, ", %u", sec_entry->sec_cam_idx + 1); + seq_puts(m, "\n"); + } +} + +static +void rtw89_vif_ids_get_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + struct seq_file *m = (struct seq_file *)data; + struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam; + + seq_printf(m, "VIF [%d] %pM\n", rtwvif->mac_id, rtwvif->mac_addr); + seq_printf(m, "\tbssid_cam_idx=%u\n", bssid_cam->bssid_cam_idx); + rtw89_dump_addr_cam(m, &rtwvif->addr_cam); +} + +static void rtw89_sta_ids_get_iter(void *data, struct ieee80211_sta *sta) +{ + struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; + struct seq_file *m = (struct seq_file *)data; + + seq_printf(m, "STA [%d] %pM\n", rtwsta->mac_id, sta->addr); + rtw89_dump_addr_cam(m, &rtwsta->addr_cam); +} + +static int rtw89_debug_priv_stations_get(struct seq_file *m, void *v) +{ + struct rtw89_debugfs_priv *debugfs_priv = m->private; + struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + struct rtw89_cam_info *cam_info = &rtwdev->cam_info; + + seq_puts(m, "map:\n"); + seq_printf(m, "\tmac_id: %*ph\n", (int)sizeof(rtwdev->mac_id_map), + rtwdev->mac_id_map); + seq_printf(m, "\taddr_cam: %*ph\n", (int)sizeof(cam_info->addr_cam_map), + cam_info->addr_cam_map); + seq_printf(m, "\tbssid_cam: %*ph\n", (int)sizeof(cam_info->bssid_cam_map), + cam_info->bssid_cam_map); + seq_printf(m, "\tsec_cam: %*ph\n", (int)sizeof(cam_info->sec_cam_map), + cam_info->sec_cam_map); + + ieee80211_iterate_active_interfaces_atomic(rtwdev->hw, + IEEE80211_IFACE_ITER_NORMAL, rtw89_vif_ids_get_iter, m); + + ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_ids_get_iter, m); + + return 0; +} + static struct rtw89_debugfs_priv rtw89_debug_priv_read_reg = { .cb_read = rtw89_debug_priv_read_reg_get, .cb_write = rtw89_debug_priv_read_reg_select, @@ -2432,6 +2504,10 @@ static struct rtw89_debugfs_priv rtw89_debug_priv_phy_info = { .cb_read = rtw89_debug_priv_phy_info_get, }; +static struct rtw89_debugfs_priv rtw89_debug_priv_stations = { + .cb_read = rtw89_debug_priv_stations_get, +}; + #define rtw89_debugfs_add(name, mode, fopname, parent) \ do { \ rtw89_debug_priv_ ##name.rtwdev = rtwdev; \ @@ -2470,6 +2546,7 @@ void rtw89_debugfs_init(struct rtw89_dev *rtwdev) rtw89_debugfs_add_w(btc_manual); rtw89_debugfs_add_w(fw_log_manual); rtw89_debugfs_add_r(phy_info); + rtw89_debugfs_add_r(stations); } #endif diff --git a/drivers/net/wireless/realtek/rtw89/debug.h b/drivers/net/wireless/realtek/rtw89/debug.h index f14b726c1a9f..1745815f5e00 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.h +++ b/drivers/net/wireless/realtek/rtw89/debug.h @@ -23,6 +23,7 @@ enum rtw89_debug_mask { RTW89_DBG_FW = BIT(12), RTW89_DBG_BTC = BIT(13), RTW89_DBG_BF = BIT(14), + RTW89_DBG_HW_SCAN = BIT(15), }; enum rtw89_debug_mac_reg_sel { diff --git a/drivers/net/wireless/realtek/rtw89/efuse.c b/drivers/net/wireless/realtek/rtw89/efuse.c index c0b80f3da56c..7bd4f8558e03 100644 --- a/drivers/net/wireless/realtek/rtw89/efuse.c +++ b/drivers/net/wireless/realtek/rtw89/efuse.c @@ -4,6 +4,7 @@ #include "debug.h" #include "efuse.h" +#include "mac.h" #include "reg.h" enum rtw89_efuse_bank { @@ -16,6 +17,9 @@ static int rtw89_switch_efuse_bank(struct rtw89_dev *rtwdev, { u8 val; + if (rtwdev->chip->chip_id != RTL8852A) + return 0; + val = rtw89_read32_mask(rtwdev, R_AX_EFUSE_CTRL_1, B_AX_EF_CELL_SEL_MASK); if (bank == val) @@ -32,14 +36,61 @@ static int rtw89_switch_efuse_bank(struct rtw89_dev *rtwdev, return -EBUSY; } -static int rtw89_dump_physical_efuse_map(struct rtw89_dev *rtwdev, u8 *map, - u32 dump_addr, u32 dump_size) +static void rtw89_enable_otp_burst_mode(struct rtw89_dev *rtwdev, bool en) +{ + if (en) + rtw89_write32_set(rtwdev, R_AX_EFUSE_CTRL_1_V1, B_AX_EF_BURST); + else + rtw89_write32_clr(rtwdev, R_AX_EFUSE_CTRL_1_V1, B_AX_EF_BURST); +} + +static void rtw89_enable_efuse_pwr_cut_ddv(struct rtw89_dev *rtwdev) +{ + enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; + struct rtw89_hal *hal = &rtwdev->hal; + + if (chip_id == RTL8852A) + return; + + rtw89_write8_set(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK); + rtw89_write16_set(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_PWC_EV2EF_B14); + + fsleep(1000); + + rtw89_write16_set(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_PWC_EV2EF_B15); + rtw89_write16_clr(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_ISO_EB2CORE); + if (chip_id == RTL8852B && hal->cv == CHIP_CAV) + rtw89_enable_otp_burst_mode(rtwdev, true); +} + +static void rtw89_disable_efuse_pwr_cut_ddv(struct rtw89_dev *rtwdev) +{ + enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; + struct rtw89_hal *hal = &rtwdev->hal; + + if (chip_id == RTL8852A) + return; + + if (chip_id == RTL8852B && hal->cv == CHIP_CAV) + rtw89_enable_otp_burst_mode(rtwdev, false); + + rtw89_write16_set(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_ISO_EB2CORE); + rtw89_write16_clr(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_PWC_EV2EF_B15); + + fsleep(1000); + + rtw89_write16_clr(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_PWC_EV2EF_B14); + rtw89_write8_clr(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK); +} + +static int rtw89_dump_physical_efuse_map_ddv(struct rtw89_dev *rtwdev, u8 *map, + u32 dump_addr, u32 dump_size) { u32 efuse_ctl; u32 addr; int ret; - rtw89_switch_efuse_bank(rtwdev, RTW89_EFUSE_BANK_WIFI); + rtw89_enable_efuse_pwr_cut_ddv(rtwdev); for (addr = dump_addr; addr < dump_addr + dump_size; addr++) { efuse_ctl = u32_encode_bits(addr, B_AX_EF_ADDR_MASK); @@ -54,6 +105,74 @@ static int rtw89_dump_physical_efuse_map(struct rtw89_dev *rtwdev, u8 *map, *map++ = (u8)(efuse_ctl & 0xff); } + rtw89_disable_efuse_pwr_cut_ddv(rtwdev); + + return 0; +} + +static int rtw89_dump_physical_efuse_map_dav(struct rtw89_dev *rtwdev, u8 *map, + u32 dump_addr, u32 dump_size) +{ + u32 addr; + u8 val8; + int err; + int ret; + + for (addr = dump_addr; addr < dump_addr + dump_size; addr++) { + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_CTRL, 0x40, FULL_BIT_MASK); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_LOW_ADDR, + addr & 0xff, XTAL_SI_LOW_ADDR_MASK); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_CTRL, addr >> 8, + XTAL_SI_HIGH_ADDR_MASK); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_CTRL, 0, + XTAL_SI_MODE_SEL_MASK); + if (ret) + return ret; + + ret = read_poll_timeout_atomic(rtw89_mac_read_xtal_si, err, + !err && (val8 & XTAL_SI_RDY), + 1, 10000, false, + rtwdev, XTAL_SI_CTRL, &val8); + if (ret) { + rtw89_warn(rtwdev, "failed to read dav efuse\n"); + return ret; + } + + ret = rtw89_mac_read_xtal_si(rtwdev, XTAL_SI_READ_VAL, &val8); + if (ret) + return ret; + *map++ = val8; + } + + return 0; +} + +static int rtw89_dump_physical_efuse_map(struct rtw89_dev *rtwdev, u8 *map, + u32 dump_addr, u32 dump_size, bool dav) +{ + int ret; + + if (!map || dump_size == 0) + return 0; + + rtw89_switch_efuse_bank(rtwdev, RTW89_EFUSE_BANK_WIFI); + + if (dav) { + ret = rtw89_dump_physical_efuse_map_dav(rtwdev, map, dump_addr, dump_size); + if (ret) + return ret; + } else { + ret = rtw89_dump_physical_efuse_map_ddv(rtwdev, map, dump_addr, dump_size); + if (ret) + return ret; + } + return 0; } @@ -78,6 +197,9 @@ static int rtw89_dump_logical_efuse_map(struct rtw89_dev *rtwdev, u8 *phy_map, u8 word_en; int i; + if (!phy_map) + return 0; + while (phy_idx < physical_size - sec_ctrl_size) { hdr1 = phy_map[phy_idx]; hdr2 = phy_map[phy_idx + 1]; @@ -109,8 +231,13 @@ int rtw89_parse_efuse_map(struct rtw89_dev *rtwdev) { u32 phy_size = rtwdev->chip->physical_efuse_size; u32 log_size = rtwdev->chip->logical_efuse_size; + u32 dav_phy_size = rtwdev->chip->dav_phy_efuse_size; + u32 dav_log_size = rtwdev->chip->dav_log_efuse_size; + u32 full_log_size = log_size + dav_log_size; u8 *phy_map = NULL; u8 *log_map = NULL; + u8 *dav_phy_map = NULL; + u8 *dav_log_map = NULL; int ret; if (rtw89_read16(rtwdev, R_AX_SYS_WL_EFUSE_CTRL) & B_AX_AUTOLOAD_SUS) @@ -119,27 +246,41 @@ int rtw89_parse_efuse_map(struct rtw89_dev *rtwdev) rtw89_warn(rtwdev, "failed to check efuse autoload\n"); phy_map = kmalloc(phy_size, GFP_KERNEL); - log_map = kmalloc(log_size, GFP_KERNEL); + log_map = kmalloc(full_log_size, GFP_KERNEL); + if (dav_phy_size && dav_log_size) { + dav_phy_map = kmalloc(dav_phy_size, GFP_KERNEL); + dav_log_map = log_map + log_size; + } - if (!phy_map || !log_map) { + if (!phy_map || !log_map || (dav_phy_size && !dav_phy_map)) { ret = -ENOMEM; goto out_free; } - ret = rtw89_dump_physical_efuse_map(rtwdev, phy_map, 0, phy_size); + ret = rtw89_dump_physical_efuse_map(rtwdev, phy_map, 0, phy_size, false); if (ret) { rtw89_warn(rtwdev, "failed to dump efuse physical map\n"); goto out_free; } + ret = rtw89_dump_physical_efuse_map(rtwdev, dav_phy_map, 0, dav_phy_size, true); + if (ret) { + rtw89_warn(rtwdev, "failed to dump efuse dav physical map\n"); + goto out_free; + } - memset(log_map, 0xff, log_size); + memset(log_map, 0xff, full_log_size); ret = rtw89_dump_logical_efuse_map(rtwdev, phy_map, log_map); if (ret) { rtw89_warn(rtwdev, "failed to dump efuse logical map\n"); goto out_free; } + ret = rtw89_dump_logical_efuse_map(rtwdev, dav_phy_map, dav_log_map); + if (ret) { + rtw89_warn(rtwdev, "failed to dump efuse dav logical map\n"); + goto out_free; + } - rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "log_map: ", log_map, log_size); + rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "log_map: ", log_map, full_log_size); ret = rtwdev->chip->ops->read_efuse(rtwdev, log_map); if (ret) { @@ -148,6 +289,7 @@ int rtw89_parse_efuse_map(struct rtw89_dev *rtwdev) } out_free: + kfree(dav_phy_map); kfree(log_map); kfree(phy_map); @@ -169,7 +311,7 @@ int rtw89_parse_phycap_map(struct rtw89_dev *rtwdev) return -ENOMEM; ret = rtw89_dump_physical_efuse_map(rtwdev, phycap_map, - phycap_addr, phycap_size); + phycap_addr, phycap_size, false); if (ret) { rtw89_warn(rtwdev, "failed to dump phycap map\n"); goto out_free; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 8a57b75b07c0..6deaf8eec6b4 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -201,6 +201,14 @@ static void rtw89_fw_recognize_features(struct rtw89_dev *rtwdev) if (chip->chip_id == RTL8852A && RTW89_FW_SUIT_VER_CODE(fw_suit) <= RTW89_FW_VER_CODE(0, 13, 29, 0)) rtwdev->fw.old_ht_ra_format = true; + + if (chip->chip_id == RTL8852A && + RTW89_FW_SUIT_VER_CODE(fw_suit) >= RTW89_FW_VER_CODE(0, 13, 35, 0)) + rtwdev->fw.scan_offload = true; + + if (chip->chip_id == RTL8852A && + RTW89_FW_SUIT_VER_CODE(fw_suit) >= RTW89_FW_VER_CODE(0, 13, 35, 0)) + rtwdev->fw.tx_wake = true; } int rtw89_fw_recognize(struct rtw89_dev *rtwdev) @@ -555,11 +563,27 @@ int rtw89_fw_h2c_cam(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, return -EBUSY; } -#define H2C_BA_CAM_LEN 4 -int rtw89_fw_h2c_ba_cam(struct rtw89_dev *rtwdev, bool valid, u8 macid, - struct ieee80211_ampdu_params *params) +#define H2C_BA_CAM_LEN 8 +int rtw89_fw_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, + bool valid, struct ieee80211_ampdu_params *params) { + u8 macid = rtwsta->mac_id; struct sk_buff *skb; + u8 entry_idx; + int ret; + + ret = valid ? + rtw89_core_acquire_sta_ba_entry(rtwsta, params->tid, &entry_idx) : + rtw89_core_release_sta_ba_entry(rtwsta, params->tid, &entry_idx); + if (ret) { + /* it still works even if we don't have static BA CAM, because + * hardware can create dynamic BA CAM automatically. + */ + rtw89_debug(rtwdev, RTW89_DBG_TXRX, + "failed to %s entry tid=%d for h2c ba cam\n", + valid ? "alloc" : "free", params->tid); + return 0; + } skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_BA_CAM_LEN); if (!skb) { @@ -568,6 +592,7 @@ int rtw89_fw_h2c_ba_cam(struct rtw89_dev *rtwdev, bool valid, u8 macid, } skb_put(skb, H2C_BA_CAM_LEN); SET_BA_CAM_MACID(skb->data, macid); + SET_BA_CAM_ENTRY_IDX(skb->data, entry_idx); if (!valid) goto end; SET_BA_CAM_VALID(skb->data, valid); @@ -577,7 +602,7 @@ int rtw89_fw_h2c_ba_cam(struct rtw89_dev *rtwdev, bool valid, u8 macid, else SET_BA_CAM_BMAP_SIZE(skb->data, 0); /* If init req is set, hw will set the ssn */ - SET_BA_CAM_INIT_REQ(skb->data, 0); + SET_BA_CAM_INIT_REQ(skb->data, 1); SET_BA_CAM_SSN(skb->data, params->ssn); end: @@ -716,12 +741,14 @@ int rtw89_fw_h2c_lps_parm(struct rtw89_dev *rtwdev, } #define H2C_CMC_TBL_LEN 68 -int rtw89_fw_h2c_default_cmac_tbl(struct rtw89_dev *rtwdev, u8 macid) +int rtw89_fw_h2c_default_cmac_tbl(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif) { struct rtw89_hal *hal = &rtwdev->hal; struct sk_buff *skb; u8 ntx_path = hal->antenna_tx ? hal->antenna_tx : RF_B; u8 map_b = hal->antenna_tx == RF_AB ? 1 : 0; + u8 macid = rtwvif->mac_id; skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_CMC_TBL_LEN); if (!skb) { @@ -743,6 +770,8 @@ int rtw89_fw_h2c_default_cmac_tbl(struct rtw89_dev *rtwdev, u8 macid) SET_CMC_TBL_ANTSEL_D(skb->data, 0); SET_CMC_TBL_DOPPLER_CTRL(skb->data, 0); SET_CMC_TBL_TXPWR_TOLERENCE(skb->data, 0); + if (rtwvif->net_type == RTW89_NET_TYPE_AP_MODE) + SET_CMC_TBL_DATA_DCM(skb->data, 0); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, H2C_CL_MAC_FR_EXCHG, @@ -821,13 +850,15 @@ int rtw89_fw_h2c_assoc_cmac_tbl(struct rtw89_dev *rtwdev, struct ieee80211_sta *sta) { struct rtw89_hal *hal = &rtwdev->hal; - struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; + struct rtw89_sta *rtwsta = sta_to_rtwsta_safe(sta); struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; struct sk_buff *skb; u8 pads[RTW89_PPE_BW_NUM]; + u8 mac_id = rtwsta ? rtwsta->mac_id : rtwvif->mac_id; memset(pads, 0, sizeof(pads)); - __get_sta_he_pkt_padding(rtwdev, sta, pads); + if (sta) + __get_sta_he_pkt_padding(rtwdev, sta, pads); skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_CMC_TBL_LEN); if (!skb) { @@ -835,7 +866,7 @@ int rtw89_fw_h2c_assoc_cmac_tbl(struct rtw89_dev *rtwdev, return -ENOMEM; } skb_put(skb, H2C_CMC_TBL_LEN); - SET_CTRL_INFO_MACID(skb->data, rtwsta->mac_id); + SET_CTRL_INFO_MACID(skb->data, mac_id); SET_CTRL_INFO_OPERATION(skb->data, 1); SET_CMC_TBL_DISRTSFB(skb->data, 1); SET_CMC_TBL_DISDATAFB(skb->data, 1); @@ -853,7 +884,10 @@ int rtw89_fw_h2c_assoc_cmac_tbl(struct rtw89_dev *rtwdev, SET_CMC_TBL_NOMINAL_PKT_PADDING(skb->data, pads[RTW89_CHANNEL_WIDTH_20]); SET_CMC_TBL_NOMINAL_PKT_PADDING40(skb->data, pads[RTW89_CHANNEL_WIDTH_40]); SET_CMC_TBL_NOMINAL_PKT_PADDING80(skb->data, pads[RTW89_CHANNEL_WIDTH_80]); - SET_CMC_TBL_BSR_QUEUE_SIZE_FORMAT(skb->data, sta->he_cap.has_he); + if (sta) + SET_CMC_TBL_BSR_QUEUE_SIZE_FORMAT(skb->data, sta->he_cap.has_he); + if (rtwvif->net_type == RTW89_NET_TYPE_AP_MODE) + SET_CMC_TBL_DATA_DCM(skb->data, 0); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, H2C_CL_MAC_FR_EXCHG, @@ -911,28 +945,93 @@ int rtw89_fw_h2c_txtime_cmac_tbl(struct rtw89_dev *rtwdev, return -EBUSY; } -#define H2C_VIF_MAINTAIN_LEN 4 -int rtw89_fw_h2c_vif_maintain(struct rtw89_dev *rtwdev, - struct rtw89_vif *rtwvif, - enum rtw89_upd_mode upd_mode) +#define H2C_BCN_BASE_LEN 12 +int rtw89_fw_h2c_update_beacon(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif) +{ + struct rtw89_hal *hal = &rtwdev->hal; + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); + struct sk_buff *skb; + struct sk_buff *skb_beacon; + u16 tim_offset; + int bcn_total_len; + + skb_beacon = ieee80211_beacon_get_tim(rtwdev->hw, vif, &tim_offset, NULL); + if (!skb_beacon) { + rtw89_err(rtwdev, "failed to get beacon skb\n"); + return -ENOMEM; + } + + bcn_total_len = H2C_BCN_BASE_LEN + skb_beacon->len; + skb = rtw89_fw_h2c_alloc_skb_with_hdr(bcn_total_len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for fw dl\n"); + dev_kfree_skb_any(skb_beacon); + return -ENOMEM; + } + skb_put(skb, H2C_BCN_BASE_LEN); + + SET_BCN_UPD_PORT(skb->data, rtwvif->port); + SET_BCN_UPD_MBSSID(skb->data, 0); + SET_BCN_UPD_BAND(skb->data, rtwvif->mac_idx); + SET_BCN_UPD_GRP_IE_OFST(skb->data, tim_offset); + SET_BCN_UPD_MACID(skb->data, rtwvif->mac_id); + SET_BCN_UPD_SSN_SEL(skb->data, RTW89_MGMT_HW_SSN_SEL); + SET_BCN_UPD_SSN_MODE(skb->data, RTW89_MGMT_HW_SEQ_MODE); + SET_BCN_UPD_RATE(skb->data, hal->current_band_type == RTW89_BAND_2G ? + RTW89_HW_RATE_CCK1 : RTW89_HW_RATE_OFDM6); + + skb_put_data(skb, skb_beacon->data, skb_beacon->len); + dev_kfree_skb_any(skb_beacon); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, H2C_CL_MAC_FR_EXCHG, + H2C_FUNC_MAC_BCN_UPD, 0, 1, + bcn_total_len); + + if (rtw89_h2c_tx(rtwdev, skb, false)) { + rtw89_err(rtwdev, "failed to send h2c\n"); + dev_kfree_skb_any(skb); + return -EBUSY; + } + + return 0; +} + +#define H2C_ROLE_MAINTAIN_LEN 4 +int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + struct rtw89_sta *rtwsta, + enum rtw89_upd_mode upd_mode) { struct sk_buff *skb; + u8 mac_id = rtwsta ? rtwsta->mac_id : rtwvif->mac_id; + u8 self_role; - skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_VIF_MAINTAIN_LEN); + if (rtwvif->net_type == RTW89_NET_TYPE_AP_MODE) { + if (rtwsta) + self_role = RTW89_SELF_ROLE_AP_CLIENT; + else + self_role = rtwvif->self_role; + } else { + self_role = rtwvif->self_role; + } + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_ROLE_MAINTAIN_LEN); if (!skb) { rtw89_err(rtwdev, "failed to alloc skb for h2c join\n"); return -ENOMEM; } - skb_put(skb, H2C_VIF_MAINTAIN_LEN); - SET_FWROLE_MAINTAIN_MACID(skb->data, rtwvif->mac_id); - SET_FWROLE_MAINTAIN_SELF_ROLE(skb->data, rtwvif->self_role); + skb_put(skb, H2C_ROLE_MAINTAIN_LEN); + SET_FWROLE_MAINTAIN_MACID(skb->data, mac_id); + SET_FWROLE_MAINTAIN_SELF_ROLE(skb->data, self_role); SET_FWROLE_MAINTAIN_UPD_MODE(skb->data, upd_mode); SET_FWROLE_MAINTAIN_WIFI_ROLE(skb->data, rtwvif->wifi_role); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, H2C_CL_MAC_MEDIA_RPT, H2C_FUNC_MAC_FWROLE_MAINTAIN, 0, 1, - H2C_VIF_MAINTAIN_LEN); + H2C_ROLE_MAINTAIN_LEN); if (rtw89_h2c_tx(rtwdev, skb, false)) { rtw89_err(rtwdev, "failed to send h2c\n"); @@ -948,9 +1047,17 @@ int rtw89_fw_h2c_vif_maintain(struct rtw89_dev *rtwdev, #define H2C_JOIN_INFO_LEN 4 int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, - u8 dis_conn) + struct rtw89_sta *rtwsta, bool dis_conn) { struct sk_buff *skb; + u8 mac_id = rtwsta ? rtwsta->mac_id : rtwvif->mac_id; + u8 self_role = rtwvif->self_role; + u8 net_type = rtwvif->net_type; + + if (net_type == RTW89_NET_TYPE_AP_MODE && rtwsta) { + self_role = RTW89_SELF_ROLE_AP_CLIENT; + net_type = dis_conn ? RTW89_NET_TYPE_NO_LINK : net_type; + } skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_JOIN_INFO_LEN); if (!skb) { @@ -958,7 +1065,7 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, return -ENOMEM; } skb_put(skb, H2C_JOIN_INFO_LEN); - SET_JOININFO_MACID(skb->data, rtwvif->mac_id); + SET_JOININFO_MACID(skb->data, mac_id); SET_JOININFO_OP(skb->data, dis_conn); SET_JOININFO_BAND(skb->data, rtwvif->mac_idx); SET_JOININFO_WMM(skb->data, rtwvif->wmm); @@ -968,9 +1075,9 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, SET_JOININFO_TF_MAC_PAD(skb->data, 0); SET_JOININFO_DL_T_PE(skb->data, 0); SET_JOININFO_PORT_ID(skb->data, rtwvif->port); - SET_JOININFO_NET_TYPE(skb->data, rtwvif->net_type); + SET_JOININFO_NET_TYPE(skb->data, net_type); SET_JOININFO_WIFI_ROLE(skb->data, rtwvif->wifi_role); - SET_JOININFO_SELF_ROLE(skb->data, rtwvif->self_role); + SET_JOININFO_SELF_ROLE(skb->data, self_role); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, H2C_CL_MAC_MEDIA_RPT, @@ -1212,7 +1319,7 @@ int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev) return -EBUSY; } -#define H2C_LEN_CXDRVINFO_ROLE (4 + 12 * RTW89_MAX_HW_PORT_NUM + H2C_LEN_CXDRVHDR) +#define H2C_LEN_CXDRVINFO_ROLE (4 + 12 * RTW89_PORT_NUM + H2C_LEN_CXDRVHDR) int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; @@ -1251,7 +1358,7 @@ int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev) RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, bpos->p2p_go); RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, bpos->nan); - for (i = 0; i < RTW89_MAX_HW_PORT_NUM; i++, active++) { + for (i = 0; i < RTW89_PORT_NUM; i++, active++) { RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED(cmd, active->connected, i); RTW89_SET_FWCMD_CXROLE_ACT_PID(cmd, active->pid, i); RTW89_SET_FWCMD_CXROLE_ACT_PHY(cmd, active->phy, i); @@ -1368,6 +1475,198 @@ int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev) return -EBUSY; } +#define H2C_LEN_PKT_OFLD 4 +int rtw89_fw_h2c_del_pkt_offload(struct rtw89_dev *rtwdev, u8 id) +{ + struct sk_buff *skb; + u8 *cmd; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_LEN_PKT_OFLD); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c pkt offload\n"); + return -ENOMEM; + } + skb_put(skb, H2C_LEN_PKT_OFLD); + cmd = skb->data; + + RTW89_SET_FWCMD_PACKET_OFLD_PKT_IDX(cmd, id); + RTW89_SET_FWCMD_PACKET_OFLD_PKT_OP(cmd, RTW89_PKT_OFLD_OP_DEL); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD, + H2C_FUNC_PACKET_OFLD, 1, 1, + H2C_LEN_PKT_OFLD); + + if (rtw89_h2c_tx(rtwdev, skb, false)) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return -EBUSY; +} + +int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id, + struct sk_buff *skb_ofld) +{ + struct sk_buff *skb; + u8 *cmd; + u8 alloc_id; + + alloc_id = rtw89_core_acquire_bit_map(rtwdev->pkt_offload, + RTW89_MAX_PKT_OFLD_NUM); + if (alloc_id == RTW89_MAX_PKT_OFLD_NUM) + return -ENOSPC; + + *id = alloc_id; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_LEN_PKT_OFLD + skb_ofld->len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c pkt offload\n"); + return -ENOMEM; + } + skb_put(skb, H2C_LEN_PKT_OFLD); + cmd = skb->data; + + RTW89_SET_FWCMD_PACKET_OFLD_PKT_IDX(cmd, alloc_id); + RTW89_SET_FWCMD_PACKET_OFLD_PKT_OP(cmd, RTW89_PKT_OFLD_OP_ADD); + RTW89_SET_FWCMD_PACKET_OFLD_PKT_LENGTH(cmd, skb_ofld->len); + skb_put_data(skb, skb_ofld->data, skb_ofld->len); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD, + H2C_FUNC_PACKET_OFLD, 1, 1, + H2C_LEN_PKT_OFLD + skb_ofld->len); + + if (rtw89_h2c_tx(rtwdev, skb, false)) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return -EBUSY; +} + +#define H2C_LEN_SCAN_LIST_OFFLOAD 4 +int rtw89_fw_h2c_scan_list_offload(struct rtw89_dev *rtwdev, int len, + struct list_head *chan_list) +{ + struct rtw89_mac_chinfo *ch_info; + struct sk_buff *skb; + int skb_len = H2C_LEN_SCAN_LIST_OFFLOAD + len * RTW89_MAC_CHINFO_SIZE; + u8 *cmd; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(skb_len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c scan list\n"); + return -ENOMEM; + } + skb_put(skb, H2C_LEN_SCAN_LIST_OFFLOAD); + cmd = skb->data; + + RTW89_SET_FWCMD_SCANOFLD_CH_NUM(cmd, len); + /* in unit of 4 bytes */ + RTW89_SET_FWCMD_SCANOFLD_CH_SIZE(cmd, RTW89_MAC_CHINFO_SIZE / 4); + + list_for_each_entry(ch_info, chan_list, list) { + cmd = skb_put(skb, RTW89_MAC_CHINFO_SIZE); + + RTW89_SET_FWCMD_CHINFO_PERIOD(cmd, ch_info->period); + RTW89_SET_FWCMD_CHINFO_DWELL(cmd, ch_info->dwell_time); + RTW89_SET_FWCMD_CHINFO_CENTER_CH(cmd, ch_info->central_ch); + RTW89_SET_FWCMD_CHINFO_PRI_CH(cmd, ch_info->pri_ch); + RTW89_SET_FWCMD_CHINFO_BW(cmd, ch_info->bw); + RTW89_SET_FWCMD_CHINFO_ACTION(cmd, ch_info->notify_action); + RTW89_SET_FWCMD_CHINFO_NUM_PKT(cmd, ch_info->num_pkt); + RTW89_SET_FWCMD_CHINFO_TX(cmd, ch_info->tx_pkt); + RTW89_SET_FWCMD_CHINFO_PAUSE_DATA(cmd, ch_info->pause_data); + RTW89_SET_FWCMD_CHINFO_BAND(cmd, ch_info->ch_band); + RTW89_SET_FWCMD_CHINFO_PKT_ID(cmd, ch_info->probe_id); + RTW89_SET_FWCMD_CHINFO_DFS(cmd, ch_info->dfs_ch); + RTW89_SET_FWCMD_CHINFO_TX_NULL(cmd, ch_info->tx_null); + RTW89_SET_FWCMD_CHINFO_RANDOM(cmd, ch_info->rand_seq_num); + RTW89_SET_FWCMD_CHINFO_PKT0(cmd, ch_info->pkt_id[0]); + RTW89_SET_FWCMD_CHINFO_PKT1(cmd, ch_info->pkt_id[1]); + RTW89_SET_FWCMD_CHINFO_PKT2(cmd, ch_info->pkt_id[2]); + RTW89_SET_FWCMD_CHINFO_PKT3(cmd, ch_info->pkt_id[3]); + RTW89_SET_FWCMD_CHINFO_PKT4(cmd, ch_info->pkt_id[4]); + RTW89_SET_FWCMD_CHINFO_PKT5(cmd, ch_info->pkt_id[5]); + RTW89_SET_FWCMD_CHINFO_PKT6(cmd, ch_info->pkt_id[6]); + RTW89_SET_FWCMD_CHINFO_PKT7(cmd, ch_info->pkt_id[7]); + } + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD, + H2C_FUNC_ADD_SCANOFLD_CH, 1, 1, skb_len); + + if (rtw89_h2c_tx(rtwdev, skb, false)) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return -EBUSY; +} + +#define H2C_LEN_SCAN_OFFLOAD 20 +int rtw89_fw_h2c_scan_offload(struct rtw89_dev *rtwdev, + struct rtw89_scan_option *option, + struct rtw89_vif *rtwvif) +{ + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct sk_buff *skb; + u8 *cmd; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_LEN_SCAN_OFFLOAD); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c scan offload\n"); + return -ENOMEM; + } + skb_put(skb, H2C_LEN_SCAN_OFFLOAD); + cmd = skb->data; + + RTW89_SET_FWCMD_SCANOFLD_MACID(cmd, rtwvif->mac_id); + RTW89_SET_FWCMD_SCANOFLD_PORT_ID(cmd, rtwvif->port); + RTW89_SET_FWCMD_SCANOFLD_BAND(cmd, RTW89_PHY_0); + RTW89_SET_FWCMD_SCANOFLD_OPERATION(cmd, option->enable); + RTW89_SET_FWCMD_SCANOFLD_NOTIFY_END(cmd, true); + RTW89_SET_FWCMD_SCANOFLD_TARGET_CH_MODE(cmd, option->target_ch_mode); + RTW89_SET_FWCMD_SCANOFLD_START_MODE(cmd, RTW89_SCAN_IMMEDIATE); + RTW89_SET_FWCMD_SCANOFLD_SCAN_TYPE(cmd, RTW89_SCAN_ONCE); + if (option->target_ch_mode) { + RTW89_SET_FWCMD_SCANOFLD_TARGET_CH_BW(cmd, scan_info->op_bw); + RTW89_SET_FWCMD_SCANOFLD_TARGET_PRI_CH(cmd, + scan_info->op_pri_ch); + RTW89_SET_FWCMD_SCANOFLD_TARGET_CENTRAL_CH(cmd, + scan_info->op_chan); + } + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD, + H2C_FUNC_SCANOFLD, 1, 1, + H2C_LEN_SCAN_OFFLOAD); + + if (rtw89_h2c_tx(rtwdev, skb, false)) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return -EBUSY; +} + int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev, struct rtw89_fw_h2c_rf_reg_info *info, u16 len, u8 page) @@ -1533,15 +1832,13 @@ void rtw89_fw_c2h_work(struct work_struct *work) static int rtw89_fw_write_h2c_reg(struct rtw89_dev *rtwdev, struct rtw89_mac_h2c_info *info) { - static const u32 h2c_reg[RTW89_H2CREG_MAX] = { - R_AX_H2CREG_DATA0, R_AX_H2CREG_DATA1, - R_AX_H2CREG_DATA2, R_AX_H2CREG_DATA3 - }; + const struct rtw89_chip_info *chip = rtwdev->chip; + const u32 *h2c_reg = chip->h2c_regs; u8 i, val, len; int ret; ret = read_poll_timeout(rtw89_read8, val, val == 0, 1000, 5000, false, - rtwdev, R_AX_H2CREG_CTRL); + rtwdev, chip->h2c_ctrl_reg); if (ret) { rtw89_warn(rtwdev, "FW does not process h2c registers\n"); return ret; @@ -1555,7 +1852,7 @@ static int rtw89_fw_write_h2c_reg(struct rtw89_dev *rtwdev, for (i = 0; i < RTW89_H2CREG_MAX; i++) rtw89_write32(rtwdev, h2c_reg[i], info->h2creg[i]); - rtw89_write8(rtwdev, R_AX_H2CREG_CTRL, B_AX_H2CREG_TRIGGER); + rtw89_write8(rtwdev, chip->h2c_ctrl_reg, B_AX_H2CREG_TRIGGER); return 0; } @@ -1563,10 +1860,8 @@ static int rtw89_fw_write_h2c_reg(struct rtw89_dev *rtwdev, static int rtw89_fw_read_c2h_reg(struct rtw89_dev *rtwdev, struct rtw89_mac_c2h_info *info) { - static const u32 c2h_reg[RTW89_C2HREG_MAX] = { - R_AX_C2HREG_DATA0, R_AX_C2HREG_DATA1, - R_AX_C2HREG_DATA2, R_AX_C2HREG_DATA3 - }; + const struct rtw89_chip_info *chip = rtwdev->chip; + const u32 *c2h_reg = chip->c2h_regs; u32 ret; u8 i, val; @@ -1574,7 +1869,7 @@ static int rtw89_fw_read_c2h_reg(struct rtw89_dev *rtwdev, ret = read_poll_timeout_atomic(rtw89_read8, val, val, 1, RTW89_C2H_TIMEOUT, false, rtwdev, - R_AX_C2HREG_CTRL); + chip->c2h_ctrl_reg); if (ret) { rtw89_warn(rtwdev, "c2h reg timeout\n"); return ret; @@ -1583,7 +1878,7 @@ static int rtw89_fw_read_c2h_reg(struct rtw89_dev *rtwdev, for (i = 0; i < RTW89_C2HREG_MAX; i++) info->c2hreg[i] = rtw89_read32(rtwdev, c2h_reg[i]); - rtw89_write8(rtwdev, R_AX_C2HREG_CTRL, 0); + rtw89_write8(rtwdev, chip->c2h_ctrl_reg, 0); info->id = RTW89_GET_C2H_HDR_FUNC(*info->c2hreg); info->content_len = (RTW89_GET_C2H_HDR_LEN(*info->c2hreg) << 2) - @@ -1640,3 +1935,322 @@ void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev) rtw89_fw_prog_cnt_dump(rtwdev); } + +static void rtw89_release_pkt_list(struct rtw89_dev *rtwdev) +{ + struct list_head *pkt_list = rtwdev->scan_info.pkt_list; + struct rtw89_pktofld_info *info, *tmp; + u8 idx; + + for (idx = RTW89_BAND_2G; idx < NUM_NL80211_BANDS; idx++) { + if (!(rtwdev->chip->support_bands & BIT(idx))) + continue; + + list_for_each_entry_safe(info, tmp, &pkt_list[idx], list) { + rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id); + rtw89_core_release_bit_map(rtwdev->pkt_offload, + info->id); + list_del(&info->list); + kfree(info); + } + } +} + +static int rtw89_append_probe_req_ie(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + struct sk_buff *skb) +{ + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct ieee80211_scan_ies *ies = rtwvif->scan_ies; + struct rtw89_pktofld_info *info; + struct sk_buff *new; + int ret = 0; + u8 band; + + for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { + if (!(rtwdev->chip->support_bands & BIT(band))) + continue; + + new = skb_copy(skb, GFP_KERNEL); + if (!new) { + ret = -ENOMEM; + goto out; + } + skb_put_data(new, ies->ies[band], ies->len[band]); + skb_put_data(new, ies->common_ies, ies->common_ie_len); + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + kfree_skb(new); + goto out; + } + + list_add_tail(&info->list, &scan_info->pkt_list[band]); + ret = rtw89_fw_h2c_add_pkt_offload(rtwdev, &info->id, new); + if (ret) + goto out; + + kfree_skb(new); + } +out: + return ret; +} + +static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif) +{ + struct cfg80211_scan_request *req = rtwvif->scan_req; + struct sk_buff *skb; + u8 num = req->n_ssids, i; + int ret; + + for (i = 0; i < num; i++) { + skb = ieee80211_probereq_get(rtwdev->hw, rtwvif->mac_addr, + req->ssids[i].ssid, + req->ssids[i].ssid_len, + req->ie_len); + if (!skb) + return -ENOMEM; + + ret = rtw89_append_probe_req_ie(rtwdev, rtwvif, skb); + kfree_skb(skb); + + if (ret) + return ret; + } + + return 0; +} + +static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, + int ssid_num, + struct rtw89_mac_chinfo *ch_info) +{ + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct rtw89_pktofld_info *info; + u8 band, probe_count = 0; + + ch_info->notify_action = RTW89_SCANOFLD_DEBUG_MASK; + ch_info->dfs_ch = chan_type == RTW89_CHAN_DFS; + ch_info->bw = RTW89_SCAN_WIDTH; + ch_info->tx_pkt = true; + ch_info->cfg_tx_pwr = false; + ch_info->tx_pwr_idx = 0; + ch_info->tx_null = false; + ch_info->pause_data = false; + + if (ssid_num) { + ch_info->num_pkt = ssid_num; + band = ch_info->ch_band; + + list_for_each_entry(info, &scan_info->pkt_list[band], list) { + ch_info->probe_id = info->id; + ch_info->pkt_id[probe_count] = info->id; + if (++probe_count >= ssid_num) + break; + } + if (probe_count != ssid_num) + rtw89_err(rtwdev, "SSID num differs from list len\n"); + } + + switch (chan_type) { + case RTW89_CHAN_OPERATE: + ch_info->probe_id = RTW89_SCANOFLD_PKT_NONE; + ch_info->central_ch = scan_info->op_chan; + ch_info->pri_ch = scan_info->op_pri_ch; + ch_info->ch_band = scan_info->op_band; + ch_info->bw = scan_info->op_bw; + ch_info->tx_null = true; + ch_info->num_pkt = 0; + break; + case RTW89_CHAN_DFS: + ch_info->period = min_t(u8, ch_info->period, + RTW89_DFS_CHAN_TIME); + ch_info->dwell_time = RTW89_DWELL_TIME; + break; + case RTW89_CHAN_ACTIVE: + break; + default: + rtw89_err(rtwdev, "Channel type out of bound\n"); + } +} + +static int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif) +{ + struct cfg80211_scan_request *req = rtwvif->scan_req; + struct rtw89_mac_chinfo *ch_info, *tmp; + struct ieee80211_channel *channel; + struct list_head chan_list; + bool random_seq = req->flags & NL80211_SCAN_FLAG_RANDOM_SN; + int list_len = req->n_channels, off_chan_time = 0; + enum rtw89_chan_type type; + int ret = 0, i; + + INIT_LIST_HEAD(&chan_list); + for (i = 0; i < req->n_channels; i++) { + channel = req->channels[i]; + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + goto out; + } + + ch_info->period = req->duration_mandatory ? + req->duration : RTW89_CHANNEL_TIME; + ch_info->ch_band = channel->band; + ch_info->central_ch = channel->hw_value; + ch_info->pri_ch = channel->hw_value; + ch_info->rand_seq_num = random_seq; + + if (channel->flags & + (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IR)) + type = RTW89_CHAN_DFS; + else + type = RTW89_CHAN_ACTIVE; + rtw89_hw_scan_add_chan(rtwdev, type, req->n_ssids, ch_info); + + if (rtwvif->net_type != RTW89_NET_TYPE_NO_LINK && + off_chan_time + ch_info->period > RTW89_OFF_CHAN_TIME) { + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) { + ret = -ENOMEM; + kfree(ch_info); + goto out; + } + + type = RTW89_CHAN_OPERATE; + tmp->period = req->duration_mandatory ? + req->duration : RTW89_CHANNEL_TIME; + rtw89_hw_scan_add_chan(rtwdev, type, 0, tmp); + list_add_tail(&tmp->list, &chan_list); + off_chan_time = 0; + list_len++; + } + list_add_tail(&ch_info->list, &chan_list); + off_chan_time += ch_info->period; + } + rtw89_fw_h2c_scan_list_offload(rtwdev, list_len, &chan_list); + +out: + list_for_each_entry_safe(ch_info, tmp, &chan_list, list) { + list_del(&ch_info->list); + kfree(ch_info); + } + + return ret; +} + +static int rtw89_hw_scan_prehandle(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif) +{ + int ret; + + ret = rtw89_hw_scan_update_probe_req(rtwdev, rtwvif); + if (ret) { + rtw89_err(rtwdev, "Update probe request failed\n"); + goto out; + } + ret = rtw89_hw_scan_add_chan_list(rtwdev, rtwvif); +out: + return ret; +} + +void rtw89_hw_scan_start(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, + struct ieee80211_scan_request *scan_req) +{ + struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + struct cfg80211_scan_request *req = &scan_req->req; + u8 mac_addr[ETH_ALEN]; + + rtwdev->scan_info.scanning_vif = vif; + rtwvif->scan_ies = &scan_req->ies; + rtwvif->scan_req = req; + ieee80211_stop_queues(rtwdev->hw); + + if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) + get_random_mask_addr(mac_addr, req->mac_addr, + req->mac_addr_mask); + else + ether_addr_copy(mac_addr, vif->addr); + rtw89_core_scan_start(rtwdev, rtwvif, mac_addr, true); + + rtwdev->hal.rx_fltr &= ~B_AX_A_BCN_CHK_EN; + rtwdev->hal.rx_fltr &= ~B_AX_A_BC; + rtwdev->hal.rx_fltr &= ~B_AX_A_A1_MATCH; + rtw89_write32_mask(rtwdev, + rtw89_mac_reg_by_idx(R_AX_RX_FLTR_OPT, RTW89_MAC_0), + B_AX_RX_FLTR_CFG_MASK, + rtwdev->hal.rx_fltr); +} + +void rtw89_hw_scan_complete(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, + bool aborted) +{ + struct cfg80211_scan_info info = { + .aborted = aborted, + }; + struct rtw89_vif *rtwvif; + + if (!vif) + return; + + rtwdev->hal.rx_fltr |= B_AX_A_BCN_CHK_EN; + rtwdev->hal.rx_fltr |= B_AX_A_BC; + rtwdev->hal.rx_fltr |= B_AX_A_A1_MATCH; + rtw89_write32_mask(rtwdev, + rtw89_mac_reg_by_idx(R_AX_RX_FLTR_OPT, RTW89_MAC_0), + B_AX_RX_FLTR_CFG_MASK, + rtwdev->hal.rx_fltr); + + rtw89_core_scan_complete(rtwdev, vif, true); + ieee80211_scan_completed(rtwdev->hw, &info); + ieee80211_wake_queues(rtwdev->hw); + + rtw89_release_pkt_list(rtwdev); + rtwvif = (struct rtw89_vif *)vif->drv_priv; + rtwvif->scan_req = NULL; + rtwvif->scan_ies = NULL; + rtwdev->scan_info.scanning_vif = NULL; +} + +void rtw89_hw_scan_abort(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif) +{ + rtw89_hw_scan_offload(rtwdev, vif, false); + rtw89_hw_scan_complete(rtwdev, vif, true); +} + +int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, + bool enable) +{ + struct rtw89_scan_option opt = {0}; + struct rtw89_vif *rtwvif; + int ret = 0; + + rtwvif = vif ? (struct rtw89_vif *)vif->drv_priv : NULL; + if (!rtwvif) + return -EINVAL; + + opt.enable = enable; + opt.target_ch_mode = rtwvif->net_type != RTW89_NET_TYPE_NO_LINK; + if (enable) { + ret = rtw89_hw_scan_prehandle(rtwdev, rtwvif); + if (ret) + goto out; + } + rtw89_fw_h2c_scan_offload(rtwdev, &opt, rtwvif); +out: + return ret; +} + +void rtw89_store_op_chan(struct rtw89_dev *rtwdev) +{ + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct rtw89_hal *hal = &rtwdev->hal; + + scan_info->op_pri_ch = hal->current_primary_channel; + scan_info->op_chan = hal->current_channel; + scan_info->op_bw = hal->current_band_width; + scan_info->op_band = hal->current_band_type; +} diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 2d36dc27222f..ed8609b204e0 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -123,6 +123,27 @@ enum rtw89_fw_log_comp { RTW89_FW_LOG_COMP_MCC = 20, }; +enum rtw89_pkt_offload_op { + RTW89_PKT_OFLD_OP_ADD, + RTW89_PKT_OFLD_OP_DEL, + RTW89_PKT_OFLD_OP_READ, +}; + +enum rtw89_scanofld_notify_reason { + RTW89_SCAN_DWELL_NOTIFY, + RTW89_SCAN_PRE_TX_NOTIFY, + RTW89_SCAN_POST_TX_NOTIFY, + RTW89_SCAN_ENTER_CH_NOTIFY, + RTW89_SCAN_LEAVE_CH_NOTIFY, + RTW89_SCAN_END_SCAN_NOTIFY, +}; + +enum rtw89_chan_type { + RTW89_CHAN_OPERATE = 0, + RTW89_CHAN_ACTIVE, + RTW89_CHAN_DFS, +}; + #define FWDL_SECTION_MAX_NUM 10 #define FWDL_SECTION_CHKSUM_LEN 8 #define FWDL_SECTION_PER_PKT_LEN 2020 @@ -156,6 +177,50 @@ struct rtw89_h2creg_sch_tx_en { u16 rsvd:15; } __packed; +#define RTW89_CHANNEL_TIME 45 +#define RTW89_DFS_CHAN_TIME 105 +#define RTW89_OFF_CHAN_TIME 100 +#define RTW89_DWELL_TIME 20 +#define RTW89_SCAN_WIDTH 0 +#define RTW89_SCANOFLD_MAX_SSID 8 +#define RTW89_SCANOFLD_MAX_IE_LEN 512 +#define RTW89_SCANOFLD_PKT_NONE 0xFF +#define RTW89_SCANOFLD_DEBUG_MASK 0x1F +#define RTW89_MAC_CHINFO_SIZE 20 + +struct rtw89_mac_chinfo { + u8 period; + u8 dwell_time; + u8 central_ch; + u8 pri_ch; + u8 bw:3; + u8 notify_action:5; + u8 num_pkt:4; + u8 tx_pkt:1; + u8 pause_data:1; + u8 ch_band:2; + u8 probe_id; + u8 dfs_ch:1; + u8 tx_null:1; + u8 rand_seq_num:1; + u8 cfg_tx_pwr:1; + u8 rsvd0: 4; + u8 pkt_id[RTW89_SCANOFLD_MAX_SSID]; + u16 tx_pwr_idx; + u8 rsvd1; + struct list_head list; +}; + +struct rtw89_scan_option { + bool enable; + bool target_ch_mode; +}; + +struct rtw89_pktofld_info { + struct list_head list; + u8 id; +}; + static inline void RTW89_SET_FWCMD_RA_IS_DIS(void *cmd, u32 val) { le32p_replace_bits((__le32 *)(cmd) + 0x00, val, BIT(0)); @@ -1056,6 +1121,106 @@ static inline void SET_CMC_TBL_CSI_BW(void *table, u32 val) GENMASK(31, 30)); } +static inline void SET_BCN_UPD_PORT(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 0)); +} + +static inline void SET_BCN_UPD_MBSSID(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(15, 8)); +} + +static inline void SET_BCN_UPD_BAND(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(23, 16)); +} + +static inline void SET_BCN_UPD_GRP_IE_OFST(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, (val - 24) | BIT(7), GENMASK(31, 24)); +} + +static inline void SET_BCN_UPD_MACID(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 1, val, GENMASK(7, 0)); +} + +static inline void SET_BCN_UPD_SSN_SEL(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 1, val, GENMASK(9, 8)); +} + +static inline void SET_BCN_UPD_SSN_MODE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 1, val, GENMASK(11, 10)); +} + +static inline void SET_BCN_UPD_RATE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 1, val, GENMASK(20, 12)); +} + +static inline void SET_BCN_UPD_TXPWR(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 1, val, GENMASK(23, 21)); +} + +static inline void SET_BCN_UPD_TXINFO_CTRL_EN(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 2, val, BIT(0)); +} + +static inline void SET_BCN_UPD_NTX_PATH_EN(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 2, val, GENMASK(4, 1)); +} + +static inline void SET_BCN_UPD_PATH_MAP_A(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 2, val, GENMASK(6, 5)); +} + +static inline void SET_BCN_UPD_PATH_MAP_B(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 2, val, GENMASK(8, 7)); +} + +static inline void SET_BCN_UPD_PATH_MAP_C(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 2, val, GENMASK(10, 9)); +} + +static inline void SET_BCN_UPD_PATH_MAP_D(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 2, val, GENMASK(12, 11)); +} + +static inline void SET_BCN_UPD_PATH_ANTSEL_A(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 2, val, BIT(13)); +} + +static inline void SET_BCN_UPD_PATH_ANTSEL_B(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 2, val, BIT(14)); +} + +static inline void SET_BCN_UPD_PATH_ANTSEL_C(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 2, val, BIT(15)); +} + +static inline void SET_BCN_UPD_PATH_ANTSEL_D(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 2, val, BIT(16)); +} + +static inline void SET_BCN_UPD_CSA_OFST(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 2, val, GENMASK(31, 17)); +} + static inline void SET_FWROLE_MAINTAIN_MACID(void *h2c, u32 val) { le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 0)); @@ -1226,6 +1391,26 @@ static inline void SET_BA_CAM_SSN(void *h2c, u32 val) le32p_replace_bits((__le32 *)h2c, val, GENMASK(31, 20)); } +static inline void SET_BA_CAM_UID(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 1, val, GENMASK(7, 0)); +} + +static inline void SET_BA_CAM_STD_EN(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 1, val, BIT(8)); +} + +static inline void SET_BA_CAM_BAND(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 1, val, BIT(9)); +} + +static inline void SET_BA_CAM_ENTRY_IDX_V1(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 1, val, GENMASK(31, 28)); +} + static inline void SET_LPS_PARM_MACID(void *h2c, u32 val) { le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 0)); @@ -1316,6 +1501,14 @@ enum rtw89_btc_cxdrvinfo { CXDRVINFO_MAX, }; +enum rtw89_scan_mode { + RTW89_SCAN_IMMEDIATE, +}; + +enum rtw89_scan_type { + RTW89_SCAN_ONCE, +}; + static inline void RTW89_SET_FWCMD_CXHDR_TYPE(void *cmd, u8 val) { u8p_replace_bits((u8 *)(cmd) + 0, val, GENMASK(7, 0)); @@ -1586,6 +1779,242 @@ static inline void RTW89_SET_FWCMD_CXRFK_TYPE(void *cmd, u32 val) le32p_replace_bits((__le32 *)((u8 *)(cmd) + 2), val, GENMASK(17, 10)); } +static inline void RTW89_SET_FWCMD_PACKET_OFLD_PKT_IDX(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_PACKET_OFLD_PKT_OP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(10, 8)); +} + +static inline void RTW89_SET_FWCMD_PACKET_OFLD_PKT_LENGTH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(31, 16)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_CH_NUM(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_CH_SIZE(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_PERIOD(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_DWELL(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_CENTER_CH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_PRI_CH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(31, 24)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_BW(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, GENMASK(2, 0)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_ACTION(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, GENMASK(7, 3)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_NUM_PKT(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, GENMASK(11, 8)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_TX(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, BIT(12)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_PAUSE_DATA(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, BIT(13)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_BAND(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, GENMASK(15, 14)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_PKT_ID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_DFS(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, BIT(24)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_TX_NULL(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, BIT(25)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_RANDOM(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, BIT(26)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_CFG_TX(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, BIT(27)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_PKT0(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 8), val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_PKT1(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 8), val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_PKT2(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 8), val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_PKT3(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 8), val, GENMASK(31, 24)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_PKT4(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 12), val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_PKT5(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 12), val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_PKT6(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 12), val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_PKT7(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 12), val, GENMASK(31, 24)); +} + +static inline void RTW89_SET_FWCMD_CHINFO_POWER_IDX(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 16), val, GENMASK(15, 0)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_MACID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_NORM_CY(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_PORT_ID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(18, 16)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_BAND(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, BIT(19)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_OPERATION(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(21, 20)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_TARGET_CH_BAND(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd)), val, GENMASK(23, 22)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_NOTIFY_END(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, BIT(0)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_TARGET_CH_MODE(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, BIT(1)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_START_MODE(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, BIT(2)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_SCAN_TYPE(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, GENMASK(4, 3)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_TARGET_CH_BW(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, GENMASK(7, 5)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_TARGET_PRI_CH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_TARGET_CENTRAL_CH(void *cmd, + u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_PROBE_REQ_PKT_ID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 4), val, GENMASK(31, 24)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_NORM_PD(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 8), val, GENMASK(15, 0)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_SLOW_PD(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 8), val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_TSF_HIGH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 12), val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_FWCMD_SCANOFLD_TSF_SLOW(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)(cmd) + 16), val, GENMASK(31, 0)); +} + #define RTW89_C2H_HEADER_LEN 8 #define RTW89_GET_C2H_CATEGORY(c2h) \ @@ -1642,6 +2071,26 @@ static inline void RTW89_SET_FWCMD_CXRFK_TYPE(void *cmd, u32 val) #define RTW89_MK_HT_RATE(nss, mcs) (FIELD_PREP(GENMASK(4, 3), nss) | \ FIELD_PREP(GENMASK(2, 0), mcs)) +#define RTW89_GET_MAC_C2H_PKTOFLD_ID(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(7, 0)) +#define RTW89_GET_MAC_C2H_PKTOFLD_OP(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(10, 8)) +#define RTW89_GET_MAC_C2H_PKTOFLD_LEN(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(31, 16)) + +#define RTW89_GET_MAC_C2H_SCANOFLD_PRI_CH(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(7, 0)) +#define RTW89_GET_MAC_C2H_SCANOFLD_RSP(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(19, 16)) +#define RTW89_GET_MAC_C2H_SCANOFLD_STATUS(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(23, 20)) +#define RTW89_GET_MAC_C2H_SCANOFLD_TX_FAIL(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 5), GENMASK(3, 0)) +#define RTW89_GET_MAC_C2H_SCANOFLD_AIR_DENSITY(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 5), GENMASK(7, 4)) +#define RTW89_GET_MAC_C2H_SCANOFLD_BAND(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 5), GENMASK(25, 24)) + #define RTW89_FW_HDR_SIZE 32 #define RTW89_FW_SECTION_HDR_SIZE 16 @@ -1709,6 +2158,7 @@ struct rtw89_fw_h2c_rf_reg_info { /* CLASS 5 - Frame Exchange */ #define H2C_CL_MAC_FR_EXCHG 0x5 #define H2C_FUNC_MAC_CCTLINFO_UD 0x2 +#define H2C_FUNC_MAC_BCN_UPD 0x5 /* CLASS 6 - Address CAM */ #define H2C_CL_MAC_ADDR_CAM_UPDATE 0x6 @@ -1721,9 +2171,12 @@ struct rtw89_fw_h2c_rf_reg_info { /* CLASS 9 - FW offload */ #define H2C_CL_MAC_FW_OFLD 0x9 +#define H2C_FUNC_PACKET_OFLD 0x1 #define H2C_FUNC_MAC_MACID_PAUSE 0x8 #define H2C_FUNC_USR_EDCA 0xF #define H2C_FUNC_OFLD_CFG 0x14 +#define H2C_FUNC_ADD_SCANOFLD_CH 0x16 +#define H2C_FUNC_SCANOFLD 0x17 /* CLASS 10 - Security CAM */ #define H2C_CL_MAC_SEC_CAM 0xa @@ -1750,21 +2203,25 @@ int rtw89_wait_firmware_completion(struct rtw89_dev *rtwdev); void rtw89_h2c_pkt_set_hdr(struct rtw89_dev *rtwdev, struct sk_buff *skb, u8 type, u8 cat, u8 class, u8 func, bool rack, bool dack, u32 len); -int rtw89_fw_h2c_default_cmac_tbl(struct rtw89_dev *rtwdev, u8 macid); +int rtw89_fw_h2c_default_cmac_tbl(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif); int rtw89_fw_h2c_assoc_cmac_tbl(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); int rtw89_fw_h2c_txtime_cmac_tbl(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta); +int rtw89_fw_h2c_update_beacon(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif); int rtw89_fw_h2c_cam(struct rtw89_dev *rtwdev, struct rtw89_vif *vif, struct rtw89_sta *rtwsta, const u8 *scan_mac_addr); void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h); void rtw89_fw_c2h_work(struct work_struct *work); -int rtw89_fw_h2c_vif_maintain(struct rtw89_dev *rtwdev, - struct rtw89_vif *rtwvif, - enum rtw89_upd_mode upd_mode); +int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + struct rtw89_sta *rtwsta, + enum rtw89_upd_mode upd_mode); int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, - u8 dis_conn); + struct rtw89_sta *rtwsta, bool dis_conn); int rtw89_fw_h2c_macid_pause(struct rtw89_dev *rtwdev, u8 sh, u8 grp, bool pause); int rtw89_fw_h2c_set_edca(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, @@ -1775,6 +2232,14 @@ int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev); +int rtw89_fw_h2c_del_pkt_offload(struct rtw89_dev *rtwdev, u8 id); +int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id, + struct sk_buff *skb_ofld); +int rtw89_fw_h2c_scan_list_offload(struct rtw89_dev *rtwdev, int len, + struct list_head *chan_list); +int rtw89_fw_h2c_scan_offload(struct rtw89_dev *rtwdev, + struct rtw89_scan_option *opt, + struct rtw89_vif *vif); int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev, struct rtw89_fw_h2c_rf_reg_info *info, u16 len, u8 page); @@ -1785,8 +2250,9 @@ int rtw89_fw_h2c_raw(struct rtw89_dev *rtwdev, const u8 *buf, u16 len); void rtw89_fw_send_all_early_h2c(struct rtw89_dev *rtwdev); void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_general_pkt(struct rtw89_dev *rtwdev, u8 macid); -int rtw89_fw_h2c_ba_cam(struct rtw89_dev *rtwdev, bool valid, u8 macid, - struct ieee80211_ampdu_params *params); +int rtw89_fw_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, + bool valid, struct ieee80211_ampdu_params *params); + int rtw89_fw_h2c_lps_parm(struct rtw89_dev *rtwdev, struct rtw89_lps_parm *lps_param); struct sk_buff *rtw89_fw_h2c_alloc_skb_with_hdr(u32 len); @@ -1796,5 +2262,16 @@ int rtw89_fw_msg_reg(struct rtw89_dev *rtwdev, struct rtw89_mac_c2h_info *c2h_info); int rtw89_fw_h2c_fw_log(struct rtw89_dev *rtwdev, bool enable); void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev); +void rtw89_store_op_chan(struct rtw89_dev *rtwdev); +void rtw89_hw_scan_start(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, + struct ieee80211_scan_request *req); +void rtw89_hw_scan_complete(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, + bool aborted); +int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, + bool enable); +void rtw89_hw_scan_status_report(struct rtw89_dev *rtwdev, struct sk_buff *skb); +void rtw89_hw_scan_chan_switch(struct rtw89_dev *rtwdev, struct sk_buff *skb); +void rtw89_hw_scan_abort(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif); +void rtw89_store_op_chan(struct rtw89_dev *rtwdev); #endif diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index b98c47e9ecfe..5e554bd9f036 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -172,6 +172,7 @@ static void rtw89_mac_dump_qta_lost(struct rtw89_dev *rtwdev) qempty.dle_type = DLE_CTRL_TYPE_PLE; qempty.grpsel = 0; + qempty.qempty = ~(u32)0; ret = dle_dfi_qempty(rtwdev, &qempty); if (ret) rtw89_warn(rtwdev, "%s: query DLE fail\n", __func__); @@ -481,9 +482,10 @@ int rtw89_mac_set_err_status(struct rtw89_dev *rtwdev, u32 err) } EXPORT_SYMBOL(rtw89_mac_set_err_status); -const struct rtw89_hfc_prec_cfg rtw_hfc_preccfg_pcie = { +const struct rtw89_hfc_prec_cfg rtw89_hfc_preccfg_pcie = { 2, 40, 0, 0, 1, 0, 0, 0 }; +EXPORT_SYMBOL(rtw89_hfc_preccfg_pcie); static int hfc_reset_param(struct rtw89_dev *rtwdev) { @@ -567,6 +569,8 @@ static int hfc_pub_cfg_chk(struct rtw89_dev *rtwdev) static int hfc_ch_ctrl(struct rtw89_dev *rtwdev, u8 ch) { + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_page_regs *regs = chip->page_regs; struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; const struct rtw89_hfc_ch_cfg *cfg = param->ch_cfg; int ret = 0; @@ -586,13 +590,15 @@ static int hfc_ch_ctrl(struct rtw89_dev *rtwdev, u8 ch) val = u32_encode_bits(cfg[ch].min, B_AX_MIN_PG_MASK) | u32_encode_bits(cfg[ch].max, B_AX_MAX_PG_MASK) | (cfg[ch].grp ? B_AX_GRP : 0); - rtw89_write32(rtwdev, R_AX_ACH0_PAGE_CTRL + ch * 4, val); + rtw89_write32(rtwdev, regs->ach_page_ctrl + ch * 4, val); return 0; } static int hfc_upd_ch_info(struct rtw89_dev *rtwdev, u8 ch) { + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_page_regs *regs = chip->page_regs; struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; struct rtw89_hfc_ch_info *info = param->ch_info; const struct rtw89_hfc_ch_cfg *cfg = param->ch_cfg; @@ -606,7 +612,7 @@ static int hfc_upd_ch_info(struct rtw89_dev *rtwdev, u8 ch) if (ch > RTW89_DMA_H2C) return -EINVAL; - val = rtw89_read32(rtwdev, R_AX_ACH0_PAGE_INFO + ch * 4); + val = rtw89_read32(rtwdev, regs->ach_page_info + ch * 4); info[ch].aval = u32_get_bits(val, B_AX_AVAL_PG_MASK); if (ch < RTW89_DMA_H2C) info[ch].used = u32_get_bits(val, B_AX_USE_PG_MASK); @@ -618,6 +624,8 @@ static int hfc_upd_ch_info(struct rtw89_dev *rtwdev, u8 ch) static int hfc_pub_ctrl(struct rtw89_dev *rtwdev) { + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_page_regs *regs = chip->page_regs; const struct rtw89_hfc_pub_cfg *cfg = &rtwdev->mac.hfc_param.pub_cfg; u32 val; int ret; @@ -632,16 +640,18 @@ static int hfc_pub_ctrl(struct rtw89_dev *rtwdev) val = u32_encode_bits(cfg->grp0, B_AX_PUBPG_G0_MASK) | u32_encode_bits(cfg->grp1, B_AX_PUBPG_G1_MASK); - rtw89_write32(rtwdev, R_AX_PUB_PAGE_CTRL1, val); + rtw89_write32(rtwdev, regs->pub_page_ctrl1, val); val = u32_encode_bits(cfg->wp_thrd, B_AX_WP_THRD_MASK); - rtw89_write32(rtwdev, R_AX_WP_PAGE_CTRL2, val); + rtw89_write32(rtwdev, regs->wp_page_ctrl2, val); return 0; } static int hfc_upd_mix_info(struct rtw89_dev *rtwdev) { + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_page_regs *regs = chip->page_regs; struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; struct rtw89_hfc_pub_cfg *pub_cfg = ¶m->pub_cfg; struct rtw89_hfc_prec_cfg *prec_cfg = ¶m->prec_cfg; @@ -653,20 +663,20 @@ static int hfc_upd_mix_info(struct rtw89_dev *rtwdev) if (ret) return ret; - val = rtw89_read32(rtwdev, R_AX_PUB_PAGE_INFO1); + val = rtw89_read32(rtwdev, regs->pub_page_info1); info->g0_used = u32_get_bits(val, B_AX_G0_USE_PG_MASK); info->g1_used = u32_get_bits(val, B_AX_G1_USE_PG_MASK); - val = rtw89_read32(rtwdev, R_AX_PUB_PAGE_INFO3); + val = rtw89_read32(rtwdev, regs->pub_page_info3); info->g0_aval = u32_get_bits(val, B_AX_G0_AVAL_PG_MASK); info->g1_aval = u32_get_bits(val, B_AX_G1_AVAL_PG_MASK); info->pub_aval = - u32_get_bits(rtw89_read32(rtwdev, R_AX_PUB_PAGE_INFO2), + u32_get_bits(rtw89_read32(rtwdev, regs->pub_page_info2), B_AX_PUB_AVAL_PG_MASK); info->wp_aval = - u32_get_bits(rtw89_read32(rtwdev, R_AX_WP_PAGE_INFO1), + u32_get_bits(rtw89_read32(rtwdev, regs->wp_page_info1), B_AX_WP_AVAL_PG_MASK); - val = rtw89_read32(rtwdev, R_AX_HCI_FC_CTRL); + val = rtw89_read32(rtwdev, regs->hci_fc_ctrl); param->en = val & B_AX_HCI_FC_EN ? 1 : 0; param->h2c_en = val & B_AX_HCI_FC_CH12_EN ? 1 : 0; param->mode = u32_get_bits(val, B_AX_HCI_FC_MODE_MASK); @@ -679,21 +689,21 @@ static int hfc_upd_mix_info(struct rtw89_dev *rtwdev) prec_cfg->wp_ch811_full_cond = u32_get_bits(val, B_AX_HCI_FC_WP_CH811_FULL_COND_MASK); - val = rtw89_read32(rtwdev, R_AX_CH_PAGE_CTRL); + val = rtw89_read32(rtwdev, regs->ch_page_ctrl); prec_cfg->ch011_prec = u32_get_bits(val, B_AX_PREC_PAGE_CH011_MASK); prec_cfg->h2c_prec = u32_get_bits(val, B_AX_PREC_PAGE_CH12_MASK); - val = rtw89_read32(rtwdev, R_AX_PUB_PAGE_CTRL2); + val = rtw89_read32(rtwdev, regs->pub_page_ctrl2); pub_cfg->pub_max = u32_get_bits(val, B_AX_PUBPG_ALL_MASK); - val = rtw89_read32(rtwdev, R_AX_WP_PAGE_CTRL1); + val = rtw89_read32(rtwdev, regs->wp_page_ctrl1); prec_cfg->wp_ch07_prec = u32_get_bits(val, B_AX_PREC_PAGE_WP_CH07_MASK); prec_cfg->wp_ch811_prec = u32_get_bits(val, B_AX_PREC_PAGE_WP_CH811_MASK); - val = rtw89_read32(rtwdev, R_AX_WP_PAGE_CTRL2); + val = rtw89_read32(rtwdev, regs->wp_page_ctrl2); pub_cfg->wp_thrd = u32_get_bits(val, B_AX_WP_THRD_MASK); - val = rtw89_read32(rtwdev, R_AX_PUB_PAGE_CTRL1); + val = rtw89_read32(rtwdev, regs->pub_page_ctrl1); pub_cfg->grp0 = u32_get_bits(val, B_AX_PUBPG_G0_MASK); pub_cfg->grp1 = u32_get_bits(val, B_AX_PUBPG_G1_MASK); @@ -706,20 +716,24 @@ static int hfc_upd_mix_info(struct rtw89_dev *rtwdev) static void hfc_h2c_cfg(struct rtw89_dev *rtwdev) { + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_page_regs *regs = chip->page_regs; struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; const struct rtw89_hfc_prec_cfg *prec_cfg = ¶m->prec_cfg; u32 val; val = u32_encode_bits(prec_cfg->h2c_prec, B_AX_PREC_PAGE_CH12_MASK); - rtw89_write32(rtwdev, R_AX_CH_PAGE_CTRL, val); + rtw89_write32(rtwdev, regs->ch_page_ctrl, val); - rtw89_write32_mask(rtwdev, R_AX_HCI_FC_CTRL, + rtw89_write32_mask(rtwdev, regs->hci_fc_ctrl, B_AX_HCI_FC_CH12_FULL_COND_MASK, prec_cfg->h2c_full_cond); } static void hfc_mix_cfg(struct rtw89_dev *rtwdev) { + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_page_regs *regs = chip->page_regs; struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; const struct rtw89_hfc_pub_cfg *pub_cfg = ¶m->pub_cfg; const struct rtw89_hfc_prec_cfg *prec_cfg = ¶m->prec_cfg; @@ -727,18 +741,18 @@ static void hfc_mix_cfg(struct rtw89_dev *rtwdev) val = u32_encode_bits(prec_cfg->ch011_prec, B_AX_PREC_PAGE_CH011_MASK) | u32_encode_bits(prec_cfg->h2c_prec, B_AX_PREC_PAGE_CH12_MASK); - rtw89_write32(rtwdev, R_AX_CH_PAGE_CTRL, val); + rtw89_write32(rtwdev, regs->ch_page_ctrl, val); val = u32_encode_bits(pub_cfg->pub_max, B_AX_PUBPG_ALL_MASK); - rtw89_write32(rtwdev, R_AX_PUB_PAGE_CTRL2, val); + rtw89_write32(rtwdev, regs->pub_page_ctrl2, val); val = u32_encode_bits(prec_cfg->wp_ch07_prec, B_AX_PREC_PAGE_WP_CH07_MASK) | u32_encode_bits(prec_cfg->wp_ch811_prec, B_AX_PREC_PAGE_WP_CH811_MASK); - rtw89_write32(rtwdev, R_AX_WP_PAGE_CTRL1, val); + rtw89_write32(rtwdev, regs->wp_page_ctrl1, val); - val = u32_replace_bits(rtw89_read32(rtwdev, R_AX_HCI_FC_CTRL), + val = u32_replace_bits(rtw89_read32(rtwdev, regs->hci_fc_ctrl), param->mode, B_AX_HCI_FC_MODE_MASK); val = u32_replace_bits(val, prec_cfg->ch011_full_cond, B_AX_HCI_FC_WD_FULL_COND_MASK); @@ -748,21 +762,23 @@ static void hfc_mix_cfg(struct rtw89_dev *rtwdev) B_AX_HCI_FC_WP_CH07_FULL_COND_MASK); val = u32_replace_bits(val, prec_cfg->wp_ch811_full_cond, B_AX_HCI_FC_WP_CH811_FULL_COND_MASK); - rtw89_write32(rtwdev, R_AX_HCI_FC_CTRL, val); + rtw89_write32(rtwdev, regs->hci_fc_ctrl, val); } static void hfc_func_en(struct rtw89_dev *rtwdev, bool en, bool h2c_en) { + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_page_regs *regs = chip->page_regs; struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; u32 val; - val = rtw89_read32(rtwdev, R_AX_HCI_FC_CTRL); + val = rtw89_read32(rtwdev, regs->hci_fc_ctrl); param->en = en; param->h2c_en = h2c_en; val = en ? (val | B_AX_HCI_FC_EN) : (val & ~B_AX_HCI_FC_EN); val = h2c_en ? (val | B_AX_HCI_FC_CH12_EN) : (val & ~B_AX_HCI_FC_CH12_EN); - rtw89_write32(rtwdev, R_AX_HCI_FC_CTRL, val); + rtw89_write32(rtwdev, regs->hci_fc_ctrl, val); } static int hfc_init(struct rtw89_dev *rtwdev, bool reset, bool en, bool h2c_en) @@ -915,23 +931,31 @@ rtw89_mac_get_req_pwr_state(struct rtw89_dev *rtwdev) } static void rtw89_mac_send_rpwm(struct rtw89_dev *rtwdev, - enum rtw89_rpwm_req_pwr_state req_pwr_state) + enum rtw89_rpwm_req_pwr_state req_pwr_state, + bool notify_wake) { u16 request; + spin_lock_bh(&rtwdev->rpwm_lock); + request = rtw89_read16(rtwdev, R_AX_RPWM); request ^= request | PS_RPWM_TOGGLE; - - rtwdev->mac.rpwm_seq_num = (rtwdev->mac.rpwm_seq_num + 1) & - RPWM_SEQ_NUM_MAX; - request |= FIELD_PREP(PS_RPWM_SEQ_NUM, rtwdev->mac.rpwm_seq_num); - request |= req_pwr_state; - if (req_pwr_state < RTW89_MAC_RPWM_REQ_PWR_STATE_CLK_GATED) - request |= PS_RPWM_ACK; + if (notify_wake) { + request |= PS_RPWM_NOTIFY_WAKE; + } else { + rtwdev->mac.rpwm_seq_num = (rtwdev->mac.rpwm_seq_num + 1) & + RPWM_SEQ_NUM_MAX; + request |= FIELD_PREP(PS_RPWM_SEQ_NUM, + rtwdev->mac.rpwm_seq_num); + if (req_pwr_state < RTW89_MAC_RPWM_REQ_PWR_STATE_CLK_GATED) + request |= PS_RPWM_ACK; + } rtw89_write16(rtwdev, rtwdev->hci.rpwm_addr, request); + + spin_unlock_bh(&rtwdev->rpwm_lock); } static int rtw89_mac_check_cpwm_state(struct rtw89_dev *rtwdev, @@ -991,7 +1015,7 @@ void rtw89_mac_power_mode_change(struct rtw89_dev *rtwdev, bool enter) else state = RTW89_MAC_RPWM_REQ_PWR_STATE_ACTIVE; - rtw89_mac_send_rpwm(rtwdev, state); + rtw89_mac_send_rpwm(rtwdev, state, false); ret = read_poll_timeout_atomic(rtw89_mac_check_cpwm_state, ret, !ret, 1000, 15000, false, rtwdev, state); if (ret) @@ -999,19 +1023,31 @@ void rtw89_mac_power_mode_change(struct rtw89_dev *rtwdev, bool enter) enter ? "entering" : "leaving"); } +void rtw89_mac_notify_wake(struct rtw89_dev *rtwdev) +{ + enum rtw89_rpwm_req_pwr_state state; + + state = rtw89_mac_get_req_pwr_state(rtwdev); + rtw89_mac_send_rpwm(rtwdev, state, true); +} + static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on) { #define PWR_ACT 1 const struct rtw89_chip_info *chip = rtwdev->chip; const struct rtw89_pwr_cfg * const *cfg_seq; + int (*cfg_func)(struct rtw89_dev *rtwdev); struct rtw89_hal *hal = &rtwdev->hal; int ret; u8 val; - if (on) + if (on) { cfg_seq = chip->pwr_on_seq; - else + cfg_func = chip->ops->pwr_on_func; + } else { cfg_seq = chip->pwr_off_seq; + cfg_func = chip->ops->pwr_off_func; + } if (test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) __rtw89_leave_ps_mode(rtwdev); @@ -1022,7 +1058,7 @@ static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on) return -EBUSY; } - ret = rtw89_mac_pwr_seq(rtwdev, cfg_seq); + ret = cfg_func ? cfg_func(rtwdev) : rtw89_mac_pwr_seq(rtwdev, cfg_seq); if (ret) return ret; @@ -1092,18 +1128,31 @@ static int cmac_func_en(struct rtw89_dev *rtwdev, u8 mac_idx, bool en) static int dmac_func_en(struct rtw89_dev *rtwdev) { + enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; u32 val32; - val32 = (B_AX_MAC_FUNC_EN | B_AX_DMAC_FUNC_EN | B_AX_MAC_SEC_EN | - B_AX_DISPATCHER_EN | B_AX_DLE_CPUIO_EN | B_AX_PKT_IN_EN | - B_AX_DMAC_TBL_EN | B_AX_PKT_BUF_EN | B_AX_STA_SCH_EN | - B_AX_TXPKT_CTRL_EN | B_AX_WD_RLS_EN | B_AX_MPDU_PROC_EN); + if (chip_id == RTL8852C) + val32 = (B_AX_MAC_FUNC_EN | B_AX_DMAC_FUNC_EN | + B_AX_MAC_SEC_EN | B_AX_DISPATCHER_EN | + B_AX_DLE_CPUIO_EN | B_AX_PKT_IN_EN | + B_AX_DMAC_TBL_EN | B_AX_PKT_BUF_EN | + B_AX_STA_SCH_EN | B_AX_TXPKT_CTRL_EN | + B_AX_WD_RLS_EN | B_AX_MPDU_PROC_EN | + B_AX_DMAC_CRPRT | B_AX_H_AXIDMA_EN); + else + val32 = (B_AX_MAC_FUNC_EN | B_AX_DMAC_FUNC_EN | + B_AX_MAC_SEC_EN | B_AX_DISPATCHER_EN | + B_AX_DLE_CPUIO_EN | B_AX_PKT_IN_EN | + B_AX_DMAC_TBL_EN | B_AX_PKT_BUF_EN | + B_AX_STA_SCH_EN | B_AX_TXPKT_CTRL_EN | + B_AX_WD_RLS_EN | B_AX_MPDU_PROC_EN | + B_AX_DMAC_CRPRT); rtw89_write32(rtwdev, R_AX_DMAC_FUNC_EN, val32); val32 = (B_AX_MAC_SEC_CLK_EN | B_AX_DISPATCHER_CLK_EN | B_AX_DLE_CPUIO_CLK_EN | B_AX_PKT_IN_CLK_EN | B_AX_STA_SCH_CLK_EN | B_AX_TXPKT_CTRL_CLK_EN | - B_AX_WD_RLS_CLK_EN); + B_AX_WD_RLS_CLK_EN | B_AX_BBRPT_CLK_EN); rtw89_write32(rtwdev, R_AX_DMAC_CLK_EN, val32); return 0; @@ -1111,7 +1160,11 @@ static int dmac_func_en(struct rtw89_dev *rtwdev) static int chip_func_en(struct rtw89_dev *rtwdev) { - rtw89_write32_set(rtwdev, R_AX_SPSLDO_ON_CTRL0, B_AX_OCP_L1_MASK); + enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; + + if (chip_id == RTL8852A) + rtw89_write32_set(rtwdev, R_AX_SPSLDO_ON_CTRL0, + B_AX_OCP_L1_MASK); return 0; } @@ -1136,49 +1189,118 @@ static int rtw89_mac_sys_init(struct rtw89_dev *rtwdev) } /* PCIE 64 */ -const struct rtw89_dle_size wde_size0 = { +const struct rtw89_dle_size rtw89_wde_size0 = { RTW89_WDE_PG_64, 4095, 1, }; +EXPORT_SYMBOL(rtw89_wde_size0); /* DLFW */ -const struct rtw89_dle_size wde_size4 = { +const struct rtw89_dle_size rtw89_wde_size4 = { RTW89_WDE_PG_64, 0, 4096, }; +EXPORT_SYMBOL(rtw89_wde_size4); + +/* 8852C DLFW */ +const struct rtw89_dle_size rtw89_wde_size18 = { + RTW89_WDE_PG_64, 0, 2048, +}; +EXPORT_SYMBOL(rtw89_wde_size18); + +/* 8852C PCIE SCC */ +const struct rtw89_dle_size rtw89_wde_size19 = { + RTW89_WDE_PG_64, 3328, 0, +}; +EXPORT_SYMBOL(rtw89_wde_size19); /* PCIE */ -const struct rtw89_dle_size ple_size0 = { +const struct rtw89_dle_size rtw89_ple_size0 = { RTW89_PLE_PG_128, 1520, 16, }; +EXPORT_SYMBOL(rtw89_ple_size0); /* DLFW */ -const struct rtw89_dle_size ple_size4 = { +const struct rtw89_dle_size rtw89_ple_size4 = { RTW89_PLE_PG_128, 64, 1472, }; +EXPORT_SYMBOL(rtw89_ple_size4); + +/* 8852C DLFW */ +const struct rtw89_dle_size rtw89_ple_size18 = { + RTW89_PLE_PG_128, 2544, 16, +}; +EXPORT_SYMBOL(rtw89_ple_size18); + +/* 8852C PCIE SCC */ +const struct rtw89_dle_size rtw89_ple_size19 = { + RTW89_PLE_PG_128, 1904, 16, +}; +EXPORT_SYMBOL(rtw89_ple_size19); /* PCIE 64 */ -const struct rtw89_wde_quota wde_qt0 = { +const struct rtw89_wde_quota rtw89_wde_qt0 = { 3792, 196, 0, 107, }; +EXPORT_SYMBOL(rtw89_wde_qt0); /* DLFW */ -const struct rtw89_wde_quota wde_qt4 = { +const struct rtw89_wde_quota rtw89_wde_qt4 = { 0, 0, 0, 0, }; +EXPORT_SYMBOL(rtw89_wde_qt4); + +/* 8852C DLFW */ +const struct rtw89_wde_quota rtw89_wde_qt17 = { + 0, 0, 0, 0, +}; +EXPORT_SYMBOL(rtw89_wde_qt17); + +/* 8852C PCIE SCC */ +const struct rtw89_wde_quota rtw89_wde_qt18 = { + 3228, 60, 0, 40, +}; +EXPORT_SYMBOL(rtw89_wde_qt18); /* PCIE SCC */ -const struct rtw89_ple_quota ple_qt4 = { +const struct rtw89_ple_quota rtw89_ple_qt4 = { 264, 0, 16, 20, 26, 13, 356, 0, 32, 40, 8, }; +EXPORT_SYMBOL(rtw89_ple_qt4); /* PCIE SCC */ -const struct rtw89_ple_quota ple_qt5 = { +const struct rtw89_ple_quota rtw89_ple_qt5 = { 264, 0, 32, 20, 64, 13, 1101, 0, 64, 128, 120, }; +EXPORT_SYMBOL(rtw89_ple_qt5); /* DLFW */ -const struct rtw89_ple_quota ple_qt13 = { +const struct rtw89_ple_quota rtw89_ple_qt13 = { 0, 0, 16, 48, 0, 0, 0, 0, 0, 0, 0 }; +EXPORT_SYMBOL(rtw89_ple_qt13); + +/* DLFW 52C */ +const struct rtw89_ple_quota rtw89_ple_qt44 = { + 0, 0, 16, 256, 0, 0, 0, 0, 0, 0, 0, 0, +}; +EXPORT_SYMBOL(rtw89_ple_qt44); + +/* DLFW 52C */ +const struct rtw89_ple_quota rtw89_ple_qt45 = { + 0, 0, 32, 256, 0, 0, 0, 0, 0, 0, 0, 0, +}; +EXPORT_SYMBOL(rtw89_ple_qt45); + +/* 8852C PCIE SCC */ +const struct rtw89_ple_quota rtw89_ple_qt46 = { + 525, 0, 16, 20, 13, 13, 178, 0, 32, 62, 8, 16, +}; +EXPORT_SYMBOL(rtw89_ple_qt46); + +/* 8852C PCIE SCC */ +const struct rtw89_ple_quota rtw89_ple_qt47 = { + 525, 0, 32, 20, 1034, 13, 1199, 0, 1053, 62, 160, 1037, +}; +EXPORT_SYMBOL(rtw89_ple_qt47); static const struct rtw89_dle_mem *get_dle_mem_cfg(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode) @@ -1334,6 +1456,8 @@ static void ple_quota_cfg(struct rtw89_dev *rtwdev, SET_QUOTA(bb_rpt, PLE, 8); SET_QUOTA(wd_rel, PLE, 9); SET_QUOTA(cpu_io, PLE, 10); + if (rtwdev->chip->chip_id == RTL8852C) + SET_QUOTA(tx_rpt, PLE, 11); } #undef SET_QUOTA @@ -1421,6 +1545,43 @@ static int dle_init(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode, return ret; } +static int preload_init_set(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx, + enum rtw89_qta_mode mode) +{ + u32 reg, max_preld_size, min_rsvd_size; + + max_preld_size = (mac_idx == RTW89_MAC_0 ? + PRELD_B0_ENT_NUM : PRELD_B1_ENT_NUM) * PRELD_AMSDU_SIZE; + reg = mac_idx == RTW89_MAC_0 ? + R_AX_TXPKTCTL_B0_PRELD_CFG0 : R_AX_TXPKTCTL_B1_PRELD_CFG0; + rtw89_write32_mask(rtwdev, reg, B_AX_B0_PRELD_USEMAXSZ_MASK, max_preld_size); + rtw89_write32_set(rtwdev, reg, B_AX_B0_PRELD_FEN); + + min_rsvd_size = PRELD_AMSDU_SIZE; + reg = mac_idx == RTW89_MAC_0 ? + R_AX_TXPKTCTL_B0_PRELD_CFG1 : R_AX_TXPKTCTL_B1_PRELD_CFG1; + rtw89_write32_mask(rtwdev, reg, B_AX_B0_PRELD_NXT_TXENDWIN_MASK, PRELD_NEXT_WND); + rtw89_write32_mask(rtwdev, reg, B_AX_B0_PRELD_NXT_RSVMINSZ_MASK, min_rsvd_size); + + return 0; +} + +static bool is_qta_poh(struct rtw89_dev *rtwdev) +{ + return rtwdev->hci.type == RTW89_HCI_TYPE_PCIE; +} + +static int preload_init(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx, + enum rtw89_qta_mode mode) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + if (chip->chip_id == RTL8852A || chip->chip_id == RTL8852B || !is_qta_poh(rtwdev)) + return 0; + + return preload_init_set(rtwdev, mac_idx, mode); +} + static bool dle_is_txq_empty(struct rtw89_dev *rtwdev) { u32 msk32; @@ -1528,6 +1689,12 @@ static int dmac_init(struct rtw89_dev *rtwdev, u8 mac_idx) return ret; } + ret = preload_init(rtwdev, RTW89_MAC_0, rtwdev->mac.qta_mode); + if (ret) { + rtw89_err(rtwdev, "[ERR]preload init %d\n", ret); + return ret; + } + ret = hfc_init(rtwdev, true, true, true); if (ret) { rtw89_err(rtwdev, "[ERR]HCI FC init %d\n", ret); @@ -2079,8 +2246,26 @@ static int rtw89_set_hw_sch_tx_en(struct rtw89_dev *rtwdev, u8 mac_idx, return 0; } +static int rtw89_set_hw_sch_tx_en_v1(struct rtw89_dev *rtwdev, u8 mac_idx, + u32 tx_en, u32 tx_en_mask) +{ + u32 reg = rtw89_mac_reg_by_idx(R_AX_CTN_DRV_TXEN, mac_idx); + u32 val; + int ret; + + ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL); + if (ret) + return ret; + + val = rtw89_read32(rtwdev, reg); + val = (val & ~tx_en_mask) | (tx_en & tx_en_mask); + rtw89_write32(rtwdev, reg, val); + + return 0; +} + int rtw89_mac_stop_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, - u16 *tx_en, enum rtw89_sch_tx_sel sel) + u32 *tx_en, enum rtw89_sch_tx_sel sel) { int ret; @@ -2089,7 +2274,8 @@ int rtw89_mac_stop_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, switch (sel) { case RTW89_SCH_TX_SEL_ALL: - ret = rtw89_set_hw_sch_tx_en(rtwdev, mac_idx, 0, 0xffff); + ret = rtw89_set_hw_sch_tx_en(rtwdev, mac_idx, 0, + B_AX_CTN_TXEN_ALL_MASK); if (ret) return ret; break; @@ -2106,7 +2292,8 @@ int rtw89_mac_stop_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, return ret; break; case RTW89_SCH_TX_SEL_MACID: - ret = rtw89_set_hw_sch_tx_en(rtwdev, mac_idx, 0, 0xffff); + ret = rtw89_set_hw_sch_tx_en(rtwdev, mac_idx, 0, + B_AX_CTN_TXEN_ALL_MASK); if (ret) return ret; break; @@ -2116,17 +2303,73 @@ int rtw89_mac_stop_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, return 0; } +EXPORT_SYMBOL(rtw89_mac_stop_sch_tx); -int rtw89_mac_resume_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, u16 tx_en) +int rtw89_mac_stop_sch_tx_v1(struct rtw89_dev *rtwdev, u8 mac_idx, + u32 *tx_en, enum rtw89_sch_tx_sel sel) { int ret; - ret = rtw89_set_hw_sch_tx_en(rtwdev, mac_idx, tx_en, 0xffff); + *tx_en = rtw89_read32(rtwdev, + rtw89_mac_reg_by_idx(R_AX_CTN_DRV_TXEN, mac_idx)); + + switch (sel) { + case RTW89_SCH_TX_SEL_ALL: + ret = rtw89_set_hw_sch_tx_en_v1(rtwdev, mac_idx, 0, + B_AX_CTN_TXEN_ALL_MASK_V1); + if (ret) + return ret; + break; + case RTW89_SCH_TX_SEL_HIQ: + ret = rtw89_set_hw_sch_tx_en_v1(rtwdev, mac_idx, + 0, B_AX_CTN_TXEN_HGQ); + if (ret) + return ret; + break; + case RTW89_SCH_TX_SEL_MG0: + ret = rtw89_set_hw_sch_tx_en_v1(rtwdev, mac_idx, + 0, B_AX_CTN_TXEN_MGQ); + if (ret) + return ret; + break; + case RTW89_SCH_TX_SEL_MACID: + ret = rtw89_set_hw_sch_tx_en_v1(rtwdev, mac_idx, 0, + B_AX_CTN_TXEN_ALL_MASK_V1); + if (ret) + return ret; + break; + default: + return 0; + } + + return 0; +} +EXPORT_SYMBOL(rtw89_mac_stop_sch_tx_v1); + +int rtw89_mac_resume_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, u32 tx_en) +{ + int ret; + + ret = rtw89_set_hw_sch_tx_en(rtwdev, mac_idx, tx_en, B_AX_CTN_TXEN_ALL_MASK); if (ret) return ret; return 0; } +EXPORT_SYMBOL(rtw89_mac_resume_sch_tx); + +int rtw89_mac_resume_sch_tx_v1(struct rtw89_dev *rtwdev, u8 mac_idx, u32 tx_en) +{ + int ret; + + ret = rtw89_set_hw_sch_tx_en_v1(rtwdev, mac_idx, tx_en, + B_AX_CTN_TXEN_ALL_MASK_V1); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(rtw89_mac_resume_sch_tx_v1); static u16 rtw89_mac_dle_buf_req(struct rtw89_dev *rtwdev, u16 buf_len, bool wd) @@ -2290,9 +2533,9 @@ static int band1_enable(struct rtw89_dev *rtwdev) int ret, i; u32 sleep_bak[4] = {0}; u32 pause_bak[4] = {0}; - u16 tx_en; + u32 tx_en; - ret = rtw89_mac_stop_sch_tx(rtwdev, 0, &tx_en, RTW89_SCH_TX_SEL_ALL); + ret = rtw89_chip_stop_sch_tx(rtwdev, 0, &tx_en, RTW89_SCH_TX_SEL_ALL); if (ret) { rtw89_err(rtwdev, "[ERR]stop sch tx %d\n", ret); return ret; @@ -2322,7 +2565,7 @@ static int band1_enable(struct rtw89_dev *rtwdev) rtw89_write32(rtwdev, R_AX_SS_MACID_PAUSE_0 + i * 4, pause_bak[i]); } - ret = rtw89_mac_resume_sch_tx(rtwdev, 0, tx_en); + ret = rtw89_chip_resume_sch_tx(rtwdev, 0, tx_en); if (ret) { rtw89_err(rtwdev, "[ERR]CMAC1 resume sch tx %d\n", ret); return ret; @@ -2516,7 +2759,11 @@ static void rtw89_mac_disable_cpu(struct rtw89_dev *rtwdev) clear_bit(RTW89_FLAG_FW_RDY, rtwdev->flags); rtw89_write32_clr(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_WCPU_EN); + rtw89_write32_clr(rtwdev, R_AX_WCPU_FW_CTRL, B_AX_WCPU_FWDL_EN | + B_AX_H2C_PATH_RDY | B_AX_FWDL_PATH_RDY); rtw89_write32_clr(rtwdev, R_AX_SYS_CLK_CTRL, B_AX_CPU_CLK_EN); + rtw89_write32_clr(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); + rtw89_write32_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); } static int rtw89_mac_enable_cpu(struct rtw89_dev *rtwdev, u8 boot_reason, @@ -2586,7 +2833,9 @@ static int rtw89_mac_fw_dl_pre_init(struct rtw89_dev *rtwdev) static void rtw89_mac_hci_func_en(struct rtw89_dev *rtwdev) { - rtw89_write32_set(rtwdev, R_AX_HCI_FUNC_EN, + const struct rtw89_chip_info *chip = rtwdev->chip; + + rtw89_write32_set(rtwdev, chip->hci_func_en_addr, B_AX_HCI_TXDMA_EN | B_AX_HCI_RXDMA_EN); } @@ -2705,7 +2954,7 @@ static void rtw89_mac_cmac_tbl_init(struct rtw89_dev *rtwdev, u8 macid) rtw89_write32(rtwdev, R_AX_INDIR_ACCESS_ENTRY + 28, 0xB8109); } -static int rtw89_set_macid_pause(struct rtw89_dev *rtwdev, u8 macid, bool pause) +int rtw89_mac_set_macid_pause(struct rtw89_dev *rtwdev, u8 macid, bool pause) { u8 sh = FIELD_GET(GENMASK(4, 0), macid); u8 grp = macid >> 5; @@ -2864,6 +3113,36 @@ static void rtw89_mac_port_cfg_bcn_intv(struct rtw89_dev *rtwdev, bcn_int); } +static void rtw89_mac_port_cfg_hiq_win(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif) +{ + static const u32 hiq_win_addr[RTW89_PORT_NUM] = { + R_AX_P0MB_HGQ_WINDOW_CFG_0, R_AX_PORT_HGQ_WINDOW_CFG, + R_AX_PORT_HGQ_WINDOW_CFG + 1, R_AX_PORT_HGQ_WINDOW_CFG + 2, + R_AX_PORT_HGQ_WINDOW_CFG + 3, + }; + u8 win = rtwvif->net_type == RTW89_NET_TYPE_AP_MODE ? 16 : 0; + u8 port = rtwvif->port; + u32 reg; + + reg = rtw89_mac_reg_by_idx(hiq_win_addr[port], rtwvif->mac_idx); + rtw89_write8(rtwdev, reg, win); +} + +static void rtw89_mac_port_cfg_hiq_dtim(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif) +{ + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); + const struct rtw89_port_reg *p = &rtw_port_base; + u32 addr; + + addr = rtw89_mac_reg_by_idx(R_AX_MD_TSFT_STMP_CTL, rtwvif->mac_idx); + rtw89_write8_set(rtwdev, addr, B_AX_UPD_HGQMD | B_AX_UPD_TIMIE); + + rtw89_write16_port_mask(rtwdev, rtwvif, p->dtim_ctrl, B_AX_DTIM_NUM_MASK, + vif->bss_conf.dtim_period); +} + static void rtw89_mac_port_cfg_bcn_setup_time(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { @@ -2978,11 +3257,11 @@ int rtw89_mac_vif_init(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) rtw89_mac_dmac_tbl_init(rtwdev, rtwvif->mac_id); rtw89_mac_cmac_tbl_init(rtwdev, rtwvif->mac_id); - ret = rtw89_set_macid_pause(rtwdev, rtwvif->mac_id, false); + ret = rtw89_mac_set_macid_pause(rtwdev, rtwvif->mac_id, false); if (ret) return ret; - ret = rtw89_fw_h2c_vif_maintain(rtwdev, rtwvif, RTW89_VIF_CREATE); + ret = rtw89_fw_h2c_role_maintain(rtwdev, rtwvif, NULL, RTW89_ROLE_CREATE); if (ret) return ret; @@ -2994,7 +3273,7 @@ int rtw89_mac_vif_init(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) if (ret) return ret; - ret = rtw89_fw_h2c_default_cmac_tbl(rtwdev, rtwvif->mac_id); + ret = rtw89_fw_h2c_default_cmac_tbl(rtwdev, rtwvif); if (ret) return ret; @@ -3005,7 +3284,7 @@ int rtw89_mac_vif_deinit(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { int ret; - ret = rtw89_fw_h2c_vif_maintain(rtwdev, rtwvif, RTW89_VIF_REMOVE); + ret = rtw89_fw_h2c_role_maintain(rtwdev, rtwvif, NULL, RTW89_ROLE_REMOVE); if (ret) return ret; @@ -3034,13 +3313,15 @@ int rtw89_mac_port_update(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) rtw89_mac_port_cfg_rx_sync(rtwdev, rtwvif); rtw89_mac_port_cfg_tx_sw(rtwdev, rtwvif); rtw89_mac_port_cfg_bcn_intv(rtwdev, rtwvif); + rtw89_mac_port_cfg_hiq_win(rtwdev, rtwvif); + rtw89_mac_port_cfg_hiq_dtim(rtwdev, rtwvif); + rtw89_mac_port_cfg_hiq_drop(rtwdev, rtwvif); rtw89_mac_port_cfg_bcn_setup_time(rtwdev, rtwvif); rtw89_mac_port_cfg_bcn_hold_time(rtwdev, rtwvif); rtw89_mac_port_cfg_bcn_mask_area(rtwdev, rtwvif); rtw89_mac_port_cfg_tbtt_early(rtwdev, rtwvif); rtw89_mac_port_cfg_bss_color(rtwdev, rtwvif); rtw89_mac_port_cfg_mbssid(rtwdev, rtwvif); - rtw89_mac_port_cfg_hiq_drop(rtwdev, rtwvif); rtw89_mac_port_cfg_func_en(rtwdev, rtwvif); fsleep(BCN_ERLY_SET_DLY); rtw89_mac_port_cfg_bcn_early(rtwdev, rtwvif); @@ -3084,6 +3365,57 @@ rtw89_mac_c2h_macid_pause(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len { } +static bool rtw89_is_op_chan(struct rtw89_dev *rtwdev, u8 band, u8 channel) +{ + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + + return band == scan_info->op_band && channel == scan_info->op_pri_ch; +} + +static void +rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *c2h, + u32 len) +{ + struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif; + struct rtw89_hal *hal = &rtwdev->hal; + u8 reason, status, tx_fail, band; + u16 chan; + + tx_fail = RTW89_GET_MAC_C2H_SCANOFLD_TX_FAIL(c2h->data); + status = RTW89_GET_MAC_C2H_SCANOFLD_STATUS(c2h->data); + chan = RTW89_GET_MAC_C2H_SCANOFLD_PRI_CH(c2h->data); + reason = RTW89_GET_MAC_C2H_SCANOFLD_RSP(c2h->data); + band = RTW89_GET_MAC_C2H_SCANOFLD_BAND(c2h->data); + + if (!(rtwdev->chip->support_bands & BIT(NL80211_BAND_6GHZ))) + band = chan > 14 ? RTW89_BAND_5G : RTW89_BAND_2G; + + rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN, + "band: %d, chan: %d, reason: %d, status: %d, tx_fail: %d\n", + band, chan, reason, status, tx_fail); + + switch (reason) { + case RTW89_SCAN_LEAVE_CH_NOTIFY: + if (rtw89_is_op_chan(rtwdev, band, chan)) + ieee80211_stop_queues(rtwdev->hw); + return; + case RTW89_SCAN_END_SCAN_NOTIFY: + rtw89_hw_scan_complete(rtwdev, vif, false); + break; + case RTW89_SCAN_ENTER_CH_NOTIFY: + if (rtw89_is_op_chan(rtwdev, band, chan)) + ieee80211_wake_queues(rtwdev->hw); + break; + default: + return; + } + + hal->prev_band_type = hal->current_band_type; + hal->prev_primary_channel = hal->current_channel; + hal->current_channel = chan; + hal->current_band_type = band; +} + static void rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) { @@ -3114,6 +3446,11 @@ rtw89_mac_c2h_log(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) RTW89_GET_C2H_LOG_SRT_PRT(c2h->data)); } +static void +rtw89_mac_c2h_bcn_cnt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ +} + static void (* const rtw89_mac_c2h_ofld_handler[])(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) = { @@ -3122,6 +3459,7 @@ void (* const rtw89_mac_c2h_ofld_handler[])(struct rtw89_dev *rtwdev, [RTW89_MAC_C2H_FUNC_PKT_OFLD_RSP] = NULL, [RTW89_MAC_C2H_FUNC_BCN_RESEND] = NULL, [RTW89_MAC_C2H_FUNC_MACID_PAUSE] = rtw89_mac_c2h_macid_pause, + [RTW89_MAC_C2H_FUNC_SCANOFLD_RSP] = rtw89_mac_c2h_scanofld_rsp, }; static @@ -3130,6 +3468,7 @@ void (* const rtw89_mac_c2h_info_handler[])(struct rtw89_dev *rtwdev, [RTW89_MAC_C2H_FUNC_REC_ACK] = rtw89_mac_c2h_rec_ack, [RTW89_MAC_C2H_FUNC_DONE_ACK] = rtw89_mac_c2h_done_ack, [RTW89_MAC_C2H_FUNC_C2H_LOG] = rtw89_mac_c2h_log, + [RTW89_MAC_C2H_FUNC_BCN_CNT] = rtw89_mac_c2h_bcn_cnt, }; void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, @@ -3192,6 +3531,7 @@ bool rtw89_mac_get_txpwr_cr(struct rtw89_dev *rtwdev, return false; } +EXPORT_SYMBOL(rtw89_mac_get_txpwr_cr); int rtw89_mac_cfg_ppdu_status(struct rtw89_dev *rtwdev, u8 mac_idx, bool enable) { @@ -3216,6 +3556,7 @@ int rtw89_mac_cfg_ppdu_status(struct rtw89_dev *rtwdev, u8 mac_idx, bool enable) return ret; } +EXPORT_SYMBOL(rtw89_mac_cfg_ppdu_status); void rtw89_mac_update_rts_threshold(struct rtw89_dev *rtwdev, u8 mac_idx) { @@ -3349,33 +3690,37 @@ int rtw89_mac_coex_init(struct rtw89_dev *rtwdev, const struct rtw89_mac_ax_coex return 0; } +EXPORT_SYMBOL(rtw89_mac_coex_init); int rtw89_mac_cfg_gnt(struct rtw89_dev *rtwdev, const struct rtw89_mac_ax_coex_gnt *gnt_cfg) { - u32 val, ret; + u32 val = 0, ret; + + if (gnt_cfg->band[0].gnt_bt) + val |= B_AX_GNT_BT_RFC_S0_SW_VAL | B_AX_GNT_BT_BB_S0_SW_VAL; + + if (gnt_cfg->band[0].gnt_bt_sw_en) + val |= B_AX_GNT_BT_RFC_S0_SW_CTRL | B_AX_GNT_BT_BB_S0_SW_CTRL; + + if (gnt_cfg->band[0].gnt_wl) + val |= B_AX_GNT_WL_RFC_S0_SW_VAL | B_AX_GNT_WL_BB_S0_SW_VAL; + + if (gnt_cfg->band[0].gnt_wl_sw_en) + val |= B_AX_GNT_WL_RFC_S0_SW_CTRL | B_AX_GNT_WL_BB_S0_SW_CTRL; + + if (gnt_cfg->band[1].gnt_bt) + val |= B_AX_GNT_BT_RFC_S1_SW_VAL | B_AX_GNT_BT_BB_S1_SW_VAL; + + if (gnt_cfg->band[1].gnt_bt_sw_en) + val |= B_AX_GNT_BT_RFC_S1_SW_CTRL | B_AX_GNT_BT_BB_S1_SW_CTRL; + + if (gnt_cfg->band[1].gnt_wl) + val |= B_AX_GNT_WL_RFC_S1_SW_VAL | B_AX_GNT_WL_BB_S1_SW_VAL; + + if (gnt_cfg->band[1].gnt_wl_sw_en) + val |= B_AX_GNT_WL_RFC_S1_SW_CTRL | B_AX_GNT_WL_BB_S1_SW_CTRL; - ret = rtw89_mac_read_lte(rtwdev, R_AX_LTE_SW_CFG_1, &val); - if (ret) { - rtw89_err(rtwdev, "Read LTE fail!\n"); - return ret; - } - val = (gnt_cfg->band[0].gnt_bt ? - B_AX_GNT_BT_RFC_S0_SW_VAL | B_AX_GNT_BT_BB_S0_SW_VAL : 0) | - (gnt_cfg->band[0].gnt_bt_sw_en ? - B_AX_GNT_BT_RFC_S0_SW_CTRL | B_AX_GNT_BT_BB_S0_SW_CTRL : 0) | - (gnt_cfg->band[0].gnt_wl ? - B_AX_GNT_WL_RFC_S0_SW_VAL | B_AX_GNT_WL_BB_S0_SW_VAL : 0) | - (gnt_cfg->band[0].gnt_wl_sw_en ? - B_AX_GNT_WL_RFC_S0_SW_CTRL | B_AX_GNT_WL_BB_S0_SW_CTRL : 0) | - (gnt_cfg->band[1].gnt_bt ? - B_AX_GNT_BT_RFC_S1_SW_VAL | B_AX_GNT_BT_BB_S1_SW_VAL : 0) | - (gnt_cfg->band[1].gnt_bt_sw_en ? - B_AX_GNT_BT_RFC_S1_SW_CTRL | B_AX_GNT_BT_BB_S1_SW_CTRL : 0) | - (gnt_cfg->band[1].gnt_wl ? - B_AX_GNT_WL_RFC_S1_SW_VAL | B_AX_GNT_WL_BB_S1_SW_VAL : 0) | - (gnt_cfg->band[1].gnt_wl_sw_en ? - B_AX_GNT_WL_RFC_S1_SW_CTRL | B_AX_GNT_WL_BB_S1_SW_CTRL : 0); ret = rtw89_mac_write_lte(rtwdev, R_AX_LTE_SW_CFG_1, val); if (ret) { rtw89_err(rtwdev, "Write LTE fail!\n"); @@ -3384,11 +3729,59 @@ int rtw89_mac_cfg_gnt(struct rtw89_dev *rtwdev, return 0; } +EXPORT_SYMBOL(rtw89_mac_cfg_gnt); + +int rtw89_mac_cfg_gnt_v1(struct rtw89_dev *rtwdev, + const struct rtw89_mac_ax_coex_gnt *gnt_cfg) +{ + u32 val = 0; + + if (gnt_cfg->band[0].gnt_bt) + val |= B_AX_GNT_BT_RFC_S0_VAL | B_AX_GNT_BT_RX_VAL | + B_AX_GNT_BT_TX_VAL; + else + val |= B_AX_WL_ACT_VAL; + + if (gnt_cfg->band[0].gnt_bt_sw_en) + val |= B_AX_GNT_BT_RFC_S0_SWCTRL | B_AX_GNT_BT_RX_SWCTRL | + B_AX_GNT_BT_TX_SWCTRL | B_AX_WL_ACT_SWCTRL; + + if (gnt_cfg->band[0].gnt_wl) + val |= B_AX_GNT_WL_RFC_S0_VAL | B_AX_GNT_WL_RX_VAL | + B_AX_GNT_WL_TX_VAL | B_AX_GNT_WL_BB_VAL; + + if (gnt_cfg->band[0].gnt_wl_sw_en) + val |= B_AX_GNT_WL_RFC_S0_SWCTRL | B_AX_GNT_WL_RX_SWCTRL | + B_AX_GNT_WL_TX_SWCTRL | B_AX_GNT_WL_BB_SWCTRL; + + if (gnt_cfg->band[1].gnt_bt) + val |= B_AX_GNT_BT_RFC_S1_VAL | B_AX_GNT_BT_RX_VAL | + B_AX_GNT_BT_TX_VAL; + else + val |= B_AX_WL_ACT_VAL; + + if (gnt_cfg->band[1].gnt_bt_sw_en) + val |= B_AX_GNT_BT_RFC_S1_SWCTRL | B_AX_GNT_BT_RX_SWCTRL | + B_AX_GNT_BT_TX_SWCTRL | B_AX_WL_ACT_SWCTRL; + + if (gnt_cfg->band[1].gnt_wl) + val |= B_AX_GNT_WL_RFC_S1_VAL | B_AX_GNT_WL_RX_VAL | + B_AX_GNT_WL_TX_VAL | B_AX_GNT_WL_BB_VAL; + + if (gnt_cfg->band[1].gnt_wl_sw_en) + val |= B_AX_GNT_WL_RFC_S1_SWCTRL | B_AX_GNT_WL_RX_SWCTRL | + B_AX_GNT_WL_TX_SWCTRL | B_AX_GNT_WL_BB_SWCTRL; + + rtw89_write32(rtwdev, R_AX_GNT_SW_CTRL, val); + + return 0; +} +EXPORT_SYMBOL(rtw89_mac_cfg_gnt_v1); int rtw89_mac_cfg_plt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_plt *plt) { u32 reg; - u8 val; + u16 val; int ret; ret = rtw89_mac_check_mac_en(rtwdev, plt->band, RTW89_CMAC_SEL); @@ -3403,8 +3796,9 @@ int rtw89_mac_cfg_plt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_plt *plt) (plt->rx & RTW89_MAC_AX_PLT_LTE_RX ? B_AX_RX_PLT_GNT_LTE_RX : 0) | (plt->rx & RTW89_MAC_AX_PLT_GNT_BT_TX ? B_AX_RX_PLT_GNT_BT_TX : 0) | (plt->rx & RTW89_MAC_AX_PLT_GNT_BT_RX ? B_AX_RX_PLT_GNT_BT_RX : 0) | - (plt->rx & RTW89_MAC_AX_PLT_GNT_WL ? B_AX_RX_PLT_GNT_WL : 0); - rtw89_write8(rtwdev, reg, val); + (plt->rx & RTW89_MAC_AX_PLT_GNT_WL ? B_AX_RX_PLT_GNT_WL : 0) | + B_AX_PLT_EN; + rtw89_write16(rtwdev, reg, val); return 0; } @@ -3442,6 +3836,28 @@ int rtw89_mac_cfg_ctrl_path(struct rtw89_dev *rtwdev, bool wl) return 0; } +EXPORT_SYMBOL(rtw89_mac_cfg_ctrl_path); + +int rtw89_mac_cfg_ctrl_path_v1(struct rtw89_dev *rtwdev, bool wl) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_mac_ax_gnt *g = dm->gnt.band; + int i; + + if (wl) + return 0; + + for (i = 0; i < RTW89_PHY_MAX; i++) { + g[i].gnt_bt_sw_en = 1; + g[i].gnt_bt = 1; + g[i].gnt_wl_sw_en = 1; + g[i].gnt_wl = 0; + } + + return rtw89_mac_cfg_gnt_v1(rtwdev, &dm->gnt); +} +EXPORT_SYMBOL(rtw89_mac_cfg_ctrl_path_v1); bool rtw89_mac_get_ctrl_path(struct rtw89_dev *rtwdev) { @@ -3845,3 +4261,51 @@ int rtw89_mac_set_hw_muedca_ctrl(struct rtw89_dev *rtwdev, return 0; } + +int rtw89_mac_write_xtal_si(struct rtw89_dev *rtwdev, u8 offset, u8 val, u8 mask) +{ + u32 val32; + int ret; + + val32 = FIELD_PREP(B_AX_WL_XTAL_SI_ADDR_MASK, offset) | + FIELD_PREP(B_AX_WL_XTAL_SI_DATA_MASK, val) | + FIELD_PREP(B_AX_WL_XTAL_SI_BITMASK_MASK, mask) | + FIELD_PREP(B_AX_WL_XTAL_SI_MODE_MASK, XTAL_SI_NORMAL_WRITE) | + FIELD_PREP(B_AX_WL_XTAL_SI_CMD_POLL, 1); + rtw89_write32(rtwdev, R_AX_WLAN_XTAL_SI_CTRL, val32); + + ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_AX_WL_XTAL_SI_CMD_POLL), + 50, 50000, false, rtwdev, R_AX_WLAN_XTAL_SI_CTRL); + if (ret) { + rtw89_warn(rtwdev, "xtal si not ready(W): offset=%x val=%x mask=%x\n", + offset, val, mask); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(rtw89_mac_write_xtal_si); + +int rtw89_mac_read_xtal_si(struct rtw89_dev *rtwdev, u8 offset, u8 *val) +{ + u32 val32; + int ret; + + val32 = FIELD_PREP(B_AX_WL_XTAL_SI_ADDR_MASK, offset) | + FIELD_PREP(B_AX_WL_XTAL_SI_DATA_MASK, 0x00) | + FIELD_PREP(B_AX_WL_XTAL_SI_BITMASK_MASK, 0x00) | + FIELD_PREP(B_AX_WL_XTAL_SI_MODE_MASK, XTAL_SI_NORMAL_READ) | + FIELD_PREP(B_AX_WL_XTAL_SI_CMD_POLL, 1); + rtw89_write32(rtwdev, R_AX_WLAN_XTAL_SI_CTRL, val32); + + ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_AX_WL_XTAL_SI_CMD_POLL), + 50, 50000, false, rtwdev, R_AX_WLAN_XTAL_SI_CTRL); + if (ret) { + rtw89_warn(rtwdev, "xtal si not ready(R): offset=%x\n", offset); + return ret; + } + + *val = rtw89_read8(rtwdev, R_AX_WLAN_XTAL_SI_CTRL + 1); + + return 0; +} diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index b7d13edf7dd1..b797667c78c6 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -301,6 +301,7 @@ enum rtw89_mac_c2h_ofld_func { RTW89_MAC_C2H_FUNC_PKT_OFLD_RSP, RTW89_MAC_C2H_FUNC_BCN_RESEND, RTW89_MAC_C2H_FUNC_MACID_PAUSE, + RTW89_MAC_C2H_FUNC_SCANOFLD_RSP = 0x9, RTW89_MAC_C2H_FUNC_OFLD_MAX, }; @@ -308,6 +309,7 @@ enum rtw89_mac_c2h_info_func { RTW89_MAC_C2H_FUNC_REC_ACK, RTW89_MAC_C2H_FUNC_DONE_ACK, RTW89_MAC_C2H_FUNC_C2H_LOG, + RTW89_MAC_C2H_FUNC_BCN_CNT, RTW89_MAC_C2H_FUNC_INFO_MAX, }; @@ -670,16 +672,26 @@ enum mac_ax_err_info { MAC_AX_SET_ERR_MAX, }; -extern const struct rtw89_hfc_prec_cfg rtw_hfc_preccfg_pcie; -extern const struct rtw89_dle_size wde_size0; -extern const struct rtw89_dle_size wde_size4; -extern const struct rtw89_dle_size ple_size0; -extern const struct rtw89_dle_size ple_size4; -extern const struct rtw89_wde_quota wde_qt0; -extern const struct rtw89_wde_quota wde_qt4; -extern const struct rtw89_ple_quota ple_qt4; -extern const struct rtw89_ple_quota ple_qt5; -extern const struct rtw89_ple_quota ple_qt13; +extern const struct rtw89_hfc_prec_cfg rtw89_hfc_preccfg_pcie; +extern const struct rtw89_dle_size rtw89_wde_size0; +extern const struct rtw89_dle_size rtw89_wde_size4; +extern const struct rtw89_dle_size rtw89_wde_size18; +extern const struct rtw89_dle_size rtw89_wde_size19; +extern const struct rtw89_dle_size rtw89_ple_size0; +extern const struct rtw89_dle_size rtw89_ple_size4; +extern const struct rtw89_dle_size rtw89_ple_size18; +extern const struct rtw89_dle_size rtw89_ple_size19; +extern const struct rtw89_wde_quota rtw89_wde_qt0; +extern const struct rtw89_wde_quota rtw89_wde_qt4; +extern const struct rtw89_wde_quota rtw89_wde_qt17; +extern const struct rtw89_wde_quota rtw89_wde_qt18; +extern const struct rtw89_ple_quota rtw89_ple_qt4; +extern const struct rtw89_ple_quota rtw89_ple_qt5; +extern const struct rtw89_ple_quota rtw89_ple_qt13; +extern const struct rtw89_ple_quota rtw89_ple_qt44; +extern const struct rtw89_ple_quota rtw89_ple_qt45; +extern const struct rtw89_ple_quota rtw89_ple_qt46; +extern const struct rtw89_ple_quota rtw89_ple_qt47; static inline u32 rtw89_mac_reg_by_idx(u32 reg_base, u8 band) { @@ -779,24 +791,31 @@ void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 len, u8 class, u8 func); int rtw89_mac_setup_phycap(struct rtw89_dev *rtwdev); int rtw89_mac_stop_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, - u16 *tx_en, enum rtw89_sch_tx_sel sel); -int rtw89_mac_resume_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, u16 tx_en); + u32 *tx_en, enum rtw89_sch_tx_sel sel); +int rtw89_mac_stop_sch_tx_v1(struct rtw89_dev *rtwdev, u8 mac_idx, + u32 *tx_en, enum rtw89_sch_tx_sel sel); +int rtw89_mac_resume_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, u32 tx_en); +int rtw89_mac_resume_sch_tx_v1(struct rtw89_dev *rtwdev, u8 mac_idx, u32 tx_en); int rtw89_mac_cfg_ppdu_status(struct rtw89_dev *rtwdev, u8 mac_ids, bool enable); void rtw89_mac_update_rts_threshold(struct rtw89_dev *rtwdev, u8 mac_idx); void rtw89_mac_flush_txq(struct rtw89_dev *rtwdev, u32 queues, bool drop); int rtw89_mac_coex_init(struct rtw89_dev *rtwdev, const struct rtw89_mac_ax_coex *coex); int rtw89_mac_cfg_gnt(struct rtw89_dev *rtwdev, const struct rtw89_mac_ax_coex_gnt *gnt_cfg); +int rtw89_mac_cfg_gnt_v1(struct rtw89_dev *rtwdev, + const struct rtw89_mac_ax_coex_gnt *gnt_cfg); int rtw89_mac_cfg_plt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_plt *plt); u16 rtw89_mac_get_plt_cnt(struct rtw89_dev *rtwdev, u8 band); void rtw89_mac_cfg_sb(struct rtw89_dev *rtwdev, u32 val); u32 rtw89_mac_get_sb(struct rtw89_dev *rtwdev); bool rtw89_mac_get_ctrl_path(struct rtw89_dev *rtwdev); int rtw89_mac_cfg_ctrl_path(struct rtw89_dev *rtwdev, bool wl); +int rtw89_mac_cfg_ctrl_path_v1(struct rtw89_dev *rtwdev, bool wl); bool rtw89_mac_get_txpwr_cr(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, u32 reg_base, u32 *cr); void rtw89_mac_power_mode_change(struct rtw89_dev *rtwdev, bool enter); +void rtw89_mac_notify_wake(struct rtw89_dev *rtwdev); void rtw89_mac_bf_assoc(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); void rtw89_mac_bf_disassoc(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, @@ -810,6 +829,7 @@ int rtw89_mac_vif_init(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); int rtw89_mac_vif_deinit(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); int rtw89_mac_set_hw_muedca_ctrl(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, bool en); +int rtw89_mac_set_macid_pause(struct rtw89_dev *rtwdev, u8 macid, bool pause); static inline void rtw89_mac_bf_monitor_track(struct rtw89_dev *rtwdev) { @@ -868,4 +888,44 @@ int rtw89_mac_set_tx_retry_limit(struct rtw89_dev *rtwdev, int rtw89_mac_get_tx_retry_limit(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, u8 *tx_retry); +enum rtw89_mac_xtal_si_offset { + XTAL_SI_XTAL_SC_XI = 0x04, +#define XTAL_SC_XI_MASK GENMASK(7, 0) + XTAL_SI_XTAL_SC_XO = 0x05, +#define XTAL_SC_XO_MASK GENMASK(7, 0) + XTAL_SI_PWR_CUT = 0x10, +#define XTAL_SI_SMALL_PWR_CUT BIT(0) +#define XTAL_SI_BIG_PWR_CUT BIT(1) + XTAL_SI_XTAL_XMD_2 = 0x24, +#define XTAL_SI_LDO_LPS GENMASK(6, 4) + XTAL_SI_XTAL_XMD_4 = 0x26, +#define XTAL_SI_LPS_CAP GENMASK(3, 0) + XTAL_SI_CV = 0x41, + XTAL_SI_LOW_ADDR = 0x62, +#define XTAL_SI_LOW_ADDR_MASK GENMASK(7, 0) + XTAL_SI_CTRL = 0x63, +#define XTAL_SI_MODE_SEL_MASK GENMASK(7, 6) +#define XTAL_SI_RDY BIT(5) +#define XTAL_SI_HIGH_ADDR_MASK GENMASK(2, 0) + XTAL_SI_READ_VAL = 0x7A, + XTAL_SI_WL_RFC_S0 = 0x80, +#define XTAL_SI_RF00 BIT(0) + XTAL_SI_WL_RFC_S1 = 0x81, +#define XTAL_SI_RF10 BIT(0) + XTAL_SI_ANAPAR_WL = 0x90, +#define XTAL_SI_SRAM2RFC BIT(7) +#define XTAL_SI_GND_SHDN_WL BIT(6) +#define XTAL_SI_SHDN_WL BIT(5) +#define XTAL_SI_RFC2RF BIT(4) +#define XTAL_SI_OFF_EI BIT(3) +#define XTAL_SI_OFF_WEI BIT(2) +#define XTAL_SI_PON_EI BIT(1) +#define XTAL_SI_PON_WEI BIT(0) + XTAL_SI_SRAM_CTRL = 0xA1, +#define FULL_BIT_MASK GENMASK(7, 0) +}; + +int rtw89_mac_write_xtal_si(struct rtw89_dev *rtwdev, u8 offset, u8 val, u8 mask); +int rtw89_mac_read_xtal_si(struct rtw89_dev *rtwdev, u8 offset, u8 *val); + #endif diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index a322259f4cc4..fca9f82bb462 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -66,6 +66,9 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed) { struct rtw89_dev *rtwdev = hw->priv; + /* let previous ips work finish to ensure we don't leave ips twice */ + cancel_work_sync(&rtwdev->ips_work); + mutex_lock(&rtwdev->mutex); rtw89_leave_ps_mode(rtwdev); @@ -102,14 +105,16 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, int ret = 0; mutex_lock(&rtwdev->mutex); + rtwvif->rtwdev = rtwdev; list_add_tail(&rtwvif->list, &rtwdev->rtwvifs_list); + INIT_WORK(&rtwvif->update_beacon_work, rtw89_core_update_beacon_work); rtw89_leave_ps_mode(rtwdev); rtw89_traffic_stats_init(rtwdev, &rtwvif->stats); rtw89_vif_type_mapping(vif, false); rtwvif->port = rtw89_core_acquire_bit_map(rtwdev->hw_port, - RTW89_MAX_HW_PORT_NUM); - if (rtwvif->port == RTW89_MAX_HW_PORT_NUM) { + RTW89_PORT_NUM); + if (rtwvif->port == RTW89_PORT_NUM) { ret = -ENOSPC; goto out; } @@ -141,6 +146,8 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + cancel_work_sync(&rtwvif->update_beacon_work); + mutex_lock(&rtwdev->mutex); rtw89_leave_ps_mode(rtwdev); rtw89_btc_ntfy_role_info(rtwdev, rtwvif, NULL, BTC_ROLE_STOP); @@ -161,7 +168,7 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, rtw89_leave_ps_mode(rtwdev); *new_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_FCSFAIL | - FIF_BCN_PRBRESP_PROMISC; + FIF_BCN_PRBRESP_PROMISC | FIF_PROBE_REQ; if (changed_flags & FIF_ALLMULTI) { if (*new_flags & FIF_ALLMULTI) @@ -192,6 +199,15 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, rtwdev->hal.rx_fltr |= B_AX_A_A1_MATCH; } } + if (changed_flags & FIF_PROBE_REQ) { + if (*new_flags & FIF_PROBE_REQ) { + rtwdev->hal.rx_fltr &= ~B_AX_A_BC_CAM_MATCH; + rtwdev->hal.rx_fltr &= ~B_AX_A_UC_CAM_MATCH; + } else { + rtwdev->hal.rx_fltr |= B_AX_A_BC_CAM_MATCH; + rtwdev->hal.rx_fltr |= B_AX_A_UC_CAM_MATCH; + } + } rtw89_write32_mask(rtwdev, rtw89_mac_reg_by_idx(R_AX_RX_FLTR_OPT, RTW89_MAC_0), @@ -311,6 +327,9 @@ static void rtw89_station_mode_sta_assoc(struct rtw89_dev *rtwdev, rtw89_err(rtwdev, "can't find sta to set sta_assoc state\n"); return; } + + rtw89_vif_type_mapping(vif, true); + rtw89_core_sta_assoc(rtwdev, vif, sta); } @@ -331,6 +350,13 @@ static void rtw89_ops_bss_info_changed(struct ieee80211_hw *hw, rtw89_phy_set_bss_color(rtwdev, vif); rtw89_chip_cfg_txpwr_ul_tb_offset(rtwdev, vif); rtw89_mac_port_update(rtwdev, rtwvif); + rtw89_store_op_chan(rtwdev); + } else { + /* Abort ongoing scan if cancel_scan isn't issued + * when disconnected by peer + */ + if (rtwdev->scanning) + rtw89_hw_scan_abort(rtwdev, vif); } } @@ -340,6 +366,9 @@ static void rtw89_ops_bss_info_changed(struct ieee80211_hw *hw, rtw89_fw_h2c_cam(rtwdev, rtwvif, NULL, NULL); } + if (changed & BSS_CHANGED_BEACON) + rtw89_fw_h2c_update_beacon(rtwdev, rtwvif); + if (changed & BSS_CHANGED_ERP_SLOT) rtw89_conf_tx(rtwdev, rtwvif); @@ -352,6 +381,49 @@ static void rtw89_ops_bss_info_changed(struct ieee80211_hw *hw, mutex_unlock(&rtwdev->mutex); } +static int rtw89_ops_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct rtw89_dev *rtwdev = hw->priv; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + + mutex_lock(&rtwdev->mutex); + ether_addr_copy(rtwvif->bssid, vif->bss_conf.bssid); + rtw89_cam_bssid_changed(rtwdev, rtwvif); + rtw89_mac_port_update(rtwdev, rtwvif); + rtw89_fw_h2c_assoc_cmac_tbl(rtwdev, vif, NULL); + rtw89_fw_h2c_role_maintain(rtwdev, rtwvif, NULL, RTW89_ROLE_TYPE_CHANGE); + rtw89_fw_h2c_join_info(rtwdev, rtwvif, NULL, true); + rtw89_fw_h2c_cam(rtwdev, rtwvif, NULL, NULL); + rtw89_chip_rfk_channel(rtwdev); + mutex_unlock(&rtwdev->mutex); + + return 0; +} + +static +void rtw89_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct rtw89_dev *rtwdev = hw->priv; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + + mutex_lock(&rtwdev->mutex); + rtw89_fw_h2c_assoc_cmac_tbl(rtwdev, vif, NULL); + rtw89_fw_h2c_join_info(rtwdev, rtwvif, NULL, true); + mutex_unlock(&rtwdev->mutex); +} + +static int rtw89_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + bool set) +{ + struct rtw89_dev *rtwdev = hw->priv; + struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; + struct rtw89_vif *rtwvif = rtwsta->rtwvif; + + ieee80211_queue_work(rtwdev->hw, &rtwvif->update_beacon_work); + + return 0; +} + static int rtw89_ops_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 ac, const struct ieee80211_tx_queue_params *params) @@ -476,7 +548,6 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw, case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: mutex_lock(&rtwdev->mutex); clear_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags); - rtw89_fw_h2c_ba_cam(rtwdev, false, rtwsta->mac_id, params); mutex_unlock(&rtwdev->mutex); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; @@ -486,11 +557,17 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw, rtwsta->ampdu_params[tid].agg_num = params->buf_size; rtwsta->ampdu_params[tid].amsdu = params->amsdu; rtw89_leave_ps_mode(rtwdev); - rtw89_fw_h2c_ba_cam(rtwdev, true, rtwsta->mac_id, params); mutex_unlock(&rtwdev->mutex); break; case IEEE80211_AMPDU_RX_START: + mutex_lock(&rtwdev->mutex); + rtw89_fw_h2c_ba_cam(rtwdev, rtwsta, true, params); + mutex_unlock(&rtwdev->mutex); + break; case IEEE80211_AMPDU_RX_STOP: + mutex_lock(&rtwdev->mutex); + rtw89_fw_h2c_ba_cam(rtwdev, rtwsta, false, params); + mutex_unlock(&rtwdev->mutex); break; default: WARN_ON(1); @@ -617,15 +694,9 @@ static void rtw89_ops_sw_scan_start(struct ieee80211_hw *hw, { struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; - struct rtw89_hal *hal = &rtwdev->hal; mutex_lock(&rtwdev->mutex); - rtwdev->scanning = true; - rtw89_leave_lps(rtwdev); - rtw89_btc_ntfy_scan_start(rtwdev, RTW89_PHY_0, hal->current_band_type); - rtw89_chip_rfk_scan(rtwdev, true); - rtw89_hci_recalc_int_mit(rtwdev); - rtw89_fw_h2c_cam(rtwdev, rtwvif, NULL, mac_addr); + rtw89_core_scan_start(rtwdev, rtwvif, mac_addr, false); mutex_unlock(&rtwdev->mutex); } @@ -633,14 +704,9 @@ static void rtw89_ops_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtw89_dev *rtwdev = hw->priv; - struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; mutex_lock(&rtwdev->mutex); - rtw89_fw_h2c_cam(rtwdev, rtwvif, NULL, NULL); - rtw89_chip_rfk_scan(rtwdev, false); - rtw89_btc_ntfy_scan_finish(rtwdev, RTW89_PHY_0); - rtwdev->scanning = false; - rtwdev->dig.bypass_dig = true; + rtw89_core_scan_complete(rtwdev, vif, false); mutex_unlock(&rtwdev->mutex); } @@ -653,6 +719,46 @@ static void rtw89_ops_reconfig_complete(struct ieee80211_hw *hw, rtw89_ser_recfg_done(rtwdev); } +static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *req) +{ + struct rtw89_dev *rtwdev = hw->priv; + int ret = 0; + + if (!rtwdev->fw.scan_offload) + return 1; + + if (rtwdev->scanning) + return -EBUSY; + + mutex_lock(&rtwdev->mutex); + rtw89_hw_scan_start(rtwdev, vif, req); + ret = rtw89_hw_scan_offload(rtwdev, vif, true); + if (ret) { + rtw89_hw_scan_abort(rtwdev, vif); + rtw89_err(rtwdev, "HW scan failed with status: %d\n", ret); + } + mutex_unlock(&rtwdev->mutex); + + return ret; +} + +static void rtw89_ops_cancel_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct rtw89_dev *rtwdev = hw->priv; + + if (!rtwdev->fw.scan_offload) + return; + + if (!rtwdev->scanning) + return; + + mutex_lock(&rtwdev->mutex); + rtw89_hw_scan_abort(rtwdev, vif); + mutex_unlock(&rtwdev->mutex); +} + const struct ieee80211_ops rtw89_ops = { .tx = rtw89_ops_tx, .wake_tx_queue = rtw89_ops_wake_tx_queue, @@ -663,6 +769,9 @@ const struct ieee80211_ops rtw89_ops = { .remove_interface = rtw89_ops_remove_interface, .configure_filter = rtw89_ops_configure_filter, .bss_info_changed = rtw89_ops_bss_info_changed, + .start_ap = rtw89_ops_start_ap, + .stop_ap = rtw89_ops_stop_ap, + .set_tim = rtw89_ops_set_tim, .conf_tx = rtw89_ops_conf_tx, .sta_state = rtw89_ops_sta_state, .set_key = rtw89_ops_set_key, @@ -676,6 +785,8 @@ const struct ieee80211_ops rtw89_ops = { .sw_scan_start = rtw89_ops_sw_scan_start, .sw_scan_complete = rtw89_ops_sw_scan_complete, .reconfig_complete = rtw89_ops_reconfig_complete, + .hw_scan = rtw89_ops_hw_scan, + .cancel_hw_scan = rtw89_ops_cancel_hw_scan, .set_sar_specs = rtw89_ops_set_sar_specs, }; EXPORT_SYMBOL(rtw89_ops); diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 2c94762e4f93..e79bfc335b44 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -62,7 +62,7 @@ static u32 rtw89_pci_txbd_recalc(struct rtw89_dev *rtwdev, struct rtw89_pci_tx_ring *tx_ring) { struct rtw89_pci_dma_ring *bd_ring = &tx_ring->bd_ring; - u32 addr_idx = bd_ring->addr_idx; + u32 addr_idx = bd_ring->addr.idx; u32 cnt, idx; idx = rtw89_read32(rtwdev, addr_idx); @@ -121,7 +121,7 @@ static u32 rtw89_pci_rxbd_recalc(struct rtw89_dev *rtwdev, struct rtw89_pci_rx_ring *rx_ring) { struct rtw89_pci_dma_ring *bd_ring = &rx_ring->bd_ring; - u32 addr_idx = bd_ring->addr_idx; + u32 addr_idx = bd_ring->addr.idx; u32 cnt, idx; idx = rtw89_read32(rtwdev, addr_idx); @@ -304,7 +304,7 @@ static void rtw89_pci_rxbd_deliver(struct rtw89_dev *rtwdev, cnt -= rx_cnt; } - rtw89_write16(rtwdev, bd_ring->addr_idx, bd_ring->wp); + rtw89_write16(rtwdev, bd_ring->addr.idx, bd_ring->wp); } static int rtw89_pci_poll_rxq_dma(struct rtw89_dev *rtwdev, @@ -555,7 +555,7 @@ static void rtw89_pci_release_tx(struct rtw89_dev *rtwdev, cnt -= release_cnt; } - rtw89_write16(rtwdev, bd_ring->addr_idx, bd_ring->wp); + rtw89_write16(rtwdev, bd_ring->addr.idx, bd_ring->wp); } static int rtw89_pci_poll_rpq_dma(struct rtw89_dev *rtwdev, @@ -598,7 +598,7 @@ static void rtw89_pci_isr_rxd_unavail(struct rtw89_dev *rtwdev, rx_ring = &rtwpci->rx_rings[i]; bd_ring = &rx_ring->bd_ring; - reg_idx = rtw89_read32(rtwdev, bd_ring->addr_idx); + reg_idx = rtw89_read32(rtwdev, bd_ring->addr.idx); hw_idx = FIELD_GET(TXBD_HW_IDX_MASK, reg_idx); host_idx = FIELD_GET(TXBD_HOST_IDX_MASK, reg_idx); hw_idx_next = (hw_idx + 1) % bd_ring->len; @@ -697,71 +697,110 @@ static irqreturn_t rtw89_pci_interrupt_handler(int irq, void *dev) return irqret; } -#define case_TXCHADDRS(txch) \ - case RTW89_TXCH_##txch: \ - *addr_num = R_AX_##txch##_TXBD_NUM; \ - *addr_idx = R_AX_##txch##_TXBD_IDX; \ - *addr_bdram = R_AX_##txch##_BDRAM_CTRL; \ - *addr_desa_l = R_AX_##txch##_TXBD_DESA_L; \ - *addr_desa_h = R_AX_##txch##_TXBD_DESA_H; \ - break - -static int rtw89_pci_get_txch_addrs(enum rtw89_tx_channel txch, - u32 *addr_num, - u32 *addr_idx, - u32 *addr_bdram, - u32 *addr_desa_l, - u32 *addr_desa_h) -{ - switch (txch) { - case_TXCHADDRS(ACH0); - case_TXCHADDRS(ACH1); - case_TXCHADDRS(ACH2); - case_TXCHADDRS(ACH3); - case_TXCHADDRS(ACH4); - case_TXCHADDRS(ACH5); - case_TXCHADDRS(ACH6); - case_TXCHADDRS(ACH7); - case_TXCHADDRS(CH8); - case_TXCHADDRS(CH9); - case_TXCHADDRS(CH10); - case_TXCHADDRS(CH11); - case_TXCHADDRS(CH12); - default: - return -EINVAL; +#define DEF_TXCHADDRS_TYPE1(info, txch, v...) \ + [RTW89_TXCH_##txch] = { \ + .num = R_AX_##txch##_TXBD_NUM ##v, \ + .idx = R_AX_##txch##_TXBD_IDX ##v, \ + .bdram = R_AX_##txch##_BDRAM_CTRL ##v, \ + .desa_l = R_AX_##txch##_TXBD_DESA_L ##v, \ + .desa_h = R_AX_##txch##_TXBD_DESA_H ##v, \ } +#define DEF_TXCHADDRS(info, txch, v...) \ + [RTW89_TXCH_##txch] = { \ + .num = R_AX_##txch##_TXBD_NUM, \ + .idx = R_AX_##txch##_TXBD_IDX, \ + .bdram = R_AX_##txch##_BDRAM_CTRL ##v, \ + .desa_l = R_AX_##txch##_TXBD_DESA_L ##v, \ + .desa_h = R_AX_##txch##_TXBD_DESA_H ##v, \ + } + +#define DEF_RXCHADDRS(info, rxch, v...) \ + [RTW89_RXCH_##rxch] = { \ + .num = R_AX_##rxch##_RXBD_NUM ##v, \ + .idx = R_AX_##rxch##_RXBD_IDX ##v, \ + .desa_l = R_AX_##rxch##_RXBD_DESA_L ##v, \ + .desa_h = R_AX_##rxch##_RXBD_DESA_H ##v, \ + } + +const struct rtw89_pci_ch_dma_addr_set rtw89_pci_ch_dma_addr_set = { + .tx = { + DEF_TXCHADDRS(info, ACH0), + DEF_TXCHADDRS(info, ACH1), + DEF_TXCHADDRS(info, ACH2), + DEF_TXCHADDRS(info, ACH3), + DEF_TXCHADDRS(info, ACH4), + DEF_TXCHADDRS(info, ACH5), + DEF_TXCHADDRS(info, ACH6), + DEF_TXCHADDRS(info, ACH7), + DEF_TXCHADDRS(info, CH8), + DEF_TXCHADDRS(info, CH9), + DEF_TXCHADDRS_TYPE1(info, CH10), + DEF_TXCHADDRS_TYPE1(info, CH11), + DEF_TXCHADDRS(info, CH12), + }, + .rx = { + DEF_RXCHADDRS(info, RXQ), + DEF_RXCHADDRS(info, RPQ), + }, +}; +EXPORT_SYMBOL(rtw89_pci_ch_dma_addr_set); + +const struct rtw89_pci_ch_dma_addr_set rtw89_pci_ch_dma_addr_set_v1 = { + .tx = { + DEF_TXCHADDRS(info, ACH0, _V1), + DEF_TXCHADDRS(info, ACH1, _V1), + DEF_TXCHADDRS(info, ACH2, _V1), + DEF_TXCHADDRS(info, ACH3, _V1), + DEF_TXCHADDRS(info, ACH4, _V1), + DEF_TXCHADDRS(info, ACH5, _V1), + DEF_TXCHADDRS(info, ACH6, _V1), + DEF_TXCHADDRS(info, ACH7, _V1), + DEF_TXCHADDRS(info, CH8, _V1), + DEF_TXCHADDRS(info, CH9, _V1), + DEF_TXCHADDRS_TYPE1(info, CH10, _V1), + DEF_TXCHADDRS_TYPE1(info, CH11, _V1), + DEF_TXCHADDRS(info, CH12, _V1), + }, + .rx = { + DEF_RXCHADDRS(info, RXQ, _V1), + DEF_RXCHADDRS(info, RPQ, _V1), + }, +}; +EXPORT_SYMBOL(rtw89_pci_ch_dma_addr_set_v1); + +#undef DEF_TXCHADDRS_TYPE1 +#undef DEF_TXCHADDRS +#undef DEF_RXCHADDRS + +static int rtw89_pci_get_txch_addrs(struct rtw89_dev *rtwdev, + enum rtw89_tx_channel txch, + const struct rtw89_pci_ch_dma_addr **addr) +{ + const struct rtw89_pci_info *info = rtwdev->pci_info; + + if (txch >= RTW89_TXCH_NUM) + return -EINVAL; + + *addr = &info->dma_addr_set->tx[txch]; + return 0; } -#undef case_TXCHADDRS - -#define case_RXCHADDRS(rxch) \ - case RTW89_RXCH_##rxch: \ - *addr_num = R_AX_##rxch##_RXBD_NUM; \ - *addr_idx = R_AX_##rxch##_RXBD_IDX; \ - *addr_desa_l = R_AX_##rxch##_RXBD_DESA_L; \ - *addr_desa_h = R_AX_##rxch##_RXBD_DESA_H; \ - break - -static int rtw89_pci_get_rxch_addrs(enum rtw89_rx_channel rxch, - u32 *addr_num, - u32 *addr_idx, - u32 *addr_desa_l, - u32 *addr_desa_h) +static int rtw89_pci_get_rxch_addrs(struct rtw89_dev *rtwdev, + enum rtw89_rx_channel rxch, + const struct rtw89_pci_ch_dma_addr **addr) { - switch (rxch) { - case_RXCHADDRS(RXQ); - case_RXCHADDRS(RPQ); - default: + const struct rtw89_pci_info *info = rtwdev->pci_info; + + if (rxch >= RTW89_RXCH_NUM) return -EINVAL; - } + + *addr = &info->dma_addr_set->rx[rxch]; return 0; } -#undef case_RXCHADDRS - static u32 rtw89_pci_get_avail_txbd_num(struct rtw89_pci_tx_ring *ring) { struct rtw89_pci_dma_ring *bd_ring = &ring->bd_ring; @@ -837,7 +876,7 @@ static void __rtw89_pci_tx_kick_off(struct rtw89_dev *rtwdev, struct rtw89_pci_t struct rtw89_pci_dma_ring *bd_ring = &tx_ring->bd_ring; u32 host_idx, addr; - addr = bd_ring->addr_idx; + addr = bd_ring->addr.idx; host_idx = bd_ring->wp; rtw89_write16(rtwdev, addr, host_idx); } @@ -879,7 +918,7 @@ static void __pci_flush_txch(struct rtw89_dev *rtwdev, u8 txch, bool drop) * just use for loop with udelay here. */ for (i = 0; i < 60; i++) { - cur_idx = rtw89_read32(rtwdev, bd_ring->addr_idx); + cur_idx = rtw89_read32(rtwdev, bd_ring->addr.idx); cur_rp = FIELD_GET(TXBD_HW_IDX_MASK, cur_idx); if (cur_rp == bd_ring->wp) return; @@ -1140,9 +1179,9 @@ static void rtw89_pci_reset_trx_rings(struct rtw89_dev *rtwdev) tx_ring = &rtwpci->tx_rings[i]; bd_ring = &tx_ring->bd_ring; bd_ram = &bd_ram_table[i]; - addr_num = bd_ring->addr_num; - addr_bdram = bd_ring->addr_bdram; - addr_desa_l = bd_ring->addr_desa_l; + addr_num = bd_ring->addr.num; + addr_bdram = bd_ring->addr.bdram; + addr_desa_l = bd_ring->addr.desa_l; bd_ring->wp = 0; bd_ring->rp = 0; @@ -1158,8 +1197,8 @@ static void rtw89_pci_reset_trx_rings(struct rtw89_dev *rtwdev) for (i = 0; i < RTW89_RXCH_NUM; i++) { rx_ring = &rtwpci->rx_rings[i]; bd_ring = &rx_ring->bd_ring; - addr_num = bd_ring->addr_num; - addr_desa_l = bd_ring->addr_desa_l; + addr_num = bd_ring->addr.num; + addr_desa_l = bd_ring->addr.desa_l; bd_ring->wp = 0; bd_ring->rp = 0; rx_ring->diliver_skb = NULL; @@ -1413,79 +1452,52 @@ static int rtw89_write16_mdio_clr(struct rtw89_dev *rtwdev, u8 addr, u16 mask, u return 0; } -static int rtw89_dbi_write8(struct rtw89_dev *rtwdev, u16 addr, u8 data) +static int rtw89_pci_write_config_byte(struct rtw89_dev *rtwdev, u16 addr, + u8 data) { - u16 write_addr; - u16 remainder = addr & ~(B_AX_DBI_ADDR_MSK | B_AX_DBI_WREN_MSK); - u8 flag; - int ret; + struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + struct pci_dev *pdev = rtwpci->pdev; - write_addr = addr & B_AX_DBI_ADDR_MSK; - write_addr |= u16_encode_bits(BIT(remainder), B_AX_DBI_WREN_MSK); - rtw89_write8(rtwdev, R_AX_DBI_WDATA + remainder, data); - rtw89_write16(rtwdev, R_AX_DBI_FLAG, write_addr); - rtw89_write8(rtwdev, R_AX_DBI_FLAG + 2, B_AX_DBI_WFLAG >> 16); - - ret = read_poll_timeout_atomic(rtw89_read8, flag, !flag, 10, - 10 * RTW89_PCI_WR_RETRY_CNT, false, - rtwdev, R_AX_DBI_FLAG + 2); - if (ret) - WARN(flag, "failed to write to DBI register, addr=0x%04x\n", - addr); - - return ret; + return pci_write_config_byte(pdev, addr, data); } -static int rtw89_dbi_read8(struct rtw89_dev *rtwdev, u16 addr, u8 *value) +static int rtw89_pci_read_config_byte(struct rtw89_dev *rtwdev, u16 addr, + u8 *value) { - u16 read_addr = addr & B_AX_DBI_ADDR_MSK; - u8 flag; - int ret; + struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + struct pci_dev *pdev = rtwpci->pdev; - rtw89_write16(rtwdev, R_AX_DBI_FLAG, read_addr); - rtw89_write8(rtwdev, R_AX_DBI_FLAG + 2, B_AX_DBI_RFLAG >> 16); - - ret = read_poll_timeout_atomic(rtw89_read8, flag, !flag, 10, - 10 * RTW89_PCI_WR_RETRY_CNT, false, - rtwdev, R_AX_DBI_FLAG + 2); - - if (!ret) { - read_addr = R_AX_DBI_RDATA + (addr & 3); - *value = rtw89_read8(rtwdev, read_addr); - } else { - WARN(1, "failed to read DBI register, addr=0x%04x\n", addr); - ret = -EIO; - } - - return ret; + return pci_read_config_byte(pdev, addr, value); } -static int rtw89_dbi_write8_set(struct rtw89_dev *rtwdev, u16 addr, u8 bit) +static int rtw89_pci_config_byte_set(struct rtw89_dev *rtwdev, u16 addr, + u8 bit) { u8 value; int ret; - ret = rtw89_dbi_read8(rtwdev, addr, &value); + ret = rtw89_pci_read_config_byte(rtwdev, addr, &value); if (ret) return ret; value |= bit; - ret = rtw89_dbi_write8(rtwdev, addr, value); + ret = rtw89_pci_write_config_byte(rtwdev, addr, value); return ret; } -static int rtw89_dbi_write8_clr(struct rtw89_dev *rtwdev, u16 addr, u8 bit) +static int rtw89_pci_config_byte_clr(struct rtw89_dev *rtwdev, u16 addr, + u8 bit) { u8 value; int ret; - ret = rtw89_dbi_read8(rtwdev, addr, &value); + ret = rtw89_pci_read_config_byte(rtwdev, addr, &value); if (ret) return ret; value &= ~bit; - ret = rtw89_dbi_write8(rtwdev, addr, value); + ret = rtw89_pci_write_config_byte(rtwdev, addr, value); return ret; } @@ -1542,9 +1554,10 @@ static int rtw89_pci_auto_refclk_cal(struct rtw89_dev *rtwdev, bool autook_en) rtwdev->chip->chip_id == RTL8852C) return 0; - ret = rtw89_dbi_read8(rtwdev, RTW89_PCIE_PHY_RATE, &val8); + ret = rtw89_pci_read_config_byte(rtwdev, RTW89_PCIE_PHY_RATE, &val8); if (ret) { - rtw89_err(rtwdev, "[ERR]dbi_r8_pcie %X\n", RTW89_PCIE_PHY_RATE); + rtw89_err(rtwdev, "[ERR]pci config read %X\n", + RTW89_PCIE_PHY_RATE); return ret; } @@ -1557,17 +1570,18 @@ static int rtw89_pci_auto_refclk_cal(struct rtw89_dev *rtwdev, bool autook_en) return -EOPNOTSUPP; } /* Disable L1BD */ - ret = rtw89_dbi_read8(rtwdev, RTW89_PCIE_L1_CTRL, &bdr_ori); + ret = rtw89_pci_read_config_byte(rtwdev, RTW89_PCIE_L1_CTRL, &bdr_ori); if (ret) { - rtw89_err(rtwdev, "[ERR]dbi_r8_pcie %X\n", RTW89_PCIE_L1_CTRL); + rtw89_err(rtwdev, "[ERR]pci config read %X\n", RTW89_PCIE_L1_CTRL); return ret; } if (bdr_ori & RTW89_PCIE_BIT_L1) { - ret = rtw89_dbi_write8(rtwdev, RTW89_PCIE_L1_CTRL, - bdr_ori & ~RTW89_PCIE_BIT_L1); + ret = rtw89_pci_write_config_byte(rtwdev, RTW89_PCIE_L1_CTRL, + bdr_ori & ~RTW89_PCIE_BIT_L1); if (ret) { - rtw89_err(rtwdev, "[ERR]dbi_w8_pcie %X\n", RTW89_PCIE_L1_CTRL); + rtw89_err(rtwdev, "[ERR]pci config write %X\n", + RTW89_PCIE_L1_CTRL); return ret; } l1_flag = true; @@ -1662,14 +1676,17 @@ static int rtw89_pci_auto_refclk_cal(struct rtw89_dev *rtwdev, bool autook_en) } /* CLK delay = 0 */ - ret = rtw89_dbi_write8(rtwdev, RTW89_PCIE_CLK_CTRL, PCIE_CLKDLY_HW_0); + ret = rtw89_pci_write_config_byte(rtwdev, RTW89_PCIE_CLK_CTRL, + PCIE_CLKDLY_HW_0); end: /* Set L1BD to ori */ if (l1_flag) { - ret = rtw89_dbi_write8(rtwdev, RTW89_PCIE_L1_CTRL, bdr_ori); + ret = rtw89_pci_write_config_byte(rtwdev, RTW89_PCIE_L1_CTRL, + bdr_ori); if (ret) { - rtw89_err(rtwdev, "[ERR]dbi_w8_pcie %X\n", RTW89_PCIE_L1_CTRL); + rtw89_err(rtwdev, "[ERR]pci config write %X\n", + RTW89_PCIE_L1_CTRL); return ret; } } @@ -2210,14 +2227,10 @@ static int rtw89_pci_alloc_tx_ring(struct rtw89_dev *rtwdev, u32 desc_size, u32 len, enum rtw89_tx_channel txch) { + const struct rtw89_pci_ch_dma_addr *txch_addr; int ring_sz = desc_size * len; u8 *head; dma_addr_t dma; - u32 addr_num; - u32 addr_idx; - u32 addr_bdram; - u32 addr_desa_l; - u32 addr_desa_h; int ret; ret = rtw89_pci_alloc_tx_wd_ring(rtwdev, pdev, tx_ring, txch); @@ -2226,8 +2239,7 @@ static int rtw89_pci_alloc_tx_ring(struct rtw89_dev *rtwdev, goto err; } - ret = rtw89_pci_get_txch_addrs(txch, &addr_num, &addr_idx, &addr_bdram, - &addr_desa_l, &addr_desa_h); + ret = rtw89_pci_get_txch_addrs(rtwdev, txch, &txch_addr); if (ret) { rtw89_err(rtwdev, "failed to get address of txch %d", txch); goto err_free_wd_ring; @@ -2244,11 +2256,7 @@ static int rtw89_pci_alloc_tx_ring(struct rtw89_dev *rtwdev, tx_ring->bd_ring.dma = dma; tx_ring->bd_ring.len = len; tx_ring->bd_ring.desc_size = desc_size; - tx_ring->bd_ring.addr_num = addr_num; - tx_ring->bd_ring.addr_idx = addr_idx; - tx_ring->bd_ring.addr_bdram = addr_bdram; - tx_ring->bd_ring.addr_desa_l = addr_desa_l; - tx_ring->bd_ring.addr_desa_h = addr_desa_h; + tx_ring->bd_ring.addr = *txch_addr; tx_ring->bd_ring.wp = 0; tx_ring->bd_ring.rp = 0; tx_ring->txch = txch; @@ -2300,20 +2308,16 @@ static int rtw89_pci_alloc_rx_ring(struct rtw89_dev *rtwdev, struct rtw89_pci_rx_ring *rx_ring, u32 desc_size, u32 len, u32 rxch) { + const struct rtw89_pci_ch_dma_addr *rxch_addr; struct sk_buff *skb; u8 *head; dma_addr_t dma; - u32 addr_num; - u32 addr_idx; - u32 addr_desa_l; - u32 addr_desa_h; int ring_sz = desc_size * len; int buf_sz = RTW89_PCI_RX_BUF_SIZE; int i, allocated; int ret; - ret = rtw89_pci_get_rxch_addrs(rxch, &addr_num, &addr_idx, - &addr_desa_l, &addr_desa_h); + ret = rtw89_pci_get_rxch_addrs(rtwdev, rxch, &rxch_addr); if (ret) { rtw89_err(rtwdev, "failed to get address of rxch %d", rxch); return ret; @@ -2329,10 +2333,7 @@ static int rtw89_pci_alloc_rx_ring(struct rtw89_dev *rtwdev, rx_ring->bd_ring.dma = dma; rx_ring->bd_ring.len = len; rx_ring->bd_ring.desc_size = desc_size; - rx_ring->bd_ring.addr_num = addr_num; - rx_ring->bd_ring.addr_idx = addr_idx; - rx_ring->bd_ring.addr_desa_l = addr_desa_l; - rx_ring->bd_ring.addr_desa_h = addr_desa_h; + rx_ring->bd_ring.addr = *rxch_addr; rx_ring->bd_ring.wp = 0; rx_ring->bd_ring.rp = 0; rx_ring->buf_sz = buf_sz; @@ -2552,17 +2553,17 @@ static void rtw89_pci_clkreq_set(struct rtw89_dev *rtwdev, bool enable) if (rtw89_pci_disable_clkreq) return; - ret = rtw89_dbi_write8(rtwdev, RTW89_PCIE_CLK_CTRL, - PCIE_CLKDLY_HW_30US); + ret = rtw89_pci_write_config_byte(rtwdev, RTW89_PCIE_CLK_CTRL, + PCIE_CLKDLY_HW_30US); if (ret) rtw89_err(rtwdev, "failed to set CLKREQ Delay\n"); if (enable) - ret = rtw89_dbi_write8_set(rtwdev, RTW89_PCIE_L1_CTRL, - RTW89_PCIE_BIT_CLK); + ret = rtw89_pci_config_byte_set(rtwdev, RTW89_PCIE_L1_CTRL, + RTW89_PCIE_BIT_CLK); else - ret = rtw89_dbi_write8_clr(rtwdev, RTW89_PCIE_L1_CTRL, - RTW89_PCIE_BIT_CLK); + ret = rtw89_pci_config_byte_clr(rtwdev, RTW89_PCIE_L1_CTRL, + RTW89_PCIE_BIT_CLK); if (ret) rtw89_err(rtwdev, "failed to %s CLKREQ_L1, ret=%d", enable ? "set" : "unset", ret); @@ -2576,7 +2577,7 @@ static void rtw89_pci_aspm_set(struct rtw89_dev *rtwdev, bool enable) if (rtw89_pci_disable_aspm_l1) return; - ret = rtw89_dbi_read8(rtwdev, RTW89_PCIE_ASPM_CTRL, &value); + ret = rtw89_pci_read_config_byte(rtwdev, RTW89_PCIE_ASPM_CTRL, &value); if (ret) rtw89_err(rtwdev, "failed to read ASPM Delay\n"); @@ -2584,16 +2585,16 @@ static void rtw89_pci_aspm_set(struct rtw89_dev *rtwdev, bool enable) value |= FIELD_PREP(RTW89_L1DLY_MASK, PCIE_L1DLY_16US) | FIELD_PREP(RTW89_L0DLY_MASK, PCIE_L0SDLY_4US); - ret = rtw89_dbi_write8(rtwdev, RTW89_PCIE_ASPM_CTRL, value); + ret = rtw89_pci_write_config_byte(rtwdev, RTW89_PCIE_ASPM_CTRL, value); if (ret) rtw89_err(rtwdev, "failed to read ASPM Delay\n"); if (enable) - ret = rtw89_dbi_write8_set(rtwdev, RTW89_PCIE_L1_CTRL, - RTW89_PCIE_BIT_L1); + ret = rtw89_pci_config_byte_set(rtwdev, RTW89_PCIE_L1_CTRL, + RTW89_PCIE_BIT_L1); else - ret = rtw89_dbi_write8_clr(rtwdev, RTW89_PCIE_L1_CTRL, - RTW89_PCIE_BIT_L1); + ret = rtw89_pci_config_byte_clr(rtwdev, RTW89_PCIE_L1_CTRL, + RTW89_PCIE_BIT_L1); if (ret) rtw89_err(rtwdev, "failed to %s ASPM L1, ret=%d", enable ? "set" : "unset", ret); @@ -2657,11 +2658,11 @@ static void rtw89_pci_l1ss_set(struct rtw89_dev *rtwdev, bool enable) int ret; if (enable) - ret = rtw89_dbi_write8_set(rtwdev, RTW89_PCIE_TIMER_CTRL, - RTW89_PCIE_BIT_L1SUB); + ret = rtw89_pci_config_byte_set(rtwdev, RTW89_PCIE_TIMER_CTRL, + RTW89_PCIE_BIT_L1SUB); else - ret = rtw89_dbi_write8_clr(rtwdev, RTW89_PCIE_TIMER_CTRL, - RTW89_PCIE_BIT_L1SUB); + ret = rtw89_pci_config_byte_clr(rtwdev, RTW89_PCIE_TIMER_CTRL, + RTW89_PCIE_BIT_L1SUB); if (ret) rtw89_err(rtwdev, "failed to %s L1SS, ret=%d", enable ? "set" : "unset", ret); @@ -2878,10 +2879,10 @@ static void rtw89_pci_l2_hci_ldo(struct rtw89_dev *rtwdev) return; /* Hardware need write the reg twice to ensure the setting work */ - rtw89_dbi_write8_set(rtwdev, RTW89_PCIE_RST_MSTATE, - RTW89_PCIE_BIT_CFG_RST_MSTATE); - rtw89_dbi_write8_set(rtwdev, RTW89_PCIE_RST_MSTATE, - RTW89_PCIE_BIT_CFG_RST_MSTATE); + rtw89_pci_write_config_byte(rtwdev, RTW89_PCIE_RST_MSTATE, + RTW89_PCIE_BIT_CFG_RST_MSTATE); + rtw89_pci_write_config_byte(rtwdev, RTW89_PCIE_RST_MSTATE, + RTW89_PCIE_BIT_CFG_RST_MSTATE); } static int __maybe_unused rtw89_pci_resume(struct device *dev) @@ -2932,11 +2933,11 @@ static const struct rtw89_hci_ops rtw89_pci_ops = { .napi_poll = rtw89_pci_napi_poll, }; -static int rtw89_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) +int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct ieee80211_hw *hw; struct rtw89_dev *rtwdev; + const struct rtw89_driver_info *info; int driver_data_size; int ret; @@ -2957,13 +2958,9 @@ static int rtw89_pci_probe(struct pci_dev *pdev, SET_IEEE80211_DEV(rtwdev->hw, &pdev->dev); - switch (id->driver_data) { - case RTL8852A: - rtwdev->chip = &rtw8852a_chip_info; - break; - default: - return -ENOENT; - } + info = (const struct rtw89_driver_info *)id->driver_data; + rtwdev->chip = info->chip; + rtwdev->pci_info = info->bus.pci; ret = rtw89_core_init(rtwdev); if (ret) { @@ -3022,8 +3019,9 @@ static int rtw89_pci_probe(struct pci_dev *pdev, return ret; } +EXPORT_SYMBOL(rtw89_pci_probe); -static void rtw89_pci_remove(struct pci_dev *pdev) +void rtw89_pci_remove(struct pci_dev *pdev) { struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct rtw89_dev *rtwdev; @@ -3038,22 +3036,7 @@ static void rtw89_pci_remove(struct pci_dev *pdev) rtw89_core_deinit(rtwdev); ieee80211_free_hw(hw); } - -static const struct pci_device_id rtw89_pci_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8852), .driver_data = RTL8852A }, - { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xa85a), .driver_data = RTL8852A }, - {}, -}; -MODULE_DEVICE_TABLE(pci, rtw89_pci_id_table); - -static struct pci_driver rtw89_pci_driver = { - .name = "rtw89_pci", - .id_table = rtw89_pci_id_table, - .probe = rtw89_pci_probe, - .remove = rtw89_pci_remove, - .driver.pm = &rtw89_pm_ops, -}; -module_pci_driver(rtw89_pci_driver); +EXPORT_SYMBOL(rtw89_pci_remove); MODULE_AUTHOR("Realtek Corporation"); MODULE_DESCRIPTION("Realtek 802.11ax wireless PCI driver"); diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index 20e6767ea5c4..b84acd0d0582 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -130,6 +130,10 @@ #define R_AX_CH10_TXBD_IDX 0x137C /* Management Queue band 1 */ #define R_AX_CH11_TXBD_IDX 0x1380 /* HI Queue band 1 */ #define R_AX_CH12_TXBD_IDX 0x1080 /* FWCMD Queue */ +#define R_AX_CH10_TXBD_IDX_V1 0x11D0 +#define R_AX_CH11_TXBD_IDX_V1 0x11D4 +#define R_AX_RXQ_RXBD_IDX_V1 0x1218 +#define R_AX_RPQ_RXBD_IDX_V1 0x121C #define TXBD_HW_IDX_MASK GENMASK(27, 16) #define TXBD_HOST_IDX_MASK GENMASK(11, 0) @@ -163,6 +167,36 @@ #define R_AX_RXQ_RXBD_DESA_H 0x1104 #define R_AX_RPQ_RXBD_DESA_L 0x1108 #define R_AX_RPQ_RXBD_DESA_H 0x110C +#define R_AX_RXQ_RXBD_DESA_L_V1 0x1220 +#define R_AX_RXQ_RXBD_DESA_H_V1 0x1224 +#define R_AX_RPQ_RXBD_DESA_L_V1 0x1228 +#define R_AX_RPQ_RXBD_DESA_H_V1 0x122C +#define R_AX_ACH0_TXBD_DESA_L_V1 0x1230 +#define R_AX_ACH0_TXBD_DESA_H_V1 0x1234 +#define R_AX_ACH1_TXBD_DESA_L_V1 0x1238 +#define R_AX_ACH1_TXBD_DESA_H_V1 0x123C +#define R_AX_ACH2_TXBD_DESA_L_V1 0x1240 +#define R_AX_ACH2_TXBD_DESA_H_V1 0x1244 +#define R_AX_ACH3_TXBD_DESA_L_V1 0x1248 +#define R_AX_ACH3_TXBD_DESA_H_V1 0x124C +#define R_AX_ACH4_TXBD_DESA_L_V1 0x1250 +#define R_AX_ACH4_TXBD_DESA_H_V1 0x1254 +#define R_AX_ACH5_TXBD_DESA_L_V1 0x1258 +#define R_AX_ACH5_TXBD_DESA_H_V1 0x125C +#define R_AX_ACH6_TXBD_DESA_L_V1 0x1260 +#define R_AX_ACH6_TXBD_DESA_H_V1 0x1264 +#define R_AX_ACH7_TXBD_DESA_L_V1 0x1268 +#define R_AX_ACH7_TXBD_DESA_H_V1 0x126C +#define R_AX_CH8_TXBD_DESA_L_V1 0x1270 +#define R_AX_CH8_TXBD_DESA_H_V1 0x1274 +#define R_AX_CH9_TXBD_DESA_L_V1 0x1278 +#define R_AX_CH9_TXBD_DESA_H_V1 0x127C +#define R_AX_CH12_TXBD_DESA_L_V1 0x1280 +#define R_AX_CH12_TXBD_DESA_H_V1 0x1284 +#define R_AX_CH10_TXBD_DESA_L_V1 0x1458 +#define R_AX_CH10_TXBD_DESA_H_V1 0x145C +#define R_AX_CH11_TXBD_DESA_L_V1 0x1460 +#define R_AX_CH11_TXBD_DESA_H_V1 0x1464 #define B_AX_DESC_NUM_MSK GENMASK(11, 0) #define R_AX_RXQ_RXBD_NUM 0x1020 @@ -180,6 +214,10 @@ #define R_AX_CH10_TXBD_NUM 0x1338 #define R_AX_CH11_TXBD_NUM 0x133A #define R_AX_CH12_TXBD_NUM 0x1038 +#define R_AX_RXQ_RXBD_NUM_V1 0x1210 +#define R_AX_RPQ_RXBD_NUM_V1 0x1212 +#define R_AX_CH10_TXBD_NUM_V1 0x1438 +#define R_AX_CH11_TXBD_NUM_V1 0x143A #define R_AX_ACH0_BDRAM_CTRL 0x1200 #define R_AX_ACH1_BDRAM_CTRL 0x1204 @@ -194,6 +232,19 @@ #define R_AX_CH10_BDRAM_CTRL 0x1320 #define R_AX_CH11_BDRAM_CTRL 0x1324 #define R_AX_CH12_BDRAM_CTRL 0x1228 +#define R_AX_ACH0_BDRAM_CTRL_V1 0x1300 +#define R_AX_ACH1_BDRAM_CTRL_V1 0x1304 +#define R_AX_ACH2_BDRAM_CTRL_V1 0x1308 +#define R_AX_ACH3_BDRAM_CTRL_V1 0x130C +#define R_AX_ACH4_BDRAM_CTRL_V1 0x1310 +#define R_AX_ACH5_BDRAM_CTRL_V1 0x1314 +#define R_AX_ACH6_BDRAM_CTRL_V1 0x1318 +#define R_AX_ACH7_BDRAM_CTRL_V1 0x131C +#define R_AX_CH8_BDRAM_CTRL_V1 0x1320 +#define R_AX_CH9_BDRAM_CTRL_V1 0x1324 +#define R_AX_CH12_BDRAM_CTRL_V1 0x1328 +#define R_AX_CH10_BDRAM_CTRL_V1 0x1420 +#define R_AX_CH11_BDRAM_CTRL_V1 0x1424 #define BDRAM_SIDX_MASK GENMASK(7, 0) #define BDRAM_MAX_MASK GENMASK(15, 8) #define BDRAM_MIN_MASK GENMASK(23, 16) @@ -382,6 +433,23 @@ enum rtw89_pcie_clkdly_hw { PCIE_CLKDLY_HW_200US = 0x5, }; +struct rtw89_pci_ch_dma_addr { + u32 num; + u32 idx; + u32 bdram; + u32 desa_l; + u32 desa_h; +}; + +struct rtw89_pci_ch_dma_addr_set { + struct rtw89_pci_ch_dma_addr tx[RTW89_TXCH_NUM]; + struct rtw89_pci_ch_dma_addr rx[RTW89_RXCH_NUM]; +}; + +struct rtw89_pci_info { + const struct rtw89_pci_ch_dma_addr_set *dma_addr_set; +}; + struct rtw89_pci_bd_ram { u8 start_idx; u8 max_num; @@ -469,11 +537,7 @@ struct rtw89_pci_dma_ring { u8 desc_size; dma_addr_t dma; - u32 addr_num; - u32 addr_idx; - u32 addr_bdram; - u32 addr_desa_l; - u32 addr_desa_h; + struct rtw89_pci_ch_dma_addr addr; u32 len; u32 wp; /* host idx */ @@ -626,5 +690,12 @@ static inline bool rtw89_pci_ltr_is_err_reg_val(u32 val) } extern const struct dev_pm_ops rtw89_pm_ops; +extern const struct rtw89_pci_ch_dma_addr_set rtw89_pci_ch_dma_addr_set; +extern const struct rtw89_pci_ch_dma_addr_set rtw89_pci_ch_dma_addr_set_v1; + +struct pci_device_id; + +int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id); +void rtw89_pci_remove(struct pci_dev *pdev); #endif diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 147009888de0..ac211d897311 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -4,6 +4,7 @@ #include "debug.h" #include "fw.h" +#include "mac.h" #include "phy.h" #include "ps.h" #include "reg.h" @@ -117,17 +118,28 @@ static u64 rtw89_phy_ra_mask_rssi(struct rtw89_dev *rtwdev, u8 rssi, else if (rssi_lv == 1) return 0xfffffffffffffff0ULL; else if (rssi_lv == 2) - return 0xffffffffffffffe0ULL; + return 0xffffffffffffefe0ULL; else if (rssi_lv == 3) - return 0xffffffffffffffc0ULL; + return 0xffffffffffffcfc0ULL; else if (rssi_lv == 4) - return 0xffffffffffffff80ULL; + return 0xffffffffffff8f80ULL; else if (rssi_lv >= 5) - return 0xffffffffffffff00ULL; + return 0xffffffffffff0f00ULL; return 0xffffffffffffffffULL; } +static u64 rtw89_phy_ra_mask_recover(u64 ra_mask, u64 ra_mask_bak) +{ + if ((ra_mask & ~(RA_MASK_CCK_RATES | RA_MASK_OFDM_RATES)) == 0) + ra_mask |= (ra_mask_bak & ~(RA_MASK_CCK_RATES | RA_MASK_OFDM_RATES)); + + if (ra_mask == 0) + ra_mask |= (ra_mask_bak & (RA_MASK_CCK_RATES | RA_MASK_OFDM_RATES)); + + return ra_mask; +} + static u64 rtw89_phy_ra_mask_cfg(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta) { struct rtw89_hal *hal = &rtwdev->hal; @@ -150,6 +162,11 @@ static u64 rtw89_phy_ra_mask_cfg(struct rtw89_dev *rtwdev, struct rtw89_sta *rtw cfg_mask = u64_encode_bits(mask->control[NL80211_BAND_5GHZ].legacy, RA_MASK_OFDM_RATES); break; + case RTW89_BAND_6G: + band = NL80211_BAND_6GHZ; + cfg_mask = u64_encode_bits(mask->control[NL80211_BAND_6GHZ].legacy, + RA_MASK_OFDM_RATES); + break; default: rtw89_warn(rtwdev, "unhandled band type %d\n", hal->current_band_type); return -1; @@ -194,8 +211,8 @@ static void rtw89_phy_ra_sta_update(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra = &rtwsta->ra; const u64 *high_rate_masks = rtw89_ra_mask_ht_rates; u8 rssi = ewma_rssi_read(&rtwsta->avg_rssi); - u64 high_rate_mask = 0; u64 ra_mask = 0; + u64 ra_mask_bak; u8 mode = 0; u8 csi_mode = RTW89_RA_RPT_MODE_LEGACY; u8 bw_mode = 0; @@ -243,37 +260,54 @@ static void rtw89_phy_ra_sta_update(struct rtw89_dev *rtwdev, ldpc_en = 1; } - if (rtwdev->hal.current_band_type == RTW89_BAND_2G) { + switch (rtwdev->hal.current_band_type) { + case RTW89_BAND_2G: + ra_mask |= sta->supp_rates[NL80211_BAND_2GHZ]; if (sta->supp_rates[NL80211_BAND_2GHZ] <= 0xf) mode |= RTW89_RA_MODE_CCK; else mode |= RTW89_RA_MODE_CCK | RTW89_RA_MODE_OFDM; - } else { + break; + case RTW89_BAND_5G: + ra_mask |= (u64)sta->supp_rates[NL80211_BAND_5GHZ] << 4; mode |= RTW89_RA_MODE_OFDM; + break; + case RTW89_BAND_6G: + ra_mask |= (u64)sta->supp_rates[NL80211_BAND_6GHZ] << 4; + mode |= RTW89_RA_MODE_OFDM; + break; + default: + rtw89_err(rtwdev, "Unknown band type\n"); + break; } + ra_mask_bak = ra_mask; + if (mode >= RTW89_RA_MODE_HT) { + u64 mask = 0; for (i = 0; i < rtwdev->hal.tx_nss; i++) - high_rate_mask |= high_rate_masks[i]; - ra_mask &= high_rate_mask; + mask |= high_rate_masks[i]; if (mode & RTW89_RA_MODE_OFDM) - ra_mask |= RA_MASK_SUBOFDM_RATES; + mask |= RA_MASK_SUBOFDM_RATES; if (mode & RTW89_RA_MODE_CCK) - ra_mask |= RA_MASK_SUBCCK_RATES; + mask |= RA_MASK_SUBCCK_RATES; + ra_mask &= mask; } else if (mode & RTW89_RA_MODE_OFDM) { - if (mode & RTW89_RA_MODE_CCK) - ra_mask |= RA_MASK_SUBCCK_RATES; - ra_mask |= RA_MASK_OFDM_RATES; - } else { - ra_mask = RA_MASK_CCK_RATES; + ra_mask &= (RA_MASK_OFDM_RATES | RA_MASK_SUBCCK_RATES); } - if (mode != RTW89_RA_MODE_CCK) { + if (mode != RTW89_RA_MODE_CCK) ra_mask &= rtw89_phy_ra_mask_rssi(rtwdev, rssi, 0); - ra_mask &= rtw89_phy_ra_mask_cfg(rtwdev, rtwsta); - } + + ra_mask = rtw89_phy_ra_mask_recover(ra_mask, ra_mask_bak); + ra_mask &= rtw89_phy_ra_mask_cfg(rtwdev, rtwsta); switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_160: + bw_mode = RTW89_CHANNEL_WIDTH_160; + sgi = sta->vht_cap.vht_supported && + (sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160); + break; case IEEE80211_STA_RX_BW_80: bw_mode = RTW89_CHANNEL_WIDTH_80; sgi = sta->vht_cap.vht_supported && @@ -568,6 +602,13 @@ u8 rtw89_phy_get_txsc(struct rtw89_dev *rtwdev, return txsc_idx; } +EXPORT_SYMBOL(rtw89_phy_get_txsc); + +static bool rtw89_phy_check_swsi_busy(struct rtw89_dev *rtwdev) +{ + return !!rtw89_phy_read32_mask(rtwdev, R_SWSI_V1, B_SWSI_W_BUSY_V1) || + !!rtw89_phy_read32_mask(rtwdev, R_SWSI_V1, B_SWSI_R_BUSY_V1); +} u32 rtw89_phy_read_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask) @@ -591,6 +632,56 @@ u32 rtw89_phy_read_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, } EXPORT_SYMBOL(rtw89_phy_read_rf); +static u32 rtw89_phy_read_rf_a(struct rtw89_dev *rtwdev, + enum rtw89_rf_path rf_path, u32 addr, u32 mask) +{ + bool busy; + bool done; + u32 val; + int ret; + + ret = read_poll_timeout_atomic(rtw89_phy_check_swsi_busy, busy, !busy, + 1, 30, false, rtwdev); + if (ret) { + rtw89_err(rtwdev, "read rf busy swsi\n"); + return INV_RF_DATA; + } + + mask &= RFREG_MASK; + + val = FIELD_PREP(B_SWSI_READ_ADDR_PATH_V1, rf_path) | + FIELD_PREP(B_SWSI_READ_ADDR_ADDR_V1, addr); + rtw89_phy_write32_mask(rtwdev, R_SWSI_READ_ADDR_V1, B_SWSI_READ_ADDR_V1, val); + udelay(2); + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, done, done, 1, + 30, false, rtwdev, R_SWSI_V1, + B_SWSI_R_DATA_DONE_V1); + if (ret) { + rtw89_err(rtwdev, "read swsi busy\n"); + return INV_RF_DATA; + } + + return rtw89_phy_read32_mask(rtwdev, R_SWSI_V1, mask); +} + +u32 rtw89_phy_read_rf_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask) +{ + bool ad_sel = FIELD_GET(RTW89_RF_ADDR_ADSEL_MASK, addr); + + if (rf_path >= rtwdev->chip->rf_path_num) { + rtw89_err(rtwdev, "unsupported rf path (%d)\n", rf_path); + return INV_RF_DATA; + } + + if (ad_sel) + return rtw89_phy_read_rf(rtwdev, rf_path, addr, mask); + else + return rtw89_phy_read_rf_a(rtwdev, rf_path, addr, mask); +} +EXPORT_SYMBOL(rtw89_phy_read_rf_v1); + bool rtw89_phy_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask, u32 data) { @@ -616,6 +707,60 @@ bool rtw89_phy_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, } EXPORT_SYMBOL(rtw89_phy_write_rf); +static bool rtw89_phy_write_rf_a(struct rtw89_dev *rtwdev, + enum rtw89_rf_path rf_path, u32 addr, u32 mask, + u32 data) +{ + u8 bit_shift; + u32 val; + bool busy, b_msk_en = false; + int ret; + + ret = read_poll_timeout_atomic(rtw89_phy_check_swsi_busy, busy, !busy, + 1, 30, false, rtwdev); + if (ret) { + rtw89_err(rtwdev, "write rf busy swsi\n"); + return false; + } + + data &= RFREG_MASK; + mask &= RFREG_MASK; + + if (mask != RFREG_MASK) { + b_msk_en = true; + rtw89_phy_write32_mask(rtwdev, R_SWSI_BIT_MASK_V1, RFREG_MASK, + mask); + bit_shift = __ffs(mask); + data = (data << bit_shift) & RFREG_MASK; + } + + val = FIELD_PREP(B_SWSI_DATA_BIT_MASK_EN_V1, b_msk_en) | + FIELD_PREP(B_SWSI_DATA_PATH_V1, rf_path) | + FIELD_PREP(B_SWSI_DATA_ADDR_V1, addr) | + FIELD_PREP(B_SWSI_DATA_VAL_V1, data); + + rtw89_phy_write32_mask(rtwdev, R_SWSI_DATA_V1, MASKDWORD, val); + + return true; +} + +bool rtw89_phy_write_rf_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask, u32 data) +{ + bool ad_sel = FIELD_GET(RTW89_RF_ADDR_ADSEL_MASK, addr); + + if (rf_path >= rtwdev->chip->rf_path_num) { + rtw89_err(rtwdev, "unsupported rf path (%d)\n", rf_path); + return false; + } + + if (ad_sel) + return rtw89_phy_write_rf(rtwdev, rf_path, addr, mask, data); + else + return rtw89_phy_write_rf_a(rtwdev, rf_path, addr, mask, data); +} +EXPORT_SYMBOL(rtw89_phy_write_rf_v1); + static void rtw89_phy_bb_reset(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { @@ -717,6 +862,21 @@ static void rtw89_phy_config_rf_reg(struct rtw89_dev *rtwdev, } } +void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev, + const struct rtw89_reg2_def *reg, + enum rtw89_rf_path rf_path, + void *extra_data) +{ + rtw89_write_rf(rtwdev, rf_path, reg->addr, RFREG_MASK, reg->data); + + if (reg->addr < 0x100) + return; + + rtw89_phy_cofig_rf_reg_store(rtwdev, reg, rf_path, + (struct rtw89_fw_h2c_rf_reg_info *)extra_data); +} +EXPORT_SYMBOL(rtw89_phy_config_rf_reg_v1); + static int rtw89_phy_sel_headline(struct rtw89_dev *rtwdev, const struct rtw89_phy_table *table, u32 *headline_size, u32 *headline_idx, @@ -888,6 +1048,8 @@ static u32 rtw89_phy_nctl_poll(struct rtw89_dev *rtwdev) void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev) { + void (*config)(struct rtw89_dev *rtwdev, const struct rtw89_reg2_def *reg, + enum rtw89_rf_path rf_path, void *data); const struct rtw89_chip_info *chip = rtwdev->chip; const struct rtw89_phy_table *rf_table; struct rtw89_fw_h2c_rf_reg_info *rf_reg_info; @@ -898,13 +1060,13 @@ void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev) return; for (path = RF_PATH_A; path < chip->rf_path_num; path++) { - rf_reg_info->rf_path = path; rf_table = chip->rf_table[path]; - rtw89_phy_init_reg(rtwdev, rf_table, rtw89_phy_config_rf_reg, - (void *)rf_reg_info); + rf_reg_info->rf_path = rf_table->rf_path; + config = rf_table->config ? rf_table->config : rtw89_phy_config_rf_reg; + rtw89_phy_init_reg(rtwdev, rf_table, config, (void *)rf_reg_info); if (rtw89_phy_config_rf_reg_fw(rtwdev, rf_reg_info)) rtw89_warn(rtwdev, "rf path %d reg h2c config failed\n", - path); + rf_reg_info->rf_path); } kfree(rf_reg_info); } @@ -972,6 +1134,7 @@ void rtw89_phy_write32_idx(struct rtw89_dev *rtwdev, u32 addr, u32 mask, addr += rtw89_phy0_phy1_offset(rtwdev, addr); rtw89_phy_write32_mask(rtwdev, addr, mask, data); } +EXPORT_SYMBOL(rtw89_phy_write32_idx); void rtw89_phy_set_phy_regs(struct rtw89_dev *rtwdev, u32 addr, u32 mask, u32 val) @@ -995,6 +1158,7 @@ void rtw89_phy_write_reg3_tbl(struct rtw89_dev *rtwdev, rtw89_phy_write32_mask(rtwdev, reg3->addr, reg3->mask, reg3->data); } } +EXPORT_SYMBOL(rtw89_phy_write_reg3_tbl); const u8 rtw89_rs_idx_max[] = { [RTW89_RS_CCK] = RTW89_RATE_CCK_MAX, @@ -1003,6 +1167,7 @@ const u8 rtw89_rs_idx_max[] = { [RTW89_RS_HEDCM] = RTW89_RATE_HEDCM_MAX, [RTW89_RS_OFFSET] = RTW89_RATE_OFFSET_MAX, }; +EXPORT_SYMBOL(rtw89_rs_idx_max); const u8 rtw89_rs_nss_max[] = { [RTW89_RS_CCK] = 1, @@ -1011,6 +1176,7 @@ const u8 rtw89_rs_nss_max[] = { [RTW89_RS_HEDCM] = RTW89_NSS_HEDCM_MAX, [RTW89_RS_OFFSET] = 1, }; +EXPORT_SYMBOL(rtw89_rs_nss_max); static const u8 _byr_of_rs[] = { [RTW89_RS_CCK] = offsetof(struct rtw89_txpwr_byrate, cck), @@ -1044,6 +1210,7 @@ void rtw89_phy_load_txpwr_byrate(struct rtw89_dev *rtwdev, } } } +EXPORT_SYMBOL(rtw89_phy_load_txpwr_byrate); #define _phy_txpwr_rf_to_mac(rtwdev, txpwr_rf) \ ({ \ @@ -1074,9 +1241,38 @@ s8 rtw89_phy_read_txpwr_byrate(struct rtw89_dev *rtwdev, return _phy_txpwr_rf_to_mac(rtwdev, byr[idx]); } +EXPORT_SYMBOL(rtw89_phy_read_txpwr_byrate); -static u8 rtw89_channel_to_idx(struct rtw89_dev *rtwdev, u8 channel) +static u8 rtw89_channel_6g_to_idx(struct rtw89_dev *rtwdev, u8 channel_6g) { + switch (channel_6g) { + case 1 ... 29: + return (channel_6g - 1) / 2; + case 33 ... 61: + return (channel_6g - 3) / 2; + case 65 ... 93: + return (channel_6g - 5) / 2; + case 97 ... 125: + return (channel_6g - 7) / 2; + case 129 ... 157: + return (channel_6g - 9) / 2; + case 161 ... 189: + return (channel_6g - 11) / 2; + case 193 ... 221: + return (channel_6g - 13) / 2; + case 225 ... 253: + return (channel_6g - 15) / 2; + default: + rtw89_warn(rtwdev, "unknown 6g channel: %d\n", channel_6g); + return 0; + } +} + +static u8 rtw89_channel_to_idx(struct rtw89_dev *rtwdev, u8 band, u8 channel) +{ + if (band == RTW89_BAND_6G) + return rtw89_channel_6g_to_idx(rtwdev, channel); + switch (channel) { case 1 ... 14: return channel - 1; @@ -1096,8 +1292,8 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 bw, u8 ntx, u8 rs, u8 bf, u8 ch) { const struct rtw89_chip_info *chip = rtwdev->chip; - u8 ch_idx = rtw89_channel_to_idx(rtwdev, ch); u8 band = rtwdev->hal.current_band_type; + u8 ch_idx = rtw89_channel_to_idx(rtwdev, band, ch); u8 regd = rtw89_regd_get(rtwdev, band); s8 lmt = 0, sar; @@ -1114,6 +1310,12 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, lmt = (*chip->txpwr_lmt_5g)[bw][ntx][rs][bf] [RTW89_WW][ch_idx]; break; + case RTW89_BAND_6G: + lmt = (*chip->txpwr_lmt_6g)[bw][ntx][rs][bf][regd][ch_idx]; + if (!lmt) + lmt = (*chip->txpwr_lmt_6g)[bw][ntx][rs][bf] + [RTW89_WW][ch_idx]; + break; default: rtw89_warn(rtwdev, "unknown band type: %d\n", band); return 0; @@ -1124,6 +1326,7 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, return min(lmt, sar); } +EXPORT_SYMBOL(rtw89_phy_read_txpwr_limit); #define __fill_txpwr_limit_nonbf_bf(ptr, bw, ntx, rs, ch) \ do { \ @@ -1151,14 +1354,14 @@ static void rtw89_phy_fill_txpwr_limit_20m(struct rtw89_dev *rtwdev, static void rtw89_phy_fill_txpwr_limit_40m(struct rtw89_dev *rtwdev, struct rtw89_txpwr_limit *lmt, - u8 ntx, u8 ch) + u8 ntx, u8 ch, u8 pri_ch) { __fill_txpwr_limit_nonbf_bf(lmt->cck_20m, RTW89_CHANNEL_WIDTH_20, ntx, RTW89_RS_CCK, ch - 2); __fill_txpwr_limit_nonbf_bf(lmt->cck_40m, RTW89_CHANNEL_WIDTH_40, ntx, RTW89_RS_CCK, ch); __fill_txpwr_limit_nonbf_bf(lmt->ofdm, RTW89_CHANNEL_WIDTH_20, - ntx, RTW89_RS_OFDM, ch - 2); + ntx, RTW89_RS_OFDM, pri_ch); __fill_txpwr_limit_nonbf_bf(lmt->mcs_20m[0], RTW89_CHANNEL_WIDTH_20, ntx, RTW89_RS_MCS, ch - 2); __fill_txpwr_limit_nonbf_bf(lmt->mcs_20m[1], RTW89_CHANNEL_WIDTH_20, @@ -1169,14 +1372,14 @@ static void rtw89_phy_fill_txpwr_limit_40m(struct rtw89_dev *rtwdev, static void rtw89_phy_fill_txpwr_limit_80m(struct rtw89_dev *rtwdev, struct rtw89_txpwr_limit *lmt, - u8 ntx, u8 ch) + u8 ntx, u8 ch, u8 pri_ch) { s8 val_0p5_n[RTW89_BF_NUM]; s8 val_0p5_p[RTW89_BF_NUM]; u8 i; __fill_txpwr_limit_nonbf_bf(lmt->ofdm, RTW89_CHANNEL_WIDTH_20, - ntx, RTW89_RS_OFDM, ch - 6); + ntx, RTW89_RS_OFDM, pri_ch); __fill_txpwr_limit_nonbf_bf(lmt->mcs_20m[0], RTW89_CHANNEL_WIDTH_20, ntx, RTW89_RS_MCS, ch - 6); __fill_txpwr_limit_nonbf_bf(lmt->mcs_20m[1], RTW89_CHANNEL_WIDTH_20, @@ -1201,10 +1404,82 @@ static void rtw89_phy_fill_txpwr_limit_80m(struct rtw89_dev *rtwdev, lmt->mcs_40m_0p5[i] = min_t(s8, val_0p5_n[i], val_0p5_p[i]); } +static void rtw89_phy_fill_txpwr_limit_160m(struct rtw89_dev *rtwdev, + struct rtw89_txpwr_limit *lmt, + u8 ntx, u8 ch, u8 pri_ch) +{ + s8 val_0p5_n[RTW89_BF_NUM]; + s8 val_0p5_p[RTW89_BF_NUM]; + s8 val_2p5_n[RTW89_BF_NUM]; + s8 val_2p5_p[RTW89_BF_NUM]; + u8 i; + + /* fill ofdm section */ + __fill_txpwr_limit_nonbf_bf(lmt->ofdm, RTW89_CHANNEL_WIDTH_20, + ntx, RTW89_RS_OFDM, pri_ch); + + /* fill mcs 20m section */ + __fill_txpwr_limit_nonbf_bf(lmt->mcs_20m[0], RTW89_CHANNEL_WIDTH_20, + ntx, RTW89_RS_MCS, ch - 14); + __fill_txpwr_limit_nonbf_bf(lmt->mcs_20m[1], RTW89_CHANNEL_WIDTH_20, + ntx, RTW89_RS_MCS, ch - 10); + __fill_txpwr_limit_nonbf_bf(lmt->mcs_20m[2], RTW89_CHANNEL_WIDTH_20, + ntx, RTW89_RS_MCS, ch - 6); + __fill_txpwr_limit_nonbf_bf(lmt->mcs_20m[3], RTW89_CHANNEL_WIDTH_20, + ntx, RTW89_RS_MCS, ch - 2); + __fill_txpwr_limit_nonbf_bf(lmt->mcs_20m[4], RTW89_CHANNEL_WIDTH_20, + ntx, RTW89_RS_MCS, ch + 2); + __fill_txpwr_limit_nonbf_bf(lmt->mcs_20m[5], RTW89_CHANNEL_WIDTH_20, + ntx, RTW89_RS_MCS, ch + 6); + __fill_txpwr_limit_nonbf_bf(lmt->mcs_20m[6], RTW89_CHANNEL_WIDTH_20, + ntx, RTW89_RS_MCS, ch + 10); + __fill_txpwr_limit_nonbf_bf(lmt->mcs_20m[7], RTW89_CHANNEL_WIDTH_20, + ntx, RTW89_RS_MCS, ch + 14); + + /* fill mcs 40m section */ + __fill_txpwr_limit_nonbf_bf(lmt->mcs_40m[0], RTW89_CHANNEL_WIDTH_40, + ntx, RTW89_RS_MCS, ch - 12); + __fill_txpwr_limit_nonbf_bf(lmt->mcs_40m[1], RTW89_CHANNEL_WIDTH_40, + ntx, RTW89_RS_MCS, ch - 4); + __fill_txpwr_limit_nonbf_bf(lmt->mcs_40m[2], RTW89_CHANNEL_WIDTH_40, + ntx, RTW89_RS_MCS, ch + 4); + __fill_txpwr_limit_nonbf_bf(lmt->mcs_40m[3], RTW89_CHANNEL_WIDTH_40, + ntx, RTW89_RS_MCS, ch + 12); + + /* fill mcs 80m section */ + __fill_txpwr_limit_nonbf_bf(lmt->mcs_80m[0], RTW89_CHANNEL_WIDTH_80, + ntx, RTW89_RS_MCS, ch - 8); + __fill_txpwr_limit_nonbf_bf(lmt->mcs_80m[1], RTW89_CHANNEL_WIDTH_80, + ntx, RTW89_RS_MCS, ch + 8); + + /* fill mcs 160m section */ + __fill_txpwr_limit_nonbf_bf(lmt->mcs_160m, RTW89_CHANNEL_WIDTH_160, + ntx, RTW89_RS_MCS, ch); + + /* fill mcs 40m 0p5 section */ + __fill_txpwr_limit_nonbf_bf(val_0p5_n, RTW89_CHANNEL_WIDTH_40, + ntx, RTW89_RS_MCS, ch - 4); + __fill_txpwr_limit_nonbf_bf(val_0p5_p, RTW89_CHANNEL_WIDTH_40, + ntx, RTW89_RS_MCS, ch + 4); + + for (i = 0; i < RTW89_BF_NUM; i++) + lmt->mcs_40m_0p5[i] = min_t(s8, val_0p5_n[i], val_0p5_p[i]); + + /* fill mcs 40m 2p5 section */ + __fill_txpwr_limit_nonbf_bf(val_2p5_n, RTW89_CHANNEL_WIDTH_40, + ntx, RTW89_RS_MCS, ch - 8); + __fill_txpwr_limit_nonbf_bf(val_2p5_p, RTW89_CHANNEL_WIDTH_40, + ntx, RTW89_RS_MCS, ch + 8); + + for (i = 0; i < RTW89_BF_NUM; i++) + lmt->mcs_40m_2p5[i] = min_t(s8, val_2p5_n[i], val_2p5_p[i]); +} + void rtw89_phy_fill_txpwr_limit(struct rtw89_dev *rtwdev, struct rtw89_txpwr_limit *lmt, u8 ntx) { + u8 pri_ch = rtwdev->hal.current_primary_channel; u8 ch = rtwdev->hal.current_channel; u8 bw = rtwdev->hal.current_band_width; @@ -1215,20 +1490,24 @@ void rtw89_phy_fill_txpwr_limit(struct rtw89_dev *rtwdev, rtw89_phy_fill_txpwr_limit_20m(rtwdev, lmt, ntx, ch); break; case RTW89_CHANNEL_WIDTH_40: - rtw89_phy_fill_txpwr_limit_40m(rtwdev, lmt, ntx, ch); + rtw89_phy_fill_txpwr_limit_40m(rtwdev, lmt, ntx, ch, pri_ch); break; case RTW89_CHANNEL_WIDTH_80: - rtw89_phy_fill_txpwr_limit_80m(rtwdev, lmt, ntx, ch); + rtw89_phy_fill_txpwr_limit_80m(rtwdev, lmt, ntx, ch, pri_ch); + break; + case RTW89_CHANNEL_WIDTH_160: + rtw89_phy_fill_txpwr_limit_160m(rtwdev, lmt, ntx, ch, pri_ch); break; } } +EXPORT_SYMBOL(rtw89_phy_fill_txpwr_limit); static s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 ru, u8 ntx, u8 ch) { const struct rtw89_chip_info *chip = rtwdev->chip; - u8 ch_idx = rtw89_channel_to_idx(rtwdev, ch); u8 band = rtwdev->hal.current_band_type; + u8 ch_idx = rtw89_channel_to_idx(rtwdev, band, ch); u8 regd = rtw89_regd_get(rtwdev, band); s8 lmt_ru = 0, sar; @@ -1245,6 +1524,12 @@ static s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, lmt_ru = (*chip->txpwr_lmt_ru_5g)[ru][ntx] [RTW89_WW][ch_idx]; break; + case RTW89_BAND_6G: + lmt_ru = (*chip->txpwr_lmt_ru_6g)[ru][ntx][regd][ch_idx]; + if (!lmt_ru) + lmt_ru = (*chip->txpwr_lmt_ru_6g)[ru][ntx] + [RTW89_WW][ch_idx]; + break; default: rtw89_warn(rtwdev, "unknown band type: %d\n", band); return 0; @@ -1319,6 +1604,31 @@ rtw89_phy_fill_txpwr_limit_ru_80m(struct rtw89_dev *rtwdev, ntx, ch + 6); } +static void +rtw89_phy_fill_txpwr_limit_ru_160m(struct rtw89_dev *rtwdev, + struct rtw89_txpwr_limit_ru *lmt_ru, + u8 ntx, u8 ch) +{ + static const int ofst[] = { -14, -10, -6, -2, 2, 6, 10, 14 }; + int i; + + static_assert(ARRAY_SIZE(ofst) == RTW89_RU_SEC_NUM); + for (i = 0; i < RTW89_RU_SEC_NUM; i++) { + lmt_ru->ru26[i] = rtw89_phy_read_txpwr_limit_ru(rtwdev, + RTW89_RU26, + ntx, + ch + ofst[i]); + lmt_ru->ru52[i] = rtw89_phy_read_txpwr_limit_ru(rtwdev, + RTW89_RU52, + ntx, + ch + ofst[i]); + lmt_ru->ru106[i] = rtw89_phy_read_txpwr_limit_ru(rtwdev, + RTW89_RU106, + ntx, + ch + ofst[i]); + } +} + void rtw89_phy_fill_txpwr_limit_ru(struct rtw89_dev *rtwdev, struct rtw89_txpwr_limit_ru *lmt_ru, u8 ntx) @@ -1338,8 +1648,12 @@ void rtw89_phy_fill_txpwr_limit_ru(struct rtw89_dev *rtwdev, case RTW89_CHANNEL_WIDTH_80: rtw89_phy_fill_txpwr_limit_ru_80m(rtwdev, lmt_ru, ntx, ch); break; + case RTW89_CHANNEL_WIDTH_160: + rtw89_phy_fill_txpwr_limit_ru_160m(rtwdev, lmt_ru, ntx, ch); + break; } } +EXPORT_SYMBOL(rtw89_phy_fill_txpwr_limit_ru); struct rtw89_phy_iter_ra_data { struct rtw89_dev *rtwdev; @@ -1401,13 +1715,7 @@ static void rtw89_phy_c2h_ra_rpt_iter(void *data, struct ieee80211_sta *sta) break; } - if (bw == RTW89_CHANNEL_WIDTH_80) - ra_report->txrate.bw = RATE_INFO_BW_80; - else if (bw == RTW89_CHANNEL_WIDTH_40) - ra_report->txrate.bw = RATE_INFO_BW_40; - else - ra_report->txrate.bw = RATE_INFO_BW_20; - + ra_report->txrate.bw = rtw89_hw_to_rate_info_bw(bw); ra_report->bit_rate = cfg80211_calculate_bitrate(&ra_report->txrate); ra_report->hw_rate = FIELD_PREP(RTW89_HW_RATE_MASK_MOD, mode) | FIELD_PREP(RTW89_HW_RATE_MASK_VAL, rate); @@ -1487,15 +1795,25 @@ static void rtw89_phy_cfo_set_crystal_cap(struct rtw89_dev *rtwdev, u8 crystal_cap, bool force) { struct rtw89_cfo_tracking_info *cfo = &rtwdev->cfo_tracking; + const struct rtw89_chip_info *chip = rtwdev->chip; u8 sc_xi_val, sc_xo_val; if (!force && cfo->crystal_cap == crystal_cap) return; crystal_cap = clamp_t(u8, crystal_cap, 0, 127); - rtw89_phy_cfo_set_xcap_reg(rtwdev, true, crystal_cap); - rtw89_phy_cfo_set_xcap_reg(rtwdev, false, crystal_cap); - sc_xo_val = rtw89_phy_cfo_get_xcap_reg(rtwdev, true); - sc_xi_val = rtw89_phy_cfo_get_xcap_reg(rtwdev, false); + if (chip->chip_id == RTL8852A) { + rtw89_phy_cfo_set_xcap_reg(rtwdev, true, crystal_cap); + rtw89_phy_cfo_set_xcap_reg(rtwdev, false, crystal_cap); + sc_xo_val = rtw89_phy_cfo_get_xcap_reg(rtwdev, true); + sc_xi_val = rtw89_phy_cfo_get_xcap_reg(rtwdev, false); + } else { + rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_XTAL_SC_XO, + crystal_cap, XTAL_SC_XO_MASK); + rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_XTAL_SC_XI, + crystal_cap, XTAL_SC_XI_MASK); + rtw89_mac_read_xtal_si(rtwdev, XTAL_SI_XTAL_SC_XO, &sc_xo_val); + rtw89_mac_read_xtal_si(rtwdev, XTAL_SI_XTAL_SC_XI, &sc_xi_val); + } cfo->crystal_cap = sc_xi_val; cfo->x_cap_ofst = (s8)((int)cfo->crystal_cap - cfo->def_x_cap); @@ -1525,9 +1843,11 @@ static void rtw89_phy_cfo_reset(struct rtw89_dev *rtwdev) static void rtw89_dcfo_comp(struct rtw89_dev *rtwdev, s32 curr_cfo) { + const struct rtw89_reg_def *dcfo_comp = rtwdev->chip->dcfo_comp; bool is_linked = rtwdev->total_sta_assoc > 0; s32 cfo_avg_312; - s32 dcfo_comp; + s32 dcfo_comp_val; + u8 dcfo_comp_sft = rtwdev->chip->dcfo_comp_sft; int sign; if (!is_linked) { @@ -1538,13 +1858,13 @@ static void rtw89_dcfo_comp(struct rtw89_dev *rtwdev, s32 curr_cfo) rtw89_debug(rtwdev, RTW89_DBG_CFO, "DCFO: curr_cfo=%d\n", curr_cfo); if (curr_cfo == 0) return; - dcfo_comp = rtw89_phy_read32_mask(rtwdev, R_DCFO, B_DCFO); + dcfo_comp_val = rtw89_phy_read32_mask(rtwdev, R_DCFO, B_DCFO); sign = curr_cfo > 0 ? 1 : -1; - cfo_avg_312 = (curr_cfo << 3) / 5 + sign * dcfo_comp; + cfo_avg_312 = (curr_cfo << dcfo_comp_sft) / 5 + sign * dcfo_comp_val; rtw89_debug(rtwdev, RTW89_DBG_CFO, "DCFO: avg_cfo=%d\n", cfo_avg_312); if (rtwdev->chip->chip_id == RTL8852A && rtwdev->hal.cv == CHIP_CBV) cfo_avg_312 = -cfo_avg_312; - rtw89_phy_set_phy_regs(rtwdev, R_DCFO_COMP_S0, B_DCFO_COMP_S0_MSK, + rtw89_phy_set_phy_regs(rtwdev, dcfo_comp->addr, dcfo_comp->mask, cfo_avg_312); } @@ -1563,8 +1883,12 @@ static void rtw89_phy_cfo_init(struct rtw89_dev *rtwdev) cfo->crystal_cap_default = efuse->xtal_cap & B_AX_XTAL_SC_MASK; cfo->crystal_cap = cfo->crystal_cap_default; cfo->def_x_cap = cfo->crystal_cap; + cfo->x_cap_ub = min_t(int, cfo->def_x_cap + CFO_BOUND, 0x7f); + cfo->x_cap_lb = max_t(int, cfo->def_x_cap - CFO_BOUND, 0x1); cfo->is_adjust = false; + cfo->divergence_lock_en = false; cfo->x_cap_ofst = 0; + cfo->lock_cnt = 0; cfo->rtw89_multi_cfo_mode = RTW89_TP_BASED_AVG_MODE; cfo->apply_compensation = false; cfo->residual_cfo_acc = 0; @@ -1782,6 +2106,23 @@ static void rtw89_phy_cfo_dm(struct rtw89_dev *rtwdev) rtw89_debug(rtwdev, RTW89_DBG_CFO, "curr_cfo=0\n"); return; } + if (cfo->divergence_lock_en) { + cfo->lock_cnt++; + if (cfo->lock_cnt > CFO_PERIOD_CNT) { + cfo->divergence_lock_en = false; + cfo->lock_cnt = 0; + } else { + rtw89_phy_cfo_reset(rtwdev); + } + return; + } + if (cfo->crystal_cap >= cfo->x_cap_ub || + cfo->crystal_cap <= cfo->x_cap_lb) { + cfo->divergence_lock_en = true; + rtw89_phy_cfo_reset(rtwdev); + return; + } + rtw89_phy_cfo_crystal_cap_adjust(rtwdev, new_cfo); cfo->cfo_avg_pre = new_cfo; x_cap_update = cfo->crystal_cap != pre_x_cap; @@ -2845,7 +3186,9 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi, enum rtw89_bandwidth cbw = rtwdev->hal.current_band_width; struct rtw89_dig_info *dig = &rtwdev->dig; u8 final_rssi = 0, under_region = dig->pd_low_th_ofst; - u32 val = 0; + u8 ofdm_cca_th; + s8 cck_cca_th; + u32 pd_val = 0; under_region += PD_TH_SB_FLTR_CMP_VAL; @@ -2856,6 +3199,9 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi, case RTW89_CHANNEL_WIDTH_80: under_region += PD_TH_BW80_CMP_VAL; break; + case RTW89_CHANNEL_WIDTH_160: + under_region += PD_TH_BW160_CMP_VAL; + break; case RTW89_CHANNEL_WIDTH_20: fallthrough; default: @@ -2866,23 +3212,38 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi, dig->dyn_pd_th_max = dig->igi_rssi; final_rssi = min_t(u8, rssi, dig->igi_rssi); - final_rssi = clamp_t(u8, final_rssi, PD_TH_MIN_RSSI + under_region, - PD_TH_MAX_RSSI + under_region); + ofdm_cca_th = clamp_t(u8, final_rssi, PD_TH_MIN_RSSI + under_region, + PD_TH_MAX_RSSI + under_region); if (enable) { - val = (final_rssi - under_region - PD_TH_MIN_RSSI) >> 1; + pd_val = (ofdm_cca_th - under_region - PD_TH_MIN_RSSI) >> 1; rtw89_debug(rtwdev, RTW89_DBG_DIG, - "dyn_max=%d, final_rssi=%d, total=%d, PD_low=%d\n", - dig->igi_rssi, final_rssi, under_region, val); + "igi=%d, ofdm_ccaTH=%d, backoff=%d, PD_low=%d\n", + final_rssi, ofdm_cca_th, under_region, pd_val); } else { rtw89_debug(rtwdev, RTW89_DBG_DIG, "Dynamic PD th disabled, Set PD_low_bd=0\n"); } rtw89_phy_write32_mask(rtwdev, R_SEG0R_PD, B_SEG0R_PD_LOWER_BOUND_MSK, - val); + pd_val); rtw89_phy_write32_mask(rtwdev, R_SEG0R_PD, B_SEG0R_PD_SPATIAL_REUSE_EN_MSK, enable); + + if (!rtwdev->hal.support_cckpd) + return; + + cck_cca_th = max_t(s8, final_rssi - under_region, CCKPD_TH_MIN_RSSI); + pd_val = (u32)(cck_cca_th - IGI_RSSI_MAX); + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "igi=%d, cck_ccaTH=%d, backoff=%d, cck_PD_low=((%d))dB\n", + final_rssi, cck_cca_th, under_region, pd_val); + + rtw89_phy_write32_mask(rtwdev, R_BMODE_PDTH_EN_V1, + B_BMODE_PDTH_LIMIT_EN_MSK_V1, enable); + rtw89_phy_write32_mask(rtwdev, R_BMODE_PDTH_V1, + B_BMODE_PDTH_LOWER_BOUND_MSK_V1, pd_val); } void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev) @@ -2994,3 +3355,55 @@ void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif rtw89_phy_write32_idx(rtwdev, R_BSS_CLR_MAP, B_BSS_CLR_MAP_STAID, vif->bss_conf.aid, phy_idx); } + +static void +_rfk_write_rf(struct rtw89_dev *rtwdev, const struct rtw89_reg5_def *def) +{ + rtw89_write_rf(rtwdev, def->path, def->addr, def->mask, def->data); +} + +static void +_rfk_write32_mask(struct rtw89_dev *rtwdev, const struct rtw89_reg5_def *def) +{ + rtw89_phy_write32_mask(rtwdev, def->addr, def->mask, def->data); +} + +static void +_rfk_write32_set(struct rtw89_dev *rtwdev, const struct rtw89_reg5_def *def) +{ + rtw89_phy_write32_set(rtwdev, def->addr, def->mask); +} + +static void +_rfk_write32_clr(struct rtw89_dev *rtwdev, const struct rtw89_reg5_def *def) +{ + rtw89_phy_write32_clr(rtwdev, def->addr, def->mask); +} + +static void +_rfk_delay(struct rtw89_dev *rtwdev, const struct rtw89_reg5_def *def) +{ + udelay(def->data); +} + +static void +(*_rfk_handler[])(struct rtw89_dev *rtwdev, const struct rtw89_reg5_def *def) = { + [RTW89_RFK_F_WRF] = _rfk_write_rf, + [RTW89_RFK_F_WM] = _rfk_write32_mask, + [RTW89_RFK_F_WS] = _rfk_write32_set, + [RTW89_RFK_F_WC] = _rfk_write32_clr, + [RTW89_RFK_F_DELAY] = _rfk_delay, +}; + +static_assert(ARRAY_SIZE(_rfk_handler) == RTW89_RFK_F_NUM); + +void +rtw89_rfk_parser(struct rtw89_dev *rtwdev, const struct rtw89_rfk_tbl *tbl) +{ + const struct rtw89_reg5_def *p = tbl->defs; + const struct rtw89_reg5_def *end = tbl->defs + tbl->size; + + for (; p < end; p++) + _rfk_handler[p->flag](rtwdev, p); +} +EXPORT_SYMBOL(rtw89_rfk_parser); diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index b1f059b725a1..adcfcb4c2429 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -8,6 +8,7 @@ #include "core.h" #define RTW89_PHY_ADDR_OFFSET 0x10000 +#define RTW89_RF_ADDR_ADSEL_MASK BIT(16) #define get_phy_headline(addr) FIELD_GET(GENMASK(31, 28), addr) #define PHY_HEADLINE_VALID 0xf @@ -55,6 +56,7 @@ #define CFO_TRK_STOP_TH (2 << 2) #define CFO_SW_COMP_FINE_TUNE (2 << 2) #define CFO_PERIOD_CNT 15 +#define CFO_BOUND 32 #define CFO_TP_UPPER 100 #define CFO_TP_LOWER 50 #define CFO_COMP_PERIOD 250 @@ -87,8 +89,11 @@ #define RXB_IDX_MAX 31 #define RXB_IDX_MIN 0 +#define IGI_RSSI_MAX 110 #define PD_TH_MAX_RSSI 70 #define PD_TH_MIN_RSSI 8 +#define CCKPD_TH_MIN_RSSI (-18) +#define PD_TH_BW160_CMP_VAL 9 #define PD_TH_BW80_CMP_VAL 6 #define PD_TH_BW40_CMP_VAL 3 #define PD_TH_BW20_CMP_VAL 0 @@ -265,6 +270,9 @@ const struct rtw89_phy_reg3_tbl _name ## _tbl = { \ .size = ARRAY_SIZE(_name), \ } +extern const u8 rtw89_rs_idx_max[RTW89_RS_MAX]; +extern const u8 rtw89_rs_nss_max[RTW89_RS_MAX]; + static inline void rtw89_phy_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) { @@ -322,6 +330,65 @@ static inline u32 rtw89_phy_read32_mask(struct rtw89_dev *rtwdev, return rtw89_read32_mask(rtwdev, addr | RTW89_PHY_ADDR_OFFSET, mask); } +enum rtw89_rfk_flag { + RTW89_RFK_F_WRF = 0, + RTW89_RFK_F_WM = 1, + RTW89_RFK_F_WS = 2, + RTW89_RFK_F_WC = 3, + RTW89_RFK_F_DELAY = 4, + RTW89_RFK_F_NUM, +}; + +struct rtw89_rfk_tbl { + const struct rtw89_reg5_def *defs; + u32 size; +}; + +#define RTW89_DECLARE_RFK_TBL(_name) \ +const struct rtw89_rfk_tbl _name ## _tbl = { \ + .defs = _name, \ + .size = ARRAY_SIZE(_name), \ +} + +#define RTW89_DECL_RFK_WRF(_path, _addr, _mask, _data) \ + {.flag = RTW89_RFK_F_WRF, \ + .path = _path, \ + .addr = _addr, \ + .mask = _mask, \ + .data = _data,} + +#define RTW89_DECL_RFK_WM(_addr, _mask, _data) \ + {.flag = RTW89_RFK_F_WM, \ + .addr = _addr, \ + .mask = _mask, \ + .data = _data,} + +#define RTW89_DECL_RFK_WS(_addr, _mask) \ + {.flag = RTW89_RFK_F_WS, \ + .addr = _addr, \ + .mask = _mask,} + +#define RTW89_DECL_RFK_WC(_addr, _mask) \ + {.flag = RTW89_RFK_F_WC, \ + .addr = _addr, \ + .mask = _mask,} + +#define RTW89_DECL_RFK_DELAY(_data) \ + {.flag = RTW89_RFK_F_DELAY, \ + .data = _data,} + +void +rtw89_rfk_parser(struct rtw89_dev *rtwdev, const struct rtw89_rfk_tbl *tbl); + +#define rtw89_rfk_parser_by_cond(dev, cond, tbl_t, tbl_f) \ + do { \ + typeof(dev) __dev = (dev); \ + if (cond) \ + rtw89_rfk_parser(__dev, (tbl_t)); \ + else \ + rtw89_rfk_parser(__dev, (tbl_f)); \ + } while (0) + void rtw89_phy_write_reg3_tbl(struct rtw89_dev *rtwdev, const struct rtw89_phy_reg3_tbl *tbl); u8 rtw89_phy_get_txsc(struct rtw89_dev *rtwdev, @@ -329,10 +396,18 @@ u8 rtw89_phy_get_txsc(struct rtw89_dev *rtwdev, enum rtw89_bandwidth dbw); u32 rtw89_phy_read_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask); +u32 rtw89_phy_read_rf_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask); bool rtw89_phy_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask, u32 data); +bool rtw89_phy_write_rf_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask, u32 data); void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev); void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev); +void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev, + const struct rtw89_reg2_def *reg, + enum rtw89_rf_path rf_path, + void *extra_data); void rtw89_phy_dm_init(struct rtw89_dev *rtwdev); void rtw89_phy_write32_idx(struct rtw89_dev *rtwdev, u32 addr, u32 mask, u32 data, enum rtw89_phy_idx phy_idx); diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index e0a416d37d0e..25b106788118 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8,16 +8,36 @@ #define R_AX_SYS_WL_EFUSE_CTRL 0x000A #define B_AX_AUTOLOAD_SUS BIT(5) +#define R_AX_SYS_ISO_CTRL 0x0000 +#define B_AX_PWC_EV2EF_MASK GENMASK(15, 14) +#define B_AX_PWC_EV2EF_B15 BIT(15) +#define B_AX_PWC_EV2EF_B14 BIT(14) +#define B_AX_ISO_EB2CORE BIT(8) + #define R_AX_SYS_FUNC_EN 0x0002 #define B_AX_FEN_BB_GLB_RSTN BIT(1) #define B_AX_FEN_BBRSTB BIT(0) #define R_AX_SYS_PW_CTRL 0x0004 +#define B_AX_XTAL_OFF_A_DIE BIT(22) +#define B_AX_DIS_WLBT_PDNSUSEN_SOPC BIT(18) +#define B_AX_RDY_SYSPWR BIT(17) +#define B_AX_EN_WLON BIT(16) +#define B_AX_APDM_HPDN BIT(15) #define B_AX_PSUS_OFF_CAPC_EN BIT(14) +#define B_AX_AFSM_PCIE_SUS_EN BIT(12) +#define B_AX_AFSM_WLSUS_EN BIT(11) +#define B_AX_APFM_SWLPS BIT(10) +#define B_AX_APFM_OFFMAC BIT(9) +#define B_AX_APFN_ONMAC BIT(8) #define R_AX_SYS_CLK_CTRL 0x0008 #define B_AX_CPU_CLK_EN BIT(14) +#define R_AX_SYS_ADIE_PAD_PWR_CTRL 0x0018 +#define B_AX_SYM_PADPDN_WL_PTA_1P3 BIT(6) +#define B_AX_SYM_PADPDN_WL_RFC_1P3 BIT(5) + #define R_AX_RSV_CTRL 0x001C #define B_AX_R_DIS_PRST BIT(6) #define B_AX_WLOCK_1C_BIT6 BIT(5) @@ -41,6 +61,17 @@ #define B_AX_EF_ADDR_MASK GENMASK(26, 16) #define B_AX_EF_DATA_MASK GENMASK(15, 0) +#define R_AX_EFUSE_CTRL_1_V1 0x0038 +#define B_AX_EF_ENT BIT(31) +#define B_AX_EF_BURST BIT(19) +#define B_AX_EF_TEST_SEL_MASK GENMASK(18, 16) +#define B_AX_EF_TROW_EN BIT(15) +#define B_AX_EF_ERR_FLAG BIT(14) +#define B_AX_EF_DSB_EN BIT(11) +#define B_AX_PCIE_CALIB_EN_V1 BIT(12) +#define B_AX_WDT_WAKE_PCIE_EN BIT(10) +#define B_AX_WDT_WAKE_USB_EN BIT(9) + #define R_AX_GPIO_MUXCFG 0x0040 #define B_AX_BOOT_MODE BIT(19) #define B_AX_WL_EECS_EXT_32K_SEL BIT(18) @@ -72,11 +103,16 @@ #define R_AX_SYS_SDIO_CTRL 0x0070 #define B_AX_PCIE_DIS_L2_CTRL_LDO_HCI BIT(15) #define B_AX_PCIE_DIS_WLSUS_AFT_PDN BIT(14) +#define B_AX_PCIE_CALIB_EN_V1 BIT(12) #define B_AX_PCIE_AUXCLK_GATE BIT(11) #define B_AX_LTE_MUX_CTRL_PATH BIT(26) #define R_AX_PLATFORM_ENABLE 0x0088 #define B_AX_WCPU_EN BIT(1) +#define B_AX_PLATFORM_EN BIT(0) + +#define R_AX_WLLPS_CTRL 0x0090 +#define B_AX_DIS_WLBT_LPSEN_LOPC BIT(1) #define R_AX_SCOREBOARD 0x00AC #define B_AX_TOGGLE BIT(31) @@ -89,11 +125,20 @@ #define R_AX_DBG_PORT_SEL 0x00C0 #define B_AX_DEBUG_ST_MASK GENMASK(31, 0) +#define R_AX_PMC_DBG_CTRL2 0x00CC +#define B_AX_SYSON_DIS_PMCR_AX_WRMSK BIT(2) + #define R_AX_SYS_CFG1 0x00F0 #define B_AX_CHIP_VER_MASK GENMASK(15, 12) #define R_AX_SYS_STATUS1 0x00F4 #define B_AX_SEL_0XC0_MASK GENMASK(17, 16) +#define B_AX_PAD_HCI_SEL_V2_MASK GENMASK(5, 3) +#define MAC_AX_HCI_SEL_SDIO_UART 0 +#define MAC_AX_HCI_SEL_MULTI_USB 1 +#define MAC_AX_HCI_SEL_PCIE_UART 2 +#define MAC_AX_HCI_SEL_PCIE_USB 3 +#define MAC_AX_HCI_SEL_MULTI_SDIO 4 #define R_AX_HALT_H2C_CTRL 0x0160 #define R_AX_HALT_H2C 0x0168 @@ -112,6 +157,7 @@ #define PS_RPWM_TOGGLE BIT(15) #define PS_RPWM_ACK BIT(14) #define PS_RPWM_SEQ_NUM GENMASK(13, 12) +#define PS_RPWM_NOTIFY_WAKE BIT(8) #define PS_RPWM_STATE 0x7 #define RPWM_SEQ_NUM_MAX 3 #define PS_CPWM_SEQ_NUM GENMASK(13, 12) @@ -130,6 +176,21 @@ #define R_AX_UDM2 0x01F8 #define R_AX_UDM3 0x01FC +#define R_AX_LDO_AON_CTRL0 0x0218 +#define B_AX_PD_REGU_L BIT(16) + +#define R_AX_WLAN_XTAL_SI_CTRL 0x0270 +#define B_AX_WL_XTAL_SI_CMD_POLL BIT(31) +#define B_AX_BT_XTAL_SI_ERR_FLAG BIT(30) +#define B_AX_WL_XTAL_GNT BIT(29) +#define B_AX_BT_XTAL_GNT BIT(28) +#define B_AX_WL_XTAL_SI_MODE_MASK GENMASK(25, 24) +#define XTAL_SI_NORMAL_WRITE 0x00 +#define XTAL_SI_NORMAL_READ 0x01 +#define B_AX_WL_XTAL_SI_BITMASK_MASK GENMASK(23, 16) +#define B_AX_WL_XTAL_SI_DATA_MASK GENMASK(15, 8) +#define B_AX_WL_XTAL_SI_ADDR_MASK GENMASK(7, 0) + #define R_AX_XTAL_ON_CTRL0 0x0280 #define B_AX_XTAL_SC_LPS BIT(31) #define B_AX_XTAL_SC_XO_MASK GENMASK(23, 17) @@ -138,6 +199,11 @@ #define R_AX_GPIO0_7_FUNC_SEL 0x02D0 +#define R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN 0x02E4 +#define B_AX_LED1_PULL_LOW_EN BIT(18) +#define B_AX_EESK_PULL_LOW_EN BIT(17) +#define B_AX_EECS_PULL_LOW_EN BIT(16) + #define R_AX_WLRF_CTRL 0x02F0 #define B_AX_WLRF1_CTRL_7 BIT(15) #define B_AX_WLRF1_CTRL_1 BIT(9) @@ -162,6 +228,58 @@ #define B_AX_ASFF_FULL_NO_STK BIT(1) #define B_AX_EN_STUCK_DBG BIT(0) +#define R_AX_HCI_FC_CTRL_V1 0x1700 +#define R_AX_CH_PAGE_CTRL_V1 0x1704 + +#define R_AX_ACH0_PAGE_CTRL_V1 0x1710 +#define R_AX_ACH1_PAGE_CTRL_V1 0x1714 +#define R_AX_ACH2_PAGE_CTRL_V1 0x1718 +#define R_AX_ACH3_PAGE_CTRL_V1 0x171C +#define R_AX_ACH4_PAGE_CTRL_V1 0x1720 +#define R_AX_ACH5_PAGE_CTRL_V1 0x1724 +#define R_AX_ACH6_PAGE_CTRL_V1 0x1728 +#define R_AX_ACH7_PAGE_CTRL_V1 0x172C +#define R_AX_CH8_PAGE_CTRL_V1 0x1730 +#define R_AX_CH9_PAGE_CTRL_V1 0x1734 +#define R_AX_CH10_PAGE_CTRL_V1 0x1738 +#define R_AX_CH11_PAGE_CTRL_V1 0x173C + +#define R_AX_ACH0_PAGE_INFO_V1 0x1750 +#define R_AX_ACH1_PAGE_INFO_V1 0x1754 +#define R_AX_ACH2_PAGE_INFO_V1 0x1758 +#define R_AX_ACH3_PAGE_INFO_V1 0x175C +#define R_AX_ACH4_PAGE_INFO_V1 0x1760 +#define R_AX_ACH5_PAGE_INFO_V1 0x1764 +#define R_AX_ACH6_PAGE_INFO_V1 0x1768 +#define R_AX_ACH7_PAGE_INFO_V1 0x176C +#define R_AX_CH8_PAGE_INFO_V1 0x1770 +#define R_AX_CH9_PAGE_INFO_V1 0x1774 +#define R_AX_CH10_PAGE_INFO_V1 0x1778 +#define R_AX_CH11_PAGE_INFO_V1 0x177C +#define R_AX_CH12_PAGE_INFO_V1 0x1780 + +#define R_AX_PUB_PAGE_INFO3_V1 0x178C +#define R_AX_PUB_PAGE_CTRL1_V1 0x1790 +#define R_AX_PUB_PAGE_CTRL2_V1 0x1794 +#define R_AX_PUB_PAGE_INFO1_V1 0x1798 +#define R_AX_PUB_PAGE_INFO2_V1 0x179C +#define R_AX_WP_PAGE_CTRL1_V1 0x17A0 +#define R_AX_WP_PAGE_CTRL2_V1 0x17A4 +#define R_AX_WP_PAGE_INFO1_V1 0x17A8 + +#define R_AX_H2CREG_DATA0_V1 0x7140 +#define R_AX_H2CREG_DATA1_V1 0x7144 +#define R_AX_H2CREG_DATA2_V1 0x7148 +#define R_AX_H2CREG_DATA3_V1 0x714C +#define R_AX_C2HREG_DATA0_V1 0x7150 +#define R_AX_C2HREG_DATA1_V1 0x7154 +#define R_AX_C2HREG_DATA2_V1 0x7158 +#define R_AX_C2HREG_DATA3_V1 0x715C +#define R_AX_H2CREG_CTRL_V1 0x7160 +#define R_AX_C2HREG_CTRL_V1 0x7164 + +#define R_AX_HCI_FUNC_EN_V1 0x7880 + #define R_AX_PHYREG_SET 0x8040 #define PHYREG_SET_ALL_CYCLE 0x8 @@ -194,6 +312,7 @@ #define R_AX_BOOT_DBG 0x83F0 #define R_AX_DMAC_FUNC_EN 0x8400 +#define B_AX_DMAC_CRPRT BIT(31) #define B_AX_MAC_FUNC_EN BIT(30) #define B_AX_DMAC_FUNC_EN BIT(29) #define B_AX_MPDU_PROC_EN BIT(28) @@ -207,7 +326,10 @@ #define B_AX_PKT_IN_EN BIT(20) #define B_AX_DLE_CPUIO_EN BIT(19) #define B_AX_DISPATCHER_EN BIT(18) +#define B_AX_BBRPT_EN BIT(17) #define B_AX_MAC_SEC_EN BIT(16) +#define B_AX_MAC_UN_EN BIT(15) +#define B_AX_H_AXIDMA_EN BIT(14) #define R_AX_DMAC_CLK_EN 0x8404 #define B_AX_WD_RLS_CLK_EN BIT(27) @@ -218,6 +340,7 @@ #define B_AX_PKT_IN_CLK_EN BIT(20) #define B_AX_DLE_CPUIO_CLK_EN BIT(19) #define B_AX_DISPATCHER_CLK_EN BIT(18) +#define B_AX_BBRPT_CLK_EN BIT(17) #define B_AX_MAC_SEC_CLK_EN BIT(16) #define PCI_LTR_IDLE_TIMER_1US 0 @@ -444,6 +567,7 @@ #define R_AX_PLE_QTA8_CFG 0x9060 #define R_AX_PLE_QTA9_CFG 0x9064 #define R_AX_PLE_QTA10_CFG 0x9068 +#define R_AX_PLE_QTA11_CFG 0x906C #define R_AX_PLE_INI_STATUS 0x9100 #define B_AX_PLE_Q_MGN_INI_RDY BIT(1) @@ -618,6 +742,30 @@ #define R_AX_DBG_FUN_INTF_DATA 0x9F34 #define B_AX_DFI_DATA_MASK GENMASK(31, 0) +#define R_AX_TXPKTCTL_B0_PRELD_CFG0 0x9F48 +#define B_AX_B0_PRELD_FEN BIT(31) +#define B_AX_B0_PRELD_USEMAXSZ_MASK GENMASK(25, 16) +#define PRELD_B0_ENT_NUM 10 +#define PRELD_AMSDU_SIZE 52 +#define B_AX_B0_PRELD_CAM_G1ENTNUM_MASK GENMASK(12, 8) +#define B_AX_B0_PRELD_CAM_G0ENTNUM_MASK GENMASK(4, 0) + +#define R_AX_TXPKTCTL_B0_PRELD_CFG1 0x9F4C +#define B_AX_B0_PRELD_NXT_TXENDWIN_MASK GENMASK(11, 8) +#define PRELD_NEXT_WND 1 +#define B_AX_B0_PRELD_NXT_RSVMINSZ_MASK GENMASK(7, 0) + +#define R_AX_TXPKTCTL_B1_PRELD_CFG0 0x9F88 +#define B_AX_B1_PRELD_FEN BIT(31) +#define B_AX_B1_PRELD_USEMAXSZ_MASK GENMASK(25, 16) +#define PRELD_B1_ENT_NUM 4 +#define B_AX_B1_PRELD_CAM_G1ENTNUM_MASK GENMASK(12, 8) +#define B_AX_B1_PRELD_CAM_G0ENTNUM_MASK GENMASK(4, 0) + +#define R_AX_TXPKTCTL_B1_PRELD_CFG1 0x9F8C +#define B_AX_B1_PRELD_NXT_TXENDWIN_MASK GENMASK(11, 8) +#define B_AX_B1_PRELD_NXT_RSVMINSZ_MASK GENMASK(7, 0) + #define R_AX_AFE_CTRL1 0x0024 #define B_AX_R_SYM_WLCMAC1_P4_PC_EN BIT(4) @@ -745,6 +893,7 @@ #define B_AX_CTN_TXEN_VI_0 BIT(2) #define B_AX_CTN_TXEN_BK_0 BIT(1) #define B_AX_CTN_TXEN_BE_0 BIT(0) +#define B_AX_CTN_TXEN_ALL_MASK GENMASK(15, 0) #define R_AX_MUEDCA_BE_PARAM_0 0xC350 #define R_AX_MUEDCA_BE_PARAM_0_C1 0xE350 @@ -791,6 +940,12 @@ #define B_AX_CTN_CHK_CCA_S20 BIT(1) #define B_AX_CTN_CHK_CCA_P20 BIT(0) +#define R_AX_CTN_DRV_TXEN 0xC398 +#define R_AX_CTN_DRV_TXEN_C1 0xE398 +#define B_AX_CTN_TXEN_TWT_3 BIT(17) +#define B_AX_CTN_TXEN_TWT_2 BIT(16) +#define B_AX_CTN_TXEN_ALL_MASK_V1 GENMASK(17, 0) + #define R_AX_SCHEDULE_ERR_IMR 0xC3E8 #define R_AX_SCHEDULE_ERR_IMR_C1 0xE3E8 #define B_AX_SORT_NON_IDLE_ERR_INT_EN BIT(1) @@ -913,7 +1068,7 @@ #define R_AX_DTIM_CTRL_P2 0xC4A6 #define R_AX_DTIM_CTRL_P3 0xC4E6 #define R_AX_DTIM_CTRL_P4 0xC526 -#define B_AX_DTIM_NUM_MASK GENMASK(15, 0) +#define B_AX_DTIM_NUM_MASK GENMASK(15, 8) #define B_AX_DTIM_CURRCNT_MASK GENMASK(7, 0) #define R_AX_TBTT_SHIFT_P0 0xC428 @@ -964,6 +1119,11 @@ #define B_AX_P0MB2_EN BIT(2) #define B_AX_P0MB1_EN BIT(1) +#define R_AX_P0MB_HGQ_WINDOW_CFG_0 0xC590 +#define R_AX_P0MB_HGQ_WINDOW_CFG_0_C1 0xE590 +#define R_AX_PORT_HGQ_WINDOW_CFG 0xC5A0 +#define R_AX_PORT_HGQ_WINDOW_CFG_C1 0xE5A0 + #define R_AX_AMPDU_AGG_LIMIT 0xC610 #define B_AX_AMPDU_MAX_TIME_MASK GENMASK(31, 24) #define B_AX_RA_TRY_RATE_AGG_LMT_MASK GENMASK(23, 16) @@ -1080,6 +1240,13 @@ #define B_AX_TCR_ZLD_USTIME_AFTERPHYTXON GENMASK(11, 8) #define B_AX_TCR_TXTIMEOUT GENMASK(7, 0) +#define R_AX_MD_TSFT_STMP_CTL 0xCA08 +#define R_AX_MD_TSFT_STMP_CTL_C1 0xEA08 +#define B_AX_TSFT_OFS_MASK GENMASK(31, 16) +#define B_AX_STMP_THSD_MASK GENMASK(15, 8) +#define B_AX_UPD_HGQMD BIT(1) +#define B_AX_UPD_TIMIE BIT(0) + #define R_AX_PPWRBIT_SETTING 0xCA0C #define R_AX_PPWRBIT_SETTING_C1 0xEA0C @@ -1391,8 +1558,10 @@ #define B_AX_PWR_UL_TB_CTRL_EN BIT(31) #define R_AX_PWR_UL_TB_1T 0xD28C #define B_AX_PWR_UL_TB_1T_MASK GENMASK(4, 0) +#define B_AX_PWR_UL_TB_1T_V1_MASK GENMASK(7, 0) #define R_AX_PWR_UL_TB_2T 0xD290 #define B_AX_PWR_UL_TB_2T_MASK GENMASK(4, 0) +#define B_AX_PWR_UL_TB_2T_V1_MASK GENMASK(7, 0) #define R_AX_PWR_BY_RATE_TABLE0 0xD2C0 #define R_AX_PWR_BY_RATE_TABLE10 0xD2E8 #define R_AX_PWR_BY_RATE R_AX_PWR_BY_RATE_TABLE0 @@ -1458,6 +1627,31 @@ #define B_AX_STATIS_BT_LO_TX_1_MASK GENMASK(15, 0) #define B_AX_STATIS_BT_LO_RX_1_MASK GENMASK(31, 16) +#define R_AX_GNT_SW_CTRL 0xDA48 +#define R_AX_GNT_SW_CTRL_C1 0xFA48 +#define B_AX_WL_ACT2_VAL BIT(21) +#define B_AX_WL_ACT2_SWCTRL BIT(20) +#define B_AX_WL_ACT_VAL BIT(19) +#define B_AX_WL_ACT_SWCTRL BIT(18) +#define B_AX_GNT_BT_RX_VAL BIT(17) +#define B_AX_GNT_BT_RX_SWCTRL BIT(16) +#define B_AX_GNT_BT_TX_VAL BIT(15) +#define B_AX_GNT_BT_TX_SWCTRL BIT(14) +#define B_AX_GNT_WL_RX_VAL BIT(13) +#define B_AX_GNT_WL_RX_SWCTRL BIT(12) +#define B_AX_GNT_WL_TX_VAL BIT(11) +#define B_AX_GNT_WL_TX_SWCTRL BIT(10) +#define B_AX_GNT_BT_RFC_S1_VAL BIT(9) +#define B_AX_GNT_BT_RFC_S1_SWCTRL BIT(8) +#define B_AX_GNT_WL_RFC_S1_VAL BIT(7) +#define B_AX_GNT_WL_RFC_S1_SWCTRL BIT(6) +#define B_AX_GNT_BT_RFC_S0_VAL BIT(5) +#define B_AX_GNT_BT_RFC_S0_SWCTRL BIT(4) +#define B_AX_GNT_WL_RFC_S0_VAL BIT(3) +#define B_AX_GNT_WL_RFC_S0_SWCTRL BIT(2) +#define B_AX_GNT_WL_BB_VAL BIT(1) +#define B_AX_GNT_WL_BB_SWCTRL BIT(0) + #define R_AX_TDMA_MODE 0xDA4C #define R_AX_TDMA_MODE_C1 0xFA4C #define B_AX_R_BT_CMD_RPT_MASK GENMASK(31, 16) @@ -1669,6 +1863,17 @@ #define B_ANAPAR_FLTRST BIT(22) #define B_ANAPAR_CRXBB GENMASK(18, 16) #define B_ANAPAR_14 GENMASK(15, 0) +#define R_SWSI_DATA_V1 0x0370 +#define B_SWSI_DATA_VAL_V1 GENMASK(19, 0) +#define B_SWSI_DATA_ADDR_V1 GENMASK(27, 20) +#define B_SWSI_DATA_PATH_V1 GENMASK(30, 28) +#define B_SWSI_DATA_BIT_MASK_EN_V1 BIT(31) +#define R_SWSI_BIT_MASK_V1 0x0374 +#define B_SWSI_BIT_MASK_V1 GENMASK(19, 0) +#define R_SWSI_READ_ADDR_V1 0x0378 +#define B_SWSI_READ_ADDR_ADDR_V1 GENMASK(7, 0) +#define B_SWSI_READ_ADDR_PATH_V1 GENMASK(10, 8) +#define B_SWSI_READ_ADDR_V1 GENMASK(10, 0) #define R_UPD_CLK_ADC 0x0700 #define B_UPD_CLK_ADC_ON BIT(24) #define B_UPD_CLK_ADC_VAL GENMASK(26, 25) @@ -1776,6 +1981,10 @@ #define R_CFO_COMP_SEG0_H 0x1388 #define R_CFO_COMP_SEG0_CTRL 0x138C #define R_DBG32_D 0x1730 +#define R_SWSI_V1 0x174C +#define B_SWSI_W_BUSY_V1 BIT(24) +#define B_SWSI_R_BUSY_V1 BIT(25) +#define B_SWSI_R_DATA_DONE_V1 BIT(26) #define R_TX_COUNTER 0x1A40 #define R_IFS_CLM_TX_CNT 0x1ACC #define B_IFS_CLM_EDCCA_EXCLUDE_CCA_FA_MSK GENMASK(31, 16) @@ -1959,6 +2168,12 @@ #define R_CHBW_MOD 0x4978 #define B_CHBW_MOD_PRICH GENMASK(11, 8) #define B_CHBW_MOD_SBW GENMASK(13, 12) +#define R_DCFO_COMP_S0_V1 0x4A40 +#define B_DCFO_COMP_S0_V1_MSK GENMASK(13, 0) +#define R_BMODE_PDTH_V1 0x4B64 +#define B_BMODE_PDTH_LOWER_BOUND_MSK_V1 GENMASK(31, 24) +#define R_BMODE_PDTH_EN_V1 0x4B74 +#define B_BMODE_PDTH_LIMIT_EN_MSK_V1 BIT(30) #define R_CFO_COMP_SEG1_L 0x5384 #define R_CFO_COMP_SEG1_H 0x5388 #define R_CFO_COMP_SEG1_CTRL 0x538C diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 6b75e4bc7352..41fc8db311ec 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -3,6 +3,7 @@ */ #include "coex.h" +#include "fw.h" #include "mac.h" #include "phy.h" #include "reg.h" @@ -36,16 +37,19 @@ static const struct rtw89_hfc_pub_cfg rtw8852a_hfc_pubcfg_pcie = { static const struct rtw89_hfc_param_ini rtw8852a_hfc_param_ini_pcie[] = { [RTW89_QTA_SCC] = {rtw8852a_hfc_chcfg_pcie, &rtw8852a_hfc_pubcfg_pcie, - &rtw_hfc_preccfg_pcie, RTW89_HCIFC_POH}, - [RTW89_QTA_DLFW] = {NULL, NULL, &rtw_hfc_preccfg_pcie, RTW89_HCIFC_POH}, + &rtw89_hfc_preccfg_pcie, RTW89_HCIFC_POH}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_hfc_preccfg_pcie, + RTW89_HCIFC_POH}, [RTW89_QTA_INVALID] = {NULL}, }; static const struct rtw89_dle_mem rtw8852a_dle_mem_pcie[] = { - [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &wde_size0, &ple_size0, &wde_qt0, - &wde_qt0, &ple_qt4, &ple_qt5}, - [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &wde_size4, &ple_size4, - &wde_qt4, &wde_qt4, &ple_qt13, &ple_qt13}, + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_wde_size0, &rtw89_ple_size0, + &rtw89_wde_qt0, &rtw89_wde_qt0, &rtw89_ple_qt4, + &rtw89_ple_qt5}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_wde_size4, &rtw89_ple_size4, + &rtw89_wde_qt4, &rtw89_wde_qt4, &rtw89_ple_qt13, + &rtw89_ple_qt13}, [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, NULL}, }; @@ -373,6 +377,35 @@ static const struct rtw89_pwr_cfg * const pwr_off_seq_8852a[] = { rtw8852a_pwroff, NULL }; +static const u32 rtw8852a_h2c_regs[RTW89_H2CREG_MAX] = { + R_AX_H2CREG_DATA0, R_AX_H2CREG_DATA1, R_AX_H2CREG_DATA2, + R_AX_H2CREG_DATA3 +}; + +static const u32 rtw8852a_c2h_regs[RTW89_C2HREG_MAX] = { + R_AX_C2HREG_DATA0, R_AX_C2HREG_DATA1, R_AX_C2HREG_DATA2, + R_AX_C2HREG_DATA3 +}; + +static const struct rtw89_page_regs rtw8852a_page_regs = { + .hci_fc_ctrl = R_AX_HCI_FC_CTRL, + .ch_page_ctrl = R_AX_CH_PAGE_CTRL, + .ach_page_ctrl = R_AX_ACH0_PAGE_CTRL, + .ach_page_info = R_AX_ACH0_PAGE_INFO, + .pub_page_info3 = R_AX_PUB_PAGE_INFO3, + .pub_page_ctrl1 = R_AX_PUB_PAGE_CTRL1, + .pub_page_ctrl2 = R_AX_PUB_PAGE_CTRL2, + .pub_page_info1 = R_AX_PUB_PAGE_INFO1, + .pub_page_info2 = R_AX_PUB_PAGE_INFO2, + .wp_page_ctrl1 = R_AX_WP_PAGE_CTRL1, + .wp_page_ctrl2 = R_AX_WP_PAGE_CTRL2, + .wp_page_info1 = R_AX_WP_PAGE_INFO1, +}; + +static const struct rtw89_reg_def rtw8852a_dcfo_comp = { + R_DCFO_COMP_S0, B_DCFO_COMP_S0_MSK +}; + static void rtw8852ae_efuse_parsing(struct rtw89_efuse *efuse, struct rtw8852a_efuse *map) { @@ -1134,7 +1167,7 @@ static void rtw8852a_set_channel_help(struct rtw89_dev *rtwdev, bool enter, u8 phy_idx = RTW89_PHY_0; if (enter) { - rtw89_mac_stop_sch_tx(rtwdev, RTW89_MAC_0, &p->tx_en, RTW89_SCH_TX_SEL_ALL); + rtw89_chip_stop_sch_tx(rtwdev, RTW89_MAC_0, &p->tx_en, RTW89_SCH_TX_SEL_ALL); rtw89_mac_cfg_ppdu_status(rtwdev, RTW89_MAC_0, false); rtw8852a_dfs_en(rtwdev, false); rtw8852a_tssi_cont_en_phyidx(rtwdev, false, RTW89_PHY_0); @@ -1147,7 +1180,7 @@ static void rtw8852a_set_channel_help(struct rtw89_dev *rtwdev, bool enter, rtw8852a_dfs_en(rtwdev, true); rtw8852a_tssi_cont_en_phyidx(rtwdev, true, RTW89_PHY_0); rtw8852a_bb_reset_en(rtwdev, phy_idx, true); - rtw89_mac_resume_sch_tx(rtwdev, RTW89_MAC_0, p->tx_en); + rtw89_chip_resume_sch_tx(rtwdev, RTW89_MAC_0, p->tx_en); } } @@ -1242,10 +1275,10 @@ static u32 rtw8852a_bb_cal_txpwr_ref(struct rtw89_dev *rtwdev, static void rtw8852a_set_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev, - s16 pw_ofst, enum rtw89_mac_idx mac_idx) + s8 pw_ofst, enum rtw89_mac_idx mac_idx) { - s32 val_1t = 0; - s32 val_2t = 0; + s8 val_1t = 0; + s8 val_2t = 0; u32 reg; if (pw_ofst < -16 || pw_ofst > 15) { @@ -1255,7 +1288,7 @@ void rtw8852a_set_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev, } reg = rtw89_mac_reg_by_idx(R_AX_PWR_UL_TB_CTRL, mac_idx); rtw89_write32_set(rtwdev, reg, B_AX_PWR_UL_TB_CTRL_EN); - val_1t = (s32)pw_ofst; + val_1t = pw_ofst; reg = rtw89_mac_reg_by_idx(R_AX_PWR_UL_TB_1T, mac_idx); rtw89_write32_mask(rtwdev, reg, B_AX_PWR_UL_TB_1T_MASK, val_1t); val_2t = max(val_1t - 3, -16); @@ -1984,6 +2017,12 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = { .query_ppdu = rtw8852a_query_ppdu, .bb_ctrl_btc_preagc = rtw8852a_bb_ctrl_btc_preagc, .set_txpwr_ul_tb_offset = rtw8852a_set_txpwr_ul_tb_offset, + .pwr_on_func = NULL, + .pwr_off_func = NULL, + .cfg_ctrl_path = rtw89_mac_cfg_ctrl_path, + .mac_cfg_gnt = rtw89_mac_cfg_gnt, + .stop_sch_tx = rtw89_mac_stop_sch_tx, + .resume_sch_tx = rtw89_mac_resume_sch_tx, .btc_set_rfe = rtw8852a_btc_set_rfe, .btc_init_cfg = rtw8852a_btc_init_cfg, @@ -2019,6 +2058,9 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .txpwr_factor_rf = 2, .txpwr_factor_mac = 1, .dig_table = &rtw89_8852a_phy_dig_table, + .support_bands = BIT(NL80211_BAND_2GHZ) | + BIT(NL80211_BAND_5GHZ), + .support_bw160 = false, .rf_path_num = 2, .tx_nss = 2, .rx_nss = 2, @@ -2029,6 +2071,8 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .physical_efuse_size = 1216, .logical_efuse_size = 1536, .limit_efuse_size = 1152, + .dav_phy_efuse_size = 0, + .dav_log_efuse_size = 0, .phycap_addr = 0x580, .phycap_size = 128, .para_ver = 0x05050864, @@ -2049,7 +2093,18 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .ps_mode_supported = BIT(RTW89_PS_MODE_RFOFF) | BIT(RTW89_PS_MODE_CLK_GATED) | BIT(RTW89_PS_MODE_PWR_GATED), + .hci_func_en_addr = R_AX_HCI_FUNC_EN, + .h2c_ctrl_reg = R_AX_H2CREG_CTRL, + .h2c_regs = rtw8852a_h2c_regs, + .c2h_ctrl_reg = R_AX_C2HREG_CTRL, + .c2h_regs = rtw8852a_c2h_regs, + .page_regs = &rtw8852a_page_regs, + .dcfo_comp = &rtw8852a_dcfo_comp, + .dcfo_comp_sft = 3, }; EXPORT_SYMBOL(rtw8852a_chip_info); MODULE_FIRMWARE("rtw89/rtw8852a_fw.bin"); +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11ax wireless 8852A driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.h b/drivers/net/wireless/realtek/rtw89/rtw8852a.h index 633384374de0..fcff1194c009 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.h @@ -93,6 +93,8 @@ struct rtw8852a_bb_pmac_info { u8 duty_cycle; }; +extern const struct rtw89_chip_info rtw8852a_chip_info; + void rtw8852a_bb_set_plcp_tx(struct rtw89_dev *rtwdev); void rtw8852a_bb_set_pmac_tx(struct rtw89_dev *rtwdev, struct rtw8852a_bb_pmac_info *tx_info, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c index c021e93eb07b..ad272854c442 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c @@ -12,66 +12,6 @@ #include "rtw8852a_rfk_table.h" #include "rtw8852a_table.h" -static void -_rfk_write_rf(struct rtw89_dev *rtwdev, const struct rtw89_reg5_def *def) -{ - rtw89_write_rf(rtwdev, def->path, def->addr, def->mask, def->data); -} - -static void -_rfk_write32_mask(struct rtw89_dev *rtwdev, const struct rtw89_reg5_def *def) -{ - rtw89_phy_write32_mask(rtwdev, def->addr, def->mask, def->data); -} - -static void -_rfk_write32_set(struct rtw89_dev *rtwdev, const struct rtw89_reg5_def *def) -{ - rtw89_phy_write32_set(rtwdev, def->addr, def->mask); -} - -static void -_rfk_write32_clr(struct rtw89_dev *rtwdev, const struct rtw89_reg5_def *def) -{ - rtw89_phy_write32_clr(rtwdev, def->addr, def->mask); -} - -static void -_rfk_delay(struct rtw89_dev *rtwdev, const struct rtw89_reg5_def *def) -{ - udelay(def->data); -} - -static void -(*_rfk_handler[])(struct rtw89_dev *rtwdev, const struct rtw89_reg5_def *def) = { - [RTW89_RFK_F_WRF] = _rfk_write_rf, - [RTW89_RFK_F_WM] = _rfk_write32_mask, - [RTW89_RFK_F_WS] = _rfk_write32_set, - [RTW89_RFK_F_WC] = _rfk_write32_clr, - [RTW89_RFK_F_DELAY] = _rfk_delay, -}; - -static_assert(ARRAY_SIZE(_rfk_handler) == RTW89_RFK_F_NUM); - -static void -rtw89_rfk_parser(struct rtw89_dev *rtwdev, const struct rtw89_rfk_tbl *tbl) -{ - const struct rtw89_reg5_def *p = tbl->defs; - const struct rtw89_reg5_def *end = tbl->defs + tbl->size; - - for (; p < end; p++) - _rfk_handler[p->flag](rtwdev, p); -} - -#define rtw89_rfk_parser_by_cond(rtwdev, cond, tbl_t, tbl_f) \ - do { \ - typeof(rtwdev) _dev = (rtwdev); \ - if (cond) \ - rtw89_rfk_parser(_dev, (tbl_t)); \ - else \ - rtw89_rfk_parser(_dev, (tbl_f)); \ - } while (0) - static u8 _kpath(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RFK]dbcc_en: %x, PHY%d\n", @@ -2977,6 +2917,7 @@ static void _tssi_set_tmeter_tbl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx ph u8 i, j; switch (subband) { + default: case RTW89_CH_2G: thm_up_a = rtw89_8852a_trk_cfg.delta_swingidx_2ga_p; thm_down_a = rtw89_8852a_trk_cfg.delta_swingidx_2ga_n; @@ -3161,6 +3102,7 @@ static void _tssi_pak(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, u8 subband = rtwdev->hal.current_subband; switch (subband) { + default: case RTW89_CH_2G: rtw89_rfk_parser_by_cond(rtwdev, path == RF_PATH_A, &rtw8852a_tssi_pak_defs_a_2g_tbl, @@ -3584,7 +3526,7 @@ static void _tssi_pre_tx(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy) const struct rtw89_chip_info *mac_reg = rtwdev->chip; u8 ch = rtwdev->hal.current_channel, ch_tmp; u8 bw = rtwdev->hal.current_band_width; - u16 tx_en; + u32 tx_en; u8 phy_map = rtw89_btc_phymap(rtwdev, phy, 0); s8 power; s16 xdbm; @@ -3612,7 +3554,7 @@ static void _tssi_pre_tx(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy) __func__, phy, power, xdbm); rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_DPK, BTC_WRFK_START); - rtw89_mac_stop_sch_tx(rtwdev, phy, &tx_en, RTW89_SCH_TX_SEL_ALL); + rtw89_chip_stop_sch_tx(rtwdev, phy, &tx_en, RTW89_SCH_TX_SEL_ALL); _wait_rx_mode(rtwdev, _kpath(rtwdev, phy)); tx_counter = rtw89_phy_read32_mask(rtwdev, R_TX_COUNTER, MASKLWORD); @@ -3658,7 +3600,7 @@ static void _tssi_pre_tx(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy) rtw8852a_bb_tx_mode_switch(rtwdev, phy, 0); - rtw89_mac_resume_sch_tx(rtwdev, phy, tx_en); + rtw89_chip_resume_sch_tx(rtwdev, phy, tx_en); rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_DPK, BTC_WRFK_STOP); } @@ -3681,11 +3623,11 @@ void rtw8852a_dack(struct rtw89_dev *rtwdev) void rtw8852a_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - u16 tx_en; + u32 tx_en; u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, 0); rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_IQK, BTC_WRFK_START); - rtw89_mac_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); + rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); _wait_rx_mode(rtwdev, _kpath(rtwdev, phy_idx)); _iqk_init(rtwdev); @@ -3694,7 +3636,7 @@ void rtw8852a_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) else _iqk(rtwdev, phy_idx, false); - rtw89_mac_resume_sch_tx(rtwdev, phy_idx, tx_en); + rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en); rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_IQK, BTC_WRFK_STOP); } @@ -3706,33 +3648,33 @@ void rtw8852a_iqk_track(struct rtw89_dev *rtwdev) void rtw8852a_rx_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool is_afe) { - u16 tx_en; + u32 tx_en; u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, 0); rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_RXDCK, BTC_WRFK_START); - rtw89_mac_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); + rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); _wait_rx_mode(rtwdev, _kpath(rtwdev, phy_idx)); _rx_dck(rtwdev, phy_idx, is_afe); - rtw89_mac_resume_sch_tx(rtwdev, phy_idx, tx_en); + rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en); rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_RXDCK, BTC_WRFK_STOP); } void rtw8852a_dpk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - u16 tx_en; + u32 tx_en; u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, 0); rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_DPK, BTC_WRFK_START); - rtw89_mac_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); + rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); _wait_rx_mode(rtwdev, _kpath(rtwdev, phy_idx)); rtwdev->dpk.is_dpk_enable = true; rtwdev->dpk.is_dpk_reload_en = false; _dpk(rtwdev, phy_idx, false); - rtw89_mac_resume_sch_tx(rtwdev, phy_idx, tx_en); + rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en); rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_DPK, BTC_WRFK_STOP); } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk_table.c index 510570090502..dd2a978b9bae 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk_table.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk_table.c @@ -5,1603 +5,1603 @@ #include "rtw8852a_rfk_table.h" static const struct rtw89_reg5_def rtw8852a_tssi_sys_defs[] = { - DECL_RFK_WM(0x12a8, 0x00000001, 0x00000001), - DECL_RFK_WM(0x12a8, 0x0000000e, 0x00000002), - DECL_RFK_WM(0x32a8, 0x00000001, 0x00000001), - DECL_RFK_WM(0x32a8, 0x0000000e, 0x00000002), - DECL_RFK_WM(0x12bc, 0x000000f0, 0x00000005), - DECL_RFK_WM(0x12bc, 0x00000f00, 0x00000005), - DECL_RFK_WM(0x12bc, 0x000f0000, 0x00000005), - DECL_RFK_WM(0x12bc, 0x0000f000, 0x00000005), - DECL_RFK_WM(0x120c, 0x000000ff, 0x00000033), - DECL_RFK_WM(0x12c0, 0x0ff00000, 0x00000033), - DECL_RFK_WM(0x32bc, 0x000000f0, 0x00000005), - DECL_RFK_WM(0x32bc, 0x00000f00, 0x00000005), - DECL_RFK_WM(0x32bc, 0x000f0000, 0x00000005), - DECL_RFK_WM(0x32bc, 0x0000f000, 0x00000005), - DECL_RFK_WM(0x320c, 0x000000ff, 0x00000033), - DECL_RFK_WM(0x32c0, 0x0ff00000, 0x00000033), - DECL_RFK_WM(0x0300, 0xff000000, 0x00000019), - DECL_RFK_WM(0x0304, 0x000000ff, 0x00000019), - DECL_RFK_WM(0x0304, 0x0000ff00, 0x0000001d), - DECL_RFK_WM(0x0314, 0xffff0000, 0x00002044), - DECL_RFK_WM(0x0318, 0x0000ffff, 0x00002042), - DECL_RFK_WM(0x0318, 0xffff0000, 0x00002002), - DECL_RFK_WM(0x0020, 0x00006000, 0x00000003), - DECL_RFK_WM(0x0024, 0x00006000, 0x00000003), - DECL_RFK_WM(0x0704, 0xffff0000, 0x0000601e), - DECL_RFK_WM(0x2704, 0xffff0000, 0x0000601e), - DECL_RFK_WM(0x0700, 0xf0000000, 0x00000004), - DECL_RFK_WM(0x2700, 0xf0000000, 0x00000004), - DECL_RFK_WM(0x0650, 0x3c000000, 0x00000000), - DECL_RFK_WM(0x2650, 0x3c000000, 0x00000000), + RTW89_DECL_RFK_WM(0x12a8, 0x00000001, 0x00000001), + RTW89_DECL_RFK_WM(0x12a8, 0x0000000e, 0x00000002), + RTW89_DECL_RFK_WM(0x32a8, 0x00000001, 0x00000001), + RTW89_DECL_RFK_WM(0x32a8, 0x0000000e, 0x00000002), + RTW89_DECL_RFK_WM(0x12bc, 0x000000f0, 0x00000005), + RTW89_DECL_RFK_WM(0x12bc, 0x00000f00, 0x00000005), + RTW89_DECL_RFK_WM(0x12bc, 0x000f0000, 0x00000005), + RTW89_DECL_RFK_WM(0x12bc, 0x0000f000, 0x00000005), + RTW89_DECL_RFK_WM(0x120c, 0x000000ff, 0x00000033), + RTW89_DECL_RFK_WM(0x12c0, 0x0ff00000, 0x00000033), + RTW89_DECL_RFK_WM(0x32bc, 0x000000f0, 0x00000005), + RTW89_DECL_RFK_WM(0x32bc, 0x00000f00, 0x00000005), + RTW89_DECL_RFK_WM(0x32bc, 0x000f0000, 0x00000005), + RTW89_DECL_RFK_WM(0x32bc, 0x0000f000, 0x00000005), + RTW89_DECL_RFK_WM(0x320c, 0x000000ff, 0x00000033), + RTW89_DECL_RFK_WM(0x32c0, 0x0ff00000, 0x00000033), + RTW89_DECL_RFK_WM(0x0300, 0xff000000, 0x00000019), + RTW89_DECL_RFK_WM(0x0304, 0x000000ff, 0x00000019), + RTW89_DECL_RFK_WM(0x0304, 0x0000ff00, 0x0000001d), + RTW89_DECL_RFK_WM(0x0314, 0xffff0000, 0x00002044), + RTW89_DECL_RFK_WM(0x0318, 0x0000ffff, 0x00002042), + RTW89_DECL_RFK_WM(0x0318, 0xffff0000, 0x00002002), + RTW89_DECL_RFK_WM(0x0020, 0x00006000, 0x00000003), + RTW89_DECL_RFK_WM(0x0024, 0x00006000, 0x00000003), + RTW89_DECL_RFK_WM(0x0704, 0xffff0000, 0x0000601e), + RTW89_DECL_RFK_WM(0x2704, 0xffff0000, 0x0000601e), + RTW89_DECL_RFK_WM(0x0700, 0xf0000000, 0x00000004), + RTW89_DECL_RFK_WM(0x2700, 0xf0000000, 0x00000004), + RTW89_DECL_RFK_WM(0x0650, 0x3c000000, 0x00000000), + RTW89_DECL_RFK_WM(0x2650, 0x3c000000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_sys_defs); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_sys_defs); static const struct rtw89_reg5_def rtw8852a_tssi_sys_defs_2g[] = { - DECL_RFK_WM(0x120c, 0x000000ff, 0x00000033), - DECL_RFK_WM(0x12c0, 0x0ff00000, 0x00000033), - DECL_RFK_WM(0x32c0, 0x0ff00000, 0x00000033), - DECL_RFK_WM(0x320c, 0x000000ff, 0x00000033), + RTW89_DECL_RFK_WM(0x120c, 0x000000ff, 0x00000033), + RTW89_DECL_RFK_WM(0x12c0, 0x0ff00000, 0x00000033), + RTW89_DECL_RFK_WM(0x32c0, 0x0ff00000, 0x00000033), + RTW89_DECL_RFK_WM(0x320c, 0x000000ff, 0x00000033), }; -DECLARE_RFK_TBL(rtw8852a_tssi_sys_defs_2g); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_sys_defs_2g); static const struct rtw89_reg5_def rtw8852a_tssi_sys_defs_5g[] = { - DECL_RFK_WM(0x120c, 0x000000ff, 0x00000044), - DECL_RFK_WM(0x12c0, 0x0ff00000, 0x00000044), - DECL_RFK_WM(0x32c0, 0x0ff00000, 0x00000044), - DECL_RFK_WM(0x320c, 0x000000ff, 0x00000044), + RTW89_DECL_RFK_WM(0x120c, 0x000000ff, 0x00000044), + RTW89_DECL_RFK_WM(0x12c0, 0x0ff00000, 0x00000044), + RTW89_DECL_RFK_WM(0x32c0, 0x0ff00000, 0x00000044), + RTW89_DECL_RFK_WM(0x320c, 0x000000ff, 0x00000044), }; -DECLARE_RFK_TBL(rtw8852a_tssi_sys_defs_5g); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_sys_defs_5g); static const struct rtw89_reg5_def rtw8852a_tssi_txpwr_ctrl_bb_defs_a[] = { - DECL_RFK_WM(0x5800, 0x000000ff, 0x0000007f), - DECL_RFK_WM(0x5800, 0x0000ff00, 0x00000080), - DECL_RFK_WM(0x5800, 0x003f0000, 0x0000003f), - DECL_RFK_WM(0x5800, 0x10000000, 0x00000000), - DECL_RFK_WM(0x5800, 0x20000000, 0x00000000), - DECL_RFK_WM(0x5800, 0xc0000000, 0x00000000), - DECL_RFK_WM(0x5804, 0xf8000000, 0x00000000), - DECL_RFK_WM(0x580c, 0x0000007f, 0x00000040), - DECL_RFK_WM(0x580c, 0x00007f00, 0x00000040), - DECL_RFK_WM(0x580c, 0x00008000, 0x00000000), - DECL_RFK_WM(0x580c, 0x0fff0000, 0x00000000), - DECL_RFK_WM(0x5810, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x5810, 0x00000200, 0x00000000), - DECL_RFK_WM(0x5810, 0x0000fc00, 0x00000000), - DECL_RFK_WM(0x5810, 0x00010000, 0x00000001), - DECL_RFK_WM(0x5810, 0x00fe0000, 0x00000000), - DECL_RFK_WM(0x5810, 0x01000000, 0x00000001), - DECL_RFK_WM(0x5810, 0x06000000, 0x00000000), - DECL_RFK_WM(0x5810, 0x38000000, 0x00000003), - DECL_RFK_WM(0x5810, 0x40000000, 0x00000001), - DECL_RFK_WM(0x5810, 0x80000000, 0x00000000), - DECL_RFK_WM(0x5814, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x5814, 0x00000c00, 0x00000000), - DECL_RFK_WM(0x5814, 0x00001000, 0x00000001), - DECL_RFK_WM(0x5814, 0x00002000, 0x00000000), - DECL_RFK_WM(0x5814, 0x00004000, 0x00000001), - DECL_RFK_WM(0x5814, 0x00038000, 0x00000005), - DECL_RFK_WM(0x5814, 0x003c0000, 0x00000000), - DECL_RFK_WM(0x5814, 0x01c00000, 0x00000000), - DECL_RFK_WM(0x5814, 0x18000000, 0x00000000), - DECL_RFK_WM(0x5814, 0xe0000000, 0x00000000), - DECL_RFK_WM(0x5818, 0x000000ff, 0x00000000), - DECL_RFK_WM(0x5818, 0x0001ff00, 0x00000018), - DECL_RFK_WM(0x5818, 0x03fe0000, 0x00000016), - DECL_RFK_WM(0x5818, 0xfc000000, 0x00000000), - DECL_RFK_WM(0x581c, 0x000003ff, 0x00000280), - DECL_RFK_WM(0x581c, 0x000ffc00, 0x00000200), - DECL_RFK_WM(0x581c, 0x00100000, 0x00000000), - DECL_RFK_WM(0x581c, 0x01e00000, 0x00000008), - DECL_RFK_WM(0x581c, 0x01e00000, 0x0000000e), - DECL_RFK_WM(0x581c, 0x1e000000, 0x00000008), - DECL_RFK_WM(0x581c, 0x1e000000, 0x0000000e), - DECL_RFK_WM(0x581c, 0x20000000, 0x00000000), - DECL_RFK_WM(0x5820, 0x00000fff, 0x00000080), - DECL_RFK_WM(0x5820, 0x0000f000, 0x0000000f), - DECL_RFK_WM(0x5820, 0x001f0000, 0x00000000), - DECL_RFK_WM(0x5820, 0xffe00000, 0x00000000), - DECL_RFK_WM(0x5824, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x5824, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5828, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x582c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x582c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5830, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x5834, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x5834, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5838, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x583c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x583c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5840, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x5844, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x5844, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5848, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x584c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x584c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5850, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x5854, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x5854, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5858, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x585c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x585c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5860, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x5828, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5828, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x5830, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5830, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x5838, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5838, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x5840, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5840, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x5848, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5848, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x5850, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5850, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x5858, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5858, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x5860, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5860, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x5860, 0x80000000, 0x00000000), - DECL_RFK_WM(0x5864, 0x000003ff, 0x000001ff), - DECL_RFK_WM(0x5864, 0x000ffc00, 0x00000200), - DECL_RFK_WM(0x5864, 0x03f00000, 0x00000000), - DECL_RFK_WM(0x5864, 0x04000000, 0x00000000), - DECL_RFK_WM(0x5898, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x589c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x58a0, 0x000000ff, 0x000000fd), - DECL_RFK_WM(0x58a0, 0x0000ff00, 0x000000e5), - DECL_RFK_WM(0x58a0, 0x00ff0000, 0x000000cd), - DECL_RFK_WM(0x58a0, 0xff000000, 0x000000b5), - DECL_RFK_WM(0x58a4, 0x000000ff, 0x00000016), - DECL_RFK_WM(0x58a4, 0x0001ff00, 0x00000000), - DECL_RFK_WM(0x58a4, 0x03fe0000, 0x00000000), - DECL_RFK_WM(0x58a8, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x58a8, 0x0003fe00, 0x00000000), - DECL_RFK_WM(0x58a8, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x58ac, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x58ac, 0x0003fe00, 0x00000000), - DECL_RFK_WM(0x58ac, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x58b0, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x58b4, 0x0000001f, 0x00000000), - DECL_RFK_WM(0x58b4, 0x00000020, 0x00000000), - DECL_RFK_WM(0x58b4, 0x000001c0, 0x00000000), - DECL_RFK_WM(0x58b4, 0x00000200, 0x00000000), - DECL_RFK_WM(0x58b4, 0x0000f000, 0x00000002), - DECL_RFK_WM(0x58b4, 0x00ff0000, 0x00000000), - DECL_RFK_WM(0x58b4, 0x7f000000, 0x0000000a), - DECL_RFK_WM(0x58b8, 0x0000007f, 0x00000028), - DECL_RFK_WM(0x58b8, 0x00007f00, 0x00000076), - DECL_RFK_WM(0x58b8, 0x007f0000, 0x00000000), - DECL_RFK_WM(0x58b8, 0x7f000000, 0x00000000), - DECL_RFK_WM(0x58bc, 0x000000ff, 0x0000007f), - DECL_RFK_WM(0x58bc, 0x0000ff00, 0x00000080), - DECL_RFK_WM(0x58bc, 0x00030000, 0x00000003), - DECL_RFK_WM(0x58bc, 0x000c0000, 0x00000001), - DECL_RFK_WM(0x58bc, 0x00300000, 0x00000002), - DECL_RFK_WM(0x58bc, 0x00c00000, 0x00000002), - DECL_RFK_WM(0x58bc, 0x07000000, 0x00000007), - DECL_RFK_WM(0x58c0, 0x00fe0000, 0x0000003f), - DECL_RFK_WM(0x58c0, 0xff000000, 0x00000000), - DECL_RFK_WM(0x58c4, 0x0003ffff, 0x0003ffff), - DECL_RFK_WM(0x58c4, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x58c4, 0xc0000000, 0x00000000), - DECL_RFK_WM(0x58c8, 0x00ffffff, 0x00000000), - DECL_RFK_WM(0x58c8, 0xf0000000, 0x00000000), - DECL_RFK_WM(0x58cc, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x58d0, 0x00001fff, 0x00000101), - DECL_RFK_WM(0x58d0, 0x0001e000, 0x00000004), - DECL_RFK_WM(0x58d0, 0x03fe0000, 0x00000100), - DECL_RFK_WM(0x58d0, 0x04000000, 0x00000000), - DECL_RFK_WM(0x58d4, 0x000000ff, 0x00000000), - DECL_RFK_WM(0x58d4, 0x0003fe00, 0x000000ff), - DECL_RFK_WM(0x58d4, 0x07fc0000, 0x00000100), - DECL_RFK_WM(0x58d8, 0x000001ff, 0x0000016c), - DECL_RFK_WM(0x58d8, 0x0003fe00, 0x0000005c), - DECL_RFK_WM(0x58d8, 0x000c0000, 0x00000002), - DECL_RFK_WM(0x58d8, 0xfff00000, 0x00000800), - DECL_RFK_WM(0x58dc, 0x000000ff, 0x0000007f), - DECL_RFK_WM(0x58dc, 0x0000ff00, 0x00000080), - DECL_RFK_WM(0x58dc, 0x00010000, 0x00000000), - DECL_RFK_WM(0x58dc, 0x3ff00000, 0x00000000), - DECL_RFK_WM(0x58dc, 0x80000000, 0x00000001), - DECL_RFK_WM(0x58f0, 0x000001ff, 0x000001ff), - DECL_RFK_WM(0x58f0, 0x0003fe00, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x5800, 0x000000ff, 0x0000007f), + RTW89_DECL_RFK_WM(0x5800, 0x0000ff00, 0x00000080), + RTW89_DECL_RFK_WM(0x5800, 0x003f0000, 0x0000003f), + RTW89_DECL_RFK_WM(0x5800, 0x10000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5800, 0x20000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5800, 0xc0000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5804, 0xf8000000, 0x00000000), + RTW89_DECL_RFK_WM(0x580c, 0x0000007f, 0x00000040), + RTW89_DECL_RFK_WM(0x580c, 0x00007f00, 0x00000040), + RTW89_DECL_RFK_WM(0x580c, 0x00008000, 0x00000000), + RTW89_DECL_RFK_WM(0x580c, 0x0fff0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5810, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x5810, 0x00000200, 0x00000000), + RTW89_DECL_RFK_WM(0x5810, 0x0000fc00, 0x00000000), + RTW89_DECL_RFK_WM(0x5810, 0x00010000, 0x00000001), + RTW89_DECL_RFK_WM(0x5810, 0x00fe0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5810, 0x01000000, 0x00000001), + RTW89_DECL_RFK_WM(0x5810, 0x06000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5810, 0x38000000, 0x00000003), + RTW89_DECL_RFK_WM(0x5810, 0x40000000, 0x00000001), + RTW89_DECL_RFK_WM(0x5810, 0x80000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5814, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x5814, 0x00000c00, 0x00000000), + RTW89_DECL_RFK_WM(0x5814, 0x00001000, 0x00000001), + RTW89_DECL_RFK_WM(0x5814, 0x00002000, 0x00000000), + RTW89_DECL_RFK_WM(0x5814, 0x00004000, 0x00000001), + RTW89_DECL_RFK_WM(0x5814, 0x00038000, 0x00000005), + RTW89_DECL_RFK_WM(0x5814, 0x003c0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5814, 0x01c00000, 0x00000000), + RTW89_DECL_RFK_WM(0x5814, 0x18000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5814, 0xe0000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5818, 0x000000ff, 0x00000000), + RTW89_DECL_RFK_WM(0x5818, 0x0001ff00, 0x00000018), + RTW89_DECL_RFK_WM(0x5818, 0x03fe0000, 0x00000016), + RTW89_DECL_RFK_WM(0x5818, 0xfc000000, 0x00000000), + RTW89_DECL_RFK_WM(0x581c, 0x000003ff, 0x00000280), + RTW89_DECL_RFK_WM(0x581c, 0x000ffc00, 0x00000200), + RTW89_DECL_RFK_WM(0x581c, 0x00100000, 0x00000000), + RTW89_DECL_RFK_WM(0x581c, 0x01e00000, 0x00000008), + RTW89_DECL_RFK_WM(0x581c, 0x01e00000, 0x0000000e), + RTW89_DECL_RFK_WM(0x581c, 0x1e000000, 0x00000008), + RTW89_DECL_RFK_WM(0x581c, 0x1e000000, 0x0000000e), + RTW89_DECL_RFK_WM(0x581c, 0x20000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5820, 0x00000fff, 0x00000080), + RTW89_DECL_RFK_WM(0x5820, 0x0000f000, 0x0000000f), + RTW89_DECL_RFK_WM(0x5820, 0x001f0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5820, 0xffe00000, 0x00000000), + RTW89_DECL_RFK_WM(0x5824, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x5824, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5828, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x582c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x582c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5830, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x5834, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x5834, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5838, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x583c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x583c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5840, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x5844, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x5844, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5848, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x584c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x584c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5850, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x5854, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x5854, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5858, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x585c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x585c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5860, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x5828, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5828, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x5830, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5830, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x5838, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5838, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x5840, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5840, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x5848, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5848, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x5850, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5850, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x5858, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5858, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x5860, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5860, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x5860, 0x80000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5864, 0x000003ff, 0x000001ff), + RTW89_DECL_RFK_WM(0x5864, 0x000ffc00, 0x00000200), + RTW89_DECL_RFK_WM(0x5864, 0x03f00000, 0x00000000), + RTW89_DECL_RFK_WM(0x5864, 0x04000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5898, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x589c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x58a0, 0x000000ff, 0x000000fd), + RTW89_DECL_RFK_WM(0x58a0, 0x0000ff00, 0x000000e5), + RTW89_DECL_RFK_WM(0x58a0, 0x00ff0000, 0x000000cd), + RTW89_DECL_RFK_WM(0x58a0, 0xff000000, 0x000000b5), + RTW89_DECL_RFK_WM(0x58a4, 0x000000ff, 0x00000016), + RTW89_DECL_RFK_WM(0x58a4, 0x0001ff00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a4, 0x03fe0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58a8, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58a8, 0x0003fe00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a8, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x0003fe00, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58b0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x58b4, 0x0000001f, 0x00000000), + RTW89_DECL_RFK_WM(0x58b4, 0x00000020, 0x00000000), + RTW89_DECL_RFK_WM(0x58b4, 0x000001c0, 0x00000000), + RTW89_DECL_RFK_WM(0x58b4, 0x00000200, 0x00000000), + RTW89_DECL_RFK_WM(0x58b4, 0x0000f000, 0x00000002), + RTW89_DECL_RFK_WM(0x58b4, 0x00ff0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58b4, 0x7f000000, 0x0000000a), + RTW89_DECL_RFK_WM(0x58b8, 0x0000007f, 0x00000028), + RTW89_DECL_RFK_WM(0x58b8, 0x00007f00, 0x00000076), + RTW89_DECL_RFK_WM(0x58b8, 0x007f0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58b8, 0x7f000000, 0x00000000), + RTW89_DECL_RFK_WM(0x58bc, 0x000000ff, 0x0000007f), + RTW89_DECL_RFK_WM(0x58bc, 0x0000ff00, 0x00000080), + RTW89_DECL_RFK_WM(0x58bc, 0x00030000, 0x00000003), + RTW89_DECL_RFK_WM(0x58bc, 0x000c0000, 0x00000001), + RTW89_DECL_RFK_WM(0x58bc, 0x00300000, 0x00000002), + RTW89_DECL_RFK_WM(0x58bc, 0x00c00000, 0x00000002), + RTW89_DECL_RFK_WM(0x58bc, 0x07000000, 0x00000007), + RTW89_DECL_RFK_WM(0x58c0, 0x00fe0000, 0x0000003f), + RTW89_DECL_RFK_WM(0x58c0, 0xff000000, 0x00000000), + RTW89_DECL_RFK_WM(0x58c4, 0x0003ffff, 0x0003ffff), + RTW89_DECL_RFK_WM(0x58c4, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58c4, 0xc0000000, 0x00000000), + RTW89_DECL_RFK_WM(0x58c8, 0x00ffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x58c8, 0xf0000000, 0x00000000), + RTW89_DECL_RFK_WM(0x58cc, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x58d0, 0x00001fff, 0x00000101), + RTW89_DECL_RFK_WM(0x58d0, 0x0001e000, 0x00000004), + RTW89_DECL_RFK_WM(0x58d0, 0x03fe0000, 0x00000100), + RTW89_DECL_RFK_WM(0x58d0, 0x04000000, 0x00000000), + RTW89_DECL_RFK_WM(0x58d4, 0x000000ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58d4, 0x0003fe00, 0x000000ff), + RTW89_DECL_RFK_WM(0x58d4, 0x07fc0000, 0x00000100), + RTW89_DECL_RFK_WM(0x58d8, 0x000001ff, 0x0000016c), + RTW89_DECL_RFK_WM(0x58d8, 0x0003fe00, 0x0000005c), + RTW89_DECL_RFK_WM(0x58d8, 0x000c0000, 0x00000002), + RTW89_DECL_RFK_WM(0x58d8, 0xfff00000, 0x00000800), + RTW89_DECL_RFK_WM(0x58dc, 0x000000ff, 0x0000007f), + RTW89_DECL_RFK_WM(0x58dc, 0x0000ff00, 0x00000080), + RTW89_DECL_RFK_WM(0x58dc, 0x00010000, 0x00000000), + RTW89_DECL_RFK_WM(0x58dc, 0x3ff00000, 0x00000000), + RTW89_DECL_RFK_WM(0x58dc, 0x80000000, 0x00000001), + RTW89_DECL_RFK_WM(0x58f0, 0x000001ff, 0x000001ff), + RTW89_DECL_RFK_WM(0x58f0, 0x0003fe00, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_txpwr_ctrl_bb_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_txpwr_ctrl_bb_defs_a); static const struct rtw89_reg5_def rtw8852a_tssi_txpwr_ctrl_bb_defs_b[] = { - DECL_RFK_WM(0x7800, 0x000000ff, 0x0000007f), - DECL_RFK_WM(0x7800, 0x0000ff00, 0x00000080), - DECL_RFK_WM(0x7800, 0x003f0000, 0x0000003f), - DECL_RFK_WM(0x7800, 0x10000000, 0x00000000), - DECL_RFK_WM(0x7800, 0x20000000, 0x00000000), - DECL_RFK_WM(0x7800, 0xc0000000, 0x00000000), - DECL_RFK_WM(0x7804, 0xf8000000, 0x00000000), - DECL_RFK_WM(0x780c, 0x0000007f, 0x00000040), - DECL_RFK_WM(0x780c, 0x00007f00, 0x00000040), - DECL_RFK_WM(0x780c, 0x00008000, 0x00000000), - DECL_RFK_WM(0x780c, 0x0fff0000, 0x00000000), - DECL_RFK_WM(0x7810, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x7810, 0x00000200, 0x00000000), - DECL_RFK_WM(0x7810, 0x0000fc00, 0x00000000), - DECL_RFK_WM(0x7810, 0x00010000, 0x00000001), - DECL_RFK_WM(0x7810, 0x00fe0000, 0x00000000), - DECL_RFK_WM(0x7810, 0x01000000, 0x00000001), - DECL_RFK_WM(0x7810, 0x06000000, 0x00000000), - DECL_RFK_WM(0x7810, 0x38000000, 0x00000003), - DECL_RFK_WM(0x7810, 0x40000000, 0x00000001), - DECL_RFK_WM(0x7810, 0x80000000, 0x00000000), - DECL_RFK_WM(0x7814, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x7814, 0x00000c00, 0x00000000), - DECL_RFK_WM(0x7814, 0x00001000, 0x00000001), - DECL_RFK_WM(0x7814, 0x00002000, 0x00000000), - DECL_RFK_WM(0x7814, 0x00004000, 0x00000001), - DECL_RFK_WM(0x7814, 0x00038000, 0x00000005), - DECL_RFK_WM(0x7814, 0x003c0000, 0x00000000), - DECL_RFK_WM(0x7814, 0x01c00000, 0x00000000), - DECL_RFK_WM(0x7814, 0x18000000, 0x00000000), - DECL_RFK_WM(0x7814, 0xe0000000, 0x00000000), - DECL_RFK_WM(0x7818, 0x000000ff, 0x00000000), - DECL_RFK_WM(0x7818, 0x0001ff00, 0x00000018), - DECL_RFK_WM(0x7818, 0x03fe0000, 0x00000016), - DECL_RFK_WM(0x7818, 0xfc000000, 0x00000000), - DECL_RFK_WM(0x781c, 0x000003ff, 0x00000280), - DECL_RFK_WM(0x781c, 0x000ffc00, 0x00000200), - DECL_RFK_WM(0x781c, 0x00100000, 0x00000000), - DECL_RFK_WM(0x781c, 0x01e00000, 0x00000008), - DECL_RFK_WM(0x781c, 0x01e00000, 0x0000000e), - DECL_RFK_WM(0x781c, 0x1e000000, 0x00000008), - DECL_RFK_WM(0x781c, 0x1e000000, 0x0000000e), - DECL_RFK_WM(0x781c, 0x20000000, 0x00000000), - DECL_RFK_WM(0x7820, 0x00000fff, 0x00000080), - DECL_RFK_WM(0x7820, 0x0000f000, 0x00000000), - DECL_RFK_WM(0x7820, 0x001f0000, 0x00000000), - DECL_RFK_WM(0x7820, 0xffe00000, 0x00000000), - DECL_RFK_WM(0x7824, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x7824, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7828, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x782c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x782c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7830, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x7834, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x7834, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7838, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x783c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x783c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7840, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x7844, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x7844, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7848, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x784c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x784c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7850, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x7854, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x7854, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7858, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x785c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x785c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7860, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x7828, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7828, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x7830, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7830, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x7838, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7838, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x7840, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7840, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x7848, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7848, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x7850, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7850, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x7858, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7858, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x7860, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7860, 0x7fc00000, 0x00000000), - DECL_RFK_WM(0x7860, 0x80000000, 0x00000000), - DECL_RFK_WM(0x7864, 0x000003ff, 0x000001ff), - DECL_RFK_WM(0x7864, 0x000ffc00, 0x00000200), - DECL_RFK_WM(0x7864, 0x03f00000, 0x00000000), - DECL_RFK_WM(0x7864, 0x04000000, 0x00000000), - DECL_RFK_WM(0x7898, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x789c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x78a0, 0x000000ff, 0x000000fd), - DECL_RFK_WM(0x78a0, 0x0000ff00, 0x000000e5), - DECL_RFK_WM(0x78a0, 0x00ff0000, 0x000000cd), - DECL_RFK_WM(0x78a0, 0xff000000, 0x000000b5), - DECL_RFK_WM(0x78a4, 0x000000ff, 0x00000016), - DECL_RFK_WM(0x78a4, 0x0001ff00, 0x00000000), - DECL_RFK_WM(0x78a4, 0x03fe0000, 0x00000000), - DECL_RFK_WM(0x78a8, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x78a8, 0x0003fe00, 0x00000000), - DECL_RFK_WM(0x78a8, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x78ac, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x78ac, 0x0003fe00, 0x00000000), - DECL_RFK_WM(0x78ac, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x78b0, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x78b4, 0x0000001f, 0x00000000), - DECL_RFK_WM(0x78b4, 0x00000020, 0x00000000), - DECL_RFK_WM(0x78b4, 0x000001c0, 0x00000000), - DECL_RFK_WM(0x78b4, 0x00000200, 0x00000000), - DECL_RFK_WM(0x78b4, 0x0000f000, 0x00000002), - DECL_RFK_WM(0x78b4, 0x00ff0000, 0x00000000), - DECL_RFK_WM(0x78b4, 0x7f000000, 0x0000000a), - DECL_RFK_WM(0x78b8, 0x0000007f, 0x00000028), - DECL_RFK_WM(0x78b8, 0x00007f00, 0x00000076), - DECL_RFK_WM(0x78b8, 0x007f0000, 0x00000000), - DECL_RFK_WM(0x78b8, 0x7f000000, 0x00000000), - DECL_RFK_WM(0x78bc, 0x000000ff, 0x0000007f), - DECL_RFK_WM(0x78bc, 0x0000ff00, 0x00000080), - DECL_RFK_WM(0x78bc, 0x00030000, 0x00000003), - DECL_RFK_WM(0x78bc, 0x000c0000, 0x00000001), - DECL_RFK_WM(0x78bc, 0x00300000, 0x00000002), - DECL_RFK_WM(0x78bc, 0x00c00000, 0x00000002), - DECL_RFK_WM(0x78bc, 0x07000000, 0x00000007), - DECL_RFK_WM(0x78c0, 0x00fe0000, 0x0000003f), - DECL_RFK_WM(0x78c0, 0xff000000, 0x00000000), - DECL_RFK_WM(0x78c4, 0x0003ffff, 0x0003ffff), - DECL_RFK_WM(0x78c4, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x78c4, 0xc0000000, 0x00000000), - DECL_RFK_WM(0x78c8, 0x00ffffff, 0x00000000), - DECL_RFK_WM(0x78c8, 0xf0000000, 0x00000000), - DECL_RFK_WM(0x78cc, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x78d0, 0x00001fff, 0x00000101), - DECL_RFK_WM(0x78d0, 0x0001e000, 0x00000004), - DECL_RFK_WM(0x78d0, 0x03fe0000, 0x00000100), - DECL_RFK_WM(0x78d0, 0x04000000, 0x00000000), - DECL_RFK_WM(0x78d4, 0x000000ff, 0x00000000), - DECL_RFK_WM(0x78d4, 0x0003fe00, 0x000000ff), - DECL_RFK_WM(0x78d4, 0x07fc0000, 0x00000100), - DECL_RFK_WM(0x78d8, 0x000001ff, 0x0000016c), - DECL_RFK_WM(0x78d8, 0x0003fe00, 0x0000005c), - DECL_RFK_WM(0x78d8, 0x000c0000, 0x00000002), - DECL_RFK_WM(0x78d8, 0xfff00000, 0x00000800), - DECL_RFK_WM(0x78dc, 0x000000ff, 0x0000007f), - DECL_RFK_WM(0x78dc, 0x0000ff00, 0x00000080), - DECL_RFK_WM(0x78dc, 0x00010000, 0x00000000), - DECL_RFK_WM(0x78dc, 0x3ff00000, 0x00000000), - DECL_RFK_WM(0x78dc, 0x80000000, 0x00000001), - DECL_RFK_WM(0x78f0, 0x000001ff, 0x000001ff), - DECL_RFK_WM(0x78f0, 0x0003fe00, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x7800, 0x000000ff, 0x0000007f), + RTW89_DECL_RFK_WM(0x7800, 0x0000ff00, 0x00000080), + RTW89_DECL_RFK_WM(0x7800, 0x003f0000, 0x0000003f), + RTW89_DECL_RFK_WM(0x7800, 0x10000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7800, 0x20000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7800, 0xc0000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7804, 0xf8000000, 0x00000000), + RTW89_DECL_RFK_WM(0x780c, 0x0000007f, 0x00000040), + RTW89_DECL_RFK_WM(0x780c, 0x00007f00, 0x00000040), + RTW89_DECL_RFK_WM(0x780c, 0x00008000, 0x00000000), + RTW89_DECL_RFK_WM(0x780c, 0x0fff0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7810, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x7810, 0x00000200, 0x00000000), + RTW89_DECL_RFK_WM(0x7810, 0x0000fc00, 0x00000000), + RTW89_DECL_RFK_WM(0x7810, 0x00010000, 0x00000001), + RTW89_DECL_RFK_WM(0x7810, 0x00fe0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7810, 0x01000000, 0x00000001), + RTW89_DECL_RFK_WM(0x7810, 0x06000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7810, 0x38000000, 0x00000003), + RTW89_DECL_RFK_WM(0x7810, 0x40000000, 0x00000001), + RTW89_DECL_RFK_WM(0x7810, 0x80000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7814, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x7814, 0x00000c00, 0x00000000), + RTW89_DECL_RFK_WM(0x7814, 0x00001000, 0x00000001), + RTW89_DECL_RFK_WM(0x7814, 0x00002000, 0x00000000), + RTW89_DECL_RFK_WM(0x7814, 0x00004000, 0x00000001), + RTW89_DECL_RFK_WM(0x7814, 0x00038000, 0x00000005), + RTW89_DECL_RFK_WM(0x7814, 0x003c0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7814, 0x01c00000, 0x00000000), + RTW89_DECL_RFK_WM(0x7814, 0x18000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7814, 0xe0000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7818, 0x000000ff, 0x00000000), + RTW89_DECL_RFK_WM(0x7818, 0x0001ff00, 0x00000018), + RTW89_DECL_RFK_WM(0x7818, 0x03fe0000, 0x00000016), + RTW89_DECL_RFK_WM(0x7818, 0xfc000000, 0x00000000), + RTW89_DECL_RFK_WM(0x781c, 0x000003ff, 0x00000280), + RTW89_DECL_RFK_WM(0x781c, 0x000ffc00, 0x00000200), + RTW89_DECL_RFK_WM(0x781c, 0x00100000, 0x00000000), + RTW89_DECL_RFK_WM(0x781c, 0x01e00000, 0x00000008), + RTW89_DECL_RFK_WM(0x781c, 0x01e00000, 0x0000000e), + RTW89_DECL_RFK_WM(0x781c, 0x1e000000, 0x00000008), + RTW89_DECL_RFK_WM(0x781c, 0x1e000000, 0x0000000e), + RTW89_DECL_RFK_WM(0x781c, 0x20000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7820, 0x00000fff, 0x00000080), + RTW89_DECL_RFK_WM(0x7820, 0x0000f000, 0x00000000), + RTW89_DECL_RFK_WM(0x7820, 0x001f0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7820, 0xffe00000, 0x00000000), + RTW89_DECL_RFK_WM(0x7824, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x7824, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7828, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x782c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x782c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7830, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x7834, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x7834, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7838, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x783c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x783c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7840, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x7844, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x7844, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7848, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x784c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x784c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7850, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x7854, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x7854, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7858, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x785c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x785c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7860, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x7828, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7828, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x7830, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7830, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x7838, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7838, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x7840, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7840, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x7848, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7848, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x7850, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7850, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x7858, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7858, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x7860, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7860, 0x7fc00000, 0x00000000), + RTW89_DECL_RFK_WM(0x7860, 0x80000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7864, 0x000003ff, 0x000001ff), + RTW89_DECL_RFK_WM(0x7864, 0x000ffc00, 0x00000200), + RTW89_DECL_RFK_WM(0x7864, 0x03f00000, 0x00000000), + RTW89_DECL_RFK_WM(0x7864, 0x04000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7898, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x789c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x78a0, 0x000000ff, 0x000000fd), + RTW89_DECL_RFK_WM(0x78a0, 0x0000ff00, 0x000000e5), + RTW89_DECL_RFK_WM(0x78a0, 0x00ff0000, 0x000000cd), + RTW89_DECL_RFK_WM(0x78a0, 0xff000000, 0x000000b5), + RTW89_DECL_RFK_WM(0x78a4, 0x000000ff, 0x00000016), + RTW89_DECL_RFK_WM(0x78a4, 0x0001ff00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a4, 0x03fe0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78a8, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78a8, 0x0003fe00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a8, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x0003fe00, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78b0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x78b4, 0x0000001f, 0x00000000), + RTW89_DECL_RFK_WM(0x78b4, 0x00000020, 0x00000000), + RTW89_DECL_RFK_WM(0x78b4, 0x000001c0, 0x00000000), + RTW89_DECL_RFK_WM(0x78b4, 0x00000200, 0x00000000), + RTW89_DECL_RFK_WM(0x78b4, 0x0000f000, 0x00000002), + RTW89_DECL_RFK_WM(0x78b4, 0x00ff0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78b4, 0x7f000000, 0x0000000a), + RTW89_DECL_RFK_WM(0x78b8, 0x0000007f, 0x00000028), + RTW89_DECL_RFK_WM(0x78b8, 0x00007f00, 0x00000076), + RTW89_DECL_RFK_WM(0x78b8, 0x007f0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78b8, 0x7f000000, 0x00000000), + RTW89_DECL_RFK_WM(0x78bc, 0x000000ff, 0x0000007f), + RTW89_DECL_RFK_WM(0x78bc, 0x0000ff00, 0x00000080), + RTW89_DECL_RFK_WM(0x78bc, 0x00030000, 0x00000003), + RTW89_DECL_RFK_WM(0x78bc, 0x000c0000, 0x00000001), + RTW89_DECL_RFK_WM(0x78bc, 0x00300000, 0x00000002), + RTW89_DECL_RFK_WM(0x78bc, 0x00c00000, 0x00000002), + RTW89_DECL_RFK_WM(0x78bc, 0x07000000, 0x00000007), + RTW89_DECL_RFK_WM(0x78c0, 0x00fe0000, 0x0000003f), + RTW89_DECL_RFK_WM(0x78c0, 0xff000000, 0x00000000), + RTW89_DECL_RFK_WM(0x78c4, 0x0003ffff, 0x0003ffff), + RTW89_DECL_RFK_WM(0x78c4, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78c4, 0xc0000000, 0x00000000), + RTW89_DECL_RFK_WM(0x78c8, 0x00ffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x78c8, 0xf0000000, 0x00000000), + RTW89_DECL_RFK_WM(0x78cc, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x78d0, 0x00001fff, 0x00000101), + RTW89_DECL_RFK_WM(0x78d0, 0x0001e000, 0x00000004), + RTW89_DECL_RFK_WM(0x78d0, 0x03fe0000, 0x00000100), + RTW89_DECL_RFK_WM(0x78d0, 0x04000000, 0x00000000), + RTW89_DECL_RFK_WM(0x78d4, 0x000000ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78d4, 0x0003fe00, 0x000000ff), + RTW89_DECL_RFK_WM(0x78d4, 0x07fc0000, 0x00000100), + RTW89_DECL_RFK_WM(0x78d8, 0x000001ff, 0x0000016c), + RTW89_DECL_RFK_WM(0x78d8, 0x0003fe00, 0x0000005c), + RTW89_DECL_RFK_WM(0x78d8, 0x000c0000, 0x00000002), + RTW89_DECL_RFK_WM(0x78d8, 0xfff00000, 0x00000800), + RTW89_DECL_RFK_WM(0x78dc, 0x000000ff, 0x0000007f), + RTW89_DECL_RFK_WM(0x78dc, 0x0000ff00, 0x00000080), + RTW89_DECL_RFK_WM(0x78dc, 0x00010000, 0x00000000), + RTW89_DECL_RFK_WM(0x78dc, 0x3ff00000, 0x00000000), + RTW89_DECL_RFK_WM(0x78dc, 0x80000000, 0x00000001), + RTW89_DECL_RFK_WM(0x78f0, 0x000001ff, 0x000001ff), + RTW89_DECL_RFK_WM(0x78f0, 0x0003fe00, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_txpwr_ctrl_bb_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_txpwr_ctrl_bb_defs_b); static const struct rtw89_reg5_def rtw8852a_tssi_txpwr_ctrl_bb_defs_2g[] = { - DECL_RFK_WM(0x58d8, 0x000001ff, 0x0000013c), - DECL_RFK_WM(0x78d8, 0x000001ff, 0x0000013c), + RTW89_DECL_RFK_WM(0x58d8, 0x000001ff, 0x0000013c), + RTW89_DECL_RFK_WM(0x78d8, 0x000001ff, 0x0000013c), }; -DECLARE_RFK_TBL(rtw8852a_tssi_txpwr_ctrl_bb_defs_2g); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_txpwr_ctrl_bb_defs_2g); static const struct rtw89_reg5_def rtw8852a_tssi_txpwr_ctrl_bb_defs_5g[] = { - DECL_RFK_WM(0x58d8, 0x000001ff, 0x0000016c), - DECL_RFK_WM(0x78d8, 0x000001ff, 0x0000016c), + RTW89_DECL_RFK_WM(0x58d8, 0x000001ff, 0x0000016c), + RTW89_DECL_RFK_WM(0x78d8, 0x000001ff, 0x0000016c), }; -DECLARE_RFK_TBL(rtw8852a_tssi_txpwr_ctrl_bb_defs_5g); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_txpwr_ctrl_bb_defs_5g); static const struct rtw89_reg5_def rtw8852a_tssi_txpwr_ctrl_bb_he_tb_defs_a[] = { - DECL_RFK_WM(0x58a0, 0xffffffff, 0x000000fc), - DECL_RFK_WM(0x58e4, 0x0000007f, 0x00000020), + RTW89_DECL_RFK_WM(0x58a0, 0xffffffff, 0x000000fc), + RTW89_DECL_RFK_WM(0x58e4, 0x0000007f, 0x00000020), }; -DECLARE_RFK_TBL(rtw8852a_tssi_txpwr_ctrl_bb_he_tb_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_txpwr_ctrl_bb_he_tb_defs_a); static const struct rtw89_reg5_def rtw8852a_tssi_txpwr_ctrl_bb_he_tb_defs_b[] = { - DECL_RFK_WM(0x78a0, 0xffffffff, 0x000000fc), - DECL_RFK_WM(0x78e4, 0x0000007f, 0x00000020), + RTW89_DECL_RFK_WM(0x78a0, 0xffffffff, 0x000000fc), + RTW89_DECL_RFK_WM(0x78e4, 0x0000007f, 0x00000020), }; -DECLARE_RFK_TBL(rtw8852a_tssi_txpwr_ctrl_bb_he_tb_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_txpwr_ctrl_bb_he_tb_defs_b); static const struct rtw89_reg5_def rtw8852a_tssi_dck_defs_a[] = { - DECL_RFK_WM(0x580c, 0x0fff0000, 0x00000000), - DECL_RFK_WM(0x5814, 0x00001000, 0x00000001), - DECL_RFK_WM(0x5814, 0x00002000, 0x00000001), - DECL_RFK_WM(0x5814, 0x00004000, 0x00000001), - DECL_RFK_WM(0x5814, 0x00038000, 0x00000005), - DECL_RFK_WM(0x5814, 0x003c0000, 0x00000003), - DECL_RFK_WM(0x5814, 0x18000000, 0x00000000), + RTW89_DECL_RFK_WM(0x580c, 0x0fff0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5814, 0x00001000, 0x00000001), + RTW89_DECL_RFK_WM(0x5814, 0x00002000, 0x00000001), + RTW89_DECL_RFK_WM(0x5814, 0x00004000, 0x00000001), + RTW89_DECL_RFK_WM(0x5814, 0x00038000, 0x00000005), + RTW89_DECL_RFK_WM(0x5814, 0x003c0000, 0x00000003), + RTW89_DECL_RFK_WM(0x5814, 0x18000000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_dck_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_dck_defs_a); static const struct rtw89_reg5_def rtw8852a_tssi_dck_defs_b[] = { - DECL_RFK_WM(0x780c, 0x0fff0000, 0x00000000), - DECL_RFK_WM(0x7814, 0x00001000, 0x00000001), - DECL_RFK_WM(0x7814, 0x00002000, 0x00000001), - DECL_RFK_WM(0x7814, 0x00004000, 0x00000001), - DECL_RFK_WM(0x7814, 0x00038000, 0x00000005), - DECL_RFK_WM(0x7814, 0x003c0000, 0x00000003), - DECL_RFK_WM(0x7814, 0x18000000, 0x00000000), + RTW89_DECL_RFK_WM(0x780c, 0x0fff0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7814, 0x00001000, 0x00000001), + RTW89_DECL_RFK_WM(0x7814, 0x00002000, 0x00000001), + RTW89_DECL_RFK_WM(0x7814, 0x00004000, 0x00000001), + RTW89_DECL_RFK_WM(0x7814, 0x00038000, 0x00000005), + RTW89_DECL_RFK_WM(0x7814, 0x003c0000, 0x00000003), + RTW89_DECL_RFK_WM(0x7814, 0x18000000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_dck_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_dck_defs_b); static const struct rtw89_reg5_def rtw8852a_tssi_dac_gain_tbl_defs_a[] = { - DECL_RFK_WM(0x58b0, 0x00000fff, 0x00000000), - DECL_RFK_WM(0x58b0, 0x00000800, 0x00000001), - DECL_RFK_WM(0x5a00, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a04, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a08, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a0c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a10, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a14, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a18, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a1c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a20, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a24, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a28, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a2c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a30, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a34, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a38, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a3c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a40, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a44, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a48, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a4c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a50, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a54, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a58, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a5c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a60, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a64, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a68, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a6c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a70, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a74, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a78, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a7c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a80, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a84, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a88, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a8c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a90, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a94, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a98, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5a9c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5aa0, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5aa4, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5aa8, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5aac, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5ab0, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5ab4, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5ab8, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5abc, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x5ac0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x58b0, 0x00000fff, 0x00000000), + RTW89_DECL_RFK_WM(0x58b0, 0x00000800, 0x00000001), + RTW89_DECL_RFK_WM(0x5a00, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a04, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a08, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a0c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a10, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a14, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a18, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a1c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a20, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a24, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a28, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a2c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a30, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a34, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a38, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a3c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a40, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a44, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a48, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a4c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a50, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a54, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a58, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a5c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a60, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a64, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a68, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a6c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a70, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a74, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a78, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a7c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a80, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a84, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a88, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a8c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a90, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a94, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a98, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a9c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5aa0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5aa4, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5aa8, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5aac, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5ab0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5ab4, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5ab8, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5abc, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5ac0, 0xffffffff, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_dac_gain_tbl_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_dac_gain_tbl_defs_a); static const struct rtw89_reg5_def rtw8852a_tssi_dac_gain_tbl_defs_b[] = { - DECL_RFK_WM(0x78b0, 0x00000fff, 0x00000000), - DECL_RFK_WM(0x78b0, 0x00000800, 0x00000001), - DECL_RFK_WM(0x7a00, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a04, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a08, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a0c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a10, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a14, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a18, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a1c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a20, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a24, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a28, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a2c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a30, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a34, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a38, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a3c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a40, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a44, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a48, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a4c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a50, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a54, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a58, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a5c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a60, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a64, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a68, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a6c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a70, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a74, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a78, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a7c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a80, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a84, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a88, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a8c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a90, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a94, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a98, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7a9c, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7aa0, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7aa4, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7aa8, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7aac, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7ab0, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7ab4, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7ab8, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7abc, 0xffffffff, 0x00000000), - DECL_RFK_WM(0x7ac0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x78b0, 0x00000fff, 0x00000000), + RTW89_DECL_RFK_WM(0x78b0, 0x00000800, 0x00000001), + RTW89_DECL_RFK_WM(0x7a00, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a04, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a08, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a0c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a10, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a14, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a18, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a1c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a20, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a24, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a28, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a2c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a30, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a34, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a38, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a3c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a40, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a44, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a48, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a4c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a50, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a54, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a58, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a5c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a60, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a64, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a68, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a6c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a70, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a74, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a78, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a7c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a80, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a84, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a88, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a8c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a90, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a94, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a98, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a9c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7aa0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7aa4, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7aa8, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7aac, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7ab0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7ab4, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7ab8, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7abc, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7ac0, 0xffffffff, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_dac_gain_tbl_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_dac_gain_tbl_defs_b); static const struct rtw89_reg5_def rtw8852a_tssi_slope_cal_org_defs_a[] = { - DECL_RFK_WM(0x581c, 0x00100000, 0x00000000), - DECL_RFK_WM(0x58cc, 0x00001000, 0x00000001), - DECL_RFK_WM(0x58cc, 0x00000007, 0x00000000), - DECL_RFK_WM(0x58cc, 0x00000038, 0x00000001), - DECL_RFK_WM(0x58cc, 0x000001c0, 0x00000002), - DECL_RFK_WM(0x58cc, 0x00000e00, 0x00000003), - DECL_RFK_WM(0x5828, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x5898, 0x000000ff, 0x00000040), - DECL_RFK_WM(0x5830, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x5898, 0x0000ff00, 0x00000040), - DECL_RFK_WM(0x5838, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x5898, 0x00ff0000, 0x00000040), - DECL_RFK_WM(0x5840, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x5898, 0xff000000, 0x00000040), - DECL_RFK_WM(0x5848, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x589c, 0x000000ff, 0x00000040), - DECL_RFK_WM(0x5850, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x589c, 0x0000ff00, 0x00000040), - DECL_RFK_WM(0x5858, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x589c, 0x00ff0000, 0x00000040), - DECL_RFK_WM(0x5860, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x589c, 0xff000000, 0x00000040), + RTW89_DECL_RFK_WM(0x581c, 0x00100000, 0x00000000), + RTW89_DECL_RFK_WM(0x58cc, 0x00001000, 0x00000001), + RTW89_DECL_RFK_WM(0x58cc, 0x00000007, 0x00000000), + RTW89_DECL_RFK_WM(0x58cc, 0x00000038, 0x00000001), + RTW89_DECL_RFK_WM(0x58cc, 0x000001c0, 0x00000002), + RTW89_DECL_RFK_WM(0x58cc, 0x00000e00, 0x00000003), + RTW89_DECL_RFK_WM(0x5828, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x5898, 0x000000ff, 0x00000040), + RTW89_DECL_RFK_WM(0x5830, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x5898, 0x0000ff00, 0x00000040), + RTW89_DECL_RFK_WM(0x5838, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x5898, 0x00ff0000, 0x00000040), + RTW89_DECL_RFK_WM(0x5840, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x5898, 0xff000000, 0x00000040), + RTW89_DECL_RFK_WM(0x5848, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x589c, 0x000000ff, 0x00000040), + RTW89_DECL_RFK_WM(0x5850, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x589c, 0x0000ff00, 0x00000040), + RTW89_DECL_RFK_WM(0x5858, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x589c, 0x00ff0000, 0x00000040), + RTW89_DECL_RFK_WM(0x5860, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x589c, 0xff000000, 0x00000040), }; -DECLARE_RFK_TBL(rtw8852a_tssi_slope_cal_org_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_slope_cal_org_defs_a); static const struct rtw89_reg5_def rtw8852a_tssi_slope_cal_org_defs_b[] = { - DECL_RFK_WM(0x781c, 0x00100000, 0x00000000), - DECL_RFK_WM(0x78cc, 0x00001000, 0x00000001), - DECL_RFK_WM(0x78cc, 0x00000007, 0x00000000), - DECL_RFK_WM(0x78cc, 0x00000038, 0x00000001), - DECL_RFK_WM(0x78cc, 0x000001c0, 0x00000002), - DECL_RFK_WM(0x78cc, 0x00000e00, 0x00000003), - DECL_RFK_WM(0x7828, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x7898, 0x000000ff, 0x00000040), - DECL_RFK_WM(0x7830, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x7898, 0x0000ff00, 0x00000040), - DECL_RFK_WM(0x7838, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x7898, 0x00ff0000, 0x00000040), - DECL_RFK_WM(0x7840, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x7898, 0xff000000, 0x00000040), - DECL_RFK_WM(0x7848, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x789c, 0x000000ff, 0x00000040), - DECL_RFK_WM(0x7850, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x789c, 0x0000ff00, 0x00000040), - DECL_RFK_WM(0x7878, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x789c, 0x00ff0000, 0x00000040), - DECL_RFK_WM(0x7860, 0x7fc00000, 0x00000040), - DECL_RFK_WM(0x789c, 0xff000000, 0x00000040), + RTW89_DECL_RFK_WM(0x781c, 0x00100000, 0x00000000), + RTW89_DECL_RFK_WM(0x78cc, 0x00001000, 0x00000001), + RTW89_DECL_RFK_WM(0x78cc, 0x00000007, 0x00000000), + RTW89_DECL_RFK_WM(0x78cc, 0x00000038, 0x00000001), + RTW89_DECL_RFK_WM(0x78cc, 0x000001c0, 0x00000002), + RTW89_DECL_RFK_WM(0x78cc, 0x00000e00, 0x00000003), + RTW89_DECL_RFK_WM(0x7828, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x7898, 0x000000ff, 0x00000040), + RTW89_DECL_RFK_WM(0x7830, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x7898, 0x0000ff00, 0x00000040), + RTW89_DECL_RFK_WM(0x7838, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x7898, 0x00ff0000, 0x00000040), + RTW89_DECL_RFK_WM(0x7840, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x7898, 0xff000000, 0x00000040), + RTW89_DECL_RFK_WM(0x7848, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x789c, 0x000000ff, 0x00000040), + RTW89_DECL_RFK_WM(0x7850, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x789c, 0x0000ff00, 0x00000040), + RTW89_DECL_RFK_WM(0x7878, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x789c, 0x00ff0000, 0x00000040), + RTW89_DECL_RFK_WM(0x7860, 0x7fc00000, 0x00000040), + RTW89_DECL_RFK_WM(0x789c, 0xff000000, 0x00000040), }; -DECLARE_RFK_TBL(rtw8852a_tssi_slope_cal_org_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_slope_cal_org_defs_b); static const struct rtw89_reg5_def rtw8852a_tssi_rf_gap_tbl_defs_a[] = { - DECL_RFK_WM(0x5814, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x58f8, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f8, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x58a4, 0x0001ff00, 0x00000000), - DECL_RFK_WM(0x58a4, 0x03fe0000, 0x00000000), - DECL_RFK_WM(0x58a8, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x58a8, 0x0003fe00, 0x00000000), - DECL_RFK_WM(0x58a8, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x58ac, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x58ac, 0x0003fe00, 0x00000000), - DECL_RFK_WM(0x58ac, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5814, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x58f8, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f8, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a4, 0x0001ff00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a4, 0x03fe0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58a8, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58a8, 0x0003fe00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a8, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x0003fe00, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x07fc0000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_rf_gap_tbl_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_rf_gap_tbl_defs_a); static const struct rtw89_reg5_def rtw8852a_tssi_rf_gap_tbl_defs_b[] = { - DECL_RFK_WM(0x7814, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x78f8, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f8, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x78a4, 0x0001ff00, 0x00000000), - DECL_RFK_WM(0x78a4, 0x03fe0000, 0x00000000), - DECL_RFK_WM(0x78a8, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x78a8, 0x0003fe00, 0x00000000), - DECL_RFK_WM(0x78a8, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x78ac, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x78ac, 0x0003fe00, 0x00000000), - DECL_RFK_WM(0x78ac, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7814, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x78f8, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f8, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a4, 0x0001ff00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a4, 0x03fe0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78a8, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78a8, 0x0003fe00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a8, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x0003fe00, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x07fc0000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_rf_gap_tbl_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_rf_gap_tbl_defs_b); static const struct rtw89_reg5_def rtw8852a_tssi_slope_defs_a[] = { - DECL_RFK_WM(0x5820, 0x80000000, 0x00000000), - DECL_RFK_WM(0x5818, 0x10000000, 0x00000000), - DECL_RFK_WM(0x5814, 0x00000800, 0x00000001), - DECL_RFK_WM(0x581c, 0x20000000, 0x00000001), - DECL_RFK_WM(0x5820, 0x0000f000, 0x00000001), - DECL_RFK_WM(0x581c, 0x000003ff, 0x00000280), - DECL_RFK_WM(0x581c, 0x000ffc00, 0x00000200), - DECL_RFK_WM(0x58b8, 0x007f0000, 0x00000000), - DECL_RFK_WM(0x58b8, 0x7f000000, 0x00000000), - DECL_RFK_WM(0x58b4, 0x7f000000, 0x0000000a), - DECL_RFK_WM(0x58b8, 0x0000007f, 0x00000028), - DECL_RFK_WM(0x58b8, 0x00007f00, 0x00000076), - DECL_RFK_WM(0x5810, 0x20000000, 0x00000000), - DECL_RFK_WM(0x5814, 0x20000000, 0x00000001), - DECL_RFK_WM(0x580c, 0x10000000, 0x00000001), - DECL_RFK_WM(0x580c, 0x40000000, 0x00000001), - DECL_RFK_WM(0x5838, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5858, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5834, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x5834, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5838, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x5854, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x5854, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5858, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x5824, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x5824, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5828, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x582c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x582c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5830, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x583c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x583c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5840, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x5844, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x5844, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5848, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x584c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x584c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5850, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x585c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x585c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x5860, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x5828, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5830, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5840, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5848, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5850, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x5860, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5820, 0x80000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5818, 0x10000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5814, 0x00000800, 0x00000001), + RTW89_DECL_RFK_WM(0x581c, 0x20000000, 0x00000001), + RTW89_DECL_RFK_WM(0x5820, 0x0000f000, 0x00000001), + RTW89_DECL_RFK_WM(0x581c, 0x000003ff, 0x00000280), + RTW89_DECL_RFK_WM(0x581c, 0x000ffc00, 0x00000200), + RTW89_DECL_RFK_WM(0x58b8, 0x007f0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58b8, 0x7f000000, 0x00000000), + RTW89_DECL_RFK_WM(0x58b4, 0x7f000000, 0x0000000a), + RTW89_DECL_RFK_WM(0x58b8, 0x0000007f, 0x00000028), + RTW89_DECL_RFK_WM(0x58b8, 0x00007f00, 0x00000076), + RTW89_DECL_RFK_WM(0x5810, 0x20000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5814, 0x20000000, 0x00000001), + RTW89_DECL_RFK_WM(0x580c, 0x10000000, 0x00000001), + RTW89_DECL_RFK_WM(0x580c, 0x40000000, 0x00000001), + RTW89_DECL_RFK_WM(0x5838, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5858, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5834, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x5834, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5838, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x5854, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x5854, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5858, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x5824, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x5824, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5828, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x582c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x582c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5830, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x583c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x583c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5840, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x5844, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x5844, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5848, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x584c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x584c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5850, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x585c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x585c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x5860, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x5828, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5830, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5840, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5848, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5850, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x5860, 0x003ff000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_slope_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_slope_defs_a); static const struct rtw89_reg5_def rtw8852a_tssi_slope_defs_b[] = { - DECL_RFK_WM(0x7820, 0x80000000, 0x00000000), - DECL_RFK_WM(0x7818, 0x10000000, 0x00000000), - DECL_RFK_WM(0x7814, 0x00000800, 0x00000001), - DECL_RFK_WM(0x781c, 0x20000000, 0x00000001), - DECL_RFK_WM(0x7820, 0x0000f000, 0x00000001), - DECL_RFK_WM(0x781c, 0x000003ff, 0x00000280), - DECL_RFK_WM(0x781c, 0x000ffc00, 0x00000200), - DECL_RFK_WM(0x78b8, 0x007f0000, 0x00000000), - DECL_RFK_WM(0x78b8, 0x7f000000, 0x00000000), - DECL_RFK_WM(0x78b4, 0x7f000000, 0x0000000a), - DECL_RFK_WM(0x78b8, 0x0000007f, 0x00000028), - DECL_RFK_WM(0x78b8, 0x00007f00, 0x00000076), - DECL_RFK_WM(0x7810, 0x20000000, 0x00000000), - DECL_RFK_WM(0x7814, 0x20000000, 0x00000001), - DECL_RFK_WM(0x780c, 0x10000000, 0x00000001), - DECL_RFK_WM(0x780c, 0x40000000, 0x00000001), - DECL_RFK_WM(0x7838, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7858, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7834, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x7834, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7838, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x7854, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x7854, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7858, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x7824, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x7824, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7828, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x782c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x782c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7830, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x783c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x783c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7840, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x7844, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x7844, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7848, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x784c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x784c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7850, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x785c, 0x0003ffff, 0x000115f2), - DECL_RFK_WM(0x785c, 0x3ffc0000, 0x00000000), - DECL_RFK_WM(0x7860, 0x00000fff, 0x00000121), - DECL_RFK_WM(0x7828, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7830, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7840, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7848, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7850, 0x003ff000, 0x00000000), - DECL_RFK_WM(0x7860, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7820, 0x80000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7818, 0x10000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7814, 0x00000800, 0x00000001), + RTW89_DECL_RFK_WM(0x781c, 0x20000000, 0x00000001), + RTW89_DECL_RFK_WM(0x7820, 0x0000f000, 0x00000001), + RTW89_DECL_RFK_WM(0x781c, 0x000003ff, 0x00000280), + RTW89_DECL_RFK_WM(0x781c, 0x000ffc00, 0x00000200), + RTW89_DECL_RFK_WM(0x78b8, 0x007f0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78b8, 0x7f000000, 0x00000000), + RTW89_DECL_RFK_WM(0x78b4, 0x7f000000, 0x0000000a), + RTW89_DECL_RFK_WM(0x78b8, 0x0000007f, 0x00000028), + RTW89_DECL_RFK_WM(0x78b8, 0x00007f00, 0x00000076), + RTW89_DECL_RFK_WM(0x7810, 0x20000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7814, 0x20000000, 0x00000001), + RTW89_DECL_RFK_WM(0x780c, 0x10000000, 0x00000001), + RTW89_DECL_RFK_WM(0x780c, 0x40000000, 0x00000001), + RTW89_DECL_RFK_WM(0x7838, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7858, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7834, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x7834, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7838, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x7854, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x7854, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7858, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x7824, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x7824, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7828, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x782c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x782c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7830, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x783c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x783c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7840, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x7844, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x7844, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7848, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x784c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x784c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7850, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x785c, 0x0003ffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x785c, 0x3ffc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x7860, 0x00000fff, 0x00000121), + RTW89_DECL_RFK_WM(0x7828, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7830, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7840, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7848, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7850, 0x003ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x7860, 0x003ff000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_slope_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_slope_defs_b); static const struct rtw89_reg5_def rtw8852a_tssi_track_defs_a[] = { - DECL_RFK_WM(0x5820, 0x80000000, 0x00000000), - DECL_RFK_WM(0x5818, 0x18000000, 0x00000000), - DECL_RFK_WM(0x5814, 0x00000800, 0x00000000), - DECL_RFK_WM(0x581c, 0x20000000, 0x00000001), - DECL_RFK_WM(0x5864, 0x000003ff, 0x000001ff), - DECL_RFK_WM(0x5864, 0x000ffc00, 0x00000200), - DECL_RFK_WM(0x5820, 0x00000fff, 0x00000080), - DECL_RFK_WM(0x5814, 0x01000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5820, 0x80000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5818, 0x18000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5814, 0x00000800, 0x00000000), + RTW89_DECL_RFK_WM(0x581c, 0x20000000, 0x00000001), + RTW89_DECL_RFK_WM(0x5864, 0x000003ff, 0x000001ff), + RTW89_DECL_RFK_WM(0x5864, 0x000ffc00, 0x00000200), + RTW89_DECL_RFK_WM(0x5820, 0x00000fff, 0x00000080), + RTW89_DECL_RFK_WM(0x5814, 0x01000000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_track_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_track_defs_a); static const struct rtw89_reg5_def rtw8852a_tssi_track_defs_b[] = { - DECL_RFK_WM(0x7820, 0x80000000, 0x00000000), - DECL_RFK_WM(0x7818, 0x18000000, 0x00000000), - DECL_RFK_WM(0x7814, 0x00000800, 0x00000000), - DECL_RFK_WM(0x781c, 0x20000000, 0x00000001), - DECL_RFK_WM(0x7864, 0x000003ff, 0x000001ff), - DECL_RFK_WM(0x7864, 0x000ffc00, 0x00000200), - DECL_RFK_WM(0x7820, 0x00000fff, 0x00000080), - DECL_RFK_WM(0x7814, 0x01000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7820, 0x80000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7818, 0x18000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7814, 0x00000800, 0x00000000), + RTW89_DECL_RFK_WM(0x781c, 0x20000000, 0x00000001), + RTW89_DECL_RFK_WM(0x7864, 0x000003ff, 0x000001ff), + RTW89_DECL_RFK_WM(0x7864, 0x000ffc00, 0x00000200), + RTW89_DECL_RFK_WM(0x7820, 0x00000fff, 0x00000080), + RTW89_DECL_RFK_WM(0x7814, 0x01000000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_track_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_track_defs_b); static const struct rtw89_reg5_def rtw8852a_tssi_txagc_ofst_mv_avg_defs_a[] = { - DECL_RFK_WM(0x58e4, 0x00004000, 0x00000000), - DECL_RFK_WM(0x58e4, 0x00004000, 0x00000001), - DECL_RFK_WM(0x58e4, 0x00004000, 0x00000000), - DECL_RFK_WM(0x58e4, 0x00008000, 0x00000000), - DECL_RFK_WM(0x58e4, 0x000f0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58e4, 0x00004000, 0x00000000), + RTW89_DECL_RFK_WM(0x58e4, 0x00004000, 0x00000001), + RTW89_DECL_RFK_WM(0x58e4, 0x00004000, 0x00000000), + RTW89_DECL_RFK_WM(0x58e4, 0x00008000, 0x00000000), + RTW89_DECL_RFK_WM(0x58e4, 0x000f0000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_txagc_ofst_mv_avg_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_txagc_ofst_mv_avg_defs_a); static const struct rtw89_reg5_def rtw8852a_tssi_txagc_ofst_mv_avg_defs_b[] = { - DECL_RFK_WM(0x78e4, 0x00004000, 0x00000000), - DECL_RFK_WM(0x78e4, 0x00004000, 0x00000001), - DECL_RFK_WM(0x78e4, 0x00004000, 0x00000000), - DECL_RFK_WM(0x78e4, 0x00008000, 0x00000000), - DECL_RFK_WM(0x78e4, 0x000f0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78e4, 0x00004000, 0x00000000), + RTW89_DECL_RFK_WM(0x78e4, 0x00004000, 0x00000001), + RTW89_DECL_RFK_WM(0x78e4, 0x00004000, 0x00000000), + RTW89_DECL_RFK_WM(0x78e4, 0x00008000, 0x00000000), + RTW89_DECL_RFK_WM(0x78e4, 0x000f0000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_tssi_txagc_ofst_mv_avg_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_txagc_ofst_mv_avg_defs_b); static const struct rtw89_reg5_def rtw8852a_tssi_pak_defs_a_2g[] = { - DECL_RFK_WM(0x5814, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x58f8, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f8, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x58a4, 0x0001ff00, 0x00000000), - DECL_RFK_WM(0x58a4, 0x03fe0000, 0x000001d0), - DECL_RFK_WM(0x58a8, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x58a8, 0x0003fe00, 0x000001e8), - DECL_RFK_WM(0x58a8, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x58ac, 0x000001ff, 0x0000000b), - DECL_RFK_WM(0x58ac, 0x0003fe00, 0x00000000), - DECL_RFK_WM(0x58ac, 0x07fc0000, 0x00000088), + RTW89_DECL_RFK_WM(0x5814, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x58f8, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f8, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a4, 0x0001ff00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a4, 0x03fe0000, 0x000001d0), + RTW89_DECL_RFK_WM(0x58a8, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58a8, 0x0003fe00, 0x000001e8), + RTW89_DECL_RFK_WM(0x58a8, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x000001ff, 0x0000000b), + RTW89_DECL_RFK_WM(0x58ac, 0x0003fe00, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x07fc0000, 0x00000088), }; -DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_a_2g); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_a_2g); static const struct rtw89_reg5_def rtw8852a_tssi_pak_defs_a_5g_1[] = { - DECL_RFK_WM(0x5814, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x58f8, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f8, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x58a4, 0x0001ff00, 0x00000000), - DECL_RFK_WM(0x58a4, 0x03fe0000, 0x000001d7), - DECL_RFK_WM(0x58a8, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x58a8, 0x0003fe00, 0x000001fb), - DECL_RFK_WM(0x58a8, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x58ac, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x58ac, 0x0003fe00, 0x00000005), - DECL_RFK_WM(0x58ac, 0x07fc0000, 0x0000007c), + RTW89_DECL_RFK_WM(0x5814, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x58f8, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f8, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a4, 0x0001ff00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a4, 0x03fe0000, 0x000001d7), + RTW89_DECL_RFK_WM(0x58a8, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58a8, 0x0003fe00, 0x000001fb), + RTW89_DECL_RFK_WM(0x58a8, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x0003fe00, 0x00000005), + RTW89_DECL_RFK_WM(0x58ac, 0x07fc0000, 0x0000007c), }; -DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_a_5g_1); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_a_5g_1); static const struct rtw89_reg5_def rtw8852a_tssi_pak_defs_a_5g_3[] = { - DECL_RFK_WM(0x5814, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x58f8, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f8, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x58a4, 0x0001ff00, 0x00000000), - DECL_RFK_WM(0x58a4, 0x03fe0000, 0x000001d8), - DECL_RFK_WM(0x58a8, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x58a8, 0x0003fe00, 0x000001fc), - DECL_RFK_WM(0x58a8, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x58ac, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x58ac, 0x0003fe00, 0x00000006), - DECL_RFK_WM(0x58ac, 0x07fc0000, 0x00000078), + RTW89_DECL_RFK_WM(0x5814, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x58f8, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f8, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a4, 0x0001ff00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a4, 0x03fe0000, 0x000001d8), + RTW89_DECL_RFK_WM(0x58a8, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58a8, 0x0003fe00, 0x000001fc), + RTW89_DECL_RFK_WM(0x58a8, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x0003fe00, 0x00000006), + RTW89_DECL_RFK_WM(0x58ac, 0x07fc0000, 0x00000078), }; -DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_a_5g_3); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_a_5g_3); static const struct rtw89_reg5_def rtw8852a_tssi_pak_defs_a_5g_4[] = { - DECL_RFK_WM(0x5814, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x58f8, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x58f8, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x58a4, 0x0001ff00, 0x00000000), - DECL_RFK_WM(0x58a4, 0x03fe0000, 0x000001e5), - DECL_RFK_WM(0x58a8, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x58a8, 0x0003fe00, 0x0000000a), - DECL_RFK_WM(0x58a8, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x58ac, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x58ac, 0x0003fe00, 0x00000011), - DECL_RFK_WM(0x58ac, 0x07fc0000, 0x00000075), + RTW89_DECL_RFK_WM(0x5814, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x58f8, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58f8, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a4, 0x0001ff00, 0x00000000), + RTW89_DECL_RFK_WM(0x58a4, 0x03fe0000, 0x000001e5), + RTW89_DECL_RFK_WM(0x58a8, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58a8, 0x0003fe00, 0x0000000a), + RTW89_DECL_RFK_WM(0x58a8, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x58ac, 0x0003fe00, 0x00000011), + RTW89_DECL_RFK_WM(0x58ac, 0x07fc0000, 0x00000075), }; -DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_a_5g_4); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_a_5g_4); static const struct rtw89_reg5_def rtw8852a_tssi_pak_defs_b_2g[] = { - DECL_RFK_WM(0x7814, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x78f8, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f8, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x78a4, 0x0001ff00, 0x00000000), - DECL_RFK_WM(0x78a4, 0x03fe0000, 0x000001cc), - DECL_RFK_WM(0x78a8, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x78a8, 0x0003fe00, 0x000001e2), - DECL_RFK_WM(0x78a8, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x78ac, 0x000001ff, 0x00000005), - DECL_RFK_WM(0x78ac, 0x0003fe00, 0x00000000), - DECL_RFK_WM(0x78ac, 0x07fc0000, 0x00000089), + RTW89_DECL_RFK_WM(0x7814, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x78f8, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f8, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a4, 0x0001ff00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a4, 0x03fe0000, 0x000001cc), + RTW89_DECL_RFK_WM(0x78a8, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78a8, 0x0003fe00, 0x000001e2), + RTW89_DECL_RFK_WM(0x78a8, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x000001ff, 0x00000005), + RTW89_DECL_RFK_WM(0x78ac, 0x0003fe00, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x07fc0000, 0x00000089), }; -DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_b_2g); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_b_2g); static const struct rtw89_reg5_def rtw8852a_tssi_pak_defs_b_5g_1[] = { - DECL_RFK_WM(0x7814, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x78f8, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f8, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x78a4, 0x0001ff00, 0x00000000), - DECL_RFK_WM(0x78a4, 0x03fe0000, 0x000001d5), - DECL_RFK_WM(0x78a8, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x78a8, 0x0003fe00, 0x000001fc), - DECL_RFK_WM(0x78a8, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x78ac, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x78ac, 0x0003fe00, 0x00000005), - DECL_RFK_WM(0x78ac, 0x07fc0000, 0x00000079), + RTW89_DECL_RFK_WM(0x7814, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x78f8, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f8, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a4, 0x0001ff00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a4, 0x03fe0000, 0x000001d5), + RTW89_DECL_RFK_WM(0x78a8, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78a8, 0x0003fe00, 0x000001fc), + RTW89_DECL_RFK_WM(0x78a8, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x0003fe00, 0x00000005), + RTW89_DECL_RFK_WM(0x78ac, 0x07fc0000, 0x00000079), }; -DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_b_5g_1); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_b_5g_1); static const struct rtw89_reg5_def rtw8852a_tssi_pak_defs_b_5g_3[] = { - DECL_RFK_WM(0x7814, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x78f8, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f8, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x78a4, 0x0001ff00, 0x00000000), - DECL_RFK_WM(0x78a4, 0x03fe0000, 0x000001dc), - DECL_RFK_WM(0x78a8, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x78a8, 0x0003fe00, 0x00000002), - DECL_RFK_WM(0x78a8, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x78ac, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x78ac, 0x0003fe00, 0x0000000b), - DECL_RFK_WM(0x78ac, 0x07fc0000, 0x00000076), + RTW89_DECL_RFK_WM(0x7814, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x78f8, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f8, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a4, 0x0001ff00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a4, 0x03fe0000, 0x000001dc), + RTW89_DECL_RFK_WM(0x78a8, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78a8, 0x0003fe00, 0x00000002), + RTW89_DECL_RFK_WM(0x78a8, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x0003fe00, 0x0000000b), + RTW89_DECL_RFK_WM(0x78ac, 0x07fc0000, 0x00000076), }; -DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_b_5g_3); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_b_5g_3); static const struct rtw89_reg5_def rtw8852a_tssi_pak_defs_b_5g_4[] = { - DECL_RFK_WM(0x7814, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x78f8, 0x000003ff, 0x00000000), - DECL_RFK_WM(0x78f8, 0x000ffc00, 0x00000000), - DECL_RFK_WM(0x78a4, 0x0001ff00, 0x00000000), - DECL_RFK_WM(0x78a4, 0x03fe0000, 0x000001f0), - DECL_RFK_WM(0x78a8, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x78a8, 0x0003fe00, 0x00000016), - DECL_RFK_WM(0x78a8, 0x07fc0000, 0x00000000), - DECL_RFK_WM(0x78ac, 0x000001ff, 0x00000000), - DECL_RFK_WM(0x78ac, 0x0003fe00, 0x0000001f), - DECL_RFK_WM(0x78ac, 0x07fc0000, 0x00000072), + RTW89_DECL_RFK_WM(0x7814, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f4, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x78f8, 0x000003ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78f8, 0x000ffc00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a4, 0x0001ff00, 0x00000000), + RTW89_DECL_RFK_WM(0x78a4, 0x03fe0000, 0x000001f0), + RTW89_DECL_RFK_WM(0x78a8, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78a8, 0x0003fe00, 0x00000016), + RTW89_DECL_RFK_WM(0x78a8, 0x07fc0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x000001ff, 0x00000000), + RTW89_DECL_RFK_WM(0x78ac, 0x0003fe00, 0x0000001f), + RTW89_DECL_RFK_WM(0x78ac, 0x07fc0000, 0x00000072), }; -DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_b_5g_4); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_pak_defs_b_5g_4); static const struct rtw89_reg5_def rtw8852a_tssi_enable_defs_a[] = { - DECL_RFK_WRF(0x0, 0x55, 0x00080, 0x00001), - DECL_RFK_WM(0x5818, 0x000000ff, 0x000000c0), - DECL_RFK_WM(0x5818, 0x10000000, 0x00000000), - DECL_RFK_WM(0x5818, 0x10000000, 0x00000001), - DECL_RFK_WM(0x5820, 0x80000000, 0x00000000), - DECL_RFK_WM(0x5820, 0x80000000, 0x00000001), - DECL_RFK_WM(0x5818, 0x18000000, 0x00000003), + RTW89_DECL_RFK_WRF(0x0, 0x55, 0x00080, 0x00001), + RTW89_DECL_RFK_WM(0x5818, 0x000000ff, 0x000000c0), + RTW89_DECL_RFK_WM(0x5818, 0x10000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5818, 0x10000000, 0x00000001), + RTW89_DECL_RFK_WM(0x5820, 0x80000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5820, 0x80000000, 0x00000001), + RTW89_DECL_RFK_WM(0x5818, 0x18000000, 0x00000003), }; -DECLARE_RFK_TBL(rtw8852a_tssi_enable_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_enable_defs_a); static const struct rtw89_reg5_def rtw8852a_tssi_enable_defs_b[] = { - DECL_RFK_WRF(0x1, 0x55, 0x00080, 0x00001), - DECL_RFK_WM(0x7818, 0x000000ff, 0x000000c0), - DECL_RFK_WM(0x7818, 0x10000000, 0x00000000), - DECL_RFK_WM(0x7818, 0x10000000, 0x00000001), - DECL_RFK_WM(0x7820, 0x80000000, 0x00000000), - DECL_RFK_WM(0x7820, 0x80000000, 0x00000001), - DECL_RFK_WM(0x7818, 0x18000000, 0x00000003), + RTW89_DECL_RFK_WRF(0x1, 0x55, 0x00080, 0x00001), + RTW89_DECL_RFK_WM(0x7818, 0x000000ff, 0x000000c0), + RTW89_DECL_RFK_WM(0x7818, 0x10000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7818, 0x10000000, 0x00000001), + RTW89_DECL_RFK_WM(0x7820, 0x80000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7820, 0x80000000, 0x00000001), + RTW89_DECL_RFK_WM(0x7818, 0x18000000, 0x00000003), }; -DECLARE_RFK_TBL(rtw8852a_tssi_enable_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_enable_defs_b); static const struct rtw89_reg5_def rtw8852a_tssi_disable_defs[] = { - DECL_RFK_WM(0x5820, 0x80000000, 0x00000000), - DECL_RFK_WM(0x5818, 0x18000000, 0x00000001), - DECL_RFK_WM(0x7820, 0x80000000, 0x00000000), - DECL_RFK_WM(0x7818, 0x18000000, 0x00000001), + RTW89_DECL_RFK_WM(0x5820, 0x80000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5818, 0x18000000, 0x00000001), + RTW89_DECL_RFK_WM(0x7820, 0x80000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7818, 0x18000000, 0x00000001), }; -DECLARE_RFK_TBL(rtw8852a_tssi_disable_defs); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_disable_defs); static const struct rtw89_reg5_def rtw8852a_tssi_enable_defs_ab[] = { - DECL_RFK_WM(0x5820, 0x80000000, 0x0), - DECL_RFK_WM(0x5820, 0x80000000, 0x1), - DECL_RFK_WM(0x5818, 0x18000000, 0x3), - DECL_RFK_WM(0x7820, 0x80000000, 0x0), - DECL_RFK_WM(0x7820, 0x80000000, 0x1), - DECL_RFK_WM(0x7818, 0x18000000, 0x3), + RTW89_DECL_RFK_WM(0x5820, 0x80000000, 0x0), + RTW89_DECL_RFK_WM(0x5820, 0x80000000, 0x1), + RTW89_DECL_RFK_WM(0x5818, 0x18000000, 0x3), + RTW89_DECL_RFK_WM(0x7820, 0x80000000, 0x0), + RTW89_DECL_RFK_WM(0x7820, 0x80000000, 0x1), + RTW89_DECL_RFK_WM(0x7818, 0x18000000, 0x3), }; -DECLARE_RFK_TBL(rtw8852a_tssi_enable_defs_ab); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_enable_defs_ab); static const struct rtw89_reg5_def rtw8852a_tssi_tracking_defs[] = { - DECL_RFK_WM(0x5800, 0x10000000, 0x00000000), - DECL_RFK_WM(0x58f0, 0x00080000, 0x00000000), - DECL_RFK_WM(0x5804, 0xf8000000, 0x00000000), - DECL_RFK_WM(0x58f0, 0xfff00000, 0x00000400), - DECL_RFK_WM(0x7800, 0x10000000, 0x00000000), - DECL_RFK_WM(0x78f0, 0x00080000, 0x00000000), - DECL_RFK_WM(0x7804, 0xf8000000, 0x00000000), - DECL_RFK_WM(0x78f0, 0xfff00000, 0x00000400), + RTW89_DECL_RFK_WM(0x5800, 0x10000000, 0x00000000), + RTW89_DECL_RFK_WM(0x58f0, 0x00080000, 0x00000000), + RTW89_DECL_RFK_WM(0x5804, 0xf8000000, 0x00000000), + RTW89_DECL_RFK_WM(0x58f0, 0xfff00000, 0x00000400), + RTW89_DECL_RFK_WM(0x7800, 0x10000000, 0x00000000), + RTW89_DECL_RFK_WM(0x78f0, 0x00080000, 0x00000000), + RTW89_DECL_RFK_WM(0x7804, 0xf8000000, 0x00000000), + RTW89_DECL_RFK_WM(0x78f0, 0xfff00000, 0x00000400), }; -DECLARE_RFK_TBL(rtw8852a_tssi_tracking_defs); +RTW89_DECLARE_RFK_TBL(rtw8852a_tssi_tracking_defs); static const struct rtw89_reg5_def rtw8852a_rfk_afe_init_defs[] = { - DECL_RFK_WC(0x12ec, 0x00008000), - DECL_RFK_WS(0x12ec, 0x00008000), - DECL_RFK_WC(0x5e00, 0x00000001), - DECL_RFK_WS(0x5e00, 0x00000001), - DECL_RFK_WC(0x32ec, 0x00008000), - DECL_RFK_WS(0x32ec, 0x00008000), - DECL_RFK_WC(0x7e00, 0x00000001), - DECL_RFK_WS(0x7e00, 0x00000001), + RTW89_DECL_RFK_WC(0x12ec, 0x00008000), + RTW89_DECL_RFK_WS(0x12ec, 0x00008000), + RTW89_DECL_RFK_WC(0x5e00, 0x00000001), + RTW89_DECL_RFK_WS(0x5e00, 0x00000001), + RTW89_DECL_RFK_WC(0x32ec, 0x00008000), + RTW89_DECL_RFK_WS(0x32ec, 0x00008000), + RTW89_DECL_RFK_WC(0x7e00, 0x00000001), + RTW89_DECL_RFK_WS(0x7e00, 0x00000001), }; -DECLARE_RFK_TBL(rtw8852a_rfk_afe_init_defs); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_afe_init_defs); static const struct rtw89_reg5_def rtw8852a_rfk_dack_reload_defs_a[] = { - DECL_RFK_WS(0x5e00, 0x00000008), - DECL_RFK_WS(0x5e50, 0x00000008), - DECL_RFK_WS(0x5e10, 0x80000000), - DECL_RFK_WS(0x5e60, 0x80000000), - DECL_RFK_WC(0x5e00, 0x00000008), - DECL_RFK_WC(0x5e50, 0x00000008), + RTW89_DECL_RFK_WS(0x5e00, 0x00000008), + RTW89_DECL_RFK_WS(0x5e50, 0x00000008), + RTW89_DECL_RFK_WS(0x5e10, 0x80000000), + RTW89_DECL_RFK_WS(0x5e60, 0x80000000), + RTW89_DECL_RFK_WC(0x5e00, 0x00000008), + RTW89_DECL_RFK_WC(0x5e50, 0x00000008), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dack_reload_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dack_reload_defs_a); static const struct rtw89_reg5_def rtw8852a_rfk_dack_reload_defs_b[] = { - DECL_RFK_WS(0x7e00, 0x00000008), - DECL_RFK_WS(0x7e50, 0x00000008), - DECL_RFK_WS(0x7e10, 0x80000000), - DECL_RFK_WS(0x7e60, 0x80000000), - DECL_RFK_WC(0x7e00, 0x00000008), - DECL_RFK_WC(0x7e50, 0x00000008), + RTW89_DECL_RFK_WS(0x7e00, 0x00000008), + RTW89_DECL_RFK_WS(0x7e50, 0x00000008), + RTW89_DECL_RFK_WS(0x7e10, 0x80000000), + RTW89_DECL_RFK_WS(0x7e60, 0x80000000), + RTW89_DECL_RFK_WC(0x7e00, 0x00000008), + RTW89_DECL_RFK_WC(0x7e50, 0x00000008), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dack_reload_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dack_reload_defs_b); static const struct rtw89_reg5_def rtw8852a_rfk_check_addc_defs_a[] = { - DECL_RFK_WC(0x20f4, 0x01000000), - DECL_RFK_WS(0x20f8, 0x80000000), - DECL_RFK_WM(0x20f0, 0x00ff0000, 0x00000001), - DECL_RFK_WM(0x20f0, 0x00000f00, 0x00000002), - DECL_RFK_WC(0x20f0, 0x0000000f), - DECL_RFK_WM(0x20f0, 0x000000c0, 0x00000002), + RTW89_DECL_RFK_WC(0x20f4, 0x01000000), + RTW89_DECL_RFK_WS(0x20f8, 0x80000000), + RTW89_DECL_RFK_WM(0x20f0, 0x00ff0000, 0x00000001), + RTW89_DECL_RFK_WM(0x20f0, 0x00000f00, 0x00000002), + RTW89_DECL_RFK_WC(0x20f0, 0x0000000f), + RTW89_DECL_RFK_WM(0x20f0, 0x000000c0, 0x00000002), }; -DECLARE_RFK_TBL(rtw8852a_rfk_check_addc_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_check_addc_defs_a); static const struct rtw89_reg5_def rtw8852a_rfk_check_addc_defs_b[] = { - DECL_RFK_WC(0x20f4, 0x01000000), - DECL_RFK_WS(0x20f8, 0x80000000), - DECL_RFK_WM(0x20f0, 0x00ff0000, 0x00000001), - DECL_RFK_WM(0x20f0, 0x00000f00, 0x00000002), - DECL_RFK_WC(0x20f0, 0x0000000f), - DECL_RFK_WM(0x20f0, 0x000000c0, 0x00000003), + RTW89_DECL_RFK_WC(0x20f4, 0x01000000), + RTW89_DECL_RFK_WS(0x20f8, 0x80000000), + RTW89_DECL_RFK_WM(0x20f0, 0x00ff0000, 0x00000001), + RTW89_DECL_RFK_WM(0x20f0, 0x00000f00, 0x00000002), + RTW89_DECL_RFK_WC(0x20f0, 0x0000000f), + RTW89_DECL_RFK_WM(0x20f0, 0x000000c0, 0x00000003), }; -DECLARE_RFK_TBL(rtw8852a_rfk_check_addc_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_check_addc_defs_b); static const struct rtw89_reg5_def rtw8852a_rfk_addck_reset_defs_a[] = { - DECL_RFK_WC(0x12d8, 0x00000030), - DECL_RFK_WC(0x32d8, 0x00000030), - DECL_RFK_WS(0x12b8, 0x40000000), - DECL_RFK_WC(0x032c, 0x40000000), - DECL_RFK_WC(0x032c, 0x00400000), - DECL_RFK_WS(0x032c, 0x00400000), - DECL_RFK_WS(0x030c, 0x0f000000), - DECL_RFK_WC(0x032c, 0x00010000), - DECL_RFK_WS(0x12dc, 0x00000002), - DECL_RFK_WM(0x030c, 0x0f000000, 0x00000003), + RTW89_DECL_RFK_WC(0x12d8, 0x00000030), + RTW89_DECL_RFK_WC(0x32d8, 0x00000030), + RTW89_DECL_RFK_WS(0x12b8, 0x40000000), + RTW89_DECL_RFK_WC(0x032c, 0x40000000), + RTW89_DECL_RFK_WC(0x032c, 0x00400000), + RTW89_DECL_RFK_WS(0x032c, 0x00400000), + RTW89_DECL_RFK_WS(0x030c, 0x0f000000), + RTW89_DECL_RFK_WC(0x032c, 0x00010000), + RTW89_DECL_RFK_WS(0x12dc, 0x00000002), + RTW89_DECL_RFK_WM(0x030c, 0x0f000000, 0x00000003), }; -DECLARE_RFK_TBL(rtw8852a_rfk_addck_reset_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_addck_reset_defs_a); static const struct rtw89_reg5_def rtw8852a_rfk_addck_trigger_defs_a[] = { - DECL_RFK_WS(0x12d8, 0x000000c0), - DECL_RFK_WS(0x12d8, 0x00000800), - DECL_RFK_WC(0x12d8, 0x00000800), - DECL_RFK_DELAY(1), - DECL_RFK_WM(0x12d8, 0x00000300, 0x00000001), + RTW89_DECL_RFK_WS(0x12d8, 0x000000c0), + RTW89_DECL_RFK_WS(0x12d8, 0x00000800), + RTW89_DECL_RFK_WC(0x12d8, 0x00000800), + RTW89_DECL_RFK_DELAY(1), + RTW89_DECL_RFK_WM(0x12d8, 0x00000300, 0x00000001), }; -DECLARE_RFK_TBL(rtw8852a_rfk_addck_trigger_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_addck_trigger_defs_a); static const struct rtw89_reg5_def rtw8852a_rfk_addck_restore_defs_a[] = { - DECL_RFK_WC(0x12dc, 0x00000002), - DECL_RFK_WS(0x032c, 0x00010000), - DECL_RFK_WM(0x030c, 0x0f000000, 0x0000000c), - DECL_RFK_WS(0x032c, 0x40000000), - DECL_RFK_WC(0x12b8, 0x40000000), + RTW89_DECL_RFK_WC(0x12dc, 0x00000002), + RTW89_DECL_RFK_WS(0x032c, 0x00010000), + RTW89_DECL_RFK_WM(0x030c, 0x0f000000, 0x0000000c), + RTW89_DECL_RFK_WS(0x032c, 0x40000000), + RTW89_DECL_RFK_WC(0x12b8, 0x40000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_addck_restore_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_addck_restore_defs_a); static const struct rtw89_reg5_def rtw8852a_rfk_addck_reset_defs_b[] = { - DECL_RFK_WS(0x32b8, 0x40000000), - DECL_RFK_WC(0x032c, 0x40000000), - DECL_RFK_WC(0x032c, 0x00400000), - DECL_RFK_WS(0x032c, 0x00400000), - DECL_RFK_WS(0x030c, 0x0f000000), - DECL_RFK_WC(0x032c, 0x00010000), - DECL_RFK_WS(0x32dc, 0x00000002), - DECL_RFK_WM(0x030c, 0x0f000000, 0x00000003), + RTW89_DECL_RFK_WS(0x32b8, 0x40000000), + RTW89_DECL_RFK_WC(0x032c, 0x40000000), + RTW89_DECL_RFK_WC(0x032c, 0x00400000), + RTW89_DECL_RFK_WS(0x032c, 0x00400000), + RTW89_DECL_RFK_WS(0x030c, 0x0f000000), + RTW89_DECL_RFK_WC(0x032c, 0x00010000), + RTW89_DECL_RFK_WS(0x32dc, 0x00000002), + RTW89_DECL_RFK_WM(0x030c, 0x0f000000, 0x00000003), }; -DECLARE_RFK_TBL(rtw8852a_rfk_addck_reset_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_addck_reset_defs_b); static const struct rtw89_reg5_def rtw8852a_rfk_addck_trigger_defs_b[] = { - DECL_RFK_WS(0x32d8, 0x000000c0), - DECL_RFK_WS(0x32d8, 0x00000800), - DECL_RFK_WC(0x32d8, 0x00000800), - DECL_RFK_DELAY(1), - DECL_RFK_WM(0x32d8, 0x00000300, 0x00000001), + RTW89_DECL_RFK_WS(0x32d8, 0x000000c0), + RTW89_DECL_RFK_WS(0x32d8, 0x00000800), + RTW89_DECL_RFK_WC(0x32d8, 0x00000800), + RTW89_DECL_RFK_DELAY(1), + RTW89_DECL_RFK_WM(0x32d8, 0x00000300, 0x00000001), }; -DECLARE_RFK_TBL(rtw8852a_rfk_addck_trigger_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_addck_trigger_defs_b); static const struct rtw89_reg5_def rtw8852a_rfk_addck_restore_defs_b[] = { - DECL_RFK_WC(0x32dc, 0x00000002), - DECL_RFK_WS(0x032c, 0x00010000), - DECL_RFK_WM(0x030c, 0x0f000000, 0x0000000c), - DECL_RFK_WS(0x032c, 0x40000000), - DECL_RFK_WC(0x32b8, 0x40000000), + RTW89_DECL_RFK_WC(0x32dc, 0x00000002), + RTW89_DECL_RFK_WS(0x032c, 0x00010000), + RTW89_DECL_RFK_WM(0x030c, 0x0f000000, 0x0000000c), + RTW89_DECL_RFK_WS(0x032c, 0x40000000), + RTW89_DECL_RFK_WC(0x32b8, 0x40000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_addck_restore_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_addck_restore_defs_b); static const struct rtw89_reg5_def rtw8852a_rfk_check_dadc_defs_f_a[] = { - DECL_RFK_WC(0x032c, 0x40000000), - DECL_RFK_WS(0x030c, 0x0f000000), - DECL_RFK_WM(0x030c, 0x0f000000, 0x00000003), - DECL_RFK_WC(0x032c, 0x00010000), - DECL_RFK_WS(0x12dc, 0x00000001), - DECL_RFK_WS(0x12e8, 0x00000004), - DECL_RFK_WRF(0x0, 0x8f, 0x02000, 0x00001), + RTW89_DECL_RFK_WC(0x032c, 0x40000000), + RTW89_DECL_RFK_WS(0x030c, 0x0f000000), + RTW89_DECL_RFK_WM(0x030c, 0x0f000000, 0x00000003), + RTW89_DECL_RFK_WC(0x032c, 0x00010000), + RTW89_DECL_RFK_WS(0x12dc, 0x00000001), + RTW89_DECL_RFK_WS(0x12e8, 0x00000004), + RTW89_DECL_RFK_WRF(0x0, 0x8f, 0x02000, 0x00001), }; -DECLARE_RFK_TBL(rtw8852a_rfk_check_dadc_defs_f_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_check_dadc_defs_f_a); static const struct rtw89_reg5_def rtw8852a_rfk_check_dadc_defs_f_b[] = { - DECL_RFK_WC(0x032c, 0x40000000), - DECL_RFK_WS(0x030c, 0x0f000000), - DECL_RFK_WM(0x030c, 0x0f000000, 0x00000003), - DECL_RFK_WC(0x032c, 0x00010000), - DECL_RFK_WS(0x32dc, 0x00000001), - DECL_RFK_WS(0x32e8, 0x00000004), - DECL_RFK_WRF(0x1, 0x8f, 0x02000, 0x00001), + RTW89_DECL_RFK_WC(0x032c, 0x40000000), + RTW89_DECL_RFK_WS(0x030c, 0x0f000000), + RTW89_DECL_RFK_WM(0x030c, 0x0f000000, 0x00000003), + RTW89_DECL_RFK_WC(0x032c, 0x00010000), + RTW89_DECL_RFK_WS(0x32dc, 0x00000001), + RTW89_DECL_RFK_WS(0x32e8, 0x00000004), + RTW89_DECL_RFK_WRF(0x1, 0x8f, 0x02000, 0x00001), }; -DECLARE_RFK_TBL(rtw8852a_rfk_check_dadc_defs_f_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_check_dadc_defs_f_b); static const struct rtw89_reg5_def rtw8852a_rfk_check_dadc_defs_r_a[] = { - DECL_RFK_WC(0x12dc, 0x00000001), - DECL_RFK_WC(0x12e8, 0x00000004), - DECL_RFK_WRF(0x0, 0x8f, 0x02000, 0x00000), - DECL_RFK_WM(0x032c, 0x00010000, 0x00000001), + RTW89_DECL_RFK_WC(0x12dc, 0x00000001), + RTW89_DECL_RFK_WC(0x12e8, 0x00000004), + RTW89_DECL_RFK_WRF(0x0, 0x8f, 0x02000, 0x00000), + RTW89_DECL_RFK_WM(0x032c, 0x00010000, 0x00000001), }; -DECLARE_RFK_TBL(rtw8852a_rfk_check_dadc_defs_r_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_check_dadc_defs_r_a); static const struct rtw89_reg5_def rtw8852a_rfk_check_dadc_defs_r_b[] = { - DECL_RFK_WC(0x32dc, 0x00000001), - DECL_RFK_WC(0x32e8, 0x00000004), - DECL_RFK_WRF(0x1, 0x8f, 0x02000, 0x00000), - DECL_RFK_WM(0x032c, 0x00010000, 0x00000001), + RTW89_DECL_RFK_WC(0x32dc, 0x00000001), + RTW89_DECL_RFK_WC(0x32e8, 0x00000004), + RTW89_DECL_RFK_WRF(0x1, 0x8f, 0x02000, 0x00000), + RTW89_DECL_RFK_WM(0x032c, 0x00010000, 0x00000001), }; -DECLARE_RFK_TBL(rtw8852a_rfk_check_dadc_defs_r_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_check_dadc_defs_r_b); static const struct rtw89_reg5_def rtw8852a_rfk_dack_defs_f_a[] = { - DECL_RFK_WS(0x5e00, 0x00000008), - DECL_RFK_WC(0x5e10, 0x80000000), - DECL_RFK_WS(0x5e50, 0x00000008), - DECL_RFK_WC(0x5e60, 0x80000000), - DECL_RFK_WS(0x12a0, 0x00008000), - DECL_RFK_WM(0x12a0, 0x00007000, 0x00000003), - DECL_RFK_WS(0x12b8, 0x40000000), - DECL_RFK_WS(0x030c, 0x10000000), - DECL_RFK_WC(0x032c, 0x80000000), - DECL_RFK_WS(0x12e0, 0x00010000), - DECL_RFK_WS(0x12e4, 0x0c000000), - DECL_RFK_WM(0x5e00, 0x03ff0000, 0x00000030), - DECL_RFK_WM(0x5e50, 0x03ff0000, 0x00000030), - DECL_RFK_WC(0x5e00, 0x0c000000), - DECL_RFK_WC(0x5e50, 0x0c000000), - DECL_RFK_WC(0x5e0c, 0x00000008), - DECL_RFK_WC(0x5e5c, 0x00000008), - DECL_RFK_WS(0x5e0c, 0x00000001), - DECL_RFK_WS(0x5e5c, 0x00000001), - DECL_RFK_DELAY(1), + RTW89_DECL_RFK_WS(0x5e00, 0x00000008), + RTW89_DECL_RFK_WC(0x5e10, 0x80000000), + RTW89_DECL_RFK_WS(0x5e50, 0x00000008), + RTW89_DECL_RFK_WC(0x5e60, 0x80000000), + RTW89_DECL_RFK_WS(0x12a0, 0x00008000), + RTW89_DECL_RFK_WM(0x12a0, 0x00007000, 0x00000003), + RTW89_DECL_RFK_WS(0x12b8, 0x40000000), + RTW89_DECL_RFK_WS(0x030c, 0x10000000), + RTW89_DECL_RFK_WC(0x032c, 0x80000000), + RTW89_DECL_RFK_WS(0x12e0, 0x00010000), + RTW89_DECL_RFK_WS(0x12e4, 0x0c000000), + RTW89_DECL_RFK_WM(0x5e00, 0x03ff0000, 0x00000030), + RTW89_DECL_RFK_WM(0x5e50, 0x03ff0000, 0x00000030), + RTW89_DECL_RFK_WC(0x5e00, 0x0c000000), + RTW89_DECL_RFK_WC(0x5e50, 0x0c000000), + RTW89_DECL_RFK_WC(0x5e0c, 0x00000008), + RTW89_DECL_RFK_WC(0x5e5c, 0x00000008), + RTW89_DECL_RFK_WS(0x5e0c, 0x00000001), + RTW89_DECL_RFK_WS(0x5e5c, 0x00000001), + RTW89_DECL_RFK_DELAY(1), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dack_defs_f_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dack_defs_f_a); static const struct rtw89_reg5_def rtw8852a_rfk_dack_defs_m_a[] = { - DECL_RFK_WC(0x12e4, 0x0c000000), - DECL_RFK_WS(0x5e0c, 0x00000008), - DECL_RFK_WS(0x5e5c, 0x00000008), - DECL_RFK_DELAY(1), + RTW89_DECL_RFK_WC(0x12e4, 0x0c000000), + RTW89_DECL_RFK_WS(0x5e0c, 0x00000008), + RTW89_DECL_RFK_WS(0x5e5c, 0x00000008), + RTW89_DECL_RFK_DELAY(1), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dack_defs_m_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dack_defs_m_a); static const struct rtw89_reg5_def rtw8852a_rfk_dack_defs_r_a[] = { - DECL_RFK_WC(0x5e0c, 0x00000001), - DECL_RFK_WC(0x5e5c, 0x00000001), - DECL_RFK_WC(0x12e0, 0x00010000), - DECL_RFK_WC(0x12a0, 0x00008000), - DECL_RFK_WS(0x12a0, 0x00007000), + RTW89_DECL_RFK_WC(0x5e0c, 0x00000001), + RTW89_DECL_RFK_WC(0x5e5c, 0x00000001), + RTW89_DECL_RFK_WC(0x12e0, 0x00010000), + RTW89_DECL_RFK_WC(0x12a0, 0x00008000), + RTW89_DECL_RFK_WS(0x12a0, 0x00007000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dack_defs_r_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dack_defs_r_a); static const struct rtw89_reg5_def rtw8852a_rfk_dack_defs_f_b[] = { - DECL_RFK_WS(0x7e00, 0x00000008), - DECL_RFK_WC(0x7e10, 0x80000000), - DECL_RFK_WS(0x7e50, 0x00000008), - DECL_RFK_WC(0x7e60, 0x80000000), - DECL_RFK_WS(0x32a0, 0x00008000), - DECL_RFK_WM(0x32a0, 0x00007000, 0x00000003), - DECL_RFK_WS(0x32b8, 0x40000000), - DECL_RFK_WS(0x030c, 0x10000000), - DECL_RFK_WC(0x032c, 0x80000000), - DECL_RFK_WS(0x32e0, 0x00010000), - DECL_RFK_WS(0x32e4, 0x0c000000), - DECL_RFK_WM(0x7e00, 0x03ff0000, 0x00000030), - DECL_RFK_WM(0x7e50, 0x03ff0000, 0x00000030), - DECL_RFK_WC(0x7e00, 0x0c000000), - DECL_RFK_WC(0x7e50, 0x0c000000), - DECL_RFK_WC(0x7e0c, 0x00000008), - DECL_RFK_WC(0x7e5c, 0x00000008), - DECL_RFK_WS(0x7e0c, 0x00000001), - DECL_RFK_WS(0x7e5c, 0x00000001), - DECL_RFK_DELAY(1), + RTW89_DECL_RFK_WS(0x7e00, 0x00000008), + RTW89_DECL_RFK_WC(0x7e10, 0x80000000), + RTW89_DECL_RFK_WS(0x7e50, 0x00000008), + RTW89_DECL_RFK_WC(0x7e60, 0x80000000), + RTW89_DECL_RFK_WS(0x32a0, 0x00008000), + RTW89_DECL_RFK_WM(0x32a0, 0x00007000, 0x00000003), + RTW89_DECL_RFK_WS(0x32b8, 0x40000000), + RTW89_DECL_RFK_WS(0x030c, 0x10000000), + RTW89_DECL_RFK_WC(0x032c, 0x80000000), + RTW89_DECL_RFK_WS(0x32e0, 0x00010000), + RTW89_DECL_RFK_WS(0x32e4, 0x0c000000), + RTW89_DECL_RFK_WM(0x7e00, 0x03ff0000, 0x00000030), + RTW89_DECL_RFK_WM(0x7e50, 0x03ff0000, 0x00000030), + RTW89_DECL_RFK_WC(0x7e00, 0x0c000000), + RTW89_DECL_RFK_WC(0x7e50, 0x0c000000), + RTW89_DECL_RFK_WC(0x7e0c, 0x00000008), + RTW89_DECL_RFK_WC(0x7e5c, 0x00000008), + RTW89_DECL_RFK_WS(0x7e0c, 0x00000001), + RTW89_DECL_RFK_WS(0x7e5c, 0x00000001), + RTW89_DECL_RFK_DELAY(1), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dack_defs_f_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dack_defs_f_b); static const struct rtw89_reg5_def rtw8852a_rfk_dack_defs_m_b[] = { - DECL_RFK_WC(0x32e4, 0x0c000000), - DECL_RFK_WM(0x7e0c, 0x00000008, 0x00000001), - DECL_RFK_WM(0x7e5c, 0x00000008, 0x00000001), - DECL_RFK_DELAY(1), + RTW89_DECL_RFK_WC(0x32e4, 0x0c000000), + RTW89_DECL_RFK_WM(0x7e0c, 0x00000008, 0x00000001), + RTW89_DECL_RFK_WM(0x7e5c, 0x00000008, 0x00000001), + RTW89_DECL_RFK_DELAY(1), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dack_defs_m_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dack_defs_m_b); static const struct rtw89_reg5_def rtw8852a_rfk_dack_defs_r_b[] = { - DECL_RFK_WC(0x7e0c, 0x00000001), - DECL_RFK_WC(0x7e5c, 0x00000001), - DECL_RFK_WC(0x32e0, 0x00010000), - DECL_RFK_WC(0x32a0, 0x00008000), - DECL_RFK_WS(0x32a0, 0x00007000), + RTW89_DECL_RFK_WC(0x7e0c, 0x00000001), + RTW89_DECL_RFK_WC(0x7e5c, 0x00000001), + RTW89_DECL_RFK_WC(0x32e0, 0x00010000), + RTW89_DECL_RFK_WC(0x32a0, 0x00008000), + RTW89_DECL_RFK_WS(0x32a0, 0x00007000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dack_defs_r_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dack_defs_r_b); static const struct rtw89_reg5_def rtw8852a_rfk_dpk_bb_afe_sf_defs_a[] = { - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000101), - DECL_RFK_WS(0x12b8, 0x40000000), - DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), - DECL_RFK_WM(0x032c, 0xffff0000, 0x00000041), - DECL_RFK_WS(0x12b8, 0x10000000), - DECL_RFK_WS(0x58c8, 0x01000000), - DECL_RFK_WS(0x5864, 0xc0000000), - DECL_RFK_WS(0x2008, 0x01ffffff), - DECL_RFK_WS(0x0c1c, 0x00000004), - DECL_RFK_WS(0x0700, 0x08000000), - DECL_RFK_WS(0x0c70, 0x000003ff), - DECL_RFK_WS(0x0c60, 0x00000003), - DECL_RFK_WS(0x0c6c, 0x00000001), - DECL_RFK_WS(0x58ac, 0x08000000), - DECL_RFK_WS(0x0c3c, 0x00000200), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000101), + RTW89_DECL_RFK_WS(0x12b8, 0x40000000), + RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), + RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x00000041), + RTW89_DECL_RFK_WS(0x12b8, 0x10000000), + RTW89_DECL_RFK_WS(0x58c8, 0x01000000), + RTW89_DECL_RFK_WS(0x5864, 0xc0000000), + RTW89_DECL_RFK_WS(0x2008, 0x01ffffff), + RTW89_DECL_RFK_WS(0x0c1c, 0x00000004), + RTW89_DECL_RFK_WS(0x0700, 0x08000000), + RTW89_DECL_RFK_WS(0x0c70, 0x000003ff), + RTW89_DECL_RFK_WS(0x0c60, 0x00000003), + RTW89_DECL_RFK_WS(0x0c6c, 0x00000001), + RTW89_DECL_RFK_WS(0x58ac, 0x08000000), + RTW89_DECL_RFK_WS(0x0c3c, 0x00000200), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_sf_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_sf_defs_a); static const struct rtw89_reg5_def rtw8852a_rfk_dpk_bb_afe_sr_defs_a[] = { - DECL_RFK_WS(0x4490, 0x80000000), - DECL_RFK_WS(0x12a0, 0x00007000), - DECL_RFK_WS(0x12a0, 0x00008000), - DECL_RFK_WM(0x12a0, 0x00070000, 0x00000003), - DECL_RFK_WS(0x12a0, 0x00080000), - DECL_RFK_WS(0x0700, 0x01000000), - DECL_RFK_WM(0x0700, 0x06000000, 0x00000002), - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00001111), - DECL_RFK_WM(0x58f0, 0x00080000, 0x00000000), + RTW89_DECL_RFK_WS(0x4490, 0x80000000), + RTW89_DECL_RFK_WS(0x12a0, 0x00007000), + RTW89_DECL_RFK_WS(0x12a0, 0x00008000), + RTW89_DECL_RFK_WM(0x12a0, 0x00070000, 0x00000003), + RTW89_DECL_RFK_WS(0x12a0, 0x00080000), + RTW89_DECL_RFK_WS(0x0700, 0x01000000), + RTW89_DECL_RFK_WM(0x0700, 0x06000000, 0x00000002), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00001111), + RTW89_DECL_RFK_WM(0x58f0, 0x00080000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_sr_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_sr_defs_a); static const struct rtw89_reg5_def rtw8852a_rfk_dpk_bb_afe_sf_defs_b[] = { - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000202), - DECL_RFK_WS(0x32b8, 0x40000000), - DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), - DECL_RFK_WM(0x032c, 0xffff0000, 0x00000041), - DECL_RFK_WS(0x32b8, 0x10000000), - DECL_RFK_WS(0x78c8, 0x01000000), - DECL_RFK_WS(0x7864, 0xc0000000), - DECL_RFK_WS(0x2008, 0x01ffffff), - DECL_RFK_WS(0x2c1c, 0x00000004), - DECL_RFK_WS(0x2700, 0x08000000), - DECL_RFK_WS(0x0c70, 0x000003ff), - DECL_RFK_WS(0x0c60, 0x00000003), - DECL_RFK_WS(0x0c6c, 0x00000001), - DECL_RFK_WS(0x78ac, 0x08000000), - DECL_RFK_WS(0x2c3c, 0x00000200), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000202), + RTW89_DECL_RFK_WS(0x32b8, 0x40000000), + RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), + RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x00000041), + RTW89_DECL_RFK_WS(0x32b8, 0x10000000), + RTW89_DECL_RFK_WS(0x78c8, 0x01000000), + RTW89_DECL_RFK_WS(0x7864, 0xc0000000), + RTW89_DECL_RFK_WS(0x2008, 0x01ffffff), + RTW89_DECL_RFK_WS(0x2c1c, 0x00000004), + RTW89_DECL_RFK_WS(0x2700, 0x08000000), + RTW89_DECL_RFK_WS(0x0c70, 0x000003ff), + RTW89_DECL_RFK_WS(0x0c60, 0x00000003), + RTW89_DECL_RFK_WS(0x0c6c, 0x00000001), + RTW89_DECL_RFK_WS(0x78ac, 0x08000000), + RTW89_DECL_RFK_WS(0x2c3c, 0x00000200), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_sf_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_sf_defs_b); static const struct rtw89_reg5_def rtw8852a_rfk_dpk_bb_afe_sr_defs_b[] = { - DECL_RFK_WS(0x6490, 0x80000000), - DECL_RFK_WS(0x32a0, 0x00007000), - DECL_RFK_WS(0x32a0, 0x00008000), - DECL_RFK_WM(0x32a0, 0x00070000, 0x00000003), - DECL_RFK_WS(0x32a0, 0x00080000), - DECL_RFK_WS(0x2700, 0x01000000), - DECL_RFK_WM(0x2700, 0x06000000, 0x00000002), - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00002222), - DECL_RFK_WM(0x78f0, 0x00080000, 0x00000000), + RTW89_DECL_RFK_WS(0x6490, 0x80000000), + RTW89_DECL_RFK_WS(0x32a0, 0x00007000), + RTW89_DECL_RFK_WS(0x32a0, 0x00008000), + RTW89_DECL_RFK_WM(0x32a0, 0x00070000, 0x00000003), + RTW89_DECL_RFK_WS(0x32a0, 0x00080000), + RTW89_DECL_RFK_WS(0x2700, 0x01000000), + RTW89_DECL_RFK_WM(0x2700, 0x06000000, 0x00000002), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00002222), + RTW89_DECL_RFK_WM(0x78f0, 0x00080000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_sr_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_sr_defs_b); static const struct rtw89_reg5_def rtw8852a_rfk_dpk_bb_afe_s_defs_ab[] = { - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000303), - DECL_RFK_WS(0x12b8, 0x40000000), - DECL_RFK_WS(0x32b8, 0x40000000), - DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), - DECL_RFK_WM(0x032c, 0xffff0000, 0x00000041), - DECL_RFK_WS(0x12b8, 0x10000000), - DECL_RFK_WS(0x58c8, 0x01000000), - DECL_RFK_WS(0x78c8, 0x01000000), - DECL_RFK_WS(0x5864, 0xc0000000), - DECL_RFK_WS(0x7864, 0xc0000000), - DECL_RFK_WS(0x2008, 0x01ffffff), - DECL_RFK_WS(0x0c1c, 0x00000004), - DECL_RFK_WS(0x0700, 0x08000000), - DECL_RFK_WS(0x0c70, 0x000003ff), - DECL_RFK_WS(0x0c60, 0x00000003), - DECL_RFK_WS(0x0c6c, 0x00000001), - DECL_RFK_WS(0x58ac, 0x08000000), - DECL_RFK_WS(0x78ac, 0x08000000), - DECL_RFK_WS(0x0c3c, 0x00000200), - DECL_RFK_WS(0x2344, 0x80000000), - DECL_RFK_WS(0x4490, 0x80000000), - DECL_RFK_WS(0x12a0, 0x00007000), - DECL_RFK_WS(0x12a0, 0x00008000), - DECL_RFK_WM(0x12a0, 0x00070000, 0x00000003), - DECL_RFK_WS(0x12a0, 0x00080000), - DECL_RFK_WM(0x32a0, 0x00070000, 0x00000003), - DECL_RFK_WS(0x32a0, 0x00080000), - DECL_RFK_WS(0x0700, 0x01000000), - DECL_RFK_WM(0x0700, 0x06000000, 0x00000002), - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00003333), - DECL_RFK_WM(0x58f0, 0x00080000, 0x00000000), - DECL_RFK_WM(0x78f0, 0x00080000, 0x00000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000303), + RTW89_DECL_RFK_WS(0x12b8, 0x40000000), + RTW89_DECL_RFK_WS(0x32b8, 0x40000000), + RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), + RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x00000041), + RTW89_DECL_RFK_WS(0x12b8, 0x10000000), + RTW89_DECL_RFK_WS(0x58c8, 0x01000000), + RTW89_DECL_RFK_WS(0x78c8, 0x01000000), + RTW89_DECL_RFK_WS(0x5864, 0xc0000000), + RTW89_DECL_RFK_WS(0x7864, 0xc0000000), + RTW89_DECL_RFK_WS(0x2008, 0x01ffffff), + RTW89_DECL_RFK_WS(0x0c1c, 0x00000004), + RTW89_DECL_RFK_WS(0x0700, 0x08000000), + RTW89_DECL_RFK_WS(0x0c70, 0x000003ff), + RTW89_DECL_RFK_WS(0x0c60, 0x00000003), + RTW89_DECL_RFK_WS(0x0c6c, 0x00000001), + RTW89_DECL_RFK_WS(0x58ac, 0x08000000), + RTW89_DECL_RFK_WS(0x78ac, 0x08000000), + RTW89_DECL_RFK_WS(0x0c3c, 0x00000200), + RTW89_DECL_RFK_WS(0x2344, 0x80000000), + RTW89_DECL_RFK_WS(0x4490, 0x80000000), + RTW89_DECL_RFK_WS(0x12a0, 0x00007000), + RTW89_DECL_RFK_WS(0x12a0, 0x00008000), + RTW89_DECL_RFK_WM(0x12a0, 0x00070000, 0x00000003), + RTW89_DECL_RFK_WS(0x12a0, 0x00080000), + RTW89_DECL_RFK_WM(0x32a0, 0x00070000, 0x00000003), + RTW89_DECL_RFK_WS(0x32a0, 0x00080000), + RTW89_DECL_RFK_WS(0x0700, 0x01000000), + RTW89_DECL_RFK_WM(0x0700, 0x06000000, 0x00000002), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00003333), + RTW89_DECL_RFK_WM(0x58f0, 0x00080000, 0x00000000), + RTW89_DECL_RFK_WM(0x78f0, 0x00080000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_s_defs_ab); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_s_defs_ab); static const struct rtw89_reg5_def rtw8852a_rfk_dpk_bb_afe_r_defs_a[] = { - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000101), - DECL_RFK_WC(0x12b8, 0x40000000), - DECL_RFK_WC(0x5864, 0xc0000000), - DECL_RFK_WC(0x2008, 0x01ffffff), - DECL_RFK_WC(0x0c1c, 0x00000004), - DECL_RFK_WC(0x0700, 0x08000000), - DECL_RFK_WM(0x0c70, 0x0000001f, 0x00000003), - DECL_RFK_WM(0x0c70, 0x000003e0, 0x00000003), - DECL_RFK_WC(0x12a0, 0x000ff000), - DECL_RFK_WC(0x0700, 0x07000000), - DECL_RFK_WC(0x5864, 0x20000000), - DECL_RFK_WC(0x0c3c, 0x00000200), - DECL_RFK_WC(0x20fc, 0xffff0000), - DECL_RFK_WC(0x58c8, 0x01000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000101), + RTW89_DECL_RFK_WC(0x12b8, 0x40000000), + RTW89_DECL_RFK_WC(0x5864, 0xc0000000), + RTW89_DECL_RFK_WC(0x2008, 0x01ffffff), + RTW89_DECL_RFK_WC(0x0c1c, 0x00000004), + RTW89_DECL_RFK_WC(0x0700, 0x08000000), + RTW89_DECL_RFK_WM(0x0c70, 0x0000001f, 0x00000003), + RTW89_DECL_RFK_WM(0x0c70, 0x000003e0, 0x00000003), + RTW89_DECL_RFK_WC(0x12a0, 0x000ff000), + RTW89_DECL_RFK_WC(0x0700, 0x07000000), + RTW89_DECL_RFK_WC(0x5864, 0x20000000), + RTW89_DECL_RFK_WC(0x0c3c, 0x00000200), + RTW89_DECL_RFK_WC(0x20fc, 0xffff0000), + RTW89_DECL_RFK_WC(0x58c8, 0x01000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_r_defs_a); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_r_defs_a); static const struct rtw89_reg5_def rtw8852a_rfk_dpk_bb_afe_r_defs_b[] = { - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000202), - DECL_RFK_WC(0x32b8, 0x40000000), - DECL_RFK_WC(0x7864, 0xc0000000), - DECL_RFK_WC(0x2008, 0x01ffffff), - DECL_RFK_WC(0x2c1c, 0x00000004), - DECL_RFK_WC(0x2700, 0x08000000), - DECL_RFK_WM(0x0c70, 0x0000001f, 0x00000003), - DECL_RFK_WM(0x0c70, 0x000003e0, 0x00000003), - DECL_RFK_WC(0x32a0, 0x000ff000), - DECL_RFK_WC(0x2700, 0x07000000), - DECL_RFK_WC(0x7864, 0x20000000), - DECL_RFK_WC(0x2c3c, 0x00000200), - DECL_RFK_WC(0x20fc, 0xffff0000), - DECL_RFK_WC(0x78c8, 0x01000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000202), + RTW89_DECL_RFK_WC(0x32b8, 0x40000000), + RTW89_DECL_RFK_WC(0x7864, 0xc0000000), + RTW89_DECL_RFK_WC(0x2008, 0x01ffffff), + RTW89_DECL_RFK_WC(0x2c1c, 0x00000004), + RTW89_DECL_RFK_WC(0x2700, 0x08000000), + RTW89_DECL_RFK_WM(0x0c70, 0x0000001f, 0x00000003), + RTW89_DECL_RFK_WM(0x0c70, 0x000003e0, 0x00000003), + RTW89_DECL_RFK_WC(0x32a0, 0x000ff000), + RTW89_DECL_RFK_WC(0x2700, 0x07000000), + RTW89_DECL_RFK_WC(0x7864, 0x20000000), + RTW89_DECL_RFK_WC(0x2c3c, 0x00000200), + RTW89_DECL_RFK_WC(0x20fc, 0xffff0000), + RTW89_DECL_RFK_WC(0x78c8, 0x01000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_r_defs_b); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_r_defs_b); static const struct rtw89_reg5_def rtw8852a_rfk_dpk_bb_afe_r_defs_ab[] = { - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000303), - DECL_RFK_WC(0x12b8, 0x40000000), - DECL_RFK_WC(0x32b8, 0x40000000), - DECL_RFK_WC(0x5864, 0xc0000000), - DECL_RFK_WC(0x7864, 0xc0000000), - DECL_RFK_WC(0x2008, 0x01ffffff), - DECL_RFK_WC(0x0c1c, 0x00000004), - DECL_RFK_WC(0x0700, 0x08000000), - DECL_RFK_WM(0x0c70, 0x0000001f, 0x00000003), - DECL_RFK_WM(0x0c70, 0x000003e0, 0x00000003), - DECL_RFK_WC(0x12a0, 0x000ff000), - DECL_RFK_WC(0x32a0, 0x000ff000), - DECL_RFK_WC(0x0700, 0x07000000), - DECL_RFK_WC(0x5864, 0x20000000), - DECL_RFK_WC(0x7864, 0x20000000), - DECL_RFK_WC(0x0c3c, 0x00000200), - DECL_RFK_WC(0x20fc, 0xffff0000), - DECL_RFK_WC(0x58c8, 0x01000000), - DECL_RFK_WC(0x78c8, 0x01000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000303), + RTW89_DECL_RFK_WC(0x12b8, 0x40000000), + RTW89_DECL_RFK_WC(0x32b8, 0x40000000), + RTW89_DECL_RFK_WC(0x5864, 0xc0000000), + RTW89_DECL_RFK_WC(0x7864, 0xc0000000), + RTW89_DECL_RFK_WC(0x2008, 0x01ffffff), + RTW89_DECL_RFK_WC(0x0c1c, 0x00000004), + RTW89_DECL_RFK_WC(0x0700, 0x08000000), + RTW89_DECL_RFK_WM(0x0c70, 0x0000001f, 0x00000003), + RTW89_DECL_RFK_WM(0x0c70, 0x000003e0, 0x00000003), + RTW89_DECL_RFK_WC(0x12a0, 0x000ff000), + RTW89_DECL_RFK_WC(0x32a0, 0x000ff000), + RTW89_DECL_RFK_WC(0x0700, 0x07000000), + RTW89_DECL_RFK_WC(0x5864, 0x20000000), + RTW89_DECL_RFK_WC(0x7864, 0x20000000), + RTW89_DECL_RFK_WC(0x0c3c, 0x00000200), + RTW89_DECL_RFK_WC(0x20fc, 0xffff0000), + RTW89_DECL_RFK_WC(0x58c8, 0x01000000), + RTW89_DECL_RFK_WC(0x78c8, 0x01000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_r_defs_ab); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dpk_bb_afe_r_defs_ab); static const struct rtw89_reg5_def rtw8852a_rfk_dpk_lbk_rxiqk_defs_f[] = { - DECL_RFK_WM(0x030c, 0xff000000, 0x0000000f), - DECL_RFK_DELAY(1), - DECL_RFK_WM(0x030c, 0xff000000, 0x00000003), - DECL_RFK_WM(0x032c, 0xffff0000, 0x0000a001), - DECL_RFK_DELAY(1), - DECL_RFK_WM(0x032c, 0xffff0000, 0x0000a041), - DECL_RFK_WS(0x8074, 0x80000000), + RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x0000000f), + RTW89_DECL_RFK_DELAY(1), + RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x00000003), + RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x0000a001), + RTW89_DECL_RFK_DELAY(1), + RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x0000a041), + RTW89_DECL_RFK_WS(0x8074, 0x80000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dpk_lbk_rxiqk_defs_f); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dpk_lbk_rxiqk_defs_f); static const struct rtw89_reg5_def rtw8852a_rfk_dpk_lbk_rxiqk_defs_r[] = { - DECL_RFK_WC(0x8074, 0x80000000), - DECL_RFK_WM(0x030c, 0xff000000, 0x0000001f), - DECL_RFK_DELAY(1), - DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), - DECL_RFK_WM(0x032c, 0xffff0000, 0x00000001), - DECL_RFK_DELAY(1), - DECL_RFK_WM(0x032c, 0xffff0000, 0x00000041), - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000303), - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00003333), + RTW89_DECL_RFK_WC(0x8074, 0x80000000), + RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x0000001f), + RTW89_DECL_RFK_DELAY(1), + RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), + RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x00000001), + RTW89_DECL_RFK_DELAY(1), + RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x00000041), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000303), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00003333), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dpk_lbk_rxiqk_defs_r); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dpk_lbk_rxiqk_defs_r); static const struct rtw89_reg5_def rtw8852a_rfk_dpk_pas_read_defs[] = { - DECL_RFK_WM(0x80d4, 0x00ff0000, 0x00000006), - DECL_RFK_WC(0x80bc, 0x00004000), - DECL_RFK_WM(0x80c0, 0x00ff0000, 0x00000008), + RTW89_DECL_RFK_WM(0x80d4, 0x00ff0000, 0x00000006), + RTW89_DECL_RFK_WC(0x80bc, 0x00004000), + RTW89_DECL_RFK_WM(0x80c0, 0x00ff0000, 0x00000008), }; -DECLARE_RFK_TBL(rtw8852a_rfk_dpk_pas_read_defs); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_dpk_pas_read_defs); static const struct rtw89_reg5_def rtw8852a_rfk_iqk_set_defs_nondbcc_path01[] = { - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000303), - DECL_RFK_WM(0x5864, 0x18000000, 0x00000003), - DECL_RFK_WM(0x7864, 0x18000000, 0x00000003), - DECL_RFK_WM(0x12b8, 0x40000000, 0x00000001), - DECL_RFK_WM(0x32b8, 0x40000000, 0x00000001), - DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), - DECL_RFK_WM(0x032c, 0xffff0000, 0x00000001), - DECL_RFK_WM(0x12b8, 0x10000000, 0x00000001), - DECL_RFK_WM(0x58c8, 0x01000000, 0x00000001), - DECL_RFK_WM(0x78c8, 0x01000000, 0x00000001), - DECL_RFK_WM(0x5864, 0xc0000000, 0x00000003), - DECL_RFK_WM(0x7864, 0xc0000000, 0x00000003), - DECL_RFK_WM(0x2008, 0x01ffffff, 0x01ffffff), - DECL_RFK_WM(0x0c1c, 0x00000004, 0x00000001), - DECL_RFK_WM(0x0700, 0x08000000, 0x00000001), - DECL_RFK_WM(0x0c70, 0x000003ff, 0x000003ff), - DECL_RFK_WM(0x0c60, 0x00000003, 0x00000003), - DECL_RFK_WM(0x0c6c, 0x00000001, 0x00000001), - DECL_RFK_WM(0x58ac, 0x08000000, 0x00000001), - DECL_RFK_WM(0x78ac, 0x08000000, 0x00000001), - DECL_RFK_WM(0x0c3c, 0x00000200, 0x00000001), - DECL_RFK_WM(0x2344, 0x80000000, 0x00000001), - DECL_RFK_WM(0x4490, 0x80000000, 0x00000001), - DECL_RFK_WM(0x12a0, 0x00007000, 0x00000007), - DECL_RFK_WM(0x12a0, 0x00008000, 0x00000001), - DECL_RFK_WM(0x12a0, 0x00070000, 0x00000003), - DECL_RFK_WM(0x12a0, 0x00080000, 0x00000001), - DECL_RFK_WM(0x32a0, 0x00070000, 0x00000003), - DECL_RFK_WM(0x32a0, 0x00080000, 0x00000001), - DECL_RFK_WM(0x0700, 0x01000000, 0x00000001), - DECL_RFK_WM(0x0700, 0x06000000, 0x00000002), - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00003333), - DECL_RFK_WM(0x58f0, 0x00080000, 0x00000000), - DECL_RFK_WM(0x78f0, 0x00080000, 0x00000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000303), + RTW89_DECL_RFK_WM(0x5864, 0x18000000, 0x00000003), + RTW89_DECL_RFK_WM(0x7864, 0x18000000, 0x00000003), + RTW89_DECL_RFK_WM(0x12b8, 0x40000000, 0x00000001), + RTW89_DECL_RFK_WM(0x32b8, 0x40000000, 0x00000001), + RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), + RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x00000001), + RTW89_DECL_RFK_WM(0x12b8, 0x10000000, 0x00000001), + RTW89_DECL_RFK_WM(0x58c8, 0x01000000, 0x00000001), + RTW89_DECL_RFK_WM(0x78c8, 0x01000000, 0x00000001), + RTW89_DECL_RFK_WM(0x5864, 0xc0000000, 0x00000003), + RTW89_DECL_RFK_WM(0x7864, 0xc0000000, 0x00000003), + RTW89_DECL_RFK_WM(0x2008, 0x01ffffff, 0x01ffffff), + RTW89_DECL_RFK_WM(0x0c1c, 0x00000004, 0x00000001), + RTW89_DECL_RFK_WM(0x0700, 0x08000000, 0x00000001), + RTW89_DECL_RFK_WM(0x0c70, 0x000003ff, 0x000003ff), + RTW89_DECL_RFK_WM(0x0c60, 0x00000003, 0x00000003), + RTW89_DECL_RFK_WM(0x0c6c, 0x00000001, 0x00000001), + RTW89_DECL_RFK_WM(0x58ac, 0x08000000, 0x00000001), + RTW89_DECL_RFK_WM(0x78ac, 0x08000000, 0x00000001), + RTW89_DECL_RFK_WM(0x0c3c, 0x00000200, 0x00000001), + RTW89_DECL_RFK_WM(0x2344, 0x80000000, 0x00000001), + RTW89_DECL_RFK_WM(0x4490, 0x80000000, 0x00000001), + RTW89_DECL_RFK_WM(0x12a0, 0x00007000, 0x00000007), + RTW89_DECL_RFK_WM(0x12a0, 0x00008000, 0x00000001), + RTW89_DECL_RFK_WM(0x12a0, 0x00070000, 0x00000003), + RTW89_DECL_RFK_WM(0x12a0, 0x00080000, 0x00000001), + RTW89_DECL_RFK_WM(0x32a0, 0x00070000, 0x00000003), + RTW89_DECL_RFK_WM(0x32a0, 0x00080000, 0x00000001), + RTW89_DECL_RFK_WM(0x0700, 0x01000000, 0x00000001), + RTW89_DECL_RFK_WM(0x0700, 0x06000000, 0x00000002), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00003333), + RTW89_DECL_RFK_WM(0x58f0, 0x00080000, 0x00000000), + RTW89_DECL_RFK_WM(0x78f0, 0x00080000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_iqk_set_defs_nondbcc_path01); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_iqk_set_defs_nondbcc_path01); static const struct rtw89_reg5_def rtw8852a_rfk_iqk_set_defs_dbcc_path0[] = { - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000101), - DECL_RFK_WM(0x5864, 0x18000000, 0x00000003), - DECL_RFK_WM(0x7864, 0x18000000, 0x00000003), - DECL_RFK_WM(0x12b8, 0x40000000, 0x00000001), - DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), - DECL_RFK_WM(0x032c, 0xffff0000, 0x00000001), - DECL_RFK_WM(0x12b8, 0x10000000, 0x00000001), - DECL_RFK_WM(0x58c8, 0x01000000, 0x00000001), - DECL_RFK_WM(0x5864, 0xc0000000, 0x00000003), - DECL_RFK_WM(0x2008, 0x01ffffff, 0x01ffffff), - DECL_RFK_WM(0x0c1c, 0x00000004, 0x00000001), - DECL_RFK_WM(0x0700, 0x08000000, 0x00000001), - DECL_RFK_WM(0x0c70, 0x000003ff, 0x000003ff), - DECL_RFK_WM(0x0c60, 0x00000003, 0x00000003), - DECL_RFK_WM(0x0c6c, 0x00000001, 0x00000001), - DECL_RFK_WM(0x58ac, 0x08000000, 0x00000001), - DECL_RFK_WM(0x0c3c, 0x00000200, 0x00000001), - DECL_RFK_WM(0x2320, 0x00000001, 0x00000001), - DECL_RFK_WM(0x4490, 0x80000000, 0x00000001), - DECL_RFK_WM(0x12a0, 0x00007000, 0x00000007), - DECL_RFK_WM(0x12a0, 0x00008000, 0x00000001), - DECL_RFK_WM(0x12a0, 0x00070000, 0x00000003), - DECL_RFK_WM(0x12a0, 0x00080000, 0x00000001), - DECL_RFK_WM(0x0700, 0x01000000, 0x00000001), - DECL_RFK_WM(0x0700, 0x06000000, 0x00000002), - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00001111), - DECL_RFK_WM(0x58f0, 0x00080000, 0x00000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000101), + RTW89_DECL_RFK_WM(0x5864, 0x18000000, 0x00000003), + RTW89_DECL_RFK_WM(0x7864, 0x18000000, 0x00000003), + RTW89_DECL_RFK_WM(0x12b8, 0x40000000, 0x00000001), + RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), + RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x00000001), + RTW89_DECL_RFK_WM(0x12b8, 0x10000000, 0x00000001), + RTW89_DECL_RFK_WM(0x58c8, 0x01000000, 0x00000001), + RTW89_DECL_RFK_WM(0x5864, 0xc0000000, 0x00000003), + RTW89_DECL_RFK_WM(0x2008, 0x01ffffff, 0x01ffffff), + RTW89_DECL_RFK_WM(0x0c1c, 0x00000004, 0x00000001), + RTW89_DECL_RFK_WM(0x0700, 0x08000000, 0x00000001), + RTW89_DECL_RFK_WM(0x0c70, 0x000003ff, 0x000003ff), + RTW89_DECL_RFK_WM(0x0c60, 0x00000003, 0x00000003), + RTW89_DECL_RFK_WM(0x0c6c, 0x00000001, 0x00000001), + RTW89_DECL_RFK_WM(0x58ac, 0x08000000, 0x00000001), + RTW89_DECL_RFK_WM(0x0c3c, 0x00000200, 0x00000001), + RTW89_DECL_RFK_WM(0x2320, 0x00000001, 0x00000001), + RTW89_DECL_RFK_WM(0x4490, 0x80000000, 0x00000001), + RTW89_DECL_RFK_WM(0x12a0, 0x00007000, 0x00000007), + RTW89_DECL_RFK_WM(0x12a0, 0x00008000, 0x00000001), + RTW89_DECL_RFK_WM(0x12a0, 0x00070000, 0x00000003), + RTW89_DECL_RFK_WM(0x12a0, 0x00080000, 0x00000001), + RTW89_DECL_RFK_WM(0x0700, 0x01000000, 0x00000001), + RTW89_DECL_RFK_WM(0x0700, 0x06000000, 0x00000002), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00001111), + RTW89_DECL_RFK_WM(0x58f0, 0x00080000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_iqk_set_defs_dbcc_path0); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_iqk_set_defs_dbcc_path0); static const struct rtw89_reg5_def rtw8852a_rfk_iqk_set_defs_dbcc_path1[] = { - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000202), - DECL_RFK_WM(0x7864, 0x18000000, 0x00000003), - DECL_RFK_WM(0x32b8, 0x40000000, 0x00000001), - DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), - DECL_RFK_WM(0x032c, 0xffff0000, 0x00000001), - DECL_RFK_WM(0x32b8, 0x10000000, 0x00000001), - DECL_RFK_WM(0x78c8, 0x01000000, 0x00000001), - DECL_RFK_WM(0x7864, 0xc0000000, 0x00000003), - DECL_RFK_WM(0x2008, 0x01ffffff, 0x01ffffff), - DECL_RFK_WM(0x2c1c, 0x00000004, 0x00000001), - DECL_RFK_WM(0x2700, 0x08000000, 0x00000001), - DECL_RFK_WM(0x0c70, 0x000003ff, 0x000003ff), - DECL_RFK_WM(0x0c60, 0x00000003, 0x00000003), - DECL_RFK_WM(0x0c6c, 0x00000001, 0x00000001), - DECL_RFK_WM(0x78ac, 0x08000000, 0x00000001), - DECL_RFK_WM(0x2c3c, 0x00000200, 0x00000001), - DECL_RFK_WM(0x6490, 0x80000000, 0x00000001), - DECL_RFK_WM(0x32a0, 0x00007000, 0x00000007), - DECL_RFK_WM(0x32a0, 0x00008000, 0x00000001), - DECL_RFK_WM(0x32a0, 0x00070000, 0x00000003), - DECL_RFK_WM(0x32a0, 0x00080000, 0x00000001), - DECL_RFK_WM(0x2700, 0x01000000, 0x00000001), - DECL_RFK_WM(0x2700, 0x06000000, 0x00000002), - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00002222), - DECL_RFK_WM(0x78f0, 0x00080000, 0x00000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000202), + RTW89_DECL_RFK_WM(0x7864, 0x18000000, 0x00000003), + RTW89_DECL_RFK_WM(0x32b8, 0x40000000, 0x00000001), + RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x00000013), + RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x00000001), + RTW89_DECL_RFK_WM(0x32b8, 0x10000000, 0x00000001), + RTW89_DECL_RFK_WM(0x78c8, 0x01000000, 0x00000001), + RTW89_DECL_RFK_WM(0x7864, 0xc0000000, 0x00000003), + RTW89_DECL_RFK_WM(0x2008, 0x01ffffff, 0x01ffffff), + RTW89_DECL_RFK_WM(0x2c1c, 0x00000004, 0x00000001), + RTW89_DECL_RFK_WM(0x2700, 0x08000000, 0x00000001), + RTW89_DECL_RFK_WM(0x0c70, 0x000003ff, 0x000003ff), + RTW89_DECL_RFK_WM(0x0c60, 0x00000003, 0x00000003), + RTW89_DECL_RFK_WM(0x0c6c, 0x00000001, 0x00000001), + RTW89_DECL_RFK_WM(0x78ac, 0x08000000, 0x00000001), + RTW89_DECL_RFK_WM(0x2c3c, 0x00000200, 0x00000001), + RTW89_DECL_RFK_WM(0x6490, 0x80000000, 0x00000001), + RTW89_DECL_RFK_WM(0x32a0, 0x00007000, 0x00000007), + RTW89_DECL_RFK_WM(0x32a0, 0x00008000, 0x00000001), + RTW89_DECL_RFK_WM(0x32a0, 0x00070000, 0x00000003), + RTW89_DECL_RFK_WM(0x32a0, 0x00080000, 0x00000001), + RTW89_DECL_RFK_WM(0x2700, 0x01000000, 0x00000001), + RTW89_DECL_RFK_WM(0x2700, 0x06000000, 0x00000002), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00002222), + RTW89_DECL_RFK_WM(0x78f0, 0x00080000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_iqk_set_defs_dbcc_path1); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_iqk_set_defs_dbcc_path1); static const struct rtw89_reg5_def rtw8852a_rfk_iqk_restore_defs_nondbcc_path01[] = { - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000303), - DECL_RFK_WM(0x12b8, 0x40000000, 0x00000000), - DECL_RFK_WM(0x32b8, 0x40000000, 0x00000000), - DECL_RFK_WM(0x5864, 0xc0000000, 0x00000000), - DECL_RFK_WM(0x7864, 0xc0000000, 0x00000000), - DECL_RFK_WM(0x2008, 0x01ffffff, 0x00000000), - DECL_RFK_WM(0x0c1c, 0x00000004, 0x00000000), - DECL_RFK_WM(0x0700, 0x08000000, 0x00000000), - DECL_RFK_WM(0x0c70, 0x0000001f, 0x00000003), - DECL_RFK_WM(0x0c70, 0x000003e0, 0x00000003), - DECL_RFK_WM(0x12a0, 0x000ff000, 0x00000000), - DECL_RFK_WM(0x32a0, 0x000ff000, 0x00000000), - DECL_RFK_WM(0x0700, 0x07000000, 0x00000000), - DECL_RFK_WM(0x5864, 0x20000000, 0x00000000), - DECL_RFK_WM(0x7864, 0x20000000, 0x00000000), - DECL_RFK_WM(0x0c3c, 0x00000200, 0x00000000), - DECL_RFK_WM(0x2320, 0x00000001, 0x00000000), - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000000), - DECL_RFK_WM(0x58c8, 0x01000000, 0x00000000), - DECL_RFK_WM(0x78c8, 0x01000000, 0x00000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000303), + RTW89_DECL_RFK_WM(0x12b8, 0x40000000, 0x00000000), + RTW89_DECL_RFK_WM(0x32b8, 0x40000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5864, 0xc0000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7864, 0xc0000000, 0x00000000), + RTW89_DECL_RFK_WM(0x2008, 0x01ffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x0c1c, 0x00000004, 0x00000000), + RTW89_DECL_RFK_WM(0x0700, 0x08000000, 0x00000000), + RTW89_DECL_RFK_WM(0x0c70, 0x0000001f, 0x00000003), + RTW89_DECL_RFK_WM(0x0c70, 0x000003e0, 0x00000003), + RTW89_DECL_RFK_WM(0x12a0, 0x000ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x32a0, 0x000ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x0700, 0x07000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5864, 0x20000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7864, 0x20000000, 0x00000000), + RTW89_DECL_RFK_WM(0x0c3c, 0x00000200, 0x00000000), + RTW89_DECL_RFK_WM(0x2320, 0x00000001, 0x00000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58c8, 0x01000000, 0x00000000), + RTW89_DECL_RFK_WM(0x78c8, 0x01000000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_iqk_restore_defs_nondbcc_path01); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_iqk_restore_defs_nondbcc_path01); static const struct rtw89_reg5_def rtw8852a_rfk_iqk_restore_defs_dbcc_path0[] = { - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000101), - DECL_RFK_WM(0x12b8, 0x40000000, 0x00000000), - DECL_RFK_WM(0x5864, 0xc0000000, 0x00000000), - DECL_RFK_WM(0x2008, 0x01ffffff, 0x00000000), - DECL_RFK_WM(0x0c1c, 0x00000004, 0x00000000), - DECL_RFK_WM(0x0700, 0x08000000, 0x00000000), - DECL_RFK_WM(0x0c70, 0x0000001f, 0x00000003), - DECL_RFK_WM(0x0c70, 0x000003e0, 0x00000003), - DECL_RFK_WM(0x12a0, 0x000ff000, 0x00000000), - DECL_RFK_WM(0x0700, 0x07000000, 0x00000000), - DECL_RFK_WM(0x5864, 0x20000000, 0x00000000), - DECL_RFK_WM(0x0c3c, 0x00000200, 0x00000000), - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000000), - DECL_RFK_WM(0x58c8, 0x01000000, 0x00000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000101), + RTW89_DECL_RFK_WM(0x12b8, 0x40000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5864, 0xc0000000, 0x00000000), + RTW89_DECL_RFK_WM(0x2008, 0x01ffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x0c1c, 0x00000004, 0x00000000), + RTW89_DECL_RFK_WM(0x0700, 0x08000000, 0x00000000), + RTW89_DECL_RFK_WM(0x0c70, 0x0000001f, 0x00000003), + RTW89_DECL_RFK_WM(0x0c70, 0x000003e0, 0x00000003), + RTW89_DECL_RFK_WM(0x12a0, 0x000ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x0700, 0x07000000, 0x00000000), + RTW89_DECL_RFK_WM(0x5864, 0x20000000, 0x00000000), + RTW89_DECL_RFK_WM(0x0c3c, 0x00000200, 0x00000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000000), + RTW89_DECL_RFK_WM(0x58c8, 0x01000000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_iqk_restore_defs_dbcc_path0); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_iqk_restore_defs_dbcc_path0); static const struct rtw89_reg5_def rtw8852a_rfk_iqk_restore_defs_dbcc_path1[] = { - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000202), - DECL_RFK_WM(0x32b8, 0x40000000, 0x00000000), - DECL_RFK_WM(0x7864, 0xc0000000, 0x00000000), - DECL_RFK_WM(0x2008, 0x01ffffff, 0x00000000), - DECL_RFK_WM(0x2c1c, 0x00000004, 0x00000000), - DECL_RFK_WM(0x2700, 0x08000000, 0x00000000), - DECL_RFK_WM(0x0c70, 0x0000001f, 0x00000003), - DECL_RFK_WM(0x0c70, 0x000003e0, 0x00000003), - DECL_RFK_WM(0x32a0, 0x000ff000, 0x00000000), - DECL_RFK_WM(0x2700, 0x07000000, 0x00000000), - DECL_RFK_WM(0x7864, 0x20000000, 0x00000000), - DECL_RFK_WM(0x2c3c, 0x00000200, 0x00000000), - DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000000), - DECL_RFK_WM(0x78c8, 0x01000000, 0x00000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000202), + RTW89_DECL_RFK_WM(0x32b8, 0x40000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7864, 0xc0000000, 0x00000000), + RTW89_DECL_RFK_WM(0x2008, 0x01ffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x2c1c, 0x00000004, 0x00000000), + RTW89_DECL_RFK_WM(0x2700, 0x08000000, 0x00000000), + RTW89_DECL_RFK_WM(0x0c70, 0x0000001f, 0x00000003), + RTW89_DECL_RFK_WM(0x0c70, 0x000003e0, 0x00000003), + RTW89_DECL_RFK_WM(0x32a0, 0x000ff000, 0x00000000), + RTW89_DECL_RFK_WM(0x2700, 0x07000000, 0x00000000), + RTW89_DECL_RFK_WM(0x7864, 0x20000000, 0x00000000), + RTW89_DECL_RFK_WM(0x2c3c, 0x00000200, 0x00000000), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x00000000), + RTW89_DECL_RFK_WM(0x78c8, 0x01000000, 0x00000000), }; -DECLARE_RFK_TBL(rtw8852a_rfk_iqk_restore_defs_dbcc_path1); +RTW89_DECLARE_RFK_TBL(rtw8852a_rfk_iqk_restore_defs_dbcc_path1); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk_table.h b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk_table.h index 4a4a45d778ff..33e6c404ecf9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk_table.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk_table.h @@ -5,54 +5,7 @@ #ifndef __RTW89_8852A_RFK_TABLE_H__ #define __RTW89_8852A_RFK_TABLE_H__ -#include "core.h" - -enum rtw89_rfk_flag { - RTW89_RFK_F_WRF = 0, - RTW89_RFK_F_WM = 1, - RTW89_RFK_F_WS = 2, - RTW89_RFK_F_WC = 3, - RTW89_RFK_F_DELAY = 4, - RTW89_RFK_F_NUM, -}; - -struct rtw89_rfk_tbl { - const struct rtw89_reg5_def *defs; - u32 size; -}; - -#define DECLARE_RFK_TBL(_name) \ -const struct rtw89_rfk_tbl _name ## _tbl = { \ - .defs = _name, \ - .size = ARRAY_SIZE(_name), \ -} - -#define DECL_RFK_WRF(_path, _addr, _mask, _data) \ - {.flag = RTW89_RFK_F_WRF, \ - .path = _path, \ - .addr = _addr, \ - .mask = _mask, \ - .data = _data,} - -#define DECL_RFK_WM(_addr, _mask, _data) \ - {.flag = RTW89_RFK_F_WM, \ - .addr = _addr, \ - .mask = _mask, \ - .data = _data,} - -#define DECL_RFK_WS(_addr, _mask) \ - {.flag = RTW89_RFK_F_WS, \ - .addr = _addr, \ - .mask = _mask,} - -#define DECL_RFK_WC(_addr, _mask) \ - {.flag = RTW89_RFK_F_WC, \ - .addr = _addr, \ - .mask = _mask,} - -#define DECL_RFK_DELAY(_data) \ - {.flag = RTW89_RFK_F_DELAY, \ - .data = _data,} +#include "phy.h" extern const struct rtw89_rfk_tbl rtw8852a_tssi_sys_defs_tbl; extern const struct rtw89_rfk_tbl rtw8852a_tssi_sys_defs_2g_tbl; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c new file mode 100644 index 000000000000..48459aba441d --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2020-2021 Realtek Corporation + */ + +#include +#include + +#include "pci.h" +#include "rtw8852a.h" + +static const struct rtw89_pci_info rtw8852a_pci_info = { + .dma_addr_set = &rtw89_pci_ch_dma_addr_set, +}; + +static const struct rtw89_driver_info rtw89_8852ae_info = { + .chip = &rtw8852a_chip_info, + .bus = { + .pci = &rtw8852a_pci_info, + }, +}; + +static const struct pci_device_id rtw89_8852ae_id_table[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8852), + .driver_data = (kernel_ulong_t)&rtw89_8852ae_info, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xa85a), + .driver_data = (kernel_ulong_t)&rtw89_8852ae_info, + }, + {}, +}; +MODULE_DEVICE_TABLE(pci, rtw89_8852ae_id_table); + +static struct pci_driver rtw89_8852ae_driver = { + .name = "rtw89_8852ae", + .id_table = rtw89_8852ae_id_table, + .probe = rtw89_pci_probe, + .remove = rtw89_pci_remove, + .driver.pm = &rtw89_pm_ops, +}; +module_pci_driver(rtw89_8852ae_driver); + +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11ax wireless 8852AE driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c new file mode 100644 index 000000000000..58920e91765e --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2022 Realtek Corporation + */ + +#include "debug.h" +#include "fw.h" +#include "mac.h" +#include "phy.h" +#include "reg.h" +#include "rtw8852c.h" + +static const struct rtw89_dle_mem rtw8852c_dle_mem_pcie[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_wde_size19, &rtw89_ple_size19, + &rtw89_wde_qt18, &rtw89_wde_qt18, &rtw89_ple_qt46, + &rtw89_ple_qt47}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_wde_size18, + &rtw89_ple_size18, &rtw89_wde_qt17, &rtw89_wde_qt17, + &rtw89_ple_qt44, &rtw89_ple_qt45}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const u32 rtw8852c_h2c_regs[RTW89_H2CREG_MAX] = { + R_AX_H2CREG_DATA0_V1, R_AX_H2CREG_DATA1_V1, R_AX_H2CREG_DATA2_V1, + R_AX_H2CREG_DATA3_V1 +}; + +static const u32 rtw8852c_c2h_regs[RTW89_H2CREG_MAX] = { + R_AX_C2HREG_DATA0_V1, R_AX_C2HREG_DATA1_V1, R_AX_C2HREG_DATA2_V1, + R_AX_C2HREG_DATA3_V1 +}; + +static const struct rtw89_page_regs rtw8852c_page_regs = { + .hci_fc_ctrl = R_AX_HCI_FC_CTRL_V1, + .ch_page_ctrl = R_AX_CH_PAGE_CTRL_V1, + .ach_page_ctrl = R_AX_ACH0_PAGE_CTRL_V1, + .ach_page_info = R_AX_ACH0_PAGE_INFO_V1, + .pub_page_info3 = R_AX_PUB_PAGE_INFO3_V1, + .pub_page_ctrl1 = R_AX_PUB_PAGE_CTRL1_V1, + .pub_page_ctrl2 = R_AX_PUB_PAGE_CTRL2_V1, + .pub_page_info1 = R_AX_PUB_PAGE_INFO1_V1, + .pub_page_info2 = R_AX_PUB_PAGE_INFO2_V1, + .wp_page_ctrl1 = R_AX_WP_PAGE_CTRL1_V1, + .wp_page_ctrl2 = R_AX_WP_PAGE_CTRL2_V1, + .wp_page_info1 = R_AX_WP_PAGE_INFO1_V1, +}; + +static const struct rtw89_reg_def rtw8852c_dcfo_comp = { + R_DCFO_COMP_S0_V1, B_DCFO_COMP_S0_V1_MSK +}; + +static int rtw8852c_pwr_on_func(struct rtw89_dev *rtwdev) +{ + u32 val32; + u32 ret; + + val32 = rtw89_read32_mask(rtwdev, R_AX_SYS_STATUS1, B_AX_PAD_HCI_SEL_V2_MASK); + if (val32 == MAC_AX_HCI_SEL_PCIE_USB) + rtw89_write32_set(rtwdev, R_AX_LDO_AON_CTRL0, B_AX_PD_REGU_L); + + rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_AFSM_WLSUS_EN | + B_AX_AFSM_PCIE_SUS_EN); + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_DIS_WLBT_PDNSUSEN_SOPC); + rtw89_write32_set(rtwdev, R_AX_WLLPS_CTRL, B_AX_DIS_WLBT_LPSEN_LOPC); + rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APDM_HPDN); + rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_SWLPS); + + ret = read_poll_timeout(rtw89_read32, val32, val32 & B_AX_RDY_SYSPWR, + 1000, 20000, false, rtwdev, R_AX_SYS_PW_CTRL); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_EN_WLON); + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFN_ONMAC); + + ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_AX_APFN_ONMAC), + 1000, 20000, false, rtwdev, R_AX_SYS_PW_CTRL); + if (ret) + return ret; + + rtw89_write8_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); + rtw89_write8_clr(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); + rtw89_write8_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); + rtw89_write8_clr(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); + + rtw89_write8_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); + rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL, B_AX_PCIE_CALIB_EN_V1); + + rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND, B_AX_CMAC1_FEN); + rtw89_write32_set(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND, B_AX_R_SYM_ISO_CMAC12PP); + rtw89_write32_clr(rtwdev, R_AX_AFE_CTRL1, B_AX_R_SYM_WLCMAC1_P4_PC_EN | + B_AX_R_SYM_WLCMAC1_P3_PC_EN | + B_AX_R_SYM_WLCMAC1_P2_PC_EN | + B_AX_R_SYM_WLCMAC1_P1_PC_EN | + B_AX_R_SYM_WLCMAC1_PC_EN); + rtw89_write32_set(rtwdev, R_AX_SYS_ADIE_PAD_PWR_CTRL, B_AX_SYM_PADPDN_WL_PTA_1P3); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, + XTAL_SI_GND_SHDN_WL, XTAL_SI_GND_SHDN_WL); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_AX_SYS_ADIE_PAD_PWR_CTRL, B_AX_SYM_PADPDN_WL_RFC_1P3); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, + XTAL_SI_SHDN_WL, XTAL_SI_SHDN_WL); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_OFF_WEI, + XTAL_SI_OFF_WEI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_OFF_EI, + XTAL_SI_OFF_EI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_RFC2RF); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_PON_WEI, + XTAL_SI_PON_WEI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_PON_EI, + XTAL_SI_PON_EI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_SRAM2RFC); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_XTAL_XMD_2, 0, XTAL_SI_LDO_LPS); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_XTAL_XMD_4, 0, XTAL_SI_LPS_CAP); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK); + rtw89_write32_set(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_ISO_EB2CORE); + rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_PWC_EV2EF_B15); + + fsleep(1000); + + rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_PWC_EV2EF_B14); + rtw89_write32_clr(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK); + rtw89_write32_set(rtwdev, R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN, + B_AX_EECS_PULL_LOW_EN | B_AX_EESK_PULL_LOW_EN | + B_AX_LED1_PULL_LOW_EN); + + rtw89_write32_set(rtwdev, R_AX_DMAC_FUNC_EN, + B_AX_MAC_FUNC_EN | B_AX_DMAC_FUNC_EN | B_AX_MPDU_PROC_EN | + B_AX_WD_RLS_EN | B_AX_DLE_WDE_EN | B_AX_TXPKT_CTRL_EN | + B_AX_STA_SCH_EN | B_AX_DLE_PLE_EN | B_AX_PKT_BUF_EN | + B_AX_DMAC_TBL_EN | B_AX_PKT_IN_EN | B_AX_DLE_CPUIO_EN | + B_AX_DISPATCHER_EN | B_AX_BBRPT_EN | B_AX_MAC_SEC_EN | + B_AX_MAC_UN_EN | B_AX_H_AXIDMA_EN); + + rtw89_write32_set(rtwdev, R_AX_CMAC_FUNC_EN, + B_AX_CMAC_EN | B_AX_CMAC_TXEN | B_AX_CMAC_RXEN | + B_AX_FORCE_CMACREG_GCKEN | B_AX_PHYINTF_EN | + B_AX_CMAC_DMA_EN | B_AX_PTCLTOP_EN | B_AX_SCHEDULER_EN | + B_AX_TMAC_EN | B_AX_RMAC_EN); + + return 0; +} + +static int rtw8852c_pwr_off_func(struct rtw89_dev *rtwdev) +{ + u32 val32; + u32 ret; + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_RFC2RF, + XTAL_SI_RFC2RF); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_OFF_EI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_OFF_WEI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_WL_RFC_S0, 0, XTAL_SI_RF00); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_WL_RFC_S1, 0, XTAL_SI_RF10); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_SRAM2RFC, + XTAL_SI_SRAM2RFC); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_PON_EI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_PON_WEI); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_EN_WLON); + rtw89_write8_clr(rtwdev, R_AX_SYS_FUNC_EN, B_AX_FEN_BB_GLB_RSTN | B_AX_FEN_BBRSTB); + rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND, + B_AX_R_SYM_FEN_WLBBGLB_1 | B_AX_R_SYM_FEN_WLBBFUN_1); + rtw89_write32_clr(rtwdev, R_AX_SYS_ADIE_PAD_PWR_CTRL, B_AX_SYM_PADPDN_WL_RFC_1P3); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_SHDN_WL); + if (ret) + return ret; + + rtw89_write32_clr(rtwdev, R_AX_SYS_ADIE_PAD_PWR_CTRL, B_AX_SYM_PADPDN_WL_PTA_1P3); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_GND_SHDN_WL); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_OFFMAC); + + ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_AX_APFM_OFFMAC), + 1000, 20000, false, rtwdev, R_AX_SYS_PW_CTRL); + if (ret) + return ret; + + rtw89_write32(rtwdev, R_AX_WLLPS_CTRL, 0x0001A0B0); + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_XTAL_OFF_A_DIE); + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_SWLPS); + + return 0; +} + +static void rtw8852c_e_efuse_parsing(struct rtw89_efuse *efuse, + struct rtw8852c_efuse *map) +{ + ether_addr_copy(efuse->addr, map->e.mac_addr); + efuse->rfe_type = map->rfe_type; + efuse->xtal_cap = map->xtal_k; +} + +static void rtw8852c_efuse_parsing_tssi(struct rtw89_dev *rtwdev, + struct rtw8852c_efuse *map) +{ + struct rtw89_tssi_info *tssi = &rtwdev->tssi; + struct rtw8852c_tssi_offset *ofst[] = {&map->path_a_tssi, &map->path_b_tssi}; + u8 *bw40_1s_tssi_6g_ofst[] = {map->bw40_1s_tssi_6g_a, map->bw40_1s_tssi_6g_b}; + u8 i, j; + + tssi->thermal[RF_PATH_A] = map->path_a_therm; + tssi->thermal[RF_PATH_B] = map->path_b_therm; + + for (i = 0; i < RF_PATH_NUM_8852C; i++) { + memcpy(tssi->tssi_cck[i], ofst[i]->cck_tssi, + sizeof(ofst[i]->cck_tssi)); + + for (j = 0; j < TSSI_CCK_CH_GROUP_NUM; j++) + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][EFUSE] path=%d cck[%d]=0x%x\n", + i, j, tssi->tssi_cck[i][j]); + + memcpy(tssi->tssi_mcs[i], ofst[i]->bw40_tssi, + sizeof(ofst[i]->bw40_tssi)); + memcpy(tssi->tssi_mcs[i] + TSSI_MCS_2G_CH_GROUP_NUM, + ofst[i]->bw40_1s_tssi_5g, sizeof(ofst[i]->bw40_1s_tssi_5g)); + memcpy(tssi->tssi_6g_mcs[i], bw40_1s_tssi_6g_ofst[i], + sizeof(tssi->tssi_6g_mcs[i])); + + for (j = 0; j < TSSI_MCS_CH_GROUP_NUM; j++) + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][EFUSE] path=%d mcs[%d]=0x%x\n", + i, j, tssi->tssi_mcs[i][j]); + } +} + +static int rtw8852c_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map) +{ + struct rtw89_efuse *efuse = &rtwdev->efuse; + struct rtw8852c_efuse *map; + + map = (struct rtw8852c_efuse *)log_map; + + efuse->country_code[0] = map->country_code[0]; + efuse->country_code[1] = map->country_code[1]; + rtw8852c_efuse_parsing_tssi(rtwdev, map); + + switch (rtwdev->hci.type) { + case RTW89_HCI_TYPE_PCIE: + rtw8852c_e_efuse_parsing(efuse, map); + break; + default: + return -ENOTSUPP; + } + + rtw89_info(rtwdev, "chip rfe_type is %d\n", efuse->rfe_type); + + return 0; +} + +static void rtw8852c_phycap_parsing_tssi(struct rtw89_dev *rtwdev, u8 *phycap_map) +{ + struct rtw89_tssi_info *tssi = &rtwdev->tssi; + static const u32 tssi_trim_addr[RF_PATH_NUM_8852C] = {0x5D6, 0x5AB}; + static const u32 tssi_trim_addr_6g[RF_PATH_NUM_8852C] = {0x5CE, 0x5A3}; + u32 addr = rtwdev->chip->phycap_addr; + bool pg = false; + u32 ofst; + u8 i, j; + + for (i = 0; i < RF_PATH_NUM_8852C; i++) { + for (j = 0; j < TSSI_TRIM_CH_GROUP_NUM; j++) { + /* addrs are in decreasing order */ + ofst = tssi_trim_addr[i] - addr - j; + tssi->tssi_trim[i][j] = phycap_map[ofst]; + + if (phycap_map[ofst] != 0xff) + pg = true; + } + + for (j = 0; j < TSSI_TRIM_CH_GROUP_NUM_6G; j++) { + /* addrs are in decreasing order */ + ofst = tssi_trim_addr_6g[i] - addr - j; + tssi->tssi_trim_6g[i][j] = phycap_map[ofst]; + + if (phycap_map[ofst] != 0xff) + pg = true; + } + } + + if (!pg) { + memset(tssi->tssi_trim, 0, sizeof(tssi->tssi_trim)); + memset(tssi->tssi_trim_6g, 0, sizeof(tssi->tssi_trim_6g)); + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM] no PG, set all trim info to 0\n"); + } + + for (i = 0; i < RF_PATH_NUM_8852C; i++) + for (j = 0; j < TSSI_TRIM_CH_GROUP_NUM; j++) + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI] path=%d idx=%d trim=0x%x addr=0x%x\n", + i, j, tssi->tssi_trim[i][j], + tssi_trim_addr[i] - j); +} + +static void rtw8852c_phycap_parsing_thermal_trim(struct rtw89_dev *rtwdev, + u8 *phycap_map) +{ + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + static const u32 thm_trim_addr[RF_PATH_NUM_8852C] = {0x5DF, 0x5DC}; + u32 addr = rtwdev->chip->phycap_addr; + u8 i; + + for (i = 0; i < RF_PATH_NUM_8852C; i++) { + info->thermal_trim[i] = phycap_map[thm_trim_addr[i] - addr]; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[THERMAL][TRIM] path=%d thermal_trim=0x%x\n", + i, info->thermal_trim[i]); + + if (info->thermal_trim[i] != 0xff) + info->pg_thermal_trim = true; + } +} + +static void rtw8852c_thermal_trim(struct rtw89_dev *rtwdev) +{ +#define __thm_setting(raw) \ +({ \ + u8 __v = (raw); \ + ((__v & 0x1) << 3) | ((__v & 0x1f) >> 1); \ +}) + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + u8 i, val; + + if (!info->pg_thermal_trim) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[THERMAL][TRIM] no PG, do nothing\n"); + + return; + } + + for (i = 0; i < RF_PATH_NUM_8852C; i++) { + val = __thm_setting(info->thermal_trim[i]); + rtw89_write_rf(rtwdev, i, RR_TM2, RR_TM2_OFF, val); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[THERMAL][TRIM] path=%d thermal_setting=0x%x\n", + i, val); + } +#undef __thm_setting +} + +static void rtw8852c_phycap_parsing_pa_bias_trim(struct rtw89_dev *rtwdev, + u8 *phycap_map) +{ + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + static const u32 pabias_trim_addr[RF_PATH_NUM_8852C] = {0x5DE, 0x5DB}; + u32 addr = rtwdev->chip->phycap_addr; + u8 i; + + for (i = 0; i < RF_PATH_NUM_8852C; i++) { + info->pa_bias_trim[i] = phycap_map[pabias_trim_addr[i] - addr]; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[PA_BIAS][TRIM] path=%d pa_bias_trim=0x%x\n", + i, info->pa_bias_trim[i]); + + if (info->pa_bias_trim[i] != 0xff) + info->pg_pa_bias_trim = true; + } +} + +static void rtw8852c_pa_bias_trim(struct rtw89_dev *rtwdev) +{ + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + u8 pabias_2g, pabias_5g; + u8 i; + + if (!info->pg_pa_bias_trim) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[PA_BIAS][TRIM] no PG, do nothing\n"); + + return; + } + + for (i = 0; i < RF_PATH_NUM_8852C; i++) { + pabias_2g = FIELD_GET(GENMASK(3, 0), info->pa_bias_trim[i]); + pabias_5g = FIELD_GET(GENMASK(7, 4), info->pa_bias_trim[i]); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[PA_BIAS][TRIM] path=%d 2G=0x%x 5G=0x%x\n", + i, pabias_2g, pabias_5g); + + rtw89_write_rf(rtwdev, i, RR_BIASA, RR_BIASA_TXG, pabias_2g); + rtw89_write_rf(rtwdev, i, RR_BIASA, RR_BIASA_TXA, pabias_5g); + } +} + +static int rtw8852c_read_phycap(struct rtw89_dev *rtwdev, u8 *phycap_map) +{ + rtw8852c_phycap_parsing_tssi(rtwdev, phycap_map); + rtw8852c_phycap_parsing_thermal_trim(rtwdev, phycap_map); + rtw8852c_phycap_parsing_pa_bias_trim(rtwdev, phycap_map); + + return 0; +} + +static void rtw8852c_power_trim(struct rtw89_dev *rtwdev) +{ + rtw8852c_thermal_trim(rtwdev); + rtw8852c_pa_bias_trim(rtwdev); +} + +static +void rtw8852c_set_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev, + s8 pw_ofst, enum rtw89_mac_idx mac_idx) +{ + s8 pw_ofst_2tx; + s8 val_1t; + s8 val_2t; + u32 reg; + u8 i; + + if (pw_ofst < -32 || pw_ofst > 31) { + rtw89_warn(rtwdev, "[ULTB] Err pwr_offset=%d\n", pw_ofst); + return; + } + val_1t = pw_ofst << 2; + pw_ofst_2tx = max(pw_ofst - 3, -32); + val_2t = pw_ofst_2tx << 2; + + rtw89_debug(rtwdev, RTW89_DBG_TXPWR, "[ULTB] val_1tx=0x%x\n", val_1t); + rtw89_debug(rtwdev, RTW89_DBG_TXPWR, "[ULTB] val_2tx=0x%x\n", val_2t); + + for (i = 0; i < 4; i++) { + /* 1TX */ + reg = rtw89_mac_reg_by_idx(R_AX_PWR_UL_TB_1T, mac_idx); + rtw89_write32_mask(rtwdev, reg, + B_AX_PWR_UL_TB_1T_V1_MASK << (8 * i), + val_1t); + /* 2TX */ + reg = rtw89_mac_reg_by_idx(R_AX_PWR_UL_TB_2T, mac_idx); + rtw89_write32_mask(rtwdev, reg, + B_AX_PWR_UL_TB_2T_V1_MASK << (8 * i), + val_2t); + } +} + +static const struct rtw89_chip_ops rtw8852c_chip_ops = { + .read_efuse = rtw8852c_read_efuse, + .read_phycap = rtw8852c_read_phycap, + .power_trim = rtw8852c_power_trim, + .read_rf = rtw89_phy_read_rf_v1, + .write_rf = rtw89_phy_write_rf_v1, + .set_txpwr_ul_tb_offset = rtw8852c_set_txpwr_ul_tb_offset, + .pwr_on_func = rtw8852c_pwr_on_func, + .pwr_off_func = rtw8852c_pwr_off_func, + .cfg_ctrl_path = rtw89_mac_cfg_ctrl_path_v1, + .mac_cfg_gnt = rtw89_mac_cfg_gnt_v1, + .stop_sch_tx = rtw89_mac_stop_sch_tx_v1, + .resume_sch_tx = rtw89_mac_resume_sch_tx_v1, +}; + +const struct rtw89_chip_info rtw8852c_chip_info = { + .chip_id = RTL8852C, + .ops = &rtw8852c_chip_ops, + .fw_name = "rtw89/rtw8852c_fw.bin", + .dle_mem = rtw8852c_dle_mem_pcie, + .rf_base_addr = {0xe000, 0xf000}, + .pwr_on_seq = NULL, + .pwr_off_seq = NULL, + .sec_ctrl_efuse_size = 4, + .physical_efuse_size = 1216, + .logical_efuse_size = 2048, + .limit_efuse_size = 1280, + .dav_phy_efuse_size = 96, + .dav_log_efuse_size = 16, + .phycap_addr = 0x590, + .phycap_size = 0x60, + .hci_func_en_addr = R_AX_HCI_FUNC_EN_V1, + .h2c_ctrl_reg = R_AX_H2CREG_CTRL_V1, + .h2c_regs = rtw8852c_h2c_regs, + .c2h_ctrl_reg = R_AX_C2HREG_CTRL_V1, + .c2h_regs = rtw8852c_c2h_regs, + .page_regs = &rtw8852c_page_regs, + .dcfo_comp = &rtw8852c_dcfo_comp, + .dcfo_comp_sft = 5, +}; +EXPORT_SYMBOL(rtw8852c_chip_info); + +MODULE_FIRMWARE("rtw89/rtw8852c_fw.bin"); +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11ax wireless 8852C driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.h b/drivers/net/wireless/realtek/rtw89/rtw8852c.h new file mode 100644 index 000000000000..d0594716040b --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2019-2022 Realtek Corporation + */ + +#ifndef __RTW89_8852C_H__ +#define __RTW89_8852C_H__ + +#include "core.h" + +#define RF_PATH_NUM_8852C 2 + +struct rtw8852c_u_efuse { + u8 rsvd[0x38]; + u8 mac_addr[ETH_ALEN]; +}; + +struct rtw8852c_e_efuse { + u8 mac_addr[ETH_ALEN]; +}; + +struct rtw8852c_tssi_offset { + u8 cck_tssi[TSSI_CCK_CH_GROUP_NUM]; + u8 bw40_tssi[TSSI_MCS_2G_CH_GROUP_NUM]; + u8 rsvd[7]; + u8 bw40_1s_tssi_5g[TSSI_MCS_5G_CH_GROUP_NUM]; +} __packed; + +struct rtw8852c_efuse { + u8 rsvd[0x210]; + struct rtw8852c_tssi_offset path_a_tssi; + u8 rsvd1[10]; + struct rtw8852c_tssi_offset path_b_tssi; + u8 rsvd2[94]; + u8 channel_plan; + u8 xtal_k; + u8 rsvd3; + u8 iqk_lck; + u8 rsvd4[5]; + u8 reg_setting:2; + u8 tx_diversity:1; + u8 rx_diversity:2; + u8 ac_mode:1; + u8 module_type:2; + u8 rsvd5; + u8 shared_ant:1; + u8 coex_type:3; + u8 ant_iso:1; + u8 radio_on_off:1; + u8 rsvd6:2; + u8 eeprom_version; + u8 customer_id; + u8 tx_bb_swing_2g; + u8 tx_bb_swing_5g; + u8 tx_cali_pwr_trk_mode; + u8 trx_path_selection; + u8 rfe_type; + u8 country_code[2]; + u8 rsvd7[3]; + u8 path_a_therm; + u8 path_b_therm; + u8 rsvd8[46]; + u8 bw40_1s_tssi_6g_a[TSSI_MCS_6G_CH_GROUP_NUM]; + u8 rsvd9[10]; + u8 bw40_1s_tssi_6g_b[TSSI_MCS_6G_CH_GROUP_NUM]; + u8 rsvd10[110]; + u8 channel_plan_6g; + u8 rsvd11[71]; + union { + struct rtw8852c_u_efuse u; + struct rtw8852c_e_efuse e; + }; +} __packed; + +extern const struct rtw89_chip_info rtw8852c_chip_info; + +#endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c new file mode 100644 index 000000000000..e71370585b4d --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2020-2022 Realtek Corporation + */ + +#include +#include + +#include "pci.h" +#include "reg.h" +#include "rtw8852c.h" + +static const struct rtw89_pci_info rtw8852c_pci_info = { + .dma_addr_set = &rtw89_pci_ch_dma_addr_set_v1, +}; + +static const struct rtw89_driver_info rtw89_8852ce_info = { + .chip = &rtw8852c_chip_info, + .bus = { + .pci = &rtw8852c_pci_info, + }, +}; + +static const struct pci_device_id rtw89_8852ce_id_table[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xc852), + .driver_data = (kernel_ulong_t)&rtw89_8852ce_info, + }, + {}, +}; +MODULE_DEVICE_TABLE(pci, rtw89_8852ce_id_table); + +static struct pci_driver rtw89_8852ce_driver = { + .name = "rtw89_8852ce", + .id_table = rtw89_8852ce_id_table, + .probe = rtw89_pci_probe, + .remove = rtw89_pci_remove, + .driver.pm = &rtw89_pm_ops, +}; +module_pci_driver(rtw89_8852ce_driver); + +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11ax wireless 8852CE driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index 75b11249f306..86e3d8b400d6 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -31,6 +31,8 @@ #define RTW89_TXWD_BODY0_HDR_LLC_LEN GENMASK(15, 11) #define RTW89_TXWD_BODY0_WD_PAGE BIT(7) #define RTW89_TXWD_BODY0_HW_AMSDU BIT(5) +#define RTW89_TXWD_BODY0_HW_SSN_SEL GENMASK(3, 2) +#define RTW89_TXWD_BODY0_HW_SSN_MODE GENMASK(1, 0) /* TX WD BODY DWORD 1 */ #define RTW89_TXWD_BODY1_PAYLOAD_ID GENMASK(31, 16) @@ -56,6 +58,7 @@ #define RTW89_TXWD_INFO0_GI_LTF GENMASK(27, 25) #define RTW89_TXWD_INFO0_DATA_RATE GENMASK(24, 16) #define RTW89_TXWD_INFO0_DISDATAFB BIT(10) +#define RTW89_TXWD_INFO0_MULTIPORT_ID GENMASK(6, 4) /* TX WD INFO DWORD 1 */ #define RTW89_TXWD_INFO1_DATA_RTY_LOWEST_RATE GENMASK(24, 16) diff --git a/drivers/net/wireless/st/cw1200/queue.c b/drivers/net/wireless/st/cw1200/queue.c index 12952b1c29df..e06da4b3b0d4 100644 --- a/drivers/net/wireless/st/cw1200/queue.c +++ b/drivers/net/wireless/st/cw1200/queue.c @@ -8,6 +8,7 @@ #include #include +#include #include "queue.h" #include "cw1200.h" #include "debug.h" @@ -94,7 +95,7 @@ static void __cw1200_queue_gc(struct cw1200_queue *queue, bool wakeup_stats = false; list_for_each_entry_safe(item, tmp, &queue->queue, head) { - if (jiffies - item->queue_timestamp < queue->ttl) + if (time_is_after_jiffies(item->queue_timestamp + queue->ttl)) break; --queue->num_queued; --queue->link_map_cache[item->txpriv.link_id]; diff --git a/drivers/net/wireless/st/cw1200/wsm.c b/drivers/net/wireless/st/cw1200/wsm.c index 99624dd34886..5a3e7a626702 100644 --- a/drivers/net/wireless/st/cw1200/wsm.c +++ b/drivers/net/wireless/st/cw1200/wsm.c @@ -537,7 +537,7 @@ int wsm_set_tx_queue_params(struct cw1200_common *priv, { int ret; struct wsm_buf *buf = &priv->wsm_cmd_buf; - u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1}; + static const u8 queue_id_to_wmm_aci[] = { 3, 2, 0, 1 }; wsm_cmd_lock(priv); diff --git a/drivers/net/wireless/zydas/zd1201.c b/drivers/net/wireless/zydas/zd1201.c index e64e4e579518..82bc0d44212e 100644 --- a/drivers/net/wireless/zydas/zd1201.c +++ b/drivers/net/wireless/zydas/zd1201.c @@ -521,7 +521,7 @@ static int zd1201_setconfig(struct zd1201 *zd, int rid, const void *buf, int len zd->rxdatas = 0; zd->rxlen = 0; for (seq=0; len > 0; seq++) { - request = kmalloc(16, gfp_mask); + request = kzalloc(16, gfp_mask); if (!request) return -ENOMEM; urb = usb_alloc_urb(0, gfp_mask); @@ -529,7 +529,6 @@ static int zd1201_setconfig(struct zd1201 *zd, int rid, const void *buf, int len kfree(request); return -ENOMEM; } - memset(request, 0, 16); reqlen = len>12 ? 12 : len; request[0] = ZD1201_USB_RESREQ; request[1] = seq; diff --git a/drivers/net/wwan/iosm/iosm_ipc_debugfs.c b/drivers/net/wwan/iosm/iosm_ipc_debugfs.c index f2f57751a7d2..e916139b8cd4 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_debugfs.c +++ b/drivers/net/wwan/iosm/iosm_ipc_debugfs.c @@ -12,10 +12,10 @@ void ipc_debugfs_init(struct iosm_imem *ipc_imem) { - struct dentry *debugfs_pdev = wwan_get_debugfs_dir(ipc_imem->dev); + ipc_imem->debugfs_wwan_dir = wwan_get_debugfs_dir(ipc_imem->dev); ipc_imem->debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, - debugfs_pdev); + ipc_imem->debugfs_wwan_dir); ipc_imem->trace = ipc_trace_init(ipc_imem); if (!ipc_imem->trace) @@ -26,4 +26,5 @@ void ipc_debugfs_deinit(struct iosm_imem *ipc_imem) { ipc_trace_deinit(ipc_imem->trace); debugfs_remove_recursive(ipc_imem->debugfs_dir); + wwan_put_debugfs_dir(ipc_imem->debugfs_wwan_dir); } diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.c b/drivers/net/wwan/iosm/iosm_ipc_imem.c index f9e8e0ee4de3..1e6a47976642 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_imem.c +++ b/drivers/net/wwan/iosm/iosm_ipc_imem.c @@ -114,17 +114,35 @@ ipc_imem_fast_update_timer_cb(struct hrtimer *hr_timer) return HRTIMER_NORESTART; } +static int ipc_imem_tq_adb_timer_cb(struct iosm_imem *ipc_imem, int arg, + void *msg, size_t size) +{ + ipc_mux_ul_adb_finish(ipc_imem->mux); + return 0; +} + +static enum hrtimer_restart +ipc_imem_adb_timer_cb(struct hrtimer *hr_timer) +{ + struct iosm_imem *ipc_imem = + container_of(hr_timer, struct iosm_imem, adb_timer); + + ipc_task_queue_send_task(ipc_imem, ipc_imem_tq_adb_timer_cb, 0, + NULL, 0, false); + return HRTIMER_NORESTART; +} + static int ipc_imem_setup_cp_mux_cap_init(struct iosm_imem *ipc_imem, struct ipc_mux_config *cfg) { ipc_mmio_update_cp_capability(ipc_imem->mmio); - if (!ipc_imem->mmio->has_mux_lite) { + if (ipc_imem->mmio->mux_protocol == MUX_UNKNOWN) { dev_err(ipc_imem->dev, "Failed to get Mux capability."); return -EINVAL; } - cfg->protocol = MUX_LITE; + cfg->protocol = ipc_imem->mmio->mux_protocol; cfg->ul_flow = (ipc_imem->mmio->has_ul_flow_credit == 1) ? MUX_UL_ON_CREDITS : @@ -153,6 +171,10 @@ void ipc_imem_msg_send_feature_set(struct iosm_imem *ipc_imem, IPC_MSG_PREP_FEATURE_SET, &prep_args); } +/** + * ipc_imem_td_update_timer_start - Starts the TD Update Timer if not started. + * @ipc_imem: Pointer to imem data-struct + */ void ipc_imem_td_update_timer_start(struct iosm_imem *ipc_imem) { /* Use the TD update timer only in the runtime phase */ @@ -179,6 +201,21 @@ void ipc_imem_hrtimer_stop(struct hrtimer *hr_timer) hrtimer_cancel(hr_timer); } +/** + * ipc_imem_adb_timer_start - Starts the adb Timer if not starting. + * @ipc_imem: Pointer to imem data-struct + */ +void ipc_imem_adb_timer_start(struct iosm_imem *ipc_imem) +{ + if (!hrtimer_active(&ipc_imem->adb_timer)) { + ipc_imem->hrtimer_period = + ktime_set(0, IOSM_AGGR_MUX_ADB_FINISH_TIMEOUT_NSEC); + hrtimer_start(&ipc_imem->adb_timer, + ipc_imem->hrtimer_period, + HRTIMER_MODE_REL); + } +} + bool ipc_imem_ul_write_td(struct iosm_imem *ipc_imem) { struct ipc_mem_channel *channel; @@ -550,6 +587,11 @@ static void ipc_imem_run_state_worker(struct work_struct *instance) while (ctrl_chl_idx < IPC_MEM_MAX_CHANNELS) { if (!ipc_chnl_cfg_get(&chnl_cfg_port, ctrl_chl_idx)) { ipc_imem->ipc_port[ctrl_chl_idx] = NULL; + if (ipc_imem->pcie->pci->device == INTEL_CP_DEVICE_7360_ID && + chnl_cfg_port.wwan_port_type == WWAN_PORT_MBIM) { + ctrl_chl_idx++; + continue; + } if (chnl_cfg_port.wwan_port_type != WWAN_PORT_UNKNOWN) { ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL, chnl_cfg_port, @@ -680,8 +722,11 @@ static void ipc_imem_handle_irq(struct iosm_imem *ipc_imem, int irq) } /* Try to generate new ADB or ADGH. */ - if (ipc_mux_ul_data_encode(ipc_imem->mux)) + if (ipc_mux_ul_data_encode(ipc_imem->mux)) { ipc_imem_td_update_timer_start(ipc_imem); + if (ipc_imem->mux->protocol == MUX_AGGREGATION) + ipc_imem_adb_timer_start(ipc_imem); + } /* Continue the send procedure with accumulated SIO or NETIF packets. * Reset the debounce flags. @@ -1330,6 +1375,9 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id, HRTIMER_MODE_REL); ipc_imem->td_alloc_timer.function = ipc_imem_td_alloc_timer_cb; + hrtimer_init(&ipc_imem->adb_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ipc_imem->adb_timer.function = ipc_imem_adb_timer_cb; + if (ipc_imem_config(ipc_imem)) { dev_err(ipc_imem->dev, "failed to initialize the imem"); goto imem_config_fail; diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.h b/drivers/net/wwan/iosm/iosm_ipc_imem.h index 98554e9beb01..e700dc8bfe0a 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_imem.h +++ b/drivers/net/wwan/iosm/iosm_ipc_imem.h @@ -317,6 +317,7 @@ enum ipc_phase { * @tdupdate_timer: Delay the TD update doorbell. * @fast_update_timer: forced head pointer update delay timer. * @td_alloc_timer: Timer for DL pipe TD allocation retry + * @adb_timer: Timer for finishing the ADB. * @rom_exit_code: Mapped boot rom exit code. * @enter_runtime: 1 means the transition to runtime phase was * executed. @@ -340,6 +341,7 @@ enum ipc_phase { * @ev_mux_net_transmit_pending:0 means inform the IPC tasklet to pass * @reset_det_n: Reset detect flag * @pcie_wake_n: Pcie wake flag + * @debugfs_wwan_dir: WWAN Debug FS directory entry * @debugfs_dir: Debug FS directory for driver-specific entries */ struct iosm_imem { @@ -364,6 +366,7 @@ struct iosm_imem { struct hrtimer tdupdate_timer; struct hrtimer fast_update_timer; struct hrtimer td_alloc_timer; + struct hrtimer adb_timer; enum rom_exit_code rom_exit_code; u32 enter_runtime; struct completion ul_pend_sem; @@ -382,6 +385,7 @@ struct iosm_imem { reset_det_n:1, pcie_wake_n:1; #ifdef CONFIG_WWAN_DEBUGFS + struct dentry *debugfs_wwan_dir; struct dentry *debugfs_dir; #endif }; @@ -593,4 +597,7 @@ void ipc_imem_channel_init(struct iosm_imem *ipc_imem, enum ipc_ctype ctype, * Returns: 0 on success, -1 on failure */ int ipc_imem_devlink_trigger_chip_info(struct iosm_imem *ipc_imem); + +void ipc_imem_adb_timer_start(struct iosm_imem *ipc_imem); + #endif diff --git a/drivers/net/wwan/iosm/iosm_ipc_mmio.c b/drivers/net/wwan/iosm/iosm_ipc_mmio.c index f09e5e77a2a5..63eb08c43c05 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_mmio.c +++ b/drivers/net/wwan/iosm/iosm_ipc_mmio.c @@ -10,6 +10,7 @@ #include #include "iosm_ipc_mmio.h" +#include "iosm_ipc_mux.h" /* Definition of MMIO offsets * note that MMIO_CI offsets are relative to end of chip info structure @@ -71,8 +72,9 @@ void ipc_mmio_update_cp_capability(struct iosm_mmio *ipc_mmio) ver = ipc_mmio_get_cp_version(ipc_mmio); cp_cap = ioread32(ipc_mmio->base + ipc_mmio->offset.cp_capability); - ipc_mmio->has_mux_lite = (ver >= IOSM_CP_VERSION) && - !(cp_cap & DL_AGGR) && !(cp_cap & UL_AGGR); + ipc_mmio->mux_protocol = ((ver >= IOSM_CP_VERSION) && (cp_cap & + (UL_AGGR | DL_AGGR))) ? MUX_AGGREGATION + : MUX_LITE; ipc_mmio->has_ul_flow_credit = (ver >= IOSM_CP_VERSION) && (cp_cap & UL_FLOW_CREDIT); diff --git a/drivers/net/wwan/iosm/iosm_ipc_mmio.h b/drivers/net/wwan/iosm/iosm_ipc_mmio.h index f861994a6d90..193d7ba2478a 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_mmio.h +++ b/drivers/net/wwan/iosm/iosm_ipc_mmio.h @@ -72,7 +72,7 @@ struct mmio_offset { * @context_info_addr: Physical base address of context info structure * @chip_info_version: Version of chip info structure * @chip_info_size: Size of chip info structure - * @has_mux_lite: It doesn't support mux aggergation + * @mux_protocol: mux protocol * @has_ul_flow_credit: Ul flow credit support * @has_slp_no_prot: Device sleep no protocol support * @has_mcr_support: Usage of mcr support @@ -84,8 +84,8 @@ struct iosm_mmio { phys_addr_t context_info_addr; unsigned int chip_info_version; unsigned int chip_info_size; - u8 has_mux_lite:1, - has_ul_flow_credit:1, + u32 mux_protocol; + u8 has_ul_flow_credit:1, has_slp_no_prot:1, has_mcr_support:1; }; diff --git a/drivers/net/wwan/iosm/iosm_ipc_mux.c b/drivers/net/wwan/iosm/iosm_ipc_mux.c index 8e66ffe92055..9c7a9a2a1f25 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_mux.c +++ b/drivers/net/wwan/iosm/iosm_ipc_mux.c @@ -279,9 +279,10 @@ struct iosm_mux *ipc_mux_init(struct ipc_mux_config *mux_cfg, struct iosm_imem *imem) { struct iosm_mux *ipc_mux = kzalloc(sizeof(*ipc_mux), GFP_KERNEL); - int i, ul_tds, ul_td_size; + int i, j, ul_tds, ul_td_size; struct sk_buff_head *free_list; struct sk_buff *skb; + int qlt_size; if (!ipc_mux) return NULL; @@ -321,6 +322,24 @@ struct iosm_mux *ipc_mux_init(struct ipc_mux_config *mux_cfg, ipc_mux->channel_id = -1; ipc_mux->channel = NULL; + if (ipc_mux->protocol != MUX_LITE) { + qlt_size = offsetof(struct mux_qlth, ql) + + MUX_QUEUE_LEVEL * sizeof(struct mux_qlth_ql); + + for (i = 0; i < IPC_MEM_MUX_IP_SESSION_ENTRIES; i++) { + ipc_mux->ul_adb.pp_qlt[i] = kzalloc(qlt_size, + GFP_ATOMIC); + if (!ipc_mux->ul_adb.pp_qlt[i]) { + for (j = i - 1; j >= 0; j--) + kfree(ipc_mux->ul_adb.pp_qlt[j]); + return NULL; + } + } + + ul_td_size = IPC_MEM_MAX_UL_ADB_BUF_SIZE; + ul_tds = IPC_MEM_MAX_TDS_MUX_AGGR_UL; + } + /* Allocate the list of UL ADB. */ for (i = 0; i < ul_tds; i++) { dma_addr_t mapping; diff --git a/drivers/net/wwan/iosm/iosm_ipc_mux.h b/drivers/net/wwan/iosm/iosm_ipc_mux.h index 88debaa1ed31..cd9d74cc097f 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_mux.h +++ b/drivers/net/wwan/iosm/iosm_ipc_mux.h @@ -8,9 +8,12 @@ #include "iosm_ipc_protocol.h" -/* Size of the buffer for the IP MUX data buffer. */ -#define IPC_MEM_MAX_DL_MUX_BUF_SIZE (16 * 1024) -#define IPC_MEM_MAX_UL_ADB_BUF_SIZE IPC_MEM_MAX_DL_MUX_BUF_SIZE +#define IPC_MEM_MAX_UL_DG_ENTRIES 100 +#define IPC_MEM_MAX_TDS_MUX_AGGR_UL 60 + +#define IPC_MEM_MAX_ADB_BUF_SIZE (16 * 1024) +#define IPC_MEM_MAX_UL_ADB_BUF_SIZE IPC_MEM_MAX_ADB_BUF_SIZE +#define IPC_MEM_MAX_DL_ADB_BUF_SIZE IPC_MEM_MAX_ADB_BUF_SIZE /* Size of the buffer for the IP MUX Lite data buffer. */ #define IPC_MEM_MAX_DL_MUX_LITE_BUF_SIZE (2 * 1024) @@ -167,6 +170,7 @@ enum mux_state { enum ipc_mux_protocol { MUX_UNKNOWN, MUX_LITE, + MUX_AGGREGATION, }; /* Supported UL data transfer methods. */ @@ -192,24 +196,111 @@ struct mux_session { flush:1; /* flush net interface ? */ }; -/* State of a single UL data block. */ -struct mux_adb { - struct sk_buff *dest_skb; /* Current UL skb for the data block. */ - u8 *buf; /* ADB memory. */ - struct mux_adgh *adgh; /* ADGH pointer */ - struct sk_buff *qlth_skb; /* QLTH pointer */ - u32 *next_table_index; /* Pointer to next table index. */ - struct sk_buff_head free_list; /* List of alloc. ADB for the UL sess.*/ - int size; /* Size of the ADB memory. */ - u32 if_cnt; /* Statistic counter */ - u32 dg_cnt_total; - u32 payload_size; +/** + * struct mux_adth_dg - Structure of the datagram in the Aggregated Datagram + * Table Header. + * @datagram_index : Index (in bytes) to the k-th datagram in the table. + * Index shall count from the start of the block including + * the 16-byte header. This value shall be non-zero. + * @datagram_length: Length of the k-th datagram including the head padding. + * This value shall be non-zero. + * @service_class: Service class identifier for the datagram. + * @reserved: Reserved bytes. Set to zero + */ +struct mux_adth_dg { + __le32 datagram_index; + __le16 datagram_length; + u8 service_class; + u8 reserved; }; -/* Temporary ACB state. */ +/** + * struct mux_qlth_ql - Structure of the queue level in the Aggregated + * Datagram Queue Level Table Header. + * @nr_of_bytes: Number of bytes available to transmit in the queue. + */ +struct mux_qlth_ql { + __le32 nr_of_bytes; +}; + +/** + * struct mux_qlth - Structure of Aggregated Datagram Queue Level Table + * Header. + * @signature: Signature of the Queue Level Table Header + * Value: 0x48544C51 (ASCII characters: 'Q' 'L' 'T' 'H') + * @table_length: Length (in bytes) of the datagram table. This length + * shall include the queue level table header size. + * Minimum value:0x10 + * @if_id: ID of the interface the queue levels in the table + * belong to. + * @reserved: Reserved byte. Set to zero. + * @next_table_index: Index (in bytes) to the next table in the buffer. Index + * shall count from the start of the block including the + * 16-byte header. Value of zero indicates end of the list. + * @reserved2: Reserved bytes. Set to zero + * @ql: Queue level table with variable length + */ +struct mux_qlth { + __le32 signature; + __le16 table_length; + u8 if_id; + u8 reserved; + __le32 next_table_index; + __le32 reserved2; + struct mux_qlth_ql ql; +}; + +/** + * struct mux_adb - Structure of State of a single UL data block. + * @dest_skb: Current UL skb for the data block. + * @buf: ADB memory + * @adgh: ADGH pointer + * @qlth_skb: QLTH pointer + * @next_table_index: Pointer to next table index. + * @free_list: List of alloc. ADB for the UL sess. + * @size: Size of the ADB memory. + * @if_cnt: Statistic counter + * @dg_cnt_total: Datagram count total + * @payload_size: Payload Size + * @dg: Datagram table. + * @pp_qlt: Pointers to hold Queue Level Tables of session + * @adbh: ADBH pointer + * @qlt_updated: Queue level table updated + * @dg_count: Datagram count + */ +struct mux_adb { + struct sk_buff *dest_skb; + u8 *buf; + struct mux_adgh *adgh; + struct sk_buff *qlth_skb; + u32 *next_table_index; + struct sk_buff_head free_list; + int size; + u32 if_cnt; + u32 dg_cnt_total; + u32 payload_size; + struct mux_adth_dg + dg[IPC_MEM_MUX_IP_SESSION_ENTRIES][IPC_MEM_MAX_UL_DG_ENTRIES]; + struct mux_qlth *pp_qlt[IPC_MEM_MUX_IP_SESSION_ENTRIES]; + struct mux_adbh *adbh; + u32 qlt_updated[IPC_MEM_MUX_IP_SESSION_ENTRIES]; + u32 dg_count[IPC_MEM_MUX_IP_SESSION_ENTRIES]; +}; + +/** + * struct mux_acb - Structure of Temporary ACB state. + * @skb: Used UL skb. + * @if_id: Session id. + * @buf_p: Command buffer. + * @wanted_response: Wanted Response + * @got_response: Got response + * @cmd: command + * @got_param: Received command/response parameter + */ struct mux_acb { struct sk_buff *skb; /* Used UL skb. */ int if_id; /* Session id. */ + u8 *buf_p; u32 wanted_response; u32 got_response; u32 cmd; @@ -241,6 +332,12 @@ struct mux_acb { * @wwan_q_offset: This will hold the offset of the given instance * Useful while passing or receiving packets from * wwan/imem layer. + * @adb_finish_timer: Timer for forcefully finishing the ADB + * @acb_tx_sequence_nr: Sequence number for the ACB header. + * @params: user configurable parameters + * @adb_tx_sequence_nr: Sequence number for ADB header + * @acc_adb_size: Statistic data for logging + * @acc_payload_size: Statistic data for logging * @initialized: MUX object is initialized * @ev_mux_net_transmit_pending: * 0 means inform the IPC tasklet to pass the @@ -269,10 +366,16 @@ struct iosm_mux { long long ul_data_pend_bytes; struct mux_acb acb; int wwan_q_offset; + struct hrtimer adb_finish_timer; + u16 acb_tx_sequence_nr; + struct ipc_params *params; + u16 adb_tx_sequence_nr; + unsigned long long acc_adb_size; + unsigned long long acc_payload_size; u8 initialized:1, ev_mux_net_transmit_pending:1, - adb_prep_ongoing:1; -}; + adb_prep_ongoing; +} __packed; /* MUX configuration structure */ struct ipc_mux_config { diff --git a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c index 40fb54a0513e..d41e373f9c0a 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c +++ b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c @@ -54,6 +54,49 @@ static int ipc_mux_acb_send(struct iosm_mux *ipc_mux, bool blocking) return 0; } +/* Initialize the command header. */ +static void ipc_mux_acb_init(struct iosm_mux *ipc_mux) +{ + struct mux_acb *acb = &ipc_mux->acb; + struct mux_acbh *header; + + header = (struct mux_acbh *)(acb->skb)->data; + header->block_length = cpu_to_le32(sizeof(struct mux_acbh)); + header->first_cmd_index = header->block_length; + header->signature = cpu_to_le32(IOSM_AGGR_MUX_SIG_ACBH); + header->sequence_nr = cpu_to_le16(ipc_mux->acb_tx_sequence_nr++); +} + +/* Add a command to the ACB. */ +static struct mux_cmdh *ipc_mux_acb_add_cmd(struct iosm_mux *ipc_mux, u32 cmd, + void *param, u32 param_size) +{ + struct mux_acbh *header; + struct mux_cmdh *cmdh; + struct mux_acb *acb; + + acb = &ipc_mux->acb; + header = (struct mux_acbh *)(acb->skb)->data; + cmdh = (struct mux_cmdh *) + ((acb->skb)->data + le32_to_cpu(header->block_length)); + + cmdh->signature = cpu_to_le32(MUX_SIG_CMDH); + cmdh->command_type = cpu_to_le32(cmd); + cmdh->if_id = acb->if_id; + + acb->cmd = cmd; + cmdh->cmd_len = cpu_to_le16(offsetof(struct mux_cmdh, param) + + param_size); + cmdh->transaction_id = cpu_to_le32(ipc_mux->tx_transaction_id++); + if (param) + memcpy(&cmdh->param, param, param_size); + + skb_put(acb->skb, le32_to_cpu(header->block_length) + + le16_to_cpu(cmdh->cmd_len)); + + return cmdh; +} + /* Prepare mux Command */ static struct mux_lite_cmdh *ipc_mux_lite_add_cmd(struct iosm_mux *ipc_mux, u32 cmd, struct mux_acb *acb, @@ -104,7 +147,7 @@ int ipc_mux_dl_acb_send_cmds(struct iosm_mux *ipc_mux, u32 cmd_type, u8 if_id, size_t res_size, bool blocking, bool respond) { struct mux_acb *acb = &ipc_mux->acb; - struct mux_lite_cmdh *ack_lite; + union mux_type_cmdh cmdh; int ret = 0; acb->if_id = if_id; @@ -112,11 +155,23 @@ int ipc_mux_dl_acb_send_cmds(struct iosm_mux *ipc_mux, u32 cmd_type, u8 if_id, if (ret) return ret; - ack_lite = ipc_mux_lite_add_cmd(ipc_mux, cmd_type, acb, param, - res_size); - if (respond) - ack_lite->transaction_id = cpu_to_le32(transaction_id); + if (ipc_mux->protocol == MUX_LITE) { + cmdh.ack_lite = ipc_mux_lite_add_cmd(ipc_mux, cmd_type, acb, + param, res_size); + if (respond) + cmdh.ack_lite->transaction_id = + cpu_to_le32(transaction_id); + } else { + /* Initialize the ACB header. */ + ipc_mux_acb_init(ipc_mux); + cmdh.ack_aggr = ipc_mux_acb_add_cmd(ipc_mux, cmd_type, param, + res_size); + + if (respond) + cmdh.ack_aggr->transaction_id = + cpu_to_le32(transaction_id); + } ret = ipc_mux_acb_send(ipc_mux, blocking); return ret; @@ -129,15 +184,17 @@ void ipc_mux_netif_tx_flowctrl(struct mux_session *session, int idx, bool on) } static int ipc_mux_dl_cmdresps_decode_process(struct iosm_mux *ipc_mux, - struct mux_lite_cmdh *cmdh) + union mux_cmd_param param, + __le32 command_type, u8 if_id, + __le32 transaction_id) { struct mux_acb *acb = &ipc_mux->acb; - switch (le32_to_cpu(cmdh->command_type)) { + switch (le32_to_cpu(command_type)) { case MUX_CMD_OPEN_SESSION_RESP: case MUX_CMD_CLOSE_SESSION_RESP: /* Resume the control application. */ - acb->got_param = cmdh->param; + acb->got_param = param; break; case MUX_LITE_CMD_FLOW_CTL_ACK: @@ -147,8 +204,16 @@ static int ipc_mux_dl_cmdresps_decode_process(struct iosm_mux *ipc_mux, if (ipc_mux->protocol != MUX_LITE) return -EINVAL; - dev_dbg(ipc_mux->dev, "if %u FLOW_CTL_ACK %u received", - cmdh->if_id, le32_to_cpu(cmdh->transaction_id)); + dev_dbg(ipc_mux->dev, "if_id %u FLOW_CTL_ACK %u received", + if_id, le32_to_cpu(transaction_id)); + break; + + case IOSM_AGGR_MUX_CMD_FLOW_CTL_ACK: + /* This command type is not expected as response for + * Lite version of the protocol. So return non-zero. + */ + if (ipc_mux->protocol == MUX_LITE) + return -EINVAL; break; default: @@ -156,38 +221,39 @@ static int ipc_mux_dl_cmdresps_decode_process(struct iosm_mux *ipc_mux, } acb->wanted_response = MUX_CMD_INVALID; - acb->got_response = le32_to_cpu(cmdh->command_type); + acb->got_response = le32_to_cpu(command_type); complete(&ipc_mux->channel->ul_sem); return 0; } -static int ipc_mux_dl_dlcmds_decode_process(struct iosm_mux *ipc_mux, - struct mux_lite_cmdh *cmdh) +static int ipc_mux_dl_cmds_decode_process(struct iosm_mux *ipc_mux, + union mux_cmd_param *param, + __le32 command_type, u8 if_id, + __le16 cmd_len, int size) { - union mux_cmd_param *param = &cmdh->param; struct mux_session *session; - int new_size; + struct hrtimer *adb_timer; dev_dbg(ipc_mux->dev, "if_id[%d]: dlcmds decode process %d", - cmdh->if_id, le32_to_cpu(cmdh->command_type)); + if_id, le32_to_cpu(command_type)); - switch (le32_to_cpu(cmdh->command_type)) { + switch (le32_to_cpu(command_type)) { case MUX_LITE_CMD_FLOW_CTL: + case IOSM_AGGR_MUX_CMD_FLOW_CTL_DISABLE: - if (cmdh->if_id >= IPC_MEM_MUX_IP_SESSION_ENTRIES) { + if (if_id >= IPC_MEM_MUX_IP_SESSION_ENTRIES) { dev_err(ipc_mux->dev, "if_id [%d] not valid", - cmdh->if_id); + if_id); return -EINVAL; /* No session interface id. */ } - session = &ipc_mux->session[cmdh->if_id]; + session = &ipc_mux->session[if_id]; + adb_timer = &ipc_mux->imem->adb_timer; - new_size = offsetof(struct mux_lite_cmdh, param) + - sizeof(param->flow_ctl); if (param->flow_ctl.mask == cpu_to_le32(0xFFFFFFFF)) { /* Backward Compatibility */ - if (cmdh->cmd_len == cpu_to_le16(new_size)) + if (cmd_len == cpu_to_le16(size)) session->flow_ctl_mask = le32_to_cpu(param->flow_ctl.mask); else @@ -197,6 +263,16 @@ static int ipc_mux_dl_dlcmds_decode_process(struct iosm_mux *ipc_mux, * to limit uplink session queueing */ session->net_tx_stop = true; + + /* We have to call Finish ADB here. + * Otherwise any already queued data + * will be sent to CP when ADB is full + * for some other sessions. + */ + if (ipc_mux->protocol == MUX_AGGREGATION) { + ipc_mux_ul_adb_finish(ipc_mux); + ipc_imem_hrtimer_stop(adb_timer); + } /* Update the stats */ session->flow_ctl_en_cnt++; } else if (param->flow_ctl.mask == 0) { @@ -205,8 +281,10 @@ static int ipc_mux_dl_dlcmds_decode_process(struct iosm_mux *ipc_mux, * our internal Tx flag and enabling kernel * flow control */ + dev_dbg(ipc_mux->dev, "if_id[%u] flow_ctl mask 0x%08X", + if_id, le32_to_cpu(param->flow_ctl.mask)); /* Backward Compatibility */ - if (cmdh->cmd_len == cpu_to_le16(new_size)) + if (cmd_len == cpu_to_le16(size)) session->flow_ctl_mask = le32_to_cpu(param->flow_ctl.mask); else @@ -217,7 +295,10 @@ static int ipc_mux_dl_dlcmds_decode_process(struct iosm_mux *ipc_mux, break; } - dev_dbg(ipc_mux->dev, "if[%u] FLOW CTRL 0x%08X", cmdh->if_id, + ipc_mux->acc_adb_size = 0; + ipc_mux->acc_payload_size = 0; + + dev_dbg(ipc_mux->dev, "if_id[%u] FLOW CTRL 0x%08X", if_id, le32_to_cpu(param->flow_ctl.mask)); break; @@ -235,12 +316,20 @@ static void ipc_mux_dl_cmd_decode(struct iosm_mux *ipc_mux, struct sk_buff *skb) { struct mux_lite_cmdh *cmdh = (struct mux_lite_cmdh *)skb->data; __le32 trans_id = cmdh->transaction_id; + int size; - if (ipc_mux_dl_cmdresps_decode_process(ipc_mux, cmdh)) { + if (ipc_mux_dl_cmdresps_decode_process(ipc_mux, cmdh->param, + cmdh->command_type, cmdh->if_id, + cmdh->transaction_id)) { /* Unable to decode command response indicates the cmd_type * may be a command instead of response. So try to decoding it. */ - if (!ipc_mux_dl_dlcmds_decode_process(ipc_mux, cmdh)) { + size = offsetof(struct mux_lite_cmdh, param) + + sizeof(cmdh->param.flow_ctl); + if (!ipc_mux_dl_cmds_decode_process(ipc_mux, &cmdh->param, + cmdh->command_type, + cmdh->if_id, + cmdh->cmd_len, size)) { /* Decoded command may need a response. Give the * response according to the command type. */ @@ -349,7 +438,7 @@ static void ipc_mux_dl_adgh_decode(struct iosm_mux *ipc_mux, adgh = (struct mux_adgh *)block; - if (adgh->signature != cpu_to_le32(MUX_SIG_ADGH)) { + if (adgh->signature != cpu_to_le32(IOSM_AGGR_MUX_SIG_ADGH)) { dev_err(ipc_mux->dev, "invalid ADGH signature received"); return; } @@ -392,6 +481,192 @@ static void ipc_mux_dl_adgh_decode(struct iosm_mux *ipc_mux, ipc_mux->session[if_id].flush = 1; } +static void ipc_mux_dl_acbcmd_decode(struct iosm_mux *ipc_mux, + struct mux_cmdh *cmdh, int size) +{ + u32 link_st = IOSM_AGGR_MUX_CMD_LINK_STATUS_REPORT_RESP; + u32 fctl_dis = IOSM_AGGR_MUX_CMD_FLOW_CTL_DISABLE; + u32 fctl_ena = IOSM_AGGR_MUX_CMD_FLOW_CTL_ENABLE; + u32 fctl_ack = IOSM_AGGR_MUX_CMD_FLOW_CTL_ACK; + union mux_cmd_param *cmd_p = NULL; + u32 cmd = link_st; + u32 trans_id; + + if (!ipc_mux_dl_cmds_decode_process(ipc_mux, &cmdh->param, + cmdh->command_type, cmdh->if_id, + cmdh->cmd_len, size)) { + size = 0; + if (cmdh->command_type == cpu_to_le32(link_st)) { + cmd_p = &cmdh->param; + cmd_p->link_status_resp.response = MUX_CMD_RESP_SUCCESS; + } else if ((cmdh->command_type == cpu_to_le32(fctl_ena)) || + (cmdh->command_type == cpu_to_le32(fctl_dis))) { + cmd = fctl_ack; + } else { + return; + } + trans_id = le32_to_cpu(cmdh->transaction_id); + ipc_mux_dl_acb_send_cmds(ipc_mux, cmd, cmdh->if_id, + trans_id, cmd_p, size, false, true); + } +} + +/* Decode an aggregated command block. */ +static void ipc_mux_dl_acb_decode(struct iosm_mux *ipc_mux, struct sk_buff *skb) +{ + struct mux_acbh *acbh; + struct mux_cmdh *cmdh; + u32 next_cmd_index; + u8 *block; + int size; + + acbh = (struct mux_acbh *)(skb->data); + block = (u8 *)(skb->data); + + next_cmd_index = le32_to_cpu(acbh->first_cmd_index); + next_cmd_index = array_index_nospec(next_cmd_index, + sizeof(struct mux_cmdh)); + + while (next_cmd_index != 0) { + cmdh = (struct mux_cmdh *)&block[next_cmd_index]; + next_cmd_index = le32_to_cpu(cmdh->next_cmd_index); + if (ipc_mux_dl_cmdresps_decode_process(ipc_mux, cmdh->param, + cmdh->command_type, + cmdh->if_id, + cmdh->transaction_id)) { + size = offsetof(struct mux_cmdh, param) + + sizeof(cmdh->param.flow_ctl); + ipc_mux_dl_acbcmd_decode(ipc_mux, cmdh, size); + } + } +} + +/* process datagram */ +static int mux_dl_process_dg(struct iosm_mux *ipc_mux, struct mux_adbh *adbh, + struct mux_adth_dg *dg, struct sk_buff *skb, + int if_id, int nr_of_dg) +{ + u32 dl_head_pad_len = ipc_mux->session[if_id].dl_head_pad_len; + u32 packet_offset, i, rc; + + for (i = 0; i < nr_of_dg; i++, dg++) { + if (le32_to_cpu(dg->datagram_index) + < sizeof(struct mux_adbh)) + goto dg_error; + + /* Is the packet inside of the ADB */ + if (le32_to_cpu(dg->datagram_index) >= + le32_to_cpu(adbh->block_length)) { + goto dg_error; + } else { + packet_offset = + le32_to_cpu(dg->datagram_index) + + dl_head_pad_len; + /* Pass the packet to the netif layer. */ + rc = ipc_mux_net_receive(ipc_mux, if_id, ipc_mux->wwan, + packet_offset, + dg->service_class, + skb); + if (rc) + goto dg_error; + } + } + return 0; +dg_error: + return -1; +} + +/* Decode an aggregated data block. */ +static void mux_dl_adb_decode(struct iosm_mux *ipc_mux, + struct sk_buff *skb) +{ + struct mux_adth_dg *dg; + struct iosm_wwan *wwan; + struct mux_adbh *adbh; + struct mux_adth *adth; + int nr_of_dg, if_id; + u32 adth_index; + u8 *block; + + block = skb->data; + adbh = (struct mux_adbh *)block; + + /* Process the aggregated datagram tables. */ + adth_index = le32_to_cpu(adbh->first_table_index); + + /* Has CP sent an empty ADB ? */ + if (adth_index < 1) { + dev_err(ipc_mux->dev, "unexpected empty ADB"); + goto adb_decode_err; + } + + /* Loop through mixed session tables. */ + while (adth_index) { + /* Get the reference to the table header. */ + adth = (struct mux_adth *)(block + adth_index); + + /* Get the interface id and map it to the netif id. */ + if_id = adth->if_id; + if (if_id >= IPC_MEM_MUX_IP_SESSION_ENTRIES) + goto adb_decode_err; + + if_id = array_index_nospec(if_id, + IPC_MEM_MUX_IP_SESSION_ENTRIES); + + /* Is the session active ? */ + wwan = ipc_mux->session[if_id].wwan; + if (!wwan) + goto adb_decode_err; + + /* Consistency checks for aggregated datagram table. */ + if (adth->signature != cpu_to_le32(IOSM_AGGR_MUX_SIG_ADTH)) + goto adb_decode_err; + + if (le16_to_cpu(adth->table_length) < (sizeof(struct mux_adth) - + sizeof(struct mux_adth_dg))) + goto adb_decode_err; + + /* Calculate the number of datagrams. */ + nr_of_dg = (le16_to_cpu(adth->table_length) - + sizeof(struct mux_adth) + + sizeof(struct mux_adth_dg)) / + sizeof(struct mux_adth_dg); + + /* Is the datagram table empty ? */ + if (nr_of_dg < 1) { + dev_err(ipc_mux->dev, + "adthidx=%u,nr_of_dg=%d,next_tblidx=%u", + adth_index, nr_of_dg, + le32_to_cpu(adth->next_table_index)); + + /* Move to the next aggregated datagram table. */ + adth_index = le32_to_cpu(adth->next_table_index); + continue; + } + + /* New aggregated datagram table. */ + dg = &adth->dg; + if (mux_dl_process_dg(ipc_mux, adbh, dg, skb, if_id, + nr_of_dg) < 0) + goto adb_decode_err; + + /* mark session for final flush */ + ipc_mux->session[if_id].flush = 1; + + /* Move to the next aggregated datagram table. */ + adth_index = le32_to_cpu(adth->next_table_index); + } + +adb_decode_err: + return; +} + +/** + * ipc_mux_dl_decode - Route the DL packet through the IP MUX layer + * depending on Header. + * @ipc_mux: Pointer to MUX data-struct + * @skb: Pointer to ipc_skb. + */ void ipc_mux_dl_decode(struct iosm_mux *ipc_mux, struct sk_buff *skb) { u32 signature; @@ -403,14 +678,18 @@ void ipc_mux_dl_decode(struct iosm_mux *ipc_mux, struct sk_buff *skb) signature = le32_to_cpup((__le32 *)skb->data); switch (signature) { - case MUX_SIG_ADGH: + case IOSM_AGGR_MUX_SIG_ADBH: /* Aggregated Data Block Header */ + mux_dl_adb_decode(ipc_mux, skb); + break; + case IOSM_AGGR_MUX_SIG_ADGH: ipc_mux_dl_adgh_decode(ipc_mux, skb); break; - case MUX_SIG_FCTH: ipc_mux_dl_fcth_decode(ipc_mux, skb->data); break; - + case IOSM_AGGR_MUX_SIG_ACBH: /* Aggregated Command Block Header */ + ipc_mux_dl_acb_decode(ipc_mux, skb); + break; case MUX_SIG_CMDH: ipc_mux_dl_cmd_decode(ipc_mux, skb); break; @@ -427,7 +706,10 @@ static int ipc_mux_ul_skb_alloc(struct iosm_mux *ipc_mux, { /* Take the first element of the free list. */ struct sk_buff *skb = skb_dequeue(&ul_adb->free_list); + u32 no_if = IPC_MEM_MUX_IP_SESSION_ENTRIES; + u32 *next_tb_id; int qlt_size; + u32 if_id; if (!skb) return -EBUSY; /* Wait for a free ADB skb. */ @@ -436,7 +718,37 @@ static int ipc_mux_ul_skb_alloc(struct iosm_mux *ipc_mux, IPC_CB(skb)->op_type = (u8)UL_MUX_OP_ADB; switch (type) { - case MUX_SIG_ADGH: + case IOSM_AGGR_MUX_SIG_ADBH: + /* Save the ADB memory settings. */ + ul_adb->dest_skb = skb; + ul_adb->buf = skb->data; + ul_adb->size = IPC_MEM_MAX_ADB_BUF_SIZE; + + /* reset statistic counter */ + ul_adb->if_cnt = 0; + ul_adb->payload_size = 0; + ul_adb->dg_cnt_total = 0; + + /* Initialize the ADBH. */ + ul_adb->adbh = (struct mux_adbh *)ul_adb->buf; + memset(ul_adb->adbh, 0, sizeof(struct mux_adbh)); + ul_adb->adbh->signature = cpu_to_le32(IOSM_AGGR_MUX_SIG_ADBH); + ul_adb->adbh->block_length = + cpu_to_le32(sizeof(struct mux_adbh)); + next_tb_id = (unsigned int *)&ul_adb->adbh->first_table_index; + ul_adb->next_table_index = next_tb_id; + + /* Clear the local copy of DGs for new ADB */ + memset(ul_adb->dg, 0, sizeof(ul_adb->dg)); + + /* Clear the DG count and QLT updated status for new ADB */ + for (if_id = 0; if_id < no_if; if_id++) { + ul_adb->dg_count[if_id] = 0; + ul_adb->qlt_updated[if_id] = 0; + } + break; + + case IOSM_AGGR_MUX_SIG_ADGH: /* Save the ADB memory settings. */ ul_adb->dest_skb = skb; ul_adb->buf = skb->data; @@ -506,6 +818,94 @@ static void ipc_mux_ul_adgh_finish(struct iosm_mux *ipc_mux) str, bytes); } +static void ipc_mux_ul_encode_adth(struct iosm_mux *ipc_mux, + struct mux_adb *ul_adb, int *out_offset) +{ + int i, qlt_size, offset = *out_offset; + struct mux_qlth *p_adb_qlt; + struct mux_adth_dg *dg; + struct mux_adth *adth; + u16 adth_dg_size; + u32 *next_tb_id; + + qlt_size = offsetof(struct mux_qlth, ql) + + MUX_QUEUE_LEVEL * sizeof(struct mux_qlth_ql); + + for (i = 0; i < ipc_mux->nr_sessions; i++) { + if (ul_adb->dg_count[i] > 0) { + adth_dg_size = offsetof(struct mux_adth, dg) + + ul_adb->dg_count[i] * sizeof(*dg); + + *ul_adb->next_table_index = offset; + adth = (struct mux_adth *)&ul_adb->buf[offset]; + next_tb_id = (unsigned int *)&adth->next_table_index; + ul_adb->next_table_index = next_tb_id; + offset += adth_dg_size; + adth->signature = cpu_to_le32(IOSM_AGGR_MUX_SIG_ADTH); + adth->if_id = i; + adth->table_length = cpu_to_le16(adth_dg_size); + adth_dg_size -= offsetof(struct mux_adth, dg); + memcpy(&adth->dg, ul_adb->dg[i], adth_dg_size); + ul_adb->if_cnt++; + } + + if (ul_adb->qlt_updated[i]) { + *ul_adb->next_table_index = offset; + p_adb_qlt = (struct mux_qlth *)&ul_adb->buf[offset]; + ul_adb->next_table_index = + (u32 *)&p_adb_qlt->next_table_index; + memcpy(p_adb_qlt, ul_adb->pp_qlt[i], qlt_size); + offset += qlt_size; + } + } + *out_offset = offset; +} + +/** + * ipc_mux_ul_adb_finish - Add the TD of the aggregated session packets to TDR. + * @ipc_mux: Pointer to MUX data-struct. + */ +void ipc_mux_ul_adb_finish(struct iosm_mux *ipc_mux) +{ + bool ul_data_pend = false; + struct mux_adb *ul_adb; + unsigned long flags; + int offset; + + ul_adb = &ipc_mux->ul_adb; + if (!ul_adb->dest_skb) + return; + + offset = *ul_adb->next_table_index; + ipc_mux_ul_encode_adth(ipc_mux, ul_adb, &offset); + ul_adb->adbh->block_length = cpu_to_le32(offset); + + if (le32_to_cpu(ul_adb->adbh->block_length) > ul_adb->size) { + ul_adb->dest_skb = NULL; + return; + } + + *ul_adb->next_table_index = 0; + ul_adb->adbh->sequence_nr = cpu_to_le16(ipc_mux->adb_tx_sequence_nr++); + skb_put(ul_adb->dest_skb, le32_to_cpu(ul_adb->adbh->block_length)); + + spin_lock_irqsave(&(&ipc_mux->channel->ul_list)->lock, flags); + __skb_queue_tail(&ipc_mux->channel->ul_list, ul_adb->dest_skb); + spin_unlock_irqrestore(&(&ipc_mux->channel->ul_list)->lock, flags); + + ul_adb->dest_skb = NULL; + /* Updates the TDs with ul_list */ + ul_data_pend = ipc_imem_ul_write_td(ipc_mux->imem); + + /* Delay the doorbell irq */ + if (ul_data_pend) + ipc_imem_td_update_timer_start(ipc_mux->imem); + + ipc_mux->acc_adb_size += le32_to_cpu(ul_adb->adbh->block_length); + ipc_mux->acc_payload_size += ul_adb->payload_size; + ipc_mux->ul_data_pend_bytes += ul_adb->payload_size; +} + /* Allocates an ADB from the free list and initializes it with ADBH */ static bool ipc_mux_ul_adb_allocate(struct iosm_mux *ipc_mux, struct mux_adb *adb, int *size_needed, @@ -688,7 +1088,7 @@ static int ipc_mux_ul_adgh_encode(struct iosm_mux *ipc_mux, int session_id, while (nr_of_pkts > 0) { /* get destination skb allocated */ if (ipc_mux_ul_adb_allocate(ipc_mux, adb, &ipc_mux->size_needed, - MUX_SIG_ADGH)) { + IOSM_AGGR_MUX_SIG_ADGH)) { dev_err(ipc_mux->dev, "no reserved memory for ADGH"); return -ENOMEM; } @@ -720,7 +1120,7 @@ static int ipc_mux_ul_adgh_encode(struct iosm_mux *ipc_mux, int session_id, memcpy(adb->buf + offset + pad_len, src_skb->data, src_skb->len); - adb->adgh->signature = cpu_to_le32(MUX_SIG_ADGH); + adb->adgh->signature = cpu_to_le32(IOSM_AGGR_MUX_SIG_ADGH); adb->adgh->if_id = session_id; adb->adgh->length = cpu_to_le16(sizeof(struct mux_adgh) + pad_len + @@ -762,6 +1162,187 @@ static int ipc_mux_ul_adgh_encode(struct iosm_mux *ipc_mux, int session_id, return adb_updated; } +/** + * ipc_mux_ul_adb_update_ql - Adds Queue Level Table and Queue Level to ADB + * @ipc_mux: pointer to MUX instance data + * @p_adb: pointer to UL aggegated data block + * @session_id: session id + * @qlth_n_ql_size: Length (in bytes) of the datagram table + * @ul_list: pointer to skb buffer head + */ +void ipc_mux_ul_adb_update_ql(struct iosm_mux *ipc_mux, struct mux_adb *p_adb, + int session_id, int qlth_n_ql_size, + struct sk_buff_head *ul_list) +{ + int qlevel = ul_list->qlen; + struct mux_qlth *p_qlt; + + p_qlt = (struct mux_qlth *)p_adb->pp_qlt[session_id]; + + /* Initialize QLTH if not been done */ + if (p_adb->qlt_updated[session_id] == 0) { + p_qlt->signature = cpu_to_le32(MUX_SIG_QLTH); + p_qlt->if_id = session_id; + p_qlt->table_length = cpu_to_le16(qlth_n_ql_size); + p_qlt->reserved = 0; + p_qlt->reserved2 = 0; + } + + /* Update Queue Level information always */ + p_qlt->ql.nr_of_bytes = cpu_to_le32(qlevel); + p_adb->qlt_updated[session_id] = 1; +} + +/* Update the next table index. */ +static int mux_ul_dg_update_tbl_index(struct iosm_mux *ipc_mux, + int session_id, + struct sk_buff_head *ul_list, + struct mux_adth_dg *dg, + int aligned_size, + u32 qlth_n_ql_size, + struct mux_adb *adb, + struct sk_buff *src_skb) +{ + ipc_mux_ul_adb_update_ql(ipc_mux, adb, session_id, + qlth_n_ql_size, ul_list); + ipc_mux_ul_adb_finish(ipc_mux); + if (ipc_mux_ul_adb_allocate(ipc_mux, adb, &ipc_mux->size_needed, + IOSM_AGGR_MUX_SIG_ADBH)) { + dev_kfree_skb(src_skb); + return -ENOMEM; + } + ipc_mux->size_needed = le32_to_cpu(adb->adbh->block_length); + + ipc_mux->size_needed += offsetof(struct mux_adth, dg); + ipc_mux->size_needed += qlth_n_ql_size; + ipc_mux->size_needed += sizeof(*dg) + aligned_size; + return 0; +} + +/* Process encode session UL data. */ +static int mux_ul_dg_encode(struct iosm_mux *ipc_mux, struct mux_adb *adb, + struct mux_adth_dg *dg, + struct sk_buff_head *ul_list, + struct sk_buff *src_skb, int session_id, + int pkt_to_send, u32 qlth_n_ql_size, + int *out_offset, int head_pad_len) +{ + int aligned_size; + int offset = *out_offset; + unsigned long flags; + int nr_of_skb = 0; + + while (pkt_to_send > 0) { + /* Peek at the head of the list. */ + src_skb = skb_peek(ul_list); + if (!src_skb) { + dev_err(ipc_mux->dev, + "skb peek return NULL with count : %d", + pkt_to_send); + return -1; + } + aligned_size = ALIGN((head_pad_len + src_skb->len), 4); + ipc_mux->size_needed += sizeof(*dg) + aligned_size; + + if (ipc_mux->size_needed > adb->size || + ((ipc_mux->size_needed + ipc_mux->ul_data_pend_bytes) >= + IPC_MEM_MUX_UL_FLOWCTRL_HIGH_B)) { + *adb->next_table_index = offset; + if (mux_ul_dg_update_tbl_index(ipc_mux, session_id, + ul_list, dg, + aligned_size, + qlth_n_ql_size, adb, + src_skb) < 0) + return -ENOMEM; + nr_of_skb = 0; + offset = le32_to_cpu(adb->adbh->block_length); + /* Load pointer to next available datagram entry */ + dg = adb->dg[session_id] + adb->dg_count[session_id]; + } + /* Add buffer without head padding to next pending transfer. */ + memcpy(adb->buf + offset + head_pad_len, + src_skb->data, src_skb->len); + /* Setup datagram entry. */ + dg->datagram_index = cpu_to_le32(offset); + dg->datagram_length = cpu_to_le16(src_skb->len + head_pad_len); + dg->service_class = (((struct sk_buff *)src_skb)->priority); + dg->reserved = 0; + adb->dg_cnt_total++; + adb->payload_size += le16_to_cpu(dg->datagram_length); + dg++; + adb->dg_count[session_id]++; + offset += aligned_size; + /* Remove the processed elements and free it. */ + spin_lock_irqsave(&ul_list->lock, flags); + src_skb = __skb_dequeue(ul_list); + spin_unlock_irqrestore(&ul_list->lock, flags); + + dev_kfree_skb(src_skb); + nr_of_skb++; + pkt_to_send--; + } + *out_offset = offset; + return nr_of_skb; +} + +/* Process encode session UL data to ADB. */ +static int mux_ul_adb_encode(struct iosm_mux *ipc_mux, int session_id, + struct mux_session *session, + struct sk_buff_head *ul_list, struct mux_adb *adb, + int pkt_to_send) +{ + int adb_updated = -EINVAL; + int head_pad_len, offset; + struct sk_buff *src_skb = NULL; + struct mux_adth_dg *dg; + u32 qlth_n_ql_size; + + /* If any of the opened session has set Flow Control ON then limit the + * UL data to mux_flow_ctrl_high_thresh_b bytes + */ + if (ipc_mux->ul_data_pend_bytes >= + IPC_MEM_MUX_UL_FLOWCTRL_HIGH_B) { + ipc_mux_stop_tx_for_all_sessions(ipc_mux); + return adb_updated; + } + + qlth_n_ql_size = offsetof(struct mux_qlth, ql) + + MUX_QUEUE_LEVEL * sizeof(struct mux_qlth_ql); + head_pad_len = session->ul_head_pad_len; + + if (session->ul_head_pad_len > IPC_MEM_DL_ETH_OFFSET) + head_pad_len = session->ul_head_pad_len - IPC_MEM_DL_ETH_OFFSET; + + if (ipc_mux_ul_adb_allocate(ipc_mux, adb, &ipc_mux->size_needed, + IOSM_AGGR_MUX_SIG_ADBH)) + return -ENOMEM; + + offset = le32_to_cpu(adb->adbh->block_length); + + if (ipc_mux->size_needed == 0) + ipc_mux->size_needed = offset; + + /* Calculate the size needed for ADTH, QLTH and QL*/ + if (adb->dg_count[session_id] == 0) { + ipc_mux->size_needed += offsetof(struct mux_adth, dg); + ipc_mux->size_needed += qlth_n_ql_size; + } + + dg = adb->dg[session_id] + adb->dg_count[session_id]; + + if (mux_ul_dg_encode(ipc_mux, adb, dg, ul_list, src_skb, + session_id, pkt_to_send, qlth_n_ql_size, &offset, + head_pad_len) > 0) { + adb_updated = 1; + *adb->next_table_index = offset; + ipc_mux_ul_adb_update_ql(ipc_mux, adb, session_id, + qlth_n_ql_size, ul_list); + adb->adbh->block_length = cpu_to_le32(offset); + } + + return adb_updated; +} + bool ipc_mux_ul_data_encode(struct iosm_mux *ipc_mux) { struct sk_buff_head *ul_list; @@ -802,28 +1383,88 @@ bool ipc_mux_ul_data_encode(struct iosm_mux *ipc_mux) * -> try next session id. */ continue; - - updated = ipc_mux_ul_adgh_encode(ipc_mux, session_id, session, - ul_list, &ipc_mux->ul_adb, - dg_n); + if (ipc_mux->protocol == MUX_LITE) + updated = ipc_mux_ul_adgh_encode(ipc_mux, session_id, + session, ul_list, + &ipc_mux->ul_adb, + dg_n); + else + updated = mux_ul_adb_encode(ipc_mux, session_id, + session, ul_list, + &ipc_mux->ul_adb, + dg_n); } ipc_mux->adb_prep_ongoing = false; return updated == 1; } +/* Calculates the Payload from any given ADB. */ +static int ipc_mux_get_payload_from_adb(struct iosm_mux *ipc_mux, + struct mux_adbh *p_adbh) +{ + struct mux_adth_dg *dg; + struct mux_adth *adth; + u32 payload_size = 0; + u32 next_table_idx; + int nr_of_dg, i; + + /* Process the aggregated datagram tables. */ + next_table_idx = le32_to_cpu(p_adbh->first_table_index); + + if (next_table_idx < sizeof(struct mux_adbh)) { + dev_err(ipc_mux->dev, "unexpected empty ADB"); + return payload_size; + } + + while (next_table_idx != 0) { + /* Get the reference to the table header. */ + adth = (struct mux_adth *)((u8 *)p_adbh + next_table_idx); + + if (adth->signature == cpu_to_le32(IOSM_AGGR_MUX_SIG_ADTH)) { + nr_of_dg = (le16_to_cpu(adth->table_length) - + sizeof(struct mux_adth) + + sizeof(struct mux_adth_dg)) / + sizeof(struct mux_adth_dg); + + if (nr_of_dg <= 0) + return payload_size; + + dg = &adth->dg; + + for (i = 0; i < nr_of_dg; i++, dg++) { + if (le32_to_cpu(dg->datagram_index) < + sizeof(struct mux_adbh)) { + return payload_size; + } + payload_size += + le16_to_cpu(dg->datagram_length); + } + } + next_table_idx = le32_to_cpu(adth->next_table_index); + } + + return payload_size; +} + void ipc_mux_ul_encoded_process(struct iosm_mux *ipc_mux, struct sk_buff *skb) { - struct mux_adgh *adgh; + union mux_type_header hr; u16 adgh_len; + int payload; - adgh = (struct mux_adgh *)skb->data; - adgh_len = le16_to_cpu(adgh->length); - - if (adgh->signature == cpu_to_le32(MUX_SIG_ADGH) && - ipc_mux->ul_flow == MUX_UL) - ipc_mux->ul_data_pend_bytes = ipc_mux->ul_data_pend_bytes - - adgh_len; + if (ipc_mux->protocol == MUX_LITE) { + hr.adgh = (struct mux_adgh *)skb->data; + adgh_len = le16_to_cpu(hr.adgh->length); + if (hr.adgh->signature == cpu_to_le32(IOSM_AGGR_MUX_SIG_ADGH) && + ipc_mux->ul_flow == MUX_UL) + ipc_mux->ul_data_pend_bytes = + ipc_mux->ul_data_pend_bytes - adgh_len; + } else { + hr.adbh = (struct mux_adbh *)(skb->data); + payload = ipc_mux_get_payload_from_adb(ipc_mux, hr.adbh); + ipc_mux->ul_data_pend_bytes -= payload; + } if (ipc_mux->ul_flow == MUX_UL) dev_dbg(ipc_mux->dev, "ul_data_pend_bytes: %lld", @@ -846,10 +1487,13 @@ static int ipc_mux_tq_ul_trigger_encode(struct iosm_imem *ipc_imem, int arg, /* Add session UL data to a ADB and ADGH */ ul_data_pend = ipc_mux_ul_data_encode(ipc_mux); - if (ul_data_pend) + if (ul_data_pend) { + if (ipc_mux->protocol == MUX_AGGREGATION) + ipc_imem_adb_timer_start(ipc_mux->imem); + /* Delay the doorbell irq */ ipc_imem_td_update_timer_start(ipc_mux->imem); - + } /* reset the debounce flag */ ipc_mux->ev_mux_net_transmit_pending = false; diff --git a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.h b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.h index aae83db5cbb8..5d4e3b89542c 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.h +++ b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.h @@ -13,6 +13,39 @@ */ #define MUX_QUEUE_LEVEL 1 +/* ADB finish timer value */ +#define IOSM_AGGR_MUX_ADB_FINISH_TIMEOUT_NSEC (500 * 1000) + +/* Enables the flow control (Flow is not allowed) */ +#define IOSM_AGGR_MUX_CMD_FLOW_CTL_ENABLE 5 + +/* Disables the flow control (Flow is allowed) */ +#define IOSM_AGGR_MUX_CMD_FLOW_CTL_DISABLE 6 + +/* ACK the flow control command. Shall have the same Transaction ID as the + * matching FLOW_CTL command + */ +#define IOSM_AGGR_MUX_CMD_FLOW_CTL_ACK 7 + +/* Aggregation Protocol Command for report packet indicating link quality + */ +#define IOSM_AGGR_MUX_CMD_LINK_STATUS_REPORT 8 + +/* Response to a report packet */ +#define IOSM_AGGR_MUX_CMD_LINK_STATUS_REPORT_RESP 9 + +/* ACBH: Signature of the Aggregated Command Block Header. */ +#define IOSM_AGGR_MUX_SIG_ACBH 0x48424341 + +/* ADTH: Signature of the Aggregated Datagram Table Header. */ +#define IOSM_AGGR_MUX_SIG_ADTH 0x48544441 + +/* ADBH: Signature of the Aggregated Data Block Header. */ +#define IOSM_AGGR_MUX_SIG_ADBH 0x48424441 + +/* ADGH: Signature of the Datagram Header. */ +#define IOSM_AGGR_MUX_SIG_ADGH 0x48474441 + /* Size of the buffer for the IP MUX commands. */ #define MUX_MAX_UL_ACB_BUF_SIZE 256 @@ -52,6 +85,85 @@ /* MUX UL flow control higher threshold in bytes (5ms worth of data)*/ #define IPC_MEM_MUX_UL_FLOWCTRL_HIGH_B (110 * 1024) +/** + * struct mux_cmdh - Structure of Command Header. + * @signature: Signature of the Command Header. + * @cmd_len: Length (in bytes) of the Aggregated Command Block. + * @if_id: ID of the interface the commands in the table belong to. + * @reserved: Reserved. Set to zero. + * @next_cmd_index: Index (in bytes) to the next command in the buffer. + * @command_type: Command Enum. See table Session Management chapter for + * details. + * @transaction_id: The Transaction ID shall be unique to the command + * @param: Optional parameters used with the command. + */ +struct mux_cmdh { + __le32 signature; + __le16 cmd_len; + u8 if_id; + u8 reserved; + __le32 next_cmd_index; + __le32 command_type; + __le32 transaction_id; + union mux_cmd_param param; +}; + +/** + * struct mux_acbh - Structure of the Aggregated Command Block Header. + * @signature: Signature of the Aggregated Command Block Header. + * @reserved: Reserved bytes. Set to zero. + * @sequence_nr: Block sequence number. + * @block_length: Length (in bytes) of the Aggregated Command Block. + * @first_cmd_index: Index (in bytes) to the first command in the buffer. + */ +struct mux_acbh { + __le32 signature; + __le16 reserved; + __le16 sequence_nr; + __le32 block_length; + __le32 first_cmd_index; +}; + +/** + * struct mux_adbh - Structure of the Aggregated Data Block Header. + * @signature: Signature of the Aggregated Data Block Header. + * @reserved: Reserved bytes. Set to zero. + * @sequence_nr: Block sequence number. + * @block_length: Length (in bytes) of the Aggregated Data Block. + * @first_table_index: Index (in bytes) to the first Datagram Table in + * the buffer. + */ +struct mux_adbh { + __le32 signature; + __le16 reserved; + __le16 sequence_nr; + __le32 block_length; + __le32 first_table_index; +}; + +/** + * struct mux_adth - Structure of the Aggregated Datagram Table Header. + * @signature: Signature of the Aggregated Datagram Table Header. + * @table_length: Length (in bytes) of the datagram table. + * @if_id: ID of the interface the datagrams in the table + * belong to. + * @opt_ipv4v6: Indicates IPv4(=0)/IPv6(=1) hint. + * @reserved: Reserved bits. Set to zero. + * @next_table_index: Index (in bytes) to the next Datagram Table in + * the buffer. + * @reserved2: Reserved bytes. Set to zero + * @dg: datagramm table with variable length + */ +struct mux_adth { + __le32 signature; + __le16 table_length; + u8 if_id; + u8 opt_ipv4v6; + __le32 next_table_index; + __le32 reserved2; + struct mux_adth_dg dg; +}; + /** * struct mux_adgh - Aggregated Datagram Header. * @signature: Signature of the Aggregated Datagram Header(0x48474441) @@ -129,11 +241,25 @@ struct ipc_mem_lite_gen_tbl { }; /** - * ipc_mux_dl_decode -Route the DL packet through the IP MUX layer - * depending on Header. - * @ipc_mux: Pointer to MUX data-struct - * @skb: Pointer to ipc_skb. + * struct mux_type_cmdh - Structure of command header for mux lite and aggr + * @ack_lite: MUX Lite Command Header pointer + * @ack_aggr: Command Header pointer */ +union mux_type_cmdh { + struct mux_lite_cmdh *ack_lite; + struct mux_cmdh *ack_aggr; +}; + +/** + * struct mux_type_header - Structure of mux header type + * @adgh: Aggregated Datagram Header pointer + * @adbh: Aggregated Data Block Header pointer + */ +union mux_type_header { + struct mux_adgh *adgh; + struct mux_adbh *adbh; +}; + void ipc_mux_dl_decode(struct iosm_mux *ipc_mux, struct sk_buff *skb); /** @@ -147,7 +273,7 @@ void ipc_mux_dl_decode(struct iosm_mux *ipc_mux, struct sk_buff *skb); * @blocking: True for blocking send * @respond: If true return transaction ID * - * Returns: 0 in success and failure value on error + * Returns: 0 in success and failure value on error */ int ipc_mux_dl_acb_send_cmds(struct iosm_mux *ipc_mux, u32 cmd_type, u8 if_id, u32 transaction_id, union mux_cmd_param *param, @@ -190,4 +316,10 @@ bool ipc_mux_ul_data_encode(struct iosm_mux *ipc_mux); */ void ipc_mux_ul_encoded_process(struct iosm_mux *ipc_mux, struct sk_buff *skb); +void ipc_mux_ul_adb_finish(struct iosm_mux *ipc_mux); + +void ipc_mux_ul_adb_update_ql(struct iosm_mux *ipc_mux, struct mux_adb *p_adb, + int session_id, int qlth_n_ql_size, + struct sk_buff_head *ul_list); + #endif diff --git a/drivers/net/wwan/iosm/iosm_ipc_pcie.c b/drivers/net/wwan/iosm/iosm_ipc_pcie.c index d73894e2a84e..31f57b986df2 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_pcie.c +++ b/drivers/net/wwan/iosm/iosm_ipc_pcie.c @@ -320,6 +320,7 @@ static int ipc_pcie_probe(struct pci_dev *pci, static const struct pci_device_id iosm_ipc_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CP_DEVICE_7560_ID) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CP_DEVICE_7360_ID) }, {} }; MODULE_DEVICE_TABLE(pci, iosm_ipc_ids); diff --git a/drivers/net/wwan/iosm/iosm_ipc_pcie.h b/drivers/net/wwan/iosm/iosm_ipc_pcie.h index 7d1f0cd7364c..844cf1fed994 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_pcie.h +++ b/drivers/net/wwan/iosm/iosm_ipc_pcie.h @@ -14,6 +14,7 @@ /* Device ID */ #define INTEL_CP_DEVICE_7560_ID 0x7560 +#define INTEL_CP_DEVICE_7360_ID 0x7360 /* Define for BAR area usage */ #define IPC_DOORBELL_BAR0 0 diff --git a/drivers/net/wwan/qcom_bam_dmux.c b/drivers/net/wwan/qcom_bam_dmux.c index 5dfa2eba6014..17d46f4d2913 100644 --- a/drivers/net/wwan/qcom_bam_dmux.c +++ b/drivers/net/wwan/qcom_bam_dmux.c @@ -755,7 +755,7 @@ static int __maybe_unused bam_dmux_runtime_resume(struct device *dev) return 0; dmux->tx = dma_request_chan(dev, "tx"); - if (IS_ERR(dmux->rx)) { + if (IS_ERR(dmux->tx)) { dev_err(dev, "Failed to request TX DMA channel: %pe\n", dmux->tx); dmux->tx = NULL; bam_dmux_runtime_suspend(dev); diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c index 1508dc2a497b..b8c7843730ed 100644 --- a/drivers/net/wwan/wwan_core.c +++ b/drivers/net/wwan/wwan_core.c @@ -160,6 +160,42 @@ struct dentry *wwan_get_debugfs_dir(struct device *parent) return wwandev->debugfs_dir; } EXPORT_SYMBOL_GPL(wwan_get_debugfs_dir); + +static int wwan_dev_debugfs_match(struct device *dev, const void *dir) +{ + struct wwan_device *wwandev; + + if (dev->type != &wwan_dev_type) + return 0; + + wwandev = to_wwan_dev(dev); + + return wwandev->debugfs_dir == dir; +} + +static struct wwan_device *wwan_dev_get_by_debugfs(struct dentry *dir) +{ + struct device *dev; + + dev = class_find_device(wwan_class, NULL, dir, wwan_dev_debugfs_match); + if (!dev) + return ERR_PTR(-ENODEV); + + return to_wwan_dev(dev); +} + +void wwan_put_debugfs_dir(struct dentry *dir) +{ + struct wwan_device *wwandev = wwan_dev_get_by_debugfs(dir); + + if (WARN_ON(IS_ERR(wwandev))) + return; + + /* wwan_dev_get_by_debugfs() also got a reference */ + put_device(&wwandev->dev); + put_device(&wwandev->dev); +} +EXPORT_SYMBOL_GPL(wwan_put_debugfs_dir); #endif /* This function allocates and registers a new WWAN device OR if a WWAN device diff --git a/drivers/nfc/st-nci/vendor_cmds.c b/drivers/nfc/st-nci/vendor_cmds.c index 30d2912d1a05..6335d7afca24 100644 --- a/drivers/nfc/st-nci/vendor_cmds.c +++ b/drivers/nfc/st-nci/vendor_cmds.c @@ -456,7 +456,7 @@ static const struct nfc_vendor_cmd st_nci_vendor_cmds[] = { int st_nci_vendor_cmds_init(struct nci_dev *ndev) { - return nfc_set_vendor_cmds(ndev->nfc_dev, st_nci_vendor_cmds, + return nci_set_vendor_cmds(ndev, st_nci_vendor_cmds, sizeof(st_nci_vendor_cmds)); } EXPORT_SYMBOL(st_nci_vendor_cmds_init); diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index a86b5edfc7ce..42dc0e5eb161 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -570,8 +570,7 @@ static int st21nfca_hci_i2c_remove(struct i2c_client *client) if (phy->powered) st21nfca_hci_i2c_disable(phy); - if (phy->pending_skb) - kfree_skb(phy->pending_skb); + kfree_skb(phy->pending_skb); return 0; } diff --git a/drivers/nfc/st21nfca/vendor_cmds.c b/drivers/nfc/st21nfca/vendor_cmds.c index 74882866dbaf..bfa418d4c6b0 100644 --- a/drivers/nfc/st21nfca/vendor_cmds.c +++ b/drivers/nfc/st21nfca/vendor_cmds.c @@ -358,7 +358,7 @@ int st21nfca_vendor_cmds_init(struct nfc_hci_dev *hdev) struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); init_completion(&info->vendor_info.req_completion); - return nfc_set_vendor_cmds(hdev->ndev, st21nfca_vendor_cmds, - sizeof(st21nfca_vendor_cmds)); + return nfc_hci_set_vendor_cmds(hdev, st21nfca_vendor_cmds, + sizeof(st21nfca_vendor_cmds)); } EXPORT_SYMBOL(st21nfca_vendor_cmds_init); diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig index c3669c28ea9f..0e91cd99c36b 100644 --- a/drivers/phy/freescale/Kconfig +++ b/drivers/phy/freescale/Kconfig @@ -22,3 +22,13 @@ config PHY_FSL_IMX8M_PCIE help Enable this to add support for the PCIE PHY as found on i.MX8M family of SOCs. + +config PHY_FSL_LYNX_28G + tristate "Freescale Layerscape Lynx 28G SerDes PHY support" + depends on OF + select GENERIC_PHY + help + Enable this to add support for the Lynx SerDes 28G PHY as + found on NXP's Layerscape platforms such as LX2160A. + Used to change the protocol running on SerDes lanes at runtime. + Only useful for a restricted set of Ethernet protocols. diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile index 55d07c742ab0..3518d5dbe8a7 100644 --- a/drivers/phy/freescale/Makefile +++ b/drivers/phy/freescale/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o obj-$(CONFIG_PHY_FSL_IMX8M_PCIE) += phy-fsl-imx8m-pcie.o +obj-$(CONFIG_PHY_FSL_LYNX_28G) += phy-fsl-lynx-28g.o diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c new file mode 100644 index 000000000000..569f12af2aaf --- /dev/null +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2021-2022 NXP. */ + +#include +#include +#include +#include +#include + +#define LYNX_28G_NUM_LANE 8 +#define LYNX_28G_NUM_PLL 2 + +/* General registers per SerDes block */ +#define LYNX_28G_PCC8 0x10a0 +#define LYNX_28G_PCC8_SGMII 0x1 +#define LYNX_28G_PCC8_SGMII_DIS 0x0 + +#define LYNX_28G_PCCC 0x10b0 +#define LYNX_28G_PCCC_10GBASER 0x9 +#define LYNX_28G_PCCC_USXGMII 0x1 +#define LYNX_28G_PCCC_SXGMII_DIS 0x0 + +#define LYNX_28G_LNa_PCC_OFFSET(lane) (4 * (LYNX_28G_NUM_LANE - (lane->id) - 1)) + +/* Per PLL registers */ +#define LYNX_28G_PLLnRSTCTL(pll) (0x400 + (pll) * 0x100 + 0x0) +#define LYNX_28G_PLLnRSTCTL_DIS(rstctl) (((rstctl) & BIT(24)) >> 24) +#define LYNX_28G_PLLnRSTCTL_LOCK(rstctl) (((rstctl) & BIT(23)) >> 23) + +#define LYNX_28G_PLLnCR0(pll) (0x400 + (pll) * 0x100 + 0x4) +#define LYNX_28G_PLLnCR0_REFCLK_SEL(cr0) (((cr0) & GENMASK(20, 16))) +#define LYNX_28G_PLLnCR0_REFCLK_SEL_100MHZ 0x0 +#define LYNX_28G_PLLnCR0_REFCLK_SEL_125MHZ 0x10000 +#define LYNX_28G_PLLnCR0_REFCLK_SEL_156MHZ 0x20000 +#define LYNX_28G_PLLnCR0_REFCLK_SEL_150MHZ 0x30000 +#define LYNX_28G_PLLnCR0_REFCLK_SEL_161MHZ 0x40000 + +#define LYNX_28G_PLLnCR1(pll) (0x400 + (pll) * 0x100 + 0x8) +#define LYNX_28G_PLLnCR1_FRATE_SEL(cr1) (((cr1) & GENMASK(28, 24))) +#define LYNX_28G_PLLnCR1_FRATE_5G_10GVCO 0x0 +#define LYNX_28G_PLLnCR1_FRATE_5G_25GVCO 0x10000000 +#define LYNX_28G_PLLnCR1_FRATE_10G_20GVCO 0x6000000 + +/* Per SerDes lane registers */ +/* Lane a General Control Register */ +#define LYNX_28G_LNaGCR0(lane) (0x800 + (lane) * 0x100 + 0x0) +#define LYNX_28G_LNaGCR0_PROTO_SEL_MSK GENMASK(7, 3) +#define LYNX_28G_LNaGCR0_PROTO_SEL_SGMII 0x8 +#define LYNX_28G_LNaGCR0_PROTO_SEL_XFI 0x50 +#define LYNX_28G_LNaGCR0_IF_WIDTH_MSK GENMASK(2, 0) +#define LYNX_28G_LNaGCR0_IF_WIDTH_10_BIT 0x0 +#define LYNX_28G_LNaGCR0_IF_WIDTH_20_BIT 0x2 + +/* Lane a Tx Reset Control Register */ +#define LYNX_28G_LNaTRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x20) +#define LYNX_28G_LNaTRSTCTL_HLT_REQ BIT(27) +#define LYNX_28G_LNaTRSTCTL_RST_DONE BIT(30) +#define LYNX_28G_LNaTRSTCTL_RST_REQ BIT(31) + +/* Lane a Tx General Control Register */ +#define LYNX_28G_LNaTGCR0(lane) (0x800 + (lane) * 0x100 + 0x24) +#define LYNX_28G_LNaTGCR0_USE_PLLF 0x0 +#define LYNX_28G_LNaTGCR0_USE_PLLS BIT(28) +#define LYNX_28G_LNaTGCR0_USE_PLL_MSK BIT(28) +#define LYNX_28G_LNaTGCR0_N_RATE_FULL 0x0 +#define LYNX_28G_LNaTGCR0_N_RATE_HALF 0x1000000 +#define LYNX_28G_LNaTGCR0_N_RATE_QUARTER 0x2000000 +#define LYNX_28G_LNaTGCR0_N_RATE_MSK GENMASK(26, 24) + +#define LYNX_28G_LNaTECR0(lane) (0x800 + (lane) * 0x100 + 0x30) + +/* Lane a Rx Reset Control Register */ +#define LYNX_28G_LNaRRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x40) +#define LYNX_28G_LNaRRSTCTL_HLT_REQ BIT(27) +#define LYNX_28G_LNaRRSTCTL_RST_DONE BIT(30) +#define LYNX_28G_LNaRRSTCTL_RST_REQ BIT(31) +#define LYNX_28G_LNaRRSTCTL_CDR_LOCK BIT(12) + +/* Lane a Rx General Control Register */ +#define LYNX_28G_LNaRGCR0(lane) (0x800 + (lane) * 0x100 + 0x44) +#define LYNX_28G_LNaRGCR0_USE_PLLF 0x0 +#define LYNX_28G_LNaRGCR0_USE_PLLS BIT(28) +#define LYNX_28G_LNaRGCR0_USE_PLL_MSK BIT(28) +#define LYNX_28G_LNaRGCR0_N_RATE_MSK GENMASK(26, 24) +#define LYNX_28G_LNaRGCR0_N_RATE_FULL 0x0 +#define LYNX_28G_LNaRGCR0_N_RATE_HALF 0x1000000 +#define LYNX_28G_LNaRGCR0_N_RATE_QUARTER 0x2000000 +#define LYNX_28G_LNaRGCR0_N_RATE_MSK GENMASK(26, 24) + +#define LYNX_28G_LNaRGCR1(lane) (0x800 + (lane) * 0x100 + 0x48) + +#define LYNX_28G_LNaRECR0(lane) (0x800 + (lane) * 0x100 + 0x50) +#define LYNX_28G_LNaRECR1(lane) (0x800 + (lane) * 0x100 + 0x54) +#define LYNX_28G_LNaRECR2(lane) (0x800 + (lane) * 0x100 + 0x58) + +#define LYNX_28G_LNaRSCCR0(lane) (0x800 + (lane) * 0x100 + 0x74) + +#define LYNX_28G_LNaPSS(lane) (0x1000 + (lane) * 0x4) +#define LYNX_28G_LNaPSS_TYPE(pss) (((pss) & GENMASK(30, 24)) >> 24) +#define LYNX_28G_LNaPSS_TYPE_SGMII 0x4 +#define LYNX_28G_LNaPSS_TYPE_XFI 0x28 + +#define LYNX_28G_SGMIIaCR1(lane) (0x1804 + (lane) * 0x10) +#define LYNX_28G_SGMIIaCR1_SGPCS_EN BIT(11) +#define LYNX_28G_SGMIIaCR1_SGPCS_DIS 0x0 +#define LYNX_28G_SGMIIaCR1_SGPCS_MSK BIT(11) + +struct lynx_28g_priv; + +struct lynx_28g_pll { + struct lynx_28g_priv *priv; + u32 rstctl, cr0, cr1; + int id; + DECLARE_PHY_INTERFACE_MASK(supported); +}; + +struct lynx_28g_lane { + struct lynx_28g_priv *priv; + struct phy *phy; + bool powered_up; + bool init; + unsigned int id; + phy_interface_t interface; +}; + +struct lynx_28g_priv { + void __iomem *base; + struct device *dev; + struct lynx_28g_pll pll[LYNX_28G_NUM_PLL]; + struct lynx_28g_lane lane[LYNX_28G_NUM_LANE]; + + struct delayed_work cdr_check; +}; + +static void lynx_28g_rmw(struct lynx_28g_priv *priv, unsigned long off, + u32 val, u32 mask) +{ + void __iomem *reg = priv->base + off; + u32 orig, tmp; + + orig = ioread32(reg); + tmp = orig & ~mask; + tmp |= val; + iowrite32(tmp, reg); +} + +#define lynx_28g_lane_rmw(lane, reg, val, mask) \ + lynx_28g_rmw((lane)->priv, LYNX_28G_##reg(lane->id), \ + LYNX_28G_##reg##_##val, LYNX_28G_##reg##_##mask) +#define lynx_28g_lane_read(lane, reg) \ + ioread32((lane)->priv->base + LYNX_28G_##reg((lane)->id)) +#define lynx_28g_pll_read(pll, reg) \ + ioread32((pll)->priv->base + LYNX_28G_##reg((pll)->id)) + +static bool lynx_28g_supports_interface(struct lynx_28g_priv *priv, int intf) +{ + int i; + + for (i = 0; i < LYNX_28G_NUM_PLL; i++) { + if (LYNX_28G_PLLnRSTCTL_DIS(priv->pll[i].rstctl)) + continue; + + if (test_bit(intf, priv->pll[i].supported)) + return true; + } + + return false; +} + +static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv, + phy_interface_t intf) +{ + struct lynx_28g_pll *pll; + int i; + + for (i = 0; i < LYNX_28G_NUM_PLL; i++) { + pll = &priv->pll[i]; + + if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl)) + continue; + + if (test_bit(intf, pll->supported)) + return pll; + } + + return NULL; +} + +static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane, + struct lynx_28g_pll *pll, + phy_interface_t intf) +{ + switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) { + case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO: + case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO: + switch (intf) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_QUARTER, N_RATE_MSK); + lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_QUARTER, N_RATE_MSK); + break; + default: + break; + } + break; + case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO: + switch (intf) { + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_USXGMII: + lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_FULL, N_RATE_MSK); + lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_FULL, N_RATE_MSK); + break; + default: + break; + } + break; + default: + break; + } +} + +static void lynx_28g_lane_set_pll(struct lynx_28g_lane *lane, + struct lynx_28g_pll *pll) +{ + if (pll->id == 0) { + lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLF, USE_PLL_MSK); + lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLF, USE_PLL_MSK); + } else { + lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLS, USE_PLL_MSK); + lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLS, USE_PLL_MSK); + } +} + +static void lynx_28g_cleanup_lane(struct lynx_28g_lane *lane) +{ + u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane); + struct lynx_28g_priv *priv = lane->priv; + + /* Cleanup the protocol configuration registers of the current protocol */ + switch (lane->interface) { + case PHY_INTERFACE_MODE_10GBASER: + lynx_28g_rmw(priv, LYNX_28G_PCCC, + LYNX_28G_PCCC_SXGMII_DIS << lane_offset, + GENMASK(3, 0) << lane_offset); + break; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + lynx_28g_rmw(priv, LYNX_28G_PCC8, + LYNX_28G_PCC8_SGMII_DIS << lane_offset, + GENMASK(3, 0) << lane_offset); + break; + default: + break; + } +} + +static void lynx_28g_lane_set_sgmii(struct lynx_28g_lane *lane) +{ + u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane); + struct lynx_28g_priv *priv = lane->priv; + struct lynx_28g_pll *pll; + + lynx_28g_cleanup_lane(lane); + + /* Setup the lane to run in SGMII */ + lynx_28g_rmw(priv, LYNX_28G_PCC8, + LYNX_28G_PCC8_SGMII << lane_offset, + GENMASK(3, 0) << lane_offset); + + /* Setup the protocol select and SerDes parallel interface width */ + lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_SGMII, PROTO_SEL_MSK); + lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_10_BIT, IF_WIDTH_MSK); + + /* Switch to the PLL that works with this interface type */ + pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_SGMII); + lynx_28g_lane_set_pll(lane, pll); + + /* Choose the portion of clock net to be used on this lane */ + lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_SGMII); + + /* Enable the SGMII PCS */ + lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_EN, SGPCS_MSK); + + /* Configure the appropriate equalization parameters for the protocol */ + iowrite32(0x00808006, priv->base + LYNX_28G_LNaTECR0(lane->id)); + iowrite32(0x04310000, priv->base + LYNX_28G_LNaRGCR1(lane->id)); + iowrite32(0x9f800000, priv->base + LYNX_28G_LNaRECR0(lane->id)); + iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id)); + iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR2(lane->id)); + iowrite32(0x00000000, priv->base + LYNX_28G_LNaRSCCR0(lane->id)); +} + +static void lynx_28g_lane_set_10gbaser(struct lynx_28g_lane *lane) +{ + u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane); + struct lynx_28g_priv *priv = lane->priv; + struct lynx_28g_pll *pll; + + lynx_28g_cleanup_lane(lane); + + /* Enable the SXGMII lane */ + lynx_28g_rmw(priv, LYNX_28G_PCCC, + LYNX_28G_PCCC_10GBASER << lane_offset, + GENMASK(3, 0) << lane_offset); + + /* Setup the protocol select and SerDes parallel interface width */ + lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_XFI, PROTO_SEL_MSK); + lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_20_BIT, IF_WIDTH_MSK); + + /* Switch to the PLL that works with this interface type */ + pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_10GBASER); + lynx_28g_lane_set_pll(lane, pll); + + /* Choose the portion of clock net to be used on this lane */ + lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_10GBASER); + + /* Disable the SGMII PCS */ + lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_DIS, SGPCS_MSK); + + /* Configure the appropriate equalization parameters for the protocol */ + iowrite32(0x10808307, priv->base + LYNX_28G_LNaTECR0(lane->id)); + iowrite32(0x10000000, priv->base + LYNX_28G_LNaRGCR1(lane->id)); + iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR0(lane->id)); + iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id)); + iowrite32(0x81000020, priv->base + LYNX_28G_LNaRECR2(lane->id)); + iowrite32(0x00002000, priv->base + LYNX_28G_LNaRSCCR0(lane->id)); +} + +static int lynx_28g_power_off(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + u32 trstctl, rrstctl; + + if (!lane->powered_up) + return 0; + + /* Issue a halt request */ + lynx_28g_lane_rmw(lane, LNaTRSTCTL, HLT_REQ, HLT_REQ); + lynx_28g_lane_rmw(lane, LNaRRSTCTL, HLT_REQ, HLT_REQ); + + /* Wait until the halting process is complete */ + do { + trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); + rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + } while ((trstctl & LYNX_28G_LNaTRSTCTL_HLT_REQ) || + (rrstctl & LYNX_28G_LNaRRSTCTL_HLT_REQ)); + + lane->powered_up = false; + + return 0; +} + +static int lynx_28g_power_on(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + u32 trstctl, rrstctl; + + if (lane->powered_up) + return 0; + + /* Issue a reset request on the lane */ + lynx_28g_lane_rmw(lane, LNaTRSTCTL, RST_REQ, RST_REQ); + lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ); + + /* Wait until the reset sequence is completed */ + do { + trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); + rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + } while (!(trstctl & LYNX_28G_LNaTRSTCTL_RST_DONE) || + !(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE)); + + lane->powered_up = true; + + return 0; +} + +static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + struct lynx_28g_priv *priv = lane->priv; + int powered_up = lane->powered_up; + int err = 0; + + if (mode != PHY_MODE_ETHERNET) + return -EOPNOTSUPP; + + if (lane->interface == PHY_INTERFACE_MODE_NA) + return -EOPNOTSUPP; + + if (!lynx_28g_supports_interface(priv, submode)) + return -EOPNOTSUPP; + + /* If the lane is powered up, put the lane into the halt state while + * the reconfiguration is being done. + */ + if (powered_up) + lynx_28g_power_off(phy); + + switch (submode) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + lynx_28g_lane_set_sgmii(lane); + break; + case PHY_INTERFACE_MODE_10GBASER: + lynx_28g_lane_set_10gbaser(lane); + break; + default: + err = -EOPNOTSUPP; + goto out; + } + + lane->interface = submode; + +out: + /* Power up the lane if necessary */ + if (powered_up) + lynx_28g_power_on(phy); + + return err; +} + +static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts __always_unused) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + struct lynx_28g_priv *priv = lane->priv; + + if (mode != PHY_MODE_ETHERNET) + return -EOPNOTSUPP; + + if (!lynx_28g_supports_interface(priv, submode)) + return -EOPNOTSUPP; + + return 0; +} + +static int lynx_28g_init(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + + /* Mark the fact that the lane was init */ + lane->init = true; + + /* SerDes lanes are powered on at boot time. Any lane that is managed + * by this driver will get powered down at init time aka at dpaa2-eth + * probe time. + */ + lane->powered_up = true; + lynx_28g_power_off(phy); + + return 0; +} + +static const struct phy_ops lynx_28g_ops = { + .init = lynx_28g_init, + .power_on = lynx_28g_power_on, + .power_off = lynx_28g_power_off, + .set_mode = lynx_28g_set_mode, + .validate = lynx_28g_validate, + .owner = THIS_MODULE, +}; + +static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv) +{ + struct lynx_28g_pll *pll; + int i; + + for (i = 0; i < LYNX_28G_NUM_PLL; i++) { + pll = &priv->pll[i]; + pll->priv = priv; + pll->id = i; + + pll->rstctl = lynx_28g_pll_read(pll, PLLnRSTCTL); + pll->cr0 = lynx_28g_pll_read(pll, PLLnCR0); + pll->cr1 = lynx_28g_pll_read(pll, PLLnCR1); + + if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl)) + continue; + + switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) { + case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO: + case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO: + /* 5GHz clock net */ + __set_bit(PHY_INTERFACE_MODE_1000BASEX, pll->supported); + __set_bit(PHY_INTERFACE_MODE_SGMII, pll->supported); + break; + case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO: + /* 10.3125GHz clock net */ + __set_bit(PHY_INTERFACE_MODE_10GBASER, pll->supported); + break; + default: + /* 6GHz, 12.890625GHz, 8GHz */ + break; + } + } +} + +#define work_to_lynx(w) container_of((w), struct lynx_28g_priv, cdr_check.work) + +static void lynx_28g_cdr_lock_check(struct work_struct *work) +{ + struct lynx_28g_priv *priv = work_to_lynx(work); + struct lynx_28g_lane *lane; + u32 rrstctl; + int i; + + for (i = 0; i < LYNX_28G_NUM_LANE; i++) { + lane = &priv->lane[i]; + + if (!lane->init) + continue; + + if (!lane->powered_up) + continue; + + rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + if (!(rrstctl & LYNX_28G_LNaRRSTCTL_CDR_LOCK)) { + lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ); + do { + rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + } while (!(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE)); + } + } + queue_delayed_work(system_power_efficient_wq, &priv->cdr_check, + msecs_to_jiffies(1000)); +} + +static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane) +{ + u32 pss, protocol; + + pss = lynx_28g_lane_read(lane, LNaPSS); + protocol = LYNX_28G_LNaPSS_TYPE(pss); + switch (protocol) { + case LYNX_28G_LNaPSS_TYPE_SGMII: + lane->interface = PHY_INTERFACE_MODE_SGMII; + break; + case LYNX_28G_LNaPSS_TYPE_XFI: + lane->interface = PHY_INTERFACE_MODE_10GBASER; + break; + default: + lane->interface = PHY_INTERFACE_MODE_NA; + } +} + +static struct phy *lynx_28g_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct lynx_28g_priv *priv = dev_get_drvdata(dev); + int idx = args->args[0]; + + if (WARN_ON(idx >= LYNX_28G_NUM_LANE)) + return ERR_PTR(-EINVAL); + + return priv->lane[idx].phy; +} + +static int lynx_28g_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct lynx_28g_priv *priv; + int i; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->dev = &pdev->dev; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + lynx_28g_pll_read_configuration(priv); + + for (i = 0; i < LYNX_28G_NUM_LANE; i++) { + struct lynx_28g_lane *lane = &priv->lane[i]; + struct phy *phy; + + memset(lane, 0, sizeof(*lane)); + + phy = devm_phy_create(&pdev->dev, NULL, &lynx_28g_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + lane->priv = priv; + lane->phy = phy; + lane->id = i; + phy_set_drvdata(phy, lane); + lynx_28g_lane_read_configuration(lane); + } + + dev_set_drvdata(dev, priv); + + INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check); + + queue_delayed_work(system_power_efficient_wq, &priv->cdr_check, + msecs_to_jiffies(1000)); + + dev_set_drvdata(&pdev->dev, priv); + provider = devm_of_phy_provider_register(&pdev->dev, lynx_28g_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id lynx_28g_of_match_table[] = { + { .compatible = "fsl,lynx-28g" }, + { }, +}; +MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table); + +static struct platform_driver lynx_28g_driver = { + .probe = lynx_28g_probe, + .driver = { + .name = "lynx-28g", + .of_match_table = lynx_28g_of_match_table, + }, +}; +module_platform_driver(lynx_28g_driver); + +MODULE_AUTHOR("Ioana Ciornei "); +MODULE_DESCRIPTION("Lynx 28G SerDes PHY driver for Layerscape SoCs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index 0e4bc8b9329d..b6f2cfd15dd2 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -317,11 +317,18 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, } EXPORT_SYMBOL(ptp_clock_register); +static int unregister_vclock(struct device *dev, void *data) +{ + struct ptp_clock *ptp = dev_get_drvdata(dev); + + ptp_vclock_unregister(info_to_vclock(ptp->info)); + return 0; +} + int ptp_clock_unregister(struct ptp_clock *ptp) { if (ptp_vclock_in_use(ptp)) { - pr_err("ptp: virtual clock in use\n"); - return -EBUSY; + device_for_each_child(&ptp->dev, NULL, unregister_vclock); } ptp->defunct = 1; diff --git a/drivers/ptp/ptp_idt82p33.c b/drivers/ptp/ptp_idt82p33.c index c1c959f7e52b..97c1be44e323 100644 --- a/drivers/ptp/ptp_idt82p33.c +++ b/drivers/ptp/ptp_idt82p33.c @@ -6,13 +6,17 @@ #define pr_fmt(fmt) "IDT_82p33xxx: " fmt #include -#include +#include #include #include #include +#include #include #include #include +#include +#include +#include #include "ptp_private.h" #include "ptp_idt82p33.h" @@ -24,15 +28,25 @@ MODULE_LICENSE("GPL"); MODULE_FIRMWARE(FW_FILENAME); /* Module Parameters */ -static u32 sync_tod_timeout = SYNC_TOD_TIMEOUT_SEC; -module_param(sync_tod_timeout, uint, 0); -MODULE_PARM_DESC(sync_tod_timeout, -"duration in second to keep SYNC_TOD on (set to 0 to keep it always on)"); - static u32 phase_snap_threshold = SNAP_THRESHOLD_NS; module_param(phase_snap_threshold, uint, 0); MODULE_PARM_DESC(phase_snap_threshold, -"threshold (150000ns by default) below which adjtime would ignore"); +"threshold (10000ns by default) below which adjtime would use double dco"); + +static char *firmware; +module_param(firmware, charp, 0); + +static inline int idt82p33_read(struct idt82p33 *idt82p33, u16 regaddr, + u8 *buf, u16 count) +{ + return regmap_bulk_read(idt82p33->regmap, regaddr, buf, count); +} + +static inline int idt82p33_write(struct idt82p33 *idt82p33, u16 regaddr, + u8 *buf, u16 count) +{ + return regmap_bulk_write(idt82p33->regmap, regaddr, buf, count); +} static void idt82p33_byte_array_to_timespec(struct timespec64 *ts, u8 buf[TOD_BYTE_COUNT]) @@ -78,110 +92,6 @@ static void idt82p33_timespec_to_byte_array(struct timespec64 const *ts, } } -static int idt82p33_xfer_read(struct idt82p33 *idt82p33, - unsigned char regaddr, - unsigned char *buf, - unsigned int count) -{ - struct i2c_client *client = idt82p33->client; - struct i2c_msg msg[2]; - int cnt; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 1; - msg[0].buf = ®addr; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = count; - msg[1].buf = buf; - - cnt = i2c_transfer(client->adapter, msg, 2); - if (cnt < 0) { - dev_err(&client->dev, "i2c_transfer returned %d\n", cnt); - return cnt; - } else if (cnt != 2) { - dev_err(&client->dev, - "i2c_transfer sent only %d of %d messages\n", cnt, 2); - return -EIO; - } - return 0; -} - -static int idt82p33_xfer_write(struct idt82p33 *idt82p33, - u8 regaddr, - u8 *buf, - u16 count) -{ - struct i2c_client *client = idt82p33->client; - /* we add 1 byte for device register */ - u8 msg[IDT82P33_MAX_WRITE_COUNT + 1]; - int err; - - if (count > IDT82P33_MAX_WRITE_COUNT) - return -EINVAL; - - msg[0] = regaddr; - memcpy(&msg[1], buf, count); - - err = i2c_master_send(client, msg, count + 1); - if (err < 0) { - dev_err(&client->dev, "i2c_master_send returned %d\n", err); - return err; - } - - return 0; -} - -static int idt82p33_page_offset(struct idt82p33 *idt82p33, unsigned char val) -{ - int err; - - if (idt82p33->page_offset == val) - return 0; - - err = idt82p33_xfer_write(idt82p33, PAGE_ADDR, &val, sizeof(val)); - if (err) - dev_err(&idt82p33->client->dev, - "failed to set page offset %d\n", val); - else - idt82p33->page_offset = val; - - return err; -} - -static int idt82p33_rdwr(struct idt82p33 *idt82p33, unsigned int regaddr, - unsigned char *buf, unsigned int count, bool write) -{ - u8 offset, page; - int err; - - page = _PAGE(regaddr); - offset = _OFFSET(regaddr); - - err = idt82p33_page_offset(idt82p33, page); - if (err) - return err; - - if (write) - return idt82p33_xfer_write(idt82p33, offset, buf, count); - - return idt82p33_xfer_read(idt82p33, offset, buf, count); -} - -static int idt82p33_read(struct idt82p33 *idt82p33, unsigned int regaddr, - unsigned char *buf, unsigned int count) -{ - return idt82p33_rdwr(idt82p33, regaddr, buf, count, false); -} - -static int idt82p33_write(struct idt82p33 *idt82p33, unsigned int regaddr, - unsigned char *buf, unsigned int count) -{ - return idt82p33_rdwr(idt82p33, regaddr, buf, count, true); -} - static int idt82p33_dpll_set_mode(struct idt82p33_channel *channel, enum pll_mode mode) { @@ -206,7 +116,7 @@ static int idt82p33_dpll_set_mode(struct idt82p33_channel *channel, if (err) return err; - channel->pll_mode = dpll_mode; + channel->pll_mode = mode; return 0; } @@ -467,7 +377,7 @@ static int idt82p33_measure_tod_write_overhead(struct idt82p33_channel *channel) err = idt82p33_measure_settime_gettime_gap_overhead(channel, &gap_ns); if (err) { - dev_err(&idt82p33->client->dev, + dev_err(idt82p33->dev, "Failed in %s with err %d!\n", __func__, err); return err; } @@ -499,8 +409,8 @@ static int idt82p33_check_and_set_masks(struct idt82p33 *idt82p33, if (page == PLLMASK_ADDR_HI && offset == PLLMASK_ADDR_LO) { if ((val & 0xfc) || !(val & 0x3)) { - dev_err(&idt82p33->client->dev, - "Invalid PLL mask 0x%hhx\n", val); + dev_err(idt82p33->dev, + "Invalid PLL mask 0x%x\n", val); err = -EINVAL; } else { idt82p33->pll_mask = val; @@ -520,14 +430,14 @@ static void idt82p33_display_masks(struct idt82p33 *idt82p33) { u8 mask, i; - dev_info(&idt82p33->client->dev, + dev_info(idt82p33->dev, "pllmask = 0x%02x\n", idt82p33->pll_mask); for (i = 0; i < MAX_PHC_PLL; i++) { mask = 1 << i; if (mask & idt82p33->pll_mask) - dev_info(&idt82p33->client->dev, + dev_info(idt82p33->dev, "PLL%d output_mask = 0x%04x\n", i, idt82p33->channel[i].output_mask); } @@ -539,11 +449,6 @@ static int idt82p33_sync_tod(struct idt82p33_channel *channel, bool enable) u8 sync_cnfg; int err; - /* Turn it off after sync_tod_timeout seconds */ - if (enable && sync_tod_timeout) - ptp_schedule_worker(channel->ptp_clock, - sync_tod_timeout * HZ); - err = idt82p33_read(idt82p33, channel->dpll_sync_cnfg, &sync_cnfg, sizeof(sync_cnfg)); if (err) @@ -557,22 +462,6 @@ static int idt82p33_sync_tod(struct idt82p33_channel *channel, bool enable) &sync_cnfg, sizeof(sync_cnfg)); } -static long idt82p33_sync_tod_work_handler(struct ptp_clock_info *ptp) -{ - struct idt82p33_channel *channel = - container_of(ptp, struct idt82p33_channel, caps); - struct idt82p33 *idt82p33 = channel->idt82p33; - - mutex_lock(&idt82p33->reg_lock); - - (void)idt82p33_sync_tod(channel, false); - - mutex_unlock(&idt82p33->reg_lock); - - /* Return a negative value here to not reschedule */ - return -1; -} - static int idt82p33_output_enable(struct idt82p33_channel *channel, bool enable, unsigned int outn) { @@ -634,18 +523,11 @@ static int idt82p33_enable_tod(struct idt82p33_channel *channel) struct idt82p33 *idt82p33 = channel->idt82p33; struct timespec64 ts = {0, 0}; int err; - u8 val; - - val = 0; - err = idt82p33_write(idt82p33, channel->dpll_input_mode_cnfg, - &val, sizeof(val)); - if (err) - return err; err = idt82p33_measure_tod_write_overhead(channel); if (err) { - dev_err(&idt82p33->client->dev, + dev_err(idt82p33->dev, "Failed in %s with err %d!\n", __func__, err); return err; } @@ -673,16 +555,14 @@ static void idt82p33_ptp_clock_unregister_all(struct idt82p33 *idt82p33) } static int idt82p33_enable(struct ptp_clock_info *ptp, - struct ptp_clock_request *rq, int on) + struct ptp_clock_request *rq, int on) { struct idt82p33_channel *channel = container_of(ptp, struct idt82p33_channel, caps); struct idt82p33 *idt82p33 = channel->idt82p33; - int err; + int err = -EOPNOTSUPP; - err = -EOPNOTSUPP; - - mutex_lock(&idt82p33->reg_lock); + mutex_lock(idt82p33->lock); if (rq->type == PTP_CLK_REQ_PEROUT) { if (!on) @@ -690,15 +570,18 @@ static int idt82p33_enable(struct ptp_clock_info *ptp, &rq->perout); /* Only accept a 1-PPS aligned to the second. */ else if (rq->perout.start.nsec || rq->perout.period.sec != 1 || - rq->perout.period.nsec) { + rq->perout.period.nsec) err = -ERANGE; - } else + else err = idt82p33_perout_enable(channel, true, &rq->perout); } - mutex_unlock(&idt82p33->reg_lock); + mutex_unlock(idt82p33->lock); + if (err) + dev_err(idt82p33->dev, + "Failed in %s with err %d!\n", __func__, err); return err; } @@ -727,11 +610,11 @@ static int idt82p33_adjwritephase(struct ptp_clock_info *ptp, s32 offset_ns) val[3] = (offset_regval >> 24) & 0x1F; val[3] |= PH_OFFSET_EN; - mutex_lock(&idt82p33->reg_lock); + mutex_lock(idt82p33->lock); err = idt82p33_dpll_set_mode(channel, PLL_MODE_WPH); if (err) { - dev_err(&idt82p33->client->dev, + dev_err(idt82p33->dev, "Failed in %s with err %d!\n", __func__, err); goto out; } @@ -740,7 +623,7 @@ static int idt82p33_adjwritephase(struct ptp_clock_info *ptp, s32 offset_ns) sizeof(val)); out: - mutex_unlock(&idt82p33->reg_lock); + mutex_unlock(idt82p33->lock); return err; } @@ -751,12 +634,12 @@ static int idt82p33_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) struct idt82p33 *idt82p33 = channel->idt82p33; int err; - mutex_lock(&idt82p33->reg_lock); + mutex_lock(idt82p33->lock); err = _idt82p33_adjfine(channel, scaled_ppm); + mutex_unlock(idt82p33->lock); if (err) - dev_err(&idt82p33->client->dev, + dev_err(idt82p33->dev, "Failed in %s with err %d!\n", __func__, err); - mutex_unlock(&idt82p33->reg_lock); return err; } @@ -768,29 +651,20 @@ static int idt82p33_adjtime(struct ptp_clock_info *ptp, s64 delta_ns) struct idt82p33 *idt82p33 = channel->idt82p33; int err; - mutex_lock(&idt82p33->reg_lock); + mutex_lock(idt82p33->lock); if (abs(delta_ns) < phase_snap_threshold) { - mutex_unlock(&idt82p33->reg_lock); + mutex_unlock(idt82p33->lock); return 0; } err = _idt82p33_adjtime(channel, delta_ns); - if (err) { - mutex_unlock(&idt82p33->reg_lock); - dev_err(&idt82p33->client->dev, - "Adjtime failed in %s with err %d!\n", __func__, err); - return err; - } + mutex_unlock(idt82p33->lock); - err = idt82p33_sync_tod(channel, true); if (err) - dev_err(&idt82p33->client->dev, - "Sync_tod failed in %s with err %d!\n", __func__, err); - - mutex_unlock(&idt82p33->reg_lock); - + dev_err(idt82p33->dev, + "Failed in %s with err %d!\n", __func__, err); return err; } @@ -801,31 +675,31 @@ static int idt82p33_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) struct idt82p33 *idt82p33 = channel->idt82p33; int err; - mutex_lock(&idt82p33->reg_lock); + mutex_lock(idt82p33->lock); err = _idt82p33_gettime(channel, ts); - if (err) - dev_err(&idt82p33->client->dev, - "Failed in %s with err %d!\n", __func__, err); - mutex_unlock(&idt82p33->reg_lock); + mutex_unlock(idt82p33->lock); + if (err) + dev_err(idt82p33->dev, + "Failed in %s with err %d!\n", __func__, err); return err; } static int idt82p33_settime(struct ptp_clock_info *ptp, - const struct timespec64 *ts) + const struct timespec64 *ts) { struct idt82p33_channel *channel = container_of(ptp, struct idt82p33_channel, caps); struct idt82p33 *idt82p33 = channel->idt82p33; int err; - mutex_lock(&idt82p33->reg_lock); + mutex_lock(idt82p33->lock); err = _idt82p33_settime(channel, ts); - if (err) - dev_err(&idt82p33->client->dev, - "Failed in %s with err %d!\n", __func__, err); - mutex_unlock(&idt82p33->reg_lock); + mutex_unlock(idt82p33->lock); + if (err) + dev_err(idt82p33->dev, + "Failed in %s with err %d!\n", __func__, err); return err; } @@ -864,7 +738,7 @@ static int idt82p33_channel_init(struct idt82p33_channel *channel, int index) static void idt82p33_caps_init(struct ptp_clock_info *caps) { caps->owner = THIS_MODULE; - caps->max_adj = 92000; + caps->max_adj = DCO_MAX_PPB; caps->n_per_out = 11; caps->adjphase = idt82p33_adjwritephase; caps->adjfine = idt82p33_adjfine; @@ -872,7 +746,6 @@ static void idt82p33_caps_init(struct ptp_clock_info *caps) caps->gettime64 = idt82p33_gettime; caps->settime64 = idt82p33_settime; caps->enable = idt82p33_enable; - caps->do_aux_work = idt82p33_sync_tod_work_handler; } static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index) @@ -887,7 +760,7 @@ static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index) err = idt82p33_channel_init(channel, index); if (err) { - dev_err(&idt82p33->client->dev, + dev_err(idt82p33->dev, "Channel_init failed in %s with err %d!\n", __func__, err); return err; @@ -912,7 +785,7 @@ static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index) err = idt82p33_dpll_set_mode(channel, PLL_MODE_DCO); if (err) { - dev_err(&idt82p33->client->dev, + dev_err(idt82p33->dev, "Dpll_set_mode failed in %s with err %d!\n", __func__, err); return err; @@ -920,13 +793,13 @@ static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index) err = idt82p33_enable_tod(channel); if (err) { - dev_err(&idt82p33->client->dev, + dev_err(idt82p33->dev, "Enable_tod failed in %s with err %d!\n", __func__, err); return err; } - dev_info(&idt82p33->client->dev, "PLL%d registered as ptp%d\n", + dev_info(idt82p33->dev, "PLL%d registered as ptp%d\n", index, channel->ptp_clock->index); return 0; @@ -940,25 +813,24 @@ static int idt82p33_load_firmware(struct idt82p33 *idt82p33) int err; s32 len; - dev_dbg(&idt82p33->client->dev, - "requesting firmware '%s'\n", FW_FILENAME); + dev_dbg(idt82p33->dev, "requesting firmware '%s'\n", FW_FILENAME); - err = request_firmware(&fw, FW_FILENAME, &idt82p33->client->dev); + err = request_firmware(&fw, FW_FILENAME, idt82p33->dev); if (err) { - dev_err(&idt82p33->client->dev, + dev_err(idt82p33->dev, "Failed in %s with err %d!\n", __func__, err); return err; } - dev_dbg(&idt82p33->client->dev, "firmware size %zu bytes\n", fw->size); + dev_dbg(idt82p33->dev, "firmware size %zu bytes\n", fw->size); rec = (struct idt82p33_fwrc *) fw->data; for (len = fw->size; len > 0; len -= sizeof(*rec)) { if (rec->reserved) { - dev_err(&idt82p33->client->dev, + dev_err(idt82p33->dev, "bad firmware, reserved field non-zero\n"); err = -EINVAL; } else { @@ -973,16 +845,11 @@ static int idt82p33_load_firmware(struct idt82p33 *idt82p33) } if (err == 0) { - /* maximum 8 pages */ - if (page >= PAGE_NUM) - continue; - /* Page size 128, last 4 bytes of page skipped */ - if (((loaddr > 0x7b) && (loaddr <= 0x7f)) - || loaddr > 0xfb) + if (loaddr > 0x7b) continue; - err = idt82p33_write(idt82p33, _ADDR(page, loaddr), + err = idt82p33_write(idt82p33, REG_ADDR(page, loaddr), &val, sizeof(val)); } @@ -997,36 +864,34 @@ static int idt82p33_load_firmware(struct idt82p33 *idt82p33) } -static int idt82p33_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int idt82p33_probe(struct platform_device *pdev) { + struct rsmu_ddata *ddata = dev_get_drvdata(pdev->dev.parent); struct idt82p33 *idt82p33; int err; u8 i; - (void)id; - - idt82p33 = devm_kzalloc(&client->dev, + idt82p33 = devm_kzalloc(&pdev->dev, sizeof(struct idt82p33), GFP_KERNEL); if (!idt82p33) return -ENOMEM; - mutex_init(&idt82p33->reg_lock); - - idt82p33->client = client; - idt82p33->page_offset = 0xff; + idt82p33->dev = &pdev->dev; + idt82p33->mfd = pdev->dev.parent; + idt82p33->lock = &ddata->lock; + idt82p33->regmap = ddata->regmap; idt82p33->tod_write_overhead_ns = 0; idt82p33->calculate_overhead_flag = 0; idt82p33->pll_mask = DEFAULT_PLL_MASK; idt82p33->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0; idt82p33->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1; - mutex_lock(&idt82p33->reg_lock); + mutex_lock(idt82p33->lock); err = idt82p33_load_firmware(idt82p33); if (err) - dev_warn(&idt82p33->client->dev, + dev_warn(idt82p33->dev, "loading firmware failed with %d\n", err); if (idt82p33->pll_mask) { @@ -1034,7 +899,7 @@ static int idt82p33_probe(struct i2c_client *client, if (idt82p33->pll_mask & (1 << i)) { err = idt82p33_enable_channel(idt82p33, i); if (err) { - dev_err(&idt82p33->client->dev, + dev_err(idt82p33->dev, "Failed in %s with err %d!\n", __func__, err); break; @@ -1042,69 +907,38 @@ static int idt82p33_probe(struct i2c_client *client, } } } else { - dev_err(&idt82p33->client->dev, + dev_err(idt82p33->dev, "no PLLs flagged as PHCs, nothing to do\n"); err = -ENODEV; } - mutex_unlock(&idt82p33->reg_lock); + mutex_unlock(idt82p33->lock); if (err) { idt82p33_ptp_clock_unregister_all(idt82p33); return err; } - i2c_set_clientdata(client, idt82p33); + platform_set_drvdata(pdev, idt82p33); return 0; } -static int idt82p33_remove(struct i2c_client *client) +static int idt82p33_remove(struct platform_device *pdev) { - struct idt82p33 *idt82p33 = i2c_get_clientdata(client); + struct idt82p33 *idt82p33 = platform_get_drvdata(pdev); idt82p33_ptp_clock_unregister_all(idt82p33); - mutex_destroy(&idt82p33->reg_lock); return 0; } -#ifdef CONFIG_OF -static const struct of_device_id idt82p33_dt_id[] = { - { .compatible = "idt,82p33810" }, - { .compatible = "idt,82p33813" }, - { .compatible = "idt,82p33814" }, - { .compatible = "idt,82p33831" }, - { .compatible = "idt,82p33910" }, - { .compatible = "idt,82p33913" }, - { .compatible = "idt,82p33914" }, - { .compatible = "idt,82p33931" }, - {}, -}; -MODULE_DEVICE_TABLE(of, idt82p33_dt_id); -#endif - -static const struct i2c_device_id idt82p33_i2c_id[] = { - { "idt82p33810", }, - { "idt82p33813", }, - { "idt82p33814", }, - { "idt82p33831", }, - { "idt82p33910", }, - { "idt82p33913", }, - { "idt82p33914", }, - { "idt82p33931", }, - {}, -}; -MODULE_DEVICE_TABLE(i2c, idt82p33_i2c_id); - -static struct i2c_driver idt82p33_driver = { +static struct platform_driver idt82p33_driver = { .driver = { - .of_match_table = of_match_ptr(idt82p33_dt_id), - .name = "idt82p33", + .name = "82p33x1x-phc", }, - .probe = idt82p33_probe, - .remove = idt82p33_remove, - .id_table = idt82p33_i2c_id, + .probe = idt82p33_probe, + .remove = idt82p33_remove, }; -module_i2c_driver(idt82p33_driver); +module_platform_driver(idt82p33_driver); diff --git a/drivers/ptp/ptp_idt82p33.h b/drivers/ptp/ptp_idt82p33.h index 1c7a0f0872e8..0ea1c35c0f9f 100644 --- a/drivers/ptp/ptp_idt82p33.h +++ b/drivers/ptp/ptp_idt82p33.h @@ -8,94 +8,19 @@ #define PTP_IDT82P33_H #include -#include +#include +#include - -/* Register Map - AN888_SMUforIEEE_SynchEther_82P33xxx_RevH.pdf */ -#define PAGE_NUM (8) -#define _ADDR(page, offset) (((page) << 0x7) | ((offset) & 0x7f)) -#define _PAGE(addr) (((addr) >> 0x7) & 0x7) -#define _OFFSET(addr) ((addr) & 0x7f) - -#define DPLL1_TOD_CNFG 0x134 -#define DPLL2_TOD_CNFG 0x1B4 - -#define DPLL1_TOD_STS 0x10B -#define DPLL2_TOD_STS 0x18B - -#define DPLL1_TOD_TRIGGER 0x115 -#define DPLL2_TOD_TRIGGER 0x195 - -#define DPLL1_OPERATING_MODE_CNFG 0x120 -#define DPLL2_OPERATING_MODE_CNFG 0x1A0 - -#define DPLL1_HOLDOVER_FREQ_CNFG 0x12C -#define DPLL2_HOLDOVER_FREQ_CNFG 0x1AC - -#define DPLL1_PHASE_OFFSET_CNFG 0x143 -#define DPLL2_PHASE_OFFSET_CNFG 0x1C3 - -#define DPLL1_SYNC_EDGE_CNFG 0X140 -#define DPLL2_SYNC_EDGE_CNFG 0X1C0 - -#define DPLL1_INPUT_MODE_CNFG 0X116 -#define DPLL2_INPUT_MODE_CNFG 0X196 - -#define OUT_MUX_CNFG(outn) _ADDR(0x6, (0xC * (outn))) - -#define PAGE_ADDR 0x7F -/* Register Map end */ - -/* Register definitions - AN888_SMUforIEEE_SynchEther_82P33xxx_RevH.pdf*/ -#define TOD_TRIGGER(wr_trig, rd_trig) ((wr_trig & 0xf) << 4 | (rd_trig & 0xf)) -#define SYNC_TOD BIT(1) -#define PH_OFFSET_EN BIT(7) -#define SQUELCH_ENABLE BIT(5) - -/* Bit definitions for the DPLL_MODE register */ -#define PLL_MODE_SHIFT (0) -#define PLL_MODE_MASK (0x1F) - -#define PEROUT_ENABLE_OUTPUT_MASK (0xdeadbeef) - -enum pll_mode { - PLL_MODE_MIN = 0, - PLL_MODE_AUTOMATIC = PLL_MODE_MIN, - PLL_MODE_FORCE_FREERUN = 1, - PLL_MODE_FORCE_HOLDOVER = 2, - PLL_MODE_FORCE_LOCKED = 4, - PLL_MODE_FORCE_PRE_LOCKED2 = 5, - PLL_MODE_FORCE_PRE_LOCKED = 6, - PLL_MODE_FORCE_LOST_PHASE = 7, - PLL_MODE_DCO = 10, - PLL_MODE_WPH = 18, - PLL_MODE_MAX = PLL_MODE_WPH, -}; - -enum hw_tod_trig_sel { - HW_TOD_TRIG_SEL_MIN = 0, - HW_TOD_TRIG_SEL_NO_WRITE = HW_TOD_TRIG_SEL_MIN, - HW_TOD_TRIG_SEL_SYNC_SEL = 1, - HW_TOD_TRIG_SEL_IN12 = 2, - HW_TOD_TRIG_SEL_IN13 = 3, - HW_TOD_TRIG_SEL_IN14 = 4, - HW_TOD_TRIG_SEL_TOD_PPS = 5, - HW_TOD_TRIG_SEL_TIMER_INTERVAL = 6, - HW_TOD_TRIG_SEL_MSB_PHASE_OFFSET_CNFG = 7, - HW_TOD_TRIG_SEL_MSB_HOLDOVER_FREQ_CNFG = 8, - HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG = 9, - HW_TOD_RD_TRIG_SEL_LSB_TOD_STS = HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG, - WR_TRIG_SEL_MAX = HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG, -}; - -/* Register bit definitions end */ #define FW_FILENAME "idt82p33xxx.bin" -#define MAX_PHC_PLL (2) -#define TOD_BYTE_COUNT (10) -#define MAX_MEASURMENT_COUNT (5) -#define SNAP_THRESHOLD_NS (150000) -#define SYNC_TOD_TIMEOUT_SEC (5) -#define IDT82P33_MAX_WRITE_COUNT (512) +#define MAX_PHC_PLL (2) +#define TOD_BYTE_COUNT (10) +#define DCO_MAX_PPB (92000) +#define MAX_MEASURMENT_COUNT (5) +#define SNAP_THRESHOLD_NS (10000) +#define IMMEDIATE_SNAP_THRESHOLD_NS (50000) +#define DDCO_THRESHOLD_NS (5) +#define IDT82P33_MAX_WRITE_COUNT (512) +#define PEROUT_ENABLE_OUTPUT_MASK (0xdeadbeef) #define PLLMASK_ADDR_HI 0xFF #define PLLMASK_ADDR_LO 0xA5 @@ -116,47 +41,6 @@ enum hw_tod_trig_sel { #define DEFAULT_OUTPUT_MASK_PLL0 (0xc0) #define DEFAULT_OUTPUT_MASK_PLL1 DEFAULT_OUTPUT_MASK_PLL0 -/* PTP Hardware Clock interface */ -struct idt82p33_channel { - struct ptp_clock_info caps; - struct ptp_clock *ptp_clock; - struct idt82p33 *idt82p33; - enum pll_mode pll_mode; - /* task to turn off SYNC_TOD bit after pps sync */ - struct delayed_work sync_tod_work; - bool sync_tod_on; - s32 current_freq_ppb; - u8 output_mask; - u16 dpll_tod_cnfg; - u16 dpll_tod_trigger; - u16 dpll_tod_sts; - u16 dpll_mode_cnfg; - u16 dpll_freq_cnfg; - u16 dpll_phase_cnfg; - u16 dpll_sync_cnfg; - u16 dpll_input_mode_cnfg; -}; - -struct idt82p33 { - struct idt82p33_channel channel[MAX_PHC_PLL]; - struct i2c_client *client; - u8 page_offset; - u8 pll_mask; - ktime_t start_time; - int calculate_overhead_flag; - s64 tod_write_overhead_ns; - /* Protects I2C read/modify/write registers from concurrent access */ - struct mutex reg_lock; -}; - -/* firmware interface */ -struct idt82p33_fwrc { - u8 hiaddr; - u8 loaddr; - u8 value; - u8 reserved; -} __packed; - /** * @brief Maximum absolute value for write phase offset in femtoseconds */ @@ -170,5 +54,44 @@ struct idt82p33_fwrc { */ #define IDT_T0DPLL_PHASE_RESOL 74506 +/* PTP Hardware Clock interface */ +struct idt82p33_channel { + struct ptp_clock_info caps; + struct ptp_clock *ptp_clock; + struct idt82p33 *idt82p33; + enum pll_mode pll_mode; + s32 current_freq_ppb; + u8 output_mask; + u16 dpll_tod_cnfg; + u16 dpll_tod_trigger; + u16 dpll_tod_sts; + u16 dpll_mode_cnfg; + u16 dpll_freq_cnfg; + u16 dpll_phase_cnfg; + u16 dpll_sync_cnfg; + u16 dpll_input_mode_cnfg; +}; + +struct idt82p33 { + struct idt82p33_channel channel[MAX_PHC_PLL]; + struct device *dev; + u8 pll_mask; + /* Mutex to protect operations from being interrupted */ + struct mutex *lock; + struct regmap *regmap; + struct device *mfd; + /* Overhead calculation for adjtime */ + ktime_t start_time; + int calculate_overhead_flag; + s64 tod_write_overhead_ns; +}; + +/* firmware interface */ +struct idt82p33_fwrc { + u8 hiaddr; + u8 loaddr; + u8 value; + u8 reserved; +} __packed; #endif /* PTP_IDT82P33_H */ diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 17ad5f0d13b2..c3d0fcf609e3 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -11,12 +11,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #ifndef PCI_VENDOR_ID_FACEBOOK #define PCI_VENDOR_ID_FACEBOOK 0x1d9b @@ -52,6 +54,8 @@ struct ocp_reg { u32 servo_offset_i; u32 servo_drift_p; u32 servo_drift_i; + u32 status_offset; + u32 status_drift; }; #define OCP_CTRL_ENABLE BIT(0) @@ -88,9 +92,10 @@ struct tod_reg { #define TOD_CTRL_GNSS_MASK ((1U << 4) - 1) #define TOD_CTRL_GNSS_SHIFT 24 -#define TOD_STATUS_UTC_MASK 0xff -#define TOD_STATUS_UTC_VALID BIT(8) -#define TOD_STATUS_LEAP_VALID BIT(16) +#define TOD_STATUS_UTC_MASK 0xff +#define TOD_STATUS_UTC_VALID BIT(8) +#define TOD_STATUS_LEAP_ANNOUNCE BIT(12) +#define TOD_STATUS_LEAP_VALID BIT(16) struct ts_reg { u32 enable; @@ -174,6 +179,35 @@ struct dcf_slave_reg { #define DCF_S_CTRL_ENABLE BIT(0) +struct signal_reg { + u32 enable; + u32 status; + u32 polarity; + u32 version; + u32 __pad0[4]; + u32 cable_delay; + u32 __pad1[3]; + u32 intr; + u32 intr_mask; + u32 __pad2[2]; + u32 start_ns; + u32 start_sec; + u32 pulse_ns; + u32 pulse_sec; + u32 period_ns; + u32 period_sec; + u32 repeat_count; +}; + +struct frequency_reg { + u32 ctrl; + u32 status; +}; +#define FREQ_STATUS_VALID BIT(31) +#define FREQ_STATUS_ERROR BIT(30) +#define FREQ_STATUS_OVERRUN BIT(29) +#define FREQ_STATUS_MASK (BIT(24) - 1) + struct ptp_ocp_flash_info { const char *name; int pci_offset; @@ -201,6 +235,40 @@ struct ptp_ocp_ext_src { int irq_vec; }; +enum ptp_ocp_sma_mode { + SMA_MODE_IN, + SMA_MODE_OUT, +}; + +struct ptp_ocp_sma_connector { + enum ptp_ocp_sma_mode mode; + bool fixed_fcn; + bool fixed_dir; + bool disabled; +}; + +struct ocp_attr_group { + u64 cap; + const struct attribute_group *group; +}; + +#define OCP_CAP_BASIC BIT(0) +#define OCP_CAP_SIGNAL BIT(1) +#define OCP_CAP_FREQ BIT(2) + +struct ptp_ocp_signal { + ktime_t period; + ktime_t pulse; + ktime_t phase; + ktime_t start; + int duty; + bool polarity; + bool running; +}; + +#define OCP_BOARD_ID_LEN 13 +#define OCP_SERIAL_LEN 6 + struct ptp_ocp { struct pci_dev *pdev; struct device dev; @@ -210,16 +278,21 @@ struct ptp_ocp { struct pps_reg __iomem *pps_to_ext; struct pps_reg __iomem *pps_to_clk; struct gpio_reg __iomem *pps_select; - struct gpio_reg __iomem *sma; + struct gpio_reg __iomem *sma_map1; + struct gpio_reg __iomem *sma_map2; struct irig_master_reg __iomem *irig_out; struct irig_slave_reg __iomem *irig_in; struct dcf_master_reg __iomem *dcf_out; struct dcf_slave_reg __iomem *dcf_in; struct tod_reg __iomem *nmea_out; + struct frequency_reg __iomem *freq_in[4]; + struct ptp_ocp_ext_src *signal_out[4]; struct ptp_ocp_ext_src *pps; struct ptp_ocp_ext_src *ts0; struct ptp_ocp_ext_src *ts1; struct ptp_ocp_ext_src *ts2; + struct ptp_ocp_ext_src *ts3; + struct ptp_ocp_ext_src *ts4; struct img_reg __iomem *image; struct ptp_clock *ptp; struct ptp_clock_info ptp_info; @@ -227,6 +300,8 @@ struct ptp_ocp { struct platform_device *spi_flash; struct clk_hw *i2c_clk; struct timer_list watchdog; + const struct ocp_attr_group *attr_tbl; + const struct ptp_ocp_eeprom_map *eeprom_map; struct dentry *debug_root; time64_t gnss_lost; int id; @@ -235,12 +310,17 @@ struct ptp_ocp { int gnss2_port; int mac_port; /* miniature atomic clock */ int nmea_port; - u8 serial[6]; - bool has_serial; + u32 fw_version; + u8 board_id[OCP_BOARD_ID_LEN]; + u8 serial[OCP_SERIAL_LEN]; + bool has_eeprom_data; u32 pps_req_map; int flash_start; u32 utc_tai_offset; u32 ts_window_adjust; + u64 fw_cap; + struct ptp_ocp_signal signal[4]; + struct ptp_ocp_sma_connector sma[4]; }; #define OCP_REQ_TIMESTAMP BIT(0) @@ -263,7 +343,36 @@ static int ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r); static int ptp_ocp_register_ext(struct ptp_ocp *bp, struct ocp_resource *r); static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r); static irqreturn_t ptp_ocp_ts_irq(int irq, void *priv); +static irqreturn_t ptp_ocp_signal_irq(int irq, void *priv); static int ptp_ocp_ts_enable(void *priv, u32 req, bool enable); +static int ptp_ocp_signal_from_perout(struct ptp_ocp *bp, int gen, + struct ptp_perout_request *req); +static int ptp_ocp_signal_enable(void *priv, u32 req, bool enable); +static int ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr); + +static const struct ocp_attr_group fb_timecard_groups[]; + +struct ptp_ocp_eeprom_map { + u16 off; + u16 len; + u32 bp_offset; + const void * const tag; +}; + +#define EEPROM_ENTRY(addr, member) \ + .off = addr, \ + .len = sizeof_field(struct ptp_ocp, member), \ + .bp_offset = offsetof(struct ptp_ocp, member) + +#define BP_MAP_ENTRY_ADDR(bp, map) ({ \ + (void *)((uintptr_t)(bp) + (map)->bp_offset); \ +}) + +static struct ptp_ocp_eeprom_map fb_eeprom_map[] = { + { EEPROM_ENTRY(0x43, board_id) }, + { EEPROM_ENTRY(0x00, serial), .tag = "mac" }, + { } +}; #define bp_assign_entry(bp, res, val) ({ \ uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset; \ @@ -289,10 +398,10 @@ static int ptp_ocp_ts_enable(void *priv, u32 req, bool enable); OCP_RES_LOCATION(member), .setup = ptp_ocp_register_ext /* This is the MSI vector mapping used. - * 0: TS3 (and PPS) + * 0: PPS (TS5) * 1: TS0 * 2: TS1 - * 3: GNSS + * 3: GNSS1 * 4: GNSS2 * 5: MAC * 6: TS2 @@ -300,6 +409,12 @@ static int ptp_ocp_ts_enable(void *priv, u32 req, bool enable); * 8: HWICAP (notused) * 9: SPI Flash * 10: NMEA + * 11: Signal Generator 1 + * 12: Signal Generator 2 + * 13: Signal Generator 3 + * 14: Signal Generator 4 + * 15: TS3 + * 16: TS4 */ static struct ocp_resource ocp_fb_resource[] = { @@ -335,14 +450,69 @@ static struct ocp_resource ocp_fb_resource[] = { }, }, { - OCP_EXT_RESOURCE(pps), - .offset = 0x010C0000, .size = 0x10000, .irq_vec = 0, + OCP_EXT_RESOURCE(ts3), + .offset = 0x01110000, .size = 0x10000, .irq_vec = 15, .extra = &(struct ptp_ocp_ext_info) { .index = 3, .irq_fcn = ptp_ocp_ts_irq, .enable = ptp_ocp_ts_enable, }, }, + { + OCP_EXT_RESOURCE(ts4), + .offset = 0x01120000, .size = 0x10000, .irq_vec = 16, + .extra = &(struct ptp_ocp_ext_info) { + .index = 4, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, + /* Timestamp for PHC and/or PPS generator */ + { + OCP_EXT_RESOURCE(pps), + .offset = 0x010C0000, .size = 0x10000, .irq_vec = 0, + .extra = &(struct ptp_ocp_ext_info) { + .index = 5, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, + { + OCP_EXT_RESOURCE(signal_out[0]), + .offset = 0x010D0000, .size = 0x10000, .irq_vec = 11, + .extra = &(struct ptp_ocp_ext_info) { + .index = 1, + .irq_fcn = ptp_ocp_signal_irq, + .enable = ptp_ocp_signal_enable, + }, + }, + { + OCP_EXT_RESOURCE(signal_out[1]), + .offset = 0x010E0000, .size = 0x10000, .irq_vec = 12, + .extra = &(struct ptp_ocp_ext_info) { + .index = 2, + .irq_fcn = ptp_ocp_signal_irq, + .enable = ptp_ocp_signal_enable, + }, + }, + { + OCP_EXT_RESOURCE(signal_out[2]), + .offset = 0x010F0000, .size = 0x10000, .irq_vec = 13, + .extra = &(struct ptp_ocp_ext_info) { + .index = 3, + .irq_fcn = ptp_ocp_signal_irq, + .enable = ptp_ocp_signal_enable, + }, + }, + { + OCP_EXT_RESOURCE(signal_out[3]), + .offset = 0x01100000, .size = 0x10000, .irq_vec = 14, + .extra = &(struct ptp_ocp_ext_info) { + .index = 4, + .irq_fcn = ptp_ocp_signal_irq, + .enable = ptp_ocp_signal_enable, + }, + }, { OCP_MEM_RESOURCE(pps_to_ext), .offset = 0x01030000, .size = 0x10000, @@ -384,15 +554,28 @@ static struct ocp_resource ocp_fb_resource[] = { .offset = 0x00130000, .size = 0x1000, }, { - OCP_MEM_RESOURCE(sma), + OCP_MEM_RESOURCE(sma_map1), .offset = 0x00140000, .size = 0x1000, }, + { + OCP_MEM_RESOURCE(sma_map2), + .offset = 0x00220000, .size = 0x1000, + }, { OCP_I2C_RESOURCE(i2c_ctrl), .offset = 0x00150000, .size = 0x10000, .irq_vec = 7, .extra = &(struct ptp_ocp_i2c_info) { .name = "xiic-i2c", .fixed_rate = 50000000, + .data_size = sizeof(struct xiic_i2c_platform_data), + .data = &(struct xiic_i2c_platform_data) { + .num_devices = 2, + .devices = (struct i2c_board_info[]) { + { I2C_BOARD_INFO("24c02", 0x50) }, + { I2C_BOARD_INFO("24mac402", 0x58), + .platform_data = "mac" }, + }, + }, }, }, { @@ -427,6 +610,22 @@ static struct ocp_resource ocp_fb_resource[] = { }, }, }, + { + OCP_MEM_RESOURCE(freq_in[0]), + .offset = 0x01200000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(freq_in[1]), + .offset = 0x01210000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(freq_in[2]), + .offset = 0x01220000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(freq_in[3]), + .offset = 0x01230000, .size = 0x10000, + }, { .setup = ptp_ocp_fb_board_init, }, @@ -460,25 +659,42 @@ static struct ocp_selector ptp_ocp_clock[] = { { } }; +#define SMA_ENABLE BIT(15) +#define SMA_SELECT_MASK ((1U << 15) - 1) +#define SMA_DISABLE 0x10000 + static struct ocp_selector ptp_ocp_sma_in[] = { - { .name = "10Mhz", .value = 0x00 }, - { .name = "PPS1", .value = 0x01 }, - { .name = "PPS2", .value = 0x02 }, - { .name = "TS1", .value = 0x04 }, - { .name = "TS2", .value = 0x08 }, - { .name = "IRIG", .value = 0x10 }, - { .name = "DCF", .value = 0x20 }, + { .name = "10Mhz", .value = 0x0000 }, + { .name = "PPS1", .value = 0x0001 }, + { .name = "PPS2", .value = 0x0002 }, + { .name = "TS1", .value = 0x0004 }, + { .name = "TS2", .value = 0x0008 }, + { .name = "IRIG", .value = 0x0010 }, + { .name = "DCF", .value = 0x0020 }, + { .name = "TS3", .value = 0x0040 }, + { .name = "TS4", .value = 0x0080 }, + { .name = "FREQ1", .value = 0x0100 }, + { .name = "FREQ2", .value = 0x0200 }, + { .name = "FREQ3", .value = 0x0400 }, + { .name = "FREQ4", .value = 0x0800 }, + { .name = "None", .value = SMA_DISABLE }, { } }; static struct ocp_selector ptp_ocp_sma_out[] = { - { .name = "10Mhz", .value = 0x00 }, - { .name = "PHC", .value = 0x01 }, - { .name = "MAC", .value = 0x02 }, - { .name = "GNSS", .value = 0x04 }, - { .name = "GNSS2", .value = 0x08 }, - { .name = "IRIG", .value = 0x10 }, - { .name = "DCF", .value = 0x20 }, + { .name = "10Mhz", .value = 0x0000 }, + { .name = "PHC", .value = 0x0001 }, + { .name = "MAC", .value = 0x0002 }, + { .name = "GNSS1", .value = 0x0004 }, + { .name = "GNSS2", .value = 0x0008 }, + { .name = "IRIG", .value = 0x0010 }, + { .name = "DCF", .value = 0x0020 }, + { .name = "GEN1", .value = 0x0040 }, + { .name = "GEN2", .value = 0x0080 }, + { .name = "GEN3", .value = 0x0100 }, + { .name = "GEN4", .value = 0x0200 }, + { .name = "GND", .value = 0x2000 }, + { .name = "VCC", .value = 0x4000 }, { } }; @@ -700,6 +916,12 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, ext = bp->ts2; break; case 3: + ext = bp->ts3; + break; + case 4: + ext = bp->ts4; + break; + case 5: ext = bp->pps; break; } @@ -709,13 +931,27 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, ext = bp->pps; break; case PTP_CLK_REQ_PEROUT: - if (on && - (rq->perout.period.sec != 1 || rq->perout.period.nsec != 0)) - return -EINVAL; - /* This is a request for 1PPS on an output SMA. - * Allow, but assume manual configuration. - */ - return 0; + switch (rq->perout.index) { + case 0: + /* This is a request for 1PPS on an output SMA. + * Allow, but assume manual configuration. + */ + if (on && (rq->perout.period.sec != 1 || + rq->perout.period.nsec != 0)) + return -EINVAL; + return 0; + case 1: + case 2: + case 3: + case 4: + req = rq->perout.index - 1; + ext = bp->signal_out[req]; + err = ptp_ocp_signal_from_perout(bp, req, &rq->perout); + if (err) + return err; + break; + } + break; default: return -EOPNOTSUPP; } @@ -727,6 +963,36 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, return err; } +static int +ptp_ocp_verify(struct ptp_clock_info *ptp_info, unsigned pin, + enum ptp_pin_function func, unsigned chan) +{ + struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info); + char buf[16]; + + switch (func) { + case PTP_PF_NONE: + snprintf(buf, sizeof(buf), "IN: None"); + break; + case PTP_PF_EXTTS: + /* Allow timestamps, but require sysfs configuration. */ + return 0; + case PTP_PF_PEROUT: + /* channel 0 is 1PPS from PHC. + * channels 1..4 are the frequency generators. + */ + if (chan) + snprintf(buf, sizeof(buf), "OUT: GEN%d", chan); + else + snprintf(buf, sizeof(buf), "OUT: PHC"); + break; + default: + return -EOPNOTSUPP; + } + + return ptp_ocp_sma_store(bp, buf, pin + 1); +} + static const struct ptp_clock_info ptp_ocp_clock_info = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, @@ -737,9 +1003,10 @@ static const struct ptp_clock_info ptp_ocp_clock_info = { .adjfine = ptp_ocp_null_adjfine, .adjphase = ptp_ocp_null_adjphase, .enable = ptp_ocp_enable, + .verify = ptp_ocp_verify, .pps = true, - .n_ext_ts = 4, - .n_per_out = 1, + .n_ext_ts = 6, + .n_per_out = 5, }; static void @@ -759,12 +1026,31 @@ __ptp_ocp_clear_drift_locked(struct ptp_ocp *bp) iowrite32(select >> 16, &bp->reg->select); } +static void +ptp_ocp_utc_distribute(struct ptp_ocp *bp, u32 val) +{ + unsigned long flags; + + spin_lock_irqsave(&bp->lock, flags); + + bp->utc_tai_offset = val; + + if (bp->irig_out) + iowrite32(val, &bp->irig_out->adj_sec); + if (bp->dcf_out) + iowrite32(val, &bp->dcf_out->adj_sec); + if (bp->nmea_out) + iowrite32(val, &bp->nmea_out->adj_sec); + + spin_unlock_irqrestore(&bp->lock, flags); +} + static void ptp_ocp_watchdog(struct timer_list *t) { struct ptp_ocp *bp = from_timer(bp, t, watchdog); unsigned long flags; - u32 status; + u32 status, utc_offset; status = ioread32(&bp->pps_to_clk->status); @@ -781,6 +1067,17 @@ ptp_ocp_watchdog(struct timer_list *t) bp->gnss_lost = 0; } + /* if GNSS provides correct data we can rely on + * it to get leap second information + */ + if (bp->tod) { + status = ioread32(&bp->tod->utc_status); + utc_offset = status & TOD_STATUS_UTC_MASK; + if (status & TOD_STATUS_UTC_VALID && + utc_offset != bp->utc_tai_offset) + ptp_ocp_utc_distribute(bp, utc_offset); + } + mod_timer(&bp->watchdog, jiffies + HZ); } @@ -849,25 +1146,6 @@ ptp_ocp_init_clock(struct ptp_ocp *bp) return 0; } -static void -ptp_ocp_utc_distribute(struct ptp_ocp *bp, u32 val) -{ - unsigned long flags; - - spin_lock_irqsave(&bp->lock, flags); - - bp->utc_tai_offset = val; - - if (bp->irig_out) - iowrite32(val, &bp->irig_out->adj_sec); - if (bp->dcf_out) - iowrite32(val, &bp->dcf_out->adj_sec); - if (bp->nmea_out) - iowrite32(val, &bp->nmea_out->adj_sec); - - spin_unlock_irqrestore(&bp->lock, flags); -} - static void ptp_ocp_tod_init(struct ptp_ocp *bp) { @@ -883,45 +1161,104 @@ ptp_ocp_tod_init(struct ptp_ocp *bp) ptp_ocp_utc_distribute(bp, reg & TOD_STATUS_UTC_MASK); } -static void -ptp_ocp_tod_info(struct ptp_ocp *bp) +static const char * +ptp_ocp_tod_proto_name(const int idx) { static const char * const proto_name[] = { "NMEA", "NMEA_ZDA", "NMEA_RMC", "NMEA_none", "UBX", "UBX_UTC", "UBX_LS", "UBX_none" }; + return proto_name[idx]; +} + +static const char * +ptp_ocp_tod_gnss_name(int idx) +{ static const char * const gnss_name[] = { "ALL", "COMBINED", "GPS", "GLONASS", "GALILEO", "BEIDOU", + "Unknown" }; - u32 version, ctrl, reg; - int idx; + if (idx >= ARRAY_SIZE(gnss_name)) + idx = ARRAY_SIZE(gnss_name) - 1; + return gnss_name[idx]; +} - version = ioread32(&bp->tod->version); - dev_info(&bp->pdev->dev, "TOD Version %d.%d.%d\n", - version >> 24, (version >> 16) & 0xff, version & 0xffff); +struct ptp_ocp_nvmem_match_info { + struct ptp_ocp *bp; + const void * const tag; +}; - ctrl = ioread32(&bp->tod->ctrl); - idx = ctrl & TOD_CTRL_PROTOCOL ? 4 : 0; - idx += (ctrl >> 16) & 3; - dev_info(&bp->pdev->dev, "control: %x\n", ctrl); - dev_info(&bp->pdev->dev, "TOD Protocol %s %s\n", proto_name[idx], - ctrl & TOD_CTRL_ENABLE ? "enabled" : ""); +static int +ptp_ocp_nvmem_match(struct device *dev, const void *data) +{ + const struct ptp_ocp_nvmem_match_info *info = data; - idx = (ctrl >> TOD_CTRL_GNSS_SHIFT) & TOD_CTRL_GNSS_MASK; - if (idx < ARRAY_SIZE(gnss_name)) - dev_info(&bp->pdev->dev, "GNSS %s\n", gnss_name[idx]); + dev = dev->parent; + if (!i2c_verify_client(dev) || info->tag != dev->platform_data) + return 0; - reg = ioread32(&bp->tod->status); - dev_info(&bp->pdev->dev, "status: %x\n", reg); + while ((dev = dev->parent)) + if (dev->driver && !strcmp(dev->driver->name, KBUILD_MODNAME)) + return info->bp == dev_get_drvdata(dev); + return 0; +} - reg = ioread32(&bp->tod->adj_sec); - dev_info(&bp->pdev->dev, "correction: %d\n", reg); +static inline struct nvmem_device * +ptp_ocp_nvmem_device_get(struct ptp_ocp *bp, const void * const tag) +{ + struct ptp_ocp_nvmem_match_info info = { .bp = bp, .tag = tag }; - reg = ioread32(&bp->tod->utc_status); - dev_info(&bp->pdev->dev, "utc_status: %x\n", reg); - dev_info(&bp->pdev->dev, "utc_offset: %d valid:%d leap_valid:%d\n", - reg & TOD_STATUS_UTC_MASK, reg & TOD_STATUS_UTC_VALID ? 1 : 0, - reg & TOD_STATUS_LEAP_VALID ? 1 : 0); + return nvmem_device_find(&info, ptp_ocp_nvmem_match); +} + +static inline void +ptp_ocp_nvmem_device_put(struct nvmem_device **nvmemp) +{ + if (*nvmemp != NULL) { + nvmem_device_put(*nvmemp); + *nvmemp = NULL; + } +} + +static void +ptp_ocp_read_eeprom(struct ptp_ocp *bp) +{ + const struct ptp_ocp_eeprom_map *map; + struct nvmem_device *nvmem; + const void *tag; + int ret; + + if (!bp->i2c_ctrl) + return; + + tag = NULL; + nvmem = NULL; + + for (map = bp->eeprom_map; map->len; map++) { + if (map->tag != tag) { + tag = map->tag; + ptp_ocp_nvmem_device_put(&nvmem); + } + if (!nvmem) { + nvmem = ptp_ocp_nvmem_device_get(bp, tag); + if (!nvmem) + goto out; + } + ret = nvmem_device_read(nvmem, map->off, map->len, + BP_MAP_ENTRY_ADDR(bp, map)); + if (ret != map->len) + goto read_fail; + } + + bp->has_eeprom_data = true; + +out: + ptp_ocp_nvmem_device_put(&nvmem); + return; + +read_fail: + dev_err(&bp->pdev->dev, "could not read eeprom: %d\n", ret); + goto out; } static int @@ -930,74 +1267,6 @@ ptp_ocp_firstchild(struct device *dev, void *data) return 1; } -static int -ptp_ocp_read_i2c(struct i2c_adapter *adap, u8 addr, u8 reg, u8 sz, u8 *data) -{ - struct i2c_msg msgs[2] = { - { - .addr = addr, - .len = 1, - .buf = ®, - }, - { - .addr = addr, - .flags = I2C_M_RD, - .len = 2, - .buf = data, - }, - }; - int err; - u8 len; - - /* xiic-i2c for some stupid reason only does 2 byte reads. */ - while (sz) { - len = min_t(u8, sz, 2); - msgs[1].len = len; - err = i2c_transfer(adap, msgs, 2); - if (err != msgs[1].len) - return err; - msgs[1].buf += len; - reg += len; - sz -= len; - } - return 0; -} - -static void -ptp_ocp_get_serial_number(struct ptp_ocp *bp) -{ - struct i2c_adapter *adap; - struct device *dev; - int err; - - if (!bp->i2c_ctrl) - return; - - dev = device_find_child(&bp->i2c_ctrl->dev, NULL, ptp_ocp_firstchild); - if (!dev) { - dev_err(&bp->pdev->dev, "Can't find I2C adapter\n"); - return; - } - - adap = i2c_verify_adapter(dev); - if (!adap) { - dev_err(&bp->pdev->dev, "device '%s' isn't an I2C adapter\n", - dev_name(dev)); - goto out; - } - - err = ptp_ocp_read_i2c(adap, 0x58, 0x9A, 6, bp->serial); - if (err) { - dev_err(&bp->pdev->dev, "could not read eeprom: %d\n", err); - goto out; - } - - bp->has_serial = true; - -out: - put_device(dev); -} - static struct device * ptp_ocp_find_flash(struct ptp_ocp *bp) { @@ -1096,33 +1365,32 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, if (err) return err; - if (bp->image) { - u32 ver = ioread32(&bp->image->version); + if (bp->fw_version & 0xffff) { + sprintf(buf, "%d", bp->fw_version); + err = devlink_info_version_running_put(req, "fw", buf); + } else { + sprintf(buf, "%d", bp->fw_version >> 16); + err = devlink_info_version_running_put(req, "loader", buf); + } + if (err) + return err; - if (ver & 0xffff) { - sprintf(buf, "%d", ver); - err = devlink_info_version_running_put(req, - "fw", - buf); - } else { - sprintf(buf, "%d", ver >> 16); - err = devlink_info_version_running_put(req, - "loader", - buf); - } - if (err) - return err; + if (!bp->has_eeprom_data) { + ptp_ocp_read_eeprom(bp); + if (!bp->has_eeprom_data) + return 0; } - if (!bp->has_serial) - ptp_ocp_get_serial_number(bp); + sprintf(buf, "%pM", bp->serial); + err = devlink_info_serial_number_put(req, buf); + if (err) + return err; - if (bp->has_serial) { - sprintf(buf, "%pM", bp->serial); - err = devlink_info_serial_number_put(req, buf); - if (err) - return err; - } + err = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, + bp->board_id); + if (err) + return err; return 0; } @@ -1240,6 +1508,137 @@ ptp_ocp_register_i2c(struct ptp_ocp *bp, struct ocp_resource *r) return 0; } +/* The expectation is that this is triggered only on error. */ +static irqreturn_t +ptp_ocp_signal_irq(int irq, void *priv) +{ + struct ptp_ocp_ext_src *ext = priv; + struct signal_reg __iomem *reg = ext->mem; + struct ptp_ocp *bp = ext->bp; + u32 enable, status; + int gen; + + gen = ext->info->index - 1; + + enable = ioread32(®->enable); + status = ioread32(®->status); + + /* disable generator on error */ + if (status || !enable) { + iowrite32(0, ®->intr_mask); + iowrite32(0, ®->enable); + bp->signal[gen].running = false; + } + + iowrite32(0, ®->intr); /* ack interrupt */ + + return IRQ_HANDLED; +} + +static int +ptp_ocp_signal_set(struct ptp_ocp *bp, int gen, struct ptp_ocp_signal *s) +{ + struct ptp_system_timestamp sts; + struct timespec64 ts; + ktime_t start_ns; + int err; + + if (!s->period) + return 0; + + if (!s->pulse) + s->pulse = ktime_divns(s->period * s->duty, 100); + + err = ptp_ocp_gettimex(&bp->ptp_info, &ts, &sts); + if (err) + return err; + + start_ns = ktime_set(ts.tv_sec, ts.tv_nsec) + NSEC_PER_MSEC; + if (!s->start) { + /* roundup() does not work on 32-bit systems */ + s->start = DIV_ROUND_UP_ULL(start_ns, s->period); + s->start = ktime_add(s->start, s->phase); + } + + if (s->duty < 1 || s->duty > 99) + return -EINVAL; + + if (s->pulse < 1 || s->pulse > s->period) + return -EINVAL; + + if (s->start < start_ns) + return -EINVAL; + + bp->signal[gen] = *s; + + return 0; +} + +static int +ptp_ocp_signal_from_perout(struct ptp_ocp *bp, int gen, + struct ptp_perout_request *req) +{ + struct ptp_ocp_signal s = { }; + + s.polarity = bp->signal[gen].polarity; + s.period = ktime_set(req->period.sec, req->period.nsec); + if (!s.period) + return 0; + + if (req->flags & PTP_PEROUT_DUTY_CYCLE) { + s.pulse = ktime_set(req->on.sec, req->on.nsec); + s.duty = ktime_divns(s.pulse * 100, s.period); + } + + if (req->flags & PTP_PEROUT_PHASE) + s.phase = ktime_set(req->phase.sec, req->phase.nsec); + else + s.start = ktime_set(req->start.sec, req->start.nsec); + + return ptp_ocp_signal_set(bp, gen, &s); +} + +static int +ptp_ocp_signal_enable(void *priv, u32 req, bool enable) +{ + struct ptp_ocp_ext_src *ext = priv; + struct signal_reg __iomem *reg = ext->mem; + struct ptp_ocp *bp = ext->bp; + struct timespec64 ts; + int gen; + + gen = ext->info->index - 1; + + iowrite32(0, ®->intr_mask); + iowrite32(0, ®->enable); + bp->signal[gen].running = false; + if (!enable) + return 0; + + ts = ktime_to_timespec64(bp->signal[gen].start); + iowrite32(ts.tv_sec, ®->start_sec); + iowrite32(ts.tv_nsec, ®->start_ns); + + ts = ktime_to_timespec64(bp->signal[gen].period); + iowrite32(ts.tv_sec, ®->period_sec); + iowrite32(ts.tv_nsec, ®->period_ns); + + ts = ktime_to_timespec64(bp->signal[gen].pulse); + iowrite32(ts.tv_sec, ®->pulse_sec); + iowrite32(ts.tv_nsec, ®->pulse_ns); + + iowrite32(bp->signal[gen].polarity, ®->polarity); + iowrite32(0, ®->repeat_count); + + iowrite32(0, ®->intr); /* clear interrupt state */ + iowrite32(1, ®->intr_mask); /* enable interrupt */ + iowrite32(3, ®->enable); /* valid & enable */ + + bp->signal[gen].running = true; + + return 0; +} + static irqreturn_t ptp_ocp_ts_irq(int irq, void *priv) { @@ -1367,7 +1766,7 @@ ptp_ocp_serial_line(struct ptp_ocp *bp, struct ocp_resource *r) uart.port.mapbase = pci_resource_start(pdev, 0) + r->offset; uart.port.irq = pci_irq_vector(pdev, r->irq_vec); uart.port.uartclk = 50000000; - uart.port.flags = UPF_FIXED_TYPE | UPF_IOREMAP; + uart.port.flags = UPF_FIXED_TYPE | UPF_IOREMAP | UPF_NO_THRE_TEST; uart.port.type = PORT_16550A; return serial8250_register_8250_port(&uart); @@ -1412,14 +1811,115 @@ ptp_ocp_nmea_out_init(struct ptp_ocp *bp) iowrite32(1, &bp->nmea_out->ctrl); /* enable */ } +static void +_ptp_ocp_signal_init(struct ptp_ocp_signal *s, struct signal_reg __iomem *reg) +{ + u32 val; + + iowrite32(0, ®->enable); /* disable */ + + val = ioread32(®->polarity); + s->polarity = val ? true : false; + s->duty = 50; +} + +static void +ptp_ocp_signal_init(struct ptp_ocp *bp) +{ + int i; + + for (i = 0; i < 4; i++) + if (bp->signal_out[i]) + _ptp_ocp_signal_init(&bp->signal[i], + bp->signal_out[i]->mem); +} + +static void +ptp_ocp_sma_init(struct ptp_ocp *bp) +{ + u32 reg; + int i; + + /* defaults */ + bp->sma[0].mode = SMA_MODE_IN; + bp->sma[1].mode = SMA_MODE_IN; + bp->sma[2].mode = SMA_MODE_OUT; + bp->sma[3].mode = SMA_MODE_OUT; + + /* If no SMA1 map, the pin functions and directions are fixed. */ + if (!bp->sma_map1) { + for (i = 0; i < 4; i++) { + bp->sma[i].fixed_fcn = true; + bp->sma[i].fixed_dir = true; + } + return; + } + + /* If SMA2 GPIO output map is all 1, it is not present. + * This indicates the firmware has fixed direction SMA pins. + */ + reg = ioread32(&bp->sma_map2->gpio2); + if (reg == 0xffffffff) { + for (i = 0; i < 4; i++) + bp->sma[i].fixed_dir = true; + } else { + reg = ioread32(&bp->sma_map1->gpio1); + bp->sma[0].mode = reg & BIT(15) ? SMA_MODE_IN : SMA_MODE_OUT; + bp->sma[1].mode = reg & BIT(31) ? SMA_MODE_IN : SMA_MODE_OUT; + + reg = ioread32(&bp->sma_map1->gpio2); + bp->sma[2].mode = reg & BIT(15) ? SMA_MODE_OUT : SMA_MODE_IN; + bp->sma[3].mode = reg & BIT(31) ? SMA_MODE_OUT : SMA_MODE_IN; + } +} + +static int +ptp_ocp_fb_set_pins(struct ptp_ocp *bp) +{ + struct ptp_pin_desc *config; + int i; + + config = kzalloc(sizeof(*config) * 4, GFP_KERNEL); + if (!config) + return -ENOMEM; + + for (i = 0; i < 4; i++) { + sprintf(config[i].name, "sma%d", i + 1); + config[i].index = i; + } + + bp->ptp_info.n_pins = 4; + bp->ptp_info.pin_config = config; + + return 0; +} + /* FB specific board initializers; last "resource" registered. */ static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r) { + int ver, err; + bp->flash_start = 1024 * 4096; + bp->eeprom_map = fb_eeprom_map; + bp->fw_version = ioread32(&bp->image->version); + bp->attr_tbl = fb_timecard_groups; + bp->fw_cap = OCP_CAP_BASIC; + + ver = bp->fw_version & 0xffff; + if (ver >= 19) + bp->fw_cap |= OCP_CAP_SIGNAL; + if (ver >= 20) + bp->fw_cap |= OCP_CAP_FREQ; ptp_ocp_tod_init(bp); ptp_ocp_nmea_out_init(bp); + ptp_ocp_sma_init(bp); + ptp_ocp_signal_init(bp); + + err = ptp_ocp_fb_set_pins(bp); + if (err) + return err; return ptp_ocp_init_clock(bp); } @@ -1521,38 +2021,8 @@ __handle_signal_inputs(struct ptp_ocp *bp, u32 val) * ANT4 == sma4 (out) */ -enum ptp_ocp_sma_mode { - SMA_MODE_IN, - SMA_MODE_OUT, -}; - -static struct ptp_ocp_sma_connector { - enum ptp_ocp_sma_mode mode; - bool fixed_mode; - u16 default_out_idx; -} ptp_ocp_sma_map[4] = { - { - .mode = SMA_MODE_IN, - .fixed_mode = true, - }, - { - .mode = SMA_MODE_IN, - .fixed_mode = true, - }, - { - .mode = SMA_MODE_OUT, - .fixed_mode = true, - .default_out_idx = 0, /* 10Mhz */ - }, - { - .mode = SMA_MODE_OUT, - .fixed_mode = true, - .default_out_idx = 1, /* PHC */ - }, -}; - static ssize_t -ptp_ocp_show_output(u32 val, char *buf, int default_idx) +ptp_ocp_show_output(u32 val, char *buf, int def_val) { const char *name; ssize_t count; @@ -1560,13 +2030,13 @@ ptp_ocp_show_output(u32 val, char *buf, int default_idx) count = sysfs_emit(buf, "OUT: "); name = ptp_ocp_select_name_from_val(ptp_ocp_sma_out, val); if (!name) - name = ptp_ocp_sma_out[default_idx].name; + name = ptp_ocp_select_name_from_val(ptp_ocp_sma_out, def_val); count += sysfs_emit_at(buf, count, "%s\n", name); return count; } static ssize_t -ptp_ocp_show_inputs(u32 val, char *buf, const char *zero_in) +ptp_ocp_show_inputs(u32 val, char *buf, int def_val) { const char *name; ssize_t count; @@ -1579,8 +2049,10 @@ ptp_ocp_show_inputs(u32 val, char *buf, const char *zero_in) count += sysfs_emit_at(buf, count, "%s ", name); } } - if (!val && zero_in) - count += sysfs_emit_at(buf, count, "%s ", zero_in); + if (!val && def_val >= 0) { + name = ptp_ocp_select_name_from_val(ptp_ocp_sma_in, def_val); + count += sysfs_emit_at(buf, count, "%s ", name); + } if (count) count--; count += sysfs_emit_at(buf, count, "\n"); @@ -1605,7 +2077,7 @@ sma_parse_inputs(const char *buf, enum ptp_ocp_sma_mode *mode) idx = 0; dir = *mode == SMA_MODE_IN ? 0 : 1; - if (!strcasecmp("IN:", argv[idx])) { + if (!strcasecmp("IN:", argv[0])) { dir = 0; idx++; } @@ -1626,102 +2098,126 @@ sma_parse_inputs(const char *buf, enum ptp_ocp_sma_mode *mode) return ret; } -static ssize_t -ptp_ocp_sma_show(struct ptp_ocp *bp, int sma_nr, u32 val, char *buf, - const char *zero_in) +static u32 +ptp_ocp_sma_get(struct ptp_ocp *bp, int sma_nr, enum ptp_ocp_sma_mode mode) { - struct ptp_ocp_sma_connector *sma = &ptp_ocp_sma_map[sma_nr - 1]; + u32 __iomem *gpio; + u32 shift; - if (sma->mode == SMA_MODE_IN) - return ptp_ocp_show_inputs(val, buf, zero_in); + if (bp->sma[sma_nr - 1].fixed_fcn) + return (sma_nr - 1) & 1; - return ptp_ocp_show_output(val, buf, sma->default_out_idx); + if (mode == SMA_MODE_IN) + gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1; + else + gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2; + shift = sma_nr & 1 ? 0 : 16; + + return (ioread32(gpio) >> shift) & 0xffff; +} + +static ssize_t +ptp_ocp_sma_show(struct ptp_ocp *bp, int sma_nr, char *buf, + int default_in_val, int default_out_val) +{ + struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1]; + u32 val; + + val = ptp_ocp_sma_get(bp, sma_nr, sma->mode) & SMA_SELECT_MASK; + + if (sma->mode == SMA_MODE_IN) { + if (sma->disabled) + val = SMA_DISABLE; + return ptp_ocp_show_inputs(val, buf, default_in_val); + } + + return ptp_ocp_show_output(val, buf, default_out_val); } static ssize_t sma1_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ptp_ocp *bp = dev_get_drvdata(dev); - u32 val; - val = ioread32(&bp->sma->gpio1) & 0x3f; - return ptp_ocp_sma_show(bp, 1, val, buf, ptp_ocp_sma_in[0].name); + return ptp_ocp_sma_show(bp, 1, buf, 0, 1); } static ssize_t sma2_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ptp_ocp *bp = dev_get_drvdata(dev); - u32 val; - val = (ioread32(&bp->sma->gpio1) >> 16) & 0x3f; - return ptp_ocp_sma_show(bp, 2, val, buf, NULL); + return ptp_ocp_sma_show(bp, 2, buf, -1, 1); } static ssize_t sma3_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ptp_ocp *bp = dev_get_drvdata(dev); - u32 val; - val = ioread32(&bp->sma->gpio2) & 0x3f; - return ptp_ocp_sma_show(bp, 3, val, buf, NULL); + return ptp_ocp_sma_show(bp, 3, buf, -1, 0); } static ssize_t sma4_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ptp_ocp *bp = dev_get_drvdata(dev); - u32 val; - val = (ioread32(&bp->sma->gpio2) >> 16) & 0x3f; - return ptp_ocp_sma_show(bp, 4, val, buf, NULL); + return ptp_ocp_sma_show(bp, 4, buf, -1, 1); } static void -ptp_ocp_sma_store_output(struct ptp_ocp *bp, u32 val, u32 shift) +ptp_ocp_sma_store_output(struct ptp_ocp *bp, int sma_nr, u32 val) { + u32 reg, mask, shift; unsigned long flags; - u32 gpio, mask; + u32 __iomem *gpio; + + gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2; + shift = sma_nr & 1 ? 0 : 16; mask = 0xffff << (16 - shift); spin_lock_irqsave(&bp->lock, flags); - gpio = ioread32(&bp->sma->gpio2); - gpio = (gpio & mask) | (val << shift); + reg = ioread32(gpio); + reg = (reg & mask) | (val << shift); - __handle_signal_outputs(bp, gpio); + __handle_signal_outputs(bp, reg); - iowrite32(gpio, &bp->sma->gpio2); + iowrite32(reg, gpio); spin_unlock_irqrestore(&bp->lock, flags); } static void -ptp_ocp_sma_store_inputs(struct ptp_ocp *bp, u32 val, u32 shift) +ptp_ocp_sma_store_inputs(struct ptp_ocp *bp, int sma_nr, u32 val) { + u32 reg, mask, shift; unsigned long flags; - u32 gpio, mask; + u32 __iomem *gpio; + + gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1; + shift = sma_nr & 1 ? 0 : 16; mask = 0xffff << (16 - shift); spin_lock_irqsave(&bp->lock, flags); - gpio = ioread32(&bp->sma->gpio1); - gpio = (gpio & mask) | (val << shift); + reg = ioread32(gpio); + reg = (reg & mask) | (val << shift); - __handle_signal_inputs(bp, gpio); + __handle_signal_inputs(bp, reg); - iowrite32(gpio, &bp->sma->gpio1); + iowrite32(reg, gpio); spin_unlock_irqrestore(&bp->lock, flags); } -static ssize_t -ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr, u32 shift) +static int +ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr) { - struct ptp_ocp_sma_connector *sma = &ptp_ocp_sma_map[sma_nr - 1]; + struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1]; enum ptp_ocp_sma_mode mode; int val; @@ -1730,18 +2226,35 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr, u32 shift) if (val < 0) return val; - if (mode != sma->mode && sma->fixed_mode) + if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE)) return -EOPNOTSUPP; - if (mode != sma->mode) { - pr_err("Mode changes not supported yet.\n"); - return -EOPNOTSUPP; + if (sma->fixed_fcn) { + if (val != ((sma_nr - 1) & 1)) + return -EOPNOTSUPP; + return 0; } - if (sma->mode == SMA_MODE_IN) - ptp_ocp_sma_store_inputs(bp, val, shift); + sma->disabled = !!(val & SMA_DISABLE); + + if (mode != sma->mode) { + if (mode == SMA_MODE_IN) + ptp_ocp_sma_store_output(bp, sma_nr, 0); + else + ptp_ocp_sma_store_inputs(bp, sma_nr, 0); + sma->mode = mode; + } + + if (!sma->fixed_dir) + val |= SMA_ENABLE; /* add enable bit */ + + if (sma->disabled) + val = 0; + + if (mode == SMA_MODE_IN) + ptp_ocp_sma_store_inputs(bp, sma_nr, val); else - ptp_ocp_sma_store_output(bp, val, shift); + ptp_ocp_sma_store_output(bp, sma_nr, val); return 0; } @@ -1753,7 +2266,7 @@ sma1_store(struct device *dev, struct device_attribute *attr, struct ptp_ocp *bp = dev_get_drvdata(dev); int err; - err = ptp_ocp_sma_store(bp, buf, 1, 0); + err = ptp_ocp_sma_store(bp, buf, 1); return err ? err : count; } @@ -1764,7 +2277,7 @@ sma2_store(struct device *dev, struct device_attribute *attr, struct ptp_ocp *bp = dev_get_drvdata(dev); int err; - err = ptp_ocp_sma_store(bp, buf, 2, 16); + err = ptp_ocp_sma_store(bp, buf, 2); return err ? err : count; } @@ -1775,7 +2288,7 @@ sma3_store(struct device *dev, struct device_attribute *attr, struct ptp_ocp *bp = dev_get_drvdata(dev); int err; - err = ptp_ocp_sma_store(bp, buf, 3, 0); + err = ptp_ocp_sma_store(bp, buf, 3); return err ? err : count; } @@ -1786,7 +2299,7 @@ sma4_store(struct device *dev, struct device_attribute *attr, struct ptp_ocp *bp = dev_get_drvdata(dev); int err; - err = ptp_ocp_sma_store(bp, buf, 4, 16); + err = ptp_ocp_sma_store(bp, buf, 4); return err ? err : count; } static DEVICE_ATTR_RW(sma1); @@ -1810,13 +2323,263 @@ available_sma_outputs_show(struct device *dev, } static DEVICE_ATTR_RO(available_sma_outputs); +#define EXT_ATTR_RO(_group, _name, _val) \ + struct dev_ext_attribute dev_attr_##_group##_val##_##_name = \ + { __ATTR_RO(_name), (void *)_val } +#define EXT_ATTR_RW(_group, _name, _val) \ + struct dev_ext_attribute dev_attr_##_group##_val##_##_name = \ + { __ATTR_RW(_name), (void *)_val } +#define to_ext_attr(x) container_of(x, struct dev_ext_attribute, attr) + +/* period [duty [phase [polarity]]] */ +static ssize_t +signal_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + struct ptp_ocp_signal s = { }; + int gen = (uintptr_t)ea->var; + int argc, err; + char **argv; + + argv = argv_split(GFP_KERNEL, buf, &argc); + if (!argv) + return -ENOMEM; + + err = -EINVAL; + s.duty = bp->signal[gen].duty; + s.phase = bp->signal[gen].phase; + s.period = bp->signal[gen].period; + s.polarity = bp->signal[gen].polarity; + + switch (argc) { + case 4: + argc--; + err = kstrtobool(argv[argc], &s.polarity); + if (err) + goto out; + fallthrough; + case 3: + argc--; + err = kstrtou64(argv[argc], 0, &s.phase); + if (err) + goto out; + fallthrough; + case 2: + argc--; + err = kstrtoint(argv[argc], 0, &s.duty); + if (err) + goto out; + fallthrough; + case 1: + argc--; + err = kstrtou64(argv[argc], 0, &s.period); + if (err) + goto out; + break; + default: + goto out; + } + + err = ptp_ocp_signal_set(bp, gen, &s); + if (err) + goto out; + + err = ptp_ocp_signal_enable(bp->signal_out[gen], gen, s.period != 0); + +out: + argv_free(argv); + return err ? err : count; +} + +static ssize_t +signal_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + struct ptp_ocp_signal *signal; + struct timespec64 ts; + ssize_t count; + int i; + + i = (uintptr_t)ea->var; + signal = &bp->signal[i]; + + count = sysfs_emit(buf, "%llu %d %llu %d", signal->period, + signal->duty, signal->phase, signal->polarity); + + ts = ktime_to_timespec64(signal->start); + count += sysfs_emit_at(buf, count, " %ptT TAI\n", &ts); + + return count; +} +static EXT_ATTR_RW(signal, signal, 0); +static EXT_ATTR_RW(signal, signal, 1); +static EXT_ATTR_RW(signal, signal, 2); +static EXT_ATTR_RW(signal, signal, 3); + +static ssize_t +duty_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + int i = (uintptr_t)ea->var; + + return sysfs_emit(buf, "%d\n", bp->signal[i].duty); +} +static EXT_ATTR_RO(signal, duty, 0); +static EXT_ATTR_RO(signal, duty, 1); +static EXT_ATTR_RO(signal, duty, 2); +static EXT_ATTR_RO(signal, duty, 3); + +static ssize_t +period_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + int i = (uintptr_t)ea->var; + + return sysfs_emit(buf, "%llu\n", bp->signal[i].period); +} +static EXT_ATTR_RO(signal, period, 0); +static EXT_ATTR_RO(signal, period, 1); +static EXT_ATTR_RO(signal, period, 2); +static EXT_ATTR_RO(signal, period, 3); + +static ssize_t +phase_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + int i = (uintptr_t)ea->var; + + return sysfs_emit(buf, "%llu\n", bp->signal[i].phase); +} +static EXT_ATTR_RO(signal, phase, 0); +static EXT_ATTR_RO(signal, phase, 1); +static EXT_ATTR_RO(signal, phase, 2); +static EXT_ATTR_RO(signal, phase, 3); + +static ssize_t +polarity_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + int i = (uintptr_t)ea->var; + + return sysfs_emit(buf, "%d\n", bp->signal[i].polarity); +} +static EXT_ATTR_RO(signal, polarity, 0); +static EXT_ATTR_RO(signal, polarity, 1); +static EXT_ATTR_RO(signal, polarity, 2); +static EXT_ATTR_RO(signal, polarity, 3); + +static ssize_t +running_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + int i = (uintptr_t)ea->var; + + return sysfs_emit(buf, "%d\n", bp->signal[i].running); +} +static EXT_ATTR_RO(signal, running, 0); +static EXT_ATTR_RO(signal, running, 1); +static EXT_ATTR_RO(signal, running, 2); +static EXT_ATTR_RO(signal, running, 3); + +static ssize_t +start_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + int i = (uintptr_t)ea->var; + struct timespec64 ts; + + ts = ktime_to_timespec64(bp->signal[i].start); + return sysfs_emit(buf, "%llu.%lu\n", ts.tv_sec, ts.tv_nsec); +} +static EXT_ATTR_RO(signal, start, 0); +static EXT_ATTR_RO(signal, start, 1); +static EXT_ATTR_RO(signal, start, 2); +static EXT_ATTR_RO(signal, start, 3); + +static ssize_t +seconds_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + int idx = (uintptr_t)ea->var; + u32 val; + int err; + + err = kstrtou32(buf, 0, &val); + if (err) + return err; + if (val > 0xff) + return -EINVAL; + + if (val) + val = (val << 8) | 0x1; + + iowrite32(val, &bp->freq_in[idx]->ctrl); + + return count; +} + +static ssize_t +seconds_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + int idx = (uintptr_t)ea->var; + u32 val; + + val = ioread32(&bp->freq_in[idx]->ctrl); + if (val & 1) + val = (val >> 8) & 0xff; + else + val = 0; + + return sysfs_emit(buf, "%u\n", val); +} +static EXT_ATTR_RW(freq, seconds, 0); +static EXT_ATTR_RW(freq, seconds, 1); +static EXT_ATTR_RW(freq, seconds, 2); +static EXT_ATTR_RW(freq, seconds, 3); + +static ssize_t +frequency_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + int idx = (uintptr_t)ea->var; + u32 val; + + val = ioread32(&bp->freq_in[idx]->status); + if (val & FREQ_STATUS_ERROR) + return sysfs_emit(buf, "error\n"); + if (val & FREQ_STATUS_OVERRUN) + return sysfs_emit(buf, "overrun\n"); + if (val & FREQ_STATUS_VALID) + return sysfs_emit(buf, "%lu\n", val & FREQ_STATUS_MASK); + return 0; +} +static EXT_ATTR_RO(freq, frequency, 0); +static EXT_ATTR_RO(freq, frequency, 1); +static EXT_ATTR_RO(freq, frequency, 2); +static EXT_ATTR_RO(freq, frequency, 3); + static ssize_t serialnum_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ptp_ocp *bp = dev_get_drvdata(dev); - if (!bp->has_serial) - ptp_ocp_get_serial_number(bp); + if (!bp->has_eeprom_data) + ptp_ocp_read_eeprom(bp); return sysfs_emit(buf, "%pM\n", bp->serial); } @@ -1974,7 +2737,122 @@ available_clock_sources_show(struct device *dev, } static DEVICE_ATTR_RO(available_clock_sources); -static struct attribute *timecard_attrs[] = { +static ssize_t +clock_status_drift_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + int res; + + val = ioread32(&bp->reg->status_drift); + res = (val & ~INT_MAX) ? -1 : 1; + res *= (val & INT_MAX); + return sysfs_emit(buf, "%d\n", res); +} +static DEVICE_ATTR_RO(clock_status_drift); + +static ssize_t +clock_status_offset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + int res; + + val = ioread32(&bp->reg->status_offset); + res = (val & ~INT_MAX) ? -1 : 1; + res *= (val & INT_MAX); + return sysfs_emit(buf, "%d\n", res); +} +static DEVICE_ATTR_RO(clock_status_offset); + +static ssize_t +tod_correction_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + int res; + + val = ioread32(&bp->tod->adj_sec); + res = (val & ~INT_MAX) ? -1 : 1; + res *= (val & INT_MAX); + return sysfs_emit(buf, "%d\n", res); +} + +static ssize_t +tod_correction_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + unsigned long flags; + int err, res; + u32 val = 0; + + err = kstrtos32(buf, 0, &res); + if (err) + return err; + if (res < 0) { + res *= -1; + val |= BIT(31); + } + val |= res; + + spin_lock_irqsave(&bp->lock, flags); + iowrite32(val, &bp->tod->adj_sec); + spin_unlock_irqrestore(&bp->lock, flags); + + return count; +} +static DEVICE_ATTR_RW(tod_correction); + +#define _DEVICE_SIGNAL_GROUP_ATTRS(_nr) \ + static struct attribute *fb_timecard_signal##_nr##_attrs[] = { \ + &dev_attr_signal##_nr##_signal.attr.attr, \ + &dev_attr_signal##_nr##_duty.attr.attr, \ + &dev_attr_signal##_nr##_phase.attr.attr, \ + &dev_attr_signal##_nr##_period.attr.attr, \ + &dev_attr_signal##_nr##_polarity.attr.attr, \ + &dev_attr_signal##_nr##_running.attr.attr, \ + &dev_attr_signal##_nr##_start.attr.attr, \ + NULL, \ + } + +#define DEVICE_SIGNAL_GROUP(_name, _nr) \ + _DEVICE_SIGNAL_GROUP_ATTRS(_nr); \ + static const struct attribute_group \ + fb_timecard_signal##_nr##_group = { \ + .name = #_name, \ + .attrs = fb_timecard_signal##_nr##_attrs, \ +} + +DEVICE_SIGNAL_GROUP(gen1, 0); +DEVICE_SIGNAL_GROUP(gen2, 1); +DEVICE_SIGNAL_GROUP(gen3, 2); +DEVICE_SIGNAL_GROUP(gen4, 3); + +#define _DEVICE_FREQ_GROUP_ATTRS(_nr) \ + static struct attribute *fb_timecard_freq##_nr##_attrs[] = { \ + &dev_attr_freq##_nr##_seconds.attr.attr, \ + &dev_attr_freq##_nr##_frequency.attr.attr, \ + NULL, \ + } + +#define DEVICE_FREQ_GROUP(_name, _nr) \ + _DEVICE_FREQ_GROUP_ATTRS(_nr); \ + static const struct attribute_group \ + fb_timecard_freq##_nr##_group = { \ + .name = #_name, \ + .attrs = fb_timecard_freq##_nr##_attrs, \ +} + +DEVICE_FREQ_GROUP(freq1, 0); +DEVICE_FREQ_GROUP(freq2, 1); +DEVICE_FREQ_GROUP(freq3, 2); +DEVICE_FREQ_GROUP(freq4, 3); + +static struct attribute *fb_timecard_attrs[] = { &dev_attr_serialnum.attr, &dev_attr_gnss_sync.attr, &dev_attr_clock_source.attr, @@ -1985,38 +2863,119 @@ static struct attribute *timecard_attrs[] = { &dev_attr_sma4.attr, &dev_attr_available_sma_inputs.attr, &dev_attr_available_sma_outputs.attr, + &dev_attr_clock_status_drift.attr, + &dev_attr_clock_status_offset.attr, &dev_attr_irig_b_mode.attr, &dev_attr_utc_tai_offset.attr, &dev_attr_ts_window_adjust.attr, + &dev_attr_tod_correction.attr, NULL, }; -ATTRIBUTE_GROUPS(timecard); +static const struct attribute_group fb_timecard_group = { + .attrs = fb_timecard_attrs, +}; +static const struct ocp_attr_group fb_timecard_groups[] = { + { .cap = OCP_CAP_BASIC, .group = &fb_timecard_group }, + { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal0_group }, + { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal1_group }, + { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal2_group }, + { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal3_group }, + { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq0_group }, + { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq1_group }, + { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq2_group }, + { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq3_group }, + { }, +}; -static const char * -gpio_map(u32 gpio, u32 bit, const char *pri, const char *sec, const char *def) +static void +gpio_input_map(char *buf, struct ptp_ocp *bp, u16 map[][2], u16 bit, + const char *def) { - const char *ans; + int i; - if (gpio & (1 << bit)) - ans = pri; - else if (gpio & (1 << (bit + 16))) - ans = sec; - else - ans = def; - return ans; + for (i = 0; i < 4; i++) { + if (bp->sma[i].mode != SMA_MODE_IN) + continue; + if (map[i][0] & (1 << bit)) { + sprintf(buf, "sma%d", i + 1); + return; + } + } + if (!def) + def = "----"; + strcpy(buf, def); } static void -gpio_multi_map(char *buf, u32 gpio, u32 bit, - const char *pri, const char *sec, const char *def) +gpio_output_map(char *buf, struct ptp_ocp *bp, u16 map[][2], u16 bit) { char *ans = buf; + int i; - strcpy(ans, def); - if (gpio & (1 << bit)) - ans += sprintf(ans, "%s ", pri); - if (gpio & (1 << (bit + 16))) - ans += sprintf(ans, "%s ", sec); + strcpy(ans, "----"); + for (i = 0; i < 4; i++) { + if (bp->sma[i].mode != SMA_MODE_OUT) + continue; + if (map[i][1] & (1 << bit)) + ans += sprintf(ans, "sma%d ", i + 1); + } +} + +static void +_signal_summary_show(struct seq_file *s, struct ptp_ocp *bp, int nr) +{ + struct signal_reg __iomem *reg = bp->signal_out[nr]->mem; + struct ptp_ocp_signal *signal = &bp->signal[nr]; + char label[8]; + bool on; + u32 val; + + if (!signal) + return; + + on = signal->running; + sprintf(label, "GEN%d", nr + 1); + seq_printf(s, "%7s: %s, period:%llu duty:%d%% phase:%llu pol:%d", + label, on ? " ON" : "OFF", + signal->period, signal->duty, signal->phase, + signal->polarity); + + val = ioread32(®->enable); + seq_printf(s, " [%x", val); + val = ioread32(®->status); + seq_printf(s, " %x]", val); + + seq_printf(s, " start:%llu\n", signal->start); +} + +static void +_frequency_summary_show(struct seq_file *s, int nr, + struct frequency_reg __iomem *reg) +{ + char label[8]; + bool on; + u32 val; + + if (!reg) + return; + + sprintf(label, "FREQ%d", nr + 1); + val = ioread32(®->ctrl); + on = val & 1; + val = (val >> 8) & 0xff; + seq_printf(s, "%7s: %s, sec:%u", + label, + on ? " ON" : "OFF", + val); + + val = ioread32(®->status); + if (val & FREQ_STATUS_ERROR) + seq_printf(s, ", error"); + if (val & FREQ_STATUS_OVERRUN) + seq_printf(s, ", overrun"); + if (val & FREQ_STATUS_VALID) + seq_printf(s, ", freq %lu Hz", val & FREQ_STATUS_MASK); + seq_printf(s, " reg:%x\n", val); } static int @@ -2024,40 +2983,72 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) { struct device *dev = s->private; struct ptp_system_timestamp sts; - u32 sma_in, sma_out, ctrl, val; struct ts_reg __iomem *ts_reg; struct timespec64 ts; struct ptp_ocp *bp; - const char *src; + u16 sma_val[4][2]; + char *src, *buf; + u32 ctrl, val; bool on, map; - char *buf; + int i; buf = (char *)__get_free_page(GFP_KERNEL); if (!buf) return -ENOMEM; bp = dev_get_drvdata(dev); - sma_in = ioread32(&bp->sma->gpio1); - sma_out = ioread32(&bp->sma->gpio2); seq_printf(s, "%7s: /dev/ptp%d\n", "PTP", ptp_clock_index(bp->ptp)); + if (bp->gnss_port != -1) + seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS1", bp->gnss_port); + if (bp->gnss2_port != -1) + seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS2", bp->gnss2_port); + if (bp->mac_port != -1) + seq_printf(s, "%7s: /dev/ttyS%d\n", "MAC", bp->mac_port); + if (bp->nmea_port != -1) + seq_printf(s, "%7s: /dev/ttyS%d\n", "NMEA", bp->nmea_port); + + memset(sma_val, 0xff, sizeof(sma_val)); + if (bp->sma_map1) { + u32 reg; + + reg = ioread32(&bp->sma_map1->gpio1); + sma_val[0][0] = reg & 0xffff; + sma_val[1][0] = reg >> 16; + + reg = ioread32(&bp->sma_map1->gpio2); + sma_val[2][1] = reg & 0xffff; + sma_val[3][1] = reg >> 16; + + reg = ioread32(&bp->sma_map2->gpio1); + sma_val[2][0] = reg & 0xffff; + sma_val[3][0] = reg >> 16; + + reg = ioread32(&bp->sma_map2->gpio2); + sma_val[0][1] = reg & 0xffff; + sma_val[1][1] = reg >> 16; + } sma1_show(dev, NULL, buf); - seq_printf(s, " sma1: %s", buf); + seq_printf(s, " sma1: %04x,%04x %s", + sma_val[0][0], sma_val[0][1], buf); sma2_show(dev, NULL, buf); - seq_printf(s, " sma2: %s", buf); + seq_printf(s, " sma2: %04x,%04x %s", + sma_val[1][0], sma_val[1][1], buf); sma3_show(dev, NULL, buf); - seq_printf(s, " sma3: %s", buf); + seq_printf(s, " sma3: %04x,%04x %s", + sma_val[2][0], sma_val[2][1], buf); sma4_show(dev, NULL, buf); - seq_printf(s, " sma4: %s", buf); + seq_printf(s, " sma4: %04x,%04x %s", + sma_val[3][0], sma_val[3][1], buf); if (bp->ts0) { ts_reg = bp->ts0->mem; on = ioread32(&ts_reg->enable); - src = "GNSS"; + src = "GNSS1"; seq_printf(s, "%7s: %s, src: %s\n", "TS0", on ? " ON" : "OFF", src); } @@ -2065,17 +3056,33 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) if (bp->ts1) { ts_reg = bp->ts1->mem; on = ioread32(&ts_reg->enable); - src = gpio_map(sma_in, 2, "sma1", "sma2", "----"); + gpio_input_map(buf, bp, sma_val, 2, NULL); seq_printf(s, "%7s: %s, src: %s\n", "TS1", - on ? " ON" : "OFF", src); + on ? " ON" : "OFF", buf); } if (bp->ts2) { ts_reg = bp->ts2->mem; on = ioread32(&ts_reg->enable); - src = gpio_map(sma_in, 3, "sma1", "sma2", "----"); + gpio_input_map(buf, bp, sma_val, 3, NULL); seq_printf(s, "%7s: %s, src: %s\n", "TS2", - on ? " ON" : "OFF", src); + on ? " ON" : "OFF", buf); + } + + if (bp->ts3) { + ts_reg = bp->ts3->mem; + on = ioread32(&ts_reg->enable); + gpio_input_map(buf, bp, sma_val, 6, NULL); + seq_printf(s, "%7s: %s, src: %s\n", "TS3", + on ? " ON" : "OFF", buf); + } + + if (bp->ts4) { + ts_reg = bp->ts4->mem; + on = ioread32(&ts_reg->enable); + gpio_input_map(buf, bp, sma_val, 7, NULL); + seq_printf(s, "%7s: %s, src: %s\n", "TS4", + on ? " ON" : "OFF", buf); } if (bp->pps) { @@ -2083,7 +3090,7 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) src = "PHC"; on = ioread32(&ts_reg->enable); map = !!(bp->pps_req_map & OCP_REQ_TIMESTAMP); - seq_printf(s, "%7s: %s, src: %s\n", "TS3", + seq_printf(s, "%7s: %s, src: %s\n", "TS5", on && map ? " ON" : "OFF", src); map = !!(bp->pps_req_map & OCP_REQ_PPS); @@ -2091,11 +3098,19 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) on && map ? " ON" : "OFF", src); } + if (bp->fw_cap & OCP_CAP_SIGNAL) + for (i = 0; i < 4; i++) + _signal_summary_show(s, bp, i); + + if (bp->fw_cap & OCP_CAP_FREQ) + for (i = 0; i < 4; i++) + _frequency_summary_show(s, i, bp->freq_in[i]); + if (bp->irig_out) { ctrl = ioread32(&bp->irig_out->ctrl); on = ctrl & IRIG_M_CTRL_ENABLE; val = ioread32(&bp->irig_out->status); - gpio_multi_map(buf, sma_out, 4, "sma3", "sma4", "----"); + gpio_output_map(buf, bp, sma_val, 4); seq_printf(s, "%7s: %s, error: %d, mode %d, out: %s\n", "IRIG", on ? " ON" : "OFF", val, (ctrl >> 16), buf); } @@ -2103,15 +3118,15 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) if (bp->irig_in) { on = ioread32(&bp->irig_in->ctrl) & IRIG_S_CTRL_ENABLE; val = ioread32(&bp->irig_in->status); - src = gpio_map(sma_in, 4, "sma1", "sma2", "----"); + gpio_input_map(buf, bp, sma_val, 4, NULL); seq_printf(s, "%7s: %s, error: %d, src: %s\n", "IRIG in", - on ? " ON" : "OFF", val, src); + on ? " ON" : "OFF", val, buf); } if (bp->dcf_out) { on = ioread32(&bp->dcf_out->ctrl) & DCF_M_CTRL_ENABLE; val = ioread32(&bp->dcf_out->status); - gpio_multi_map(buf, sma_out, 5, "sma3", "sma4", "----"); + gpio_output_map(buf, bp, sma_val, 5); seq_printf(s, "%7s: %s, error: %d, out: %s\n", "DCF", on ? " ON" : "OFF", val, buf); } @@ -2119,9 +3134,9 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) if (bp->dcf_in) { on = ioread32(&bp->dcf_in->ctrl) & DCF_S_CTRL_ENABLE; val = ioread32(&bp->dcf_in->status); - src = gpio_map(sma_in, 5, "sma1", "sma2", "----"); + gpio_input_map(buf, bp, sma_val, 5, NULL); seq_printf(s, "%7s: %s, error: %d, src: %s\n", "DCF in", - on ? " ON" : "OFF", val, src); + on ? " ON" : "OFF", val, buf); } if (bp->nmea_out) { @@ -2134,12 +3149,13 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) /* compute src for PPS1, used below. */ if (bp->pps_select) { val = ioread32(&bp->pps_select->gpio1); + src = &buf[80]; if (val & 0x01) - src = gpio_map(sma_in, 0, "sma1", "sma2", "----"); + gpio_input_map(src, bp, sma_val, 0, NULL); else if (val & 0x02) src = "MAC"; else if (val & 0x04) - src = "GNSS"; + src = "GNSS1"; else src = "----"; } else { @@ -2172,8 +3188,8 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) /* reuses PPS1 src from earlier */ seq_printf(s, "MAC PPS1 src: %s\n", src); - src = gpio_map(sma_in, 1, "sma1", "sma2", "GNSS2"); - seq_printf(s, "MAC PPS2 src: %s\n", src); + gpio_input_map(buf, bp, sma_val, 1, "GNSS2"); + seq_printf(s, "MAC PPS2 src: %s\n", buf); if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, &sts)) { struct timespec64 sys_ts; @@ -2200,6 +3216,57 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(ptp_ocp_summary); +static int +ptp_ocp_tod_status_show(struct seq_file *s, void *data) +{ + struct device *dev = s->private; + struct ptp_ocp *bp; + u32 val; + int idx; + + bp = dev_get_drvdata(dev); + + val = ioread32(&bp->tod->ctrl); + if (!(val & TOD_CTRL_ENABLE)) { + seq_printf(s, "TOD Slave disabled\n"); + return 0; + } + seq_printf(s, "TOD Slave enabled, Control Register 0x%08X\n", val); + + idx = val & TOD_CTRL_PROTOCOL ? 4 : 0; + idx += (val >> 16) & 3; + seq_printf(s, "Protocol %s\n", ptp_ocp_tod_proto_name(idx)); + + idx = (val >> TOD_CTRL_GNSS_SHIFT) & TOD_CTRL_GNSS_MASK; + seq_printf(s, "GNSS %s\n", ptp_ocp_tod_gnss_name(idx)); + + val = ioread32(&bp->tod->version); + seq_printf(s, "TOD Version %d.%d.%d\n", + val >> 24, (val >> 16) & 0xff, val & 0xffff); + + val = ioread32(&bp->tod->status); + seq_printf(s, "Status register: 0x%08X\n", val); + + val = ioread32(&bp->tod->adj_sec); + idx = (val & ~INT_MAX) ? -1 : 1; + idx *= (val & INT_MAX); + seq_printf(s, "Correction seconds: %d\n", idx); + + val = ioread32(&bp->tod->utc_status); + seq_printf(s, "UTC status register: 0x%08X\n", val); + seq_printf(s, "UTC offset: %d valid:%d\n", + val & TOD_STATUS_UTC_MASK, val & TOD_STATUS_UTC_VALID ? 1 : 0); + seq_printf(s, "Leap second info valid:%d, Leap second announce %d\n", + val & TOD_STATUS_LEAP_VALID ? 1 : 0, + val & TOD_STATUS_LEAP_ANNOUNCE ? 1 : 0); + + val = ioread32(&bp->tod->leap); + seq_printf(s, "Time to next leap second (in sec): %d\n", (s32) val); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ptp_ocp_tod_status); + static struct dentry *ptp_ocp_debugfs_root; static void @@ -2211,6 +3278,9 @@ ptp_ocp_debugfs_add_device(struct ptp_ocp *bp) bp->debug_root = d; debugfs_create_file("summary", 0444, bp->debug_root, &bp->dev, &ptp_ocp_summary_fops); + if (bp->tod) + debugfs_create_file("tod_status", 0444, bp->debug_root, + &bp->dev, &ptp_ocp_tod_status_fops); } static void @@ -2317,6 +3387,7 @@ ptp_ocp_complete(struct ptp_ocp *bp) { struct pps_device *pps; char buf[32]; + int i, err; if (bp->gnss_port != -1) { sprintf(buf, "ttyS%d", bp->gnss_port); @@ -2341,8 +3412,13 @@ ptp_ocp_complete(struct ptp_ocp *bp) if (pps) ptp_ocp_symlink(bp, pps->dev, "pps"); - if (device_add_groups(&bp->dev, timecard_groups)) - pr_err("device add groups failed\n"); + for (i = 0; bp->attr_tbl[i].cap; i++) { + if (!(bp->attr_tbl[i].cap & bp->fw_cap)) + continue; + err = sysfs_create_group(&bp->dev.kobj, bp->attr_tbl[i].group); + if (err) + return err; + } ptp_ocp_debugfs_add_device(bp); @@ -2389,20 +3465,15 @@ ptp_ocp_info(struct ptp_ocp *bp) u32 reg; ptp_ocp_phc_info(bp); - if (bp->tod) - ptp_ocp_tod_info(bp); - if (bp->image) { - u32 ver = ioread32(&bp->image->version); + dev_info(dev, "version %x\n", bp->fw_version); + if (bp->fw_version & 0xffff) + dev_info(dev, "regular image, version %d\n", + bp->fw_version & 0xffff); + else + dev_info(dev, "golden image, version %d\n", + bp->fw_version >> 16); - dev_info(dev, "version %x\n", ver); - if (ver & 0xffff) - dev_info(dev, "regular image, version %d\n", - ver & 0xffff); - else - dev_info(dev, "golden image, version %d\n", - ver >> 16); - } ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port, 115200); ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port, 115200); ptp_ocp_serial_info(dev, "MAC", bp->mac_port, 57600); @@ -2420,17 +3491,22 @@ static void ptp_ocp_detach_sysfs(struct ptp_ocp *bp) { struct device *dev = &bp->dev; + int i; sysfs_remove_link(&dev->kobj, "ttyGNSS"); sysfs_remove_link(&dev->kobj, "ttyMAC"); sysfs_remove_link(&dev->kobj, "ptp"); sysfs_remove_link(&dev->kobj, "pps"); - device_remove_groups(dev, timecard_groups); + if (bp->attr_tbl) + for (i = 0; bp->attr_tbl[i].cap; i++) + sysfs_remove_group(&dev->kobj, bp->attr_tbl[i].group); } static void ptp_ocp_detach(struct ptp_ocp *bp) { + int i; + ptp_ocp_debugfs_remove_device(bp); ptp_ocp_detach_sysfs(bp); if (timer_pending(&bp->watchdog)) @@ -2441,8 +3517,15 @@ ptp_ocp_detach(struct ptp_ocp *bp) ptp_ocp_unregister_ext(bp->ts1); if (bp->ts2) ptp_ocp_unregister_ext(bp->ts2); + if (bp->ts3) + ptp_ocp_unregister_ext(bp->ts3); + if (bp->ts4) + ptp_ocp_unregister_ext(bp->ts4); if (bp->pps) ptp_ocp_unregister_ext(bp->pps); + for (i = 0; i < 4; i++) + if (bp->signal_out[i]) + ptp_ocp_unregister_ext(bp->signal_out[i]); if (bp->gnss_port != -1) serial8250_unregister_port(bp->gnss_port); if (bp->gnss2_port != -1) @@ -2461,6 +3544,7 @@ ptp_ocp_detach(struct ptp_ocp *bp) pci_free_irq_vectors(bp->pdev); if (bp->ptp) ptp_clock_unregister(bp->ptp); + kfree(bp->ptp_info.pin_config); device_unregister(&bp->dev); } @@ -2480,7 +3564,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) err = pci_enable_device(pdev); if (err) { dev_err(&pdev->dev, "pci_enable_device\n"); - goto out_unregister; + goto out_free; } bp = devlink_priv(devlink); @@ -2493,7 +3577,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) * allow this - if not all of the IRQ's are returned, skip the * extra devices and just register the clock. */ - err = pci_alloc_irq_vectors(pdev, 1, 11, PCI_IRQ_MSI | PCI_IRQ_MSIX); + err = pci_alloc_irq_vectors(pdev, 1, 17, PCI_IRQ_MSI | PCI_IRQ_MSIX); if (err < 0) { dev_err(&pdev->dev, "alloc_irq_vectors err: %d\n", err); goto out; @@ -2526,7 +3610,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, NULL); out_disable: pci_disable_device(pdev); -out_unregister: +out_free: devlink_free(devlink); return err; } diff --git a/drivers/ptp/ptp_pch.c b/drivers/ptp/ptp_pch.c index 8070f3fd98f0..7d4da9e605ef 100644 --- a/drivers/ptp/ptp_pch.c +++ b/drivers/ptp/ptp_pch.c @@ -10,9 +10,10 @@ #include #include -#include #include #include +#include +#include #include #include #include @@ -100,7 +101,6 @@ struct pch_ts_regs { #define PCH_ECS_ETH (1 << 0) #define PCH_ECS_CAN (1 << 1) -#define PCH_STATION_BYTES 6 #define PCH_IEEE1588_ETH (1 << 0) #define PCH_IEEE1588_CAN (1 << 1) @@ -115,8 +115,6 @@ struct pch_dev { int exts0_enabled; int exts1_enabled; - u32 mem_base; - u32 mem_size; u32 irq; struct pci_dev *pdev; spinlock_t register_lock; @@ -148,28 +146,15 @@ static inline void pch_eth_enable_set(struct pch_dev *chip) static u64 pch_systime_read(struct pch_ts_regs __iomem *regs) { u64 ns; - u32 lo, hi; - lo = ioread32(®s->systime_lo); - hi = ioread32(®s->systime_hi); + ns = ioread64_lo_hi(®s->systime_lo); - ns = ((u64) hi) << 32; - ns |= lo; - ns <<= TICKS_NS_SHIFT; - - return ns; + return ns << TICKS_NS_SHIFT; } static void pch_systime_write(struct pch_ts_regs __iomem *regs, u64 ns) { - u32 hi, lo; - - ns >>= TICKS_NS_SHIFT; - hi = ns >> 32; - lo = ns & 0xffffffff; - - iowrite32(lo, ®s->systime_lo); - iowrite32(hi, ®s->systime_hi); + iowrite64_lo_hi(ns >> TICKS_NS_SHIFT, ®s->systime_lo); } static inline void pch_block_reset(struct pch_dev *chip) @@ -235,16 +220,10 @@ u64 pch_rx_snap_read(struct pci_dev *pdev) { struct pch_dev *chip = pci_get_drvdata(pdev); u64 ns; - u32 lo, hi; - lo = ioread32(&chip->regs->rx_snap_lo); - hi = ioread32(&chip->regs->rx_snap_hi); + ns = ioread64_lo_hi(&chip->regs->rx_snap_lo); - ns = ((u64) hi) << 32; - ns |= lo; - ns <<= TICKS_NS_SHIFT; - - return ns; + return ns << TICKS_NS_SHIFT; } EXPORT_SYMBOL(pch_rx_snap_read); @@ -252,16 +231,10 @@ u64 pch_tx_snap_read(struct pci_dev *pdev) { struct pch_dev *chip = pci_get_drvdata(pdev); u64 ns; - u32 lo, hi; - lo = ioread32(&chip->regs->tx_snap_lo); - hi = ioread32(&chip->regs->tx_snap_hi); + ns = ioread64_lo_hi(&chip->regs->tx_snap_lo); - ns = ((u64) hi) << 32; - ns |= lo; - ns <<= TICKS_NS_SHIFT; - - return ns; + return ns << TICKS_NS_SHIFT; } EXPORT_SYMBOL(pch_tx_snap_read); @@ -292,8 +265,9 @@ static void pch_reset(struct pch_dev *chip) */ int pch_set_station_address(u8 *addr, struct pci_dev *pdev) { - s32 i; struct pch_dev *chip = pci_get_drvdata(pdev); + bool valid; + u64 mac; /* Verify the parameter */ if ((chip->regs == NULL) || addr == (u8 *)NULL) { @@ -301,37 +275,15 @@ int pch_set_station_address(u8 *addr, struct pci_dev *pdev) "invalid params returning PCH_INVALIDPARAM\n"); return PCH_INVALIDPARAM; } - /* For all station address bytes */ - for (i = 0; i < PCH_STATION_BYTES; i++) { - u32 val; - s32 tmp; - tmp = hex_to_bin(addr[i * 3]); - if (tmp < 0) { - dev_err(&pdev->dev, - "invalid params returning PCH_INVALIDPARAM\n"); - return PCH_INVALIDPARAM; - } - val = tmp * 16; - tmp = hex_to_bin(addr[(i * 3) + 1]); - if (tmp < 0) { - dev_err(&pdev->dev, - "invalid params returning PCH_INVALIDPARAM\n"); - return PCH_INVALIDPARAM; - } - val += tmp; - /* Expects ':' separated addresses */ - if ((i < 5) && (addr[(i * 3) + 2] != ':')) { - dev_err(&pdev->dev, - "invalid params returning PCH_INVALIDPARAM\n"); - return PCH_INVALIDPARAM; - } - - /* Ideally we should set the address only after validating - entire string */ - dev_dbg(&pdev->dev, "invoking pch_station_set\n"); - iowrite32(val, &chip->regs->ts_st[i]); + valid = mac_pton(addr, (u8 *)&mac); + if (!valid) { + dev_err(&pdev->dev, "invalid params returning PCH_INVALIDPARAM\n"); + return PCH_INVALIDPARAM; } + + dev_dbg(&pdev->dev, "invoking pch_station_set\n"); + iowrite64_lo_hi(mac, &chip->regs->ts_st); return 0; } EXPORT_SYMBOL(pch_set_station_address); @@ -344,19 +296,16 @@ static irqreturn_t isr(int irq, void *priv) struct pch_dev *pch_dev = priv; struct pch_ts_regs __iomem *regs = pch_dev->regs; struct ptp_clock_event event; - u32 ack = 0, lo, hi, val; + u32 ack = 0, val; val = ioread32(®s->event); if (val & PCH_TSE_SNS) { ack |= PCH_TSE_SNS; if (pch_dev->exts0_enabled) { - hi = ioread32(®s->asms_hi); - lo = ioread32(®s->asms_lo); event.type = PTP_CLOCK_EXTTS; event.index = 0; - event.timestamp = ((u64) hi) << 32; - event.timestamp |= lo; + event.timestamp = ioread64_hi_lo(®s->asms_hi); event.timestamp <<= TICKS_NS_SHIFT; ptp_clock_event(pch_dev->ptp_clock, &event); } @@ -365,12 +314,9 @@ static irqreturn_t isr(int irq, void *priv) if (val & PCH_TSE_SNM) { ack |= PCH_TSE_SNM; if (pch_dev->exts1_enabled) { - hi = ioread32(®s->amms_hi); - lo = ioread32(®s->amms_lo); event.type = PTP_CLOCK_EXTTS; event.index = 1; - event.timestamp = ((u64) hi) << 32; - event.timestamp |= lo; + event.timestamp = ioread64_hi_lo(®s->asms_hi); event.timestamp <<= TICKS_NS_SHIFT; ptp_clock_event(pch_dev->ptp_clock, &event); } @@ -501,31 +447,12 @@ static const struct ptp_clock_info ptp_pch_caps = { .enable = ptp_pch_enable, }; -#define pch_suspend NULL -#define pch_resume NULL - static void pch_remove(struct pci_dev *pdev) { struct pch_dev *chip = pci_get_drvdata(pdev); + free_irq(pdev->irq, chip); ptp_clock_unregister(chip->ptp_clock); - /* free the interrupt */ - if (pdev->irq != 0) - free_irq(pdev->irq, chip); - - /* unmap the virtual IO memory space */ - if (chip->regs != NULL) { - iounmap(chip->regs); - chip->regs = NULL; - } - /* release the reserved IO memory space */ - if (chip->mem_base != 0) { - release_mem_region(chip->mem_base, chip->mem_size); - chip->mem_base = 0; - } - pci_disable_device(pdev); - kfree(chip); - dev_info(&pdev->dev, "complete\n"); } static s32 @@ -535,50 +462,29 @@ pch_probe(struct pci_dev *pdev, const struct pci_device_id *id) unsigned long flags; struct pch_dev *chip; - chip = kzalloc(sizeof(struct pch_dev), GFP_KERNEL); + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; /* enable the 1588 pci device */ - ret = pci_enable_device(pdev); + ret = pcim_enable_device(pdev); if (ret != 0) { dev_err(&pdev->dev, "could not enable the pci device\n"); - goto err_pci_en; + return ret; } - chip->mem_base = pci_resource_start(pdev, IO_MEM_BAR); - if (!chip->mem_base) { + ret = pcim_iomap_regions(pdev, BIT(IO_MEM_BAR), "1588_regs"); + if (ret) { dev_err(&pdev->dev, "could not locate IO memory address\n"); - ret = -ENODEV; - goto err_pci_start; - } - - /* retrieve the available length of the IO memory space */ - chip->mem_size = pci_resource_len(pdev, IO_MEM_BAR); - - /* allocate the memory for the device registers */ - if (!request_mem_region(chip->mem_base, chip->mem_size, "1588_regs")) { - dev_err(&pdev->dev, - "could not allocate register memory space\n"); - ret = -EBUSY; - goto err_req_mem_region; + return ret; } /* get the virtual address to the 1588 registers */ - chip->regs = ioremap(chip->mem_base, chip->mem_size); - - if (!chip->regs) { - dev_err(&pdev->dev, "Could not get virtual address\n"); - ret = -ENOMEM; - goto err_ioremap; - } - + chip->regs = pcim_iomap_table(pdev)[IO_MEM_BAR]; chip->caps = ptp_pch_caps; chip->ptp_clock = ptp_clock_register(&chip->caps, &pdev->dev); - if (IS_ERR(chip->ptp_clock)) { - ret = PTR_ERR(chip->ptp_clock); - goto err_ptp_clock_reg; - } + if (IS_ERR(chip->ptp_clock)) + return PTR_ERR(chip->ptp_clock); spin_lock_init(&chip->register_lock); @@ -598,8 +504,7 @@ pch_probe(struct pci_dev *pdev, const struct pci_device_id *id) pch_reset(chip); iowrite32(DEFAULT_ADDEND, &chip->regs->addend); - iowrite32(1, &chip->regs->trgt_lo); - iowrite32(0, &chip->regs->trgt_hi); + iowrite64_lo_hi(1, &chip->regs->trgt_lo); iowrite32(PCH_TSE_TTIPEND, &chip->regs->event); pch_eth_enable_set(chip); @@ -617,21 +522,7 @@ pch_probe(struct pci_dev *pdev, const struct pci_device_id *id) err_req_irq: ptp_clock_unregister(chip->ptp_clock); -err_ptp_clock_reg: - iounmap(chip->regs); - chip->regs = NULL; -err_ioremap: - release_mem_region(chip->mem_base, chip->mem_size); - -err_req_mem_region: - chip->mem_base = 0; - -err_pci_start: - pci_disable_device(pdev); - -err_pci_en: - kfree(chip); dev_err(&pdev->dev, "probe failed(ret=0x%x)\n", ret); return ret; @@ -646,33 +537,13 @@ static const struct pci_device_id pch_ieee1588_pcidev_id[] = { }; MODULE_DEVICE_TABLE(pci, pch_ieee1588_pcidev_id); -static SIMPLE_DEV_PM_OPS(pch_pm_ops, pch_suspend, pch_resume); - static struct pci_driver pch_driver = { .name = KBUILD_MODNAME, .id_table = pch_ieee1588_pcidev_id, .probe = pch_probe, .remove = pch_remove, - .driver.pm = &pch_pm_ops, }; - -static void __exit ptp_pch_exit(void) -{ - pci_unregister_driver(&pch_driver); -} - -static s32 __init ptp_pch_init(void) -{ - s32 ret; - - /* register the driver with the pci core */ - ret = pci_register_driver(&pch_driver); - - return ret; -} - -module_init(ptp_pch_init); -module_exit(ptp_pch_exit); +module_pci_driver(pch_driver); module_param_string(station, pch_param.station, sizeof(pch_param.station), 0444); diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c index 41b92dc2f011..9233bfedeb17 100644 --- a/drivers/ptp/ptp_sysfs.c +++ b/drivers/ptp/ptp_sysfs.c @@ -14,7 +14,7 @@ static ssize_t clock_name_show(struct device *dev, struct device_attribute *attr, char *page) { struct ptp_clock *ptp = dev_get_drvdata(dev); - return snprintf(page, PAGE_SIZE-1, "%s\n", ptp->info->name); + return sysfs_emit(page, "%s\n", ptp->info->name); } static DEVICE_ATTR_RO(clock_name); @@ -387,7 +387,7 @@ static ssize_t ptp_pin_show(struct device *dev, struct device_attribute *attr, mutex_unlock(&ptp->pincfg_mux); - return snprintf(page, PAGE_SIZE, "%u %u\n", func, chan); + return sysfs_emit(page, "%u %u\n", func, chan); } static ssize_t ptp_pin_store(struct device *dev, struct device_attribute *attr, diff --git a/drivers/ptp/ptp_vclock.c b/drivers/ptp/ptp_vclock.c index ab1d233173e1..cb179a3ea508 100644 --- a/drivers/ptp/ptp_vclock.c +++ b/drivers/ptp/ptp_vclock.c @@ -57,6 +57,30 @@ static int ptp_vclock_gettime(struct ptp_clock_info *ptp, return 0; } +static int ptp_vclock_gettimex(struct ptp_clock_info *ptp, + struct timespec64 *ts, + struct ptp_system_timestamp *sts) +{ + struct ptp_vclock *vclock = info_to_vclock(ptp); + struct ptp_clock *pptp = vclock->pclock; + struct timespec64 pts; + unsigned long flags; + int err; + u64 ns; + + err = pptp->info->gettimex64(pptp->info, &pts, sts); + if (err) + return err; + + spin_lock_irqsave(&vclock->lock, flags); + ns = timecounter_cyc2time(&vclock->tc, timespec64_to_ns(&pts)); + spin_unlock_irqrestore(&vclock->lock, flags); + + *ts = ns_to_timespec64(ns); + + return 0; +} + static int ptp_vclock_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts) { @@ -71,6 +95,28 @@ static int ptp_vclock_settime(struct ptp_clock_info *ptp, return 0; } +static int ptp_vclock_getcrosststamp(struct ptp_clock_info *ptp, + struct system_device_crosststamp *xtstamp) +{ + struct ptp_vclock *vclock = info_to_vclock(ptp); + struct ptp_clock *pptp = vclock->pclock; + unsigned long flags; + int err; + u64 ns; + + err = pptp->info->getcrosststamp(pptp->info, xtstamp); + if (err) + return err; + + spin_lock_irqsave(&vclock->lock, flags); + ns = timecounter_cyc2time(&vclock->tc, ktime_to_ns(xtstamp->device)); + spin_unlock_irqrestore(&vclock->lock, flags); + + xtstamp->device = ns_to_ktime(ns); + + return 0; +} + static long ptp_vclock_refresh(struct ptp_clock_info *ptp) { struct ptp_vclock *vclock = info_to_vclock(ptp); @@ -84,11 +130,9 @@ static long ptp_vclock_refresh(struct ptp_clock_info *ptp) static const struct ptp_clock_info ptp_vclock_info = { .owner = THIS_MODULE, .name = "ptp virtual clock", - /* The maximum ppb value that long scaled_ppm can support */ - .max_adj = 32767999, + .max_adj = 500000000, .adjfine = ptp_vclock_adjfine, .adjtime = ptp_vclock_adjtime, - .gettime64 = ptp_vclock_gettime, .settime64 = ptp_vclock_settime, .do_aux_work = ptp_vclock_refresh, }; @@ -124,6 +168,12 @@ struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock) vclock->pclock = pclock; vclock->info = ptp_vclock_info; + if (pclock->info->gettimex64) + vclock->info.gettimex64 = ptp_vclock_gettimex; + else + vclock->info.gettime64 = ptp_vclock_gettime; + if (pclock->info->getcrosststamp) + vclock->info.getcrosststamp = ptp_vclock_getcrosststamp; vclock->cc = ptp_vclock_cc; snprintf(vclock->info.name, PTP_CLOCK_NAME_LEN, "ptp%d_virt", diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c index 5db591cf7215..dfb84bb03d32 100644 --- a/drivers/s390/net/ctcm_fsms.c +++ b/drivers/s390/net/ctcm_fsms.c @@ -1394,7 +1394,7 @@ static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg) if (len < TH_HEADER_LENGTH) { CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, - "%s(%s): packet length %d to short", + "%s(%s): packet length %d too short", CTCM_FUNTAIL, dev->name, len); priv->stats.rx_dropped++; priv->stats.rx_length_errors++; diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 5ea7eeb07002..e0fdd54bfeb7 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -166,7 +166,7 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb) ch->logflags = 0; priv->stats.rx_packets++; priv->stats.rx_bytes += skblen; - netif_rx_ni(skb); + netif_rx(skb); if (len > 0) { skb_pull(pskb, header->length); if (skb_tailroom(pskb) < LL_HEADER_LENGTH) { diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index a61d38a1b4ed..bab9b34926c6 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -223,7 +223,7 @@ lcs_setup_read_ccws(struct lcs_card *card) * we do not need to do set_normalized_cda. */ card->read.ccws[cnt].cda = - (__u32) __pa(card->read.iob[cnt].data); + (__u32)virt_to_phys(card->read.iob[cnt].data); ((struct lcs_header *) card->read.iob[cnt].data)->offset = LCS_ILLEGAL_OFFSET; card->read.iob[cnt].callback = lcs_get_frames_cb; @@ -236,7 +236,7 @@ lcs_setup_read_ccws(struct lcs_card *card) /* Last ccw is a tic (transfer in channel). */ card->read.ccws[LCS_NUM_BUFFS].cmd_code = LCS_CCW_TRANSFER; card->read.ccws[LCS_NUM_BUFFS].cda = - (__u32) __pa(card->read.ccws); + (__u32)virt_to_phys(card->read.ccws); /* Setg initial state of the read channel. */ card->read.state = LCS_CH_STATE_INIT; @@ -278,12 +278,12 @@ lcs_setup_write_ccws(struct lcs_card *card) * we do not need to do set_normalized_cda. */ card->write.ccws[cnt].cda = - (__u32) __pa(card->write.iob[cnt].data); + (__u32)virt_to_phys(card->write.iob[cnt].data); } /* Last ccw is a tic (transfer in channel). */ card->write.ccws[LCS_NUM_BUFFS].cmd_code = LCS_CCW_TRANSFER; card->write.ccws[LCS_NUM_BUFFS].cda = - (__u32) __pa(card->write.ccws); + (__u32)virt_to_phys(card->write.ccws); /* Set initial state of the write channel. */ card->read.state = LCS_CH_STATE_INIT; diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 981e7b1c6b96..65aa0a96c21d 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -620,11 +620,7 @@ static void netiucv_unpack_skb(struct iucv_connection *conn, pskb->ip_summed = CHECKSUM_UNNECESSARY; privptr->stats.rx_packets++; privptr->stats.rx_bytes += skb->len; - /* - * Since receiving is always initiated from a tasklet (in iucv.c), - * we must use netif_rx_ni() instead of netif_rx() - */ - netif_rx_ni(skb); + netif_rx(skb); skb_pull(pskb, header->next); skb_put(pskb, NETIUCV_HDRLEN); } diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 29f0111f8e11..d99c5b773e22 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -426,7 +426,7 @@ static void qeth_setup_ccw(struct ccw1 *ccw, u8 cmd_code, u8 flags, u32 len, ccw->cmd_code = cmd_code; ccw->flags = flags | CCW_FLAG_SLI; ccw->count = len; - ccw->cda = (__u32) __pa(data); + ccw->cda = (__u32)virt_to_phys(data); } static int __qeth_issue_next_read(struct qeth_card *card) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 9251ad276ee8..d2f422a9a4f7 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1961,7 +1961,6 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev) if (card->dev->reg_state == NETREG_REGISTERED) unregister_netdev(card->dev); - flush_workqueue(card->cmd_wq); destroy_workqueue(card->cmd_wq); qeth_l3_clear_ip_htable(card, 0); qeth_l3_clear_ipato_list(card); diff --git a/drivers/soc/fsl/dpio/qbman-portal.c b/drivers/soc/fsl/dpio/qbman-portal.c index 058b78fac5e3..0a3fb6c115f4 100644 --- a/drivers/soc/fsl/dpio/qbman-portal.c +++ b/drivers/soc/fsl/dpio/qbman-portal.c @@ -743,8 +743,8 @@ int qbman_swp_enqueue_multiple_mem_back(struct qbman_swp *s, full_mask = s->eqcr.pi_ci_mask; if (!s->eqcr.available) { eqcr_ci = s->eqcr.ci; - p = s->addr_cena + QBMAN_CENA_SWP_EQCR_CI_MEMBACK; - s->eqcr.ci = *p & full_mask; + s->eqcr.ci = qbman_read_register(s, QBMAN_CINH_SWP_EQCR_CI); + s->eqcr.ci &= full_mask; s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size, eqcr_ci, s->eqcr.ci); if (!s->eqcr.available) { @@ -887,8 +887,8 @@ int qbman_swp_enqueue_multiple_desc_mem_back(struct qbman_swp *s, full_mask = s->eqcr.pi_ci_mask; if (!s->eqcr.available) { eqcr_ci = s->eqcr.ci; - p = s->addr_cena + QBMAN_CENA_SWP_EQCR_CI_MEMBACK; - s->eqcr.ci = *p & full_mask; + s->eqcr.ci = qbman_read_register(s, QBMAN_CINH_SWP_EQCR_CI); + s->eqcr.ci &= full_mask; s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size, eqcr_ci, s->eqcr.ci); if (!s->eqcr.available) diff --git a/drivers/staging/gdm724x/gdm_lte.c b/drivers/staging/gdm724x/gdm_lte.c index 0d8d8fed283d..8ebb21d4b24b 100644 --- a/drivers/staging/gdm724x/gdm_lte.c +++ b/drivers/staging/gdm724x/gdm_lte.c @@ -79,7 +79,7 @@ static int gdm_lte_rx(struct sk_buff *skb, struct nic *nic, int nic_type) int ret, len; len = skb->len + ETH_HLEN; - ret = netif_rx_ni(skb); + ret = netif_rx(skb); if (ret == NET_RX_DROP) { nic->stats.rx_dropped++; } else { diff --git a/drivers/staging/wlan-ng/p80211netdev.c b/drivers/staging/wlan-ng/p80211netdev.c index 255500448ad3..e04fc666d218 100644 --- a/drivers/staging/wlan-ng/p80211netdev.c +++ b/drivers/staging/wlan-ng/p80211netdev.c @@ -255,7 +255,7 @@ static int p80211_convert_to_ether(struct wlandevice *wlandev, if (skb_p80211_to_ether(wlandev, wlandev->ethconv, skb) == 0) { wlandev->netdev->stats.rx_packets++; wlandev->netdev->stats.rx_bytes += skb->len; - netif_rx_ni(skb); + netif_rx(skb); return 0; } @@ -290,7 +290,7 @@ static void p80211netdev_rx_bh(struct tasklet_struct *t) dev->stats.rx_packets++; dev->stats.rx_bytes += skb->len; - netif_rx_ni(skb); + netif_rx(skb); continue; } else { if (!p80211_convert_to_ether(wlandev, skb)) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 28ef323882fb..792ab5f23647 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -473,6 +473,7 @@ static void vhost_tx_batch(struct vhost_net *net, goto signal_used; msghdr->msg_control = &ctl; + msghdr->msg_controllen = sizeof(ctl); err = sock->ops->sendmsg(sock, msghdr, 0); if (unlikely(err < 0)) { vq_err(&nvq->vq, "Fail to batch sending packets\n"); diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index b525d8cdc25b..88a51b242adc 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -8,6 +8,7 @@ #include #include #include +#include #include struct sock; @@ -165,11 +166,23 @@ int bpf_percpu_cgroup_storage_copy(struct bpf_map *map, void *key, void *value); int bpf_percpu_cgroup_storage_update(struct bpf_map *map, void *key, void *value, u64 flags); +/* Opportunistic check to see whether we have any BPF program attached*/ +static inline bool cgroup_bpf_sock_enabled(struct sock *sk, + enum cgroup_bpf_attach_type type) +{ + struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); + struct bpf_prog_array *array; + + array = rcu_access_pointer(cgrp->bpf.effective[type]); + return array != &bpf_empty_prog_array.hdr; +} + /* Wrappers for __cgroup_bpf_run_filter_skb() guarded by cgroup_bpf_enabled. */ #define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk, skb) \ ({ \ int __ret = 0; \ - if (cgroup_bpf_enabled(CGROUP_INET_INGRESS)) \ + if (cgroup_bpf_enabled(CGROUP_INET_INGRESS) && \ + cgroup_bpf_sock_enabled(sk, CGROUP_INET_INGRESS)) \ __ret = __cgroup_bpf_run_filter_skb(sk, skb, \ CGROUP_INET_INGRESS); \ \ @@ -181,7 +194,8 @@ int bpf_percpu_cgroup_storage_update(struct bpf_map *map, void *key, int __ret = 0; \ if (cgroup_bpf_enabled(CGROUP_INET_EGRESS) && sk && sk == skb->sk) { \ typeof(sk) __sk = sk_to_full_sk(sk); \ - if (sk_fullsock(__sk)) \ + if (sk_fullsock(__sk) && \ + cgroup_bpf_sock_enabled(__sk, CGROUP_INET_EGRESS)) \ __ret = __cgroup_bpf_run_filter_skb(__sk, skb, \ CGROUP_INET_EGRESS); \ } \ @@ -347,7 +361,8 @@ int bpf_percpu_cgroup_storage_update(struct bpf_map *map, void *key, kernel_optval) \ ({ \ int __ret = 0; \ - if (cgroup_bpf_enabled(CGROUP_SETSOCKOPT)) \ + if (cgroup_bpf_enabled(CGROUP_SETSOCKOPT) && \ + cgroup_bpf_sock_enabled(sock, CGROUP_SETSOCKOPT)) \ __ret = __cgroup_bpf_run_filter_setsockopt(sock, level, \ optname, optval, \ optlen, \ @@ -367,7 +382,8 @@ int bpf_percpu_cgroup_storage_update(struct bpf_map *map, void *key, max_optlen, retval) \ ({ \ int __ret = retval; \ - if (cgroup_bpf_enabled(CGROUP_GETSOCKOPT)) \ + if (cgroup_bpf_enabled(CGROUP_GETSOCKOPT) && \ + cgroup_bpf_sock_enabled(sock, CGROUP_GETSOCKOPT)) \ if (!(sock)->sk_prot->bpf_bypass_getsockopt || \ !INDIRECT_CALL_INET_1((sock)->sk_prot->bpf_bypass_getsockopt, \ tcp_bpf_bypass_getsockopt, \ diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 3121d1fc8e75..bdb5298735ce 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -194,6 +194,17 @@ struct bpf_map { struct work_struct work; struct mutex freeze_mutex; atomic64_t writecnt; + /* 'Ownership' of program-containing map is claimed by the first program + * that is going to use this map or by the first program which FD is + * stored in the map to make sure that all callers and callees have the + * same prog type, JITed flag and xdp_has_frags flag. + */ + struct { + spinlock_t lock; + enum bpf_prog_type type; + bool jited; + bool xdp_has_frags; + } owner; }; static inline bool map_value_has_spin_lock(const struct bpf_map *map) @@ -320,7 +331,18 @@ enum bpf_type_flag { */ MEM_ALLOC = BIT(2 + BPF_BASE_TYPE_BITS), - __BPF_TYPE_LAST_FLAG = MEM_ALLOC, + /* MEM is in user address space. */ + MEM_USER = BIT(3 + BPF_BASE_TYPE_BITS), + + /* MEM is a percpu memory. MEM_PERCPU tags PTR_TO_BTF_ID. When tagged + * with MEM_PERCPU, PTR_TO_BTF_ID _cannot_ be directly accessed. In + * order to drop this tag, it must be passed into bpf_per_cpu_ptr() + * or bpf_this_cpu_ptr(), which will return the pointer corresponding + * to the specified cpu. + */ + MEM_PERCPU = BIT(4 + BPF_BASE_TYPE_BITS), + + __BPF_TYPE_LAST_FLAG = MEM_PERCPU, }; /* Max number of base types. */ @@ -502,7 +524,6 @@ enum bpf_reg_type { */ PTR_TO_MEM, /* reg points to valid memory region */ PTR_TO_BUF, /* reg points to a read/write buffer */ - PTR_TO_PERCPU_BTF_ID, /* reg points to a percpu kernel variable */ PTR_TO_FUNC, /* reg points to a bpf program function */ __BPF_REG_TYPE_MAX, @@ -576,8 +597,7 @@ struct bpf_verifier_ops { const struct btf *btf, const struct btf_type *t, int off, int size, enum bpf_access_type atype, - u32 *next_btf_id); - bool (*check_kfunc_call)(u32 kfunc_btf_id, struct module *owner); + u32 *next_btf_id, enum bpf_type_flag *flag); }; struct bpf_prog_offload_ops { @@ -832,8 +852,8 @@ void bpf_image_ksym_add(void *data, struct bpf_ksym *ksym); void bpf_image_ksym_del(struct bpf_ksym *ksym); void bpf_ksym_add(struct bpf_ksym *ksym); void bpf_ksym_del(struct bpf_ksym *ksym); -int bpf_jit_charge_modmem(u32 pages); -void bpf_jit_uncharge_modmem(u32 pages); +int bpf_jit_charge_modmem(u32 size); +void bpf_jit_uncharge_modmem(u32 size); bool bpf_prog_has_trampoline(const struct bpf_prog *prog); #else static inline int bpf_trampoline_link_prog(struct bpf_prog *prog, @@ -938,6 +958,8 @@ struct bpf_prog_aux { bool func_proto_unreliable; bool sleepable; bool tail_call_reachable; + bool xdp_has_frags; + bool use_bpf_prog_pack; struct hlist_node tramp_hlist; /* BTF_KIND_FUNC_PROTO for valid attach_btf_id */ const struct btf_type *attach_func_proto; @@ -998,16 +1020,6 @@ struct bpf_prog_aux { }; struct bpf_array_aux { - /* 'Ownership' of prog array is claimed by the first program that - * is going to use this map or by the first program which FD is - * stored in the map to make sure that all callers and callees have - * the same prog type and JITed flag. - */ - struct { - spinlock_t lock; - enum bpf_prog_type type; - bool jited; - } owner; /* Programs with direct jumps into programs part of this array. */ struct list_head poke_progs; struct bpf_map *map; @@ -1182,7 +1194,14 @@ struct bpf_event_entry { struct rcu_head rcu; }; -bool bpf_prog_array_compatible(struct bpf_array *array, const struct bpf_prog *fp); +static inline bool map_type_contains_progs(struct bpf_map *map) +{ + return map->map_type == BPF_MAP_TYPE_PROG_ARRAY || + map->map_type == BPF_MAP_TYPE_DEVMAP || + map->map_type == BPF_MAP_TYPE_CPUMAP; +} + +bool bpf_prog_map_compatible(struct bpf_map *map, const struct bpf_prog *fp); int bpf_prog_calc_tag(struct bpf_prog *fp); const struct bpf_func_proto *bpf_get_trace_printk_proto(void); @@ -1224,6 +1243,19 @@ struct bpf_prog_array { struct bpf_prog_array_item items[]; }; +struct bpf_empty_prog_array { + struct bpf_prog_array hdr; + struct bpf_prog *null_prog; +}; + +/* to avoid allocating empty bpf_prog_array for cgroups that + * don't have bpf program attached use one global 'bpf_empty_prog_array' + * It will not be modified the caller of bpf_prog_array_alloc() + * (since caller requested prog_cnt == 0) + * that pointer should be 'freed' by bpf_prog_array_free() + */ +extern struct bpf_empty_prog_array bpf_empty_prog_array; + struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags); void bpf_prog_array_free(struct bpf_prog_array *progs); int bpf_prog_array_length(struct bpf_prog_array *progs); @@ -1250,6 +1282,7 @@ struct bpf_run_ctx {}; struct bpf_cg_run_ctx { struct bpf_run_ctx run_ctx; const struct bpf_prog_array_item *prog_item; + int retval; }; struct bpf_trace_run_ctx { @@ -1282,19 +1315,19 @@ static inline void bpf_reset_run_ctx(struct bpf_run_ctx *old_ctx) typedef u32 (*bpf_prog_run_fn)(const struct bpf_prog *prog, const void *ctx); -static __always_inline u32 +static __always_inline int BPF_PROG_RUN_ARRAY_CG_FLAGS(const struct bpf_prog_array __rcu *array_rcu, const void *ctx, bpf_prog_run_fn run_prog, - u32 *ret_flags) + int retval, u32 *ret_flags) { const struct bpf_prog_array_item *item; const struct bpf_prog *prog; const struct bpf_prog_array *array; struct bpf_run_ctx *old_run_ctx; struct bpf_cg_run_ctx run_ctx; - u32 ret = 1; u32 func_ret; + run_ctx.retval = retval; migrate_disable(); rcu_read_lock(); array = rcu_dereference(array_rcu); @@ -1303,27 +1336,29 @@ BPF_PROG_RUN_ARRAY_CG_FLAGS(const struct bpf_prog_array __rcu *array_rcu, while ((prog = READ_ONCE(item->prog))) { run_ctx.prog_item = item; func_ret = run_prog(prog, ctx); - ret &= (func_ret & 1); + if (!(func_ret & 1) && !IS_ERR_VALUE((long)run_ctx.retval)) + run_ctx.retval = -EPERM; *(ret_flags) |= (func_ret >> 1); item++; } bpf_reset_run_ctx(old_run_ctx); rcu_read_unlock(); migrate_enable(); - return ret; + return run_ctx.retval; } -static __always_inline u32 +static __always_inline int BPF_PROG_RUN_ARRAY_CG(const struct bpf_prog_array __rcu *array_rcu, - const void *ctx, bpf_prog_run_fn run_prog) + const void *ctx, bpf_prog_run_fn run_prog, + int retval) { const struct bpf_prog_array_item *item; const struct bpf_prog *prog; const struct bpf_prog_array *array; struct bpf_run_ctx *old_run_ctx; struct bpf_cg_run_ctx run_ctx; - u32 ret = 1; + run_ctx.retval = retval; migrate_disable(); rcu_read_lock(); array = rcu_dereference(array_rcu); @@ -1331,13 +1366,14 @@ BPF_PROG_RUN_ARRAY_CG(const struct bpf_prog_array __rcu *array_rcu, old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx); while ((prog = READ_ONCE(item->prog))) { run_ctx.prog_item = item; - ret &= run_prog(prog, ctx); + if (!run_prog(prog, ctx) && !IS_ERR_VALUE((long)run_ctx.retval)) + run_ctx.retval = -EPERM; item++; } bpf_reset_run_ctx(old_run_ctx); rcu_read_unlock(); migrate_enable(); - return ret; + return run_ctx.retval; } static __always_inline u32 @@ -1390,19 +1426,21 @@ BPF_PROG_RUN_ARRAY(const struct bpf_prog_array __rcu *array_rcu, * 0: NET_XMIT_SUCCESS skb should be transmitted * 1: NET_XMIT_DROP skb should be dropped and cn * 2: NET_XMIT_CN skb should be transmitted and cn - * 3: -EPERM skb should be dropped + * 3: -err skb should be dropped */ #define BPF_PROG_CGROUP_INET_EGRESS_RUN_ARRAY(array, ctx, func) \ ({ \ u32 _flags = 0; \ bool _cn; \ u32 _ret; \ - _ret = BPF_PROG_RUN_ARRAY_CG_FLAGS(array, ctx, func, &_flags); \ + _ret = BPF_PROG_RUN_ARRAY_CG_FLAGS(array, ctx, func, 0, &_flags); \ _cn = _flags & BPF_RET_SET_CN; \ - if (_ret) \ + if (_ret && !IS_ERR_VALUE((long)_ret)) \ + _ret = -EFAULT; \ + if (!_ret) \ _ret = (_cn ? NET_XMIT_CN : NET_XMIT_SUCCESS); \ else \ - _ret = (_cn ? NET_XMIT_DROP : -EPERM); \ + _ret = (_cn ? NET_XMIT_DROP : _ret); \ _ret; \ }) @@ -1723,7 +1761,6 @@ int bpf_prog_test_run_raw_tp(struct bpf_prog *prog, int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog, const union bpf_attr *kattr, union bpf_attr __user *uattr); -bool bpf_prog_test_check_kfunc_call(u32 kfunc_id, struct module *owner); bool btf_ctx_access(int off, int size, enum bpf_access_type type, const struct bpf_prog *prog, struct bpf_insn_access_aux *info); @@ -1753,7 +1790,7 @@ static inline bool bpf_tracing_btf_ctx_access(int off, int size, int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf, const struct btf_type *t, int off, int size, enum bpf_access_type atype, - u32 *next_btf_id); + u32 *next_btf_id, enum bpf_type_flag *flag); bool btf_struct_ids_match(struct bpf_verifier_log *log, const struct btf *btf, u32 id, int off, const struct btf *need_btf, u32 need_type_id); @@ -1866,11 +1903,6 @@ static inline int bpf_obj_get_user(const char __user *pathname, int flags) return -EOPNOTSUPP; } -static inline bool dev_map_can_have_prog(struct bpf_map *map) -{ - return false; -} - static inline void __dev_flush(void) { } @@ -1934,11 +1966,6 @@ static inline int cpu_map_generic_redirect(struct bpf_cpu_map_entry *rcpu, return -EOPNOTSUPP; } -static inline bool cpu_map_prog_allowed(struct bpf_map *map) -{ - return false; -} - static inline struct bpf_prog *bpf_prog_get_type_path(const char *name, enum bpf_prog_type type) { @@ -1980,12 +2007,6 @@ static inline int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog, return -ENOTSUPP; } -static inline bool bpf_prog_test_check_kfunc_call(u32 kfunc_id, - struct module *owner) -{ - return false; -} - static inline void bpf_map_put(struct bpf_map *map) { } @@ -2086,6 +2107,9 @@ int bpf_prog_test_run_syscall(struct bpf_prog *prog, int sock_map_get_from_fd(const union bpf_attr *attr, struct bpf_prog *prog); int sock_map_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype); int sock_map_update_elem_sys(struct bpf_map *map, void *key, void *value, u64 flags); +int sock_map_bpf_prog_query(const union bpf_attr *attr, + union bpf_attr __user *uattr); + void sock_map_unhash(struct sock *sk); void sock_map_close(struct sock *sk, long timeout); #else @@ -2139,6 +2163,12 @@ static inline int sock_map_update_elem_sys(struct bpf_map *map, void *key, void { return -EOPNOTSUPP; } + +static inline int sock_map_bpf_prog_query(const union bpf_attr *attr, + union bpf_attr __user *uattr) +{ + return -EINVAL; +} #endif /* CONFIG_BPF_SYSCALL */ #endif /* CONFIG_NET && CONFIG_BPF_SYSCALL */ @@ -2237,6 +2267,7 @@ extern const struct bpf_func_proto bpf_kallsyms_lookup_name_proto; extern const struct bpf_func_proto bpf_find_vma_proto; extern const struct bpf_func_proto bpf_loop_proto; extern const struct bpf_func_proto bpf_strncmp_proto; +extern const struct bpf_func_proto bpf_copy_from_user_task_proto; const struct bpf_func_proto *tracing_prog_func_proto( enum bpf_func_id func_id, const struct bpf_prog *prog); @@ -2349,6 +2380,8 @@ enum bpf_text_poke_type { int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t, void *addr1, void *addr2); +void *bpf_arch_text_copy(void *dst, void *src, size_t len); + struct btf_id_set; bool btf_id_set_contains(const struct btf_id_set *set, u32 id); diff --git a/include/linux/bpf_local_storage.h b/include/linux/bpf_local_storage.h index 37b3906af8b1..493e63258497 100644 --- a/include/linux/bpf_local_storage.h +++ b/include/linux/bpf_local_storage.h @@ -154,16 +154,17 @@ void bpf_selem_unlink_map(struct bpf_local_storage_elem *selem); struct bpf_local_storage_elem * bpf_selem_alloc(struct bpf_local_storage_map *smap, void *owner, void *value, - bool charge_mem); + bool charge_mem, gfp_t gfp_flags); int bpf_local_storage_alloc(void *owner, struct bpf_local_storage_map *smap, - struct bpf_local_storage_elem *first_selem); + struct bpf_local_storage_elem *first_selem, + gfp_t gfp_flags); struct bpf_local_storage_data * bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, - void *value, u64 map_flags); + void *value, u64 map_flags, gfp_t gfp_flags); void bpf_local_storage_free_rcu(struct rcu_head *rcu); diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index 48a91c51c015..3e24ad0c4b3c 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -140,3 +140,4 @@ BPF_LINK_TYPE(BPF_LINK_TYPE_XDP, xdp) #ifdef CONFIG_PERF_EVENTS BPF_LINK_TYPE(BPF_LINK_TYPE_PERF_EVENT, perf) #endif +BPF_LINK_TYPE(BPF_LINK_TYPE_KPROBE_MULTI, kprobe_multi) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index e9993172f892..c1fc4af47f69 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -521,6 +521,12 @@ bpf_prog_offload_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt); int check_ptr_off_reg(struct bpf_verifier_env *env, const struct bpf_reg_state *reg, int regno); +int check_func_arg_reg_off(struct bpf_verifier_env *env, + const struct bpf_reg_state *reg, int regno, + enum bpf_arg_type arg_type, + bool is_release_func); +int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, + u32 regno); int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, u32 regno, u32 mem_size); @@ -564,4 +570,9 @@ static inline u32 type_flag(u32 type) return type & ~BPF_BASE_TYPE_MASK; } +static inline enum bpf_prog_type resolve_prog_type(struct bpf_prog *prog) +{ + return prog->aux->dst_prog ? prog->aux->dst_prog->type : prog->type; +} + #endif /* _LINUX_BPF_VERIFIER_H */ diff --git a/include/linux/btf.h b/include/linux/btf.h index 0c74348cbc9d..36bc09b8e890 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -12,11 +12,33 @@ #define BTF_TYPE_EMIT(type) ((void)(type *)0) #define BTF_TYPE_EMIT_ENUM(enum_val) ((void)enum_val) +enum btf_kfunc_type { + BTF_KFUNC_TYPE_CHECK, + BTF_KFUNC_TYPE_ACQUIRE, + BTF_KFUNC_TYPE_RELEASE, + BTF_KFUNC_TYPE_RET_NULL, + BTF_KFUNC_TYPE_MAX, +}; + struct btf; struct btf_member; struct btf_type; union bpf_attr; struct btf_show; +struct btf_id_set; + +struct btf_kfunc_id_set { + struct module *owner; + union { + struct { + struct btf_id_set *check_set; + struct btf_id_set *acquire_set; + struct btf_id_set *release_set; + struct btf_id_set *ret_null_set; + }; + struct btf_id_set *sets[BTF_KFUNC_TYPE_MAX]; + }; +}; extern const struct file_operations btf_fops; @@ -216,6 +238,11 @@ static inline bool btf_type_is_var(const struct btf_type *t) return BTF_INFO_KIND(t->info) == BTF_KIND_VAR; } +static inline bool btf_type_is_type_tag(const struct btf_type *t) +{ + return BTF_INFO_KIND(t->info) == BTF_KIND_TYPE_TAG; +} + /* union is only a special case of struct: * all its offsetof(member) == 0 */ @@ -300,6 +327,11 @@ static inline const struct btf_var_secinfo *btf_type_var_secinfo( return (const struct btf_var_secinfo *)(t + 1); } +static inline struct btf_param *btf_params(const struct btf_type *t) +{ + return (struct btf_param *)(t + 1); +} + #ifdef CONFIG_BPF_SYSCALL struct bpf_prog; @@ -307,6 +339,11 @@ const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id); const char *btf_name_by_offset(const struct btf *btf, u32 offset); struct btf *btf_parse_vmlinux(void); struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog); +bool btf_kfunc_id_set_contains(const struct btf *btf, + enum bpf_prog_type prog_type, + enum btf_kfunc_type type, u32 kfunc_btf_id); +int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, + const struct btf_kfunc_id_set *s); #else static inline const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id) @@ -318,50 +355,18 @@ static inline const char *btf_name_by_offset(const struct btf *btf, { return NULL; } -#endif - -struct kfunc_btf_id_set { - struct list_head list; - struct btf_id_set *set; - struct module *owner; -}; - -struct kfunc_btf_id_list { - struct list_head list; - struct mutex mutex; -}; - -#ifdef CONFIG_DEBUG_INFO_BTF_MODULES -void register_kfunc_btf_id_set(struct kfunc_btf_id_list *l, - struct kfunc_btf_id_set *s); -void unregister_kfunc_btf_id_set(struct kfunc_btf_id_list *l, - struct kfunc_btf_id_set *s); -bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist, u32 kfunc_id, - struct module *owner); - -extern struct kfunc_btf_id_list bpf_tcp_ca_kfunc_list; -extern struct kfunc_btf_id_list prog_test_kfunc_list; -#else -static inline void register_kfunc_btf_id_set(struct kfunc_btf_id_list *l, - struct kfunc_btf_id_set *s) -{ -} -static inline void unregister_kfunc_btf_id_set(struct kfunc_btf_id_list *l, - struct kfunc_btf_id_set *s) -{ -} -static inline bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist, - u32 kfunc_id, struct module *owner) +static inline bool btf_kfunc_id_set_contains(const struct btf *btf, + enum bpf_prog_type prog_type, + enum btf_kfunc_type type, + u32 kfunc_btf_id) { return false; } - -static struct kfunc_btf_id_list bpf_tcp_ca_kfunc_list __maybe_unused; -static struct kfunc_btf_id_list prog_test_kfunc_list __maybe_unused; +static inline int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, + const struct btf_kfunc_id_set *s) +{ + return 0; +} #endif -#define DEFINE_KFUNC_BTF_ID_SET(set, name) \ - struct kfunc_btf_id_set name = { LIST_HEAD_INIT(name.list), (set), \ - THIS_MODULE } - #endif diff --git a/include/linux/btf_ids.h b/include/linux/btf_ids.h index 919c0fde1c51..bc5d9cc34e4c 100644 --- a/include/linux/btf_ids.h +++ b/include/linux/btf_ids.h @@ -11,6 +11,7 @@ struct btf_id_set { #ifdef CONFIG_DEBUG_INFO_BTF #include /* for __PASTE */ +#include /* for __maybe_unused */ /* * Following macros help to define lists of BTF IDs placed @@ -146,14 +147,14 @@ extern struct btf_id_set name; #else -#define BTF_ID_LIST(name) static u32 name[5]; +#define BTF_ID_LIST(name) static u32 __maybe_unused name[5]; #define BTF_ID(prefix, name) #define BTF_ID_UNUSED -#define BTF_ID_LIST_GLOBAL(name, n) u32 name[n]; -#define BTF_ID_LIST_SINGLE(name, prefix, typename) static u32 name[1]; -#define BTF_ID_LIST_GLOBAL_SINGLE(name, prefix, typename) u32 name[1]; -#define BTF_SET_START(name) static struct btf_id_set name = { 0 }; -#define BTF_SET_START_GLOBAL(name) static struct btf_id_set name = { 0 }; +#define BTF_ID_LIST_GLOBAL(name, n) u32 __maybe_unused name[n]; +#define BTF_ID_LIST_SINGLE(name, prefix, typename) static u32 __maybe_unused name[1]; +#define BTF_ID_LIST_GLOBAL_SINGLE(name, prefix, typename) u32 __maybe_unused name[1]; +#define BTF_SET_START(name) static struct btf_id_set __maybe_unused name = { 0 }; +#define BTF_SET_START_GLOBAL(name) static struct btf_id_set __maybe_unused name = { 0 }; #define BTF_SET_END(name) #endif /* CONFIG_DEBUG_INFO_BTF */ diff --git a/include/linux/can/bittiming.h b/include/linux/can/bittiming.h index a81652d1c6f3..7ae21c0f7f23 100644 --- a/include/linux/can/bittiming.h +++ b/include/linux/can/bittiming.h @@ -113,7 +113,7 @@ struct can_tdc_const { }; #ifdef CONFIG_CAN_CALC_BITTIMING -int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt, +int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt, const struct can_bittiming_const *btc); void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const, @@ -121,7 +121,7 @@ void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const, u32 *ctrlmode, u32 ctrlmode_supported); #else /* !CONFIG_CAN_CALC_BITTIMING */ static inline int -can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt, +can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt, const struct can_bittiming_const *btc) { netdev_err(dev, "bit-timing calculation not available\n"); @@ -136,7 +136,7 @@ can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const, } #endif /* CONFIG_CAN_CALC_BITTIMING */ -int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt, +int can_get_bittiming(const struct net_device *dev, struct can_bittiming *bt, const struct can_bittiming_const *btc, const u32 *bitrate_const, const unsigned int bitrate_const_cnt); diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h index 3c4de9b6c6e3..babb1347148c 100644 --- a/include/linux/compiler-clang.h +++ b/include/linux/compiler-clang.h @@ -68,3 +68,28 @@ #define __nocfi __attribute__((__no_sanitize__("cfi"))) #define __cficanonical __attribute__((__cfi_canonical_jump_table__)) + +/* + * Turn individual warnings and errors on and off locally, depending + * on version. + */ +#define __diag_clang(version, severity, s) \ + __diag_clang_ ## version(__diag_clang_ ## severity s) + +/* Severity used in pragma directives */ +#define __diag_clang_ignore ignored +#define __diag_clang_warn warning +#define __diag_clang_error error + +#define __diag_str1(s) #s +#define __diag_str(s) __diag_str1(s) +#define __diag(s) _Pragma(__diag_str(clang diagnostic s)) + +#if CONFIG_CLANG_VERSION >= 110000 +#define __diag_clang_11(s) __diag(s) +#else +#define __diag_clang_11(s) +#endif + +#define __diag_ignore_all(option, comment) \ + __diag_clang(11, ignore, option) diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h index deff5b308470..52299c957c98 100644 --- a/include/linux/compiler-gcc.h +++ b/include/linux/compiler-gcc.h @@ -155,6 +155,9 @@ #define __diag_GCC_8(s) #endif +#define __diag_ignore_all(option, comment) \ + __diag_GCC(8, ignore, option) + /* * Prior to 9.1, -Wno-alloc-size-larger-than (and therefore the "alloc_size" * attribute) do not work, and must be disabled. diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index 3c1795fdb568..1bc760ba400c 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -4,6 +4,13 @@ #ifndef __ASSEMBLY__ +#if defined(CONFIG_DEBUG_INFO_BTF) && defined(CONFIG_PAHOLE_HAS_BTF_TAG) && \ + __has_attribute(btf_type_tag) +# define BTF_TYPE_TAG(value) __attribute__((btf_type_tag(#value))) +#else +# define BTF_TYPE_TAG(value) /* nothing */ +#endif + #ifdef __CHECKER__ /* address spaces */ # define __kernel __attribute__((address_space(0))) @@ -32,10 +39,10 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { } # ifdef STRUCTLEAK_PLUGIN # define __user __attribute__((user)) # else -# define __user +# define __user BTF_TYPE_TAG(user) # endif # define __iomem -# define __percpu +# define __percpu BTF_TYPE_TAG(percpu) # define __rcu # define __chk_user_ptr(x) (void)0 # define __chk_io_ptr(x) (void)0 @@ -368,4 +375,8 @@ struct ftrace_likely_data { #define __diag_error(compiler, version, option, comment) \ __diag_ ## compiler(version, error, option) +#ifndef __diag_ignore_all +#define __diag_ignore_all(option, comment) +#endif + #endif /* __LINUX_COMPILER_TYPES_H */ diff --git a/include/linux/dsa/8021q.h b/include/linux/dsa/8021q.h index 939a1beaddf7..3ed117e299ec 100644 --- a/include/linux/dsa/8021q.h +++ b/include/linux/dsa/8021q.h @@ -32,31 +32,29 @@ int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto); void dsa_tag_8021q_unregister(struct dsa_switch *ds); +int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge); + +void dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge); + struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, u16 tpid, u16 tci); -void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id); +void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id, + int *vbid); -int dsa_tag_8021q_bridge_tx_fwd_offload(struct dsa_switch *ds, int port, - struct dsa_bridge bridge); +struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *master, + int vbid); -void dsa_tag_8021q_bridge_tx_fwd_unoffload(struct dsa_switch *ds, int port, - struct dsa_bridge bridge); +u16 dsa_tag_8021q_bridge_vid(unsigned int bridge_num); -u16 dsa_8021q_bridge_tx_fwd_offload_vid(unsigned int bridge_num); - -u16 dsa_tag_8021q_tx_vid(const struct dsa_port *dp); - -u16 dsa_tag_8021q_rx_vid(const struct dsa_port *dp); +u16 dsa_tag_8021q_standalone_vid(const struct dsa_port *dp); int dsa_8021q_rx_switch_id(u16 vid); int dsa_8021q_rx_source_port(u16 vid); -bool vid_is_dsa_8021q_rxvlan(u16 vid); - -bool vid_is_dsa_8021q_txvlan(u16 vid); - bool vid_is_dsa_8021q(u16 vid); #endif /* _NET_DSA_8021Q_H */ diff --git a/include/linux/dsa/tag_qca.h b/include/linux/dsa/tag_qca.h new file mode 100644 index 000000000000..4359fb0221cf --- /dev/null +++ b/include/linux/dsa/tag_qca.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __TAG_QCA_H +#define __TAG_QCA_H + +#define QCA_HDR_LEN 2 +#define QCA_HDR_VERSION 0x2 + +#define QCA_HDR_RECV_VERSION GENMASK(15, 14) +#define QCA_HDR_RECV_PRIORITY GENMASK(13, 11) +#define QCA_HDR_RECV_TYPE GENMASK(10, 6) +#define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3) +#define QCA_HDR_RECV_SOURCE_PORT GENMASK(2, 0) + +/* Packet type for recv */ +#define QCA_HDR_RECV_TYPE_NORMAL 0x0 +#define QCA_HDR_RECV_TYPE_MIB 0x1 +#define QCA_HDR_RECV_TYPE_RW_REG_ACK 0x2 + +#define QCA_HDR_XMIT_VERSION GENMASK(15, 14) +#define QCA_HDR_XMIT_PRIORITY GENMASK(13, 11) +#define QCA_HDR_XMIT_CONTROL GENMASK(10, 8) +#define QCA_HDR_XMIT_FROM_CPU BIT(7) +#define QCA_HDR_XMIT_DP_BIT GENMASK(6, 0) + +/* Packet type for xmit */ +#define QCA_HDR_XMIT_TYPE_NORMAL 0x0 +#define QCA_HDR_XMIT_TYPE_RW_REG 0x1 + +/* Check code for a valid mgmt packet. Switch will ignore the packet + * with this wrong. + */ +#define QCA_HDR_MGMT_CHECK_CODE_VAL 0x5 + +/* Specific define for in-band MDIO read/write with Ethernet packet */ +#define QCA_HDR_MGMT_SEQ_LEN 4 /* 4 byte for the seq */ +#define QCA_HDR_MGMT_COMMAND_LEN 4 /* 4 byte for the command */ +#define QCA_HDR_MGMT_DATA1_LEN 4 /* First 4 byte for the mdio data */ +#define QCA_HDR_MGMT_HEADER_LEN (QCA_HDR_MGMT_SEQ_LEN + \ + QCA_HDR_MGMT_COMMAND_LEN + \ + QCA_HDR_MGMT_DATA1_LEN) + +#define QCA_HDR_MGMT_DATA2_LEN 12 /* Other 12 byte for the mdio data */ +#define QCA_HDR_MGMT_PADDING_LEN 34 /* Padding to reach the min Ethernet packet */ + +#define QCA_HDR_MGMT_PKT_LEN (QCA_HDR_MGMT_HEADER_LEN + \ + QCA_HDR_LEN + \ + QCA_HDR_MGMT_DATA2_LEN + \ + QCA_HDR_MGMT_PADDING_LEN) + +#define QCA_HDR_MGMT_SEQ_NUM GENMASK(31, 0) /* 63, 32 */ +#define QCA_HDR_MGMT_CHECK_CODE GENMASK(31, 29) /* 31, 29 */ +#define QCA_HDR_MGMT_CMD BIT(28) /* 28 */ +#define QCA_HDR_MGMT_LENGTH GENMASK(23, 20) /* 23, 20 */ +#define QCA_HDR_MGMT_ADDR GENMASK(18, 0) /* 18, 0 */ + +/* Special struct emulating a Ethernet header */ +struct qca_mgmt_ethhdr { + u32 command; /* command bit 31:0 */ + u32 seq; /* seq 63:32 */ + u32 mdio_data; /* first 4byte mdio */ + __be16 hdr; /* qca hdr */ +} __packed; + +enum mdio_cmd { + MDIO_WRITE = 0x0, + MDIO_READ +}; + +struct mib_ethhdr { + u32 data[3]; /* first 3 mib counter */ + __be16 hdr; /* qca hdr */ +} __packed; + +struct qca_tagger_data { + void (*rw_reg_ack_handler)(struct dsa_switch *ds, + struct sk_buff *skb); + void (*mib_autocast_handler)(struct dsa_switch *ds, + struct sk_buff *skb); +}; + +#endif /* __TAG_QCA_H */ diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 2ad71cc90b37..92b10e67d5f8 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -134,7 +134,7 @@ static inline bool is_multicast_ether_addr(const u8 *addr) #endif } -static inline bool is_multicast_ether_addr_64bits(const u8 addr[6+2]) +static inline bool is_multicast_ether_addr_64bits(const u8 *addr) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 #ifdef __BIG_ENDIAN @@ -372,8 +372,7 @@ static inline bool ether_addr_equal(const u8 *addr1, const u8 *addr2) * Please note that alignment of addr1 & addr2 are only guaranteed to be 16 bits. */ -static inline bool ether_addr_equal_64bits(const u8 addr1[6+2], - const u8 addr2[6+2]) +static inline bool ether_addr_equal_64bits(const u8 *addr1, const u8 *addr2) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 u64 fold = (*(const u64 *)addr1) ^ (*(const u64 *)addr2); diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 11efc45de66a..4af58459a1e7 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -70,17 +70,23 @@ enum { /** * struct kernel_ethtool_ringparam - RX/TX ring configuration * @rx_buf_len: Current length of buffers on the rx ring. + * @tcp_data_split: Scatter packet headers and data to separate buffers + * @cqe_size: Size of TX/RX completion queue event */ struct kernel_ethtool_ringparam { u32 rx_buf_len; + u8 tcp_data_split; + u32 cqe_size; }; /** * enum ethtool_supported_ring_param - indicator caps for setting ring params * @ETHTOOL_RING_USE_RX_BUF_LEN: capture for setting rx_buf_len + * @ETHTOOL_RING_USE_CQE_SIZE: capture for setting cqe_size */ enum ethtool_supported_ring_param { ETHTOOL_RING_USE_RX_BUF_LEN = BIT(0), + ETHTOOL_RING_USE_CQE_SIZE = BIT(1), }; #define __ETH_RSS_HASH_BIT(bit) ((u32)1 << (bit)) diff --git a/include/linux/filter.h b/include/linux/filter.h index 71fa57b88bfc..ed0c0ff42ad5 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -548,7 +548,7 @@ struct sock_fprog_kern { #define BPF_IMAGE_ALIGNMENT 8 struct bpf_binary_header { - u32 pages; + u32 size; u8 image[] __aligned(BPF_IMAGE_ALIGNMENT); }; @@ -566,13 +566,15 @@ struct bpf_prog { gpl_compatible:1, /* Is filter GPL compatible? */ cb_access:1, /* Is control block accessed? */ dst_needed:1, /* Do we need dst entry? */ + blinding_requested:1, /* needs constant blinding */ blinded:1, /* Was blinded */ is_func:1, /* program is a bpf function */ kprobe_override:1, /* Do we override a kprobe? */ has_callchain_buf:1, /* callchain buffer allocated? */ enforce_expected_attach_type:1, /* Enforce expected_attach_type checking at attach time */ call_get_stack:1, /* Do we call bpf_get_stack() or bpf_get_stackid() */ - call_get_func_ip:1; /* Do we call get_func_ip() */ + call_get_func_ip:1, /* Do we call get_func_ip() */ + tstamp_type_access:1; /* Accessed __sk_buff->tstamp_type */ enum bpf_prog_type type; /* Type of BPF program */ enum bpf_attach_type expected_attach_type; /* For some prog types */ u32 len; /* Number of filter blocks */ @@ -886,17 +888,8 @@ static inline void bpf_prog_lock_ro(struct bpf_prog *fp) static inline void bpf_jit_binary_lock_ro(struct bpf_binary_header *hdr) { set_vm_flush_reset_perms(hdr); - set_memory_ro((unsigned long)hdr, hdr->pages); - set_memory_x((unsigned long)hdr, hdr->pages); -} - -static inline struct bpf_binary_header * -bpf_jit_binary_hdr(const struct bpf_prog *fp) -{ - unsigned long real_start = (unsigned long)fp->bpf_func; - unsigned long addr = real_start & PAGE_MASK; - - return (void *)addr; + set_memory_ro((unsigned long)hdr, hdr->size >> PAGE_SHIFT); + set_memory_x((unsigned long)hdr, hdr->size >> PAGE_SHIFT); } int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap); @@ -1068,6 +1061,18 @@ void *bpf_jit_alloc_exec(unsigned long size); void bpf_jit_free_exec(void *addr); void bpf_jit_free(struct bpf_prog *fp); +struct bpf_binary_header * +bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **ro_image, + unsigned int alignment, + struct bpf_binary_header **rw_hdr, + u8 **rw_image, + bpf_jit_fill_hole_t bpf_fill_ill_insns); +int bpf_jit_binary_pack_finalize(struct bpf_prog *prog, + struct bpf_binary_header *ro_header, + struct bpf_binary_header *rw_header); +void bpf_jit_binary_pack_free(struct bpf_binary_header *ro_header, + struct bpf_binary_header *rw_header); + int bpf_jit_add_poke_descriptor(struct bpf_prog *prog, struct bpf_jit_poke_descriptor *poke); @@ -1356,7 +1361,10 @@ struct bpf_sockopt_kern { s32 level; s32 optname; s32 optlen; - s32 retval; + /* for retval in struct bpf_cg_run_ctx */ + struct task_struct *current_task; + /* Temporary "register" for indirect stores to ppos. */ + u64 tmp_reg; }; int copy_bpf_fprog_from_user(struct sock_fprog *dst, sockptr_t src, int len); diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h new file mode 100644 index 000000000000..1c2bde0ead73 --- /dev/null +++ b/include/linux/fprobe.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Simple ftrace probe wrapper */ +#ifndef _LINUX_FPROBE_H +#define _LINUX_FPROBE_H + +#include +#include +#include + +/** + * struct fprobe - ftrace based probe. + * @ops: The ftrace_ops. + * @nmissed: The counter for missing events. + * @flags: The status flag. + * @rethook: The rethook data structure. (internal data) + * @entry_handler: The callback function for function entry. + * @exit_handler: The callback function for function exit. + */ +struct fprobe { +#ifdef CONFIG_FUNCTION_TRACER + /* + * If CONFIG_FUNCTION_TRACER is not set, CONFIG_FPROBE is disabled too. + * But user of fprobe may keep embedding the struct fprobe on their own + * code. To avoid build error, this will keep the fprobe data structure + * defined here, but remove ftrace_ops data structure. + */ + struct ftrace_ops ops; +#endif + unsigned long nmissed; + unsigned int flags; + struct rethook *rethook; + + void (*entry_handler)(struct fprobe *fp, unsigned long entry_ip, struct pt_regs *regs); + void (*exit_handler)(struct fprobe *fp, unsigned long entry_ip, struct pt_regs *regs); +}; + +/* This fprobe is soft-disabled. */ +#define FPROBE_FL_DISABLED 1 + +/* + * This fprobe handler will be shared with kprobes. + * This flag must be set before registering. + */ +#define FPROBE_FL_KPROBE_SHARED 2 + +static inline bool fprobe_disabled(struct fprobe *fp) +{ + return (fp) ? fp->flags & FPROBE_FL_DISABLED : false; +} + +static inline bool fprobe_shared_with_kprobes(struct fprobe *fp) +{ + return (fp) ? fp->flags & FPROBE_FL_KPROBE_SHARED : false; +} + +#ifdef CONFIG_FPROBE +int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter); +int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num); +int register_fprobe_syms(struct fprobe *fp, const char **syms, int num); +int unregister_fprobe(struct fprobe *fp); +#else +static inline int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter) +{ + return -EOPNOTSUPP; +} +static inline int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num) +{ + return -EOPNOTSUPP; +} +static inline int register_fprobe_syms(struct fprobe *fp, const char **syms, int num) +{ + return -EOPNOTSUPP; +} +static inline int unregister_fprobe(struct fprobe *fp) +{ + return -EOPNOTSUPP; +} +#endif + +/** + * disable_fprobe() - Disable fprobe + * @fp: The fprobe to be disabled. + * + * This will soft-disable @fp. Note that this doesn't remove the ftrace + * hooks from the function entry. + */ +static inline void disable_fprobe(struct fprobe *fp) +{ + if (fp) + fp->flags |= FPROBE_FL_DISABLED; +} + +/** + * enable_fprobe() - Enable fprobe + * @fp: The fprobe to be enabled. + * + * This will soft-enable @fp. + */ +static inline void enable_fprobe(struct fprobe *fp) +{ + if (fp) + fp->flags &= ~FPROBE_FL_DISABLED; +} + +#endif diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 37b619185ec9..ed8cf433a46a 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -521,6 +521,8 @@ struct dyn_ftrace { int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip, int remove, int reset); +int ftrace_set_filter_ips(struct ftrace_ops *ops, unsigned long *ips, + unsigned int cnt, int remove, int reset); int ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf, int len, int reset); int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, @@ -811,6 +813,7 @@ static inline unsigned long ftrace_location(unsigned long ip) #define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; }) #define ftrace_set_early_filter(ops, buf, enable) do { } while (0) #define ftrace_set_filter_ip(ops, ip, remove, reset) ({ -ENODEV; }) +#define ftrace_set_filter_ips(ops, ips, cnt, remove, reset) ({ -ENODEV; }) #define ftrace_set_filter(ops, buf, len, reset) ({ -ENODEV; }) #define ftrace_set_notrace(ops, buf, len, reset) ({ -ENODEV; }) #define ftrace_free_filter(ops) do { } while (0) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 559b6c644938..75d40acb60c1 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -9,7 +9,7 @@ * Copyright (c) 2006, Michael Wu * Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright (c) 2016 - 2017 Intel Deutschland GmbH - * Copyright (c) 2018 - 2021 Intel Corporation + * Copyright (c) 2018 - 2022 Intel Corporation */ #ifndef LINUX_IEEE80211_H @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -1023,6 +1024,8 @@ struct ieee80211_tpc_report_ie { #define IEEE80211_ADDBA_EXT_FRAG_LEVEL_MASK GENMASK(2, 1) #define IEEE80211_ADDBA_EXT_FRAG_LEVEL_SHIFT 1 #define IEEE80211_ADDBA_EXT_NO_FRAG BIT(0) +#define IEEE80211_ADDBA_EXT_BUF_SIZE_MASK GENMASK(7, 5) +#define IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT 10 struct ieee80211_addba_ext_ie { u8 data; @@ -1697,10 +1700,12 @@ struct ieee80211_ht_operation { * A-MPDU buffer sizes * According to HT size varies from 8 to 64 frames * HE adds the ability to have up to 256 frames. + * EHT adds the ability to have up to 1K frames. */ #define IEEE80211_MIN_AMPDU_BUF 0x8 #define IEEE80211_MAX_AMPDU_BUF_HT 0x40 -#define IEEE80211_MAX_AMPDU_BUF 0x100 +#define IEEE80211_MAX_AMPDU_BUF_HE 0x100 +#define IEEE80211_MAX_AMPDU_BUF_EHT 0x400 /* Spatial Multiplexing Power Save Modes (for capability) */ @@ -1925,6 +1930,111 @@ struct ieee80211_mu_edca_param_set { struct ieee80211_he_mu_edca_param_ac_rec ac_vo; } __packed; +#define IEEE80211_EHT_MCS_NSS_RX 0x0f +#define IEEE80211_EHT_MCS_NSS_TX 0xf0 + +/** + * struct ieee80211_eht_mcs_nss_supp_20mhz_only - EHT 20MHz only station max + * supported NSS for per MCS. + * + * For each field below, bits 0 - 3 indicate the maximal number of spatial + * streams for Rx, and bits 4 - 7 indicate the maximal number of spatial streams + * for Tx. + * + * @rx_tx_mcs7_max_nss: indicates the maximum number of spatial streams + * supported for reception and the maximum number of spatial streams + * supported for transmission for MCS 0 - 7. + * @rx_tx_mcs9_max_nss: indicates the maximum number of spatial streams + * supported for reception and the maximum number of spatial streams + * supported for transmission for MCS 8 - 9. + * @rx_tx_mcs11_max_nss: indicates the maximum number of spatial streams + * supported for reception and the maximum number of spatial streams + * supported for transmission for MCS 10 - 11. + * @rx_tx_mcs13_max_nss: indicates the maximum number of spatial streams + * supported for reception and the maximum number of spatial streams + * supported for transmission for MCS 12 - 13. + */ +struct ieee80211_eht_mcs_nss_supp_20mhz_only { + u8 rx_tx_mcs7_max_nss; + u8 rx_tx_mcs9_max_nss; + u8 rx_tx_mcs11_max_nss; + u8 rx_tx_mcs13_max_nss; +}; + +/** + * struct ieee80211_eht_mcs_nss_supp_bw - EHT max supported NSS per MCS (except + * 20MHz only stations). + * + * For each field below, bits 0 - 3 indicate the maximal number of spatial + * streams for Rx, and bits 4 - 7 indicate the maximal number of spatial streams + * for Tx. + * + * @rx_tx_mcs9_max_nss: indicates the maximum number of spatial streams + * supported for reception and the maximum number of spatial streams + * supported for transmission for MCS 0 - 9. + * @rx_tx_mcs11_max_nss: indicates the maximum number of spatial streams + * supported for reception and the maximum number of spatial streams + * supported for transmission for MCS 10 - 11. + * @rx_tx_mcs13_max_nss: indicates the maximum number of spatial streams + * supported for reception and the maximum number of spatial streams + * supported for transmission for MCS 12 - 13. + */ +struct ieee80211_eht_mcs_nss_supp_bw { + u8 rx_tx_mcs9_max_nss; + u8 rx_tx_mcs11_max_nss; + u8 rx_tx_mcs13_max_nss; +}; + +/** + * struct ieee80211_eht_cap_elem_fixed - EHT capabilities fixed data + * + * This structure is the "EHT Capabilities element" fixed fields as + * described in P802.11be_D1.4 section 9.4.2.313. + * + * @mac_cap_info: MAC capabilities, see IEEE80211_EHT_MAC_CAP* + * @phy_cap_info: PHY capabilities, see IEEE80211_EHT_PHY_CAP* + */ +struct ieee80211_eht_cap_elem_fixed { + u8 mac_cap_info[2]; + u8 phy_cap_info[9]; +} __packed; + +/** + * struct ieee80211_eht_cap_elem - EHT capabilities element + * @fixed: fixed parts, see &ieee80211_eht_cap_elem_fixed + * @optional: optional parts + */ +struct ieee80211_eht_cap_elem { + struct ieee80211_eht_cap_elem_fixed fixed; + + /* + * Followed by: + * Supported EHT-MCS And NSS Set field: 4, 3, 6 or 9 octets. + * EHT PPE Thresholds field: variable length. + */ + u8 optional[]; +} __packed; + +/** + * struct ieee80211_eht_operation - eht operation element + * + * This structure is the "EHT Operation Element" fields as + * described in P802.11be_D1.4 section 9.4.2.311 + * + * FIXME: The spec is unclear how big the fields are, and doesn't + * indicate the "Disabled Subchannel Bitmap Present" in the + * structure (Figure 9-1002a) at all ... + */ +struct ieee80211_eht_operation { + u8 chan_width; + u8 ccfs; + u8 present_bm; + + u8 disable_subchannel_bitmap[]; +} __packed; + +#define IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT 0x1 + /* 802.11ac VHT Capabilities */ #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 0x00000000 #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 0x00000001 @@ -2129,6 +2239,8 @@ enum ieee80211_client_reg_power { #define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G 0x04 #define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G 0x08 #define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G 0x10 +#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL 0x1e + #define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G 0x20 #define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G 0x40 #define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK 0xfe @@ -2309,6 +2421,7 @@ ieee80211_he_mcs_nss_size(const struct ieee80211_he_cap_elem *he_cap) #define IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK 0x78 #define IEEE80211_PPE_THRES_RU_INDEX_BITMASK_POS (3) #define IEEE80211_PPE_THRES_INFO_PPET_SIZE (3) +#define IEEE80211_HE_PPE_THRES_INFO_HEADER_SIZE (7) /* * Calculate 802.11ax HE capabilities IE PPE field size @@ -2338,6 +2451,29 @@ ieee80211_he_ppe_size(u8 ppe_thres_hdr, const u8 *phy_cap_info) return n; } +static inline bool ieee80211_he_capa_size_ok(const u8 *data, u8 len) +{ + const struct ieee80211_he_cap_elem *he_cap_ie_elem = (const void *)data; + u8 needed = sizeof(*he_cap_ie_elem); + + if (len < needed) + return false; + + needed += ieee80211_he_mcs_nss_size(he_cap_ie_elem); + if (len < needed) + return false; + + if (he_cap_ie_elem->phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + if (len < needed + 1) + return false; + needed += ieee80211_he_ppe_size(data[needed], + he_cap_ie_elem->phy_cap_info); + } + + return len >= needed; +} + /* HE Operation defines */ #define IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK 0x00000007 #define IEEE80211_HE_OPERATION_TWT_REQUIRED 0x00000008 @@ -2427,7 +2563,7 @@ struct ieee80211_tx_pwr_env { static inline u8 ieee80211_he_oper_size(const u8 *he_oper_ie) { - struct ieee80211_he_operation *he_oper = (void *)he_oper_ie; + const struct ieee80211_he_operation *he_oper = (const void *)he_oper_ie; u8 oper_len = sizeof(struct ieee80211_he_operation); u32 he_oper_params; @@ -2460,7 +2596,7 @@ ieee80211_he_oper_size(const u8 *he_oper_ie) static inline const struct ieee80211_he_6ghz_oper * ieee80211_he_6ghz_oper(const struct ieee80211_he_operation *he_oper) { - const u8 *ret = (void *)&he_oper->optional; + const u8 *ret = (const void *)&he_oper->optional; u32 he_oper_params; if (!he_oper) @@ -2475,7 +2611,7 @@ ieee80211_he_6ghz_oper(const struct ieee80211_he_operation *he_oper) if (he_oper_params & IEEE80211_HE_OPERATION_CO_HOSTED_BSS) ret++; - return (void *)ret; + return (const void *)ret; } /* HE Spatial Reuse defines */ @@ -2496,7 +2632,7 @@ ieee80211_he_6ghz_oper(const struct ieee80211_he_operation *he_oper) static inline u8 ieee80211_he_spr_size(const u8 *he_spr_ie) { - struct ieee80211_he_spr *he_spr = (void *)he_spr_ie; + const struct ieee80211_he_spr *he_spr = (const void *)he_spr_ie; u8 spr_len = sizeof(struct ieee80211_he_spr); u8 he_spr_params; @@ -2599,6 +2735,194 @@ ieee80211_he_spr_size(const u8 *he_spr_ie) #define S1G_OPER_CH_WIDTH_PRIMARY_1MHZ BIT(0) #define S1G_OPER_CH_WIDTH_OPER GENMASK(4, 1) +/* EHT MAC capabilities as defined in P802.11be_D1.4 section 9.4.2.313.2 */ +#define IEEE80211_EHT_MAC_CAP0_NSEP_PRIO_ACCESS 0x01 +#define IEEE80211_EHT_MAC_CAP0_OM_CONTROL 0x02 +#define IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 0x04 +#define IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2 0x08 +#define IEEE80211_EHT_MAC_CAP0_RESTRICTED_TWT 0x10 +#define IEEE80211_EHT_MAC_CAP0_SCS_TRAFFIC_DESC 0x20 +#define IEEE80211_EHT_MAC_CAP0_MAX_AMPDU_LEN_MASK 0xc0 +#define IEEE80211_EHT_MAC_CAP0_MAX_AMPDU_LEN_3895 0 +#define IEEE80211_EHT_MAC_CAP0_MAX_AMPDU_LEN_7991 1 +#define IEEE80211_EHT_MAC_CAP0_MAX_AMPDU_LEN_11454 2 + +/* EHT PHY capabilities as defined in P802.11be_D1.4 section 9.4.2.313.3 */ +#define IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ 0x02 +#define IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ 0x04 +#define IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI 0x08 +#define IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO 0x10 +#define IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER 0x20 +#define IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE 0x40 + +/* EHT beamformee number of spatial streams <= 80MHz is split */ +#define IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK 0x80 +#define IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK 0x03 + +#define IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK 0x1c +#define IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK 0xe0 + +#define IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK 0x07 +#define IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK 0x38 + +/* EHT number of sounding dimensions for 320MHz is split */ +#define IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK 0xc0 +#define IEEE80211_EHT_PHY_CAP3_SOUNDING_DIM_320MHZ_MASK 0x01 +#define IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK 0x02 +#define IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK 0x04 +#define IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK 0x08 +#define IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK 0x10 +#define IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK 0x20 +#define IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK 0x40 +#define IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK 0x80 + +#define IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO 0x01 +#define IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP 0x02 +#define IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP 0x04 +#define IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI 0x08 +#define IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK 0xf0 + +#define IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK 0x01 +#define IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP 0x02 +#define IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP 0x04 +#define IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT 0x08 +#define IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK 0x30 +#define IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_0US 0 +#define IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US 1 +#define IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US 2 +#define IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_20US 3 + +/* Maximum number of supported EHT LTF is split */ +#define IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK 0xc0 +#define IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK 0x07 + +#define IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK 0x78 +#define IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP 0x80 + +#define IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW 0x01 +#define IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ 0x02 +#define IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ 0x04 +#define IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ 0x08 +#define IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ 0x10 +#define IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ 0x20 +#define IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ 0x40 +#define IEEE80211_EHT_PHY_CAP7_TB_SOUNDING_FDBK_RATE_LIMIT 0x80 + +#define IEEE80211_EHT_PHY_CAP8_RX_1024QAM_WIDER_BW_DL_OFDMA 0x01 +#define IEEE80211_EHT_PHY_CAP8_RX_4096QAM_WIDER_BW_DL_OFDMA 0x02 + +/* + * EHT operation channel width as defined in P802.11be_D1.4 section 9.4.2.311 + */ +#define IEEE80211_EHT_OPER_CHAN_WIDTH 0x7 +#define IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ 0 +#define IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ 1 +#define IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ 2 +#define IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ 3 +#define IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ 4 + +/* Calculate 802.11be EHT capabilities IE Tx/Rx EHT MCS NSS Support Field size */ +static inline u8 +ieee80211_eht_mcs_nss_size(const struct ieee80211_he_cap_elem *he_cap, + const struct ieee80211_eht_cap_elem_fixed *eht_cap) +{ + u8 count = 0; + + /* on 2.4 GHz, if it supports 40 MHz, the result is 3 */ + if (he_cap->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) + return 3; + + /* on 2.4 GHz, these three bits are reserved, so should be 0 */ + if (he_cap->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) + count += 3; + + if (he_cap->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) + count += 3; + + if (eht_cap->phy_cap_info[0] & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) + count += 3; + + return count ? count : 4; +} + +/* 802.11be EHT PPE Thresholds */ +#define IEEE80211_EHT_PPE_THRES_NSS_POS 0 +#define IEEE80211_EHT_PPE_THRES_NSS_MASK 0xf +#define IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK 0x1f0 +#define IEEE80211_EHT_PPE_THRES_INFO_PPET_SIZE 3 +#define IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE 9 + +/* + * Calculate 802.11be EHT capabilities IE EHT field size + */ +static inline u8 +ieee80211_eht_ppe_size(u16 ppe_thres_hdr, const u8 *phy_cap_info) +{ + u32 n; + + if (!(phy_cap_info[5] & + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT)) + return 0; + + n = hweight16(ppe_thres_hdr & + IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK); + n *= 1 + u16_get_bits(ppe_thres_hdr, IEEE80211_EHT_PPE_THRES_NSS_MASK); + + /* + * Each pair is 6 bits, and we need to add the 9 "header" bits to the + * total size. + */ + n = n * IEEE80211_EHT_PPE_THRES_INFO_PPET_SIZE * 2 + + IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE; + return DIV_ROUND_UP(n, 8); +} + +static inline bool +ieee80211_eht_capa_size_ok(const u8 *he_capa, const u8 *data, u8 len) +{ + const struct ieee80211_eht_cap_elem_fixed *elem = (const void *)data; + u8 needed = sizeof(struct ieee80211_eht_cap_elem_fixed); + + if (len < needed || !he_capa) + return false; + + needed += ieee80211_eht_mcs_nss_size((const void *)he_capa, + (const void *)data); + if (len < needed) + return false; + + if (elem->phy_cap_info[5] & + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) { + u16 ppe_thres_hdr; + + if (len < needed + sizeof(ppe_thres_hdr)) + return false; + + ppe_thres_hdr = get_unaligned_le16(data + needed); + needed += ieee80211_eht_ppe_size(ppe_thres_hdr, + elem->phy_cap_info); + } + + return len >= needed; +} + +static inline bool +ieee80211_eht_oper_size_ok(const u8 *data, u8 len) +{ + const struct ieee80211_eht_operation *elem = (const void *)data; + u8 needed = sizeof(*elem); + + if (len < needed) + return false; + + if (elem->present_bm & IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT) + needed += 2; + + return len >= needed; +} #define LISTEN_INT_USF GENMASK(15, 14) #define LISTEN_INT_UI GENMASK(13, 0) @@ -3054,6 +3378,9 @@ enum ieee80211_eid_ext { WLAN_EID_EXT_SHORT_SSID_LIST = 58, WLAN_EID_EXT_HE_6GHZ_CAPA = 59, WLAN_EID_EXT_UL_MU_POWER_CAPA = 60, + WLAN_EID_EXT_EHT_OPERATION = 106, + WLAN_EID_EXT_EHT_MULTI_LINK = 107, + WLAN_EID_EXT_EHT_CAPABILITY = 108, }; /* Action category code */ @@ -4005,10 +4332,10 @@ static inline bool for_each_element_completed(const struct element *element, #define IEEE80211_RNR_TBTT_PARAMS_COLOC_AP 0x40 struct ieee80211_neighbor_ap_info { - u8 tbtt_info_hdr; - u8 tbtt_info_len; - u8 op_class; - u8 channel; + u8 tbtt_info_hdr; + u8 tbtt_info_len; + u8 op_class; + u8 channel; } __packed; enum ieee80211_range_params_max_total_ltf { diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 509e18c7e740..d62ef428e3aa 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -58,6 +58,7 @@ struct br_ip_list { #define BR_MRP_LOST_CONT BIT(18) #define BR_MRP_LOST_IN_CONT BIT(19) #define BR_TX_FWD_OFFLOAD BIT(20) +#define BR_PORT_LOCKED BIT(21) #define BR_DEFAULT_AGEING_TIME (300 * HZ) @@ -118,6 +119,9 @@ int br_vlan_get_info(const struct net_device *dev, u16 vid, struct bridge_vlan_info *p_vinfo); int br_vlan_get_info_rcu(const struct net_device *dev, u16 vid, struct bridge_vlan_info *p_vinfo); +bool br_mst_enabled(const struct net_device *dev); +int br_mst_get_info(const struct net_device *dev, u16 msti, unsigned long *vids); +int br_mst_get_state(const struct net_device *dev, u16 msti, u8 *state); #else static inline bool br_vlan_enabled(const struct net_device *dev) { @@ -150,6 +154,22 @@ static inline int br_vlan_get_info_rcu(const struct net_device *dev, u16 vid, { return -EINVAL; } + +static inline bool br_mst_enabled(const struct net_device *dev) +{ + return false; +} + +static inline int br_mst_get_info(const struct net_device *dev, u16 msti, + unsigned long *vids) +{ + return -EINVAL; +} +static inline int br_mst_get_state(const struct net_device *dev, u16 msti, + u8 *state) +{ + return -EINVAL; +} #endif #if IS_ENABLED(CONFIG_BRIDGE) diff --git a/include/linux/if_hsr.h b/include/linux/if_hsr.h index 38bbc537d4e4..408539d5ea5f 100644 --- a/include/linux/if_hsr.h +++ b/include/linux/if_hsr.h @@ -9,6 +9,22 @@ enum hsr_version { PRP_V1, }; +/* HSR Tag. + * As defined in IEC-62439-3:2010, the HSR tag is really { ethertype = 0x88FB, + * path, LSDU_size, sequence Nr }. But we let eth_header() create { h_dest, + * h_source, h_proto = 0x88FB }, and add { path, LSDU_size, sequence Nr, + * encapsulated protocol } instead. + * + * Field names as defined in the IEC:2010 standard for HSR. + */ +struct hsr_tag { + __be16 path_and_LSDU_size; + __be16 sequence_nr; + __be16 encap_proto; +} __packed; + +#define HSR_HLEN 6 + #if IS_ENABLED(CONFIG_HSR) extern bool is_hsr_master(struct net_device *dev); extern int hsr_get_version(struct net_device *dev, enum hsr_version *ver); diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index 10c94a3936ca..b42294739063 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -21,6 +21,7 @@ struct macvlan_dev { struct hlist_node hlist; struct macvlan_port *port; struct net_device *lowerdev; + netdevice_tracker dev_tracker; void *accel_priv; struct vlan_pcpu_stats __percpu *pcpu_stats; diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 674aeead6260..ead323243e7b 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -150,6 +150,7 @@ struct in_ifaddr { __be32 ifa_broadcast; unsigned char ifa_scope; unsigned char ifa_prefixlen; + unsigned char ifa_proto; __u32 ifa_flags; char ifa_label[IFNAMSIZ]; diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index a59d25f19385..16870f86c74d 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -51,7 +51,7 @@ struct ipv6_devconf { __s32 use_optimistic; #endif #ifdef CONFIG_IPV6_MROUTE - __s32 mc_forwarding; + atomic_t mc_forwarding; #endif __s32 disable_ipv6; __s32 drop_unicast_in_l2_multicast; @@ -371,19 +371,12 @@ static inline struct ipv6_pinfo * inet6_sk(const struct sock *__sk) return NULL; } -static inline struct inet6_request_sock * - inet6_rsk(const struct request_sock *rsk) -{ - return NULL; -} - static inline struct raw6_sock *raw6_sk(const struct sock *sk) { return NULL; } #define inet6_rcv_saddr(__sk) NULL -#define tcp_twsk_ipv6only(__sk) 0 #define inet_v6_ipv6only(__sk) 0 #endif /* IS_ENABLED(CONFIG_IPV6) */ #endif /* _IPV6_H */ diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 19b884353b15..5f1859836deb 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -427,6 +427,9 @@ static inline struct kprobe *kprobe_running(void) { return NULL; } +#define kprobe_busy_begin() do {} while (0) +#define kprobe_busy_end() do {} while (0) + static inline int register_kprobe(struct kprobe *p) { return -EOPNOTSUPP; diff --git a/include/linux/linkmode.h b/include/linux/linkmode.h index f8397f300fcd..15e0e0209da4 100644 --- a/include/linux/linkmode.h +++ b/include/linux/linkmode.h @@ -66,11 +66,6 @@ static inline void linkmode_mod_bit(int nr, volatile unsigned long *addr, linkmode_clear_bit(nr, addr); } -static inline void linkmode_change_bit(int nr, volatile unsigned long *addr) -{ - __change_bit(nr, addr); -} - static inline int linkmode_test_bit(int nr, const volatile unsigned long *addr) { return test_bit(nr, addr); diff --git a/include/linux/mfd/idt82p33_reg.h b/include/linux/mfd/idt82p33_reg.h index 129a6c078221..1db532feeb91 100644 --- a/include/linux/mfd/idt82p33_reg.h +++ b/include/linux/mfd/idt82p33_reg.h @@ -7,6 +7,8 @@ #ifndef HAVE_IDT82P33_REG #define HAVE_IDT82P33_REG +#define REG_ADDR(page, offset) (((page) << 0x7) | ((offset) & 0x7f)) + /* Register address */ #define DPLL1_TOD_CNFG 0x134 #define DPLL2_TOD_CNFG 0x1B4 @@ -41,6 +43,7 @@ #define REG_SOFT_RESET 0X381 #define OUT_MUX_CNFG(outn) REG_ADDR(0x6, (0xC * (outn))) +#define TOD_TRIGGER(wr_trig, rd_trig) ((wr_trig & 0xf) << 4 | (rd_trig & 0xf)) /* Register bit definitions */ #define SYNC_TOD BIT(1) diff --git a/include/linux/mii.h b/include/linux/mii.h index 12ea29e04293..5ee13083cec7 100644 --- a/include/linux/mii.h +++ b/include/linux/mii.h @@ -354,56 +354,6 @@ static inline u32 mii_adv_to_ethtool_adv_x(u32 adv) return result; } -/** - * mii_lpa_mod_linkmode_adv_sgmii - * @lp_advertising: pointer to destination link mode. - * @lpa: value of the MII_LPA register - * - * A small helper function that translates MII_LPA bits to - * linkmode advertisement settings for SGMII. - * Leaves other bits unchanged. - */ -static inline void -mii_lpa_mod_linkmode_lpa_sgmii(unsigned long *lp_advertising, u32 lpa) -{ - u32 speed_duplex = lpa & LPA_SGMII_DPX_SPD_MASK; - - linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, lp_advertising, - speed_duplex == LPA_SGMII_1000HALF); - - linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, lp_advertising, - speed_duplex == LPA_SGMII_1000FULL); - - linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, lp_advertising, - speed_duplex == LPA_SGMII_100HALF); - - linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, lp_advertising, - speed_duplex == LPA_SGMII_100FULL); - - linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, lp_advertising, - speed_duplex == LPA_SGMII_10HALF); - - linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, lp_advertising, - speed_duplex == LPA_SGMII_10FULL); -} - -/** - * mii_lpa_to_linkmode_adv_sgmii - * @advertising: pointer to destination link mode. - * @lpa: value of the MII_LPA register - * - * A small helper function that translates MII_ADVERTISE bits - * to linkmode advertisement settings when in SGMII mode. - * Clears the old value of advertising. - */ -static inline void mii_lpa_to_linkmode_lpa_sgmii(unsigned long *lp_advertising, - u32 lpa) -{ - linkmode_zero(lp_advertising); - - mii_lpa_mod_linkmode_lpa_sgmii(lp_advertising, lpa); -} - /** * mii_adv_mod_linkmode_adv_t * @advertising:pointer to destination link mode. diff --git a/include/linux/mlx5/cq.h b/include/linux/mlx5/cq.h index 7bfb67363434..cb15308b5cb0 100644 --- a/include/linux/mlx5/cq.h +++ b/include/linux/mlx5/cq.h @@ -183,6 +183,8 @@ static inline void mlx5_cq_put(struct mlx5_core_cq *cq) complete(&cq->free); } +int mlx5_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, + u32 *in, int inlen, u32 *out, int outlen); int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, u32 *in, int inlen, u32 *out, int outlen); int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 319322a8ff94..9424503eb8d3 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -264,6 +264,14 @@ enum { struct mlx5_cmd_stats { u64 sum; u64 n; + /* number of times command failed */ + u64 failed; + /* number of times command failed on bad status returned by FW */ + u64 failed_mbox_status; + /* last command failed returned errno */ + u32 last_failed_errno; + /* last bad status returned by FW */ + u8 last_failed_mbox_status; struct dentry *root; /* protect command average calculations */ spinlock_t lock; @@ -543,6 +551,15 @@ struct mlx5_adev { int idx; }; +struct mlx5_debugfs_entries { + struct dentry *dbg_root; + struct dentry *qp_debugfs; + struct dentry *eq_debugfs; + struct dentry *cq_debugfs; + struct dentry *cmdif_debugfs; + struct dentry *pages_debugfs; +}; + struct mlx5_ft_pool; struct mlx5_priv { /* IRQ table valid only for real pci devices PF or VF */ @@ -553,21 +570,19 @@ struct mlx5_priv { struct mlx5_nb pg_nb; struct workqueue_struct *pg_wq; struct xarray page_root_xa; - int fw_pages; + u32 fw_pages; atomic_t reg_pages; struct list_head free_list; - int vfs_pages; - int host_pf_pages; + u32 vfs_pages; + u32 host_pf_pages; + u32 fw_pages_alloc_failed; + u32 give_pages_dropped; + u32 reclaim_pages_discard; struct mlx5_core_health health; struct list_head traps; - /* start: qp staff */ - struct dentry *qp_debugfs; - struct dentry *eq_debugfs; - struct dentry *cq_debugfs; - struct dentry *cmdif_debugfs; - /* end: qp staff */ + struct mlx5_debugfs_entries dbg; /* start: alloc staff */ /* protect buffer allocation according to numa node */ @@ -577,7 +592,6 @@ struct mlx5_priv { struct mutex pgdir_mutex; struct list_head pgdir_list; /* end: alloc staff */ - struct dentry *dbg_root; struct list_head ctx_list; spinlock_t ctx_lock; @@ -863,20 +877,10 @@ struct mlx5_hca_vport_context { bool grh_required; }; -static inline void *mlx5_buf_offset(struct mlx5_frag_buf *buf, int offset) -{ - return buf->frags->buf + offset; -} - #define STRUCT_FIELD(header, field) \ .struct_offset_bytes = offsetof(struct ib_unpacked_ ## header, field), \ .struct_size_bytes = sizeof((struct ib_unpacked_ ## header *)0)->field -static inline struct mlx5_core_dev *pci2mlx5_core_dev(struct pci_dev *pdev) -{ - return pci_get_drvdata(pdev); -} - extern struct dentry *mlx5_debugfs_root; static inline u16 fw_rev_maj(struct mlx5_core_dev *dev) @@ -965,6 +969,8 @@ typedef void (*mlx5_async_cbk_t)(int status, struct mlx5_async_work *context); struct mlx5_async_work { struct mlx5_async_ctx *ctx; mlx5_async_cbk_t user_callback; + u16 opcode; /* cmd opcode */ + void *out; /* pointer to the cmd output buffer */ }; void mlx5_cmd_init_async_ctx(struct mlx5_core_dev *dev, @@ -973,7 +979,9 @@ void mlx5_cmd_cleanup_async_ctx(struct mlx5_async_ctx *ctx); int mlx5_cmd_exec_cb(struct mlx5_async_ctx *ctx, void *in, int in_size, void *out, int out_size, mlx5_async_cbk_t callback, struct mlx5_async_work *work); - +void mlx5_cmd_out_err(struct mlx5_core_dev *dev, u16 opcode, u16 op_mod, void *out); +int mlx5_cmd_do(struct mlx5_core_dev *dev, void *in, int in_size, void *out, int out_size); +int mlx5_cmd_check(struct mlx5_core_dev *dev, int err, void *in, void *out); int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, int out_size); @@ -991,7 +999,6 @@ int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, int mlx5_cmd_exec_polling(struct mlx5_core_dev *dev, void *in, int in_size, void *out, int out_size); -void mlx5_cmd_mbox_status(void *out, u8 *status, u32 *syndrome); bool mlx5_cmd_is_down(struct mlx5_core_dev *dev); int mlx5_core_get_caps(struct mlx5_core_dev *dev, enum mlx5_cap_type cap_type); @@ -1002,9 +1009,6 @@ void mlx5_start_health_poll(struct mlx5_core_dev *dev); void mlx5_stop_health_poll(struct mlx5_core_dev *dev, bool disable_health); void mlx5_drain_health_wq(struct mlx5_core_dev *dev); void mlx5_trigger_health_work(struct mlx5_core_dev *dev); -int mlx5_buf_alloc(struct mlx5_core_dev *dev, - int size, struct mlx5_frag_buf *buf); -void mlx5_buf_free(struct mlx5_core_dev *dev, struct mlx5_frag_buf *buf); int mlx5_frag_buf_alloc_node(struct mlx5_core_dev *dev, int size, struct mlx5_frag_buf *buf, int node); void mlx5_frag_buf_free(struct mlx5_core_dev *dev, struct mlx5_frag_buf *buf); @@ -1023,6 +1027,8 @@ int mlx5_pagealloc_init(struct mlx5_core_dev *dev); void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev); void mlx5_pagealloc_start(struct mlx5_core_dev *dev); void mlx5_pagealloc_stop(struct mlx5_core_dev *dev); +void mlx5_pages_debugfs_init(struct mlx5_core_dev *dev); +void mlx5_pages_debugfs_cleanup(struct mlx5_core_dev *dev); void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id, s32 npages, bool ec_function); int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot); @@ -1030,15 +1036,18 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev); void mlx5_register_debugfs(void); void mlx5_unregister_debugfs(void); -void mlx5_fill_page_array(struct mlx5_frag_buf *buf, __be64 *pas); void mlx5_fill_page_frag_array_perm(struct mlx5_frag_buf *buf, __be64 *pas, u8 perm); void mlx5_fill_page_frag_array(struct mlx5_frag_buf *frag_buf, __be64 *pas); int mlx5_vector2eqn(struct mlx5_core_dev *dev, int vector, int *eqn); int mlx5_core_attach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn); int mlx5_core_detach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn); +struct dentry *mlx5_debugfs_get_dev_root(struct mlx5_core_dev *dev); void mlx5_qp_debugfs_init(struct mlx5_core_dev *dev); void mlx5_qp_debugfs_cleanup(struct mlx5_core_dev *dev); +int mlx5_access_reg(struct mlx5_core_dev *dev, void *data_in, int size_in, + void *data_out, int size_out, u16 reg_id, int arg, + int write, bool verbose); int mlx5_core_access_reg(struct mlx5_core_dev *dev, void *data_in, int size_in, void *data_out, int size_out, u16 reg_num, int arg, int write); diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index b1aad14689e3..e3bfed68b08a 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -224,6 +224,7 @@ struct mlx5_flow_act { u32 flags; struct mlx5_fs_vlan vlan[MLX5_FS_VLAN_DEPTH]; struct ib_counters *counters; + struct mlx5_flow_group *fg; }; #define MLX5_DECLARE_FLOW_ACT(name) \ diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 0235054fe55c..7d2d0ba82144 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -63,13 +63,6 @@ enum { MLX5_EVENT_TYPE_CODING_FPGA_QP_ERROR = 0x21 }; -enum { - MLX5_MODIFY_TIR_BITMASK_LRO = 0x0, - MLX5_MODIFY_TIR_BITMASK_INDIRECT_TABLE = 0x1, - MLX5_MODIFY_TIR_BITMASK_HASH = 0x2, - MLX5_MODIFY_TIR_BITMASK_TUNNELED_OFFLOAD_EN = 0x3 -}; - enum { MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE = 0x0, MLX5_SET_HCA_CAP_OP_MOD_ODP = 0x2, @@ -505,7 +498,10 @@ struct mlx5_ifc_fte_match_set_lyr_2_4_bits { u8 tcp_sport[0x10]; u8 tcp_dport[0x10]; - u8 reserved_at_c0[0x18]; + u8 reserved_at_c0[0x10]; + u8 ipv4_ihl[0x4]; + u8 reserved_at_c4[0x4]; + u8 ttl_hoplimit[0x8]; u8 udp_sport[0x10]; @@ -1355,6 +1351,7 @@ enum mlx5_fc_bulk_alloc_bitmask { enum { MLX5_STEERING_FORMAT_CONNECTX_5 = 0, MLX5_STEERING_FORMAT_CONNECTX_6DX = 1, + MLX5_STEERING_FORMAT_CONNECTX_7 = 2, }; struct mlx5_ifc_cmd_hca_cap_bits { @@ -1431,8 +1428,9 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 reserved_at_130[0xa]; u8 log_max_ra_res_dc[0x6]; - u8 reserved_at_140[0x6]; + u8 reserved_at_140[0x5]; u8 release_all_pages[0x1]; + u8 must_not_use[0x1]; u8 reserved_at_147[0x2]; u8 roce_accl[0x1]; u8 log_max_ra_req_qp[0x6]; @@ -9700,7 +9698,10 @@ struct mlx5_ifc_pcam_reg_bits { }; struct mlx5_ifc_mcam_enhanced_features_bits { - u8 reserved_at_0[0x6b]; + u8 reserved_at_0[0x5d]; + u8 mcia_32dwords[0x1]; + u8 reserved_at_5e[0xc]; + u8 reset_state[0x1]; u8 ptpcyc2realtime_modify[0x1]; u8 reserved_at_6c[0x2]; u8 pci_status_and_power[0x1]; @@ -9894,10 +9895,10 @@ struct mlx5_ifc_pcmr_reg_bits { }; struct mlx5_ifc_lane_2_module_mapping_bits { - u8 reserved_at_0[0x6]; - u8 rx_lane[0x2]; - u8 reserved_at_8[0x6]; - u8 tx_lane[0x2]; + u8 reserved_at_0[0x4]; + u8 rx_lane[0x4]; + u8 reserved_at_8[0x4]; + u8 tx_lane[0x4]; u8 reserved_at_10[0x8]; u8 module[0x8]; }; @@ -10381,6 +10382,14 @@ struct mlx5_ifc_mcda_reg_bits { u8 data[][0x20]; }; +enum { + MLX5_MFRL_REG_RESET_STATE_IDLE = 0, + MLX5_MFRL_REG_RESET_STATE_IN_NEGOTIATION = 1, + MLX5_MFRL_REG_RESET_STATE_RESET_IN_PROGRESS = 2, + MLX5_MFRL_REG_RESET_STATE_TIMEOUT = 3, + MLX5_MFRL_REG_RESET_STATE_NACK = 4, +}; + enum { MLX5_MFRL_REG_RESET_TYPE_FULL_CHIP = BIT(0), MLX5_MFRL_REG_RESET_TYPE_NET_PORT_ALIVE = BIT(1), @@ -10399,7 +10408,8 @@ struct mlx5_ifc_mfrl_reg_bits { u8 pci_sync_for_fw_update_start[0x1]; u8 pci_sync_for_fw_update_resp[0x2]; u8 rst_type_sel[0x3]; - u8 reserved_at_28[0x8]; + u8 reserved_at_28[0x4]; + u8 reset_state[0x4]; u8 reset_type[0x8]; u8 reset_level[0x8]; }; diff --git a/include/linux/mlx5/port.h b/include/linux/mlx5/port.h index 77ea4f9c5265..28a928b0684b 100644 --- a/include/linux/mlx5/port.h +++ b/include/linux/mlx5/port.h @@ -56,8 +56,6 @@ enum mlx5_an_status { MLX5_AN_LINK_DOWN = 4, }; -#define MLX5_EEPROM_MAX_BYTES 32 -#define MLX5_EEPROM_IDENTIFIER_BYTE_MASK 0x000000ff #define MLX5_I2C_ADDR_LOW 0x50 #define MLX5_I2C_ADDR_HIGH 0x51 #define MLX5_EEPROM_PAGE_LENGTH 256 diff --git a/include/linux/mlx5/qp.h b/include/linux/mlx5/qp.h index 61e48d459b23..8bda3ba5b109 100644 --- a/include/linux/mlx5/qp.h +++ b/include/linux/mlx5/qp.h @@ -202,6 +202,9 @@ struct mlx5_wqe_fmr_seg { struct mlx5_wqe_ctrl_seg { __be32 opmod_idx_opcode; __be32 qpn_ds; + + struct_group(trailer, + u8 signature; u8 rsvd[2]; u8 fm_ce_se; @@ -211,6 +214,8 @@ struct mlx5_wqe_ctrl_seg { __be32 umr_mkey; __be32 tis_tir_num; }; + + ); /* end of trailer group */ }; #define MLX5_WQE_CTRL_DS_MASK 0x3f diff --git a/include/linux/net/intel/i40e_client.h b/include/linux/net/intel/i40e_client.h index 6b3267b49755..ed42bd5f639f 100644 --- a/include/linux/net/intel/i40e_client.h +++ b/include/linux/net/intel/i40e_client.h @@ -26,11 +26,6 @@ struct i40e_client_version { u8 rsvd; }; -enum i40e_client_state { - __I40E_CLIENT_NULL, - __I40E_CLIENT_REGISTERED -}; - enum i40e_client_instance_state { __I40E_CLIENT_INSTANCE_NONE, __I40E_CLIENT_INSTANCE_OPENED, @@ -190,11 +185,6 @@ struct i40e_client { const struct i40e_client_ops *ops; /* client ops provided by the client */ }; -static inline bool i40e_client_is_registered(struct i40e_client *client) -{ - return test_bit(__I40E_CLIENT_REGISTERED, &client->state); -} - void i40e_client_device_register(struct i40e_info *ldev, struct i40e_client *client); void i40e_client_device_unregister(struct i40e_info *ldev); diff --git a/include/linux/net/intel/iidc.h b/include/linux/net/intel/iidc.h index 1289593411d3..1c1332e4df26 100644 --- a/include/linux/net/intel/iidc.h +++ b/include/linux/net/intel/iidc.h @@ -32,6 +32,8 @@ enum iidc_rdma_protocol { }; #define IIDC_MAX_USER_PRIORITY 8 +#define IIDC_MAX_DSCP_MAPPING 64 +#define IIDC_DSCP_PFC_MODE 0x1 /* Struct to hold per RDMA Qset info */ struct iidc_rdma_qset_params { @@ -60,6 +62,8 @@ struct iidc_qos_params { u8 vport_relative_bw; u8 vport_priority_type; u8 num_tc; + u8 pfc_mode; + u8 dscp_map[IIDC_MAX_DSCP_MAPPING]; }; struct iidc_event { diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f53ea7038441..cd7a597c55b1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -194,6 +195,14 @@ struct net_device_stats { unsigned long tx_compressed; }; +/* per-cpu stats, allocated on demand. + * Try to fit them in a single cache line, for dev_get_stats() sake. + */ +struct net_device_core_stats { + local_t rx_dropped; + local_t tx_dropped; + local_t rx_nohandler; +} __aligned(4 * sizeof(local_t)); #include #include @@ -1735,12 +1744,8 @@ enum netdev_ml_priv_type { * @stats: Statistics struct, which was left as a legacy, use * rtnl_link_stats64 instead * - * @rx_dropped: Dropped packets by core network, + * @core_stats: core networking counters, * do not use this in drivers - * @tx_dropped: Dropped packets by core network, - * do not use this in drivers - * @rx_nohandler: nohandler dropped packets by core network on - * inactive devices, do not use this in drivers * @carrier_up_count: Number of times the carrier has been up * @carrier_down_count: Number of times the carrier has been down * @@ -1894,6 +1899,8 @@ enum netdev_ml_priv_type { * @garp_port: GARP * @mrp_port: MRP * + * @dm_private: Drop monitor private + * * @dev: Class/net/name entry * @sysfs_groups: Space for optional device, statistics and wireless * sysfs groups @@ -1948,6 +1955,9 @@ enum netdev_ml_priv_type { * @dev_addr_shadow: Copy of @dev_addr to catch direct writes. * @linkwatch_dev_tracker: refcount tracker used by linkwatch. * @watchdog_dev_tracker: refcount tracker used by watchdog. + * @dev_registered_tracker: tracker for reference held while + * registered + * @offload_xstats_l3: L3 HW stats for this netdevice. * * FIXME: cleanup struct net_device such that network protocol info * moves out. @@ -2020,9 +2030,7 @@ struct net_device { struct net_device_stats stats; /* not used by modern drivers */ - atomic_long_t rx_dropped; - atomic_long_t tx_dropped; - atomic_long_t rx_nohandler; + struct net_device_core_stats __percpu *core_stats; /* Stats to monitor link on/off, flapping */ atomic_t carrier_up_count; @@ -2234,7 +2242,9 @@ struct net_device { #if IS_ENABLED(CONFIG_MRP) struct mrp_port __rcu *mrp_port; #endif - +#if IS_ENABLED(CONFIG_NET_DROP_MONITOR) + struct dm_hw_stat_delta __rcu *dm_private; +#endif struct device dev; const struct attribute_group *sysfs_groups[4]; const struct attribute_group *sysfs_rx_queue_group; @@ -2282,6 +2292,8 @@ struct net_device { u8 dev_addr_shadow[MAX_ADDR_LEN]; netdevice_tracker linkwatch_dev_tracker; netdevice_tracker watchdog_dev_tracker; + netdevice_tracker dev_registered_tracker; + struct rtnl_hw_stats64 *offload_xstats_l3; }; #define to_net_dev(d) container_of(d, struct net_device, dev) @@ -2722,6 +2734,10 @@ enum netdev_cmd { NETDEV_CVLAN_FILTER_DROP_INFO, NETDEV_SVLAN_FILTER_PUSH_INFO, NETDEV_SVLAN_FILTER_DROP_INFO, + NETDEV_OFFLOAD_XSTATS_ENABLE, + NETDEV_OFFLOAD_XSTATS_DISABLE, + NETDEV_OFFLOAD_XSTATS_REPORT_USED, + NETDEV_OFFLOAD_XSTATS_REPORT_DELTA, }; const char *netdev_cmd_to_name(enum netdev_cmd cmd); @@ -2772,6 +2788,42 @@ struct netdev_notifier_pre_changeaddr_info { const unsigned char *dev_addr; }; +enum netdev_offload_xstats_type { + NETDEV_OFFLOAD_XSTATS_TYPE_L3 = 1, +}; + +struct netdev_notifier_offload_xstats_info { + struct netdev_notifier_info info; /* must be first */ + enum netdev_offload_xstats_type type; + + union { + /* NETDEV_OFFLOAD_XSTATS_REPORT_DELTA */ + struct netdev_notifier_offload_xstats_rd *report_delta; + /* NETDEV_OFFLOAD_XSTATS_REPORT_USED */ + struct netdev_notifier_offload_xstats_ru *report_used; + }; +}; + +int netdev_offload_xstats_enable(struct net_device *dev, + enum netdev_offload_xstats_type type, + struct netlink_ext_ack *extack); +int netdev_offload_xstats_disable(struct net_device *dev, + enum netdev_offload_xstats_type type); +bool netdev_offload_xstats_enabled(const struct net_device *dev, + enum netdev_offload_xstats_type type); +int netdev_offload_xstats_get(struct net_device *dev, + enum netdev_offload_xstats_type type, + struct rtnl_hw_stats64 *stats, bool *used, + struct netlink_ext_ack *extack); +void +netdev_offload_xstats_report_delta(struct netdev_notifier_offload_xstats_rd *rd, + const struct rtnl_hw_stats64 *stats); +void +netdev_offload_xstats_report_used(struct netdev_notifier_offload_xstats_ru *ru); +void netdev_offload_xstats_push_delta(struct net_device *dev, + enum netdev_offload_xstats_type type, + const struct rtnl_hw_stats64 *stats); + static inline void netdev_notifier_info_init(struct netdev_notifier_info *info, struct net_device *dev) { @@ -3614,7 +3666,6 @@ static inline unsigned int get_netdev_rx_queue_index( } #endif -#define DEFAULT_MAX_NUM_RSS_QUEUES (8) int netif_get_num_default_rss_queues(void); enum skb_free_reason { @@ -3669,8 +3720,8 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp, void generic_xdp_tx(struct sk_buff *skb, struct bpf_prog *xdp_prog); int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb); int netif_rx(struct sk_buff *skb); -int netif_rx_ni(struct sk_buff *skb); -int netif_rx_any_context(struct sk_buff *skb); +int __netif_rx(struct sk_buff *skb); + int netif_receive_skb(struct sk_buff *skb); int netif_receive_skb_core(struct sk_buff *skb); void netif_receive_skb_list_internal(struct list_head *head); @@ -3792,13 +3843,42 @@ static __always_inline bool __is_skb_forwardable(const struct net_device *dev, return false; } +struct net_device_core_stats *netdev_core_stats_alloc(struct net_device *dev); + +static inline struct net_device_core_stats *dev_core_stats(struct net_device *dev) +{ + /* This READ_ONCE() pairs with the write in netdev_core_stats_alloc() */ + struct net_device_core_stats __percpu *p = READ_ONCE(dev->core_stats); + + if (likely(p)) + return this_cpu_ptr(p); + + return netdev_core_stats_alloc(dev); +} + +#define DEV_CORE_STATS_INC(FIELD) \ +static inline void dev_core_stats_##FIELD##_inc(struct net_device *dev) \ +{ \ + struct net_device_core_stats *p; \ + \ + preempt_disable(); \ + p = dev_core_stats(dev); \ + \ + if (p) \ + local_inc(&p->FIELD); \ + preempt_enable(); \ +} +DEV_CORE_STATS_INC(rx_dropped) +DEV_CORE_STATS_INC(tx_dropped) +DEV_CORE_STATS_INC(rx_nohandler) + static __always_inline int ____dev_forward_skb(struct net_device *dev, struct sk_buff *skb, const bool check_mtu) { if (skb_orphan_frags(skb, GFP_ATOMIC) || unlikely(!__is_skb_forwardable(dev, skb, check_mtu))) { - atomic_long_inc(&dev->rx_dropped); + dev_core_stats_rx_dropped_inc(dev); kfree_skb(skb); return NET_RX_DROP; } @@ -3817,14 +3897,7 @@ extern unsigned int netdev_budget_usecs; /* Called by rtnetlink.c:rtnl_unlock() */ void netdev_run_todo(void); -/** - * dev_put - release reference to device - * @dev: network device - * - * Release reference to device to allow it to be freed. - * Try using dev_put_track() instead. - */ -static inline void dev_put(struct net_device *dev) +static inline void __dev_put(struct net_device *dev) { if (dev) { #ifdef CONFIG_PCPU_DEV_REFCNT @@ -3835,14 +3908,7 @@ static inline void dev_put(struct net_device *dev) } } -/** - * dev_hold - get reference to device - * @dev: network device - * - * Hold reference to device to keep it from being freed. - * Try using dev_hold_track() instead. - */ -static inline void dev_hold(struct net_device *dev) +static inline void __dev_hold(struct net_device *dev) { if (dev) { #ifdef CONFIG_PCPU_DEV_REFCNT @@ -3853,11 +3919,24 @@ static inline void dev_hold(struct net_device *dev) } } +static inline void __netdev_tracker_alloc(struct net_device *dev, + netdevice_tracker *tracker, + gfp_t gfp) +{ +#ifdef CONFIG_NET_DEV_REFCNT_TRACKER + ref_tracker_alloc(&dev->refcnt_tracker, tracker, gfp); +#endif +} + +/* netdev_tracker_alloc() can upgrade a prior untracked reference + * taken by dev_get_by_name()/dev_get_by_index() to a tracked one. + */ static inline void netdev_tracker_alloc(struct net_device *dev, netdevice_tracker *tracker, gfp_t gfp) { #ifdef CONFIG_NET_DEV_REFCNT_TRACKER - ref_tracker_alloc(&dev->refcnt_tracker, tracker, gfp); + refcount_dec(&dev->refcnt_tracker.no_tracker); + __netdev_tracker_alloc(dev, tracker, gfp); #endif } @@ -3873,8 +3952,8 @@ static inline void dev_hold_track(struct net_device *dev, netdevice_tracker *tracker, gfp_t gfp) { if (dev) { - dev_hold(dev); - netdev_tracker_alloc(dev, tracker, gfp); + __dev_hold(dev); + __netdev_tracker_alloc(dev, tracker, gfp); } } @@ -3883,10 +3962,34 @@ static inline void dev_put_track(struct net_device *dev, { if (dev) { netdev_tracker_free(dev, tracker); - dev_put(dev); + __dev_put(dev); } } +/** + * dev_hold - get reference to device + * @dev: network device + * + * Hold reference to device to keep it from being freed. + * Try using dev_hold_track() instead. + */ +static inline void dev_hold(struct net_device *dev) +{ + dev_hold_track(dev, NULL, GFP_ATOMIC); +} + +/** + * dev_put - release reference to device + * @dev: network device + * + * Release reference to device to allow it to be freed. + * Try using dev_put_track() instead. + */ +static inline void dev_put(struct net_device *dev) +{ + dev_put_track(dev, NULL); +} + static inline void dev_replace_track(struct net_device *odev, struct net_device *ndev, netdevice_tracker *tracker, @@ -3895,11 +3998,11 @@ static inline void dev_replace_track(struct net_device *odev, if (odev) netdev_tracker_free(odev, tracker); - dev_hold(ndev); - dev_put(odev); + __dev_hold(ndev); + __dev_put(odev); if (ndev) - netdev_tracker_alloc(ndev, tracker, gfp); + __netdev_tracker_alloc(ndev, tracker, gfp); } /* Carrier loss detection, dial on demand. The functions netif_carrier_on diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 15e71bfff726..c2c6f332fb90 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -379,6 +379,7 @@ struct nf_nat_hook { unsigned int (*manip_pkt)(struct sk_buff *skb, struct nf_conn *ct, enum nf_nat_manip_type mtype, enum ip_conntrack_dir dir); + void (*remove_nat_bysrc)(struct nf_conn *ct); }; extern const struct nf_nat_hook __rcu *nf_nat_hook; diff --git a/include/linux/netfilter/nf_conntrack_pptp.h b/include/linux/netfilter/nf_conntrack_pptp.h index a28aa289afdc..c3bdb4370938 100644 --- a/include/linux/netfilter/nf_conntrack_pptp.h +++ b/include/linux/netfilter/nf_conntrack_pptp.h @@ -300,26 +300,22 @@ union pptp_ctrl_union { struct PptpSetLinkInfo setlink; }; -extern int -(*nf_nat_pptp_hook_outbound)(struct sk_buff *skb, - struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned int protoff, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq); - -extern int -(*nf_nat_pptp_hook_inbound)(struct sk_buff *skb, - struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned int protoff, - struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq); - -extern void -(*nf_nat_pptp_hook_exp_gre)(struct nf_conntrack_expect *exp_orig, - struct nf_conntrack_expect *exp_reply); - -extern void -(*nf_nat_pptp_hook_expectfn)(struct nf_conn *ct, - struct nf_conntrack_expect *exp); +struct nf_nat_pptp_hook { + int (*outbound)(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, + struct PptpControlHeader *ctlh, + union pptp_ctrl_union *pptpReq); + int (*inbound)(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff, + struct PptpControlHeader *ctlh, + union pptp_ctrl_union *pptpReq); + void (*exp_gre)(struct nf_conntrack_expect *exp_orig, + struct nf_conntrack_expect *exp_reply); + void (*expectfn)(struct nf_conn *ct, + struct nf_conntrack_expect *exp); +}; +extern const struct nf_nat_pptp_hook __rcu *nf_nat_pptp_hook; #endif /* _NF_CONNTRACK_PPTP_H */ diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 1ec631838af9..bda1c385cffb 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -135,15 +135,6 @@ static inline void nl_set_extack_cookie_u64(struct netlink_ext_ack *extack, extack->cookie_len = sizeof(cookie); } -static inline void nl_set_extack_cookie_u32(struct netlink_ext_ack *extack, - u32 cookie) -{ - if (!extack) - return; - memcpy(extack->cookie, &cookie, sizeof(cookie)); - extack->cookie_len = sizeof(cookie); -} - void netlink_kernel_release(struct sock *sk); int __netlink_change_ngroups(struct sock *sk, unsigned int groups); int netlink_change_ngroups(struct sock *sk, unsigned int groups); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 31dee2b65a62..3a6d1728e190 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2534,9 +2534,11 @@ #define PCI_DEVICE_ID_HUAWEI_HPRE_VF 0xa259 #define PCI_VENDOR_ID_NETRONOME 0x19ee +#define PCI_DEVICE_ID_NETRONOME_NFP3800 0x3800 #define PCI_DEVICE_ID_NETRONOME_NFP4000 0x4000 #define PCI_DEVICE_ID_NETRONOME_NFP5000 0x5000 #define PCI_DEVICE_ID_NETRONOME_NFP6000 0x6000 +#define PCI_DEVICE_ID_NETRONOME_NFP3800_VF 0x3803 #define PCI_DEVICE_ID_NETRONOME_NFP6000_VF 0x6003 #define PCI_VENDOR_ID_QMI 0x1a32 @@ -2564,6 +2566,8 @@ #define PCI_VENDOR_ID_HYGON 0x1d94 +#define PCI_VENDOR_ID_FUNGIBLE 0x1dad + #define PCI_VENDOR_ID_HXT 0x1dbf #define PCI_VENDOR_ID_TEKRAM 0x1de1 diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h index add077a81b21..266eb26fb029 100644 --- a/include/linux/pcs/pcs-xpcs.h +++ b/include/linux/pcs/pcs-xpcs.h @@ -31,8 +31,7 @@ void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode, phy_interface_t interface, int speed, int duplex); int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, unsigned int mode); -void xpcs_validate(struct dw_xpcs *xpcs, unsigned long *supported, - struct phylink_link_state *state); +void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces); int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable); struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, diff --git a/include/linux/phy.h b/include/linux/phy.h index 8fa70ba063a5..36ca2b5c2253 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -1578,6 +1578,7 @@ int genphy_update_link(struct phy_device *phydev); int genphy_read_lpa(struct phy_device *phydev); int genphy_read_status_fixed(struct phy_device *phydev); int genphy_read_status(struct phy_device *phydev); +int genphy_read_master_slave(struct phy_device *phydev); int genphy_suspend(struct phy_device *phydev); int genphy_resume(struct phy_device *phydev); int genphy_loopback(struct phy_device *phydev, bool enable); @@ -1661,7 +1662,7 @@ int phy_disable_interrupts(struct phy_device *phydev); void phy_request_interrupt(struct phy_device *phydev); void phy_free_interrupt(struct phy_device *phydev); void phy_print_status(struct phy_device *phydev); -int phy_set_max_speed(struct phy_device *phydev, u32 max_speed); +void phy_set_max_speed(struct phy_device *phydev, u32 max_speed); void phy_remove_link_mode(struct phy_device *phydev, u32 link_mode); void phy_advertise_supported(struct phy_device *phydev); void phy_support_sym_pause(struct phy_device *phydev); diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 713a0c928b7c..223781622b33 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -86,7 +86,6 @@ enum phylink_op_type { * @type: operation type of PHYLINK instance * @legacy_pre_march2020: driver has not been updated for March 2020 updates * (See commit 7cceb599d15d ("net: phylink: avoid mac_config calls") - * @pcs_poll: MAC PCS cannot provide link change interrupt * @poll_fixed_state: if true, starts link_poll, * if MAC link is at %MLO_AN_FIXED mode. * @ovr_an_inband: if true, override PCS to MLO_AN_INBAND @@ -100,7 +99,6 @@ struct phylink_config { struct device *dev; enum phylink_op_type type; bool legacy_pre_march2020; - bool pcs_poll; bool poll_fixed_state; bool ovr_an_inband; void (*get_fixed_state)(struct phylink_config *config, @@ -534,7 +532,6 @@ void phylink_generic_validate(struct phylink_config *config, struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *, phy_interface_t iface, const struct phylink_mac_ops *mac_ops); -void phylink_set_pcs(struct phylink *, struct phylink_pcs *pcs); void phylink_destroy(struct phylink *); int phylink_connect_phy(struct phylink *, struct phy_device *); @@ -582,7 +579,6 @@ int phylink_speed_up(struct phylink *pl); #define phylink_test(bm, mode) __phylink_do_bit(test_bit, bm, mode) void phylink_set_port_modes(unsigned long *bits); -void phylink_set_10g_modes(unsigned long *mask); void phylink_helper_basex_speed(struct phylink_link_state *state); void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state, diff --git a/include/linux/ptp_classify.h b/include/linux/ptp_classify.h index 9afd34a2d36c..fefa7790dc46 100644 --- a/include/linux/ptp_classify.h +++ b/include/linux/ptp_classify.h @@ -126,6 +126,17 @@ static inline u8 ptp_get_msgtype(const struct ptp_header *hdr, return msgtype; } +/** + * ptp_msg_is_sync - Evaluates whether the given skb is a PTP Sync message + * @skb: packet buffer + * @type: type of the packet (see ptp_classify_raw()) + * + * This function evaluates whether the given skb is a PTP Sync message. + * + * Return: true if sync message, false otherwise + */ +bool ptp_msg_is_sync(struct sk_buff *skb, unsigned int type); + void __init ptp_classifier_init(void); #else static inline void ptp_classifier_init(void) @@ -148,5 +159,9 @@ static inline u8 ptp_get_msgtype(const struct ptp_header *hdr, */ return PTP_MSGTYPE_SYNC; } +static inline bool ptp_msg_is_sync(struct sk_buff *skb, unsigned int type) +{ + return false; +} #endif #endif /* _PTP_CLASSIFY_H_ */ diff --git a/include/linux/ref_tracker.h b/include/linux/ref_tracker.h index 60f3453be23e..9ca353ab712b 100644 --- a/include/linux/ref_tracker.h +++ b/include/linux/ref_tracker.h @@ -13,6 +13,8 @@ struct ref_tracker_dir { spinlock_t lock; unsigned int quarantine_avail; refcount_t untracked; + refcount_t no_tracker; + bool dead; struct list_head list; /* List of active trackers */ struct list_head quarantine; /* List of dead trackers */ #endif @@ -26,7 +28,9 @@ static inline void ref_tracker_dir_init(struct ref_tracker_dir *dir, INIT_LIST_HEAD(&dir->quarantine); spin_lock_init(&dir->lock); dir->quarantine_avail = quarantine_count; + dir->dead = false; refcount_set(&dir->untracked, 1); + refcount_set(&dir->no_tracker, 1); stack_depot_init(); } diff --git a/include/linux/rethook.h b/include/linux/rethook.h new file mode 100644 index 000000000000..c8ac1e5afcd1 --- /dev/null +++ b/include/linux/rethook.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Return hooking with list-based shadow stack. + */ +#ifndef _LINUX_RETHOOK_H +#define _LINUX_RETHOOK_H + +#include +#include +#include +#include +#include +#include + +struct rethook_node; + +typedef void (*rethook_handler_t) (struct rethook_node *, void *, struct pt_regs *); + +/** + * struct rethook - The rethook management data structure. + * @data: The user-defined data storage. + * @handler: The user-defined return hook handler. + * @pool: The pool of struct rethook_node. + * @ref: The reference counter. + * @rcu: The rcu_head for deferred freeing. + * + * Don't embed to another data structure, because this is a self-destructive + * data structure when all rethook_node are freed. + */ +struct rethook { + void *data; + rethook_handler_t handler; + struct freelist_head pool; + refcount_t ref; + struct rcu_head rcu; +}; + +/** + * struct rethook_node - The rethook shadow-stack entry node. + * @freelist: The freelist, linked to struct rethook::pool. + * @rcu: The rcu_head for deferred freeing. + * @llist: The llist, linked to a struct task_struct::rethooks. + * @rethook: The pointer to the struct rethook. + * @ret_addr: The storage for the real return address. + * @frame: The storage for the frame pointer. + * + * You can embed this to your extended data structure to store any data + * on each entry of the shadow stack. + */ +struct rethook_node { + union { + struct freelist_node freelist; + struct rcu_head rcu; + }; + struct llist_node llist; + struct rethook *rethook; + unsigned long ret_addr; + unsigned long frame; +}; + +struct rethook *rethook_alloc(void *data, rethook_handler_t handler); +void rethook_free(struct rethook *rh); +void rethook_add_node(struct rethook *rh, struct rethook_node *node); +struct rethook_node *rethook_try_get(struct rethook *rh); +void rethook_recycle(struct rethook_node *node); +void rethook_hook(struct rethook_node *node, struct pt_regs *regs, bool mcount); +unsigned long rethook_find_ret_addr(struct task_struct *tsk, unsigned long frame, + struct llist_node **cur); + +/* Arch dependent code must implement arch_* and trampoline code */ +void arch_rethook_prepare(struct rethook_node *node, struct pt_regs *regs, bool mcount); +void arch_rethook_trampoline(void); + +/** + * is_rethook_trampoline() - Check whether the address is rethook trampoline + * @addr: The address to be checked + * + * Return true if the @addr is the rethook trampoline address. + */ +static inline bool is_rethook_trampoline(unsigned long addr) +{ + return addr == (unsigned long)dereference_symbol_descriptor(arch_rethook_trampoline); +} + +/* If the architecture needs to fixup the return address, implement it. */ +void arch_rethook_fixup_return(struct pt_regs *regs, + unsigned long correct_ret_addr); + +/* Generic trampoline handler, arch code must prepare asm stub */ +unsigned long rethook_trampoline_handler(struct pt_regs *regs, + unsigned long frame); + +#ifdef CONFIG_RETHOOK +void rethook_flush_task(struct task_struct *tk); +#else +#define rethook_flush_task(tsk) do { } while (0) +#endif + +#endif + diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index bb9cb84114c1..7f970b16da3a 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -134,4 +134,7 @@ extern int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, int (*vlan_fill)(struct sk_buff *skb, struct net_device *dev, u32 filter_mask)); + +extern void rtnl_offload_xstats_notify(struct net_device *dev); + #endif /* __LINUX_RTNETLINK_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index ff6901dcb06d..4a6fdd2a679f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1487,6 +1487,9 @@ struct task_struct { #ifdef CONFIG_KRETPROBES struct llist_head kretprobe_instances; #endif +#ifdef CONFIG_RETHOOK + struct llist_head rethooks; +#endif #ifdef CONFIG_ARCH_HAS_PARANOID_L1D_FLUSH /* diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 8a636e678902..3a30cae8b0a5 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -314,12 +314,136 @@ struct sk_buff; * used to translate the reason to string. */ enum skb_drop_reason { - SKB_DROP_REASON_NOT_SPECIFIED, - SKB_DROP_REASON_NO_SOCKET, - SKB_DROP_REASON_PKT_TOO_SMALL, - SKB_DROP_REASON_TCP_CSUM, - SKB_DROP_REASON_SOCKET_FILTER, - SKB_DROP_REASON_UDP_CSUM, + SKB_NOT_DROPPED_YET = 0, + SKB_DROP_REASON_NOT_SPECIFIED, /* drop reason is not specified */ + SKB_DROP_REASON_NO_SOCKET, /* socket not found */ + SKB_DROP_REASON_PKT_TOO_SMALL, /* packet size is too small */ + SKB_DROP_REASON_TCP_CSUM, /* TCP checksum error */ + SKB_DROP_REASON_SOCKET_FILTER, /* dropped by socket filter */ + SKB_DROP_REASON_UDP_CSUM, /* UDP checksum error */ + SKB_DROP_REASON_NETFILTER_DROP, /* dropped by netfilter */ + SKB_DROP_REASON_OTHERHOST, /* packet don't belong to current + * host (interface is in promisc + * mode) + */ + SKB_DROP_REASON_IP_CSUM, /* IP checksum error */ + SKB_DROP_REASON_IP_INHDR, /* there is something wrong with + * IP header (see + * IPSTATS_MIB_INHDRERRORS) + */ + SKB_DROP_REASON_IP_RPFILTER, /* IP rpfilter validate failed. + * see the document for rp_filter + * in ip-sysctl.rst for more + * information + */ + SKB_DROP_REASON_UNICAST_IN_L2_MULTICAST, /* destination address of L2 + * is multicast, but L3 is + * unicast. + */ + SKB_DROP_REASON_XFRM_POLICY, /* xfrm policy check failed */ + SKB_DROP_REASON_IP_NOPROTO, /* no support for IP protocol */ + SKB_DROP_REASON_SOCKET_RCVBUFF, /* socket receive buff is full */ + SKB_DROP_REASON_PROTO_MEM, /* proto memory limition, such as + * udp packet drop out of + * udp_memory_allocated. + */ + SKB_DROP_REASON_TCP_MD5NOTFOUND, /* no MD5 hash and one + * expected, corresponding + * to LINUX_MIB_TCPMD5NOTFOUND + */ + SKB_DROP_REASON_TCP_MD5UNEXPECTED, /* MD5 hash and we're not + * expecting one, corresponding + * to LINUX_MIB_TCPMD5UNEXPECTED + */ + SKB_DROP_REASON_TCP_MD5FAILURE, /* MD5 hash and its wrong, + * corresponding to + * LINUX_MIB_TCPMD5FAILURE + */ + SKB_DROP_REASON_SOCKET_BACKLOG, /* failed to add skb to socket + * backlog (see + * LINUX_MIB_TCPBACKLOGDROP) + */ + SKB_DROP_REASON_TCP_FLAGS, /* TCP flags invalid */ + SKB_DROP_REASON_TCP_ZEROWINDOW, /* TCP receive window size is zero, + * see LINUX_MIB_TCPZEROWINDOWDROP + */ + SKB_DROP_REASON_TCP_OLD_DATA, /* the TCP data reveived is already + * received before (spurious retrans + * may happened), see + * LINUX_MIB_DELAYEDACKLOST + */ + SKB_DROP_REASON_TCP_OVERWINDOW, /* the TCP data is out of window, + * the seq of the first byte exceed + * the right edges of receive + * window + */ + SKB_DROP_REASON_TCP_OFOMERGE, /* the data of skb is already in + * the ofo queue, corresponding to + * LINUX_MIB_TCPOFOMERGE + */ + SKB_DROP_REASON_IP_OUTNOROUTES, /* route lookup failed */ + SKB_DROP_REASON_BPF_CGROUP_EGRESS, /* dropped by + * BPF_PROG_TYPE_CGROUP_SKB + * eBPF program + */ + SKB_DROP_REASON_IPV6DISABLED, /* IPv6 is disabled on the device */ + SKB_DROP_REASON_NEIGH_CREATEFAIL, /* failed to create neigh + * entry + */ + SKB_DROP_REASON_NEIGH_FAILED, /* neigh entry in failed state */ + SKB_DROP_REASON_NEIGH_QUEUEFULL, /* arp_queue for neigh + * entry is full + */ + SKB_DROP_REASON_NEIGH_DEAD, /* neigh entry is dead */ + SKB_DROP_REASON_TC_EGRESS, /* dropped in TC egress HOOK */ + SKB_DROP_REASON_QDISC_DROP, /* dropped by qdisc when packet + * outputting (failed to enqueue to + * current qdisc) + */ + SKB_DROP_REASON_CPU_BACKLOG, /* failed to enqueue the skb to + * the per CPU backlog queue. This + * can be caused by backlog queue + * full (see netdev_max_backlog in + * net.rst) or RPS flow limit + */ + SKB_DROP_REASON_XDP, /* dropped by XDP in input path */ + SKB_DROP_REASON_TC_INGRESS, /* dropped in TC ingress HOOK */ + SKB_DROP_REASON_PTYPE_ABSENT, /* not packet_type found to handle + * the skb. For an etner packet, + * this means that L3 protocol is + * not supported + */ + SKB_DROP_REASON_SKB_CSUM, /* sk_buff checksum computation + * error + */ + SKB_DROP_REASON_SKB_GSO_SEG, /* gso segmentation error */ + SKB_DROP_REASON_SKB_UCOPY_FAULT, /* failed to copy data from + * user space, e.g., via + * zerocopy_sg_from_iter() + * or skb_orphan_frags_rx() + */ + SKB_DROP_REASON_DEV_HDR, /* device driver specific + * header/metadata is invalid + */ + /* the device is not ready to xmit/recv due to any of its data + * structure that is not up/ready/initialized, e.g., the IFF_UP is + * not set, or driver specific tun->tfiles[txq] is not initialized + */ + SKB_DROP_REASON_DEV_READY, + SKB_DROP_REASON_FULL_RING, /* ring buffer is full */ + SKB_DROP_REASON_NOMEM, /* error due to OOM */ + SKB_DROP_REASON_HDR_TRUNC, /* failed to trunc/extract the header + * from networking data, e.g., failed + * to pull the protocol header from + * frags via pskb_may_pull() + */ + SKB_DROP_REASON_TAP_FILTER, /* dropped by (ebpf) filter directly + * attached to tun/tap, e.g., via + * TUNSETFILTEREBPF + */ + SKB_DROP_REASON_TAP_TXFILTER, /* dropped by tx filter implemented + * at tun/tap, e.g., check_filter() + */ SKB_DROP_REASON_MAX, }; @@ -557,6 +681,7 @@ struct skb_shared_info { * Warning : all fields before dataref are cleared in __alloc_skb() */ atomic_t dataref; + unsigned int xdp_frags_size; /* Intermediate layers must ensure that destructor_arg * remains valid until skb destructor */ @@ -720,6 +845,10 @@ typedef unsigned char *sk_buff_data_t; * @dst_pending_confirm: need to confirm neighbour * @decrypted: Decrypted SKB * @slow_gro: state present at GRO time, slower prepare step required + * @mono_delivery_time: When set, skb->tstamp has the + * delivery_time in mono clock base (i.e. EDT). Otherwise, the + * skb->tstamp has the (rcv) timestamp at ingress and + * delivery_time at egress. * @napi_id: id of the NAPI struct this skb came from * @sender_cpu: (aka @napi_id) source CPU in XPS * @secmark: security marking @@ -862,8 +991,12 @@ struct sk_buff { __u8 vlan_present:1; /* See PKT_VLAN_PRESENT_BIT */ __u8 csum_complete_sw:1; __u8 csum_level:2; - __u8 csum_not_inet:1; __u8 dst_pending_confirm:1; + __u8 mono_delivery_time:1; /* See SKB_MONO_DELIVERY_TIME_MASK */ +#ifdef CONFIG_NET_CLS_ACT + __u8 tc_skip_classify:1; + __u8 tc_at_ingress:1; /* See TC_AT_INGRESS_MASK */ +#endif #ifdef CONFIG_IPV6_NDISC_NODETYPE __u8 ndisc_nodetype:2; #endif @@ -874,10 +1007,6 @@ struct sk_buff { #ifdef CONFIG_NET_SWITCHDEV __u8 offload_fwd_mark:1; __u8 offload_l3_fwd_mark:1; -#endif -#ifdef CONFIG_NET_CLS_ACT - __u8 tc_skip_classify:1; - __u8 tc_at_ingress:1; #endif __u8 redirected:1; #ifdef CONFIG_NET_REDIRECT @@ -890,6 +1019,7 @@ struct sk_buff { __u8 decrypted:1; #endif __u8 slow_gro:1; + __u8 csum_not_inet:1; #ifdef CONFIG_NET_SCHED __u16 tc_index; /* traffic control index */ @@ -964,11 +1094,17 @@ struct sk_buff { #endif #define PKT_TYPE_OFFSET offsetof(struct sk_buff, __pkt_type_offset) -/* if you move pkt_vlan_present around you also must adapt these constants */ +/* if you move pkt_vlan_present, tc_at_ingress, or mono_delivery_time + * around, you also must adapt these constants. + */ #ifdef __BIG_ENDIAN_BITFIELD #define PKT_VLAN_PRESENT_BIT 7 +#define TC_AT_INGRESS_MASK (1 << 0) +#define SKB_MONO_DELIVERY_TIME_MASK (1 << 2) #else #define PKT_VLAN_PRESENT_BIT 0 +#define TC_AT_INGRESS_MASK (1 << 7) +#define SKB_MONO_DELIVERY_TIME_MASK (1 << 5) #endif #define PKT_VLAN_PRESENT_OFFSET offsetof(struct sk_buff, __pkt_vlan_present_offset) @@ -1115,10 +1251,16 @@ static inline void kfree_skb(struct sk_buff *skb) } void skb_release_head_state(struct sk_buff *skb); -void kfree_skb_list(struct sk_buff *segs); +void kfree_skb_list_reason(struct sk_buff *segs, + enum skb_drop_reason reason); void skb_dump(const char *level, const struct sk_buff *skb, bool full_pkt); void skb_tx_error(struct sk_buff *skb); +static inline void kfree_skb_list(struct sk_buff *segs) +{ + kfree_skb_list_reason(segs, SKB_DROP_REASON_NOT_SPECIFIED); +} + #ifdef CONFIG_TRACEPOINTS void consume_skb(struct sk_buff *skb); #else @@ -1475,6 +1617,11 @@ static inline unsigned int skb_end_offset(const struct sk_buff *skb) { return skb->end; } + +static inline void skb_set_end_offset(struct sk_buff *skb, unsigned int offset) +{ + skb->end = offset; +} #else static inline unsigned char *skb_end_pointer(const struct sk_buff *skb) { @@ -1485,6 +1632,11 @@ static inline unsigned int skb_end_offset(const struct sk_buff *skb) { return skb->end - skb->head; } + +static inline void skb_set_end_offset(struct sk_buff *skb, unsigned int offset) +{ + skb->end = skb->head + offset; +} #endif /* Internal */ @@ -1724,19 +1876,19 @@ static inline int skb_unclone(struct sk_buff *skb, gfp_t pri) return 0; } -/* This variant of skb_unclone() makes sure skb->truesize is not changed */ +/* This variant of skb_unclone() makes sure skb->truesize + * and skb_end_offset() are not changed, whenever a new skb->head is needed. + * + * Indeed there is no guarantee that ksize(kmalloc(X)) == ksize(kmalloc(X)) + * when various debugging features are in place. + */ +int __skb_unclone_keeptruesize(struct sk_buff *skb, gfp_t pri); static inline int skb_unclone_keeptruesize(struct sk_buff *skb, gfp_t pri) { might_sleep_if(gfpflags_allow_blocking(pri)); - if (skb_cloned(skb)) { - unsigned int save = skb->truesize; - int res; - - res = pskb_expand_head(skb, 0, 0, pri); - skb->truesize = save; - return res; - } + if (skb_cloned(skb)) + return __skb_unclone_keeptruesize(skb, pri); return 0; } @@ -3891,6 +4043,7 @@ static inline void skb_get_new_timestampns(const struct sk_buff *skb, static inline void __net_timestamp(struct sk_buff *skb) { skb->tstamp = ktime_get_real(); + skb->mono_delivery_time = 0; } static inline ktime_t net_timedelta(ktime_t t) @@ -3898,8 +4051,53 @@ static inline ktime_t net_timedelta(ktime_t t) return ktime_sub(ktime_get_real(), t); } -static inline ktime_t net_invalid_timestamp(void) +static inline void skb_set_delivery_time(struct sk_buff *skb, ktime_t kt, + bool mono) { + skb->tstamp = kt; + skb->mono_delivery_time = kt && mono; +} + +DECLARE_STATIC_KEY_FALSE(netstamp_needed_key); + +/* It is used in the ingress path to clear the delivery_time. + * If needed, set the skb->tstamp to the (rcv) timestamp. + */ +static inline void skb_clear_delivery_time(struct sk_buff *skb) +{ + if (skb->mono_delivery_time) { + skb->mono_delivery_time = 0; + if (static_branch_unlikely(&netstamp_needed_key)) + skb->tstamp = ktime_get_real(); + else + skb->tstamp = 0; + } +} + +static inline void skb_clear_tstamp(struct sk_buff *skb) +{ + if (skb->mono_delivery_time) + return; + + skb->tstamp = 0; +} + +static inline ktime_t skb_tstamp(const struct sk_buff *skb) +{ + if (skb->mono_delivery_time) + return 0; + + return skb->tstamp; +} + +static inline ktime_t skb_tstamp_cond(const struct sk_buff *skb, bool cond) +{ + if (!skb->mono_delivery_time && skb->tstamp) + return skb->tstamp; + + if (static_branch_unlikely(&netstamp_needed_key) || cond) + return ktime_get_real(); + return 0; } @@ -4759,7 +4957,7 @@ static inline void skb_set_redirected(struct sk_buff *skb, bool from_ingress) #ifdef CONFIG_NET_REDIRECT skb->from_ingress = from_ingress; if (skb->from_ingress) - skb->tstamp = 0; + skb_clear_tstamp(skb); #endif } diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 18a717fe62eb..c5a2d6f50f25 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -29,7 +29,7 @@ struct sk_msg_sg { u32 end; u32 size; u32 copybreak; - unsigned long copy; + DECLARE_BITMAP(copy, MAX_MSG_FRAGS + 2); /* The extra two elements: * 1) used for chaining the front and sections when the list becomes * partitioned (e.g. end < start). The crypto APIs require the @@ -38,7 +38,6 @@ struct sk_msg_sg { */ struct scatterlist data[MAX_MSG_FRAGS + 2]; }; -static_assert(BITS_PER_LONG >= NR_MSG_FRAG_IDS); /* UAPI in filter.c depends on struct sk_msg_sg being first element. */ struct sk_msg { @@ -171,11 +170,6 @@ static inline u32 sk_msg_iter_dist(u32 start, u32 end) #define sk_msg_iter_next(msg, which) \ sk_msg_iter_var_next(msg->sg.which) -static inline void sk_msg_clear_meta(struct sk_msg *msg) -{ - memset(&msg->sg, 0, offsetofend(struct sk_msg_sg, copy)); -} - static inline void sk_msg_init(struct sk_msg *msg) { BUILD_BUG_ON(ARRAY_SIZE(msg->sg.data) - 1 != NR_MSG_FRAG_IDS); @@ -234,7 +228,7 @@ static inline void sk_msg_compute_data_pointers(struct sk_msg *msg) { struct scatterlist *sge = sk_msg_elem(msg, msg->sg.start); - if (test_bit(msg->sg.start, &msg->sg.copy)) { + if (test_bit(msg->sg.start, msg->sg.copy)) { msg->data = NULL; msg->data_end = NULL; } else { @@ -253,7 +247,7 @@ static inline void sk_msg_page_add(struct sk_msg *msg, struct page *page, sg_set_page(sge, page, len, offset); sg_unmark_end(sge); - __set_bit(msg->sg.end, &msg->sg.copy); + __set_bit(msg->sg.end, msg->sg.copy); msg->sg.size += len; sk_msg_iter_next(msg, end); } @@ -262,9 +256,9 @@ static inline void sk_msg_sg_copy(struct sk_msg *msg, u32 i, bool copy_state) { do { if (copy_state) - __set_bit(i, &msg->sg.copy); + __set_bit(i, msg->sg.copy); else - __clear_bit(i, &msg->sg.copy); + __clear_bit(i, msg->sg.copy); sk_msg_iter_var_next(i); if (i == msg->sg.end) break; @@ -310,21 +304,16 @@ static inline void sock_drop(struct sock *sk, struct sk_buff *skb) kfree_skb(skb); } -static inline void drop_sk_msg(struct sk_psock *psock, struct sk_msg *msg) -{ - if (msg->skb) - sock_drop(psock->sk, msg->skb); - kfree(msg); -} - static inline void sk_psock_queue_msg(struct sk_psock *psock, struct sk_msg *msg) { spin_lock_bh(&psock->ingress_lock); if (sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) list_add_tail(&msg->list, &psock->ingress_msg); - else - drop_sk_msg(psock, msg); + else { + sk_msg_free(psock->sk, msg); + kfree(msg); + } spin_unlock_bh(&psock->ingress_lock); } diff --git a/include/linux/socket.h b/include/linux/socket.h index 8ef26d89ef49..6f85f5d957ef 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -366,6 +366,7 @@ struct ucred { #define SOL_XDP 283 #define SOL_MPTCP 284 #define SOL_MCTP 285 +#define SOL_SMC 286 /* IPX options */ #define IPX_TYPE 1 diff --git a/include/linux/sort.h b/include/linux/sort.h index b5898725fe9d..e163287ac6c1 100644 --- a/include/linux/sort.h +++ b/include/linux/sort.h @@ -6,7 +6,7 @@ void sort_r(void *base, size_t num, size_t size, cmp_r_func_t cmp_func, - swap_func_t swap_func, + swap_r_func_t swap_func, const void *priv); void sort(void *base, size_t num, size_t size, diff --git a/include/linux/ssb/ssb_driver_gige.h b/include/linux/ssb/ssb_driver_gige.h index 15ba0df1ee0d..28c145a51e57 100644 --- a/include/linux/ssb/ssb_driver_gige.h +++ b/include/linux/ssb/ssb_driver_gige.h @@ -95,7 +95,7 @@ static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev) struct ssb_gige *dev = pdev_to_ssb_gige(pdev); if (dev) return (dev->dev->bus->chip_id == 0x4785); - return 0; + return false; } /* Get the device MAC address */ diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h index 20068ccfd0cc..d42a75b3be10 100644 --- a/include/linux/sunrpc/svc_xprt.h +++ b/include/linux/sunrpc/svc_xprt.h @@ -89,6 +89,7 @@ struct svc_xprt { struct list_head xpt_users; /* callbacks on free */ struct net *xpt_net; + netns_tracker ns_tracker; const struct cred *xpt_cred; struct rpc_xprt *xpt_bc_xprt; /* NFSv4.1 backchannel */ struct rpc_xprt_switch *xpt_bc_xps; /* NFSv4.1 backchannel */ diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 955ea4d7af0b..3cdc8d878d81 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -284,6 +284,7 @@ struct rpc_xprt { } stat; struct net *xprt_net; + netns_tracker ns_tracker; const char *servername; const char *address_strings[RPC_DISPLAY_MAX]; #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 78b91bb92f0d..1168302b7927 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -394,6 +394,7 @@ struct tcp_sock { bool is_mptcp; #endif #if IS_ENABLED(CONFIG_SMC) + bool (*smc_hs_congested)(const struct sock *sk); bool syn_smc; /* SYN includes SMC */ #endif diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 6c7ae3c2ba9b..e6e95a9f07a5 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -15,6 +15,7 @@ struct array_buffer; struct tracer; struct dentry; struct bpf_prog; +union bpf_attr; const char *trace_print_flags_seq(struct trace_seq *p, const char *delim, unsigned long flags, @@ -745,6 +746,7 @@ void bpf_put_raw_tracepoint(struct bpf_raw_event_map *btp); int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id, u32 *fd_type, const char **buf, u64 *probe_offset, u64 *probe_addr); +int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog); #else static inline unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx) { @@ -786,6 +788,11 @@ static inline int bpf_get_perf_event_info(const struct perf_event *event, { return -EOPNOTSUPP; } +static inline int +bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) +{ + return -EOPNOTSUPP; +} #endif enum { diff --git a/include/linux/types.h b/include/linux/types.h index ac825ad90e44..ea8cf60a8a79 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -226,6 +226,7 @@ struct callback_head { typedef void (*rcu_callback_t)(struct rcu_head *head); typedef void (*call_rcu_func_t)(struct rcu_head *head, rcu_callback_t func); +typedef void (*swap_r_func_t)(void *a, void *b, int size, const void *priv); typedef void (*swap_func_t)(void *a, void *b, int size); typedef int (*cmp_r_func_t)(const void *a, const void *b, const void *priv); diff --git a/include/linux/udp.h b/include/linux/udp.h index ae66dadd8543..254a2654400f 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -23,11 +23,6 @@ static inline struct udphdr *udp_hdr(const struct sk_buff *skb) return (struct udphdr *)skb_transport_header(skb); } -static inline struct udphdr *inner_udp_hdr(const struct sk_buff *skb) -{ - return (struct udphdr *)skb_inner_transport_header(skb); -} - #define UDP_HTABLE_SIZE_MIN (CONFIG_BASE_SMALL ? 128 : 256) static inline u32 udp_hashfn(const struct net *net, u32 num, u32 mask) diff --git a/include/linux/uio.h b/include/linux/uio.h index 1198a2bfc9bf..739285fe5a2f 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -273,6 +273,23 @@ static inline void iov_iter_reexpand(struct iov_iter *i, size_t count) i->count = count; } +static inline int +iov_iter_npages_cap(struct iov_iter *i, int maxpages, size_t max_bytes) +{ + size_t shorted = 0; + int npages; + + if (iov_iter_count(i) > max_bytes) { + shorted = iov_iter_count(i) - max_bytes; + iov_iter_truncate(i, max_bytes); + } + npages = iov_iter_npages(i, INT_MAX); + if (shorted) + iov_iter_reexpand(i, iov_iter_count(i) + shorted); + + return npages; +} + struct csum_state { __wsum csum; size_t off; diff --git a/include/linux/wwan.h b/include/linux/wwan.h index afb3334ec8c5..5ce2acf444fb 100644 --- a/include/linux/wwan.h +++ b/include/linux/wwan.h @@ -174,11 +174,13 @@ void wwan_unregister_ops(struct device *parent); #ifdef CONFIG_WWAN_DEBUGFS struct dentry *wwan_get_debugfs_dir(struct device *parent); +void wwan_put_debugfs_dir(struct dentry *dir); #else static inline struct dentry *wwan_get_debugfs_dir(struct device *parent) { return ERR_PTR(-ENODEV); } +static inline void wwan_put_debugfs_dir(struct dentry *dir) {} #endif #endif /* __WWAN_H */ diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 59940e230b78..f7506f08e505 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -64,6 +64,8 @@ struct ifa6_config { const struct in6_addr *pfx; unsigned int plen; + u8 ifa_proto; + const struct in6_addr *peer_pfx; u32 rt_priority; diff --git a/include/net/arp.h b/include/net/arp.h index 031374ac2f22..d7ef4ec71dfe 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -65,6 +65,7 @@ void arp_send(int type, int ptype, __be32 dest_ip, const unsigned char *src_hw, const unsigned char *th); int arp_mc_map(__be32 addr, u8 *haddr, struct net_device *dev, int dir); void arp_ifdown(struct net_device *dev); +int arp_invalidate(struct net_device *dev, __be32 ip, bool force); struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip, struct net_device *dev, __be32 src_ip, diff --git a/include/net/ax25.h b/include/net/ax25.h index 8221af1811df..0f9790c455bb 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -187,18 +187,12 @@ typedef struct { typedef struct ax25_route { struct ax25_route *next; - refcount_t refcount; ax25_address callsign; struct net_device *dev; ax25_digi *digipeat; char ip_mode; } ax25_route; -static inline void ax25_hold_route(ax25_route *ax25_rt) -{ - refcount_inc(&ax25_rt->refcount); -} - void __ax25_put_route(ax25_route *ax25_rt); extern rwlock_t ax25_route_lock; @@ -213,12 +207,6 @@ static inline void ax25_route_lock_unuse(void) read_unlock(&ax25_route_lock); } -static inline void ax25_put_route(ax25_route *ax25_rt) -{ - if (refcount_dec_and_test(&ax25_rt->refcount)) - __ax25_put_route(ax25_rt); -} - typedef struct { char slave; /* slave_mode? */ struct timer_list slave_timer; /* timeout timer */ diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index a647e5fabdbd..6b48d9e2aab9 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -204,19 +204,21 @@ void bt_err_ratelimited(const char *fmt, ...); #define BT_DBG(fmt, ...) pr_debug(fmt "\n", ##__VA_ARGS__) #endif +#define bt_dev_name(hdev) ((hdev) ? (hdev)->name : "null") + #define bt_dev_info(hdev, fmt, ...) \ - BT_INFO("%s: " fmt, (hdev)->name, ##__VA_ARGS__) + BT_INFO("%s: " fmt, bt_dev_name(hdev), ##__VA_ARGS__) #define bt_dev_warn(hdev, fmt, ...) \ - BT_WARN("%s: " fmt, (hdev)->name, ##__VA_ARGS__) + BT_WARN("%s: " fmt, bt_dev_name(hdev), ##__VA_ARGS__) #define bt_dev_err(hdev, fmt, ...) \ - BT_ERR("%s: " fmt, (hdev)->name, ##__VA_ARGS__) + BT_ERR("%s: " fmt, bt_dev_name(hdev), ##__VA_ARGS__) #define bt_dev_dbg(hdev, fmt, ...) \ - BT_DBG("%s: " fmt, (hdev)->name, ##__VA_ARGS__) + BT_DBG("%s: " fmt, bt_dev_name(hdev), ##__VA_ARGS__) #define bt_dev_warn_ratelimited(hdev, fmt, ...) \ - bt_warn_ratelimited("%s: " fmt, (hdev)->name, ##__VA_ARGS__) + bt_warn_ratelimited("%s: " fmt, bt_dev_name(hdev), ##__VA_ARGS__) #define bt_dev_err_ratelimited(hdev, fmt, ...) \ - bt_err_ratelimited("%s: " fmt, (hdev)->name, ##__VA_ARGS__) + bt_err_ratelimited("%s: " fmt, bt_dev_name(hdev), ##__VA_ARGS__) /* Connection and socket states */ enum { @@ -343,7 +345,7 @@ int bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg, __poll_t bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait); int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo); -int bt_sock_wait_ready(struct sock *sk, unsigned long flags); +int bt_sock_wait_ready(struct sock *sk, unsigned int msg_flags); void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh); void bt_accept_unlink(struct sock *sk); diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 35c073d44ec5..5cb095b09a94 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -255,6 +255,16 @@ enum { * during the hdev->setup vendor callback. */ HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, + + /* When this quirk is set, HCI_OP_SET_EVENT_FLT requests with + * HCI_FLT_CLEAR_ALL are ignored and event filtering is + * completely avoided. A subset of the CSR controller + * clones struggle with this and instantly lock up. + * + * Note that devices using this must (separately) disable + * runtime suspend, because event filtering takes place there. + */ + HCI_QUIRK_BROKEN_FILTER_CLEAR_ALL, }; /* HCI device flags */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e336e9c1dda4..d5377740e99c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -258,6 +258,15 @@ struct adv_info { #define HCI_ADV_TX_POWER_NO_PREFERENCE 0x7F +struct monitored_device { + struct list_head list; + + bdaddr_t bdaddr; + __u8 addr_type; + __u16 handle; + bool notified; +}; + struct adv_pattern { struct list_head list; __u8 ad_type; @@ -294,6 +303,9 @@ struct adv_monitor { #define HCI_MAX_SHORT_NAME_LENGTH 10 +#define HCI_CONN_HANDLE_UNSET 0xffff +#define HCI_CONN_HANDLE_MAX 0x0eff + /* Min encryption key size to match with SMP */ #define HCI_MIN_ENC_KEY_SIZE 7 @@ -591,6 +603,9 @@ struct hci_dev { struct delayed_work interleave_scan; + struct list_head monitored_devices; + bool advmon_pend_notify; + #if IS_ENABLED(CONFIG_BT_LEDS) struct led_trigger *power_led; #endif @@ -1855,6 +1870,8 @@ void mgmt_adv_monitor_removed(struct hci_dev *hdev, u16 handle); int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip); int mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status); int mgmt_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status); +void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle, + bdaddr_t *bdaddr, u8 addr_type); u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 9607ec289fd0..7c1ad0f6fcec 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -1104,3 +1104,19 @@ struct mgmt_ev_controller_resume { #define MGMT_WAKE_REASON_NON_BT_WAKE 0x0 #define MGMT_WAKE_REASON_UNEXPECTED 0x1 #define MGMT_WAKE_REASON_REMOTE_WAKE 0x2 + +#define MGMT_EV_ADV_MONITOR_DEVICE_FOUND 0x002f +struct mgmt_ev_adv_monitor_device_found { + __le16 monitor_handle; + struct mgmt_addr_info addr; + __s8 rssi; + __le32 flags; + __le16 eir_len; + __u8 eir[]; +} __packed; + +#define MGMT_EV_ADV_MONITOR_DEVICE_LOST 0x0030 +struct mgmt_ev_adv_monitor_device_lost { + __le16 monitor_handle; + struct mgmt_addr_info addr; +} __packed; diff --git a/include/net/bond_options.h b/include/net/bond_options.h index dd75c071f67e..61b49063791c 100644 --- a/include/net/bond_options.h +++ b/include/net/bond_options.h @@ -66,19 +66,24 @@ enum { BOND_OPT_PEER_NOTIF_DELAY, BOND_OPT_LACP_ACTIVE, BOND_OPT_MISSED_MAX, + BOND_OPT_NS_TARGETS, BOND_OPT_LAST }; /* This structure is used for storing option values and for passing option * values when changing an option. The logic when used as an arg is as follows: - * - if string != NULL -> parse it, if the opt is RAW type then return it, else - * return the parse result - * - if string == NULL -> parse value + * - if value != ULLONG_MAX -> parse value + * - if string != NULL -> parse string + * - if the opt is RAW data and length less than maxlen, + * copy the data to extra storage */ + +#define BOND_OPT_EXTRA_MAXLEN 16 struct bond_opt_value { char *string; u64 value; u32 flags; + char extra[BOND_OPT_EXTRA_MAXLEN]; }; struct bonding; @@ -118,18 +123,26 @@ const struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val); * When value is ULLONG_MAX then string will be used. */ static inline void __bond_opt_init(struct bond_opt_value *optval, - char *string, u64 value) + char *string, u64 value, + void *extra, size_t extra_len) { memset(optval, 0, sizeof(*optval)); optval->value = ULLONG_MAX; - if (value == ULLONG_MAX) - optval->string = string; - else + if (value != ULLONG_MAX) optval->value = value; + else if (string) + optval->string = string; + else if (extra_len <= BOND_OPT_EXTRA_MAXLEN) + memcpy(optval->extra, extra, extra_len); } -#define bond_opt_initval(optval, value) __bond_opt_init(optval, NULL, value) -#define bond_opt_initstr(optval, str) __bond_opt_init(optval, str, ULLONG_MAX) +#define bond_opt_initval(optval, value) __bond_opt_init(optval, NULL, value, NULL, 0) +#define bond_opt_initstr(optval, str) __bond_opt_init(optval, str, ULLONG_MAX, NULL, 0) +#define bond_opt_initextra(optval, extra, extra_len) \ + __bond_opt_init(optval, NULL, ULLONG_MAX, extra, extra_len) void bond_option_arp_ip_targets_clear(struct bonding *bond); +#if IS_ENABLED(CONFIG_IPV6) +void bond_option_ns_ip6_targets_clear(struct bonding *bond); +#endif #endif /* _NET_BOND_OPTIONS_H */ diff --git a/include/net/bonding.h b/include/net/bonding.h index 83cfd2d70247..b14f4c0b4e9e 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -29,8 +29,11 @@ #include #include #include +#include +#include #define BOND_MAX_ARP_TARGETS 16 +#define BOND_MAX_NS_TARGETS BOND_MAX_ARP_TARGETS #define BOND_DEFAULT_MIIMON 100 @@ -146,6 +149,7 @@ struct bond_params { struct reciprocal_value reciprocal_packets_per_slave; u16 ad_actor_sys_prio; u16 ad_user_port_key; + struct in6_addr ns_targets[BOND_MAX_NS_TARGETS]; /* 2 bytes of padding : see ether_addr_equal_64bits() */ u8 ad_actor_system[ETH_ALEN + 2]; @@ -499,6 +503,13 @@ static inline int bond_is_ip_target_ok(__be32 addr) return !ipv4_is_lbcast(addr) && !ipv4_is_zeronet(addr); } +static inline int bond_is_ip6_target_ok(struct in6_addr *addr) +{ + return !ipv6_addr_any(addr) && + !ipv6_addr_loopback(addr) && + !ipv6_addr_is_multicast(addr); +} + /* Get the oldest arp which we've received on this slave for bond's * arp_targets. */ @@ -628,7 +639,7 @@ struct bond_net { struct class_attribute class_attr_bonding_masters; }; -int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, struct slave *slave); +int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond, struct slave *slave); netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev); int bond_create(struct net *net, const char *name); int bond_create_sysfs(struct bond_net *net); @@ -698,20 +709,6 @@ static inline struct slave *bond_slave_has_mac(struct bonding *bond, return NULL; } -/* Caller must hold rcu_read_lock() for read */ -static inline struct slave *bond_slave_has_mac_rcu(struct bonding *bond, - const u8 *mac) -{ - struct list_head *iter; - struct slave *tmp; - - bond_for_each_slave_rcu(bond, tmp, iter) - if (ether_addr_equal_64bits(mac, tmp->dev->dev_addr)) - return tmp; - - return NULL; -} - /* Caller must hold rcu_read_lock() for read */ static inline bool bond_slave_has_mac_rx(struct bonding *bond, const u8 *mac) { @@ -749,6 +746,19 @@ static inline int bond_get_targets_ip(__be32 *targets, __be32 ip) return -1; } +static inline int bond_get_targets_ip6(struct in6_addr *targets, struct in6_addr *ip) +{ + int i; + + for (i = 0; i < BOND_MAX_NS_TARGETS; i++) + if (ipv6_addr_equal(&targets[i], ip)) + return i; + else if (ipv6_addr_any(&targets[i])) + break; + + return -1; +} + /* exported from bond_main.c */ extern unsigned int bond_net_id; @@ -760,7 +770,7 @@ extern const struct sysfs_ops slave_sysfs_ops; static inline netdev_tx_t bond_tx_drop(struct net_device *dev, struct sk_buff *skb) { - atomic_long_inc(&dev->tx_dropped); + dev_core_stats_tx_dropped_inc(dev); dev_kfree_skb_any(skb); return NET_XMIT_DROP; } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d19e48f9fdc6..68713388b617 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -109,7 +109,12 @@ struct wiphy; * on this channel. * @IEEE80211_CHAN_16MHZ: 16 MHz bandwidth is permitted * on this channel. - * + * @IEEE80211_CHAN_NO_320MHZ: If the driver supports 320 MHz on the band, + * this flag indicates that a 320 MHz channel cannot use this + * channel as the control or any of the secondary channels. + * This may be due to the driver or due to regulatory bandwidth + * restrictions. + * @IEEE80211_CHAN_NO_EHT: EHT operation is not permitted on this channel. */ enum ieee80211_channel_flags { IEEE80211_CHAN_DISABLED = 1<<0, @@ -131,6 +136,8 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_4MHZ = 1<<16, IEEE80211_CHAN_8MHZ = 1<<17, IEEE80211_CHAN_16MHZ = 1<<18, + IEEE80211_CHAN_NO_320MHZ = 1<<19, + IEEE80211_CHAN_NO_EHT = 1<<20, }; #define IEEE80211_CHAN_NO_HT40 \ @@ -360,6 +367,48 @@ struct ieee80211_sta_he_cap { u8 ppe_thres[IEEE80211_HE_PPE_THRES_MAX_LEN]; }; +/** + * struct ieee80211_eht_mcs_nss_supp - EHT max supported NSS per MCS + * + * See P802.11be_D1.3 Table 9-401k - "Subfields of the Supported EHT-MCS + * and NSS Set field" + * + * @only_20mhz: MCS/NSS support for 20 MHz-only STA. + * @bw._80: MCS/NSS support for BW <= 80 MHz + * @bw._160: MCS/NSS support for BW = 160 MHz + * @bw._320: MCS/NSS support for BW = 320 MHz + */ +struct ieee80211_eht_mcs_nss_supp { + union { + struct ieee80211_eht_mcs_nss_supp_20mhz_only only_20mhz; + struct { + struct ieee80211_eht_mcs_nss_supp_bw _80; + struct ieee80211_eht_mcs_nss_supp_bw _160; + struct ieee80211_eht_mcs_nss_supp_bw _320; + } __packed bw; + } __packed; +} __packed; + +#define IEEE80211_EHT_PPE_THRES_MAX_LEN 32 + +/** + * struct ieee80211_sta_eht_cap - STA's EHT capabilities + * + * This structure describes most essential parameters needed + * to describe 802.11be EHT capabilities for a STA. + * + * @has_eht: true iff EHT data is valid. + * @eht_cap_elem: Fixed portion of the eht capabilities element. + * @eht_mcs_nss_supp: The supported NSS/MCS combinations. + * @eht_ppe_thres: Holds the PPE Thresholds data. + */ +struct ieee80211_sta_eht_cap { + bool has_eht; + struct ieee80211_eht_cap_elem_fixed eht_cap_elem; + struct ieee80211_eht_mcs_nss_supp eht_mcs_nss_supp; + u8 eht_ppe_thres[IEEE80211_EHT_PPE_THRES_MAX_LEN]; +}; + /** * struct ieee80211_sband_iftype_data - sband data per interface type * @@ -379,6 +428,7 @@ struct ieee80211_sband_iftype_data { u16 types_mask; struct ieee80211_sta_he_cap he_cap; struct ieee80211_he_6ghz_capa he_6ghz_capa; + struct ieee80211_sta_eht_cap eht_cap; struct { const u8 *data; unsigned int len; @@ -561,6 +611,26 @@ ieee80211_get_he_6ghz_capa(const struct ieee80211_supported_band *sband, return data->he_6ghz_capa.capa; } +/** + * ieee80211_get_eht_iftype_cap - return ETH capabilities for an sband's iftype + * @sband: the sband to search for the iftype on + * @iftype: enum nl80211_iftype + * + * Return: pointer to the struct ieee80211_sta_eht_cap, or NULL is none found + */ +static inline const struct ieee80211_sta_eht_cap * +ieee80211_get_eht_iftype_cap(const struct ieee80211_supported_band *sband, + enum nl80211_iftype iftype) +{ + const struct ieee80211_sband_iftype_data *data = + ieee80211_get_sband_iftype_data(sband, iftype); + + if (data && data->eht_cap.has_eht) + return &data->eht_cap; + + return NULL; +} + /** * wiphy_read_of_freq_limits - read frequency limits from device tree * @@ -1417,6 +1487,8 @@ struct sta_txpwr { * @airtime_weight: airtime scheduler weight for this station * @txpwr: transmit power for an associated station * @he_6ghz_capa: HE 6 GHz Band capabilities of station + * @eht_capa: EHT capabilities of station + * @eht_capa_len: the length of the EHT capabilities */ struct station_parameters { const u8 *supported_rates; @@ -1450,6 +1522,8 @@ struct station_parameters { u16 airtime_weight; struct sta_txpwr txpwr; const struct ieee80211_he_6ghz_capa *he_6ghz_capa; + const struct ieee80211_eht_cap_elem *eht_capa; + u8 eht_capa_len; }; /** @@ -1527,6 +1601,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy, * @RATE_INFO_FLAGS_HE_MCS: HE MCS information * @RATE_INFO_FLAGS_EDMG: 60GHz MCS in EDMG mode * @RATE_INFO_FLAGS_EXTENDED_SC_DMG: 60GHz extended SC MCS + * @RATE_INFO_FLAGS_EHT_MCS: EHT MCS information */ enum rate_info_flags { RATE_INFO_FLAGS_MCS = BIT(0), @@ -1536,6 +1611,7 @@ enum rate_info_flags { RATE_INFO_FLAGS_HE_MCS = BIT(4), RATE_INFO_FLAGS_EDMG = BIT(5), RATE_INFO_FLAGS_EXTENDED_SC_DMG = BIT(6), + RATE_INFO_FLAGS_EHT_MCS = BIT(7), }; /** @@ -1550,6 +1626,8 @@ enum rate_info_flags { * @RATE_INFO_BW_80: 80 MHz bandwidth * @RATE_INFO_BW_160: 160 MHz bandwidth * @RATE_INFO_BW_HE_RU: bandwidth determined by HE RU allocation + * @RATE_INFO_BW_320: 320 MHz bandwidth + * @RATE_INFO_BW_EHT_RU: bandwidth determined by EHT RU allocation */ enum rate_info_bw { RATE_INFO_BW_20 = 0, @@ -1559,6 +1637,8 @@ enum rate_info_bw { RATE_INFO_BW_80, RATE_INFO_BW_160, RATE_INFO_BW_HE_RU, + RATE_INFO_BW_320, + RATE_INFO_BW_EHT_RU, }; /** @@ -1576,6 +1656,9 @@ enum rate_info_bw { * @he_ru_alloc: HE RU allocation (from &enum nl80211_he_ru_alloc, * only valid if bw is %RATE_INFO_BW_HE_RU) * @n_bonded_ch: In case of EDMG the number of bonded channels (1-4) + * @eht_gi: EHT guard interval (from &enum nl80211_eht_gi) + * @eht_ru_alloc: EHT RU allocation (from &enum nl80211_eht_ru_alloc, + * only valid if bw is %RATE_INFO_BW_EHT_RU) */ struct rate_info { u8 flags; @@ -1587,6 +1670,8 @@ struct rate_info { u8 he_dcm; u8 he_ru_alloc; u8 n_bonded_ch; + u8 eht_gi; + u8 eht_ru_alloc; }; /** @@ -2604,7 +2689,7 @@ const struct element *ieee80211_bss_get_elem(struct cfg80211_bss *bss, u8 id); */ static inline const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 id) { - return (void *)ieee80211_bss_get_elem(bss, id); + return (const void *)ieee80211_bss_get_elem(bss, id); } @@ -5970,9 +6055,9 @@ cfg80211_find_ie_match(u8 eid, const u8 *ies, unsigned int len, (!match_len && match_offset))) return NULL; - return (void *)cfg80211_find_elem_match(eid, ies, len, - match, match_len, - match_offset ? + return (const void *)cfg80211_find_elem_match(eid, ies, len, + match, match_len, + match_offset ? match_offset - 2 : 0); } @@ -6099,7 +6184,7 @@ static inline const u8 * cfg80211_find_vendor_ie(unsigned int oui, int oui_type, const u8 *ies, unsigned int len) { - return (void *)cfg80211_find_vendor_elem(oui, oui_type, ies, len); + return (const void *)cfg80211_find_vendor_elem(oui, oui_type, ies, len); } /** diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 6ed07844eb24..833672d6fbe4 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -227,6 +227,16 @@ static inline void wpan_phy_net_set(struct wpan_phy *wpan_phy, struct net *net) write_pnet(&wpan_phy->_net, net); } +/** + * struct ieee802154_addr - IEEE802.15.4 device address + * @mode: Address mode from frame header. Can be one of: + * - @IEEE802154_ADDR_NONE + * - @IEEE802154_ADDR_SHORT + * - @IEEE802154_ADDR_LONG + * @pan_id: The PAN ID this address belongs to + * @short_addr: address if @mode is @IEEE802154_ADDR_SHORT + * @extended_addr: address if @mode is @IEEE802154_ADDR_LONG + */ struct ieee802154_addr { u8 mode; __le16 pan_id; diff --git a/include/net/checksum.h b/include/net/checksum.h index 79c67f14c448..6bc783b7a06c 100644 --- a/include/net/checksum.h +++ b/include/net/checksum.h @@ -80,6 +80,7 @@ static __always_inline __sum16 csum16_sub(__sum16 csum, __be16 addend) return csum16_add(csum, ~addend); } +#ifndef HAVE_ARCH_CSUM_SHIFT static __always_inline __wsum csum_shift(__wsum sum, int offset) { /* rotate sum to align it with a 16b boundary */ @@ -87,6 +88,7 @@ static __always_inline __wsum csum_shift(__wsum sum, int offset) return (__force __wsum)ror32((__force u32)sum, 8); return sum; } +#endif static __always_inline __wsum csum_block_add(__wsum csum, __wsum csum2, int offset) diff --git a/include/net/devlink.h b/include/net/devlink.h index 8d5349d2fb68..a30180c0988a 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1197,9 +1197,9 @@ struct devlink_ops { struct netlink_ext_ack *extack); int (*port_type_set)(struct devlink_port *devlink_port, enum devlink_port_type port_type); - int (*port_split)(struct devlink *devlink, unsigned int port_index, + int (*port_split)(struct devlink *devlink, struct devlink_port *port, unsigned int count, struct netlink_ext_ack *extack); - int (*port_unsplit)(struct devlink *devlink, unsigned int port_index, + int (*port_unsplit)(struct devlink *devlink, struct devlink_port *port, struct netlink_ext_ack *extack); int (*sb_pool_get)(struct devlink *devlink, unsigned int sb_index, u16 pool_index, @@ -1479,6 +1479,21 @@ void *devlink_priv(struct devlink *devlink); struct devlink *priv_to_devlink(void *priv); struct device *devlink_to_dev(const struct devlink *devlink); +/* Devlink instance explicit locking */ +void devl_lock(struct devlink *devlink); +void devl_unlock(struct devlink *devlink); +void devl_assert_locked(struct devlink *devlink); +bool devl_lock_is_held(struct devlink *devlink); + +int devl_port_register(struct devlink *devlink, + struct devlink_port *devlink_port, + unsigned int port_index); +void devl_port_unregister(struct devlink_port *devlink_port); + +int devl_rate_leaf_create(struct devlink_port *port, void *priv); +void devl_rate_leaf_destroy(struct devlink_port *devlink_port); +void devl_rate_nodes_destroy(struct devlink *devlink); + struct ib_device; struct net *devlink_net(const struct devlink *devlink); diff --git a/include/net/dsa.h b/include/net/dsa.h index 85a5ba3772f5..934958fda962 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -52,6 +52,7 @@ struct phylink_link_state; #define DSA_TAG_PROTO_BRCM_LEGACY_VALUE 22 #define DSA_TAG_PROTO_SJA1110_VALUE 23 #define DSA_TAG_PROTO_RTL8_4_VALUE 24 +#define DSA_TAG_PROTO_RTL8_4T_VALUE 25 enum dsa_tag_protocol { DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, @@ -79,6 +80,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_SEVILLE = DSA_TAG_PROTO_SEVILLE_VALUE, DSA_TAG_PROTO_SJA1110 = DSA_TAG_PROTO_SJA1110_VALUE, DSA_TAG_PROTO_RTL8_4 = DSA_TAG_PROTO_RTL8_4_VALUE, + DSA_TAG_PROTO_RTL8_4T = DSA_TAG_PROTO_RTL8_4T_VALUE, }; struct dsa_switch; @@ -116,6 +118,14 @@ struct dsa_netdevice_ops { #define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \ MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __stringify(__proto##_VALUE)) +struct dsa_lag { + struct net_device *dev; + unsigned int id; + struct mutex fdb_lock; + struct list_head fdbs; + refcount_t refcount; +}; + struct dsa_switch_tree { struct list_head list; @@ -134,7 +144,7 @@ struct dsa_switch_tree { /* Maps offloaded LAG netdevs to a zero-based linear ID for * drivers that need it. */ - struct net_device **lags; + struct dsa_lag **lags; /* Tagging protocol operations */ const struct dsa_device_ops *tag_ops; @@ -163,32 +173,36 @@ struct dsa_switch_tree { unsigned int last_switch; }; +/* LAG IDs are one-based, the dst->lags array is zero-based */ #define dsa_lags_foreach_id(_id, _dst) \ - for ((_id) = 0; (_id) < (_dst)->lags_len; (_id)++) \ - if ((_dst)->lags[(_id)]) + for ((_id) = 1; (_id) <= (_dst)->lags_len; (_id)++) \ + if ((_dst)->lags[(_id) - 1]) #define dsa_lag_foreach_port(_dp, _dst, _lag) \ list_for_each_entry((_dp), &(_dst)->ports, list) \ - if ((_dp)->lag_dev == (_lag)) + if (dsa_port_offloads_lag((_dp), (_lag))) #define dsa_hsr_foreach_port(_dp, _ds, _hsr) \ list_for_each_entry((_dp), &(_ds)->dst->ports, list) \ if ((_dp)->ds == (_ds) && (_dp)->hsr_dev == (_hsr)) -static inline struct net_device *dsa_lag_dev(struct dsa_switch_tree *dst, - unsigned int id) +static inline struct dsa_lag *dsa_lag_by_id(struct dsa_switch_tree *dst, + unsigned int id) { - return dst->lags[id]; + /* DSA LAG IDs are one-based, dst->lags is zero-based */ + return dst->lags[id - 1]; } static inline int dsa_lag_id(struct dsa_switch_tree *dst, - struct net_device *lag) + struct net_device *lag_dev) { unsigned int id; dsa_lags_foreach_id(id, dst) { - if (dsa_lag_dev(dst, id) == lag) - return id; + struct dsa_lag *lag = dsa_lag_by_id(dst, id); + + if (lag->dev == lag_dev) + return lag->id; } return -ENODEV; @@ -278,6 +292,10 @@ struct dsa_port { u8 devlink_port_setup:1; + /* Master state bits, valid only on CPU ports */ + u8 master_admin_up:1; + u8 master_oper_up:1; + u8 setup:1; struct device_node *dn; @@ -287,7 +305,7 @@ struct dsa_port { struct devlink_port devlink_port; struct phylink *pl; struct phylink_config pl_config; - struct net_device *lag_dev; + struct dsa_lag *lag; struct net_device *hsr_dev; struct list_head list; @@ -308,6 +326,10 @@ struct dsa_port { struct mutex addr_lists_lock; struct list_head fdbs; struct list_head mdbs; + + /* List of VLANs that CPU and DSA ports are members of. */ + struct mutex vlans_lock; + struct list_head vlans; }; /* TODO: ideally DSA ports would have a single dp->link_dp member, @@ -321,11 +343,34 @@ struct dsa_link { struct list_head list; }; +enum dsa_db_type { + DSA_DB_PORT, + DSA_DB_LAG, + DSA_DB_BRIDGE, +}; + +struct dsa_db { + enum dsa_db_type type; + + union { + const struct dsa_port *dp; + struct dsa_lag lag; + struct dsa_bridge bridge; + }; +}; + struct dsa_mac_addr { unsigned char addr[ETH_ALEN]; u16 vid; refcount_t refcount; struct list_head list; + struct dsa_db db; +}; + +struct dsa_vlan { + u16 vid; + refcount_t refcount; + struct list_head list; }; struct dsa_switch { @@ -377,17 +422,19 @@ struct dsa_switch { */ u32 vlan_filtering:1; - /* MAC PCS does not provide link state change interrupt, and requires - * polling. Flag passed on to PHYLINK. - */ - u32 pcs_poll:1; - /* For switches that only have the MRU configurable. To ensure the * configured MTU is not exceeded, normalization of MRU on all bridged * interfaces is needed. */ u32 mtu_enforcement_ingress:1; + /* Drivers that isolate the FDBs of multiple bridges must set this + * to true to receive the bridge as an argument in .port_fdb_{add,del} + * and .port_mdb_{add,del}. Otherwise, the bridge.num will always be + * passed as zero. + */ + u32 fdb_isolation:1; + /* Listener for switch fabric events */ struct notifier_block nb; @@ -478,6 +525,12 @@ static inline bool dsa_port_is_unused(struct dsa_port *dp) return dp->type == DSA_PORT_TYPE_UNUSED; } +static inline bool dsa_port_master_is_operational(struct dsa_port *dp) +{ + return dsa_port_is_cpu(dp) && dp->master_admin_up && + dp->master_oper_up; +} + static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p) { return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_UNUSED; @@ -581,6 +634,24 @@ static inline bool dsa_is_upstream_port(struct dsa_switch *ds, int port) return port == dsa_upstream_port(ds, port); } +/* Return true if this is a DSA port leading away from the CPU */ +static inline bool dsa_is_downstream_port(struct dsa_switch *ds, int port) +{ + return dsa_is_dsa_port(ds, port) && !dsa_is_upstream_port(ds, port); +} + +/* Return the local port used to reach the CPU port */ +static inline unsigned int dsa_switch_upstream_port(struct dsa_switch *ds) +{ + struct dsa_port *dp; + + dsa_switch_for_each_available_port(dp, ds) { + return dsa_upstream_port(ds, dp->index); + } + + return ds->num_ports; +} + /* Return true if @upstream_ds is an upstream switch of @downstream_ds, meaning * that the routing port from @downstream_ds to @upstream_ds is also the port * which @downstream_ds uses to reach its dedicated CPU. @@ -608,14 +679,30 @@ static inline bool dsa_port_is_vlan_filtering(const struct dsa_port *dp) return dp->vlan_filtering; } +static inline unsigned int dsa_port_lag_id_get(struct dsa_port *dp) +{ + return dp->lag ? dp->lag->id : 0; +} + +static inline struct net_device *dsa_port_lag_dev_get(struct dsa_port *dp) +{ + return dp->lag ? dp->lag->dev : NULL; +} + +static inline bool dsa_port_offloads_lag(struct dsa_port *dp, + const struct dsa_lag *lag) +{ + return dsa_port_lag_dev_get(dp) == lag->dev; +} + static inline struct net_device *dsa_port_to_bridge_port(const struct dsa_port *dp) { if (!dp->bridge) return NULL; - if (dp->lag_dev) - return dp->lag_dev; + if (dp->lag) + return dp->lag->dev; else if (dp->hsr_dev) return dp->hsr_dev; @@ -750,6 +837,9 @@ struct dsa_switch_ops { void (*phylink_validate)(struct dsa_switch *ds, int port, unsigned long *supported, struct phylink_link_state *state); + struct phylink_pcs *(*phylink_mac_select_pcs)(struct dsa_switch *ds, + int port, + phy_interface_t iface); int (*phylink_mac_link_state)(struct dsa_switch *ds, int port, struct phylink_link_state *state); void (*phylink_mac_config)(struct dsa_switch *ds, int port, @@ -802,6 +892,18 @@ struct dsa_switch_ops { int (*get_ts_info)(struct dsa_switch *ds, int port, struct ethtool_ts_info *ts); + /* + * DCB ops + */ + int (*port_get_default_prio)(struct dsa_switch *ds, int port); + int (*port_set_default_prio)(struct dsa_switch *ds, int port, + u8 prio); + int (*port_get_dscp_prio)(struct dsa_switch *ds, int port, u8 dscp); + int (*port_add_dscp_prio)(struct dsa_switch *ds, int port, u8 dscp, + u8 prio); + int (*port_del_dscp_prio)(struct dsa_switch *ds, int port, u8 dscp, + u8 prio); + /* * Suspend and resume */ @@ -849,12 +951,16 @@ struct dsa_switch_ops { int (*set_ageing_time)(struct dsa_switch *ds, unsigned int msecs); int (*port_bridge_join)(struct dsa_switch *ds, int port, struct dsa_bridge bridge, - bool *tx_fwd_offload); + bool *tx_fwd_offload, + struct netlink_ext_ack *extack); void (*port_bridge_leave)(struct dsa_switch *ds, int port, struct dsa_bridge bridge); void (*port_stp_state_set)(struct dsa_switch *ds, int port, u8 state); + int (*port_mst_state_set)(struct dsa_switch *ds, int port, + const struct switchdev_mst_state *state); void (*port_fast_age)(struct dsa_switch *ds, int port); + int (*port_vlan_fast_age)(struct dsa_switch *ds, int port, u16 vid); int (*port_pre_bridge_flags)(struct dsa_switch *ds, int port, struct switchdev_brport_flags flags, struct netlink_ext_ack *extack); @@ -873,23 +979,36 @@ struct dsa_switch_ops { struct netlink_ext_ack *extack); int (*port_vlan_del)(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan); + int (*vlan_msti_set)(struct dsa_switch *ds, struct dsa_bridge bridge, + const struct switchdev_vlan_msti *msti); + /* * Forwarding database */ int (*port_fdb_add)(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); + const unsigned char *addr, u16 vid, + struct dsa_db db); int (*port_fdb_del)(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); + const unsigned char *addr, u16 vid, + struct dsa_db db); int (*port_fdb_dump)(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data); + int (*lag_fdb_add)(struct dsa_switch *ds, struct dsa_lag lag, + const unsigned char *addr, u16 vid, + struct dsa_db db); + int (*lag_fdb_del)(struct dsa_switch *ds, struct dsa_lag lag, + const unsigned char *addr, u16 vid, + struct dsa_db db); /* * Multicast database */ int (*port_mdb_add)(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb); + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); int (*port_mdb_del)(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb); + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); /* * RXNFC */ @@ -909,7 +1028,7 @@ struct dsa_switch_ops { struct flow_cls_offload *cls, bool ingress); int (*port_mirror_add)(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, - bool ingress); + bool ingress, struct netlink_ext_ack *extack); void (*port_mirror_del)(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror); int (*port_policer_add)(struct dsa_switch *ds, int port, @@ -923,17 +1042,18 @@ struct dsa_switch_ops { */ int (*crosschip_bridge_join)(struct dsa_switch *ds, int tree_index, int sw_index, int port, - struct dsa_bridge bridge); + struct dsa_bridge bridge, + struct netlink_ext_ack *extack); void (*crosschip_bridge_leave)(struct dsa_switch *ds, int tree_index, int sw_index, int port, struct dsa_bridge bridge); int (*crosschip_lag_change)(struct dsa_switch *ds, int sw_index, int port); int (*crosschip_lag_join)(struct dsa_switch *ds, int sw_index, - int port, struct net_device *lag, + int port, struct dsa_lag lag, struct netdev_lag_upper_info *info); int (*crosschip_lag_leave)(struct dsa_switch *ds, int sw_index, - int port, struct net_device *lag); + int port, struct dsa_lag lag); /* * PTP functionality @@ -1005,10 +1125,10 @@ struct dsa_switch_ops { */ int (*port_lag_change)(struct dsa_switch *ds, int port); int (*port_lag_join)(struct dsa_switch *ds, int port, - struct net_device *lag, + struct dsa_lag lag, struct netdev_lag_upper_info *info); int (*port_lag_leave)(struct dsa_switch *ds, int port, - struct net_device *lag); + struct dsa_lag lag); /* * HSR integration @@ -1036,6 +1156,13 @@ struct dsa_switch_ops { int (*tag_8021q_vlan_add)(struct dsa_switch *ds, int port, u16 vid, u16 flags); int (*tag_8021q_vlan_del)(struct dsa_switch *ds, int port, u16 vid); + + /* + * DSA master tracking operations + */ + void (*master_state_change)(struct dsa_switch *ds, + const struct net_device *master, + bool operational); }; #define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \ @@ -1112,6 +1239,19 @@ struct dsa_switch_driver { struct net_device *dsa_dev_to_net_device(struct device *dev); +typedef int dsa_fdb_walk_cb_t(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db); + +int dsa_port_walk_fdbs(struct dsa_switch *ds, int port, dsa_fdb_walk_cb_t cb); +int dsa_port_walk_mdbs(struct dsa_switch *ds, int port, dsa_fdb_walk_cb_t cb); +bool dsa_fdb_present_in_other_db(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db); +bool dsa_mdb_present_in_other_db(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); + /* Keep inline for faster access in hot path */ static inline bool netdev_uses_dsa(const struct net_device *dev) { @@ -1212,9 +1352,6 @@ static inline bool dsa_slave_dev_check(const struct net_device *dev) #endif netdev_tx_t dsa_enqueue_skb(struct sk_buff *skb, struct net_device *dev); -int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data); -int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data); -int dsa_port_get_phy_sset_count(struct dsa_port *dp); void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up); struct dsa_tag_driver { @@ -1247,7 +1384,7 @@ module_exit(dsa_tag_driver_module_exit) /** * module_dsa_tag_drivers() - Helper macro for registering DSA tag * drivers - * @__ops_array: Array of tag driver strucutres + * @__ops_array: Array of tag driver structures * * Helper macro for DSA tag drivers which do not do anything special * in module init/exit. Each module may only use this macro once, and diff --git a/include/net/flow.h b/include/net/flow.h index 58beb16a49b8..987bd511d652 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -29,6 +29,7 @@ struct flowi_tunnel { struct flowi_common { int flowic_oif; int flowic_iif; + int flowic_l3mdev; __u32 flowic_mark; __u8 flowic_tos; __u8 flowic_scope; @@ -36,7 +37,6 @@ struct flowi_common { __u8 flowic_flags; #define FLOWI_FLAG_ANYSRC 0x01 #define FLOWI_FLAG_KNOWN_NH 0x02 -#define FLOWI_FLAG_SKIP_NH_OIF 0x04 __u32 flowic_secid; kuid_t flowic_uid; struct flowi_tunnel flowic_tun_key; @@ -70,6 +70,7 @@ struct flowi4 { struct flowi_common __fl_common; #define flowi4_oif __fl_common.flowic_oif #define flowi4_iif __fl_common.flowic_iif +#define flowi4_l3mdev __fl_common.flowic_l3mdev #define flowi4_mark __fl_common.flowic_mark #define flowi4_tos __fl_common.flowic_tos #define flowi4_scope __fl_common.flowic_scope @@ -102,6 +103,7 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, { fl4->flowi4_oif = oif; fl4->flowi4_iif = LOOPBACK_IFINDEX; + fl4->flowi4_l3mdev = 0; fl4->flowi4_mark = mark; fl4->flowi4_tos = tos; fl4->flowi4_scope = scope; @@ -132,6 +134,7 @@ struct flowi6 { struct flowi_common __fl_common; #define flowi6_oif __fl_common.flowic_oif #define flowi6_iif __fl_common.flowic_iif +#define flowi6_l3mdev __fl_common.flowic_l3mdev #define flowi6_mark __fl_common.flowic_mark #define flowi6_scope __fl_common.flowic_scope #define flowi6_proto __fl_common.flowic_proto @@ -177,6 +180,7 @@ struct flowi { } u; #define flowi_oif u.__fl_common.flowic_oif #define flowi_iif u.__fl_common.flowic_iif +#define flowi_l3mdev u.__fl_common.flowic_l3mdev #define flowi_mark u.__fl_common.flowic_mark #define flowi_tos u.__fl_common.flowic_tos #define flowi_scope u.__fl_common.flowic_scope diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 5b8c54eb7a6b..021778a7e1af 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -148,6 +148,10 @@ enum flow_action_id { FLOW_ACTION_MPLS_MANGLE, FLOW_ACTION_GATE, FLOW_ACTION_PPPOE_PUSH, + FLOW_ACTION_JUMP, + FLOW_ACTION_PIPE, + FLOW_ACTION_VLAN_PUSH_ETH, + FLOW_ACTION_VLAN_POP_ETH, NUM_FLOW_ACTIONS, }; @@ -209,6 +213,10 @@ struct flow_action_entry { __be16 proto; u8 prio; } vlan; + struct { /* FLOW_ACTION_VLAN_PUSH_ETH */ + unsigned char dst[ETH_ALEN]; + unsigned char src[ETH_ALEN]; + } vlan_push_eth; struct { /* FLOW_ACTION_MANGLE */ /* FLOW_ACTION_ADD */ enum flow_action_mangle_base htype; @@ -235,9 +243,16 @@ struct flow_action_entry { struct { /* FLOW_ACTION_POLICE */ u32 burst; u64 rate_bytes_ps; + u64 peakrate_bytes_ps; + u32 avrate; + u16 overhead; u64 burst_pkt; u64 rate_pkt_ps; u32 mtu; + struct { + enum flow_action_id act_id; + u32 extval; + } exceed, notexceed; } police; struct { /* FLOW_ACTION_CT */ int action; @@ -302,6 +317,12 @@ static inline bool flow_offload_has_one_action(const struct flow_action *action) return action->num_entries == 1; } +static inline bool flow_action_is_last_entry(const struct flow_action *action, + const struct flow_action_entry *entry) +{ + return entry == &action->entries[action->num_entries - 1]; +} + #define flow_action_for_each(__i, __act, __actions) \ for (__i = 0, __act = &(__actions)->entries[0]; \ __i < (__actions)->num_entries; \ diff --git a/include/net/gro.h b/include/net/gro.h index 8f75802d50fd..867656b0739c 100644 --- a/include/net/gro.h +++ b/include/net/gro.h @@ -29,46 +29,51 @@ struct napi_gro_cb { /* Number of segments aggregated. */ u16 count; - /* Start offset for remote checksum offload */ - u16 gro_remcsum_start; + /* Used in ipv6_gro_receive() and foo-over-udp */ + u16 proto; /* jiffies when first packet was created/queued */ unsigned long age; - /* Used in ipv6_gro_receive() and foo-over-udp */ - u16 proto; - - /* This is non-zero if the packet may be of the same flow. */ - u8 same_flow:1; - - /* Used in tunnel GRO receive */ - u8 encap_mark:1; - - /* GRO checksum is valid */ - u8 csum_valid:1; - - /* Number of checksums via CHECKSUM_UNNECESSARY */ - u8 csum_cnt:3; - - /* Free the skb? */ - u8 free:2; -#define NAPI_GRO_FREE 1 +/* Used in napi_gro_cb::free */ +#define NAPI_GRO_FREE 1 #define NAPI_GRO_FREE_STOLEN_HEAD 2 + /* portion of the cb set to zero at every gro iteration */ + struct_group(zeroed, - /* Used in foo-over-udp, set in udp[46]_gro_receive */ - u8 is_ipv6:1; + /* Start offset for remote checksum offload */ + u16 gro_remcsum_start; - /* Used in GRE, set in fou/gue_gro_receive */ - u8 is_fou:1; + /* This is non-zero if the packet may be of the same flow. */ + u8 same_flow:1; - /* Used to determine if flush_id can be ignored */ - u8 is_atomic:1; + /* Used in tunnel GRO receive */ + u8 encap_mark:1; - /* Number of gro_receive callbacks this packet already went through */ - u8 recursion_counter:4; + /* GRO checksum is valid */ + u8 csum_valid:1; - /* GRO is done by frag_list pointer chaining. */ - u8 is_flist:1; + /* Number of checksums via CHECKSUM_UNNECESSARY */ + u8 csum_cnt:3; + + /* Free the skb? */ + u8 free:2; + + /* Used in foo-over-udp, set in udp[46]_gro_receive */ + u8 is_ipv6:1; + + /* Used in GRE, set in fou/gue_gro_receive */ + u8 is_fou:1; + + /* Used to determine if flush_id can be ignored */ + u8 is_atomic:1; + + /* Number of gro_receive callbacks this packet already went through */ + u8 recursion_counter:4; + + /* GRO is done by frag_list pointer chaining. */ + u8 is_flist:1; + ); /* used to support CHECKSUM_COMPLETE for tunneling protocols */ __wsum csum; diff --git a/include/net/gtp.h b/include/net/gtp.h index 0e16ebb2a82d..c1d6169df331 100644 --- a/include/net/gtp.h +++ b/include/net/gtp.h @@ -7,8 +7,13 @@ #define GTP0_PORT 3386 #define GTP1U_PORT 2152 +/* GTP messages types */ +#define GTP_ECHO_REQ 1 /* Echo Request */ +#define GTP_ECHO_RSP 2 /* Echo Response */ #define GTP_TPDU 255 +#define GTPIE_RECOVERY 14 + struct gtp0_header { /* According to GSM TS 09.60. */ __u8 flags; __u8 type; @@ -27,6 +32,43 @@ struct gtp1_header { /* According to 3GPP TS 29.060. */ __be32 tid; } __attribute__ ((packed)); +struct gtp1_header_long { /* According to 3GPP TS 29.060. */ + __u8 flags; + __u8 type; + __be16 length; + __be32 tid; + __be16 seq; + __u8 npdu; + __u8 next; +} __packed; + +/* GTP Information Element */ +struct gtp_ie { + __u8 tag; + __u8 val; +} __packed; + +struct gtp0_packet { + struct gtp0_header gtp0_h; + struct gtp_ie ie; +} __packed; + +struct gtp1u_packet { + struct gtp1_header_long gtp1u_h; + struct gtp_ie ie; +} __packed; + +struct gtp_pdu_session_info { /* According to 3GPP TS 38.415. */ + u8 pdu_type; + u8 qfi; +}; + +static inline bool netif_is_gtp(const struct net_device *dev) +{ + return dev->rtnl_link_ops && + !strcmp(dev->rtnl_link_ops->kind, "gtp"); +} + #define GTP1_F_NPDU 0x01 #define GTP1_F_SEQ 0x02 #define GTP1_F_EXTHDR 0x04 diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 11630351c978..598f53d2a3a0 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2017 Intel Deutschland GmbH - * Copyright (c) 2018-2019 Intel Corporation + * Copyright (c) 2018-2019, 2021 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -365,7 +365,7 @@ enum ieee80211_radiotap_zero_len_psdu_type { */ static inline u16 ieee80211_get_radiotap_len(const char *data) { - struct ieee80211_radiotap_header *hdr = (void *)data; + const struct ieee80211_radiotap_header *hdr = (const void *)data; return get_unaligned_le16(&hdr->it_len); } diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index f026cf08a8e8..4cfdef6ca4f6 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -71,6 +71,8 @@ struct inet6_ifaddr { bool tokenized; + u8 ifa_proto; + struct rcu_head rcu; struct in6_addr peer_addr; }; diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 4ad47d9f9d27..3908296d103f 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -285,6 +285,14 @@ static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk) bool inet_csk_reqsk_queue_drop(struct sock *sk, struct request_sock *req); void inet_csk_reqsk_queue_drop_and_put(struct sock *sk, struct request_sock *req); +static inline unsigned long +reqsk_timeout(struct request_sock *req, unsigned long max_timeout) +{ + u64 timeout = (u64)req->timeout << req->num_timeout; + + return (unsigned long)min_t(u64, timeout, max_timeout); +} + static inline void inet_csk_prepare_for_destroy_sock(struct sock *sk) { /* The below has to be done to allow calling inet_csk_destroy_sock */ diff --git a/include/net/inet_dscp.h b/include/net/inet_dscp.h new file mode 100644 index 000000000000..72f250dffada --- /dev/null +++ b/include/net/inet_dscp.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * inet_dscp.h: helpers for handling differentiated services codepoints (DSCP) + * + * DSCP is defined in RFC 2474: + * + * 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ + * | DSCP | CU | + * +---+---+---+---+---+---+---+---+ + * + * DSCP: differentiated services codepoint + * CU: currently unused + * + * The whole DSCP + CU bits form the DS field. + * The DS field is also commonly called TOS or Traffic Class (for IPv6). + * + * Note: the CU bits are now used for Explicit Congestion Notification + * (RFC 3168). + */ + +#ifndef _INET_DSCP_H +#define _INET_DSCP_H + +#include + +/* Special type for storing DSCP values. + * + * A dscp_t variable stores a DS field with the CU (ECN) bits cleared. + * Using dscp_t allows to strictly separate DSCP and ECN bits, thus avoiding + * bugs where ECN bits are erroneously taken into account during FIB lookups + * or policy routing. + * + * Note: to get the real DSCP value contained in a dscp_t variable one would + * have to do a bit shift after calling inet_dscp_to_dsfield(). We could have + * a helper for that, but there's currently no users. + */ +typedef u8 __bitwise dscp_t; + +#define INET_DSCP_MASK 0xfc + +static inline dscp_t inet_dsfield_to_dscp(__u8 dsfield) +{ + return (__force dscp_t)(dsfield & INET_DSCP_MASK); +} + +static inline __u8 inet_dscp_to_dsfield(dscp_t dscp) +{ + return (__force __u8)dscp; +} + +static inline bool inet_validate_dscp(__u8 val) +{ + return !(val & ~INET_DSCP_MASK); +} + +#endif /* _INET_DSCP_H */ diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 63540be0fc34..911ad930867d 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -70,6 +70,7 @@ struct frag_v6_compare_key { * @stamp: timestamp of the last received fragment * @len: total length of the original datagram * @meat: length of received fragments so far + * @mono_delivery_time: stamp has a mono delivery time (EDT) * @flags: fragment queue flags * @max_size: maximum received fragment size * @fqdir: pointer to struct fqdir @@ -90,6 +91,7 @@ struct inet_frag_queue { ktime_t stamp; int len; int meat; + u8 mono_delivery_time; __u8 flags; u16 max_size; struct fqdir *fqdir; diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h index dfd919b3119e..463ae5d33eb0 100644 --- a/include/net/inet_timewait_sock.h +++ b/include/net/inet_timewait_sock.h @@ -65,13 +65,13 @@ struct inet_timewait_sock { /* these three are in inet_sock */ __be16 tw_sport; /* And these are ours. */ - unsigned int tw_kill : 1, - tw_transparent : 1, + unsigned int tw_transparent : 1, tw_flowlabel : 20, - tw_pad : 2, /* 2 bits hole */ + tw_pad : 3, /* 3 bits hole */ tw_tos : 8; u32 tw_txhash; u32 tw_priority; + u32 tw_bslot; /* bind bucket slot */ struct timer_list tw_timer; struct inet_bind_bucket *tw_tb; }; @@ -110,8 +110,6 @@ static inline void inet_twsk_reschedule(struct inet_timewait_sock *tw, int timeo void inet_twsk_deschedule_put(struct inet_timewait_sock *tw); -void inet_twsk_purge(struct inet_hashinfo *hashinfo, int family); - static inline struct net *twsk_net(const struct inet_timewait_sock *twsk) { diff --git a/include/net/ip.h b/include/net/ip.h index b51bae43b0dd..3984f2c39c4b 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -517,7 +517,6 @@ void ip_dst_metrics_put(struct dst_entry *dst) kfree(p); } -u32 ip_idents_reserve(u32 hash, int segs); void __ip_select_ident(struct net *net, struct iphdr *iph, int segs); static inline void ip_select_ident_segs(struct net *net, struct sk_buff *skb, @@ -712,7 +711,7 @@ int ip_forward(struct sk_buff *skb); */ void ip_options_build(struct sk_buff *skb, struct ip_options *opt, - __be32 daddr, struct rtable *rt, int is_frag); + __be32 daddr, struct rtable *rt); int __ip_options_echo(struct net *net, struct ip_options *dopt, struct sk_buff *skb, const struct ip_options *sopt); diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 2048bc8748cb..6268963d9599 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -369,9 +369,8 @@ struct rt6_statistics { __u32 fib_rt_cache; /* cached rt entries in exception table */ __u32 fib_discarded_routes; /* total number of routes delete */ - /* The following stats are not protected by any lock */ + /* The following stat is not protected by any lock */ atomic_t fib_rt_alloc; /* total number of routes alloced */ - atomic_t fib_rt_uncache; /* rt entries in uncached list */ }; #define RTN_TL_ROOT 0x0001 diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index c4297704bbcb..6a82bcb8813b 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +25,7 @@ struct fib_config { u8 fc_dst_len; - u8 fc_tos; + dscp_t fc_dscp; u8 fc_protocol; u8 fc_scope; u8 fc_type; diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 92eec13d1693..213612f1680c 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -15,9 +15,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -440,8 +440,16 @@ struct ipv6_txoptions *ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, int newtype, struct ipv6_opt_hdr *newopt); -struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, - struct ipv6_txoptions *opt); +struct ipv6_txoptions *__ipv6_fixup_options(struct ipv6_txoptions *opt_space, + struct ipv6_txoptions *opt); + +static inline struct ipv6_txoptions * +ipv6_fixup_options(struct ipv6_txoptions *opt_space, struct ipv6_txoptions *opt) +{ + if (!opt) + return NULL; + return __ipv6_fixup_options(opt_space, opt); +} bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb, const struct inet6_skb_parm *opt); @@ -970,6 +978,11 @@ static inline u8 ip6_tclass(__be32 flowinfo) return ntohl(flowinfo & IPV6_TCLASS_MASK) >> IPV6_TCLASS_SHIFT; } +static inline dscp_t ip6_dscp(__be32 flowinfo) +{ + return inet_dsfield_to_dscp(ip6_tclass(flowinfo)); +} + static inline __be32 ip6_make_flowinfo(unsigned int tclass, __be32 flowlabel) { return htonl(tclass << IPV6_TCLASS_SHIFT) | flowlabel; @@ -1023,7 +1036,7 @@ struct sk_buff *ip6_make_skb(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), void *from, int length, int transhdrlen, - struct ipcm6_cookie *ipc6, struct flowi6 *fl6, + struct ipcm6_cookie *ipc6, struct rt6_info *rt, unsigned int flags, struct inet_cork_full *cork); diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h index 0a4779175a52..5052c66e22d2 100644 --- a/include/net/ipv6_frag.h +++ b/include/net/ipv6_frag.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #ifndef _IPV6_FRAG_H #define _IPV6_FRAG_H +#include #include #include #include diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c50221d7e82c..382ebb862ea8 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -7,7 +7,7 @@ * Copyright 2007-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2021 Intel Corporation + * Copyright (C) 2018 - 2022 Intel Corporation */ #ifndef MAC80211_H @@ -636,6 +636,7 @@ struct ieee80211_fils_discovery { * @tx_pwr_env: transmit power envelope array of BSS. * @tx_pwr_env_num: number of @tx_pwr_env. * @pwr_reduction: power constraint of BSS. + * @eht_support: does this BSS support EHT */ struct ieee80211_bss_conf { const u8 *bssid; @@ -710,6 +711,7 @@ struct ieee80211_bss_conf { struct ieee80211_tx_pwr_env tx_pwr_env[IEEE80211_TPE_MAX_IE_COUNT]; u8 tx_pwr_env_num; u8 pwr_reduction; + bool eht_support; }; /** @@ -883,6 +885,17 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTRL_DONT_REORDER = BIT(8), }; +/** + * enum mac80211_tx_status_flags - flags to describe transmit status + * + * @IEEE80211_TX_STATUS_ACK_SIGNAL_VALID: ACK signal is valid + * + * These flags are used in tx_info->status.flags. + */ +enum mac80211_tx_status_flags { + IEEE80211_TX_STATUS_ACK_SIGNAL_VALID = BIT(0), +}; + /* * This definition is used as a mask to clear all temporary flags, which are * set by the tx handlers for each transmission attempt by the mac80211 stack. @@ -1046,7 +1059,7 @@ ieee80211_rate_get_vht_nss(const struct ieee80211_tx_rate *rate) * @status.antenna: (legacy, kept only for iwlegacy) * @status.tx_time: airtime consumed for transmission; note this is only * used for WMM AC, not for airtime fairness - * @status.is_valid_ack_signal: ACK signal is valid + * @status.flags: status flags, see &enum mac80211_tx_status_flags * @status.status_driver_data: driver use area * @ack: union part for pure ACK data * @ack.cookie: cookie for the ACK @@ -1099,8 +1112,8 @@ struct ieee80211_tx_info { u8 ampdu_len; u8 antenna; u16 tx_time; - bool is_valid_ack_signal; - void *status_driver_data[19 / sizeof(void *)]; + u8 flags; + void *status_driver_data[18 / sizeof(void *)]; } status; struct { struct ieee80211_tx_rate driver_rates[ @@ -1994,6 +2007,7 @@ enum ieee80211_sta_state { * @IEEE80211_STA_RX_BW_80: station can receive up to 80 MHz * @IEEE80211_STA_RX_BW_160: station can receive up to 160 MHz * (including 80+80 MHz) + * @IEEE80211_STA_RX_BW_320: station can receive up to 320 MHz * * Implementation note: 20 must be zero to be initialized * correctly, the values must be sorted. @@ -2003,6 +2017,7 @@ enum ieee80211_sta_rx_bandwidth { IEEE80211_STA_RX_BW_40, IEEE80211_STA_RX_BW_80, IEEE80211_STA_RX_BW_160, + IEEE80211_STA_RX_BW_320, }; /** @@ -2058,6 +2073,7 @@ struct ieee80211_sta_txpwr { * @vht_cap: VHT capabilities of this STA; restricted to our own capabilities * @he_cap: HE capabilities of this STA * @he_6ghz_capa: on 6 GHz, holds the HE 6 GHz band capabilities + * @eht_cap: EHT capabilities of this STA * @max_rx_aggregation_subframes: maximal amount of frames in a single AMPDU * that this station is allowed to transmit to us. * Can be modified by driver. @@ -2098,6 +2114,7 @@ struct ieee80211_sta { struct ieee80211_sta_vht_cap vht_cap; struct ieee80211_sta_he_cap he_cap; struct ieee80211_he_6ghz_capa he_6ghz_capa; + struct ieee80211_sta_eht_cap eht_cap; u16 max_rx_aggregation_subframes; bool wme; u8 uapsd_queues; @@ -4931,12 +4948,14 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets); * @cntdwn_counter_offs: array of IEEE80211_MAX_CNTDWN_COUNTERS_NUM offsets * to countdown counters. This array can contain zero values which * should be ignored. + * @mbssid_off: position of the multiple bssid element */ struct ieee80211_mutable_offsets { u16 tim_offset; u16 tim_length; u16 cntdwn_counter_offs[IEEE80211_MAX_CNTDWN_COUNTERS_NUM]; + u16 mbssid_off; }; /** @@ -6053,6 +6072,16 @@ void ieee80211_disconnect(struct ieee80211_vif *vif, bool reconnect); */ void ieee80211_resume_disconnect(struct ieee80211_vif *vif); +/** + * ieee80211_hw_restart_disconnect - disconnect from AP after + * hardware restart + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Instructs mac80211 to disconnect from the AP after + * hardware restart. + */ +void ieee80211_hw_restart_disconnect(struct ieee80211_vif *vif); + /** * ieee80211_cqm_rssi_notify - inform a configured connection quality monitoring * rssi threshold triggered diff --git a/include/net/mac802154.h b/include/net/mac802154.h index d524ffb9eb25..2c3bbc6645ba 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -464,6 +464,12 @@ void ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb, * ieee802154_wake_queue - wake ieee802154 queue * @hw: pointer as obtained from ieee802154_alloc_hw(). * + * Tranceivers usually have either one transmit framebuffer or one framebuffer + * for both transmitting and receiving. Hence, the core currently only handles + * one frame at a time for each phy, which means we had to stop the queue to + * avoid new skb to come during the transmission. The queue then needs to be + * woken up after the operation. + * * Drivers should use this function instead of netif_wake_queue. */ void ieee802154_wake_queue(struct ieee802154_hw *hw); @@ -472,6 +478,12 @@ void ieee802154_wake_queue(struct ieee802154_hw *hw); * ieee802154_stop_queue - stop ieee802154 queue * @hw: pointer as obtained from ieee802154_alloc_hw(). * + * Tranceivers usually have either one transmit framebuffer or one framebuffer + * for both transmitting and receiving. Hence, the core currently only handles + * one frame at a time for each phy, which means we need to tell upper layers to + * stop giving us new skbs while we are busy with the transmitted one. The queue + * must then be stopped before transmitting. + * * Drivers should use this function instead of netif_stop_queue. */ void ieee802154_stop_queue(struct ieee802154_hw *hw); diff --git a/include/net/mctp.h b/include/net/mctp.h index 7e35ec79b909..d37268fe6825 100644 --- a/include/net/mctp.h +++ b/include/net/mctp.h @@ -40,11 +40,26 @@ struct mctp_hdr { #define MCTP_INITIAL_DEFAULT_NET 1 -static inline bool mctp_address_ok(mctp_eid_t eid) +static inline bool mctp_address_unicast(mctp_eid_t eid) { return eid >= 8 && eid < 255; } +static inline bool mctp_address_broadcast(mctp_eid_t eid) +{ + return eid == 255; +} + +static inline bool mctp_address_null(mctp_eid_t eid) +{ + return eid == 0; +} + +static inline bool mctp_address_matches(mctp_eid_t match, mctp_eid_t eid) +{ + return match == eid || match == MCTP_ADDR_ANY; +} + static inline struct mctp_hdr *mctp_hdr(struct sk_buff *skb) { return (struct mctp_hdr *)skb_network_header(skb); @@ -121,7 +136,7 @@ struct mctp_sock { */ struct mctp_sk_key { mctp_eid_t peer_addr; - mctp_eid_t local_addr; + mctp_eid_t local_addr; /* MCTP_ADDR_ANY for local owned tags */ __u8 tag; /* incoming tag match; invert TO for local */ /* we hold a ref to sk when set */ @@ -158,6 +173,12 @@ struct mctp_sk_key { */ unsigned long dev_flow_state; struct mctp_dev *dev; + + /* a tag allocated with SIOCMCTPALLOCTAG ioctl will not expire + * automatically on timeout or response, instead SIOCMCTPDROPTAG + * is used. + */ + bool manual_alloc; }; struct mctp_skb_cb { @@ -234,6 +255,9 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt, struct sk_buff *skb, mctp_eid_t daddr, u8 req_tag); void mctp_key_unref(struct mctp_sk_key *key); +struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk, + mctp_eid_t daddr, mctp_eid_t saddr, + bool manual, u8 *tagp); /* routing <--> device interface */ unsigned int mctp_default_net(struct net *net); diff --git a/include/net/mptcp.h b/include/net/mptcp.h index a925349b4b89..0a3b0fb04a3b 100644 --- a/include/net/mptcp.h +++ b/include/net/mptcp.h @@ -217,12 +217,6 @@ static inline bool rsk_drop_req(const struct request_sock *req) return false; } -static inline void mptcp_parse_option(const struct sk_buff *skb, - const unsigned char *ptr, int opsize, - struct tcp_options_received *opt_rx) -{ -} - static inline bool mptcp_syn_options(struct sock *sk, const struct sk_buff *skb, unsigned int *size, struct mptcp_out_options *opts) diff --git a/include/net/ndisc.h b/include/net/ndisc.h index 47ffb360ddfa..da7eec8669ec 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -447,10 +447,15 @@ void ndisc_cleanup(void); int ndisc_rcv(struct sk_buff *skb); +struct sk_buff *ndisc_ns_create(struct net_device *dev, const struct in6_addr *solicit, + const struct in6_addr *saddr, u64 nonce); void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, const struct in6_addr *daddr, const struct in6_addr *saddr, u64 nonce); +void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, + const struct in6_addr *saddr); + void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, const struct in6_addr *daddr); void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr, diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 5b61c462e534..c4f5601f6e32 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -63,7 +63,7 @@ struct net { */ spinlock_t rules_mod_lock; - unsigned int dev_unreg_count; + atomic_t dev_unreg_count; unsigned int dev_base_seq; /* protected by rtnl_mutex */ int ifindex; @@ -513,4 +513,10 @@ static inline void fnhe_genid_bump(struct net *net) atomic_inc(&net->fnhe_genid); } +#ifdef CONFIG_NET +void net_ns_init(void); +#else +static inline void net_ns_init(void) {} +#endif + #endif /* __NET_NET_NAMESPACE_H */ diff --git a/include/net/netfilter/nf_conntrack_acct.h b/include/net/netfilter/nf_conntrack_acct.h index 7f44a771530e..4b2b7f8914ea 100644 --- a/include/net/netfilter/nf_conntrack_acct.h +++ b/include/net/netfilter/nf_conntrack_acct.h @@ -78,7 +78,6 @@ static inline void nf_ct_acct_update(struct nf_conn *ct, u32 dir, void nf_conntrack_acct_pernet_init(struct net *net); -int nf_conntrack_acct_init(void); void nf_conntrack_acct_fini(void); #endif /* _NF_CONNTRACK_ACCT_H */ diff --git a/include/net/netfilter/nf_conntrack_bpf.h b/include/net/netfilter/nf_conntrack_bpf.h new file mode 100644 index 000000000000..a473b56842c5 --- /dev/null +++ b/include/net/netfilter/nf_conntrack_bpf.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _NF_CONNTRACK_BPF_H +#define _NF_CONNTRACK_BPF_H + +#include +#include + +#if (IS_BUILTIN(CONFIG_NF_CONNTRACK) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) || \ + (IS_MODULE(CONFIG_NF_CONNTRACK) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) + +extern int register_nf_conntrack_bpf(void); + +#else + +static inline int register_nf_conntrack_bpf(void) +{ + return 0; +} + +#endif + +#endif /* _NF_CONNTRACK_BPF_H */ diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index d932e22edcb4..6c4c490a3e34 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -21,10 +21,10 @@ enum nf_ct_ecache_state { struct nf_conntrack_ecache { unsigned long cache; /* bitops want long */ - u16 missed; /* missed events */ u16 ctmask; /* bitmask of ct events to be delivered */ u16 expmask; /* bitmask of expect events to be delivered */ enum nf_ct_ecache_state state:8;/* ecache state */ + u32 missed; /* missed events */ u32 portid; /* netlink portid of destroyer */ }; @@ -166,9 +166,6 @@ void nf_conntrack_ecache_work(struct net *net, enum nf_ct_ecache_state state); void nf_conntrack_ecache_pernet_init(struct net *net); void nf_conntrack_ecache_pernet_fini(struct net *net); -int nf_conntrack_ecache_init(void); -void nf_conntrack_ecache_fini(void); - static inline bool nf_conntrack_ecache_dwork_pending(const struct net *net) { return net->ct.ecache_dwork_pending; @@ -194,16 +191,6 @@ static inline void nf_conntrack_ecache_pernet_init(struct net *net) static inline void nf_conntrack_ecache_pernet_fini(struct net *net) { } - -static inline int nf_conntrack_ecache_init(void) -{ - return 0; -} - -static inline void nf_conntrack_ecache_fini(void) -{ -} - static inline bool nf_conntrack_ecache_dwork_pending(const struct net *net) { return false; } #endif /* CONFIG_NF_CONNTRACK_EVENTS */ #endif /*_NF_CONNTRACK_ECACHE_H*/ diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index c7515d82ab06..96635ad2acc7 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -49,7 +49,7 @@ enum nf_ct_ext_id { struct nf_ct_ext { u8 offset[NF_CT_EXT_NUM]; u8 len; - char data[]; + char data[] __aligned(8); }; static inline bool __nf_ct_ext_exist(const struct nf_ct_ext *ext, u8 id) @@ -72,23 +72,7 @@ static inline void *__nf_ct_ext_find(const struct nf_conn *ct, u8 id) #define nf_ct_ext_find(ext, id) \ ((id##_TYPE *)__nf_ct_ext_find((ext), (id))) -/* Destroy all relationships */ -void nf_ct_ext_destroy(struct nf_conn *ct); - /* Add this type, returns pointer to data or NULL. */ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp); -struct nf_ct_ext_type { - /* Destroys relationships (can be NULL). */ - void (*destroy)(struct nf_conn *ct); - - enum nf_ct_ext_id id; - - /* Length and min alignment. */ - u8 len; - u8 align; -}; - -int nf_ct_extend_register(const struct nf_ct_ext_type *type); -void nf_ct_extend_unregister(const struct nf_ct_ext_type *type); #endif /* _NF_CONNTRACK_EXTEND_H */ diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 37f0fbefb060..9939c366f720 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -177,4 +177,5 @@ void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat); int nf_nat_helper_try_module_get(const char *name, u16 l3num, u8 protonum); void nf_nat_helper_put(struct nf_conntrack_helper *helper); +void nf_ct_set_auto_assign_helper_warned(struct net *net); #endif /*_NF_CONNTRACK_HELPER_H*/ diff --git a/include/net/netfilter/nf_conntrack_labels.h b/include/net/netfilter/nf_conntrack_labels.h index ba916411c4e1..3c23298e68ca 100644 --- a/include/net/netfilter/nf_conntrack_labels.h +++ b/include/net/netfilter/nf_conntrack_labels.h @@ -45,12 +45,9 @@ int nf_connlabels_replace(struct nf_conn *ct, #ifdef CONFIG_NF_CONNTRACK_LABELS int nf_conntrack_labels_init(void); -void nf_conntrack_labels_fini(void); int nf_connlabels_get(struct net *net, unsigned int bit); void nf_connlabels_put(struct net *net); #else -static inline int nf_conntrack_labels_init(void) { return 0; } -static inline void nf_conntrack_labels_fini(void) {} static inline int nf_connlabels_get(struct net *net, unsigned int bit) { return 0; } static inline void nf_connlabels_put(struct net *net) {} #endif diff --git a/include/net/netfilter/nf_conntrack_seqadj.h b/include/net/netfilter/nf_conntrack_seqadj.h index 0a10b50537ae..883c414b768e 100644 --- a/include/net/netfilter/nf_conntrack_seqadj.h +++ b/include/net/netfilter/nf_conntrack_seqadj.h @@ -42,7 +42,4 @@ int nf_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int protoff); s32 nf_ct_seq_offset(const struct nf_conn *ct, enum ip_conntrack_dir, u32 seq); -int nf_conntrack_seqadj_init(void); -void nf_conntrack_seqadj_fini(void); - #endif /* _NF_CONNTRACK_SEQADJ_H */ diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h index 659b0ea25b4d..3ea94f6f3844 100644 --- a/include/net/netfilter/nf_conntrack_timeout.h +++ b/include/net/netfilter/nf_conntrack_timeout.h @@ -89,23 +89,11 @@ static inline unsigned int *nf_ct_timeout_lookup(const struct nf_conn *ct) } #ifdef CONFIG_NF_CONNTRACK_TIMEOUT -int nf_conntrack_timeout_init(void); -void nf_conntrack_timeout_fini(void); void nf_ct_untimeout(struct net *net, struct nf_ct_timeout *timeout); int nf_ct_set_timeout(struct net *net, struct nf_conn *ct, u8 l3num, u8 l4num, const char *timeout_name); void nf_ct_destroy_timeout(struct nf_conn *ct); #else -static inline int nf_conntrack_timeout_init(void) -{ - return 0; -} - -static inline void nf_conntrack_timeout_fini(void) -{ - return; -} - static inline int nf_ct_set_timeout(struct net *net, struct nf_conn *ct, u8 l3num, u8 l4num, const char *timeout_name) @@ -120,8 +108,12 @@ static inline void nf_ct_destroy_timeout(struct nf_conn *ct) #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ #ifdef CONFIG_NF_CONNTRACK_TIMEOUT -extern struct nf_ct_timeout *(*nf_ct_timeout_find_get_hook)(struct net *net, const char *name); -extern void (*nf_ct_timeout_put_hook)(struct nf_ct_timeout *timeout); +struct nf_ct_timeout_hooks { + struct nf_ct_timeout *(*timeout_find_get)(struct net *net, const char *name); + void (*timeout_put)(struct nf_ct_timeout *timeout); +}; + +extern const struct nf_ct_timeout_hooks *nf_ct_timeout_hook; #endif #endif /* _NF_CONNTRACK_TIMEOUT_H */ diff --git a/include/net/netfilter/nf_conntrack_timestamp.h b/include/net/netfilter/nf_conntrack_timestamp.h index 820ea34b6029..57138d974a9f 100644 --- a/include/net/netfilter/nf_conntrack_timestamp.h +++ b/include/net/netfilter/nf_conntrack_timestamp.h @@ -40,21 +40,8 @@ struct nf_conn_tstamp *nf_ct_tstamp_ext_add(struct nf_conn *ct, gfp_t gfp) #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP void nf_conntrack_tstamp_pernet_init(struct net *net); - -int nf_conntrack_tstamp_init(void); -void nf_conntrack_tstamp_fini(void); #else static inline void nf_conntrack_tstamp_pernet_init(struct net *net) {} - -static inline int nf_conntrack_tstamp_init(void) -{ - return 0; -} - -static inline void nf_conntrack_tstamp_fini(void) -{ - return; -} #endif /* CONFIG_NF_CONNTRACK_TIMESTAMP */ #endif /* _NF_CONNTRACK_TSTAMP_H */ diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index bd59e950f4d6..64daafd1fc41 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -10,6 +10,8 @@ #include #include #include +#include +#include struct nf_flowtable; struct nf_flow_rule; @@ -317,4 +319,20 @@ int nf_flow_rule_route_ipv6(struct net *net, const struct flow_offload *flow, int nf_flow_table_offload_init(void); void nf_flow_table_offload_exit(void); +static inline __be16 nf_flow_pppoe_proto(const struct sk_buff *skb) +{ + __be16 proto; + + proto = *((__be16 *)(skb_mac_header(skb) + ETH_HLEN + + sizeof(struct pppoe_hdr))); + switch (proto) { + case htons(PPP_IP): + return htons(ETH_P_IP); + case htons(PPP_IPV6): + return htons(ETH_P_IPV6); + } + + return 0; +} + #endif /* _NF_FLOW_TABLE_H */ diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index c4c0861deac1..20af9d3557b9 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -126,6 +126,7 @@ struct nft_regs_track { struct { const struct nft_expr *selector; const struct nft_expr *bitwise; + u8 num_reg; } regs[NFT_REG32_NUM]; const struct nft_expr *cur; @@ -1633,4 +1634,25 @@ static inline struct nftables_pernet *nft_pernet(const struct net *net) return net_generic(net, nf_tables_net_id); } +#define __NFT_REDUCE_READONLY 1UL +#define NFT_REDUCE_READONLY (void *)__NFT_REDUCE_READONLY + +static inline bool nft_reduce_is_readonly(const struct nft_expr *expr) +{ + return expr->ops->reduce == NFT_REDUCE_READONLY; +} + +void nft_reg_track_update(struct nft_regs_track *track, + const struct nft_expr *expr, u8 dreg, u8 len); +void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len); +void __nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg); + +static inline bool nft_reg_track_cmp(struct nft_regs_track *track, + const struct nft_expr *expr, u8 dreg) +{ + return track->regs[dreg].selector && + track->regs[dreg].selector->ops == expr->ops && + track->regs[dreg].num_reg == 0; +} + #endif /* _NET_NF_TABLES_H */ diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h index b6fb1fdff9b2..0ea7c55cea4d 100644 --- a/include/net/netfilter/nf_tables_core.h +++ b/include/net/netfilter/nf_tables_core.h @@ -42,6 +42,14 @@ struct nft_cmp_fast_expr { bool inv; }; +struct nft_cmp16_fast_expr { + struct nft_data data; + struct nft_data mask; + u8 sreg; + u8 len; + bool inv; +}; + struct nft_immediate_expr { struct nft_data data; u8 dreg; @@ -59,6 +67,7 @@ static inline u32 nft_cmp_fast_mask(unsigned int len) } extern const struct nft_expr_ops nft_cmp_fast_ops; +extern const struct nft_expr_ops nft_cmp16_fast_ops; struct nft_payload { enum nft_payload_bases base:8; diff --git a/include/net/netfilter/nft_fib.h b/include/net/netfilter/nft_fib.h index 237f3757637e..eed099eae672 100644 --- a/include/net/netfilter/nft_fib.h +++ b/include/net/netfilter/nft_fib.h @@ -37,4 +37,7 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs, void nft_fib_store_result(void *reg, const struct nft_fib *priv, const struct net_device *dev); + +bool nft_fib_reduce(struct nft_regs_track *track, + const struct nft_expr *expr); #endif diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h index 2dce55c736f4..9b51cc67de54 100644 --- a/include/net/netfilter/nft_meta.h +++ b/include/net/netfilter/nft_meta.h @@ -6,6 +6,7 @@ struct nft_meta { enum nft_meta_keys key:8; + u8 len; union { u8 dreg; u8 sreg; @@ -43,4 +44,6 @@ int nft_meta_set_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nft_data **data); +bool nft_meta_get_reduce(struct nft_regs_track *track, + const struct nft_expr *expr); #endif diff --git a/include/net/netns/core.h b/include/net/netns/core.h index 552bc25b1933..388244e315e7 100644 --- a/include/net/netns/core.h +++ b/include/net/netns/core.h @@ -10,6 +10,7 @@ struct netns_core { struct ctl_table_header *sysctl_hdr; int sysctl_somaxconn; + u8 sysctl_txrehash; #ifdef CONFIG_PROC_FS struct prot_inuse __percpu *prot_inuse; diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 78557643526e..ce0cc4e8d8c7 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -31,18 +31,16 @@ struct ping_group_range { struct inet_hashinfo; struct inet_timewait_death_row { - atomic_t tw_count; - char tw_pad[L1_CACHE_BYTES - sizeof(atomic_t)]; + refcount_t tw_refcount; - struct inet_hashinfo *hashinfo; + struct inet_hashinfo *hashinfo ____cacheline_aligned_in_smp; int sysctl_max_tw_buckets; }; struct tcp_fastopen_context; struct netns_ipv4 { - /* Please keep tcp_death_row at first field in netns_ipv4 */ - struct inet_timewait_death_row tcp_death_row ____cacheline_aligned_in_smp; + struct inet_timewait_death_row *tcp_death_row; #ifdef CONFIG_SYSCTL struct ctl_table_header *forw_hdr; @@ -70,11 +68,9 @@ struct netns_ipv4 { struct hlist_head *fib_table_hash; struct sock *fibnl; - struct sock * __percpu *icmp_sk; struct sock *mc_autojoin_sk; struct inet_peer_base *peers; - struct sock * __percpu *tcp_sk; struct fqdir *fqdir; u8 sysctl_icmp_echo_ignore_all; @@ -87,6 +83,7 @@ struct netns_ipv4 { u32 ip_rt_min_pmtu; int ip_rt_mtu_expires; + int ip_rt_min_advmss; struct local_ports ip_local_ports; @@ -130,6 +127,7 @@ struct netns_ipv4 { u8 sysctl_tcp_synack_retries; u8 sysctl_tcp_syncookies; u8 sysctl_tcp_migrate_req; + u8 sysctl_tcp_comp_sack_nr; int sysctl_tcp_reordering; u8 sysctl_tcp_retries1; u8 sysctl_tcp_retries2; @@ -163,9 +161,9 @@ struct netns_ipv4 { int sysctl_tcp_challenge_ack_limit; int sysctl_tcp_min_rtt_wlen; u8 sysctl_tcp_min_tso_segs; + u8 sysctl_tcp_tso_rtt_log; u8 sysctl_tcp_autocorking; u8 sysctl_tcp_reflect_tos; - u8 sysctl_tcp_comp_sack_nr; int sysctl_tcp_invalid_ratelimit; int sysctl_tcp_pacing_ss_ratio; int sysctl_tcp_pacing_ca_ratio; diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 6bd7e5a85ce7..3d83b64471d3 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -89,11 +89,15 @@ struct netns_ipv6 { struct fib6_table *fib6_local_tbl; struct fib_rules_ops *fib6_rules_ops; #endif - struct sock * __percpu *icmp_sk; struct sock *ndisc_sk; struct sock *tcp_sk; struct sock *igmp_sk; struct sock *mc_autojoin_sk; + + struct hlist_head *inet6_addr_lst; + spinlock_t addrconf_hash_lock; + struct delayed_work addr_chk_work; + #ifdef CONFIG_IPV6_MROUTE #ifndef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES struct mr_table *mrt6; diff --git a/include/net/netns/smc.h b/include/net/netns/smc.h index ea8a9cf2619b..e5389eeaf8bd 100644 --- a/include/net/netns/smc.h +++ b/include/net/netns/smc.h @@ -12,5 +12,11 @@ struct netns_smc { /* protect fback_rsn */ struct mutex mutex_fback_rsn; struct smc_stats_rsn *fback_rsn; + + bool limit_smc_hs; /* constraint on handshake */ +#ifdef CONFIG_SYSCTL + struct ctl_table_header *smc_hdr; +#endif + unsigned int sysctl_autocorking_size; }; #endif diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h index 947733a639a6..bd7c3be4af5d 100644 --- a/include/net/netns/xfrm.h +++ b/include/net/netns/xfrm.h @@ -66,11 +66,7 @@ struct netns_xfrm { int sysctl_larval_drop; u32 sysctl_acq_expires; - u8 policy_default; -#define XFRM_POL_DEFAULT_IN 1 -#define XFRM_POL_DEFAULT_OUT 2 -#define XFRM_POL_DEFAULT_FWD 4 -#define XFRM_POL_DEFAULT_MASK 7 + u8 policy_default[XFRM_POLICY_MAX]; #ifdef CONFIG_SYSCTL struct ctl_table_header *sysctl_hdr; diff --git a/include/net/page_pool.h b/include/net/page_pool.h index 79a805542d0f..ea5fb70e5101 100644 --- a/include/net/page_pool.h +++ b/include/net/page_pool.h @@ -84,6 +84,48 @@ struct page_pool_params { void *init_arg; }; +#ifdef CONFIG_PAGE_POOL_STATS +struct page_pool_alloc_stats { + u64 fast; /* fast path allocations */ + u64 slow; /* slow-path order 0 allocations */ + u64 slow_high_order; /* slow-path high order allocations */ + u64 empty; /* failed refills due to empty ptr ring, forcing + * slow path allocation + */ + u64 refill; /* allocations via successful refill */ + u64 waive; /* failed refills due to numa zone mismatch */ +}; + +struct page_pool_recycle_stats { + u64 cached; /* recycling placed page in the cache. */ + u64 cache_full; /* cache was full */ + u64 ring; /* recycling placed page back into ptr ring */ + u64 ring_full; /* page was released from page-pool because + * PTR ring was full. + */ + u64 released_refcnt; /* page released because of elevated + * refcnt + */ +}; + +/* This struct wraps the above stats structs so users of the + * page_pool_get_stats API can pass a single argument when requesting the + * stats for the page pool. + */ +struct page_pool_stats { + struct page_pool_alloc_stats alloc_stats; + struct page_pool_recycle_stats recycle_stats; +}; + +/* + * Drivers that wish to harvest page pool stats and report them to users + * (perhaps via ethtool, debugfs, or another mechanism) can allocate a + * struct page_pool_stats call page_pool_get_stats to get stats for the specified pool. + */ +bool page_pool_get_stats(struct page_pool *pool, + struct page_pool_stats *stats); +#endif + struct page_pool { struct page_pool_params p; @@ -96,6 +138,11 @@ struct page_pool { unsigned int frag_offset; struct page *frag_page; long frag_users; + +#ifdef CONFIG_PAGE_POOL_STATS + /* these stats are incremented while in softirq context */ + struct page_pool_alloc_stats alloc_stats; +#endif u32 xdp_mem_id; /* @@ -126,6 +173,10 @@ struct page_pool { */ struct ptr_ring ring; +#ifdef CONFIG_PAGE_POOL_STATS + /* recycle stats are per-cpu to avoid locking */ + struct page_pool_recycle_stats __percpu *recycle_stats; +#endif atomic_t pages_state_release_cnt; /* A page_pool is strictly tied to a single RX-queue being @@ -201,21 +252,67 @@ static inline void page_pool_put_page_bulk(struct page_pool *pool, void **data, } #endif -void page_pool_put_page(struct page_pool *pool, struct page *page, - unsigned int dma_sync_size, bool allow_direct); +void page_pool_put_defragged_page(struct page_pool *pool, struct page *page, + unsigned int dma_sync_size, + bool allow_direct); -/* Same as above but will try to sync the entire area pool->max_len */ -static inline void page_pool_put_full_page(struct page_pool *pool, - struct page *page, bool allow_direct) +static inline void page_pool_fragment_page(struct page *page, long nr) +{ + atomic_long_set(&page->pp_frag_count, nr); +} + +static inline long page_pool_defrag_page(struct page *page, long nr) +{ + long ret; + + /* If nr == pp_frag_count then we have cleared all remaining + * references to the page. No need to actually overwrite it, instead + * we can leave this to be overwritten by the calling function. + * + * The main advantage to doing this is that an atomic_read is + * generally a much cheaper operation than an atomic update, + * especially when dealing with a page that may be partitioned + * into only 2 or 3 pieces. + */ + if (atomic_long_read(&page->pp_frag_count) == nr) + return 0; + + ret = atomic_long_sub_return(nr, &page->pp_frag_count); + WARN_ON(ret < 0); + return ret; +} + +static inline bool page_pool_is_last_frag(struct page_pool *pool, + struct page *page) +{ + /* If fragments aren't enabled or count is 0 we were the last user */ + return !(pool->p.flags & PP_FLAG_PAGE_FRAG) || + (page_pool_defrag_page(page, 1) == 0); +} + +static inline void page_pool_put_page(struct page_pool *pool, + struct page *page, + unsigned int dma_sync_size, + bool allow_direct) { /* When page_pool isn't compiled-in, net/core/xdp.c doesn't * allow registering MEM_TYPE_PAGE_POOL, but shield linker. */ #ifdef CONFIG_PAGE_POOL - page_pool_put_page(pool, page, -1, allow_direct); + if (!page_pool_is_last_frag(pool, page)) + return; + + page_pool_put_defragged_page(pool, page, dma_sync_size, allow_direct); #endif } +/* Same as above but will try to sync the entire area pool->max_len */ +static inline void page_pool_put_full_page(struct page_pool *pool, + struct page *page, bool allow_direct) +{ + page_pool_put_page(pool, page, -1, allow_direct); +} + /* Same as above but the caller must guarantee safe context. e.g NAPI */ static inline void page_pool_recycle_direct(struct page_pool *pool, struct page *page) @@ -243,30 +340,6 @@ static inline void page_pool_set_dma_addr(struct page *page, dma_addr_t addr) page->dma_addr_upper = upper_32_bits(addr); } -static inline void page_pool_set_frag_count(struct page *page, long nr) -{ - atomic_long_set(&page->pp_frag_count, nr); -} - -static inline long page_pool_atomic_sub_frag_count_return(struct page *page, - long nr) -{ - long ret; - - /* As suggested by Alexander, atomic_long_read() may cover up the - * reference count errors, so avoid calling atomic_long_read() in - * the cases of freeing or draining the page_frags, where we would - * not expect it to match or that are slowpath anyway. - */ - if (__builtin_constant_p(nr) && - atomic_long_read(&page->pp_frag_count) == nr) - return 0; - - ret = atomic_long_sub_return(nr, &page->pp_frag_count); - WARN_ON(ret < 0); - return ret; -} - static inline bool is_page_pool_compiled_in(void) { #ifdef CONFIG_PAGE_POOL diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 676cb8ea9e15..a3b57a93228a 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -1028,4 +1028,15 @@ struct tc_fifo_qopt_offload { }; }; +#ifdef CONFIG_NET_CLS_ACT +DECLARE_STATIC_KEY_FALSE(tc_skb_ext_tc); +void tc_skb_ext_tc_enable(void); +void tc_skb_ext_tc_disable(void); +#define tc_skb_ext_tc_enabled() static_branch_unlikely(&tc_skb_ext_tc) +#else /* CONFIG_NET_CLS_ACT */ +static inline void tc_skb_ext_tc_enable(void) { } +static inline void tc_skb_ext_tc_disable(void) { } +#define tc_skb_ext_tc_enabled() false +#endif + #endif diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 9e7b21c0b3a6..44a35531952e 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -63,12 +63,6 @@ static inline psched_time_t psched_get_time(void) return PSCHED_NS2TICKS(ktime_get_ns()); } -static inline psched_tdiff_t -psched_tdiff_bounded(psched_time_t tv1, psched_time_t tv2, psched_time_t bound) -{ - return min(tv1 - tv2, bound); -} - struct qdisc_watchdog { u64 last_expires; struct hrtimer timer; diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 29e41ff3ec93..144c39db9898 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -70,6 +70,7 @@ struct request_sock { struct saved_syn *saved_syn; u32 secid; u32 peer_secid; + u32 timeout; }; static inline struct request_sock *inet_reqsk(const struct sock *sk) @@ -104,6 +105,7 @@ reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener, sk_node_init(&req_to_sk(req)->sk_node); sk_tx_queue_clear(req_to_sk(req)); req->saved_syn = NULL; + req->timeout = 0; req->num_timeout = 0; req->num_retrans = 0; req->sk = NULL; diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 472843eedbae..9bab396c1f3b 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -518,11 +518,6 @@ static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz) BUILD_BUG_ON(sizeof(qcb->data) < sz); } -static inline int qdisc_qlen_cpu(const struct Qdisc *q) -{ - return this_cpu_ptr(q->cpu_qstats)->qlen; -} - static inline int qdisc_qlen(const struct Qdisc *q) { return q->q.qlen; diff --git a/include/net/sock.h b/include/net/sock.h index 50aecd28b355..c4b91fc19b9c 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -316,6 +316,7 @@ struct sk_filter; * @sk_rcvtimeo: %SO_RCVTIMEO setting * @sk_sndtimeo: %SO_SNDTIMEO setting * @sk_txhash: computed flow hash for use on transmit + * @sk_txrehash: enable TX hash rethink * @sk_filter: socket filtering instructions * @sk_timer: sock cleanup timer * @sk_stamp: time stamp of last packet received @@ -491,6 +492,7 @@ struct sock { u32 sk_ack_backlog; u32 sk_max_ack_backlog; kuid_t sk_uid; + u8 sk_txrehash; #ifdef CONFIG_NET_RX_BUSY_POLL u8 sk_prefer_busy_poll; u16 sk_busy_poll_budget; @@ -587,6 +589,18 @@ static inline bool sk_user_data_is_nocopy(const struct sock *sk) __tmp | SK_USER_DATA_NOCOPY); \ }) +static inline +struct net *sock_net(const struct sock *sk) +{ + return read_pnet(&sk->sk_net); +} + +static inline +void sock_net_set(struct sock *sk, struct net *net) +{ + write_pnet(&sk->sk_net, net); +} + /* * SK_CAN_REUSE and SK_NO_REUSE on a socket mean that the socket is OK * or not whether his port will be reused by someone else. SK_FORCE_REUSE @@ -2054,7 +2068,7 @@ static inline void sk_set_txhash(struct sock *sk) static inline bool sk_rethink_txhash(struct sock *sk) { - if (sk->sk_txhash) { + if (sk->sk_txhash && sk->sk_txrehash == SOCK_TXREHASH_ENABLED) { sk_set_txhash(sk); return true; } @@ -2704,18 +2718,6 @@ static inline void sk_eat_skb(struct sock *sk, struct sk_buff *skb) __kfree_skb(skb); } -static inline -struct net *sock_net(const struct sock *sk) -{ - return read_pnet(&sk->sk_net); -} - -static inline -void sock_net_set(struct sock *sk, struct net *net) -{ - write_pnet(&sk->sk_net, net); -} - static inline bool skb_sk_is_prefetched(struct sk_buff *skb) { diff --git a/include/net/switchdev.h b/include/net/switchdev.h index d353793dfeb5..aa0171d5786d 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -19,6 +19,7 @@ enum switchdev_attr_id { SWITCHDEV_ATTR_ID_UNDEFINED, SWITCHDEV_ATTR_ID_PORT_STP_STATE, + SWITCHDEV_ATTR_ID_PORT_MST_STATE, SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS, SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS, SWITCHDEV_ATTR_ID_PORT_MROUTER, @@ -27,7 +28,14 @@ enum switchdev_attr_id { SWITCHDEV_ATTR_ID_BRIDGE_VLAN_PROTOCOL, SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED, SWITCHDEV_ATTR_ID_BRIDGE_MROUTER, + SWITCHDEV_ATTR_ID_BRIDGE_MST, SWITCHDEV_ATTR_ID_MRP_PORT_ROLE, + SWITCHDEV_ATTR_ID_VLAN_MSTI, +}; + +struct switchdev_mst_state { + u16 msti; + u8 state; }; struct switchdev_brport_flags { @@ -35,6 +43,11 @@ struct switchdev_brport_flags { unsigned long mask; }; +struct switchdev_vlan_msti { + u16 vid; + u16 msti; +}; + struct switchdev_attr { struct net_device *orig_dev; enum switchdev_attr_id id; @@ -43,13 +56,16 @@ struct switchdev_attr { void (*complete)(struct net_device *dev, int err, void *priv); union { u8 stp_state; /* PORT_STP_STATE */ + struct switchdev_mst_state mst_state; /* PORT_MST_STATE */ struct switchdev_brport_flags brport_flags; /* PORT_BRIDGE_FLAGS */ bool mrouter; /* PORT_MROUTER */ clock_t ageing_time; /* BRIDGE_AGEING_TIME */ bool vlan_filtering; /* BRIDGE_VLAN_FILTERING */ u16 vlan_protocol; /* BRIDGE_VLAN_PROTOCOL */ + bool mst; /* BRIDGE_MST */ bool mc_disabled; /* MC_DISABLED */ u8 mrp_port_role; /* MRP_PORT_ROLE */ + struct switchdev_vlan_msti vlan_msti; /* VLAN_MSTI */ } u; }; @@ -81,6 +97,13 @@ struct switchdev_obj_port_vlan { struct switchdev_obj obj; u16 flags; u16 vid; + /* If set, the notifier signifies a change of one of the following + * flags for a VLAN that already exists: + * - BRIDGE_VLAN_INFO_PVID + * - BRIDGE_VLAN_INFO_UNTAGGED + * Entries with BRIDGE_VLAN_INFO_BRENTRY unset are not notified at all. + */ + bool changed; }; #define SWITCHDEV_OBJ_PORT_VLAN(OBJ) \ @@ -306,10 +329,7 @@ int switchdev_handle_fdb_event_to_device(struct net_device *dev, unsigned long e const struct net_device *foreign_dev), int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev, unsigned long event, const void *ctx, - const struct switchdev_notifier_fdb_info *fdb_info), - int (*lag_mod_cb)(struct net_device *dev, struct net_device *orig_dev, - unsigned long event, const void *ctx, - const struct switchdev_notifier_fdb_info *fdb_info)); + const struct switchdev_notifier_fdb_info *fdb_info)); int switchdev_handle_port_obj_add(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, @@ -317,11 +337,26 @@ int switchdev_handle_port_obj_add(struct net_device *dev, int (*add_cb)(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack)); +int switchdev_handle_port_obj_add_foreign(struct net_device *dev, + struct switchdev_notifier_port_obj_info *port_obj_info, + bool (*check_cb)(const struct net_device *dev), + bool (*foreign_dev_check_cb)(const struct net_device *dev, + const struct net_device *foreign_dev), + int (*add_cb)(struct net_device *dev, const void *ctx, + const struct switchdev_obj *obj, + struct netlink_ext_ack *extack)); int switchdev_handle_port_obj_del(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), int (*del_cb)(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj)); +int switchdev_handle_port_obj_del_foreign(struct net_device *dev, + struct switchdev_notifier_port_obj_info *port_obj_info, + bool (*check_cb)(const struct net_device *dev), + bool (*foreign_dev_check_cb)(const struct net_device *dev, + const struct net_device *foreign_dev), + int (*del_cb)(struct net_device *dev, const void *ctx, + const struct switchdev_obj *obj)); int switchdev_handle_port_attr_set(struct net_device *dev, struct switchdev_notifier_port_attr_info *port_attr_info, @@ -421,10 +456,7 @@ switchdev_handle_fdb_event_to_device(struct net_device *dev, unsigned long event const struct net_device *foreign_dev), int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev, unsigned long event, const void *ctx, - const struct switchdev_notifier_fdb_info *fdb_info), - int (*lag_mod_cb)(struct net_device *dev, struct net_device *orig_dev, - unsigned long event, const void *ctx, - const struct switchdev_notifier_fdb_info *fdb_info)) + const struct switchdev_notifier_fdb_info *fdb_info)) { return 0; } @@ -440,6 +472,18 @@ switchdev_handle_port_obj_add(struct net_device *dev, return 0; } +static inline int switchdev_handle_port_obj_add_foreign(struct net_device *dev, + struct switchdev_notifier_port_obj_info *port_obj_info, + bool (*check_cb)(const struct net_device *dev), + bool (*foreign_dev_check_cb)(const struct net_device *dev, + const struct net_device *foreign_dev), + int (*add_cb)(struct net_device *dev, const void *ctx, + const struct switchdev_obj *obj, + struct netlink_ext_ack *extack)) +{ + return 0; +} + static inline int switchdev_handle_port_obj_del(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, @@ -450,6 +494,18 @@ switchdev_handle_port_obj_del(struct net_device *dev, return 0; } +static inline int +switchdev_handle_port_obj_del_foreign(struct net_device *dev, + struct switchdev_notifier_port_obj_info *port_obj_info, + bool (*check_cb)(const struct net_device *dev), + bool (*foreign_dev_check_cb)(const struct net_device *dev, + const struct net_device *foreign_dev), + int (*del_cb)(struct net_device *dev, const void *ctx, + const struct switchdev_obj *obj)) +{ + return 0; +} + static inline int switchdev_handle_port_attr_set(struct net_device *dev, struct switchdev_notifier_port_attr_info *port_attr_info, diff --git a/include/net/tc_act/tc_police.h b/include/net/tc_act/tc_police.h index 72649512dcdd..283bde711a42 100644 --- a/include/net/tc_act/tc_police.h +++ b/include/net/tc_act/tc_police.h @@ -159,4 +159,34 @@ static inline u32 tcf_police_tcfp_mtu(const struct tc_action *act) return params->tcfp_mtu; } +static inline u64 tcf_police_peakrate_bytes_ps(const struct tc_action *act) +{ + struct tcf_police *police = to_police(act); + struct tcf_police_params *params; + + params = rcu_dereference_protected(police->params, + lockdep_is_held(&police->tcf_lock)); + return params->peak.rate_bytes_ps; +} + +static inline u32 tcf_police_tcfp_ewma_rate(const struct tc_action *act) +{ + struct tcf_police *police = to_police(act); + struct tcf_police_params *params; + + params = rcu_dereference_protected(police->params, + lockdep_is_held(&police->tcf_lock)); + return params->tcfp_ewma_rate; +} + +static inline u16 tcf_police_rate_overhead(const struct tc_action *act) +{ + struct tcf_police *police = to_police(act); + struct tcf_police_params *params; + + params = rcu_dereference_protected(police->params, + lockdep_is_held(&police->tcf_lock)); + return params->rate.overhead; +} + #endif /* __NET_TC_POLICE_H */ diff --git a/include/net/tc_act/tc_vlan.h b/include/net/tc_act/tc_vlan.h index f94b8bc26f9e..904eddfc1826 100644 --- a/include/net/tc_act/tc_vlan.h +++ b/include/net/tc_act/tc_vlan.h @@ -78,4 +78,14 @@ static inline u8 tcf_vlan_push_prio(const struct tc_action *a) return tcfv_push_prio; } + +static inline void tcf_vlan_push_eth(unsigned char *src, unsigned char *dest, + const struct tc_action *a) +{ + rcu_read_lock(); + memcpy(dest, rcu_dereference(to_vlan(a)->vlan_p)->tcfv_push_dst, ETH_ALEN); + memcpy(src, rcu_dereference(to_vlan(a)->vlan_p)->tcfv_push_src, ETH_ALEN); + rcu_read_unlock(); +} + #endif /* __NET_TC_VLAN_H */ diff --git a/include/net/tcp.h b/include/net/tcp.h index b9fc978fb2ca..70ca4a5e330a 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1367,7 +1367,8 @@ static inline bool tcp_checksum_complete(struct sk_buff *skb) __skb_checksum_complete(skb); } -bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb); +bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb, + enum skb_drop_reason *reason); #ifdef CONFIG_INET void __sk_defer_free_flush(struct sock *sk); @@ -1674,6 +1675,12 @@ tcp_md5_do_lookup(const struct sock *sk, int l3index, return __tcp_md5_do_lookup(sk, l3index, addr, family); } +enum skb_drop_reason +tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, + const void *saddr, const void *daddr, + int family, int dif, int sdif); + + #define tcp_twsk_md5_key(twsk) ((twsk)->tw_md5_key) #else static inline struct tcp_md5sig_key * @@ -1682,6 +1689,14 @@ tcp_md5_do_lookup(const struct sock *sk, int l3index, { return NULL; } + +static inline enum skb_drop_reason +tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, + const void *saddr, const void *daddr, + int family, int dif, int sdif) +{ + return SKB_NOT_DROPPED_YET; +} #define tcp_twsk_md5_key(twsk) NULL #endif @@ -1817,11 +1832,6 @@ static inline struct sk_buff *tcp_rtx_queue_tail(const struct sock *sk) return skb_rb_last(&sk->tcp_rtx_queue); } -static inline struct sk_buff *tcp_write_queue_head(const struct sock *sk) -{ - return skb_peek(&sk->sk_write_queue); -} - static inline struct sk_buff *tcp_write_queue_tail(const struct sock *sk) { return skb_peek_tail(&sk->sk_write_queue); @@ -2358,7 +2368,7 @@ static inline u32 tcp_timeout_init(struct sock *sk) if (timeout <= 0) timeout = TCP_TIMEOUT_INIT; - return timeout; + return min_t(int, timeout, TCP_RTO_MAX); } static inline u32 tcp_rwnd_init_bpf(struct sock *sk) diff --git a/include/net/tls.h b/include/net/tls.h index 526cb2c3b724..b6968a5b5538 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -626,7 +626,6 @@ tls_offload_ctx_rx(const struct tls_context *tls_ctx) return (struct tls_offload_context_rx *)tls_ctx->priv_ctx_rx; } -#if IS_ENABLED(CONFIG_TLS_DEVICE) static inline void *__tls_driver_ctx(struct tls_context *tls_ctx, enum tls_offload_ctx_dir direction) { @@ -641,7 +640,6 @@ tls_driver_ctx(const struct sock *sk, enum tls_offload_ctx_dir direction) { return __tls_driver_ctx(tls_get_ctx(sk), direction); } -#endif #define RESYNC_REQ BIT(0) #define RESYNC_REQ_ASYNC BIT(1) diff --git a/include/net/udplite.h b/include/net/udplite.h index 9185e45b997f..a3c53110d30b 100644 --- a/include/net/udplite.h +++ b/include/net/udplite.h @@ -70,49 +70,6 @@ static inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh) return 0; } -/* Slow-path computation of checksum. Socket is locked. */ -static inline __wsum udplite_csum_outgoing(struct sock *sk, struct sk_buff *skb) -{ - const struct udp_sock *up = udp_sk(skb->sk); - int cscov = up->len; - __wsum csum = 0; - - if (up->pcflag & UDPLITE_SEND_CC) { - /* - * Sender has set `partial coverage' option on UDP-Lite socket. - * The special case "up->pcslen == 0" signifies full coverage. - */ - if (up->pcslen < up->len) { - if (0 < up->pcslen) - cscov = up->pcslen; - udp_hdr(skb)->len = htons(up->pcslen); - } - /* - * NOTE: Causes for the error case `up->pcslen > up->len': - * (i) Application error (will not be penalized). - * (ii) Payload too big for send buffer: data is split - * into several packets, each with its own header. - * In this case (e.g. last segment), coverage may - * exceed packet length. - * Since packets with coverage length > packet length are - * illegal, we fall back to the defaults here. - */ - } - - skb->ip_summed = CHECKSUM_NONE; /* no HW support for checksumming */ - - skb_queue_walk(&sk->sk_write_queue, skb) { - const int off = skb_transport_offset(skb); - const int len = skb->len - off; - - csum = skb_checksum(skb, off, (cscov > len)? len : cscov, csum); - - if ((cscov -= len) <= 0) - break; - } - return csum; -} - /* Fast-path computation of checksum. Socket may not be locked. */ static inline __wsum udplite_csum(struct sk_buff *skb) { diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 5a934bebe630..bca5b01af247 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -227,11 +227,56 @@ struct vxlan_config { enum ifla_vxlan_df df; }; +enum { + VXLAN_VNI_STATS_RX, + VXLAN_VNI_STATS_RX_DROPS, + VXLAN_VNI_STATS_RX_ERRORS, + VXLAN_VNI_STATS_TX, + VXLAN_VNI_STATS_TX_DROPS, + VXLAN_VNI_STATS_TX_ERRORS, +}; + +struct vxlan_vni_stats { + u64 rx_packets; + u64 rx_bytes; + u64 rx_drops; + u64 rx_errors; + u64 tx_packets; + u64 tx_bytes; + u64 tx_drops; + u64 tx_errors; +}; + +struct vxlan_vni_stats_pcpu { + struct vxlan_vni_stats stats; + struct u64_stats_sync syncp; +}; + struct vxlan_dev_node { struct hlist_node hlist; struct vxlan_dev *vxlan; }; +struct vxlan_vni_node { + struct rhash_head vnode; + struct vxlan_dev_node hlist4; /* vni hash table for IPv4 socket */ +#if IS_ENABLED(CONFIG_IPV6) + struct vxlan_dev_node hlist6; /* vni hash table for IPv6 socket */ +#endif + struct list_head vlist; + __be32 vni; + union vxlan_addr remote_ip; /* default remote ip for this vni */ + struct vxlan_vni_stats_pcpu __percpu *stats; + + struct rcu_head rcu; +}; + +struct vxlan_vni_group { + struct rhashtable vni_hash; + struct list_head vni_list; + u32 num_vnis; +}; + /* Pseudo network device */ struct vxlan_dev { struct vxlan_dev_node hlist4; /* vni hash table for IPv4 socket */ @@ -254,6 +299,8 @@ struct vxlan_dev { struct vxlan_config cfg; + struct vxlan_vni_group __rcu *vnigrp; + struct hlist_head fdb_head[FDB_HASH_SIZE]; }; @@ -274,6 +321,7 @@ struct vxlan_dev { #define VXLAN_F_GPE 0x4000 #define VXLAN_F_IPV6_LINKLOCAL 0x8000 #define VXLAN_F_TTL_INHERIT 0x10000 +#define VXLAN_F_VNIFILTER 0x20000 /* Flags that are used in the receive path. These flags must match in * order for a socket to be shareable @@ -283,7 +331,8 @@ struct vxlan_dev { VXLAN_F_UDP_ZERO_CSUM6_RX | \ VXLAN_F_REMCSUM_RX | \ VXLAN_F_REMCSUM_NOPARTIAL | \ - VXLAN_F_COLLECT_METADATA) + VXLAN_F_COLLECT_METADATA | \ + VXLAN_F_VNIFILTER) /* Flags that can be set together with VXLAN_F_GPE. */ #define VXLAN_F_ALLOWED_GPE (VXLAN_F_GPE | \ @@ -292,7 +341,8 @@ struct vxlan_dev { VXLAN_F_UDP_ZERO_CSUM_TX | \ VXLAN_F_UDP_ZERO_CSUM6_TX | \ VXLAN_F_UDP_ZERO_CSUM6_RX | \ - VXLAN_F_COLLECT_METADATA) + VXLAN_F_COLLECT_METADATA | \ + VXLAN_F_VNIFILTER) struct net_device *vxlan_dev_create(struct net *net, const char *name, u8 name_assign_type, struct vxlan_config *conf); diff --git a/include/net/xdp.h b/include/net/xdp.h index 8f0812e4996d..04c852c7a77f 100644 --- a/include/net/xdp.h +++ b/include/net/xdp.h @@ -60,12 +60,20 @@ struct xdp_rxq_info { u32 reg_state; struct xdp_mem_info mem; unsigned int napi_id; + u32 frag_size; } ____cacheline_aligned; /* perf critical, avoid false-sharing */ struct xdp_txq_info { struct net_device *dev; }; +enum xdp_buff_flags { + XDP_FLAGS_HAS_FRAGS = BIT(0), /* non-linear xdp buff */ + XDP_FLAGS_FRAGS_PF_MEMALLOC = BIT(1), /* xdp paged memory is under + * pressure + */ +}; + struct xdp_buff { void *data; void *data_end; @@ -74,13 +82,40 @@ struct xdp_buff { struct xdp_rxq_info *rxq; struct xdp_txq_info *txq; u32 frame_sz; /* frame size to deduce data_hard_end/reserved tailroom*/ + u32 flags; /* supported values defined in xdp_buff_flags */ }; +static __always_inline bool xdp_buff_has_frags(struct xdp_buff *xdp) +{ + return !!(xdp->flags & XDP_FLAGS_HAS_FRAGS); +} + +static __always_inline void xdp_buff_set_frags_flag(struct xdp_buff *xdp) +{ + xdp->flags |= XDP_FLAGS_HAS_FRAGS; +} + +static __always_inline void xdp_buff_clear_frags_flag(struct xdp_buff *xdp) +{ + xdp->flags &= ~XDP_FLAGS_HAS_FRAGS; +} + +static __always_inline bool xdp_buff_is_frag_pfmemalloc(struct xdp_buff *xdp) +{ + return !!(xdp->flags & XDP_FLAGS_FRAGS_PF_MEMALLOC); +} + +static __always_inline void xdp_buff_set_frag_pfmemalloc(struct xdp_buff *xdp) +{ + xdp->flags |= XDP_FLAGS_FRAGS_PF_MEMALLOC; +} + static __always_inline void xdp_init_buff(struct xdp_buff *xdp, u32 frame_sz, struct xdp_rxq_info *rxq) { xdp->frame_sz = frame_sz; xdp->rxq = rxq; + xdp->flags = 0; } static __always_inline void @@ -111,6 +146,20 @@ xdp_get_shared_info_from_buff(struct xdp_buff *xdp) return (struct skb_shared_info *)xdp_data_hard_end(xdp); } +static __always_inline unsigned int xdp_get_buff_len(struct xdp_buff *xdp) +{ + unsigned int len = xdp->data_end - xdp->data; + struct skb_shared_info *sinfo; + + if (likely(!xdp_buff_has_frags(xdp))) + goto out; + + sinfo = xdp_get_shared_info_from_buff(xdp); + len += sinfo->xdp_frags_size; +out: + return len; +} + struct xdp_frame { void *data; u16 len; @@ -122,8 +171,19 @@ struct xdp_frame { */ struct xdp_mem_info mem; struct net_device *dev_rx; /* used by cpumap */ + u32 flags; /* supported values defined in xdp_buff_flags */ }; +static __always_inline bool xdp_frame_has_frags(struct xdp_frame *frame) +{ + return !!(frame->flags & XDP_FLAGS_HAS_FRAGS); +} + +static __always_inline bool xdp_frame_is_frag_pfmemalloc(struct xdp_frame *frame) +{ + return !!(frame->flags & XDP_FLAGS_FRAGS_PF_MEMALLOC); +} + #define XDP_BULK_QUEUE_SIZE 16 struct xdp_frame_bulk { int count; @@ -159,6 +219,19 @@ static inline void xdp_scrub_frame(struct xdp_frame *frame) frame->dev_rx = NULL; } +static inline void +xdp_update_skb_shared_info(struct sk_buff *skb, u8 nr_frags, + unsigned int size, unsigned int truesize, + bool pfmemalloc) +{ + skb_shinfo(skb)->nr_frags = nr_frags; + + skb->len += size; + skb->data_len += size; + skb->truesize += truesize; + skb->pfmemalloc |= pfmemalloc; +} + /* Avoids inlining WARN macro in fast-path */ void xdp_warn(const char *msg, const char *func, const int line); #define XDP_WARN(msg) xdp_warn(msg, __func__, __LINE__) @@ -180,6 +253,7 @@ void xdp_convert_frame_to_buff(struct xdp_frame *frame, struct xdp_buff *xdp) xdp->data_end = frame->data + frame->len; xdp->data_meta = frame->data - frame->metasize; xdp->frame_sz = frame->frame_sz; + xdp->flags = frame->flags; } static inline @@ -206,6 +280,7 @@ int xdp_update_frame_from_buff(struct xdp_buff *xdp, xdp_frame->headroom = headroom - sizeof(*xdp_frame); xdp_frame->metasize = metasize; xdp_frame->frame_sz = xdp->frame_sz; + xdp_frame->flags = xdp->flags; return 0; } @@ -230,6 +305,8 @@ struct xdp_frame *xdp_convert_buff_to_frame(struct xdp_buff *xdp) return xdp_frame; } +void __xdp_return(void *data, struct xdp_mem_info *mem, bool napi_direct, + struct xdp_buff *xdp); void xdp_return_frame(struct xdp_frame *xdpf); void xdp_return_frame_rx_napi(struct xdp_frame *xdpf); void xdp_return_buff(struct xdp_buff *xdp); @@ -246,14 +323,51 @@ void __xdp_release_frame(void *data, struct xdp_mem_info *mem); static inline void xdp_release_frame(struct xdp_frame *xdpf) { struct xdp_mem_info *mem = &xdpf->mem; + struct skb_shared_info *sinfo; + int i; /* Curr only page_pool needs this */ - if (mem->type == MEM_TYPE_PAGE_POOL) - __xdp_release_frame(xdpf->data, mem); + if (mem->type != MEM_TYPE_PAGE_POOL) + return; + + if (likely(!xdp_frame_has_frags(xdpf))) + goto out; + + sinfo = xdp_get_shared_info_from_frame(xdpf); + for (i = 0; i < sinfo->nr_frags; i++) { + struct page *page = skb_frag_page(&sinfo->frags[i]); + + __xdp_release_frame(page_address(page), mem); + } +out: + __xdp_release_frame(xdpf->data, mem); +} + +static __always_inline unsigned int xdp_get_frame_len(struct xdp_frame *xdpf) +{ + struct skb_shared_info *sinfo; + unsigned int len = xdpf->len; + + if (likely(!xdp_frame_has_frags(xdpf))) + goto out; + + sinfo = xdp_get_shared_info_from_frame(xdpf); + len += sinfo->xdp_frags_size; +out: + return len; +} + +int __xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq, + struct net_device *dev, u32 queue_index, + unsigned int napi_id, u32 frag_size); +static inline int +xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq, + struct net_device *dev, u32 queue_index, + unsigned int napi_id) +{ + return __xdp_rxq_info_reg(xdp_rxq, dev, queue_index, napi_id, 0); } -int xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq, - struct net_device *dev, u32 queue_index, unsigned int napi_id); void xdp_rxq_info_unreg(struct xdp_rxq_info *xdp_rxq); void xdp_rxq_info_unused(struct xdp_rxq_info *xdp_rxq); bool xdp_rxq_info_is_reg(struct xdp_rxq_info *xdp_rxq); diff --git a/include/net/xdp_sock_drv.h b/include/net/xdp_sock_drv.h index 443d45951564..4aa031849668 100644 --- a/include/net/xdp_sock_drv.h +++ b/include/net/xdp_sock_drv.h @@ -13,7 +13,7 @@ void xsk_tx_completed(struct xsk_buff_pool *pool, u32 nb_entries); bool xsk_tx_peek_desc(struct xsk_buff_pool *pool, struct xdp_desc *desc); -u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, struct xdp_desc *desc, u32 max); +u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 max); void xsk_tx_release(struct xsk_buff_pool *pool); struct xsk_buff_pool *xsk_get_pool_from_qid(struct net_device *dev, u16 queue_id); @@ -142,8 +142,7 @@ static inline bool xsk_tx_peek_desc(struct xsk_buff_pool *pool, return false; } -static inline u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, struct xdp_desc *desc, - u32 max) +static inline u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 max) { return 0; } diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 76aa6f11a540..6fb899ff5afc 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1081,25 +1081,18 @@ xfrm_state_addr_cmp(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x, un } #ifdef CONFIG_XFRM -static inline bool -xfrm_default_allow(struct net *net, int dir) -{ - u8 def = net->xfrm.policy_default; - - switch (dir) { - case XFRM_POLICY_IN: - return def & XFRM_POL_DEFAULT_IN ? false : true; - case XFRM_POLICY_OUT: - return def & XFRM_POL_DEFAULT_OUT ? false : true; - case XFRM_POLICY_FWD: - return def & XFRM_POL_DEFAULT_FWD ? false : true; - } - return false; -} - int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, unsigned short family); +static inline bool __xfrm_check_nopolicy(struct net *net, struct sk_buff *skb, + int dir) +{ + if (!net->xfrm.policy_count[dir] && !secpath_exists(skb)) + return net->xfrm.policy_default[dir] == XFRM_USERPOLICY_ACCEPT; + + return false; +} + static inline int __xfrm_policy_check2(struct sock *sk, int dir, struct sk_buff *skb, unsigned int family, int reverse) @@ -1110,13 +1103,9 @@ static inline int __xfrm_policy_check2(struct sock *sk, int dir, if (sk && sk->sk_policy[XFRM_POLICY_IN]) return __xfrm_policy_check(sk, ndir, skb, family); - if (xfrm_default_allow(net, dir)) - return (!net->xfrm.policy_count[dir] && !secpath_exists(skb)) || - (skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY)) || - __xfrm_policy_check(sk, ndir, skb, family); - else - return (skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY)) || - __xfrm_policy_check(sk, ndir, skb, family); + return __xfrm_check_nopolicy(net, skb, dir) || + (skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY)) || + __xfrm_policy_check(sk, ndir, skb, family); } static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, unsigned short family) @@ -1168,13 +1157,12 @@ static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family) { struct net *net = dev_net(skb->dev); - if (xfrm_default_allow(net, XFRM_POLICY_OUT)) - return !net->xfrm.policy_count[XFRM_POLICY_OUT] || - (skb_dst(skb)->flags & DST_NOXFRM) || - __xfrm_route_forward(skb, family); - else - return (skb_dst(skb)->flags & DST_NOXFRM) || - __xfrm_route_forward(skb, family); + if (!net->xfrm.policy_count[XFRM_POLICY_OUT] && + net->xfrm.policy_default[XFRM_POLICY_OUT] == XFRM_USERPOLICY_ACCEPT) + return true; + + return (skb_dst(skb)->flags & DST_NOXFRM) || + __xfrm_route_forward(skb, family); } static inline int xfrm4_route_forward(struct sk_buff *skb) diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h index ddeefc4a1040..5554ee75e7da 100644 --- a/include/net/xsk_buff_pool.h +++ b/include/net/xsk_buff_pool.h @@ -60,6 +60,7 @@ struct xsk_buff_pool { */ dma_addr_t *dma_pages; struct xdp_buff_xsk *heads; + struct xdp_desc *tx_descs; u64 chunk_mask; u64 addrs_cnt; u32 free_list_cnt; diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 5c3a3597f1d2..9b4e6c78d0f4 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -105,8 +105,6 @@ #define REG_RESERVED_ADDR 0xffffffff #define REG_RESERVED(reg) REG(reg, REG_RESERVED_ADDR) -#define OCELOT_MRP_CPUQ 7 - enum ocelot_target { ANA = 1, QS, @@ -540,6 +538,13 @@ struct ocelot_stat_layout { char name[ETH_GSTRING_LEN]; }; +struct ocelot_stats_region { + struct list_head node; + u32 offset; + int count; + u32 *buf; +}; + enum ocelot_tag_prefix { OCELOT_TAG_PREFIX_DISABLED = 0, OCELOT_TAG_PREFIX_NONE, @@ -630,6 +635,18 @@ enum macaccess_entry_type { #define OCELOT_QUIRK_PCS_PERFORMS_RATE_ADAPTATION BIT(0) #define OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP BIT(1) +struct ocelot_lag_fdb { + unsigned char addr[ETH_ALEN]; + u16 vid; + struct net_device *bond; + struct list_head list; +}; + +struct ocelot_mirror { + refcount_t refcount; + int to; +}; + struct ocelot_port { struct ocelot *ocelot; @@ -656,6 +673,7 @@ struct ocelot_port { u16 mrp_ring_id; struct net_device *bridge; + int bridge_num; u8 stp_state; int speed; @@ -671,6 +689,7 @@ struct ocelot { struct regmap_field *regfields[REGFIELD_MAX]; const u32 *const *map; const struct ocelot_stat_layout *stats_layout; + struct list_head stats_regions; unsigned int num_stats; u32 pool_size[OCELOT_SB_NUM][OCELOT_SB_POOL_NUM]; @@ -683,6 +702,8 @@ struct ocelot { u8 base_mac[ETH_ALEN]; struct list_head vlans; + struct list_head traps; + struct list_head lag_fdbs; /* Switches like VSC9959 have flooding per traffic class */ int num_flooding_pgids; @@ -698,6 +719,8 @@ struct ocelot { enum ocelot_tag_prefix npi_inj_prefix; enum ocelot_tag_prefix npi_xtr_prefix; + unsigned long bridges; + struct list_head multicast; struct list_head pgids; @@ -705,6 +728,7 @@ struct ocelot { struct ocelot_vcap_block block[3]; struct ocelot_vcap_policer vcap_pol; struct vcap_props *vcap; + struct ocelot_mirror *mirror; struct ocelot_psfp_list psfp; @@ -742,25 +766,42 @@ struct ocelot_policer { u32 burst; /* bytes */ }; -#define ocelot_read_ix(ocelot, reg, gi, ri) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri)) -#define ocelot_read_gix(ocelot, reg, gi) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi)) -#define ocelot_read_rix(ocelot, reg, ri) __ocelot_read_ix(ocelot, reg, reg##_RSZ * (ri)) -#define ocelot_read(ocelot, reg) __ocelot_read_ix(ocelot, reg, 0) +#define ocelot_bulk_read_rix(ocelot, reg, ri, buf, count) \ + __ocelot_bulk_read_ix(ocelot, reg, reg##_RSZ * (ri), buf, count) -#define ocelot_write_ix(ocelot, val, reg, gi, ri) __ocelot_write_ix(ocelot, val, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri)) -#define ocelot_write_gix(ocelot, val, reg, gi) __ocelot_write_ix(ocelot, val, reg, reg##_GSZ * (gi)) -#define ocelot_write_rix(ocelot, val, reg, ri) __ocelot_write_ix(ocelot, val, reg, reg##_RSZ * (ri)) +#define ocelot_read_ix(ocelot, reg, gi, ri) \ + __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri)) +#define ocelot_read_gix(ocelot, reg, gi) \ + __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi)) +#define ocelot_read_rix(ocelot, reg, ri) \ + __ocelot_read_ix(ocelot, reg, reg##_RSZ * (ri)) +#define ocelot_read(ocelot, reg) \ + __ocelot_read_ix(ocelot, reg, 0) + +#define ocelot_write_ix(ocelot, val, reg, gi, ri) \ + __ocelot_write_ix(ocelot, val, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri)) +#define ocelot_write_gix(ocelot, val, reg, gi) \ + __ocelot_write_ix(ocelot, val, reg, reg##_GSZ * (gi)) +#define ocelot_write_rix(ocelot, val, reg, ri) \ + __ocelot_write_ix(ocelot, val, reg, reg##_RSZ * (ri)) #define ocelot_write(ocelot, val, reg) __ocelot_write_ix(ocelot, val, reg, 0) -#define ocelot_rmw_ix(ocelot, val, m, reg, gi, ri) __ocelot_rmw_ix(ocelot, val, m, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri)) -#define ocelot_rmw_gix(ocelot, val, m, reg, gi) __ocelot_rmw_ix(ocelot, val, m, reg, reg##_GSZ * (gi)) -#define ocelot_rmw_rix(ocelot, val, m, reg, ri) __ocelot_rmw_ix(ocelot, val, m, reg, reg##_RSZ * (ri)) +#define ocelot_rmw_ix(ocelot, val, m, reg, gi, ri) \ + __ocelot_rmw_ix(ocelot, val, m, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri)) +#define ocelot_rmw_gix(ocelot, val, m, reg, gi) \ + __ocelot_rmw_ix(ocelot, val, m, reg, reg##_GSZ * (gi)) +#define ocelot_rmw_rix(ocelot, val, m, reg, ri) \ + __ocelot_rmw_ix(ocelot, val, m, reg, reg##_RSZ * (ri)) #define ocelot_rmw(ocelot, val, m, reg) __ocelot_rmw_ix(ocelot, val, m, reg, 0) -#define ocelot_field_write(ocelot, reg, val) regmap_field_write((ocelot)->regfields[(reg)], (val)) -#define ocelot_field_read(ocelot, reg, val) regmap_field_read((ocelot)->regfields[(reg)], (val)) -#define ocelot_fields_write(ocelot, id, reg, val) regmap_fields_write((ocelot)->regfields[(reg)], (id), (val)) -#define ocelot_fields_read(ocelot, id, reg, val) regmap_fields_read((ocelot)->regfields[(reg)], (id), (val)) +#define ocelot_field_write(ocelot, reg, val) \ + regmap_field_write((ocelot)->regfields[(reg)], (val)) +#define ocelot_field_read(ocelot, reg, val) \ + regmap_field_read((ocelot)->regfields[(reg)], (val)) +#define ocelot_fields_write(ocelot, id, reg, val) \ + regmap_fields_write((ocelot)->regfields[(reg)], (id), (val)) +#define ocelot_fields_read(ocelot, id, reg, val) \ + regmap_fields_read((ocelot)->regfields[(reg)], (id), (val)) #define ocelot_target_read_ix(ocelot, target, reg, gi, ri) \ __ocelot_target_read_ix(ocelot, target, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri)) @@ -784,6 +825,8 @@ struct ocelot_policer { u32 ocelot_port_readl(struct ocelot_port *port, u32 reg); void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg); void ocelot_port_rmwl(struct ocelot_port *port, u32 val, u32 mask, u32 reg); +int __ocelot_bulk_read_ix(struct ocelot *ocelot, u32 reg, u32 offset, void *buf, + int count); u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset); void __ocelot_write_ix(struct ocelot *ocelot, u32 val, u32 reg, u32 offset); void __ocelot_rmw_ix(struct ocelot *ocelot, u32 val, u32 mask, u32 reg, @@ -812,6 +855,9 @@ void ocelot_deinit(struct ocelot *ocelot); void ocelot_init_port(struct ocelot *ocelot, int port); void ocelot_deinit_port(struct ocelot *ocelot, int port); +void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port); +void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int port); + /* DSA callbacks */ void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data); void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data); @@ -829,17 +875,29 @@ int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port, struct switchdev_brport_flags val); void ocelot_port_bridge_flags(struct ocelot *ocelot, int port, struct switchdev_brport_flags val); -void ocelot_port_bridge_join(struct ocelot *ocelot, int port, - struct net_device *bridge); +int ocelot_port_get_default_prio(struct ocelot *ocelot, int port); +int ocelot_port_set_default_prio(struct ocelot *ocelot, int port, u8 prio); +int ocelot_port_get_dscp_prio(struct ocelot *ocelot, int port, u8 dscp); +int ocelot_port_add_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio); +int ocelot_port_del_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio); +int ocelot_port_bridge_join(struct ocelot *ocelot, int port, + struct net_device *bridge, int bridge_num, + struct netlink_ext_ack *extack); void ocelot_port_bridge_leave(struct ocelot *ocelot, int port, struct net_device *bridge); int ocelot_mact_flush(struct ocelot *ocelot, int port); int ocelot_fdb_dump(struct ocelot *ocelot, int port, dsa_fdb_dump_cb_t *cb, void *data); -int ocelot_fdb_add(struct ocelot *ocelot, int port, - const unsigned char *addr, u16 vid); -int ocelot_fdb_del(struct ocelot *ocelot, int port, - const unsigned char *addr, u16 vid); +int ocelot_fdb_add(struct ocelot *ocelot, int port, const unsigned char *addr, + u16 vid, const struct net_device *bridge); +int ocelot_fdb_del(struct ocelot *ocelot, int port, const unsigned char *addr, + u16 vid, const struct net_device *bridge); +int ocelot_lag_fdb_add(struct ocelot *ocelot, struct net_device *bond, + const unsigned char *addr, u16 vid, + const struct net_device *bridge); +int ocelot_lag_fdb_del(struct ocelot *ocelot, struct net_device *bond, + const unsigned char *addr, u16 vid, + const struct net_device *bridge); int ocelot_vlan_prepare(struct ocelot *ocelot, int port, u16 vid, bool pvid, bool untagged, struct netlink_ext_ack *extack); int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid, @@ -856,6 +914,9 @@ int ocelot_get_max_mtu(struct ocelot *ocelot, int port); int ocelot_port_policer_add(struct ocelot *ocelot, int port, struct ocelot_policer *pol); int ocelot_port_policer_del(struct ocelot *ocelot, int port); +int ocelot_port_mirror_add(struct ocelot *ocelot, int from, int to, + bool ingress, struct netlink_ext_ack *extack); +void ocelot_port_mirror_del(struct ocelot *ocelot, int from, bool ingress); int ocelot_cls_flower_replace(struct ocelot *ocelot, int port, struct flow_cls_offload *f, bool ingress); int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port, @@ -863,9 +924,11 @@ int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port, int ocelot_cls_flower_stats(struct ocelot *ocelot, int port, struct flow_cls_offload *f, bool ingress); int ocelot_port_mdb_add(struct ocelot *ocelot, int port, - const struct switchdev_obj_port_mdb *mdb); + const struct switchdev_obj_port_mdb *mdb, + const struct net_device *bridge); int ocelot_port_mdb_del(struct ocelot *ocelot, int port, - const struct switchdev_obj_port_mdb *mdb); + const struct switchdev_obj_port_mdb *mdb, + const struct net_device *bridge); int ocelot_port_lag_join(struct ocelot *ocelot, int port, struct net_device *bond, struct netdev_lag_upper_info *info); diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h index 709cbc198fd2..7b2bf9b1fe69 100644 --- a/include/soc/mscc/ocelot_vcap.h +++ b/include/soc/mscc/ocelot_vcap.h @@ -8,6 +8,20 @@ #include +/* Cookie definitions for private VCAP filters installed by the driver. + * Must be unique per VCAP block. + */ +#define OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port) (port) +#define OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port) (port) +#define OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port) (port) +#define OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port) ((ocelot)->num_phys_ports + (port)) +#define OCELOT_VCAP_IS2_MRP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2) +#define OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 1) +#define OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 2) +#define OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 3) +#define OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 4) +#define OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 5) + /* ================================================================= * VCAP Common * ================================================================= @@ -640,6 +654,7 @@ struct ocelot_vcap_action { enum ocelot_mask_mode mask_mode; unsigned long port_mask; bool police_ena; + bool mirror_ena; struct ocelot_policer pol; u32 pol_ix; }; @@ -666,6 +681,7 @@ struct ocelot_vcap_id { struct ocelot_vcap_filter { struct list_head list; + struct list_head trap_list; enum ocelot_vcap_filter_type type; int block_id; @@ -678,9 +694,11 @@ struct ocelot_vcap_filter { struct ocelot_vcap_action action; struct ocelot_vcap_stats stats; /* For VCAP IS1 and IS2 */ + bool take_ts; unsigned long ingress_port_mask; /* For VCAP ES0 */ struct ocelot_vcap_port ingress_port; + /* For VCAP IS2 mirrors and ES0 */ struct ocelot_vcap_port egress_port; enum ocelot_vcap_bit dmac_mc; diff --git a/include/trace/events/mctp.h b/include/trace/events/mctp.h index 175b057c507f..165cf25f77a7 100644 --- a/include/trace/events/mctp.h +++ b/include/trace/events/mctp.h @@ -15,6 +15,7 @@ enum { MCTP_TRACE_KEY_REPLIED, MCTP_TRACE_KEY_INVALIDATED, MCTP_TRACE_KEY_CLOSED, + MCTP_TRACE_KEY_DROPPED, }; #endif /* __TRACE_MCTP_ENUMS */ @@ -22,6 +23,7 @@ TRACE_DEFINE_ENUM(MCTP_TRACE_KEY_TIMEOUT); TRACE_DEFINE_ENUM(MCTP_TRACE_KEY_REPLIED); TRACE_DEFINE_ENUM(MCTP_TRACE_KEY_INVALIDATED); TRACE_DEFINE_ENUM(MCTP_TRACE_KEY_CLOSED); +TRACE_DEFINE_ENUM(MCTP_TRACE_KEY_DROPPED); TRACE_EVENT(mctp_key_acquire, TP_PROTO(const struct mctp_sk_key *key), @@ -66,7 +68,8 @@ TRACE_EVENT(mctp_key_release, { MCTP_TRACE_KEY_TIMEOUT, "timeout" }, { MCTP_TRACE_KEY_REPLIED, "replied" }, { MCTP_TRACE_KEY_INVALIDATED, "invalidated" }, - { MCTP_TRACE_KEY_CLOSED, "closed" }) + { MCTP_TRACE_KEY_CLOSED, "closed" }, + { MCTP_TRACE_KEY_DROPPED, "dropped" }) ) ); diff --git a/include/trace/events/mptcp.h b/include/trace/events/mptcp.h index 6bf43176f14c..f8e28e686c65 100644 --- a/include/trace/events/mptcp.h +++ b/include/trace/events/mptcp.h @@ -115,6 +115,10 @@ DECLARE_EVENT_CLASS(mptcp_dump_mpext, __entry->csum_reqd) ); +DEFINE_EVENT(mptcp_dump_mpext, mptcp_sendmsg_frag, + TP_PROTO(struct mptcp_ext *mpext), + TP_ARGS(mpext)); + DEFINE_EVENT(mptcp_dump_mpext, get_mapping_status, TP_PROTO(struct mptcp_ext *mpext), TP_ARGS(mpext)); diff --git a/include/trace/events/net.h b/include/trace/events/net.h index 78c448c6ab4c..032b431b987b 100644 --- a/include/trace/events/net.h +++ b/include/trace/events/net.h @@ -260,13 +260,6 @@ DEFINE_EVENT(net_dev_rx_verbose_template, netif_rx_entry, TP_ARGS(skb) ); -DEFINE_EVENT(net_dev_rx_verbose_template, netif_rx_ni_entry, - - TP_PROTO(const struct sk_buff *skb), - - TP_ARGS(skb) -); - DECLARE_EVENT_CLASS(net_dev_rx_exit_template, TP_PROTO(int ret), @@ -312,13 +305,6 @@ DEFINE_EVENT(net_dev_rx_exit_template, netif_rx_exit, TP_ARGS(ret) ); -DEFINE_EVENT(net_dev_rx_exit_template, netif_rx_ni_exit, - - TP_PROTO(int ret), - - TP_ARGS(ret) -); - DEFINE_EVENT(net_dev_rx_exit_template, netif_receive_skb_list_exit, TP_PROTO(int ret), diff --git a/include/trace/events/skb.h b/include/trace/events/skb.h index a8a64b97504d..e1670e1e4934 100644 --- a/include/trace/events/skb.h +++ b/include/trace/events/skb.h @@ -16,6 +16,51 @@ EM(SKB_DROP_REASON_TCP_CSUM, TCP_CSUM) \ EM(SKB_DROP_REASON_SOCKET_FILTER, SOCKET_FILTER) \ EM(SKB_DROP_REASON_UDP_CSUM, UDP_CSUM) \ + EM(SKB_DROP_REASON_NETFILTER_DROP, NETFILTER_DROP) \ + EM(SKB_DROP_REASON_OTHERHOST, OTHERHOST) \ + EM(SKB_DROP_REASON_IP_CSUM, IP_CSUM) \ + EM(SKB_DROP_REASON_IP_INHDR, IP_INHDR) \ + EM(SKB_DROP_REASON_IP_RPFILTER, IP_RPFILTER) \ + EM(SKB_DROP_REASON_UNICAST_IN_L2_MULTICAST, \ + UNICAST_IN_L2_MULTICAST) \ + EM(SKB_DROP_REASON_XFRM_POLICY, XFRM_POLICY) \ + EM(SKB_DROP_REASON_IP_NOPROTO, IP_NOPROTO) \ + EM(SKB_DROP_REASON_SOCKET_RCVBUFF, SOCKET_RCVBUFF) \ + EM(SKB_DROP_REASON_PROTO_MEM, PROTO_MEM) \ + EM(SKB_DROP_REASON_TCP_MD5NOTFOUND, TCP_MD5NOTFOUND) \ + EM(SKB_DROP_REASON_TCP_MD5UNEXPECTED, \ + TCP_MD5UNEXPECTED) \ + EM(SKB_DROP_REASON_TCP_MD5FAILURE, TCP_MD5FAILURE) \ + EM(SKB_DROP_REASON_SOCKET_BACKLOG, SOCKET_BACKLOG) \ + EM(SKB_DROP_REASON_TCP_FLAGS, TCP_FLAGS) \ + EM(SKB_DROP_REASON_TCP_ZEROWINDOW, TCP_ZEROWINDOW) \ + EM(SKB_DROP_REASON_TCP_OLD_DATA, TCP_OLD_DATA) \ + EM(SKB_DROP_REASON_TCP_OVERWINDOW, TCP_OVERWINDOW) \ + EM(SKB_DROP_REASON_TCP_OFOMERGE, TCP_OFOMERGE) \ + EM(SKB_DROP_REASON_IP_OUTNOROUTES, IP_OUTNOROUTES) \ + EM(SKB_DROP_REASON_BPF_CGROUP_EGRESS, \ + BPF_CGROUP_EGRESS) \ + EM(SKB_DROP_REASON_IPV6DISABLED, IPV6DISABLED) \ + EM(SKB_DROP_REASON_NEIGH_CREATEFAIL, NEIGH_CREATEFAIL) \ + EM(SKB_DROP_REASON_NEIGH_FAILED, NEIGH_FAILED) \ + EM(SKB_DROP_REASON_NEIGH_QUEUEFULL, NEIGH_QUEUEFULL) \ + EM(SKB_DROP_REASON_NEIGH_DEAD, NEIGH_DEAD) \ + EM(SKB_DROP_REASON_TC_EGRESS, TC_EGRESS) \ + EM(SKB_DROP_REASON_QDISC_DROP, QDISC_DROP) \ + EM(SKB_DROP_REASON_CPU_BACKLOG, CPU_BACKLOG) \ + EM(SKB_DROP_REASON_XDP, XDP) \ + EM(SKB_DROP_REASON_TC_INGRESS, TC_INGRESS) \ + EM(SKB_DROP_REASON_PTYPE_ABSENT, PTYPE_ABSENT) \ + EM(SKB_DROP_REASON_SKB_CSUM, SKB_CSUM) \ + EM(SKB_DROP_REASON_SKB_GSO_SEG, SKB_GSO_SEG) \ + EM(SKB_DROP_REASON_SKB_UCOPY_FAULT, SKB_UCOPY_FAULT) \ + EM(SKB_DROP_REASON_DEV_HDR, DEV_HDR) \ + EM(SKB_DROP_REASON_DEV_READY, DEV_READY) \ + EM(SKB_DROP_REASON_FULL_RING, FULL_RING) \ + EM(SKB_DROP_REASON_NOMEM, NOMEM) \ + EM(SKB_DROP_REASON_HDR_TRUNC, HDR_TRUNC) \ + EM(SKB_DROP_REASON_TAP_FILTER, TAP_FILTER) \ + EM(SKB_DROP_REASON_TAP_TXFILTER, TAP_TXFILTER) \ EMe(SKB_DROP_REASON_MAX, MAX) #undef EM diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h index c77a1313b3b0..467ca2f28760 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -128,6 +128,8 @@ #define SO_RESERVE_MEM 73 +#define SO_TXREHASH 74 + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__)) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index b0383d371b9a..d14b10b85e51 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -330,6 +330,8 @@ union bpf_iter_link_info { * *ctx_out*, *data_in* and *data_out* must be NULL. * *repeat* must be zero. * + * BPF_PROG_RUN is an alias for BPF_PROG_TEST_RUN. + * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. @@ -995,6 +997,7 @@ enum bpf_attach_type { BPF_SK_REUSEPORT_SELECT, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, BPF_PERF_EVENT, + BPF_TRACE_KPROBE_MULTI, __MAX_BPF_ATTACH_TYPE }; @@ -1009,6 +1012,7 @@ enum bpf_link_type { BPF_LINK_TYPE_NETNS = 5, BPF_LINK_TYPE_XDP = 6, BPF_LINK_TYPE_PERF_EVENT = 7, + BPF_LINK_TYPE_KPROBE_MULTI = 8, MAX_BPF_LINK_TYPE, }; @@ -1111,6 +1115,16 @@ enum bpf_link_type { */ #define BPF_F_SLEEPABLE (1U << 4) +/* If BPF_F_XDP_HAS_FRAGS is used in BPF_PROG_LOAD command, the loaded program + * fully support xdp frags. + */ +#define BPF_F_XDP_HAS_FRAGS (1U << 5) + +/* link_create.kprobe_multi.flags used in LINK_CREATE command for + * BPF_TRACE_KPROBE_MULTI attach type to create return probe. + */ +#define BPF_F_KPROBE_MULTI_RETURN (1U << 0) + /* When BPF ldimm64's insn[0].src_reg != 0 then this can have * the following extensions: * @@ -1225,6 +1239,8 @@ enum { /* If set, run the test on the cpu specified by bpf_attr.test.cpu */ #define BPF_F_TEST_RUN_ON_CPU (1U << 0) +/* If set, XDP frames will be transmitted after processing */ +#define BPF_F_TEST_XDP_LIVE_FRAMES (1U << 1) /* type for BPF_ENABLE_STATS */ enum bpf_stats_type { @@ -1386,6 +1402,7 @@ union bpf_attr { __aligned_u64 ctx_out; __u32 flags; __u32 cpu; + __u32 batch_size; } test; struct { /* anonymous struct used by BPF_*_GET_*_ID */ @@ -1465,6 +1482,13 @@ union bpf_attr { */ __u64 bpf_cookie; } perf_event; + struct { + __u32 flags; + __u32 cnt; + __aligned_u64 syms; + __aligned_u64 addrs; + __aligned_u64 cookies; + } kprobe_multi; }; } link_create; @@ -1775,6 +1799,8 @@ union bpf_attr { * 0 on success, or a negative error in case of failure. * * u64 bpf_get_current_pid_tgid(void) + * Description + * Get the current pid and tgid. * Return * A 64-bit integer containing the current tgid and pid, and * created as such: @@ -1782,6 +1808,8 @@ union bpf_attr { * *current_task*\ **->pid**. * * u64 bpf_get_current_uid_gid(void) + * Description + * Get the current uid and gid. * Return * A 64-bit integer containing the current GID and UID, and * created as such: *current_gid* **<< 32 \|** *current_uid*. @@ -2256,6 +2284,8 @@ union bpf_attr { * The 32-bit hash. * * u64 bpf_get_current_task(void) + * Description + * Get the current task. * Return * A pointer to the current task struct. * @@ -2286,8 +2316,8 @@ union bpf_attr { * Return * The return value depends on the result of the test, and can be: * - * * 0, if current task belongs to the cgroup2. - * * 1, if current task does not belong to the cgroup2. + * * 1, if current task belongs to the cgroup2. + * * 0, if current task does not belong to the cgroup2. * * A negative error code, if an error occurred. * * long bpf_skb_change_tail(struct sk_buff *skb, u32 len, u64 flags) @@ -2369,6 +2399,8 @@ union bpf_attr { * indicate that the hash is outdated and to trigger a * recalculation the next time the kernel tries to access this * hash or when the **bpf_get_hash_recalc**\ () helper is called. + * Return + * void. * * long bpf_get_numa_node_id(void) * Description @@ -2466,6 +2498,8 @@ union bpf_attr { * A 8-byte long unique number or 0 if *sk* is NULL. * * u32 bpf_get_socket_uid(struct sk_buff *skb) + * Description + * Get the owner UID of the socked associated to *skb*. * Return * The owner UID of the socket associated to *skb*. If the socket * is **NULL**, or if it is not a full socket (i.e. if it is a @@ -2975,8 +3009,8 @@ union bpf_attr { * * # sysctl kernel.perf_event_max_stack= * Return - * A non-negative value equal to or less than *size* on success, - * or a negative error in case of failure. + * The non-negative copied *buf* length equal to or less than + * *size* on success, or a negative error in case of failure. * * long bpf_skb_load_bytes_relative(const void *skb, u32 offset, void *to, u32 len, u32 start_header) * Description @@ -3240,6 +3274,9 @@ union bpf_attr { * The id is returned or 0 in case the id could not be retrieved. * * u64 bpf_get_current_cgroup_id(void) + * Description + * Get the current cgroup id based on the cgroup within which + * the current task is running. * Return * A 64-bit integer containing the current cgroup id based * on the cgroup within which the current task is running. @@ -4279,8 +4316,8 @@ union bpf_attr { * * # sysctl kernel.perf_event_max_stack= * Return - * A non-negative value equal to or less than *size* on success, - * or a negative error in case of failure. + * The non-negative copied *buf* length equal to or less than + * *size* on success, or a negative error in case of failure. * * long bpf_load_hdr_opt(struct bpf_sock_ops *skops, void *searchby_res, u32 len, u64 flags) * Description @@ -5018,6 +5055,94 @@ union bpf_attr { * * Return * The number of arguments of the traced function. + * + * int bpf_get_retval(void) + * Description + * Get the syscall's return value that will be returned to userspace. + * + * This helper is currently supported by cgroup programs only. + * Return + * The syscall's return value. + * + * int bpf_set_retval(int retval) + * Description + * Set the syscall's return value that will be returned to userspace. + * + * This helper is currently supported by cgroup programs only. + * Return + * 0 on success, or a negative error in case of failure. + * + * u64 bpf_xdp_get_buff_len(struct xdp_buff *xdp_md) + * Description + * Get the total size of a given xdp buff (linear and paged area) + * Return + * The total size of a given xdp buffer. + * + * long bpf_xdp_load_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len) + * Description + * This helper is provided as an easy way to load data from a + * xdp buffer. It can be used to load *len* bytes from *offset* from + * the frame associated to *xdp_md*, into the buffer pointed by + * *buf*. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_xdp_store_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len) + * Description + * Store *len* bytes from buffer *buf* into the frame + * associated to *xdp_md*, at *offset*. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_copy_from_user_task(void *dst, u32 size, const void *user_ptr, struct task_struct *tsk, u64 flags) + * Description + * Read *size* bytes from user space address *user_ptr* in *tsk*'s + * address space, and stores the data in *dst*. *flags* is not + * used yet and is provided for future extensibility. This helper + * can only be used by sleepable programs. + * Return + * 0 on success, or a negative error in case of failure. On error + * *dst* buffer is zeroed out. + * + * long bpf_skb_set_tstamp(struct sk_buff *skb, u64 tstamp, u32 tstamp_type) + * Description + * Change the __sk_buff->tstamp_type to *tstamp_type* + * and set *tstamp* to the __sk_buff->tstamp together. + * + * If there is no need to change the __sk_buff->tstamp_type, + * the tstamp value can be directly written to __sk_buff->tstamp + * instead. + * + * BPF_SKB_TSTAMP_DELIVERY_MONO is the only tstamp that + * will be kept during bpf_redirect_*(). A non zero + * *tstamp* must be used with the BPF_SKB_TSTAMP_DELIVERY_MONO + * *tstamp_type*. + * + * A BPF_SKB_TSTAMP_UNSPEC *tstamp_type* can only be used + * with a zero *tstamp*. + * + * Only IPv4 and IPv6 skb->protocol are supported. + * + * This function is most useful when it needs to set a + * mono delivery time to __sk_buff->tstamp and then + * bpf_redirect_*() to the egress of an iface. For example, + * changing the (rcv) timestamp in __sk_buff->tstamp at + * ingress to a mono delivery time and then bpf_redirect_*() + * to sch_fq@phy-dev. + * Return + * 0 on success. + * **-EINVAL** for invalid input + * **-EOPNOTSUPP** for unsupported protocol + * + * long bpf_ima_file_hash(struct file *file, void *dst, u32 size) + * Description + * Returns a calculated IMA hash of the *file*. + * If the hash is larger than *size*, then only *size* + * bytes will be copied to *dst* + * Return + * The **hash_algo** is returned on success, + * **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if + * invalid arguments are passed. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -5206,6 +5331,14 @@ union bpf_attr { FN(get_func_arg), \ FN(get_func_ret), \ FN(get_func_arg_cnt), \ + FN(get_retval), \ + FN(set_retval), \ + FN(xdp_get_buff_len), \ + FN(xdp_load_bytes), \ + FN(xdp_store_bytes), \ + FN(copy_from_user_task), \ + FN(skb_set_tstamp), \ + FN(ima_file_hash), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper @@ -5395,6 +5528,15 @@ union { \ __u64 :64; \ } __attribute__((aligned(8))) +enum { + BPF_SKB_TSTAMP_UNSPEC, + BPF_SKB_TSTAMP_DELIVERY_MONO, /* tstamp has mono delivery time */ + /* For any BPF_SKB_TSTAMP_* that the bpf prog cannot handle, + * the bpf prog should handle it like BPF_SKB_TSTAMP_UNSPEC + * and try to deduce it by ingress, egress or skb->sk->sk_clockid. + */ +}; + /* user accessible mirror of in-kernel sk_buff. * new fields can only be added to the end of this structure */ @@ -5435,7 +5577,8 @@ struct __sk_buff { __u32 gso_segs; __bpf_md_ptr(struct bpf_sock *, sk); __u32 gso_size; - __u32 :32; /* Padding, future use. */ + __u8 tstamp_type; + __u32 :24; /* Padding, future use. */ __u64 hwtstamp; }; @@ -5500,7 +5643,8 @@ struct bpf_sock { __u32 src_ip4; __u32 src_ip6[4]; __u32 src_port; /* host byte order */ - __u32 dst_port; /* network byte order */ + __be16 dst_port; /* network byte order */ + __u16 :16; /* zero padding */ __u32 dst_ip4; __u32 dst_ip6[4]; __u32 state; @@ -6378,7 +6522,8 @@ struct bpf_sk_lookup { __u32 protocol; /* IP protocol (IPPROTO_TCP, IPPROTO_UDP) */ __u32 remote_ip4; /* Network byte order */ __u32 remote_ip6[4]; /* Network byte order */ - __u32 remote_port; /* Network byte order */ + __be16 remote_port; /* Network byte order */ + __u16 :16; /* Zero padding */ __u32 local_ip4; /* Network byte order */ __u32 local_ip6[4]; /* Network byte order */ __u32 local_port; /* Host byte order */ diff --git a/include/uapi/linux/can/isotp.h b/include/uapi/linux/can/isotp.h index c55935b64ccc..590f8aea2b6d 100644 --- a/include/uapi/linux/can/isotp.h +++ b/include/uapi/linux/can/isotp.h @@ -137,20 +137,16 @@ struct can_isotp_ll_options { #define CAN_ISOTP_WAIT_TX_DONE 0x400 /* wait for tx completion */ #define CAN_ISOTP_SF_BROADCAST 0x800 /* 1-to-N functional addressing */ -/* default values */ +/* protocol machine default values */ #define CAN_ISOTP_DEFAULT_FLAGS 0 #define CAN_ISOTP_DEFAULT_EXT_ADDRESS 0x00 #define CAN_ISOTP_DEFAULT_PAD_CONTENT 0xCC /* prevent bit-stuffing */ -#define CAN_ISOTP_DEFAULT_FRAME_TXTIME 0 +#define CAN_ISOTP_DEFAULT_FRAME_TXTIME 50000 /* 50 micro seconds */ #define CAN_ISOTP_DEFAULT_RECV_BS 0 #define CAN_ISOTP_DEFAULT_RECV_STMIN 0x00 #define CAN_ISOTP_DEFAULT_RECV_WFTMAX 0 -#define CAN_ISOTP_DEFAULT_LL_MTU CAN_MTU -#define CAN_ISOTP_DEFAULT_LL_TX_DL CAN_MAX_DLEN -#define CAN_ISOTP_DEFAULT_LL_TX_FLAGS 0 - /* * Remark on CAN_ISOTP_DEFAULT_RECV_* values: * @@ -162,4 +158,24 @@ struct can_isotp_ll_options { * consistency and copied directly into the flow control (FC) frame. */ +/* link layer default values => make use of Classical CAN frames */ + +#define CAN_ISOTP_DEFAULT_LL_MTU CAN_MTU +#define CAN_ISOTP_DEFAULT_LL_TX_DL CAN_MAX_DLEN +#define CAN_ISOTP_DEFAULT_LL_TX_FLAGS 0 + +/* + * The CAN_ISOTP_DEFAULT_FRAME_TXTIME has become a non-zero value as + * it only makes sense for isotp implementation tests to run without + * a N_As value. As user space applications usually do not set the + * frame_txtime element of struct can_isotp_options the new in-kernel + * default is very likely overwritten with zero when the sockopt() + * CAN_ISOTP_OPTS is invoked. + * To make sure that a N_As value of zero is only set intentional the + * value '0' is now interpreted as 'do not change the current value'. + * When a frame_txtime of zero is required for testing purposes this + * CAN_ISOTP_FRAME_TXTIME_ZERO u32 value has to be set in frame_txtime. + */ +#define CAN_ISOTP_FRAME_TXTIME_ZERO 0xFFFFFFFF + #endif /* !_UAPI_CAN_ISOTP_H */ diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index cca6e474a085..979850221b8d 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -318,6 +318,12 @@ enum { /* RINGS */ +enum { + ETHTOOL_TCP_DATA_SPLIT_UNKNOWN = 0, + ETHTOOL_TCP_DATA_SPLIT_DISABLED, + ETHTOOL_TCP_DATA_SPLIT_ENABLED, +}; + enum { ETHTOOL_A_RINGS_UNSPEC, ETHTOOL_A_RINGS_HEADER, /* nest - _A_HEADER_* */ @@ -330,6 +336,8 @@ enum { ETHTOOL_A_RINGS_RX_JUMBO, /* u32 */ ETHTOOL_A_RINGS_TX, /* u32 */ ETHTOOL_A_RINGS_RX_BUF_LEN, /* u32 */ + ETHTOOL_A_RINGS_TCP_DATA_SPLIT, /* u8 */ + ETHTOOL_A_RINGS_CQE_SIZE, /* u32 */ /* add new constants above here */ __ETHTOOL_A_RINGS_CNT, diff --git a/include/uapi/linux/gtp.h b/include/uapi/linux/gtp.h index 79f9191bbb24..2f61298a7b77 100644 --- a/include/uapi/linux/gtp.h +++ b/include/uapi/linux/gtp.h @@ -8,6 +8,7 @@ enum gtp_genl_cmds { GTP_CMD_NEWPDP, GTP_CMD_DELPDP, GTP_CMD_GETPDP, + GTP_CMD_ECHOREQ, GTP_CMD_MAX, }; diff --git a/include/uapi/linux/if_addr.h b/include/uapi/linux/if_addr.h index dfcf3ce0097f..1c392dd95a5e 100644 --- a/include/uapi/linux/if_addr.h +++ b/include/uapi/linux/if_addr.h @@ -33,8 +33,9 @@ enum { IFA_CACHEINFO, IFA_MULTICAST, IFA_FLAGS, - IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */ + IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */ IFA_TARGET_NETNSID, + IFA_PROTO, /* u8, address protocol */ __IFA_MAX, }; @@ -69,4 +70,10 @@ struct ifa_cacheinfo { #define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) #endif +/* ifa_proto */ +#define IFAPROT_UNSPEC 0 +#define IFAPROT_KERNEL_LO 1 /* loopback */ +#define IFAPROT_KERNEL_RA 2 /* set by kernel from router announcement */ +#define IFAPROT_KERNEL_LL 3 /* link-local set by kernel */ + #endif diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index 2711c3522010..a86a7e7b811f 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -122,6 +122,7 @@ enum { IFLA_BRIDGE_VLAN_TUNNEL_INFO, IFLA_BRIDGE_MRP, IFLA_BRIDGE_CFM, + IFLA_BRIDGE_MST, __IFLA_BRIDGE_MAX, }; #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) @@ -453,6 +454,21 @@ enum { #define IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX (__IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX - 1) +enum { + IFLA_BRIDGE_MST_UNSPEC, + IFLA_BRIDGE_MST_ENTRY, + __IFLA_BRIDGE_MST_MAX, +}; +#define IFLA_BRIDGE_MST_MAX (__IFLA_BRIDGE_MST_MAX - 1) + +enum { + IFLA_BRIDGE_MST_ENTRY_UNSPEC, + IFLA_BRIDGE_MST_ENTRY_MSTI, + IFLA_BRIDGE_MST_ENTRY_STATE, + __IFLA_BRIDGE_MST_ENTRY_MAX, +}; +#define IFLA_BRIDGE_MST_ENTRY_MAX (__IFLA_BRIDGE_MST_ENTRY_MAX - 1) + struct bridge_stp_xstats { __u64 transition_blk; __u64 transition_fwd; @@ -564,6 +580,7 @@ enum { BRIDGE_VLANDB_GOPTS_MCAST_QUERIER, BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS, BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE, + BRIDGE_VLANDB_GOPTS_MSTI, __BRIDGE_VLANDB_GOPTS_MAX }; #define BRIDGE_VLANDB_GOPTS_MAX (__BRIDGE_VLANDB_GOPTS_MAX - 1) @@ -759,6 +776,7 @@ struct br_mcast_stats { enum br_boolopt_id { BR_BOOLOPT_NO_LL_LEARN, BR_BOOLOPT_MCAST_VLAN_SNOOPING, + BR_BOOLOPT_MST_ENABLE, BR_BOOLOPT_MAX }; diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index c0c2f3ed5729..1d0bccc3fa54 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -86,8 +86,10 @@ * over Ethernet */ #define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#define ETH_P_PROFINET 0x8892 /* PROFINET */ #define ETH_P_REALTEK 0x8899 /* Multiple proprietary protocols */ #define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ +#define ETH_P_ETHERCAT 0x88A4 /* EtherCAT */ #define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ #define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */ #define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */ diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 6218f93f5c1a..cc284c048e69 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -245,6 +245,21 @@ struct rtnl_link_stats64 { __u64 rx_nohandler; }; +/* Subset of link stats useful for in-HW collection. Meaning of the fields is as + * for struct rtnl_link_stats64. + */ +struct rtnl_hw_stats64 { + __u64 rx_packets; + __u64 tx_packets; + __u64 rx_bytes; + __u64 tx_bytes; + __u64 rx_errors; + __u64 tx_errors; + __u64 rx_dropped; + __u64 tx_dropped; + __u64 multicast; +}; + /* The struct should be in sync with struct ifmap */ struct rtnl_link_ifmap { __u64 mem_start; @@ -537,6 +552,7 @@ enum { IFLA_BRPORT_MRP_IN_OPEN, IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT, IFLA_BRPORT_MCAST_EHT_HOSTS_CNT, + IFLA_BRPORT_LOCKED, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) @@ -712,7 +728,55 @@ enum ipvlan_mode { #define IPVLAN_F_PRIVATE 0x01 #define IPVLAN_F_VEPA 0x02 +/* Tunnel RTM header */ +struct tunnel_msg { + __u8 family; + __u8 flags; + __u16 reserved2; + __u32 ifindex; +}; + /* VXLAN section */ + +/* include statistics in the dump */ +#define TUNNEL_MSG_FLAG_STATS 0x01 + +#define TUNNEL_MSG_VALID_USER_FLAGS TUNNEL_MSG_FLAG_STATS + +/* Embedded inside VXLAN_VNIFILTER_ENTRY_STATS */ +enum { + VNIFILTER_ENTRY_STATS_UNSPEC, + VNIFILTER_ENTRY_STATS_RX_BYTES, + VNIFILTER_ENTRY_STATS_RX_PKTS, + VNIFILTER_ENTRY_STATS_RX_DROPS, + VNIFILTER_ENTRY_STATS_RX_ERRORS, + VNIFILTER_ENTRY_STATS_TX_BYTES, + VNIFILTER_ENTRY_STATS_TX_PKTS, + VNIFILTER_ENTRY_STATS_TX_DROPS, + VNIFILTER_ENTRY_STATS_TX_ERRORS, + VNIFILTER_ENTRY_STATS_PAD, + __VNIFILTER_ENTRY_STATS_MAX +}; +#define VNIFILTER_ENTRY_STATS_MAX (__VNIFILTER_ENTRY_STATS_MAX - 1) + +enum { + VXLAN_VNIFILTER_ENTRY_UNSPEC, + VXLAN_VNIFILTER_ENTRY_START, + VXLAN_VNIFILTER_ENTRY_END, + VXLAN_VNIFILTER_ENTRY_GROUP, + VXLAN_VNIFILTER_ENTRY_GROUP6, + VXLAN_VNIFILTER_ENTRY_STATS, + __VXLAN_VNIFILTER_ENTRY_MAX +}; +#define VXLAN_VNIFILTER_ENTRY_MAX (__VXLAN_VNIFILTER_ENTRY_MAX - 1) + +enum { + VXLAN_VNIFILTER_UNSPEC, + VXLAN_VNIFILTER_ENTRY, + __VXLAN_VNIFILTER_MAX +}; +#define VXLAN_VNIFILTER_MAX (__VXLAN_VNIFILTER_MAX - 1) + enum { IFLA_VXLAN_UNSPEC, IFLA_VXLAN_ID, @@ -744,6 +808,7 @@ enum { IFLA_VXLAN_GPE, IFLA_VXLAN_TTL_INHERIT, IFLA_VXLAN_DF, + IFLA_VXLAN_VNIFILTER, /* only applicable with COLLECT_METADATA mode */ __IFLA_VXLAN_MAX }; #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) @@ -777,6 +842,7 @@ enum { IFLA_GENEVE_LABEL, IFLA_GENEVE_TTL_INHERIT, IFLA_GENEVE_DF, + IFLA_GENEVE_INNER_PROTO_INHERIT, __IFLA_GENEVE_MAX }; #define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1) @@ -822,6 +888,8 @@ enum { IFLA_GTP_FD1, IFLA_GTP_PDP_HASHSIZE, IFLA_GTP_ROLE, + IFLA_GTP_CREATE_SOCKETS, + IFLA_GTP_RESTART_COUNT, __IFLA_GTP_MAX, }; #define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1) @@ -860,6 +928,7 @@ enum { IFLA_BOND_PEER_NOTIF_DELAY, IFLA_BOND_AD_LACP_ACTIVE, IFLA_BOND_MISSED_MAX, + IFLA_BOND_NS_IP6_TARGET, __IFLA_BOND_MAX, }; @@ -1156,6 +1225,17 @@ enum { #define IFLA_STATS_FILTER_BIT(ATTR) (1 << (ATTR - 1)) +enum { + IFLA_STATS_GETSET_UNSPEC, + IFLA_STATS_GET_FILTERS, /* Nest of IFLA_STATS_LINK_xxx, each a u32 with + * a filter mask for the corresponding group. + */ + IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS, /* 0 or 1 as u8 */ + __IFLA_STATS_GETSET_MAX, +}; + +#define IFLA_STATS_GETSET_MAX (__IFLA_STATS_GETSET_MAX - 1) + /* These are embedded into IFLA_STATS_LINK_XSTATS: * [IFLA_STATS_LINK_XSTATS] * -> [LINK_XSTATS_TYPE_xxx] @@ -1173,10 +1253,21 @@ enum { enum { IFLA_OFFLOAD_XSTATS_UNSPEC, IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */ + IFLA_OFFLOAD_XSTATS_HW_S_INFO, /* HW stats info. A nest */ + IFLA_OFFLOAD_XSTATS_L3_STATS, /* struct rtnl_hw_stats64 */ __IFLA_OFFLOAD_XSTATS_MAX }; #define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1) +enum { + IFLA_OFFLOAD_XSTATS_HW_S_INFO_UNSPEC, + IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST, /* u8 */ + IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED, /* u8 */ + __IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX, +}; +#define IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX \ + (__IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX - 1) + /* XDP section */ #define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0) diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h index 7d9105533c7b..102119628ff5 100644 --- a/include/uapi/linux/if_tunnel.h +++ b/include/uapi/linux/if_tunnel.h @@ -176,8 +176,10 @@ enum { #define TUNNEL_VXLAN_OPT __cpu_to_be16(0x1000) #define TUNNEL_NOCACHE __cpu_to_be16(0x2000) #define TUNNEL_ERSPAN_OPT __cpu_to_be16(0x4000) +#define TUNNEL_GTP_OPT __cpu_to_be16(0x8000) #define TUNNEL_OPTIONS_PRESENT \ - (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT) + (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT | \ + TUNNEL_GTP_OPT) #endif /* _UAPI_IF_TUNNEL_H_ */ diff --git a/include/uapi/linux/ioam6_iptunnel.h b/include/uapi/linux/ioam6_iptunnel.h index 829ffdfcacca..38f6a8fdfd34 100644 --- a/include/uapi/linux/ioam6_iptunnel.h +++ b/include/uapi/linux/ioam6_iptunnel.h @@ -41,6 +41,15 @@ enum { /* IOAM Trace Header */ IOAM6_IPTUNNEL_TRACE, /* struct ioam6_trace_hdr */ + /* Insertion frequency: + * "k over n" packets (0 < k <= n) + * [0.0001% ... 100%] + */ +#define IOAM6_IPTUNNEL_FREQ_MIN 1 +#define IOAM6_IPTUNNEL_FREQ_MAX 1000000 + IOAM6_IPTUNNEL_FREQ_K, /* u32 */ + IOAM6_IPTUNNEL_FREQ_N, /* u32 */ + __IOAM6_IPTUNNEL_MAX, }; diff --git a/include/uapi/linux/mctp.h b/include/uapi/linux/mctp.h index 07b0318716fc..154ab56651f1 100644 --- a/include/uapi/linux/mctp.h +++ b/include/uapi/linux/mctp.h @@ -44,7 +44,25 @@ struct sockaddr_mctp_ext { #define MCTP_TAG_MASK 0x07 #define MCTP_TAG_OWNER 0x08 +#define MCTP_TAG_PREALLOC 0x10 #define MCTP_OPT_ADDR_EXT 1 +#define SIOCMCTPALLOCTAG (SIOCPROTOPRIVATE + 0) +#define SIOCMCTPDROPTAG (SIOCPROTOPRIVATE + 1) + +struct mctp_ioc_tag_ctl { + mctp_eid_t peer_addr; + + /* For SIOCMCTPALLOCTAG: must be passed as zero, kernel will + * populate with the allocated tag value. Returned tag value will + * always have TO and PREALLOC set. + * + * For SIOCMCTPDROPTAG: userspace provides tag value to drop, from + * a prior SIOCMCTPALLOCTAG call (and so must have TO and PREALLOC set). + */ + __u8 tag; + __u16 flags; +}; + #endif /* __UAPI_MCTP_H */ diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h index f106a3941cdf..9690efedb5fa 100644 --- a/include/uapi/linux/mptcp.h +++ b/include/uapi/linux/mptcp.h @@ -81,6 +81,7 @@ enum { #define MPTCP_PM_ADDR_FLAG_SUBFLOW (1 << 1) #define MPTCP_PM_ADDR_FLAG_BACKUP (1 << 2) #define MPTCP_PM_ADDR_FLAG_FULLMESH (1 << 3) +#define MPTCP_PM_ADDR_FLAG_IMPLICIT (1 << 4) enum { MPTCP_PM_CMD_UNSPEC, diff --git a/include/uapi/linux/mroute6.h b/include/uapi/linux/mroute6.h index a1fd6173e2db..1d90c21a6251 100644 --- a/include/uapi/linux/mroute6.h +++ b/include/uapi/linux/mroute6.h @@ -134,6 +134,7 @@ struct mrt6msg { #define MRT6MSG_NOCACHE 1 #define MRT6MSG_WRONGMIF 2 #define MRT6MSG_WHOLEPKT 3 /* used for use level encap */ +#define MRT6MSG_WRMIFWHOLE 4 /* For PIM Register and assert processing */ __u8 im6_mbz; /* must be zero */ __u8 im6_msgtype; /* what type of message */ __u16 im6_mif; /* mif rec'd on */ diff --git a/include/uapi/linux/net_dropmon.h b/include/uapi/linux/net_dropmon.h index 66048cc5d7b3..1bbea8f0681e 100644 --- a/include/uapi/linux/net_dropmon.h +++ b/include/uapi/linux/net_dropmon.h @@ -93,6 +93,7 @@ enum net_dm_attr { NET_DM_ATTR_SW_DROPS, /* flag */ NET_DM_ATTR_HW_DROPS, /* flag */ NET_DM_ATTR_FLOW_ACTION_COOKIE, /* binary */ + NET_DM_ATTR_REASON, /* string */ __NET_DM_ATTR_MAX, NET_DM_ATTR_MAX = __NET_DM_ATTR_MAX - 1 diff --git a/include/uapi/linux/netfilter/nfnetlink_queue.h b/include/uapi/linux/netfilter/nfnetlink_queue.h index aed90c4df0c8..ef7c97f21a15 100644 --- a/include/uapi/linux/netfilter/nfnetlink_queue.h +++ b/include/uapi/linux/netfilter/nfnetlink_queue.h @@ -61,6 +61,7 @@ enum nfqnl_attr_type { NFQA_SECCTX, /* security context string */ NFQA_VLAN, /* nested attribute: packet vlan info */ NFQA_L2HDR, /* full L2 header */ + NFQA_PRIORITY, /* skb->priority */ __NFQA_MAX }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 195a238a322e..0568a79097b8 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -11,7 +11,7 @@ * Copyright 2008 Jouni Malinen * Copyright 2008 Colin McCabe * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2022 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -2659,6 +2659,10 @@ enum nl80211_commands { * enumerated in &enum nl80211_ap_settings_flags. This attribute shall be * used with %NL80211_CMD_START_AP request. * + * @NL80211_ATTR_EHT_CAPABILITY: EHT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION). Can be set + * only if %NL80211_STA_FLAG_WME is set. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3169,6 +3173,8 @@ enum nl80211_attrs { NL80211_ATTR_AP_SETTINGS_FLAGS, + NL80211_ATTR_EHT_CAPABILITY, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3224,6 +3230,8 @@ enum nl80211_attrs { #define NL80211_HE_MAX_CAPABILITY_LEN 54 #define NL80211_MAX_NR_CIPHER_SUITES 5 #define NL80211_MAX_NR_AKM_SUITES 2 +#define NL80211_EHT_MIN_CAPABILITY_LEN 13 +#define NL80211_EHT_MAX_CAPABILITY_LEN 51 #define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10 @@ -3251,7 +3259,7 @@ enum nl80211_attrs { * and therefore can't be created in the normal ways, use the * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE * commands to create and destroy one - * @NL80211_IF_TYPE_OCB: Outside Context of a BSS + * @NL80211_IFTYPE_OCB: Outside Context of a BSS * This mode corresponds to the MIB variable dot11OCBActivated=true * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev) * @NL80211_IFTYPE_MAX: highest interface type number currently defined @@ -3392,6 +3400,56 @@ enum nl80211_he_ru_alloc { NL80211_RATE_INFO_HE_RU_ALLOC_2x996, }; +/** + * enum nl80211_eht_gi - EHT guard interval + * @NL80211_RATE_INFO_EHT_GI_0_8: 0.8 usec + * @NL80211_RATE_INFO_EHT_GI_1_6: 1.6 usec + * @NL80211_RATE_INFO_EHT_GI_3_2: 3.2 usec + */ +enum nl80211_eht_gi { + NL80211_RATE_INFO_EHT_GI_0_8, + NL80211_RATE_INFO_EHT_GI_1_6, + NL80211_RATE_INFO_EHT_GI_3_2, +}; + +/** + * enum nl80211_eht_ru_alloc - EHT RU allocation values + * @NL80211_RATE_INFO_EHT_RU_ALLOC_26: 26-tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_52: 52-tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_52P26: 52+26-tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_106: 106-tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_106P26: 106+26 tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_242: 242-tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_484: 484-tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_484P242: 484+242 tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_996: 996-tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_996P484: 996+484 tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242: 996+484+242 tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_2x996: 2x996-tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484: 2x996+484 tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_3x996: 3x996-tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484: 3x996+484 tone RU allocation + * @NL80211_RATE_INFO_EHT_RU_ALLOC_4x996: 4x996-tone RU allocation + */ +enum nl80211_eht_ru_alloc { + NL80211_RATE_INFO_EHT_RU_ALLOC_26, + NL80211_RATE_INFO_EHT_RU_ALLOC_52, + NL80211_RATE_INFO_EHT_RU_ALLOC_52P26, + NL80211_RATE_INFO_EHT_RU_ALLOC_106, + NL80211_RATE_INFO_EHT_RU_ALLOC_106P26, + NL80211_RATE_INFO_EHT_RU_ALLOC_242, + NL80211_RATE_INFO_EHT_RU_ALLOC_484, + NL80211_RATE_INFO_EHT_RU_ALLOC_484P242, + NL80211_RATE_INFO_EHT_RU_ALLOC_996, + NL80211_RATE_INFO_EHT_RU_ALLOC_996P484, + NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242, + NL80211_RATE_INFO_EHT_RU_ALLOC_2x996, + NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484, + NL80211_RATE_INFO_EHT_RU_ALLOC_3x996, + NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484, + NL80211_RATE_INFO_EHT_RU_ALLOC_4x996, +}; + /** * enum nl80211_rate_info - bitrate information * @@ -3431,6 +3489,13 @@ enum nl80211_he_ru_alloc { * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1) * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then * non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc) + * @NL80211_RATE_INFO_320_MHZ_WIDTH: 320 MHz bitrate + * @NL80211_RATE_INFO_EHT_MCS: EHT MCS index (u8, 0-15) + * @NL80211_RATE_INFO_EHT_NSS: EHT NSS value (u8, 1-8) + * @NL80211_RATE_INFO_EHT_GI: EHT guard interval identifier + * (u8, see &enum nl80211_eht_gi) + * @NL80211_RATE_INFO_EHT_RU_ALLOC: EHT RU allocation, if not present then + * non-OFDMA was used (u8, see &enum nl80211_eht_ru_alloc) * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ enum nl80211_rate_info { @@ -3452,6 +3517,11 @@ enum nl80211_rate_info { NL80211_RATE_INFO_HE_GI, NL80211_RATE_INFO_HE_DCM, NL80211_RATE_INFO_HE_RU_ALLOC, + NL80211_RATE_INFO_320_MHZ_WIDTH, + NL80211_RATE_INFO_EHT_MCS, + NL80211_RATE_INFO_EHT_NSS, + NL80211_RATE_INFO_EHT_GI, + NL80211_RATE_INFO_EHT_RU_ALLOC, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, @@ -3766,6 +3836,14 @@ enum nl80211_mpath_info { * given for all 6 GHz band channels * @NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: vendor element capabilities that are * advertised on this band/for this iftype (binary) + * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC: EHT MAC capabilities as in EHT + * capabilities element + * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY: EHT PHY capabilities as in EHT + * capabilities element + * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET: EHT supported NSS/MCS as in EHT + * capabilities element + * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE: EHT PPE thresholds information as + * defined in EHT capabilities element * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band attribute currently defined */ @@ -3779,6 +3857,10 @@ enum nl80211_band_iftype_attr { NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE, NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA, NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS, + NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC, + NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY, + NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET, + NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE, /* keep last */ __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST, @@ -3923,6 +4005,10 @@ enum nl80211_wmm_rule { * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_16MHZ: 16 MHz operation is allowed * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_320MHZ: any 320 MHz channel using this channel + * as the primary or any of the secondary channels isn't possible + * @NL80211_FREQUENCY_ATTR_NO_EHT: EHT operation is not allowed on this channel + * in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -3959,6 +4045,8 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_4MHZ, NL80211_FREQUENCY_ATTR_8MHZ, NL80211_FREQUENCY_ATTR_16MHZ, + NL80211_FREQUENCY_ATTR_NO_320MHZ, + NL80211_FREQUENCY_ATTR_NO_EHT, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -4157,6 +4245,7 @@ enum nl80211_sched_scan_match_attr { * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed * @NL80211_RRF_NO_HE: HE operation not allowed + * @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed */ enum nl80211_reg_rule_flags { NL80211_RRF_NO_OFDM = 1<<0, @@ -4175,6 +4264,7 @@ enum nl80211_reg_rule_flags { NL80211_RRF_NO_80MHZ = 1<<15, NL80211_RRF_NO_160MHZ = 1<<16, NL80211_RRF_NO_HE = 1<<17, + NL80211_RRF_NO_320MHZ = 1<<18, }; #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR @@ -4672,6 +4762,8 @@ enum nl80211_key_mode { * @NL80211_CHAN_WIDTH_4: 4 MHz OFDM channel * @NL80211_CHAN_WIDTH_8: 8 MHz OFDM channel * @NL80211_CHAN_WIDTH_16: 16 MHz OFDM channel + * @NL80211_CHAN_WIDTH_320: 320 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well */ enum nl80211_chan_width { NL80211_CHAN_WIDTH_20_NOHT, @@ -4687,6 +4779,7 @@ enum nl80211_chan_width { NL80211_CHAN_WIDTH_4, NL80211_CHAN_WIDTH_8, NL80211_CHAN_WIDTH_16, + NL80211_CHAN_WIDTH_320, }; /** diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 150bcff49b1c..ce3e1738d427 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -352,9 +352,20 @@ enum ovs_key_attr { OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6, /* struct ovs_key_ct_tuple_ipv6 */ OVS_KEY_ATTR_NSH, /* Nested set of ovs_nsh_key_* */ -#ifdef __KERNEL__ - OVS_KEY_ATTR_TUNNEL_INFO, /* struct ip_tunnel_info */ -#endif + /* User space decided to squat on types 29 and 30. They are defined + * below, but should not be sent to the kernel. + * + * WARNING: No new types should be added unless they are defined + * for both kernel and user space (no 'ifdef's). It's hard + * to keep compatibility otherwise. + */ + OVS_KEY_ATTR_PACKET_TYPE, /* be32 packet type */ + OVS_KEY_ATTR_ND_EXTENSIONS, /* IPv6 Neighbor Discovery extensions */ + + OVS_KEY_ATTR_TUNNEL_INFO, /* struct ip_tunnel_info. + * For in-kernel use only. + */ + OVS_KEY_ATTR_IPV6_EXTHDRS, /* struct ovs_key_ipv6_exthdr */ __OVS_KEY_ATTR_MAX }; @@ -430,6 +441,11 @@ struct ovs_key_ipv6 { __u8 ipv6_frag; /* One of OVS_FRAG_TYPE_*. */ }; +/* separate structure to support backward compatibility with older user space */ +struct ovs_key_ipv6_exthdrs { + __u16 hdrs; +}; + struct ovs_key_tcp { __be16 tcp_src; __be16 tcp_dst; diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index ee38b35c3f57..404f97fb239c 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -616,6 +616,10 @@ enum { * TCA_FLOWER_KEY_ENC_OPT_ERSPAN_ * attributes */ + TCA_FLOWER_KEY_ENC_OPTS_GTP, /* Nested + * TCA_FLOWER_KEY_ENC_OPT_GTP_ + * attributes + */ __TCA_FLOWER_KEY_ENC_OPTS_MAX, }; @@ -654,6 +658,17 @@ enum { #define TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX \ (__TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX - 1) +enum { + TCA_FLOWER_KEY_ENC_OPT_GTP_UNSPEC, + TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE, /* u8 */ + TCA_FLOWER_KEY_ENC_OPT_GTP_QFI, /* u8 */ + + __TCA_FLOWER_KEY_ENC_OPT_GTP_MAX, +}; + +#define TCA_FLOWER_KEY_ENC_OPT_GTP_MAX \ + (__TCA_FLOWER_KEY_ENC_OPT_GTP_MAX - 1) + enum { TCA_FLOWER_KEY_MPLS_OPTS_UNSPEC, TCA_FLOWER_KEY_MPLS_OPTS_LSE, diff --git a/include/uapi/linux/rfkill.h b/include/uapi/linux/rfkill.h index 9b77cfc42efa..283c5a7b3f2c 100644 --- a/include/uapi/linux/rfkill.h +++ b/include/uapi/linux/rfkill.h @@ -159,8 +159,16 @@ struct rfkill_event_ext { * old behaviour for all userspace, unless it explicitly opts in to the * rules outlined here by using the new &struct rfkill_event_ext. * - * Userspace using &struct rfkill_event_ext must adhere to the following - * rules + * Additionally, some other userspace (bluez, g-s-d) was reading with a + * large size but as streaming reads rather than message-based, or with + * too strict checks for the returned size. So eventually, we completely + * reverted this, and extended messages need to be opted in to by using + * an ioctl: + * + * ioctl(fd, RFKILL_IOCTL_MAX_SIZE, sizeof(struct rfkill_event_ext)); + * + * Userspace using &struct rfkill_event_ext and the ioctl must adhere to + * the following rules: * * 1. accept short writes, optionally using them to detect that it's * running on an older kernel; @@ -175,6 +183,8 @@ struct rfkill_event_ext { #define RFKILL_IOC_MAGIC 'R' #define RFKILL_IOC_NOINPUT 1 #define RFKILL_IOCTL_NOINPUT _IO(RFKILL_IOC_MAGIC, RFKILL_IOC_NOINPUT) +#define RFKILL_IOC_MAX_SIZE 2 +#define RFKILL_IOCTL_MAX_SIZE _IOW(RFKILL_IOC_MAGIC, RFKILL_IOC_EXT_SIZE, __u32) /* and that's all userspace gets */ diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 93d934cc4613..83849a37db5b 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -146,6 +146,8 @@ enum { #define RTM_NEWSTATS RTM_NEWSTATS RTM_GETSTATS = 94, #define RTM_GETSTATS RTM_GETSTATS + RTM_SETSTATS, +#define RTM_SETSTATS RTM_SETSTATS RTM_NEWCACHEREPORT = 96, #define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT @@ -185,6 +187,13 @@ enum { RTM_GETNEXTHOPBUCKET, #define RTM_GETNEXTHOPBUCKET RTM_GETNEXTHOPBUCKET + RTM_NEWTUNNEL = 120, +#define RTM_NEWTUNNEL RTM_NEWTUNNEL + RTM_DELTUNNEL, +#define RTM_DELTUNNEL RTM_DELTUNNEL + RTM_GETTUNNEL, +#define RTM_GETTUNNEL RTM_GETTUNNEL + __RTM_MAX, #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) }; @@ -756,6 +765,10 @@ enum rtnetlink_groups { #define RTNLGRP_BRVLAN RTNLGRP_BRVLAN RTNLGRP_MCTP_IFADDR, #define RTNLGRP_MCTP_IFADDR RTNLGRP_MCTP_IFADDR + RTNLGRP_TUNNEL, +#define RTNLGRP_TUNNEL RTNLGRP_TUNNEL + RTNLGRP_STATS, +#define RTNLGRP_STATS RTNLGRP_STATS __RTNLGRP_MAX }; #define RTNLGRP_MAX (__RTNLGRP_MAX - 1) @@ -804,6 +817,7 @@ enum { #define RTEXT_FILTER_MRP (1 << 4) #define RTEXT_FILTER_CFM_CONFIG (1 << 5) #define RTEXT_FILTER_CFM_STATUS (1 << 6) +#define RTEXT_FILTER_MST (1 << 7) /* End of information exported to user level */ diff --git a/include/uapi/linux/smc.h b/include/uapi/linux/smc.h index 6c2874fd2c00..693f549f6966 100644 --- a/include/uapi/linux/smc.h +++ b/include/uapi/linux/smc.h @@ -59,6 +59,9 @@ enum { SMC_NETLINK_DUMP_SEID, SMC_NETLINK_ENABLE_SEID, SMC_NETLINK_DISABLE_SEID, + SMC_NETLINK_DUMP_HS_LIMITATION, + SMC_NETLINK_ENABLE_HS_LIMITATION, + SMC_NETLINK_DISABLE_HS_LIMITATION, }; /* SMC_GENL_FAMILY top level attributes */ @@ -284,4 +287,16 @@ enum { __SMC_NLA_SEID_TABLE_MAX, SMC_NLA_SEID_TABLE_MAX = __SMC_NLA_SEID_TABLE_MAX - 1 }; + +/* SMC_NETLINK_HS_LIMITATION attributes */ +enum { + SMC_NLA_HS_LIMITATION_UNSPEC, + SMC_NLA_HS_LIMITATION_ENABLED, /* u8 */ + __SMC_NLA_HS_LIMITATION_MAX, + SMC_NLA_HS_LIMITATION_MAX = __SMC_NLA_HS_LIMITATION_MAX - 1 +}; + +/* SMC socket options */ +#define SMC_LIMIT_HS 1 /* constraint on smc handshake */ + #endif /* _UAPI_LINUX_SMC_H */ diff --git a/include/uapi/linux/socket.h b/include/uapi/linux/socket.h index eb0a9a5b6e71..51d6bb2f6765 100644 --- a/include/uapi/linux/socket.h +++ b/include/uapi/linux/socket.h @@ -31,4 +31,8 @@ struct __kernel_sockaddr_storage { #define SOCK_BUF_LOCK_MASK (SOCK_SNDBUF_LOCK | SOCK_RCVBUF_LOCK) +#define SOCK_TXREHASH_DEFAULT ((u8)-1) +#define SOCK_TXREHASH_DISABLED 0 +#define SOCK_TXREHASH_ENABLED 1 + #endif /* _UAPI_LINUX_SOCKET_H */ diff --git a/init/Kconfig b/init/Kconfig index beb5b866c318..97463a33baa7 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -86,6 +86,10 @@ config CC_HAS_ASM_INLINE config CC_HAS_NO_PROFILE_FN_ATTR def_bool $(success,echo '__attribute__((no_profile_instrument_function)) int x();' | $(CC) -x c - -c -o /dev/null -Werror) +config PAHOLE_VERSION + int + default $(shell,$(srctree)/scripts/pahole-version.sh $(PAHOLE)) + config CONSTRUCTORS bool diff --git a/init/main.c b/init/main.c index 560f45c27ffe..0c064c2c79fd 100644 --- a/init/main.c +++ b/init/main.c @@ -99,6 +99,7 @@ #include #include #include +#include #include #include @@ -1116,6 +1117,7 @@ asmlinkage __visible void __init __no_sanitize_address start_kernel(void) key_init(); security_init(); dbg_late_init(); + net_ns_init(); vfs_caches_init(); pagecache_init(); signals_init(); diff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig index d24d518ddd63..d56ee177d5f8 100644 --- a/kernel/bpf/Kconfig +++ b/kernel/bpf/Kconfig @@ -30,6 +30,7 @@ config BPF_SYSCALL select TASKS_TRACE_RCU select BINARY_PRINTF select NET_SOCK_MSG if NET + select PAGE_POOL if NET default n help Enable the bpf() system call that allows to manipulate BPF programs @@ -58,6 +59,10 @@ config BPF_JIT_ALWAYS_ON Enables BPF JIT and removes BPF interpreter to avoid speculative execution of BPF instructions by the interpreter. + When CONFIG_BPF_JIT_ALWAYS_ON is enabled, /proc/sys/net/core/bpf_jit_enable + is permanently set to 1 and setting any other value than that will + return failure. + config BPF_JIT_DEFAULT_ON def_bool ARCH_WANT_DEFAULT_BPF_JIT || BPF_JIT_ALWAYS_ON depends on HAVE_EBPF_JIT && BPF_JIT diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index c7a5be3bf8be..7f145aefbff8 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -837,13 +837,12 @@ static int fd_array_map_delete_elem(struct bpf_map *map, void *key) static void *prog_fd_array_get_ptr(struct bpf_map *map, struct file *map_file, int fd) { - struct bpf_array *array = container_of(map, struct bpf_array, map); struct bpf_prog *prog = bpf_prog_get(fd); if (IS_ERR(prog)) return prog; - if (!bpf_prog_array_compatible(array, prog)) { + if (!bpf_prog_map_compatible(map, prog)) { bpf_prog_put(prog); return ERR_PTR(-EINVAL); } @@ -1071,7 +1070,6 @@ static struct bpf_map *prog_array_map_alloc(union bpf_attr *attr) INIT_WORK(&aux->work, prog_array_map_clear_deferred); INIT_LIST_HEAD(&aux->poke_progs); mutex_init(&aux->poke_mutex); - spin_lock_init(&aux->owner.lock); map = array_map_alloc(attr); if (IS_ERR(map)) { diff --git a/kernel/bpf/bpf_inode_storage.c b/kernel/bpf/bpf_inode_storage.c index e29d9e3d853e..96be8d518885 100644 --- a/kernel/bpf/bpf_inode_storage.c +++ b/kernel/bpf/bpf_inode_storage.c @@ -136,7 +136,7 @@ static int bpf_fd_inode_storage_update_elem(struct bpf_map *map, void *key, sdata = bpf_local_storage_update(f->f_inode, (struct bpf_local_storage_map *)map, - value, map_flags); + value, map_flags, GFP_ATOMIC); fput(f); return PTR_ERR_OR_ZERO(sdata); } @@ -169,8 +169,9 @@ static int bpf_fd_inode_storage_delete_elem(struct bpf_map *map, void *key) return err; } -BPF_CALL_4(bpf_inode_storage_get, struct bpf_map *, map, struct inode *, inode, - void *, value, u64, flags) +/* *gfp_flags* is a hidden argument provided by the verifier */ +BPF_CALL_5(bpf_inode_storage_get, struct bpf_map *, map, struct inode *, inode, + void *, value, u64, flags, gfp_t, gfp_flags) { struct bpf_local_storage_data *sdata; @@ -196,7 +197,7 @@ BPF_CALL_4(bpf_inode_storage_get, struct bpf_map *, map, struct inode *, inode, if (flags & BPF_LOCAL_STORAGE_GET_F_CREATE) { sdata = bpf_local_storage_update( inode, (struct bpf_local_storage_map *)map, value, - BPF_NOEXIST); + BPF_NOEXIST, gfp_flags); return IS_ERR(sdata) ? (unsigned long)NULL : (unsigned long)sdata->data; } diff --git a/kernel/bpf/bpf_iter.c b/kernel/bpf/bpf_iter.c index b7aef5b3416d..110029ede71e 100644 --- a/kernel/bpf/bpf_iter.c +++ b/kernel/bpf/bpf_iter.c @@ -5,6 +5,7 @@ #include #include #include +#include struct bpf_iter_target_info { struct list_head list; @@ -684,11 +685,20 @@ int bpf_iter_run_prog(struct bpf_prog *prog, void *ctx) { int ret; - rcu_read_lock(); - migrate_disable(); - ret = bpf_prog_run(prog, ctx); - migrate_enable(); - rcu_read_unlock(); + if (prog->aux->sleepable) { + rcu_read_lock_trace(); + migrate_disable(); + might_fault(); + ret = bpf_prog_run(prog, ctx); + migrate_enable(); + rcu_read_unlock_trace(); + } else { + rcu_read_lock(); + migrate_disable(); + ret = bpf_prog_run(prog, ctx); + migrate_enable(); + rcu_read_unlock(); + } /* bpf program can only return 0 or 1: * 0 : okay diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c index 71de2a89869c..01aa2b51ec4d 100644 --- a/kernel/bpf/bpf_local_storage.c +++ b/kernel/bpf/bpf_local_storage.c @@ -63,7 +63,7 @@ static bool selem_linked_to_map(const struct bpf_local_storage_elem *selem) struct bpf_local_storage_elem * bpf_selem_alloc(struct bpf_local_storage_map *smap, void *owner, - void *value, bool charge_mem) + void *value, bool charge_mem, gfp_t gfp_flags) { struct bpf_local_storage_elem *selem; @@ -71,7 +71,7 @@ bpf_selem_alloc(struct bpf_local_storage_map *smap, void *owner, return NULL; selem = bpf_map_kzalloc(&smap->map, smap->elem_size, - GFP_ATOMIC | __GFP_NOWARN); + gfp_flags | __GFP_NOWARN); if (selem) { if (value) memcpy(SDATA(selem)->data, value, smap->map.value_size); @@ -136,7 +136,7 @@ bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_storage, * will be done by the caller. * * Although the unlock will be done under - * rcu_read_lock(), it is more intutivie to + * rcu_read_lock(), it is more intuitive to * read if the freeing of the storage is done * after the raw_spin_unlock_bh(&local_storage->lock). * @@ -282,7 +282,8 @@ static int check_flags(const struct bpf_local_storage_data *old_sdata, int bpf_local_storage_alloc(void *owner, struct bpf_local_storage_map *smap, - struct bpf_local_storage_elem *first_selem) + struct bpf_local_storage_elem *first_selem, + gfp_t gfp_flags) { struct bpf_local_storage *prev_storage, *storage; struct bpf_local_storage **owner_storage_ptr; @@ -293,7 +294,7 @@ int bpf_local_storage_alloc(void *owner, return err; storage = bpf_map_kzalloc(&smap->map, sizeof(*storage), - GFP_ATOMIC | __GFP_NOWARN); + gfp_flags | __GFP_NOWARN); if (!storage) { err = -ENOMEM; goto uncharge; @@ -350,10 +351,10 @@ int bpf_local_storage_alloc(void *owner, */ struct bpf_local_storage_data * bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, - void *value, u64 map_flags) + void *value, u64 map_flags, gfp_t gfp_flags) { struct bpf_local_storage_data *old_sdata = NULL; - struct bpf_local_storage_elem *selem; + struct bpf_local_storage_elem *selem = NULL; struct bpf_local_storage *local_storage; unsigned long flags; int err; @@ -365,6 +366,9 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, !map_value_has_spin_lock(&smap->map))) return ERR_PTR(-EINVAL); + if (gfp_flags == GFP_KERNEL && (map_flags & ~BPF_F_LOCK) != BPF_NOEXIST) + return ERR_PTR(-EINVAL); + local_storage = rcu_dereference_check(*owner_storage(smap, owner), bpf_rcu_lock_held()); if (!local_storage || hlist_empty(&local_storage->list)) { @@ -373,11 +377,11 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, if (err) return ERR_PTR(err); - selem = bpf_selem_alloc(smap, owner, value, true); + selem = bpf_selem_alloc(smap, owner, value, true, gfp_flags); if (!selem) return ERR_PTR(-ENOMEM); - err = bpf_local_storage_alloc(owner, smap, selem); + err = bpf_local_storage_alloc(owner, smap, selem, gfp_flags); if (err) { kfree(selem); mem_uncharge(smap, owner, smap->elem_size); @@ -404,6 +408,12 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, } } + if (gfp_flags == GFP_KERNEL) { + selem = bpf_selem_alloc(smap, owner, value, true, gfp_flags); + if (!selem) + return ERR_PTR(-ENOMEM); + } + raw_spin_lock_irqsave(&local_storage->lock, flags); /* Recheck local_storage->list under local_storage->lock */ @@ -429,19 +439,21 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, goto unlock; } - /* local_storage->lock is held. Hence, we are sure - * we can unlink and uncharge the old_sdata successfully - * later. Hence, instead of charging the new selem now - * and then uncharge the old selem later (which may cause - * a potential but unnecessary charge failure), avoid taking - * a charge at all here (the "!old_sdata" check) and the - * old_sdata will not be uncharged later during - * bpf_selem_unlink_storage_nolock(). - */ - selem = bpf_selem_alloc(smap, owner, value, !old_sdata); - if (!selem) { - err = -ENOMEM; - goto unlock_err; + if (gfp_flags != GFP_KERNEL) { + /* local_storage->lock is held. Hence, we are sure + * we can unlink and uncharge the old_sdata successfully + * later. Hence, instead of charging the new selem now + * and then uncharge the old selem later (which may cause + * a potential but unnecessary charge failure), avoid taking + * a charge at all here (the "!old_sdata" check) and the + * old_sdata will not be uncharged later during + * bpf_selem_unlink_storage_nolock(). + */ + selem = bpf_selem_alloc(smap, owner, value, !old_sdata, gfp_flags); + if (!selem) { + err = -ENOMEM; + goto unlock_err; + } } /* First, link the new selem to the map */ @@ -463,6 +475,10 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, unlock_err: raw_spin_unlock_irqrestore(&local_storage->lock, flags); + if (selem) { + mem_uncharge(smap, owner, smap->elem_size); + kfree(selem); + } return ERR_PTR(err); } diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 9e4ecc990647..064eccba641d 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -99,6 +99,24 @@ static const struct bpf_func_proto bpf_ima_inode_hash_proto = { .allowed = bpf_ima_inode_hash_allowed, }; +BPF_CALL_3(bpf_ima_file_hash, struct file *, file, void *, dst, u32, size) +{ + return ima_file_hash(file, dst, size); +} + +BTF_ID_LIST_SINGLE(bpf_ima_file_hash_btf_ids, struct, file) + +static const struct bpf_func_proto bpf_ima_file_hash_proto = { + .func = bpf_ima_file_hash, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_BTF_ID, + .arg1_btf_id = &bpf_ima_file_hash_btf_ids[0], + .arg2_type = ARG_PTR_TO_UNINIT_MEM, + .arg3_type = ARG_CONST_SIZE, + .allowed = bpf_ima_inode_hash_allowed, +}; + static const struct bpf_func_proto * bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { @@ -121,6 +139,8 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_bprm_opts_set_proto; case BPF_FUNC_ima_inode_hash: return prog->aux->sleepable ? &bpf_ima_inode_hash_proto : NULL; + case BPF_FUNC_ima_file_hash: + return prog->aux->sleepable ? &bpf_ima_file_hash_proto : NULL; default: return tracing_prog_func_proto(func_id, prog); } @@ -167,6 +187,7 @@ BTF_ID(func, bpf_lsm_inode_setxattr) BTF_ID(func, bpf_lsm_inode_symlink) BTF_ID(func, bpf_lsm_inode_unlink) BTF_ID(func, bpf_lsm_kernel_module_request) +BTF_ID(func, bpf_lsm_kernel_read_file) BTF_ID(func, bpf_lsm_kernfs_init_security) #ifdef CONFIG_KEYS diff --git a/kernel/bpf/bpf_task_storage.c b/kernel/bpf/bpf_task_storage.c index 5da7bed0f5f6..6638a0ecc3d2 100644 --- a/kernel/bpf/bpf_task_storage.c +++ b/kernel/bpf/bpf_task_storage.c @@ -174,7 +174,8 @@ static int bpf_pid_task_storage_update_elem(struct bpf_map *map, void *key, bpf_task_storage_lock(); sdata = bpf_local_storage_update( - task, (struct bpf_local_storage_map *)map, value, map_flags); + task, (struct bpf_local_storage_map *)map, value, map_flags, + GFP_ATOMIC); bpf_task_storage_unlock(); err = PTR_ERR_OR_ZERO(sdata); @@ -226,8 +227,9 @@ static int bpf_pid_task_storage_delete_elem(struct bpf_map *map, void *key) return err; } -BPF_CALL_4(bpf_task_storage_get, struct bpf_map *, map, struct task_struct *, - task, void *, value, u64, flags) +/* *gfp_flags* is a hidden argument provided by the verifier */ +BPF_CALL_5(bpf_task_storage_get, struct bpf_map *, map, struct task_struct *, + task, void *, value, u64, flags, gfp_t, gfp_flags) { struct bpf_local_storage_data *sdata; @@ -250,7 +252,7 @@ BPF_CALL_4(bpf_task_storage_get, struct bpf_map *, map, struct task_struct *, (flags & BPF_LOCAL_STORAGE_GET_F_CREATE)) sdata = bpf_local_storage_update( task, (struct bpf_local_storage_map *)map, value, - BPF_NOEXIST); + BPF_NOEXIST, gfp_flags); unlock: bpf_task_storage_unlock(); diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 3e23b3fa79ff..24788ce564a0 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2018 Facebook */ #include @@ -198,6 +198,21 @@ DEFINE_IDR(btf_idr); DEFINE_SPINLOCK(btf_idr_lock); +enum btf_kfunc_hook { + BTF_KFUNC_HOOK_XDP, + BTF_KFUNC_HOOK_TC, + BTF_KFUNC_HOOK_STRUCT_OPS, + BTF_KFUNC_HOOK_MAX, +}; + +enum { + BTF_KFUNC_SET_MAX_CNT = 32, +}; + +struct btf_kfunc_set_tab { + struct btf_id_set *sets[BTF_KFUNC_HOOK_MAX][BTF_KFUNC_TYPE_MAX]; +}; + struct btf { void *data; struct btf_type **types; @@ -212,6 +227,7 @@ struct btf { refcount_t refcnt; u32 id; struct rcu_head rcu; + struct btf_kfunc_set_tab *kfunc_set_tab; /* split BTF support */ struct btf *base_btf; @@ -403,6 +419,9 @@ static struct btf_type btf_void; static int btf_resolve(struct btf_verifier_env *env, const struct btf_type *t, u32 type_id); +static int btf_func_check(struct btf_verifier_env *env, + const struct btf_type *t); + static bool btf_type_is_modifier(const struct btf_type *t) { /* Some of them is not strictly a C modifier @@ -506,6 +525,50 @@ s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind) return -ENOENT; } +static s32 bpf_find_btf_id(const char *name, u32 kind, struct btf **btf_p) +{ + struct btf *btf; + s32 ret; + int id; + + btf = bpf_get_btf_vmlinux(); + if (IS_ERR(btf)) + return PTR_ERR(btf); + if (!btf) + return -EINVAL; + + ret = btf_find_by_name_kind(btf, name, kind); + /* ret is never zero, since btf_find_by_name_kind returns + * positive btf_id or negative error. + */ + if (ret > 0) { + btf_get(btf); + *btf_p = btf; + return ret; + } + + /* If name is not found in vmlinux's BTF then search in module's BTFs */ + spin_lock_bh(&btf_idr_lock); + idr_for_each_entry(&btf_idr, btf, id) { + if (!btf_is_module(btf)) + continue; + /* linear search could be slow hence unlock/lock + * the IDR to avoiding holding it for too long + */ + btf_get(btf); + spin_unlock_bh(&btf_idr_lock); + ret = btf_find_by_name_kind(btf, name, kind); + if (ret > 0) { + *btf_p = btf; + return ret; + } + spin_lock_bh(&btf_idr_lock); + btf_put(btf); + } + spin_unlock_bh(&btf_idr_lock); + return ret; +} + const struct btf_type *btf_type_skip_modifiers(const struct btf *btf, u32 id, u32 *res_id) { @@ -579,6 +642,7 @@ static bool btf_type_needs_resolve(const struct btf_type *t) btf_type_is_struct(t) || btf_type_is_array(t) || btf_type_is_var(t) || + btf_type_is_func(t) || btf_type_is_decl_tag(t) || btf_type_is_datasec(t); } @@ -1531,8 +1595,30 @@ static void btf_free_id(struct btf *btf) spin_unlock_irqrestore(&btf_idr_lock, flags); } +static void btf_free_kfunc_set_tab(struct btf *btf) +{ + struct btf_kfunc_set_tab *tab = btf->kfunc_set_tab; + int hook, type; + + if (!tab) + return; + /* For module BTF, we directly assign the sets being registered, so + * there is nothing to free except kfunc_set_tab. + */ + if (btf_is_module(btf)) + goto free_tab; + for (hook = 0; hook < ARRAY_SIZE(tab->sets); hook++) { + for (type = 0; type < ARRAY_SIZE(tab->sets[0]); type++) + kfree(tab->sets[hook][type]); + } +free_tab: + kfree(tab); + btf->kfunc_set_tab = NULL; +} + static void btf_free(struct btf *btf) { + btf_free_kfunc_set_tab(btf); kvfree(btf->types); kvfree(btf->resolved_sizes); kvfree(btf->resolved_ids); @@ -2505,7 +2591,7 @@ static int btf_ptr_resolve(struct btf_verifier_env *env, * * We now need to continue from the last-resolved-ptr to * ensure the last-resolved-ptr will not referring back to - * the currenct ptr (t). + * the current ptr (t). */ if (btf_type_is_modifier(next_type)) { const struct btf_type *resolved_type; @@ -3533,9 +3619,24 @@ static s32 btf_func_check_meta(struct btf_verifier_env *env, return 0; } +static int btf_func_resolve(struct btf_verifier_env *env, + const struct resolve_vertex *v) +{ + const struct btf_type *t = v->t; + u32 next_type_id = t->type; + int err; + + err = btf_func_check(env, t); + if (err) + return err; + + env_stack_pop_resolved(env, next_type_id, 0); + return 0; +} + static struct btf_kind_operations func_ops = { .check_meta = btf_func_check_meta, - .resolve = btf_df_resolve, + .resolve = btf_func_resolve, .check_member = btf_df_check_member, .check_kflag_member = btf_df_check_kflag_member, .log_details = btf_ref_type_log, @@ -4156,7 +4257,7 @@ static bool btf_resolve_valid(struct btf_verifier_env *env, return !btf_resolved_type_id(btf, type_id) && !btf_resolved_type_size(btf, type_id); - if (btf_type_is_decl_tag(t)) + if (btf_type_is_decl_tag(t) || btf_type_is_func(t)) return btf_resolved_type_id(btf, type_id) && !btf_resolved_type_size(btf, type_id); @@ -4246,12 +4347,6 @@ static int btf_check_all_types(struct btf_verifier_env *env) if (err) return err; } - - if (btf_type_is_func(t)) { - err = btf_func_check(env, t); - if (err) - return err; - } } return 0; @@ -4387,8 +4482,7 @@ static int btf_parse_hdr(struct btf_verifier_env *env) btf = env->btf; btf_data_size = btf->data_size; - if (btf_data_size < - offsetof(struct btf_header, hdr_len) + sizeof(hdr->hdr_len)) { + if (btf_data_size < offsetofend(struct btf_header, hdr_len)) { btf_verifier_log(env, "hdr_len not found"); return -EINVAL; } @@ -4848,6 +4942,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, const char *tname = prog->aux->attach_func_name; struct bpf_verifier_log *log = info->log; const struct btf_param *args; + const char *tag_value; u32 nr_args, arg; int i, ret; @@ -5000,6 +5095,15 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, info->btf = btf; info->btf_id = t->type; t = btf_type_by_id(btf, t->type); + + if (btf_type_is_type_tag(t)) { + tag_value = __btf_name_by_offset(btf, t->name_off); + if (strcmp(tag_value, "user") == 0) + info->reg_type |= MEM_USER; + if (strcmp(tag_value, "percpu") == 0) + info->reg_type |= MEM_PERCPU; + } + /* skip modifiers */ while (btf_type_is_modifier(t)) { info->btf_id = t->type; @@ -5026,12 +5130,12 @@ enum bpf_struct_walk_result { static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, const struct btf_type *t, int off, int size, - u32 *next_btf_id) + u32 *next_btf_id, enum bpf_type_flag *flag) { u32 i, moff, mtrue_end, msize = 0, total_nelems = 0; const struct btf_type *mtype, *elem_type = NULL; const struct btf_member *member; - const char *tname, *mname; + const char *tname, *mname, *tag_value; u32 vlen, elem_id, mid; again: @@ -5215,7 +5319,8 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, } if (btf_type_is_ptr(mtype)) { - const struct btf_type *stype; + const struct btf_type *stype, *t; + enum bpf_type_flag tmp_flag = 0; u32 id; if (msize != size || off != moff) { @@ -5224,9 +5329,23 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, mname, moff, tname, off, size); return -EACCES; } + + /* check type tag */ + t = btf_type_by_id(btf, mtype->type); + if (btf_type_is_type_tag(t)) { + tag_value = __btf_name_by_offset(btf, t->name_off); + /* check __user tag */ + if (strcmp(tag_value, "user") == 0) + tmp_flag = MEM_USER; + /* check __percpu tag */ + if (strcmp(tag_value, "percpu") == 0) + tmp_flag = MEM_PERCPU; + } + stype = btf_type_skip_modifiers(btf, mtype->type, &id); if (btf_type_is_struct(stype)) { *next_btf_id = id; + *flag = tmp_flag; return WALK_PTR; } } @@ -5253,13 +5372,14 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf, const struct btf_type *t, int off, int size, enum bpf_access_type atype __maybe_unused, - u32 *next_btf_id) + u32 *next_btf_id, enum bpf_type_flag *flag) { + enum bpf_type_flag tmp_flag = 0; int err; u32 id; do { - err = btf_struct_walk(log, btf, t, off, size, &id); + err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag); switch (err) { case WALK_PTR: @@ -5267,6 +5387,7 @@ int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf, * we're done. */ *next_btf_id = id; + *flag = tmp_flag; return PTR_TO_BTF_ID; case WALK_SCALAR: return SCALAR_VALUE; @@ -5311,6 +5432,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log, const struct btf *need_btf, u32 need_type_id) { const struct btf_type *type; + enum bpf_type_flag flag; int err; /* Are we already done? */ @@ -5321,7 +5443,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log, type = btf_type_by_id(btf, id); if (!type) return false; - err = btf_struct_walk(log, btf, type, off, 1, &id); + err = btf_struct_walk(log, btf, type, off, 1, &id, &flag); if (err != WALK_STRUCT) return false; @@ -5616,17 +5738,45 @@ static bool __btf_type_is_scalar_struct(struct bpf_verifier_log *log, return true; } +static bool is_kfunc_arg_mem_size(const struct btf *btf, + const struct btf_param *arg, + const struct bpf_reg_state *reg) +{ + int len, sfx_len = sizeof("__sz") - 1; + const struct btf_type *t; + const char *param_name; + + t = btf_type_skip_modifiers(btf, arg->type, NULL); + if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE) + return false; + + /* In the future, this can be ported to use BTF tagging */ + param_name = btf_name_by_offset(btf, arg->name_off); + if (str_is_empty(param_name)) + return false; + len = strlen(param_name); + if (len < sfx_len) + return false; + param_name += len - sfx_len; + if (strncmp(param_name, "__sz", sfx_len)) + return false; + + return true; +} + static int btf_check_func_arg_match(struct bpf_verifier_env *env, const struct btf *btf, u32 func_id, struct bpf_reg_state *regs, bool ptr_to_mem_ok) { struct bpf_verifier_log *log = &env->log; + u32 i, nargs, ref_id, ref_obj_id = 0; bool is_kfunc = btf_is_kernel(btf); const char *func_name, *ref_tname; const struct btf_type *t, *ref_t; const struct btf_param *args; - u32 i, nargs, ref_id; + int ref_regno = 0, ret; + bool rel = false; t = btf_type_by_id(btf, func_id); if (!t || !btf_type_is_func(t)) { @@ -5652,6 +5802,10 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, return -EINVAL; } + /* Only kfunc can be release func */ + if (is_kfunc) + rel = btf_kfunc_id_set_contains(btf, resolve_prog_type(env->prog), + BTF_KFUNC_TYPE_RELEASE, func_id); /* check that BTF function arguments match actual types that the * verifier sees. */ @@ -5675,6 +5829,11 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id); ref_tname = btf_name_by_offset(btf, ref_t->name_off); + + ret = check_func_arg_reg_off(env, reg, regno, ARG_DONTCARE, rel); + if (ret < 0) + return ret; + if (btf_get_prog_ctx_type(log, btf, t, env->prog->type, i)) { /* If function expects ctx type in BTF check that caller @@ -5686,8 +5845,6 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, i, btf_type_str(t)); return -EINVAL; } - if (check_ptr_off_reg(env, reg, regno)) - return -EINVAL; } else if (is_kfunc && (reg->type == PTR_TO_BTF_ID || (reg2btf_ids[base_type(reg->type)] && !type_flag(reg->type)))) { const struct btf_type *reg_ref_t; @@ -5705,6 +5862,20 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, if (reg->type == PTR_TO_BTF_ID) { reg_btf = reg->btf; reg_ref_id = reg->btf_id; + /* Ensure only one argument is referenced + * PTR_TO_BTF_ID, check_func_arg_reg_off relies + * on only one referenced register being allowed + * for kfuncs. + */ + if (reg->ref_obj_id) { + if (ref_obj_id) { + bpf_log(log, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n", + regno, reg->ref_obj_id, ref_obj_id); + return -EFAULT; + } + ref_regno = regno; + ref_obj_id = reg->ref_obj_id; + } } else { reg_btf = btf_vmlinux; reg_ref_id = *reg2btf_ids[base_type(reg->type)]; @@ -5728,17 +5899,33 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, u32 type_size; if (is_kfunc) { + bool arg_mem_size = i + 1 < nargs && is_kfunc_arg_mem_size(btf, &args[i + 1], ®s[regno + 1]); + /* Permit pointer to mem, but only when argument * type is pointer to scalar, or struct composed * (recursively) of scalars. + * When arg_mem_size is true, the pointer can be + * void *. */ if (!btf_type_is_scalar(ref_t) && - !__btf_type_is_scalar_struct(log, btf, ref_t, 0)) { + !__btf_type_is_scalar_struct(log, btf, ref_t, 0) && + (arg_mem_size ? !btf_type_is_void(ref_t) : 1)) { bpf_log(log, - "arg#%d pointer type %s %s must point to scalar or struct with scalar\n", - i, btf_type_str(ref_t), ref_tname); + "arg#%d pointer type %s %s must point to %sscalar, or struct with scalar\n", + i, btf_type_str(ref_t), ref_tname, arg_mem_size ? "void, " : ""); return -EINVAL; } + + /* Check for mem, len pair */ + if (arg_mem_size) { + if (check_kfunc_mem_size_reg(env, ®s[regno + 1], regno + 1)) { + bpf_log(log, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", + i, i + 1); + return -EINVAL; + } + i++; + continue; + } } resolve_ret = btf_resolve_size(btf, ref_t, &type_size); @@ -5759,7 +5946,20 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, } } - return 0; + /* Either both are set, or neither */ + WARN_ON_ONCE((ref_obj_id && !ref_regno) || (!ref_obj_id && ref_regno)); + /* We already made sure ref_obj_id is set only for one argument. We do + * allow (!rel && ref_obj_id), so that passing such referenced + * PTR_TO_BTF_ID to other kfuncs works. Note that rel is only true when + * is_kfunc is true. + */ + if (rel && !ref_obj_id) { + bpf_log(log, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n", + func_name); + return -EINVAL; + } + /* returns argument register number > 0 in case of reference release kfunc */ + return rel ? ref_regno : 0; } /* Compare BTF of a function with given bpf_reg_state. @@ -6005,7 +6205,7 @@ int btf_type_snprintf_show(const struct btf *btf, u32 type_id, void *obj, btf_type_show(btf, type_id, obj, (struct btf_show *)&ssnprintf); - /* If we encontered an error, return it. */ + /* If we encountered an error, return it. */ if (ssnprintf.show.state.status) return ssnprintf.show.state.status; @@ -6201,12 +6401,17 @@ bool btf_id_set_contains(const struct btf_id_set *set, u32 id) return bsearch(&id, set->ids, set->cnt, sizeof(u32), btf_id_cmp_func) != NULL; } +enum { + BTF_MODULE_F_LIVE = (1 << 0), +}; + #ifdef CONFIG_DEBUG_INFO_BTF_MODULES struct btf_module { struct list_head list; struct module *module; struct btf *btf; struct bin_attribute *sysfs_attr; + int flags; }; static LIST_HEAD(btf_modules); @@ -6234,7 +6439,8 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op, int err = 0; if (mod->btf_data_size == 0 || - (op != MODULE_STATE_COMING && op != MODULE_STATE_GOING)) + (op != MODULE_STATE_COMING && op != MODULE_STATE_LIVE && + op != MODULE_STATE_GOING)) goto out; switch (op) { @@ -6249,7 +6455,8 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op, pr_warn("failed to validate module [%s] BTF: %ld\n", mod->name, PTR_ERR(btf)); kfree(btf_mod); - err = PTR_ERR(btf); + if (!IS_ENABLED(CONFIG_MODULE_ALLOW_BTF_MISMATCH)) + err = PTR_ERR(btf); goto out; } err = btf_alloc_id(btf); @@ -6292,6 +6499,17 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op, btf_mod->sysfs_attr = attr; } + break; + case MODULE_STATE_LIVE: + mutex_lock(&btf_module_mutex); + list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) { + if (btf_mod->module != module) + continue; + + btf_mod->flags |= BTF_MODULE_F_LIVE; + break; + } + mutex_unlock(&btf_module_mutex); break; case MODULE_STATE_GOING: mutex_lock(&btf_module_mutex); @@ -6339,7 +6557,12 @@ struct module *btf_try_get_module(const struct btf *btf) if (btf_mod->btf != btf) continue; - if (try_module_get(btf_mod->module)) + /* We must only consider module whose __init routine has + * finished, hence we must check for BTF_MODULE_F_LIVE flag, + * which is set from the notifier callback for + * MODULE_STATE_LIVE. + */ + if ((btf_mod->flags & BTF_MODULE_F_LIVE) && try_module_get(btf_mod->module)) res = btf_mod->module; break; @@ -6350,9 +6573,43 @@ struct module *btf_try_get_module(const struct btf *btf) return res; } +/* Returns struct btf corresponding to the struct module. + * This function can return NULL or ERR_PTR. + */ +static struct btf *btf_get_module_btf(const struct module *module) +{ +#ifdef CONFIG_DEBUG_INFO_BTF_MODULES + struct btf_module *btf_mod, *tmp; +#endif + struct btf *btf = NULL; + + if (!module) { + btf = bpf_get_btf_vmlinux(); + if (!IS_ERR_OR_NULL(btf)) + btf_get(btf); + return btf; + } + +#ifdef CONFIG_DEBUG_INFO_BTF_MODULES + mutex_lock(&btf_module_mutex); + list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) { + if (btf_mod->module != module) + continue; + + btf_get(btf_mod->btf); + btf = btf_mod->btf; + break; + } + mutex_unlock(&btf_module_mutex); +#endif + + return btf; +} + BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int, flags) { - struct btf *btf; + struct btf *btf = NULL; + int btf_obj_fd = 0; long ret; if (flags) @@ -6361,44 +6618,17 @@ BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int if (name_sz <= 1 || name[name_sz - 1]) return -EINVAL; - btf = bpf_get_btf_vmlinux(); - if (IS_ERR(btf)) - return PTR_ERR(btf); - - ret = btf_find_by_name_kind(btf, name, kind); - /* ret is never zero, since btf_find_by_name_kind returns - * positive btf_id or negative error. - */ - if (ret < 0) { - struct btf *mod_btf; - int id; - - /* If name is not found in vmlinux's BTF then search in module's BTFs */ - spin_lock_bh(&btf_idr_lock); - idr_for_each_entry(&btf_idr, mod_btf, id) { - if (!btf_is_module(mod_btf)) - continue; - /* linear search could be slow hence unlock/lock - * the IDR to avoiding holding it for too long - */ - btf_get(mod_btf); - spin_unlock_bh(&btf_idr_lock); - ret = btf_find_by_name_kind(mod_btf, name, kind); - if (ret > 0) { - int btf_obj_fd; - - btf_obj_fd = __btf_new_fd(mod_btf); - if (btf_obj_fd < 0) { - btf_put(mod_btf); - return btf_obj_fd; - } - return ret | (((u64)btf_obj_fd) << 32); - } - spin_lock_bh(&btf_idr_lock); - btf_put(mod_btf); + ret = bpf_find_btf_id(name, kind, &btf); + if (ret > 0 && btf_is_module(btf)) { + btf_obj_fd = __btf_new_fd(btf); + if (btf_obj_fd < 0) { + btf_put(btf); + return btf_obj_fd; } - spin_unlock_bh(&btf_idr_lock); + return ret | (((u64)btf_obj_fd) << 32); } + if (ret > 0) + btf_put(btf); return ret; } @@ -6417,58 +6647,298 @@ BTF_ID_LIST_GLOBAL(btf_tracing_ids, MAX_BTF_TRACING_TYPE) BTF_TRACING_TYPE_xxx #undef BTF_TRACING_TYPE -/* BTF ID set registration API for modules */ +/* Kernel Function (kfunc) BTF ID set registration API */ -#ifdef CONFIG_DEBUG_INFO_BTF_MODULES - -void register_kfunc_btf_id_set(struct kfunc_btf_id_list *l, - struct kfunc_btf_id_set *s) +static int __btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook, + enum btf_kfunc_type type, + struct btf_id_set *add_set, bool vmlinux_set) { - mutex_lock(&l->mutex); - list_add(&s->list, &l->list); - mutex_unlock(&l->mutex); -} -EXPORT_SYMBOL_GPL(register_kfunc_btf_id_set); + struct btf_kfunc_set_tab *tab; + struct btf_id_set *set; + u32 set_cnt; + int ret; -void unregister_kfunc_btf_id_set(struct kfunc_btf_id_list *l, - struct kfunc_btf_id_set *s) -{ - mutex_lock(&l->mutex); - list_del_init(&s->list); - mutex_unlock(&l->mutex); -} -EXPORT_SYMBOL_GPL(unregister_kfunc_btf_id_set); - -bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist, u32 kfunc_id, - struct module *owner) -{ - struct kfunc_btf_id_set *s; - - mutex_lock(&klist->mutex); - list_for_each_entry(s, &klist->list, list) { - if (s->owner == owner && btf_id_set_contains(s->set, kfunc_id)) { - mutex_unlock(&klist->mutex); - return true; - } + if (hook >= BTF_KFUNC_HOOK_MAX || type >= BTF_KFUNC_TYPE_MAX) { + ret = -EINVAL; + goto end; } - mutex_unlock(&klist->mutex); - return false; + + if (!add_set->cnt) + return 0; + + tab = btf->kfunc_set_tab; + if (!tab) { + tab = kzalloc(sizeof(*tab), GFP_KERNEL | __GFP_NOWARN); + if (!tab) + return -ENOMEM; + btf->kfunc_set_tab = tab; + } + + set = tab->sets[hook][type]; + /* Warn when register_btf_kfunc_id_set is called twice for the same hook + * for module sets. + */ + if (WARN_ON_ONCE(set && !vmlinux_set)) { + ret = -EINVAL; + goto end; + } + + /* We don't need to allocate, concatenate, and sort module sets, because + * only one is allowed per hook. Hence, we can directly assign the + * pointer and return. + */ + if (!vmlinux_set) { + tab->sets[hook][type] = add_set; + return 0; + } + + /* In case of vmlinux sets, there may be more than one set being + * registered per hook. To create a unified set, we allocate a new set + * and concatenate all individual sets being registered. While each set + * is individually sorted, they may become unsorted when concatenated, + * hence re-sorting the final set again is required to make binary + * searching the set using btf_id_set_contains function work. + */ + set_cnt = set ? set->cnt : 0; + + if (set_cnt > U32_MAX - add_set->cnt) { + ret = -EOVERFLOW; + goto end; + } + + if (set_cnt + add_set->cnt > BTF_KFUNC_SET_MAX_CNT) { + ret = -E2BIG; + goto end; + } + + /* Grow set */ + set = krealloc(tab->sets[hook][type], + offsetof(struct btf_id_set, ids[set_cnt + add_set->cnt]), + GFP_KERNEL | __GFP_NOWARN); + if (!set) { + ret = -ENOMEM; + goto end; + } + + /* For newly allocated set, initialize set->cnt to 0 */ + if (!tab->sets[hook][type]) + set->cnt = 0; + tab->sets[hook][type] = set; + + /* Concatenate the two sets */ + memcpy(set->ids + set->cnt, add_set->ids, add_set->cnt * sizeof(set->ids[0])); + set->cnt += add_set->cnt; + + sort(set->ids, set->cnt, sizeof(set->ids[0]), btf_id_cmp_func, NULL); + + return 0; +end: + btf_free_kfunc_set_tab(btf); + return ret; } -#define DEFINE_KFUNC_BTF_ID_LIST(name) \ - struct kfunc_btf_id_list name = { LIST_HEAD_INIT(name.list), \ - __MUTEX_INITIALIZER(name.mutex) }; \ - EXPORT_SYMBOL_GPL(name) +static int btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook, + const struct btf_kfunc_id_set *kset) +{ + bool vmlinux_set = !btf_is_module(btf); + int type, ret = 0; -DEFINE_KFUNC_BTF_ID_LIST(bpf_tcp_ca_kfunc_list); -DEFINE_KFUNC_BTF_ID_LIST(prog_test_kfunc_list); + for (type = 0; type < ARRAY_SIZE(kset->sets); type++) { + if (!kset->sets[type]) + continue; -#endif + ret = __btf_populate_kfunc_set(btf, hook, type, kset->sets[type], vmlinux_set); + if (ret) + break; + } + return ret; +} +static bool __btf_kfunc_id_set_contains(const struct btf *btf, + enum btf_kfunc_hook hook, + enum btf_kfunc_type type, + u32 kfunc_btf_id) +{ + struct btf_id_set *set; + + if (hook >= BTF_KFUNC_HOOK_MAX || type >= BTF_KFUNC_TYPE_MAX) + return false; + if (!btf->kfunc_set_tab) + return false; + set = btf->kfunc_set_tab->sets[hook][type]; + if (!set) + return false; + return btf_id_set_contains(set, kfunc_btf_id); +} + +static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type) +{ + switch (prog_type) { + case BPF_PROG_TYPE_XDP: + return BTF_KFUNC_HOOK_XDP; + case BPF_PROG_TYPE_SCHED_CLS: + return BTF_KFUNC_HOOK_TC; + case BPF_PROG_TYPE_STRUCT_OPS: + return BTF_KFUNC_HOOK_STRUCT_OPS; + default: + return BTF_KFUNC_HOOK_MAX; + } +} + +/* Caution: + * Reference to the module (obtained using btf_try_get_module) corresponding to + * the struct btf *MUST* be held when calling this function from verifier + * context. This is usually true as we stash references in prog's kfunc_btf_tab; + * keeping the reference for the duration of the call provides the necessary + * protection for looking up a well-formed btf->kfunc_set_tab. + */ +bool btf_kfunc_id_set_contains(const struct btf *btf, + enum bpf_prog_type prog_type, + enum btf_kfunc_type type, u32 kfunc_btf_id) +{ + enum btf_kfunc_hook hook; + + hook = bpf_prog_type_to_kfunc_hook(prog_type); + return __btf_kfunc_id_set_contains(btf, hook, type, kfunc_btf_id); +} + +/* This function must be invoked only from initcalls/module init functions */ +int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, + const struct btf_kfunc_id_set *kset) +{ + enum btf_kfunc_hook hook; + struct btf *btf; + int ret; + + btf = btf_get_module_btf(kset->owner); + if (!btf) { + if (!kset->owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) { + pr_err("missing vmlinux BTF, cannot register kfuncs\n"); + return -ENOENT; + } + if (kset->owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) { + pr_err("missing module BTF, cannot register kfuncs\n"); + return -ENOENT; + } + return 0; + } + if (IS_ERR(btf)) + return PTR_ERR(btf); + + hook = bpf_prog_type_to_kfunc_hook(prog_type); + ret = btf_populate_kfunc_set(btf, hook, kset); + btf_put(btf); + return ret; +} +EXPORT_SYMBOL_GPL(register_btf_kfunc_id_set); + +#define MAX_TYPES_ARE_COMPAT_DEPTH 2 + +static +int __bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id, + const struct btf *targ_btf, __u32 targ_id, + int level) +{ + const struct btf_type *local_type, *targ_type; + int depth = 32; /* max recursion depth */ + + /* caller made sure that names match (ignoring flavor suffix) */ + local_type = btf_type_by_id(local_btf, local_id); + targ_type = btf_type_by_id(targ_btf, targ_id); + if (btf_kind(local_type) != btf_kind(targ_type)) + return 0; + +recur: + depth--; + if (depth < 0) + return -EINVAL; + + local_type = btf_type_skip_modifiers(local_btf, local_id, &local_id); + targ_type = btf_type_skip_modifiers(targ_btf, targ_id, &targ_id); + if (!local_type || !targ_type) + return -EINVAL; + + if (btf_kind(local_type) != btf_kind(targ_type)) + return 0; + + switch (btf_kind(local_type)) { + case BTF_KIND_UNKN: + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + case BTF_KIND_ENUM: + case BTF_KIND_FWD: + return 1; + case BTF_KIND_INT: + /* just reject deprecated bitfield-like integers; all other + * integers are by default compatible between each other + */ + return btf_int_offset(local_type) == 0 && btf_int_offset(targ_type) == 0; + case BTF_KIND_PTR: + local_id = local_type->type; + targ_id = targ_type->type; + goto recur; + case BTF_KIND_ARRAY: + local_id = btf_array(local_type)->type; + targ_id = btf_array(targ_type)->type; + goto recur; + case BTF_KIND_FUNC_PROTO: { + struct btf_param *local_p = btf_params(local_type); + struct btf_param *targ_p = btf_params(targ_type); + __u16 local_vlen = btf_vlen(local_type); + __u16 targ_vlen = btf_vlen(targ_type); + int i, err; + + if (local_vlen != targ_vlen) + return 0; + + for (i = 0; i < local_vlen; i++, local_p++, targ_p++) { + if (level <= 0) + return -EINVAL; + + btf_type_skip_modifiers(local_btf, local_p->type, &local_id); + btf_type_skip_modifiers(targ_btf, targ_p->type, &targ_id); + err = __bpf_core_types_are_compat(local_btf, local_id, + targ_btf, targ_id, + level - 1); + if (err <= 0) + return err; + } + + /* tail recurse for return type check */ + btf_type_skip_modifiers(local_btf, local_type->type, &local_id); + btf_type_skip_modifiers(targ_btf, targ_type->type, &targ_id); + goto recur; + } + default: + return 0; + } +} + +/* Check local and target types for compatibility. This check is used for + * type-based CO-RE relocations and follow slightly different rules than + * field-based relocations. This function assumes that root types were already + * checked for name match. Beyond that initial root-level name check, names + * are completely ignored. Compatibility rules are as follows: + * - any two STRUCTs/UNIONs/FWDs/ENUMs/INTs are considered compatible, but + * kind should match for local and target types (i.e., STRUCT is not + * compatible with UNION); + * - for ENUMs, the size is ignored; + * - for INT, size and signedness are ignored; + * - for ARRAY, dimensionality is ignored, element types are checked for + * compatibility recursively; + * - CONST/VOLATILE/RESTRICT modifiers are ignored; + * - TYPEDEFs/PTRs are compatible if types they pointing to are compatible; + * - FUNC_PROTOs are compatible if they have compatible signature: same + * number of input args and compatible return and argument types. + * These rules are not set in stone and probably will be adjusted as we get + * more experience with using BPF CO-RE relocations. + */ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id, const struct btf *targ_btf, __u32 targ_id) { - return -EOPNOTSUPP; + return __bpf_core_types_are_compat(local_btf, local_id, + targ_btf, targ_id, + MAX_TYPES_ARE_COMPAT_DEPTH); } static bool bpf_core_is_flavor_sep(const char *s) @@ -6711,6 +7181,8 @@ bpf_core_find_cands(struct bpf_core_ctx *ctx, u32 local_type_id) main_btf = bpf_get_btf_vmlinux(); if (IS_ERR(main_btf)) return ERR_CAST(main_btf); + if (!main_btf) + return ERR_PTR(-EINVAL); local_type = btf_type_by_id(local_btf, local_type_id); if (!local_type) @@ -6789,6 +7261,7 @@ int bpf_core_apply(struct bpf_core_ctx *ctx, const struct bpf_core_relo *relo, { bool need_cands = relo->kind != BPF_CORE_TYPE_ID_LOCAL; struct bpf_core_cand_list cands = {}; + struct bpf_core_relo_res targ_res; struct bpf_core_spec *specs; int err; @@ -6828,13 +7301,19 @@ int bpf_core_apply(struct bpf_core_ctx *ctx, const struct bpf_core_relo *relo, cands.len = cc->cnt; /* cand_cache_mutex needs to span the cache lookup and * copy of btf pointer into bpf_core_cand_list, - * since module can be unloaded while bpf_core_apply_relo_insn + * since module can be unloaded while bpf_core_calc_relo_insn * is working with module's btf. */ } - err = bpf_core_apply_relo_insn((void *)ctx->log, insn, relo->insn_off / 8, - relo, relo_idx, ctx->btf, &cands, specs); + err = bpf_core_calc_relo_insn((void *)ctx->log, relo, relo_idx, ctx->btf, &cands, specs, + &targ_res); + if (err) + goto out; + + err = bpf_core_patch_insn((void *)ctx->log, insn, relo->insn_off / 8, relo, relo_idx, + &targ_res); + out: kfree(specs); if (need_cands) { diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 514b4681a90a..128028efda64 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1031,7 +1031,7 @@ int cgroup_bpf_prog_query(const union bpf_attr *attr, * __cgroup_bpf_run_filter_skb() - Run a program for packet filtering * @sk: The socket sending or receiving traffic * @skb: The skb that is being sent or received - * @type: The type of program to be exectuted + * @type: The type of program to be executed * * If no socket is passed, or the socket is not of type INET or INET6, * this function does nothing and returns 0. @@ -1044,7 +1044,7 @@ int cgroup_bpf_prog_query(const union bpf_attr *attr, * NET_XMIT_DROP (1) - drop packet and notify TCP to call cwr * NET_XMIT_CN (2) - continue with packet output and notify TCP * to call cwr - * -EPERM - drop packet + * -err - drop packet * * For ingress packets, this function will return -EPERM if any * attached program was found and if it returned != 1 during execution. @@ -1079,8 +1079,9 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk, cgrp->bpf.effective[atype], skb, __bpf_prog_run_save_cb); } else { ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], skb, - __bpf_prog_run_save_cb); - ret = (ret == 1 ? 0 : -EPERM); + __bpf_prog_run_save_cb, 0); + if (ret && !IS_ERR_VALUE((long)ret)) + ret = -EFAULT; } bpf_restore_data_end(skb, saved_data_end); __skb_pull(skb, offset); @@ -1093,7 +1094,7 @@ EXPORT_SYMBOL(__cgroup_bpf_run_filter_skb); /** * __cgroup_bpf_run_filter_sk() - Run a program on a sock * @sk: sock structure to manipulate - * @type: The type of program to be exectuted + * @type: The type of program to be executed * * socket is passed is expected to be of type INET or INET6. * @@ -1107,10 +1108,9 @@ int __cgroup_bpf_run_filter_sk(struct sock *sk, enum cgroup_bpf_attach_type atype) { struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); - int ret; - ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], sk, bpf_prog_run); - return ret == 1 ? 0 : -EPERM; + return BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], sk, + bpf_prog_run, 0); } EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk); @@ -1119,7 +1119,7 @@ EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk); * provided by user sockaddr * @sk: sock struct that will use sockaddr * @uaddr: sockaddr struct provided by user - * @type: The type of program to be exectuted + * @type: The type of program to be executed * @t_ctx: Pointer to attach type specific context * @flags: Pointer to u32 which contains higher bits of BPF program * return value (OR'ed together). @@ -1142,7 +1142,6 @@ int __cgroup_bpf_run_filter_sock_addr(struct sock *sk, }; struct sockaddr_storage unspec; struct cgroup *cgrp; - int ret; /* Check socket family since not all sockets represent network * endpoint (e.g. AF_UNIX). @@ -1156,10 +1155,8 @@ int __cgroup_bpf_run_filter_sock_addr(struct sock *sk, } cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); - ret = BPF_PROG_RUN_ARRAY_CG_FLAGS(cgrp->bpf.effective[atype], &ctx, - bpf_prog_run, flags); - - return ret == 1 ? 0 : -EPERM; + return BPF_PROG_RUN_ARRAY_CG_FLAGS(cgrp->bpf.effective[atype], &ctx, + bpf_prog_run, 0, flags); } EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_addr); @@ -1169,7 +1166,7 @@ EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_addr); * @sock_ops: bpf_sock_ops_kern struct to pass to program. Contains * sk with connection information (IP addresses, etc.) May not contain * cgroup info if it is a req sock. - * @type: The type of program to be exectuted + * @type: The type of program to be executed * * socket passed is expected to be of type INET or INET6. * @@ -1184,11 +1181,9 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk, enum cgroup_bpf_attach_type atype) { struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); - int ret; - ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], sock_ops, - bpf_prog_run); - return ret == 1 ? 0 : -EPERM; + return BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], sock_ops, + bpf_prog_run, 0); } EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_ops); @@ -1201,17 +1196,47 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor, .major = major, .minor = minor, }; - int allow; + int ret; rcu_read_lock(); cgrp = task_dfl_cgroup(current); - allow = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], &ctx, - bpf_prog_run); + ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], &ctx, + bpf_prog_run, 0); rcu_read_unlock(); - return !allow; + return ret; } +BPF_CALL_0(bpf_get_retval) +{ + struct bpf_cg_run_ctx *ctx = + container_of(current->bpf_ctx, struct bpf_cg_run_ctx, run_ctx); + + return ctx->retval; +} + +static const struct bpf_func_proto bpf_get_retval_proto = { + .func = bpf_get_retval, + .gpl_only = false, + .ret_type = RET_INTEGER, +}; + +BPF_CALL_1(bpf_set_retval, int, retval) +{ + struct bpf_cg_run_ctx *ctx = + container_of(current->bpf_ctx, struct bpf_cg_run_ctx, run_ctx); + + ctx->retval = retval; + return 0; +} + +static const struct bpf_func_proto bpf_set_retval_proto = { + .func = bpf_set_retval, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_ANYTHING, +}; + static const struct bpf_func_proto * cgroup_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { @@ -1224,6 +1249,10 @@ cgroup_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_get_current_cgroup_id_proto; case BPF_FUNC_perf_event_output: return &bpf_event_output_data_proto; + case BPF_FUNC_get_retval: + return &bpf_get_retval_proto; + case BPF_FUNC_set_retval: + return &bpf_set_retval_proto; default: return bpf_base_func_proto(func_id); } @@ -1337,7 +1366,8 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, rcu_read_lock(); cgrp = task_dfl_cgroup(current); - ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], &ctx, bpf_prog_run); + ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], &ctx, + bpf_prog_run, 0); rcu_read_unlock(); kfree(ctx.cur_val); @@ -1350,24 +1380,10 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, kfree(ctx.new_val); } - return ret == 1 ? 0 : -EPERM; + return ret; } #ifdef CONFIG_NET -static bool __cgroup_bpf_prog_array_is_empty(struct cgroup *cgrp, - enum cgroup_bpf_attach_type attach_type) -{ - struct bpf_prog_array *prog_array; - bool empty; - - rcu_read_lock(); - prog_array = rcu_dereference(cgrp->bpf.effective[attach_type]); - empty = bpf_prog_array_is_empty(prog_array); - rcu_read_unlock(); - - return empty; -} - static int sockopt_alloc_buf(struct bpf_sockopt_kern *ctx, int max_optlen, struct bpf_sockopt_buf *buf) { @@ -1426,19 +1442,11 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, }; int ret, max_optlen; - /* Opportunistic check to see whether we have any BPF program - * attached to the hook so we don't waste time allocating - * memory and locking the socket. - */ - if (__cgroup_bpf_prog_array_is_empty(cgrp, CGROUP_SETSOCKOPT)) - return 0; - /* Allocate a bit more than the initial user buffer for * BPF program. The canonical use case is overriding * TCP_CONGESTION(nv) to TCP_CONGESTION(cubic). */ max_optlen = max_t(int, 16, *optlen); - max_optlen = sockopt_alloc_buf(&ctx, max_optlen, &buf); if (max_optlen < 0) return max_optlen; @@ -1452,13 +1460,11 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, lock_sock(sk); ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[CGROUP_SETSOCKOPT], - &ctx, bpf_prog_run); + &ctx, bpf_prog_run, 0); release_sock(sk); - if (!ret) { - ret = -EPERM; + if (ret) goto out; - } if (ctx.optlen == -1) { /* optlen set to -1, bypass kernel */ @@ -1518,19 +1524,11 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, .sk = sk, .level = level, .optname = optname, - .retval = retval, + .current_task = current, }; int ret; - /* Opportunistic check to see whether we have any BPF program - * attached to the hook so we don't waste time allocating - * memory and locking the socket. - */ - if (__cgroup_bpf_prog_array_is_empty(cgrp, CGROUP_GETSOCKOPT)) - return retval; - ctx.optlen = max_optlen; - max_optlen = sockopt_alloc_buf(&ctx, max_optlen, &buf); if (max_optlen < 0) return max_optlen; @@ -1562,27 +1560,17 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, lock_sock(sk); ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[CGROUP_GETSOCKOPT], - &ctx, bpf_prog_run); + &ctx, bpf_prog_run, retval); release_sock(sk); - if (!ret) { - ret = -EPERM; + if (ret < 0) goto out; - } if (ctx.optlen > max_optlen || ctx.optlen < 0) { ret = -EFAULT; goto out; } - /* BPF programs only allowed to set retval to 0, not some - * arbitrary value. - */ - if (ctx.retval != 0 && ctx.retval != retval) { - ret = -EFAULT; - goto out; - } - if (ctx.optlen != 0) { if (copy_to_user(optval, ctx.optval, ctx.optlen) || put_user(ctx.optlen, optlen)) { @@ -1591,8 +1579,6 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, } } - ret = ctx.retval; - out: sockopt_free_buf(&ctx, &buf); return ret; @@ -1607,10 +1593,10 @@ int __cgroup_bpf_run_filter_getsockopt_kern(struct sock *sk, int level, .sk = sk, .level = level, .optname = optname, - .retval = retval, .optlen = *optlen, .optval = optval, .optval_end = optval + *optlen, + .current_task = current, }; int ret; @@ -1623,25 +1609,19 @@ int __cgroup_bpf_run_filter_getsockopt_kern(struct sock *sk, int level, */ ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[CGROUP_GETSOCKOPT], - &ctx, bpf_prog_run); - if (!ret) - return -EPERM; + &ctx, bpf_prog_run, retval); + if (ret < 0) + return ret; if (ctx.optlen > *optlen) return -EFAULT; - /* BPF programs only allowed to set retval to 0, not some - * arbitrary value. - */ - if (ctx.retval != 0 && ctx.retval != retval) - return -EFAULT; - /* BPF programs can shrink the buffer, export the modifications. */ if (ctx.optlen != 0) *optlen = ctx.optlen; - return ctx.retval; + return ret; } #endif @@ -2057,10 +2037,39 @@ static u32 cg_sockopt_convert_ctx_access(enum bpf_access_type type, *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, optlen); break; case offsetof(struct bpf_sockopt, retval): - if (type == BPF_WRITE) - *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_STX_MEM, retval); - else - *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, retval); + BUILD_BUG_ON(offsetof(struct bpf_cg_run_ctx, run_ctx) != 0); + + if (type == BPF_WRITE) { + int treg = BPF_REG_9; + + if (si->src_reg == treg || si->dst_reg == treg) + --treg; + if (si->src_reg == treg || si->dst_reg == treg) + --treg; + *insn++ = BPF_STX_MEM(BPF_DW, si->dst_reg, treg, + offsetof(struct bpf_sockopt_kern, tmp_reg)); + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_sockopt_kern, current_task), + treg, si->dst_reg, + offsetof(struct bpf_sockopt_kern, current_task)); + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct task_struct, bpf_ctx), + treg, treg, + offsetof(struct task_struct, bpf_ctx)); + *insn++ = BPF_STX_MEM(BPF_FIELD_SIZEOF(struct bpf_cg_run_ctx, retval), + treg, si->src_reg, + offsetof(struct bpf_cg_run_ctx, retval)); + *insn++ = BPF_LDX_MEM(BPF_DW, treg, si->dst_reg, + offsetof(struct bpf_sockopt_kern, tmp_reg)); + } else { + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_sockopt_kern, current_task), + si->dst_reg, si->src_reg, + offsetof(struct bpf_sockopt_kern, current_task)); + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct task_struct, bpf_ctx), + si->dst_reg, si->dst_reg, + offsetof(struct task_struct, bpf_ctx)); + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_cg_run_ctx, retval), + si->dst_reg, si->dst_reg, + offsetof(struct bpf_cg_run_ctx, retval)); + } break; case offsetof(struct bpf_sockopt, optval): *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, optval); diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index de3e5bc6781f..13e9dbeeedf3 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -105,6 +106,7 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag fp->aux = aux; fp->aux->prog = fp; fp->jit_requested = ebpf_jit_enabled(); + fp->blinding_requested = bpf_jit_blinding_enabled(fp); INIT_LIST_HEAD_RCU(&fp->aux->ksym.lnode); mutex_init(&fp->aux->used_maps_mutex); @@ -537,13 +539,10 @@ long bpf_jit_limit_max __read_mostly; static void bpf_prog_ksym_set_addr(struct bpf_prog *prog) { - const struct bpf_binary_header *hdr = bpf_jit_binary_hdr(prog); - unsigned long addr = (unsigned long)hdr; - WARN_ON_ONCE(!bpf_prog_ebpf_jited(prog)); prog->aux->ksym.start = (unsigned long) prog->bpf_func; - prog->aux->ksym.end = addr + hdr->pages * PAGE_SIZE; + prog->aux->ksym.end = prog->aux->ksym.start + prog->jited_len; } static void @@ -808,6 +807,176 @@ int bpf_jit_add_poke_descriptor(struct bpf_prog *prog, return slot; } +/* + * BPF program pack allocator. + * + * Most BPF programs are pretty small. Allocating a hole page for each + * program is sometime a waste. Many small bpf program also adds pressure + * to instruction TLB. To solve this issue, we introduce a BPF program pack + * allocator. The prog_pack allocator uses HPAGE_PMD_SIZE page (2MB on x86) + * to host BPF programs. + */ +#define BPF_PROG_CHUNK_SHIFT 6 +#define BPF_PROG_CHUNK_SIZE (1 << BPF_PROG_CHUNK_SHIFT) +#define BPF_PROG_CHUNK_MASK (~(BPF_PROG_CHUNK_SIZE - 1)) + +struct bpf_prog_pack { + struct list_head list; + void *ptr; + unsigned long bitmap[]; +}; + +#define BPF_PROG_SIZE_TO_NBITS(size) (round_up(size, BPF_PROG_CHUNK_SIZE) / BPF_PROG_CHUNK_SIZE) + +static size_t bpf_prog_pack_size = -1; +static size_t bpf_prog_pack_mask = -1; + +static int bpf_prog_chunk_count(void) +{ + WARN_ON_ONCE(bpf_prog_pack_size == -1); + return bpf_prog_pack_size / BPF_PROG_CHUNK_SIZE; +} + +static DEFINE_MUTEX(pack_mutex); +static LIST_HEAD(pack_list); + +/* PMD_SIZE is not available in some special config, e.g. ARCH=arm with + * CONFIG_MMU=n. Use PAGE_SIZE in these cases. + */ +#ifdef PMD_SIZE +#define BPF_HPAGE_SIZE PMD_SIZE +#define BPF_HPAGE_MASK PMD_MASK +#else +#define BPF_HPAGE_SIZE PAGE_SIZE +#define BPF_HPAGE_MASK PAGE_MASK +#endif + +static size_t select_bpf_prog_pack_size(void) +{ + size_t size; + void *ptr; + + size = BPF_HPAGE_SIZE * num_online_nodes(); + ptr = module_alloc(size); + + /* Test whether we can get huge pages. If not just use PAGE_SIZE + * packs. + */ + if (!ptr || !is_vm_area_hugepages(ptr)) { + size = PAGE_SIZE; + bpf_prog_pack_mask = PAGE_MASK; + } else { + bpf_prog_pack_mask = BPF_HPAGE_MASK; + } + + vfree(ptr); + return size; +} + +static struct bpf_prog_pack *alloc_new_pack(void) +{ + struct bpf_prog_pack *pack; + + pack = kzalloc(struct_size(pack, bitmap, BITS_TO_LONGS(bpf_prog_chunk_count())), + GFP_KERNEL); + if (!pack) + return NULL; + pack->ptr = module_alloc(bpf_prog_pack_size); + if (!pack->ptr) { + kfree(pack); + return NULL; + } + bitmap_zero(pack->bitmap, bpf_prog_pack_size / BPF_PROG_CHUNK_SIZE); + list_add_tail(&pack->list, &pack_list); + + set_vm_flush_reset_perms(pack->ptr); + set_memory_ro((unsigned long)pack->ptr, bpf_prog_pack_size / PAGE_SIZE); + set_memory_x((unsigned long)pack->ptr, bpf_prog_pack_size / PAGE_SIZE); + return pack; +} + +static void *bpf_prog_pack_alloc(u32 size) +{ + unsigned int nbits = BPF_PROG_SIZE_TO_NBITS(size); + struct bpf_prog_pack *pack; + unsigned long pos; + void *ptr = NULL; + + mutex_lock(&pack_mutex); + if (bpf_prog_pack_size == -1) + bpf_prog_pack_size = select_bpf_prog_pack_size(); + + if (size > bpf_prog_pack_size) { + size = round_up(size, PAGE_SIZE); + ptr = module_alloc(size); + if (ptr) { + set_vm_flush_reset_perms(ptr); + set_memory_ro((unsigned long)ptr, size / PAGE_SIZE); + set_memory_x((unsigned long)ptr, size / PAGE_SIZE); + } + goto out; + } + list_for_each_entry(pack, &pack_list, list) { + pos = bitmap_find_next_zero_area(pack->bitmap, bpf_prog_chunk_count(), 0, + nbits, 0); + if (pos < bpf_prog_chunk_count()) + goto found_free_area; + } + + pack = alloc_new_pack(); + if (!pack) + goto out; + + pos = 0; + +found_free_area: + bitmap_set(pack->bitmap, pos, nbits); + ptr = (void *)(pack->ptr) + (pos << BPF_PROG_CHUNK_SHIFT); + +out: + mutex_unlock(&pack_mutex); + return ptr; +} + +static void bpf_prog_pack_free(struct bpf_binary_header *hdr) +{ + struct bpf_prog_pack *pack = NULL, *tmp; + unsigned int nbits; + unsigned long pos; + void *pack_ptr; + + mutex_lock(&pack_mutex); + if (hdr->size > bpf_prog_pack_size) { + module_memfree(hdr); + goto out; + } + + pack_ptr = (void *)((unsigned long)hdr & bpf_prog_pack_mask); + + list_for_each_entry(tmp, &pack_list, list) { + if (tmp->ptr == pack_ptr) { + pack = tmp; + break; + } + } + + if (WARN_ONCE(!pack, "bpf_prog_pack bug\n")) + goto out; + + nbits = BPF_PROG_SIZE_TO_NBITS(hdr->size); + pos = ((unsigned long)hdr - (unsigned long)pack_ptr) >> BPF_PROG_CHUNK_SHIFT; + + bitmap_clear(pack->bitmap, pos, nbits); + if (bitmap_find_next_zero_area(pack->bitmap, bpf_prog_chunk_count(), 0, + bpf_prog_chunk_count(), 0) == 0) { + list_del(&pack->list); + module_memfree(pack->ptr); + kfree(pack); + } +out: + mutex_unlock(&pack_mutex); +} + static atomic_long_t bpf_jit_current; /* Can be overridden by an arch's JIT compiler if it has a custom, @@ -833,12 +1002,11 @@ static int __init bpf_jit_charge_init(void) } pure_initcall(bpf_jit_charge_init); -int bpf_jit_charge_modmem(u32 pages) +int bpf_jit_charge_modmem(u32 size) { - if (atomic_long_add_return(pages, &bpf_jit_current) > - (bpf_jit_limit >> PAGE_SHIFT)) { + if (atomic_long_add_return(size, &bpf_jit_current) > bpf_jit_limit) { if (!bpf_capable()) { - atomic_long_sub(pages, &bpf_jit_current); + atomic_long_sub(size, &bpf_jit_current); return -EPERM; } } @@ -846,9 +1014,9 @@ int bpf_jit_charge_modmem(u32 pages) return 0; } -void bpf_jit_uncharge_modmem(u32 pages) +void bpf_jit_uncharge_modmem(u32 size) { - atomic_long_sub(pages, &bpf_jit_current); + atomic_long_sub(size, &bpf_jit_current); } void *__weak bpf_jit_alloc_exec(unsigned long size) @@ -867,7 +1035,7 @@ bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr, bpf_jit_fill_hole_t bpf_fill_ill_insns) { struct bpf_binary_header *hdr; - u32 size, hole, start, pages; + u32 size, hole, start; WARN_ON_ONCE(!is_power_of_2(alignment) || alignment > BPF_IMAGE_ALIGNMENT); @@ -877,20 +1045,19 @@ bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr, * random section of illegal instructions. */ size = round_up(proglen + sizeof(*hdr) + 128, PAGE_SIZE); - pages = size / PAGE_SIZE; - if (bpf_jit_charge_modmem(pages)) + if (bpf_jit_charge_modmem(size)) return NULL; hdr = bpf_jit_alloc_exec(size); if (!hdr) { - bpf_jit_uncharge_modmem(pages); + bpf_jit_uncharge_modmem(size); return NULL; } /* Fill space with illegal/arch-dep instructions. */ bpf_fill_ill_insns(hdr, size); - hdr->pages = pages; + hdr->size = size; hole = min_t(unsigned int, size - (proglen + sizeof(*hdr)), PAGE_SIZE - sizeof(*hdr)); start = (get_random_int() % hole) & ~(alignment - 1); @@ -903,10 +1070,117 @@ bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr, void bpf_jit_binary_free(struct bpf_binary_header *hdr) { - u32 pages = hdr->pages; + u32 size = hdr->size; bpf_jit_free_exec(hdr); - bpf_jit_uncharge_modmem(pages); + bpf_jit_uncharge_modmem(size); +} + +/* Allocate jit binary from bpf_prog_pack allocator. + * Since the allocated memory is RO+X, the JIT engine cannot write directly + * to the memory. To solve this problem, a RW buffer is also allocated at + * as the same time. The JIT engine should calculate offsets based on the + * RO memory address, but write JITed program to the RW buffer. Once the + * JIT engine finishes, it calls bpf_jit_binary_pack_finalize, which copies + * the JITed program to the RO memory. + */ +struct bpf_binary_header * +bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **image_ptr, + unsigned int alignment, + struct bpf_binary_header **rw_header, + u8 **rw_image, + bpf_jit_fill_hole_t bpf_fill_ill_insns) +{ + struct bpf_binary_header *ro_header; + u32 size, hole, start; + + WARN_ON_ONCE(!is_power_of_2(alignment) || + alignment > BPF_IMAGE_ALIGNMENT); + + /* add 16 bytes for a random section of illegal instructions */ + size = round_up(proglen + sizeof(*ro_header) + 16, BPF_PROG_CHUNK_SIZE); + + if (bpf_jit_charge_modmem(size)) + return NULL; + ro_header = bpf_prog_pack_alloc(size); + if (!ro_header) { + bpf_jit_uncharge_modmem(size); + return NULL; + } + + *rw_header = kvmalloc(size, GFP_KERNEL); + if (!*rw_header) { + bpf_arch_text_copy(&ro_header->size, &size, sizeof(size)); + bpf_prog_pack_free(ro_header); + bpf_jit_uncharge_modmem(size); + return NULL; + } + + /* Fill space with illegal/arch-dep instructions. */ + bpf_fill_ill_insns(*rw_header, size); + (*rw_header)->size = size; + + hole = min_t(unsigned int, size - (proglen + sizeof(*ro_header)), + BPF_PROG_CHUNK_SIZE - sizeof(*ro_header)); + start = (get_random_int() % hole) & ~(alignment - 1); + + *image_ptr = &ro_header->image[start]; + *rw_image = &(*rw_header)->image[start]; + + return ro_header; +} + +/* Copy JITed text from rw_header to its final location, the ro_header. */ +int bpf_jit_binary_pack_finalize(struct bpf_prog *prog, + struct bpf_binary_header *ro_header, + struct bpf_binary_header *rw_header) +{ + void *ptr; + + ptr = bpf_arch_text_copy(ro_header, rw_header, rw_header->size); + + kvfree(rw_header); + + if (IS_ERR(ptr)) { + bpf_prog_pack_free(ro_header); + return PTR_ERR(ptr); + } + prog->aux->use_bpf_prog_pack = true; + return 0; +} + +/* bpf_jit_binary_pack_free is called in two different scenarios: + * 1) when the program is freed after; + * 2) when the JIT engine fails (before bpf_jit_binary_pack_finalize). + * For case 2), we need to free both the RO memory and the RW buffer. + * + * bpf_jit_binary_pack_free requires proper ro_header->size. However, + * bpf_jit_binary_pack_alloc does not set it. Therefore, ro_header->size + * must be set with either bpf_jit_binary_pack_finalize (normal path) or + * bpf_arch_text_copy (when jit fails). + */ +void bpf_jit_binary_pack_free(struct bpf_binary_header *ro_header, + struct bpf_binary_header *rw_header) +{ + u32 size = ro_header->size; + + bpf_prog_pack_free(ro_header); + kvfree(rw_header); + bpf_jit_uncharge_modmem(size); +} + +static inline struct bpf_binary_header * +bpf_jit_binary_hdr(const struct bpf_prog *fp) +{ + unsigned long real_start = (unsigned long)fp->bpf_func; + unsigned long addr; + + if (fp->aux->use_bpf_prog_pack) + addr = real_start & BPF_PROG_CHUNK_MASK; + else + addr = real_start & PAGE_MASK; + + return (void *)addr; } /* This symbol is only overridden by archs that have different @@ -918,7 +1192,10 @@ void __weak bpf_jit_free(struct bpf_prog *fp) if (fp->jited) { struct bpf_binary_header *hdr = bpf_jit_binary_hdr(fp); - bpf_jit_binary_free(hdr); + if (fp->aux->use_bpf_prog_pack) + bpf_jit_binary_pack_free(hdr, NULL /* rw_buffer */); + else + bpf_jit_binary_free(hdr); WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(fp)); } @@ -1146,7 +1423,7 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog) struct bpf_insn *insn; int i, rewritten; - if (!bpf_jit_blinding_enabled(prog) || prog->blinded) + if (!prog->blinding_requested || prog->blinded) return prog; clone = bpf_prog_clone_create(prog, GFP_USER); @@ -1829,28 +2106,30 @@ static unsigned int __bpf_prog_ret0_warn(const void *ctx, } #endif -bool bpf_prog_array_compatible(struct bpf_array *array, - const struct bpf_prog *fp) +bool bpf_prog_map_compatible(struct bpf_map *map, + const struct bpf_prog *fp) { bool ret; if (fp->kprobe_override) return false; - spin_lock(&array->aux->owner.lock); - - if (!array->aux->owner.type) { + spin_lock(&map->owner.lock); + if (!map->owner.type) { /* There's no owner yet where we could check for * compatibility. */ - array->aux->owner.type = fp->type; - array->aux->owner.jited = fp->jited; + map->owner.type = fp->type; + map->owner.jited = fp->jited; + map->owner.xdp_has_frags = fp->aux->xdp_has_frags; ret = true; } else { - ret = array->aux->owner.type == fp->type && - array->aux->owner.jited == fp->jited; + ret = map->owner.type == fp->type && + map->owner.jited == fp->jited && + map->owner.xdp_has_frags == fp->aux->xdp_has_frags; } - spin_unlock(&array->aux->owner.lock); + spin_unlock(&map->owner.lock); + return ret; } @@ -1862,13 +2141,11 @@ static int bpf_check_tail_call(const struct bpf_prog *fp) mutex_lock(&aux->used_maps_mutex); for (i = 0; i < aux->used_map_cnt; i++) { struct bpf_map *map = aux->used_maps[i]; - struct bpf_array *array; - if (map->map_type != BPF_MAP_TYPE_PROG_ARRAY) + if (!map_type_contains_progs(map)) continue; - array = container_of(map, struct bpf_array, map); - if (!bpf_prog_array_compatible(array, fp)) { + if (!bpf_prog_map_compatible(map, fp)) { ret = -EINVAL; goto out; } @@ -1968,18 +2245,10 @@ static struct bpf_prog_dummy { }, }; -/* to avoid allocating empty bpf_prog_array for cgroups that - * don't have bpf program attached use one global 'empty_prog_array' - * It will not be modified the caller of bpf_prog_array_alloc() - * (since caller requested prog_cnt == 0) - * that pointer should be 'freed' by bpf_prog_array_free() - */ -static struct { - struct bpf_prog_array hdr; - struct bpf_prog *null_prog; -} empty_prog_array = { +struct bpf_empty_prog_array bpf_empty_prog_array = { .null_prog = NULL, }; +EXPORT_SYMBOL(bpf_empty_prog_array); struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags) { @@ -1989,12 +2258,12 @@ struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags) (prog_cnt + 1), flags); - return &empty_prog_array.hdr; + return &bpf_empty_prog_array.hdr; } void bpf_prog_array_free(struct bpf_prog_array *progs) { - if (!progs || progs == &empty_prog_array.hdr) + if (!progs || progs == &bpf_empty_prog_array.hdr) return; kfree_rcu(progs, rcu); } @@ -2453,6 +2722,11 @@ int __weak bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t, return -ENOTSUPP; } +void * __weak bpf_arch_text_copy(void *dst, void *src, size_t len) +{ + return ERR_PTR(-ENOTSUPP); +} + DEFINE_STATIC_KEY_FALSE(bpf_stats_enabled_key); EXPORT_SYMBOL(bpf_stats_enabled_key); diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index b3e6b9422238..650e5d21f90d 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -397,7 +397,8 @@ static int cpu_map_kthread_run(void *data) return 0; } -static int __cpu_map_load_bpf_program(struct bpf_cpu_map_entry *rcpu, int fd) +static int __cpu_map_load_bpf_program(struct bpf_cpu_map_entry *rcpu, + struct bpf_map *map, int fd) { struct bpf_prog *prog; @@ -405,7 +406,8 @@ static int __cpu_map_load_bpf_program(struct bpf_cpu_map_entry *rcpu, int fd) if (IS_ERR(prog)) return PTR_ERR(prog); - if (prog->expected_attach_type != BPF_XDP_CPUMAP) { + if (prog->expected_attach_type != BPF_XDP_CPUMAP || + !bpf_prog_map_compatible(map, prog)) { bpf_prog_put(prog); return -EINVAL; } @@ -457,7 +459,7 @@ __cpu_map_entry_alloc(struct bpf_map *map, struct bpf_cpumap_val *value, rcpu->map_id = map->id; rcpu->value.qsize = value->qsize; - if (fd > 0 && __cpu_map_load_bpf_program(rcpu, fd)) + if (fd > 0 && __cpu_map_load_bpf_program(rcpu, map, fd)) goto free_ptr_ring; /* Setup kthread */ diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index fe019dbdb3f0..038f6d7a83e4 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -858,7 +858,8 @@ static struct bpf_dtab_netdev *__dev_map_alloc_node(struct net *net, BPF_PROG_TYPE_XDP, false); if (IS_ERR(prog)) goto err_put_dev; - if (prog->expected_attach_type != BPF_XDP_DEVMAP) + if (prog->expected_attach_type != BPF_XDP_DEVMAP || + !bpf_prog_map_compatible(&dtab->map, prog)) goto err_put_prog; } diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index d29af9988f37..65877967f414 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -1636,7 +1636,7 @@ __htab_map_lookup_and_delete_batch(struct bpf_map *map, value_size = size * num_possible_cpus(); total = 0; /* while experimenting with hash tables with sizes ranging from 10 to - * 1000, it was observed that a bucket can have upto 5 entries. + * 1000, it was observed that a bucket can have up to 5 entries. */ bucket_size = 5; diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 55c084251fab..315053ef6a75 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "../../lib/kstrtox.h" @@ -224,13 +225,8 @@ BPF_CALL_2(bpf_get_current_comm, char *, buf, u32, size) if (unlikely(!task)) goto err_clear; - strncpy(buf, task->comm, size); - - /* Verifier guarantees that size > 0. For task->comm exceeding - * size, guarantee that buf is %NUL-terminated. Unconditionally - * done here to save the size test. - */ - buf[size - 1] = 0; + /* Verifier guarantees that size > 0 */ + strscpy(buf, task->comm, size); return 0; err_clear: memset(buf, 0, size); @@ -672,6 +668,39 @@ const struct bpf_func_proto bpf_copy_from_user_proto = { .arg3_type = ARG_ANYTHING, }; +BPF_CALL_5(bpf_copy_from_user_task, void *, dst, u32, size, + const void __user *, user_ptr, struct task_struct *, tsk, u64, flags) +{ + int ret; + + /* flags is not used yet */ + if (unlikely(flags)) + return -EINVAL; + + if (unlikely(!size)) + return 0; + + ret = access_process_vm(tsk, (unsigned long)user_ptr, dst, size, 0); + if (ret == size) + return 0; + + memset(dst, 0, size); + /* Return -EFAULT for partial read */ + return ret < 0 ? ret : -EFAULT; +} + +const struct bpf_func_proto bpf_copy_from_user_task_proto = { + .func = bpf_copy_from_user_task, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_UNINIT_MEM, + .arg2_type = ARG_CONST_SIZE_OR_ZERO, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_PTR_TO_BTF_ID, + .arg4_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK], + .arg5_type = ARG_ANYTHING +}; + BPF_CALL_2(bpf_per_cpu_ptr, const void *, ptr, u32, cpu) { if (cpu >= nr_cpu_ids) @@ -1059,7 +1088,7 @@ struct bpf_hrtimer { struct bpf_timer_kern { struct bpf_hrtimer *timer; /* bpf_spin_lock is used here instead of spinlock_t to make - * sure that it always fits into space resereved by struct bpf_timer + * sure that it always fits into space reserved by struct bpf_timer * regardless of LOCKDEP and spinlock debug flags. */ struct bpf_spin_lock lock; diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index 5a8d9f7467bf..4f841e16779e 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -710,11 +710,10 @@ static DEFINE_MUTEX(bpf_preload_lock); static int populate_bpffs(struct dentry *parent) { struct bpf_preload_info objs[BPF_PRELOAD_LINKS] = {}; - struct bpf_link *links[BPF_PRELOAD_LINKS] = {}; int err = 0, i; /* grab the mutex to make sure the kernel interactions with bpf_preload - * UMD are serialized + * are serialized */ mutex_lock(&bpf_preload_lock); @@ -722,40 +721,22 @@ static int populate_bpffs(struct dentry *parent) if (!bpf_preload_mod_get()) goto out; - if (!bpf_preload_ops->info.tgid) { - /* preload() will start UMD that will load BPF iterator programs */ - err = bpf_preload_ops->preload(objs); - if (err) + err = bpf_preload_ops->preload(objs); + if (err) + goto out_put; + for (i = 0; i < BPF_PRELOAD_LINKS; i++) { + bpf_link_inc(objs[i].link); + err = bpf_iter_link_pin_kernel(parent, + objs[i].link_name, objs[i].link); + if (err) { + bpf_link_put(objs[i].link); goto out_put; - for (i = 0; i < BPF_PRELOAD_LINKS; i++) { - links[i] = bpf_link_by_id(objs[i].link_id); - if (IS_ERR(links[i])) { - err = PTR_ERR(links[i]); - goto out_put; - } } - for (i = 0; i < BPF_PRELOAD_LINKS; i++) { - err = bpf_iter_link_pin_kernel(parent, - objs[i].link_name, links[i]); - if (err) - goto out_put; - /* do not unlink successfully pinned links even - * if later link fails to pin - */ - links[i] = NULL; - } - /* finish() will tell UMD process to exit */ - err = bpf_preload_ops->finish(); - if (err) - goto out_put; } out_put: bpf_preload_mod_put(); out: mutex_unlock(&bpf_preload_lock); - for (i = 0; i < BPF_PRELOAD_LINKS && err; i++) - if (!IS_ERR_OR_NULL(links[i])) - bpf_link_put(links[i]); return err; } diff --git a/kernel/bpf/local_storage.c b/kernel/bpf/local_storage.c index 23f7f9d08a62..497916060ac7 100644 --- a/kernel/bpf/local_storage.c +++ b/kernel/bpf/local_storage.c @@ -1,4 +1,4 @@ -//SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0 #include #include #include diff --git a/kernel/bpf/preload/Kconfig b/kernel/bpf/preload/Kconfig index 26bced262473..c9d45c9d6918 100644 --- a/kernel/bpf/preload/Kconfig +++ b/kernel/bpf/preload/Kconfig @@ -18,10 +18,9 @@ menuconfig BPF_PRELOAD if BPF_PRELOAD config BPF_PRELOAD_UMD - tristate "bpf_preload kernel module with user mode driver" - depends on CC_CAN_LINK - depends on m || CC_CAN_LINK_STATIC + tristate "bpf_preload kernel module" default m help - This builds bpf_preload kernel module with embedded user mode driver. + This builds bpf_preload kernel module with embedded BPF programs for + introspection in bpffs. endif diff --git a/kernel/bpf/preload/Makefile b/kernel/bpf/preload/Makefile index 1400ac58178e..20f89cc0a0a6 100644 --- a/kernel/bpf/preload/Makefile +++ b/kernel/bpf/preload/Makefile @@ -1,42 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 -LIBBPF_SRCS = $(srctree)/tools/lib/bpf/ -LIBBPF_OUT = $(abspath $(obj))/libbpf -LIBBPF_A = $(LIBBPF_OUT)/libbpf.a -LIBBPF_DESTDIR = $(LIBBPF_OUT) -LIBBPF_INCLUDE = $(LIBBPF_DESTDIR)/include - -# Although not in use by libbpf's Makefile, set $(O) so that the "dummy" test -# in tools/scripts/Makefile.include always succeeds when building the kernel -# with $(O) pointing to a relative path, as in "make O=build bindeb-pkg". -$(LIBBPF_A): | $(LIBBPF_OUT) - $(Q)$(MAKE) -C $(LIBBPF_SRCS) O=$(LIBBPF_OUT)/ OUTPUT=$(LIBBPF_OUT)/ \ - DESTDIR=$(LIBBPF_DESTDIR) prefix= \ - $(LIBBPF_OUT)/libbpf.a install_headers - -libbpf_hdrs: $(LIBBPF_A) - -.PHONY: libbpf_hdrs - -$(LIBBPF_OUT): - $(call msg,MKDIR,$@) - $(Q)mkdir -p $@ - -userccflags += -I $(srctree)/tools/include/ -I $(srctree)/tools/include/uapi \ - -I $(LIBBPF_INCLUDE) -Wno-unused-result - -userprogs := bpf_preload_umd - -clean-files := libbpf/ - -$(obj)/iterators/iterators.o: | libbpf_hdrs - -bpf_preload_umd-objs := iterators/iterators.o -bpf_preload_umd-userldlibs := $(LIBBPF_A) -lelf -lz - -$(obj)/bpf_preload_umd: $(LIBBPF_A) - -$(obj)/bpf_preload_umd_blob.o: $(obj)/bpf_preload_umd +LIBBPF_INCLUDE = $(srctree)/tools/lib obj-$(CONFIG_BPF_PRELOAD_UMD) += bpf_preload.o -bpf_preload-objs += bpf_preload_kern.o bpf_preload_umd_blob.o +CFLAGS_bpf_preload_kern.o += -I$(LIBBPF_INCLUDE) +bpf_preload-objs += bpf_preload_kern.o diff --git a/kernel/bpf/preload/bpf_preload.h b/kernel/bpf/preload/bpf_preload.h index 2f9932276f2e..f065c91213a0 100644 --- a/kernel/bpf/preload/bpf_preload.h +++ b/kernel/bpf/preload/bpf_preload.h @@ -2,13 +2,13 @@ #ifndef _BPF_PRELOAD_H #define _BPF_PRELOAD_H -#include -#include "iterators/bpf_preload_common.h" +struct bpf_preload_info { + char link_name[16]; + struct bpf_link *link; +}; struct bpf_preload_ops { - struct umd_info info; int (*preload)(struct bpf_preload_info *); - int (*finish)(void); struct module *owner; }; extern struct bpf_preload_ops *bpf_preload_ops; diff --git a/kernel/bpf/preload/bpf_preload_kern.c b/kernel/bpf/preload/bpf_preload_kern.c index 53736e52c1df..5106b5372f0c 100644 --- a/kernel/bpf/preload/bpf_preload_kern.c +++ b/kernel/bpf/preload/bpf_preload_kern.c @@ -2,101 +2,87 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include -#include -#include -#include #include "bpf_preload.h" +#include "iterators/iterators.lskel.h" -extern char bpf_preload_umd_start; -extern char bpf_preload_umd_end; +static struct bpf_link *maps_link, *progs_link; +static struct iterators_bpf *skel; -static int preload(struct bpf_preload_info *obj); -static int finish(void); - -static struct bpf_preload_ops umd_ops = { - .info.driver_name = "bpf_preload", - .preload = preload, - .finish = finish, - .owner = THIS_MODULE, -}; +static void free_links_and_skel(void) +{ + if (!IS_ERR_OR_NULL(maps_link)) + bpf_link_put(maps_link); + if (!IS_ERR_OR_NULL(progs_link)) + bpf_link_put(progs_link); + iterators_bpf__destroy(skel); +} static int preload(struct bpf_preload_info *obj) { - int magic = BPF_PRELOAD_START; - loff_t pos = 0; - int i, err; - ssize_t n; - - err = fork_usermode_driver(&umd_ops.info); - if (err) - return err; - - /* send the start magic to let UMD proceed with loading BPF progs */ - n = kernel_write(umd_ops.info.pipe_to_umh, - &magic, sizeof(magic), &pos); - if (n != sizeof(magic)) - return -EPIPE; - - /* receive bpf_link IDs and names from UMD */ - pos = 0; - for (i = 0; i < BPF_PRELOAD_LINKS; i++) { - n = kernel_read(umd_ops.info.pipe_from_umh, - &obj[i], sizeof(*obj), &pos); - if (n != sizeof(*obj)) - return -EPIPE; - } + strlcpy(obj[0].link_name, "maps.debug", sizeof(obj[0].link_name)); + obj[0].link = maps_link; + strlcpy(obj[1].link_name, "progs.debug", sizeof(obj[1].link_name)); + obj[1].link = progs_link; return 0; } -static int finish(void) -{ - int magic = BPF_PRELOAD_END; - struct pid *tgid; - loff_t pos = 0; - ssize_t n; +static struct bpf_preload_ops ops = { + .preload = preload, + .owner = THIS_MODULE, +}; - /* send the last magic to UMD. It will do a normal exit. */ - n = kernel_write(umd_ops.info.pipe_to_umh, - &magic, sizeof(magic), &pos); - if (n != sizeof(magic)) - return -EPIPE; - - tgid = umd_ops.info.tgid; - if (tgid) { - wait_event(tgid->wait_pidfd, thread_group_exited(tgid)); - umd_cleanup_helper(&umd_ops.info); - } - return 0; -} - -static int __init load_umd(void) +static int load_skel(void) { int err; - err = umd_load_blob(&umd_ops.info, &bpf_preload_umd_start, - &bpf_preload_umd_end - &bpf_preload_umd_start); + skel = iterators_bpf__open(); + if (!skel) + return -ENOMEM; + err = iterators_bpf__load(skel); if (err) - return err; - bpf_preload_ops = &umd_ops; + goto out; + err = iterators_bpf__attach(skel); + if (err) + goto out; + maps_link = bpf_link_get_from_fd(skel->links.dump_bpf_map_fd); + if (IS_ERR(maps_link)) { + err = PTR_ERR(maps_link); + goto out; + } + progs_link = bpf_link_get_from_fd(skel->links.dump_bpf_prog_fd); + if (IS_ERR(progs_link)) { + err = PTR_ERR(progs_link); + goto out; + } + /* Avoid taking over stdin/stdout/stderr of init process. Zeroing out + * makes skel_closenz() a no-op later in iterators_bpf__destroy(). + */ + close_fd(skel->links.dump_bpf_map_fd); + skel->links.dump_bpf_map_fd = 0; + close_fd(skel->links.dump_bpf_prog_fd); + skel->links.dump_bpf_prog_fd = 0; + return 0; +out: + free_links_and_skel(); return err; } -static void __exit fini_umd(void) +static int __init load(void) { - struct pid *tgid; + int err; - bpf_preload_ops = NULL; - - /* kill UMD in case it's still there due to earlier error */ - tgid = umd_ops.info.tgid; - if (tgid) { - kill_pid(tgid, SIGKILL, 1); - - wait_event(tgid->wait_pidfd, thread_group_exited(tgid)); - umd_cleanup_helper(&umd_ops.info); - } - umd_unload_blob(&umd_ops.info); + err = load_skel(); + if (err) + return err; + bpf_preload_ops = &ops; + return err; } -late_initcall(load_umd); -module_exit(fini_umd); + +static void __exit fini(void) +{ + bpf_preload_ops = NULL; + free_links_and_skel(); +} +late_initcall(load); +module_exit(fini); MODULE_LICENSE("GPL"); diff --git a/kernel/bpf/preload/bpf_preload_umd_blob.S b/kernel/bpf/preload/bpf_preload_umd_blob.S deleted file mode 100644 index f1f40223b5c3..000000000000 --- a/kernel/bpf/preload/bpf_preload_umd_blob.S +++ /dev/null @@ -1,7 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - .section .init.rodata, "a" - .global bpf_preload_umd_start -bpf_preload_umd_start: - .incbin "kernel/bpf/preload/bpf_preload_umd" - .global bpf_preload_umd_end -bpf_preload_umd_end: diff --git a/kernel/bpf/preload/iterators/Makefile b/kernel/bpf/preload/iterators/Makefile index b8bd60511227..bfe24f8c5a20 100644 --- a/kernel/bpf/preload/iterators/Makefile +++ b/kernel/bpf/preload/iterators/Makefile @@ -35,15 +35,15 @@ endif .PHONY: all clean -all: iterators.skel.h +all: iterators.lskel.h clean: $(call msg,CLEAN) $(Q)rm -rf $(OUTPUT) iterators -iterators.skel.h: $(OUTPUT)/iterators.bpf.o | $(BPFTOOL) +iterators.lskel.h: $(OUTPUT)/iterators.bpf.o | $(BPFTOOL) $(call msg,GEN-SKEL,$@) - $(Q)$(BPFTOOL) gen skeleton $< > $@ + $(Q)$(BPFTOOL) gen skeleton -L $< > $@ $(OUTPUT)/iterators.bpf.o: iterators.bpf.c $(BPFOBJ) | $(OUTPUT) diff --git a/kernel/bpf/preload/iterators/bpf_preload_common.h b/kernel/bpf/preload/iterators/bpf_preload_common.h deleted file mode 100644 index 8464d1a48c05..000000000000 --- a/kernel/bpf/preload/iterators/bpf_preload_common.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _BPF_PRELOAD_COMMON_H -#define _BPF_PRELOAD_COMMON_H - -#define BPF_PRELOAD_START 0x5555 -#define BPF_PRELOAD_END 0xAAAA - -struct bpf_preload_info { - char link_name[16]; - int link_id; -}; - -#endif diff --git a/kernel/bpf/preload/iterators/iterators.c b/kernel/bpf/preload/iterators/iterators.c deleted file mode 100644 index 5d872a705470..000000000000 --- a/kernel/bpf/preload/iterators/iterators.c +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2020 Facebook */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "iterators.skel.h" -#include "bpf_preload_common.h" - -int to_kernel = -1; -int from_kernel = 0; - -static int send_link_to_kernel(struct bpf_link *link, const char *link_name) -{ - struct bpf_preload_info obj = {}; - struct bpf_link_info info = {}; - __u32 info_len = sizeof(info); - int err; - - err = bpf_obj_get_info_by_fd(bpf_link__fd(link), &info, &info_len); - if (err) - return err; - obj.link_id = info.id; - if (strlen(link_name) >= sizeof(obj.link_name)) - return -E2BIG; - strcpy(obj.link_name, link_name); - if (write(to_kernel, &obj, sizeof(obj)) != sizeof(obj)) - return -EPIPE; - return 0; -} - -int main(int argc, char **argv) -{ - struct rlimit rlim = { RLIM_INFINITY, RLIM_INFINITY }; - struct iterators_bpf *skel; - int err, magic; - int debug_fd; - - debug_fd = open("/dev/console", O_WRONLY | O_NOCTTY | O_CLOEXEC); - if (debug_fd < 0) - return 1; - to_kernel = dup(1); - close(1); - dup(debug_fd); - /* now stdin and stderr point to /dev/console */ - - read(from_kernel, &magic, sizeof(magic)); - if (magic != BPF_PRELOAD_START) { - printf("bad start magic %d\n", magic); - return 1; - } - setrlimit(RLIMIT_MEMLOCK, &rlim); - /* libbpf opens BPF object and loads it into the kernel */ - skel = iterators_bpf__open_and_load(); - if (!skel) { - /* iterators.skel.h is little endian. - * libbpf doesn't support automatic little->big conversion - * of BPF bytecode yet. - * The program load will fail in such case. - */ - printf("Failed load could be due to wrong endianness\n"); - return 1; - } - err = iterators_bpf__attach(skel); - if (err) - goto cleanup; - - /* send two bpf_link IDs with names to the kernel */ - err = send_link_to_kernel(skel->links.dump_bpf_map, "maps.debug"); - if (err) - goto cleanup; - err = send_link_to_kernel(skel->links.dump_bpf_prog, "progs.debug"); - if (err) - goto cleanup; - - /* The kernel will proceed with pinnging the links in bpffs. - * UMD will wait on read from pipe. - */ - read(from_kernel, &magic, sizeof(magic)); - if (magic != BPF_PRELOAD_END) { - printf("bad final magic %d\n", magic); - err = -EINVAL; - } -cleanup: - iterators_bpf__destroy(skel); - - return err != 0; -} diff --git a/kernel/bpf/preload/iterators/iterators.lskel.h b/kernel/bpf/preload/iterators/iterators.lskel.h new file mode 100644 index 000000000000..70f236a82fe1 --- /dev/null +++ b/kernel/bpf/preload/iterators/iterators.lskel.h @@ -0,0 +1,425 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +/* THIS FILE IS AUTOGENERATED! */ +#ifndef __ITERATORS_BPF_SKEL_H__ +#define __ITERATORS_BPF_SKEL_H__ + +#include + +struct iterators_bpf { + struct bpf_loader_ctx ctx; + struct { + struct bpf_map_desc rodata; + } maps; + struct { + struct bpf_prog_desc dump_bpf_map; + struct bpf_prog_desc dump_bpf_prog; + } progs; + struct { + int dump_bpf_map_fd; + int dump_bpf_prog_fd; + } links; + struct iterators_bpf__rodata { + } *rodata; +}; + +static inline int +iterators_bpf__dump_bpf_map__attach(struct iterators_bpf *skel) +{ + int prog_fd = skel->progs.dump_bpf_map.prog_fd; + int fd = skel_link_create(prog_fd, 0, BPF_TRACE_ITER); + + if (fd > 0) + skel->links.dump_bpf_map_fd = fd; + return fd; +} + +static inline int +iterators_bpf__dump_bpf_prog__attach(struct iterators_bpf *skel) +{ + int prog_fd = skel->progs.dump_bpf_prog.prog_fd; + int fd = skel_link_create(prog_fd, 0, BPF_TRACE_ITER); + + if (fd > 0) + skel->links.dump_bpf_prog_fd = fd; + return fd; +} + +static inline int +iterators_bpf__attach(struct iterators_bpf *skel) +{ + int ret = 0; + + ret = ret < 0 ? ret : iterators_bpf__dump_bpf_map__attach(skel); + ret = ret < 0 ? ret : iterators_bpf__dump_bpf_prog__attach(skel); + return ret < 0 ? ret : 0; +} + +static inline void +iterators_bpf__detach(struct iterators_bpf *skel) +{ + skel_closenz(skel->links.dump_bpf_map_fd); + skel_closenz(skel->links.dump_bpf_prog_fd); +} +static void +iterators_bpf__destroy(struct iterators_bpf *skel) +{ + if (!skel) + return; + iterators_bpf__detach(skel); + skel_closenz(skel->progs.dump_bpf_map.prog_fd); + skel_closenz(skel->progs.dump_bpf_prog.prog_fd); + skel_free_map_data(skel->rodata, skel->maps.rodata.initial_value, 4096); + skel_closenz(skel->maps.rodata.map_fd); + skel_free(skel); +} +static inline struct iterators_bpf * +iterators_bpf__open(void) +{ + struct iterators_bpf *skel; + + skel = skel_alloc(sizeof(*skel)); + if (!skel) + goto cleanup; + skel->ctx.sz = (void *)&skel->links - (void *)skel; + skel->rodata = skel_prep_map_data((void *)"\ +\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\x0a\0\x25\x34\x75\x20\ +\x25\x2d\x31\x36\x73\x25\x36\x64\x0a\0\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x61\x74\x74\x61\x63\x68\x65\ +\x64\x0a\0\x25\x34\x75\x20\x25\x2d\x31\x36\x73\x20\x25\x73\x20\x25\x73\x0a\0", 4096, 98); + if (!skel->rodata) + goto cleanup; + skel->maps.rodata.initial_value = (__u64) (long) skel->rodata; + return skel; +cleanup: + iterators_bpf__destroy(skel); + return NULL; +} + +static inline int +iterators_bpf__load(struct iterators_bpf *skel) +{ + struct bpf_load_and_run_opts opts = {}; + int err; + + opts.ctx = (struct bpf_loader_ctx *)skel; + opts.data_sz = 6056; + opts.data = (void *)"\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9f\xeb\x01\0\ +\x18\0\0\0\0\0\0\0\x1c\x04\0\0\x1c\x04\0\0\xf9\x04\0\0\0\0\0\0\0\0\0\x02\x02\0\ +\0\0\x01\0\0\0\x02\0\0\x04\x10\0\0\0\x13\0\0\0\x03\0\0\0\0\0\0\0\x18\0\0\0\x04\ +\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\x08\0\0\0\0\0\0\0\0\0\0\x02\x0d\0\0\0\0\0\0\ +\0\x01\0\0\x0d\x06\0\0\0\x1c\0\0\0\x01\0\0\0\x20\0\0\0\0\0\0\x01\x04\0\0\0\x20\ +\0\0\x01\x24\0\0\0\x01\0\0\x0c\x05\0\0\0\xa3\0\0\0\x03\0\0\x04\x18\0\0\0\xb1\0\ +\0\0\x09\0\0\0\0\0\0\0\xb5\0\0\0\x0b\0\0\0\x40\0\0\0\xc0\0\0\0\x0b\0\0\0\x80\0\ +\0\0\0\0\0\0\0\0\0\x02\x0a\0\0\0\xc8\0\0\0\0\0\0\x07\0\0\0\0\xd1\0\0\0\0\0\0\ +\x08\x0c\0\0\0\xd7\0\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\0\x94\x01\0\0\x03\0\0\x04\ +\x18\0\0\0\x9c\x01\0\0\x0e\0\0\0\0\0\0\0\x9f\x01\0\0\x11\0\0\0\x20\0\0\0\xa4\ +\x01\0\0\x0e\0\0\0\xa0\0\0\0\xb0\x01\0\0\0\0\0\x08\x0f\0\0\0\xb6\x01\0\0\0\0\0\ +\x01\x04\0\0\0\x20\0\0\0\xc3\x01\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x01\0\0\0\0\0\ +\0\0\x03\0\0\0\0\x10\0\0\0\x12\0\0\0\x10\0\0\0\xc8\x01\0\0\0\0\0\x01\x04\0\0\0\ +\x20\0\0\0\0\0\0\0\0\0\0\x02\x14\0\0\0\x2c\x02\0\0\x02\0\0\x04\x10\0\0\0\x13\0\ +\0\0\x03\0\0\0\0\0\0\0\x3f\x02\0\0\x15\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\x18\0\ +\0\0\0\0\0\0\x01\0\0\x0d\x06\0\0\0\x1c\0\0\0\x13\0\0\0\x44\x02\0\0\x01\0\0\x0c\ +\x16\0\0\0\x90\x02\0\0\x01\0\0\x04\x08\0\0\0\x99\x02\0\0\x19\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\x02\x1a\0\0\0\xea\x02\0\0\x06\0\0\x04\x38\0\0\0\x9c\x01\0\0\x0e\0\0\ +\0\0\0\0\0\x9f\x01\0\0\x11\0\0\0\x20\0\0\0\xf7\x02\0\0\x1b\0\0\0\xc0\0\0\0\x08\ +\x03\0\0\x15\0\0\0\0\x01\0\0\x11\x03\0\0\x1d\0\0\0\x40\x01\0\0\x1b\x03\0\0\x1e\ +\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\x02\x1c\0\0\0\0\0\0\0\0\0\0\x0a\x10\0\0\0\0\0\ +\0\0\0\0\0\x02\x1f\0\0\0\0\0\0\0\0\0\0\x02\x20\0\0\0\x65\x03\0\0\x02\0\0\x04\ +\x08\0\0\0\x73\x03\0\0\x0e\0\0\0\0\0\0\0\x7c\x03\0\0\x0e\0\0\0\x20\0\0\0\x1b\ +\x03\0\0\x03\0\0\x04\x18\0\0\0\x86\x03\0\0\x1b\0\0\0\0\0\0\0\x8e\x03\0\0\x21\0\ +\0\0\x40\0\0\0\x94\x03\0\0\x23\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\x02\x22\0\0\0\0\0\ +\0\0\0\0\0\x02\x24\0\0\0\x98\x03\0\0\x01\0\0\x04\x04\0\0\0\xa3\x03\0\0\x0e\0\0\ +\0\0\0\0\0\x0c\x04\0\0\x01\0\0\x04\x04\0\0\0\x15\x04\0\0\x0e\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\x03\0\0\0\0\x1c\0\0\0\x12\0\0\0\x23\0\0\0\x8b\x04\0\0\0\0\0\x0e\x25\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x1c\0\0\0\x12\0\0\0\x0e\0\0\0\x9f\x04\ +\0\0\0\0\0\x0e\x27\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x1c\0\0\0\x12\0\0\0\ +\x20\0\0\0\xb5\x04\0\0\0\0\0\x0e\x29\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\ +\x1c\0\0\0\x12\0\0\0\x11\0\0\0\xca\x04\0\0\0\0\0\x0e\x2b\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\x03\0\0\0\0\x10\0\0\0\x12\0\0\0\x04\0\0\0\xe1\x04\0\0\0\0\0\x0e\x2d\0\0\ +\0\x01\0\0\0\xe9\x04\0\0\x04\0\0\x0f\x62\0\0\0\x26\0\0\0\0\0\0\0\x23\0\0\0\x28\ +\0\0\0\x23\0\0\0\x0e\0\0\0\x2a\0\0\0\x31\0\0\0\x20\0\0\0\x2c\0\0\0\x51\0\0\0\ +\x11\0\0\0\xf1\x04\0\0\x01\0\0\x0f\x04\0\0\0\x2e\0\0\0\0\0\0\0\x04\0\0\0\0\x62\ +\x70\x66\x5f\x69\x74\x65\x72\x5f\x5f\x62\x70\x66\x5f\x6d\x61\x70\0\x6d\x65\x74\ +\x61\0\x6d\x61\x70\0\x63\x74\x78\0\x69\x6e\x74\0\x64\x75\x6d\x70\x5f\x62\x70\ +\x66\x5f\x6d\x61\x70\0\x69\x74\x65\x72\x2f\x62\x70\x66\x5f\x6d\x61\x70\0\x30\ +\x3a\x30\0\x2f\x77\x2f\x6e\x65\x74\x2d\x6e\x65\x78\x74\x2f\x6b\x65\x72\x6e\x65\ +\x6c\x2f\x62\x70\x66\x2f\x70\x72\x65\x6c\x6f\x61\x64\x2f\x69\x74\x65\x72\x61\ +\x74\x6f\x72\x73\x2f\x69\x74\x65\x72\x61\x74\x6f\x72\x73\x2e\x62\x70\x66\x2e\ +\x63\0\x09\x73\x74\x72\x75\x63\x74\x20\x73\x65\x71\x5f\x66\x69\x6c\x65\x20\x2a\ +\x73\x65\x71\x20\x3d\x20\x63\x74\x78\x2d\x3e\x6d\x65\x74\x61\x2d\x3e\x73\x65\ +\x71\x3b\0\x62\x70\x66\x5f\x69\x74\x65\x72\x5f\x6d\x65\x74\x61\0\x73\x65\x71\0\ +\x73\x65\x73\x73\x69\x6f\x6e\x5f\x69\x64\0\x73\x65\x71\x5f\x6e\x75\x6d\0\x73\ +\x65\x71\x5f\x66\x69\x6c\x65\0\x5f\x5f\x75\x36\x34\0\x75\x6e\x73\x69\x67\x6e\ +\x65\x64\x20\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\x67\0\x30\x3a\x31\0\x09\x73\x74\ +\x72\x75\x63\x74\x20\x62\x70\x66\x5f\x6d\x61\x70\x20\x2a\x6d\x61\x70\x20\x3d\ +\x20\x63\x74\x78\x2d\x3e\x6d\x61\x70\x3b\0\x09\x69\x66\x20\x28\x21\x6d\x61\x70\ +\x29\0\x09\x5f\x5f\x75\x36\x34\x20\x73\x65\x71\x5f\x6e\x75\x6d\x20\x3d\x20\x63\ +\x74\x78\x2d\x3e\x6d\x65\x74\x61\x2d\x3e\x73\x65\x71\x5f\x6e\x75\x6d\x3b\0\x30\ +\x3a\x32\0\x09\x69\x66\x20\x28\x73\x65\x71\x5f\x6e\x75\x6d\x20\x3d\x3d\x20\x30\ +\x29\0\x09\x09\x42\x50\x46\x5f\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\ +\x65\x71\x2c\x20\x22\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\ +\x5c\x6e\x22\x29\x3b\0\x62\x70\x66\x5f\x6d\x61\x70\0\x69\x64\0\x6e\x61\x6d\x65\ +\0\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x5f\x5f\x75\x33\x32\0\x75\x6e\ +\x73\x69\x67\x6e\x65\x64\x20\x69\x6e\x74\0\x63\x68\x61\x72\0\x5f\x5f\x41\x52\ +\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0\x09\x42\x50\x46\ +\x5f\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\x65\x71\x2c\x20\x22\x25\ +\x34\x75\x20\x25\x2d\x31\x36\x73\x25\x36\x64\x5c\x6e\x22\x2c\x20\x6d\x61\x70\ +\x2d\x3e\x69\x64\x2c\x20\x6d\x61\x70\x2d\x3e\x6e\x61\x6d\x65\x2c\x20\x6d\x61\ +\x70\x2d\x3e\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\x29\x3b\0\x7d\0\x62\ +\x70\x66\x5f\x69\x74\x65\x72\x5f\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x70\x72\ +\x6f\x67\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x69\x74\x65\ +\x72\x2f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x09\x73\x74\x72\x75\x63\x74\x20\x62\ +\x70\x66\x5f\x70\x72\x6f\x67\x20\x2a\x70\x72\x6f\x67\x20\x3d\x20\x63\x74\x78\ +\x2d\x3e\x70\x72\x6f\x67\x3b\0\x09\x69\x66\x20\x28\x21\x70\x72\x6f\x67\x29\0\ +\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x61\x75\x78\0\x09\x61\x75\x78\x20\x3d\x20\ +\x70\x72\x6f\x67\x2d\x3e\x61\x75\x78\x3b\0\x09\x09\x42\x50\x46\x5f\x53\x45\x51\ +\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\x65\x71\x2c\x20\x22\x20\x20\x69\x64\x20\ +\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x61\x74\ +\x74\x61\x63\x68\x65\x64\x5c\x6e\x22\x29\x3b\0\x62\x70\x66\x5f\x70\x72\x6f\x67\ +\x5f\x61\x75\x78\0\x61\x74\x74\x61\x63\x68\x5f\x66\x75\x6e\x63\x5f\x6e\x61\x6d\ +\x65\0\x64\x73\x74\x5f\x70\x72\x6f\x67\0\x66\x75\x6e\x63\x5f\x69\x6e\x66\x6f\0\ +\x62\x74\x66\0\x09\x42\x50\x46\x5f\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\ +\x73\x65\x71\x2c\x20\x22\x25\x34\x75\x20\x25\x2d\x31\x36\x73\x20\x25\x73\x20\ +\x25\x73\x5c\x6e\x22\x2c\x20\x61\x75\x78\x2d\x3e\x69\x64\x2c\0\x30\x3a\x34\0\ +\x30\x3a\x35\0\x09\x69\x66\x20\x28\x21\x62\x74\x66\x29\0\x62\x70\x66\x5f\x66\ +\x75\x6e\x63\x5f\x69\x6e\x66\x6f\0\x69\x6e\x73\x6e\x5f\x6f\x66\x66\0\x74\x79\ +\x70\x65\x5f\x69\x64\0\x30\0\x73\x74\x72\x69\x6e\x67\x73\0\x74\x79\x70\x65\x73\ +\0\x68\x64\x72\0\x62\x74\x66\x5f\x68\x65\x61\x64\x65\x72\0\x73\x74\x72\x5f\x6c\ +\x65\x6e\0\x09\x74\x79\x70\x65\x73\x20\x3d\x20\x62\x74\x66\x2d\x3e\x74\x79\x70\ +\x65\x73\x3b\0\x09\x62\x70\x66\x5f\x70\x72\x6f\x62\x65\x5f\x72\x65\x61\x64\x5f\ +\x6b\x65\x72\x6e\x65\x6c\x28\x26\x74\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x74\ +\x29\x2c\x20\x74\x79\x70\x65\x73\x20\x2b\x20\x62\x74\x66\x5f\x69\x64\x29\x3b\0\ +\x09\x73\x74\x72\x20\x3d\x20\x62\x74\x66\x2d\x3e\x73\x74\x72\x69\x6e\x67\x73\ +\x3b\0\x62\x74\x66\x5f\x74\x79\x70\x65\0\x6e\x61\x6d\x65\x5f\x6f\x66\x66\0\x09\ +\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x20\x3d\x20\x42\x50\x46\x5f\x43\x4f\x52\x45\ +\x5f\x52\x45\x41\x44\x28\x74\x2c\x20\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x29\x3b\0\ +\x30\x3a\x32\x3a\x30\0\x09\x69\x66\x20\x28\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x20\ +\x3e\x3d\x20\x62\x74\x66\x2d\x3e\x68\x64\x72\x2e\x73\x74\x72\x5f\x6c\x65\x6e\ +\x29\0\x09\x72\x65\x74\x75\x72\x6e\x20\x73\x74\x72\x20\x2b\x20\x6e\x61\x6d\x65\ +\x5f\x6f\x66\x66\x3b\0\x30\x3a\x33\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\ +\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\x74\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\ +\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\x74\x2e\x31\0\x64\x75\x6d\x70\x5f\x62\x70\x66\ +\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\x5f\x66\x6d\x74\0\x64\x75\x6d\x70\x5f\x62\x70\ +\x66\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\x5f\x66\x6d\x74\x2e\x32\0\x4c\x49\x43\x45\ +\x4e\x53\x45\0\x2e\x72\x6f\x64\x61\x74\x61\0\x6c\x69\x63\x65\x6e\x73\x65\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2d\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\ +\0\x04\0\0\0\x62\0\0\0\x01\0\0\0\x80\x04\0\0\0\0\0\0\0\0\0\0\x69\x74\x65\x72\ +\x61\x74\x6f\x72\x2e\x72\x6f\x64\x61\x74\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\x2f\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\ +\x73\x0a\0\x25\x34\x75\x20\x25\x2d\x31\x36\x73\x25\x36\x64\x0a\0\x20\x20\x69\ +\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x61\x74\x74\x61\x63\x68\x65\x64\x0a\0\x25\x34\x75\x20\x25\x2d\x31\x36\x73\x20\ +\x25\x73\x20\x25\x73\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\ +\x79\x12\0\0\0\0\0\0\x79\x26\0\0\0\0\0\0\x79\x17\x08\0\0\0\0\0\x15\x07\x1b\0\0\ +\0\0\0\x79\x11\0\0\0\0\0\0\x79\x11\x10\0\0\0\0\0\x55\x01\x08\0\0\0\0\0\xbf\xa4\ +\0\0\0\0\0\0\x07\x04\0\0\xe8\xff\xff\xff\xbf\x61\0\0\0\0\0\0\x18\x62\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\xb7\x03\0\0\x23\0\0\0\xb7\x05\0\0\0\0\0\0\x85\0\0\0\x7e\0\0\ +\0\x61\x71\0\0\0\0\0\0\x7b\x1a\xe8\xff\0\0\0\0\xb7\x01\0\0\x04\0\0\0\xbf\x72\0\ +\0\0\0\0\0\x0f\x12\0\0\0\0\0\0\x7b\x2a\xf0\xff\0\0\0\0\x61\x71\x14\0\0\0\0\0\ +\x7b\x1a\xf8\xff\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\xe8\xff\xff\xff\xbf\ +\x61\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x23\0\0\0\xb7\x03\0\0\x0e\0\0\0\ +\xb7\x05\0\0\x18\0\0\0\x85\0\0\0\x7e\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\ +\0\0\0\0\x07\0\0\0\0\0\0\0\x42\0\0\0\x7b\0\0\0\x1e\x3c\x01\0\x01\0\0\0\x42\0\0\ +\0\x7b\0\0\0\x24\x3c\x01\0\x02\0\0\0\x42\0\0\0\xee\0\0\0\x1d\x44\x01\0\x03\0\0\ +\0\x42\0\0\0\x0f\x01\0\0\x06\x4c\x01\0\x04\0\0\0\x42\0\0\0\x1a\x01\0\0\x17\x40\ +\x01\0\x05\0\0\0\x42\0\0\0\x1a\x01\0\0\x1d\x40\x01\0\x06\0\0\0\x42\0\0\0\x43\ +\x01\0\0\x06\x58\x01\0\x08\0\0\0\x42\0\0\0\x56\x01\0\0\x03\x5c\x01\0\x0f\0\0\0\ +\x42\0\0\0\xdc\x01\0\0\x02\x64\x01\0\x1f\0\0\0\x42\0\0\0\x2a\x02\0\0\x01\x6c\ +\x01\0\0\0\0\0\x02\0\0\0\x3e\0\0\0\0\0\0\0\x08\0\0\0\x08\0\0\0\x3e\0\0\0\0\0\0\ +\0\x10\0\0\0\x02\0\0\0\xea\0\0\0\0\0\0\0\x20\0\0\0\x02\0\0\0\x3e\0\0\0\0\0\0\0\ +\x28\0\0\0\x08\0\0\0\x3f\x01\0\0\0\0\0\0\x78\0\0\0\x0d\0\0\0\x3e\0\0\0\0\0\0\0\ +\x88\0\0\0\x0d\0\0\0\xea\0\0\0\0\0\0\0\xa8\0\0\0\x0d\0\0\0\x3f\x01\0\0\0\0\0\0\ +\x1a\0\0\0\x21\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\x61\x70\0\0\0\0\ +\0\0\0\0\x1c\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x10\0\0\0\0\0\0\ +\0\0\0\0\0\x0a\0\0\0\x01\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\x10\0\0\0\0\0\0\0\x62\x70\x66\x5f\x69\x74\x65\x72\x5f\x62\x70\x66\x5f\x6d\ +\x61\x70\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\x79\x12\0\0\0\0\0\0\x79\x26\0\0\ +\0\0\0\0\x79\x12\x08\0\0\0\0\0\x15\x02\x3c\0\0\0\0\0\x79\x11\0\0\0\0\0\0\x79\ +\x27\0\0\0\0\0\0\x79\x11\x10\0\0\0\0\0\x55\x01\x08\0\0\0\0\0\xbf\xa4\0\0\0\0\0\ +\0\x07\x04\0\0\xd0\xff\xff\xff\xbf\x61\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\ +\x31\0\0\0\xb7\x03\0\0\x20\0\0\0\xb7\x05\0\0\0\0\0\0\x85\0\0\0\x7e\0\0\0\x7b\ +\x6a\xc8\xff\0\0\0\0\x61\x71\0\0\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xb7\x03\0\0\ +\x04\0\0\0\xbf\x79\0\0\0\0\0\0\x0f\x39\0\0\0\0\0\0\x79\x71\x28\0\0\0\0\0\x79\ +\x78\x30\0\0\0\0\0\x15\x08\x18\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\x0f\x21\0\0\0\0\0\ +\0\x61\x11\x04\0\0\0\0\0\x79\x83\x08\0\0\0\0\0\x67\x01\0\0\x03\0\0\0\x0f\x13\0\ +\0\0\0\0\0\x79\x86\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\xf8\xff\xff\xff\ +\xb7\x02\0\0\x08\0\0\0\x85\0\0\0\x71\0\0\0\xb7\x01\0\0\0\0\0\0\x79\xa3\xf8\xff\ +\0\0\0\0\x0f\x13\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\xf4\xff\xff\xff\ +\xb7\x02\0\0\x04\0\0\0\x85\0\0\0\x71\0\0\0\xb7\x03\0\0\x04\0\0\0\x61\xa1\xf4\ +\xff\0\0\0\0\x61\x82\x10\0\0\0\0\0\x3d\x21\x02\0\0\0\0\0\x0f\x16\0\0\0\0\0\0\ +\xbf\x69\0\0\0\0\0\0\x7b\x9a\xd8\xff\0\0\0\0\x79\x71\x18\0\0\0\0\0\x7b\x1a\xe0\ +\xff\0\0\0\0\x79\x71\x20\0\0\0\0\0\x79\x11\0\0\0\0\0\0\x0f\x31\0\0\0\0\0\0\x7b\ +\x1a\xe8\xff\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\xd0\xff\xff\xff\x79\xa1\ +\xc8\xff\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x51\0\0\0\xb7\x03\0\0\x11\0\0\0\ +\xb7\x05\0\0\x20\0\0\0\x85\0\0\0\x7e\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\ +\0\0\0\0\x17\0\0\0\0\0\0\0\x42\0\0\0\x7b\0\0\0\x1e\x80\x01\0\x01\0\0\0\x42\0\0\ +\0\x7b\0\0\0\x24\x80\x01\0\x02\0\0\0\x42\0\0\0\x60\x02\0\0\x1f\x88\x01\0\x03\0\ +\0\0\x42\0\0\0\x84\x02\0\0\x06\x94\x01\0\x04\0\0\0\x42\0\0\0\x1a\x01\0\0\x17\ +\x84\x01\0\x05\0\0\0\x42\0\0\0\x9d\x02\0\0\x0e\xa0\x01\0\x06\0\0\0\x42\0\0\0\ +\x1a\x01\0\0\x1d\x84\x01\0\x07\0\0\0\x42\0\0\0\x43\x01\0\0\x06\xa4\x01\0\x09\0\ +\0\0\x42\0\0\0\xaf\x02\0\0\x03\xa8\x01\0\x11\0\0\0\x42\0\0\0\x1f\x03\0\0\x02\ +\xb0\x01\0\x18\0\0\0\x42\0\0\0\x5a\x03\0\0\x06\x04\x01\0\x1b\0\0\0\x42\0\0\0\0\ +\0\0\0\0\0\0\0\x1c\0\0\0\x42\0\0\0\xab\x03\0\0\x0f\x10\x01\0\x1d\0\0\0\x42\0\0\ +\0\xc0\x03\0\0\x2d\x14\x01\0\x1f\0\0\0\x42\0\0\0\xf7\x03\0\0\x0d\x0c\x01\0\x21\ +\0\0\0\x42\0\0\0\0\0\0\0\0\0\0\0\x22\0\0\0\x42\0\0\0\xc0\x03\0\0\x02\x14\x01\0\ +\x25\0\0\0\x42\0\0\0\x1e\x04\0\0\x0d\x18\x01\0\x28\0\0\0\x42\0\0\0\0\0\0\0\0\0\ +\0\0\x29\0\0\0\x42\0\0\0\x1e\x04\0\0\x0d\x18\x01\0\x2c\0\0\0\x42\0\0\0\x1e\x04\ +\0\0\x0d\x18\x01\0\x2d\0\0\0\x42\0\0\0\x4c\x04\0\0\x1b\x1c\x01\0\x2e\0\0\0\x42\ +\0\0\0\x4c\x04\0\0\x06\x1c\x01\0\x2f\0\0\0\x42\0\0\0\x6f\x04\0\0\x0d\x24\x01\0\ +\x31\0\0\0\x42\0\0\0\x1f\x03\0\0\x02\xb0\x01\0\x40\0\0\0\x42\0\0\0\x2a\x02\0\0\ +\x01\xc0\x01\0\0\0\0\0\x14\0\0\0\x3e\0\0\0\0\0\0\0\x08\0\0\0\x08\0\0\0\x3e\0\0\ +\0\0\0\0\0\x10\0\0\0\x14\0\0\0\xea\0\0\0\0\0\0\0\x20\0\0\0\x14\0\0\0\x3e\0\0\0\ +\0\0\0\0\x28\0\0\0\x18\0\0\0\x3e\0\0\0\0\0\0\0\x30\0\0\0\x08\0\0\0\x3f\x01\0\0\ +\0\0\0\0\x88\0\0\0\x1a\0\0\0\x3e\0\0\0\0\0\0\0\x98\0\0\0\x1a\0\0\0\xea\0\0\0\0\ +\0\0\0\xb0\0\0\0\x1a\0\0\0\x52\x03\0\0\0\0\0\0\xb8\0\0\0\x1a\0\0\0\x56\x03\0\0\ +\0\0\0\0\xc8\0\0\0\x1f\0\0\0\x84\x03\0\0\0\0\0\0\xe0\0\0\0\x20\0\0\0\xea\0\0\0\ +\0\0\0\0\xf8\0\0\0\x20\0\0\0\x3e\0\0\0\0\0\0\0\x20\x01\0\0\x24\0\0\0\x3e\0\0\0\ +\0\0\0\0\x58\x01\0\0\x1a\0\0\0\xea\0\0\0\0\0\0\0\x68\x01\0\0\x20\0\0\0\x46\x04\ +\0\0\0\0\0\0\x90\x01\0\0\x1a\0\0\0\x3f\x01\0\0\0\0\0\0\xa0\x01\0\0\x1a\0\0\0\ +\x87\x04\0\0\0\0\0\0\xa8\x01\0\0\x18\0\0\0\x3e\0\0\0\0\0\0\0\x1a\0\0\0\x42\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\0\0\0\0\0\0\x1c\0\0\ +\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\x1a\0\ +\0\0\x01\0\0\0\0\0\0\0\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\ +\0\0\x62\x70\x66\x5f\x69\x74\x65\x72\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\0\0\ +\0\0\0\0"; + opts.insns_sz = 2216; + opts.insns = (void *)"\ +\xbf\x16\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\x78\xff\xff\xff\xb7\x02\0\ +\0\x88\0\0\0\xb7\x03\0\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x05\0\x14\0\0\0\0\0\x61\ +\xa1\x78\xff\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x7c\xff\ +\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x80\xff\0\0\0\0\xd5\ +\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x84\xff\0\0\0\0\xd5\x01\x01\0\0\ +\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x01\0\0\0\0\ +\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\xbf\x70\0\0\ +\0\0\0\0\x95\0\0\0\0\0\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\ +\x48\x0e\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\ +\0\0\x44\x0e\0\0\x63\x01\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\ +\0\0\0\0\x38\x0e\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x05\0\0\ +\x18\x61\0\0\0\0\0\0\0\0\0\0\x30\x0e\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x12\0\ +\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x30\x0e\0\0\xb7\x03\0\0\x1c\0\0\0\x85\0\0\0\ +\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xd4\xff\0\0\0\0\x63\x7a\x78\xff\0\0\0\0\ +\x61\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x80\x0e\0\0\x63\x01\0\0\0\ +\0\0\0\x61\x60\x1c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\ +\x5c\x0e\0\0\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\ +\0\x50\x0e\0\0\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\ +\xc5\x07\xc3\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x63\x71\0\0\0\0\0\ +\0\x79\x63\x20\0\0\0\0\0\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x98\ +\x0e\0\0\xb7\x02\0\0\x62\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\ +\0\0\0\x94\0\0\0\x05\0\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x08\x0f\0\0\x63\ +\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x0f\0\0\x18\x61\0\0\0\0\0\0\0\0\ +\0\0\x10\x0f\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x98\x0e\0\0\ +\x18\x61\0\0\0\0\0\0\0\0\0\0\x18\x0f\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\ +\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x08\x0f\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\ +\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x9f\xff\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0f\0\0\x63\ +\x01\0\0\0\0\0\0\xb7\x01\0\0\x16\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x28\x0f\0\0\ +\xb7\x03\0\0\x04\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x92\xff\ +\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x30\x0f\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\ +\x78\x11\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x38\x0f\0\0\x18\ +\x61\0\0\0\0\0\0\0\0\0\0\x70\x11\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\ +\0\0\0\x40\x10\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb8\x11\0\0\x7b\x01\0\0\0\0\0\0\ +\x18\x60\0\0\0\0\0\0\0\0\0\0\x48\x10\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc8\x11\0\ +\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xe8\x10\0\0\x18\x61\0\0\0\0\ +\0\0\0\0\0\0\xe8\x11\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xe0\x11\0\0\x7b\x01\0\0\0\0\0\0\x61\x60\x08\0\0\ +\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x80\x11\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x0c\ +\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x84\x11\0\0\x63\x01\0\0\0\0\0\0\x79\x60\ +\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x88\x11\0\0\x7b\x01\0\0\0\0\0\0\x61\ +\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb0\x11\0\0\x63\x01\0\0\0\0\0\ +\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf8\x11\0\0\xb7\x02\0\0\x11\0\0\0\xb7\x03\0\0\ +\x0c\0\0\0\xb7\x04\0\0\0\0\0\0\x85\0\0\0\xa7\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\ +\x5c\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x68\x11\0\0\x63\x70\x6c\0\0\0\0\0\ +\x77\x07\0\0\x20\0\0\0\x63\x70\x70\0\0\0\0\0\xb7\x01\0\0\x05\0\0\0\x18\x62\0\0\ +\0\0\0\0\0\0\0\0\x68\x11\0\0\xb7\x03\0\0\x8c\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\ +\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xd8\x11\0\0\x61\x01\0\0\0\0\0\0\xd5\ +\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\xc5\x07\x4a\xff\0\0\ +\0\0\x63\x7a\x80\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x10\x12\0\0\x18\x61\0\ +\0\0\0\0\0\0\0\0\0\x10\x17\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\ +\x18\x12\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x08\x17\0\0\x7b\x01\0\0\0\0\0\0\x18\ +\x60\0\0\0\0\0\0\0\0\0\0\x28\x14\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x50\x17\0\0\ +\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x30\x14\0\0\x18\x61\0\0\0\0\0\ +\0\0\0\0\0\x60\x17\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xd0\x15\ +\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x80\x17\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x78\x17\0\0\x7b\x01\0\0\0\0\ +\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x18\x17\0\0\x63\x01\0\0\ +\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x1c\x17\0\0\x63\x01\ +\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x20\x17\0\0\x7b\ +\x01\0\0\0\0\0\0\x61\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x48\x17\0\ +\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x17\0\0\xb7\x02\0\0\x12\ +\0\0\0\xb7\x03\0\0\x0c\0\0\0\xb7\x04\0\0\0\0\0\0\x85\0\0\0\xa7\0\0\0\xbf\x07\0\ +\0\0\0\0\0\xc5\x07\x13\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x17\0\0\x63\ +\x70\x6c\0\0\0\0\0\x77\x07\0\0\x20\0\0\0\x63\x70\x70\0\0\0\0\0\xb7\x01\0\0\x05\ +\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\0\x17\0\0\xb7\x03\0\0\x8c\0\0\0\x85\0\0\0\ +\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x70\x17\0\0\x61\x01\ +\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\xc5\ +\x07\x01\xff\0\0\0\0\x63\x7a\x84\xff\0\0\0\0\x61\xa1\x78\xff\0\0\0\0\xd5\x01\ +\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa0\x80\xff\0\0\0\0\ +\x63\x06\x28\0\0\0\0\0\x61\xa0\x84\xff\0\0\0\0\x63\x06\x2c\0\0\0\0\0\x18\x61\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x18\0\0\0\0\0\xb7\0\0\0\ +\0\0\0\0\x95\0\0\0\0\0\0\0"; + err = bpf_load_and_run(&opts); + if (err < 0) + return err; + skel->rodata = skel_finalize_map_data(&skel->maps.rodata.initial_value, + 4096, PROT_READ, skel->maps.rodata.map_fd); + if (!skel->rodata) + return -ENOMEM; + return 0; +} + +static inline struct iterators_bpf * +iterators_bpf__open_and_load(void) +{ + struct iterators_bpf *skel; + + skel = iterators_bpf__open(); + if (!skel) + return NULL; + if (iterators_bpf__load(skel)) { + iterators_bpf__destroy(skel); + return NULL; + } + return skel; +} + +#endif /* __ITERATORS_BPF_SKEL_H__ */ diff --git a/kernel/bpf/preload/iterators/iterators.skel.h b/kernel/bpf/preload/iterators/iterators.skel.h deleted file mode 100644 index cf9a6a94b3a4..000000000000 --- a/kernel/bpf/preload/iterators/iterators.skel.h +++ /dev/null @@ -1,412 +0,0 @@ -/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ - -/* THIS FILE IS AUTOGENERATED! */ -#ifndef __ITERATORS_BPF_SKEL_H__ -#define __ITERATORS_BPF_SKEL_H__ - -#include -#include - -struct iterators_bpf { - struct bpf_object_skeleton *skeleton; - struct bpf_object *obj; - struct { - struct bpf_map *rodata; - } maps; - struct { - struct bpf_program *dump_bpf_map; - struct bpf_program *dump_bpf_prog; - } progs; - struct { - struct bpf_link *dump_bpf_map; - struct bpf_link *dump_bpf_prog; - } links; - struct iterators_bpf__rodata { - char dump_bpf_map____fmt[35]; - char dump_bpf_map____fmt_1[14]; - char dump_bpf_prog____fmt[32]; - char dump_bpf_prog____fmt_2[17]; - } *rodata; -}; - -static void -iterators_bpf__destroy(struct iterators_bpf *obj) -{ - if (!obj) - return; - if (obj->skeleton) - bpf_object__destroy_skeleton(obj->skeleton); - free(obj); -} - -static inline int -iterators_bpf__create_skeleton(struct iterators_bpf *obj); - -static inline struct iterators_bpf * -iterators_bpf__open_opts(const struct bpf_object_open_opts *opts) -{ - struct iterators_bpf *obj; - - obj = (struct iterators_bpf *)calloc(1, sizeof(*obj)); - if (!obj) - return NULL; - if (iterators_bpf__create_skeleton(obj)) - goto err; - if (bpf_object__open_skeleton(obj->skeleton, opts)) - goto err; - - return obj; -err: - iterators_bpf__destroy(obj); - return NULL; -} - -static inline struct iterators_bpf * -iterators_bpf__open(void) -{ - return iterators_bpf__open_opts(NULL); -} - -static inline int -iterators_bpf__load(struct iterators_bpf *obj) -{ - return bpf_object__load_skeleton(obj->skeleton); -} - -static inline struct iterators_bpf * -iterators_bpf__open_and_load(void) -{ - struct iterators_bpf *obj; - - obj = iterators_bpf__open(); - if (!obj) - return NULL; - if (iterators_bpf__load(obj)) { - iterators_bpf__destroy(obj); - return NULL; - } - return obj; -} - -static inline int -iterators_bpf__attach(struct iterators_bpf *obj) -{ - return bpf_object__attach_skeleton(obj->skeleton); -} - -static inline void -iterators_bpf__detach(struct iterators_bpf *obj) -{ - return bpf_object__detach_skeleton(obj->skeleton); -} - -static inline int -iterators_bpf__create_skeleton(struct iterators_bpf *obj) -{ - struct bpf_object_skeleton *s; - - s = (struct bpf_object_skeleton *)calloc(1, sizeof(*s)); - if (!s) - return -1; - obj->skeleton = s; - - s->sz = sizeof(*s); - s->name = "iterators_bpf"; - s->obj = &obj->obj; - - /* maps */ - s->map_cnt = 1; - s->map_skel_sz = sizeof(*s->maps); - s->maps = (struct bpf_map_skeleton *)calloc(s->map_cnt, s->map_skel_sz); - if (!s->maps) - goto err; - - s->maps[0].name = "iterator.rodata"; - s->maps[0].map = &obj->maps.rodata; - s->maps[0].mmaped = (void **)&obj->rodata; - - /* programs */ - s->prog_cnt = 2; - s->prog_skel_sz = sizeof(*s->progs); - s->progs = (struct bpf_prog_skeleton *)calloc(s->prog_cnt, s->prog_skel_sz); - if (!s->progs) - goto err; - - s->progs[0].name = "dump_bpf_map"; - s->progs[0].prog = &obj->progs.dump_bpf_map; - s->progs[0].link = &obj->links.dump_bpf_map; - - s->progs[1].name = "dump_bpf_prog"; - s->progs[1].prog = &obj->progs.dump_bpf_prog; - s->progs[1].link = &obj->links.dump_bpf_prog; - - s->data_sz = 7176; - s->data = (void *)"\ -\x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\x48\x18\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0f\0\ -\x0e\0\x79\x12\0\0\0\0\0\0\x79\x26\0\0\0\0\0\0\x79\x17\x08\0\0\0\0\0\x15\x07\ -\x1a\0\0\0\0\0\x79\x21\x10\0\0\0\0\0\x55\x01\x08\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\ -\x07\x04\0\0\xe8\xff\xff\xff\xbf\x61\0\0\0\0\0\0\x18\x02\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\xb7\x03\0\0\x23\0\0\0\xb7\x05\0\0\0\0\0\0\x85\0\0\0\x7e\0\0\0\x61\x71\0\ -\0\0\0\0\0\x7b\x1a\xe8\xff\0\0\0\0\xb7\x01\0\0\x04\0\0\0\xbf\x72\0\0\0\0\0\0\ -\x0f\x12\0\0\0\0\0\0\x7b\x2a\xf0\xff\0\0\0\0\x61\x71\x14\0\0\0\0\0\x7b\x1a\xf8\ -\xff\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\xe8\xff\xff\xff\xbf\x61\0\0\0\0\0\ -\0\x18\x02\0\0\x23\0\0\0\0\0\0\0\0\0\0\0\xb7\x03\0\0\x0e\0\0\0\xb7\x05\0\0\x18\ -\0\0\0\x85\0\0\0\x7e\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\x79\x12\0\0\0\0\ -\0\0\x79\x26\0\0\0\0\0\0\x79\x11\x08\0\0\0\0\0\x15\x01\x3b\0\0\0\0\0\x79\x17\0\ -\0\0\0\0\0\x79\x21\x10\0\0\0\0\0\x55\x01\x08\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\ -\x04\0\0\xd0\xff\xff\xff\xbf\x61\0\0\0\0\0\0\x18\x02\0\0\x31\0\0\0\0\0\0\0\0\0\ -\0\0\xb7\x03\0\0\x20\0\0\0\xb7\x05\0\0\0\0\0\0\x85\0\0\0\x7e\0\0\0\x7b\x6a\xc8\ -\xff\0\0\0\0\x61\x71\0\0\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xb7\x03\0\0\x04\0\0\0\ -\xbf\x79\0\0\0\0\0\0\x0f\x39\0\0\0\0\0\0\x79\x71\x28\0\0\0\0\0\x79\x78\x30\0\0\ -\0\0\0\x15\x08\x18\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\x0f\x21\0\0\0\0\0\0\x61\x11\ -\x04\0\0\0\0\0\x79\x83\x08\0\0\0\0\0\x67\x01\0\0\x03\0\0\0\x0f\x13\0\0\0\0\0\0\ -\x79\x86\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\xf8\xff\xff\xff\xb7\x02\0\ -\0\x08\0\0\0\x85\0\0\0\x71\0\0\0\xb7\x01\0\0\0\0\0\0\x79\xa3\xf8\xff\0\0\0\0\ -\x0f\x13\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\xf4\xff\xff\xff\xb7\x02\0\ -\0\x04\0\0\0\x85\0\0\0\x71\0\0\0\xb7\x03\0\0\x04\0\0\0\x61\xa1\xf4\xff\0\0\0\0\ -\x61\x82\x10\0\0\0\0\0\x3d\x21\x02\0\0\0\0\0\x0f\x16\0\0\0\0\0\0\xbf\x69\0\0\0\ -\0\0\0\x7b\x9a\xd8\xff\0\0\0\0\x79\x71\x18\0\0\0\0\0\x7b\x1a\xe0\xff\0\0\0\0\ -\x79\x71\x20\0\0\0\0\0\x79\x11\0\0\0\0\0\0\x0f\x31\0\0\0\0\0\0\x7b\x1a\xe8\xff\ -\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\xd0\xff\xff\xff\x79\xa1\xc8\xff\0\0\0\ -\0\x18\x02\0\0\x51\0\0\0\0\0\0\0\0\0\0\0\xb7\x03\0\0\x11\0\0\0\xb7\x05\0\0\x20\ -\0\0\0\x85\0\0\0\x7e\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\x20\x20\x69\x64\ -\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6d\ -\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\x0a\0\x25\x34\x75\x20\x25\x2d\x31\x36\ -\x73\x25\x36\x64\x0a\0\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x61\x74\x74\x61\x63\x68\x65\x64\x0a\0\x25\x34\ -\x75\x20\x25\x2d\x31\x36\x73\x20\x25\x73\x20\x25\x73\x0a\0\x47\x50\x4c\0\x9f\ -\xeb\x01\0\x18\0\0\0\0\0\0\0\x1c\x04\0\0\x1c\x04\0\0\x09\x05\0\0\0\0\0\0\0\0\0\ -\x02\x02\0\0\0\x01\0\0\0\x02\0\0\x04\x10\0\0\0\x13\0\0\0\x03\0\0\0\0\0\0\0\x18\ -\0\0\0\x04\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\x08\0\0\0\0\0\0\0\0\0\0\x02\x0d\0\ -\0\0\0\0\0\0\x01\0\0\x0d\x06\0\0\0\x1c\0\0\0\x01\0\0\0\x20\0\0\0\0\0\0\x01\x04\ -\0\0\0\x20\0\0\x01\x24\0\0\0\x01\0\0\x0c\x05\0\0\0\xaf\0\0\0\x03\0\0\x04\x18\0\ -\0\0\xbd\0\0\0\x09\0\0\0\0\0\0\0\xc1\0\0\0\x0b\0\0\0\x40\0\0\0\xcc\0\0\0\x0b\0\ -\0\0\x80\0\0\0\0\0\0\0\0\0\0\x02\x0a\0\0\0\xd4\0\0\0\0\0\0\x07\0\0\0\0\xdd\0\0\ -\0\0\0\0\x08\x0c\0\0\0\xe3\0\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\0\xa4\x01\0\0\x03\ -\0\0\x04\x18\0\0\0\xac\x01\0\0\x0e\0\0\0\0\0\0\0\xaf\x01\0\0\x11\0\0\0\x20\0\0\ -\0\xb4\x01\0\0\x0e\0\0\0\xa0\0\0\0\xc0\x01\0\0\0\0\0\x08\x0f\0\0\0\xc6\x01\0\0\ -\0\0\0\x01\x04\0\0\0\x20\0\0\0\xd3\x01\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x01\0\0\ -\0\0\0\0\0\x03\0\0\0\0\x10\0\0\0\x12\0\0\0\x10\0\0\0\xd8\x01\0\0\0\0\0\x01\x04\ -\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x02\x14\0\0\0\x3c\x02\0\0\x02\0\0\x04\x10\0\0\0\ -\x13\0\0\0\x03\0\0\0\0\0\0\0\x4f\x02\0\0\x15\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\ -\x18\0\0\0\0\0\0\0\x01\0\0\x0d\x06\0\0\0\x1c\0\0\0\x13\0\0\0\x54\x02\0\0\x01\0\ -\0\x0c\x16\0\0\0\xa0\x02\0\0\x01\0\0\x04\x08\0\0\0\xa9\x02\0\0\x19\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x02\x1a\0\0\0\xfa\x02\0\0\x06\0\0\x04\x38\0\0\0\xac\x01\0\0\ -\x0e\0\0\0\0\0\0\0\xaf\x01\0\0\x11\0\0\0\x20\0\0\0\x07\x03\0\0\x1b\0\0\0\xc0\0\ -\0\0\x18\x03\0\0\x15\0\0\0\0\x01\0\0\x21\x03\0\0\x1d\0\0\0\x40\x01\0\0\x2b\x03\ -\0\0\x1e\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\x02\x1c\0\0\0\0\0\0\0\0\0\0\x0a\x10\0\ -\0\0\0\0\0\0\0\0\0\x02\x1f\0\0\0\0\0\0\0\0\0\0\x02\x20\0\0\0\x75\x03\0\0\x02\0\ -\0\x04\x08\0\0\0\x83\x03\0\0\x0e\0\0\0\0\0\0\0\x8c\x03\0\0\x0e\0\0\0\x20\0\0\0\ -\x2b\x03\0\0\x03\0\0\x04\x18\0\0\0\x96\x03\0\0\x1b\0\0\0\0\0\0\0\x9e\x03\0\0\ -\x21\0\0\0\x40\0\0\0\xa4\x03\0\0\x23\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\x02\x22\0\0\ -\0\0\0\0\0\0\0\0\x02\x24\0\0\0\xa8\x03\0\0\x01\0\0\x04\x04\0\0\0\xb3\x03\0\0\ -\x0e\0\0\0\0\0\0\0\x1c\x04\0\0\x01\0\0\x04\x04\0\0\0\x25\x04\0\0\x0e\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x1c\0\0\0\x12\0\0\0\x23\0\0\0\x9b\x04\0\0\0\0\0\ -\x0e\x25\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x1c\0\0\0\x12\0\0\0\x0e\0\0\0\ -\xaf\x04\0\0\0\0\0\x0e\x27\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x1c\0\0\0\ -\x12\0\0\0\x20\0\0\0\xc5\x04\0\0\0\0\0\x0e\x29\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\ -\0\0\0\0\x1c\0\0\0\x12\0\0\0\x11\0\0\0\xda\x04\0\0\0\0\0\x0e\x2b\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\x03\0\0\0\0\x10\0\0\0\x12\0\0\0\x04\0\0\0\xf1\x04\0\0\0\0\0\x0e\ -\x2d\0\0\0\x01\0\0\0\xf9\x04\0\0\x04\0\0\x0f\0\0\0\0\x26\0\0\0\0\0\0\0\x23\0\0\ -\0\x28\0\0\0\x23\0\0\0\x0e\0\0\0\x2a\0\0\0\x31\0\0\0\x20\0\0\0\x2c\0\0\0\x51\0\ -\0\0\x11\0\0\0\x01\x05\0\0\x01\0\0\x0f\0\0\0\0\x2e\0\0\0\0\0\0\0\x04\0\0\0\0\ -\x62\x70\x66\x5f\x69\x74\x65\x72\x5f\x5f\x62\x70\x66\x5f\x6d\x61\x70\0\x6d\x65\ -\x74\x61\0\x6d\x61\x70\0\x63\x74\x78\0\x69\x6e\x74\0\x64\x75\x6d\x70\x5f\x62\ -\x70\x66\x5f\x6d\x61\x70\0\x69\x74\x65\x72\x2f\x62\x70\x66\x5f\x6d\x61\x70\0\ -\x30\x3a\x30\0\x2f\x68\x6f\x6d\x65\x2f\x61\x6c\x72\x75\x61\x2f\x62\x75\x69\x6c\ -\x64\x2f\x6c\x69\x6e\x75\x78\x2f\x6b\x65\x72\x6e\x65\x6c\x2f\x62\x70\x66\x2f\ -\x70\x72\x65\x6c\x6f\x61\x64\x2f\x69\x74\x65\x72\x61\x74\x6f\x72\x73\x2f\x69\ -\x74\x65\x72\x61\x74\x6f\x72\x73\x2e\x62\x70\x66\x2e\x63\0\x09\x73\x74\x72\x75\ -\x63\x74\x20\x73\x65\x71\x5f\x66\x69\x6c\x65\x20\x2a\x73\x65\x71\x20\x3d\x20\ -\x63\x74\x78\x2d\x3e\x6d\x65\x74\x61\x2d\x3e\x73\x65\x71\x3b\0\x62\x70\x66\x5f\ -\x69\x74\x65\x72\x5f\x6d\x65\x74\x61\0\x73\x65\x71\0\x73\x65\x73\x73\x69\x6f\ -\x6e\x5f\x69\x64\0\x73\x65\x71\x5f\x6e\x75\x6d\0\x73\x65\x71\x5f\x66\x69\x6c\ -\x65\0\x5f\x5f\x75\x36\x34\0\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\x67\x20\x75\x6e\ -\x73\x69\x67\x6e\x65\x64\x20\x69\x6e\x74\0\x30\x3a\x31\0\x09\x73\x74\x72\x75\ -\x63\x74\x20\x62\x70\x66\x5f\x6d\x61\x70\x20\x2a\x6d\x61\x70\x20\x3d\x20\x63\ -\x74\x78\x2d\x3e\x6d\x61\x70\x3b\0\x09\x69\x66\x20\x28\x21\x6d\x61\x70\x29\0\ -\x30\x3a\x32\0\x09\x5f\x5f\x75\x36\x34\x20\x73\x65\x71\x5f\x6e\x75\x6d\x20\x3d\ -\x20\x63\x74\x78\x2d\x3e\x6d\x65\x74\x61\x2d\x3e\x73\x65\x71\x5f\x6e\x75\x6d\ -\x3b\0\x09\x69\x66\x20\x28\x73\x65\x71\x5f\x6e\x75\x6d\x20\x3d\x3d\x20\x30\x29\ -\0\x09\x09\x42\x50\x46\x5f\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\x65\ -\x71\x2c\x20\x22\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\x5c\ -\x6e\x22\x29\x3b\0\x62\x70\x66\x5f\x6d\x61\x70\0\x69\x64\0\x6e\x61\x6d\x65\0\ -\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x5f\x5f\x75\x33\x32\0\x75\x6e\ -\x73\x69\x67\x6e\x65\x64\x20\x69\x6e\x74\0\x63\x68\x61\x72\0\x5f\x5f\x41\x52\ -\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0\x09\x42\x50\x46\ -\x5f\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\x65\x71\x2c\x20\x22\x25\ -\x34\x75\x20\x25\x2d\x31\x36\x73\x25\x36\x64\x5c\x6e\x22\x2c\x20\x6d\x61\x70\ -\x2d\x3e\x69\x64\x2c\x20\x6d\x61\x70\x2d\x3e\x6e\x61\x6d\x65\x2c\x20\x6d\x61\ -\x70\x2d\x3e\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\x29\x3b\0\x7d\0\x62\ -\x70\x66\x5f\x69\x74\x65\x72\x5f\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x70\x72\ -\x6f\x67\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x69\x74\x65\ -\x72\x2f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x09\x73\x74\x72\x75\x63\x74\x20\x62\ -\x70\x66\x5f\x70\x72\x6f\x67\x20\x2a\x70\x72\x6f\x67\x20\x3d\x20\x63\x74\x78\ -\x2d\x3e\x70\x72\x6f\x67\x3b\0\x09\x69\x66\x20\x28\x21\x70\x72\x6f\x67\x29\0\ -\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x61\x75\x78\0\x09\x61\x75\x78\x20\x3d\x20\ -\x70\x72\x6f\x67\x2d\x3e\x61\x75\x78\x3b\0\x09\x09\x42\x50\x46\x5f\x53\x45\x51\ -\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\x65\x71\x2c\x20\x22\x20\x20\x69\x64\x20\ -\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x61\x74\ -\x74\x61\x63\x68\x65\x64\x5c\x6e\x22\x29\x3b\0\x62\x70\x66\x5f\x70\x72\x6f\x67\ -\x5f\x61\x75\x78\0\x61\x74\x74\x61\x63\x68\x5f\x66\x75\x6e\x63\x5f\x6e\x61\x6d\ -\x65\0\x64\x73\x74\x5f\x70\x72\x6f\x67\0\x66\x75\x6e\x63\x5f\x69\x6e\x66\x6f\0\ -\x62\x74\x66\0\x09\x42\x50\x46\x5f\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\ -\x73\x65\x71\x2c\x20\x22\x25\x34\x75\x20\x25\x2d\x31\x36\x73\x20\x25\x73\x20\ -\x25\x73\x5c\x6e\x22\x2c\x20\x61\x75\x78\x2d\x3e\x69\x64\x2c\0\x30\x3a\x34\0\ -\x30\x3a\x35\0\x09\x69\x66\x20\x28\x21\x62\x74\x66\x29\0\x62\x70\x66\x5f\x66\ -\x75\x6e\x63\x5f\x69\x6e\x66\x6f\0\x69\x6e\x73\x6e\x5f\x6f\x66\x66\0\x74\x79\ -\x70\x65\x5f\x69\x64\0\x30\0\x73\x74\x72\x69\x6e\x67\x73\0\x74\x79\x70\x65\x73\ -\0\x68\x64\x72\0\x62\x74\x66\x5f\x68\x65\x61\x64\x65\x72\0\x73\x74\x72\x5f\x6c\ -\x65\x6e\0\x09\x74\x79\x70\x65\x73\x20\x3d\x20\x62\x74\x66\x2d\x3e\x74\x79\x70\ -\x65\x73\x3b\0\x09\x62\x70\x66\x5f\x70\x72\x6f\x62\x65\x5f\x72\x65\x61\x64\x5f\ -\x6b\x65\x72\x6e\x65\x6c\x28\x26\x74\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x74\ -\x29\x2c\x20\x74\x79\x70\x65\x73\x20\x2b\x20\x62\x74\x66\x5f\x69\x64\x29\x3b\0\ -\x09\x73\x74\x72\x20\x3d\x20\x62\x74\x66\x2d\x3e\x73\x74\x72\x69\x6e\x67\x73\ -\x3b\0\x62\x74\x66\x5f\x74\x79\x70\x65\0\x6e\x61\x6d\x65\x5f\x6f\x66\x66\0\x09\ -\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x20\x3d\x20\x42\x50\x46\x5f\x43\x4f\x52\x45\ -\x5f\x52\x45\x41\x44\x28\x74\x2c\x20\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x29\x3b\0\ -\x30\x3a\x32\x3a\x30\0\x09\x69\x66\x20\x28\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x20\ -\x3e\x3d\x20\x62\x74\x66\x2d\x3e\x68\x64\x72\x2e\x73\x74\x72\x5f\x6c\x65\x6e\ -\x29\0\x09\x72\x65\x74\x75\x72\x6e\x20\x73\x74\x72\x20\x2b\x20\x6e\x61\x6d\x65\ -\x5f\x6f\x66\x66\x3b\0\x30\x3a\x33\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\ -\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\x74\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\ -\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\x74\x2e\x31\0\x64\x75\x6d\x70\x5f\x62\x70\x66\ -\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\x5f\x66\x6d\x74\0\x64\x75\x6d\x70\x5f\x62\x70\ -\x66\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\x5f\x66\x6d\x74\x2e\x32\0\x4c\x49\x43\x45\ -\x4e\x53\x45\0\x2e\x72\x6f\x64\x61\x74\x61\0\x6c\x69\x63\x65\x6e\x73\x65\0\x9f\ -\xeb\x01\0\x20\0\0\0\0\0\0\0\x24\0\0\0\x24\0\0\0\x44\x02\0\0\x68\x02\0\0\xa4\ -\x01\0\0\x08\0\0\0\x31\0\0\0\x01\0\0\0\0\0\0\0\x07\0\0\0\x62\x02\0\0\x01\0\0\0\ -\0\0\0\0\x17\0\0\0\x10\0\0\0\x31\0\0\0\x09\0\0\0\0\0\0\0\x42\0\0\0\x87\0\0\0\ -\x1e\x40\x01\0\x08\0\0\0\x42\0\0\0\x87\0\0\0\x24\x40\x01\0\x10\0\0\0\x42\0\0\0\ -\xfe\0\0\0\x1d\x48\x01\0\x18\0\0\0\x42\0\0\0\x1f\x01\0\0\x06\x50\x01\0\x20\0\0\ -\0\x42\0\0\0\x2e\x01\0\0\x1d\x44\x01\0\x28\0\0\0\x42\0\0\0\x53\x01\0\0\x06\x5c\ -\x01\0\x38\0\0\0\x42\0\0\0\x66\x01\0\0\x03\x60\x01\0\x70\0\0\0\x42\0\0\0\xec\ -\x01\0\0\x02\x68\x01\0\xf0\0\0\0\x42\0\0\0\x3a\x02\0\0\x01\x70\x01\0\x62\x02\0\ -\0\x1a\0\0\0\0\0\0\0\x42\0\0\0\x87\0\0\0\x1e\x84\x01\0\x08\0\0\0\x42\0\0\0\x87\ -\0\0\0\x24\x84\x01\0\x10\0\0\0\x42\0\0\0\x70\x02\0\0\x1f\x8c\x01\0\x18\0\0\0\ -\x42\0\0\0\x94\x02\0\0\x06\x98\x01\0\x20\0\0\0\x42\0\0\0\xad\x02\0\0\x0e\xa4\ -\x01\0\x28\0\0\0\x42\0\0\0\x2e\x01\0\0\x1d\x88\x01\0\x30\0\0\0\x42\0\0\0\x53\ -\x01\0\0\x06\xa8\x01\0\x40\0\0\0\x42\0\0\0\xbf\x02\0\0\x03\xac\x01\0\x80\0\0\0\ -\x42\0\0\0\x2f\x03\0\0\x02\xb4\x01\0\xb8\0\0\0\x42\0\0\0\x6a\x03\0\0\x06\x08\ -\x01\0\xd0\0\0\0\x42\0\0\0\0\0\0\0\0\0\0\0\xd8\0\0\0\x42\0\0\0\xbb\x03\0\0\x0f\ -\x14\x01\0\xe0\0\0\0\x42\0\0\0\xd0\x03\0\0\x2d\x18\x01\0\xf0\0\0\0\x42\0\0\0\ -\x07\x04\0\0\x0d\x10\x01\0\0\x01\0\0\x42\0\0\0\0\0\0\0\0\0\0\0\x08\x01\0\0\x42\ -\0\0\0\xd0\x03\0\0\x02\x18\x01\0\x20\x01\0\0\x42\0\0\0\x2e\x04\0\0\x0d\x1c\x01\ -\0\x38\x01\0\0\x42\0\0\0\0\0\0\0\0\0\0\0\x40\x01\0\0\x42\0\0\0\x2e\x04\0\0\x0d\ -\x1c\x01\0\x58\x01\0\0\x42\0\0\0\x2e\x04\0\0\x0d\x1c\x01\0\x60\x01\0\0\x42\0\0\ -\0\x5c\x04\0\0\x1b\x20\x01\0\x68\x01\0\0\x42\0\0\0\x5c\x04\0\0\x06\x20\x01\0\ -\x70\x01\0\0\x42\0\0\0\x7f\x04\0\0\x0d\x28\x01\0\x78\x01\0\0\x42\0\0\0\0\0\0\0\ -\0\0\0\0\x80\x01\0\0\x42\0\0\0\x2f\x03\0\0\x02\xb4\x01\0\xf8\x01\0\0\x42\0\0\0\ -\x3a\x02\0\0\x01\xc4\x01\0\x10\0\0\0\x31\0\0\0\x07\0\0\0\0\0\0\0\x02\0\0\0\x3e\ -\0\0\0\0\0\0\0\x08\0\0\0\x08\0\0\0\x3e\0\0\0\0\0\0\0\x10\0\0\0\x02\0\0\0\xfa\0\ -\0\0\0\0\0\0\x20\0\0\0\x08\0\0\0\x2a\x01\0\0\0\0\0\0\x70\0\0\0\x0d\0\0\0\x3e\0\ -\0\0\0\0\0\0\x80\0\0\0\x0d\0\0\0\xfa\0\0\0\0\0\0\0\xa0\0\0\0\x0d\0\0\0\x2a\x01\ -\0\0\0\0\0\0\x62\x02\0\0\x12\0\0\0\0\0\0\0\x14\0\0\0\x3e\0\0\0\0\0\0\0\x08\0\0\ -\0\x08\0\0\0\x3e\0\0\0\0\0\0\0\x10\0\0\0\x14\0\0\0\xfa\0\0\0\0\0\0\0\x20\0\0\0\ -\x18\0\0\0\x3e\0\0\0\0\0\0\0\x28\0\0\0\x08\0\0\0\x2a\x01\0\0\0\0\0\0\x80\0\0\0\ -\x1a\0\0\0\x3e\0\0\0\0\0\0\0\x90\0\0\0\x1a\0\0\0\xfa\0\0\0\0\0\0\0\xa8\0\0\0\ -\x1a\0\0\0\x62\x03\0\0\0\0\0\0\xb0\0\0\0\x1a\0\0\0\x66\x03\0\0\0\0\0\0\xc0\0\0\ -\0\x1f\0\0\0\x94\x03\0\0\0\0\0\0\xd8\0\0\0\x20\0\0\0\xfa\0\0\0\0\0\0\0\xf0\0\0\ -\0\x20\0\0\0\x3e\0\0\0\0\0\0\0\x18\x01\0\0\x24\0\0\0\x3e\0\0\0\0\0\0\0\x50\x01\ -\0\0\x1a\0\0\0\xfa\0\0\0\0\0\0\0\x60\x01\0\0\x20\0\0\0\x56\x04\0\0\0\0\0\0\x88\ -\x01\0\0\x1a\0\0\0\x2a\x01\0\0\0\0\0\0\x98\x01\0\0\x1a\0\0\0\x97\x04\0\0\0\0\0\ -\0\xa0\x01\0\0\x18\0\0\0\x3e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\x91\0\0\0\x04\0\xf1\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe6\0\0\ -\0\0\0\x02\0\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd8\0\0\0\0\0\x02\0\xf0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\xdf\0\0\0\0\0\x03\0\x78\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\xd1\0\0\0\0\0\x03\0\x80\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xca\0\0\0\0\0\x03\0\ -\xf8\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x14\0\0\0\x01\0\x04\0\0\0\0\0\0\0\0\0\x23\ -\0\0\0\0\0\0\0\x04\x01\0\0\x01\0\x04\0\x23\0\0\0\0\0\0\0\x0e\0\0\0\0\0\0\0\x28\ -\0\0\0\x01\0\x04\0\x31\0\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\xed\0\0\0\x01\0\x04\0\ -\x51\0\0\0\0\0\0\0\x11\0\0\0\0\0\0\0\0\0\0\0\x03\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\ -\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc2\0\0\0\x11\0\x05\0\0\0\0\0\0\0\0\0\ -\x04\0\0\0\0\0\0\0\x3d\0\0\0\x12\0\x02\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\x5b\ -\0\0\0\x12\0\x03\0\0\0\0\0\0\0\0\0\x08\x02\0\0\0\0\0\0\x48\0\0\0\0\0\0\0\x01\0\ -\0\0\x0d\0\0\0\xc8\0\0\0\0\0\0\0\x01\0\0\0\x0d\0\0\0\x50\0\0\0\0\0\0\0\x01\0\0\ -\0\x0d\0\0\0\xd0\x01\0\0\0\0\0\0\x01\0\0\0\x0d\0\0\0\xf0\x03\0\0\0\0\0\0\x0a\0\ -\0\0\x0d\0\0\0\xfc\x03\0\0\0\0\0\0\x0a\0\0\0\x0d\0\0\0\x08\x04\0\0\0\0\0\0\x0a\ -\0\0\0\x0d\0\0\0\x14\x04\0\0\0\0\0\0\x0a\0\0\0\x0d\0\0\0\x2c\x04\0\0\0\0\0\0\0\ -\0\0\0\x0e\0\0\0\x2c\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\x3c\0\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x50\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\x60\0\0\0\0\0\0\0\0\0\0\0\x0b\0\ -\0\0\x70\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\ -\x90\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xa0\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xb0\0\ -\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xc0\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xd0\0\0\0\0\ -\0\0\0\0\0\0\0\x0b\0\0\0\xe8\0\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xf8\0\0\0\0\0\0\0\ -\0\0\0\0\x0c\0\0\0\x08\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x18\x01\0\0\0\0\0\0\0\ -\0\0\0\x0c\0\0\0\x28\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x38\x01\0\0\0\0\0\0\0\0\ -\0\0\x0c\0\0\0\x48\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x58\x01\0\0\0\0\0\0\0\0\0\ -\0\x0c\0\0\0\x68\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x78\x01\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x88\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x98\x01\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\xa8\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xb8\x01\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\xc8\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xd8\x01\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\xe8\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xf8\x01\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x08\x02\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x18\x02\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x28\x02\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x38\x02\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x48\x02\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x58\x02\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x68\x02\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x78\x02\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x94\x02\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xa4\x02\0\0\0\0\0\0\0\0\0\0\ -\x0b\0\0\0\xb4\x02\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xc4\x02\0\0\0\0\0\0\0\0\0\0\ -\x0b\0\0\0\xd4\x02\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xe4\x02\0\0\0\0\0\0\0\0\0\0\ -\x0b\0\0\0\xf4\x02\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\x0c\x03\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x1c\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x2c\x03\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x3c\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x4c\x03\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x5c\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x6c\x03\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x7c\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x8c\x03\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x9c\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xac\x03\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\xbc\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xcc\x03\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\xdc\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xec\x03\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\xfc\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x0c\x04\0\0\0\0\0\0\0\0\0\0\ -\x0c\0\0\0\x1c\x04\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x4d\x4e\x40\x41\x42\x43\x4c\0\ -\x2e\x74\x65\x78\x74\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\x2e\x65\x78\x74\0\x64\ -\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\x74\0\x64\ -\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\x5f\x66\x6d\x74\0\ -\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\x61\x70\0\x2e\x72\x65\x6c\x69\x74\x65\ -\x72\x2f\x62\x70\x66\x5f\x6d\x61\x70\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\ -\x72\x6f\x67\0\x2e\x72\x65\x6c\x69\x74\x65\x72\x2f\x62\x70\x66\x5f\x70\x72\x6f\ -\x67\0\x2e\x6c\x6c\x76\x6d\x5f\x61\x64\x64\x72\x73\x69\x67\0\x6c\x69\x63\x65\ -\x6e\x73\x65\0\x69\x74\x65\x72\x61\x74\x6f\x72\x73\x2e\x62\x70\x66\x2e\x63\0\ -\x2e\x73\x74\x72\x74\x61\x62\0\x2e\x73\x79\x6d\x74\x61\x62\0\x2e\x72\x6f\x64\ -\x61\x74\x61\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\0\x4c\x49\x43\x45\x4e\x53\x45\0\ -\x4c\x42\x42\x31\x5f\x37\0\x4c\x42\x42\x31\x5f\x36\0\x4c\x42\x42\x30\x5f\x34\0\ -\x4c\x42\x42\x31\x5f\x33\0\x4c\x42\x42\x30\x5f\x33\0\x64\x75\x6d\x70\x5f\x62\ -\x70\x66\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\x5f\x66\x6d\x74\x2e\x32\0\x64\x75\x6d\ -\x70\x5f\x62\x70\x66\x5f\x6d\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\x74\x2e\x31\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\ -\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x4e\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\x6d\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\x40\x01\0\0\0\0\0\0\x08\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\xb1\0\0\0\x01\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x48\x03\0\ -\0\0\0\0\0\x62\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\x89\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xaa\x03\0\0\0\0\0\0\x04\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbd\0\0\0\x01\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xae\x03\0\0\0\0\0\0\x3d\x09\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\x01\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\xeb\x0c\0\0\0\0\0\0\x2c\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa9\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\x18\x11\0\0\0\0\0\0\x98\x01\0\0\0\0\0\0\x0e\0\0\0\x0e\0\0\0\x08\0\0\ -\0\0\0\0\0\x18\0\0\0\0\0\0\0\x4a\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\xb0\x12\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\x08\0\0\0\x02\0\0\0\x08\0\0\0\0\0\0\0\ -\x10\0\0\0\0\0\0\0\x69\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd0\x12\ -\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\x08\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\ -\0\0\0\0\xb9\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf0\x12\0\0\0\0\0\ -\0\x50\0\0\0\0\0\0\0\x08\0\0\0\x06\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\ -\x07\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\x13\0\0\0\0\0\0\xe0\ -\x03\0\0\0\0\0\0\x08\0\0\0\x07\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x7b\0\ -\0\0\x03\x4c\xff\x6f\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\0\0\x20\x17\0\0\0\0\0\0\x07\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa1\0\0\0\x03\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x27\x17\0\0\0\0\0\0\x1a\x01\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - - return 0; -err: - bpf_object__destroy_skeleton(s); - return -1; -} - -#endif /* __ITERATORS_BPF_SKEL_H__ */ diff --git a/kernel/bpf/reuseport_array.c b/kernel/bpf/reuseport_array.c index 556a769b5b80..8251243022a2 100644 --- a/kernel/bpf/reuseport_array.c +++ b/kernel/bpf/reuseport_array.c @@ -143,7 +143,7 @@ static void reuseport_array_free(struct bpf_map *map) /* * Once reaching here, all sk->sk_user_data is not - * referenceing this "array". "array" can be freed now. + * referencing this "array". "array" can be freed now. */ bpf_map_area_free(array); } diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index 22c8ae94e4c1..34725bfa1e97 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -132,7 +132,8 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs, int i; struct mmap_unlock_irq_work *work = NULL; bool irq_work_busy = bpf_mmap_unlock_get_irq_work(&work); - struct vm_area_struct *vma; + struct vm_area_struct *vma, *prev_vma = NULL; + const char *prev_build_id; /* If the irq_work is in use, fall back to report ips. Same * fallback is used for kernel stack (!user) on a stackmap with @@ -150,6 +151,12 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs, } for (i = 0; i < trace_nr; i++) { + if (range_in_vma(prev_vma, ips[i], ips[i])) { + vma = prev_vma; + memcpy(id_offs[i].build_id, prev_build_id, + BUILD_ID_SIZE_MAX); + goto build_id_valid; + } vma = find_vma(current->mm, ips[i]); if (!vma || build_id_parse(vma, id_offs[i].build_id, NULL)) { /* per entry fall back to ips */ @@ -158,15 +165,18 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs, memset(id_offs[i].build_id, 0, BUILD_ID_SIZE_MAX); continue; } +build_id_valid: id_offs[i].offset = (vma->vm_pgoff << PAGE_SHIFT) + ips[i] - vma->vm_start; id_offs[i].status = BPF_STACK_BUILD_ID_VALID; + prev_vma = vma; + prev_build_id = id_offs[i].build_id; } bpf_mmap_unlock_mm(work, current->mm); } static struct perf_callchain_entry * -get_callchain_entry_for_task(struct task_struct *task, u32 init_nr) +get_callchain_entry_for_task(struct task_struct *task, u32 max_depth) { #ifdef CONFIG_STACKTRACE struct perf_callchain_entry *entry; @@ -177,9 +187,8 @@ get_callchain_entry_for_task(struct task_struct *task, u32 init_nr) if (!entry) return NULL; - entry->nr = init_nr + - stack_trace_save_tsk(task, (unsigned long *)(entry->ip + init_nr), - sysctl_perf_event_max_stack - init_nr, 0); + entry->nr = stack_trace_save_tsk(task, (unsigned long *)entry->ip, + max_depth, 0); /* stack_trace_save_tsk() works on unsigned long array, while * perf_callchain_entry uses u64 array. For 32-bit systems, it is @@ -191,7 +200,7 @@ get_callchain_entry_for_task(struct task_struct *task, u32 init_nr) int i; /* copy data from the end to avoid using extra buffer */ - for (i = entry->nr - 1; i >= (int)init_nr; i--) + for (i = entry->nr - 1; i >= 0; i--) to[i] = (u64)(from[i]); } @@ -208,27 +217,19 @@ static long __bpf_get_stackid(struct bpf_map *map, { struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); struct stack_map_bucket *bucket, *new_bucket, *old_bucket; - u32 max_depth = map->value_size / stack_map_data_size(map); - /* stack_map_alloc() checks that max_depth <= sysctl_perf_event_max_stack */ - u32 init_nr = sysctl_perf_event_max_stack - max_depth; u32 skip = flags & BPF_F_SKIP_FIELD_MASK; u32 hash, id, trace_nr, trace_len; bool user = flags & BPF_F_USER_STACK; u64 *ips; bool hash_matches; - /* get_perf_callchain() guarantees that trace->nr >= init_nr - * and trace-nr <= sysctl_perf_event_max_stack, so trace_nr <= max_depth - */ - trace_nr = trace->nr - init_nr; - - if (trace_nr <= skip) + if (trace->nr <= skip) /* skipping more than usable stack trace */ return -EFAULT; - trace_nr -= skip; + trace_nr = trace->nr - skip; trace_len = trace_nr * sizeof(u64); - ips = trace->ip + skip + init_nr; + ips = trace->ip + skip; hash = jhash2((u32 *)ips, trace_len / sizeof(u32), 0); id = hash & (smap->n_buckets - 1); bucket = READ_ONCE(smap->buckets[id]); @@ -285,8 +286,7 @@ BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map, u64, flags) { u32 max_depth = map->value_size / stack_map_data_size(map); - /* stack_map_alloc() checks that max_depth <= sysctl_perf_event_max_stack */ - u32 init_nr = sysctl_perf_event_max_stack - max_depth; + u32 skip = flags & BPF_F_SKIP_FIELD_MASK; bool user = flags & BPF_F_USER_STACK; struct perf_callchain_entry *trace; bool kernel = !user; @@ -295,8 +295,12 @@ BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map, BPF_F_FAST_STACK_CMP | BPF_F_REUSE_STACKID))) return -EINVAL; - trace = get_perf_callchain(regs, init_nr, kernel, user, - sysctl_perf_event_max_stack, false, false); + max_depth += skip; + if (max_depth > sysctl_perf_event_max_stack) + max_depth = sysctl_perf_event_max_stack; + + trace = get_perf_callchain(regs, 0, kernel, user, max_depth, + false, false); if (unlikely(!trace)) /* couldn't fetch the stack trace */ @@ -387,7 +391,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task, struct perf_callchain_entry *trace_in, void *buf, u32 size, u64 flags) { - u32 init_nr, trace_nr, copy_len, elem_size, num_elem; + u32 trace_nr, copy_len, elem_size, num_elem, max_depth; bool user_build_id = flags & BPF_F_USER_BUILD_ID; u32 skip = flags & BPF_F_SKIP_FIELD_MASK; bool user = flags & BPF_F_USER_STACK; @@ -412,30 +416,28 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task, goto err_fault; num_elem = size / elem_size; - if (sysctl_perf_event_max_stack < num_elem) - init_nr = 0; - else - init_nr = sysctl_perf_event_max_stack - num_elem; + max_depth = num_elem + skip; + if (sysctl_perf_event_max_stack < max_depth) + max_depth = sysctl_perf_event_max_stack; if (trace_in) trace = trace_in; else if (kernel && task) - trace = get_callchain_entry_for_task(task, init_nr); + trace = get_callchain_entry_for_task(task, max_depth); else - trace = get_perf_callchain(regs, init_nr, kernel, user, - sysctl_perf_event_max_stack, + trace = get_perf_callchain(regs, 0, kernel, user, max_depth, false, false); if (unlikely(!trace)) goto err_fault; - trace_nr = trace->nr - init_nr; - if (trace_nr < skip) + if (trace->nr < skip) goto err_fault; - trace_nr -= skip; + trace_nr = trace->nr - skip; trace_nr = (trace_nr <= num_elem) ? trace_nr : num_elem; copy_len = trace_nr * elem_size; - ips = trace->ip + skip + init_nr; + + ips = trace->ip + skip; if (user && user_build_id) stack_map_get_build_id_offset(buf, ips, trace_nr, user); else diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index ca70fe6fba38..cdaa1152436a 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -32,6 +32,7 @@ #include #include #include +#include #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \ (map)->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || \ @@ -556,16 +557,14 @@ static unsigned long bpf_map_memory_footprint(const struct bpf_map *map) static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp) { - const struct bpf_map *map = filp->private_data; - const struct bpf_array *array; + struct bpf_map *map = filp->private_data; u32 type = 0, jited = 0; - if (map->map_type == BPF_MAP_TYPE_PROG_ARRAY) { - array = container_of(map, struct bpf_array, map); - spin_lock(&array->aux->owner.lock); - type = array->aux->owner.type; - jited = array->aux->owner.jited; - spin_unlock(&array->aux->owner.lock); + if (map_type_contains_progs(map)) { + spin_lock(&map->owner.lock); + type = map->owner.type; + jited = map->owner.jited; + spin_unlock(&map->owner.lock); } seq_printf(m, @@ -874,6 +873,7 @@ static int map_create(union bpf_attr *attr) atomic64_set(&map->refcnt, 1); atomic64_set(&map->usercnt, 1); mutex_init(&map->freeze_mutex); + spin_lock_init(&map->owner.lock); map->spin_lock_off = -EINVAL; map->timer_off = -EINVAL; @@ -986,6 +986,7 @@ struct bpf_map *bpf_map_get(u32 ufd) return map; } +EXPORT_SYMBOL(bpf_map_get); struct bpf_map *bpf_map_get_with_uref(u32 ufd) { @@ -1352,7 +1353,6 @@ int generic_map_delete_batch(struct bpf_map *map, err = map->ops->map_delete_elem(map, key); rcu_read_unlock(); bpf_enable_instrumentation(); - maybe_wait_bpf_programs(map); if (err) break; cond_resched(); @@ -1361,6 +1361,8 @@ int generic_map_delete_batch(struct bpf_map *map, err = -EFAULT; kvfree(key); + + maybe_wait_bpf_programs(map); return err; } @@ -2220,7 +2222,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr) BPF_F_ANY_ALIGNMENT | BPF_F_TEST_STATE_FREQ | BPF_F_SLEEPABLE | - BPF_F_TEST_RND_HI32)) + BPF_F_TEST_RND_HI32 | + BPF_F_XDP_HAS_FRAGS)) return -EINVAL; if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && @@ -2306,6 +2309,7 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr) prog->aux->dst_prog = dst_prog; prog->aux->offload_requested = !!attr->prog_ifindex; prog->aux->sleepable = attr->prog_flags & BPF_F_SLEEPABLE; + prog->aux->xdp_has_frags = attr->prog_flags & BPF_F_XDP_HAS_FRAGS; err = security_bpf_prog_alloc(prog->aux); if (err) @@ -2491,6 +2495,7 @@ void bpf_link_put(struct bpf_link *link) bpf_link_free(link); } } +EXPORT_SYMBOL(bpf_link_put); static int bpf_link_release(struct inode *inode, struct file *filp) { @@ -2562,7 +2567,7 @@ static int bpf_link_alloc_id(struct bpf_link *link) * pre-allocated resources are to be freed with bpf_cleanup() call. All the * transient state is passed around in struct bpf_link_primer. * This is preferred way to create and initialize bpf_link, especially when - * there are complicated and expensive operations inbetween creating bpf_link + * there are complicated and expensive operations in between creating bpf_link * itself and attaching it to BPF hook. By using bpf_link_prime() and * bpf_link_settle() kernel code using bpf_link doesn't have to perform * expensive (and potentially failing) roll back operations in a rare case @@ -2633,6 +2638,7 @@ struct bpf_link *bpf_link_get_from_fd(u32 ufd) return link; } +EXPORT_SYMBOL(bpf_link_get_from_fd); struct bpf_tracing_link { struct bpf_link link; @@ -3017,6 +3023,11 @@ static int bpf_perf_link_attach(const union bpf_attr *attr, struct bpf_prog *pro fput(perf_file); return err; } +#else +static int bpf_perf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) +{ + return -EOPNOTSUPP; +} #endif /* CONFIG_PERF_EVENTS */ #define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd @@ -3321,12 +3332,17 @@ static int bpf_prog_query(const union bpf_attr *attr, case BPF_FLOW_DISSECTOR: case BPF_SK_LOOKUP: return netns_bpf_prog_query(attr, uattr); + case BPF_SK_SKB_STREAM_PARSER: + case BPF_SK_SKB_STREAM_VERDICT: + case BPF_SK_MSG_VERDICT: + case BPF_SK_SKB_VERDICT: + return sock_map_bpf_prog_query(attr, uattr); default: return -EINVAL; } } -#define BPF_PROG_TEST_RUN_LAST_FIELD test.cpu +#define BPF_PROG_TEST_RUN_LAST_FIELD test.batch_size static int bpf_prog_test_run(const union bpf_attr *attr, union bpf_attr __user *uattr) @@ -4245,7 +4261,7 @@ static int tracing_bpf_link_attach(const union bpf_attr *attr, bpfptr_t uattr, return -EINVAL; } -#define BPF_LINK_CREATE_LAST_FIELD link_create.iter_info_len +#define BPF_LINK_CREATE_LAST_FIELD link_create.kprobe_multi.cookies static int link_create(union bpf_attr *attr, bpfptr_t uattr) { enum bpf_prog_type ptype; @@ -4269,7 +4285,6 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr) ret = tracing_bpf_link_attach(attr, uattr, prog); goto out; case BPF_PROG_TYPE_PERF_EVENT: - case BPF_PROG_TYPE_KPROBE: case BPF_PROG_TYPE_TRACEPOINT: if (attr->link_create.attach_type != BPF_PERF_EVENT) { ret = -EINVAL; @@ -4277,6 +4292,14 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr) } ptype = prog->type; break; + case BPF_PROG_TYPE_KPROBE: + if (attr->link_create.attach_type != BPF_PERF_EVENT && + attr->link_create.attach_type != BPF_TRACE_KPROBE_MULTI) { + ret = -EINVAL; + goto out; + } + ptype = prog->type; + break; default: ptype = attach_type_to_prog_type(attr->link_create.attach_type); if (ptype == BPF_PROG_TYPE_UNSPEC || ptype != prog->type) { @@ -4308,13 +4331,16 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr) ret = bpf_xdp_link_attach(attr, prog); break; #endif -#ifdef CONFIG_PERF_EVENTS case BPF_PROG_TYPE_PERF_EVENT: case BPF_PROG_TYPE_TRACEPOINT: - case BPF_PROG_TYPE_KPROBE: ret = bpf_perf_link_attach(attr, prog); break; -#endif + case BPF_PROG_TYPE_KPROBE: + if (attr->link_create.attach_type == BPF_PERF_EVENT) + ret = bpf_perf_link_attach(attr, prog); + else + ret = bpf_kprobe_multi_link_attach(attr, prog); + break; default: ret = -EINVAL; } @@ -4753,23 +4779,52 @@ static bool syscall_prog_is_valid_access(int off, int size, return true; } -BPF_CALL_3(bpf_sys_bpf, int, cmd, void *, attr, u32, attr_size) +BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, attr, u32, attr_size) { + struct bpf_prog * __maybe_unused prog; + switch (cmd) { case BPF_MAP_CREATE: case BPF_MAP_UPDATE_ELEM: case BPF_MAP_FREEZE: case BPF_PROG_LOAD: case BPF_BTF_LOAD: + case BPF_LINK_CREATE: + case BPF_RAW_TRACEPOINT_OPEN: break; - /* case BPF_PROG_TEST_RUN: - * is not part of this list to prevent recursive test_run - */ +#ifdef CONFIG_BPF_JIT /* __bpf_prog_enter_sleepable used by trampoline and JIT */ + case BPF_PROG_TEST_RUN: + if (attr->test.data_in || attr->test.data_out || + attr->test.ctx_out || attr->test.duration || + attr->test.repeat || attr->test.flags) + return -EINVAL; + + prog = bpf_prog_get_type(attr->test.prog_fd, BPF_PROG_TYPE_SYSCALL); + if (IS_ERR(prog)) + return PTR_ERR(prog); + + if (attr->test.ctx_size_in < prog->aux->max_ctx_offset || + attr->test.ctx_size_in > U16_MAX) { + bpf_prog_put(prog); + return -EINVAL; + } + + if (!__bpf_prog_enter_sleepable(prog)) { + /* recursion detected */ + bpf_prog_put(prog); + return -EBUSY; + } + attr->test.retval = bpf_prog_run(prog, (void *) (long) attr->test.ctx_in); + __bpf_prog_exit_sleepable(prog, 0 /* bpf_prog_run does runtime stats */); + bpf_prog_put(prog); + return 0; +#endif default: return -EINVAL; } return __sys_bpf(cmd, KERNEL_BPFPTR(attr), attr_size); } +EXPORT_SYMBOL(bpf_sys_bpf); static const struct bpf_func_proto bpf_sys_bpf_proto = { .func = bpf_sys_bpf, diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 5e7edf913060..0b41fa993825 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -45,7 +45,7 @@ void *bpf_jit_alloc_exec_page(void) set_vm_flush_reset_perms(image); /* Keep image as writeable. The alternative is to keep flipping ro/rw - * everytime new program is attached or detached. + * every time new program is attached or detached. */ set_memory_x((long)image, 1); return image; @@ -213,7 +213,7 @@ static void __bpf_tramp_image_put_deferred(struct work_struct *work) im = container_of(work, struct bpf_tramp_image, work); bpf_image_ksym_del(&im->ksym); bpf_jit_free_exec(im->image); - bpf_jit_uncharge_modmem(1); + bpf_jit_uncharge_modmem(PAGE_SIZE); percpu_ref_exit(&im->pcref); kfree_rcu(im, rcu); } @@ -310,7 +310,7 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, u32 idx) if (!im) goto out; - err = bpf_jit_charge_modmem(1); + err = bpf_jit_charge_modmem(PAGE_SIZE); if (err) goto out_free_im; @@ -332,7 +332,7 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, u32 idx) out_free_image: bpf_jit_free_exec(im->image); out_uncharge: - bpf_jit_uncharge_modmem(1); + bpf_jit_uncharge_modmem(PAGE_SIZE); out_free_im: kfree(im); out: diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a39eedecc93a..d175b70067b3 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -452,7 +452,8 @@ static bool reg_type_may_be_refcounted_or_null(enum bpf_reg_type type) { return base_type(type) == PTR_TO_SOCKET || base_type(type) == PTR_TO_TCP_SOCK || - base_type(type) == PTR_TO_MEM; + base_type(type) == PTR_TO_MEM || + base_type(type) == PTR_TO_BTF_ID; } static bool type_is_rdonly_mem(u32 type) @@ -535,10 +536,10 @@ static bool is_cmpxchg_insn(const struct bpf_insn *insn) static const char *reg_type_str(struct bpf_verifier_env *env, enum bpf_reg_type type) { - char postfix[16] = {0}, prefix[16] = {0}; + char postfix[16] = {0}, prefix[32] = {0}; static const char * const str[] = { [NOT_INIT] = "?", - [SCALAR_VALUE] = "inv", + [SCALAR_VALUE] = "scalar", [PTR_TO_CTX] = "ctx", [CONST_PTR_TO_MAP] = "map_ptr", [PTR_TO_MAP_VALUE] = "map_value", @@ -553,7 +554,6 @@ static const char *reg_type_str(struct bpf_verifier_env *env, [PTR_TO_TP_BUFFER] = "tp_buffer", [PTR_TO_XDP_SOCK] = "xdp_sock", [PTR_TO_BTF_ID] = "ptr_", - [PTR_TO_PERCPU_BTF_ID] = "percpu_ptr_", [PTR_TO_MEM] = "mem", [PTR_TO_BUF] = "buf", [PTR_TO_FUNC] = "func", @@ -561,17 +561,20 @@ static const char *reg_type_str(struct bpf_verifier_env *env, }; if (type & PTR_MAYBE_NULL) { - if (base_type(type) == PTR_TO_BTF_ID || - base_type(type) == PTR_TO_PERCPU_BTF_ID) + if (base_type(type) == PTR_TO_BTF_ID) strncpy(postfix, "or_null_", 16); else strncpy(postfix, "_or_null", 16); } if (type & MEM_RDONLY) - strncpy(prefix, "rdonly_", 16); + strncpy(prefix, "rdonly_", 32); if (type & MEM_ALLOC) - strncpy(prefix, "alloc_", 16); + strncpy(prefix, "alloc_", 32); + if (type & MEM_USER) + strncpy(prefix, "user_", 32); + if (type & MEM_PERCPU) + strncpy(prefix, "percpu_", 32); snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s", prefix, str[base_type(type)], postfix); @@ -682,74 +685,79 @@ static void print_verifier_state(struct bpf_verifier_env *env, continue; verbose(env, " R%d", i); print_liveness(env, reg->live); - verbose(env, "=%s", reg_type_str(env, t)); + verbose(env, "="); if (t == SCALAR_VALUE && reg->precise) verbose(env, "P"); if ((t == SCALAR_VALUE || t == PTR_TO_STACK) && tnum_is_const(reg->var_off)) { /* reg->off should be 0 for SCALAR_VALUE */ + verbose(env, "%s", t == SCALAR_VALUE ? "" : reg_type_str(env, t)); verbose(env, "%lld", reg->var_off.value + reg->off); } else { - if (base_type(t) == PTR_TO_BTF_ID || - base_type(t) == PTR_TO_PERCPU_BTF_ID) + const char *sep = ""; + + verbose(env, "%s", reg_type_str(env, t)); + if (base_type(t) == PTR_TO_BTF_ID) verbose(env, "%s", kernel_type_name(reg->btf, reg->btf_id)); - verbose(env, "(id=%d", reg->id); - if (reg_type_may_be_refcounted_or_null(t)) - verbose(env, ",ref_obj_id=%d", reg->ref_obj_id); + verbose(env, "("); +/* + * _a stands for append, was shortened to avoid multiline statements below. + * This macro is used to output a comma separated list of attributes. + */ +#define verbose_a(fmt, ...) ({ verbose(env, "%s" fmt, sep, __VA_ARGS__); sep = ","; }) + + if (reg->id) + verbose_a("id=%d", reg->id); + if (reg_type_may_be_refcounted_or_null(t) && reg->ref_obj_id) + verbose_a("ref_obj_id=%d", reg->ref_obj_id); if (t != SCALAR_VALUE) - verbose(env, ",off=%d", reg->off); + verbose_a("off=%d", reg->off); if (type_is_pkt_pointer(t)) - verbose(env, ",r=%d", reg->range); + verbose_a("r=%d", reg->range); else if (base_type(t) == CONST_PTR_TO_MAP || base_type(t) == PTR_TO_MAP_KEY || base_type(t) == PTR_TO_MAP_VALUE) - verbose(env, ",ks=%d,vs=%d", - reg->map_ptr->key_size, - reg->map_ptr->value_size); + verbose_a("ks=%d,vs=%d", + reg->map_ptr->key_size, + reg->map_ptr->value_size); if (tnum_is_const(reg->var_off)) { /* Typically an immediate SCALAR_VALUE, but * could be a pointer whose offset is too big * for reg->off */ - verbose(env, ",imm=%llx", reg->var_off.value); + verbose_a("imm=%llx", reg->var_off.value); } else { if (reg->smin_value != reg->umin_value && reg->smin_value != S64_MIN) - verbose(env, ",smin_value=%lld", - (long long)reg->smin_value); + verbose_a("smin=%lld", (long long)reg->smin_value); if (reg->smax_value != reg->umax_value && reg->smax_value != S64_MAX) - verbose(env, ",smax_value=%lld", - (long long)reg->smax_value); + verbose_a("smax=%lld", (long long)reg->smax_value); if (reg->umin_value != 0) - verbose(env, ",umin_value=%llu", - (unsigned long long)reg->umin_value); + verbose_a("umin=%llu", (unsigned long long)reg->umin_value); if (reg->umax_value != U64_MAX) - verbose(env, ",umax_value=%llu", - (unsigned long long)reg->umax_value); + verbose_a("umax=%llu", (unsigned long long)reg->umax_value); if (!tnum_is_unknown(reg->var_off)) { char tn_buf[48]; tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); - verbose(env, ",var_off=%s", tn_buf); + verbose_a("var_off=%s", tn_buf); } if (reg->s32_min_value != reg->smin_value && reg->s32_min_value != S32_MIN) - verbose(env, ",s32_min_value=%d", - (int)(reg->s32_min_value)); + verbose_a("s32_min=%d", (int)(reg->s32_min_value)); if (reg->s32_max_value != reg->smax_value && reg->s32_max_value != S32_MAX) - verbose(env, ",s32_max_value=%d", - (int)(reg->s32_max_value)); + verbose_a("s32_max=%d", (int)(reg->s32_max_value)); if (reg->u32_min_value != reg->umin_value && reg->u32_min_value != U32_MIN) - verbose(env, ",u32_min_value=%d", - (int)(reg->u32_min_value)); + verbose_a("u32_min=%d", (int)(reg->u32_min_value)); if (reg->u32_max_value != reg->umax_value && reg->u32_max_value != U32_MAX) - verbose(env, ",u32_max_value=%d", - (int)(reg->u32_max_value)); + verbose_a("u32_max=%d", (int)(reg->u32_max_value)); } +#undef verbose_a + verbose(env, ")"); } } @@ -774,7 +782,7 @@ static void print_verifier_state(struct bpf_verifier_env *env, if (is_spilled_reg(&state->stack[i])) { reg = &state->stack[i].spilled_ptr; t = reg->type; - verbose(env, "=%s", reg_type_str(env, t)); + verbose(env, "=%s", t == SCALAR_VALUE ? "" : reg_type_str(env, t)); if (t == SCALAR_VALUE && reg->precise) verbose(env, "P"); if (t == SCALAR_VALUE && tnum_is_const(reg->var_off)) @@ -1546,14 +1554,15 @@ static void mark_reg_not_init(struct bpf_verifier_env *env, static void mark_btf_ld_reg(struct bpf_verifier_env *env, struct bpf_reg_state *regs, u32 regno, enum bpf_reg_type reg_type, - struct btf *btf, u32 btf_id) + struct btf *btf, u32 btf_id, + enum bpf_type_flag flag) { if (reg_type == SCALAR_VALUE) { mark_reg_unknown(env, regs, regno); return; } mark_reg_known_zero(env, regs, regno); - regs[regno].type = PTR_TO_BTF_ID; + regs[regno].type = PTR_TO_BTF_ID | flag; regs[regno].btf = btf; regs[regno].btf_id = btf_id; } @@ -1743,7 +1752,7 @@ find_kfunc_desc(const struct bpf_prog *prog, u32 func_id, u16 offset) } static struct btf *__find_kfunc_desc_btf(struct bpf_verifier_env *env, - s16 offset, struct module **btf_modp) + s16 offset) { struct bpf_kfunc_btf kf_btf = { .offset = offset }; struct bpf_kfunc_btf_tab *tab; @@ -1797,8 +1806,6 @@ static struct btf *__find_kfunc_desc_btf(struct bpf_verifier_env *env, sort(tab->descs, tab->nr_descs, sizeof(tab->descs[0]), kfunc_btf_cmp_by_off, NULL); } - if (btf_modp) - *btf_modp = b->module; return b->btf; } @@ -1815,8 +1822,7 @@ void bpf_free_kfunc_btf_tab(struct bpf_kfunc_btf_tab *tab) } static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, - u32 func_id, s16 offset, - struct module **btf_modp) + u32 func_id, s16 offset) { if (offset) { if (offset < 0) { @@ -1827,7 +1833,7 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, return ERR_PTR(-EINVAL); } - return __find_kfunc_desc_btf(env, offset, btf_modp); + return __find_kfunc_desc_btf(env, offset); } return btf_vmlinux ?: ERR_PTR(-ENOENT); } @@ -1841,6 +1847,7 @@ static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, s16 offset) struct bpf_kfunc_desc *desc; const char *func_name; struct btf *desc_btf; + unsigned long call_imm; unsigned long addr; int err; @@ -1890,7 +1897,7 @@ static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, s16 offset) prog_aux->kfunc_btf_tab = btf_tab; } - desc_btf = find_kfunc_desc_btf(env, func_id, offset, NULL); + desc_btf = find_kfunc_desc_btf(env, func_id, offset); if (IS_ERR(desc_btf)) { verbose(env, "failed to find BTF for kernel function\n"); return PTR_ERR(desc_btf); @@ -1925,9 +1932,17 @@ static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, s16 offset) return -EINVAL; } + call_imm = BPF_CALL_IMM(addr); + /* Check whether or not the relative offset overflows desc->imm */ + if ((unsigned long)(s32)call_imm != call_imm) { + verbose(env, "address of kernel function %s is out of range\n", + func_name); + return -EINVAL; + } + desc = &tab->descs[tab->nr_descs++]; desc->func_id = func_id; - desc->imm = BPF_CALL_IMM(addr); + desc->imm = call_imm; desc->offset = offset; err = btf_distill_func_proto(&env->log, desc_btf, func_proto, func_name, @@ -2351,7 +2366,7 @@ static const char *disasm_kfunc_name(void *data, const struct bpf_insn *insn) if (insn->src_reg != BPF_PSEUDO_KFUNC_CALL) return NULL; - desc_btf = find_kfunc_desc_btf(data, insn->imm, insn->off, NULL); + desc_btf = find_kfunc_desc_btf(data, insn->imm, insn->off); if (IS_ERR(desc_btf)) return ""; @@ -2767,7 +2782,6 @@ static bool is_spillable_regtype(enum bpf_reg_type type) case PTR_TO_XDP_SOCK: case PTR_TO_BTF_ID: case PTR_TO_BUF: - case PTR_TO_PERCPU_BTF_ID: case PTR_TO_MEM: case PTR_TO_FUNC: case PTR_TO_MAP_KEY: @@ -3498,11 +3512,6 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, #define MAX_PACKET_OFF 0xffff -static enum bpf_prog_type resolve_prog_type(struct bpf_prog *prog) -{ - return prog->aux->dst_prog ? prog->aux->dst_prog->type : prog->type; -} - static bool may_access_direct_pkt_data(struct bpf_verifier_env *env, const struct bpf_call_arg_meta *meta, enum bpf_access_type t) @@ -3979,6 +3988,12 @@ static int __check_ptr_off_reg(struct bpf_verifier_env *env, * is only allowed in its original, unmodified form. */ + if (reg->off < 0) { + verbose(env, "negative offset %s ptr R%d off=%d disallowed\n", + reg_type_str(env, reg->type), regno, reg->off); + return -EACCES; + } + if (!fixed_off_ok && reg->off) { verbose(env, "dereference of modified %s ptr R%d off=%d disallowed\n", reg_type_str(env, reg->type), regno, reg->off); @@ -4047,9 +4062,9 @@ static int check_buffer_access(struct bpf_verifier_env *env, const struct bpf_reg_state *reg, int regno, int off, int size, bool zero_size_allowed, - const char *buf_info, u32 *max_access) { + const char *buf_info = type_is_rdonly_mem(reg->type) ? "rdonly" : "rdwr"; int err; err = __check_buffer_access(env, buf_info, reg, regno, off, size); @@ -4159,6 +4174,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, struct bpf_reg_state *reg = regs + regno; const struct btf_type *t = btf_type_by_id(reg->btf, reg->btf_id); const char *tname = btf_name_by_offset(reg->btf, t->name_off); + enum bpf_type_flag flag = 0; u32 btf_id; int ret; @@ -4178,9 +4194,23 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, return -EACCES; } + if (reg->type & MEM_USER) { + verbose(env, + "R%d is ptr_%s access user memory: off=%d\n", + regno, tname, off); + return -EACCES; + } + + if (reg->type & MEM_PERCPU) { + verbose(env, + "R%d is ptr_%s access percpu memory: off=%d\n", + regno, tname, off); + return -EACCES; + } + if (env->ops->btf_struct_access) { ret = env->ops->btf_struct_access(&env->log, reg->btf, t, - off, size, atype, &btf_id); + off, size, atype, &btf_id, &flag); } else { if (atype != BPF_READ) { verbose(env, "only read is supported\n"); @@ -4188,14 +4218,14 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, } ret = btf_struct_access(&env->log, reg->btf, t, off, size, - atype, &btf_id); + atype, &btf_id, &flag); } if (ret < 0) return ret; if (atype == BPF_READ && value_regno >= 0) - mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id); + mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag); return 0; } @@ -4208,6 +4238,7 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env, { struct bpf_reg_state *reg = regs + regno; struct bpf_map *map = reg->map_ptr; + enum bpf_type_flag flag = 0; const struct btf_type *t; const char *tname; u32 btf_id; @@ -4245,12 +4276,12 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env, return -EACCES; } - ret = btf_struct_access(&env->log, btf_vmlinux, t, off, size, atype, &btf_id); + ret = btf_struct_access(&env->log, btf_vmlinux, t, off, size, atype, &btf_id, &flag); if (ret < 0) return ret; if (value_regno >= 0) - mark_btf_ld_reg(env, regs, value_regno, ret, btf_vmlinux, btf_id); + mark_btf_ld_reg(env, regs, value_regno, ret, btf_vmlinux, btf_id, flag); return 0; } @@ -4451,7 +4482,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn if (err < 0) return err; - err = check_ctx_access(env, insn_idx, off, size, t, ®_type, &btf, &btf_id); + err = check_ctx_access(env, insn_idx, off, size, t, ®_type, &btf, + &btf_id); if (err) verbose_linfo(env, insn_idx, "; "); if (!err && t == BPF_READ && value_regno >= 0) { @@ -4535,7 +4567,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn err = check_tp_buffer_access(env, reg, regno, off, size); if (!err && t == BPF_READ && value_regno >= 0) mark_reg_unknown(env, regs, value_regno); - } else if (reg->type == PTR_TO_BTF_ID) { + } else if (base_type(reg->type) == PTR_TO_BTF_ID && + !type_may_be_null(reg->type)) { err = check_ptr_to_btf_access(env, regs, regno, off, size, t, value_regno); } else if (reg->type == CONST_PTR_TO_MAP) { @@ -4543,7 +4576,6 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn value_regno); } else if (base_type(reg->type) == PTR_TO_BUF) { bool rdonly_mem = type_is_rdonly_mem(reg->type); - const char *buf_info; u32 *max_access; if (rdonly_mem) { @@ -4552,15 +4584,13 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn regno, reg_type_str(env, reg->type)); return -EACCES; } - buf_info = "rdonly"; max_access = &env->prog->aux->max_rdonly_access; } else { - buf_info = "rdwr"; max_access = &env->prog->aux->max_rdwr_access; } err = check_buffer_access(env, reg, regno, off, size, false, - buf_info, max_access); + max_access); if (!err && value_regno >= 0 && (rdonly_mem || t == BPF_READ)) mark_reg_unknown(env, regs, value_regno); @@ -4781,7 +4811,7 @@ static int check_stack_range_initialized( } if (is_spilled_reg(&state->stack[spi]) && - state->stack[spi].spilled_ptr.type == PTR_TO_BTF_ID) + base_type(state->stack[spi].spilled_ptr.type) == PTR_TO_BTF_ID) goto mark; if (is_spilled_reg(&state->stack[spi]) && @@ -4823,7 +4853,6 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, struct bpf_call_arg_meta *meta) { struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; - const char *buf_info; u32 *max_access; switch (base_type(reg->type)) { @@ -4850,15 +4879,13 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, if (meta && meta->raw_mode) return -EACCES; - buf_info = "rdonly"; max_access = &env->prog->aux->max_rdonly_access; } else { - buf_info = "rdwr"; max_access = &env->prog->aux->max_rdwr_access; } return check_buffer_access(env, reg, regno, reg->off, access_size, zero_size_allowed, - buf_info, max_access); + max_access); case PTR_TO_STACK: return check_stack_range_initialized( env, @@ -4877,6 +4904,62 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, } } +static int check_mem_size_reg(struct bpf_verifier_env *env, + struct bpf_reg_state *reg, u32 regno, + bool zero_size_allowed, + struct bpf_call_arg_meta *meta) +{ + int err; + + /* This is used to refine r0 return value bounds for helpers + * that enforce this value as an upper bound on return values. + * See do_refine_retval_range() for helpers that can refine + * the return value. C type of helper is u32 so we pull register + * bound from umax_value however, if negative verifier errors + * out. Only upper bounds can be learned because retval is an + * int type and negative retvals are allowed. + */ + if (meta) + meta->msize_max_value = reg->umax_value; + + /* The register is SCALAR_VALUE; the access check + * happens using its boundaries. + */ + if (!tnum_is_const(reg->var_off)) + /* For unprivileged variable accesses, disable raw + * mode so that the program is required to + * initialize all the memory that the helper could + * just partially fill up. + */ + meta = NULL; + + if (reg->smin_value < 0) { + verbose(env, "R%d min value is negative, either use unsigned or 'var &= const'\n", + regno); + return -EACCES; + } + + if (reg->umin_value == 0) { + err = check_helper_mem_access(env, regno - 1, 0, + zero_size_allowed, + meta); + if (err) + return err; + } + + if (reg->umax_value >= BPF_MAX_VAR_SIZ) { + verbose(env, "R%d unbounded memory access, use 'var &= const' or 'if (var < const)'\n", + regno); + return -EACCES; + } + err = check_helper_mem_access(env, regno - 1, + reg->umax_value, + zero_size_allowed, meta); + if (!err) + err = mark_chain_precision(env, regno); + return err; +} + int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, u32 regno, u32 mem_size) { @@ -4900,6 +4983,28 @@ int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, return check_helper_mem_access(env, regno, mem_size, true, NULL); } +int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, + u32 regno) +{ + struct bpf_reg_state *mem_reg = &cur_regs(env)[regno - 1]; + bool may_be_null = type_may_be_null(mem_reg->type); + struct bpf_reg_state saved_reg; + int err; + + WARN_ON_ONCE(regno < BPF_REG_2 || regno > BPF_REG_5); + + if (may_be_null) { + saved_reg = *mem_reg; + mark_ptr_not_null_reg(mem_reg); + } + + err = check_mem_size_reg(env, reg, regno, true, NULL); + + if (may_be_null) + *mem_reg = saved_reg; + return err; +} + /* Implementation details: * bpf_map_lookup returns PTR_TO_MAP_VALUE_OR_NULL * Two bpf_map_lookups (even with the same key) will have different reg->id. @@ -5159,7 +5264,7 @@ static const struct bpf_reg_types alloc_mem_types = { .types = { PTR_TO_MEM | ME static const struct bpf_reg_types const_map_ptr_types = { .types = { CONST_PTR_TO_MAP } }; static const struct bpf_reg_types btf_ptr_types = { .types = { PTR_TO_BTF_ID } }; static const struct bpf_reg_types spin_lock_types = { .types = { PTR_TO_MAP_VALUE } }; -static const struct bpf_reg_types percpu_btf_ptr_types = { .types = { PTR_TO_PERCPU_BTF_ID } }; +static const struct bpf_reg_types percpu_btf_ptr_types = { .types = { PTR_TO_BTF_ID | MEM_PERCPU } }; static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } }; static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } }; static const struct bpf_reg_types const_str_ptr_types = { .types = { PTR_TO_MAP_VALUE } }; @@ -5260,6 +5365,60 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno, return 0; } +int check_func_arg_reg_off(struct bpf_verifier_env *env, + const struct bpf_reg_state *reg, int regno, + enum bpf_arg_type arg_type, + bool is_release_func) +{ + bool fixed_off_ok = false, release_reg; + enum bpf_reg_type type = reg->type; + + switch ((u32)type) { + case SCALAR_VALUE: + /* Pointer types where reg offset is explicitly allowed: */ + case PTR_TO_PACKET: + case PTR_TO_PACKET_META: + case PTR_TO_MAP_KEY: + case PTR_TO_MAP_VALUE: + case PTR_TO_MEM: + case PTR_TO_MEM | MEM_RDONLY: + case PTR_TO_MEM | MEM_ALLOC: + case PTR_TO_BUF: + case PTR_TO_BUF | MEM_RDONLY: + case PTR_TO_STACK: + /* Some of the argument types nevertheless require a + * zero register offset. + */ + if (arg_type != ARG_PTR_TO_ALLOC_MEM) + return 0; + break; + /* All the rest must be rejected, except PTR_TO_BTF_ID which allows + * fixed offset. + */ + case PTR_TO_BTF_ID: + /* When referenced PTR_TO_BTF_ID is passed to release function, + * it's fixed offset must be 0. We rely on the property that + * only one referenced register can be passed to BPF helpers and + * kfuncs. In the other cases, fixed offset can be non-zero. + */ + release_reg = is_release_func && reg->ref_obj_id; + if (release_reg && reg->off) { + verbose(env, "R%d must have zero offset when passed to release func\n", + regno); + return -EINVAL; + } + /* For release_reg == true, fixed_off_ok must be false, but we + * already checked and rejected reg->off != 0 above, so set to + * true to allow fixed offset for all other cases. + */ + fixed_off_ok = true; + break; + default: + break; + } + return __check_ptr_off_reg(env, reg, regno, fixed_off_ok); +} + static int check_func_arg(struct bpf_verifier_env *env, u32 arg, struct bpf_call_arg_meta *meta, const struct bpf_func_proto *fn) @@ -5309,36 +5468,14 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, if (err) return err; - switch ((u32)type) { - case SCALAR_VALUE: - /* Pointer types where reg offset is explicitly allowed: */ - case PTR_TO_PACKET: - case PTR_TO_PACKET_META: - case PTR_TO_MAP_KEY: - case PTR_TO_MAP_VALUE: - case PTR_TO_MEM: - case PTR_TO_MEM | MEM_RDONLY: - case PTR_TO_MEM | MEM_ALLOC: - case PTR_TO_BUF: - case PTR_TO_BUF | MEM_RDONLY: - case PTR_TO_STACK: - /* Some of the argument types nevertheless require a - * zero register offset. - */ - if (arg_type == ARG_PTR_TO_ALLOC_MEM) - goto force_off_check; - break; - /* All the rest must be rejected: */ - default: -force_off_check: - err = __check_ptr_off_reg(env, reg, regno, - type == PTR_TO_BTF_ID); - if (err < 0) - return err; - break; - } + err = check_func_arg_reg_off(env, reg, regno, arg_type, is_release_function(meta->func_id)); + if (err) + return err; skip_type_check: + /* check_func_arg_reg_off relies on only one referenced register being + * allowed for BPF helpers. + */ if (reg->ref_obj_id) { if (meta->ref_obj_id) { verbose(env, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n", @@ -5439,51 +5576,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, } else if (arg_type_is_mem_size(arg_type)) { bool zero_size_allowed = (arg_type == ARG_CONST_SIZE_OR_ZERO); - /* This is used to refine r0 return value bounds for helpers - * that enforce this value as an upper bound on return values. - * See do_refine_retval_range() for helpers that can refine - * the return value. C type of helper is u32 so we pull register - * bound from umax_value however, if negative verifier errors - * out. Only upper bounds can be learned because retval is an - * int type and negative retvals are allowed. - */ - meta->msize_max_value = reg->umax_value; - - /* The register is SCALAR_VALUE; the access check - * happens using its boundaries. - */ - if (!tnum_is_const(reg->var_off)) - /* For unprivileged variable accesses, disable raw - * mode so that the program is required to - * initialize all the memory that the helper could - * just partially fill up. - */ - meta = NULL; - - if (reg->smin_value < 0) { - verbose(env, "R%d min value is negative, either use unsigned or 'var &= const'\n", - regno); - return -EACCES; - } - - if (reg->umin_value == 0) { - err = check_helper_mem_access(env, regno - 1, 0, - zero_size_allowed, - meta); - if (err) - return err; - } - - if (reg->umax_value >= BPF_MAX_VAR_SIZ) { - verbose(env, "R%d unbounded memory access, use 'var &= const' or 'if (var < const)'\n", - regno); - return -EACCES; - } - err = check_helper_mem_access(env, regno - 1, - reg->umax_value, - zero_size_allowed, meta); - if (!err) - err = mark_chain_precision(env, regno); + err = check_mem_size_reg(env, reg, regno, zero_size_allowed, meta); } else if (arg_type_is_alloc_size(arg_type)) { if (!tnum_is_const(reg->var_off)) { verbose(env, "R%d is not a known constant'\n", @@ -6842,22 +6935,23 @@ static void mark_btf_func_reg_size(struct bpf_verifier_env *env, u32 regno, } } -static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn) +static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, + int *insn_idx_p) { const struct btf_type *t, *func, *func_proto, *ptr_type; struct bpf_reg_state *regs = cur_regs(env); const char *func_name, *ptr_type_name; u32 i, nargs, func_id, ptr_type_id; - struct module *btf_mod = NULL; + int err, insn_idx = *insn_idx_p; const struct btf_param *args; struct btf *desc_btf; - int err; + bool acq; /* skip for now, but return error when we find this in fixup_kfunc_call */ if (!insn->imm) return 0; - desc_btf = find_kfunc_desc_btf(env, insn->imm, insn->off, &btf_mod); + desc_btf = find_kfunc_desc_btf(env, insn->imm, insn->off); if (IS_ERR(desc_btf)) return PTR_ERR(desc_btf); @@ -6866,23 +6960,43 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn) func_name = btf_name_by_offset(desc_btf, func->name_off); func_proto = btf_type_by_id(desc_btf, func->type); - if (!env->ops->check_kfunc_call || - !env->ops->check_kfunc_call(func_id, btf_mod)) { + if (!btf_kfunc_id_set_contains(desc_btf, resolve_prog_type(env->prog), + BTF_KFUNC_TYPE_CHECK, func_id)) { verbose(env, "calling kernel function %s is not allowed\n", func_name); return -EACCES; } + acq = btf_kfunc_id_set_contains(desc_btf, resolve_prog_type(env->prog), + BTF_KFUNC_TYPE_ACQUIRE, func_id); + /* Check the arguments */ err = btf_check_kfunc_arg_match(env, desc_btf, func_id, regs); - if (err) + if (err < 0) return err; + /* In case of release function, we get register number of refcounted + * PTR_TO_BTF_ID back from btf_check_kfunc_arg_match, do the release now + */ + if (err) { + err = release_reference(env, regs[err].ref_obj_id); + if (err) { + verbose(env, "kfunc %s#%d reference has not been acquired before\n", + func_name, func_id); + return err; + } + } for (i = 0; i < CALLER_SAVED_REGS; i++) mark_reg_not_init(env, regs, caller_saved[i]); /* Check return type */ t = btf_type_skip_modifiers(desc_btf, func_proto->type, NULL); + + if (acq && !btf_type_is_ptr(t)) { + verbose(env, "acquire kernel function does not return PTR_TO_BTF_ID\n"); + return -EINVAL; + } + if (btf_type_is_scalar(t)) { mark_reg_unknown(env, regs, BPF_REG_0); mark_btf_func_reg_size(env, BPF_REG_0, t->size); @@ -6901,7 +7015,21 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn) regs[BPF_REG_0].btf = desc_btf; regs[BPF_REG_0].type = PTR_TO_BTF_ID; regs[BPF_REG_0].btf_id = ptr_type_id; + if (btf_kfunc_id_set_contains(desc_btf, resolve_prog_type(env->prog), + BTF_KFUNC_TYPE_RET_NULL, func_id)) { + regs[BPF_REG_0].type |= PTR_MAYBE_NULL; + /* For mark_ptr_or_null_reg, see 93c230e3f5bd6 */ + regs[BPF_REG_0].id = ++env->id_gen; + } mark_btf_func_reg_size(env, BPF_REG_0, sizeof(void *)); + if (acq) { + int id = acquire_reference_state(env, insn_idx); + + if (id < 0) + return id; + regs[BPF_REG_0].id = id; + regs[BPF_REG_0].ref_obj_id = id; + } } /* else { add_kfunc_call() ensures it is btf_type_is_void(t) } */ nargs = btf_type_vlen(func_proto); @@ -9548,7 +9676,6 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn) dst_reg->mem_size = aux->btf_var.mem_size; break; case PTR_TO_BTF_ID: - case PTR_TO_PERCPU_BTF_ID: dst_reg->btf = aux->btf_var.btf; dst_reg->btf_id = aux->btf_var.btf_id; break; @@ -10273,8 +10400,7 @@ static void adjust_btf_func(struct bpf_verifier_env *env) aux->func_info[i].insn_off = env->subprog_info[i].start; } -#define MIN_BPF_LINEINFO_SIZE (offsetof(struct bpf_line_info, line_col) + \ - sizeof(((struct bpf_line_info *)(0))->line_col)) +#define MIN_BPF_LINEINFO_SIZE offsetofend(struct bpf_line_info, line_col) #define MAX_LINEINFO_REC_SIZE MAX_FUNCINFO_REC_SIZE static int check_btf_line(struct bpf_verifier_env *env, @@ -11549,7 +11675,7 @@ static int do_check(struct bpf_verifier_env *env) if (insn->src_reg == BPF_PSEUDO_CALL) err = check_func_call(env, insn, &env->insn_idx); else if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) - err = check_kfunc_call(env, insn); + err = check_kfunc_call(env, insn, &env->insn_idx); else err = check_helper_call(env, insn, &env->insn_idx); if (err) @@ -11748,7 +11874,7 @@ static int check_pseudo_btf_id(struct bpf_verifier_env *env, type = t->type; t = btf_type_skip_modifiers(btf, type, NULL); if (percpu) { - aux->btf_var.reg_type = PTR_TO_PERCPU_BTF_ID; + aux->btf_var.reg_type = PTR_TO_BTF_ID | MEM_PERCPU; aux->btf_var.btf = btf; aux->btf_var.btf_id = type; } else if (!btf_type_is_struct(t)) { @@ -12897,6 +13023,7 @@ static int jit_subprogs(struct bpf_verifier_env *env) func[i]->aux->name[0] = 'F'; func[i]->aux->stack_depth = env->subprog_info[i].stack_depth; func[i]->jit_requested = 1; + func[i]->blinding_requested = prog->blinding_requested; func[i]->aux->kfunc_tab = prog->aux->kfunc_tab; func[i]->aux->kfunc_btf_tab = prog->aux->kfunc_btf_tab; func[i]->aux->linfo = prog->aux->linfo; @@ -12992,6 +13119,7 @@ static int jit_subprogs(struct bpf_verifier_env *env) prog->jited = 1; prog->bpf_func = func[0]->bpf_func; + prog->jited_len = func[0]->jited_len; prog->aux->func = func; prog->aux->func_cnt = env->subprog_cnt; bpf_prog_jit_attempt_done(prog); @@ -13019,6 +13147,7 @@ static int jit_subprogs(struct bpf_verifier_env *env) out_undo_insn: /* cleanup main prog to be interpreted */ prog->jit_requested = 0; + prog->blinding_requested = 0; for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) { if (!bpf_pseudo_call(insn)) continue; @@ -13112,7 +13241,6 @@ static int do_misc_fixups(struct bpf_verifier_env *env) { struct bpf_prog *prog = env->prog; enum bpf_attach_type eatype = prog->expected_attach_type; - bool expect_blinding = bpf_jit_blinding_enabled(prog); enum bpf_prog_type prog_type = resolve_prog_type(prog); struct bpf_insn *insn = prog->insnsi; const struct bpf_func_proto *fn; @@ -13276,7 +13404,7 @@ static int do_misc_fixups(struct bpf_verifier_env *env) insn->code = BPF_JMP | BPF_TAIL_CALL; aux = &env->insn_aux_data[i + delta]; - if (env->bpf_capable && !expect_blinding && + if (env->bpf_capable && !prog->blinding_requested && prog->jit_requested && !bpf_map_key_poisoned(aux) && !bpf_map_ptr_poisoned(aux) && @@ -13364,6 +13492,26 @@ static int do_misc_fixups(struct bpf_verifier_env *env) goto patch_call_imm; } + if (insn->imm == BPF_FUNC_task_storage_get || + insn->imm == BPF_FUNC_sk_storage_get || + insn->imm == BPF_FUNC_inode_storage_get) { + if (env->prog->aux->sleepable) + insn_buf[0] = BPF_MOV64_IMM(BPF_REG_5, (__force __s32)GFP_KERNEL); + else + insn_buf[0] = BPF_MOV64_IMM(BPF_REG_5, (__force __s32)GFP_ATOMIC); + insn_buf[1] = *insn; + cnt = 2; + + new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt); + if (!new_prog) + return -ENOMEM; + + delta += cnt - 1; + env->prog = prog = new_prog; + insn = new_prog->insnsi + i + delta; + goto patch_call_imm; + } + /* BPF_EMIT_CALL() assumptions in some of the map_gen_lookup * and other inlining handlers are currently limited to 64 bit * only. diff --git a/kernel/exit.c b/kernel/exit.c index f5459e28b861..9b1862488bf2 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -64,6 +64,7 @@ #include #include #include +#include #include #include @@ -169,6 +170,7 @@ static void delayed_put_task_struct(struct rcu_head *rhp) struct task_struct *tsk = container_of(rhp, struct task_struct, rcu); kprobe_flush_task(tsk); + rethook_flush_task(tsk); perf_event_delayed_put(tsk); trace_sched_process_free(tsk); put_task_struct(tsk); diff --git a/kernel/fork.c b/kernel/fork.c index d74c30b6733b..834af51ed397 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2322,6 +2322,9 @@ static __latent_entropy struct task_struct *copy_process( #ifdef CONFIG_KRETPROBES p->kretprobe_instances.first = NULL; #endif +#ifdef CONFIG_RETHOOK + p->rethooks.first = NULL; +#endif /* * Ensure that the cgroup subsystem policies allow the new process to be diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 951c93216fc4..79f2eb617a62 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -212,6 +212,10 @@ unsigned long kallsyms_lookup_name(const char *name) unsigned long i; unsigned int off; + /* Skip the search for empty string. */ + if (!*name) + return 0; + for (i = 0, off = 0; i < kallsyms_num_syms; i++) { off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf)); diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 16a52a71732d..9bb54c0b3b2d 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -10,6 +10,17 @@ config USER_STACKTRACE_SUPPORT config NOP_TRACER bool +config HAVE_RETHOOK + bool + +config RETHOOK + bool + depends on HAVE_RETHOOK + help + Enable generic return hooking feature. This is an internal + API, which will be used by other function-entry hooking + features like fprobe and kprobes. + config HAVE_FUNCTION_TRACER bool help @@ -236,6 +247,21 @@ config DYNAMIC_FTRACE_WITH_ARGS depends on DYNAMIC_FTRACE depends on HAVE_DYNAMIC_FTRACE_WITH_ARGS +config FPROBE + bool "Kernel Function Probe (fprobe)" + depends on FUNCTION_TRACER + depends on DYNAMIC_FTRACE_WITH_REGS + depends on HAVE_RETHOOK + select RETHOOK + default n + help + This option enables kernel function probe (fprobe) based on ftrace. + The fprobe is similar to kprobes, but probes only for kernel function + entries and exits. This also can probe multiple functions by one + fprobe. + + If unsure, say N. + config FUNCTION_PROFILER bool "Kernel function profiler" depends on FUNCTION_TRACER diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 19ef3758da95..d77cd8032213 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -98,6 +98,8 @@ obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o obj-$(CONFIG_BOOTTIME_TRACING) += trace_boot.o obj-$(CONFIG_FTRACE_RECORD_RECURSION) += trace_recursion_record.o +obj-$(CONFIG_FPROBE) += fprobe.o +obj-$(CONFIG_RETHOOK) += rethook.o obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 8115fff17018..7fa2ebc07f60 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include @@ -77,6 +80,8 @@ u64 bpf_get_stack(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); static int bpf_btf_printf_prepare(struct btf_ptr *ptr, u32 btf_ptr_size, u64 flags, const struct btf **btf, s32 *btf_id); +static u64 bpf_kprobe_multi_cookie(struct bpf_run_ctx *ctx); +static u64 bpf_kprobe_multi_entry_ip(struct bpf_run_ctx *ctx); /** * trace_call_bpf - invoke BPF program @@ -1032,6 +1037,30 @@ static const struct bpf_func_proto bpf_get_func_ip_proto_kprobe = { .arg1_type = ARG_PTR_TO_CTX, }; +BPF_CALL_1(bpf_get_func_ip_kprobe_multi, struct pt_regs *, regs) +{ + return bpf_kprobe_multi_entry_ip(current->bpf_ctx); +} + +static const struct bpf_func_proto bpf_get_func_ip_proto_kprobe_multi = { + .func = bpf_get_func_ip_kprobe_multi, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +}; + +BPF_CALL_1(bpf_get_attach_cookie_kprobe_multi, struct pt_regs *, regs) +{ + return bpf_kprobe_multi_cookie(current->bpf_ctx); +} + +static const struct bpf_func_proto bpf_get_attach_cookie_proto_kmulti = { + .func = bpf_get_attach_cookie_kprobe_multi, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +}; + BPF_CALL_1(bpf_get_attach_cookie_trace, void *, ctx) { struct bpf_trace_run_ctx *run_ctx; @@ -1231,6 +1260,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_get_task_stack_proto; case BPF_FUNC_copy_from_user: return prog->aux->sleepable ? &bpf_copy_from_user_proto : NULL; + case BPF_FUNC_copy_from_user_task: + return prog->aux->sleepable ? &bpf_copy_from_user_task_proto : NULL; case BPF_FUNC_snprintf_btf: return &bpf_snprintf_btf_proto; case BPF_FUNC_per_cpu_ptr: @@ -1273,9 +1304,13 @@ kprobe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_override_return_proto; #endif case BPF_FUNC_get_func_ip: - return &bpf_get_func_ip_proto_kprobe; + return prog->expected_attach_type == BPF_TRACE_KPROBE_MULTI ? + &bpf_get_func_ip_proto_kprobe_multi : + &bpf_get_func_ip_proto_kprobe; case BPF_FUNC_get_attach_cookie: - return &bpf_get_attach_cookie_proto_trace; + return prog->expected_attach_type == BPF_TRACE_KPROBE_MULTI ? + &bpf_get_attach_cookie_proto_kmulti : + &bpf_get_attach_cookie_proto_trace; default: return bpf_tracing_func_proto(func_id, prog); } @@ -1558,6 +1593,7 @@ static const struct bpf_func_proto bpf_perf_event_output_proto_raw_tp = { extern const struct bpf_func_proto bpf_skb_output_proto; extern const struct bpf_func_proto bpf_xdp_output_proto; +extern const struct bpf_func_proto bpf_xdp_get_buff_len_trace_proto; BPF_CALL_3(bpf_get_stackid_raw_tp, struct bpf_raw_tracepoint_args *, args, struct bpf_map *, map, u64, flags) @@ -1657,6 +1693,8 @@ tracing_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_sock_from_file_proto; case BPF_FUNC_get_socket_cookie: return &bpf_get_socket_ptr_cookie_proto; + case BPF_FUNC_xdp_get_buff_len: + return &bpf_xdp_get_buff_len_trace_proto; #endif case BPF_FUNC_seq_printf: return prog->expected_attach_type == BPF_TRACE_ITER ? @@ -2172,3 +2210,314 @@ static int __init bpf_event_init(void) fs_initcall(bpf_event_init); #endif /* CONFIG_MODULES */ + +#ifdef CONFIG_FPROBE +struct bpf_kprobe_multi_link { + struct bpf_link link; + struct fprobe fp; + unsigned long *addrs; + u64 *cookies; + u32 cnt; +}; + +struct bpf_kprobe_multi_run_ctx { + struct bpf_run_ctx run_ctx; + struct bpf_kprobe_multi_link *link; + unsigned long entry_ip; +}; + +static void bpf_kprobe_multi_link_release(struct bpf_link *link) +{ + struct bpf_kprobe_multi_link *kmulti_link; + + kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); + unregister_fprobe(&kmulti_link->fp); +} + +static void bpf_kprobe_multi_link_dealloc(struct bpf_link *link) +{ + struct bpf_kprobe_multi_link *kmulti_link; + + kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); + kvfree(kmulti_link->addrs); + kvfree(kmulti_link->cookies); + kfree(kmulti_link); +} + +static const struct bpf_link_ops bpf_kprobe_multi_link_lops = { + .release = bpf_kprobe_multi_link_release, + .dealloc = bpf_kprobe_multi_link_dealloc, +}; + +static void bpf_kprobe_multi_cookie_swap(void *a, void *b, int size, const void *priv) +{ + const struct bpf_kprobe_multi_link *link = priv; + unsigned long *addr_a = a, *addr_b = b; + u64 *cookie_a, *cookie_b; + unsigned long tmp1; + u64 tmp2; + + cookie_a = link->cookies + (addr_a - link->addrs); + cookie_b = link->cookies + (addr_b - link->addrs); + + /* swap addr_a/addr_b and cookie_a/cookie_b values */ + tmp1 = *addr_a; *addr_a = *addr_b; *addr_b = tmp1; + tmp2 = *cookie_a; *cookie_a = *cookie_b; *cookie_b = tmp2; +} + +static int __bpf_kprobe_multi_cookie_cmp(const void *a, const void *b) +{ + const unsigned long *addr_a = a, *addr_b = b; + + if (*addr_a == *addr_b) + return 0; + return *addr_a < *addr_b ? -1 : 1; +} + +static int bpf_kprobe_multi_cookie_cmp(const void *a, const void *b, const void *priv) +{ + return __bpf_kprobe_multi_cookie_cmp(a, b); +} + +static u64 bpf_kprobe_multi_cookie(struct bpf_run_ctx *ctx) +{ + struct bpf_kprobe_multi_run_ctx *run_ctx; + struct bpf_kprobe_multi_link *link; + u64 *cookie, entry_ip; + unsigned long *addr; + + if (WARN_ON_ONCE(!ctx)) + return 0; + run_ctx = container_of(current->bpf_ctx, struct bpf_kprobe_multi_run_ctx, run_ctx); + link = run_ctx->link; + if (!link->cookies) + return 0; + entry_ip = run_ctx->entry_ip; + addr = bsearch(&entry_ip, link->addrs, link->cnt, sizeof(entry_ip), + __bpf_kprobe_multi_cookie_cmp); + if (!addr) + return 0; + cookie = link->cookies + (addr - link->addrs); + return *cookie; +} + +static u64 bpf_kprobe_multi_entry_ip(struct bpf_run_ctx *ctx) +{ + struct bpf_kprobe_multi_run_ctx *run_ctx; + + run_ctx = container_of(current->bpf_ctx, struct bpf_kprobe_multi_run_ctx, run_ctx); + return run_ctx->entry_ip; +} + +static int +kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link, + unsigned long entry_ip, struct pt_regs *regs) +{ + struct bpf_kprobe_multi_run_ctx run_ctx = { + .link = link, + .entry_ip = entry_ip, + }; + struct bpf_run_ctx *old_run_ctx; + int err; + + if (unlikely(__this_cpu_inc_return(bpf_prog_active) != 1)) { + err = 0; + goto out; + } + + migrate_disable(); + rcu_read_lock(); + old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx); + err = bpf_prog_run(link->link.prog, regs); + bpf_reset_run_ctx(old_run_ctx); + rcu_read_unlock(); + migrate_enable(); + + out: + __this_cpu_dec(bpf_prog_active); + return err; +} + +static void +kprobe_multi_link_handler(struct fprobe *fp, unsigned long entry_ip, + struct pt_regs *regs) +{ + struct bpf_kprobe_multi_link *link; + + link = container_of(fp, struct bpf_kprobe_multi_link, fp); + kprobe_multi_link_prog_run(link, entry_ip, regs); +} + +static int +kprobe_multi_resolve_syms(const void *usyms, u32 cnt, + unsigned long *addrs) +{ + unsigned long addr, size; + const char **syms; + int err = -ENOMEM; + unsigned int i; + char *func; + + size = cnt * sizeof(*syms); + syms = kvzalloc(size, GFP_KERNEL); + if (!syms) + return -ENOMEM; + + func = kmalloc(KSYM_NAME_LEN, GFP_KERNEL); + if (!func) + goto error; + + if (copy_from_user(syms, usyms, size)) { + err = -EFAULT; + goto error; + } + + for (i = 0; i < cnt; i++) { + err = strncpy_from_user(func, syms[i], KSYM_NAME_LEN); + if (err == KSYM_NAME_LEN) + err = -E2BIG; + if (err < 0) + goto error; + err = -EINVAL; + addr = kallsyms_lookup_name(func); + if (!addr) + goto error; + if (!kallsyms_lookup_size_offset(addr, &size, NULL)) + goto error; + addr = ftrace_location_range(addr, addr + size - 1); + if (!addr) + goto error; + addrs[i] = addr; + } + + err = 0; +error: + kvfree(syms); + kfree(func); + return err; +} + +int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) +{ + struct bpf_kprobe_multi_link *link = NULL; + struct bpf_link_primer link_primer; + void __user *ucookies; + unsigned long *addrs; + u32 flags, cnt, size; + void __user *uaddrs; + u64 *cookies = NULL; + void __user *usyms; + int err; + + /* no support for 32bit archs yet */ + if (sizeof(u64) != sizeof(void *)) + return -EOPNOTSUPP; + + if (prog->expected_attach_type != BPF_TRACE_KPROBE_MULTI) + return -EINVAL; + + flags = attr->link_create.kprobe_multi.flags; + if (flags & ~BPF_F_KPROBE_MULTI_RETURN) + return -EINVAL; + + uaddrs = u64_to_user_ptr(attr->link_create.kprobe_multi.addrs); + usyms = u64_to_user_ptr(attr->link_create.kprobe_multi.syms); + if (!!uaddrs == !!usyms) + return -EINVAL; + + cnt = attr->link_create.kprobe_multi.cnt; + if (!cnt) + return -EINVAL; + + size = cnt * sizeof(*addrs); + addrs = kvmalloc(size, GFP_KERNEL); + if (!addrs) + return -ENOMEM; + + if (uaddrs) { + if (copy_from_user(addrs, uaddrs, size)) { + err = -EFAULT; + goto error; + } + } else { + err = kprobe_multi_resolve_syms(usyms, cnt, addrs); + if (err) + goto error; + } + + ucookies = u64_to_user_ptr(attr->link_create.kprobe_multi.cookies); + if (ucookies) { + cookies = kvmalloc(size, GFP_KERNEL); + if (!cookies) { + err = -ENOMEM; + goto error; + } + if (copy_from_user(cookies, ucookies, size)) { + err = -EFAULT; + goto error; + } + } + + link = kzalloc(sizeof(*link), GFP_KERNEL); + if (!link) { + err = -ENOMEM; + goto error; + } + + bpf_link_init(&link->link, BPF_LINK_TYPE_KPROBE_MULTI, + &bpf_kprobe_multi_link_lops, prog); + + err = bpf_link_prime(&link->link, &link_primer); + if (err) + goto error; + + if (flags & BPF_F_KPROBE_MULTI_RETURN) + link->fp.exit_handler = kprobe_multi_link_handler; + else + link->fp.entry_handler = kprobe_multi_link_handler; + + link->addrs = addrs; + link->cookies = cookies; + link->cnt = cnt; + + if (cookies) { + /* + * Sorting addresses will trigger sorting cookies as well + * (check bpf_kprobe_multi_cookie_swap). This way we can + * find cookie based on the address in bpf_get_attach_cookie + * helper. + */ + sort_r(addrs, cnt, sizeof(*addrs), + bpf_kprobe_multi_cookie_cmp, + bpf_kprobe_multi_cookie_swap, + link); + } + + err = register_fprobe_ips(&link->fp, addrs, cnt); + if (err) { + bpf_link_cleanup(&link_primer); + return err; + } + + return bpf_link_settle(&link_primer); + +error: + kfree(link); + kvfree(addrs); + kvfree(cookies); + return err; +} +#else /* !CONFIG_FPROBE */ +int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) +{ + return -EOPNOTSUPP; +} +static u64 bpf_kprobe_multi_cookie(struct bpf_run_ctx *ctx) +{ + return 0; +} +static u64 bpf_kprobe_multi_entry_ip(struct bpf_run_ctx *ctx) +{ + return 0; +} +#endif diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c new file mode 100644 index 000000000000..8b2dd5b9dcd1 --- /dev/null +++ b/kernel/trace/fprobe.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fprobe - Simple ftrace probe wrapper for function entry. + */ +#define pr_fmt(fmt) "fprobe: " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "trace.h" + +struct fprobe_rethook_node { + struct rethook_node node; + unsigned long entry_ip; +}; + +static void fprobe_handler(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *ops, struct ftrace_regs *fregs) +{ + struct fprobe_rethook_node *fpr; + struct rethook_node *rh; + struct fprobe *fp; + int bit; + + fp = container_of(ops, struct fprobe, ops); + if (fprobe_disabled(fp)) + return; + + bit = ftrace_test_recursion_trylock(ip, parent_ip); + if (bit < 0) { + fp->nmissed++; + return; + } + + if (fp->entry_handler) + fp->entry_handler(fp, ip, ftrace_get_regs(fregs)); + + if (fp->exit_handler) { + rh = rethook_try_get(fp->rethook); + if (!rh) { + fp->nmissed++; + goto out; + } + fpr = container_of(rh, struct fprobe_rethook_node, node); + fpr->entry_ip = ip; + rethook_hook(rh, ftrace_get_regs(fregs), true); + } + +out: + ftrace_test_recursion_unlock(bit); +} +NOKPROBE_SYMBOL(fprobe_handler); + +static void fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *ops, struct ftrace_regs *fregs) +{ + struct fprobe *fp = container_of(ops, struct fprobe, ops); + + if (unlikely(kprobe_running())) { + fp->nmissed++; + return; + } + kprobe_busy_begin(); + fprobe_handler(ip, parent_ip, ops, fregs); + kprobe_busy_end(); +} + +static void fprobe_exit_handler(struct rethook_node *rh, void *data, + struct pt_regs *regs) +{ + struct fprobe *fp = (struct fprobe *)data; + struct fprobe_rethook_node *fpr; + + if (!fp || fprobe_disabled(fp)) + return; + + fpr = container_of(rh, struct fprobe_rethook_node, node); + + fp->exit_handler(fp, fpr->entry_ip, regs); +} +NOKPROBE_SYMBOL(fprobe_exit_handler); + +/* Convert ftrace location address from symbols */ +static unsigned long *get_ftrace_locations(const char **syms, int num) +{ + unsigned long addr, size; + unsigned long *addrs; + int i; + + /* Convert symbols to symbol address */ + addrs = kcalloc(num, sizeof(*addrs), GFP_KERNEL); + if (!addrs) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < num; i++) { + addr = kallsyms_lookup_name(syms[i]); + if (!addr) /* Maybe wrong symbol */ + goto error; + + /* Convert symbol address to ftrace location. */ + if (!kallsyms_lookup_size_offset(addr, &size, NULL) || !size) + goto error; + + addr = ftrace_location_range(addr, addr + size - 1); + if (!addr) /* No dynamic ftrace there. */ + goto error; + + addrs[i] = addr; + } + + return addrs; + +error: + kfree(addrs); + + return ERR_PTR(-ENOENT); +} + +static void fprobe_init(struct fprobe *fp) +{ + fp->nmissed = 0; + if (fprobe_shared_with_kprobes(fp)) + fp->ops.func = fprobe_kprobe_handler; + else + fp->ops.func = fprobe_handler; + fp->ops.flags |= FTRACE_OPS_FL_SAVE_REGS; +} + +static int fprobe_init_rethook(struct fprobe *fp, int num) +{ + int i, size; + + if (num < 0) + return -EINVAL; + + if (!fp->exit_handler) { + fp->rethook = NULL; + return 0; + } + + /* Initialize rethook if needed */ + size = num * num_possible_cpus() * 2; + if (size < 0) + return -E2BIG; + + fp->rethook = rethook_alloc((void *)fp, fprobe_exit_handler); + for (i = 0; i < size; i++) { + struct rethook_node *node; + + node = kzalloc(sizeof(struct fprobe_rethook_node), GFP_KERNEL); + if (!node) { + rethook_free(fp->rethook); + fp->rethook = NULL; + return -ENOMEM; + } + rethook_add_node(fp->rethook, node); + } + return 0; +} + +static void fprobe_fail_cleanup(struct fprobe *fp) +{ + if (fp->rethook) { + /* Don't need to cleanup rethook->handler because this is not used. */ + rethook_free(fp->rethook); + fp->rethook = NULL; + } + ftrace_free_filter(&fp->ops); +} + +/** + * register_fprobe() - Register fprobe to ftrace by pattern. + * @fp: A fprobe data structure to be registered. + * @filter: A wildcard pattern of probed symbols. + * @notfilter: A wildcard pattern of NOT probed symbols. + * + * Register @fp to ftrace for enabling the probe on the symbols matched to @filter. + * If @notfilter is not NULL, the symbols matched the @notfilter are not probed. + * + * Return 0 if @fp is registered successfully, -errno if not. + */ +int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter) +{ + struct ftrace_hash *hash; + unsigned char *str; + int ret, len; + + if (!fp || !filter) + return -EINVAL; + + fprobe_init(fp); + + len = strlen(filter); + str = kstrdup(filter, GFP_KERNEL); + ret = ftrace_set_filter(&fp->ops, str, len, 0); + kfree(str); + if (ret) + return ret; + + if (notfilter) { + len = strlen(notfilter); + str = kstrdup(notfilter, GFP_KERNEL); + ret = ftrace_set_notrace(&fp->ops, str, len, 0); + kfree(str); + if (ret) + goto out; + } + + /* TODO: + * correctly calculate the total number of filtered symbols + * from both filter and notfilter. + */ + hash = fp->ops.local_hash.filter_hash; + if (WARN_ON_ONCE(!hash)) + goto out; + + ret = fprobe_init_rethook(fp, (int)hash->count); + if (!ret) + ret = register_ftrace_function(&fp->ops); + +out: + if (ret) + fprobe_fail_cleanup(fp); + return ret; +} +EXPORT_SYMBOL_GPL(register_fprobe); + +/** + * register_fprobe_ips() - Register fprobe to ftrace by address. + * @fp: A fprobe data structure to be registered. + * @addrs: An array of target ftrace location addresses. + * @num: The number of entries of @addrs. + * + * Register @fp to ftrace for enabling the probe on the address given by @addrs. + * The @addrs must be the addresses of ftrace location address, which may be + * the symbol address + arch-dependent offset. + * If you unsure what this mean, please use other registration functions. + * + * Return 0 if @fp is registered successfully, -errno if not. + */ +int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num) +{ + int ret; + + if (!fp || !addrs || num <= 0) + return -EINVAL; + + fprobe_init(fp); + + ret = ftrace_set_filter_ips(&fp->ops, addrs, num, 0, 0); + if (ret) + return ret; + + ret = fprobe_init_rethook(fp, num); + if (!ret) + ret = register_ftrace_function(&fp->ops); + + if (ret) + fprobe_fail_cleanup(fp); + return ret; +} +EXPORT_SYMBOL_GPL(register_fprobe_ips); + +/** + * register_fprobe_syms() - Register fprobe to ftrace by symbols. + * @fp: A fprobe data structure to be registered. + * @syms: An array of target symbols. + * @num: The number of entries of @syms. + * + * Register @fp to the symbols given by @syms array. This will be useful if + * you are sure the symbols exist in the kernel. + * + * Return 0 if @fp is registered successfully, -errno if not. + */ +int register_fprobe_syms(struct fprobe *fp, const char **syms, int num) +{ + unsigned long *addrs; + int ret; + + if (!fp || !syms || num <= 0) + return -EINVAL; + + addrs = get_ftrace_locations(syms, num); + if (IS_ERR(addrs)) + return PTR_ERR(addrs); + + ret = register_fprobe_ips(fp, addrs, num); + + kfree(addrs); + + return ret; +} +EXPORT_SYMBOL_GPL(register_fprobe_syms); + +/** + * unregister_fprobe() - Unregister fprobe from ftrace + * @fp: A fprobe data structure to be unregistered. + * + * Unregister fprobe (and remove ftrace hooks from the function entries). + * + * Return 0 if @fp is unregistered successfully, -errno if not. + */ +int unregister_fprobe(struct fprobe *fp) +{ + int ret; + + if (!fp || fp->ops.func != fprobe_handler) + return -EINVAL; + + /* + * rethook_free() starts disabling the rethook, but the rethook handlers + * may be running on other processors at this point. To make sure that all + * current running handlers are finished, call unregister_ftrace_function() + * after this. + */ + if (fp->rethook) + rethook_free(fp->rethook); + + ret = unregister_ftrace_function(&fp->ops); + if (ret < 0) + return ret; + + ftrace_free_filter(&fp->ops); + + return ret; +} +EXPORT_SYMBOL_GPL(unregister_fprobe); diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index e59d80c9afd7..2e114659f7f8 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -4958,7 +4958,7 @@ ftrace_notrace_write(struct file *file, const char __user *ubuf, } static int -ftrace_match_addr(struct ftrace_hash *hash, unsigned long ip, int remove) +__ftrace_match_addr(struct ftrace_hash *hash, unsigned long ip, int remove) { struct ftrace_func_entry *entry; @@ -4976,9 +4976,30 @@ ftrace_match_addr(struct ftrace_hash *hash, unsigned long ip, int remove) return add_hash_entry(hash, ip); } +static int +ftrace_match_addr(struct ftrace_hash *hash, unsigned long *ips, + unsigned int cnt, int remove) +{ + unsigned int i; + int err; + + for (i = 0; i < cnt; i++) { + err = __ftrace_match_addr(hash, ips[i], remove); + if (err) { + /* + * This expects the @hash is a temporary hash and if this + * fails the caller must free the @hash. + */ + return err; + } + } + return 0; +} + static int ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len, - unsigned long ip, int remove, int reset, int enable) + unsigned long *ips, unsigned int cnt, + int remove, int reset, int enable) { struct ftrace_hash **orig_hash; struct ftrace_hash *hash; @@ -5008,8 +5029,8 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len, ret = -EINVAL; goto out_regex_unlock; } - if (ip) { - ret = ftrace_match_addr(hash, ip, remove); + if (ips) { + ret = ftrace_match_addr(hash, ips, cnt, remove); if (ret < 0) goto out_regex_unlock; } @@ -5026,10 +5047,10 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len, } static int -ftrace_set_addr(struct ftrace_ops *ops, unsigned long ip, int remove, - int reset, int enable) +ftrace_set_addr(struct ftrace_ops *ops, unsigned long *ips, unsigned int cnt, + int remove, int reset, int enable) { - return ftrace_set_hash(ops, NULL, 0, ip, remove, reset, enable); + return ftrace_set_hash(ops, NULL, 0, ips, cnt, remove, reset, enable); } #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS @@ -5634,10 +5655,29 @@ int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip, int remove, int reset) { ftrace_ops_init(ops); - return ftrace_set_addr(ops, ip, remove, reset, 1); + return ftrace_set_addr(ops, &ip, 1, remove, reset, 1); } EXPORT_SYMBOL_GPL(ftrace_set_filter_ip); +/** + * ftrace_set_filter_ips - set functions to filter on in ftrace by addresses + * @ops - the ops to set the filter with + * @ips - the array of addresses to add to or remove from the filter. + * @cnt - the number of addresses in @ips + * @remove - non zero to remove ips from the filter + * @reset - non zero to reset all filters before applying this filter. + * + * Filters denote which functions should be enabled when tracing is enabled + * If @ips array or any ip specified within is NULL , it fails to update filter. + */ +int ftrace_set_filter_ips(struct ftrace_ops *ops, unsigned long *ips, + unsigned int cnt, int remove, int reset) +{ + ftrace_ops_init(ops); + return ftrace_set_addr(ops, ips, cnt, remove, reset, 1); +} +EXPORT_SYMBOL_GPL(ftrace_set_filter_ips); + /** * ftrace_ops_set_global_filter - setup ops to use global filters * @ops - the ops which will use the global filters @@ -5659,7 +5699,7 @@ static int ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, int reset, int enable) { - return ftrace_set_hash(ops, buf, len, 0, 0, reset, enable); + return ftrace_set_hash(ops, buf, len, NULL, 0, 0, reset, enable); } /** diff --git a/kernel/trace/rethook.c b/kernel/trace/rethook.c new file mode 100644 index 000000000000..ab463a4d2b23 --- /dev/null +++ b/kernel/trace/rethook.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define pr_fmt(fmt) "rethook: " fmt + +#include +#include +#include +#include +#include +#include +#include + +/* Return hook list (shadow stack by list) */ + +/* + * This function is called from delayed_put_task_struct() when a task is + * dead and cleaned up to recycle any kretprobe instances associated with + * this task. These left over instances represent probed functions that + * have been called but will never return. + */ +void rethook_flush_task(struct task_struct *tk) +{ + struct rethook_node *rhn; + struct llist_node *node; + + node = __llist_del_all(&tk->rethooks); + while (node) { + rhn = container_of(node, struct rethook_node, llist); + node = node->next; + preempt_disable(); + rethook_recycle(rhn); + preempt_enable(); + } +} + +static void rethook_free_rcu(struct rcu_head *head) +{ + struct rethook *rh = container_of(head, struct rethook, rcu); + struct rethook_node *rhn; + struct freelist_node *node; + int count = 1; + + node = rh->pool.head; + while (node) { + rhn = container_of(node, struct rethook_node, freelist); + node = node->next; + kfree(rhn); + count++; + } + + /* The rh->ref is the number of pooled node + 1 */ + if (refcount_sub_and_test(count, &rh->ref)) + kfree(rh); +} + +/** + * rethook_free() - Free struct rethook. + * @rh: the struct rethook to be freed. + * + * Free the rethook. Before calling this function, user must ensure the + * @rh::data is cleaned if needed (or, the handler can access it after + * calling this function.) This function will set the @rh to be freed + * after all rethook_node are freed (not soon). And the caller must + * not touch @rh after calling this. + */ +void rethook_free(struct rethook *rh) +{ + rcu_assign_pointer(rh->handler, NULL); + + call_rcu(&rh->rcu, rethook_free_rcu); +} + +/** + * rethook_alloc() - Allocate struct rethook. + * @data: a data to pass the @handler when hooking the return. + * @handler: the return hook callback function. + * + * Allocate and initialize a new rethook with @data and @handler. + * Return NULL if memory allocation fails or @handler is NULL. + * Note that @handler == NULL means this rethook is going to be freed. + */ +struct rethook *rethook_alloc(void *data, rethook_handler_t handler) +{ + struct rethook *rh = kzalloc(sizeof(struct rethook), GFP_KERNEL); + + if (!rh || !handler) + return NULL; + + rh->data = data; + rh->handler = handler; + rh->pool.head = NULL; + refcount_set(&rh->ref, 1); + + return rh; +} + +/** + * rethook_add_node() - Add a new node to the rethook. + * @rh: the struct rethook. + * @node: the struct rethook_node to be added. + * + * Add @node to @rh. User must allocate @node (as a part of user's + * data structure.) The @node fields are initialized in this function. + */ +void rethook_add_node(struct rethook *rh, struct rethook_node *node) +{ + node->rethook = rh; + freelist_add(&node->freelist, &rh->pool); + refcount_inc(&rh->ref); +} + +static void free_rethook_node_rcu(struct rcu_head *head) +{ + struct rethook_node *node = container_of(head, struct rethook_node, rcu); + + if (refcount_dec_and_test(&node->rethook->ref)) + kfree(node->rethook); + kfree(node); +} + +/** + * rethook_recycle() - return the node to rethook. + * @node: The struct rethook_node to be returned. + * + * Return back the @node to @node::rethook. If the @node::rethook is already + * marked as freed, this will free the @node. + */ +void rethook_recycle(struct rethook_node *node) +{ + lockdep_assert_preemption_disabled(); + + if (likely(READ_ONCE(node->rethook->handler))) + freelist_add(&node->freelist, &node->rethook->pool); + else + call_rcu(&node->rcu, free_rethook_node_rcu); +} +NOKPROBE_SYMBOL(rethook_recycle); + +/** + * rethook_try_get() - get an unused rethook node. + * @rh: The struct rethook which pools the nodes. + * + * Get an unused rethook node from @rh. If the node pool is empty, this + * will return NULL. Caller must disable preemption. + */ +struct rethook_node *rethook_try_get(struct rethook *rh) +{ + rethook_handler_t handler = READ_ONCE(rh->handler); + struct freelist_node *fn; + + lockdep_assert_preemption_disabled(); + + /* Check whether @rh is going to be freed. */ + if (unlikely(!handler)) + return NULL; + + fn = freelist_try_get(&rh->pool); + if (!fn) + return NULL; + + return container_of(fn, struct rethook_node, freelist); +} +NOKPROBE_SYMBOL(rethook_try_get); + +/** + * rethook_hook() - Hook the current function return. + * @node: The struct rethook node to hook the function return. + * @regs: The struct pt_regs for the function entry. + * @mcount: True if this is called from mcount(ftrace) context. + * + * Hook the current running function return. This must be called when the + * function entry (or at least @regs must be the registers of the function + * entry.) @mcount is used for identifying the context. If this is called + * from ftrace (mcount) callback, @mcount must be set true. If this is called + * from the real function entry (e.g. kprobes) @mcount must be set false. + * This is because the way to hook the function return depends on the context. + */ +void rethook_hook(struct rethook_node *node, struct pt_regs *regs, bool mcount) +{ + arch_rethook_prepare(node, regs, mcount); + __llist_add(&node->llist, ¤t->rethooks); +} +NOKPROBE_SYMBOL(rethook_hook); + +/* This assumes the 'tsk' is the current task or is not running. */ +static unsigned long __rethook_find_ret_addr(struct task_struct *tsk, + struct llist_node **cur) +{ + struct rethook_node *rh = NULL; + struct llist_node *node = *cur; + + if (!node) + node = tsk->rethooks.first; + else + node = node->next; + + while (node) { + rh = container_of(node, struct rethook_node, llist); + if (rh->ret_addr != (unsigned long)arch_rethook_trampoline) { + *cur = node; + return rh->ret_addr; + } + node = node->next; + } + return 0; +} +NOKPROBE_SYMBOL(__rethook_find_ret_addr); + +/** + * rethook_find_ret_addr -- Find correct return address modified by rethook + * @tsk: Target task + * @frame: A frame pointer + * @cur: a storage of the loop cursor llist_node pointer for next call + * + * Find the correct return address modified by a rethook on @tsk in unsigned + * long type. + * The @tsk must be 'current' or a task which is not running. @frame is a hint + * to get the currect return address - which is compared with the + * rethook::frame field. The @cur is a loop cursor for searching the + * kretprobe return addresses on the @tsk. The '*@cur' should be NULL at the + * first call, but '@cur' itself must NOT NULL. + * + * Returns found address value or zero if not found. + */ +unsigned long rethook_find_ret_addr(struct task_struct *tsk, unsigned long frame, + struct llist_node **cur) +{ + struct rethook_node *rhn = NULL; + unsigned long ret; + + if (WARN_ON_ONCE(!cur)) + return 0; + + if (WARN_ON_ONCE(tsk != current && task_is_running(tsk))) + return 0; + + do { + ret = __rethook_find_ret_addr(tsk, cur); + if (!ret) + break; + rhn = container_of(*cur, struct rethook_node, llist); + } while (rhn->frame != frame); + + return ret; +} +NOKPROBE_SYMBOL(rethook_find_ret_addr); + +void __weak arch_rethook_fixup_return(struct pt_regs *regs, + unsigned long correct_ret_addr) +{ + /* + * Do nothing by default. If the architecture which uses a + * frame pointer to record real return address on the stack, + * it should fill this function to fixup the return address + * so that stacktrace works from the rethook handler. + */ +} + +/* This function will be called from each arch-defined trampoline. */ +unsigned long rethook_trampoline_handler(struct pt_regs *regs, + unsigned long frame) +{ + struct llist_node *first, *node = NULL; + unsigned long correct_ret_addr; + rethook_handler_t handler; + struct rethook_node *rhn; + + correct_ret_addr = __rethook_find_ret_addr(current, &node); + if (!correct_ret_addr) { + pr_err("rethook: Return address not found! Maybe there is a bug in the kernel\n"); + BUG_ON(1); + } + + instruction_pointer_set(regs, correct_ret_addr); + + /* + * These loops must be protected from rethook_free_rcu() because those + * are accessing 'rhn->rethook'. + */ + preempt_disable(); + + /* + * Run the handler on the shadow stack. Do not unlink the list here because + * stackdump inside the handlers needs to decode it. + */ + first = current->rethooks.first; + while (first) { + rhn = container_of(first, struct rethook_node, llist); + if (WARN_ON_ONCE(rhn->frame != frame)) + break; + handler = READ_ONCE(rhn->rethook->handler); + if (handler) + handler(rhn, rhn->rethook->data, regs); + + if (first == node) + break; + first = first->next; + } + + /* Fixup registers for returning to correct address. */ + arch_rethook_fixup_return(regs, correct_ret_addr); + + /* Unlink used shadow stack */ + first = current->rethooks.first; + current->rethooks.first = node->next; + node->next = NULL; + + while (first) { + rhn = container_of(first, struct rethook_node, llist); + first = first->next; + rethook_recycle(rhn); + } + preempt_enable(); + + return correct_ret_addr; +} +NOKPROBE_SYMBOL(rethook_trampoline_handler); diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index a5556ab05240..1063ce0abd97 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -296,7 +296,7 @@ config DEBUG_INFO_DWARF4 config DEBUG_INFO_DWARF5 bool "Generate DWARF Version 5 debuginfo" depends on !CC_IS_CLANG || (CC_IS_CLANG && (AS_IS_LLVM || (AS_IS_GNU && AS_VERSION >= 23502))) - depends on !DEBUG_INFO_BTF + depends on !DEBUG_INFO_BTF || PAHOLE_VERSION >= 121 help Generate DWARF v5 debug info. Requires binutils 2.35.2, gcc 5.0+ (gcc 5.0+ accepts the -gdwarf-5 flag but only had partial support for some @@ -323,7 +323,15 @@ config DEBUG_INFO_BTF DWARF type info into equivalent deduplicated BTF type info. config PAHOLE_HAS_SPLIT_BTF - def_bool $(success, test `$(PAHOLE) --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/'` -ge "119") + def_bool PAHOLE_VERSION >= 119 + +config PAHOLE_HAS_BTF_TAG + def_bool PAHOLE_VERSION >= 123 + depends on CC_IS_CLANG + help + Decide whether pahole emits btf_tag attributes (btf_type_tag and + btf_decl_tag) or not. Currently only clang compiler implements + these attributes, so make the config depend on CC_IS_CLANG. config DEBUG_INFO_BTF_MODULES def_bool y @@ -331,6 +339,16 @@ config DEBUG_INFO_BTF_MODULES help Generate compact split BTF type information for kernel modules. +config MODULE_ALLOW_BTF_MISMATCH + bool "Allow loading modules with non-matching BTF type info" + depends on DEBUG_INFO_BTF_MODULES + help + For modules whose split BTF does not match vmlinux, load without + BTF rather than refusing to load. The default behavior with + module BTF enabled is to reject modules with such mismatches; + this option will still load module BTF where possible but ignore + it when a mismatch is found. + config GDB_SCRIPTS bool "Provide GDB scripts for kernel debugging" help @@ -2100,6 +2118,18 @@ config KPROBES_SANITY_TEST Say N if you are unsure. +config FPROBE_SANITY_TEST + bool "Self test for fprobe" + depends on DEBUG_KERNEL + depends on FPROBE + depends on KUNIT=y + help + This option will enable testing the fprobe when the system boot. + A series of tests are made to verify that the fprobe is functioning + properly. + + Say N if you are unsure. + config BACKTRACE_SELF_TEST tristate "Self test for the backtrace code" depends on DEBUG_KERNEL diff --git a/lib/Makefile b/lib/Makefile index 353bc09ce38d..2fd40c5bf378 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -100,6 +100,8 @@ obj-$(CONFIG_TEST_HMM) += test_hmm.o obj-$(CONFIG_TEST_FREE_PAGES) += test_free_pages.o obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o obj-$(CONFIG_TEST_REF_TRACKER) += test_ref_tracker.o +CFLAGS_test_fprobe.o += $(CC_FLAGS_FTRACE) +obj-$(CONFIG_FPROBE_SANITY_TEST) += test_fprobe.o # # CFLAGS for compiling floating point code inside the kernel. x86/Makefile turns # off the generation of FPU/SSE* instructions for kernel proper but FPU_FLAGS diff --git a/lib/ref_tracker.c b/lib/ref_tracker.c index a6789c0c626b..dc7b14aa3431 100644 --- a/lib/ref_tracker.c +++ b/lib/ref_tracker.c @@ -20,6 +20,7 @@ void ref_tracker_dir_exit(struct ref_tracker_dir *dir) unsigned long flags; bool leak = false; + dir->dead = true; spin_lock_irqsave(&dir->lock, flags); list_for_each_entry_safe(tracker, n, &dir->quarantine, head) { list_del(&tracker->head); @@ -37,6 +38,7 @@ void ref_tracker_dir_exit(struct ref_tracker_dir *dir) spin_unlock_irqrestore(&dir->lock, flags); WARN_ON_ONCE(leak); WARN_ON_ONCE(refcount_read(&dir->untracked) != 1); + WARN_ON_ONCE(refcount_read(&dir->no_tracker) != 1); } EXPORT_SYMBOL(ref_tracker_dir_exit); @@ -72,6 +74,12 @@ int ref_tracker_alloc(struct ref_tracker_dir *dir, gfp_t gfp_mask = gfp; unsigned long flags; + WARN_ON_ONCE(dir->dead); + + if (!trackerp) { + refcount_inc(&dir->no_tracker); + return 0; + } if (gfp & __GFP_DIRECT_RECLAIM) gfp_mask |= __GFP_NOFAIL; *trackerp = tracker = kzalloc(sizeof(*tracker), gfp_mask); @@ -81,7 +89,6 @@ int ref_tracker_alloc(struct ref_tracker_dir *dir, return -ENOMEM; } nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 1); - nr_entries = filter_irq_stacks(entries, nr_entries); tracker->alloc_stack_handle = stack_depot_save(entries, nr_entries, gfp); spin_lock_irqsave(&dir->lock, flags); @@ -95,17 +102,23 @@ int ref_tracker_free(struct ref_tracker_dir *dir, struct ref_tracker **trackerp) { unsigned long entries[REF_TRACKER_STACK_ENTRIES]; - struct ref_tracker *tracker = *trackerp; depot_stack_handle_t stack_handle; + struct ref_tracker *tracker; unsigned int nr_entries; unsigned long flags; + WARN_ON_ONCE(dir->dead); + + if (!trackerp) { + refcount_dec(&dir->no_tracker); + return 0; + } + tracker = *trackerp; if (!tracker) { refcount_dec(&dir->untracked); return -EEXIST; } nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 1); - nr_entries = filter_irq_stacks(entries, nr_entries); stack_handle = stack_depot_save(entries, nr_entries, GFP_ATOMIC); spin_lock_irqsave(&dir->lock, flags); diff --git a/lib/sort.c b/lib/sort.c index aa18153864d2..b399bf10d675 100644 --- a/lib/sort.c +++ b/lib/sort.c @@ -122,16 +122,27 @@ static void swap_bytes(void *a, void *b, size_t n) * a pointer, but small integers make for the smallest compare * instructions. */ -#define SWAP_WORDS_64 (swap_func_t)0 -#define SWAP_WORDS_32 (swap_func_t)1 -#define SWAP_BYTES (swap_func_t)2 +#define SWAP_WORDS_64 (swap_r_func_t)0 +#define SWAP_WORDS_32 (swap_r_func_t)1 +#define SWAP_BYTES (swap_r_func_t)2 +#define SWAP_WRAPPER (swap_r_func_t)3 + +struct wrapper { + cmp_func_t cmp; + swap_func_t swap; +}; /* * The function pointer is last to make tail calls most efficient if the * compiler decides not to inline this function. */ -static void do_swap(void *a, void *b, size_t size, swap_func_t swap_func) +static void do_swap(void *a, void *b, size_t size, swap_r_func_t swap_func, const void *priv) { + if (swap_func == SWAP_WRAPPER) { + ((const struct wrapper *)priv)->swap(a, b, (int)size); + return; + } + if (swap_func == SWAP_WORDS_64) swap_words_64(a, b, size); else if (swap_func == SWAP_WORDS_32) @@ -139,7 +150,7 @@ static void do_swap(void *a, void *b, size_t size, swap_func_t swap_func) else if (swap_func == SWAP_BYTES) swap_bytes(a, b, size); else - swap_func(a, b, (int)size); + swap_func(a, b, (int)size, priv); } #define _CMP_WRAPPER ((cmp_r_func_t)0L) @@ -147,7 +158,7 @@ static void do_swap(void *a, void *b, size_t size, swap_func_t swap_func) static int do_cmp(const void *a, const void *b, cmp_r_func_t cmp, const void *priv) { if (cmp == _CMP_WRAPPER) - return ((cmp_func_t)(priv))(a, b); + return ((const struct wrapper *)priv)->cmp(a, b); return cmp(a, b, priv); } @@ -198,7 +209,7 @@ static size_t parent(size_t i, unsigned int lsbit, size_t size) */ void sort_r(void *base, size_t num, size_t size, cmp_r_func_t cmp_func, - swap_func_t swap_func, + swap_r_func_t swap_func, const void *priv) { /* pre-scale counters for performance */ @@ -208,6 +219,10 @@ void sort_r(void *base, size_t num, size_t size, if (!a) /* num < 2 || size == 0 */ return; + /* called from 'sort' without swap function, let's pick the default */ + if (swap_func == SWAP_WRAPPER && !((struct wrapper *)priv)->swap) + swap_func = NULL; + if (!swap_func) { if (is_aligned(base, size, 8)) swap_func = SWAP_WORDS_64; @@ -230,7 +245,7 @@ void sort_r(void *base, size_t num, size_t size, if (a) /* Building heap: sift down --a */ a -= size; else if (n -= size) /* Sorting: Extract root to --n */ - do_swap(base, base + n, size, swap_func); + do_swap(base, base + n, size, swap_func, priv); else /* Sort complete */ break; @@ -257,7 +272,7 @@ void sort_r(void *base, size_t num, size_t size, c = b; /* Where "a" belongs */ while (b != a) { /* Shift it into place */ b = parent(b, lsbit, size); - do_swap(base + b, base + c, size, swap_func); + do_swap(base + b, base + c, size, swap_func, priv); } } } @@ -267,6 +282,11 @@ void sort(void *base, size_t num, size_t size, cmp_func_t cmp_func, swap_func_t swap_func) { - return sort_r(base, num, size, _CMP_WRAPPER, swap_func, cmp_func); + struct wrapper w = { + .cmp = cmp_func, + .swap = swap_func, + }; + + return sort_r(base, num, size, _CMP_WRAPPER, SWAP_WRAPPER, &w); } EXPORT_SYMBOL(sort); diff --git a/lib/test_fprobe.c b/lib/test_fprobe.c new file mode 100644 index 000000000000..ed70637a2ffa --- /dev/null +++ b/lib/test_fprobe.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * test_fprobe.c - simple sanity test for fprobe + */ + +#include +#include +#include +#include + +#define div_factor 3 + +static struct kunit *current_test; + +static u32 rand1, entry_val, exit_val; + +/* Use indirect calls to avoid inlining the target functions */ +static u32 (*target)(u32 value); +static u32 (*target2)(u32 value); +static unsigned long target_ip; +static unsigned long target2_ip; + +static noinline u32 fprobe_selftest_target(u32 value) +{ + return (value / div_factor); +} + +static noinline u32 fprobe_selftest_target2(u32 value) +{ + return (value / div_factor) + 1; +} + +static notrace void fp_entry_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs) +{ + KUNIT_EXPECT_FALSE(current_test, preemptible()); + /* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */ + if (ip != target_ip) + KUNIT_EXPECT_EQ(current_test, ip, target2_ip); + entry_val = (rand1 / div_factor); +} + +static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs) +{ + unsigned long ret = regs_return_value(regs); + + KUNIT_EXPECT_FALSE(current_test, preemptible()); + if (ip != target_ip) { + KUNIT_EXPECT_EQ(current_test, ip, target2_ip); + KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor) + 1); + } else + KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor)); + KUNIT_EXPECT_EQ(current_test, entry_val, (rand1 / div_factor)); + exit_val = entry_val + div_factor; +} + +/* Test entry only (no rethook) */ +static void test_fprobe_entry(struct kunit *test) +{ + struct fprobe fp_entry = { + .entry_handler = fp_entry_handler, + }; + + current_test = test; + + /* Before register, unregister should be failed. */ + KUNIT_EXPECT_NE(test, 0, unregister_fprobe(&fp_entry)); + KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp_entry, "fprobe_selftest_target*", NULL)); + + entry_val = 0; + exit_val = 0; + target(rand1); + KUNIT_EXPECT_NE(test, 0, entry_val); + KUNIT_EXPECT_EQ(test, 0, exit_val); + + entry_val = 0; + exit_val = 0; + target2(rand1); + KUNIT_EXPECT_NE(test, 0, entry_val); + KUNIT_EXPECT_EQ(test, 0, exit_val); + + KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp_entry)); +} + +static void test_fprobe(struct kunit *test) +{ + struct fprobe fp = { + .entry_handler = fp_entry_handler, + .exit_handler = fp_exit_handler, + }; + + current_test = test; + KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target*", NULL)); + + entry_val = 0; + exit_val = 0; + target(rand1); + KUNIT_EXPECT_NE(test, 0, entry_val); + KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val); + + entry_val = 0; + exit_val = 0; + target2(rand1); + KUNIT_EXPECT_NE(test, 0, entry_val); + KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val); + + KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp)); +} + +static void test_fprobe_syms(struct kunit *test) +{ + static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_target2"}; + struct fprobe fp = { + .entry_handler = fp_entry_handler, + .exit_handler = fp_exit_handler, + }; + + current_test = test; + KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2)); + + entry_val = 0; + exit_val = 0; + target(rand1); + KUNIT_EXPECT_NE(test, 0, entry_val); + KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val); + + entry_val = 0; + exit_val = 0; + target2(rand1); + KUNIT_EXPECT_NE(test, 0, entry_val); + KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val); + + KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp)); +} + +static unsigned long get_ftrace_location(void *func) +{ + unsigned long size, addr = (unsigned long)func; + + if (!kallsyms_lookup_size_offset(addr, &size, NULL) || !size) + return 0; + + return ftrace_location_range(addr, addr + size - 1); +} + +static int fprobe_test_init(struct kunit *test) +{ + do { + rand1 = prandom_u32(); + } while (rand1 <= div_factor); + + target = fprobe_selftest_target; + target2 = fprobe_selftest_target2; + target_ip = get_ftrace_location(target); + target2_ip = get_ftrace_location(target2); + + return 0; +} + +static struct kunit_case fprobe_testcases[] = { + KUNIT_CASE(test_fprobe_entry), + KUNIT_CASE(test_fprobe), + KUNIT_CASE(test_fprobe_syms), + {} +}; + +static struct kunit_suite fprobe_test_suite = { + .name = "fprobe_test", + .init = fprobe_test_init, + .test_cases = fprobe_testcases, +}; + +kunit_test_suites(&fprobe_test_suite); + +MODULE_LICENSE("GPL"); diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c index a068757eabaf..7b3341cef926 100644 --- a/net/6lowpan/core.c +++ b/net/6lowpan/core.c @@ -5,6 +5,7 @@ * (C) 2015 Pengutronix, Alexander Aring */ +#include #include #include diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index d1902828a18a..e5d23e75572a 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -638,12 +638,7 @@ void vlan_dev_free_egress_priority(const struct net_device *dev) static void vlan_dev_uninit(struct net_device *dev) { - struct vlan_dev_priv *vlan = vlan_dev_priv(dev); - vlan_dev_free_egress_priority(dev); - - /* Get rid of the vlan's reference to real_dev */ - dev_put_track(vlan->real_dev, &vlan->dev_tracker); } static netdev_features_t vlan_dev_fix_features(struct net_device *dev, @@ -856,6 +851,9 @@ static void vlan_dev_free(struct net_device *dev) free_percpu(vlan->vlan_pcpu_stats); vlan->vlan_pcpu_stats = NULL; + + /* Get rid of the vlan's reference to real_dev */ + dev_put_track(vlan->real_dev, &vlan->dev_tracker); } void vlan_setup(struct net_device *dev) diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c index 08bf6c839e25..7825c129742a 100644 --- a/net/8021q/vlanproc.c +++ b/net/8021q/vlanproc.c @@ -280,7 +280,7 @@ static int vlandev_seq_show(struct seq_file *seq, void *offset) const struct vlan_priority_tci_mapping *mp = vlan->egress_priority_map[i]; while (mp) { - seq_printf(seq, "%u:%hu ", + seq_printf(seq, "%u:%d ", mp->priority, ((mp->vlan_qos >> 13) & 0x7)); mp = mp->next; } diff --git a/net/Kconfig b/net/Kconfig index 8a1f9d0287de..6b78f695caa6 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -434,6 +434,19 @@ config NET_DEVLINK config PAGE_POOL bool +config PAGE_POOL_STATS + default n + bool "Page pool stats" + depends on PAGE_POOL + help + Enable page pool statistics to track page allocation and recycling + in page pools. This option incurs additional CPU cost in allocation + and recycle paths and additional memory cost to store the statistics. + These statistics are only available if this option is enabled and if + the driver using the page pool supports exporting this data. + + If unsure, say N. + config FAILOVER tristate "Generic failover module" help diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 6bd097180772..992b6e5d85d7 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -89,18 +89,20 @@ static void ax25_kill_by_device(struct net_device *dev) sk = s->sk; if (!sk) { spin_unlock_bh(&ax25_list_lock); - s->ax25_dev = NULL; ax25_disconnect(s, ENETUNREACH); + s->ax25_dev = NULL; spin_lock_bh(&ax25_list_lock); goto again; } sock_hold(sk); spin_unlock_bh(&ax25_list_lock); lock_sock(sk); - s->ax25_dev = NULL; - dev_put_track(ax25_dev->dev, &ax25_dev->dev_tracker); - ax25_dev_put(ax25_dev); ax25_disconnect(s, ENETUNREACH); + s->ax25_dev = NULL; + if (sk->sk_socket) { + dev_put_track(ax25_dev->dev, &ax25_dev->dev_tracker); + ax25_dev_put(ax25_dev); + } release_sock(sk); spin_lock_bh(&ax25_list_lock); sock_put(sk); @@ -979,14 +981,20 @@ static int ax25_release(struct socket *sock) { struct sock *sk = sock->sk; ax25_cb *ax25; + ax25_dev *ax25_dev; if (sk == NULL) return 0; sock_hold(sk); - sock_orphan(sk); lock_sock(sk); + sock_orphan(sk); ax25 = sk_to_ax25(sk); + ax25_dev = ax25->ax25_dev; + if (ax25_dev) { + dev_put_track(ax25_dev->dev, &ax25_dev->dev_tracker); + ax25_dev_put(ax25_dev); + } if (sk->sk_type == SOCK_SEQPACKET) { switch (ax25->state) { diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c index 9751207f7757..b7c4d656a94b 100644 --- a/net/ax25/ax25_route.c +++ b/net/ax25/ax25_route.c @@ -116,7 +116,6 @@ static int __must_check ax25_rt_add(struct ax25_routes_struct *route) return -ENOMEM; } - refcount_set(&ax25_rt->refcount, 1); ax25_rt->callsign = route->dest_addr; ax25_rt->dev = ax25_dev->dev; ax25_rt->digipeat = NULL; @@ -167,12 +166,12 @@ static int ax25_rt_del(struct ax25_routes_struct *route) ax25cmp(&route->dest_addr, &s->callsign) == 0) { if (ax25_route_list == s) { ax25_route_list = s->next; - ax25_put_route(s); + __ax25_put_route(s); } else { for (t = ax25_route_list; t != NULL; t = t->next) { if (t->next == s) { t->next = s->next; - ax25_put_route(s); + __ax25_put_route(s); break; } } diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c index 15ab812c4fe4..3a476e4f6cd0 100644 --- a/net/ax25/ax25_subr.c +++ b/net/ax25/ax25_subr.c @@ -261,12 +261,20 @@ void ax25_disconnect(ax25_cb *ax25, int reason) { ax25_clear_queues(ax25); - if (!ax25->sk || !sock_flag(ax25->sk, SOCK_DESTROY)) - ax25_stop_heartbeat(ax25); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); + if (reason == ENETUNREACH) { + del_timer_sync(&ax25->timer); + del_timer_sync(&ax25->t1timer); + del_timer_sync(&ax25->t2timer); + del_timer_sync(&ax25->t3timer); + del_timer_sync(&ax25->idletimer); + } else { + if (!ax25->sk || !sock_flag(ax25->sk, SOCK_DESTROY)) + ax25_stop_heartbeat(ax25); + ax25_stop_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_stop_t3timer(ax25); + ax25_stop_idletimer(ax25); + } ax25->state = AX25_STATE_0; diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index f94f538fa382..7f6a7c96ac92 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -13,13 +13,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index 71999e13f729..b6db999abf75 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -10,13 +10,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 1d750f3cb2e4..033639df96d8 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -9,12 +9,12 @@ #include #include +#include #include #include #include #include #include -#include #include #include #include diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 2ed9496fc41f..7f8a14d99cdb 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -443,7 +444,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac, batadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES, skb->len + ETH_HLEN); - netif_rx_any_context(skb); + netif_rx(skb); out: batadv_hardif_put(primary_if); } diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 2f008e329007..fefb51a5f606 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -20,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index b7466136e292..d26124bc27e1 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 35fadb924849..83fb51b6e299 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -9,11 +9,11 @@ #include #include +#include #include #include #include #include -#include #include #include #include @@ -168,9 +168,9 @@ static bool batadv_is_on_batman_iface(const struct net_device *net_dev) /* recurse over the parent device */ parent_dev = __dev_get_by_index((struct net *)parent_net, iflink); - /* if we got a NULL parent_dev there is something broken.. */ if (!parent_dev) { - pr_err("Cannot find parent device\n"); + pr_warn("Cannot find parent device. Skipping batadv-on-batadv check for %s\n", + net_dev->name); return false; } diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 5207cd8d6ad8..e8a449915566 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -132,7 +133,6 @@ static void __exit batadv_exit(void) rtnl_link_unregister(&batadv_link_ops); unregister_netdevice_notifier(&batadv_hard_if_notifier); - flush_workqueue(batadv_event_workqueue); destroy_workqueue(batadv_event_workqueue); batadv_event_workqueue = NULL; diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 494d1ebecac2..f3be82999f1f 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -13,7 +13,7 @@ #define BATADV_DRIVER_DEVICE "batman-adv" #ifndef BATADV_SOURCE_VERSION -#define BATADV_SOURCE_VERSION "2022.0" +#define BATADV_SOURCE_VERSION "2022.1" #endif /* B.A.T.M.A.N. parameters */ diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index f4004cf0ff6f..b238455913df 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -134,7 +135,7 @@ static u8 batadv_mcast_mla_rtr_flags_softif_get_ipv6(struct net_device *dev) { struct inet6_dev *in6_dev = __in6_dev_get(dev); - if (in6_dev && in6_dev->cnf.mc_forwarding) + if (in6_dev && atomic_read(&in6_dev->cnf.mc_forwarding)) return BATADV_NO_FLAGS; else return BATADV_MCAST_WANT_NO_RTR6; diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index 974d726fabb9..5f4aeeb60dc4 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index aadc653ca1d8..34903df4fe93 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -8,11 +8,11 @@ #include "main.h" #include +#include #include #include #include #include -#include #include #include #include diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 477d85a3b558..0379b126865d 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -10,13 +10,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 2dbbe6c19609..0f5c0679b55a 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 93730d30af54..7f3dd3c393e0 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -12,13 +12,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 4b7ad6684bc4..8478034d3abf 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index 0cb58eb04093..7ec2e2343884 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -7,10 +7,10 @@ #include "main.h" #include +#include #include #include #include -#include #include #include #include diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 133d7ea063fb..215af9b3b589 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -240,7 +240,7 @@ static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev) if (!skb_cp) return NET_RX_DROP; - return netif_rx_ni(skb_cp); + return netif_rx(skb_cp); } static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev, @@ -641,7 +641,6 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, return NULL; peer->chan = chan; - memset(&peer->peer_addr, 0, sizeof(struct in6_addr)); baswap((void *)peer->lladdr, &chan->dst); diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index ee319779781e..a0cb2e3da8d4 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -568,7 +568,7 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo) EXPORT_SYMBOL(bt_sock_wait_state); /* This function expects the sk lock to be held when called */ -int bt_sock_wait_ready(struct sock *sk, unsigned long flags) +int bt_sock_wait_ready(struct sock *sk, unsigned int msg_flags) { DECLARE_WAITQUEUE(wait, current); unsigned long timeo; @@ -576,7 +576,7 @@ int bt_sock_wait_ready(struct sock *sk, unsigned long flags) BT_DBG("sk %p", sk); - timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); + timeo = sock_sndtimeo(sk, !!(msg_flags & MSG_DONTWAIT)); add_wait_queue(sk_sleep(sk), &wait); set_current_state(TASK_INTERRUPTIBLE); diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 40baa6b7321a..5a6a49885ab6 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -400,7 +400,7 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb) dev->stats.rx_packets++; nskb->ip_summed = CHECKSUM_NONE; nskb->protocol = eth_type_trans(nskb, dev); - netif_rx_ni(nskb); + netif_rx(nskb); return 0; badframe: diff --git a/net/bluetooth/eir.h b/net/bluetooth/eir.h index 05e2e917fc25..43f1945bffc5 100644 --- a/net/bluetooth/eir.h +++ b/net/bluetooth/eir.h @@ -15,6 +15,11 @@ u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr); u8 eir_append_local_name(struct hci_dev *hdev, u8 *eir, u8 ad_len); u8 eir_append_appearance(struct hci_dev *hdev, u8 *ptr, u8 ad_len); +static inline u16 eir_precalc_len(u8 data_len) +{ + return sizeof(u8) * 2 + data_len; +} + static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, u8 data_len) { @@ -36,6 +41,21 @@ static inline u16 eir_append_le16(u8 *eir, u16 eir_len, u8 type, u16 data) return eir_len; } +static inline u16 eir_skb_put_data(struct sk_buff *skb, u8 type, u8 *data, u8 data_len) +{ + u8 *eir; + u16 eir_len; + + eir_len = eir_precalc_len(data_len); + eir = skb_put(skb, eir_len); + WARN_ON(sizeof(type) + data_len > U8_MAX); + eir[0] = sizeof(type) + data_len; + eir[1] = type; + memcpy(&eir[2], data, data_len); + + return eir_len; +} + static inline void *eir_get_data(u8 *eir, size_t eir_len, u8 type, size_t *data_len) { diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 04ebe901e86f..84312c836549 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -669,7 +669,9 @@ static void le_conn_timeout(struct work_struct *work) if (conn->role == HCI_ROLE_SLAVE) { /* Disable LE Advertising */ le_disable_advertising(hdev); + hci_dev_lock(hdev); hci_le_conn_failed(conn, HCI_ERROR_ADVERTISING_TIMEOUT); + hci_dev_unlock(hdev); return; } @@ -689,6 +691,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, bacpy(&conn->dst, dst); bacpy(&conn->src, &hdev->bdaddr); + conn->handle = HCI_CONN_HANDLE_UNSET; conn->hdev = hdev; conn->type = type; conn->role = role; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2882bc7d79d7..b4782a6c1025 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2503,6 +2503,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv) INIT_LIST_HEAD(&hdev->conn_hash.list); INIT_LIST_HEAD(&hdev->adv_instances); INIT_LIST_HEAD(&hdev->blocked_keys); + INIT_LIST_HEAD(&hdev->monitored_devices); INIT_LIST_HEAD(&hdev->local_codecs); INIT_WORK(&hdev->rx_work, hci_rx_work); @@ -3667,8 +3668,8 @@ static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb) sco_recv_scodata(conn, skb); return; } else { - bt_dev_err(hdev, "SCO packet for unknown connection handle %d", - handle); + bt_dev_err_ratelimited(hdev, "SCO packet for unknown connection handle %d", + handle); } kfree_skb(skb); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index fc30f4c03d29..abaabfae19cc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3068,6 +3068,11 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data, struct hci_ev_conn_complete *ev = data; struct hci_conn *conn; + if (__le16_to_cpu(ev->handle) > HCI_CONN_HANDLE_MAX) { + bt_dev_err(hdev, "Ignoring HCI_Connection_Complete for invalid handle"); + return; + } + bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); hci_dev_lock(hdev); @@ -3106,6 +3111,17 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data, } } + /* The HCI_Connection_Complete event is only sent once per connection. + * Processing it more than once per connection can corrupt kernel memory. + * + * As the connection handle is set here for the first time, it indicates + * whether the connection is already set up. + */ + if (conn->handle != HCI_CONN_HANDLE_UNSET) { + bt_dev_err(hdev, "Ignoring HCI_Connection_Complete for existing connection"); + goto unlock; + } + if (!ev->status) { conn->handle = __le16_to_cpu(ev->handle); @@ -4534,7 +4550,7 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, void *edata, if (!info) { bt_dev_err(hdev, "Malformed HCI Event: 0x%2.2x", HCI_EV_INQUIRY_RESULT_WITH_RSSI); - return; + goto unlock; } bacpy(&data.bdaddr, &info->bdaddr); @@ -4565,7 +4581,7 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, void *edata, if (!info) { bt_dev_err(hdev, "Malformed HCI Event: 0x%2.2x", HCI_EV_INQUIRY_RESULT_WITH_RSSI); - return; + goto unlock; } bacpy(&data.bdaddr, &info->bdaddr); @@ -4587,7 +4603,7 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, void *edata, bt_dev_err(hdev, "Malformed HCI Event: 0x%2.2x", HCI_EV_INQUIRY_RESULT_WITH_RSSI); } - +unlock: hci_dev_unlock(hdev); } @@ -4661,6 +4677,24 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data, struct hci_ev_sync_conn_complete *ev = data; struct hci_conn *conn; + switch (ev->link_type) { + case SCO_LINK: + case ESCO_LINK: + break; + default: + /* As per Core 5.3 Vol 4 Part E 7.7.35 (p.2219), Link_Type + * for HCI_Synchronous_Connection_Complete is limited to + * either SCO or eSCO + */ + bt_dev_err(hdev, "Ignoring connect complete event for invalid link type"); + return; + } + + if (__le16_to_cpu(ev->handle) > HCI_CONN_HANDLE_MAX) { + bt_dev_err(hdev, "Ignoring HCI_Sync_Conn_Complete for invalid handle"); + return; + } + bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); hci_dev_lock(hdev); @@ -4684,23 +4718,19 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data, goto unlock; } + /* The HCI_Synchronous_Connection_Complete event is only sent once per connection. + * Processing it more than once per connection can corrupt kernel memory. + * + * As the connection handle is set here for the first time, it indicates + * whether the connection is already set up. + */ + if (conn->handle != HCI_CONN_HANDLE_UNSET) { + bt_dev_err(hdev, "Ignoring HCI_Sync_Conn_Complete event for existing connection"); + goto unlock; + } + switch (ev->status) { case 0x00: - /* The synchronous connection complete event should only be - * sent once per new connection. Receiving a successful - * complete event when the connection status is already - * BT_CONNECTED means that the device is misbehaving and sent - * multiple complete event packets for the same new connection. - * - * Registering the device more than once can corrupt kernel - * memory, hence upon detecting this invalid event, we report - * an error and ignore the packet. - */ - if (conn->state == BT_CONNECTED) { - bt_dev_err(hdev, "Ignoring connect complete event for existing connection"); - goto unlock; - } - conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; conn->type = ev->link_type; @@ -5423,8 +5453,9 @@ static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev, void *data, hci_dev_lock(hdev); hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle); - if (hcon) { + if (hcon && hcon->type == AMP_LINK) { hcon->state = BT_CLOSED; + hci_disconn_cfm(hcon, ev->reason); hci_conn_del(hcon); } @@ -5496,6 +5527,11 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status, struct smp_irk *irk; u8 addr_type; + if (handle > HCI_CONN_HANDLE_MAX) { + bt_dev_err(hdev, "Ignoring HCI_LE_Connection_Complete for invalid handle"); + return; + } + hci_dev_lock(hdev); /* All controllers implicitly stop advertising in the event of a @@ -5537,6 +5573,17 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status, cancel_delayed_work(&conn->le_conn_timeout); } + /* The HCI_LE_Connection_Complete event is only sent once per connection. + * Processing it more than once per connection can corrupt kernel memory. + * + * As the connection handle is set here for the first time, it indicates + * whether the connection is already set up. + */ + if (conn->handle != HCI_CONN_HANDLE_UNSET) { + bt_dev_err(hdev, "Ignoring HCI_Connection_Complete for existing connection"); + goto unlock; + } + le_conn_update_addr(conn, bdaddr, bdaddr_type, local_rpa); /* Lookup the identity address from the stored connection @@ -5670,8 +5717,6 @@ static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); - adv = hci_find_adv_instance(hdev, ev->handle); - /* The Bluetooth Core 5.3 specification clearly states that this event * shall not be sent when the Host disables the advertising set. So in * case of HCI_ERROR_CANCELLED_BY_HOST, just ignore the event. @@ -5684,9 +5729,13 @@ static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, void *data, return; } + hci_dev_lock(hdev); + + adv = hci_find_adv_instance(hdev, ev->handle); + if (ev->status) { if (!adv) - return; + goto unlock; /* Remove advertising as it has been terminated */ hci_remove_adv_instance(hdev, ev->handle); @@ -5694,12 +5743,12 @@ static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, void *data, list_for_each_entry_safe(adv, n, &hdev->adv_instances, list) { if (adv->enabled) - return; + goto unlock; } /* We are no longer advertising, clear HCI_LE_ADV */ hci_dev_clear_flag(hdev, HCI_LE_ADV); - return; + goto unlock; } if (adv) @@ -5714,16 +5763,19 @@ static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, void *data, if (hdev->adv_addr_type != ADDR_LE_DEV_RANDOM || bacmp(&conn->resp_addr, BDADDR_ANY)) - return; + goto unlock; if (!ev->handle) { bacpy(&conn->resp_addr, &hdev->random_addr); - return; + goto unlock; } if (adv) bacpy(&conn->resp_addr, &adv->random_addr); } + +unlock: + hci_dev_unlock(hdev); } static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, void *data, @@ -6798,7 +6850,7 @@ static const struct hci_ev { HCI_EV(HCI_EV_NUM_COMP_BLOCKS, hci_num_comp_blocks_evt, sizeof(struct hci_ev_num_comp_blocks)), /* [0xff = HCI_EV_VENDOR] */ - HCI_EV(HCI_EV_VENDOR, msft_vendor_evt, 0), + HCI_EV_VL(HCI_EV_VENDOR, msft_vendor_evt, 0, HCI_MAX_EVENT_SIZE), }; static void hci_event_func(struct hci_dev *hdev, u8 event, struct sk_buff *skb, @@ -6823,8 +6875,9 @@ static void hci_event_func(struct hci_dev *hdev, u8 event, struct sk_buff *skb, * decide if that is acceptable. */ if (skb->len > ev->max_len) - bt_dev_warn(hdev, "unexpected event 0x%2.2x length: %u > %u", - event, skb->len, ev->max_len); + bt_dev_warn_ratelimited(hdev, + "unexpected event 0x%2.2x length: %u > %u", + event, skb->len, ev->max_len); data = hci_ev_skb_pull(hdev, skb, event, ev->min_len); if (!data) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index ab9aa700b6b3..8f4c5698913d 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -379,6 +379,9 @@ int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, { struct hci_cmd_sync_work_entry *entry; + if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) + return -ENODEV; + entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -2806,6 +2809,9 @@ static int hci_set_event_filter_sync(struct hci_dev *hdev, u8 flt_type, if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) return 0; + if (test_bit(HCI_QUIRK_BROKEN_FILTER_CLEAR_ALL, &hdev->quirks)) + return 0; + memset(&cp, 0, sizeof(cp)); cp.flt_type = flt_type; @@ -2826,6 +2832,13 @@ static int hci_clear_event_filter_sync(struct hci_dev *hdev) if (!hci_dev_test_flag(hdev, HCI_EVENT_FILTER_CONFIGURED)) return 0; + /* In theory the state machine should not reach here unless + * a hci_set_event_filter_sync() call succeeds, but we do + * the check both for parity and as a future reminder. + */ + if (test_bit(HCI_QUIRK_BROKEN_FILTER_CLEAR_ALL, &hdev->quirks)) + return 0; + return hci_set_event_filter_sync(hdev, HCI_FLT_CLEAR_ALL, 0x00, BDADDR_ANY, 0x00); } @@ -4422,7 +4435,7 @@ static int hci_disconnect_all_sync(struct hci_dev *hdev, u8 reason) return err; } - return err; + return 0; } /* This function perform power off HCI command sequence as follows: @@ -4825,6 +4838,12 @@ static int hci_update_event_filter_sync(struct hci_dev *hdev) if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) return 0; + /* Some fake CSR controllers lock up after setting this type of + * filter, so avoid sending the request altogether. + */ + if (test_bit(HCI_QUIRK_BROKEN_FILTER_CLEAR_ALL, &hdev->quirks)) + return 0; + /* Always clear event filter when starting */ hci_clear_event_filter_sync(hdev); @@ -5140,8 +5159,8 @@ static void set_ext_conn_params(struct hci_conn *conn, p->max_ce_len = cpu_to_le16(0x0000); } -int hci_le_ext_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, - u8 own_addr_type) +static int hci_le_ext_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn, u8 own_addr_type) { struct hci_cp_le_ext_create_conn *cp; struct hci_cp_le_ext_conn_param *p; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e817ff0607a0..ae78490ecd3d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1436,6 +1436,7 @@ static void l2cap_ecred_connect(struct l2cap_chan *chan) l2cap_ecred_init(chan, 0); + memset(&data, 0, sizeof(data)); data.pdu.req.psm = chan->psm; data.pdu.req.mtu = cpu_to_le16(chan->imtu); data.pdu.req.mps = cpu_to_le16(chan->mps); @@ -1443,7 +1444,6 @@ static void l2cap_ecred_connect(struct l2cap_chan *chan) data.pdu.scid[0] = cpu_to_le16(chan->scid); chan->ident = l2cap_get_ident(conn); - data.pid = chan->ops->get_peer_pid(chan); data.count = 1; data.chan = chan; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 230a7a8196c0..d2d390534e54 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -42,7 +42,7 @@ #include "aosp.h" #define MGMT_VERSION 1 -#define MGMT_REVISION 21 +#define MGMT_REVISION 22 static const u16 mgmt_commands[] = { MGMT_OP_READ_INDEX_LIST, @@ -174,6 +174,8 @@ static const u16 mgmt_events[] = { MGMT_EV_ADV_MONITOR_REMOVED, MGMT_EV_CONTROLLER_SUSPEND, MGMT_EV_CONTROLLER_RESUME, + MGMT_EV_ADV_MONITOR_DEVICE_FOUND, + MGMT_EV_ADV_MONITOR_DEVICE_LOST, }; static const u16 mgmt_untrusted_commands[] = { @@ -2296,7 +2298,9 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, struct mgmt_cp_remove_uuid *cp = data; struct mgmt_pending_cmd *cmd; struct bt_uuid *match, *tmp; - u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static const u8 bt_uuid_any[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; int err, found; bt_dev_dbg(hdev, "sock %p", sk); @@ -7951,7 +7955,7 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, return false; /* Make sure that the data is correctly formatted. */ - for (i = 0, cur_len = 0; i < len; i += (cur_len + 1)) { + for (i = 0; i < len; i += (cur_len + 1)) { cur_len = data[i]; if (!cur_len) @@ -8075,7 +8079,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, u32 flags; u8 status; u16 timeout, duration; - unsigned int prev_instance_cnt = hdev->adv_instance_cnt; + unsigned int prev_instance_cnt; u8 schedule_instance = 0; struct adv_info *next_instance; int err; @@ -8126,6 +8130,8 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, goto unlock; } + prev_instance_cnt = hdev->adv_instance_cnt; + err = hci_add_adv_instance(hdev, cp->instance, flags, cp->adv_data_len, cp->data, cp->scan_rsp_len, @@ -8628,7 +8634,6 @@ static int get_adv_size_info(struct sock *sk, struct hci_dev *hdev, struct mgmt_cp_get_adv_size_info *cp = data; struct mgmt_rp_get_adv_size_info rp; u32 flags, supported_flags; - int err; bt_dev_dbg(hdev, "sock %p", sk); @@ -8655,10 +8660,8 @@ static int get_adv_size_info(struct sock *sk, struct hci_dev *hdev, rp.max_adv_data_len = tlv_data_max_len(hdev, flags, true); rp.max_scan_rsp_len = tlv_data_max_len(hdev, flags, false); - err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO, - MGMT_STATUS_SUCCESS, &rp, sizeof(rp)); - - return err; + return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO, + MGMT_STATUS_SUCCESS, &rp, sizeof(rp)); } static const struct hci_mgmt_handler mgmt_handlers[] = { @@ -9086,12 +9089,14 @@ void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn, u16 eir_len = 0; u32 flags = 0; + /* allocate buff for LE or BR/EDR adv */ if (conn->le_adv_data_len > 0) skb = mgmt_alloc_skb(hdev, MGMT_EV_DEVICE_CONNECTED, - conn->le_adv_data_len); + sizeof(*ev) + conn->le_adv_data_len); else skb = mgmt_alloc_skb(hdev, MGMT_EV_DEVICE_CONNECTED, - 2 + name_len + 5); + sizeof(*ev) + (name ? eir_precalc_len(name_len) : 0) + + eir_precalc_len(sizeof(conn->dev_class))); ev = skb_put(skb, sizeof(*ev)); bacpy(&ev->addr.bdaddr, &conn->dst); @@ -9110,18 +9115,12 @@ void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn, skb_put_data(skb, conn->le_adv_data, conn->le_adv_data_len); eir_len = conn->le_adv_data_len; } else { - if (name_len > 0) { - eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, - name, name_len); - skb_put(skb, eir_len); - } + if (name) + eir_len += eir_skb_put_data(skb, EIR_NAME_COMPLETE, name, name_len); - if (memcmp(conn->dev_class, "\0\0\0", 3) != 0) { - eir_len = eir_append_data(ev->eir, eir_len, - EIR_CLASS_OF_DEV, - conn->dev_class, 3); - skb_put(skb, 5); - } + if (memcmp(conn->dev_class, "\0\0\0", sizeof(conn->dev_class))) + eir_len += eir_skb_put_data(skb, EIR_CLASS_OF_DEV, + conn->dev_class, sizeof(conn->dev_class)); } ev->eir_len = cpu_to_le16(eir_len); @@ -9616,12 +9615,120 @@ static bool is_filter_match(struct hci_dev *hdev, s8 rssi, u8 *eir, return true; } +void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle, + bdaddr_t *bdaddr, u8 addr_type) +{ + struct mgmt_ev_adv_monitor_device_lost ev; + + ev.monitor_handle = cpu_to_le16(handle); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = addr_type; + + mgmt_event(MGMT_EV_ADV_MONITOR_DEVICE_LOST, hdev, &ev, sizeof(ev), + NULL); +} + +static void mgmt_send_adv_monitor_device_found(struct hci_dev *hdev, + struct sk_buff *skb, + struct sock *skip_sk, + u16 handle) +{ + struct sk_buff *advmon_skb; + size_t advmon_skb_len; + __le16 *monitor_handle; + + if (!skb) + return; + + advmon_skb_len = (sizeof(struct mgmt_ev_adv_monitor_device_found) - + sizeof(struct mgmt_ev_device_found)) + skb->len; + advmon_skb = mgmt_alloc_skb(hdev, MGMT_EV_ADV_MONITOR_DEVICE_FOUND, + advmon_skb_len); + if (!advmon_skb) + return; + + /* ADV_MONITOR_DEVICE_FOUND is similar to DEVICE_FOUND event except + * that it also has 'monitor_handle'. Make a copy of DEVICE_FOUND and + * store monitor_handle of the matched monitor. + */ + monitor_handle = skb_put(advmon_skb, sizeof(*monitor_handle)); + *monitor_handle = cpu_to_le16(handle); + skb_put_data(advmon_skb, skb->data, skb->len); + + mgmt_event_skb(advmon_skb, skip_sk); +} + +static void mgmt_adv_monitor_device_found(struct hci_dev *hdev, + bdaddr_t *bdaddr, bool report_device, + struct sk_buff *skb, + struct sock *skip_sk) +{ + struct monitored_device *dev, *tmp; + bool matched = false; + bool notified = false; + + /* We have received the Advertisement Report because: + * 1. the kernel has initiated active discovery + * 2. if not, we have pend_le_reports > 0 in which case we are doing + * passive scanning + * 3. if none of the above is true, we have one or more active + * Advertisement Monitor + * + * For case 1 and 2, report all advertisements via MGMT_EV_DEVICE_FOUND + * and report ONLY one advertisement per device for the matched Monitor + * via MGMT_EV_ADV_MONITOR_DEVICE_FOUND event. + * + * For case 3, since we are not active scanning and all advertisements + * received are due to a matched Advertisement Monitor, report all + * advertisements ONLY via MGMT_EV_ADV_MONITOR_DEVICE_FOUND event. + */ + if (report_device && !hdev->advmon_pend_notify) { + mgmt_event_skb(skb, skip_sk); + return; + } + + hdev->advmon_pend_notify = false; + + list_for_each_entry_safe(dev, tmp, &hdev->monitored_devices, list) { + if (!bacmp(&dev->bdaddr, bdaddr)) { + matched = true; + + if (!dev->notified) { + mgmt_send_adv_monitor_device_found(hdev, skb, + skip_sk, + dev->handle); + notified = true; + dev->notified = true; + } + } + + if (!dev->notified) + hdev->advmon_pend_notify = true; + } + + if (!report_device && + ((matched && !notified) || !msft_monitor_supported(hdev))) { + /* Handle 0 indicates that we are not active scanning and this + * is a subsequent advertisement report for an already matched + * Advertisement Monitor or the controller offloading support + * is not available. + */ + mgmt_send_adv_monitor_device_found(hdev, skb, skip_sk, 0); + } + + if (report_device) + mgmt_event_skb(skb, skip_sk); + else + kfree_skb(skb); +} + void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, u32 flags, u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len) { struct sk_buff *skb; struct mgmt_ev_device_found *ev; + bool report_device = hci_discovery_active(hdev); /* Don't send events for a non-kernel initiated discovery. With * LE one exception is if we have pend_le_reports > 0 in which @@ -9630,11 +9737,10 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, if (!hci_discovery_active(hdev)) { if (link_type == ACL_LINK) return; - if (link_type == LE_LINK && - list_empty(&hdev->pend_le_reports) && - !hci_is_adv_monitoring(hdev)) { + if (link_type == LE_LINK && !list_empty(&hdev->pend_le_reports)) + report_device = true; + else if (!hci_is_adv_monitoring(hdev)) return; - } } if (hdev->discovery.result_filtering) { @@ -9699,7 +9805,7 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len); - mgmt_event_skb(skb, NULL); + mgmt_adv_monitor_device_found(hdev, bdaddr, report_device, skb, NULL); } void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, @@ -9707,28 +9813,21 @@ void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, { struct sk_buff *skb; struct mgmt_ev_device_found *ev; - u16 eir_len; - u32 flags; + u16 eir_len = 0; + u32 flags = 0; - if (name_len) - skb = mgmt_alloc_skb(hdev, MGMT_EV_DEVICE_FOUND, 2 + name_len); - else - skb = mgmt_alloc_skb(hdev, MGMT_EV_DEVICE_FOUND, 0); + skb = mgmt_alloc_skb(hdev, MGMT_EV_DEVICE_FOUND, + sizeof(*ev) + (name ? eir_precalc_len(name_len) : 0)); ev = skb_put(skb, sizeof(*ev)); bacpy(&ev->addr.bdaddr, bdaddr); ev->addr.type = link_to_bdaddr(link_type, addr_type); ev->rssi = rssi; - if (name) { - eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name, - name_len); - flags = 0; - skb_put(skb, eir_len); - } else { - eir_len = 0; + if (name) + eir_len += eir_skb_put_data(skb, EIR_NAME_COMPLETE, name, name_len); + else flags = MGMT_DEV_FOUND_NAME_REQUEST_FAILED; - } ev->eir_len = cpu_to_le16(eir_len); ev->flags = cpu_to_le32(flags); diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index 6a943634b31a..f43994523b1f 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -80,6 +80,14 @@ struct msft_rp_le_set_advertisement_filter_enable { __u8 sub_opcode; } __packed; +#define MSFT_EV_LE_MONITOR_DEVICE 0x02 +struct msft_ev_le_monitor_device { + __u8 addr_type; + bdaddr_t bdaddr; + __u8 monitor_handle; + __u8 monitor_state; +} __packed; + struct msft_monitor_advertisement_handle_data { __u8 msft_handle; __u16 mgmt_handle; @@ -204,6 +212,37 @@ static struct msft_monitor_advertisement_handle_data *msft_find_handle_data return NULL; } +/* This function requires the caller holds hdev->lock */ +static int msft_monitor_device_del(struct hci_dev *hdev, __u16 mgmt_handle, + bdaddr_t *bdaddr, __u8 addr_type, + bool notify) +{ + struct monitored_device *dev, *tmp; + int count = 0; + + list_for_each_entry_safe(dev, tmp, &hdev->monitored_devices, list) { + /* mgmt_handle == 0 indicates remove all devices, whereas, + * bdaddr == NULL indicates remove all devices matching the + * mgmt_handle. + */ + if ((!mgmt_handle || dev->handle == mgmt_handle) && + (!bdaddr || (!bacmp(bdaddr, &dev->bdaddr) && + addr_type == dev->addr_type))) { + if (notify && dev->notified) { + mgmt_adv_monitor_device_lost(hdev, dev->handle, + &dev->bdaddr, + dev->addr_type); + } + + list_del(&dev->list); + kfree(dev); + count++; + } + } + + return count; +} + static void msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u8 status, u16 opcode, struct sk_buff *skb) @@ -291,9 +330,14 @@ static void msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev, /* Do not free the monitor if it is being removed due to * suspend. It will be re-monitored on resume. */ - if (monitor && !msft->suspending) + if (monitor && !msft->suspending) { hci_free_adv_monitor(hdev, monitor); + /* Clear any monitored devices by this Adv Monitor */ + msft_monitor_device_del(hdev, handle_data->mgmt_handle, + NULL, 0, false); + } + list_del(&handle_data->list); kfree(handle_data); } @@ -479,6 +523,16 @@ int msft_resume_sync(struct hci_dev *hdev) if (!msft || !msft_monitor_supported(hdev)) return 0; + hci_dev_lock(hdev); + + /* Clear already tracked devices on resume. Once the monitors are + * reregistered, devices in range will be found again after resume. + */ + hdev->advmon_pend_notify = false; + msft_monitor_device_del(hdev, 0, NULL, 0, true); + + hci_dev_unlock(hdev); + msft->resuming = true; while (1) { @@ -557,6 +611,14 @@ void msft_do_close(struct hci_dev *hdev) list_del(&handle_data->list); kfree(handle_data); } + + hci_dev_lock(hdev); + + /* Clear any devices that are being monitored and notify device lost */ + hdev->advmon_pend_notify = false; + msft_monitor_device_del(hdev, 0, NULL, 0, true); + + hci_dev_unlock(hdev); } void msft_register(struct hci_dev *hdev) @@ -590,10 +652,101 @@ void msft_unregister(struct hci_dev *hdev) kfree(msft); } +/* This function requires the caller holds hdev->lock */ +static void msft_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, + __u8 addr_type, __u16 mgmt_handle) +{ + struct monitored_device *dev; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + bt_dev_err(hdev, "MSFT vendor event %u: no memory", + MSFT_EV_LE_MONITOR_DEVICE); + return; + } + + bacpy(&dev->bdaddr, bdaddr); + dev->addr_type = addr_type; + dev->handle = mgmt_handle; + dev->notified = false; + + INIT_LIST_HEAD(&dev->list); + list_add(&dev->list, &hdev->monitored_devices); + hdev->advmon_pend_notify = true; +} + +/* This function requires the caller holds hdev->lock */ +static void msft_device_lost(struct hci_dev *hdev, bdaddr_t *bdaddr, + __u8 addr_type, __u16 mgmt_handle) +{ + if (!msft_monitor_device_del(hdev, mgmt_handle, bdaddr, addr_type, + true)) { + bt_dev_err(hdev, "MSFT vendor event %u: dev %pMR not in list", + MSFT_EV_LE_MONITOR_DEVICE, bdaddr); + } +} + +static void *msft_skb_pull(struct hci_dev *hdev, struct sk_buff *skb, + u8 ev, size_t len) +{ + void *data; + + data = skb_pull_data(skb, len); + if (!data) + bt_dev_err(hdev, "Malformed MSFT vendor event: 0x%02x", ev); + + return data; +} + +/* This function requires the caller holds hdev->lock */ +static void msft_monitor_device_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct msft_ev_le_monitor_device *ev; + struct msft_monitor_advertisement_handle_data *handle_data; + u8 addr_type; + + ev = msft_skb_pull(hdev, skb, MSFT_EV_LE_MONITOR_DEVICE, sizeof(*ev)); + if (!ev) + return; + + bt_dev_dbg(hdev, + "MSFT vendor event 0x%02x: handle 0x%04x state %d addr %pMR", + MSFT_EV_LE_MONITOR_DEVICE, ev->monitor_handle, + ev->monitor_state, &ev->bdaddr); + + handle_data = msft_find_handle_data(hdev, ev->monitor_handle, false); + if (!handle_data) + return; + + switch (ev->addr_type) { + case ADDR_LE_DEV_PUBLIC: + addr_type = BDADDR_LE_PUBLIC; + break; + + case ADDR_LE_DEV_RANDOM: + addr_type = BDADDR_LE_RANDOM; + break; + + default: + bt_dev_err(hdev, + "MSFT vendor event 0x%02x: unknown addr type 0x%02x", + MSFT_EV_LE_MONITOR_DEVICE, ev->addr_type); + return; + } + + if (ev->monitor_state) + msft_device_found(hdev, &ev->bdaddr, addr_type, + handle_data->mgmt_handle); + else + msft_device_lost(hdev, &ev->bdaddr, addr_type, + handle_data->mgmt_handle); +} + void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb) { struct msft_data *msft = hdev->msft_data; - u8 event; + u8 *evt_prefix; + u8 *evt; if (!msft) return; @@ -602,13 +755,12 @@ void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb) * matches, and otherwise just return. */ if (msft->evt_prefix_len > 0) { - if (skb->len < msft->evt_prefix_len) + evt_prefix = msft_skb_pull(hdev, skb, 0, msft->evt_prefix_len); + if (!evt_prefix) return; - if (memcmp(skb->data, msft->evt_prefix, msft->evt_prefix_len)) + if (memcmp(evt_prefix, msft->evt_prefix, msft->evt_prefix_len)) return; - - skb_pull(skb, msft->evt_prefix_len); } /* Every event starts at least with an event code and the rest of @@ -617,10 +769,23 @@ void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb) if (skb->len < 1) return; - event = *skb->data; - skb_pull(skb, 1); + evt = msft_skb_pull(hdev, skb, 0, sizeof(*evt)); + if (!evt) + return; - bt_dev_dbg(hdev, "MSFT vendor event %u", event); + hci_dev_lock(hdev); + + switch (*evt) { + case MSFT_EV_LE_MONITOR_DEVICE: + msft_monitor_device_evt(hdev, skb); + break; + + default: + bt_dev_dbg(hdev, "MSFT vendor event 0x%02x", *evt); + break; + } + + hci_dev_unlock(hdev); } __u64 msft_get_features(struct hci_dev *hdev) diff --git a/net/bpf/bpf_dummy_struct_ops.c b/net/bpf/bpf_dummy_struct_ops.c index fbc896323bec..d0e54e30658a 100644 --- a/net/bpf/bpf_dummy_struct_ops.c +++ b/net/bpf/bpf_dummy_struct_ops.c @@ -145,7 +145,8 @@ static int bpf_dummy_ops_btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf, const struct btf_type *t, int off, int size, enum bpf_access_type atype, - u32 *next_btf_id) + u32 *next_btf_id, + enum bpf_type_flag *flag) { const struct btf_type *state; s32 type_id; @@ -162,7 +163,8 @@ static int bpf_dummy_ops_btf_struct_access(struct bpf_verifier_log *log, return -EACCES; } - err = btf_struct_access(log, btf, t, off, size, atype, next_btf_id); + err = btf_struct_access(log, btf, t, off, size, atype, next_btf_id, + flag); if (err < 0) return err; diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 46dd95755967..e7b9c2636d10 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -52,10 +54,11 @@ static void bpf_test_timer_leave(struct bpf_test_timer *t) rcu_read_unlock(); } -static bool bpf_test_timer_continue(struct bpf_test_timer *t, u32 repeat, int *err, u32 *duration) +static bool bpf_test_timer_continue(struct bpf_test_timer *t, int iterations, + u32 repeat, int *err, u32 *duration) __must_hold(rcu) { - t->i++; + t->i += iterations; if (t->i >= repeat) { /* We're done. */ t->time_spent += ktime_get_ns() - t->time_start; @@ -87,6 +90,284 @@ static bool bpf_test_timer_continue(struct bpf_test_timer *t, u32 repeat, int *e return false; } +/* We put this struct at the head of each page with a context and frame + * initialised when the page is allocated, so we don't have to do this on each + * repetition of the test run. + */ +struct xdp_page_head { + struct xdp_buff orig_ctx; + struct xdp_buff ctx; + struct xdp_frame frm; + u8 data[]; +}; + +struct xdp_test_data { + struct xdp_buff *orig_ctx; + struct xdp_rxq_info rxq; + struct net_device *dev; + struct page_pool *pp; + struct xdp_frame **frames; + struct sk_buff **skbs; + u32 batch_size; + u32 frame_cnt; +}; + +#define TEST_XDP_FRAME_SIZE (PAGE_SIZE - sizeof(struct xdp_page_head)) +#define TEST_XDP_MAX_BATCH 256 + +static void xdp_test_run_init_page(struct page *page, void *arg) +{ + struct xdp_page_head *head = phys_to_virt(page_to_phys(page)); + struct xdp_buff *new_ctx, *orig_ctx; + u32 headroom = XDP_PACKET_HEADROOM; + struct xdp_test_data *xdp = arg; + size_t frm_len, meta_len; + struct xdp_frame *frm; + void *data; + + orig_ctx = xdp->orig_ctx; + frm_len = orig_ctx->data_end - orig_ctx->data_meta; + meta_len = orig_ctx->data - orig_ctx->data_meta; + headroom -= meta_len; + + new_ctx = &head->ctx; + frm = &head->frm; + data = &head->data; + memcpy(data + headroom, orig_ctx->data_meta, frm_len); + + xdp_init_buff(new_ctx, TEST_XDP_FRAME_SIZE, &xdp->rxq); + xdp_prepare_buff(new_ctx, data, headroom, frm_len, true); + new_ctx->data = new_ctx->data_meta + meta_len; + + xdp_update_frame_from_buff(new_ctx, frm); + frm->mem = new_ctx->rxq->mem; + + memcpy(&head->orig_ctx, new_ctx, sizeof(head->orig_ctx)); +} + +static int xdp_test_run_setup(struct xdp_test_data *xdp, struct xdp_buff *orig_ctx) +{ + struct xdp_mem_info mem = {}; + struct page_pool *pp; + int err = -ENOMEM; + struct page_pool_params pp_params = { + .order = 0, + .flags = 0, + .pool_size = xdp->batch_size, + .nid = NUMA_NO_NODE, + .init_callback = xdp_test_run_init_page, + .init_arg = xdp, + }; + + xdp->frames = kvmalloc_array(xdp->batch_size, sizeof(void *), GFP_KERNEL); + if (!xdp->frames) + return -ENOMEM; + + xdp->skbs = kvmalloc_array(xdp->batch_size, sizeof(void *), GFP_KERNEL); + if (!xdp->skbs) + goto err_skbs; + + pp = page_pool_create(&pp_params); + if (IS_ERR(pp)) { + err = PTR_ERR(pp); + goto err_pp; + } + + /* will copy 'mem.id' into pp->xdp_mem_id */ + err = xdp_reg_mem_model(&mem, MEM_TYPE_PAGE_POOL, pp); + if (err) + goto err_mmodel; + + xdp->pp = pp; + + /* We create a 'fake' RXQ referencing the original dev, but with an + * xdp_mem_info pointing to our page_pool + */ + xdp_rxq_info_reg(&xdp->rxq, orig_ctx->rxq->dev, 0, 0); + xdp->rxq.mem.type = MEM_TYPE_PAGE_POOL; + xdp->rxq.mem.id = pp->xdp_mem_id; + xdp->dev = orig_ctx->rxq->dev; + xdp->orig_ctx = orig_ctx; + + return 0; + +err_mmodel: + page_pool_destroy(pp); +err_pp: + kvfree(xdp->skbs); +err_skbs: + kvfree(xdp->frames); + return err; +} + +static void xdp_test_run_teardown(struct xdp_test_data *xdp) +{ + page_pool_destroy(xdp->pp); + kfree(xdp->frames); + kfree(xdp->skbs); +} + +static bool ctx_was_changed(struct xdp_page_head *head) +{ + return head->orig_ctx.data != head->ctx.data || + head->orig_ctx.data_meta != head->ctx.data_meta || + head->orig_ctx.data_end != head->ctx.data_end; +} + +static void reset_ctx(struct xdp_page_head *head) +{ + if (likely(!ctx_was_changed(head))) + return; + + head->ctx.data = head->orig_ctx.data; + head->ctx.data_meta = head->orig_ctx.data_meta; + head->ctx.data_end = head->orig_ctx.data_end; + xdp_update_frame_from_buff(&head->ctx, &head->frm); +} + +static int xdp_recv_frames(struct xdp_frame **frames, int nframes, + struct sk_buff **skbs, + struct net_device *dev) +{ + gfp_t gfp = __GFP_ZERO | GFP_ATOMIC; + int i, n; + LIST_HEAD(list); + + n = kmem_cache_alloc_bulk(skbuff_head_cache, gfp, nframes, (void **)skbs); + if (unlikely(n == 0)) { + for (i = 0; i < nframes; i++) + xdp_return_frame(frames[i]); + return -ENOMEM; + } + + for (i = 0; i < nframes; i++) { + struct xdp_frame *xdpf = frames[i]; + struct sk_buff *skb = skbs[i]; + + skb = __xdp_build_skb_from_frame(xdpf, skb, dev); + if (!skb) { + xdp_return_frame(xdpf); + continue; + } + + list_add_tail(&skb->list, &list); + } + netif_receive_skb_list(&list); + + return 0; +} + +static int xdp_test_run_batch(struct xdp_test_data *xdp, struct bpf_prog *prog, + u32 repeat) +{ + struct bpf_redirect_info *ri = this_cpu_ptr(&bpf_redirect_info); + int err = 0, act, ret, i, nframes = 0, batch_sz; + struct xdp_frame **frames = xdp->frames; + struct xdp_page_head *head; + struct xdp_frame *frm; + bool redirect = false; + struct xdp_buff *ctx; + struct page *page; + + batch_sz = min_t(u32, repeat, xdp->batch_size); + + local_bh_disable(); + xdp_set_return_frame_no_direct(); + + for (i = 0; i < batch_sz; i++) { + page = page_pool_dev_alloc_pages(xdp->pp); + if (!page) { + err = -ENOMEM; + goto out; + } + + head = phys_to_virt(page_to_phys(page)); + reset_ctx(head); + ctx = &head->ctx; + frm = &head->frm; + xdp->frame_cnt++; + + act = bpf_prog_run_xdp(prog, ctx); + + /* if program changed pkt bounds we need to update the xdp_frame */ + if (unlikely(ctx_was_changed(head))) { + ret = xdp_update_frame_from_buff(ctx, frm); + if (ret) { + xdp_return_buff(ctx); + continue; + } + } + + switch (act) { + case XDP_TX: + /* we can't do a real XDP_TX since we're not in the + * driver, so turn it into a REDIRECT back to the same + * index + */ + ri->tgt_index = xdp->dev->ifindex; + ri->map_id = INT_MAX; + ri->map_type = BPF_MAP_TYPE_UNSPEC; + fallthrough; + case XDP_REDIRECT: + redirect = true; + ret = xdp_do_redirect_frame(xdp->dev, ctx, frm, prog); + if (ret) + xdp_return_buff(ctx); + break; + case XDP_PASS: + frames[nframes++] = frm; + break; + default: + bpf_warn_invalid_xdp_action(NULL, prog, act); + fallthrough; + case XDP_DROP: + xdp_return_buff(ctx); + break; + } + } + +out: + if (redirect) + xdp_do_flush(); + if (nframes) { + ret = xdp_recv_frames(frames, nframes, xdp->skbs, xdp->dev); + if (ret) + err = ret; + } + + xdp_clear_return_frame_no_direct(); + local_bh_enable(); + return err; +} + +static int bpf_test_run_xdp_live(struct bpf_prog *prog, struct xdp_buff *ctx, + u32 repeat, u32 batch_size, u32 *time) + +{ + struct xdp_test_data xdp = { .batch_size = batch_size }; + struct bpf_test_timer t = { .mode = NO_MIGRATE }; + int ret; + + if (!repeat) + repeat = 1; + + ret = xdp_test_run_setup(&xdp, ctx); + if (ret) + return ret; + + bpf_test_timer_enter(&t); + do { + xdp.frame_cnt = 0; + ret = xdp_test_run_batch(&xdp, prog, repeat - t.i); + if (unlikely(ret < 0)) + break; + } while (bpf_test_timer_continue(&t, xdp.frame_cnt, repeat, &ret, time)); + bpf_test_timer_leave(&t); + + xdp_test_run_teardown(&xdp); + return ret; +} + static int bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, u32 *retval, u32 *time, bool xdp) { @@ -118,7 +399,7 @@ static int bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, *retval = bpf_prog_run_xdp(prog, ctx); else *retval = bpf_prog_run(prog, ctx); - } while (bpf_test_timer_continue(&t, repeat, &ret, time)); + } while (bpf_test_timer_continue(&t, 1, repeat, &ret, time)); bpf_reset_run_ctx(old_ctx); bpf_test_timer_leave(&t); @@ -130,7 +411,8 @@ static int bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, static int bpf_test_finish(const union bpf_attr *kattr, union bpf_attr __user *uattr, const void *data, - u32 size, u32 retval, u32 duration) + struct skb_shared_info *sinfo, u32 size, + u32 retval, u32 duration) { void __user *data_out = u64_to_user_ptr(kattr->test.data_out); int err = -EFAULT; @@ -145,8 +427,42 @@ static int bpf_test_finish(const union bpf_attr *kattr, err = -ENOSPC; } - if (data_out && copy_to_user(data_out, data, copy_size)) - goto out; + if (data_out) { + int len = sinfo ? copy_size - sinfo->xdp_frags_size : copy_size; + + if (len < 0) { + err = -ENOSPC; + goto out; + } + + if (copy_to_user(data_out, data, len)) + goto out; + + if (sinfo) { + int i, offset = len; + u32 data_len; + + for (i = 0; i < sinfo->nr_frags; i++) { + skb_frag_t *frag = &sinfo->frags[i]; + + if (offset >= copy_size) { + err = -ENOSPC; + break; + } + + data_len = min_t(u32, copy_size - offset, + skb_frag_size(frag)); + + if (copy_to_user(data_out + offset, + skb_frag_address(frag), + data_len)) + goto out; + + offset += data_len; + } + } + } + if (copy_to_user(&uattr->test.data_size_out, &size, sizeof(size))) goto out; if (copy_to_user(&uattr->test.retval, &retval, sizeof(retval))) @@ -165,12 +481,14 @@ static int bpf_test_finish(const union bpf_attr *kattr, * future. */ __diag_push(); -__diag_ignore(GCC, 8, "-Wmissing-prototypes", - "Global functions as their definitions will be in vmlinux BTF"); +__diag_ignore_all("-Wmissing-prototypes", + "Global functions as their definitions will be in vmlinux BTF"); int noinline bpf_fentry_test1(int a) { return a + 1; } +EXPORT_SYMBOL_GPL(bpf_fentry_test1); +ALLOW_ERROR_INJECTION(bpf_fentry_test1, ERRNO); int noinline bpf_fentry_test2(int a, u64 b) { @@ -232,28 +550,153 @@ struct sock * noinline bpf_kfunc_call_test3(struct sock *sk) return sk; } +struct prog_test_member { + u64 c; +}; + +struct prog_test_ref_kfunc { + int a; + int b; + struct prog_test_member memb; + struct prog_test_ref_kfunc *next; +}; + +static struct prog_test_ref_kfunc prog_test_struct = { + .a = 42, + .b = 108, + .next = &prog_test_struct, +}; + +noinline struct prog_test_ref_kfunc * +bpf_kfunc_call_test_acquire(unsigned long *scalar_ptr) +{ + /* randomly return NULL */ + if (get_jiffies_64() % 2) + return NULL; + return &prog_test_struct; +} + +noinline void bpf_kfunc_call_test_release(struct prog_test_ref_kfunc *p) +{ +} + +noinline void bpf_kfunc_call_memb_release(struct prog_test_member *p) +{ +} + +struct prog_test_pass1 { + int x0; + struct { + int x1; + struct { + int x2; + struct { + int x3; + }; + }; + }; +}; + +struct prog_test_pass2 { + int len; + short arr1[4]; + struct { + char arr2[4]; + unsigned long arr3[8]; + } x; +}; + +struct prog_test_fail1 { + void *p; + int x; +}; + +struct prog_test_fail2 { + int x8; + struct prog_test_pass1 x; +}; + +struct prog_test_fail3 { + int len; + char arr1[2]; + char arr2[]; +}; + +noinline void bpf_kfunc_call_test_pass_ctx(struct __sk_buff *skb) +{ +} + +noinline void bpf_kfunc_call_test_pass1(struct prog_test_pass1 *p) +{ +} + +noinline void bpf_kfunc_call_test_pass2(struct prog_test_pass2 *p) +{ +} + +noinline void bpf_kfunc_call_test_fail1(struct prog_test_fail1 *p) +{ +} + +noinline void bpf_kfunc_call_test_fail2(struct prog_test_fail2 *p) +{ +} + +noinline void bpf_kfunc_call_test_fail3(struct prog_test_fail3 *p) +{ +} + +noinline void bpf_kfunc_call_test_mem_len_pass1(void *mem, int mem__sz) +{ +} + +noinline void bpf_kfunc_call_test_mem_len_fail1(void *mem, int len) +{ +} + +noinline void bpf_kfunc_call_test_mem_len_fail2(u64 *mem, int len) +{ +} + __diag_pop(); ALLOW_ERROR_INJECTION(bpf_modify_return_test, ERRNO); -BTF_SET_START(test_sk_kfunc_ids) +BTF_SET_START(test_sk_check_kfunc_ids) BTF_ID(func, bpf_kfunc_call_test1) BTF_ID(func, bpf_kfunc_call_test2) BTF_ID(func, bpf_kfunc_call_test3) -BTF_SET_END(test_sk_kfunc_ids) +BTF_ID(func, bpf_kfunc_call_test_acquire) +BTF_ID(func, bpf_kfunc_call_test_release) +BTF_ID(func, bpf_kfunc_call_memb_release) +BTF_ID(func, bpf_kfunc_call_test_pass_ctx) +BTF_ID(func, bpf_kfunc_call_test_pass1) +BTF_ID(func, bpf_kfunc_call_test_pass2) +BTF_ID(func, bpf_kfunc_call_test_fail1) +BTF_ID(func, bpf_kfunc_call_test_fail2) +BTF_ID(func, bpf_kfunc_call_test_fail3) +BTF_ID(func, bpf_kfunc_call_test_mem_len_pass1) +BTF_ID(func, bpf_kfunc_call_test_mem_len_fail1) +BTF_ID(func, bpf_kfunc_call_test_mem_len_fail2) +BTF_SET_END(test_sk_check_kfunc_ids) -bool bpf_prog_test_check_kfunc_call(u32 kfunc_id, struct module *owner) -{ - if (btf_id_set_contains(&test_sk_kfunc_ids, kfunc_id)) - return true; - return bpf_check_mod_kfunc_call(&prog_test_kfunc_list, kfunc_id, owner); -} +BTF_SET_START(test_sk_acquire_kfunc_ids) +BTF_ID(func, bpf_kfunc_call_test_acquire) +BTF_SET_END(test_sk_acquire_kfunc_ids) -static void *bpf_test_init(const union bpf_attr *kattr, u32 size, - u32 headroom, u32 tailroom) +BTF_SET_START(test_sk_release_kfunc_ids) +BTF_ID(func, bpf_kfunc_call_test_release) +BTF_ID(func, bpf_kfunc_call_memb_release) +BTF_SET_END(test_sk_release_kfunc_ids) + +BTF_SET_START(test_sk_ret_null_kfunc_ids) +BTF_ID(func, bpf_kfunc_call_test_acquire) +BTF_SET_END(test_sk_ret_null_kfunc_ids) + +static void *bpf_test_init(const union bpf_attr *kattr, u32 user_size, + u32 size, u32 headroom, u32 tailroom) { void __user *data_in = u64_to_user_ptr(kattr->test.data_in); - u32 user_size = kattr->test.data_size_in; void *data; if (size < ETH_HLEN || size > PAGE_SIZE - headroom - tailroom) @@ -283,7 +726,7 @@ int bpf_prog_test_run_tracing(struct bpf_prog *prog, int b = 2, err = -EFAULT; u32 retval = 0; - if (kattr->test.flags || kattr->test.cpu) + if (kattr->test.flags || kattr->test.cpu || kattr->test.batch_size) return -EINVAL; switch (prog->expected_attach_type) { @@ -347,7 +790,7 @@ int bpf_prog_test_run_raw_tp(struct bpf_prog *prog, /* doesn't support data_in/out, ctx_out, duration, or repeat */ if (kattr->test.data_in || kattr->test.data_out || kattr->test.ctx_out || kattr->test.duration || - kattr->test.repeat) + kattr->test.repeat || kattr->test.batch_size) return -EINVAL; if (ctx_size_in < prog->aux->max_ctx_offset || @@ -578,10 +1021,11 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, void *data; int ret; - if (kattr->test.flags || kattr->test.cpu) + if (kattr->test.flags || kattr->test.cpu || kattr->test.batch_size) return -EINVAL; - data = bpf_test_init(kattr, size, NET_SKB_PAD + NET_IP_ALIGN, + data = bpf_test_init(kattr, kattr->test.data_size_in, + size, NET_SKB_PAD + NET_IP_ALIGN, SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); if (IS_ERR(data)) return PTR_ERR(data); @@ -683,7 +1127,8 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, /* bpf program can never convert linear skb to non-linear */ if (WARN_ON_ONCE(skb_is_nonlinear(skb))) size = skb_headlen(skb); - ret = bpf_test_finish(kattr, uattr, skb->data, size, retval, duration); + ret = bpf_test_finish(kattr, uattr, skb->data, NULL, size, retval, + duration); if (!ret) ret = bpf_ctx_finish(kattr, uattr, ctx, sizeof(struct __sk_buff)); @@ -757,22 +1202,38 @@ static void xdp_convert_buff_to_md(struct xdp_buff *xdp, struct xdp_md *xdp_md) int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, union bpf_attr __user *uattr) { + bool do_live = (kattr->test.flags & BPF_F_TEST_XDP_LIVE_FRAMES); u32 tailroom = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - u32 headroom = XDP_PACKET_HEADROOM; + u32 batch_size = kattr->test.batch_size; + u32 retval = 0, duration, max_data_sz; u32 size = kattr->test.data_size_in; + u32 headroom = XDP_PACKET_HEADROOM; u32 repeat = kattr->test.repeat; struct netdev_rx_queue *rxqueue; + struct skb_shared_info *sinfo; struct xdp_buff xdp = {}; - u32 retval, duration; + int i, ret = -EINVAL; struct xdp_md *ctx; - u32 max_data_sz; void *data; - int ret = -EINVAL; if (prog->expected_attach_type == BPF_XDP_DEVMAP || prog->expected_attach_type == BPF_XDP_CPUMAP) return -EINVAL; + if (kattr->test.flags & ~BPF_F_TEST_XDP_LIVE_FRAMES) + return -EINVAL; + + if (do_live) { + if (!batch_size) + batch_size = NAPI_POLL_WEIGHT; + else if (batch_size > TEST_XDP_MAX_BATCH) + return -E2BIG; + + headroom += sizeof(struct xdp_page_head); + } else if (batch_size) { + return -EINVAL; + } + ctx = bpf_ctx_init(kattr, sizeof(struct xdp_md)); if (IS_ERR(ctx)) return PTR_ERR(ctx); @@ -781,33 +1242,81 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, /* There can't be user provided data before the meta data */ if (ctx->data_meta || ctx->data_end != size || ctx->data > ctx->data_end || - unlikely(xdp_metalen_invalid(ctx->data))) + unlikely(xdp_metalen_invalid(ctx->data)) || + (do_live && (kattr->test.data_out || kattr->test.ctx_out))) goto free_ctx; /* Meta data is allocated from the headroom */ headroom -= ctx->data; } - /* XDP have extra tailroom as (most) drivers use full page */ max_data_sz = 4096 - headroom - tailroom; + if (size > max_data_sz) { + /* disallow live data mode for jumbo frames */ + if (do_live) + goto free_ctx; + size = max_data_sz; + } - data = bpf_test_init(kattr, max_data_sz, headroom, tailroom); + data = bpf_test_init(kattr, size, max_data_sz, headroom, tailroom); if (IS_ERR(data)) { ret = PTR_ERR(data); goto free_ctx; } rxqueue = __netif_get_rx_queue(current->nsproxy->net_ns->loopback_dev, 0); - xdp_init_buff(&xdp, headroom + max_data_sz + tailroom, - &rxqueue->xdp_rxq); + rxqueue->xdp_rxq.frag_size = headroom + max_data_sz + tailroom; + xdp_init_buff(&xdp, rxqueue->xdp_rxq.frag_size, &rxqueue->xdp_rxq); xdp_prepare_buff(&xdp, data, headroom, size, true); + sinfo = xdp_get_shared_info_from_buff(&xdp); ret = xdp_convert_md_to_buff(ctx, &xdp); if (ret) goto free_data; + if (unlikely(kattr->test.data_size_in > size)) { + void __user *data_in = u64_to_user_ptr(kattr->test.data_in); + + while (size < kattr->test.data_size_in) { + struct page *page; + skb_frag_t *frag; + u32 data_len; + + if (sinfo->nr_frags == MAX_SKB_FRAGS) { + ret = -ENOMEM; + goto out; + } + + page = alloc_page(GFP_KERNEL); + if (!page) { + ret = -ENOMEM; + goto out; + } + + frag = &sinfo->frags[sinfo->nr_frags++]; + __skb_frag_set_page(frag, page); + + data_len = min_t(u32, kattr->test.data_size_in - size, + PAGE_SIZE); + skb_frag_size_set(frag, data_len); + + if (copy_from_user(page_address(page), data_in + size, + data_len)) { + ret = -EFAULT; + goto out; + } + sinfo->xdp_frags_size += data_len; + size += data_len; + } + xdp_buff_set_frags_flag(&xdp); + } + if (repeat > 1) bpf_prog_change_xdp(NULL, prog); - ret = bpf_test_run(prog, &xdp, repeat, &retval, &duration, true); + + if (do_live) + ret = bpf_test_run_xdp_live(prog, &xdp, repeat, batch_size, &duration); + else + ret = bpf_test_run(prog, &xdp, repeat, &retval, &duration, true); /* We convert the xdp_buff back to an xdp_md before checking the return * code so the reference count of any held netdevice will be decremented * even if the test run failed. @@ -816,12 +1325,9 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, if (ret) goto out; - if (xdp.data_meta != data + headroom || - xdp.data_end != xdp.data_meta + size) - size = xdp.data_end - xdp.data_meta; - - ret = bpf_test_finish(kattr, uattr, xdp.data_meta, size, retval, - duration); + size = xdp.data_end - xdp.data_meta + sinfo->xdp_frags_size; + ret = bpf_test_finish(kattr, uattr, xdp.data_meta, sinfo, size, + retval, duration); if (!ret) ret = bpf_ctx_finish(kattr, uattr, ctx, sizeof(struct xdp_md)); @@ -830,6 +1336,8 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, if (repeat > 1) bpf_prog_change_xdp(prog, NULL); free_data: + for (i = 0; i < sinfo->nr_frags; i++) + __free_page(skb_frag_page(&sinfo->frags[i])); kfree(data); free_ctx: kfree(ctx); @@ -870,13 +1378,13 @@ int bpf_prog_test_run_flow_dissector(struct bpf_prog *prog, if (prog->type != BPF_PROG_TYPE_FLOW_DISSECTOR) return -EINVAL; - if (kattr->test.flags || kattr->test.cpu) + if (kattr->test.flags || kattr->test.cpu || kattr->test.batch_size) return -EINVAL; if (size < ETH_HLEN) return -EINVAL; - data = bpf_test_init(kattr, size, 0, 0); + data = bpf_test_init(kattr, kattr->test.data_size_in, size, 0, 0); if (IS_ERR(data)) return PTR_ERR(data); @@ -905,14 +1413,14 @@ int bpf_prog_test_run_flow_dissector(struct bpf_prog *prog, do { retval = bpf_flow_dissect(prog, &ctx, eth->h_proto, ETH_HLEN, size, flags); - } while (bpf_test_timer_continue(&t, repeat, &ret, &duration)); + } while (bpf_test_timer_continue(&t, 1, repeat, &ret, &duration)); bpf_test_timer_leave(&t); if (ret < 0) goto out; - ret = bpf_test_finish(kattr, uattr, &flow_keys, sizeof(flow_keys), - retval, duration); + ret = bpf_test_finish(kattr, uattr, &flow_keys, NULL, + sizeof(flow_keys), retval, duration); if (!ret) ret = bpf_ctx_finish(kattr, uattr, user_ctx, sizeof(struct bpf_flow_keys)); @@ -937,7 +1445,7 @@ int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog, const union bpf_attr *kat if (prog->type != BPF_PROG_TYPE_SK_LOOKUP) return -EINVAL; - if (kattr->test.flags || kattr->test.cpu) + if (kattr->test.flags || kattr->test.cpu || kattr->test.batch_size) return -EINVAL; if (kattr->test.data_in || kattr->test.data_size_in || kattr->test.data_out || @@ -960,7 +1468,7 @@ int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog, const union bpf_attr *kat if (!range_is_zero(user_ctx, offsetofend(typeof(*user_ctx), local_port), sizeof(*user_ctx))) goto out; - if (user_ctx->local_port > U16_MAX || user_ctx->remote_port > U16_MAX) { + if (user_ctx->local_port > U16_MAX) { ret = -ERANGE; goto out; } @@ -968,7 +1476,7 @@ int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog, const union bpf_attr *kat ctx.family = (u16)user_ctx->family; ctx.protocol = (u16)user_ctx->protocol; ctx.dport = (u16)user_ctx->local_port; - ctx.sport = (__force __be16)user_ctx->remote_port; + ctx.sport = user_ctx->remote_port; switch (ctx.family) { case AF_INET: @@ -1000,7 +1508,7 @@ int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog, const union bpf_attr *kat do { ctx.selected_sk = NULL; retval = BPF_PROG_SK_LOOKUP_RUN_ARRAY(progs, ctx, bpf_prog_run); - } while (bpf_test_timer_continue(&t, repeat, &ret, &duration)); + } while (bpf_test_timer_continue(&t, 1, repeat, &ret, &duration)); bpf_test_timer_leave(&t); if (ret < 0) @@ -1016,7 +1524,7 @@ int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog, const union bpf_attr *kat user_ctx->cookie = sock_gen_cookie(ctx.selected_sk); } - ret = bpf_test_finish(kattr, uattr, NULL, 0, retval, duration); + ret = bpf_test_finish(kattr, uattr, NULL, NULL, 0, retval, duration); if (!ret) ret = bpf_ctx_finish(kattr, uattr, user_ctx, sizeof(*user_ctx)); @@ -1039,7 +1547,8 @@ int bpf_prog_test_run_syscall(struct bpf_prog *prog, /* doesn't support data_in/out, ctx_out, duration, or repeat or flags */ if (kattr->test.data_in || kattr->test.data_out || kattr->test.ctx_out || kattr->test.duration || - kattr->test.repeat || kattr->test.flags) + kattr->test.repeat || kattr->test.flags || + kattr->test.batch_size) return -EINVAL; if (ctx_size_in < prog->aux->max_ctx_offset || @@ -1067,3 +1576,17 @@ int bpf_prog_test_run_syscall(struct bpf_prog *prog, kfree(ctx); return err; } + +static const struct btf_kfunc_id_set bpf_prog_test_kfunc_set = { + .owner = THIS_MODULE, + .check_set = &test_sk_check_kfunc_ids, + .acquire_set = &test_sk_acquire_kfunc_ids, + .release_set = &test_sk_release_kfunc_ids, + .ret_null_set = &test_sk_ret_null_kfunc_ids, +}; + +static int __init bpf_prog_test_run_init(void) +{ + return register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_prog_test_kfunc_set); +} +late_initcall(bpf_prog_test_run_init); diff --git a/net/bridge/Makefile b/net/bridge/Makefile index 7fb9a021873b..24bd1c0a9a5a 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile @@ -20,7 +20,7 @@ obj-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o br_multicast_eht.o -bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o br_vlan_tunnel.o br_vlan_options.o +bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o br_vlan_tunnel.o br_vlan_options.o br_mst.o bridge-$(CONFIG_NET_SWITCHDEV) += br_switchdev.o diff --git a/net/bridge/br.c b/net/bridge/br.c index 1fac72cc617f..96e91d69a9a8 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -265,6 +265,9 @@ int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on, case BR_BOOLOPT_MCAST_VLAN_SNOOPING: err = br_multicast_toggle_vlan_snooping(br, on, extack); break; + case BR_BOOLOPT_MST_ENABLE: + err = br_mst_set_enabled(br, on, extack); + break; default: /* shouldn't be called with unsupported options */ WARN_ON(1); @@ -281,6 +284,8 @@ int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt) return br_opt_get(br, BROPT_NO_LL_LEARN); case BR_BOOLOPT_MCAST_VLAN_SNOOPING: return br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED); + case BR_BOOLOPT_MST_ENABLE: + return br_opt_get(br, BROPT_MST_ENABLED); default: /* shouldn't be called with unsupported options */ WARN_ON(1); @@ -342,23 +347,26 @@ void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on) clear_bit(opt, &br->options); } -static void __net_exit br_net_exit(struct net *net) +static void __net_exit br_net_exit_batch(struct list_head *net_list) { struct net_device *dev; + struct net *net; LIST_HEAD(list); rtnl_lock(); - for_each_netdev(net, dev) - if (netif_is_bridge_master(dev)) - br_dev_delete(dev, &list); + + list_for_each_entry(net, net_list, exit_list) + for_each_netdev(net, dev) + if (netif_is_bridge_master(dev)) + br_dev_delete(dev, &list); unregister_netdevice_many(&list); - rtnl_unlock(); + rtnl_unlock(); } static struct pernet_operations br_net_ops = { - .exit = br_net_exit, + .exit_batch = br_net_exit_batch, }; static const struct stp_proto br_stp_proto = { diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c index 3db1def4437b..e5e48c6e35d7 100644 --- a/net/bridge/br_arp_nd_proxy.c +++ b/net/bridge/br_arp_nd_proxy.c @@ -84,7 +84,7 @@ static void br_arp_send(struct net_bridge *br, struct net_bridge_port *p, skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_HOST; - netif_rx_ni(skb); + netif_rx(skb); } } @@ -364,7 +364,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, reply->ip_summed = CHECKSUM_UNNECESSARY; reply->pkt_type = PACKET_HOST; - netif_rx_ni(reply); + netif_rx(reply); } } diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index ec646656dbf1..02bb620d3b8d 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -62,7 +62,7 @@ EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit); int br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { - skb->tstamp = 0; + skb_clear_tstamp(skb); return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, net, sk, skb, NULL, skb->dev, br_dev_queue_push_xmit); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index b50382f957c1..196417859c4a 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -78,20 +78,38 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb u16 vid = 0; u8 state; - if (!p || p->state == BR_STATE_DISABLED) + if (!p) goto drop; + br = p->br; + + if (br_mst_is_enabled(br)) { + state = BR_STATE_FORWARDING; + } else { + if (p->state == BR_STATE_DISABLED) + goto drop; + + state = p->state; + } + brmctx = &p->br->multicast_ctx; pmctx = &p->multicast_ctx; - state = p->state; if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid, &state, &vlan)) goto out; + if (p->flags & BR_PORT_LOCKED) { + struct net_bridge_fdb_entry *fdb_src = + br_fdb_find_rcu(br, eth_hdr(skb)->h_source, vid); + + if (!fdb_src || READ_ONCE(fdb_src->dst) != p || + test_bit(BR_FDB_LOCAL, &fdb_src->flags)) + goto drop; + } + nbp_switchdev_frame_mark(p, skb); /* insert into forwarding database after filtering to avoid spoofing */ - br = p->br; if (p->flags & BR_LEARNING) br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, 0); @@ -361,9 +379,13 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb) return RX_HANDLER_PASS; forward: + if (br_mst_is_enabled(p->br)) + goto defer_stp_filtering; + switch (p->state) { case BR_STATE_FORWARDING: case BR_STATE_LEARNING: +defer_stp_filtering: if (ether_addr_equal(p->br->dev->dev_addr, dest)) skb->pkt_type = PACKET_HOST; diff --git a/net/bridge/br_mst.c b/net/bridge/br_mst.c new file mode 100644 index 000000000000..ee680adcee17 --- /dev/null +++ b/net/bridge/br_mst.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Bridge Multiple Spanning Tree Support + * + * Authors: + * Tobias Waldekranz + */ + +#include +#include + +#include "br_private.h" + +DEFINE_STATIC_KEY_FALSE(br_mst_used); + +bool br_mst_enabled(const struct net_device *dev) +{ + if (!netif_is_bridge_master(dev)) + return false; + + return br_opt_get(netdev_priv(dev), BROPT_MST_ENABLED); +} +EXPORT_SYMBOL_GPL(br_mst_enabled); + +int br_mst_get_info(const struct net_device *dev, u16 msti, unsigned long *vids) +{ + const struct net_bridge_vlan_group *vg; + const struct net_bridge_vlan *v; + const struct net_bridge *br; + + ASSERT_RTNL(); + + if (!netif_is_bridge_master(dev)) + return -EINVAL; + + br = netdev_priv(dev); + if (!br_opt_get(br, BROPT_MST_ENABLED)) + return -EINVAL; + + vg = br_vlan_group(br); + + list_for_each_entry(v, &vg->vlan_list, vlist) { + if (v->msti == msti) + __set_bit(v->vid, vids); + } + + return 0; +} +EXPORT_SYMBOL_GPL(br_mst_get_info); + +int br_mst_get_state(const struct net_device *dev, u16 msti, u8 *state) +{ + const struct net_bridge_port *p = NULL; + const struct net_bridge_vlan_group *vg; + const struct net_bridge_vlan *v; + + ASSERT_RTNL(); + + p = br_port_get_check_rtnl(dev); + if (!p || !br_opt_get(p->br, BROPT_MST_ENABLED)) + return -EINVAL; + + vg = nbp_vlan_group(p); + + list_for_each_entry(v, &vg->vlan_list, vlist) { + if (v->brvlan->msti == msti) { + *state = v->state; + return 0; + } + } + + return -ENOENT; +} +EXPORT_SYMBOL_GPL(br_mst_get_state); + +static void br_mst_vlan_set_state(struct net_bridge_port *p, struct net_bridge_vlan *v, + u8 state) +{ + struct net_bridge_vlan_group *vg = nbp_vlan_group(p); + + if (v->state == state) + return; + + br_vlan_set_state(v, state); + + if (v->vid == vg->pvid) + br_vlan_set_pvid_state(vg, state); +} + +int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state, + struct netlink_ext_ack *extack) +{ + struct switchdev_attr attr = { + .id = SWITCHDEV_ATTR_ID_PORT_MST_STATE, + .orig_dev = p->dev, + .u.mst_state = { + .msti = msti, + .state = state, + }, + }; + struct net_bridge_vlan_group *vg; + struct net_bridge_vlan *v; + int err; + + vg = nbp_vlan_group(p); + if (!vg) + return 0; + + /* MSTI 0 (CST) state changes are notified via the regular + * SWITCHDEV_ATTR_ID_PORT_STP_STATE. + */ + if (msti) { + err = switchdev_port_attr_set(p->dev, &attr, extack); + if (err && err != -EOPNOTSUPP) + return err; + } + + list_for_each_entry(v, &vg->vlan_list, vlist) { + if (v->brvlan->msti != msti) + continue; + + br_mst_vlan_set_state(p, v, state); + } + + return 0; +} + +static void br_mst_vlan_sync_state(struct net_bridge_vlan *pv, u16 msti) +{ + struct net_bridge_vlan_group *vg = nbp_vlan_group(pv->port); + struct net_bridge_vlan *v; + + list_for_each_entry(v, &vg->vlan_list, vlist) { + /* If this port already has a defined state in this + * MSTI (through some other VLAN membership), inherit + * it. + */ + if (v != pv && v->brvlan->msti == msti) { + br_mst_vlan_set_state(pv->port, pv, v->state); + return; + } + } + + /* Otherwise, start out in a new MSTI with all ports disabled. */ + return br_mst_vlan_set_state(pv->port, pv, BR_STATE_DISABLED); +} + +int br_mst_vlan_set_msti(struct net_bridge_vlan *mv, u16 msti) +{ + struct switchdev_attr attr = { + .id = SWITCHDEV_ATTR_ID_VLAN_MSTI, + .orig_dev = mv->br->dev, + .u.vlan_msti = { + .vid = mv->vid, + .msti = msti, + }, + }; + struct net_bridge_vlan_group *vg; + struct net_bridge_vlan *pv; + struct net_bridge_port *p; + int err; + + if (mv->msti == msti) + return 0; + + err = switchdev_port_attr_set(mv->br->dev, &attr, NULL); + if (err && err != -EOPNOTSUPP) + return err; + + mv->msti = msti; + + list_for_each_entry(p, &mv->br->port_list, list) { + vg = nbp_vlan_group(p); + + pv = br_vlan_find(vg, mv->vid); + if (pv) + br_mst_vlan_sync_state(pv, msti); + } + + return 0; +} + +void br_mst_vlan_init_state(struct net_bridge_vlan *v) +{ + /* VLANs always start out in MSTI 0 (CST) */ + v->msti = 0; + + if (br_vlan_is_master(v)) + v->state = BR_STATE_FORWARDING; + else + v->state = v->port->state; +} + +int br_mst_set_enabled(struct net_bridge *br, bool on, + struct netlink_ext_ack *extack) +{ + struct switchdev_attr attr = { + .id = SWITCHDEV_ATTR_ID_BRIDGE_MST, + .orig_dev = br->dev, + .u.mst = on, + }; + struct net_bridge_vlan_group *vg; + struct net_bridge_port *p; + int err; + + list_for_each_entry(p, &br->port_list, list) { + vg = nbp_vlan_group(p); + + if (!vg->num_vlans) + continue; + + NL_SET_ERR_MSG(extack, + "MST mode can't be changed while VLANs exist"); + return -EBUSY; + } + + if (br_opt_get(br, BROPT_MST_ENABLED) == on) + return 0; + + err = switchdev_port_attr_set(br->dev, &attr, extack); + if (err && err != -EOPNOTSUPP) + return err; + + if (on) + static_branch_enable(&br_mst_used); + else + static_branch_disable(&br_mst_used); + + br_opt_toggle(br, BROPT_MST_ENABLED, on); + return 0; +} + +size_t br_mst_info_size(const struct net_bridge_vlan_group *vg) +{ + DECLARE_BITMAP(seen, VLAN_N_VID) = { 0 }; + const struct net_bridge_vlan *v; + size_t sz; + + /* IFLA_BRIDGE_MST */ + sz = nla_total_size(0); + + list_for_each_entry_rcu(v, &vg->vlan_list, vlist) { + if (test_bit(v->brvlan->msti, seen)) + continue; + + /* IFLA_BRIDGE_MST_ENTRY */ + sz += nla_total_size(0) + + /* IFLA_BRIDGE_MST_ENTRY_MSTI */ + nla_total_size(sizeof(u16)) + + /* IFLA_BRIDGE_MST_ENTRY_STATE */ + nla_total_size(sizeof(u8)); + + __set_bit(v->brvlan->msti, seen); + } + + return sz; +} + +int br_mst_fill_info(struct sk_buff *skb, + const struct net_bridge_vlan_group *vg) +{ + DECLARE_BITMAP(seen, VLAN_N_VID) = { 0 }; + const struct net_bridge_vlan *v; + struct nlattr *nest; + int err = 0; + + list_for_each_entry(v, &vg->vlan_list, vlist) { + if (test_bit(v->brvlan->msti, seen)) + continue; + + nest = nla_nest_start_noflag(skb, IFLA_BRIDGE_MST_ENTRY); + if (!nest || + nla_put_u16(skb, IFLA_BRIDGE_MST_ENTRY_MSTI, v->brvlan->msti) || + nla_put_u8(skb, IFLA_BRIDGE_MST_ENTRY_STATE, v->state)) { + err = -EMSGSIZE; + break; + } + nla_nest_end(skb, nest); + + __set_bit(v->brvlan->msti, seen); + } + + return err; +} + +static const struct nla_policy br_mst_nl_policy[IFLA_BRIDGE_MST_ENTRY_MAX + 1] = { + [IFLA_BRIDGE_MST_ENTRY_MSTI] = NLA_POLICY_RANGE(NLA_U16, + 1, /* 0 reserved for CST */ + VLAN_N_VID - 1), + [IFLA_BRIDGE_MST_ENTRY_STATE] = NLA_POLICY_RANGE(NLA_U8, + BR_STATE_DISABLED, + BR_STATE_BLOCKING), +}; + +static int br_mst_process_one(struct net_bridge_port *p, + const struct nlattr *attr, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[IFLA_BRIDGE_MST_ENTRY_MAX + 1]; + u16 msti; + u8 state; + int err; + + err = nla_parse_nested(tb, IFLA_BRIDGE_MST_ENTRY_MAX, attr, + br_mst_nl_policy, extack); + if (err) + return err; + + if (!tb[IFLA_BRIDGE_MST_ENTRY_MSTI]) { + NL_SET_ERR_MSG_MOD(extack, "MSTI not specified"); + return -EINVAL; + } + + if (!tb[IFLA_BRIDGE_MST_ENTRY_STATE]) { + NL_SET_ERR_MSG_MOD(extack, "State not specified"); + return -EINVAL; + } + + msti = nla_get_u16(tb[IFLA_BRIDGE_MST_ENTRY_MSTI]); + state = nla_get_u8(tb[IFLA_BRIDGE_MST_ENTRY_STATE]); + + return br_mst_set_state(p, msti, state, extack); +} + +int br_mst_process(struct net_bridge_port *p, const struct nlattr *mst_attr, + struct netlink_ext_ack *extack) +{ + struct nlattr *attr; + int err, msts = 0; + int rem; + + if (!br_opt_get(p->br, BROPT_MST_ENABLED)) { + NL_SET_ERR_MSG_MOD(extack, "Can't modify MST state when MST is disabled"); + return -EBUSY; + } + + nla_for_each_nested(attr, mst_attr, rem) { + switch (nla_type(attr)) { + case IFLA_BRIDGE_MST_ENTRY: + err = br_mst_process_one(p, attr, extack); + break; + default: + continue; + } + + msts++; + if (err) + break; + } + + if (!msts) { + NL_SET_ERR_MSG_MOD(extack, "Found no MST entries to process"); + err = -EINVAL; + } + + return err; +} diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 2ff83d84230d..200ad05b296f 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -119,6 +119,9 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev, /* Each VLAN is returned in bridge_vlan_info along with flags */ vinfo_sz += num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info)); + if (p && vg && (filter_mask & RTEXT_FILTER_MST)) + vinfo_sz += br_mst_info_size(vg); + if (!(filter_mask & RTEXT_FILTER_CFM_STATUS)) return vinfo_sz; @@ -184,6 +187,7 @@ static inline size_t br_port_info_size(void) + nla_total_size(1) /* IFLA_BRPORT_VLAN_TUNNEL */ + nla_total_size(1) /* IFLA_BRPORT_NEIGH_SUPPRESS */ + nla_total_size(1) /* IFLA_BRPORT_ISOLATED */ + + nla_total_size(1) /* IFLA_BRPORT_LOCKED */ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */ + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */ @@ -269,7 +273,8 @@ static int br_port_fill_attrs(struct sk_buff *skb, BR_MRP_LOST_CONT)) || nla_put_u8(skb, IFLA_BRPORT_MRP_IN_OPEN, !!(p->flags & BR_MRP_LOST_IN_CONT)) || - nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED))) + nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED)) || + nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(p->flags & BR_PORT_LOCKED))) return -EMSGSIZE; timerval = br_timer_value(&p->message_age_timer); @@ -483,7 +488,8 @@ static int br_fill_ifinfo(struct sk_buff *skb, RTEXT_FILTER_BRVLAN_COMPRESSED | RTEXT_FILTER_MRP | RTEXT_FILTER_CFM_CONFIG | - RTEXT_FILTER_CFM_STATUS)) { + RTEXT_FILTER_CFM_STATUS | + RTEXT_FILTER_MST)) { af = nla_nest_start_noflag(skb, IFLA_AF_SPEC); if (!af) goto nla_put_failure; @@ -562,7 +568,28 @@ static int br_fill_ifinfo(struct sk_buff *skb, nla_nest_end(skb, cfm_nest); } + if ((filter_mask & RTEXT_FILTER_MST) && + br_opt_get(br, BROPT_MST_ENABLED) && port) { + const struct net_bridge_vlan_group *vg = nbp_vlan_group(port); + struct nlattr *mst_nest; + int err; + + if (!vg || !vg->num_vlans) + goto done; + + mst_nest = nla_nest_start(skb, IFLA_BRIDGE_MST); + if (!mst_nest) + goto nla_put_failure; + + err = br_mst_fill_info(skb, vg); + if (err) + goto nla_put_failure; + + nla_nest_end(skb, mst_nest); + } + done: + if (af) nla_nest_end(skb, af); nlmsg_end(skb, nlh); @@ -801,6 +828,23 @@ static int br_afspec(struct net_bridge *br, if (err) return err; break; + case IFLA_BRIDGE_MST: + if (!p) { + NL_SET_ERR_MSG(extack, + "MST states can only be set on bridge ports"); + return -EINVAL; + } + + if (cmd != RTM_SETLINK) { + NL_SET_ERR_MSG(extack, + "MST states can only be set through RTM_SETLINK"); + return -EINVAL; + } + + err = br_mst_process(p, attr, extack); + if (err) + return err; + break; } } @@ -827,6 +871,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_GROUP_FWD_MASK] = { .type = NLA_U16 }, [IFLA_BRPORT_NEIGH_SUPPRESS] = { .type = NLA_U8 }, [IFLA_BRPORT_ISOLATED] = { .type = NLA_U8 }, + [IFLA_BRPORT_LOCKED] = { .type = NLA_U8 }, [IFLA_BRPORT_BACKUP_PORT] = { .type = NLA_U32 }, [IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = { .type = NLA_U32 }, }; @@ -893,6 +938,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[], br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL); br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS, BR_NEIGH_SUPPRESS); br_set_port_flag(p, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED); + br_set_port_flag(p, tb, IFLA_BRPORT_LOCKED, BR_PORT_LOCKED); changed_mask = old_flags ^ p->flags; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 2661dda1a92b..18ccc3d5d296 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -178,6 +178,7 @@ enum { * @br_mcast_ctx: if MASTER flag set, this is the global vlan multicast context * @port_mcast_ctx: if MASTER flag unset, this is the per-port/vlan multicast * context + * @msti: if MASTER flag set, this holds the VLANs MST instance * @vlist: sorted list of VLAN entries * @rcu: used for entry destruction * @@ -210,6 +211,8 @@ struct net_bridge_vlan { struct net_bridge_mcast_port port_mcast_ctx; }; + u16 msti; + struct list_head vlist; struct rcu_head rcu; @@ -445,6 +448,7 @@ enum net_bridge_opts { BROPT_NO_LL_LEARN, BROPT_VLAN_BRIDGE_BINDING, BROPT_MCAST_VLAN_SNOOPING_ENABLED, + BROPT_MST_ENABLED, }; struct net_bridge { @@ -1765,6 +1769,63 @@ static inline bool br_vlan_state_allowed(u8 state, bool learn_allow) } #endif +/* br_mst.c */ +#ifdef CONFIG_BRIDGE_VLAN_FILTERING +DECLARE_STATIC_KEY_FALSE(br_mst_used); +static inline bool br_mst_is_enabled(struct net_bridge *br) +{ + return static_branch_unlikely(&br_mst_used) && + br_opt_get(br, BROPT_MST_ENABLED); +} + +int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state, + struct netlink_ext_ack *extack); +int br_mst_vlan_set_msti(struct net_bridge_vlan *v, u16 msti); +void br_mst_vlan_init_state(struct net_bridge_vlan *v); +int br_mst_set_enabled(struct net_bridge *br, bool on, + struct netlink_ext_ack *extack); +size_t br_mst_info_size(const struct net_bridge_vlan_group *vg); +int br_mst_fill_info(struct sk_buff *skb, + const struct net_bridge_vlan_group *vg); +int br_mst_process(struct net_bridge_port *p, const struct nlattr *mst_attr, + struct netlink_ext_ack *extack); +#else +static inline bool br_mst_is_enabled(struct net_bridge *br) +{ + return false; +} + +static inline int br_mst_set_state(struct net_bridge_port *p, u16 msti, + u8 state, struct netlink_ext_ack *extack) +{ + return -EOPNOTSUPP; +} + +static inline int br_mst_set_enabled(struct net_bridge *br, bool on, + struct netlink_ext_ack *extack) +{ + return -EOPNOTSUPP; +} + +static inline size_t br_mst_info_size(const struct net_bridge_vlan_group *vg) +{ + return 0; +} + +static inline int br_mst_fill_info(struct sk_buff *skb, + const struct net_bridge_vlan_group *vg) +{ + return -EOPNOTSUPP; +} + +static inline int br_mst_process(struct net_bridge_port *p, + const struct nlattr *mst_attr, + struct netlink_ext_ack *extack) +{ + return -EOPNOTSUPP; +} +#endif + struct nf_br_ops { int (*br_dev_xmit_hook)(struct sk_buff *skb); }; @@ -1985,7 +2046,7 @@ void br_switchdev_mdb_notify(struct net_device *dev, struct net_bridge_port_group *pg, int type); int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, - struct netlink_ext_ack *extack); + bool changed, struct netlink_ext_ack *extack); int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid); void br_switchdev_init(struct net_bridge *br); @@ -2052,8 +2113,8 @@ static inline int br_switchdev_set_port_flag(struct net_bridge_port *p, return 0; } -static inline int br_switchdev_port_vlan_add(struct net_device *dev, - u16 vid, u16 flags, +static inline int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, + u16 flags, bool changed, struct netlink_ext_ack *extack) { return -EOPNOTSUPP; diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 1d80f34a139c..7d27b2e6038f 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -43,6 +43,12 @@ void br_set_state(struct net_bridge_port *p, unsigned int state) return; p->state = state; + if (br_opt_get(p->br, BROPT_MST_ENABLED)) { + err = br_mst_set_state(p, 0, state, NULL); + if (err) + br_warn(p->br, "error setting MST state on port %u(%s)\n", + p->port_no, netdev_name(p->dev)); + } err = switchdev_port_attr_set(p->dev, &attr, NULL); if (err && err != -EOPNOTSUPP) br_warn(p->br, "error setting offload STP state on port %u(%s)\n", diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c index f8fbaaa7c501..8cc44c367231 100644 --- a/net/bridge/br_switchdev.c +++ b/net/bridge/br_switchdev.c @@ -72,7 +72,7 @@ bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, /* Flags that can be offloaded to hardware */ #define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \ - BR_MCAST_FLOOD | BR_BCAST_FLOOD) + BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_PORT_LOCKED) int br_switchdev_set_port_flag(struct net_bridge_port *p, unsigned long flags, @@ -160,13 +160,14 @@ br_switchdev_fdb_notify(struct net_bridge *br, } int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, - struct netlink_ext_ack *extack) + bool changed, struct netlink_ext_ack *extack) { struct switchdev_obj_port_vlan v = { .obj.orig_dev = dev, .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, .flags = flags, .vid = vid, + .changed = changed, }; return switchdev_port_obj_add(dev, &v.obj, extack); @@ -330,6 +331,46 @@ br_switchdev_fdb_replay(const struct net_device *br_dev, const void *ctx, return err; } +static int br_switchdev_vlan_attr_replay(struct net_device *br_dev, + const void *ctx, + struct notifier_block *nb, + struct netlink_ext_ack *extack) +{ + struct switchdev_notifier_port_attr_info attr_info = { + .info = { + .dev = br_dev, + .extack = extack, + .ctx = ctx, + }, + }; + struct net_bridge *br = netdev_priv(br_dev); + struct net_bridge_vlan_group *vg; + struct switchdev_attr attr; + struct net_bridge_vlan *v; + int err; + + attr_info.attr = &attr; + attr.orig_dev = br_dev; + + vg = br_vlan_group(br); + + list_for_each_entry(v, &vg->vlan_list, vlist) { + if (v->msti) { + attr.id = SWITCHDEV_ATTR_ID_VLAN_MSTI; + attr.u.vlan_msti.vid = v->vid; + attr.u.vlan_msti.msti = v->msti; + + err = nb->notifier_call(nb, SWITCHDEV_PORT_ATTR_SET, + &attr_info); + err = notifier_to_errno(err); + if (err) + return err; + } + } + + return 0; +} + static int br_switchdev_vlan_replay_one(struct notifier_block *nb, struct net_device *dev, @@ -351,51 +392,19 @@ br_switchdev_vlan_replay_one(struct notifier_block *nb, return notifier_to_errno(err); } -static int br_switchdev_vlan_replay(struct net_device *br_dev, - struct net_device *dev, - const void *ctx, bool adding, - struct notifier_block *nb, - struct netlink_ext_ack *extack) +static int br_switchdev_vlan_replay_group(struct notifier_block *nb, + struct net_device *dev, + struct net_bridge_vlan_group *vg, + const void *ctx, unsigned long action, + struct netlink_ext_ack *extack) { - struct net_bridge_vlan_group *vg; struct net_bridge_vlan *v; - struct net_bridge_port *p; - struct net_bridge *br; - unsigned long action; int err = 0; u16 pvid; - ASSERT_RTNL(); - - if (!nb) - return 0; - - if (!netif_is_bridge_master(br_dev)) - return -EINVAL; - - if (!netif_is_bridge_master(dev) && !netif_is_bridge_port(dev)) - return -EINVAL; - - if (netif_is_bridge_master(dev)) { - br = netdev_priv(dev); - vg = br_vlan_group(br); - p = NULL; - } else { - p = br_port_get_rtnl(dev); - if (WARN_ON(!p)) - return -EINVAL; - vg = nbp_vlan_group(p); - br = p->br; - } - if (!vg) return 0; - if (adding) - action = SWITCHDEV_PORT_OBJ_ADD; - else - action = SWITCHDEV_PORT_OBJ_DEL; - pvid = br_get_pvid(vg); list_for_each_entry(v, &vg->vlan_list, vlist) { @@ -415,7 +424,54 @@ static int br_switchdev_vlan_replay(struct net_device *br_dev, return err; } - return err; + return 0; +} + +static int br_switchdev_vlan_replay(struct net_device *br_dev, + const void *ctx, bool adding, + struct notifier_block *nb, + struct netlink_ext_ack *extack) +{ + struct net_bridge *br = netdev_priv(br_dev); + struct net_bridge_port *p; + unsigned long action; + int err; + + ASSERT_RTNL(); + + if (!nb) + return 0; + + if (!netif_is_bridge_master(br_dev)) + return -EINVAL; + + if (adding) + action = SWITCHDEV_PORT_OBJ_ADD; + else + action = SWITCHDEV_PORT_OBJ_DEL; + + err = br_switchdev_vlan_replay_group(nb, br_dev, br_vlan_group(br), + ctx, action, extack); + if (err) + return err; + + list_for_each_entry(p, &br->port_list, list) { + struct net_device *dev = p->dev; + + err = br_switchdev_vlan_replay_group(nb, dev, + nbp_vlan_group(p), + ctx, action, extack); + if (err) + return err; + } + + if (adding) { + err = br_switchdev_vlan_attr_replay(br_dev, ctx, nb, extack); + if (err) + return err; + } + + return 0; } #ifdef CONFIG_BRIDGE_IGMP_SNOOPING @@ -681,8 +737,7 @@ static int nbp_switchdev_sync_objs(struct net_bridge_port *p, const void *ctx, struct net_device *dev = p->dev; int err; - err = br_switchdev_vlan_replay(br_dev, dev, ctx, true, blocking_nb, - extack); + err = br_switchdev_vlan_replay(br_dev, ctx, true, blocking_nb, extack); if (err && err != -EOPNOTSUPP) return err; @@ -706,11 +761,11 @@ static void nbp_switchdev_unsync_objs(struct net_bridge_port *p, struct net_device *br_dev = p->br->dev; struct net_device *dev = p->dev; - br_switchdev_vlan_replay(br_dev, dev, ctx, false, blocking_nb, NULL); + br_switchdev_fdb_replay(br_dev, ctx, false, atomic_nb); br_switchdev_mdb_replay(br_dev, dev, ctx, false, blocking_nb, NULL); - br_switchdev_fdb_replay(br_dev, ctx, false, atomic_nb); + br_switchdev_vlan_replay(br_dev, ctx, false, blocking_nb, NULL); } /* Let the bridge know that this port is offloaded, so that it can assign a diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 1402d5ca242d..0f5e75ccac79 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -34,53 +34,70 @@ static struct net_bridge_vlan *br_vlan_lookup(struct rhashtable *tbl, u16 vid) return rhashtable_lookup_fast(tbl, &vid, br_vlan_rht_params); } -static bool __vlan_add_pvid(struct net_bridge_vlan_group *vg, +static void __vlan_add_pvid(struct net_bridge_vlan_group *vg, const struct net_bridge_vlan *v) { if (vg->pvid == v->vid) - return false; + return; smp_wmb(); br_vlan_set_pvid_state(vg, v->state); vg->pvid = v->vid; - - return true; } -static bool __vlan_delete_pvid(struct net_bridge_vlan_group *vg, u16 vid) +static void __vlan_delete_pvid(struct net_bridge_vlan_group *vg, u16 vid) { if (vg->pvid != vid) - return false; + return; smp_wmb(); vg->pvid = 0; - - return true; } -/* return true if anything changed, false otherwise */ -static bool __vlan_add_flags(struct net_bridge_vlan *v, u16 flags) +/* Update the BRIDGE_VLAN_INFO_PVID and BRIDGE_VLAN_INFO_UNTAGGED flags of @v. + * If @commit is false, return just whether the BRIDGE_VLAN_INFO_PVID and + * BRIDGE_VLAN_INFO_UNTAGGED bits of @flags would produce any change onto @v. + */ +static bool __vlan_flags_update(struct net_bridge_vlan *v, u16 flags, + bool commit) { struct net_bridge_vlan_group *vg; - u16 old_flags = v->flags; - bool ret; + bool change; if (br_vlan_is_master(v)) vg = br_vlan_group(v->br); else vg = nbp_vlan_group(v->port); + /* check if anything would be changed on commit */ + change = !!(flags & BRIDGE_VLAN_INFO_PVID) == !!(vg->pvid != v->vid) || + ((flags ^ v->flags) & BRIDGE_VLAN_INFO_UNTAGGED); + + if (!commit) + goto out; + if (flags & BRIDGE_VLAN_INFO_PVID) - ret = __vlan_add_pvid(vg, v); + __vlan_add_pvid(vg, v); else - ret = __vlan_delete_pvid(vg, v->vid); + __vlan_delete_pvid(vg, v->vid); if (flags & BRIDGE_VLAN_INFO_UNTAGGED) v->flags |= BRIDGE_VLAN_INFO_UNTAGGED; else v->flags &= ~BRIDGE_VLAN_INFO_UNTAGGED; - return ret || !!(old_flags ^ v->flags); +out: + return change; +} + +static bool __vlan_flags_would_change(struct net_bridge_vlan *v, u16 flags) +{ + return __vlan_flags_update(v, flags, false); +} + +static void __vlan_flags_commit(struct net_bridge_vlan *v, u16 flags) +{ + __vlan_flags_update(v, flags, true); } static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br, @@ -92,7 +109,7 @@ static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br, /* Try switchdev op first. In case it is not supported, fallback to * 8021q add. */ - err = br_switchdev_port_vlan_add(dev, v->vid, flags, extack); + err = br_switchdev_port_vlan_add(dev, v->vid, flags, false, extack); if (err == -EOPNOTSUPP) return vlan_vid_add(dev, br->vlan_proto, v->vid); v->priv_flags |= BR_VLFLAG_ADDED_BY_SWITCHDEV; @@ -209,6 +226,24 @@ static void nbp_vlan_rcu_free(struct rcu_head *rcu) kfree(v); } +static void br_vlan_init_state(struct net_bridge_vlan *v) +{ + struct net_bridge *br; + + if (br_vlan_is_master(v)) + br = v->br; + else + br = v->port->br; + + if (br_opt_get(br, BROPT_MST_ENABLED)) { + br_mst_vlan_init_state(v); + return; + } + + v->state = BR_STATE_FORWARDING; + v->msti = 0; +} + /* This is the shared VLAN add function which works for both ports and bridge * devices. There are four possible calls to this function in terms of the * vlan entry type: @@ -284,9 +319,12 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags, } br_multicast_port_ctx_init(p, v, &v->port_mcast_ctx); } else { - err = br_switchdev_port_vlan_add(dev, v->vid, flags, extack); - if (err && err != -EOPNOTSUPP) - goto out; + if (br_vlan_should_use(v)) { + err = br_switchdev_port_vlan_add(dev, v->vid, flags, + false, extack); + if (err && err != -EOPNOTSUPP) + goto out; + } br_multicast_ctx_init(br, v, &v->br_mcast_ctx); v->priv_flags |= BR_VLFLAG_GLOBAL_MCAST_ENABLED; } @@ -302,7 +340,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags, } /* set the state before publishing */ - v->state = BR_STATE_FORWARDING; + br_vlan_init_state(v); err = rhashtable_lookup_insert_fast(&vg->vlan_hash, &v->vnode, br_vlan_rht_params); @@ -310,7 +348,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags, goto out_fdb_insert; __vlan_add_list(v); - __vlan_add_flags(v, flags); + __vlan_flags_commit(v, flags); br_multicast_toggle_one_vlan(v, true); if (p) @@ -404,6 +442,7 @@ static void __vlan_flush(const struct net_bridge *br, { struct net_bridge_vlan *vlan, *tmp; u16 v_start = 0, v_end = 0; + int err; __vlan_delete_pvid(vg, vg->pvid); list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) { @@ -417,7 +456,13 @@ static void __vlan_flush(const struct net_bridge *br, } v_end = vlan->vid; - __vlan_del(vlan); + err = __vlan_del(vlan); + if (err) { + br_err(br, + "port %u(%s) failed to delete vlan %d: %pe\n", + (unsigned int) p->port_no, p->dev->name, + vlan->vid, ERR_PTR(err)); + } } /* notify about the last/whole vlan range */ @@ -670,18 +715,29 @@ static int br_vlan_add_existing(struct net_bridge *br, u16 flags, bool *changed, struct netlink_ext_ack *extack) { + bool would_change = __vlan_flags_would_change(vlan, flags); + bool becomes_brentry = false; int err; - err = br_switchdev_port_vlan_add(br->dev, vlan->vid, flags, extack); - if (err && err != -EOPNOTSUPP) - return err; - if (!br_vlan_is_brentry(vlan)) { /* Trying to change flags of non-existent bridge vlan */ - if (!(flags & BRIDGE_VLAN_INFO_BRENTRY)) { - err = -EINVAL; - goto err_flags; - } + if (!(flags & BRIDGE_VLAN_INFO_BRENTRY)) + return -EINVAL; + + becomes_brentry = true; + } + + /* Master VLANs that aren't brentries weren't notified before, + * time to notify them now. + */ + if (becomes_brentry || would_change) { + err = br_switchdev_port_vlan_add(br->dev, vlan->vid, flags, + would_change, extack); + if (err && err != -EOPNOTSUPP) + return err; + } + + if (becomes_brentry) { /* It was only kept for port vlans, now make it real */ err = br_fdb_add_local(br, NULL, br->dev->dev_addr, vlan->vid); if (err) { @@ -696,13 +752,13 @@ static int br_vlan_add_existing(struct net_bridge *br, br_multicast_toggle_one_vlan(vlan, true); } - if (__vlan_add_flags(vlan, flags)) + __vlan_flags_commit(vlan, flags); + if (would_change) *changed = true; return 0; err_fdb_insert: -err_flags: br_switchdev_port_vlan_del(br->dev, vlan->vid); return err; } @@ -1247,11 +1303,18 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags, *changed = false; vlan = br_vlan_find(nbp_vlan_group(port), vid); if (vlan) { - /* Pass the flags to the hardware bridge */ - ret = br_switchdev_port_vlan_add(port->dev, vid, flags, extack); - if (ret && ret != -EOPNOTSUPP) - return ret; - *changed = __vlan_add_flags(vlan, flags); + bool would_change = __vlan_flags_would_change(vlan, flags); + + if (would_change) { + /* Pass the flags to the hardware bridge */ + ret = br_switchdev_port_vlan_add(port->dev, vid, flags, + true, extack); + if (ret && ret != -EOPNOTSUPP) + return ret; + } + + __vlan_flags_commit(vlan, flags); + *changed = would_change; return 0; } diff --git a/net/bridge/br_vlan_options.c b/net/bridge/br_vlan_options.c index a6382973b3e7..a2724d03278c 100644 --- a/net/bridge/br_vlan_options.c +++ b/net/bridge/br_vlan_options.c @@ -99,6 +99,11 @@ static int br_vlan_modify_state(struct net_bridge_vlan_group *vg, return -EBUSY; } + if (br_opt_get(br, BROPT_MST_ENABLED)) { + NL_SET_ERR_MSG_MOD(extack, "Can't modify vlan state directly when MST is enabled"); + return -EBUSY; + } + if (v->state == state) return 0; @@ -291,6 +296,7 @@ bool br_vlan_global_opts_can_enter_range(const struct net_bridge_vlan *v_curr, const struct net_bridge_vlan *r_end) { return v_curr->vid - r_end->vid == 1 && + v_curr->msti == r_end->msti && ((v_curr->priv_flags ^ r_end->priv_flags) & BR_VLFLAG_GLOBAL_MCAST_ENABLED) == 0 && br_multicast_ctx_options_equal(&v_curr->br_mcast_ctx, @@ -379,6 +385,9 @@ bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range, #endif #endif + if (nla_put_u16(skb, BRIDGE_VLANDB_GOPTS_MSTI, v_opts->msti)) + goto out_err; + nla_nest_end(skb, nest); return true; @@ -410,6 +419,7 @@ static size_t rtnl_vlan_global_opts_nlmsg_size(const struct net_bridge_vlan *v) + nla_total_size(0) /* BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS */ + br_rports_size(&v->br_mcast_ctx) /* BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS */ #endif + + nla_total_size(sizeof(u16)) /* BRIDGE_VLANDB_GOPTS_MSTI */ + nla_total_size(sizeof(u16)); /* BRIDGE_VLANDB_GOPTS_RANGE */ } @@ -559,6 +569,15 @@ static int br_vlan_process_global_one_opts(const struct net_bridge *br, } #endif #endif + if (tb[BRIDGE_VLANDB_GOPTS_MSTI]) { + u16 msti; + + msti = nla_get_u16(tb[BRIDGE_VLANDB_GOPTS_MSTI]); + err = br_mst_vlan_set_msti(v, msti); + if (err) + return err; + *changed = true; + } return 0; } @@ -578,6 +597,7 @@ static const struct nla_policy br_vlan_db_gpol[BRIDGE_VLANDB_GOPTS_MAX + 1] = { [BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL] = { .type = NLA_U64 }, [BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL] = { .type = NLA_U64 }, [BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL] = { .type = NLA_U64 }, + [BRIDGE_VLANDB_GOPTS_MSTI] = NLA_POLICY_MAX(NLA_U16, VLAN_N_VID - 1), }; int br_vlan_rtm_process_global_options(struct net_device *dev, diff --git a/net/bridge/netfilter/nf_conntrack_bridge.c b/net/bridge/netfilter/nf_conntrack_bridge.c index fdbed3158555..73242962be5d 100644 --- a/net/bridge/netfilter/nf_conntrack_bridge.c +++ b/net/bridge/netfilter/nf_conntrack_bridge.c @@ -32,6 +32,7 @@ static int nf_br_ip_fragment(struct net *net, struct sock *sk, struct sk_buff *)) { int frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size; + bool mono_delivery_time = skb->mono_delivery_time; unsigned int hlen, ll_rs, mtu; ktime_t tstamp = skb->tstamp; struct ip_frag_state state; @@ -81,7 +82,7 @@ static int nf_br_ip_fragment(struct net *net, struct sock *sk, if (iter.frag) ip_fraglist_prepare(skb, &iter); - skb->tstamp = tstamp; + skb_set_delivery_time(skb, tstamp, mono_delivery_time); err = output(net, sk, data, skb); if (err || !iter.frag) break; @@ -112,7 +113,7 @@ static int nf_br_ip_fragment(struct net *net, struct sock *sk, goto blackhole; } - skb2->tstamp = tstamp; + skb_set_delivery_time(skb2, tstamp, mono_delivery_time); err = output(net, sk, data, skb2); if (err) goto blackhole; @@ -380,7 +381,7 @@ static unsigned int nf_ct_bridge_confirm(struct sk_buff *skb) protoff = skb_network_offset(skb) + ip_hdrlen(skb); break; case htons(ETH_P_IPV6): { - unsigned char pnum = ipv6_hdr(skb)->nexthdr; + unsigned char pnum = ipv6_hdr(skb)->nexthdr; __be16 frag_off; protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum, diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c index c1ef9cc89b78..8c3eaba87ad2 100644 --- a/net/bridge/netfilter/nft_meta_bridge.c +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -87,6 +87,7 @@ static int nft_meta_bridge_get_init(const struct nft_ctx *ctx, return nft_meta_get_init(ctx, expr, tb); } + priv->len = len; return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, len); } @@ -98,6 +99,7 @@ static const struct nft_expr_ops nft_meta_bridge_get_ops = { .eval = nft_meta_bridge_get_eval, .init = nft_meta_bridge_get_init, .dump = nft_meta_get_dump, + .reduce = nft_meta_get_reduce, }; static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track, @@ -112,8 +114,7 @@ static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track, if (track->regs[i].selector->ops != &nft_meta_bridge_get_ops) continue; - track->regs[i].selector = NULL; - track->regs[i].bitwise = NULL; + __nft_reg_track_cancel(track, i); } return false; diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c index fbf858ddec35..71b54fed7263 100644 --- a/net/bridge/netfilter/nft_reject_bridge.c +++ b/net/bridge/netfilter/nft_reject_bridge.c @@ -185,6 +185,7 @@ static const struct nft_expr_ops nft_reject_bridge_ops = { .init = nft_reject_init, .dump = nft_reject_dump, .validate = nft_reject_bridge_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_reject_bridge_type __read_mostly = { diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index 440139706130..52dd0b6835bc 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -268,7 +268,7 @@ static int receive(struct sk_buff *skb, struct net_device *dev, err = caifd->layer.up->receive(caifd->layer.up, pkt); - /* For -EILSEQ the packet is not freed so so it now */ + /* For -EILSEQ the packet is not freed so free it now */ if (err == -EILSEQ) cfpkt_destroy(pkt); diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c index 414dc5671c45..4d63ef13a1fd 100644 --- a/net/caif/chnl_net.c +++ b/net/caif/chnl_net.c @@ -99,7 +99,7 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) else skb->ip_summed = CHECKSUM_NONE; - netif_rx_any_context(skb); + netif_rx(skb); /* Update statistics. */ priv->netdev->stats.rx_packets++; diff --git a/net/can/af_can.c b/net/can/af_can.c index cce2af10eb3e..1fb49d51b25d 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -284,7 +284,7 @@ int can_send(struct sk_buff *skb, int loop) } if (newskb) - netif_rx_ni(newskb); + netif_rx(newskb); /* update statistics */ pkg_stats->tx_frames++; diff --git a/net/can/gw.c b/net/can/gw.c index d8861e862f15..1ea4cc527db3 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -577,6 +577,13 @@ static inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj) gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj); } +static void cgw_job_free_rcu(struct rcu_head *rcu_head) +{ + struct cgw_job *gwj = container_of(rcu_head, struct cgw_job, rcu); + + kmem_cache_free(cgw_cache, gwj); +} + static int cgw_notifier(struct notifier_block *nb, unsigned long msg, void *ptr) { @@ -596,8 +603,7 @@ static int cgw_notifier(struct notifier_block *nb, if (gwj->src.dev == dev || gwj->dst.dev == dev) { hlist_del(&gwj->list); cgw_unregister_filter(net, gwj); - synchronize_rcu(); - kmem_cache_free(cgw_cache, gwj); + call_rcu(&gwj->rcu, cgw_job_free_rcu); } } } @@ -1155,8 +1161,7 @@ static void cgw_remove_all_jobs(struct net *net) hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) { hlist_del(&gwj->list); cgw_unregister_filter(net, gwj); - synchronize_rcu(); - kmem_cache_free(cgw_cache, gwj); + call_rcu(&gwj->rcu, cgw_job_free_rcu); } } @@ -1224,8 +1229,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, hlist_del(&gwj->list); cgw_unregister_filter(net, gwj); - synchronize_rcu(); - kmem_cache_free(cgw_cache, gwj); + call_rcu(&gwj->rcu, cgw_job_free_rcu); err = 0; break; } @@ -1239,16 +1243,19 @@ static int __net_init cangw_pernet_init(struct net *net) return 0; } -static void __net_exit cangw_pernet_exit(struct net *net) +static void __net_exit cangw_pernet_exit_batch(struct list_head *net_list) { + struct net *net; + rtnl_lock(); - cgw_remove_all_jobs(net); + list_for_each_entry(net, net_list, exit_list) + cgw_remove_all_jobs(net); rtnl_unlock(); } static struct pernet_operations cangw_pernet_ops = { .init = cangw_pernet_init, - .exit = cangw_pernet_exit, + .exit_batch = cangw_pernet_exit_batch, }; static __init int cgw_module_init(void) diff --git a/net/can/isotp.c b/net/can/isotp.c index d2a430b6a13b..f6f8ba1f816d 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -14,7 +14,6 @@ * - use CAN_ISOTP_WAIT_TX_DONE flag to block the caller until the PDU is sent * - as we have static buffers the check whether the PDU fits into the buffer * is done at FF reception time (no support for sending 'wait frames') - * - take care of the tx-queue-len as traffic shaping is still on the TODO list * * Copyright (c) 2020 Volkswagen Group Electronic Research * All rights reserved. @@ -87,9 +86,9 @@ MODULE_ALIAS("can-proto-6"); /* ISO 15765-2:2016 supports more than 4095 byte per ISO PDU as the FF_DL can * take full 32 bit values (4 Gbyte). We would need some good concept to handle * this between user space and kernel space. For now increase the static buffer - * to something about 8 kbyte to be able to test this new functionality. + * to something about 64 kbyte to be able to test this new functionality. */ -#define MAX_MSG_LENGTH 8200 +#define MAX_MSG_LENGTH 66000 /* N_PCI type values in bits 7-4 of N_PCI bytes */ #define N_PCI_SF 0x00 /* single frame */ @@ -141,8 +140,10 @@ struct isotp_sock { struct can_isotp_options opt; struct can_isotp_fc_options rxfc, txfc; struct can_isotp_ll_options ll; + u32 frame_txtime; u32 force_tx_stmin; u32 force_rx_stmin; + u32 cfecho; /* consecutive frame echo tag */ struct tpcon rx, tx; struct list_head notifier; wait_queue_head_t wait; @@ -360,7 +361,7 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) so->tx_gap = ktime_set(0, 0); /* add transmission time for CAN frame N_As */ - so->tx_gap = ktime_add_ns(so->tx_gap, so->opt.frame_txtime); + so->tx_gap = ktime_add_ns(so->tx_gap, so->frame_txtime); /* add waiting time for consecutive frames N_Cs */ if (so->opt.flags & CAN_ISOTP_FORCE_TXSTMIN) so->tx_gap = ktime_add_ns(so->tx_gap, @@ -712,6 +713,63 @@ static void isotp_fill_dataframe(struct canfd_frame *cf, struct isotp_sock *so, cf->data[0] = so->opt.ext_address; } +static void isotp_send_cframe(struct isotp_sock *so) +{ + struct sock *sk = &so->sk; + struct sk_buff *skb; + struct net_device *dev; + struct canfd_frame *cf; + int can_send_ret; + int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; + + dev = dev_get_by_index(sock_net(sk), so->ifindex); + if (!dev) + return; + + skb = alloc_skb(so->ll.mtu + sizeof(struct can_skb_priv), GFP_ATOMIC); + if (!skb) { + dev_put(dev); + return; + } + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; + + cf = (struct canfd_frame *)skb->data; + skb_put_zero(skb, so->ll.mtu); + + /* create consecutive frame */ + isotp_fill_dataframe(cf, so, ae, 0); + + /* place consecutive frame N_PCI in appropriate index */ + cf->data[ae] = N_PCI_CF | so->tx.sn++; + so->tx.sn %= 16; + so->tx.bs++; + + cf->flags = so->ll.tx_flags; + + skb->dev = dev; + can_skb_set_owner(skb, sk); + + /* cfecho should have been zero'ed by init/isotp_rcv_echo() */ + if (so->cfecho) + pr_notice_once("can-isotp: cfecho is %08X != 0\n", so->cfecho); + + /* set consecutive frame echo tag */ + so->cfecho = *(u32 *)cf->data; + + /* send frame with local echo enabled */ + can_send_ret = can_send(skb, 1); + if (can_send_ret) { + pr_notice_once("can-isotp: %s: can_send_ret %pe\n", + __func__, ERR_PTR(can_send_ret)); + if (can_send_ret == -ENOBUFS) + pr_notice_once("can-isotp: tx queue is full\n"); + } + dev_put(dev); +} + static void isotp_create_fframe(struct canfd_frame *cf, struct isotp_sock *so, int ae) { @@ -748,19 +806,74 @@ static void isotp_create_fframe(struct canfd_frame *cf, struct isotp_sock *so, so->tx.state = ISOTP_WAIT_FIRST_FC; } +static void isotp_rcv_echo(struct sk_buff *skb, void *data) +{ + struct sock *sk = (struct sock *)data; + struct isotp_sock *so = isotp_sk(sk); + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + + /* only handle my own local echo skb's */ + if (skb->sk != sk || so->cfecho != *(u32 *)cf->data) + return; + + /* cancel local echo timeout */ + hrtimer_cancel(&so->txtimer); + + /* local echo skb with consecutive frame has been consumed */ + so->cfecho = 0; + + if (so->tx.idx >= so->tx.len) { + /* we are done */ + so->tx.state = ISOTP_IDLE; + wake_up_interruptible(&so->wait); + return; + } + + if (so->txfc.bs && so->tx.bs >= so->txfc.bs) { + /* stop and wait for FC with timeout */ + so->tx.state = ISOTP_WAIT_FC; + hrtimer_start(&so->txtimer, ktime_set(1, 0), + HRTIMER_MODE_REL_SOFT); + return; + } + + /* no gap between data frames needed => use burst mode */ + if (!so->tx_gap) { + isotp_send_cframe(so); + return; + } + + /* start timer to send next consecutive frame with correct delay */ + hrtimer_start(&so->txtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT); +} + static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) { struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, txtimer); struct sock *sk = &so->sk; - struct sk_buff *skb; - struct net_device *dev; - struct canfd_frame *cf; enum hrtimer_restart restart = HRTIMER_NORESTART; - int can_send_ret; - int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; switch (so->tx.state) { + case ISOTP_SENDING: + + /* cfecho should be consumed by isotp_rcv_echo() here */ + if (!so->cfecho) { + /* start timeout for unlikely lost echo skb */ + hrtimer_set_expires(&so->txtimer, + ktime_add(ktime_get(), + ktime_set(2, 0))); + restart = HRTIMER_RESTART; + + /* push out the next consecutive frame */ + isotp_send_cframe(so); + break; + } + + /* cfecho has not been cleared in isotp_rcv_echo() */ + pr_notice_once("can-isotp: cfecho %08X timeout\n", so->cfecho); + fallthrough; + case ISOTP_WAIT_FC: case ISOTP_WAIT_FIRST_FC: @@ -776,78 +889,6 @@ static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) wake_up_interruptible(&so->wait); break; - case ISOTP_SENDING: - - /* push out the next segmented pdu */ - dev = dev_get_by_index(sock_net(sk), so->ifindex); - if (!dev) - break; - -isotp_tx_burst: - skb = alloc_skb(so->ll.mtu + sizeof(struct can_skb_priv), - GFP_ATOMIC); - if (!skb) { - dev_put(dev); - break; - } - - can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; - - cf = (struct canfd_frame *)skb->data; - skb_put_zero(skb, so->ll.mtu); - - /* create consecutive frame */ - isotp_fill_dataframe(cf, so, ae, 0); - - /* place consecutive frame N_PCI in appropriate index */ - cf->data[ae] = N_PCI_CF | so->tx.sn++; - so->tx.sn %= 16; - so->tx.bs++; - - cf->flags = so->ll.tx_flags; - - skb->dev = dev; - can_skb_set_owner(skb, sk); - - can_send_ret = can_send(skb, 1); - if (can_send_ret) { - pr_notice_once("can-isotp: %s: can_send_ret %pe\n", - __func__, ERR_PTR(can_send_ret)); - if (can_send_ret == -ENOBUFS) - pr_notice_once("can-isotp: tx queue is full, increasing txqueuelen may prevent this error\n"); - } - if (so->tx.idx >= so->tx.len) { - /* we are done */ - so->tx.state = ISOTP_IDLE; - dev_put(dev); - wake_up_interruptible(&so->wait); - break; - } - - if (so->txfc.bs && so->tx.bs >= so->txfc.bs) { - /* stop and wait for FC */ - so->tx.state = ISOTP_WAIT_FC; - dev_put(dev); - hrtimer_set_expires(&so->txtimer, - ktime_add(ktime_get(), - ktime_set(1, 0))); - restart = HRTIMER_RESTART; - break; - } - - /* no gap between data frames needed => use burst mode */ - if (!so->tx_gap) - goto isotp_tx_burst; - - /* start timer to send next data frame with correct delay */ - dev_put(dev); - hrtimer_set_expires(&so->txtimer, - ktime_add(ktime_get(), so->tx_gap)); - restart = HRTIMER_RESTART; - break; - default: WARN_ON_ONCE(1); } @@ -1005,26 +1046,29 @@ static int isotp_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, { struct sock *sk = sock->sk; struct sk_buff *skb; - int err = 0; - int noblock; + struct isotp_sock *so = isotp_sk(sk); + int noblock = flags & MSG_DONTWAIT; + int ret = 0; + + if (flags & ~(MSG_DONTWAIT | MSG_TRUNC)) + return -EINVAL; + + if (!so->bound) + return -EADDRNOTAVAIL; - noblock = flags & MSG_DONTWAIT; flags &= ~MSG_DONTWAIT; - - skb = skb_recv_datagram(sk, flags, noblock, &err); + skb = skb_recv_datagram(sk, flags, noblock, &ret); if (!skb) - return err; + return ret; if (size < skb->len) msg->msg_flags |= MSG_TRUNC; else size = skb->len; - err = memcpy_to_msg(msg, skb->data, size); - if (err < 0) { - skb_free_datagram(sk, skb); - return err; - } + ret = memcpy_to_msg(msg, skb->data, size); + if (ret < 0) + goto out_err; sock_recv_timestamp(msg, sk, skb); @@ -1034,9 +1078,13 @@ static int isotp_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, memcpy(msg->msg_name, skb->cb, msg->msg_namelen); } + /* set length of return value */ + ret = (flags & MSG_TRUNC) ? skb->len : size; + +out_err: skb_free_datagram(sk, skb); - return size; + return ret; } static int isotp_release(struct socket *sock) @@ -1075,6 +1123,9 @@ static int isotp_release(struct socket *sock) can_rx_unregister(net, dev, so->rxid, SINGLE_MASK(so->rxid), isotp_rcv, sk); + can_rx_unregister(net, dev, so->txid, + SINGLE_MASK(so->txid), + isotp_rcv_echo, sk); dev_put(dev); synchronize_rcu(); } @@ -1104,6 +1155,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) struct net *net = sock_net(sk); int ifindex; struct net_device *dev; + canid_t tx_id, rx_id; int err = 0; int notify_enetdown = 0; int do_rx_reg = 1; @@ -1111,8 +1163,18 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) if (len < ISOTP_MIN_NAMELEN) return -EINVAL; - if (addr->can_addr.tp.tx_id & (CAN_ERR_FLAG | CAN_RTR_FLAG)) - return -EADDRNOTAVAIL; + /* sanitize tx/rx CAN identifiers */ + tx_id = addr->can_addr.tp.tx_id; + if (tx_id & CAN_EFF_FLAG) + tx_id &= (CAN_EFF_FLAG | CAN_EFF_MASK); + else + tx_id &= CAN_SFF_MASK; + + rx_id = addr->can_addr.tp.rx_id; + if (rx_id & CAN_EFF_FLAG) + rx_id &= (CAN_EFF_FLAG | CAN_EFF_MASK); + else + rx_id &= CAN_SFF_MASK; if (!addr->can_ifindex) return -ENODEV; @@ -1124,21 +1186,13 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) do_rx_reg = 0; /* do not validate rx address for functional addressing */ - if (do_rx_reg) { - if (addr->can_addr.tp.rx_id == addr->can_addr.tp.tx_id) { - err = -EADDRNOTAVAIL; - goto out; - } - - if (addr->can_addr.tp.rx_id & (CAN_ERR_FLAG | CAN_RTR_FLAG)) { - err = -EADDRNOTAVAIL; - goto out; - } + if (do_rx_reg && rx_id == tx_id) { + err = -EADDRNOTAVAIL; + goto out; } if (so->bound && addr->can_ifindex == so->ifindex && - addr->can_addr.tp.rx_id == so->rxid && - addr->can_addr.tp.tx_id == so->txid) + rx_id == so->rxid && tx_id == so->txid) goto out; dev = dev_get_by_index(net, addr->can_ifindex); @@ -1161,11 +1215,18 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) ifindex = dev->ifindex; - if (do_rx_reg) - can_rx_register(net, dev, addr->can_addr.tp.rx_id, - SINGLE_MASK(addr->can_addr.tp.rx_id), + if (do_rx_reg) { + can_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id), isotp_rcv, sk, "isotp", sk); + /* no consecutive frame echo skb in flight */ + so->cfecho = 0; + + /* register for echo skb's */ + can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id), + isotp_rcv_echo, sk, "isotpe", sk); + } + dev_put(dev); if (so->bound && do_rx_reg) { @@ -1176,6 +1237,9 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) can_rx_unregister(net, dev, so->rxid, SINGLE_MASK(so->rxid), isotp_rcv, sk); + can_rx_unregister(net, dev, so->txid, + SINGLE_MASK(so->txid), + isotp_rcv_echo, sk); dev_put(dev); } } @@ -1183,8 +1247,8 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) /* switch to new settings */ so->ifindex = ifindex; - so->rxid = addr->can_addr.tp.rx_id; - so->txid = addr->can_addr.tp.tx_id; + so->rxid = rx_id; + so->txid = tx_id; so->bound = 1; out: @@ -1238,6 +1302,14 @@ static int isotp_setsockopt_locked(struct socket *sock, int level, int optname, /* no separate rx_ext_address is given => use ext_address */ if (!(so->opt.flags & CAN_ISOTP_RX_EXT_ADDR)) so->opt.rx_ext_address = so->opt.ext_address; + + /* check for frame_txtime changes (0 => no changes) */ + if (so->opt.frame_txtime) { + if (so->opt.frame_txtime == CAN_ISOTP_FRAME_TXTIME_ZERO) + so->frame_txtime = 0; + else + so->frame_txtime = so->opt.frame_txtime; + } break; case CAN_ISOTP_RECV_FC: @@ -1381,10 +1453,14 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg, case NETDEV_UNREGISTER: lock_sock(sk); /* remove current filters & unregister */ - if (so->bound && (!(so->opt.flags & CAN_ISOTP_SF_BROADCAST))) + if (so->bound && (!(so->opt.flags & CAN_ISOTP_SF_BROADCAST))) { can_rx_unregister(dev_net(dev), dev, so->rxid, SINGLE_MASK(so->rxid), isotp_rcv, sk); + can_rx_unregister(dev_net(dev), dev, so->txid, + SINGLE_MASK(so->txid), + isotp_rcv_echo, sk); + } so->ifindex = 0; so->bound = 0; @@ -1439,6 +1515,7 @@ static int isotp_init(struct sock *sk) so->opt.rxpad_content = CAN_ISOTP_DEFAULT_PAD_CONTENT; so->opt.txpad_content = CAN_ISOTP_DEFAULT_PAD_CONTENT; so->opt.frame_txtime = CAN_ISOTP_DEFAULT_FRAME_TXTIME; + so->frame_txtime = CAN_ISOTP_DEFAULT_FRAME_TXTIME; so->rxfc.bs = CAN_ISOTP_DEFAULT_RECV_BS; so->rxfc.stmin = CAN_ISOTP_DEFAULT_RECV_STMIN; so->rxfc.wftmax = CAN_ISOTP_DEFAULT_RECV_WFTMAX; diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index d9c37fd10809..e3ac36380520 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -141,7 +141,7 @@ static int bpf_fd_sk_storage_update_elem(struct bpf_map *map, void *key, if (sock) { sdata = bpf_local_storage_update( sock->sk, (struct bpf_local_storage_map *)map, value, - map_flags); + map_flags, GFP_ATOMIC); sockfd_put(sock); return PTR_ERR_OR_ZERO(sdata); } @@ -172,7 +172,7 @@ bpf_sk_storage_clone_elem(struct sock *newsk, { struct bpf_local_storage_elem *copy_selem; - copy_selem = bpf_selem_alloc(smap, newsk, NULL, true); + copy_selem = bpf_selem_alloc(smap, newsk, NULL, true, GFP_ATOMIC); if (!copy_selem) return NULL; @@ -230,7 +230,7 @@ int bpf_sk_storage_clone(const struct sock *sk, struct sock *newsk) bpf_selem_link_map(smap, copy_selem); bpf_selem_link_storage_nolock(new_sk_storage, copy_selem); } else { - ret = bpf_local_storage_alloc(newsk, smap, copy_selem); + ret = bpf_local_storage_alloc(newsk, smap, copy_selem, GFP_ATOMIC); if (ret) { kfree(copy_selem); atomic_sub(smap->elem_size, @@ -255,8 +255,9 @@ int bpf_sk_storage_clone(const struct sock *sk, struct sock *newsk) return ret; } -BPF_CALL_4(bpf_sk_storage_get, struct bpf_map *, map, struct sock *, sk, - void *, value, u64, flags) +/* *gfp_flags* is a hidden argument provided by the verifier */ +BPF_CALL_5(bpf_sk_storage_get, struct bpf_map *, map, struct sock *, sk, + void *, value, u64, flags, gfp_t, gfp_flags) { struct bpf_local_storage_data *sdata; @@ -277,7 +278,7 @@ BPF_CALL_4(bpf_sk_storage_get, struct bpf_map *, map, struct sock *, sk, refcount_inc_not_zero(&sk->sk_refcnt)) { sdata = bpf_local_storage_update( sk, (struct bpf_local_storage_map *)map, value, - BPF_NOEXIST); + BPF_NOEXIST, gfp_flags); /* sk must be a fullsock (guaranteed by verifier), * so sock_gen_put() is unnecessary. */ @@ -405,6 +406,8 @@ static bool bpf_sk_storage_tracing_allowed(const struct bpf_prog *prog) case BPF_TRACE_FENTRY: case BPF_TRACE_FEXIT: btf_vmlinux = bpf_get_btf_vmlinux(); + if (IS_ERR_OR_NULL(btf_vmlinux)) + return false; btf_id = prog->aux->attach_btf_id; t = btf_type_by_id(btf_vmlinux, btf_id); tname = btf_name_by_offset(btf_vmlinux, t->name_off); @@ -417,14 +420,16 @@ static bool bpf_sk_storage_tracing_allowed(const struct bpf_prog *prog) return false; } -BPF_CALL_4(bpf_sk_storage_get_tracing, struct bpf_map *, map, struct sock *, sk, - void *, value, u64, flags) +/* *gfp_flags* is a hidden argument provided by the verifier */ +BPF_CALL_5(bpf_sk_storage_get_tracing, struct bpf_map *, map, struct sock *, sk, + void *, value, u64, flags, gfp_t, gfp_flags) { WARN_ON_ONCE(!bpf_rcu_lock_held()); if (in_hardirq() || in_nmi()) return (unsigned long)NULL; - return (unsigned long)____bpf_sk_storage_get(map, sk, value, flags); + return (unsigned long)____bpf_sk_storage_get(map, sk, value, flags, + gfp_flags); } BPF_CALL_2(bpf_sk_storage_delete_tracing, struct bpf_map *, map, diff --git a/net/core/dev.c b/net/core/dev.c index 1baab07820f6..8a5109479dbe 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -216,18 +216,38 @@ static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex) return &net->dev_index_head[ifindex & (NETDEV_HASHENTRIES - 1)]; } -static inline void rps_lock(struct softnet_data *sd) +static inline void rps_lock_irqsave(struct softnet_data *sd, + unsigned long *flags) { -#ifdef CONFIG_RPS - spin_lock(&sd->input_pkt_queue.lock); -#endif + if (IS_ENABLED(CONFIG_RPS)) + spin_lock_irqsave(&sd->input_pkt_queue.lock, *flags); + else if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_irq_save(*flags); } -static inline void rps_unlock(struct softnet_data *sd) +static inline void rps_lock_irq_disable(struct softnet_data *sd) { -#ifdef CONFIG_RPS - spin_unlock(&sd->input_pkt_queue.lock); -#endif + if (IS_ENABLED(CONFIG_RPS)) + spin_lock_irq(&sd->input_pkt_queue.lock); + else if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_irq_disable(); +} + +static inline void rps_unlock_irq_restore(struct softnet_data *sd, + unsigned long *flags) +{ + if (IS_ENABLED(CONFIG_RPS)) + spin_unlock_irqrestore(&sd->input_pkt_queue.lock, *flags); + else if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_irq_restore(*flags); +} + +static inline void rps_unlock_irq_enable(struct softnet_data *sd) +{ + if (IS_ENABLED(CONFIG_RPS)) + spin_unlock_irq(&sd->input_pkt_queue.lock); + else if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_irq_enable(); } static struct netdev_name_node *netdev_name_node_alloc(struct net_device *dev, @@ -320,7 +340,6 @@ int netdev_name_node_alt_create(struct net_device *dev, const char *name) return 0; } -EXPORT_SYMBOL(netdev_name_node_alt_create); static void __netdev_name_node_alt_destroy(struct netdev_name_node *name_node) { @@ -348,7 +367,6 @@ int netdev_name_node_alt_destroy(struct net_device *dev, const char *name) return 0; } -EXPORT_SYMBOL(netdev_name_node_alt_destroy); static void netdev_name_node_alt_flush(struct net_device *dev) { @@ -1037,7 +1055,7 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf) /* avoid cases where sscanf is not exact inverse of printf */ snprintf(buf, IFNAMSIZ, name, i); if (!strncmp(buf, name_node->name, IFNAMSIZ)) - set_bit(i, inuse); + __set_bit(i, inuse); } if (!sscanf(d->name, name, &i)) continue; @@ -1047,7 +1065,7 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf) /* avoid cases where sscanf is not exact inverse of printf */ snprintf(buf, IFNAMSIZ, name, i); if (!strncmp(buf, d->name, IFNAMSIZ)) - set_bit(i, inuse); + __set_bit(i, inuse); } i = find_first_zero_bit(inuse, max_netdevices); @@ -1602,7 +1620,8 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd) N(UDP_TUNNEL_DROP_INFO) N(CHANGE_TX_QUEUE_LEN) N(CVLAN_FILTER_PUSH_INFO) N(CVLAN_FILTER_DROP_INFO) N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO) - N(PRE_CHANGEADDR) + N(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE) + N(OFFLOAD_XSTATS_REPORT_USED) N(OFFLOAD_XSTATS_REPORT_DELTA) } #undef N return "UNKNOWN_NETDEV_EVENT"; @@ -1919,6 +1938,32 @@ static int call_netdevice_notifiers_info(unsigned long val, return raw_notifier_call_chain(&netdev_chain, val, info); } +/** + * call_netdevice_notifiers_info_robust - call per-netns notifier blocks + * for and rollback on error + * @val_up: value passed unmodified to notifier function + * @val_down: value passed unmodified to the notifier function when + * recovering from an error on @val_up + * @info: notifier information data + * + * Call all per-netns network notifier blocks, but not notifier blocks on + * the global notifier chain. Parameters and return value are as for + * raw_notifier_call_chain_robust(). + */ + +static int +call_netdevice_notifiers_info_robust(unsigned long val_up, + unsigned long val_down, + struct netdev_notifier_info *info) +{ + struct net *net = dev_net(info->dev); + + ASSERT_RTNL(); + + return raw_notifier_call_chain_robust(&net->netdev_chain, + val_up, val_down, info); +} + static int call_netdevice_notifiers_extack(unsigned long val, struct net_device *dev, struct netlink_ext_ack *extack) @@ -2000,7 +2045,8 @@ void net_dec_egress_queue(void) EXPORT_SYMBOL_GPL(net_dec_egress_queue); #endif -static DEFINE_STATIC_KEY_FALSE(netstamp_needed_key); +DEFINE_STATIC_KEY_FALSE(netstamp_needed_key); +EXPORT_SYMBOL(netstamp_needed_key); #ifdef CONFIG_JUMP_LABEL static atomic_t netstamp_needed_deferred; static atomic_t netstamp_wanted; @@ -2061,14 +2107,15 @@ EXPORT_SYMBOL(net_disable_timestamp); static inline void net_timestamp_set(struct sk_buff *skb) { skb->tstamp = 0; + skb->mono_delivery_time = 0; if (static_branch_unlikely(&netstamp_needed_key)) - __net_timestamp(skb); + skb->tstamp = ktime_get_real(); } #define net_timestamp_check(COND, SKB) \ if (static_branch_unlikely(&netstamp_needed_key)) { \ if ((COND) && !(SKB)->tstamp) \ - __net_timestamp(SKB); \ + (SKB)->tstamp = ktime_get_real(); \ } \ bool is_skb_forwardable(const struct net_device *dev, const struct sk_buff *skb) @@ -2943,13 +2990,25 @@ EXPORT_SYMBOL(netif_set_real_num_queues); /** * netif_get_num_default_rss_queues - default number of RSS queues * - * This routine should set an upper limit on the number of RSS queues - * used by default by multiqueue devices. + * Default value is the number of physical cores if there are only 1 or 2, or + * divided by 2 if there are more. */ int netif_get_num_default_rss_queues(void) { - return is_kdump_kernel() ? - 1 : min_t(int, DEFAULT_MAX_NUM_RSS_QUEUES, num_online_cpus()); + cpumask_var_t cpus; + int cpu, count = 0; + + if (unlikely(is_kdump_kernel() || !zalloc_cpumask_var(&cpus, GFP_KERNEL))) + return 1; + + cpumask_copy(cpus, cpu_online_mask); + for_each_cpu(cpu, cpus) { + ++count; + cpumask_andnot(cpus, cpus, topology_sibling_cpumask(cpu)); + } + free_cpumask_var(cpus); + + return count > 2 ? DIV_ROUND_UP(count, 2) : count; } EXPORT_SYMBOL(netif_get_num_default_rss_queues); @@ -3586,7 +3645,7 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device out_kfree_skb: kfree_skb(skb); out_null: - atomic_long_inc(&dev->tx_dropped); + dev_core_stats_tx_dropped_inc(dev); return NULL; } @@ -3710,7 +3769,8 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, no_lock_out: if (unlikely(to_free)) - kfree_skb_list(to_free); + kfree_skb_list_reason(to_free, + SKB_DROP_REASON_QDISC_DROP); return rc; } @@ -3765,7 +3825,7 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, } spin_unlock(root_lock); if (unlikely(to_free)) - kfree_skb_list(to_free); + kfree_skb_list_reason(to_free, SKB_DROP_REASON_QDISC_DROP); if (unlikely(contended)) spin_unlock(&q->busylock); return rc; @@ -3811,7 +3871,7 @@ int dev_loopback_xmit(struct net *net, struct sock *sk, struct sk_buff *skb) skb->ip_summed = CHECKSUM_UNNECESSARY; WARN_ON(!skb_dst(skb)); skb_dst_force(skb); - netif_rx_ni(skb); + netif_rx(skb); return 0; } EXPORT_SYMBOL(dev_loopback_xmit); @@ -3840,7 +3900,7 @@ sch_handle_egress(struct sk_buff *skb, int *ret, struct net_device *dev) case TC_ACT_SHOT: mini_qdisc_qstats_cpu_drop(miniq); *ret = NET_XMIT_DROP; - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_TC_EGRESS); return NULL; case TC_ACT_STOLEN: case TC_ACT_QUEUED: @@ -4136,7 +4196,7 @@ static int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) rc = -ENETDOWN; rcu_read_unlock_bh(); - atomic_long_inc(&dev->tx_dropped); + dev_core_stats_tx_dropped_inc(dev); kfree_skb_list(skb); return rc; out: @@ -4188,7 +4248,7 @@ int __dev_direct_xmit(struct sk_buff *skb, u16 queue_id) local_bh_enable(); return ret; drop: - atomic_long_inc(&dev->tx_dropped); + dev_core_stats_tx_dropped_inc(dev); kfree_skb_list(skb); return NET_XMIT_DROP; } @@ -4217,6 +4277,8 @@ static inline void ____napi_schedule(struct softnet_data *sd, { struct task_struct *thread; + lockdep_assert_irqs_disabled(); + if (test_bit(NAPI_STATE_THREADED, &napi->state)) { /* Paired with smp_mb__before_atomic() in * napi_enable()/dev_set_threaded(). @@ -4456,11 +4518,11 @@ static void rps_trigger_softirq(void *data) * If yes, queue it to our IPI list and return 1 * If no, return 0 */ -static int rps_ipi_queued(struct softnet_data *sd) +static int napi_schedule_rps(struct softnet_data *sd) { -#ifdef CONFIG_RPS struct softnet_data *mysd = this_cpu_ptr(&softnet_data); +#ifdef CONFIG_RPS if (sd != mysd) { sd->rps_ipi_next = mysd->rps_ipi_list; mysd->rps_ipi_list = sd; @@ -4469,6 +4531,7 @@ static int rps_ipi_queued(struct softnet_data *sd) return 1; } #endif /* CONFIG_RPS */ + __napi_schedule_irqoff(&mysd->backlog); return 0; } @@ -4519,15 +4582,15 @@ static bool skb_flow_limit(struct sk_buff *skb, unsigned int qlen) static int enqueue_to_backlog(struct sk_buff *skb, int cpu, unsigned int *qtail) { + enum skb_drop_reason reason; struct softnet_data *sd; unsigned long flags; unsigned int qlen; + reason = SKB_DROP_REASON_NOT_SPECIFIED; sd = &per_cpu(softnet_data, cpu); - local_irq_save(flags); - - rps_lock(sd); + rps_lock_irqsave(sd, &flags); if (!netif_running(skb->dev)) goto drop; qlen = skb_queue_len(&sd->input_pkt_queue); @@ -4536,29 +4599,25 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu, enqueue: __skb_queue_tail(&sd->input_pkt_queue, skb); input_queue_tail_incr_save(sd, qtail); - rps_unlock(sd); - local_irq_restore(flags); + rps_unlock_irq_restore(sd, &flags); return NET_RX_SUCCESS; } /* Schedule NAPI for backlog device * We can use non atomic operation since we own the queue lock */ - if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state)) { - if (!rps_ipi_queued(sd)) - ____napi_schedule(sd, &sd->backlog); - } + if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state)) + napi_schedule_rps(sd); goto enqueue; } + reason = SKB_DROP_REASON_CPU_BACKLOG; drop: sd->dropped++; - rps_unlock(sd); + rps_unlock_irq_restore(sd, &flags); - local_irq_restore(flags); - - atomic_long_inc(&skb->dev->rx_dropped); - kfree_skb(skb); + dev_core_stats_rx_dropped_inc(skb->dev); + kfree_skb_reason(skb, reason); return NET_RX_DROP; } @@ -4778,7 +4837,7 @@ int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb) } return XDP_PASS; out_redir: - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_XDP); return XDP_DROP; } EXPORT_SYMBOL_GPL(do_xdp_generic); @@ -4796,7 +4855,6 @@ static int netif_rx_internal(struct sk_buff *skb) struct rps_dev_flow voidflow, *rflow = &voidflow; int cpu; - preempt_disable(); rcu_read_lock(); cpu = get_rps_cpu(skb->dev, skb, &rflow); @@ -4806,78 +4864,72 @@ static int netif_rx_internal(struct sk_buff *skb) ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail); rcu_read_unlock(); - preempt_enable(); } else #endif { unsigned int qtail; - ret = enqueue_to_backlog(skb, get_cpu(), &qtail); - put_cpu(); + ret = enqueue_to_backlog(skb, smp_processor_id(), &qtail); } return ret; } +/** + * __netif_rx - Slightly optimized version of netif_rx + * @skb: buffer to post + * + * This behaves as netif_rx except that it does not disable bottom halves. + * As a result this function may only be invoked from the interrupt context + * (either hard or soft interrupt). + */ +int __netif_rx(struct sk_buff *skb) +{ + int ret; + + lockdep_assert_once(hardirq_count() | softirq_count()); + + trace_netif_rx_entry(skb); + ret = netif_rx_internal(skb); + trace_netif_rx_exit(ret); + return ret; +} +EXPORT_SYMBOL(__netif_rx); + /** * netif_rx - post buffer to the network code * @skb: buffer to post * * This function receives a packet from a device driver and queues it for - * the upper (protocol) levels to process. It always succeeds. The buffer - * may be dropped during processing for congestion control or by the - * protocol layers. + * the upper (protocol) levels to process via the backlog NAPI device. It + * always succeeds. The buffer may be dropped during processing for + * congestion control or by the protocol layers. + * The network buffer is passed via the backlog NAPI device. Modern NIC + * driver should use NAPI and GRO. + * This function can used from interrupt and from process context. The + * caller from process context must not disable interrupts before invoking + * this function. * * return values: * NET_RX_SUCCESS (no congestion) * NET_RX_DROP (packet was dropped) * */ - int netif_rx(struct sk_buff *skb) { + bool need_bh_off = !(hardirq_count() | softirq_count()); int ret; + if (need_bh_off) + local_bh_disable(); trace_netif_rx_entry(skb); - ret = netif_rx_internal(skb); trace_netif_rx_exit(ret); - + if (need_bh_off) + local_bh_enable(); return ret; } EXPORT_SYMBOL(netif_rx); -int netif_rx_ni(struct sk_buff *skb) -{ - int err; - - trace_netif_rx_ni_entry(skb); - - preempt_disable(); - err = netif_rx_internal(skb); - if (local_softirq_pending()) - do_softirq(); - preempt_enable(); - trace_netif_rx_ni_exit(err); - - return err; -} -EXPORT_SYMBOL(netif_rx_ni); - -int netif_rx_any_context(struct sk_buff *skb) -{ - /* - * If invoked from contexts which do not invoke bottom half - * processing either at return from interrupt or when softrqs are - * reenabled, use netif_rx_ni() which invokes bottomhalf processing - * directly. - */ - if (in_interrupt()) - return netif_rx(skb); - else - return netif_rx_ni(skb); -} -EXPORT_SYMBOL(netif_rx_any_context); - static __latent_entropy void net_tx_action(struct softirq_action *h) { struct softnet_data *sd = this_cpu_ptr(&softnet_data); @@ -5001,7 +5053,7 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, break; case TC_ACT_SHOT: mini_qdisc_qstats_cpu_drop(miniq); - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_TC_INGRESS); return NULL; case TC_ACT_STOLEN: case TC_ACT_QUEUED: @@ -5318,11 +5370,13 @@ static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc, *ppt_prev = pt_prev; } else { drop: - if (!deliver_exact) - atomic_long_inc(&skb->dev->rx_dropped); - else - atomic_long_inc(&skb->dev->rx_nohandler); - kfree_skb(skb); + if (!deliver_exact) { + dev_core_stats_rx_dropped_inc(skb->dev); + kfree_skb_reason(skb, SKB_DROP_REASON_PTYPE_ABSENT); + } else { + dev_core_stats_rx_nohandler_inc(skb->dev); + kfree_skb(skb); + } /* Jamal, now you will not able to escape explaining * me how you were going to use this. :-) */ @@ -5650,8 +5704,7 @@ static void flush_backlog(struct work_struct *work) local_bh_disable(); sd = this_cpu_ptr(&softnet_data); - local_irq_disable(); - rps_lock(sd); + rps_lock_irq_disable(sd); skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) { if (skb->dev->reg_state == NETREG_UNREGISTERING) { __skb_unlink(skb, &sd->input_pkt_queue); @@ -5659,8 +5712,7 @@ static void flush_backlog(struct work_struct *work) input_queue_head_incr(sd); } } - rps_unlock(sd); - local_irq_enable(); + rps_unlock_irq_enable(sd); skb_queue_walk_safe(&sd->process_queue, skb, tmp) { if (skb->dev->reg_state == NETREG_UNREGISTERING) { @@ -5678,16 +5730,14 @@ static bool flush_required(int cpu) struct softnet_data *sd = &per_cpu(softnet_data, cpu); bool do_flush; - local_irq_disable(); - rps_lock(sd); + rps_lock_irq_disable(sd); /* as insertion into process_queue happens with the rps lock held, * process_queue access may race only with dequeue */ do_flush = !skb_queue_empty(&sd->input_pkt_queue) || !skb_queue_empty_lockless(&sd->process_queue); - rps_unlock(sd); - local_irq_enable(); + rps_unlock_irq_enable(sd); return do_flush; #endif @@ -5802,8 +5852,7 @@ static int process_backlog(struct napi_struct *napi, int quota) } - local_irq_disable(); - rps_lock(sd); + rps_lock_irq_disable(sd); if (skb_queue_empty(&sd->input_pkt_queue)) { /* * Inline a custom version of __napi_complete(). @@ -5819,8 +5868,7 @@ static int process_backlog(struct napi_struct *napi, int quota) skb_queue_splice_tail_init(&sd->input_pkt_queue, &sd->process_queue); } - rps_unlock(sd); - local_irq_enable(); + rps_unlock_irq_enable(sd); } return work; @@ -7727,6 +7775,242 @@ void netdev_bonding_info_change(struct net_device *dev, } EXPORT_SYMBOL(netdev_bonding_info_change); +static int netdev_offload_xstats_enable_l3(struct net_device *dev, + struct netlink_ext_ack *extack) +{ + struct netdev_notifier_offload_xstats_info info = { + .info.dev = dev, + .info.extack = extack, + .type = NETDEV_OFFLOAD_XSTATS_TYPE_L3, + }; + int err; + int rc; + + dev->offload_xstats_l3 = kzalloc(sizeof(*dev->offload_xstats_l3), + GFP_KERNEL); + if (!dev->offload_xstats_l3) + return -ENOMEM; + + rc = call_netdevice_notifiers_info_robust(NETDEV_OFFLOAD_XSTATS_ENABLE, + NETDEV_OFFLOAD_XSTATS_DISABLE, + &info.info); + err = notifier_to_errno(rc); + if (err) + goto free_stats; + + return 0; + +free_stats: + kfree(dev->offload_xstats_l3); + dev->offload_xstats_l3 = NULL; + return err; +} + +int netdev_offload_xstats_enable(struct net_device *dev, + enum netdev_offload_xstats_type type, + struct netlink_ext_ack *extack) +{ + ASSERT_RTNL(); + + if (netdev_offload_xstats_enabled(dev, type)) + return -EALREADY; + + switch (type) { + case NETDEV_OFFLOAD_XSTATS_TYPE_L3: + return netdev_offload_xstats_enable_l3(dev, extack); + } + + WARN_ON(1); + return -EINVAL; +} +EXPORT_SYMBOL(netdev_offload_xstats_enable); + +static void netdev_offload_xstats_disable_l3(struct net_device *dev) +{ + struct netdev_notifier_offload_xstats_info info = { + .info.dev = dev, + .type = NETDEV_OFFLOAD_XSTATS_TYPE_L3, + }; + + call_netdevice_notifiers_info(NETDEV_OFFLOAD_XSTATS_DISABLE, + &info.info); + kfree(dev->offload_xstats_l3); + dev->offload_xstats_l3 = NULL; +} + +int netdev_offload_xstats_disable(struct net_device *dev, + enum netdev_offload_xstats_type type) +{ + ASSERT_RTNL(); + + if (!netdev_offload_xstats_enabled(dev, type)) + return -EALREADY; + + switch (type) { + case NETDEV_OFFLOAD_XSTATS_TYPE_L3: + netdev_offload_xstats_disable_l3(dev); + return 0; + } + + WARN_ON(1); + return -EINVAL; +} +EXPORT_SYMBOL(netdev_offload_xstats_disable); + +static void netdev_offload_xstats_disable_all(struct net_device *dev) +{ + netdev_offload_xstats_disable(dev, NETDEV_OFFLOAD_XSTATS_TYPE_L3); +} + +static struct rtnl_hw_stats64 * +netdev_offload_xstats_get_ptr(const struct net_device *dev, + enum netdev_offload_xstats_type type) +{ + switch (type) { + case NETDEV_OFFLOAD_XSTATS_TYPE_L3: + return dev->offload_xstats_l3; + } + + WARN_ON(1); + return NULL; +} + +bool netdev_offload_xstats_enabled(const struct net_device *dev, + enum netdev_offload_xstats_type type) +{ + ASSERT_RTNL(); + + return netdev_offload_xstats_get_ptr(dev, type); +} +EXPORT_SYMBOL(netdev_offload_xstats_enabled); + +struct netdev_notifier_offload_xstats_ru { + bool used; +}; + +struct netdev_notifier_offload_xstats_rd { + struct rtnl_hw_stats64 stats; + bool used; +}; + +static void netdev_hw_stats64_add(struct rtnl_hw_stats64 *dest, + const struct rtnl_hw_stats64 *src) +{ + dest->rx_packets += src->rx_packets; + dest->tx_packets += src->tx_packets; + dest->rx_bytes += src->rx_bytes; + dest->tx_bytes += src->tx_bytes; + dest->rx_errors += src->rx_errors; + dest->tx_errors += src->tx_errors; + dest->rx_dropped += src->rx_dropped; + dest->tx_dropped += src->tx_dropped; + dest->multicast += src->multicast; +} + +static int netdev_offload_xstats_get_used(struct net_device *dev, + enum netdev_offload_xstats_type type, + bool *p_used, + struct netlink_ext_ack *extack) +{ + struct netdev_notifier_offload_xstats_ru report_used = {}; + struct netdev_notifier_offload_xstats_info info = { + .info.dev = dev, + .info.extack = extack, + .type = type, + .report_used = &report_used, + }; + int rc; + + WARN_ON(!netdev_offload_xstats_enabled(dev, type)); + rc = call_netdevice_notifiers_info(NETDEV_OFFLOAD_XSTATS_REPORT_USED, + &info.info); + *p_used = report_used.used; + return notifier_to_errno(rc); +} + +static int netdev_offload_xstats_get_stats(struct net_device *dev, + enum netdev_offload_xstats_type type, + struct rtnl_hw_stats64 *p_stats, + bool *p_used, + struct netlink_ext_ack *extack) +{ + struct netdev_notifier_offload_xstats_rd report_delta = {}; + struct netdev_notifier_offload_xstats_info info = { + .info.dev = dev, + .info.extack = extack, + .type = type, + .report_delta = &report_delta, + }; + struct rtnl_hw_stats64 *stats; + int rc; + + stats = netdev_offload_xstats_get_ptr(dev, type); + if (WARN_ON(!stats)) + return -EINVAL; + + rc = call_netdevice_notifiers_info(NETDEV_OFFLOAD_XSTATS_REPORT_DELTA, + &info.info); + + /* Cache whatever we got, even if there was an error, otherwise the + * successful stats retrievals would get lost. + */ + netdev_hw_stats64_add(stats, &report_delta.stats); + + if (p_stats) + *p_stats = *stats; + *p_used = report_delta.used; + + return notifier_to_errno(rc); +} + +int netdev_offload_xstats_get(struct net_device *dev, + enum netdev_offload_xstats_type type, + struct rtnl_hw_stats64 *p_stats, bool *p_used, + struct netlink_ext_ack *extack) +{ + ASSERT_RTNL(); + + if (p_stats) + return netdev_offload_xstats_get_stats(dev, type, p_stats, + p_used, extack); + else + return netdev_offload_xstats_get_used(dev, type, p_used, + extack); +} +EXPORT_SYMBOL(netdev_offload_xstats_get); + +void +netdev_offload_xstats_report_delta(struct netdev_notifier_offload_xstats_rd *report_delta, + const struct rtnl_hw_stats64 *stats) +{ + report_delta->used = true; + netdev_hw_stats64_add(&report_delta->stats, stats); +} +EXPORT_SYMBOL(netdev_offload_xstats_report_delta); + +void +netdev_offload_xstats_report_used(struct netdev_notifier_offload_xstats_ru *report_used) +{ + report_used->used = true; +} +EXPORT_SYMBOL(netdev_offload_xstats_report_used); + +void netdev_offload_xstats_push_delta(struct net_device *dev, + enum netdev_offload_xstats_type type, + const struct rtnl_hw_stats64 *p_stats) +{ + struct rtnl_hw_stats64 *stats; + + ASSERT_RTNL(); + + stats = netdev_offload_xstats_get_ptr(dev, type); + if (WARN_ON(!stats)) + return; + + netdev_hw_stats64_add(stats, p_stats); +} +EXPORT_SYMBOL(netdev_offload_xstats_push_delta); + /** * netdev_get_xmit_slave - Get the xmit slave of master device * @dev: device @@ -9143,7 +9427,7 @@ DECLARE_WAIT_QUEUE_HEAD(netdev_unregistering_wq); static void net_set_todo(struct net_device *dev) { list_add_tail(&dev->todo_list, &net_todo_list); - dev_net(dev)->dev_unreg_count++; + atomic_inc(&dev_net(dev)->dev_unreg_count); } static netdev_features_t netdev_sync_upper_features(struct net_device *lower, @@ -9683,8 +9967,10 @@ int register_netdevice(struct net_device *dev) linkwatch_init_dev(dev); dev_init_scheduler(dev); - dev_hold(dev); + + dev_hold_track(dev, &dev->dev_registered_tracker, GFP_KERNEL); list_netdevice(dev); + add_device_randomness(dev->dev_addr, dev->addr_len); /* If the device has permanent device address, driver should @@ -9813,8 +10099,8 @@ int netdev_unregister_timeout_secs __read_mostly = 10; #define WAIT_REFS_MIN_MSECS 1 #define WAIT_REFS_MAX_MSECS 250 /** - * netdev_wait_allrefs - wait until all references are gone. - * @dev: target net_device + * netdev_wait_allrefs_any - wait until all references are gone. + * @list: list of net_devices to wait on * * This is called when unregistering network devices. * @@ -9824,37 +10110,42 @@ int netdev_unregister_timeout_secs __read_mostly = 10; * We can get stuck here if buggy protocols don't correctly * call dev_put. */ -static void netdev_wait_allrefs(struct net_device *dev) +static struct net_device *netdev_wait_allrefs_any(struct list_head *list) { unsigned long rebroadcast_time, warning_time; - int wait = 0, refcnt; - - linkwatch_forget_dev(dev); + struct net_device *dev; + int wait = 0; rebroadcast_time = warning_time = jiffies; - refcnt = netdev_refcnt_read(dev); - while (refcnt != 1) { + list_for_each_entry(dev, list, todo_list) + if (netdev_refcnt_read(dev) == 1) + return dev; + + while (true) { if (time_after(jiffies, rebroadcast_time + 1 * HZ)) { rtnl_lock(); /* Rebroadcast unregister notification */ - call_netdevice_notifiers(NETDEV_UNREGISTER, dev); + list_for_each_entry(dev, list, todo_list) + call_netdevice_notifiers(NETDEV_UNREGISTER, dev); __rtnl_unlock(); rcu_barrier(); rtnl_lock(); - if (test_bit(__LINK_STATE_LINKWATCH_PENDING, - &dev->state)) { - /* We must not have linkwatch events - * pending on unregister. If this - * happens, we simply run the queue - * unscheduled, resulting in a noop - * for this device. - */ - linkwatch_run_queue(); - } + list_for_each_entry(dev, list, todo_list) + if (test_bit(__LINK_STATE_LINKWATCH_PENDING, + &dev->state)) { + /* We must not have linkwatch events + * pending on unregister. If this + * happens, we simply run the queue + * unscheduled, resulting in a noop + * for this device. + */ + linkwatch_run_queue(); + break; + } __rtnl_unlock(); @@ -9869,14 +10160,18 @@ static void netdev_wait_allrefs(struct net_device *dev) wait = min(wait << 1, WAIT_REFS_MAX_MSECS); } - refcnt = netdev_refcnt_read(dev); + list_for_each_entry(dev, list, todo_list) + if (netdev_refcnt_read(dev) == 1) + return dev; - if (refcnt != 1 && - time_after(jiffies, warning_time + + if (time_after(jiffies, warning_time + netdev_unregister_timeout_secs * HZ)) { - pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n", - dev->name, refcnt); - ref_tracker_dir_print(&dev->refcnt_tracker, 10); + list_for_each_entry(dev, list, todo_list) { + pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n", + dev->name, netdev_refcnt_read(dev)); + ref_tracker_dir_print(&dev->refcnt_tracker, 10); + } + warning_time = jiffies; } } @@ -9908,6 +10203,7 @@ static void netdev_wait_allrefs(struct net_device *dev) */ void netdev_run_todo(void) { + struct net_device *dev, *tmp; struct list_head list; #ifdef CONFIG_LOCKDEP struct list_head unlink_list; @@ -9928,26 +10224,24 @@ void netdev_run_todo(void) __rtnl_unlock(); - /* Wait for rcu callbacks to finish before next phase */ if (!list_empty(&list)) rcu_barrier(); - while (!list_empty(&list)) { - struct net_device *dev - = list_first_entry(&list, struct net_device, todo_list); - list_del(&dev->todo_list); - + list_for_each_entry_safe(dev, tmp, &list, todo_list) { if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) { - pr_err("network todo '%s' but state %d\n", - dev->name, dev->reg_state); - dump_stack(); + netdev_WARN(dev, "run_todo but not unregistering\n"); + list_del(&dev->todo_list); continue; } dev->reg_state = NETREG_UNREGISTERED; + linkwatch_forget_dev(dev); + } - netdev_wait_allrefs(dev); + while (!list_empty(&list)) { + dev = netdev_wait_allrefs_any(&list); + list_del(&dev->todo_list); /* paranoia */ BUG_ON(netdev_refcnt_read(dev) != 1); @@ -9963,11 +10257,8 @@ void netdev_run_todo(void) if (dev->needs_free_netdev) free_netdev(dev); - /* Report a network device has been unregistered */ - rtnl_lock(); - dev_net(dev)->dev_unreg_count--; - __rtnl_unlock(); - wake_up(&netdev_unregistering_wq); + if (atomic_dec_and_test(&dev_net(dev)->dev_unreg_count)) + wake_up(&netdev_unregistering_wq); /* Free network device */ kobject_put(&dev->dev.kobj); @@ -10003,6 +10294,25 @@ void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, } EXPORT_SYMBOL(netdev_stats_to_stats64); +struct net_device_core_stats *netdev_core_stats_alloc(struct net_device *dev) +{ + struct net_device_core_stats __percpu *p; + + p = alloc_percpu_gfp(struct net_device_core_stats, + GFP_ATOMIC | __GFP_NOWARN); + + if (p && cmpxchg(&dev->core_stats, NULL, p)) + free_percpu(p); + + /* This READ_ONCE() pairs with the cmpxchg() above */ + p = READ_ONCE(dev->core_stats); + if (!p) + return NULL; + + return this_cpu_ptr(p); +} +EXPORT_SYMBOL(netdev_core_stats_alloc); + /** * dev_get_stats - get network device statistics * @dev: device to get statistics from @@ -10017,6 +10327,7 @@ struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage) { const struct net_device_ops *ops = dev->netdev_ops; + const struct net_device_core_stats __percpu *p; if (ops->ndo_get_stats64) { memset(storage, 0, sizeof(*storage)); @@ -10026,9 +10337,20 @@ struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, } else { netdev_stats_to_stats64(storage, &dev->stats); } - storage->rx_dropped += (unsigned long)atomic_long_read(&dev->rx_dropped); - storage->tx_dropped += (unsigned long)atomic_long_read(&dev->tx_dropped); - storage->rx_nohandler += (unsigned long)atomic_long_read(&dev->rx_nohandler); + + /* This READ_ONCE() pairs with the write in netdev_core_stats_alloc() */ + p = READ_ONCE(dev->core_stats); + if (p) { + const struct net_device_core_stats *core_stats; + int i; + + for_each_possible_cpu(i) { + core_stats = per_cpu_ptr(p, i); + storage->rx_dropped += local_read(&core_stats->rx_dropped); + storage->tx_dropped += local_read(&core_stats->tx_dropped); + storage->rx_nohandler += local_read(&core_stats->rx_nohandler); + } + } return storage; } EXPORT_SYMBOL(dev_get_stats); @@ -10172,7 +10494,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, dev->pcpu_refcnt = alloc_percpu(int); if (!dev->pcpu_refcnt) goto free_dev; - dev_hold(dev); + __dev_hold(dev); #else refcount_set(&dev->dev_refcnt, 1); #endif @@ -10290,6 +10612,8 @@ void free_netdev(struct net_device *dev) free_percpu(dev->pcpu_refcnt); dev->pcpu_refcnt = NULL; #endif + free_percpu(dev->core_stats); + dev->core_stats = NULL; free_percpu(dev->xdp_bulkq); dev->xdp_bulkq = NULL; @@ -10409,6 +10733,8 @@ void unregister_netdevice_many(struct list_head *head) dev_xdp_uninstall(dev); + netdev_offload_xstats_disable_all(dev); + /* Notify protocols, that we are about to destroy * this device. They should clean all the things. */ @@ -10449,7 +10775,7 @@ void unregister_netdevice_many(struct list_head *head) synchronize_net(); list_for_each_entry(dev, head, unreg_list) { - dev_put(dev); + dev_put_track(dev, &dev->dev_registered_tracker); net_set_todo(dev); } @@ -10674,11 +11000,11 @@ static int dev_cpu_dead(unsigned int oldcpu) /* Process offline CPU's input_pkt_queue */ while ((skb = __skb_dequeue(&oldsd->process_queue))) { - netif_rx_ni(skb); + netif_rx(skb); input_queue_head_incr(oldsd); } while ((skb = skb_dequeue(&oldsd->input_pkt_queue))) { - netif_rx_ni(skb); + netif_rx(skb); input_queue_head_incr(oldsd); } @@ -10732,8 +11058,7 @@ static int __net_init netdev_init(struct net *net) BUILD_BUG_ON(GRO_HASH_BUCKETS > 8 * sizeof_field(struct napi_struct, gro_bitmask)); - if (net != &init_net) - INIT_LIST_HEAD(&net->dev_base_head); + INIT_LIST_HEAD(&net->dev_base_head); net->dev_name_head = netdev_create_hash(); if (net->dev_name_head == NULL) @@ -10849,14 +11174,14 @@ static struct pernet_operations __net_initdata netdev_net_ops = { .exit = netdev_exit, }; -static void __net_exit default_device_exit(struct net *net) +static void __net_exit default_device_exit_net(struct net *net) { struct net_device *dev, *aux; /* * Push all migratable network devices back to the * initial network namespace */ - rtnl_lock(); + ASSERT_RTNL(); for_each_netdev_safe(net, dev, aux) { int err; char fb_name[IFNAMSIZ]; @@ -10880,35 +11205,6 @@ static void __net_exit default_device_exit(struct net *net) BUG(); } } - rtnl_unlock(); -} - -static void __net_exit rtnl_lock_unregistering(struct list_head *net_list) -{ - /* Return with the rtnl_lock held when there are no network - * devices unregistering in any network namespace in net_list. - */ - struct net *net; - bool unregistering; - DEFINE_WAIT_FUNC(wait, woken_wake_function); - - add_wait_queue(&netdev_unregistering_wq, &wait); - for (;;) { - unregistering = false; - rtnl_lock(); - list_for_each_entry(net, net_list, exit_list) { - if (net->dev_unreg_count > 0) { - unregistering = true; - break; - } - } - if (!unregistering) - break; - __rtnl_unlock(); - - wait_woken(&wait, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); - } - remove_wait_queue(&netdev_unregistering_wq, &wait); } static void __net_exit default_device_exit_batch(struct list_head *net_list) @@ -10922,18 +11218,12 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list) struct net *net; LIST_HEAD(dev_kill_list); - /* To prevent network device cleanup code from dereferencing - * loopback devices or network devices that have been freed - * wait here for all pending unregistrations to complete, - * before unregistring the loopback device and allowing the - * network namespace be freed. - * - * The netdev todo list containing all network devices - * unregistrations that happen in default_device_exit_batch - * will run in the rtnl_unlock() at the end of - * default_device_exit_batch. - */ - rtnl_lock_unregistering(net_list); + rtnl_lock(); + list_for_each_entry(net, net_list, exit_list) { + default_device_exit_net(net); + cond_resched(); + } + list_for_each_entry(net, net_list, exit_list) { for_each_netdev_reverse(net, dev) { if (dev->rtnl_link_ops && dev->rtnl_link_ops->dellink) @@ -10947,7 +11237,6 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list) } static struct pernet_operations __net_initdata default_device_ops = { - .exit = default_device_exit, .exit_batch = default_device_exit_batch, }; diff --git a/net/core/devlink.c b/net/core/devlink.c index fcd9f6d85cf1..aeca13b6e57b 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -225,6 +225,33 @@ struct devlink *__must_check devlink_try_get(struct devlink *devlink) return NULL; } +void devl_assert_locked(struct devlink *devlink) +{ + lockdep_assert_held(&devlink->lock); +} +EXPORT_SYMBOL_GPL(devl_assert_locked); + +#ifdef CONFIG_LOCKDEP +/* For use in conjunction with LOCKDEP only e.g. rcu_dereference_protected() */ +bool devl_lock_is_held(struct devlink *devlink) +{ + return lockdep_is_held(&devlink->lock); +} +EXPORT_SYMBOL_GPL(devl_lock_is_held); +#endif + +void devl_lock(struct devlink *devlink) +{ + mutex_lock(&devlink->lock); +} +EXPORT_SYMBOL_GPL(devl_lock); + +void devl_unlock(struct devlink *devlink) +{ + mutex_unlock(&devlink->lock); +} +EXPORT_SYMBOL_GPL(devl_unlock); + static struct devlink *devlink_get_from_attrs(struct net *net, struct nlattr **attrs) { @@ -1541,35 +1568,20 @@ static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb, return 0; } -static int devlink_port_split(struct devlink *devlink, u32 port_index, - u32 count, struct netlink_ext_ack *extack) - -{ - if (devlink->ops->port_split) - return devlink->ops->port_split(devlink, port_index, count, - extack); - return -EOPNOTSUPP; -} - static int devlink_nl_cmd_port_split_doit(struct sk_buff *skb, struct genl_info *info) { + struct devlink_port *devlink_port = info->user_ptr[1]; struct devlink *devlink = info->user_ptr[0]; - struct devlink_port *devlink_port; - u32 port_index; u32 count; - if (!info->attrs[DEVLINK_ATTR_PORT_INDEX] || - !info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT]) + if (!info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT]) return -EINVAL; + if (!devlink->ops->port_split) + return -EOPNOTSUPP; - devlink_port = devlink_port_get_from_info(devlink, info); - port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]); count = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT]); - if (IS_ERR(devlink_port)) - return -EINVAL; - if (!devlink_port->attrs.splittable) { /* Split ports cannot be split. */ if (devlink_port->attrs.split) @@ -1584,29 +1596,19 @@ static int devlink_nl_cmd_port_split_doit(struct sk_buff *skb, return -EINVAL; } - return devlink_port_split(devlink, port_index, count, info->extack); -} - -static int devlink_port_unsplit(struct devlink *devlink, u32 port_index, - struct netlink_ext_ack *extack) - -{ - if (devlink->ops->port_unsplit) - return devlink->ops->port_unsplit(devlink, port_index, extack); - return -EOPNOTSUPP; + return devlink->ops->port_split(devlink, devlink_port, count, + info->extack); } static int devlink_nl_cmd_port_unsplit_doit(struct sk_buff *skb, struct genl_info *info) { + struct devlink_port *devlink_port = info->user_ptr[1]; struct devlink *devlink = info->user_ptr[0]; - u32 port_index; - if (!info->attrs[DEVLINK_ATTR_PORT_INDEX]) - return -EINVAL; - - port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]); - return devlink_port_unsplit(devlink, port_index, info->extack); + if (!devlink->ops->port_unsplit) + return -EOPNOTSUPP; + return devlink->ops->port_unsplit(devlink, devlink_port, info->extack); } static int devlink_port_new_notifiy(struct devlink *devlink, @@ -2866,15 +2868,11 @@ static int devlink_rate_nodes_check(struct devlink *devlink, u16 mode, { struct devlink_rate *devlink_rate; - /* Take the lock to sync with devlink_rate_nodes_destroy() */ - mutex_lock(&devlink->lock); list_for_each_entry(devlink_rate, &devlink->rate_list, list) if (devlink_rate_is_node(devlink_rate)) { - mutex_unlock(&devlink->lock); NL_SET_ERR_MSG_MOD(extack, "Rate node(s) exists."); return -EBUSY; } - mutex_unlock(&devlink->lock); return 0; } @@ -8645,14 +8643,14 @@ static const struct genl_small_ops devlink_nl_ops[] = { .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .doit = devlink_nl_cmd_port_split_doit, .flags = GENL_ADMIN_PERM, - .internal_flags = DEVLINK_NL_FLAG_NO_LOCK, + .internal_flags = DEVLINK_NL_FLAG_NEED_PORT, }, { .cmd = DEVLINK_CMD_PORT_UNSPLIT, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .doit = devlink_nl_cmd_port_unsplit_doit, .flags = GENL_ADMIN_PERM, - .internal_flags = DEVLINK_NL_FLAG_NO_LOCK, + .internal_flags = DEVLINK_NL_FLAG_NEED_PORT, }, { .cmd = DEVLINK_CMD_PORT_NEW, @@ -8733,14 +8731,12 @@ static const struct genl_small_ops devlink_nl_ops[] = { .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .doit = devlink_nl_cmd_eswitch_get_doit, .flags = GENL_ADMIN_PERM, - .internal_flags = DEVLINK_NL_FLAG_NO_LOCK, }, { .cmd = DEVLINK_CMD_ESWITCH_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .doit = devlink_nl_cmd_eswitch_set_doit, .flags = GENL_ADMIN_PERM, - .internal_flags = DEVLINK_NL_FLAG_NO_LOCK, }, { .cmd = DEVLINK_CMD_DPIPE_TABLE_GET, @@ -9249,6 +9245,32 @@ static void devlink_port_type_warn_cancel(struct devlink_port *devlink_port) cancel_delayed_work_sync(&devlink_port->type_warn_dw); } +int devl_port_register(struct devlink *devlink, + struct devlink_port *devlink_port, + unsigned int port_index) +{ + lockdep_assert_held(&devlink->lock); + + if (devlink_port_index_exists(devlink, port_index)) + return -EEXIST; + + WARN_ON(devlink_port->devlink); + devlink_port->devlink = devlink; + devlink_port->index = port_index; + spin_lock_init(&devlink_port->type_lock); + INIT_LIST_HEAD(&devlink_port->reporter_list); + mutex_init(&devlink_port->reporters_lock); + list_add_tail(&devlink_port->list, &devlink->port_list); + INIT_LIST_HEAD(&devlink_port->param_list); + INIT_LIST_HEAD(&devlink_port->region_list); + + INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn); + devlink_port_type_warn_schedule(devlink_port); + devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); + return 0; +} +EXPORT_SYMBOL_GPL(devl_port_register); + /** * devlink_port_register - Register devlink port * @@ -9266,29 +9288,28 @@ int devlink_port_register(struct devlink *devlink, struct devlink_port *devlink_port, unsigned int port_index) { - mutex_lock(&devlink->lock); - if (devlink_port_index_exists(devlink, port_index)) { - mutex_unlock(&devlink->lock); - return -EEXIST; - } + int err; - WARN_ON(devlink_port->devlink); - devlink_port->devlink = devlink; - devlink_port->index = port_index; - spin_lock_init(&devlink_port->type_lock); - INIT_LIST_HEAD(&devlink_port->reporter_list); - mutex_init(&devlink_port->reporters_lock); - list_add_tail(&devlink_port->list, &devlink->port_list); - INIT_LIST_HEAD(&devlink_port->param_list); - INIT_LIST_HEAD(&devlink_port->region_list); + mutex_lock(&devlink->lock); + err = devl_port_register(devlink, devlink_port, port_index); mutex_unlock(&devlink->lock); - INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn); - devlink_port_type_warn_schedule(devlink_port); - devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); - return 0; + return err; } EXPORT_SYMBOL_GPL(devlink_port_register); +void devl_port_unregister(struct devlink_port *devlink_port) +{ + lockdep_assert_held(&devlink_port->devlink->lock); + + devlink_port_type_warn_cancel(devlink_port); + devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL); + list_del(&devlink_port->list); + WARN_ON(!list_empty(&devlink_port->reporter_list)); + WARN_ON(!list_empty(&devlink_port->region_list)); + mutex_destroy(&devlink_port->reporters_lock); +} +EXPORT_SYMBOL_GPL(devl_port_unregister); + /** * devlink_port_unregister - Unregister devlink port * @@ -9298,14 +9319,9 @@ void devlink_port_unregister(struct devlink_port *devlink_port) { struct devlink *devlink = devlink_port->devlink; - devlink_port_type_warn_cancel(devlink_port); - devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL); mutex_lock(&devlink->lock); - list_del(&devlink_port->list); + devl_port_unregister(devlink_port); mutex_unlock(&devlink->lock); - WARN_ON(!list_empty(&devlink_port->reporter_list)); - WARN_ON(!list_empty(&devlink_port->region_list)); - mutex_destroy(&devlink_port->reporters_lock); } EXPORT_SYMBOL_GPL(devlink_port_unregister); @@ -9526,30 +9542,26 @@ void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port, u32 contro EXPORT_SYMBOL_GPL(devlink_port_attrs_pci_sf_set); /** - * devlink_rate_leaf_create - create devlink rate leaf - * + * devl_rate_leaf_create - create devlink rate leaf * @devlink_port: devlink port object to create rate object on * @priv: driver private data * * Create devlink rate object of type leaf on provided @devlink_port. - * Throws call trace if @devlink_port already has a devlink rate object. - * - * Context: Takes and release devlink->lock . - * - * Return: -ENOMEM if failed to allocate rate object, 0 otherwise. */ -int -devlink_rate_leaf_create(struct devlink_port *devlink_port, void *priv) +int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv) { struct devlink *devlink = devlink_port->devlink; struct devlink_rate *devlink_rate; + devl_assert_locked(devlink_port->devlink); + + if (WARN_ON(devlink_port->devlink_rate)) + return -EBUSY; + devlink_rate = kzalloc(sizeof(*devlink_rate), GFP_KERNEL); if (!devlink_rate) return -ENOMEM; - mutex_lock(&devlink->lock); - WARN_ON(devlink_port->devlink_rate); devlink_rate->type = DEVLINK_RATE_TYPE_LEAF; devlink_rate->devlink = devlink; devlink_rate->devlink_port = devlink_port; @@ -9557,12 +9569,42 @@ devlink_rate_leaf_create(struct devlink_port *devlink_port, void *priv) list_add_tail(&devlink_rate->list, &devlink->rate_list); devlink_port->devlink_rate = devlink_rate; devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW); - mutex_unlock(&devlink->lock); return 0; } +EXPORT_SYMBOL_GPL(devl_rate_leaf_create); + +int +devlink_rate_leaf_create(struct devlink_port *devlink_port, void *priv) +{ + struct devlink *devlink = devlink_port->devlink; + int ret; + + mutex_lock(&devlink->lock); + ret = devl_rate_leaf_create(devlink_port, priv); + mutex_unlock(&devlink->lock); + + return ret; +} EXPORT_SYMBOL_GPL(devlink_rate_leaf_create); +void devl_rate_leaf_destroy(struct devlink_port *devlink_port) +{ + struct devlink_rate *devlink_rate = devlink_port->devlink_rate; + + devl_assert_locked(devlink_port->devlink); + if (!devlink_rate) + return; + + devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_DEL); + if (devlink_rate->parent) + refcount_dec(&devlink_rate->parent->refcnt); + list_del(&devlink_rate->list); + devlink_port->devlink_rate = NULL; + kfree(devlink_rate); +} +EXPORT_SYMBOL_GPL(devl_rate_leaf_destroy); + /** * devlink_rate_leaf_destroy - destroy devlink rate leaf * @@ -9579,32 +9621,25 @@ void devlink_rate_leaf_destroy(struct devlink_port *devlink_port) return; mutex_lock(&devlink->lock); - devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_DEL); - if (devlink_rate->parent) - refcount_dec(&devlink_rate->parent->refcnt); - list_del(&devlink_rate->list); - devlink_port->devlink_rate = NULL; + devl_rate_leaf_destroy(devlink_port); mutex_unlock(&devlink->lock); - kfree(devlink_rate); } EXPORT_SYMBOL_GPL(devlink_rate_leaf_destroy); /** - * devlink_rate_nodes_destroy - destroy all devlink rate nodes on device - * + * devl_rate_nodes_destroy - destroy all devlink rate nodes on device * @devlink: devlink instance * * Unset parent for all rate objects and destroy all rate nodes * on specified device. - * - * Context: Takes and release devlink->lock . */ -void devlink_rate_nodes_destroy(struct devlink *devlink) +void devl_rate_nodes_destroy(struct devlink *devlink) { static struct devlink_rate *devlink_rate, *tmp; const struct devlink_ops *ops = devlink->ops; - mutex_lock(&devlink->lock); + devl_assert_locked(devlink); + list_for_each_entry(devlink_rate, &devlink->rate_list, list) { if (!devlink_rate->parent) continue; @@ -9625,6 +9660,23 @@ void devlink_rate_nodes_destroy(struct devlink *devlink) kfree(devlink_rate); } } +} +EXPORT_SYMBOL_GPL(devl_rate_nodes_destroy); + +/** + * devlink_rate_nodes_destroy - destroy all devlink rate nodes on device + * + * @devlink: devlink instance + * + * Unset parent for all rate objects and destroy all rate nodes + * on specified device. + * + * Context: Takes and release devlink->lock . + */ +void devlink_rate_nodes_destroy(struct devlink *devlink) +{ + mutex_lock(&devlink->lock); + devl_rate_nodes_destroy(devlink); mutex_unlock(&devlink->lock); } EXPORT_SYMBOL_GPL(devlink_rate_nodes_destroy); diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index d5dc6be2522c..b89e3e95bffc 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -48,10 +48,22 @@ static int trace_state = TRACE_OFF; static bool monitor_hw; +#undef EM +#undef EMe + +#define EM(a, b) [a] = #b, +#define EMe(a, b) [a] = #b + +/* drop_reasons is used to translate 'enum skb_drop_reason' to string, + * which is reported to user space. + */ +static const char * const drop_reasons[] = { + TRACE_SKB_DROP_REASON +}; + /* net_dm_mutex * * An overall lock guarding every operation coming from userspace. - * It also guards the global 'hw_stats_list' list. */ static DEFINE_MUTEX(net_dm_mutex); @@ -87,11 +99,9 @@ struct per_cpu_dm_data { }; struct dm_hw_stat_delta { - struct net_device *dev; unsigned long last_rx; - struct list_head list; - struct rcu_head rcu; unsigned long last_drop_val; + struct rcu_head rcu; }; static struct genl_family net_drop_monitor_family; @@ -102,7 +112,6 @@ static DEFINE_PER_CPU(struct per_cpu_dm_data, dm_hw_cpu_data); static int dm_hit_limit = 64; static int dm_delay = 1; static unsigned long dm_hw_check_delta = 2*HZ; -static LIST_HEAD(hw_stats_list); static enum net_dm_alert_mode net_dm_alert_mode = NET_DM_ALERT_MODE_SUMMARY; static u32 net_dm_trunc_len; @@ -126,6 +135,7 @@ struct net_dm_skb_cb { struct devlink_trap_metadata *hw_metadata; void *pc; }; + enum skb_drop_reason reason; }; #define NET_DM_SKB_CB(__skb) ((struct net_dm_skb_cb *)&((__skb)->cb[0])) @@ -273,33 +283,27 @@ static void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, static void trace_napi_poll_hit(void *ignore, struct napi_struct *napi, int work, int budget) { - struct dm_hw_stat_delta *new_stat; - + struct net_device *dev = napi->dev; + struct dm_hw_stat_delta *stat; /* * Don't check napi structures with no associated device */ - if (!napi->dev) + if (!dev) return; rcu_read_lock(); - list_for_each_entry_rcu(new_stat, &hw_stats_list, list) { - struct net_device *dev; - + stat = rcu_dereference(dev->dm_private); + if (stat) { /* * only add a note to our monitor buffer if: - * 1) this is the dev we received on - * 2) its after the last_rx delta - * 3) our rx_dropped count has gone up + * 1) its after the last_rx delta + * 2) our rx_dropped count has gone up */ - /* Paired with WRITE_ONCE() in dropmon_net_event() */ - dev = READ_ONCE(new_stat->dev); - if ((dev == napi->dev) && - (time_after(jiffies, new_stat->last_rx + dm_hw_check_delta)) && - (napi->dev->stats.rx_dropped != new_stat->last_drop_val)) { + if (time_after(jiffies, stat->last_rx + dm_hw_check_delta) && + (dev->stats.rx_dropped != stat->last_drop_val)) { trace_drop_common(NULL, NULL); - new_stat->last_drop_val = napi->dev->stats.rx_dropped; - new_stat->last_rx = jiffies; - break; + stat->last_drop_val = dev->stats.rx_dropped; + stat->last_rx = jiffies; } } rcu_read_unlock(); @@ -502,6 +506,7 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore, { ktime_t tstamp = ktime_get_real(); struct per_cpu_dm_data *data; + struct net_dm_skb_cb *cb; struct sk_buff *nskb; unsigned long flags; @@ -512,7 +517,11 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore, if (!nskb) return; - NET_DM_SKB_CB(nskb)->pc = location; + if ((unsigned int)reason >= SKB_DROP_REASON_MAX) + reason = SKB_DROP_REASON_NOT_SPECIFIED; + cb = NET_DM_SKB_CB(nskb); + cb->reason = reason; + cb->pc = location; /* Override the timestamp because we care about the time when the * packet was dropped. */ @@ -557,7 +566,8 @@ static size_t net_dm_in_port_size(void) #define NET_DM_MAX_SYMBOL_LEN 40 -static size_t net_dm_packet_report_size(size_t payload_len) +static size_t net_dm_packet_report_size(size_t payload_len, + enum skb_drop_reason reason) { size_t size; @@ -578,6 +588,8 @@ static size_t net_dm_packet_report_size(size_t payload_len) nla_total_size(sizeof(u32)) + /* NET_DM_ATTR_PROTO */ nla_total_size(sizeof(u16)) + + /* NET_DM_ATTR_REASON */ + nla_total_size(strlen(drop_reasons[reason]) + 1) + /* NET_DM_ATTR_PAYLOAD */ nla_total_size(payload_len); } @@ -610,7 +622,7 @@ static int net_dm_packet_report_in_port_put(struct sk_buff *msg, int ifindex, static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb, size_t payload_len) { - u64 pc = (u64)(uintptr_t) NET_DM_SKB_CB(skb)->pc; + struct net_dm_skb_cb *cb = NET_DM_SKB_CB(skb); char buf[NET_DM_MAX_SYMBOL_LEN]; struct nlattr *attr; void *hdr; @@ -624,10 +636,15 @@ static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb, if (nla_put_u16(msg, NET_DM_ATTR_ORIGIN, NET_DM_ORIGIN_SW)) goto nla_put_failure; - if (nla_put_u64_64bit(msg, NET_DM_ATTR_PC, pc, NET_DM_ATTR_PAD)) + if (nla_put_u64_64bit(msg, NET_DM_ATTR_PC, (u64)(uintptr_t)cb->pc, + NET_DM_ATTR_PAD)) goto nla_put_failure; - snprintf(buf, sizeof(buf), "%pS", NET_DM_SKB_CB(skb)->pc); + if (nla_put_string(msg, NET_DM_ATTR_REASON, + drop_reasons[cb->reason])) + goto nla_put_failure; + + snprintf(buf, sizeof(buf), "%pS", cb->pc); if (nla_put_string(msg, NET_DM_ATTR_SYMBOL, buf)) goto nla_put_failure; @@ -683,7 +700,9 @@ static void net_dm_packet_report(struct sk_buff *skb) if (net_dm_trunc_len) payload_len = min_t(size_t, net_dm_trunc_len, payload_len); - msg = nlmsg_new(net_dm_packet_report_size(payload_len), GFP_KERNEL); + msg = nlmsg_new(net_dm_packet_report_size(payload_len, + NET_DM_SKB_CB(skb)->reason), + GFP_KERNEL); if (!msg) goto out; @@ -1169,7 +1188,6 @@ static int net_dm_trace_on_set(struct netlink_ext_ack *extack) static void net_dm_trace_off_set(void) { - struct dm_hw_stat_delta *new_stat, *temp; const struct net_dm_alert_ops *ops; int cpu; @@ -1193,13 +1211,6 @@ static void net_dm_trace_off_set(void) consume_skb(skb); } - list_for_each_entry_safe(new_stat, temp, &hw_stats_list, list) { - if (new_stat->dev == NULL) { - list_del_rcu(&new_stat->list); - kfree_rcu(new_stat, rcu); - } - } - module_put(THIS_MODULE); } @@ -1560,41 +1571,28 @@ static int dropmon_net_event(struct notifier_block *ev_block, unsigned long event, void *ptr) { struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct dm_hw_stat_delta *new_stat = NULL; - struct dm_hw_stat_delta *tmp; + struct dm_hw_stat_delta *stat; switch (event) { case NETDEV_REGISTER: - new_stat = kzalloc(sizeof(struct dm_hw_stat_delta), GFP_KERNEL); + if (WARN_ON_ONCE(rtnl_dereference(dev->dm_private))) + break; + stat = kzalloc(sizeof(*stat), GFP_KERNEL); + if (!stat) + break; - if (!new_stat) - goto out; + stat->last_rx = jiffies; + rcu_assign_pointer(dev->dm_private, stat); - new_stat->dev = dev; - new_stat->last_rx = jiffies; - mutex_lock(&net_dm_mutex); - list_add_rcu(&new_stat->list, &hw_stats_list); - mutex_unlock(&net_dm_mutex); break; case NETDEV_UNREGISTER: - mutex_lock(&net_dm_mutex); - list_for_each_entry_safe(new_stat, tmp, &hw_stats_list, list) { - if (new_stat->dev == dev) { - - /* Paired with READ_ONCE() in trace_napi_poll_hit() */ - WRITE_ONCE(new_stat->dev, NULL); - - if (trace_state == TRACE_OFF) { - list_del_rcu(&new_stat->list); - kfree_rcu(new_stat, rcu); - break; - } - } + stat = rtnl_dereference(dev->dm_private); + if (stat) { + rcu_assign_pointer(dev->dm_private, NULL); + kfree_rcu(stat, rcu); } - mutex_unlock(&net_dm_mutex); break; } -out: return NOTIFY_DONE; } diff --git a/net/core/filter.c b/net/core/filter.c index 9eb785842258..a7044e98765e 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2107,7 +2107,7 @@ static inline int __bpf_tx_skb(struct net_device *dev, struct sk_buff *skb) } skb->dev = dev; - skb->tstamp = 0; + skb_clear_tstamp(skb); dev_xmit_recursion_inc(); ret = dev_queue_xmit(skb); @@ -2176,7 +2176,7 @@ static int bpf_out_neigh_v6(struct net *net, struct sk_buff *skb, } skb->dev = dev; - skb->tstamp = 0; + skb_clear_tstamp(skb); if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) { skb = skb_expand_head(skb, hh_len); @@ -2274,7 +2274,7 @@ static int bpf_out_neigh_v4(struct net *net, struct sk_buff *skb, } skb->dev = dev; - skb->tstamp = 0; + skb_clear_tstamp(skb); if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) { skb = skb_expand_head(skb, hh_len); @@ -2603,7 +2603,7 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start, * account for the headroom. */ bytes_sg_total = start - offset + bytes; - if (!test_bit(i, &msg->sg.copy) && bytes_sg_total <= len) + if (!test_bit(i, msg->sg.copy) && bytes_sg_total <= len) goto out; /* At this point we need to linearize multiple scatterlist @@ -2812,7 +2812,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, /* Place newly allocated data buffer */ sk_mem_charge(msg->sk, len); msg->sg.size += len; - __clear_bit(new, &msg->sg.copy); + __clear_bit(new, msg->sg.copy); sg_set_page(&msg->sg.data[new], page, len + copy, 0); if (rsge.length) { get_page(sg_page(&rsge)); @@ -3786,6 +3786,28 @@ static const struct bpf_func_proto sk_skb_change_head_proto = { .arg2_type = ARG_ANYTHING, .arg3_type = ARG_ANYTHING, }; + +BPF_CALL_1(bpf_xdp_get_buff_len, struct xdp_buff*, xdp) +{ + return xdp_get_buff_len(xdp); +} + +static const struct bpf_func_proto bpf_xdp_get_buff_len_proto = { + .func = bpf_xdp_get_buff_len, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +}; + +BTF_ID_LIST_SINGLE(bpf_xdp_get_buff_len_bpf_ids, struct, xdp_buff) + +const struct bpf_func_proto bpf_xdp_get_buff_len_trace_proto = { + .func = bpf_xdp_get_buff_len, + .gpl_only = false, + .arg1_type = ARG_PTR_TO_BTF_ID, + .arg1_btf_id = &bpf_xdp_get_buff_len_bpf_ids[0], +}; + static unsigned long xdp_get_metalen(const struct xdp_buff *xdp) { return xdp_data_meta_unsupported(xdp) ? 0 : @@ -3820,11 +3842,208 @@ static const struct bpf_func_proto bpf_xdp_adjust_head_proto = { .arg2_type = ARG_ANYTHING, }; +static void bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off, + void *buf, unsigned long len, bool flush) +{ + unsigned long ptr_len, ptr_off = 0; + skb_frag_t *next_frag, *end_frag; + struct skb_shared_info *sinfo; + void *src, *dst; + u8 *ptr_buf; + + if (likely(xdp->data_end - xdp->data >= off + len)) { + src = flush ? buf : xdp->data + off; + dst = flush ? xdp->data + off : buf; + memcpy(dst, src, len); + return; + } + + sinfo = xdp_get_shared_info_from_buff(xdp); + end_frag = &sinfo->frags[sinfo->nr_frags]; + next_frag = &sinfo->frags[0]; + + ptr_len = xdp->data_end - xdp->data; + ptr_buf = xdp->data; + + while (true) { + if (off < ptr_off + ptr_len) { + unsigned long copy_off = off - ptr_off; + unsigned long copy_len = min(len, ptr_len - copy_off); + + src = flush ? buf : ptr_buf + copy_off; + dst = flush ? ptr_buf + copy_off : buf; + memcpy(dst, src, copy_len); + + off += copy_len; + len -= copy_len; + buf += copy_len; + } + + if (!len || next_frag == end_frag) + break; + + ptr_off += ptr_len; + ptr_buf = skb_frag_address(next_frag); + ptr_len = skb_frag_size(next_frag); + next_frag++; + } +} + +static void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len) +{ + struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); + u32 size = xdp->data_end - xdp->data; + void *addr = xdp->data; + int i; + + if (unlikely(offset > 0xffff || len > 0xffff)) + return ERR_PTR(-EFAULT); + + if (offset + len > xdp_get_buff_len(xdp)) + return ERR_PTR(-EINVAL); + + if (offset < size) /* linear area */ + goto out; + + offset -= size; + for (i = 0; i < sinfo->nr_frags; i++) { /* paged area */ + u32 frag_size = skb_frag_size(&sinfo->frags[i]); + + if (offset < frag_size) { + addr = skb_frag_address(&sinfo->frags[i]); + size = frag_size; + break; + } + offset -= frag_size; + } +out: + return offset + len < size ? addr + offset : NULL; +} + +BPF_CALL_4(bpf_xdp_load_bytes, struct xdp_buff *, xdp, u32, offset, + void *, buf, u32, len) +{ + void *ptr; + + ptr = bpf_xdp_pointer(xdp, offset, len); + if (IS_ERR(ptr)) + return PTR_ERR(ptr); + + if (!ptr) + bpf_xdp_copy_buf(xdp, offset, buf, len, false); + else + memcpy(buf, ptr, len); + + return 0; +} + +static const struct bpf_func_proto bpf_xdp_load_bytes_proto = { + .func = bpf_xdp_load_bytes, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_PTR_TO_UNINIT_MEM, + .arg4_type = ARG_CONST_SIZE, +}; + +BPF_CALL_4(bpf_xdp_store_bytes, struct xdp_buff *, xdp, u32, offset, + void *, buf, u32, len) +{ + void *ptr; + + ptr = bpf_xdp_pointer(xdp, offset, len); + if (IS_ERR(ptr)) + return PTR_ERR(ptr); + + if (!ptr) + bpf_xdp_copy_buf(xdp, offset, buf, len, true); + else + memcpy(ptr, buf, len); + + return 0; +} + +static const struct bpf_func_proto bpf_xdp_store_bytes_proto = { + .func = bpf_xdp_store_bytes, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_PTR_TO_UNINIT_MEM, + .arg4_type = ARG_CONST_SIZE, +}; + +static int bpf_xdp_frags_increase_tail(struct xdp_buff *xdp, int offset) +{ + struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); + skb_frag_t *frag = &sinfo->frags[sinfo->nr_frags - 1]; + struct xdp_rxq_info *rxq = xdp->rxq; + unsigned int tailroom; + + if (!rxq->frag_size || rxq->frag_size > xdp->frame_sz) + return -EOPNOTSUPP; + + tailroom = rxq->frag_size - skb_frag_size(frag) - skb_frag_off(frag); + if (unlikely(offset > tailroom)) + return -EINVAL; + + memset(skb_frag_address(frag) + skb_frag_size(frag), 0, offset); + skb_frag_size_add(frag, offset); + sinfo->xdp_frags_size += offset; + + return 0; +} + +static int bpf_xdp_frags_shrink_tail(struct xdp_buff *xdp, int offset) +{ + struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); + int i, n_frags_free = 0, len_free = 0; + + if (unlikely(offset > (int)xdp_get_buff_len(xdp) - ETH_HLEN)) + return -EINVAL; + + for (i = sinfo->nr_frags - 1; i >= 0 && offset > 0; i--) { + skb_frag_t *frag = &sinfo->frags[i]; + int shrink = min_t(int, offset, skb_frag_size(frag)); + + len_free += shrink; + offset -= shrink; + + if (skb_frag_size(frag) == shrink) { + struct page *page = skb_frag_page(frag); + + __xdp_return(page_address(page), &xdp->rxq->mem, + false, NULL); + n_frags_free++; + } else { + skb_frag_size_sub(frag, shrink); + break; + } + } + sinfo->nr_frags -= n_frags_free; + sinfo->xdp_frags_size -= len_free; + + if (unlikely(!sinfo->nr_frags)) { + xdp_buff_clear_frags_flag(xdp); + xdp->data_end -= offset; + } + + return 0; +} + BPF_CALL_2(bpf_xdp_adjust_tail, struct xdp_buff *, xdp, int, offset) { void *data_hard_end = xdp_data_hard_end(xdp); /* use xdp->frame_sz */ void *data_end = xdp->data_end + offset; + if (unlikely(xdp_buff_has_frags(xdp))) { /* non-linear xdp buff */ + if (offset < 0) + return bpf_xdp_frags_shrink_tail(xdp, -offset); + + return bpf_xdp_frags_increase_tail(xdp, offset); + } + /* Notice that xdp_data_hard_end have reserved some tailroom */ if (unlikely(data_end > data_hard_end)) return -EINVAL; @@ -4050,6 +4269,14 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, struct bpf_redirect_info *ri = this_cpu_ptr(&bpf_redirect_info); enum bpf_map_type map_type = ri->map_type; + /* XDP_REDIRECT is not fully supported yet for xdp frags since + * not all XDP capable drivers can map non-linear xdp_frame in + * ndo_xdp_xmit. + */ + if (unlikely(xdp_buff_has_frags(xdp) && + map_type != BPF_MAP_TYPE_CPUMAP)) + return -EOPNOTSUPP; + if (map_type == BPF_MAP_TYPE_XSKMAP) return __xdp_do_redirect_xsk(ri, dev, xdp, xdp_prog); @@ -4593,10 +4820,12 @@ static const struct bpf_func_proto bpf_sk_ancestor_cgroup_id_proto = { }; #endif -static unsigned long bpf_xdp_copy(void *dst_buff, const void *src_buff, +static unsigned long bpf_xdp_copy(void *dst, const void *ctx, unsigned long off, unsigned long len) { - memcpy(dst_buff, src_buff + off, len); + struct xdp_buff *xdp = (struct xdp_buff *)ctx; + + bpf_xdp_copy_buf(xdp, off, dst, len, false); return 0; } @@ -4607,11 +4836,11 @@ BPF_CALL_5(bpf_xdp_event_output, struct xdp_buff *, xdp, struct bpf_map *, map, if (unlikely(flags & ~(BPF_F_CTXLEN_MASK | BPF_F_INDEX_MASK))) return -EINVAL; - if (unlikely(!xdp || - xdp_size > (unsigned long)(xdp->data_end - xdp->data))) + + if (unlikely(!xdp || xdp_size > xdp_get_buff_len(xdp))) return -EFAULT; - return bpf_event_output(map, flags, meta, meta_size, xdp->data, + return bpf_event_output(map, flags, meta, meta_size, xdp, xdp_size, bpf_xdp_copy); } @@ -4865,6 +5094,13 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname, case SO_REUSEPORT: sk->sk_reuseport = valbool; break; + case SO_TXREHASH: + if (val < -1 || val > 1) { + ret = -EINVAL; + break; + } + sk->sk_txrehash = (u8)val; + break; default: ret = -EINVAL; } @@ -5043,6 +5279,9 @@ static int _bpf_getsockopt(struct sock *sk, int level, int optname, case SO_REUSEPORT: *((int *)optval) = sk->sk_reuseport; break; + case SO_TXREHASH: + *((int *)optval) = sk->sk_txrehash; + break; default: goto err_clear; } @@ -7149,6 +7388,43 @@ static const struct bpf_func_proto bpf_sock_ops_reserve_hdr_opt_proto = { .arg3_type = ARG_ANYTHING, }; +BPF_CALL_3(bpf_skb_set_tstamp, struct sk_buff *, skb, + u64, tstamp, u32, tstamp_type) +{ + /* skb_clear_delivery_time() is done for inet protocol */ + if (skb->protocol != htons(ETH_P_IP) && + skb->protocol != htons(ETH_P_IPV6)) + return -EOPNOTSUPP; + + switch (tstamp_type) { + case BPF_SKB_TSTAMP_DELIVERY_MONO: + if (!tstamp) + return -EINVAL; + skb->tstamp = tstamp; + skb->mono_delivery_time = 1; + break; + case BPF_SKB_TSTAMP_UNSPEC: + if (tstamp) + return -EINVAL; + skb->tstamp = 0; + skb->mono_delivery_time = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct bpf_func_proto bpf_skb_set_tstamp_proto = { + .func = bpf_skb_set_tstamp, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, +}; + #endif /* CONFIG_INET */ bool bpf_helper_changes_pkt_data(void *func) @@ -7510,6 +7786,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_tcp_gen_syncookie_proto; case BPF_FUNC_sk_assign: return &bpf_sk_assign_proto; + case BPF_FUNC_skb_set_tstamp: + return &bpf_skb_set_tstamp_proto; #endif default: return bpf_sk_base_func_proto(func_id); @@ -7536,6 +7814,12 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_xdp_redirect_map_proto; case BPF_FUNC_xdp_adjust_tail: return &bpf_xdp_adjust_tail_proto; + case BPF_FUNC_xdp_get_buff_len: + return &bpf_xdp_get_buff_len_proto; + case BPF_FUNC_xdp_load_bytes: + return &bpf_xdp_load_bytes_proto; + case BPF_FUNC_xdp_store_bytes: + return &bpf_xdp_store_bytes_proto; case BPF_FUNC_fib_lookup: return &bpf_xdp_fib_lookup_proto; case BPF_FUNC_check_mtu: @@ -7843,7 +8127,9 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type return false; info->reg_type = PTR_TO_SOCK_COMMON_OR_NULL; break; - case offsetofend(struct __sk_buff, gso_size) ... offsetof(struct __sk_buff, hwtstamp) - 1: + case offsetof(struct __sk_buff, tstamp_type): + return false; + case offsetofend(struct __sk_buff, tstamp_type) ... offsetof(struct __sk_buff, hwtstamp) - 1: /* Explicitly prohibit access to padding in __sk_buff. */ return false; default: @@ -8033,6 +8319,7 @@ bool bpf_sock_is_valid_access(int off, int size, enum bpf_access_type type, struct bpf_insn_access_aux *info) { const int size_default = sizeof(__u32); + int field_size; if (off < 0 || off >= sizeof(struct bpf_sock)) return false; @@ -8044,7 +8331,6 @@ bool bpf_sock_is_valid_access(int off, int size, enum bpf_access_type type, case offsetof(struct bpf_sock, family): case offsetof(struct bpf_sock, type): case offsetof(struct bpf_sock, protocol): - case offsetof(struct bpf_sock, dst_port): case offsetof(struct bpf_sock, src_port): case offsetof(struct bpf_sock, rx_queue_mapping): case bpf_ctx_range(struct bpf_sock, src_ip4): @@ -8053,6 +8339,14 @@ bool bpf_sock_is_valid_access(int off, int size, enum bpf_access_type type, case bpf_ctx_range_till(struct bpf_sock, dst_ip6[0], dst_ip6[3]): bpf_ctx_record_field_size(info, size_default); return bpf_ctx_narrow_access_ok(off, size, size_default); + case bpf_ctx_range(struct bpf_sock, dst_port): + field_size = size == size_default ? + size_default : sizeof_field(struct bpf_sock, dst_port); + bpf_ctx_record_field_size(info, field_size); + return bpf_ctx_narrow_access_ok(off, size, field_size); + case offsetofend(struct bpf_sock, dst_port) ... + offsetof(struct bpf_sock, dst_ip4) - 1: + return false; } return size == size_default; @@ -8190,6 +8484,15 @@ static bool tc_cls_act_is_valid_access(int off, int size, break; case bpf_ctx_range_till(struct __sk_buff, family, local_port): return false; + case offsetof(struct __sk_buff, tstamp_type): + /* The convert_ctx_access() on reading and writing + * __sk_buff->tstamp depends on whether the bpf prog + * has used __sk_buff->tstamp_type or not. + * Thus, we need to set prog->tstamp_type_access + * earlier during is_valid_access() here. + */ + ((struct bpf_prog *)prog)->tstamp_type_access = 1; + return size == sizeof(__u8); } return bpf_skb_is_valid_access(off, size, type, prog, info); @@ -8585,6 +8888,25 @@ static u32 flow_dissector_convert_ctx_access(enum bpf_access_type type, return insn - insn_buf; } +static struct bpf_insn *bpf_convert_tstamp_type_read(const struct bpf_insn *si, + struct bpf_insn *insn) +{ + __u8 value_reg = si->dst_reg; + __u8 skb_reg = si->src_reg; + /* AX is needed because src_reg and dst_reg could be the same */ + __u8 tmp_reg = BPF_REG_AX; + + *insn++ = BPF_LDX_MEM(BPF_B, tmp_reg, skb_reg, + PKT_VLAN_PRESENT_OFFSET); + *insn++ = BPF_JMP32_IMM(BPF_JSET, tmp_reg, + SKB_MONO_DELIVERY_TIME_MASK, 2); + *insn++ = BPF_MOV32_IMM(value_reg, BPF_SKB_TSTAMP_UNSPEC); + *insn++ = BPF_JMP_A(1); + *insn++ = BPF_MOV32_IMM(value_reg, BPF_SKB_TSTAMP_DELIVERY_MONO); + + return insn; +} + static struct bpf_insn *bpf_convert_shinfo_access(const struct bpf_insn *si, struct bpf_insn *insn) { @@ -8606,6 +8928,74 @@ static struct bpf_insn *bpf_convert_shinfo_access(const struct bpf_insn *si, return insn; } +static struct bpf_insn *bpf_convert_tstamp_read(const struct bpf_prog *prog, + const struct bpf_insn *si, + struct bpf_insn *insn) +{ + __u8 value_reg = si->dst_reg; + __u8 skb_reg = si->src_reg; + +#ifdef CONFIG_NET_CLS_ACT + /* If the tstamp_type is read, + * the bpf prog is aware the tstamp could have delivery time. + * Thus, read skb->tstamp as is if tstamp_type_access is true. + */ + if (!prog->tstamp_type_access) { + /* AX is needed because src_reg and dst_reg could be the same */ + __u8 tmp_reg = BPF_REG_AX; + + *insn++ = BPF_LDX_MEM(BPF_B, tmp_reg, skb_reg, PKT_VLAN_PRESENT_OFFSET); + *insn++ = BPF_ALU32_IMM(BPF_AND, tmp_reg, + TC_AT_INGRESS_MASK | SKB_MONO_DELIVERY_TIME_MASK); + *insn++ = BPF_JMP32_IMM(BPF_JNE, tmp_reg, + TC_AT_INGRESS_MASK | SKB_MONO_DELIVERY_TIME_MASK, 2); + /* skb->tc_at_ingress && skb->mono_delivery_time, + * read 0 as the (rcv) timestamp. + */ + *insn++ = BPF_MOV64_IMM(value_reg, 0); + *insn++ = BPF_JMP_A(1); + } +#endif + + *insn++ = BPF_LDX_MEM(BPF_DW, value_reg, skb_reg, + offsetof(struct sk_buff, tstamp)); + return insn; +} + +static struct bpf_insn *bpf_convert_tstamp_write(const struct bpf_prog *prog, + const struct bpf_insn *si, + struct bpf_insn *insn) +{ + __u8 value_reg = si->src_reg; + __u8 skb_reg = si->dst_reg; + +#ifdef CONFIG_NET_CLS_ACT + /* If the tstamp_type is read, + * the bpf prog is aware the tstamp could have delivery time. + * Thus, write skb->tstamp as is if tstamp_type_access is true. + * Otherwise, writing at ingress will have to clear the + * mono_delivery_time bit also. + */ + if (!prog->tstamp_type_access) { + __u8 tmp_reg = BPF_REG_AX; + + *insn++ = BPF_LDX_MEM(BPF_B, tmp_reg, skb_reg, PKT_VLAN_PRESENT_OFFSET); + /* Writing __sk_buff->tstamp as ingress, goto */ + *insn++ = BPF_JMP32_IMM(BPF_JSET, tmp_reg, TC_AT_INGRESS_MASK, 1); + /* goto */ + *insn++ = BPF_JMP_A(2); + /* : mono_delivery_time */ + *insn++ = BPF_ALU32_IMM(BPF_AND, tmp_reg, ~SKB_MONO_DELIVERY_TIME_MASK); + *insn++ = BPF_STX_MEM(BPF_B, skb_reg, tmp_reg, PKT_VLAN_PRESENT_OFFSET); + } +#endif + + /* : skb->tstamp = tstamp */ + *insn++ = BPF_STX_MEM(BPF_DW, skb_reg, value_reg, + offsetof(struct sk_buff, tstamp)); + return insn; +} + static u32 bpf_convert_ctx_access(enum bpf_access_type type, const struct bpf_insn *si, struct bpf_insn *insn_buf, @@ -8914,17 +9304,13 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, BUILD_BUG_ON(sizeof_field(struct sk_buff, tstamp) != 8); if (type == BPF_WRITE) - *insn++ = BPF_STX_MEM(BPF_DW, - si->dst_reg, si->src_reg, - bpf_target_off(struct sk_buff, - tstamp, 8, - target_size)); + insn = bpf_convert_tstamp_write(prog, si, insn); else - *insn++ = BPF_LDX_MEM(BPF_DW, - si->dst_reg, si->src_reg, - bpf_target_off(struct sk_buff, - tstamp, 8, - target_size)); + insn = bpf_convert_tstamp_read(prog, si, insn); + break; + + case offsetof(struct __sk_buff, tstamp_type): + insn = bpf_convert_tstamp_type_read(si, insn); break; case offsetof(struct __sk_buff, gso_segs): @@ -10065,7 +10451,6 @@ const struct bpf_verifier_ops tc_cls_act_verifier_ops = { .convert_ctx_access = tc_cls_act_convert_ctx_access, .gen_prologue = tc_cls_act_prologue, .gen_ld_abs = bpf_gen_ld_abs, - .check_kfunc_call = bpf_prog_test_check_kfunc_call, }; const struct bpf_prog_ops tc_cls_act_prog_ops = { @@ -10604,12 +10989,24 @@ static bool sk_lookup_is_valid_access(int off, int size, case bpf_ctx_range(struct bpf_sk_lookup, local_ip4): case bpf_ctx_range_till(struct bpf_sk_lookup, remote_ip6[0], remote_ip6[3]): case bpf_ctx_range_till(struct bpf_sk_lookup, local_ip6[0], local_ip6[3]): - case bpf_ctx_range(struct bpf_sk_lookup, remote_port): case bpf_ctx_range(struct bpf_sk_lookup, local_port): case bpf_ctx_range(struct bpf_sk_lookup, ingress_ifindex): bpf_ctx_record_field_size(info, sizeof(__u32)); return bpf_ctx_narrow_access_ok(off, size, sizeof(__u32)); + case bpf_ctx_range(struct bpf_sk_lookup, remote_port): + /* Allow 4-byte access to 2-byte field for backward compatibility */ + if (size == sizeof(__u32)) + return true; + bpf_ctx_record_field_size(info, sizeof(__be16)); + return bpf_ctx_narrow_access_ok(off, size, sizeof(__be16)); + + case offsetofend(struct bpf_sk_lookup, remote_port) ... + offsetof(struct bpf_sk_lookup, local_ip4) - 1: + /* Allow access to zero padding for backward compatibility */ + bpf_ctx_record_field_size(info, sizeof(__u16)); + return bpf_ctx_narrow_access_ok(off, size, sizeof(__u16)); + default: return false; } @@ -10691,6 +11088,11 @@ static u32 sk_lookup_convert_ctx_access(enum bpf_access_type type, sport, 2, target_size)); break; + case offsetofend(struct bpf_sk_lookup, remote_port): + *target_size = 2; + *insn++ = BPF_MOV32_IMM(si->dst_reg, 0); + break; + case offsetof(struct bpf_sk_lookup, local_port): *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg, bpf_target_off(struct bpf_sk_lookup_kern, diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 15833e1d6ea1..03b6e649c428 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1282,6 +1283,23 @@ bool __skb_flow_dissect(const struct net *net, break; } + case htons(ETH_P_PRP): + case htons(ETH_P_HSR): { + struct hsr_tag *hdr, _hdr; + + hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, + &_hdr); + if (!hdr) { + fdret = FLOW_DISSECT_RET_OUT_BAD; + break; + } + + proto = hdr->encap_proto; + nhoff += HSR_HLEN; + fdret = FLOW_DISSECT_RET_PROTO_AGAIN; + break; + } + default: fdret = FLOW_DISSECT_RET_OUT_BAD; break; diff --git a/net/core/gro.c b/net/core/gro.c index b7d2b0dc59a2..78110edf5d4b 100644 --- a/net/core/gro.c +++ b/net/core/gro.c @@ -484,29 +484,22 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff skb_set_network_header(skb, skb_gro_offset(skb)); skb_reset_mac_len(skb); - NAPI_GRO_CB(skb)->same_flow = 0; + BUILD_BUG_ON(sizeof_field(struct napi_gro_cb, zeroed) != sizeof(u32)); + BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct napi_gro_cb, zeroed), + sizeof(u32))); /* Avoid slow unaligned acc */ + *(u32 *)&NAPI_GRO_CB(skb)->zeroed = 0; NAPI_GRO_CB(skb)->flush = skb_is_gso(skb) || skb_has_frag_list(skb); - NAPI_GRO_CB(skb)->free = 0; - NAPI_GRO_CB(skb)->encap_mark = 0; - NAPI_GRO_CB(skb)->recursion_counter = 0; - NAPI_GRO_CB(skb)->is_fou = 0; NAPI_GRO_CB(skb)->is_atomic = 1; - NAPI_GRO_CB(skb)->gro_remcsum_start = 0; /* Setup for GRO checksum validation */ switch (skb->ip_summed) { case CHECKSUM_COMPLETE: NAPI_GRO_CB(skb)->csum = skb->csum; NAPI_GRO_CB(skb)->csum_valid = 1; - NAPI_GRO_CB(skb)->csum_cnt = 0; break; case CHECKSUM_UNNECESSARY: NAPI_GRO_CB(skb)->csum_cnt = skb->csum_level + 1; - NAPI_GRO_CB(skb)->csum_valid = 0; break; - default: - NAPI_GRO_CB(skb)->csum_cnt = 0; - NAPI_GRO_CB(skb)->csum_valid = 0; } pp = INDIRECT_CALL_INET(ptype->callbacks.gro_receive, @@ -659,7 +652,6 @@ static void napi_reuse_skb(struct napi_struct *napi, struct sk_buff *skb) skb->encapsulation = 0; skb_shinfo(skb)->gso_type = 0; - skb->truesize = SKB_TRUESIZE(skb_end_offset(skb)); if (unlikely(skb->slow_gro)) { skb_orphan(skb); skb_ext_reset(skb); diff --git a/net/core/gro_cells.c b/net/core/gro_cells.c index 6eb2e5ec2c50..541c7a72a28a 100644 --- a/net/core/gro_cells.c +++ b/net/core/gro_cells.c @@ -28,7 +28,7 @@ int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb) if (skb_queue_len(&cell->napi_skbs) > netdev_max_backlog) { drop: - atomic_long_inc(&dev->rx_dropped); + dev_core_stats_rx_dropped_inc(dev); kfree_skb(skb); res = NET_RX_DROP; goto unlock; @@ -89,8 +89,23 @@ int gro_cells_init(struct gro_cells *gcells, struct net_device *dev) } EXPORT_SYMBOL(gro_cells_init); +struct percpu_free_defer { + struct rcu_head rcu; + void __percpu *ptr; +}; + +static void percpu_free_defer_callback(struct rcu_head *head) +{ + struct percpu_free_defer *defer; + + defer = container_of(head, struct percpu_free_defer, rcu); + free_percpu(defer->ptr); + kfree(defer); +} + void gro_cells_destroy(struct gro_cells *gcells) { + struct percpu_free_defer *defer; int i; if (!gcells->cells) @@ -102,12 +117,23 @@ void gro_cells_destroy(struct gro_cells *gcells) __netif_napi_del(&cell->napi); __skb_queue_purge(&cell->napi_skbs); } - /* This barrier is needed because netpoll could access dev->napi_list - * under rcu protection. + /* We need to observe an rcu grace period before freeing ->cells, + * because netpoll could access dev->napi_list under rcu protection. + * Try hard using call_rcu() instead of synchronize_rcu(), + * because we might be called from cleanup_net(), and we + * definitely do not want to block this critical task. */ - synchronize_net(); - - free_percpu(gcells->cells); + defer = kmalloc(sizeof(*defer), GFP_KERNEL | __GFP_NOWARN); + if (likely(defer)) { + defer->ptr = gcells->cells; + call_rcu(&defer->rcu, percpu_free_defer_callback); + } else { + /* We do not hold RTNL at this point, synchronize_net() + * would not be able to expedite this sync. + */ + synchronize_rcu_expedited(); + free_percpu(gcells->cells); + } gcells->cells = NULL; } EXPORT_SYMBOL(gro_cells_destroy); diff --git a/net/core/link_watch.c b/net/core/link_watch.c index b0f5344d1185..95098d1a49bd 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -166,10 +166,10 @@ static void linkwatch_do_dev(struct net_device *dev) netdev_state_change(dev); } - /* Note: our callers are responsible for - * calling netdev_tracker_free(). + /* Note: our callers are responsible for calling netdev_tracker_free(). + * This is the reason we use __dev_put() instead of dev_put(). */ - dev_put(dev); + __dev_put(dev); } static void __linkwatch_run_queue(int urgent_only) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index ec0bf737b076..f64ebd050f6c 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1171,7 +1171,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb, neigh->updated = jiffies; write_unlock_bh(&neigh->lock); - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_FAILED); return 1; } } else if (neigh->nud_state & NUD_STALE) { @@ -1193,7 +1193,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb, if (!buff) break; neigh->arp_queue_len_bytes -= buff->truesize; - kfree_skb(buff); + kfree_skb_reason(buff, SKB_DROP_REASON_NEIGH_QUEUEFULL); NEIGH_CACHE_STAT_INC(neigh->tbl, unres_discards); } skb_dst_force(skb); @@ -1215,7 +1215,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb, if (neigh->nud_state & NUD_STALE) goto out_unlock_bh; write_unlock_bh(&neigh->lock); - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_DEAD); trace_neigh_event_send_dead(neigh, 1); return 1; } diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index a5b5bb99c644..0ec2f5906a27 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -44,13 +44,7 @@ EXPORT_SYMBOL_GPL(net_rwsem); static struct key_tag init_net_key_domain = { .usage = REFCOUNT_INIT(1) }; #endif -struct net init_net = { - .ns.count = REFCOUNT_INIT(1), - .dev_base_head = LIST_HEAD_INIT(init_net.dev_base_head), -#ifdef CONFIG_KEYS - .key_domain = &init_net_key_domain, -#endif -}; +struct net init_net; EXPORT_SYMBOL(init_net); static bool init_net_initialized; @@ -301,6 +295,7 @@ struct net *get_net_ns_by_id(const struct net *net, int id) return peer; } +EXPORT_SYMBOL_GPL(get_net_ns_by_id); /* * setup_net runs the initializers for the network namespace object. @@ -363,6 +358,8 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns) static int __net_init net_defaults_init_net(struct net *net) { net->core.sysctl_somaxconn = SOMAXCONN; + net->core.sysctl_txrehash = SOCK_TXREHASH_ENABLED; + return 0; } @@ -1084,7 +1081,7 @@ static void rtnl_net_notifyid(struct net *net, int cmd, int id, u32 portid, rtnl_set_sk_err(net, RTNLGRP_NSID, err); } -static int __init net_ns_init(void) +void __init net_ns_init(void) { struct net_generic *ng; @@ -1105,6 +1102,9 @@ static int __init net_ns_init(void) rcu_assign_pointer(init_net.gen, ng); +#ifdef CONFIG_KEYS + init_net.key_domain = &init_net_key_domain; +#endif down_write(&pernet_ops_rwsem); if (setup_net(&init_net, &init_user_ns)) panic("Could not setup the initial network namespace"); @@ -1119,12 +1119,8 @@ static int __init net_ns_init(void) RTNL_FLAG_DOIT_UNLOCKED); rtnl_register(PF_UNSPEC, RTM_GETNSID, rtnl_net_getid, rtnl_net_dumpid, RTNL_FLAG_DOIT_UNLOCKED); - - return 0; } -pure_initcall(net_ns_init); - static void free_exit_list(struct pernet_operations *ops, struct list_head *net_exit_list) { ops_pre_exit_list(ops, net_exit_list); diff --git a/net/core/page_pool.c b/net/core/page_pool.c index bd62c01a2ec3..1943c0f0307d 100644 --- a/net/core/page_pool.c +++ b/net/core/page_pool.c @@ -26,6 +26,45 @@ #define BIAS_MAX LONG_MAX +#ifdef CONFIG_PAGE_POOL_STATS +/* alloc_stat_inc is intended to be used in softirq context */ +#define alloc_stat_inc(pool, __stat) (pool->alloc_stats.__stat++) +/* recycle_stat_inc is safe to use when preemption is possible. */ +#define recycle_stat_inc(pool, __stat) \ + do { \ + struct page_pool_recycle_stats __percpu *s = pool->recycle_stats; \ + this_cpu_inc(s->__stat); \ + } while (0) + +bool page_pool_get_stats(struct page_pool *pool, + struct page_pool_stats *stats) +{ + int cpu = 0; + + if (!stats) + return false; + + memcpy(&stats->alloc_stats, &pool->alloc_stats, sizeof(pool->alloc_stats)); + + for_each_possible_cpu(cpu) { + const struct page_pool_recycle_stats *pcpu = + per_cpu_ptr(pool->recycle_stats, cpu); + + stats->recycle_stats.cached += pcpu->cached; + stats->recycle_stats.cache_full += pcpu->cache_full; + stats->recycle_stats.ring += pcpu->ring; + stats->recycle_stats.ring_full += pcpu->ring_full; + stats->recycle_stats.released_refcnt += pcpu->released_refcnt; + } + + return true; +} +EXPORT_SYMBOL(page_pool_get_stats); +#else +#define alloc_stat_inc(pool, __stat) +#define recycle_stat_inc(pool, __stat) +#endif + static int page_pool_init(struct page_pool *pool, const struct page_pool_params *params) { @@ -73,6 +112,12 @@ static int page_pool_init(struct page_pool *pool, pool->p.flags & PP_FLAG_PAGE_FRAG) return -EINVAL; +#ifdef CONFIG_PAGE_POOL_STATS + pool->recycle_stats = alloc_percpu(struct page_pool_recycle_stats); + if (!pool->recycle_stats) + return -ENOMEM; +#endif + if (ptr_ring_init(&pool->ring, ring_qsize, GFP_KERNEL) < 0) return -ENOMEM; @@ -117,8 +162,10 @@ static struct page *page_pool_refill_alloc_cache(struct page_pool *pool) int pref_nid; /* preferred NUMA node */ /* Quicker fallback, avoid locks when ring is empty */ - if (__ptr_ring_empty(r)) + if (__ptr_ring_empty(r)) { + alloc_stat_inc(pool, empty); return NULL; + } /* Softirq guarantee CPU and thus NUMA node is stable. This, * assumes CPU refilling driver RX-ring will also run RX-NAPI. @@ -145,14 +192,17 @@ static struct page *page_pool_refill_alloc_cache(struct page_pool *pool) * This limit stress on page buddy alloactor. */ page_pool_return_page(pool, page); + alloc_stat_inc(pool, waive); page = NULL; break; } } while (pool->alloc.count < PP_ALLOC_CACHE_REFILL); /* Return last page */ - if (likely(pool->alloc.count > 0)) + if (likely(pool->alloc.count > 0)) { page = pool->alloc.cache[--pool->alloc.count]; + alloc_stat_inc(pool, refill); + } return page; } @@ -166,6 +216,7 @@ static struct page *__page_pool_get_cached(struct page_pool *pool) if (likely(pool->alloc.count)) { /* Fast-path */ page = pool->alloc.cache[--pool->alloc.count]; + alloc_stat_inc(pool, fast); } else { page = page_pool_refill_alloc_cache(pool); } @@ -239,6 +290,7 @@ static struct page *__page_pool_alloc_page_order(struct page_pool *pool, return NULL; } + alloc_stat_inc(pool, slow_high_order); page_pool_set_pp_info(pool, page); /* Track how many pages are held 'in-flight' */ @@ -293,10 +345,12 @@ static struct page *__page_pool_alloc_pages_slow(struct page_pool *pool, } /* Return last page */ - if (likely(pool->alloc.count > 0)) + if (likely(pool->alloc.count > 0)) { page = pool->alloc.cache[--pool->alloc.count]; - else + alloc_stat_inc(pool, slow); + } else { page = NULL; + } /* When page just alloc'ed is should/must have refcnt 1. */ return page; @@ -394,7 +448,12 @@ static bool page_pool_recycle_in_ring(struct page_pool *pool, struct page *page) else ret = ptr_ring_produce_bh(&pool->ring, page); - return (ret == 0) ? true : false; + if (!ret) { + recycle_stat_inc(pool, ring); + return true; + } + + return false; } /* Only allow direct recycling in special circumstances, into the @@ -405,11 +464,14 @@ static bool page_pool_recycle_in_ring(struct page_pool *pool, struct page *page) static bool page_pool_recycle_in_cache(struct page *page, struct page_pool *pool) { - if (unlikely(pool->alloc.count == PP_ALLOC_CACHE_SIZE)) + if (unlikely(pool->alloc.count == PP_ALLOC_CACHE_SIZE)) { + recycle_stat_inc(pool, cache_full); return false; + } /* Caller MUST have verified/know (page_ref_count(page) == 1) */ pool->alloc.cache[pool->alloc.count++] = page; + recycle_stat_inc(pool, cached); return true; } @@ -423,11 +485,6 @@ static __always_inline struct page * __page_pool_put_page(struct page_pool *pool, struct page *page, unsigned int dma_sync_size, bool allow_direct) { - /* It is not the last user for the page frag case */ - if (pool->p.flags & PP_FLAG_PAGE_FRAG && - page_pool_atomic_sub_frag_count_return(page, 1)) - return NULL; - /* This allocator is optimized for the XDP mode that uses * one-frame-per-page, but have fallbacks that act like the * regular page allocator APIs. @@ -464,6 +521,7 @@ __page_pool_put_page(struct page_pool *pool, struct page *page, * doing refcnt based recycle tricks, meaning another process * will be invoking put_page. */ + recycle_stat_inc(pool, released_refcnt); /* Do not replace this with page_pool_return_page() */ page_pool_release_page(pool, page); put_page(page); @@ -471,16 +529,17 @@ __page_pool_put_page(struct page_pool *pool, struct page *page, return NULL; } -void page_pool_put_page(struct page_pool *pool, struct page *page, - unsigned int dma_sync_size, bool allow_direct) +void page_pool_put_defragged_page(struct page_pool *pool, struct page *page, + unsigned int dma_sync_size, bool allow_direct) { page = __page_pool_put_page(pool, page, dma_sync_size, allow_direct); if (page && !page_pool_recycle_in_ring(pool, page)) { /* Cache full, fallback to free pages */ + recycle_stat_inc(pool, ring_full); page_pool_return_page(pool, page); } } -EXPORT_SYMBOL(page_pool_put_page); +EXPORT_SYMBOL(page_pool_put_defragged_page); /* Caller must not use data area after call, as this function overwrites it */ void page_pool_put_page_bulk(struct page_pool *pool, void **data, @@ -491,6 +550,10 @@ void page_pool_put_page_bulk(struct page_pool *pool, void **data, for (i = 0; i < count; i++) { struct page *page = virt_to_head_page(data[i]); + /* It is not the last user for the page frag case */ + if (!page_pool_is_last_frag(pool, page)) + continue; + page = __page_pool_put_page(pool, page, -1, false); /* Approved for bulk recycling in ptr_ring cache */ if (page) @@ -526,8 +589,7 @@ static struct page *page_pool_drain_frag(struct page_pool *pool, long drain_count = BIAS_MAX - pool->frag_users; /* Some user is still using the page frag */ - if (likely(page_pool_atomic_sub_frag_count_return(page, - drain_count))) + if (likely(page_pool_defrag_page(page, drain_count))) return NULL; if (page_ref_count(page) == 1 && !page_is_pfmemalloc(page)) { @@ -548,8 +610,7 @@ static void page_pool_free_frag(struct page_pool *pool) pool->frag_page = NULL; - if (!page || - page_pool_atomic_sub_frag_count_return(page, drain_count)) + if (!page || page_pool_defrag_page(page, drain_count)) return; page_pool_return_page(pool, page); @@ -588,7 +649,7 @@ struct page *page_pool_alloc_frag(struct page_pool *pool, pool->frag_users = 1; *offset = 0; pool->frag_offset = size; - page_pool_set_frag_count(page, BIAS_MAX); + page_pool_fragment_page(page, BIAS_MAX); return page; } @@ -623,6 +684,9 @@ static void page_pool_free(struct page_pool *pool) if (pool->p.flags & PP_FLAG_DMA_MAP) put_device(pool->p.dev); +#ifdef CONFIG_PAGE_POOL_STATS + free_percpu(pool->recycle_stats); +#endif kfree(pool); } diff --git a/net/core/ptp_classifier.c b/net/core/ptp_classifier.c index dd4cf01d1e0a..598041b0499e 100644 --- a/net/core/ptp_classifier.c +++ b/net/core/ptp_classifier.c @@ -137,6 +137,18 @@ struct ptp_header *ptp_parse_header(struct sk_buff *skb, unsigned int type) } EXPORT_SYMBOL_GPL(ptp_parse_header); +bool ptp_msg_is_sync(struct sk_buff *skb, unsigned int type) +{ + struct ptp_header *hdr; + + hdr = ptp_parse_header(skb, type); + if (!hdr) + return false; + + return ptp_get_msgtype(hdr, type) == PTP_MSGTYPE_SYNC; +} +EXPORT_SYMBOL_GPL(ptp_msg_is_sync); + void __init ptp_classifier_init(void) { static struct sock_filter ptp_filter[] __initdata = { diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 2fb8eb6791e8..159c9c61e6af 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -459,7 +459,7 @@ static void rtnl_lock_unregistering_all(void) * setup_net() and cleanup_net() are not possible. */ for_each_net(net) { - if (net->dev_unreg_count > 0) { + if (atomic_read(&net->dev_unreg_count) > 0) { unregistering = true; break; } @@ -3652,13 +3652,24 @@ static int rtnl_alt_ifname(int cmd, struct net_device *dev, struct nlattr *attr, bool *changed, struct netlink_ext_ack *extack) { char *alt_ifname; + size_t size; int err; err = nla_validate(attr, attr->nla_len, IFLA_MAX, ifla_policy, extack); if (err) return err; - alt_ifname = nla_strdup(attr, GFP_KERNEL); + if (cmd == RTM_NEWLINKPROP) { + size = rtnl_prop_list_size(dev); + size += nla_total_size(ALTIFNAMSIZ); + if (size >= U16_MAX) { + NL_SET_ERR_MSG(extack, + "effective property list too long"); + return -EINVAL; + } + } + + alt_ifname = nla_strdup(attr, GFP_KERNEL_ACCOUNT); if (!alt_ifname) return -ENOMEM; @@ -5048,82 +5059,256 @@ static bool stats_attr_valid(unsigned int mask, int attrid, int idxattr) (!idxattr || idxattr == attrid); } -#define IFLA_OFFLOAD_XSTATS_FIRST (IFLA_OFFLOAD_XSTATS_UNSPEC + 1) -static int rtnl_get_offload_stats_attr_size(int attr_id) +static bool +rtnl_offload_xstats_have_ndo(const struct net_device *dev, int attr_id) { - switch (attr_id) { - case IFLA_OFFLOAD_XSTATS_CPU_HIT: - return sizeof(struct rtnl_link_stats64); - } + return dev->netdev_ops && + dev->netdev_ops->ndo_has_offload_stats && + dev->netdev_ops->ndo_get_offload_stats && + dev->netdev_ops->ndo_has_offload_stats(dev, attr_id); +} + +static unsigned int +rtnl_offload_xstats_get_size_ndo(const struct net_device *dev, int attr_id) +{ + return rtnl_offload_xstats_have_ndo(dev, attr_id) ? + sizeof(struct rtnl_link_stats64) : 0; +} + +static int +rtnl_offload_xstats_fill_ndo(struct net_device *dev, int attr_id, + struct sk_buff *skb) +{ + unsigned int size = rtnl_offload_xstats_get_size_ndo(dev, attr_id); + struct nlattr *attr = NULL; + void *attr_data; + int err; + + if (!size) + return -ENODATA; + + attr = nla_reserve_64bit(skb, attr_id, size, + IFLA_OFFLOAD_XSTATS_UNSPEC); + if (!attr) + return -EMSGSIZE; + + attr_data = nla_data(attr); + memset(attr_data, 0, size); + + err = dev->netdev_ops->ndo_get_offload_stats(attr_id, dev, attr_data); + if (err) + return err; return 0; } -static int rtnl_get_offload_stats(struct sk_buff *skb, struct net_device *dev, - int *prividx) +static unsigned int +rtnl_offload_xstats_get_size_stats(const struct net_device *dev, + enum netdev_offload_xstats_type type) { - struct nlattr *attr = NULL; - int attr_id, size; - void *attr_data; + bool enabled = netdev_offload_xstats_enabled(dev, type); + + return enabled ? sizeof(struct rtnl_hw_stats64) : 0; +} + +struct rtnl_offload_xstats_request_used { + bool request; + bool used; +}; + +static int +rtnl_offload_xstats_get_stats(struct net_device *dev, + enum netdev_offload_xstats_type type, + struct rtnl_offload_xstats_request_used *ru, + struct rtnl_hw_stats64 *stats, + struct netlink_ext_ack *extack) +{ + bool request; + bool used; int err; - if (!(dev->netdev_ops && dev->netdev_ops->ndo_has_offload_stats && - dev->netdev_ops->ndo_get_offload_stats)) - return -ENODATA; - - for (attr_id = IFLA_OFFLOAD_XSTATS_FIRST; - attr_id <= IFLA_OFFLOAD_XSTATS_MAX; attr_id++) { - if (attr_id < *prividx) - continue; - - size = rtnl_get_offload_stats_attr_size(attr_id); - if (!size) - continue; - - if (!dev->netdev_ops->ndo_has_offload_stats(dev, attr_id)) - continue; - - attr = nla_reserve_64bit(skb, attr_id, size, - IFLA_OFFLOAD_XSTATS_UNSPEC); - if (!attr) - goto nla_put_failure; - - attr_data = nla_data(attr); - memset(attr_data, 0, size); - err = dev->netdev_ops->ndo_get_offload_stats(attr_id, dev, - attr_data); - if (err) - goto get_offload_stats_failure; + request = netdev_offload_xstats_enabled(dev, type); + if (!request) { + used = false; + goto out; } - if (!attr) + err = netdev_offload_xstats_get(dev, type, stats, &used, extack); + if (err) + return err; + +out: + if (ru) { + ru->request = request; + ru->used = used; + } + return 0; +} + +static int +rtnl_offload_xstats_fill_hw_s_info_one(struct sk_buff *skb, int attr_id, + struct rtnl_offload_xstats_request_used *ru) +{ + struct nlattr *nest; + + nest = nla_nest_start(skb, attr_id); + if (!nest) + return -EMSGSIZE; + + if (nla_put_u8(skb, IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST, ru->request)) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED, ru->used)) + goto nla_put_failure; + + nla_nest_end(skb, nest); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nest); + return -EMSGSIZE; +} + +static int +rtnl_offload_xstats_fill_hw_s_info(struct sk_buff *skb, struct net_device *dev, + struct netlink_ext_ack *extack) +{ + enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3; + struct rtnl_offload_xstats_request_used ru_l3; + struct nlattr *nest; + int err; + + err = rtnl_offload_xstats_get_stats(dev, t_l3, &ru_l3, NULL, extack); + if (err) + return err; + + nest = nla_nest_start(skb, IFLA_OFFLOAD_XSTATS_HW_S_INFO); + if (!nest) + return -EMSGSIZE; + + if (rtnl_offload_xstats_fill_hw_s_info_one(skb, + IFLA_OFFLOAD_XSTATS_L3_STATS, + &ru_l3)) + goto nla_put_failure; + + nla_nest_end(skb, nest); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nest); + return -EMSGSIZE; +} + +static int rtnl_offload_xstats_fill(struct sk_buff *skb, struct net_device *dev, + int *prividx, u32 off_filter_mask, + struct netlink_ext_ack *extack) +{ + enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3; + int attr_id_hw_s_info = IFLA_OFFLOAD_XSTATS_HW_S_INFO; + int attr_id_l3_stats = IFLA_OFFLOAD_XSTATS_L3_STATS; + int attr_id_cpu_hit = IFLA_OFFLOAD_XSTATS_CPU_HIT; + bool have_data = false; + int err; + + if (*prividx <= attr_id_cpu_hit && + (off_filter_mask & + IFLA_STATS_FILTER_BIT(attr_id_cpu_hit))) { + err = rtnl_offload_xstats_fill_ndo(dev, attr_id_cpu_hit, skb); + if (!err) { + have_data = true; + } else if (err != -ENODATA) { + *prividx = attr_id_cpu_hit; + return err; + } + } + + if (*prividx <= attr_id_hw_s_info && + (off_filter_mask & IFLA_STATS_FILTER_BIT(attr_id_hw_s_info))) { + *prividx = attr_id_hw_s_info; + + err = rtnl_offload_xstats_fill_hw_s_info(skb, dev, extack); + if (err) + return err; + + have_data = true; + *prividx = 0; + } + + if (*prividx <= attr_id_l3_stats && + (off_filter_mask & IFLA_STATS_FILTER_BIT(attr_id_l3_stats))) { + unsigned int size_l3; + struct nlattr *attr; + + *prividx = attr_id_l3_stats; + + size_l3 = rtnl_offload_xstats_get_size_stats(dev, t_l3); + attr = nla_reserve_64bit(skb, attr_id_l3_stats, size_l3, + IFLA_OFFLOAD_XSTATS_UNSPEC); + if (!attr) + return -EMSGSIZE; + + err = rtnl_offload_xstats_get_stats(dev, t_l3, NULL, + nla_data(attr), extack); + if (err) + return err; + + have_data = true; + *prividx = 0; + } + + if (!have_data) return -ENODATA; *prividx = 0; return 0; - -nla_put_failure: - err = -EMSGSIZE; -get_offload_stats_failure: - *prividx = attr_id; - return err; } -static int rtnl_get_offload_stats_size(const struct net_device *dev) +static unsigned int +rtnl_offload_xstats_get_size_hw_s_info_one(const struct net_device *dev, + enum netdev_offload_xstats_type type) { + bool enabled = netdev_offload_xstats_enabled(dev, type); + + return nla_total_size(0) + + /* IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST */ + nla_total_size(sizeof(u8)) + + /* IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED */ + (enabled ? nla_total_size(sizeof(u8)) : 0) + + 0; +} + +static unsigned int +rtnl_offload_xstats_get_size_hw_s_info(const struct net_device *dev) +{ + enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3; + + return nla_total_size(0) + + /* IFLA_OFFLOAD_XSTATS_L3_STATS */ + rtnl_offload_xstats_get_size_hw_s_info_one(dev, t_l3) + + 0; +} + +static int rtnl_offload_xstats_get_size(const struct net_device *dev, + u32 off_filter_mask) +{ + enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3; + int attr_id_cpu_hit = IFLA_OFFLOAD_XSTATS_CPU_HIT; int nla_size = 0; - int attr_id; int size; - if (!(dev->netdev_ops && dev->netdev_ops->ndo_has_offload_stats && - dev->netdev_ops->ndo_get_offload_stats)) - return 0; + if (off_filter_mask & + IFLA_STATS_FILTER_BIT(attr_id_cpu_hit)) { + size = rtnl_offload_xstats_get_size_ndo(dev, attr_id_cpu_hit); + nla_size += nla_total_size_64bit(size); + } - for (attr_id = IFLA_OFFLOAD_XSTATS_FIRST; - attr_id <= IFLA_OFFLOAD_XSTATS_MAX; attr_id++) { - if (!dev->netdev_ops->ndo_has_offload_stats(dev, attr_id)) - continue; - size = rtnl_get_offload_stats_attr_size(attr_id); + if (off_filter_mask & + IFLA_STATS_FILTER_BIT(IFLA_OFFLOAD_XSTATS_HW_S_INFO)) + nla_size += rtnl_offload_xstats_get_size_hw_s_info(dev); + + if (off_filter_mask & + IFLA_STATS_FILTER_BIT(IFLA_OFFLOAD_XSTATS_L3_STATS)) { + size = rtnl_offload_xstats_get_size_stats(dev, t_l3); nla_size += nla_total_size_64bit(size); } @@ -5133,11 +5318,21 @@ static int rtnl_get_offload_stats_size(const struct net_device *dev) return nla_size; } +struct rtnl_stats_dump_filters { + /* mask[0] filters outer attributes. Then individual nests have their + * filtering mask at the index of the nested attribute. + */ + u32 mask[IFLA_STATS_MAX + 1]; +}; + static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev, int type, u32 pid, u32 seq, u32 change, - unsigned int flags, unsigned int filter_mask, - int *idxattr, int *prividx) + unsigned int flags, + const struct rtnl_stats_dump_filters *filters, + int *idxattr, int *prividx, + struct netlink_ext_ack *extack) { + unsigned int filter_mask = filters->mask[0]; struct if_stats_msg *ifsm; struct nlmsghdr *nlh; struct nlattr *attr; @@ -5163,8 +5358,10 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev, attr = nla_reserve_64bit(skb, IFLA_STATS_LINK_64, sizeof(struct rtnl_link_stats64), IFLA_STATS_UNSPEC); - if (!attr) + if (!attr) { + err = -EMSGSIZE; goto nla_put_failure; + } sp = nla_data(attr); dev_get_stats(dev, sp); @@ -5177,8 +5374,10 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev, *idxattr = IFLA_STATS_LINK_XSTATS; attr = nla_nest_start_noflag(skb, IFLA_STATS_LINK_XSTATS); - if (!attr) + if (!attr) { + err = -EMSGSIZE; goto nla_put_failure; + } err = ops->fill_linkxstats(skb, dev, prividx, *idxattr); nla_nest_end(skb, attr); @@ -5200,8 +5399,10 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev, *idxattr = IFLA_STATS_LINK_XSTATS_SLAVE; attr = nla_nest_start_noflag(skb, IFLA_STATS_LINK_XSTATS_SLAVE); - if (!attr) + if (!attr) { + err = -EMSGSIZE; goto nla_put_failure; + } err = ops->fill_linkxstats(skb, dev, prividx, *idxattr); nla_nest_end(skb, attr); @@ -5213,13 +5414,19 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev, if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_OFFLOAD_XSTATS, *idxattr)) { + u32 off_filter_mask; + + off_filter_mask = filters->mask[IFLA_STATS_LINK_OFFLOAD_XSTATS]; *idxattr = IFLA_STATS_LINK_OFFLOAD_XSTATS; attr = nla_nest_start_noflag(skb, IFLA_STATS_LINK_OFFLOAD_XSTATS); - if (!attr) + if (!attr) { + err = -EMSGSIZE; goto nla_put_failure; + } - err = rtnl_get_offload_stats(skb, dev, prividx); + err = rtnl_offload_xstats_fill(skb, dev, prividx, + off_filter_mask, extack); if (err == -ENODATA) nla_nest_cancel(skb, attr); else @@ -5235,19 +5442,21 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev, *idxattr = IFLA_STATS_AF_SPEC; attr = nla_nest_start_noflag(skb, IFLA_STATS_AF_SPEC); - if (!attr) + if (!attr) { + err = -EMSGSIZE; goto nla_put_failure; + } rcu_read_lock(); list_for_each_entry_rcu(af_ops, &rtnl_af_ops, list) { if (af_ops->fill_stats_af) { struct nlattr *af; - int err; af = nla_nest_start_noflag(skb, af_ops->family); if (!af) { rcu_read_unlock(); + err = -EMSGSIZE; goto nla_put_failure; } err = af_ops->fill_stats_af(skb, dev); @@ -5280,13 +5489,14 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev, else nlmsg_end(skb, nlh); - return -EMSGSIZE; + return err; } static size_t if_nlmsg_stats_size(const struct net_device *dev, - u32 filter_mask) + const struct rtnl_stats_dump_filters *filters) { size_t size = NLMSG_ALIGN(sizeof(struct if_stats_msg)); + unsigned int filter_mask = filters->mask[0]; if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_64, 0)) size += nla_total_size_64bit(sizeof(struct rtnl_link_stats64)); @@ -5322,8 +5532,12 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev, } } - if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_OFFLOAD_XSTATS, 0)) - size += rtnl_get_offload_stats_size(dev); + if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_OFFLOAD_XSTATS, 0)) { + u32 off_filter_mask; + + off_filter_mask = filters->mask[IFLA_STATS_LINK_OFFLOAD_XSTATS]; + size += rtnl_offload_xstats_get_size(dev, off_filter_mask); + } if (stats_attr_valid(filter_mask, IFLA_STATS_AF_SPEC, 0)) { struct rtnl_af_ops *af_ops; @@ -5347,6 +5561,79 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev, return size; } +#define RTNL_STATS_OFFLOAD_XSTATS_VALID ((1 << __IFLA_OFFLOAD_XSTATS_MAX) - 1) + +static const struct nla_policy +rtnl_stats_get_policy_filters[IFLA_STATS_MAX + 1] = { + [IFLA_STATS_LINK_OFFLOAD_XSTATS] = + NLA_POLICY_MASK(NLA_U32, RTNL_STATS_OFFLOAD_XSTATS_VALID), +}; + +static const struct nla_policy +rtnl_stats_get_policy[IFLA_STATS_GETSET_MAX + 1] = { + [IFLA_STATS_GET_FILTERS] = + NLA_POLICY_NESTED(rtnl_stats_get_policy_filters), +}; + +static const struct nla_policy +ifla_stats_set_policy[IFLA_STATS_GETSET_MAX + 1] = { + [IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS] = NLA_POLICY_MAX(NLA_U8, 1), +}; + +static int rtnl_stats_get_parse_filters(struct nlattr *ifla_filters, + struct rtnl_stats_dump_filters *filters, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[IFLA_STATS_MAX + 1]; + int err; + int at; + + err = nla_parse_nested(tb, IFLA_STATS_MAX, ifla_filters, + rtnl_stats_get_policy_filters, extack); + if (err < 0) + return err; + + for (at = 1; at <= IFLA_STATS_MAX; at++) { + if (tb[at]) { + if (!(filters->mask[0] & IFLA_STATS_FILTER_BIT(at))) { + NL_SET_ERR_MSG(extack, "Filtered attribute not enabled in filter_mask"); + return -EINVAL; + } + filters->mask[at] = nla_get_u32(tb[at]); + } + } + + return 0; +} + +static int rtnl_stats_get_parse(const struct nlmsghdr *nlh, + u32 filter_mask, + struct rtnl_stats_dump_filters *filters, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[IFLA_STATS_GETSET_MAX + 1]; + int err; + int i; + + filters->mask[0] = filter_mask; + for (i = 1; i < ARRAY_SIZE(filters->mask); i++) + filters->mask[i] = -1U; + + err = nlmsg_parse(nlh, sizeof(struct if_stats_msg), tb, + IFLA_STATS_GETSET_MAX, rtnl_stats_get_policy, extack); + if (err < 0) + return err; + + if (tb[IFLA_STATS_GET_FILTERS]) { + err = rtnl_stats_get_parse_filters(tb[IFLA_STATS_GET_FILTERS], + filters, extack); + if (err) + return err; + } + + return 0; +} + static int rtnl_valid_stats_req(const struct nlmsghdr *nlh, bool strict_check, bool is_dump, struct netlink_ext_ack *extack) { @@ -5369,10 +5656,6 @@ static int rtnl_valid_stats_req(const struct nlmsghdr *nlh, bool strict_check, NL_SET_ERR_MSG(extack, "Invalid values in header for stats dump request"); return -EINVAL; } - if (nlmsg_attrlen(nlh, sizeof(*ifsm))) { - NL_SET_ERR_MSG(extack, "Invalid attributes after stats header"); - return -EINVAL; - } if (ifsm->filter_mask >= IFLA_STATS_FILTER_BIT(IFLA_STATS_MAX + 1)) { NL_SET_ERR_MSG(extack, "Invalid stats requested through filter mask"); return -EINVAL; @@ -5384,12 +5667,12 @@ static int rtnl_valid_stats_req(const struct nlmsghdr *nlh, bool strict_check, static int rtnl_stats_get(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { + struct rtnl_stats_dump_filters filters; struct net *net = sock_net(skb->sk); struct net_device *dev = NULL; int idxattr = 0, prividx = 0; struct if_stats_msg *ifsm; struct sk_buff *nskb; - u32 filter_mask; int err; err = rtnl_valid_stats_req(nlh, netlink_strict_get_check(skb), @@ -5406,17 +5689,22 @@ static int rtnl_stats_get(struct sk_buff *skb, struct nlmsghdr *nlh, if (!dev) return -ENODEV; - filter_mask = ifsm->filter_mask; - if (!filter_mask) + if (!ifsm->filter_mask) { + NL_SET_ERR_MSG(extack, "Filter mask must be set for stats get"); return -EINVAL; + } - nskb = nlmsg_new(if_nlmsg_stats_size(dev, filter_mask), GFP_KERNEL); + err = rtnl_stats_get_parse(nlh, ifsm->filter_mask, &filters, extack); + if (err) + return err; + + nskb = nlmsg_new(if_nlmsg_stats_size(dev, &filters), GFP_KERNEL); if (!nskb) return -ENOBUFS; err = rtnl_fill_statsinfo(nskb, dev, RTM_NEWSTATS, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0, - 0, filter_mask, &idxattr, &prividx); + 0, &filters, &idxattr, &prividx, extack); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_stats_size */ WARN_ON(err == -EMSGSIZE); @@ -5432,12 +5720,12 @@ static int rtnl_stats_dump(struct sk_buff *skb, struct netlink_callback *cb) { struct netlink_ext_ack *extack = cb->extack; int h, s_h, err, s_idx, s_idxattr, s_prividx; + struct rtnl_stats_dump_filters filters; struct net *net = sock_net(skb->sk); unsigned int flags = NLM_F_MULTI; struct if_stats_msg *ifsm; struct hlist_head *head; struct net_device *dev; - u32 filter_mask = 0; int idx = 0; s_h = cb->args[0]; @@ -5452,12 +5740,16 @@ static int rtnl_stats_dump(struct sk_buff *skb, struct netlink_callback *cb) return err; ifsm = nlmsg_data(cb->nlh); - filter_mask = ifsm->filter_mask; - if (!filter_mask) { + if (!ifsm->filter_mask) { NL_SET_ERR_MSG(extack, "Filter mask must be set for stats dump"); return -EINVAL; } + err = rtnl_stats_get_parse(cb->nlh, ifsm->filter_mask, &filters, + extack); + if (err) + return err; + for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { idx = 0; head = &net->dev_index_head[h]; @@ -5467,8 +5759,9 @@ static int rtnl_stats_dump(struct sk_buff *skb, struct netlink_callback *cb) err = rtnl_fill_statsinfo(skb, dev, RTM_NEWSTATS, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 0, - flags, filter_mask, - &s_idxattr, &s_prividx); + flags, &filters, + &s_idxattr, &s_prividx, + extack); /* If we ran out of room on the first message, * we're in trouble */ @@ -5492,6 +5785,107 @@ static int rtnl_stats_dump(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; } +void rtnl_offload_xstats_notify(struct net_device *dev) +{ + struct rtnl_stats_dump_filters response_filters = {}; + struct net *net = dev_net(dev); + int idxattr = 0, prividx = 0; + struct sk_buff *skb; + int err = -ENOBUFS; + + ASSERT_RTNL(); + + response_filters.mask[0] |= + IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_OFFLOAD_XSTATS); + response_filters.mask[IFLA_STATS_LINK_OFFLOAD_XSTATS] |= + IFLA_STATS_FILTER_BIT(IFLA_OFFLOAD_XSTATS_HW_S_INFO); + + skb = nlmsg_new(if_nlmsg_stats_size(dev, &response_filters), + GFP_KERNEL); + if (!skb) + goto errout; + + err = rtnl_fill_statsinfo(skb, dev, RTM_NEWSTATS, 0, 0, 0, 0, + &response_filters, &idxattr, &prividx, NULL); + if (err < 0) { + kfree_skb(skb); + goto errout; + } + + rtnl_notify(skb, net, 0, RTNLGRP_STATS, NULL, GFP_KERNEL); + return; + +errout: + rtnl_set_sk_err(net, RTNLGRP_STATS, err); +} +EXPORT_SYMBOL(rtnl_offload_xstats_notify); + +static int rtnl_stats_set(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) +{ + enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3; + struct rtnl_stats_dump_filters response_filters = {}; + struct nlattr *tb[IFLA_STATS_GETSET_MAX + 1]; + struct net *net = sock_net(skb->sk); + struct net_device *dev = NULL; + struct if_stats_msg *ifsm; + bool notify = false; + int err; + + err = rtnl_valid_stats_req(nlh, netlink_strict_get_check(skb), + false, extack); + if (err) + return err; + + ifsm = nlmsg_data(nlh); + if (ifsm->family != AF_UNSPEC) { + NL_SET_ERR_MSG(extack, "Address family should be AF_UNSPEC"); + return -EINVAL; + } + + if (ifsm->ifindex > 0) + dev = __dev_get_by_index(net, ifsm->ifindex); + else + return -EINVAL; + + if (!dev) + return -ENODEV; + + if (ifsm->filter_mask) { + NL_SET_ERR_MSG(extack, "Filter mask must be 0 for stats set"); + return -EINVAL; + } + + err = nlmsg_parse(nlh, sizeof(*ifsm), tb, IFLA_STATS_GETSET_MAX, + ifla_stats_set_policy, extack); + if (err < 0) + return err; + + if (tb[IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS]) { + u8 req = nla_get_u8(tb[IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS]); + + if (req) + err = netdev_offload_xstats_enable(dev, t_l3, extack); + else + err = netdev_offload_xstats_disable(dev, t_l3); + + if (!err) + notify = true; + else if (err != -EALREADY) + return err; + + response_filters.mask[0] |= + IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_OFFLOAD_XSTATS); + response_filters.mask[IFLA_STATS_LINK_OFFLOAD_XSTATS] |= + IFLA_STATS_FILTER_BIT(IFLA_OFFLOAD_XSTATS_HW_S_INFO); + } + + if (notify) + rtnl_offload_xstats_notify(dev); + + return 0; +} + /* Process one rtnetlink message. */ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -5717,4 +6111,5 @@ void __init rtnetlink_init(void) rtnl_register(PF_UNSPEC, RTM_GETSTATS, rtnl_stats_get, rtnl_stats_dump, 0); + rtnl_register(PF_UNSPEC, RTM_SETSTATS, rtnl_stats_set, NULL, 0); } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ea51e23e9247..10bde7c6db44 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -201,7 +201,7 @@ static void __build_skb_around(struct sk_buff *skb, void *data, skb->head = data; skb->data = data; skb_reset_tail_pointer(skb); - skb->end = skb->tail + size; + skb_set_end_offset(skb, size); skb->mac_header = (typeof(skb->mac_header))~0U; skb->transport_header = (typeof(skb->transport_header))~0U; @@ -777,16 +777,17 @@ void kfree_skb_reason(struct sk_buff *skb, enum skb_drop_reason reason) } EXPORT_SYMBOL(kfree_skb_reason); -void kfree_skb_list(struct sk_buff *segs) +void kfree_skb_list_reason(struct sk_buff *segs, + enum skb_drop_reason reason) { while (segs) { struct sk_buff *next = segs->next; - kfree_skb(segs); + kfree_skb_reason(segs, reason); segs = next; } } -EXPORT_SYMBOL(kfree_skb_list); +EXPORT_SYMBOL(kfree_skb_list_reason); /* Dump skb information and contents. * @@ -1736,11 +1737,10 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, skb->head = data; skb->head_frag = 0; skb->data += off; + + skb_set_end_offset(skb, size); #ifdef NET_SKBUFF_DATA_USES_OFFSET - skb->end = size; off = nhead; -#else - skb->end = skb->head + size; #endif skb->tail += off; skb_headers_offset_update(skb, nhead); @@ -1788,6 +1788,38 @@ struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom) } EXPORT_SYMBOL(skb_realloc_headroom); +int __skb_unclone_keeptruesize(struct sk_buff *skb, gfp_t pri) +{ + unsigned int saved_end_offset, saved_truesize; + struct skb_shared_info *shinfo; + int res; + + saved_end_offset = skb_end_offset(skb); + saved_truesize = skb->truesize; + + res = pskb_expand_head(skb, 0, 0, pri); + if (res) + return res; + + skb->truesize = saved_truesize; + + if (likely(skb_end_offset(skb) == saved_end_offset)) + return 0; + + shinfo = skb_shinfo(skb); + + /* We are about to change back skb->end, + * we need to move skb_shinfo() to its new location. + */ + memmove(skb->head + saved_end_offset, + shinfo, + offsetof(struct skb_shared_info, frags[shinfo->nr_frags])); + + skb_set_end_offset(skb, saved_end_offset); + + return 0; +} + /** * skb_expand_head - reallocate header of &sk_buff * @skb: buffer to reallocate @@ -4820,7 +4852,7 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb, if (hwtstamps) *skb_hwtstamps(skb) = *hwtstamps; else - skb->tstamp = ktime_get_real(); + __net_timestamp(skb); __skb_complete_tx_timestamp(skb, sk, tstype, opt_stats); } @@ -5350,7 +5382,7 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet) ipvs_reset(skb); skb->mark = 0; - skb->tstamp = 0; + skb_clear_tstamp(skb); } EXPORT_SYMBOL_GPL(skb_scrub_packet); @@ -6044,11 +6076,7 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off, skb->head = data; skb->data = data; skb->head_frag = 0; -#ifdef NET_SKBUFF_DATA_USES_OFFSET - skb->end = size; -#else - skb->end = skb->head + size; -#endif + skb_set_end_offset(skb, size); skb_set_tail_pointer(skb, skb_headlen(skb)); skb_headers_offset_update(skb, 0); skb->cloned = 0; @@ -6186,11 +6214,7 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off, skb->head = data; skb->head_frag = 0; skb->data = data; -#ifdef NET_SKBUFF_DATA_USES_OFFSET - skb->end = size; -#else - skb->end = skb->head + size; -#endif + skb_set_end_offset(skb, size); skb_reset_tail_pointer(skb); skb_headers_offset_update(skb, 0); skb->cloned = 0; diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 929a2b096b04..cc381165ea08 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -27,6 +27,7 @@ int sk_msg_alloc(struct sock *sk, struct sk_msg *msg, int len, int elem_first_coalesce) { struct page_frag *pfrag = sk_page_frag(sk); + u32 osize = msg->sg.size; int ret = 0; len -= msg->sg.size; @@ -35,13 +36,17 @@ int sk_msg_alloc(struct sock *sk, struct sk_msg *msg, int len, u32 orig_offset; int use, i; - if (!sk_page_frag_refill(sk, pfrag)) - return -ENOMEM; + if (!sk_page_frag_refill(sk, pfrag)) { + ret = -ENOMEM; + goto msg_trim; + } orig_offset = pfrag->offset; use = min_t(int, len, pfrag->size - orig_offset); - if (!sk_wmem_schedule(sk, use)) - return -ENOMEM; + if (!sk_wmem_schedule(sk, use)) { + ret = -ENOMEM; + goto msg_trim; + } i = msg->sg.end; sk_msg_iter_var_prev(i); @@ -71,6 +76,10 @@ int sk_msg_alloc(struct sock *sk, struct sk_msg *msg, int len, } return ret; + +msg_trim: + sk_msg_trim(sk, msg, osize); + return ret; } EXPORT_SYMBOL_GPL(sk_msg_alloc); diff --git a/net/core/sock.c b/net/core/sock.c index 6eb174805bf0..1180a0cb0110 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1377,9 +1377,9 @@ int sock_setsockopt(struct socket *sock, int level, int optname, if (!(sk_is_tcp(sk) || (sk->sk_type == SOCK_DGRAM && sk->sk_protocol == IPPROTO_UDP))) - ret = -ENOTSUPP; + ret = -EOPNOTSUPP; } else if (sk->sk_family != PF_RDS) { - ret = -ENOTSUPP; + ret = -EOPNOTSUPP; } if (!ret) { if (val < 0 || val > 1) @@ -1447,6 +1447,15 @@ int sock_setsockopt(struct socket *sock, int level, int optname, break; } + case SO_TXREHASH: + if (val < -1 || val > 1) { + ret = -EINVAL; + break; + } + /* Paired with READ_ONCE() in tcp_rtx_synack() */ + WRITE_ONCE(sk->sk_txrehash, (u8)val); + break; + default: ret = -ENOPROTOOPT; break; @@ -1834,6 +1843,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.val = sk->sk_reserved_mem; break; + case SO_TXREHASH: + v.val = sk->sk_txrehash; + break; + default: /* We implement the SO_SNDLOWAT etc to not be settable * (1003.1g 7). @@ -2266,6 +2279,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst) sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM; /* pairs with the WRITE_ONCE() in netif_set_gso_max_size() */ sk->sk_gso_max_size = READ_ONCE(dst->dev->gso_max_size); + sk->sk_gso_max_size -= (MAX_TCP_HEADER + 1); /* pairs with the WRITE_ONCE() in netif_set_gso_max_segs() */ max_segs = max_t(u32, READ_ONCE(dst->dev->gso_max_segs), 1); } @@ -2611,7 +2625,8 @@ int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg, switch (cmsg->cmsg_type) { case SO_MARK: - if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) + if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) && + !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return -EPERM; if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32))) return -EINVAL; @@ -3278,6 +3293,7 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_pacing_rate = ~0UL; WRITE_ONCE(sk->sk_pacing_shift, 10); sk->sk_incoming_cpu = -1; + sk->sk_txrehash = SOCK_TXREHASH_DEFAULT; sk_rx_queue_clear(sk); /* @@ -3702,6 +3718,10 @@ int proto_register(struct proto *prot, int alloc_slab) { int ret = -ENOBUFS; + if (prot->memory_allocated && !prot->sysctl_mem) { + pr_err("%s: missing sysctl_mem\n", prot->name); + return -EINVAL; + } if (alloc_slab) { prot->slab = kmem_cache_create_usercopy(prot->name, prot->obj_size, 0, diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 1827669eedd6..2d213c4011db 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -1416,38 +1416,50 @@ static struct sk_psock_progs *sock_map_progs(struct bpf_map *map) return NULL; } -static int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog, - struct bpf_prog *old, u32 which) +static int sock_map_prog_lookup(struct bpf_map *map, struct bpf_prog ***pprog, + u32 which) { struct sk_psock_progs *progs = sock_map_progs(map); - struct bpf_prog **pprog; if (!progs) return -EOPNOTSUPP; switch (which) { case BPF_SK_MSG_VERDICT: - pprog = &progs->msg_parser; + *pprog = &progs->msg_parser; break; #if IS_ENABLED(CONFIG_BPF_STREAM_PARSER) case BPF_SK_SKB_STREAM_PARSER: - pprog = &progs->stream_parser; + *pprog = &progs->stream_parser; break; #endif case BPF_SK_SKB_STREAM_VERDICT: if (progs->skb_verdict) return -EBUSY; - pprog = &progs->stream_verdict; + *pprog = &progs->stream_verdict; break; case BPF_SK_SKB_VERDICT: if (progs->stream_verdict) return -EBUSY; - pprog = &progs->skb_verdict; + *pprog = &progs->skb_verdict; break; default: return -EOPNOTSUPP; } + return 0; +} + +static int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog, + struct bpf_prog *old, u32 which) +{ + struct bpf_prog **pprog; + int ret; + + ret = sock_map_prog_lookup(map, &pprog, which); + if (ret) + return ret; + if (old) return psock_replace_prog(pprog, prog, old); @@ -1455,6 +1467,57 @@ static int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog, return 0; } +int sock_map_bpf_prog_query(const union bpf_attr *attr, + union bpf_attr __user *uattr) +{ + __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids); + u32 prog_cnt = 0, flags = 0, ufd = attr->target_fd; + struct bpf_prog **pprog; + struct bpf_prog *prog; + struct bpf_map *map; + struct fd f; + u32 id = 0; + int ret; + + if (attr->query.query_flags) + return -EINVAL; + + f = fdget(ufd); + map = __bpf_map_get(f); + if (IS_ERR(map)) + return PTR_ERR(map); + + rcu_read_lock(); + + ret = sock_map_prog_lookup(map, &pprog, attr->query.attach_type); + if (ret) + goto end; + + prog = *pprog; + prog_cnt = !prog ? 0 : 1; + + if (!attr->query.prog_cnt || !prog_ids || !prog_cnt) + goto end; + + /* we do not hold the refcnt, the bpf prog may be released + * asynchronously and the id would be set to 0. + */ + id = data_race(prog->aux->id); + if (id == 0) + prog_cnt = 0; + +end: + rcu_read_unlock(); + + if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)) || + (id != 0 && copy_to_user(prog_ids, &id, sizeof(u32))) || + copy_to_user(&uattr->query.prog_cnt, &prog_cnt, sizeof(prog_cnt))) + ret = -EFAULT; + + fdput(f); + return ret; +} + static void sock_map_unlink(struct sock *sk, struct sk_psock_link *link) { switch (link->map->map_type) { diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 7b4d485aac7a..7123fe7feeac 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -103,8 +103,7 @@ static int rps_sock_flow_sysctl(struct ctl_table *table, int write, if (orig_sock_table) { static_branch_dec(&rps_needed); static_branch_dec(&rfs_needed); - synchronize_rcu(); - vfree(orig_sock_table); + kvfree_rcu(orig_sock_table); } } } @@ -142,8 +141,7 @@ static int flow_limit_cpu_sysctl(struct ctl_table *table, int write, lockdep_is_held(&flow_limit_update_mutex)); if (cur && !cpumask_test_cpu(i, mask)) { RCU_INIT_POINTER(sd->flow_limit, NULL); - synchronize_rcu(); - kfree(cur); + kfree_rcu(cur); } else if (!cur && cpumask_test_cpu(i, mask)) { cur = kzalloc_node(len, GFP_KERNEL, cpu_to_node(i)); @@ -593,6 +591,15 @@ static struct ctl_table netns_core_table[] = { .extra1 = SYSCTL_ZERO, .proc_handler = proc_dointvec_minmax }, + { + .procname = "txrehash", + .data = &init_net.core.sysctl_txrehash, + .maxlen = sizeof(u8), + .mode = 0644, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + .proc_handler = proc_dou8vec_minmax, + }, { } }; @@ -611,7 +618,7 @@ __setup("fb_tunnels=", fb_tunnels_only_for_init_net_sysctl_setup); static __net_init int sysctl_core_net_init(struct net *net) { - struct ctl_table *tbl; + struct ctl_table *tbl, *tmp; tbl = netns_core_table; if (!net_eq(net, &init_net)) { @@ -619,7 +626,8 @@ static __net_init int sysctl_core_net_init(struct net *net) if (tbl == NULL) goto err_dup; - tbl[0].data = &net->core.sysctl_somaxconn; + for (tmp = tbl; tmp->procname; tmp++) + tmp->data += (char *)net - (char *)&init_net; /* Don't export any sysctls to unprivileged users */ if (net->user_ns != &init_user_ns) { diff --git a/net/core/utils.c b/net/core/utils.c index 1f31a39236d5..938495bc1d34 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -476,9 +476,9 @@ void inet_proto_csum_replace_by_diff(__sum16 *sum, struct sk_buff *skb, __wsum diff, bool pseudohdr) { if (skb->ip_summed != CHECKSUM_PARTIAL) { - *sum = csum_fold(csum_add(diff, ~csum_unfold(*sum))); + csum_replace_by_diff(sum, diff); if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr) - skb->csum = ~csum_add(diff, ~skb->csum); + skb->csum = ~csum_sub(diff, skb->csum); } else if (pseudohdr) { *sum = ~csum_fold(csum_add(diff, csum_unfold(*sum))); } diff --git a/net/core/xdp.c b/net/core/xdp.c index 73fae16264e1..24420209bf0e 100644 --- a/net/core/xdp.c +++ b/net/core/xdp.c @@ -162,8 +162,9 @@ static void xdp_rxq_info_init(struct xdp_rxq_info *xdp_rxq) } /* Returns 0 on success, negative on failure */ -int xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq, - struct net_device *dev, u32 queue_index, unsigned int napi_id) +int __xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq, + struct net_device *dev, u32 queue_index, + unsigned int napi_id, u32 frag_size) { if (!dev) { WARN(1, "Missing net_device from driver"); @@ -185,11 +186,12 @@ int xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq, xdp_rxq->dev = dev; xdp_rxq->queue_index = queue_index; xdp_rxq->napi_id = napi_id; + xdp_rxq->frag_size = frag_size; xdp_rxq->reg_state = REG_STATE_REGISTERED; return 0; } -EXPORT_SYMBOL_GPL(xdp_rxq_info_reg); +EXPORT_SYMBOL_GPL(__xdp_rxq_info_reg); void xdp_rxq_info_unused(struct xdp_rxq_info *xdp_rxq) { @@ -370,8 +372,8 @@ EXPORT_SYMBOL_GPL(xdp_rxq_info_reg_mem_model); * is used for those calls sites. Thus, allowing for faster recycling * of xdp_frames/pages in those cases. */ -static void __xdp_return(void *data, struct xdp_mem_info *mem, bool napi_direct, - struct xdp_buff *xdp) +void __xdp_return(void *data, struct xdp_mem_info *mem, bool napi_direct, + struct xdp_buff *xdp) { struct xdp_mem_allocator *xa; struct page *page; @@ -407,12 +409,38 @@ static void __xdp_return(void *data, struct xdp_mem_info *mem, bool napi_direct, void xdp_return_frame(struct xdp_frame *xdpf) { + struct skb_shared_info *sinfo; + int i; + + if (likely(!xdp_frame_has_frags(xdpf))) + goto out; + + sinfo = xdp_get_shared_info_from_frame(xdpf); + for (i = 0; i < sinfo->nr_frags; i++) { + struct page *page = skb_frag_page(&sinfo->frags[i]); + + __xdp_return(page_address(page), &xdpf->mem, false, NULL); + } +out: __xdp_return(xdpf->data, &xdpf->mem, false, NULL); } EXPORT_SYMBOL_GPL(xdp_return_frame); void xdp_return_frame_rx_napi(struct xdp_frame *xdpf) { + struct skb_shared_info *sinfo; + int i; + + if (likely(!xdp_frame_has_frags(xdpf))) + goto out; + + sinfo = xdp_get_shared_info_from_frame(xdpf); + for (i = 0; i < sinfo->nr_frags; i++) { + struct page *page = skb_frag_page(&sinfo->frags[i]); + + __xdp_return(page_address(page), &xdpf->mem, true, NULL); + } +out: __xdp_return(xdpf->data, &xdpf->mem, true, NULL); } EXPORT_SYMBOL_GPL(xdp_return_frame_rx_napi); @@ -448,7 +476,7 @@ void xdp_return_frame_bulk(struct xdp_frame *xdpf, struct xdp_mem_allocator *xa; if (mem->type != MEM_TYPE_PAGE_POOL) { - __xdp_return(xdpf->data, &xdpf->mem, false, NULL); + xdp_return_frame(xdpf); return; } @@ -467,14 +495,41 @@ void xdp_return_frame_bulk(struct xdp_frame *xdpf, bq->xa = rhashtable_lookup(mem_id_ht, &mem->id, mem_id_rht_params); } + if (unlikely(xdp_frame_has_frags(xdpf))) { + struct skb_shared_info *sinfo; + int i; + + sinfo = xdp_get_shared_info_from_frame(xdpf); + for (i = 0; i < sinfo->nr_frags; i++) { + skb_frag_t *frag = &sinfo->frags[i]; + + bq->q[bq->count++] = skb_frag_address(frag); + if (bq->count == XDP_BULK_QUEUE_SIZE) + xdp_flush_frame_bulk(bq); + } + } bq->q[bq->count++] = xdpf->data; } EXPORT_SYMBOL_GPL(xdp_return_frame_bulk); void xdp_return_buff(struct xdp_buff *xdp) { + struct skb_shared_info *sinfo; + int i; + + if (likely(!xdp_buff_has_frags(xdp))) + goto out; + + sinfo = xdp_get_shared_info_from_buff(xdp); + for (i = 0; i < sinfo->nr_frags; i++) { + struct page *page = skb_frag_page(&sinfo->frags[i]); + + __xdp_return(page_address(page), &xdp->rxq->mem, true, xdp); + } +out: __xdp_return(xdp->data, &xdp->rxq->mem, true, xdp); } +EXPORT_SYMBOL_GPL(xdp_return_buff); /* Only called for MEM_TYPE_PAGE_POOL see xdp.h */ void __xdp_release_frame(void *data, struct xdp_mem_info *mem) @@ -562,8 +617,14 @@ struct sk_buff *__xdp_build_skb_from_frame(struct xdp_frame *xdpf, struct sk_buff *skb, struct net_device *dev) { + struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf); unsigned int headroom, frame_size; void *hard_start; + u8 nr_frags; + + /* xdp frags frame */ + if (unlikely(xdp_frame_has_frags(xdpf))) + nr_frags = sinfo->nr_frags; /* Part of headroom was reserved to xdpf */ headroom = sizeof(*xdpf) + xdpf->headroom; @@ -583,6 +644,12 @@ struct sk_buff *__xdp_build_skb_from_frame(struct xdp_frame *xdpf, if (xdpf->metasize) skb_metadata_set(skb, xdpf->metasize); + if (unlikely(xdp_frame_has_frags(xdpf))) + xdp_update_skb_shared_info(skb, nr_frags, + sinfo->xdp_frags_size, + nr_frags * xdpf->frame_sz, + xdp_frame_is_frag_pfmemalloc(xdpf)); + /* Essential SKB info: protocol and skb->dev */ skb->protocol = eth_type_trans(skb, dev); diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 5183e627468d..671c377f0889 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -136,11 +136,6 @@ static inline int between48(const u64 seq1, const u64 seq2, const u64 seq3) return (seq3 << 16) - (seq2 << 16) >= (seq1 << 16) - (seq2 << 16); } -static inline u64 max48(const u64 seq1, const u64 seq2) -{ - return after48(seq1, seq2) ? seq1 : seq2; -} - /** * dccp_loss_count - Approximate the number of lost data packets in a burst loss * @s1: last known sequence number before the loss ('hole') diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 0ea29270d7e5..ae662567a6cb 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -1030,15 +1030,9 @@ static void __net_exit dccp_v4_exit_net(struct net *net) inet_ctl_sock_destroy(pn->v4_ctl_sk); } -static void __net_exit dccp_v4_exit_batch(struct list_head *net_exit_list) -{ - inet_twsk_purge(&dccp_hashinfo, AF_INET); -} - static struct pernet_operations dccp_v4_ops = { .init = dccp_v4_init_net, .exit = dccp_v4_exit_net, - .exit_batch = dccp_v4_exit_batch, .id = &dccp_v4_pernet_id, .size = sizeof(struct dccp_v4_pernet), }; diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index fa663518fa0e..eab3bd1ee9a0 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -1115,15 +1115,9 @@ static void __net_exit dccp_v6_exit_net(struct net *net) inet_ctl_sock_destroy(pn->v6_ctl_sk); } -static void __net_exit dccp_v6_exit_batch(struct list_head *net_exit_list) -{ - inet_twsk_purge(&dccp_hashinfo, AF_INET6); -} - static struct pernet_operations dccp_v6_ops = { .init = dccp_v6_init_net, .exit = dccp_v6_exit_net, - .exit_batch = dccp_v6_exit_batch, .id = &dccp_v6_pernet_id, .size = sizeof(struct dccp_v6_pernet), }; diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 91e7a2202697..64d805b27add 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -22,6 +22,7 @@ #include "feat.h" struct inet_timewait_death_row dccp_death_row = { + .tw_refcount = REFCOUNT_INIT(1), .sysctl_max_tw_buckets = NR_FILE * 2, .hashinfo = &dccp_hashinfo, }; diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c index eadc89583168..b05639bdfc8f 100644 --- a/net/decnet/dn_nsp_out.c +++ b/net/decnet/dn_nsp_out.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -351,7 +352,7 @@ void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, * Slow start: If we have been idle for more than * one RTT, then reset window to min size. */ - if ((jiffies - scp->stamp) > t) + if (time_is_before_jiffies(scp->stamp + t)) scp->snd_window = NSP_MIN_WINDOW; if (oth) diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index c43f7446a75d..89c6c86e746f 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -467,6 +467,106 @@ struct dsa_port *dsa_port_from_netdev(struct net_device *netdev) } EXPORT_SYMBOL_GPL(dsa_port_from_netdev); +int dsa_port_walk_fdbs(struct dsa_switch *ds, int port, dsa_fdb_walk_cb_t cb) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct dsa_mac_addr *a; + int err = 0; + + mutex_lock(&dp->addr_lists_lock); + + list_for_each_entry(a, &dp->fdbs, list) { + err = cb(ds, port, a->addr, a->vid, a->db); + if (err) + break; + } + + mutex_unlock(&dp->addr_lists_lock); + + return err; +} +EXPORT_SYMBOL_GPL(dsa_port_walk_fdbs); + +int dsa_port_walk_mdbs(struct dsa_switch *ds, int port, dsa_fdb_walk_cb_t cb) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct dsa_mac_addr *a; + int err = 0; + + mutex_lock(&dp->addr_lists_lock); + + list_for_each_entry(a, &dp->mdbs, list) { + err = cb(ds, port, a->addr, a->vid, a->db); + if (err) + break; + } + + mutex_unlock(&dp->addr_lists_lock); + + return err; +} +EXPORT_SYMBOL_GPL(dsa_port_walk_mdbs); + +bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b) +{ + if (a->type != b->type) + return false; + + switch (a->type) { + case DSA_DB_PORT: + return a->dp == b->dp; + case DSA_DB_LAG: + return a->lag.dev == b->lag.dev; + case DSA_DB_BRIDGE: + return a->bridge.num == b->bridge.num; + default: + WARN_ON(1); + return false; + } +} + +bool dsa_fdb_present_in_other_db(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct dsa_mac_addr *a; + + lockdep_assert_held(&dp->addr_lists_lock); + + list_for_each_entry(a, &dp->fdbs, list) { + if (!ether_addr_equal(a->addr, addr) || a->vid != vid) + continue; + + if (a->db.type == db.type && !dsa_db_equal(&a->db, &db)) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(dsa_fdb_present_in_other_db); + +bool dsa_mdb_present_in_other_db(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct dsa_mac_addr *a; + + lockdep_assert_held(&dp->addr_lists_lock); + + list_for_each_entry(a, &dp->mdbs, list) { + if (!ether_addr_equal(a->addr, mdb->addr) || a->vid != mdb->vid) + continue; + + if (a->db.type == db.type && !dsa_db_equal(&a->db, &db)) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(dsa_mdb_present_in_other_db); + static int __init dsa_init_module(void) { int rc; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 88e2808019b4..ca6af86964bc 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "dsa_priv.h" @@ -71,27 +72,24 @@ int dsa_broadcast(unsigned long e, void *v) } /** - * dsa_lag_map() - Map LAG netdev to a linear LAG ID + * dsa_lag_map() - Map LAG structure to a linear LAG array * @dst: Tree in which to record the mapping. - * @lag: Netdev that is to be mapped to an ID. + * @lag: LAG structure that is to be mapped to the tree's array. * - * dsa_lag_id/dsa_lag_dev can then be used to translate between the + * dsa_lag_id/dsa_lag_by_id can then be used to translate between the * two spaces. The size of the mapping space is determined by the * driver by setting ds->num_lag_ids. It is perfectly legal to leave * it unset if it is not needed, in which case these functions become * no-ops. */ -void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag) +void dsa_lag_map(struct dsa_switch_tree *dst, struct dsa_lag *lag) { unsigned int id; - if (dsa_lag_id(dst, lag) >= 0) - /* Already mapped */ - return; - - for (id = 0; id < dst->lags_len; id++) { - if (!dsa_lag_dev(dst, id)) { - dst->lags[id] = lag; + for (id = 1; id <= dst->lags_len; id++) { + if (!dsa_lag_by_id(dst, id)) { + dst->lags[id - 1] = lag; + lag->id = id; return; } } @@ -107,28 +105,36 @@ void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag) /** * dsa_lag_unmap() - Remove a LAG ID mapping * @dst: Tree in which the mapping is recorded. - * @lag: Netdev that was mapped. + * @lag: LAG structure that was mapped. * * As there may be multiple users of the mapping, it is only removed * if there are no other references to it. */ -void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag) +void dsa_lag_unmap(struct dsa_switch_tree *dst, struct dsa_lag *lag) { - struct dsa_port *dp; unsigned int id; - dsa_lag_foreach_port(dp, dst, lag) - /* There are remaining users of this mapping */ - return; - dsa_lags_foreach_id(id, dst) { - if (dsa_lag_dev(dst, id) == lag) { - dst->lags[id] = NULL; + if (dsa_lag_by_id(dst, id) == lag) { + dst->lags[id - 1] = NULL; + lag->id = 0; break; } } } +struct dsa_lag *dsa_tree_lag_find(struct dsa_switch_tree *dst, + const struct net_device *lag_dev) +{ + struct dsa_port *dp; + + list_for_each_entry(dp, &dst->ports, list) + if (dsa_port_lag_dev_get(dp) == lag_dev) + return dp->lag; + + return NULL; +} + struct dsa_bridge *dsa_tree_bridge_find(struct dsa_switch_tree *dst, const struct net_device *br) { @@ -451,10 +457,6 @@ static int dsa_port_setup(struct dsa_port *dp) if (dp->setup) return 0; - mutex_init(&dp->addr_lists_lock); - INIT_LIST_HEAD(&dp->fdbs); - INIT_LIST_HEAD(&dp->mdbs); - if (ds->ops->port_setup) { err = ds->ops->port_setup(ds, dp->index); if (err) @@ -560,7 +562,6 @@ static void dsa_port_teardown(struct dsa_port *dp) { struct devlink_port *dlp = &dp->devlink_port; struct dsa_switch *ds = dp->ds; - struct dsa_mac_addr *a, *tmp; struct net_device *slave; if (!dp->setup) @@ -592,16 +593,6 @@ static void dsa_port_teardown(struct dsa_port *dp) break; } - list_for_each_entry_safe(a, tmp, &dp->fdbs, list) { - list_del(&a->list); - kfree(a); - } - - list_for_each_entry_safe(a, tmp, &dp->mdbs, list) { - list_del(&a->list); - kfree(a); - } - dp->setup = false; } @@ -1064,9 +1055,18 @@ static int dsa_tree_setup_master(struct dsa_switch_tree *dst) list_for_each_entry(dp, &dst->ports, list) { if (dsa_port_is_cpu(dp)) { - err = dsa_master_setup(dp->master, dp); + struct net_device *master = dp->master; + bool admin_up = (master->flags & IFF_UP) && + !qdisc_tx_is_noop(master); + + err = dsa_master_setup(master, dp); if (err) break; + + /* Replay master state event */ + dsa_tree_master_admin_state_change(dst, master, admin_up); + dsa_tree_master_oper_state_change(dst, master, + netif_oper_up(master)); } } @@ -1081,9 +1081,19 @@ static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) rtnl_lock(); - list_for_each_entry(dp, &dst->ports, list) - if (dsa_port_is_cpu(dp)) - dsa_master_teardown(dp->master); + list_for_each_entry(dp, &dst->ports, list) { + if (dsa_port_is_cpu(dp)) { + struct net_device *master = dp->master; + + /* Synthesizing an "admin down" state is sufficient for + * the switches to get a notification if the master is + * currently up and running. + */ + dsa_tree_master_admin_state_change(dst, master, false); + + dsa_master_teardown(master); + } + } rtnl_unlock(); } @@ -1279,6 +1289,52 @@ int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst, return err; } +static void dsa_tree_master_state_change(struct dsa_switch_tree *dst, + struct net_device *master) +{ + struct dsa_notifier_master_state_info info; + struct dsa_port *cpu_dp = master->dsa_ptr; + + info.master = master; + info.operational = dsa_port_master_is_operational(cpu_dp); + + dsa_tree_notify(dst, DSA_NOTIFIER_MASTER_STATE_CHANGE, &info); +} + +void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst, + struct net_device *master, + bool up) +{ + struct dsa_port *cpu_dp = master->dsa_ptr; + bool notify = false; + + if ((dsa_port_master_is_operational(cpu_dp)) != + (up && cpu_dp->master_oper_up)) + notify = true; + + cpu_dp->master_admin_up = up; + + if (notify) + dsa_tree_master_state_change(dst, master); +} + +void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst, + struct net_device *master, + bool up) +{ + struct dsa_port *cpu_dp = master->dsa_ptr; + bool notify = false; + + if ((dsa_port_master_is_operational(cpu_dp)) != + (cpu_dp->master_admin_up && up)) + notify = true; + + cpu_dp->master_oper_up = up; + + if (notify) + dsa_tree_master_state_change(dst, master); +} + static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index) { struct dsa_switch_tree *dst = ds->dst; @@ -1295,6 +1351,11 @@ static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index) dp->ds = ds; dp->index = index; + mutex_init(&dp->addr_lists_lock); + mutex_init(&dp->vlans_lock); + INIT_LIST_HEAD(&dp->fdbs); + INIT_LIST_HEAD(&dp->mdbs); + INIT_LIST_HEAD(&dp->vlans); INIT_LIST_HEAD(&dp->list); list_add_tail(&dp->list, &dst->ports); @@ -1634,6 +1695,9 @@ static void dsa_switch_release_ports(struct dsa_switch *ds) struct dsa_port *dp, *next; dsa_switch_for_each_port_safe(dp, next, ds) { + WARN_ON(!list_empty(&dp->fdbs)); + WARN_ON(!list_empty(&dp->mdbs)); + WARN_ON(!list_empty(&dp->vlans)); list_del(&dp->list); kfree(dp); } @@ -1722,6 +1786,10 @@ void dsa_switch_shutdown(struct dsa_switch *ds) struct dsa_port *dp; mutex_lock(&dsa2_mutex); + + if (!ds->setup) + goto out; + rtnl_lock(); dsa_switch_for_each_user_port(dp, ds) { @@ -1738,6 +1806,7 @@ void dsa_switch_shutdown(struct dsa_switch *ds) dp->master->dsa_ptr = NULL; rtnl_unlock(); +out: mutex_unlock(&dsa2_mutex); } EXPORT_SYMBOL_GPL(dsa_switch_shutdown); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 23c79e91ac67..5d3f4a67dce1 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -25,6 +25,8 @@ enum { DSA_NOTIFIER_FDB_DEL, DSA_NOTIFIER_HOST_FDB_ADD, DSA_NOTIFIER_HOST_FDB_DEL, + DSA_NOTIFIER_LAG_FDB_ADD, + DSA_NOTIFIER_LAG_FDB_DEL, DSA_NOTIFIER_LAG_CHANGE, DSA_NOTIFIER_LAG_JOIN, DSA_NOTIFIER_LAG_LEAVE, @@ -34,12 +36,15 @@ enum { DSA_NOTIFIER_HOST_MDB_DEL, DSA_NOTIFIER_VLAN_ADD, DSA_NOTIFIER_VLAN_DEL, + DSA_NOTIFIER_HOST_VLAN_ADD, + DSA_NOTIFIER_HOST_VLAN_DEL, DSA_NOTIFIER_MTU, DSA_NOTIFIER_TAG_PROTO, DSA_NOTIFIER_TAG_PROTO_CONNECT, DSA_NOTIFIER_TAG_PROTO_DISCONNECT, DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, + DSA_NOTIFIER_MASTER_STATE_CHANGE, }; /* DSA_NOTIFIER_AGEING_TIME */ @@ -54,6 +59,7 @@ struct dsa_notifier_bridge_info { int sw_index; int port; bool tx_fwd_offload; + struct netlink_ext_ack *extack; }; /* DSA_NOTIFIER_FDB_* */ @@ -62,6 +68,15 @@ struct dsa_notifier_fdb_info { int port; const unsigned char *addr; u16 vid; + struct dsa_db db; +}; + +/* DSA_NOTIFIER_LAG_FDB_* */ +struct dsa_notifier_lag_fdb_info { + struct dsa_lag *lag; + const unsigned char *addr; + u16 vid; + struct dsa_db db; }; /* DSA_NOTIFIER_MDB_* */ @@ -69,11 +84,12 @@ struct dsa_notifier_mdb_info { const struct switchdev_obj_port_mdb *mdb; int sw_index; int port; + struct dsa_db db; }; /* DSA_NOTIFIER_LAG_* */ struct dsa_notifier_lag_info { - struct net_device *lag; + struct dsa_lag lag; int sw_index; int port; @@ -109,10 +125,15 @@ struct dsa_notifier_tag_8021q_vlan_info { u16 vid; }; +/* DSA_NOTIFIER_MASTER_STATE_CHANGE */ +struct dsa_notifier_master_state_info { + const struct net_device *master; + bool operational; +}; + struct dsa_switchdev_event_work { - struct dsa_switch *ds; - int port; struct net_device *dev; + struct net_device *orig_dev; struct work_struct work; unsigned long event; /* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and @@ -123,6 +144,21 @@ struct dsa_switchdev_event_work { bool host_addr; }; +enum dsa_standalone_event { + DSA_UC_ADD, + DSA_UC_DEL, + DSA_MC_ADD, + DSA_MC_DEL, +}; + +struct dsa_standalone_event_work { + struct work_struct work; + struct net_device *dev; + enum dsa_standalone_event event; + unsigned char addr[ETH_ALEN]; + u16 vid; +}; + struct dsa_slave_priv { /* Copy of CPU port xmit for faster access in slave transmit hot path */ struct sk_buff * (*xmit)(struct sk_buff *skb, @@ -146,6 +182,8 @@ const struct dsa_device_ops *dsa_tag_driver_get(int tag_protocol); void dsa_tag_driver_put(const struct dsa_device_ops *ops); const struct dsa_device_ops *dsa_find_tagger_by_name(const char *buf); +bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b); + bool dsa_schedule_work(struct work_struct *work); const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops); @@ -177,6 +215,9 @@ static inline struct net_device *dsa_master_find_slave(struct net_device *dev, void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, const struct dsa_device_ops *tag_ops); int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age); +int dsa_port_set_mst_state(struct dsa_port *dp, + const struct switchdev_mst_state *state, + struct netlink_ext_ack *extack); int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy); int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy); void dsa_port_disable_rt(struct dsa_port *dp); @@ -196,25 +237,41 @@ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, struct netlink_ext_ack *extack); bool dsa_port_skip_vlan_configuration(struct dsa_port *dp); int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock); +int dsa_port_mst_enable(struct dsa_port *dp, bool on, + struct netlink_ext_ack *extack); +int dsa_port_vlan_msti(struct dsa_port *dp, + const struct switchdev_vlan_msti *msti); int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu, bool targeted_match); int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, u16 vid); int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, u16 vid); -int dsa_port_host_fdb_add(struct dsa_port *dp, const unsigned char *addr, - u16 vid); -int dsa_port_host_fdb_del(struct dsa_port *dp, const unsigned char *addr, - u16 vid); +int dsa_port_standalone_host_fdb_add(struct dsa_port *dp, + const unsigned char *addr, u16 vid); +int dsa_port_standalone_host_fdb_del(struct dsa_port *dp, + const unsigned char *addr, u16 vid); +int dsa_port_bridge_host_fdb_add(struct dsa_port *dp, const unsigned char *addr, + u16 vid); +int dsa_port_bridge_host_fdb_del(struct dsa_port *dp, const unsigned char *addr, + u16 vid); +int dsa_port_lag_fdb_add(struct dsa_port *dp, const unsigned char *addr, + u16 vid); +int dsa_port_lag_fdb_del(struct dsa_port *dp, const unsigned char *addr, + u16 vid); int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data); int dsa_port_mdb_add(const struct dsa_port *dp, const struct switchdev_obj_port_mdb *mdb); int dsa_port_mdb_del(const struct dsa_port *dp, const struct switchdev_obj_port_mdb *mdb); -int dsa_port_host_mdb_add(const struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb); -int dsa_port_host_mdb_del(const struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb); +int dsa_port_standalone_host_mdb_add(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb); +int dsa_port_standalone_host_mdb_del(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb); +int dsa_port_bridge_host_mdb_add(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb); +int dsa_port_bridge_host_mdb_del(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb); int dsa_port_pre_bridge_flags(const struct dsa_port *dp, struct switchdev_brport_flags flags, struct netlink_ext_ack *extack); @@ -226,6 +283,11 @@ int dsa_port_vlan_add(struct dsa_port *dp, struct netlink_ext_ack *extack); int dsa_port_vlan_del(struct dsa_port *dp, const struct switchdev_obj_port_vlan *vlan); +int dsa_port_host_vlan_add(struct dsa_port *dp, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack); +int dsa_port_host_vlan_del(struct dsa_port *dp, + const struct switchdev_obj_port_vlan *vlan); int dsa_port_mrp_add(const struct dsa_port *dp, const struct switchdev_obj_mrp *mrp); int dsa_port_mrp_del(const struct dsa_port *dp, @@ -472,15 +534,37 @@ static inline void *dsa_etype_header_pos_tx(struct sk_buff *skb) int dsa_switch_register_notifier(struct dsa_switch *ds); void dsa_switch_unregister_notifier(struct dsa_switch *ds); +static inline bool dsa_switch_supports_uc_filtering(struct dsa_switch *ds) +{ + return ds->ops->port_fdb_add && ds->ops->port_fdb_del && + ds->fdb_isolation && !ds->vlan_filtering_is_global && + !ds->needs_standalone_vlan_filtering; +} + +static inline bool dsa_switch_supports_mc_filtering(struct dsa_switch *ds) +{ + return ds->ops->port_mdb_add && ds->ops->port_mdb_del && + ds->fdb_isolation && !ds->vlan_filtering_is_global && + !ds->needs_standalone_vlan_filtering; +} + /* dsa2.c */ -void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag); -void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag); +void dsa_lag_map(struct dsa_switch_tree *dst, struct dsa_lag *lag); +void dsa_lag_unmap(struct dsa_switch_tree *dst, struct dsa_lag *lag); +struct dsa_lag *dsa_tree_lag_find(struct dsa_switch_tree *dst, + const struct net_device *lag_dev); int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v); int dsa_broadcast(unsigned long e, void *v); int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst, struct net_device *master, const struct dsa_device_ops *tag_ops, const struct dsa_device_ops *old_tag_ops); +void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst, + struct net_device *master, + bool up); +void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst, + struct net_device *master, + bool up); unsigned int dsa_bridge_num_get(const struct net_device *bridge_dev, int max); void dsa_bridge_num_put(const struct net_device *bridge_dev, unsigned int bridge_num); @@ -488,10 +572,6 @@ struct dsa_bridge *dsa_tree_bridge_find(struct dsa_switch_tree *dst, const struct net_device *br); /* tag_8021q.c */ -int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, - struct dsa_notifier_bridge_info *info); -int dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, - struct dsa_notifier_bridge_info *info); int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds, struct dsa_notifier_tag_8021q_vlan_info *info); int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds, diff --git a/net/dsa/master.c b/net/dsa/master.c index 880f910b23a9..991c2930d631 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -335,8 +335,6 @@ static const struct attribute_group dsa_group = { .attrs = dsa_slave_attrs, }; -static struct lock_class_key dsa_master_addr_list_lock_key; - int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp) { struct dsa_switch *ds = cpu_dp->ds; @@ -358,8 +356,6 @@ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp) wmb(); dev->dsa_ptr = cpu_dp; - lockdep_set_class(&dev->addr_list_lock, - &dsa_master_addr_list_lock_key); dsa_master_set_promiscuity(dev, 1); diff --git a/net/dsa/port.c b/net/dsa/port.c index 1a40c52f5a42..32d472a82241 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -30,12 +30,11 @@ static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v) return dsa_tree_notify(dp->ds->dst, e, v); } -static void dsa_port_notify_bridge_fdb_flush(const struct dsa_port *dp) +static void dsa_port_notify_bridge_fdb_flush(const struct dsa_port *dp, u16 vid) { struct net_device *brport_dev = dsa_port_to_bridge_port(dp); struct switchdev_notifier_fdb_info info = { - /* flush all VLANs */ - .vid = 0, + .vid = vid, }; /* When the port becomes standalone it has already left the bridge. @@ -57,7 +56,42 @@ static void dsa_port_fast_age(const struct dsa_port *dp) ds->ops->port_fast_age(ds, dp->index); - dsa_port_notify_bridge_fdb_flush(dp); + /* flush all VLANs */ + dsa_port_notify_bridge_fdb_flush(dp, 0); +} + +static int dsa_port_vlan_fast_age(const struct dsa_port *dp, u16 vid) +{ + struct dsa_switch *ds = dp->ds; + int err; + + if (!ds->ops->port_vlan_fast_age) + return -EOPNOTSUPP; + + err = ds->ops->port_vlan_fast_age(ds, dp->index, vid); + + if (!err) + dsa_port_notify_bridge_fdb_flush(dp, vid); + + return err; +} + +static int dsa_port_msti_fast_age(const struct dsa_port *dp, u16 msti) +{ + DECLARE_BITMAP(vids, VLAN_N_VID) = { 0 }; + int err, vid; + + err = br_mst_get_info(dsa_port_bridge_dev_get(dp), msti, vids); + if (err) + return err; + + for_each_set_bit(vid, vids, VLAN_N_VID) { + err = dsa_port_vlan_fast_age(dp, vid); + if (err) + return err; + } + + return 0; } static bool dsa_port_can_configure_learning(struct dsa_port *dp) @@ -118,6 +152,42 @@ static void dsa_port_set_state_now(struct dsa_port *dp, u8 state, pr_err("DSA: failed to set STP state %u (%d)\n", state, err); } +int dsa_port_set_mst_state(struct dsa_port *dp, + const struct switchdev_mst_state *state, + struct netlink_ext_ack *extack) +{ + struct dsa_switch *ds = dp->ds; + u8 prev_state; + int err; + + if (!ds->ops->port_mst_state_set) + return -EOPNOTSUPP; + + err = br_mst_get_state(dsa_port_to_bridge_port(dp), state->msti, + &prev_state); + if (err) + return err; + + err = ds->ops->port_mst_state_set(ds, dp->index, state); + if (err) + return err; + + if (!(dp->learning && + (prev_state == BR_STATE_LEARNING || + prev_state == BR_STATE_FORWARDING) && + (state->state == BR_STATE_DISABLED || + state->state == BR_STATE_BLOCKING || + state->state == BR_STATE_LISTENING))) + return 0; + + err = dsa_port_msti_fast_age(dp, state->msti); + if (err) + NL_SET_ERR_MSG_MOD(extack, + "Unable to flush associated VLANs"); + + return 0; +} + int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy) { struct dsa_switch *ds = dp->ds; @@ -176,7 +246,7 @@ static int dsa_port_inherit_brport_flags(struct dsa_port *dp, struct netlink_ext_ack *extack) { const unsigned long mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | - BR_BCAST_FLOOD; + BR_BCAST_FLOOD | BR_PORT_LOCKED; struct net_device *brport_dev = dsa_port_to_bridge_port(dp); int flag, err; @@ -200,7 +270,7 @@ static void dsa_port_clear_brport_flags(struct dsa_port *dp) { const unsigned long val = BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; const unsigned long mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | - BR_BCAST_FLOOD; + BR_BCAST_FLOOD | BR_PORT_LOCKED; int flag, err; for_each_set_bit(flag, &mask, 32) { @@ -321,6 +391,16 @@ static void dsa_port_bridge_destroy(struct dsa_port *dp, kfree(bridge); } +static bool dsa_port_supports_mst(struct dsa_port *dp) +{ + struct dsa_switch *ds = dp->ds; + + return ds->ops->vlan_msti_set && + ds->ops->port_mst_state_set && + ds->ops->port_vlan_fast_age && + dsa_port_can_configure_learning(dp); +} + int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br, struct netlink_ext_ack *extack) { @@ -328,11 +408,15 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br, .tree_index = dp->ds->dst->index, .sw_index = dp->ds->index, .port = dp->index, + .extack = extack, }; struct net_device *dev = dp->slave; struct net_device *brport_dev; int err; + if (br_mst_enabled(br) && !dsa_port_supports_mst(dp)) + return -EOPNOTSUPP; + /* Here the interface is already bridged. Reflect the current * configuration so that drivers can program their chips accordingly. */ @@ -429,7 +513,7 @@ int dsa_port_lag_change(struct dsa_port *dp, }; bool tx_enabled; - if (!dp->lag_dev) + if (!dp->lag) return 0; /* On statically configured aggregates (e.g. loadbalance @@ -447,27 +531,70 @@ int dsa_port_lag_change(struct dsa_port *dp, return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info); } -int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag, +static int dsa_port_lag_create(struct dsa_port *dp, + struct net_device *lag_dev) +{ + struct dsa_switch *ds = dp->ds; + struct dsa_lag *lag; + + lag = dsa_tree_lag_find(ds->dst, lag_dev); + if (lag) { + refcount_inc(&lag->refcount); + dp->lag = lag; + return 0; + } + + lag = kzalloc(sizeof(*lag), GFP_KERNEL); + if (!lag) + return -ENOMEM; + + refcount_set(&lag->refcount, 1); + mutex_init(&lag->fdb_lock); + INIT_LIST_HEAD(&lag->fdbs); + lag->dev = lag_dev; + dsa_lag_map(ds->dst, lag); + dp->lag = lag; + + return 0; +} + +static void dsa_port_lag_destroy(struct dsa_port *dp) +{ + struct dsa_lag *lag = dp->lag; + + dp->lag = NULL; + dp->lag_tx_enabled = false; + + if (!refcount_dec_and_test(&lag->refcount)) + return; + + WARN_ON(!list_empty(&lag->fdbs)); + dsa_lag_unmap(dp->ds->dst, lag); + kfree(lag); +} + +int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev, struct netdev_lag_upper_info *uinfo, struct netlink_ext_ack *extack) { struct dsa_notifier_lag_info info = { .sw_index = dp->ds->index, .port = dp->index, - .lag = lag, .info = uinfo, }; struct net_device *bridge_dev; int err; - dsa_lag_map(dp->ds->dst, lag); - dp->lag_dev = lag; + err = dsa_port_lag_create(dp, lag_dev); + if (err) + goto err_lag_create; + info.lag = *dp->lag; err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info); if (err) goto err_lag_join; - bridge_dev = netdev_master_upper_dev_get(lag); + bridge_dev = netdev_master_upper_dev_get(lag_dev); if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) return 0; @@ -480,12 +607,12 @@ int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag, err_bridge_join: dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); err_lag_join: - dp->lag_dev = NULL; - dsa_lag_unmap(dp->ds->dst, lag); + dsa_port_lag_destroy(dp); +err_lag_create: return err; } -void dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag) +void dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag_dev) { struct net_device *br = dsa_port_bridge_dev_get(dp); @@ -493,17 +620,16 @@ void dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag) dsa_port_pre_bridge_leave(dp, br); } -void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag) +void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev) { struct net_device *br = dsa_port_bridge_dev_get(dp); struct dsa_notifier_lag_info info = { .sw_index = dp->ds->index, .port = dp->index, - .lag = lag, }; int err; - if (!dp->lag_dev) + if (!dp->lag) return; /* Port might have been part of a LAG that in turn was @@ -512,16 +638,15 @@ void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag) if (br) dsa_port_bridge_leave(dp, br); - dp->lag_tx_enabled = false; - dp->lag_dev = NULL; + info.lag = *dp->lag; + + dsa_port_lag_destroy(dp); err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); if (err) dev_err(dp->ds->dev, "port %d failed to notify DSA_NOTIFIER_LAG_LEAVE: %pe\n", dp->index, ERR_PTR(err)); - - dsa_lag_unmap(dp->ds->dst, lag); } /* Must be called under rcu_read_lock() */ @@ -693,6 +818,17 @@ int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock) return 0; } +int dsa_port_mst_enable(struct dsa_port *dp, bool on, + struct netlink_ext_ack *extack) +{ + if (on && !dsa_port_supports_mst(dp)) { + NL_SET_ERR_MSG_MOD(extack, "Hardware does not support MST"); + return -EINVAL; + } + + return 0; +} + int dsa_port_pre_bridge_flags(const struct dsa_port *dp, struct switchdev_brport_flags flags, struct netlink_ext_ack *extack) @@ -736,6 +872,17 @@ int dsa_port_bridge_flags(struct dsa_port *dp, return 0; } +int dsa_port_vlan_msti(struct dsa_port *dp, + const struct switchdev_vlan_msti *msti) +{ + struct dsa_switch *ds = dp->ds; + + if (!ds->ops->vlan_msti_set) + return -EOPNOTSUPP; + + return ds->ops->vlan_msti_set(ds, *dp->bridge, msti); +} + int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu, bool targeted_match) { @@ -757,8 +904,19 @@ int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, .port = dp->index, .addr = addr, .vid = vid, + .db = { + .type = DSA_DB_BRIDGE, + .bridge = *dp->bridge, + }, }; + /* Refcounting takes bridge.num as a key, and should be global for all + * bridges in the absence of FDB isolation, and per bridge otherwise. + * Force the bridge.num to zero here in the absence of FDB isolation. + */ + if (!dp->ds->fdb_isolation) + info.db.bridge.num = 0; + return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); } @@ -770,22 +928,55 @@ int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, .port = dp->index, .addr = addr, .vid = vid, - + .db = { + .type = DSA_DB_BRIDGE, + .bridge = *dp->bridge, + }, }; + if (!dp->ds->fdb_isolation) + info.db.bridge.num = 0; + return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); } -int dsa_port_host_fdb_add(struct dsa_port *dp, const unsigned char *addr, - u16 vid) +static int dsa_port_host_fdb_add(struct dsa_port *dp, + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct dsa_notifier_fdb_info info = { .sw_index = dp->ds->index, .port = dp->index, .addr = addr, .vid = vid, + .db = db, }; + + if (!dp->ds->fdb_isolation) + info.db.bridge.num = 0; + + return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_ADD, &info); +} + +int dsa_port_standalone_host_fdb_add(struct dsa_port *dp, + const unsigned char *addr, u16 vid) +{ + struct dsa_db db = { + .type = DSA_DB_PORT, + .dp = dp, + }; + + return dsa_port_host_fdb_add(dp, addr, vid, db); +} + +int dsa_port_bridge_host_fdb_add(struct dsa_port *dp, + const unsigned char *addr, u16 vid) +{ struct dsa_port *cpu_dp = dp->cpu_dp; + struct dsa_db db = { + .type = DSA_DB_BRIDGE, + .bridge = *dp->bridge, + }; int err; /* Avoid a call to __dev_set_promiscuity() on the master, which @@ -798,19 +989,46 @@ int dsa_port_host_fdb_add(struct dsa_port *dp, const unsigned char *addr, return err; } - return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_ADD, &info); + return dsa_port_host_fdb_add(dp, addr, vid, db); } -int dsa_port_host_fdb_del(struct dsa_port *dp, const unsigned char *addr, - u16 vid) +static int dsa_port_host_fdb_del(struct dsa_port *dp, + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct dsa_notifier_fdb_info info = { .sw_index = dp->ds->index, .port = dp->index, .addr = addr, .vid = vid, + .db = db, }; + + if (!dp->ds->fdb_isolation) + info.db.bridge.num = 0; + + return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_DEL, &info); +} + +int dsa_port_standalone_host_fdb_del(struct dsa_port *dp, + const unsigned char *addr, u16 vid) +{ + struct dsa_db db = { + .type = DSA_DB_PORT, + .dp = dp, + }; + + return dsa_port_host_fdb_del(dp, addr, vid, db); +} + +int dsa_port_bridge_host_fdb_del(struct dsa_port *dp, + const unsigned char *addr, u16 vid) +{ struct dsa_port *cpu_dp = dp->cpu_dp; + struct dsa_db db = { + .type = DSA_DB_BRIDGE, + .bridge = *dp->bridge, + }; int err; if (cpu_dp->master->priv_flags & IFF_UNICAST_FLT) { @@ -819,7 +1037,45 @@ int dsa_port_host_fdb_del(struct dsa_port *dp, const unsigned char *addr, return err; } - return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_DEL, &info); + return dsa_port_host_fdb_del(dp, addr, vid, db); +} + +int dsa_port_lag_fdb_add(struct dsa_port *dp, const unsigned char *addr, + u16 vid) +{ + struct dsa_notifier_lag_fdb_info info = { + .lag = dp->lag, + .addr = addr, + .vid = vid, + .db = { + .type = DSA_DB_BRIDGE, + .bridge = *dp->bridge, + }, + }; + + if (!dp->ds->fdb_isolation) + info.db.bridge.num = 0; + + return dsa_port_notify(dp, DSA_NOTIFIER_LAG_FDB_ADD, &info); +} + +int dsa_port_lag_fdb_del(struct dsa_port *dp, const unsigned char *addr, + u16 vid) +{ + struct dsa_notifier_lag_fdb_info info = { + .lag = dp->lag, + .addr = addr, + .vid = vid, + .db = { + .type = DSA_DB_BRIDGE, + .bridge = *dp->bridge, + }, + }; + + if (!dp->ds->fdb_isolation) + info.db.bridge.num = 0; + + return dsa_port_notify(dp, DSA_NOTIFIER_LAG_FDB_DEL, &info); } int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) @@ -840,8 +1096,15 @@ int dsa_port_mdb_add(const struct dsa_port *dp, .sw_index = dp->ds->index, .port = dp->index, .mdb = mdb, + .db = { + .type = DSA_DB_BRIDGE, + .bridge = *dp->bridge, + }, }; + if (!dp->ds->fdb_isolation) + info.db.bridge.num = 0; + return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); } @@ -852,45 +1115,106 @@ int dsa_port_mdb_del(const struct dsa_port *dp, .sw_index = dp->ds->index, .port = dp->index, .mdb = mdb, + .db = { + .type = DSA_DB_BRIDGE, + .bridge = *dp->bridge, + }, }; + if (!dp->ds->fdb_isolation) + info.db.bridge.num = 0; + return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); } -int dsa_port_host_mdb_add(const struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb) +static int dsa_port_host_mdb_add(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct dsa_notifier_mdb_info info = { .sw_index = dp->ds->index, .port = dp->index, .mdb = mdb, + .db = db, }; + + if (!dp->ds->fdb_isolation) + info.db.bridge.num = 0; + + return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_ADD, &info); +} + +int dsa_port_standalone_host_mdb_add(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb) +{ + struct dsa_db db = { + .type = DSA_DB_PORT, + .dp = dp, + }; + + return dsa_port_host_mdb_add(dp, mdb, db); +} + +int dsa_port_bridge_host_mdb_add(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb) +{ struct dsa_port *cpu_dp = dp->cpu_dp; + struct dsa_db db = { + .type = DSA_DB_BRIDGE, + .bridge = *dp->bridge, + }; int err; err = dev_mc_add(cpu_dp->master, mdb->addr); if (err) return err; - return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_ADD, &info); + return dsa_port_host_mdb_add(dp, mdb, db); } -int dsa_port_host_mdb_del(const struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb) +static int dsa_port_host_mdb_del(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct dsa_notifier_mdb_info info = { .sw_index = dp->ds->index, .port = dp->index, .mdb = mdb, + .db = db, }; + + if (!dp->ds->fdb_isolation) + info.db.bridge.num = 0; + + return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_DEL, &info); +} + +int dsa_port_standalone_host_mdb_del(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb) +{ + struct dsa_db db = { + .type = DSA_DB_PORT, + .dp = dp, + }; + + return dsa_port_host_mdb_del(dp, mdb, db); +} + +int dsa_port_bridge_host_mdb_del(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb) +{ struct dsa_port *cpu_dp = dp->cpu_dp; + struct dsa_db db = { + .type = DSA_DB_BRIDGE, + .bridge = *dp->bridge, + }; int err; err = dev_mc_del(cpu_dp->master, mdb->addr); if (err) return err; - return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_DEL, &info); + return dsa_port_host_mdb_del(dp, mdb, db); } int dsa_port_vlan_add(struct dsa_port *dp, @@ -919,6 +1243,48 @@ int dsa_port_vlan_del(struct dsa_port *dp, return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); } +int dsa_port_host_vlan_add(struct dsa_port *dp, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + struct dsa_notifier_vlan_info info = { + .sw_index = dp->ds->index, + .port = dp->index, + .vlan = vlan, + .extack = extack, + }; + struct dsa_port *cpu_dp = dp->cpu_dp; + int err; + + err = dsa_port_notify(dp, DSA_NOTIFIER_HOST_VLAN_ADD, &info); + if (err && err != -EOPNOTSUPP) + return err; + + vlan_vid_add(cpu_dp->master, htons(ETH_P_8021Q), vlan->vid); + + return err; +} + +int dsa_port_host_vlan_del(struct dsa_port *dp, + const struct switchdev_obj_port_vlan *vlan) +{ + struct dsa_notifier_vlan_info info = { + .sw_index = dp->ds->index, + .port = dp->index, + .vlan = vlan, + }; + struct dsa_port *cpu_dp = dp->cpu_dp; + int err; + + err = dsa_port_notify(dp, DSA_NOTIFIER_HOST_VLAN_DEL, &info); + if (err && err != -EOPNOTSUPP) + return err; + + vlan_vid_del(cpu_dp->master, htons(ETH_P_8021Q), vlan->vid); + + return err; +} + int dsa_port_mrp_add(const struct dsa_port *dp, const struct switchdev_obj_mrp *mrp) { @@ -1026,6 +1392,20 @@ static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, } } +static struct phylink_pcs * +dsa_port_phylink_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); + struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP); + struct dsa_switch *ds = dp->ds; + + if (ds->ops->phylink_mac_select_pcs) + pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface); + + return pcs; +} + static void dsa_port_phylink_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) @@ -1092,6 +1472,7 @@ static void dsa_port_phylink_mac_link_up(struct phylink_config *config, static const struct phylink_mac_ops dsa_port_phylink_mac_ops = { .validate = dsa_port_phylink_validate, + .mac_select_pcs = dsa_port_phylink_mac_select_pcs, .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, .mac_config = dsa_port_phylink_mac_config, .mac_an_restart = dsa_port_phylink_mac_an_restart, @@ -1209,7 +1590,6 @@ static int dsa_port_phylink_register(struct dsa_port *dp) dp->pl_config.dev = ds->dev; dp->pl_config.type = PHYLINK_DEV; - dp->pl_config.pcs_poll = ds->pcs_poll; err = dsa_port_phylink_create(dp); if (err) @@ -1273,63 +1653,6 @@ void dsa_port_link_unregister_of(struct dsa_port *dp) dsa_port_setup_phy_of(dp, false); } -int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) -{ - struct phy_device *phydev; - int ret = -EOPNOTSUPP; - - if (of_phy_is_fixed_link(dp->dn)) - return ret; - - phydev = dsa_port_get_phy_device(dp); - if (IS_ERR_OR_NULL(phydev)) - return ret; - - ret = phy_ethtool_get_strings(phydev, data); - put_device(&phydev->mdio.dev); - - return ret; -} -EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings); - -int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data) -{ - struct phy_device *phydev; - int ret = -EOPNOTSUPP; - - if (of_phy_is_fixed_link(dp->dn)) - return ret; - - phydev = dsa_port_get_phy_device(dp); - if (IS_ERR_OR_NULL(phydev)) - return ret; - - ret = phy_ethtool_get_stats(phydev, NULL, data); - put_device(&phydev->mdio.dev); - - return ret; -} -EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats); - -int dsa_port_get_phy_sset_count(struct dsa_port *dp) -{ - struct phy_device *phydev; - int ret = -EOPNOTSUPP; - - if (of_phy_is_fixed_link(dp->dn)) - return ret; - - phydev = dsa_port_get_phy_device(dp); - if (IS_ERR_OR_NULL(phydev)) - return ret; - - ret = phy_ethtool_get_sset_count(phydev); - put_device(&phydev->mdio.dev); - - return ret; -} -EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count); - int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr) { struct dsa_switch *ds = dp->ds; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 22241afcac81..41c69a6e7854 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -19,10 +19,151 @@ #include #include #include +#include #include #include "dsa_priv.h" +static void dsa_slave_standalone_event_work(struct work_struct *work) +{ + struct dsa_standalone_event_work *standalone_work = + container_of(work, struct dsa_standalone_event_work, work); + const unsigned char *addr = standalone_work->addr; + struct net_device *dev = standalone_work->dev; + struct dsa_port *dp = dsa_slave_to_port(dev); + struct switchdev_obj_port_mdb mdb; + struct dsa_switch *ds = dp->ds; + u16 vid = standalone_work->vid; + int err; + + switch (standalone_work->event) { + case DSA_UC_ADD: + err = dsa_port_standalone_host_fdb_add(dp, addr, vid); + if (err) { + dev_err(ds->dev, + "port %d failed to add %pM vid %d to fdb: %d\n", + dp->index, addr, vid, err); + break; + } + break; + + case DSA_UC_DEL: + err = dsa_port_standalone_host_fdb_del(dp, addr, vid); + if (err) { + dev_err(ds->dev, + "port %d failed to delete %pM vid %d from fdb: %d\n", + dp->index, addr, vid, err); + } + + break; + case DSA_MC_ADD: + ether_addr_copy(mdb.addr, addr); + mdb.vid = vid; + + err = dsa_port_standalone_host_mdb_add(dp, &mdb); + if (err) { + dev_err(ds->dev, + "port %d failed to add %pM vid %d to mdb: %d\n", + dp->index, addr, vid, err); + break; + } + break; + case DSA_MC_DEL: + ether_addr_copy(mdb.addr, addr); + mdb.vid = vid; + + err = dsa_port_standalone_host_mdb_del(dp, &mdb); + if (err) { + dev_err(ds->dev, + "port %d failed to delete %pM vid %d from mdb: %d\n", + dp->index, addr, vid, err); + } + + break; + } + + kfree(standalone_work); +} + +static int dsa_slave_schedule_standalone_work(struct net_device *dev, + enum dsa_standalone_event event, + const unsigned char *addr, + u16 vid) +{ + struct dsa_standalone_event_work *standalone_work; + + standalone_work = kzalloc(sizeof(*standalone_work), GFP_ATOMIC); + if (!standalone_work) + return -ENOMEM; + + INIT_WORK(&standalone_work->work, dsa_slave_standalone_event_work); + standalone_work->event = event; + standalone_work->dev = dev; + + ether_addr_copy(standalone_work->addr, addr); + standalone_work->vid = vid; + + dsa_schedule_work(&standalone_work->work); + + return 0; +} + +static int dsa_slave_sync_uc(struct net_device *dev, + const unsigned char *addr) +{ + struct net_device *master = dsa_slave_to_master(dev); + struct dsa_port *dp = dsa_slave_to_port(dev); + + dev_uc_add(master, addr); + + if (!dsa_switch_supports_uc_filtering(dp->ds)) + return 0; + + return dsa_slave_schedule_standalone_work(dev, DSA_UC_ADD, addr, 0); +} + +static int dsa_slave_unsync_uc(struct net_device *dev, + const unsigned char *addr) +{ + struct net_device *master = dsa_slave_to_master(dev); + struct dsa_port *dp = dsa_slave_to_port(dev); + + dev_uc_del(master, addr); + + if (!dsa_switch_supports_uc_filtering(dp->ds)) + return 0; + + return dsa_slave_schedule_standalone_work(dev, DSA_UC_DEL, addr, 0); +} + +static int dsa_slave_sync_mc(struct net_device *dev, + const unsigned char *addr) +{ + struct net_device *master = dsa_slave_to_master(dev); + struct dsa_port *dp = dsa_slave_to_port(dev); + + dev_mc_add(master, addr); + + if (!dsa_switch_supports_mc_filtering(dp->ds)) + return 0; + + return dsa_slave_schedule_standalone_work(dev, DSA_MC_ADD, addr, 0); +} + +static int dsa_slave_unsync_mc(struct net_device *dev, + const unsigned char *addr) +{ + struct net_device *master = dsa_slave_to_master(dev); + struct dsa_port *dp = dsa_slave_to_port(dev); + + dev_mc_del(master, addr); + + if (!dsa_switch_supports_mc_filtering(dp->ds)) + return 0; + + return dsa_slave_schedule_standalone_work(dev, DSA_MC_DEL, addr, 0); +} + /* slave mii_bus handling ***************************************************/ static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg) { @@ -67,6 +208,7 @@ static int dsa_slave_open(struct net_device *dev) { struct net_device *master = dsa_slave_to_master(dev); struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_switch *ds = dp->ds; int err; err = dev_open(master, NULL); @@ -75,38 +217,30 @@ static int dsa_slave_open(struct net_device *dev) goto out; } - if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) { - err = dev_uc_add(master, dev->dev_addr); - if (err < 0) + if (dsa_switch_supports_uc_filtering(ds)) { + err = dsa_port_standalone_host_fdb_add(dp, dev->dev_addr, 0); + if (err) goto out; } - if (dev->flags & IFF_ALLMULTI) { - err = dev_set_allmulti(master, 1); + if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) { + err = dev_uc_add(master, dev->dev_addr); if (err < 0) - goto del_unicast; - } - if (dev->flags & IFF_PROMISC) { - err = dev_set_promiscuity(master, 1); - if (err < 0) - goto clear_allmulti; + goto del_host_addr; } err = dsa_port_enable_rt(dp, dev->phydev); if (err) - goto clear_promisc; + goto del_unicast; return 0; -clear_promisc: - if (dev->flags & IFF_PROMISC) - dev_set_promiscuity(master, -1); -clear_allmulti: - if (dev->flags & IFF_ALLMULTI) - dev_set_allmulti(master, -1); del_unicast: if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) dev_uc_del(master, dev->dev_addr); +del_host_addr: + if (dsa_switch_supports_uc_filtering(ds)) + dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0); out: return err; } @@ -115,68 +249,121 @@ static int dsa_slave_close(struct net_device *dev) { struct net_device *master = dsa_slave_to_master(dev); struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_switch *ds = dp->ds; dsa_port_disable_rt(dp); - dev_mc_unsync(master, dev); - dev_uc_unsync(master, dev); - if (dev->flags & IFF_ALLMULTI) - dev_set_allmulti(master, -1); - if (dev->flags & IFF_PROMISC) - dev_set_promiscuity(master, -1); - if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) dev_uc_del(master, dev->dev_addr); + if (dsa_switch_supports_uc_filtering(ds)) + dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0); + return 0; } +/* Keep flooding enabled towards this port's CPU port as long as it serves at + * least one port in the tree that requires it. + */ +static void dsa_port_manage_cpu_flood(struct dsa_port *dp) +{ + struct switchdev_brport_flags flags = { + .mask = BR_FLOOD | BR_MCAST_FLOOD, + }; + struct dsa_switch_tree *dst = dp->ds->dst; + struct dsa_port *cpu_dp = dp->cpu_dp; + struct dsa_port *other_dp; + int err; + + list_for_each_entry(other_dp, &dst->ports, list) { + if (!dsa_port_is_user(other_dp)) + continue; + + if (other_dp->cpu_dp != cpu_dp) + continue; + + if (other_dp->slave->flags & IFF_ALLMULTI) + flags.val |= BR_MCAST_FLOOD; + if (other_dp->slave->flags & IFF_PROMISC) + flags.val |= BR_FLOOD; + } + + err = dsa_port_pre_bridge_flags(dp, flags, NULL); + if (err) + return; + + dsa_port_bridge_flags(cpu_dp, flags, NULL); +} + static void dsa_slave_change_rx_flags(struct net_device *dev, int change) { struct net_device *master = dsa_slave_to_master(dev); - if (dev->flags & IFF_UP) { - if (change & IFF_ALLMULTI) - dev_set_allmulti(master, - dev->flags & IFF_ALLMULTI ? 1 : -1); - if (change & IFF_PROMISC) - dev_set_promiscuity(master, - dev->flags & IFF_PROMISC ? 1 : -1); - } + struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_switch *ds = dp->ds; + + if (change & IFF_ALLMULTI) + dev_set_allmulti(master, + dev->flags & IFF_ALLMULTI ? 1 : -1); + if (change & IFF_PROMISC) + dev_set_promiscuity(master, + dev->flags & IFF_PROMISC ? 1 : -1); + + if (dsa_switch_supports_uc_filtering(ds) && + dsa_switch_supports_mc_filtering(ds)) + dsa_port_manage_cpu_flood(dp); } static void dsa_slave_set_rx_mode(struct net_device *dev) { - struct net_device *master = dsa_slave_to_master(dev); - - dev_mc_sync(master, dev); - dev_uc_sync(master, dev); + __dev_mc_sync(dev, dsa_slave_sync_mc, dsa_slave_unsync_mc); + __dev_uc_sync(dev, dsa_slave_sync_uc, dsa_slave_unsync_uc); } static int dsa_slave_set_mac_address(struct net_device *dev, void *a) { struct net_device *master = dsa_slave_to_master(dev); + struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_switch *ds = dp->ds; struct sockaddr *addr = a; int err; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; + /* If the port is down, the address isn't synced yet to hardware or + * to the DSA master, so there is nothing to change. + */ if (!(dev->flags & IFF_UP)) - goto out; + goto out_change_dev_addr; + + if (dsa_switch_supports_uc_filtering(ds)) { + err = dsa_port_standalone_host_fdb_add(dp, addr->sa_data, 0); + if (err) + return err; + } if (!ether_addr_equal(addr->sa_data, master->dev_addr)) { err = dev_uc_add(master, addr->sa_data); if (err < 0) - return err; + goto del_unicast; } if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) dev_uc_del(master, dev->dev_addr); -out: + if (dsa_switch_supports_uc_filtering(ds)) + dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0); + +out_change_dev_addr: eth_hw_addr_set(dev, addr->sa_data); return 0; + +del_unicast: + if (dsa_switch_supports_uc_filtering(ds)) + dsa_port_standalone_host_fdb_del(dp, addr->sa_data, 0); + + return err; } struct dsa_slave_dump_ctx { @@ -288,6 +475,12 @@ static int dsa_slave_port_attr_set(struct net_device *dev, const void *ctx, ret = dsa_port_set_state(dp, attr->u.stp_state, true); break; + case SWITCHDEV_ATTR_ID_PORT_MST_STATE: + if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) + return -EOPNOTSUPP; + + ret = dsa_port_set_mst_state(dp, &attr->u.mst_state, extack); + break; case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev)) return -EOPNOTSUPP; @@ -301,6 +494,12 @@ static int dsa_slave_port_attr_set(struct net_device *dev, const void *ctx, ret = dsa_port_ageing_time(dp, attr->u.ageing_time); break; + case SWITCHDEV_ATTR_ID_BRIDGE_MST: + if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev)) + return -EOPNOTSUPP; + + ret = dsa_port_mst_enable(dp, attr->u.mst, extack); + break; case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) return -EOPNOTSUPP; @@ -314,6 +513,12 @@ static int dsa_slave_port_attr_set(struct net_device *dev, const void *ctx, ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, extack); break; + case SWITCHDEV_ATTR_ID_VLAN_MSTI: + if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev)) + return -EOPNOTSUPP; + + ret = dsa_port_vlan_msti(dp, &attr->u.vlan_msti); + break; default: ret = -EOPNOTSUPP; break; @@ -348,9 +553,8 @@ static int dsa_slave_vlan_add(struct net_device *dev, const struct switchdev_obj *obj, struct netlink_ext_ack *extack) { - struct net_device *master = dsa_slave_to_master(dev); struct dsa_port *dp = dsa_slave_to_port(dev); - struct switchdev_obj_port_vlan vlan; + struct switchdev_obj_port_vlan *vlan; int err; if (dsa_port_skip_vlan_configuration(dp)) { @@ -358,14 +562,14 @@ static int dsa_slave_vlan_add(struct net_device *dev, return 0; } - vlan = *SWITCHDEV_OBJ_PORT_VLAN(obj); + vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); /* Deny adding a bridge VLAN when there is already an 802.1Q upper with * the same VID. */ if (br_vlan_enabled(dsa_port_bridge_dev_get(dp))) { rcu_read_lock(); - err = dsa_slave_vlan_check_for_8021q_uppers(dev, &vlan); + err = dsa_slave_vlan_check_for_8021q_uppers(dev, vlan); rcu_read_unlock(); if (err) { NL_SET_ERR_MSG_MOD(extack, @@ -374,21 +578,36 @@ static int dsa_slave_vlan_add(struct net_device *dev, } } - err = dsa_port_vlan_add(dp, &vlan, extack); - if (err) - return err; + return dsa_port_vlan_add(dp, vlan, extack); +} - /* We need the dedicated CPU port to be a member of the VLAN as well. - * Even though drivers often handle CPU membership in special ways, +/* Offload a VLAN installed on the bridge or on a foreign interface by + * installing it as a VLAN towards the CPU port. + */ +static int dsa_slave_host_vlan_add(struct net_device *dev, + const struct switchdev_obj *obj, + struct netlink_ext_ack *extack) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + struct switchdev_obj_port_vlan vlan; + + /* Do nothing if this is a software bridge */ + if (!dp->bridge) + return -EOPNOTSUPP; + + if (dsa_port_skip_vlan_configuration(dp)) { + NL_SET_ERR_MSG_MOD(extack, "skipping configuration of VLAN"); + return 0; + } + + vlan = *SWITCHDEV_OBJ_PORT_VLAN(obj); + + /* Even though drivers often handle CPU membership in special ways, * it doesn't make sense to program a PVID, so clear this flag. */ vlan.flags &= ~BRIDGE_VLAN_INFO_PVID; - err = dsa_port_vlan_add(dp->cpu_dp, &vlan, extack); - if (err) - return err; - - return vlan_vid_add(master, htons(ETH_P_8021Q), vlan.vid); + return dsa_port_host_vlan_add(dp, &vlan, extack); } static int dsa_slave_port_obj_add(struct net_device *dev, const void *ctx, @@ -412,13 +631,13 @@ static int dsa_slave_port_obj_add(struct net_device *dev, const void *ctx, if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) return -EOPNOTSUPP; - err = dsa_port_host_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); + err = dsa_port_bridge_host_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); break; case SWITCHDEV_OBJ_ID_PORT_VLAN: - if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) - return -EOPNOTSUPP; - - err = dsa_slave_vlan_add(dev, obj, extack); + if (dsa_port_offloads_bridge_port(dp, obj->orig_dev)) + err = dsa_slave_vlan_add(dev, obj, extack); + else + err = dsa_slave_host_vlan_add(dev, obj, extack); break; case SWITCHDEV_OBJ_ID_MRP: if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) @@ -444,26 +663,33 @@ static int dsa_slave_port_obj_add(struct net_device *dev, const void *ctx, static int dsa_slave_vlan_del(struct net_device *dev, const struct switchdev_obj *obj) { - struct net_device *master = dsa_slave_to_master(dev); struct dsa_port *dp = dsa_slave_to_port(dev); struct switchdev_obj_port_vlan *vlan; - int err; if (dsa_port_skip_vlan_configuration(dp)) return 0; vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); - /* Do not deprogram the CPU port as it may be shared with other user - * ports which can be members of this VLAN as well. - */ - err = dsa_port_vlan_del(dp, vlan); - if (err) - return err; + return dsa_port_vlan_del(dp, vlan); +} - vlan_vid_del(master, htons(ETH_P_8021Q), vlan->vid); +static int dsa_slave_host_vlan_del(struct net_device *dev, + const struct switchdev_obj *obj) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + struct switchdev_obj_port_vlan *vlan; - return 0; + /* Do nothing if this is a software bridge */ + if (!dp->bridge) + return -EOPNOTSUPP; + + if (dsa_port_skip_vlan_configuration(dp)) + return 0; + + vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); + + return dsa_port_host_vlan_del(dp, vlan); } static int dsa_slave_port_obj_del(struct net_device *dev, const void *ctx, @@ -486,13 +712,13 @@ static int dsa_slave_port_obj_del(struct net_device *dev, const void *ctx, if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) return -EOPNOTSUPP; - err = dsa_port_host_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); + err = dsa_port_bridge_host_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); break; case SWITCHDEV_OBJ_ID_PORT_VLAN: - if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) - return -EOPNOTSUPP; - - err = dsa_slave_vlan_del(dev, obj); + if (dsa_port_offloads_bridge_port(dp, obj->orig_dev)) + err = dsa_slave_vlan_del(dev, obj); + else + err = dsa_slave_host_vlan_del(dev, obj); break; case SWITCHDEV_OBJ_ID_MRP: if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) @@ -515,26 +741,6 @@ static int dsa_slave_port_obj_del(struct net_device *dev, const void *ctx, return err; } -static int dsa_slave_get_port_parent_id(struct net_device *dev, - struct netdev_phys_item_id *ppid) -{ - struct dsa_port *dp = dsa_slave_to_port(dev); - struct dsa_switch *ds = dp->ds; - struct dsa_switch_tree *dst = ds->dst; - - /* For non-legacy ports, devlink is used and it takes - * care of the name generation. This ndo implementation - * should be removed with legacy support. - */ - if (dp->ds->devlink) - return -EOPNOTSUPP; - - ppid->id_len = sizeof(dst->index); - memcpy(&ppid->id, &dst->index, ppid->id_len); - - return 0; -} - static inline netdev_tx_t dsa_slave_netpoll_send_skb(struct net_device *dev, struct sk_buff *skb) { @@ -973,24 +1179,6 @@ static void dsa_slave_poll_controller(struct net_device *dev) } #endif -static int dsa_slave_get_phys_port_name(struct net_device *dev, - char *name, size_t len) -{ - struct dsa_port *dp = dsa_slave_to_port(dev); - - /* For non-legacy ports, devlink is used and it takes - * care of the name generation. This ndo implementation - * should be removed with legacy support. - */ - if (dp->ds->devlink) - return -EOPNOTSUPP; - - if (snprintf(name, len, "p%d", dp->index) >= len) - return -EINVAL; - - return 0; -} - static struct dsa_mall_tc_entry * dsa_slave_mall_tc_entry_find(struct net_device *dev, unsigned long cookie) { @@ -1009,6 +1197,7 @@ dsa_slave_add_cls_matchall_mirred(struct net_device *dev, struct tc_cls_matchall_offload *cls, bool ingress) { + struct netlink_ext_ack *extack = cls->common.extack; struct dsa_port *dp = dsa_slave_to_port(dev); struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_mall_mirror_tc_entry *mirror; @@ -1046,7 +1235,7 @@ dsa_slave_add_cls_matchall_mirred(struct net_device *dev, mirror->to_local_port = to_dp->index; mirror->ingress = ingress; - err = ds->ops->port_mirror_add(ds, dp->index, mirror, ingress); + err = ds->ops->port_mirror_add(ds, dp->index, mirror, ingress, extack); if (err) { kfree(mall_tc_entry); return err; @@ -1385,7 +1574,6 @@ static int dsa_slave_get_ts_info(struct net_device *dev, static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) { - struct net_device *master = dsa_slave_to_master(dev); struct dsa_port *dp = dsa_slave_to_port(dev); struct switchdev_obj_port_vlan vlan = { .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, @@ -1405,7 +1593,7 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, } /* And CPU port... */ - ret = dsa_port_vlan_add(dp->cpu_dp, &vlan, &extack); + ret = dsa_port_host_vlan_add(dp, &vlan, &extack); if (ret) { if (extack._msg) netdev_err(dev, "CPU port %d: %s\n", dp->cpu_dp->index, @@ -1413,13 +1601,12 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, return ret; } - return vlan_vid_add(master, proto, vid); + return 0; } static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid) { - struct net_device *master = dsa_slave_to_master(dev); struct dsa_port *dp = dsa_slave_to_port(dev); struct switchdev_obj_port_vlan vlan = { .vid = vid, @@ -1428,16 +1615,11 @@ static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, }; int err; - /* Do not deprogram the CPU port as it may be shared with other user - * ports which can be members of this VLAN as well. - */ err = dsa_port_vlan_del(dp, &vlan); if (err) return err; - vlan_vid_del(master, proto, vid); - - return 0; + return dsa_port_host_vlan_del(dp, &vlan); } static int dsa_slave_restore_vlan(struct net_device *vdev, int vid, void *arg) @@ -1714,6 +1896,209 @@ int dsa_slave_change_mtu(struct net_device *dev, int new_mtu) return err; } +static int __maybe_unused +dsa_slave_dcbnl_set_default_prio(struct net_device *dev, struct dcb_app *app) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_switch *ds = dp->ds; + unsigned long mask, new_prio; + int err, port = dp->index; + + if (!ds->ops->port_set_default_prio) + return -EOPNOTSUPP; + + err = dcb_ieee_setapp(dev, app); + if (err) + return err; + + mask = dcb_ieee_getapp_mask(dev, app); + new_prio = __fls(mask); + + err = ds->ops->port_set_default_prio(ds, port, new_prio); + if (err) { + dcb_ieee_delapp(dev, app); + return err; + } + + return 0; +} + +static int __maybe_unused +dsa_slave_dcbnl_add_dscp_prio(struct net_device *dev, struct dcb_app *app) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_switch *ds = dp->ds; + unsigned long mask, new_prio; + int err, port = dp->index; + u8 dscp = app->protocol; + + if (!ds->ops->port_add_dscp_prio) + return -EOPNOTSUPP; + + if (dscp >= 64) { + netdev_err(dev, "DSCP APP entry with protocol value %u is invalid\n", + dscp); + return -EINVAL; + } + + err = dcb_ieee_setapp(dev, app); + if (err) + return err; + + mask = dcb_ieee_getapp_mask(dev, app); + new_prio = __fls(mask); + + err = ds->ops->port_add_dscp_prio(ds, port, dscp, new_prio); + if (err) { + dcb_ieee_delapp(dev, app); + return err; + } + + return 0; +} + +static int __maybe_unused dsa_slave_dcbnl_ieee_setapp(struct net_device *dev, + struct dcb_app *app) +{ + switch (app->selector) { + case IEEE_8021QAZ_APP_SEL_ETHERTYPE: + switch (app->protocol) { + case 0: + return dsa_slave_dcbnl_set_default_prio(dev, app); + default: + return -EOPNOTSUPP; + } + break; + case IEEE_8021QAZ_APP_SEL_DSCP: + return dsa_slave_dcbnl_add_dscp_prio(dev, app); + default: + return -EOPNOTSUPP; + } +} + +static int __maybe_unused +dsa_slave_dcbnl_del_default_prio(struct net_device *dev, struct dcb_app *app) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_switch *ds = dp->ds; + unsigned long mask, new_prio; + int err, port = dp->index; + + if (!ds->ops->port_set_default_prio) + return -EOPNOTSUPP; + + err = dcb_ieee_delapp(dev, app); + if (err) + return err; + + mask = dcb_ieee_getapp_mask(dev, app); + new_prio = mask ? __fls(mask) : 0; + + err = ds->ops->port_set_default_prio(ds, port, new_prio); + if (err) { + dcb_ieee_setapp(dev, app); + return err; + } + + return 0; +} + +static int __maybe_unused +dsa_slave_dcbnl_del_dscp_prio(struct net_device *dev, struct dcb_app *app) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_switch *ds = dp->ds; + int err, port = dp->index; + u8 dscp = app->protocol; + + if (!ds->ops->port_del_dscp_prio) + return -EOPNOTSUPP; + + err = dcb_ieee_delapp(dev, app); + if (err) + return err; + + err = ds->ops->port_del_dscp_prio(ds, port, dscp, app->priority); + if (err) { + dcb_ieee_setapp(dev, app); + return err; + } + + return 0; +} + +static int __maybe_unused dsa_slave_dcbnl_ieee_delapp(struct net_device *dev, + struct dcb_app *app) +{ + switch (app->selector) { + case IEEE_8021QAZ_APP_SEL_ETHERTYPE: + switch (app->protocol) { + case 0: + return dsa_slave_dcbnl_del_default_prio(dev, app); + default: + return -EOPNOTSUPP; + } + break; + case IEEE_8021QAZ_APP_SEL_DSCP: + return dsa_slave_dcbnl_del_dscp_prio(dev, app); + default: + return -EOPNOTSUPP; + } +} + +/* Pre-populate the DCB application priority table with the priorities + * configured during switch setup, which we read from hardware here. + */ +static int dsa_slave_dcbnl_init(struct net_device *dev) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_switch *ds = dp->ds; + int port = dp->index; + int err; + + if (ds->ops->port_get_default_prio) { + int prio = ds->ops->port_get_default_prio(ds, port); + struct dcb_app app = { + .selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE, + .protocol = 0, + .priority = prio, + }; + + if (prio < 0) + return prio; + + err = dcb_ieee_setapp(dev, &app); + if (err) + return err; + } + + if (ds->ops->port_get_dscp_prio) { + int protocol; + + for (protocol = 0; protocol < 64; protocol++) { + struct dcb_app app = { + .selector = IEEE_8021QAZ_APP_SEL_DSCP, + .protocol = protocol, + }; + int prio; + + prio = ds->ops->port_get_dscp_prio(ds, port, protocol); + if (prio == -EOPNOTSUPP) + continue; + if (prio < 0) + return prio; + + app.priority = prio; + + err = dcb_ieee_setapp(dev, &app); + if (err) + return err; + } + } + + return 0; +} + static const struct ethtool_ops dsa_slave_ethtool_ops = { .get_drvinfo = dsa_slave_get_drvinfo, .get_regs_len = dsa_slave_get_regs_len, @@ -1743,11 +2128,16 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = { .self_test = dsa_slave_net_selftest, }; +static const struct dcbnl_rtnl_ops __maybe_unused dsa_slave_dcbnl_ops = { + .ieee_setapp = dsa_slave_dcbnl_ieee_setapp, + .ieee_delapp = dsa_slave_dcbnl_ieee_delapp, +}; + static struct devlink_port *dsa_slave_get_devlink_port(struct net_device *dev) { struct dsa_port *dp = dsa_slave_to_port(dev); - return dp->ds->devlink ? &dp->devlink_port : NULL; + return &dp->devlink_port; } static void dsa_slave_get_stats64(struct net_device *dev, @@ -1792,10 +2182,8 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_netpoll_cleanup = dsa_slave_netpoll_cleanup, .ndo_poll_controller = dsa_slave_poll_controller, #endif - .ndo_get_phys_port_name = dsa_slave_get_phys_port_name, .ndo_setup_tc = dsa_slave_setup_tc, .ndo_get_stats64 = dsa_slave_get_stats64, - .ndo_get_port_parent_id = dsa_slave_get_port_parent_id, .ndo_vlan_rx_add_vid = dsa_slave_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = dsa_slave_vlan_rx_kill_vid, .ndo_get_devlink_port = dsa_slave_get_devlink_port, @@ -1917,15 +2305,6 @@ void dsa_slave_setup_tagger(struct net_device *slave) slave->features |= NETIF_F_HW_VLAN_CTAG_FILTER; } -static struct lock_class_key dsa_slave_netdev_xmit_lock_key; -static void dsa_slave_set_lockdep_class_one(struct net_device *dev, - struct netdev_queue *txq, - void *_unused) -{ - lockdep_set_class(&txq->_xmit_lock, - &dsa_slave_netdev_xmit_lock_key); -} - int dsa_slave_suspend(struct net_device *slave_dev) { struct dsa_port *dp = dsa_slave_to_port(slave_dev); @@ -1978,19 +2357,21 @@ int dsa_slave_create(struct dsa_port *port) return -ENOMEM; slave_dev->ethtool_ops = &dsa_slave_ethtool_ops; +#if IS_ENABLED(CONFIG_DCB) + slave_dev->dcbnl_ops = &dsa_slave_dcbnl_ops; +#endif if (!is_zero_ether_addr(port->mac)) eth_hw_addr_set(slave_dev, port->mac); else eth_hw_addr_inherit(slave_dev, master); slave_dev->priv_flags |= IFF_NO_QUEUE; + if (dsa_switch_supports_uc_filtering(ds)) + slave_dev->priv_flags |= IFF_UNICAST_FLT; slave_dev->netdev_ops = &dsa_slave_netdev_ops; if (ds->ops->port_max_mtu) slave_dev->max_mtu = ds->ops->port_max_mtu(ds, port->index); SET_NETDEV_DEVTYPE(slave_dev, &dsa_type); - netdev_for_each_tx_queue(slave_dev, dsa_slave_set_lockdep_class_one, - NULL); - SET_NETDEV_DEV(slave_dev, port->ds->dev); slave_dev->dev.of_node = port->dn; slave_dev->vlan_features = master->vlan_features; @@ -2036,6 +2417,17 @@ int dsa_slave_create(struct dsa_port *port) goto out_phy; } + if (IS_ENABLED(CONFIG_DCB)) { + ret = dsa_slave_dcbnl_init(slave_dev); + if (ret) { + netdev_err(slave_dev, + "failed to initialize DCB: %pe\n", + ERR_PTR(ret)); + rtnl_unlock(); + goto out_unregister; + } + } + ret = netdev_upper_dev_link(master, slave_dev, NULL); rtnl_unlock(); @@ -2172,7 +2564,7 @@ dsa_slave_lag_changeupper(struct net_device *dev, continue; dp = dsa_slave_to_port(lower); - if (!dp->lag_dev) + if (!dp->lag) /* Software LAG */ continue; @@ -2201,7 +2593,7 @@ dsa_slave_lag_prechangeupper(struct net_device *dev, continue; dp = dsa_slave_to_port(lower); - if (!dp->lag_dev) + if (!dp->lag) /* Software LAG */ continue; @@ -2346,6 +2738,36 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb, err = dsa_port_lag_change(dp, info->lower_state_info); return notifier_from_errno(err); } + case NETDEV_CHANGE: + case NETDEV_UP: { + /* Track state of master port. + * DSA driver may require the master port (and indirectly + * the tagger) to be available for some special operation. + */ + if (netdev_uses_dsa(dev)) { + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_switch_tree *dst = cpu_dp->ds->dst; + + /* Track when the master port is UP */ + dsa_tree_master_oper_state_change(dst, dev, + netif_oper_up(dev)); + + /* Track when the master port is ready and can accept + * packet. + * NETDEV_UP event is not enough to flag a port as ready. + * We also have to wait for linkwatch_do_dev to dev_activate + * and emit a NETDEV_CHANGE event. + * We check if a master port is ready by checking if the dev + * have a qdisc assigned and is not noop. + */ + dsa_tree_master_admin_state_change(dst, dev, + !qdisc_tx_is_noop(dev)); + + return NOTIFY_OK; + } + + return NOTIFY_DONE; + } case NETDEV_GOING_DOWN: { struct dsa_port *dp, *cpu_dp; struct dsa_switch_tree *dst; @@ -2357,6 +2779,8 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb, cpu_dp = dev->dsa_ptr; dst = cpu_dp->ds->dst; + dsa_tree_master_admin_state_change(dst, dev, false); + list_for_each_entry(dp, &dst->ports, list) { if (!dsa_port_is_user(dp)) continue; @@ -2379,43 +2803,40 @@ static void dsa_fdb_offload_notify(struct dsa_switchdev_event_work *switchdev_work) { struct switchdev_notifier_fdb_info info = {}; - struct dsa_switch *ds = switchdev_work->ds; - struct dsa_port *dp; - - if (!dsa_is_user_port(ds, switchdev_work->port)) - return; info.addr = switchdev_work->addr; info.vid = switchdev_work->vid; info.offloaded = true; - dp = dsa_to_port(ds, switchdev_work->port); call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, - dp->slave, &info.info, NULL); + switchdev_work->orig_dev, &info.info, NULL); } static void dsa_slave_switchdev_event_work(struct work_struct *work) { struct dsa_switchdev_event_work *switchdev_work = container_of(work, struct dsa_switchdev_event_work, work); - struct dsa_switch *ds = switchdev_work->ds; + const unsigned char *addr = switchdev_work->addr; + struct net_device *dev = switchdev_work->dev; + u16 vid = switchdev_work->vid; + struct dsa_switch *ds; struct dsa_port *dp; int err; - dp = dsa_to_port(ds, switchdev_work->port); + dp = dsa_slave_to_port(dev); + ds = dp->ds; switch (switchdev_work->event) { case SWITCHDEV_FDB_ADD_TO_DEVICE: if (switchdev_work->host_addr) - err = dsa_port_host_fdb_add(dp, switchdev_work->addr, - switchdev_work->vid); + err = dsa_port_bridge_host_fdb_add(dp, addr, vid); + else if (dp->lag) + err = dsa_port_lag_fdb_add(dp, addr, vid); else - err = dsa_port_fdb_add(dp, switchdev_work->addr, - switchdev_work->vid); + err = dsa_port_fdb_add(dp, addr, vid); if (err) { dev_err(ds->dev, "port %d failed to add %pM vid %d to fdb: %d\n", - dp->index, switchdev_work->addr, - switchdev_work->vid, err); + dp->index, addr, vid, err); break; } dsa_fdb_offload_notify(switchdev_work); @@ -2423,16 +2844,15 @@ static void dsa_slave_switchdev_event_work(struct work_struct *work) case SWITCHDEV_FDB_DEL_TO_DEVICE: if (switchdev_work->host_addr) - err = dsa_port_host_fdb_del(dp, switchdev_work->addr, - switchdev_work->vid); + err = dsa_port_bridge_host_fdb_del(dp, addr, vid); + else if (dp->lag) + err = dsa_port_lag_fdb_del(dp, addr, vid); else - err = dsa_port_fdb_del(dp, switchdev_work->addr, - switchdev_work->vid); + err = dsa_port_fdb_del(dp, addr, vid); if (err) { dev_err(ds->dev, "port %d failed to delete %pM vid %d from fdb: %d\n", - dp->index, switchdev_work->addr, - switchdev_work->vid, err); + dp->index, addr, vid, err); } break; @@ -2470,19 +2890,20 @@ static int dsa_slave_fdb_event(struct net_device *dev, if (ctx && ctx != dp) return 0; - if (!ds->ops->port_fdb_add || !ds->ops->port_fdb_del) - return -EOPNOTSUPP; - - if (dsa_slave_dev_check(orig_dev) && - switchdev_fdb_is_dynamically_learned(fdb_info)) + if (!dp->bridge) return 0; - /* FDB entries learned by the software bridge should be installed as - * host addresses only if the driver requests assisted learning. - */ - if (switchdev_fdb_is_dynamically_learned(fdb_info) && - !ds->assisted_learning_on_cpu_port) - return 0; + if (switchdev_fdb_is_dynamically_learned(fdb_info)) { + if (dsa_port_offloads_bridge_port(dp, orig_dev)) + return 0; + + /* FDB entries learned by the software bridge or by foreign + * bridge ports should be installed as host addresses only if + * the driver requests assisted learning. + */ + if (!ds->assisted_learning_on_cpu_port) + return 0; + } /* Also treat FDB entries on foreign interfaces bridged with us as host * addresses. @@ -2490,6 +2911,18 @@ static int dsa_slave_fdb_event(struct net_device *dev, if (dsa_foreign_dev_check(dev, orig_dev)) host_addr = true; + /* Check early that we're not doing work in vain. + * Host addresses on LAG ports still require regular FDB ops, + * since the CPU port isn't in a LAG. + */ + if (dp->lag && !host_addr) { + if (!ds->ops->lag_fdb_add || !ds->ops->lag_fdb_del) + return -EOPNOTSUPP; + } else { + if (!ds->ops->port_fdb_add || !ds->ops->port_fdb_del) + return -EOPNOTSUPP; + } + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); if (!switchdev_work) return -ENOMEM; @@ -2500,10 +2933,9 @@ static int dsa_slave_fdb_event(struct net_device *dev, host_addr ? " as host address" : ""); INIT_WORK(&switchdev_work->work, dsa_slave_switchdev_event_work); - switchdev_work->ds = ds; - switchdev_work->port = dp->index; switchdev_work->event = event; switchdev_work->dev = dev; + switchdev_work->orig_dev = orig_dev; ether_addr_copy(switchdev_work->addr, fdb_info->addr); switchdev_work->vid = fdb_info->vid; @@ -2532,8 +2964,7 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused, err = switchdev_handle_fdb_event_to_device(dev, event, ptr, dsa_slave_dev_check, dsa_foreign_dev_check, - dsa_slave_fdb_event, - NULL); + dsa_slave_fdb_event); return notifier_from_errno(err); default: return NOTIFY_DONE; @@ -2550,14 +2981,16 @@ static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused, switch (event) { case SWITCHDEV_PORT_OBJ_ADD: - err = switchdev_handle_port_obj_add(dev, ptr, - dsa_slave_dev_check, - dsa_slave_port_obj_add); + err = switchdev_handle_port_obj_add_foreign(dev, ptr, + dsa_slave_dev_check, + dsa_foreign_dev_check, + dsa_slave_port_obj_add); return notifier_from_errno(err); case SWITCHDEV_PORT_OBJ_DEL: - err = switchdev_handle_port_obj_del(dev, ptr, - dsa_slave_dev_check, - dsa_slave_port_obj_del); + err = switchdev_handle_port_obj_del_foreign(dev, ptr, + dsa_slave_dev_check, + dsa_foreign_dev_check, + dsa_slave_port_obj_del); return notifier_from_errno(err); case SWITCHDEV_PORT_ATTR_SET: err = switchdev_handle_port_attr_set(dev, ptr, diff --git a/net/dsa/switch.c b/net/dsa/switch.c index e3c7d2627a61..d25cd1da3eb3 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -96,7 +96,8 @@ static int dsa_switch_bridge_join(struct dsa_switch *ds, return -EOPNOTSUPP; err = ds->ops->port_bridge_join(ds, info->port, info->bridge, - &info->tx_fwd_offload); + &info->tx_fwd_offload, + info->extack); if (err) return err; } @@ -105,34 +106,24 @@ static int dsa_switch_bridge_join(struct dsa_switch *ds, ds->ops->crosschip_bridge_join) { err = ds->ops->crosschip_bridge_join(ds, info->tree_index, info->sw_index, - info->port, info->bridge); + info->port, info->bridge, + info->extack); if (err) return err; } - return dsa_tag_8021q_bridge_join(ds, info); + return 0; } -static int dsa_switch_bridge_leave(struct dsa_switch *ds, - struct dsa_notifier_bridge_info *info) +static int dsa_switch_sync_vlan_filtering(struct dsa_switch *ds, + struct dsa_notifier_bridge_info *info) { - struct dsa_switch_tree *dst = ds->dst; struct netlink_ext_ack extack = {0}; bool change_vlan_filtering = false; bool vlan_filtering; struct dsa_port *dp; int err; - if (dst->index == info->tree_index && ds->index == info->sw_index && - ds->ops->port_bridge_leave) - ds->ops->port_bridge_leave(ds, info->port, info->bridge); - - if ((dst->index != info->tree_index || ds->index != info->sw_index) && - ds->ops->crosschip_bridge_leave) - ds->ops->crosschip_bridge_leave(ds, info->tree_index, - info->sw_index, info->port, - info->bridge); - if (ds->needs_standalone_vlan_filtering && !br_vlan_enabled(info->bridge.dev)) { change_vlan_filtering = true; @@ -172,7 +163,32 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds, return err; } - return dsa_tag_8021q_bridge_leave(ds, info); + return 0; +} + +static int dsa_switch_bridge_leave(struct dsa_switch *ds, + struct dsa_notifier_bridge_info *info) +{ + struct dsa_switch_tree *dst = ds->dst; + int err; + + if (dst->index == info->tree_index && ds->index == info->sw_index && + ds->ops->port_bridge_leave) + ds->ops->port_bridge_leave(ds, info->port, info->bridge); + + if ((dst->index != info->tree_index || ds->index != info->sw_index) && + ds->ops->crosschip_bridge_leave) + ds->ops->crosschip_bridge_leave(ds, info->tree_index, + info->sw_index, info->port, + info->bridge); + + if (ds->dst->index == info->tree_index && ds->index == info->sw_index) { + err = dsa_switch_sync_vlan_filtering(ds, info); + if (err) + return err; + } + + return 0; } /* Matches for all upstream-facing ports (the CPU port and all upstream-facing @@ -197,20 +213,22 @@ static bool dsa_port_host_address_match(struct dsa_port *dp, } static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list, - const unsigned char *addr, - u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct dsa_mac_addr *a; list_for_each_entry(a, addr_list, list) - if (ether_addr_equal(a->addr, addr) && a->vid == vid) + if (ether_addr_equal(a->addr, addr) && a->vid == vid && + dsa_db_equal(&a->db, &db)) return a; return NULL; } static int dsa_port_do_mdb_add(struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct dsa_switch *ds = dp->ds; struct dsa_mac_addr *a; @@ -219,11 +237,11 @@ static int dsa_port_do_mdb_add(struct dsa_port *dp, /* No need to bother with refcounting for user ports */ if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) - return ds->ops->port_mdb_add(ds, port, mdb); + return ds->ops->port_mdb_add(ds, port, mdb, db); mutex_lock(&dp->addr_lists_lock); - a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid); + a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db); if (a) { refcount_inc(&a->refcount); goto out; @@ -235,7 +253,7 @@ static int dsa_port_do_mdb_add(struct dsa_port *dp, goto out; } - err = ds->ops->port_mdb_add(ds, port, mdb); + err = ds->ops->port_mdb_add(ds, port, mdb, db); if (err) { kfree(a); goto out; @@ -243,6 +261,7 @@ static int dsa_port_do_mdb_add(struct dsa_port *dp, ether_addr_copy(a->addr, mdb->addr); a->vid = mdb->vid; + a->db = db; refcount_set(&a->refcount, 1); list_add_tail(&a->list, &dp->mdbs); @@ -253,7 +272,8 @@ static int dsa_port_do_mdb_add(struct dsa_port *dp, } static int dsa_port_do_mdb_del(struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct dsa_switch *ds = dp->ds; struct dsa_mac_addr *a; @@ -262,11 +282,11 @@ static int dsa_port_do_mdb_del(struct dsa_port *dp, /* No need to bother with refcounting for user ports */ if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) - return ds->ops->port_mdb_del(ds, port, mdb); + return ds->ops->port_mdb_del(ds, port, mdb, db); mutex_lock(&dp->addr_lists_lock); - a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid); + a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db); if (!a) { err = -ENOENT; goto out; @@ -275,7 +295,7 @@ static int dsa_port_do_mdb_del(struct dsa_port *dp, if (!refcount_dec_and_test(&a->refcount)) goto out; - err = ds->ops->port_mdb_del(ds, port, mdb); + err = ds->ops->port_mdb_del(ds, port, mdb, db); if (err) { refcount_set(&a->refcount, 1); goto out; @@ -291,7 +311,7 @@ static int dsa_port_do_mdb_del(struct dsa_port *dp, } static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr, - u16 vid) + u16 vid, struct dsa_db db) { struct dsa_switch *ds = dp->ds; struct dsa_mac_addr *a; @@ -300,11 +320,11 @@ static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr, /* No need to bother with refcounting for user ports */ if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) - return ds->ops->port_fdb_add(ds, port, addr, vid); + return ds->ops->port_fdb_add(ds, port, addr, vid, db); mutex_lock(&dp->addr_lists_lock); - a = dsa_mac_addr_find(&dp->fdbs, addr, vid); + a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db); if (a) { refcount_inc(&a->refcount); goto out; @@ -316,7 +336,7 @@ static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr, goto out; } - err = ds->ops->port_fdb_add(ds, port, addr, vid); + err = ds->ops->port_fdb_add(ds, port, addr, vid, db); if (err) { kfree(a); goto out; @@ -324,6 +344,7 @@ static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr, ether_addr_copy(a->addr, addr); a->vid = vid; + a->db = db; refcount_set(&a->refcount, 1); list_add_tail(&a->list, &dp->fdbs); @@ -334,7 +355,7 @@ static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr, } static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr, - u16 vid) + u16 vid, struct dsa_db db) { struct dsa_switch *ds = dp->ds; struct dsa_mac_addr *a; @@ -343,11 +364,11 @@ static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr, /* No need to bother with refcounting for user ports */ if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) - return ds->ops->port_fdb_del(ds, port, addr, vid); + return ds->ops->port_fdb_del(ds, port, addr, vid, db); mutex_lock(&dp->addr_lists_lock); - a = dsa_mac_addr_find(&dp->fdbs, addr, vid); + a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db); if (!a) { err = -ENOENT; goto out; @@ -356,7 +377,7 @@ static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr, if (!refcount_dec_and_test(&a->refcount)) goto out; - err = ds->ops->port_fdb_del(ds, port, addr, vid); + err = ds->ops->port_fdb_del(ds, port, addr, vid, db); if (err) { refcount_set(&a->refcount, 1); goto out; @@ -371,6 +392,77 @@ static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr, return err; } +static int dsa_switch_do_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag *lag, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct dsa_mac_addr *a; + int err = 0; + + mutex_lock(&lag->fdb_lock); + + a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db); + if (a) { + refcount_inc(&a->refcount); + goto out; + } + + a = kzalloc(sizeof(*a), GFP_KERNEL); + if (!a) { + err = -ENOMEM; + goto out; + } + + err = ds->ops->lag_fdb_add(ds, *lag, addr, vid, db); + if (err) { + kfree(a); + goto out; + } + + ether_addr_copy(a->addr, addr); + a->vid = vid; + refcount_set(&a->refcount, 1); + list_add_tail(&a->list, &lag->fdbs); + +out: + mutex_unlock(&lag->fdb_lock); + + return err; +} + +static int dsa_switch_do_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag *lag, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct dsa_mac_addr *a; + int err = 0; + + mutex_lock(&lag->fdb_lock); + + a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db); + if (!a) { + err = -ENOENT; + goto out; + } + + if (!refcount_dec_and_test(&a->refcount)) + goto out; + + err = ds->ops->lag_fdb_del(ds, *lag, addr, vid, db); + if (err) { + refcount_set(&a->refcount, 1); + goto out; + } + + list_del(&a->list); + kfree(a); + +out: + mutex_unlock(&lag->fdb_lock); + + return err; +} + static int dsa_switch_host_fdb_add(struct dsa_switch *ds, struct dsa_notifier_fdb_info *info) { @@ -383,7 +475,8 @@ static int dsa_switch_host_fdb_add(struct dsa_switch *ds, dsa_switch_for_each_port(dp, ds) { if (dsa_port_host_address_match(dp, info->sw_index, info->port)) { - err = dsa_port_do_fdb_add(dp, info->addr, info->vid); + err = dsa_port_do_fdb_add(dp, info->addr, info->vid, + info->db); if (err) break; } @@ -404,7 +497,8 @@ static int dsa_switch_host_fdb_del(struct dsa_switch *ds, dsa_switch_for_each_port(dp, ds) { if (dsa_port_host_address_match(dp, info->sw_index, info->port)) { - err = dsa_port_do_fdb_del(dp, info->addr, info->vid); + err = dsa_port_do_fdb_del(dp, info->addr, info->vid, + info->db); if (err) break; } @@ -422,7 +516,7 @@ static int dsa_switch_fdb_add(struct dsa_switch *ds, if (!ds->ops->port_fdb_add) return -EOPNOTSUPP; - return dsa_port_do_fdb_add(dp, info->addr, info->vid); + return dsa_port_do_fdb_add(dp, info->addr, info->vid, info->db); } static int dsa_switch_fdb_del(struct dsa_switch *ds, @@ -434,7 +528,43 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds, if (!ds->ops->port_fdb_del) return -EOPNOTSUPP; - return dsa_port_do_fdb_del(dp, info->addr, info->vid); + return dsa_port_do_fdb_del(dp, info->addr, info->vid, info->db); +} + +static int dsa_switch_lag_fdb_add(struct dsa_switch *ds, + struct dsa_notifier_lag_fdb_info *info) +{ + struct dsa_port *dp; + + if (!ds->ops->lag_fdb_add) + return -EOPNOTSUPP; + + /* Notify switch only if it has a port in this LAG */ + dsa_switch_for_each_port(dp, ds) + if (dsa_port_offloads_lag(dp, info->lag)) + return dsa_switch_do_lag_fdb_add(ds, info->lag, + info->addr, info->vid, + info->db); + + return 0; +} + +static int dsa_switch_lag_fdb_del(struct dsa_switch *ds, + struct dsa_notifier_lag_fdb_info *info) +{ + struct dsa_port *dp; + + if (!ds->ops->lag_fdb_del) + return -EOPNOTSUPP; + + /* Notify switch only if it has a port in this LAG */ + dsa_switch_for_each_port(dp, ds) + if (dsa_port_offloads_lag(dp, info->lag)) + return dsa_switch_do_lag_fdb_del(ds, info->lag, + info->addr, info->vid, + info->db); + + return 0; } static int dsa_switch_lag_change(struct dsa_switch *ds, @@ -487,7 +617,7 @@ static int dsa_switch_mdb_add(struct dsa_switch *ds, if (!ds->ops->port_mdb_add) return -EOPNOTSUPP; - return dsa_port_do_mdb_add(dp, info->mdb); + return dsa_port_do_mdb_add(dp, info->mdb, info->db); } static int dsa_switch_mdb_del(struct dsa_switch *ds, @@ -499,7 +629,7 @@ static int dsa_switch_mdb_del(struct dsa_switch *ds, if (!ds->ops->port_mdb_del) return -EOPNOTSUPP; - return dsa_port_do_mdb_del(dp, info->mdb); + return dsa_port_do_mdb_del(dp, info->mdb, info->db); } static int dsa_switch_host_mdb_add(struct dsa_switch *ds, @@ -514,7 +644,7 @@ static int dsa_switch_host_mdb_add(struct dsa_switch *ds, dsa_switch_for_each_port(dp, ds) { if (dsa_port_host_address_match(dp, info->sw_index, info->port)) { - err = dsa_port_do_mdb_add(dp, info->mdb); + err = dsa_port_do_mdb_add(dp, info->mdb, info->db); if (err) break; } @@ -535,7 +665,7 @@ static int dsa_switch_host_mdb_del(struct dsa_switch *ds, dsa_switch_for_each_port(dp, ds) { if (dsa_port_host_address_match(dp, info->sw_index, info->port)) { - err = dsa_port_do_mdb_del(dp, info->mdb); + err = dsa_port_do_mdb_del(dp, info->mdb, info->db); if (err) break; } @@ -544,6 +674,7 @@ static int dsa_switch_host_mdb_del(struct dsa_switch *ds, return err; } +/* Port VLANs match on the targeted port and on all DSA ports */ static bool dsa_port_vlan_match(struct dsa_port *dp, struct dsa_notifier_vlan_info *info) { @@ -556,6 +687,126 @@ static bool dsa_port_vlan_match(struct dsa_port *dp, return false; } +/* Host VLANs match on the targeted port's CPU port, and on all DSA ports + * (upstream and downstream) of that switch and its upstream switches. + */ +static bool dsa_port_host_vlan_match(struct dsa_port *dp, + struct dsa_notifier_vlan_info *info) +{ + struct dsa_port *targeted_dp, *cpu_dp; + struct dsa_switch *targeted_ds; + + targeted_ds = dsa_switch_find(dp->ds->dst->index, info->sw_index); + targeted_dp = dsa_to_port(targeted_ds, info->port); + cpu_dp = targeted_dp->cpu_dp; + + if (dsa_switch_is_upstream_of(dp->ds, targeted_ds)) + return dsa_port_is_dsa(dp) || dp == cpu_dp; + + return false; +} + +static struct dsa_vlan *dsa_vlan_find(struct list_head *vlan_list, + const struct switchdev_obj_port_vlan *vlan) +{ + struct dsa_vlan *v; + + list_for_each_entry(v, vlan_list, list) + if (v->vid == vlan->vid) + return v; + + return NULL; +} + +static int dsa_port_do_vlan_add(struct dsa_port *dp, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + struct dsa_switch *ds = dp->ds; + int port = dp->index; + struct dsa_vlan *v; + int err = 0; + + /* No need to bother with refcounting for user ports. */ + if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) + return ds->ops->port_vlan_add(ds, port, vlan, extack); + + /* No need to propagate on shared ports the existing VLANs that were + * re-notified after just the flags have changed. This would cause a + * refcount bump which we need to avoid, since it unbalances the + * additions with the deletions. + */ + if (vlan->changed) + return 0; + + mutex_lock(&dp->vlans_lock); + + v = dsa_vlan_find(&dp->vlans, vlan); + if (v) { + refcount_inc(&v->refcount); + goto out; + } + + v = kzalloc(sizeof(*v), GFP_KERNEL); + if (!v) { + err = -ENOMEM; + goto out; + } + + err = ds->ops->port_vlan_add(ds, port, vlan, extack); + if (err) { + kfree(v); + goto out; + } + + v->vid = vlan->vid; + refcount_set(&v->refcount, 1); + list_add_tail(&v->list, &dp->vlans); + +out: + mutex_unlock(&dp->vlans_lock); + + return err; +} + +static int dsa_port_do_vlan_del(struct dsa_port *dp, + const struct switchdev_obj_port_vlan *vlan) +{ + struct dsa_switch *ds = dp->ds; + int port = dp->index; + struct dsa_vlan *v; + int err = 0; + + /* No need to bother with refcounting for user ports */ + if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) + return ds->ops->port_vlan_del(ds, port, vlan); + + mutex_lock(&dp->vlans_lock); + + v = dsa_vlan_find(&dp->vlans, vlan); + if (!v) { + err = -ENOENT; + goto out; + } + + if (!refcount_dec_and_test(&v->refcount)) + goto out; + + err = ds->ops->port_vlan_del(ds, port, vlan); + if (err) { + refcount_set(&v->refcount, 1); + goto out; + } + + list_del(&v->list); + kfree(v); + +out: + mutex_unlock(&dp->vlans_lock); + + return err; +} + static int dsa_switch_vlan_add(struct dsa_switch *ds, struct dsa_notifier_vlan_info *info) { @@ -567,8 +818,8 @@ static int dsa_switch_vlan_add(struct dsa_switch *ds, dsa_switch_for_each_port(dp, ds) { if (dsa_port_vlan_match(dp, info)) { - err = ds->ops->port_vlan_add(ds, dp->index, info->vlan, - info->extack); + err = dsa_port_do_vlan_add(dp, info->vlan, + info->extack); if (err) return err; } @@ -580,15 +831,61 @@ static int dsa_switch_vlan_add(struct dsa_switch *ds, static int dsa_switch_vlan_del(struct dsa_switch *ds, struct dsa_notifier_vlan_info *info) { + struct dsa_port *dp; + int err; + if (!ds->ops->port_vlan_del) return -EOPNOTSUPP; - if (ds->index == info->sw_index) - return ds->ops->port_vlan_del(ds, info->port, info->vlan); + dsa_switch_for_each_port(dp, ds) { + if (dsa_port_vlan_match(dp, info)) { + err = dsa_port_do_vlan_del(dp, info->vlan); + if (err) + return err; + } + } + + return 0; +} + +static int dsa_switch_host_vlan_add(struct dsa_switch *ds, + struct dsa_notifier_vlan_info *info) +{ + struct dsa_port *dp; + int err; + + if (!ds->ops->port_vlan_add) + return -EOPNOTSUPP; + + dsa_switch_for_each_port(dp, ds) { + if (dsa_port_host_vlan_match(dp, info)) { + err = dsa_port_do_vlan_add(dp, info->vlan, + info->extack); + if (err) + return err; + } + } + + return 0; +} + +static int dsa_switch_host_vlan_del(struct dsa_switch *ds, + struct dsa_notifier_vlan_info *info) +{ + struct dsa_port *dp; + int err; + + if (!ds->ops->port_vlan_del) + return -EOPNOTSUPP; + + dsa_switch_for_each_port(dp, ds) { + if (dsa_port_host_vlan_match(dp, info)) { + err = dsa_port_do_vlan_del(dp, info->vlan); + if (err) + return err; + } + } - /* Do not deprogram the DSA links as they may be used as conduit - * for other VLAN members in the fabric. - */ return 0; } @@ -683,6 +980,18 @@ dsa_switch_disconnect_tag_proto(struct dsa_switch *ds, return 0; } +static int +dsa_switch_master_state_change(struct dsa_switch *ds, + struct dsa_notifier_master_state_info *info) +{ + if (!ds->ops->master_state_change) + return 0; + + ds->ops->master_state_change(ds, info->master, info->operational); + + return 0; +} + static int dsa_switch_event(struct notifier_block *nb, unsigned long event, void *info) { @@ -711,6 +1020,12 @@ static int dsa_switch_event(struct notifier_block *nb, case DSA_NOTIFIER_HOST_FDB_DEL: err = dsa_switch_host_fdb_del(ds, info); break; + case DSA_NOTIFIER_LAG_FDB_ADD: + err = dsa_switch_lag_fdb_add(ds, info); + break; + case DSA_NOTIFIER_LAG_FDB_DEL: + err = dsa_switch_lag_fdb_del(ds, info); + break; case DSA_NOTIFIER_LAG_CHANGE: err = dsa_switch_lag_change(ds, info); break; @@ -738,6 +1053,12 @@ static int dsa_switch_event(struct notifier_block *nb, case DSA_NOTIFIER_VLAN_DEL: err = dsa_switch_vlan_del(ds, info); break; + case DSA_NOTIFIER_HOST_VLAN_ADD: + err = dsa_switch_host_vlan_add(ds, info); + break; + case DSA_NOTIFIER_HOST_VLAN_DEL: + err = dsa_switch_host_vlan_del(ds, info); + break; case DSA_NOTIFIER_MTU: err = dsa_switch_mtu(ds, info); break; @@ -756,6 +1077,9 @@ static int dsa_switch_event(struct notifier_block *nb, case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL: err = dsa_switch_tag_8021q_vlan_del(ds, info); break; + case DSA_NOTIFIER_MASTER_STATE_CHANGE: + err = dsa_switch_master_state_change(ds, info); + break; default: err = -EOPNOTSUPP; break; diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c index 27712a81c967..a786569203f0 100644 --- a/net/dsa/tag_8021q.c +++ b/net/dsa/tag_8021q.c @@ -16,15 +16,11 @@ * * | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | * +-----------+-----+-----------------+-----------+-----------------------+ - * | DIR | VBID| SWITCH_ID | VBID | PORT | + * | RSV | VBID| SWITCH_ID | VBID | PORT | * +-----------+-----+-----------------+-----------+-----------------------+ * - * DIR - VID[11:10]: - * Direction flags. - * * 1 (0b01) for RX VLAN, - * * 2 (0b10) for TX VLAN. - * These values make the special VIDs of 0, 1 and 4095 to be left - * unused by this coding scheme. + * RSV - VID[11:10]: + * Reserved. Must be set to 3 (0b11). * * SWITCH_ID - VID[8:6]: * Index of switch within DSA tree. Must be between 0 and 7. @@ -32,18 +28,17 @@ * VBID - { VID[9], VID[5:4] }: * Virtual bridge ID. If between 1 and 7, packet targets the broadcast * domain of a bridge. If transmitted as zero, packet targets a single - * port. Field only valid on transmit, must be ignored on receive. + * port. * * PORT - VID[3:0]: * Index of switch port. Must be between 0 and 15. */ -#define DSA_8021Q_DIR_SHIFT 10 -#define DSA_8021Q_DIR_MASK GENMASK(11, 10) -#define DSA_8021Q_DIR(x) (((x) << DSA_8021Q_DIR_SHIFT) & \ - DSA_8021Q_DIR_MASK) -#define DSA_8021Q_DIR_RX DSA_8021Q_DIR(1) -#define DSA_8021Q_DIR_TX DSA_8021Q_DIR(2) +#define DSA_8021Q_RSV_VAL 3 +#define DSA_8021Q_RSV_SHIFT 10 +#define DSA_8021Q_RSV_MASK GENMASK(11, 10) +#define DSA_8021Q_RSV ((DSA_8021Q_RSV_VAL << DSA_8021Q_RSV_SHIFT) & \ + DSA_8021Q_RSV_MASK) #define DSA_8021Q_SWITCH_ID_SHIFT 6 #define DSA_8021Q_SWITCH_ID_MASK GENMASK(8, 6) @@ -67,34 +62,24 @@ #define DSA_8021Q_PORT(x) (((x) << DSA_8021Q_PORT_SHIFT) & \ DSA_8021Q_PORT_MASK) -u16 dsa_8021q_bridge_tx_fwd_offload_vid(unsigned int bridge_num) +u16 dsa_tag_8021q_bridge_vid(unsigned int bridge_num) { /* The VBID value of 0 is reserved for precise TX, but it is also * reserved/invalid for the bridge_num, so all is well. */ - return DSA_8021Q_DIR_TX | DSA_8021Q_VBID(bridge_num); + return DSA_8021Q_RSV | DSA_8021Q_VBID(bridge_num); } -EXPORT_SYMBOL_GPL(dsa_8021q_bridge_tx_fwd_offload_vid); - -/* Returns the VID to be inserted into the frame from xmit for switch steering - * instructions on egress. Encodes switch ID and port ID. - */ -u16 dsa_tag_8021q_tx_vid(const struct dsa_port *dp) -{ - return DSA_8021Q_DIR_TX | DSA_8021Q_SWITCH_ID(dp->ds->index) | - DSA_8021Q_PORT(dp->index); -} -EXPORT_SYMBOL_GPL(dsa_tag_8021q_tx_vid); +EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_vid); /* Returns the VID that will be installed as pvid for this switch port, sent as * tagged egress towards the CPU port and decoded by the rcv function. */ -u16 dsa_tag_8021q_rx_vid(const struct dsa_port *dp) +u16 dsa_tag_8021q_standalone_vid(const struct dsa_port *dp) { - return DSA_8021Q_DIR_RX | DSA_8021Q_SWITCH_ID(dp->ds->index) | + return DSA_8021Q_RSV | DSA_8021Q_SWITCH_ID(dp->ds->index) | DSA_8021Q_PORT(dp->index); } -EXPORT_SYMBOL_GPL(dsa_tag_8021q_rx_vid); +EXPORT_SYMBOL_GPL(dsa_tag_8021q_standalone_vid); /* Returns the decoded switch ID from the RX VID. */ int dsa_8021q_rx_switch_id(u16 vid) @@ -110,21 +95,20 @@ int dsa_8021q_rx_source_port(u16 vid) } EXPORT_SYMBOL_GPL(dsa_8021q_rx_source_port); -bool vid_is_dsa_8021q_rxvlan(u16 vid) +/* Returns the decoded VBID from the RX VID. */ +static int dsa_tag_8021q_rx_vbid(u16 vid) { - return (vid & DSA_8021Q_DIR_MASK) == DSA_8021Q_DIR_RX; -} -EXPORT_SYMBOL_GPL(vid_is_dsa_8021q_rxvlan); + u16 vbid_hi = (vid & DSA_8021Q_VBID_HI_MASK) >> DSA_8021Q_VBID_HI_SHIFT; + u16 vbid_lo = (vid & DSA_8021Q_VBID_LO_MASK) >> DSA_8021Q_VBID_LO_SHIFT; -bool vid_is_dsa_8021q_txvlan(u16 vid) -{ - return (vid & DSA_8021Q_DIR_MASK) == DSA_8021Q_DIR_TX; + return (vbid_hi << 2) | vbid_lo; } -EXPORT_SYMBOL_GPL(vid_is_dsa_8021q_txvlan); bool vid_is_dsa_8021q(u16 vid) { - return vid_is_dsa_8021q_rxvlan(vid) || vid_is_dsa_8021q_txvlan(vid); + u16 rsv = (vid & DSA_8021Q_RSV_MASK) >> DSA_8021Q_RSV_SHIFT; + + return rsv == DSA_8021Q_RSV_VAL; } EXPORT_SYMBOL_GPL(vid_is_dsa_8021q); @@ -242,12 +226,8 @@ int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds, u16 flags = 0; if (dsa_port_is_user(dp)) - flags |= BRIDGE_VLAN_INFO_UNTAGGED; - - if (vid_is_dsa_8021q_rxvlan(info->vid) && - dsa_8021q_rx_switch_id(info->vid) == ds->index && - dsa_8021q_rx_source_port(info->vid) == dp->index) - flags |= BRIDGE_VLAN_INFO_PVID; + flags |= BRIDGE_VLAN_INFO_UNTAGGED | + BRIDGE_VLAN_INFO_PVID; err = dsa_port_do_tag_8021q_vlan_add(dp, info->vid, flags); @@ -279,162 +259,78 @@ int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds, return 0; } -/* RX VLAN tagging (left) and TX VLAN tagging (right) setup shown for a single - * front-panel switch port (here swp0). +/* There are 2 ways of offloading tag_8021q VLANs. * - * Port identification through VLAN (802.1Q) tags has different requirements - * for it to work effectively: - * - On RX (ingress from network): each front-panel port must have a pvid - * that uniquely identifies it, and the egress of this pvid must be tagged - * towards the CPU port, so that software can recover the source port based - * on the VID in the frame. But this would only work for standalone ports; - * if bridged, this VLAN setup would break autonomous forwarding and would - * force all switched traffic to pass through the CPU. So we must also make - * the other front-panel ports members of this VID we're adding, albeit - * we're not making it their PVID (they'll still have their own). - * - On TX (ingress from CPU and towards network) we are faced with a problem. - * If we were to tag traffic (from within DSA) with the port's pvid, all - * would be well, assuming the switch ports were standalone. Frames would - * have no choice but to be directed towards the correct front-panel port. - * But because we also want the RX VLAN to not break bridging, then - * inevitably that means that we have to give them a choice (of what - * front-panel port to go out on), and therefore we cannot steer traffic - * based on the RX VID. So what we do is simply install one more VID on the - * front-panel and CPU ports, and profit off of the fact that steering will - * work just by virtue of the fact that there is only one other port that's - * a member of the VID we're tagging the traffic with - the desired one. + * One is to use a hardware TCAM to push the port's standalone VLAN into the + * frame when forwarding it to the CPU, as an egress modification rule on the + * CPU port. This is preferable because it has no side effects for the + * autonomous forwarding path, and accomplishes tag_8021q's primary goal of + * identifying the source port of each packet based on VLAN ID. * - * So at the end, each front-panel port will have one RX VID (also the PVID), - * the RX VID of all other front-panel ports that are in the same bridge, and - * one TX VID. Whereas the CPU port will have the RX and TX VIDs of all - * front-panel ports, and on top of that, is also tagged-input and - * tagged-output (VLAN trunk). + * The other is to commit the tag_8021q VLAN as a PVID to the VLAN table, and + * to configure the port as VLAN-unaware. This is less preferable because + * unique source port identification can only be done for standalone ports; + * under a VLAN-unaware bridge, all ports share the same tag_8021q VLAN as + * PVID, and under a VLAN-aware bridge, packets received by software will not + * have tag_8021q VLANs appended, just bridge VLANs. * - * CPU port CPU port - * +-------------+-----+-------------+ +-------------+-----+-------------+ - * | RX VID | | | | TX VID | | | - * | of swp0 | | | | of swp0 | | | - * | +-----+ | | +-----+ | - * | ^ T | | | Tagged | - * | | | | | ingress | - * | +-------+---+---+-------+ | | +-----------+ | - * | | | | | | | | Untagged | - * | | U v U v U v | | v egress | - * | +-----+ +-----+ +-----+ +-----+ | | +-----+ +-----+ +-----+ +-----+ | - * | | | | | | | | | | | | | | | | | | | | - * | |PVID | | | | | | | | | | | | | | | | | | - * +-+-----+-+-----+-+-----+-+-----+-+ +-+-----+-+-----+-+-----+-+-----+-+ - * swp0 swp1 swp2 swp3 swp0 swp1 swp2 swp3 + * For tag_8021q implementations of the second type, this method is used to + * replace the standalone tag_8021q VLAN of a port with the tag_8021q VLAN to + * be used for VLAN-unaware bridging. */ -static bool -dsa_port_tag_8021q_bridge_match(struct dsa_port *dp, - struct dsa_notifier_bridge_info *info) +int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) { - /* Don't match on self */ - if (dp->ds->dst->index == info->tree_index && - dp->ds->index == info->sw_index && - dp->index == info->port) - return false; - - if (dsa_port_is_user(dp)) - return dsa_port_offloads_bridge(dp, &info->bridge); - - return false; -} - -int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, - struct dsa_notifier_bridge_info *info) -{ - struct dsa_switch *targeted_ds; - struct dsa_port *targeted_dp; - struct dsa_port *dp; - u16 targeted_rx_vid; + struct dsa_port *dp = dsa_to_port(ds, port); + u16 standalone_vid, bridge_vid; int err; - if (!ds->tag_8021q_ctx) - return 0; + /* Delete the standalone VLAN of the port and replace it with a + * bridging VLAN + */ + standalone_vid = dsa_tag_8021q_standalone_vid(dp); + bridge_vid = dsa_tag_8021q_bridge_vid(bridge.num); - targeted_ds = dsa_switch_find(info->tree_index, info->sw_index); - targeted_dp = dsa_to_port(targeted_ds, info->port); - targeted_rx_vid = dsa_tag_8021q_rx_vid(targeted_dp); + err = dsa_port_tag_8021q_vlan_add(dp, bridge_vid, true); + if (err) + return err; - dsa_switch_for_each_port(dp, ds) { - u16 rx_vid = dsa_tag_8021q_rx_vid(dp); - - if (!dsa_port_tag_8021q_bridge_match(dp, info)) - continue; - - /* Install the RX VID of the targeted port in our VLAN table */ - err = dsa_port_tag_8021q_vlan_add(dp, targeted_rx_vid, true); - if (err) - return err; - - /* Install our RX VID into the targeted port's VLAN table */ - err = dsa_port_tag_8021q_vlan_add(targeted_dp, rx_vid, true); - if (err) - return err; - } + dsa_port_tag_8021q_vlan_del(dp, standalone_vid, false); return 0; } +EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_join); -int dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, - struct dsa_notifier_bridge_info *info) +void dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) { - struct dsa_switch *targeted_ds; - struct dsa_port *targeted_dp; - struct dsa_port *dp; - u16 targeted_rx_vid; + struct dsa_port *dp = dsa_to_port(ds, port); + u16 standalone_vid, bridge_vid; + int err; - if (!ds->tag_8021q_ctx) - return 0; + /* Delete the bridging VLAN of the port and replace it with a + * standalone VLAN + */ + standalone_vid = dsa_tag_8021q_standalone_vid(dp); + bridge_vid = dsa_tag_8021q_bridge_vid(bridge.num); - targeted_ds = dsa_switch_find(info->tree_index, info->sw_index); - targeted_dp = dsa_to_port(targeted_ds, info->port); - targeted_rx_vid = dsa_tag_8021q_rx_vid(targeted_dp); - - dsa_switch_for_each_port(dp, ds) { - u16 rx_vid = dsa_tag_8021q_rx_vid(dp); - - if (!dsa_port_tag_8021q_bridge_match(dp, info)) - continue; - - /* Remove the RX VID of the targeted port from our VLAN table */ - dsa_port_tag_8021q_vlan_del(dp, targeted_rx_vid, true); - - /* Remove our RX VID from the targeted port's VLAN table */ - dsa_port_tag_8021q_vlan_del(targeted_dp, rx_vid, true); + err = dsa_port_tag_8021q_vlan_add(dp, standalone_vid, false); + if (err) { + dev_err(ds->dev, + "Failed to delete tag_8021q standalone VLAN %d from port %d: %pe\n", + standalone_vid, port, ERR_PTR(err)); } - return 0; + dsa_port_tag_8021q_vlan_del(dp, bridge_vid, true); } +EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_leave); -int dsa_tag_8021q_bridge_tx_fwd_offload(struct dsa_switch *ds, int port, - struct dsa_bridge bridge) -{ - u16 tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge.num); - - return dsa_port_tag_8021q_vlan_add(dsa_to_port(ds, port), tx_vid, - true); -} -EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_tx_fwd_offload); - -void dsa_tag_8021q_bridge_tx_fwd_unoffload(struct dsa_switch *ds, int port, - struct dsa_bridge bridge) -{ - u16 tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge.num); - - dsa_port_tag_8021q_vlan_del(dsa_to_port(ds, port), tx_vid, true); -} -EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_tx_fwd_unoffload); - -/* Set up a port's tag_8021q RX and TX VLAN for standalone mode operation */ +/* Set up a port's standalone tag_8021q VLAN */ static int dsa_tag_8021q_port_setup(struct dsa_switch *ds, int port) { struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; struct dsa_port *dp = dsa_to_port(ds, port); - u16 rx_vid = dsa_tag_8021q_rx_vid(dp); - u16 tx_vid = dsa_tag_8021q_tx_vid(dp); + u16 vid = dsa_tag_8021q_standalone_vid(dp); struct net_device *master; int err; @@ -446,30 +342,16 @@ static int dsa_tag_8021q_port_setup(struct dsa_switch *ds, int port) master = dp->cpu_dp->master; - /* Add this user port's RX VID to the membership list of all others - * (including itself). This is so that bridging will not be hindered. - * L2 forwarding rules still take precedence when there are no VLAN - * restrictions, so there are no concerns about leaking traffic. - */ - err = dsa_port_tag_8021q_vlan_add(dp, rx_vid, false); + err = dsa_port_tag_8021q_vlan_add(dp, vid, false); if (err) { dev_err(ds->dev, - "Failed to apply RX VID %d to port %d: %pe\n", - rx_vid, port, ERR_PTR(err)); + "Failed to apply standalone VID %d to port %d: %pe\n", + vid, port, ERR_PTR(err)); return err; } - /* Add @rx_vid to the master's RX filter. */ - vlan_vid_add(master, ctx->proto, rx_vid); - - /* Finally apply the TX VID on this port and on the CPU port */ - err = dsa_port_tag_8021q_vlan_add(dp, tx_vid, false); - if (err) { - dev_err(ds->dev, - "Failed to apply TX VID %d on port %d: %pe\n", - tx_vid, port, ERR_PTR(err)); - return err; - } + /* Add the VLAN to the master's RX filter. */ + vlan_vid_add(master, ctx->proto, vid); return err; } @@ -478,8 +360,7 @@ static void dsa_tag_8021q_port_teardown(struct dsa_switch *ds, int port) { struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; struct dsa_port *dp = dsa_to_port(ds, port); - u16 rx_vid = dsa_tag_8021q_rx_vid(dp); - u16 tx_vid = dsa_tag_8021q_tx_vid(dp); + u16 vid = dsa_tag_8021q_standalone_vid(dp); struct net_device *master; /* The CPU port is implicitly configured by @@ -490,11 +371,9 @@ static void dsa_tag_8021q_port_teardown(struct dsa_switch *ds, int port) master = dp->cpu_dp->master; - dsa_port_tag_8021q_vlan_del(dp, rx_vid, false); + dsa_port_tag_8021q_vlan_del(dp, vid, false); - vlan_vid_del(master, ctx->proto, rx_vid); - - dsa_port_tag_8021q_vlan_del(dp, tx_vid, false); + vlan_vid_del(master, ctx->proto, vid); } static int dsa_tag_8021q_setup(struct dsa_switch *ds) @@ -573,23 +452,57 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, } EXPORT_SYMBOL_GPL(dsa_8021q_xmit); -void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id) +struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *master, + int vbid) +{ + struct dsa_port *cpu_dp = master->dsa_ptr; + struct dsa_switch_tree *dst = cpu_dp->dst; + struct dsa_port *dp; + + if (WARN_ON(!vbid)) + return NULL; + + dsa_tree_for_each_user_port(dp, dst) { + if (!dp->bridge) + continue; + + if (dp->stp_state != BR_STATE_LEARNING && + dp->stp_state != BR_STATE_FORWARDING) + continue; + + if (dp->cpu_dp != cpu_dp) + continue; + + if (dsa_port_bridge_num_get(dp) == vbid) + return dp->slave; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(dsa_tag_8021q_find_port_by_vbid); + +void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id, + int *vbid) { u16 vid, tci; - skb_push_rcsum(skb, ETH_HLEN); if (skb_vlan_tag_present(skb)) { tci = skb_vlan_tag_get(skb); __vlan_hwaccel_clear_tag(skb); } else { + skb_push_rcsum(skb, ETH_HLEN); __skb_vlan_pop(skb, &tci); + skb_pull_rcsum(skb, ETH_HLEN); } - skb_pull_rcsum(skb, ETH_HLEN); vid = tci & VLAN_VID_MASK; *source_port = dsa_8021q_rx_source_port(vid); *switch_id = dsa_8021q_rx_switch_id(vid); + + if (vbid) + *vbid = dsa_tag_8021q_rx_vbid(vid); + skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; } EXPORT_SYMBOL_GPL(dsa_8021q_rcv); diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index 8abf39dcac64..e4b6e3f2a3db 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -127,6 +127,7 @@ static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *dev, u8 extra) { struct dsa_port *dp = dsa_slave_to_port(dev); + struct net_device *br_dev; u8 tag_dev, tag_port; enum dsa_cmd cmd; u8 *dsa_header; @@ -149,7 +150,16 @@ static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *dev, tag_port = dp->index; } - if (skb->protocol == htons(ETH_P_8021Q)) { + br_dev = dsa_port_bridge_dev_get(dp); + + /* If frame is already 802.1Q tagged, we can convert it to a DSA + * tag (avoiding a memmove), but only if the port is standalone + * (in which case we always send FROM_CPU) or if the port's + * bridge has VLAN filtering enabled (in which case the CPU port + * will be a member of the VLAN). + */ + if (skb->protocol == htons(ETH_P_8021Q) && + (!br_dev || br_vlan_enabled(br_dev))) { if (extra) { skb_push(skb, extra); dsa_alloc_etype_header(skb, extra); @@ -166,10 +176,9 @@ static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *dev, dsa_header[2] &= ~0x10; } } else { - struct net_device *br = dsa_port_bridge_dev_get(dp); u16 vid; - vid = br ? MV88E6XXX_VID_BRIDGED : MV88E6XXX_VID_STANDALONE; + vid = br_dev ? MV88E6XXX_VID_BRIDGED : MV88E6XXX_VID_STANDALONE; skb_push(skb, DSA_HLEN + extra); dsa_alloc_etype_header(skb, DSA_HLEN + extra); @@ -246,12 +255,14 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, if (trunk) { struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_lag *lag; /* The exact source port is not available in the tag, * so we inject the frame directly on the upper * team/bond. */ - skb->dev = dsa_lag_dev(cpu_dp->dst, source_port); + lag = dsa_lag_by_id(cpu_dp->dst, source_port + 1); + skb->dev = lag ? lag->dev : NULL; } else { skb->dev = dsa_master_find_slave(dev, source_device, source_port); diff --git a/net/dsa/tag_ocelot_8021q.c b/net/dsa/tag_ocelot_8021q.c index 68982b2789a5..37ccf00404ea 100644 --- a/net/dsa/tag_ocelot_8021q.c +++ b/net/dsa/tag_ocelot_8021q.c @@ -32,6 +32,13 @@ static struct sk_buff *ocelot_defer_xmit(struct dsa_port *dp, if (!xmit_work_fn || !xmit_worker) return NULL; + /* PTP over IP packets need UDP checksumming. We may have inherited + * NETIF_F_HW_CSUM from the DSA master, but these packets are not sent + * through the DSA master, so calculate the checksum here. + */ + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + return NULL; + xmit_work = kzalloc(sizeof(*xmit_work), GFP_ATOMIC); if (!xmit_work) return NULL; @@ -55,7 +62,7 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb, struct dsa_port *dp = dsa_slave_to_port(netdev); u16 queue_mapping = skb_get_queue_mapping(skb); u8 pcp = netdev_txq_to_tc(netdev, queue_mapping); - u16 tx_vid = dsa_tag_8021q_tx_vid(dp); + u16 tx_vid = dsa_tag_8021q_standalone_vid(dp); struct ethhdr *hdr = eth_hdr(skb); if (ocelot_ptp_rew_op(skb) || is_link_local_ether_addr(hdr->h_dest)) @@ -70,7 +77,7 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, { int src_port, switch_id; - dsa_8021q_rcv(skb, &src_port, &switch_id); + dsa_8021q_rcv(skb, &src_port, &switch_id, NULL); skb->dev = dsa_master_find_slave(netdev, switch_id, src_port); if (!skb->dev) diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c index 1ea9401b8ace..57d2e00f1e5d 100644 --- a/net/dsa/tag_qca.c +++ b/net/dsa/tag_qca.c @@ -4,30 +4,12 @@ */ #include +#include +#include +#include #include "dsa_priv.h" -#define QCA_HDR_LEN 2 -#define QCA_HDR_VERSION 0x2 - -#define QCA_HDR_RECV_VERSION_MASK GENMASK(15, 14) -#define QCA_HDR_RECV_VERSION_S 14 -#define QCA_HDR_RECV_PRIORITY_MASK GENMASK(13, 11) -#define QCA_HDR_RECV_PRIORITY_S 11 -#define QCA_HDR_RECV_TYPE_MASK GENMASK(10, 6) -#define QCA_HDR_RECV_TYPE_S 6 -#define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3) -#define QCA_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0) - -#define QCA_HDR_XMIT_VERSION_MASK GENMASK(15, 14) -#define QCA_HDR_XMIT_VERSION_S 14 -#define QCA_HDR_XMIT_PRIORITY_MASK GENMASK(13, 11) -#define QCA_HDR_XMIT_PRIORITY_S 11 -#define QCA_HDR_XMIT_CONTROL_MASK GENMASK(10, 8) -#define QCA_HDR_XMIT_CONTROL_S 8 -#define QCA_HDR_XMIT_FROM_CPU BIT(7) -#define QCA_HDR_XMIT_DP_BIT_MASK GENMASK(6, 0) - static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_port *dp = dsa_slave_to_port(dev); @@ -40,8 +22,9 @@ static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) phdr = dsa_etype_header_pos_tx(skb); /* Set the version field, and set destination port information */ - hdr = QCA_HDR_VERSION << QCA_HDR_XMIT_VERSION_S | - QCA_HDR_XMIT_FROM_CPU | BIT(dp->index); + hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION); + hdr |= QCA_HDR_XMIT_FROM_CPU; + hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(dp->index)); *phdr = htons(hdr); @@ -50,10 +33,17 @@ static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) { - u8 ver; - u16 hdr; - int port; + struct qca_tagger_data *tagger_data; + struct dsa_port *dp = dev->dsa_ptr; + struct dsa_switch *ds = dp->ds; + u8 ver, pk_type; __be16 *phdr; + int port; + u16 hdr; + + BUILD_BUG_ON(sizeof(struct qca_mgmt_ethhdr) != QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN); + + tagger_data = ds->tagger_data; if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) return NULL; @@ -62,16 +52,33 @@ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) hdr = ntohs(*phdr); /* Make sure the version is correct */ - ver = (hdr & QCA_HDR_RECV_VERSION_MASK) >> QCA_HDR_RECV_VERSION_S; + ver = FIELD_GET(QCA_HDR_RECV_VERSION, hdr); if (unlikely(ver != QCA_HDR_VERSION)) return NULL; + /* Get pk type */ + pk_type = FIELD_GET(QCA_HDR_RECV_TYPE, hdr); + + /* Ethernet mgmt read/write packet */ + if (pk_type == QCA_HDR_RECV_TYPE_RW_REG_ACK) { + if (likely(tagger_data->rw_reg_ack_handler)) + tagger_data->rw_reg_ack_handler(ds, skb); + return NULL; + } + + /* Ethernet MIB counter packet */ + if (pk_type == QCA_HDR_RECV_TYPE_MIB) { + if (likely(tagger_data->mib_autocast_handler)) + tagger_data->mib_autocast_handler(ds, skb); + return NULL; + } + /* Remove QCA tag and recalculate checksum */ skb_pull_rcsum(skb, QCA_HDR_LEN); dsa_strip_etype_header(skb, QCA_HDR_LEN); /* Get source port information */ - port = (hdr & QCA_HDR_RECV_SOURCE_PORT_MASK); + port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, hdr); skb->dev = dsa_master_find_slave(dev, 0, port); if (!skb->dev) @@ -80,12 +87,34 @@ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) return skb; } +static int qca_tag_connect(struct dsa_switch *ds) +{ + struct qca_tagger_data *tagger_data; + + tagger_data = kzalloc(sizeof(*tagger_data), GFP_KERNEL); + if (!tagger_data) + return -ENOMEM; + + ds->tagger_data = tagger_data; + + return 0; +} + +static void qca_tag_disconnect(struct dsa_switch *ds) +{ + kfree(ds->tagger_data); + ds->tagger_data = NULL; +} + static const struct dsa_device_ops qca_netdev_ops = { .name = "qca", .proto = DSA_TAG_PROTO_QCA, + .connect = qca_tag_connect, + .disconnect = qca_tag_disconnect, .xmit = qca_tag_xmit, .rcv = qca_tag_rcv, .needed_headroom = QCA_HDR_LEN, + .promisc_on_master = true, }; MODULE_LICENSE("GPL"); diff --git a/net/dsa/tag_rtl8_4.c b/net/dsa/tag_rtl8_4.c index 02686ad4045d..a593ead7ff26 100644 --- a/net/dsa/tag_rtl8_4.c +++ b/net/dsa/tag_rtl8_4.c @@ -7,13 +7,8 @@ * NOTE: Currently only supports protocol "4" found in the RTL8365MB, hence * named tag_rtl8_4. * - * This tag header has the following format: + * This tag has the following format: * - * ------------------------------------------- - * | MAC DA | MAC SA | 8 byte tag | Type | ... - * ------------------------------------------- - * _______________/ \______________________________________ - * / \ * 0 7|8 15 * |-----------------------------------+-----------------------------------|--- * | (16-bit) | ^ @@ -58,6 +53,24 @@ * TX/RX | TX (switch->CPU): port number the packet was received on * | RX (CPU->switch): forwarding port mask (if ALLOW=0) * | allowance port mask (if ALLOW=1) + * + * The tag can be positioned before Ethertype, using tag "rtl8_4": + * + * +--------+--------+------------+------+----- + * | MAC DA | MAC SA | 8 byte tag | Type | ... + * +--------+--------+------------+------+----- + * + * The tag can also appear between the end of the payload and before the CRC, + * using tag "rtl8_4t": + * + * +--------+--------+------+-----+---------+------------+-----+ + * | MAC DA | MAC SA | TYPE | ... | payload | 8-byte tag | CRC | + * +--------+--------+------+-----+---------+------------+-----+ + * + * The added bytes after the payload will break most checksums, either in + * software or hardware. To avoid this issue, if the checksum is still pending, + * this tagger checksums the packet in software before adding the tag. + * */ #include @@ -84,87 +97,133 @@ #define RTL8_4_TX GENMASK(3, 0) #define RTL8_4_RX GENMASK(10, 0) +static void rtl8_4_write_tag(struct sk_buff *skb, struct net_device *dev, + void *tag) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + __be16 tag16[RTL8_4_TAG_LEN / 2]; + + /* Set Realtek EtherType */ + tag16[0] = htons(ETH_P_REALTEK); + + /* Set Protocol; zero REASON */ + tag16[1] = htons(FIELD_PREP(RTL8_4_PROTOCOL, RTL8_4_PROTOCOL_RTL8365MB)); + + /* Zero FID_EN, FID, PRI_EN, PRI, KEEP; set LEARN_DIS */ + tag16[2] = htons(FIELD_PREP(RTL8_4_LEARN_DIS, 1)); + + /* Zero ALLOW; set RX (CPU->switch) forwarding port mask */ + tag16[3] = htons(FIELD_PREP(RTL8_4_RX, BIT(dp->index))); + + memcpy(tag, tag16, RTL8_4_TAG_LEN); +} + static struct sk_buff *rtl8_4_tag_xmit(struct sk_buff *skb, struct net_device *dev) { - struct dsa_port *dp = dsa_slave_to_port(dev); - __be16 *tag; - skb_push(skb, RTL8_4_TAG_LEN); dsa_alloc_etype_header(skb, RTL8_4_TAG_LEN); - tag = dsa_etype_header_pos_tx(skb); - /* Set Realtek EtherType */ - tag[0] = htons(ETH_P_REALTEK); - - /* Set Protocol; zero REASON */ - tag[1] = htons(FIELD_PREP(RTL8_4_PROTOCOL, RTL8_4_PROTOCOL_RTL8365MB)); - - /* Zero FID_EN, FID, PRI_EN, PRI, KEEP; set LEARN_DIS */ - tag[2] = htons(FIELD_PREP(RTL8_4_LEARN_DIS, 1)); - - /* Zero ALLOW; set RX (CPU->switch) forwarding port mask */ - tag[3] = htons(FIELD_PREP(RTL8_4_RX, BIT(dp->index))); + rtl8_4_write_tag(skb, dev, dsa_etype_header_pos_tx(skb)); return skb; } -static struct sk_buff *rtl8_4_tag_rcv(struct sk_buff *skb, - struct net_device *dev) +static struct sk_buff *rtl8_4t_tag_xmit(struct sk_buff *skb, + struct net_device *dev) { - __be16 *tag; + /* Calculate the checksum here if not done yet as trailing tags will + * break either software or hardware based checksum + */ + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + return NULL; + + rtl8_4_write_tag(skb, dev, skb_put(skb, RTL8_4_TAG_LEN)); + + return skb; +} + +static int rtl8_4_read_tag(struct sk_buff *skb, struct net_device *dev, + void *tag) +{ + __be16 tag16[RTL8_4_TAG_LEN / 2]; u16 etype; u8 reason; u8 proto; u8 port; - if (unlikely(!pskb_may_pull(skb, RTL8_4_TAG_LEN))) - return NULL; - - tag = dsa_etype_header_pos_rx(skb); + memcpy(tag16, tag, RTL8_4_TAG_LEN); /* Parse Realtek EtherType */ - etype = ntohs(tag[0]); + etype = ntohs(tag16[0]); if (unlikely(etype != ETH_P_REALTEK)) { dev_warn_ratelimited(&dev->dev, "non-realtek ethertype 0x%04x\n", etype); - return NULL; + return -EPROTO; } /* Parse Protocol */ - proto = FIELD_GET(RTL8_4_PROTOCOL, ntohs(tag[1])); + proto = FIELD_GET(RTL8_4_PROTOCOL, ntohs(tag16[1])); if (unlikely(proto != RTL8_4_PROTOCOL_RTL8365MB)) { dev_warn_ratelimited(&dev->dev, "unknown realtek protocol 0x%02x\n", proto); - return NULL; + return -EPROTO; } /* Parse REASON */ - reason = FIELD_GET(RTL8_4_REASON, ntohs(tag[1])); + reason = FIELD_GET(RTL8_4_REASON, ntohs(tag16[1])); /* Parse TX (switch->CPU) */ - port = FIELD_GET(RTL8_4_TX, ntohs(tag[3])); + port = FIELD_GET(RTL8_4_TX, ntohs(tag16[3])); skb->dev = dsa_master_find_slave(dev, 0, port); if (!skb->dev) { dev_warn_ratelimited(&dev->dev, "could not find slave for port %d\n", port); - return NULL; + return -ENOENT; } + if (reason != RTL8_4_REASON_TRAP) + dsa_default_offload_fwd_mark(skb); + + return 0; +} + +static struct sk_buff *rtl8_4_tag_rcv(struct sk_buff *skb, + struct net_device *dev) +{ + if (unlikely(!pskb_may_pull(skb, RTL8_4_TAG_LEN))) + return NULL; + + if (unlikely(rtl8_4_read_tag(skb, dev, dsa_etype_header_pos_rx(skb)))) + return NULL; + /* Remove tag and recalculate checksum */ skb_pull_rcsum(skb, RTL8_4_TAG_LEN); dsa_strip_etype_header(skb, RTL8_4_TAG_LEN); - if (reason != RTL8_4_REASON_TRAP) - dsa_default_offload_fwd_mark(skb); + return skb; +} + +static struct sk_buff *rtl8_4t_tag_rcv(struct sk_buff *skb, + struct net_device *dev) +{ + if (skb_linearize(skb)) + return NULL; + + if (unlikely(rtl8_4_read_tag(skb, dev, skb_tail_pointer(skb) - RTL8_4_TAG_LEN))) + return NULL; + + if (pskb_trim_rcsum(skb, skb->len - RTL8_4_TAG_LEN)) + return NULL; return skb; } +/* Ethertype version */ static const struct dsa_device_ops rtl8_4_netdev_ops = { .name = "rtl8_4", .proto = DSA_TAG_PROTO_RTL8_4, @@ -172,7 +231,28 @@ static const struct dsa_device_ops rtl8_4_netdev_ops = { .rcv = rtl8_4_tag_rcv, .needed_headroom = RTL8_4_TAG_LEN, }; -module_dsa_tag_driver(rtl8_4_netdev_ops); + +DSA_TAG_DRIVER(rtl8_4_netdev_ops); + +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4); + +/* Tail version */ +static const struct dsa_device_ops rtl8_4t_netdev_ops = { + .name = "rtl8_4t", + .proto = DSA_TAG_PROTO_RTL8_4T, + .xmit = rtl8_4t_tag_xmit, + .rcv = rtl8_4t_tag_rcv, + .needed_tailroom = RTL8_4_TAG_LEN, +}; + +DSA_TAG_DRIVER(rtl8_4t_netdev_ops); + +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4T); + +static struct dsa_tag_driver *dsa_tag_drivers[] = { + &DSA_TAG_DRIVER_NAME(rtl8_4_netdev_ops), + &DSA_TAG_DRIVER_NAME(rtl8_4t_netdev_ops), +}; +module_dsa_tag_drivers(dsa_tag_drivers); MODULE_LICENSE("GPL"); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4); diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index 72d5e0ef8dcf..83e4136516b0 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -226,7 +226,7 @@ static struct sk_buff *sja1105_imprecise_xmit(struct sk_buff *skb, * TX VLAN that targets the bridge's entire broadcast domain, * instead of just the specific port. */ - tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge_num); + tx_vid = dsa_tag_8021q_bridge_vid(bridge_num); return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp), tx_vid); } @@ -267,7 +267,7 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb, struct dsa_port *dp = dsa_slave_to_port(netdev); u16 queue_mapping = skb_get_queue_mapping(skb); u8 pcp = netdev_txq_to_tc(netdev, queue_mapping); - u16 tx_vid = dsa_tag_8021q_tx_vid(dp); + u16 tx_vid = dsa_tag_8021q_standalone_vid(dp); if (skb->offload_fwd_mark) return sja1105_imprecise_xmit(skb, netdev); @@ -295,7 +295,7 @@ static struct sk_buff *sja1110_xmit(struct sk_buff *skb, struct dsa_port *dp = dsa_slave_to_port(netdev); u16 queue_mapping = skb_get_queue_mapping(skb); u8 pcp = netdev_txq_to_tc(netdev, queue_mapping); - u16 tx_vid = dsa_tag_8021q_tx_vid(dp); + u16 tx_vid = dsa_tag_8021q_standalone_vid(dp); __be32 *tx_trailer; __be16 *tx_header; int trailer_pos; @@ -509,7 +509,7 @@ static bool sja1110_skb_has_inband_control_extension(const struct sk_buff *skb) * packet. */ static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port, - int *switch_id, u16 *vid) + int *switch_id, int *vbid, u16 *vid) { struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)skb_mac_header(skb); u16 vlan_tci; @@ -519,8 +519,8 @@ static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port, else vlan_tci = ntohs(hdr->h_vlan_TCI); - if (vid_is_dsa_8021q_rxvlan(vlan_tci & VLAN_VID_MASK)) - return dsa_8021q_rcv(skb, source_port, switch_id); + if (vid_is_dsa_8021q(vlan_tci & VLAN_VID_MASK)) + return dsa_8021q_rcv(skb, source_port, switch_id, vbid); /* Try our best with imprecise RX */ *vid = vlan_tci & VLAN_VID_MASK; @@ -529,7 +529,7 @@ static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port, static struct sk_buff *sja1105_rcv(struct sk_buff *skb, struct net_device *netdev) { - int source_port = -1, switch_id = -1; + int source_port = -1, switch_id = -1, vbid = -1; struct sja1105_meta meta = {0}; struct ethhdr *hdr; bool is_link_local; @@ -542,7 +542,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, if (sja1105_skb_has_tag_8021q(skb)) { /* Normal traffic path. */ - sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid); + sja1105_vlan_rcv(skb, &source_port, &switch_id, &vbid, &vid); } else if (is_link_local) { /* Management traffic path. Switch embeds the switch ID and * port ID into bytes of the destination MAC, courtesy of @@ -561,7 +561,9 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, return NULL; } - if (source_port == -1 || switch_id == -1) + if (vbid >= 1) + skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid); + else if (source_port == -1 || switch_id == -1) skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid); else skb->dev = dsa_master_find_slave(netdev, switch_id, source_port); @@ -686,7 +688,7 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb, static struct sk_buff *sja1110_rcv(struct sk_buff *skb, struct net_device *netdev) { - int source_port = -1, switch_id = -1; + int source_port = -1, switch_id = -1, vbid = -1; bool host_only = false; u16 vid = 0; @@ -700,9 +702,11 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb, /* Packets with in-band control extensions might still have RX VLANs */ if (likely(sja1105_skb_has_tag_8021q(skb))) - sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid); + sja1105_vlan_rcv(skb, &source_port, &switch_id, &vbid, &vid); - if (source_port == -1 || switch_id == -1) + if (vbid >= 1) + skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid); + else if (source_port == -1 || switch_id == -1) skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid); else skb->dev = dsa_master_find_slave(netdev, switch_id, source_port); diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 75856db299e9..29d01662a48b 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -363,7 +363,7 @@ extern const struct nla_policy ethnl_features_set_policy[ETHTOOL_A_FEATURES_WANT extern const struct nla_policy ethnl_privflags_get_policy[ETHTOOL_A_PRIVFLAGS_HEADER + 1]; extern const struct nla_policy ethnl_privflags_set_policy[ETHTOOL_A_PRIVFLAGS_FLAGS + 1]; extern const struct nla_policy ethnl_rings_get_policy[ETHTOOL_A_RINGS_HEADER + 1]; -extern const struct nla_policy ethnl_rings_set_policy[ETHTOOL_A_RINGS_RX_BUF_LEN + 1]; +extern const struct nla_policy ethnl_rings_set_policy[ETHTOOL_A_RINGS_CQE_SIZE + 1]; extern const struct nla_policy ethnl_channels_get_policy[ETHTOOL_A_CHANNELS_HEADER + 1]; extern const struct nla_policy ethnl_channels_set_policy[ETHTOOL_A_CHANNELS_COMBINED_COUNT + 1]; extern const struct nla_policy ethnl_coalesce_get_policy[ETHTOOL_A_COALESCE_HEADER + 1]; diff --git a/net/ethtool/rings.c b/net/ethtool/rings.c index c1d5f5e0fdc9..9f33c9689b56 100644 --- a/net/ethtool/rings.c +++ b/net/ethtool/rings.c @@ -53,7 +53,9 @@ static int rings_reply_size(const struct ethnl_req_info *req_base, nla_total_size(sizeof(u32)) + /* _RINGS_RX_MINI */ nla_total_size(sizeof(u32)) + /* _RINGS_RX_JUMBO */ nla_total_size(sizeof(u32)) + /* _RINGS_TX */ - nla_total_size(sizeof(u32)); /* _RINGS_RX_BUF_LEN */ + nla_total_size(sizeof(u32)) + /* _RINGS_RX_BUF_LEN */ + nla_total_size(sizeof(u8)) + /* _RINGS_TCP_DATA_SPLIT */ + nla_total_size(sizeof(u32)); /* _RINGS_CQE_SIZE */ } static int rings_fill_reply(struct sk_buff *skb, @@ -61,9 +63,11 @@ static int rings_fill_reply(struct sk_buff *skb, const struct ethnl_reply_data *reply_base) { const struct rings_reply_data *data = RINGS_REPDATA(reply_base); - const struct kernel_ethtool_ringparam *kernel_ringparam = &data->kernel_ringparam; + const struct kernel_ethtool_ringparam *kr = &data->kernel_ringparam; const struct ethtool_ringparam *ringparam = &data->ringparam; + WARN_ON(kr->tcp_data_split > ETHTOOL_TCP_DATA_SPLIT_ENABLED); + if ((ringparam->rx_max_pending && (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MAX, ringparam->rx_max_pending) || @@ -84,9 +88,13 @@ static int rings_fill_reply(struct sk_buff *skb, ringparam->tx_max_pending) || nla_put_u32(skb, ETHTOOL_A_RINGS_TX, ringparam->tx_pending))) || - (kernel_ringparam->rx_buf_len && - (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_BUF_LEN, - kernel_ringparam->rx_buf_len)))) + (kr->rx_buf_len && + (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_BUF_LEN, kr->rx_buf_len))) || + (kr->tcp_data_split && + (nla_put_u8(skb, ETHTOOL_A_RINGS_TCP_DATA_SPLIT, + kr->tcp_data_split))) || + (kr->cqe_size && + (nla_put_u32(skb, ETHTOOL_A_RINGS_CQE_SIZE, kr->cqe_size)))) return -EMSGSIZE; return 0; @@ -114,6 +122,7 @@ const struct nla_policy ethnl_rings_set_policy[] = { [ETHTOOL_A_RINGS_RX_JUMBO] = { .type = NLA_U32 }, [ETHTOOL_A_RINGS_TX] = { .type = NLA_U32 }, [ETHTOOL_A_RINGS_RX_BUF_LEN] = NLA_POLICY_MIN(NLA_U32, 1), + [ETHTOOL_A_RINGS_CQE_SIZE] = NLA_POLICY_MIN(NLA_U32, 1), }; int ethnl_set_rings(struct sk_buff *skb, struct genl_info *info) @@ -154,6 +163,8 @@ int ethnl_set_rings(struct sk_buff *skb, struct genl_info *info) ethnl_update_u32(&ringparam.tx_pending, tb[ETHTOOL_A_RINGS_TX], &mod); ethnl_update_u32(&kernel_ringparam.rx_buf_len, tb[ETHTOOL_A_RINGS_RX_BUF_LEN], &mod); + ethnl_update_u32(&kernel_ringparam.cqe_size, + tb[ETHTOOL_A_RINGS_CQE_SIZE], &mod); ret = 0; if (!mod) goto out_ops; @@ -185,6 +196,15 @@ int ethnl_set_rings(struct sk_buff *skb, struct genl_info *info) goto out_ops; } + if (kernel_ringparam.cqe_size && + !(ops->supported_ring_params & ETHTOOL_RING_USE_CQE_SIZE)) { + ret = -EOPNOTSUPP; + NL_SET_ERR_MSG_ATTR(info->extack, + tb[ETHTOOL_A_RINGS_CQE_SIZE], + "setting cqe size not supported"); + goto out_ops; + } + ret = dev->ethtool_ops->set_ringparam(dev, &ringparam, &kernel_ringparam, info->extack); if (ret < 0) diff --git a/net/hsr/hsr_debugfs.c b/net/hsr/hsr_debugfs.c index 99f3af1a9d4d..fe6094e9a2db 100644 --- a/net/hsr/hsr_debugfs.c +++ b/net/hsr/hsr_debugfs.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "hsr_main.h" #include "hsr_framereg.h" @@ -28,6 +29,7 @@ hsr_node_table_show(struct seq_file *sfp, void *data) { struct hsr_priv *priv = (struct hsr_priv *)sfp->private; struct hsr_node *node; + int i; seq_printf(sfp, "Node Table entries for (%s) device\n", (priv->prot_version == PRP_V1 ? "PRP" : "HSR")); @@ -39,22 +41,28 @@ hsr_node_table_show(struct seq_file *sfp, void *data) seq_puts(sfp, "DAN-H\n"); rcu_read_lock(); - list_for_each_entry_rcu(node, &priv->node_db, mac_list) { - /* skip self node */ - if (hsr_addr_is_self(priv, node->macaddress_A)) - continue; - seq_printf(sfp, "%pM ", &node->macaddress_A[0]); - seq_printf(sfp, "%pM ", &node->macaddress_B[0]); - seq_printf(sfp, "%10lx, ", node->time_in[HSR_PT_SLAVE_A]); - seq_printf(sfp, "%10lx, ", node->time_in[HSR_PT_SLAVE_B]); - seq_printf(sfp, "%14x, ", node->addr_B_port); - if (priv->prot_version == PRP_V1) - seq_printf(sfp, "%5x, %5x, %5x\n", - node->san_a, node->san_b, - (node->san_a == 0 && node->san_b == 0)); - else - seq_printf(sfp, "%5x\n", 1); + for (i = 0 ; i < priv->hash_buckets; i++) { + hlist_for_each_entry_rcu(node, &priv->node_db[i], mac_list) { + /* skip self node */ + if (hsr_addr_is_self(priv, node->macaddress_A)) + continue; + seq_printf(sfp, "%pM ", &node->macaddress_A[0]); + seq_printf(sfp, "%pM ", &node->macaddress_B[0]); + seq_printf(sfp, "%10lx, ", + node->time_in[HSR_PT_SLAVE_A]); + seq_printf(sfp, "%10lx, ", + node->time_in[HSR_PT_SLAVE_B]); + seq_printf(sfp, "%14x, ", node->addr_B_port); + + if (priv->prot_version == PRP_V1) + seq_printf(sfp, "%5x, %5x, %5x\n", + node->san_a, node->san_b, + (node->san_a == 0 && + node->san_b == 0)); + else + seq_printf(sfp, "%5x\n", 1); + } } rcu_read_unlock(); return 0; diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index e57fdad9ef94..6ffef47e9be5 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -221,7 +221,7 @@ static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev) skb_reset_mac_len(skb); hsr_forward_skb(skb, master); } else { - atomic_long_inc(&dev->tx_dropped); + dev_core_stats_tx_dropped_inc(dev); dev_kfree_skb_any(skb); } return NETDEV_TX_OK; @@ -485,12 +485,16 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], { bool unregister = false; struct hsr_priv *hsr; - int res; + int res, i; hsr = netdev_priv(hsr_dev); INIT_LIST_HEAD(&hsr->ports); - INIT_LIST_HEAD(&hsr->node_db); - INIT_LIST_HEAD(&hsr->self_node_db); + INIT_HLIST_HEAD(&hsr->self_node_db); + hsr->hash_buckets = HSR_HSIZE; + get_random_bytes(&hsr->hash_seed, sizeof(hsr->hash_seed)); + for (i = 0; i < hsr->hash_buckets; i++) + INIT_HLIST_HEAD(&hsr->node_db[i]); + spin_lock_init(&hsr->list_lock); eth_hw_addr_set(hsr_dev, slave[0]->dev_addr); diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index e59cbb4f0cd1..5bf357734b11 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -570,20 +570,23 @@ static int fill_frame_info(struct hsr_frame_info *frame, struct ethhdr *ethhdr; __be16 proto; int ret; + u32 hash; /* Check if skb contains ethhdr */ if (skb->mac_len < sizeof(struct ethhdr)) return -EINVAL; memset(frame, 0, sizeof(*frame)); + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + hash = hsr_mac_hash(port->hsr, ethhdr->h_source); frame->is_supervision = is_supervision_frame(port->hsr, skb); - frame->node_src = hsr_get_node(port, &hsr->node_db, skb, + frame->node_src = hsr_get_node(port, &hsr->node_db[hash], skb, frame->is_supervision, port->type); if (!frame->node_src) return -1; /* Unknown node and !is_supervision, or no mem */ - ethhdr = (struct ethhdr *)skb_mac_header(skb); frame->is_vlan = false; proto = ethhdr->h_proto; diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index 0775f0f95dbf..584e21788799 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -15,11 +15,36 @@ #include #include #include +#include #include "hsr_main.h" #include "hsr_framereg.h" #include "hsr_netlink.h" -/* TODO: use hash lists for mac addresses (linux/jhash.h)? */ +#ifdef CONFIG_LOCKDEP +int lockdep_hsr_is_held(spinlock_t *lock) +{ + return lockdep_is_held(lock); +} +#endif + +u32 hsr_mac_hash(struct hsr_priv *hsr, const unsigned char *addr) +{ + u32 hash = jhash(addr, ETH_ALEN, hsr->hash_seed); + + return reciprocal_scale(hash, hsr->hash_buckets); +} + +struct hsr_node *hsr_node_get_first(struct hlist_head *head, spinlock_t *lock) +{ + struct hlist_node *first; + + first = rcu_dereference_bh_check(hlist_first_rcu(head), + lockdep_hsr_is_held(lock)); + if (first) + return hlist_entry(first, struct hsr_node, mac_list); + + return NULL; +} /* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b, * false otherwise. @@ -42,8 +67,7 @@ bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr) { struct hsr_node *node; - node = list_first_or_null_rcu(&hsr->self_node_db, struct hsr_node, - mac_list); + node = hsr_node_get_first(&hsr->self_node_db, &hsr->list_lock); if (!node) { WARN_ONCE(1, "HSR: No self node\n"); return false; @@ -59,12 +83,12 @@ bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr) /* Search for mac entry. Caller must hold rcu read lock. */ -static struct hsr_node *find_node_by_addr_A(struct list_head *node_db, +static struct hsr_node *find_node_by_addr_A(struct hlist_head *node_db, const unsigned char addr[ETH_ALEN]) { struct hsr_node *node; - list_for_each_entry_rcu(node, node_db, mac_list) { + hlist_for_each_entry_rcu(node, node_db, mac_list) { if (ether_addr_equal(node->macaddress_A, addr)) return node; } @@ -79,7 +103,7 @@ int hsr_create_self_node(struct hsr_priv *hsr, const unsigned char addr_a[ETH_ALEN], const unsigned char addr_b[ETH_ALEN]) { - struct list_head *self_node_db = &hsr->self_node_db; + struct hlist_head *self_node_db = &hsr->self_node_db; struct hsr_node *node, *oldnode; node = kmalloc(sizeof(*node), GFP_KERNEL); @@ -90,14 +114,13 @@ int hsr_create_self_node(struct hsr_priv *hsr, ether_addr_copy(node->macaddress_B, addr_b); spin_lock_bh(&hsr->list_lock); - oldnode = list_first_or_null_rcu(self_node_db, - struct hsr_node, mac_list); + oldnode = hsr_node_get_first(self_node_db, &hsr->list_lock); if (oldnode) { - list_replace_rcu(&oldnode->mac_list, &node->mac_list); + hlist_replace_rcu(&oldnode->mac_list, &node->mac_list); spin_unlock_bh(&hsr->list_lock); kfree_rcu(oldnode, rcu_head); } else { - list_add_tail_rcu(&node->mac_list, self_node_db); + hlist_add_tail_rcu(&node->mac_list, self_node_db); spin_unlock_bh(&hsr->list_lock); } @@ -106,25 +129,25 @@ int hsr_create_self_node(struct hsr_priv *hsr, void hsr_del_self_node(struct hsr_priv *hsr) { - struct list_head *self_node_db = &hsr->self_node_db; + struct hlist_head *self_node_db = &hsr->self_node_db; struct hsr_node *node; spin_lock_bh(&hsr->list_lock); - node = list_first_or_null_rcu(self_node_db, struct hsr_node, mac_list); + node = hsr_node_get_first(self_node_db, &hsr->list_lock); if (node) { - list_del_rcu(&node->mac_list); + hlist_del_rcu(&node->mac_list); kfree_rcu(node, rcu_head); } spin_unlock_bh(&hsr->list_lock); } -void hsr_del_nodes(struct list_head *node_db) +void hsr_del_nodes(struct hlist_head *node_db) { struct hsr_node *node; - struct hsr_node *tmp; + struct hlist_node *tmp; - list_for_each_entry_safe(node, tmp, node_db, mac_list) - kfree(node); + hlist_for_each_entry_safe(node, tmp, node_db, mac_list) + kfree_rcu(node, rcu_head); } void prp_handle_san_frame(bool san, enum hsr_port_type port, @@ -145,7 +168,7 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port, * originating from the newly added node. */ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr, - struct list_head *node_db, + struct hlist_head *node_db, unsigned char addr[], u16 seq_out, bool san, enum hsr_port_type rx_port) @@ -175,14 +198,14 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr, hsr->proto_ops->handle_san_frame(san, rx_port, new_node); spin_lock_bh(&hsr->list_lock); - list_for_each_entry_rcu(node, node_db, mac_list, - lockdep_is_held(&hsr->list_lock)) { + hlist_for_each_entry_rcu(node, node_db, mac_list, + lockdep_hsr_is_held(&hsr->list_lock)) { if (ether_addr_equal(node->macaddress_A, addr)) goto out; if (ether_addr_equal(node->macaddress_B, addr)) goto out; } - list_add_tail_rcu(&new_node->mac_list, node_db); + hlist_add_tail_rcu(&new_node->mac_list, node_db); spin_unlock_bh(&hsr->list_lock); return new_node; out: @@ -202,7 +225,7 @@ void prp_update_san_info(struct hsr_node *node, bool is_sup) /* Get the hsr_node from which 'skb' was sent. */ -struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db, +struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db, struct sk_buff *skb, bool is_sup, enum hsr_port_type rx_port) { @@ -218,7 +241,7 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db, ethhdr = (struct ethhdr *)skb_mac_header(skb); - list_for_each_entry_rcu(node, node_db, mac_list) { + hlist_for_each_entry_rcu(node, node_db, mac_list) { if (ether_addr_equal(node->macaddress_A, ethhdr->h_source)) { if (hsr->proto_ops->update_san_info) hsr->proto_ops->update_san_info(node, is_sup); @@ -268,11 +291,12 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) struct hsr_sup_tlv *hsr_sup_tlv; struct hsr_node *node_real; struct sk_buff *skb = NULL; - struct list_head *node_db; + struct hlist_head *node_db; struct ethhdr *ethhdr; int i; unsigned int pull_size = 0; unsigned int total_pull_size = 0; + u32 hash; /* Here either frame->skb_hsr or frame->skb_prp should be * valid as supervision frame always will have protocol @@ -310,11 +334,13 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) hsr_sp = (struct hsr_sup_payload *)skb->data; /* Merge node_curr (registered on macaddress_B) into node_real */ - node_db = &port_rcv->hsr->node_db; - node_real = find_node_by_addr_A(node_db, hsr_sp->macaddress_A); + node_db = port_rcv->hsr->node_db; + hash = hsr_mac_hash(hsr, hsr_sp->macaddress_A); + node_real = find_node_by_addr_A(&node_db[hash], hsr_sp->macaddress_A); if (!node_real) /* No frame received from AddrA of this node yet */ - node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A, + node_real = hsr_add_node(hsr, &node_db[hash], + hsr_sp->macaddress_A, HSR_SEQNR_START - 1, true, port_rcv->type); if (!node_real) @@ -348,7 +374,8 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) hsr_sp = (struct hsr_sup_payload *)skb->data; /* Check if redbox mac and node mac are equal. */ - if (!ether_addr_equal(node_real->macaddress_A, hsr_sp->macaddress_A)) { + if (!ether_addr_equal(node_real->macaddress_A, + hsr_sp->macaddress_A)) { /* This is a redbox supervision frame for a VDAN! */ goto done; } @@ -368,7 +395,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) node_real->addr_B_port = port_rcv->type; spin_lock_bh(&hsr->list_lock); - list_del_rcu(&node_curr->mac_list); + hlist_del_rcu(&node_curr->mac_list); spin_unlock_bh(&hsr->list_lock); kfree_rcu(node_curr, rcu_head); @@ -406,6 +433,7 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb, struct hsr_port *port) { struct hsr_node *node_dst; + u32 hash; if (!skb_mac_header_was_set(skb)) { WARN_ONCE(1, "%s: Mac header not set\n", __func__); @@ -415,7 +443,8 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb, if (!is_unicast_ether_addr(eth_hdr(skb)->h_dest)) return; - node_dst = find_node_by_addr_A(&port->hsr->node_db, + hash = hsr_mac_hash(port->hsr, eth_hdr(skb)->h_dest); + node_dst = find_node_by_addr_A(&port->hsr->node_db[hash], eth_hdr(skb)->h_dest); if (!node_dst) { if (net_ratelimit()) @@ -491,59 +520,73 @@ static struct hsr_port *get_late_port(struct hsr_priv *hsr, void hsr_prune_nodes(struct timer_list *t) { struct hsr_priv *hsr = from_timer(hsr, t, prune_timer); + struct hlist_node *tmp; struct hsr_node *node; - struct hsr_node *tmp; struct hsr_port *port; unsigned long timestamp; unsigned long time_a, time_b; + int i; spin_lock_bh(&hsr->list_lock); - list_for_each_entry_safe(node, tmp, &hsr->node_db, mac_list) { - /* Don't prune own node. Neither time_in[HSR_PT_SLAVE_A] - * nor time_in[HSR_PT_SLAVE_B], will ever be updated for - * the master port. Thus the master node will be repeatedly - * pruned leading to packet loss. - */ - if (hsr_addr_is_self(hsr, node->macaddress_A)) - continue; - /* Shorthand */ - time_a = node->time_in[HSR_PT_SLAVE_A]; - time_b = node->time_in[HSR_PT_SLAVE_B]; + for (i = 0; i < hsr->hash_buckets; i++) { + hlist_for_each_entry_safe(node, tmp, &hsr->node_db[i], + mac_list) { + /* Don't prune own node. + * Neither time_in[HSR_PT_SLAVE_A] + * nor time_in[HSR_PT_SLAVE_B], will ever be updated + * for the master port. Thus the master node will be + * repeatedly pruned leading to packet loss. + */ + if (hsr_addr_is_self(hsr, node->macaddress_A)) + continue; - /* Check for timestamps old enough to risk wrap-around */ - if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET / 2)) - node->time_in_stale[HSR_PT_SLAVE_A] = true; - if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET / 2)) - node->time_in_stale[HSR_PT_SLAVE_B] = true; + /* Shorthand */ + time_a = node->time_in[HSR_PT_SLAVE_A]; + time_b = node->time_in[HSR_PT_SLAVE_B]; - /* Get age of newest frame from node. - * At least one time_in is OK here; nodes get pruned long - * before both time_ins can get stale - */ - timestamp = time_a; - if (node->time_in_stale[HSR_PT_SLAVE_A] || - (!node->time_in_stale[HSR_PT_SLAVE_B] && - time_after(time_b, time_a))) - timestamp = time_b; + /* Check for timestamps old enough to + * risk wrap-around + */ + if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET / 2)) + node->time_in_stale[HSR_PT_SLAVE_A] = true; + if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET / 2)) + node->time_in_stale[HSR_PT_SLAVE_B] = true; - /* Warn of ring error only as long as we get frames at all */ - if (time_is_after_jiffies(timestamp + - msecs_to_jiffies(1.5 * MAX_SLAVE_DIFF))) { - rcu_read_lock(); - port = get_late_port(hsr, node); - if (port) - hsr_nl_ringerror(hsr, node->macaddress_A, port); - rcu_read_unlock(); - } + /* Get age of newest frame from node. + * At least one time_in is OK here; nodes get pruned + * long before both time_ins can get stale + */ + timestamp = time_a; + if (node->time_in_stale[HSR_PT_SLAVE_A] || + (!node->time_in_stale[HSR_PT_SLAVE_B] && + time_after(time_b, time_a))) + timestamp = time_b; - /* Prune old entries */ - if (time_is_before_jiffies(timestamp + - msecs_to_jiffies(HSR_NODE_FORGET_TIME))) { - hsr_nl_nodedown(hsr, node->macaddress_A); - list_del_rcu(&node->mac_list); - /* Note that we need to free this entry later: */ - kfree_rcu(node, rcu_head); + /* Warn of ring error only as long as we get + * frames at all + */ + if (time_is_after_jiffies(timestamp + + msecs_to_jiffies(1.5 * MAX_SLAVE_DIFF))) { + rcu_read_lock(); + port = get_late_port(hsr, node); + if (port) + hsr_nl_ringerror(hsr, + node->macaddress_A, + port); + rcu_read_unlock(); + } + + /* Prune old entries */ + if (time_is_before_jiffies(timestamp + + msecs_to_jiffies(HSR_NODE_FORGET_TIME))) { + hsr_nl_nodedown(hsr, node->macaddress_A); + hlist_del_rcu(&node->mac_list); + /* Note that we need to free this + * entry later: + */ + kfree_rcu(node, rcu_head); + } } } spin_unlock_bh(&hsr->list_lock); @@ -557,17 +600,20 @@ void *hsr_get_next_node(struct hsr_priv *hsr, void *_pos, unsigned char addr[ETH_ALEN]) { struct hsr_node *node; + u32 hash; + + hash = hsr_mac_hash(hsr, addr); if (!_pos) { - node = list_first_or_null_rcu(&hsr->node_db, - struct hsr_node, mac_list); + node = hsr_node_get_first(&hsr->node_db[hash], + &hsr->list_lock); if (node) ether_addr_copy(addr, node->macaddress_A); return node; } node = _pos; - list_for_each_entry_continue_rcu(node, &hsr->node_db, mac_list) { + hlist_for_each_entry_continue_rcu(node, mac_list) { ether_addr_copy(addr, node->macaddress_A); return node; } @@ -587,8 +633,11 @@ int hsr_get_node_data(struct hsr_priv *hsr, struct hsr_node *node; struct hsr_port *port; unsigned long tdiff; + u32 hash; - node = find_node_by_addr_A(&hsr->node_db, addr); + hash = hsr_mac_hash(hsr, addr); + + node = find_node_by_addr_A(&hsr->node_db[hash], addr); if (!node) return -ENOENT; diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h index bdbb8c822ba1..f3762e9e42b5 100644 --- a/net/hsr/hsr_framereg.h +++ b/net/hsr/hsr_framereg.h @@ -28,9 +28,17 @@ struct hsr_frame_info { bool is_from_san; }; +#ifdef CONFIG_LOCKDEP +int lockdep_hsr_is_held(spinlock_t *lock); +#else +#define lockdep_hsr_is_held(lock) 1 +#endif + +u32 hsr_mac_hash(struct hsr_priv *hsr, const unsigned char *addr); +struct hsr_node *hsr_node_get_first(struct hlist_head *head, spinlock_t *lock); void hsr_del_self_node(struct hsr_priv *hsr); -void hsr_del_nodes(struct list_head *node_db); -struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db, +void hsr_del_nodes(struct hlist_head *node_db); +struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db, struct sk_buff *skb, bool is_sup, enum hsr_port_type rx_port); void hsr_handle_sup_frame(struct hsr_frame_info *frame); @@ -68,7 +76,7 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port, void prp_update_san_info(struct hsr_node *node, bool is_sup); struct hsr_node { - struct list_head mac_list; + struct hlist_node mac_list; unsigned char macaddress_A[ETH_ALEN]; unsigned char macaddress_B[ETH_ALEN]; /* Local slave through which AddrB frames are received from this node */ diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index 043e4e9a1694..b158ba409f9a 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -45,24 +45,11 @@ /* PRP V1 life redundancy box MAC address */ #define PRP_TLV_REDBOX_MAC 30 -/* HSR Tag. - * As defined in IEC-62439-3:2010, the HSR tag is really { ethertype = 0x88FB, - * path, LSDU_size, sequence Nr }. But we let eth_header() create { h_dest, - * h_source, h_proto = 0x88FB }, and add { path, LSDU_size, sequence Nr, - * encapsulated protocol } instead. - * - * Field names as defined in the IEC:2010 standard for HSR. - */ -struct hsr_tag { - __be16 path_and_LSDU_size; - __be16 sequence_nr; - __be16 encap_proto; -} __packed; - -#define HSR_HLEN 6 - #define HSR_V1_SUP_LSDUSIZE 52 +#define HSR_HSIZE_SHIFT 8 +#define HSR_HSIZE BIT(HSR_HSIZE_SHIFT) + /* The helper functions below assumes that 'path' occupies the 4 most * significant bits of the 16-bit field shared by 'path' and 'LSDU_size' (or * equivalently, the 4 most significant bits of HSR tag byte 14). @@ -201,8 +188,8 @@ struct hsr_proto_ops { struct hsr_priv { struct rcu_head rcu_head; struct list_head ports; - struct list_head node_db; /* Known HSR nodes */ - struct list_head self_node_db; /* MACs of slaves */ + struct hlist_head node_db[HSR_HSIZE]; /* Known HSR nodes */ + struct hlist_head self_node_db; /* MACs of slaves */ struct timer_list announce_timer; /* Supervision frame dispatch */ struct timer_list prune_timer; int announce_count; @@ -212,6 +199,8 @@ struct hsr_priv { spinlock_t seqnr_lock; /* locking for sequence_nr */ spinlock_t list_lock; /* locking for node list */ struct hsr_proto_ops *proto_ops; + u32 hash_buckets; + u32 hash_seed; #define PRP_LAN_ID 0x5 /* 0x1010 for A and 0x1011 for B. Bit 0 is set * based on SLAVE_A or SLAVE_B */ @@ -259,11 +248,6 @@ static inline u16 prp_get_skb_sequence_nr(struct prp_rct *rct) return ntohs(rct->sequence_nr); } -static inline u16 get_prp_lan_id(struct prp_rct *rct) -{ - return ntohs(rct->lan_id_and_LSDU_size) >> 12; -} - /* assume there is a valid rct */ static inline bool prp_check_lsdu_size(struct sk_buff *skb, struct prp_rct *rct, diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c index f3c8f91dbe2c..1405c037cf7a 100644 --- a/net/hsr/hsr_netlink.c +++ b/net/hsr/hsr_netlink.c @@ -105,6 +105,7 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev, static void hsr_dellink(struct net_device *dev, struct list_head *head) { struct hsr_priv *hsr = netdev_priv(dev); + int i; del_timer_sync(&hsr->prune_timer); del_timer_sync(&hsr->announce_timer); @@ -113,7 +114,8 @@ static void hsr_dellink(struct net_device *dev, struct list_head *head) hsr_del_ports(hsr); hsr_del_self_node(hsr); - hsr_del_nodes(&hsr->node_db); + for (i = 0; i < hsr->hash_buckets; i++) + hsr_del_nodes(&hsr->node_db[i]); unregister_netdevice_queue(dev, head); } diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c index 2cf62718a282..2c087b7f17c5 100644 --- a/net/ieee802154/6lowpan/core.c +++ b/net/ieee802154/6lowpan/core.c @@ -47,6 +47,7 @@ #include #include #include +#include #include diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c index be6f06adefe0..a91283d1e5bf 100644 --- a/net/ieee802154/6lowpan/reassembly.c +++ b/net/ieee802154/6lowpan/reassembly.c @@ -130,6 +130,7 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq, goto err; fq->q.stamp = skb->tstamp; + fq->q.mono_delivery_time = skb->mono_delivery_time; if (frag_type == LOWPAN_DISPATCH_FRAG1) fq->q.flags |= INET_FRAG_FIRST_IN; diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index dd5a45f8a78a..359249ab77bf 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -30,7 +30,7 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, { void *hdr; int i, pages = 0; - uint32_t *buf = kcalloc(32, sizeof(uint32_t), GFP_KERNEL); + u32 *buf = kcalloc(IEEE802154_MAX_PAGE + 1, sizeof(u32), GFP_KERNEL); pr_debug("%s\n", __func__); @@ -47,7 +47,7 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) || nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel)) goto nla_put_failure; - for (i = 0; i < 32; i++) { + for (i = 0; i <= IEEE802154_MAX_PAGE; i++) { if (phy->supported.channels[i]) buf[pages++] = phy->supported.channels[i] | (i << 27); } diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 4db0325f6e1a..2d0c05ca9c6f 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -293,7 +293,7 @@ static int arp_constructor(struct neighbour *neigh) static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb) { dst_link_failure(skb); - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_FAILED); } /* Create and send an arp packet. */ @@ -1116,13 +1116,18 @@ static int arp_req_get(struct arpreq *r, struct net_device *dev) return err; } -static int arp_invalidate(struct net_device *dev, __be32 ip) +int arp_invalidate(struct net_device *dev, __be32 ip, bool force) { struct neighbour *neigh = neigh_lookup(&arp_tbl, &ip, dev); int err = -ENXIO; struct neigh_table *tbl = &arp_tbl; if (neigh) { + if ((neigh->nud_state & NUD_VALID) && !force) { + neigh_release(neigh); + return 0; + } + if (neigh->nud_state & ~NUD_NOARP) err = neigh_update(neigh, NULL, NUD_FAILED, NEIGH_UPDATE_F_OVERRIDE| @@ -1169,7 +1174,7 @@ static int arp_req_delete(struct net *net, struct arpreq *r, if (!dev) return -EINVAL; } - return arp_invalidate(dev, ip); + return arp_invalidate(dev, ip, true); } /* diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c index de610cb83694..f79ab942f03b 100644 --- a/net/ipv4/bpf_tcp_ca.c +++ b/net/ipv4/bpf_tcp_ca.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2019 Facebook */ +#include #include #include #include @@ -95,12 +96,14 @@ static int bpf_tcp_ca_btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf, const struct btf_type *t, int off, int size, enum bpf_access_type atype, - u32 *next_btf_id) + u32 *next_btf_id, + enum bpf_type_flag *flag) { size_t end; if (atype == BPF_READ) - return btf_struct_access(log, btf, t, off, size, atype, next_btf_id); + return btf_struct_access(log, btf, t, off, size, atype, next_btf_id, + flag); if (t != tcp_sock_type) { bpf_log(log, "only read is supported\n"); @@ -212,26 +215,23 @@ bpf_tcp_ca_get_func_proto(enum bpf_func_id func_id, } } -BTF_SET_START(bpf_tcp_ca_kfunc_ids) +BTF_SET_START(bpf_tcp_ca_check_kfunc_ids) BTF_ID(func, tcp_reno_ssthresh) BTF_ID(func, tcp_reno_cong_avoid) BTF_ID(func, tcp_reno_undo_cwnd) BTF_ID(func, tcp_slow_start) BTF_ID(func, tcp_cong_avoid_ai) -BTF_SET_END(bpf_tcp_ca_kfunc_ids) +BTF_SET_END(bpf_tcp_ca_check_kfunc_ids) -static bool bpf_tcp_ca_check_kfunc_call(u32 kfunc_btf_id, struct module *owner) -{ - if (btf_id_set_contains(&bpf_tcp_ca_kfunc_ids, kfunc_btf_id)) - return true; - return bpf_check_mod_kfunc_call(&bpf_tcp_ca_kfunc_list, kfunc_btf_id, owner); -} +static const struct btf_kfunc_id_set bpf_tcp_ca_kfunc_set = { + .owner = THIS_MODULE, + .check_set = &bpf_tcp_ca_check_kfunc_ids, +}; static const struct bpf_verifier_ops bpf_tcp_ca_verifier_ops = { .get_func_proto = bpf_tcp_ca_get_func_proto, .is_valid_access = bpf_tcp_ca_is_valid_access, .btf_struct_access = bpf_tcp_ca_btf_struct_access, - .check_kfunc_call = bpf_tcp_ca_check_kfunc_call, }; static int bpf_tcp_ca_init_member(const struct btf_type *t, @@ -300,3 +300,9 @@ struct bpf_struct_ops bpf_tcp_congestion_ops = { .init = bpf_tcp_ca_init, .name = "tcp_congestion_ops", }; + +static int __init bpf_tcp_ca_kfunc_init(void) +{ + return register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &bpf_tcp_ca_kfunc_set); +} +late_initcall(bpf_tcp_ca_kfunc_init); diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index fba2bffd65f7..53a6b14dc50a 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -104,6 +104,7 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { [IFA_FLAGS] = { .type = NLA_U32 }, [IFA_RT_PRIORITY] = { .type = NLA_U32 }, [IFA_TARGET_NETNSID] = { .type = NLA_S32 }, + [IFA_PROTO] = { .type = NLA_U8 }, }; struct inet_fill_args { @@ -889,6 +890,9 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, if (tb[IFA_RT_PRIORITY]) ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]); + if (tb[IFA_PROTO]) + ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]); + if (tb[IFA_CACHEINFO]) { struct ifa_cacheinfo *ci; @@ -1625,6 +1629,7 @@ static size_t inet_nlmsg_size(void) + nla_total_size(4) /* IFA_BROADCAST */ + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ + nla_total_size(4) /* IFA_FLAGS */ + + nla_total_size(1) /* IFA_PROTO */ + nla_total_size(4) /* IFA_RT_PRIORITY */ + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */ } @@ -1699,6 +1704,8 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || (ifa->ifa_label[0] && nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || + (ifa->ifa_proto && + nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) || nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) || (ifa->ifa_rt_priority && nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) || diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 85117b45216d..af8209f912ab 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -290,7 +291,7 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb) bool vmark = in_dev && IN_DEV_SRC_VMARK(in_dev); struct flowi4 fl4 = { .flowi4_iif = LOOPBACK_IFINDEX, - .flowi4_oif = l3mdev_master_ifindex_rcu(dev), + .flowi4_l3mdev = l3mdev_master_ifindex_rcu(dev), .daddr = ip_hdr(skb)->saddr, .flowi4_tos = ip_hdr(skb)->tos & IPTOS_RT_MASK, .flowi4_scope = scope, @@ -352,9 +353,8 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, bool dev_match; fl4.flowi4_oif = 0; - fl4.flowi4_iif = l3mdev_master_ifindex_rcu(dev); - if (!fl4.flowi4_iif) - fl4.flowi4_iif = oif ? : LOOPBACK_IFINDEX; + fl4.flowi4_l3mdev = l3mdev_master_ifindex_rcu(dev); + fl4.flowi4_iif = oif ? : LOOPBACK_IFINDEX; fl4.daddr = src; fl4.saddr = dst; fl4.flowi4_tos = tos; @@ -738,8 +738,16 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, memset(cfg, 0, sizeof(*cfg)); rtm = nlmsg_data(nlh); + + if (!inet_validate_dscp(rtm->rtm_tos)) { + NL_SET_ERR_MSG(extack, + "Invalid dsfield (tos): ECN bits must be 0"); + err = -EINVAL; + goto errout; + } + cfg->fc_dscp = inet_dsfield_to_dscp(rtm->rtm_tos); + cfg->fc_dst_len = rtm->rtm_dst_len; - cfg->fc_tos = rtm->rtm_tos; cfg->fc_table = rtm->rtm_table; cfg->fc_protocol = rtm->rtm_protocol; cfg->fc_scope = rtm->rtm_scope; @@ -1115,9 +1123,11 @@ void fib_add_ifaddr(struct in_ifaddr *ifa) return; /* Add broadcast address, if it is explicitly assigned. */ - if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF)) + if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF)) { fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim, 0); + arp_invalidate(dev, ifa->ifa_broadcast, false); + } if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags & IFA_F_SECONDARY) && (prefix != addr || ifa->ifa_prefixlen < 32)) { @@ -1131,6 +1141,7 @@ void fib_add_ifaddr(struct in_ifaddr *ifa) if (ifa->ifa_prefixlen < 31) { fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix | ~mask, 32, prim, 0); + arp_invalidate(dev, prefix | ~mask, false); } } } @@ -1550,7 +1561,7 @@ static void ip_fib_net_exit(struct net *net) { int i; - rtnl_lock(); + ASSERT_RTNL(); #ifdef CONFIG_IP_MULTIPLE_TABLES RCU_INIT_POINTER(net->ipv4.fib_main, NULL); RCU_INIT_POINTER(net->ipv4.fib_default, NULL); @@ -1575,7 +1586,7 @@ static void ip_fib_net_exit(struct net *net) #ifdef CONFIG_IP_MULTIPLE_TABLES fib4_rules_exit(net); #endif - rtnl_unlock(); + kfree(net->ipv4.fib_table_hash); fib4_notifier_exit(net); } @@ -1602,7 +1613,9 @@ static int __net_init fib_net_init(struct net *net) out_proc: nl_fib_lookup_exit(net); out_nlfl: + rtnl_lock(); ip_fib_net_exit(net); + rtnl_unlock(); goto out; } @@ -1610,12 +1623,23 @@ static void __net_exit fib_net_exit(struct net *net) { fib_proc_exit(net); nl_fib_lookup_exit(net); - ip_fib_net_exit(net); +} + +static void __net_exit fib_net_exit_batch(struct list_head *net_list) +{ + struct net *net; + + rtnl_lock(); + list_for_each_entry(net, net_list, exit_list) + ip_fib_net_exit(net); + + rtnl_unlock(); } static struct pernet_operations fib_net_ops = { .init = fib_net_init, .exit = fib_net_exit, + .exit_batch = fib_net_exit_batch, }; void __init ip_fib_init(void) diff --git a/net/ipv4/fib_lookup.h b/net/ipv4/fib_lookup.h index 78e40ea42e58..f9b9e26c32c1 100644 --- a/net/ipv4/fib_lookup.h +++ b/net/ipv4/fib_lookup.h @@ -4,13 +4,14 @@ #include #include +#include #include #include struct fib_alias { struct hlist_node fa_list; struct fib_info *fa_info; - u8 fa_tos; + dscp_t fa_dscp; u8 fa_type; u8 fa_state; u8 fa_slen; diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index e0b6c8b6de57..001fea394bde 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +36,7 @@ struct fib4_rule { struct fib_rule common; u8 dst_len; u8 src_len; - u8 tos; + dscp_t dscp; __be32 src; __be32 srcmask; __be32 dst; @@ -49,7 +50,7 @@ static bool fib4_rule_matchall(const struct fib_rule *rule) { struct fib4_rule *r = container_of(rule, struct fib4_rule, common); - if (r->dst_len || r->src_len || r->tos) + if (r->dst_len || r->src_len || r->dscp) return false; return fib_rule_matchall(rule); } @@ -185,7 +186,7 @@ INDIRECT_CALLABLE_SCOPE int fib4_rule_match(struct fib_rule *rule, ((daddr ^ r->dst) & r->dstmask)) return 0; - if (r->tos && (r->tos != fl4->flowi4_tos)) + if (r->dscp && r->dscp != inet_dsfield_to_dscp(fl4->flowi4_tos)) return 0; if (rule->ip_proto && (rule->ip_proto != fl4->flowi4_proto)) @@ -225,10 +226,17 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, int err = -EINVAL; struct fib4_rule *rule4 = (struct fib4_rule *) rule; + if (!inet_validate_dscp(frh->tos)) { + NL_SET_ERR_MSG(extack, + "Invalid dsfield (tos): ECN bits must be 0"); + goto errout; + } + /* IPv4 currently doesn't handle high order DSCP bits correctly */ if (frh->tos & ~IPTOS_TOS_MASK) { NL_SET_ERR_MSG(extack, "Invalid tos"); goto errout; } + rule4->dscp = inet_dsfield_to_dscp(frh->tos); /* split local/main if they are not already split */ err = fib_unmerge(net); @@ -270,7 +278,6 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, rule4->srcmask = inet_make_mask(rule4->src_len); rule4->dst_len = frh->dst_len; rule4->dstmask = inet_make_mask(rule4->dst_len); - rule4->tos = frh->tos; net->ipv4.fib_has_custom_rules = true; @@ -313,7 +320,7 @@ static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, if (frh->dst_len && (rule4->dst_len != frh->dst_len)) return 0; - if (frh->tos && (rule4->tos != frh->tos)) + if (frh->tos && inet_dscp_to_dsfield(rule4->dscp) != frh->tos) return 0; #ifdef CONFIG_IP_ROUTE_CLASSID @@ -337,7 +344,7 @@ static int fib4_rule_fill(struct fib_rule *rule, struct sk_buff *skb, frh->dst_len = rule4->dst_len; frh->src_len = rule4->src_len; - frh->tos = rule4->tos; + frh->tos = inet_dscp_to_dsfield(rule4->dscp); if ((rule4->dst_len && nla_put_in_addr(skb, FRA_DST, rule4->dst)) || diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 2dd375f7407b..cc8e84ef2ae4 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -523,7 +524,7 @@ void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, fri.tb_id = tb_id; fri.dst = key; fri.dst_len = dst_len; - fri.tos = fa->fa_tos; + fri.tos = inet_dscp_to_dsfield(fa->fa_dscp); fri.type = fa->fa_type; fri.offload = READ_ONCE(fa->offload); fri.trap = READ_ONCE(fa->trap); @@ -1257,34 +1258,13 @@ fib_info_laddrhash_bucket(const struct net *net, __be32 val) return &fib_info_laddrhash[slot]; } -static struct hlist_head *fib_info_hash_alloc(int bytes) -{ - if (bytes <= PAGE_SIZE) - return kzalloc(bytes, GFP_KERNEL); - else - return (struct hlist_head *) - __get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(bytes)); -} - -static void fib_info_hash_free(struct hlist_head *hash, int bytes) -{ - if (!hash) - return; - - if (bytes <= PAGE_SIZE) - kfree(hash); - else - free_pages((unsigned long) hash, get_order(bytes)); -} - static void fib_info_hash_move(struct hlist_head *new_info_hash, struct hlist_head *new_laddrhash, unsigned int new_size) { struct hlist_head *old_info_hash, *old_laddrhash; unsigned int old_size = fib_info_hash_size; - unsigned int i, bytes; + unsigned int i; spin_lock_bh(&fib_info_lock); old_info_hash = fib_info_hash; @@ -1325,9 +1305,8 @@ static void fib_info_hash_move(struct hlist_head *new_info_hash, spin_unlock_bh(&fib_info_lock); - bytes = old_size * sizeof(struct hlist_head *); - fib_info_hash_free(old_info_hash, bytes); - fib_info_hash_free(old_laddrhash, bytes); + kvfree(old_info_hash); + kvfree(old_laddrhash); } __be32 fib_info_update_nhc_saddr(struct net *net, struct fib_nh_common *nhc, @@ -1444,19 +1423,19 @@ struct fib_info *fib_create_info(struct fib_config *cfg, unsigned int new_size = fib_info_hash_size << 1; struct hlist_head *new_info_hash; struct hlist_head *new_laddrhash; - unsigned int bytes; + size_t bytes; if (!new_size) new_size = 16; - bytes = new_size * sizeof(struct hlist_head *); - new_info_hash = fib_info_hash_alloc(bytes); - new_laddrhash = fib_info_hash_alloc(bytes); + bytes = (size_t)new_size * sizeof(struct hlist_head *); + new_info_hash = kvzalloc(bytes, GFP_KERNEL); + new_laddrhash = kvzalloc(bytes, GFP_KERNEL); if (!new_info_hash || !new_laddrhash) { - fib_info_hash_free(new_info_hash, bytes); - fib_info_hash_free(new_laddrhash, bytes); - } else + kvfree(new_info_hash); + kvfree(new_laddrhash); + } else { fib_info_hash_move(new_info_hash, new_laddrhash, new_size); - + } if (!fib_info_hash_size) goto failure; } @@ -2061,7 +2040,7 @@ static void fib_select_default(const struct flowi4 *flp, struct fib_result *res) int order = -1, last_idx = -1; struct fib_alias *fa, *fa1 = NULL; u32 last_prio = res->fi->fib_priority; - u8 last_tos = 0; + dscp_t last_dscp = 0; hlist_for_each_entry_rcu(fa, fa_head, fa_list) { struct fib_info *next_fi = fa->fa_info; @@ -2069,19 +2048,20 @@ static void fib_select_default(const struct flowi4 *flp, struct fib_result *res) if (fa->fa_slen != slen) continue; - if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos) + if (fa->fa_dscp && + fa->fa_dscp != inet_dsfield_to_dscp(flp->flowi4_tos)) continue; if (fa->tb_id != tb->tb_id) continue; if (next_fi->fib_priority > last_prio && - fa->fa_tos == last_tos) { - if (last_tos) + fa->fa_dscp == last_dscp) { + if (last_dscp) continue; break; } if (next_fi->fib_flags & RTNH_F_DEAD) continue; - last_tos = fa->fa_tos; + last_dscp = fa->fa_dscp; last_prio = next_fi->fib_priority; if (next_fi->fib_scope != res->scope || @@ -2254,7 +2234,7 @@ void fib_select_multipath(struct fib_result *res, int hash) void fib_select_path(struct net *net, struct fib_result *res, struct flowi4 *fl4, const struct sk_buff *skb) { - if (fl4->flowi4_oif && !(fl4->flowi4_flags & FLOWI_FLAG_SKIP_NH_OIF)) + if (fl4->flowi4_oif) goto check_saddr; #ifdef CONFIG_IP_ROUTE_MULTIPATH diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index f7f74d5c14da..fb0e49c36c2e 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -81,7 +82,7 @@ static int call_fib_entry_notifier(struct notifier_block *nb, .dst = dst, .dst_len = dst_len, .fi = fa->fa_info, - .tos = fa->fa_tos, + .tos = inet_dscp_to_dsfield(fa->fa_dscp), .type = fa->fa_type, .tb_id = fa->tb_id, }; @@ -98,7 +99,7 @@ static int call_fib_entry_notifiers(struct net *net, .dst = dst, .dst_len = dst_len, .fi = fa->fa_info, - .tos = fa->fa_tos, + .tos = inet_dscp_to_dsfield(fa->fa_dscp), .type = fa->fa_type, .tb_id = fa->tb_id, }; @@ -973,13 +974,13 @@ static struct key_vector *fib_find_node(struct trie *t, return n; } -/* Return the first fib alias matching TOS with +/* Return the first fib alias matching DSCP with * priority less than or equal to PRIO. * If 'find_first' is set, return the first matching - * fib alias, regardless of TOS and priority. + * fib alias, regardless of DSCP and priority. */ static struct fib_alias *fib_find_alias(struct hlist_head *fah, u8 slen, - u8 tos, u32 prio, u32 tb_id, + dscp_t dscp, u32 prio, u32 tb_id, bool find_first) { struct fib_alias *fa; @@ -988,6 +989,10 @@ static struct fib_alias *fib_find_alias(struct hlist_head *fah, u8 slen, return NULL; hlist_for_each_entry(fa, fah, fa_list) { + /* Avoid Sparse warning when using dscp_t in inequalities */ + u8 __fa_dscp = inet_dscp_to_dsfield(fa->fa_dscp); + u8 __dscp = inet_dscp_to_dsfield(dscp); + if (fa->fa_slen < slen) continue; if (fa->fa_slen != slen) @@ -998,9 +1003,9 @@ static struct fib_alias *fib_find_alias(struct hlist_head *fah, u8 slen, break; if (find_first) return fa; - if (fa->fa_tos > tos) + if (__fa_dscp > __dscp) continue; - if (fa->fa_info->fib_priority >= prio || fa->fa_tos < tos) + if (fa->fa_info->fib_priority >= prio || __fa_dscp < __dscp) return fa; } @@ -1027,8 +1032,8 @@ fib_find_matching_alias(struct net *net, const struct fib_rt_info *fri) hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) { if (fa->fa_slen == slen && fa->tb_id == fri->tb_id && - fa->fa_tos == fri->tos && fa->fa_info == fri->fi && - fa->fa_type == fri->type) + fa->fa_dscp == inet_dsfield_to_dscp(fri->tos) && + fa->fa_info == fri->fi && fa->fa_type == fri->type) return fa; } @@ -1214,7 +1219,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb, struct fib_info *fi; u8 plen = cfg->fc_dst_len; u8 slen = KEYLENGTH - plen; - u8 tos = cfg->fc_tos; + dscp_t dscp; u32 key; int err; @@ -1231,12 +1236,13 @@ int fib_table_insert(struct net *net, struct fib_table *tb, goto err; } + dscp = cfg->fc_dscp; l = fib_find_node(t, &tp, key); - fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, + fa = l ? fib_find_alias(&l->leaf, slen, dscp, fi->fib_priority, tb->tb_id, false) : NULL; /* Now fa, if non-NULL, points to the first fib alias - * with the same keys [prefix,tos,priority], if such key already + * with the same keys [prefix,dscp,priority], if such key already * exists or to the node before which we will insert new one. * * If fa is NULL, we will need to allocate a new one and @@ -1244,7 +1250,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb, * of the new alias. */ - if (fa && fa->fa_tos == tos && + if (fa && fa->fa_dscp == dscp && fa->fa_info->fib_priority == fi->fib_priority) { struct fib_alias *fa_first, *fa_match; @@ -1264,7 +1270,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb, hlist_for_each_entry_from(fa, fa_list) { if ((fa->fa_slen != slen) || (fa->tb_id != tb->tb_id) || - (fa->fa_tos != tos)) + (fa->fa_dscp != dscp)) break; if (fa->fa_info->fib_priority != fi->fib_priority) break; @@ -1292,7 +1298,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb, goto out; fi_drop = fa->fa_info; - new_fa->fa_tos = fa->fa_tos; + new_fa->fa_dscp = fa->fa_dscp; new_fa->fa_info = fi; new_fa->fa_type = cfg->fc_type; state = fa->fa_state; @@ -1355,7 +1361,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb, goto out; new_fa->fa_info = fi; - new_fa->fa_tos = tos; + new_fa->fa_dscp = dscp; new_fa->fa_type = cfg->fc_type; new_fa->fa_state = 0; new_fa->fa_slen = slen; @@ -1423,11 +1429,8 @@ bool fib_lookup_good_nhc(const struct fib_nh_common *nhc, int fib_flags, !(fib_flags & FIB_LOOKUP_IGNORE_LINKSTATE)) return false; - if (!(flp->flowi4_flags & FLOWI_FLAG_SKIP_NH_OIF)) { - if (flp->flowi4_oif && - flp->flowi4_oif != nhc->nhc_oif) - return false; - } + if (flp->flowi4_oif && flp->flowi4_oif != nhc->nhc_oif) + return false; return true; } @@ -1571,7 +1574,8 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, if (index >= (1ul << fa->fa_slen)) continue; } - if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos) + if (fa->fa_dscp && + inet_dscp_to_dsfield(fa->fa_dscp) != flp->flowi4_tos) continue; if (fi->fib_dead) continue; @@ -1707,7 +1711,7 @@ int fib_table_delete(struct net *net, struct fib_table *tb, struct key_vector *l, *tp; u8 plen = cfg->fc_dst_len; u8 slen = KEYLENGTH - plen; - u8 tos = cfg->fc_tos; + dscp_t dscp; u32 key; key = ntohl(cfg->fc_dst); @@ -1719,11 +1723,13 @@ int fib_table_delete(struct net *net, struct fib_table *tb, if (!l) return -ESRCH; - fa = fib_find_alias(&l->leaf, slen, tos, 0, tb->tb_id, false); + dscp = cfg->fc_dscp; + fa = fib_find_alias(&l->leaf, slen, dscp, 0, tb->tb_id, false); if (!fa) return -ESRCH; - pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t); + pr_debug("Deleting %08x/%d dsfield=0x%02x t=%p\n", key, plen, + inet_dscp_to_dsfield(dscp), t); fa_to_delete = NULL; hlist_for_each_entry_from(fa, fa_list) { @@ -1731,7 +1737,7 @@ int fib_table_delete(struct net *net, struct fib_table *tb, if ((fa->fa_slen != slen) || (fa->tb_id != tb->tb_id) || - (fa->fa_tos != tos)) + (fa->fa_dscp != dscp)) break; if ((!cfg->fc_type || fa->fa_type == cfg->fc_type) && @@ -2299,7 +2305,7 @@ static int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb, fri.tb_id = tb->tb_id; fri.dst = xkey; fri.dst_len = KEYLENGTH - fa->fa_slen; - fri.tos = fa->fa_tos; + fri.tos = inet_dscp_to_dsfield(fa->fa_dscp); fri.type = fa->fa_type; fri.offload = READ_ONCE(fa->offload); fri.trap = READ_ONCE(fa->trap); @@ -2811,8 +2817,9 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v) fa->fa_info->fib_scope), rtn_type(buf2, sizeof(buf2), fa->fa_type)); - if (fa->fa_tos) - seq_printf(seq, " tos=%d", fa->fa_tos); + if (fa->fa_dscp) + seq_printf(seq, " tos=%d", + inet_dscp_to_dsfield(fa->fa_dscp)); seq_putc(seq, '\n'); } } diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index b7e277d8a84d..72a375c7f417 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -192,24 +192,14 @@ struct icmp_control { static const struct icmp_control icmp_pointers[NR_ICMP_TYPES+1]; -/* - * The ICMP socket(s). This is the most convenient way to flow control - * our ICMP output as well as maintain a clean interface throughout - * all layers. All Socketless IP sends will soon be gone. - * - * On SMP we have one ICMP socket per-cpu. - */ -static struct sock *icmp_sk(struct net *net) -{ - return this_cpu_read(*net->ipv4.icmp_sk); -} +static DEFINE_PER_CPU(struct sock *, ipv4_icmp_sk); /* Called with BH disabled */ static inline struct sock *icmp_xmit_lock(struct net *net) { struct sock *sk; - sk = icmp_sk(net); + sk = this_cpu_read(ipv4_icmp_sk); if (unlikely(!spin_trylock(&sk->sk_lock.slock))) { /* This can happen if the output path signals a @@ -217,11 +207,13 @@ static inline struct sock *icmp_xmit_lock(struct net *net) */ return NULL; } + sock_net_set(sk, net); return sk; } static inline void icmp_xmit_unlock(struct sock *sk) { + sock_net_set(sk, &init_net); spin_unlock(&sk->sk_lock.slock); } @@ -363,14 +355,13 @@ static int icmp_glue_bits(void *from, char *to, int offset, int len, int odd, return 0; } -static void icmp_push_reply(struct icmp_bxm *icmp_param, +static void icmp_push_reply(struct sock *sk, + struct icmp_bxm *icmp_param, struct flowi4 *fl4, struct ipcm_cookie *ipc, struct rtable **rt) { - struct sock *sk; struct sk_buff *skb; - sk = icmp_sk(dev_net((*rt)->dst.dev)); if (ip_append_data(sk, fl4, icmp_glue_bits, icmp_param, icmp_param->data_len+icmp_param->head_len, icmp_param->head_len, @@ -452,7 +443,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) if (IS_ERR(rt)) goto out_unlock; if (icmpv4_xrlim_allow(net, rt, &fl4, type, code)) - icmp_push_reply(icmp_param, &fl4, &ipc, &rt); + icmp_push_reply(sk, icmp_param, &fl4, &ipc, &rt); ip_rt_put(rt); out_unlock: icmp_xmit_unlock(sk); @@ -766,7 +757,7 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info, if (!fl4.saddr) fl4.saddr = htonl(INADDR_DUMMY); - icmp_push_reply(&icmp_param, &fl4, &ipc, &rt); + icmp_push_reply(sk, &icmp_param, &fl4, &ipc, &rt); ende: ip_rt_put(rt); out_unlock: @@ -1434,46 +1425,8 @@ static const struct icmp_control icmp_pointers[NR_ICMP_TYPES + 1] = { }, }; -static void __net_exit icmp_sk_exit(struct net *net) -{ - int i; - - for_each_possible_cpu(i) - inet_ctl_sock_destroy(*per_cpu_ptr(net->ipv4.icmp_sk, i)); - free_percpu(net->ipv4.icmp_sk); - net->ipv4.icmp_sk = NULL; -} - static int __net_init icmp_sk_init(struct net *net) { - int i, err; - - net->ipv4.icmp_sk = alloc_percpu(struct sock *); - if (!net->ipv4.icmp_sk) - return -ENOMEM; - - for_each_possible_cpu(i) { - struct sock *sk; - - err = inet_ctl_sock_create(&sk, PF_INET, - SOCK_RAW, IPPROTO_ICMP, net); - if (err < 0) - goto fail; - - *per_cpu_ptr(net->ipv4.icmp_sk, i) = sk; - - /* Enough space for 2 64K ICMP packets, including - * sk_buff/skb_shared_info struct overhead. - */ - sk->sk_sndbuf = 2 * SKB_TRUESIZE(64 * 1024); - - /* - * Speedup sock_wfree() - */ - sock_set_flag(sk, SOCK_USE_WRITE_QUEUE); - inet_sk(sk)->pmtudisc = IP_PMTUDISC_DONT; - } - /* Control parameters for ECHO replies. */ net->ipv4.sysctl_icmp_echo_ignore_all = 0; net->ipv4.sysctl_icmp_echo_enable_probe = 0; @@ -1499,18 +1452,36 @@ static int __net_init icmp_sk_init(struct net *net) net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr = 0; return 0; - -fail: - icmp_sk_exit(net); - return err; } static struct pernet_operations __net_initdata icmp_sk_ops = { .init = icmp_sk_init, - .exit = icmp_sk_exit, }; int __init icmp_init(void) { + int err, i; + + for_each_possible_cpu(i) { + struct sock *sk; + + err = inet_ctl_sock_create(&sk, PF_INET, + SOCK_RAW, IPPROTO_ICMP, &init_net); + if (err < 0) + return err; + + per_cpu(ipv4_icmp_sk, i) = sk; + + /* Enough space for 2 64K ICMP packets, including + * sk_buff/skb_shared_info struct overhead. + */ + sk->sk_sndbuf = 2 * SKB_TRUESIZE(64 * 1024); + + /* + * Speedup sock_wfree() + */ + sock_set_flag(sk, SOCK_USE_WRITE_QUEUE); + inet_sk(sk)->pmtudisc = IP_PMTUDISC_DONT; + } return register_pernet_subsys(&icmp_sk_ops); } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index fc2a985f6064..1e5b53c2bb26 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -866,12 +866,9 @@ static void reqsk_timer_handler(struct timer_list *t) (!resend || !inet_rtx_syn_ack(sk_listener, req) || inet_rsk(req)->acked)) { - unsigned long timeo; - if (req->num_timeout++ == 0) atomic_dec(&queue->young); - timeo = min(TCP_TIMEOUT_INIT << req->num_timeout, TCP_RTO_MAX); - mod_timer(&req->rsk_timer, jiffies + timeo); + mod_timer(&req->rsk_timer, jiffies + reqsk_timeout(req, TCP_RTO_MAX)); if (!nreq) return; @@ -1046,6 +1043,9 @@ int inet_csk_listen_start(struct sock *sk) sk->sk_ack_backlog = 0; inet_csk_delack_init(sk); + if (sk->sk_txrehash == SOCK_TXREHASH_DEFAULT) + sk->sk_txrehash = READ_ONCE(sock_net(sk)->core.sysctl_txrehash); + /* There is race window here: we announce ourselves listening, * but this transition is still not validated by get_port(). * It is OK, because this socket enters to hash table only diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 341096807100..63948f6aeca0 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -572,6 +572,7 @@ void inet_frag_reasm_finish(struct inet_frag_queue *q, struct sk_buff *head, skb_mark_not_on_list(head); head->prev = NULL; head->tstamp = q->stamp; + head->mono_delivery_time = q->mono_delivery_time; } EXPORT_SYMBOL(inet_frag_reasm_finish); diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 30ab717ff1b8..17440840a791 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -637,7 +637,9 @@ int __inet_hash(struct sock *sk, struct sock *osk) int err = 0; if (sk->sk_state != TCP_LISTEN) { + local_bh_disable(); inet_ehash_nolisten(sk, osk, NULL); + local_bh_enable(); return 0; } WARN_ON(!sk_unhashed(sk)); @@ -669,45 +671,54 @@ int inet_hash(struct sock *sk) { int err = 0; - if (sk->sk_state != TCP_CLOSE) { - local_bh_disable(); + if (sk->sk_state != TCP_CLOSE) err = __inet_hash(sk, NULL); - local_bh_enable(); - } return err; } EXPORT_SYMBOL_GPL(inet_hash); -void inet_unhash(struct sock *sk) +static void __inet_unhash(struct sock *sk, struct inet_listen_hashbucket *ilb) { - struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; - struct inet_listen_hashbucket *ilb = NULL; - spinlock_t *lock; - if (sk_unhashed(sk)) return; - if (sk->sk_state == TCP_LISTEN) { - ilb = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)]; - lock = &ilb->lock; - } else { - lock = inet_ehash_lockp(hashinfo, sk->sk_hash); - } - spin_lock_bh(lock); - if (sk_unhashed(sk)) - goto unlock; - if (rcu_access_pointer(sk->sk_reuseport_cb)) reuseport_stop_listen_sock(sk); if (ilb) { + struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; + inet_unhash2(hashinfo, sk); ilb->count--; } __sk_nulls_del_node_init_rcu(sk); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); -unlock: - spin_unlock_bh(lock); +} + +void inet_unhash(struct sock *sk) +{ + struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; + + if (sk_unhashed(sk)) + return; + + if (sk->sk_state == TCP_LISTEN) { + struct inet_listen_hashbucket *ilb; + + ilb = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)]; + /* Don't disable bottom halves while acquiring the lock to + * avoid circular locking dependency on PREEMPT_RT. + */ + spin_lock(&ilb->lock); + __inet_unhash(sk, ilb); + spin_unlock(&ilb->lock); + } else { + spinlock_t *lock = inet_ehash_lockp(hashinfo, sk->sk_hash); + + spin_lock_bh(lock); + __inet_unhash(sk, NULL); + spin_unlock_bh(lock); + } } EXPORT_SYMBOL_GPL(inet_unhash); diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c index 437afe392e66..9e0bbd026560 100644 --- a/net/ipv4/inet_timewait_sock.c +++ b/net/ipv4/inet_timewait_sock.c @@ -52,14 +52,15 @@ static void inet_twsk_kill(struct inet_timewait_sock *tw) spin_unlock(lock); /* Disassociate with bind bucket. */ - bhead = &hashinfo->bhash[inet_bhashfn(twsk_net(tw), tw->tw_num, - hashinfo->bhash_size)]; + bhead = &hashinfo->bhash[tw->tw_bslot]; spin_lock(&bhead->lock); inet_twsk_bind_unhash(tw, hashinfo); spin_unlock(&bhead->lock); - atomic_dec(&tw->tw_dr->tw_count); + if (refcount_dec_and_test(&tw->tw_dr->tw_refcount)) + kfree(tw->tw_dr); + inet_twsk_put(tw); } @@ -110,8 +111,12 @@ void inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk, Note, that any socket with inet->num != 0 MUST be bound in binding cache, even if it is closed. */ - bhead = &hashinfo->bhash[inet_bhashfn(twsk_net(tw), inet->inet_num, - hashinfo->bhash_size)]; + /* Cache inet_bhashfn(), because 'struct net' might be no longer + * available later in inet_twsk_kill(). + */ + tw->tw_bslot = inet_bhashfn(twsk_net(tw), inet->inet_num, + hashinfo->bhash_size); + bhead = &hashinfo->bhash[tw->tw_bslot]; spin_lock(&bhead->lock); tw->tw_tb = icsk->icsk_bind_hash; WARN_ON(!icsk->icsk_bind_hash); @@ -145,10 +150,6 @@ static void tw_timer_handler(struct timer_list *t) { struct inet_timewait_sock *tw = from_timer(tw, t, tw_timer); - if (tw->tw_kill) - __NET_INC_STATS(twsk_net(tw), LINUX_MIB_TIMEWAITKILLED); - else - __NET_INC_STATS(twsk_net(tw), LINUX_MIB_TIMEWAITED); inet_twsk_kill(tw); } @@ -158,7 +159,7 @@ struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, { struct inet_timewait_sock *tw; - if (atomic_read(&dr->tw_count) >= dr->sysctl_max_tw_buckets) + if (refcount_read(&dr->tw_refcount) - 1 >= dr->sysctl_max_tw_buckets) return NULL; tw = kmem_cache_alloc(sk->sk_prot_creator->twsk_prot->twsk_slab, @@ -244,59 +245,15 @@ void __inet_twsk_schedule(struct inet_timewait_sock *tw, int timeo, bool rearm) * of PAWS. */ - tw->tw_kill = timeo <= 4*HZ; if (!rearm) { + bool kill = timeo <= 4*HZ; + + __NET_INC_STATS(twsk_net(tw), kill ? LINUX_MIB_TIMEWAITKILLED : + LINUX_MIB_TIMEWAITED); BUG_ON(mod_timer(&tw->tw_timer, jiffies + timeo)); - atomic_inc(&tw->tw_dr->tw_count); + refcount_inc(&tw->tw_dr->tw_refcount); } else { mod_timer_pending(&tw->tw_timer, jiffies + timeo); } } EXPORT_SYMBOL_GPL(__inet_twsk_schedule); - -void inet_twsk_purge(struct inet_hashinfo *hashinfo, int family) -{ - struct inet_timewait_sock *tw; - struct sock *sk; - struct hlist_nulls_node *node; - unsigned int slot; - - for (slot = 0; slot <= hashinfo->ehash_mask; slot++) { - struct inet_ehash_bucket *head = &hashinfo->ehash[slot]; -restart_rcu: - cond_resched(); - rcu_read_lock(); -restart: - sk_nulls_for_each_rcu(sk, node, &head->chain) { - if (sk->sk_state != TCP_TIME_WAIT) - continue; - tw = inet_twsk(sk); - if ((tw->tw_family != family) || - refcount_read(&twsk_net(tw)->ns.count)) - continue; - - if (unlikely(!refcount_inc_not_zero(&tw->tw_refcnt))) - continue; - - if (unlikely((tw->tw_family != family) || - refcount_read(&twsk_net(tw)->ns.count))) { - inet_twsk_put(tw); - goto restart; - } - - rcu_read_unlock(); - local_bh_disable(); - inet_twsk_deschedule_put(tw); - local_bh_enable(); - goto restart_rcu; - } - /* If the nulls value we got at the end of this lookup is - * not the expected one, we must restart lookup. - * We probably met an item that was moved to another chain. - */ - if (get_nulls_value(node) != slot) - goto restart; - rcu_read_unlock(); - } -} -EXPORT_SYMBOL_GPL(inet_twsk_purge); diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 00ec819f949b..92ba3350274b 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -79,7 +79,7 @@ static int ip_forward_finish(struct net *net, struct sock *sk, struct sk_buff *s if (unlikely(opt->optlen)) ip_forward_options(skb); - skb->tstamp = 0; + skb_clear_tstamp(skb); return dst_output(net, sk, skb); } diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index fad803d2d711..fb153569889e 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -349,6 +349,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) qp->iif = dev->ifindex; qp->q.stamp = skb->tstamp; + qp->q.mono_delivery_time = skb->mono_delivery_time; qp->q.meat += skb->len; qp->ecn |= ecn; add_frag_mem_limit(qp->q.fqdir, skb->truesize); diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 3a025c011971..95f7bb052784 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -196,7 +196,8 @@ void ip_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int protocol) if (ipprot) { if (!ipprot->no_policy) { if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { - kfree_skb(skb); + kfree_skb_reason(skb, + SKB_DROP_REASON_XFRM_POLICY); return; } nf_reset_ct(skb); @@ -215,7 +216,7 @@ void ip_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int protocol) icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0); } - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_IP_NOPROTO); } else { __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS); consume_skb(skb); @@ -225,6 +226,7 @@ void ip_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int protocol) static int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { + skb_clear_delivery_time(skb); __skb_pull(skb, skb_network_header_len(skb)); rcu_read_lock(); @@ -318,8 +320,10 @@ static int ip_rcv_finish_core(struct net *net, struct sock *sk, { const struct iphdr *iph = ip_hdr(skb); int (*edemux)(struct sk_buff *skb); + int err, drop_reason; struct rtable *rt; - int err; + + drop_reason = SKB_DROP_REASON_NOT_SPECIFIED; if (ip_can_use_hint(skb, iph, hint)) { err = ip_route_use_hint(skb, iph->daddr, iph->saddr, iph->tos, @@ -396,19 +400,23 @@ static int ip_rcv_finish_core(struct net *net, struct sock *sk, * so-called "hole-196" attack) so do it for both. */ if (in_dev && - IN_DEV_ORCONF(in_dev, DROP_UNICAST_IN_L2_MULTICAST)) + IN_DEV_ORCONF(in_dev, DROP_UNICAST_IN_L2_MULTICAST)) { + drop_reason = SKB_DROP_REASON_UNICAST_IN_L2_MULTICAST; goto drop; + } } return NET_RX_SUCCESS; drop: - kfree_skb(skb); + kfree_skb_reason(skb, drop_reason); return NET_RX_DROP; drop_error: - if (err == -EXDEV) + if (err == -EXDEV) { + drop_reason = SKB_DROP_REASON_IP_RPFILTER; __NET_INC_STATS(net, LINUX_MIB_IPRPFILTER); + } goto drop; } @@ -436,13 +444,16 @@ static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net) { const struct iphdr *iph; + int drop_reason; u32 len; /* When the interface is in promisc. mode, drop all the crap * that it receives, do not try to analyse it. */ - if (skb->pkt_type == PACKET_OTHERHOST) + if (skb->pkt_type == PACKET_OTHERHOST) { + drop_reason = SKB_DROP_REASON_OTHERHOST; goto drop; + } __IP_UPD_PO_STATS(net, IPSTATS_MIB_IN, skb->len); @@ -452,6 +463,7 @@ static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net) goto out; } + drop_reason = SKB_DROP_REASON_NOT_SPECIFIED; if (!pskb_may_pull(skb, sizeof(struct iphdr))) goto inhdr_error; @@ -488,6 +500,7 @@ static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net) len = ntohs(iph->tot_len); if (skb->len < len) { + drop_reason = SKB_DROP_REASON_PKT_TOO_SMALL; __IP_INC_STATS(net, IPSTATS_MIB_INTRUNCATEDPKTS); goto drop; } else if (len < (iph->ihl*4)) @@ -516,11 +529,14 @@ static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net) return skb; csum_error: + drop_reason = SKB_DROP_REASON_IP_CSUM; __IP_INC_STATS(net, IPSTATS_MIB_CSUMERRORS); inhdr_error: + if (drop_reason == SKB_DROP_REASON_NOT_SPECIFIED) + drop_reason = SKB_DROP_REASON_IP_INHDR; __IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS); drop: - kfree_skb(skb); + kfree_skb_reason(skb, drop_reason); out: return NULL; } diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index da1b5038bdfd..a9e22a098872 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -42,7 +42,7 @@ */ void ip_options_build(struct sk_buff *skb, struct ip_options *opt, - __be32 daddr, struct rtable *rt, int is_frag) + __be32 daddr, struct rtable *rt) { unsigned char *iph = skb_network_header(skb); @@ -53,28 +53,15 @@ void ip_options_build(struct sk_buff *skb, struct ip_options *opt, if (opt->srr) memcpy(iph + opt->srr + iph[opt->srr + 1] - 4, &daddr, 4); - if (!is_frag) { - if (opt->rr_needaddr) - ip_rt_get_source(iph + opt->rr + iph[opt->rr + 2] - 5, skb, rt); - if (opt->ts_needaddr) - ip_rt_get_source(iph + opt->ts + iph[opt->ts + 2] - 9, skb, rt); - if (opt->ts_needtime) { - __be32 midtime; + if (opt->rr_needaddr) + ip_rt_get_source(iph + opt->rr + iph[opt->rr + 2] - 5, skb, rt); + if (opt->ts_needaddr) + ip_rt_get_source(iph + opt->ts + iph[opt->ts + 2] - 9, skb, rt); + if (opt->ts_needtime) { + __be32 midtime; - midtime = inet_current_timestamp(); - memcpy(iph + opt->ts + iph[opt->ts + 2] - 5, &midtime, 4); - } - return; - } - if (opt->rr) { - memset(iph + opt->rr, IPOPT_NOP, iph[opt->rr + 1]); - opt->rr = 0; - opt->rr_needaddr = 0; - } - if (opt->ts) { - memset(iph + opt->ts, IPOPT_NOP, iph[opt->ts + 1]); - opt->ts = 0; - opt->ts_needaddr = opt->ts_needtime = 0; + midtime = inet_current_timestamp(); + memcpy(iph + opt->ts + iph[opt->ts + 2] - 5, &midtime, 4); } } diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 7911916a480b..00b4bf26fd93 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -179,7 +179,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk, if (opt && opt->opt.optlen) { iph->ihl += opt->opt.optlen>>2; - ip_options_build(skb, &opt->opt, daddr, rt, 0); + ip_options_build(skb, &opt->opt, daddr, rt); } skb->priority = sk->sk_priority; @@ -233,7 +233,7 @@ static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *s net_dbg_ratelimited("%s: No header cache and no neighbour!\n", __func__); - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_CREATEFAIL); return -EINVAL; } @@ -317,7 +317,7 @@ static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *sk case NET_XMIT_CN: return __ip_finish_output(net, sk, skb) ? : ret; default: - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_BPF_CGROUP_EGRESS); return ret; } } @@ -337,7 +337,7 @@ static int ip_mc_finish_output(struct net *net, struct sock *sk, case NET_XMIT_SUCCESS: break; default: - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_BPF_CGROUP_EGRESS); return ret; } @@ -519,7 +519,7 @@ int __ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, if (inet_opt && inet_opt->opt.optlen) { iph->ihl += inet_opt->opt.optlen >> 2; - ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0); + ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt); } ip_select_ident_segs(net, skb, sk, @@ -536,7 +536,7 @@ int __ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, no_route: rcu_read_unlock(); IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES); - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_IP_OUTNOROUTES); return -EHOSTUNREACH; } EXPORT_SYMBOL(__ip_queue_xmit); @@ -761,6 +761,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, { struct iphdr *iph; struct sk_buff *skb2; + bool mono_delivery_time = skb->mono_delivery_time; struct rtable *rt = skb_rtable(skb); unsigned int mtu, hlen, ll_rs; struct ip_fraglist_iter iter; @@ -852,7 +853,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, } } - skb->tstamp = tstamp; + skb_set_delivery_time(skb, tstamp, mono_delivery_time); err = output(net, sk, skb); if (!err) @@ -908,7 +909,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, /* * Put this fragment into the sending queue. */ - skb2->tstamp = tstamp; + skb_set_delivery_time(skb2, tstamp, mono_delivery_time); err = output(net, sk, skb2); if (err) goto fail; @@ -1541,7 +1542,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk, if (opt) { iph->ihl += opt->optlen >> 2; - ip_options_build(skb, opt, cork->addr, rt, 0); + ip_options_build(skb, opt, cork->addr, rt); } skb->priority = (cork->tos != -1) ? cork->priority: sk->sk_priority; @@ -1727,6 +1728,7 @@ void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb, arg->csumoffset) = csum_fold(csum_add(nskb->csum, arg->csum)); nskb->ip_summed = CHECKSUM_NONE; + nskb->mono_delivery_time = !!transmit_time; ip_push_pending_frames(sk, &fl4); } out: diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 29bbe2b08ae9..c860519d57ee 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -268,13 +268,12 @@ static void __net_exit ipmr_rules_exit(struct net *net) { struct mr_table *mrt, *next; - rtnl_lock(); + ASSERT_RTNL(); list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) { list_del(&mrt->list); ipmr_free_table(mrt); } fib_rules_unregister(net->ipv4.mr_rules_ops); - rtnl_unlock(); } static int ipmr_rules_dump(struct net *net, struct notifier_block *nb, @@ -330,10 +329,9 @@ static int __net_init ipmr_rules_init(struct net *net) static void __net_exit ipmr_rules_exit(struct net *net) { - rtnl_lock(); + ASSERT_RTNL(); ipmr_free_table(net->ipv4.mrt); net->ipv4.mrt = NULL; - rtnl_unlock(); } static int ipmr_rules_dump(struct net *net, struct notifier_block *nb, @@ -3077,7 +3075,9 @@ static int __net_init ipmr_net_init(struct net *net) proc_cache_fail: remove_proc_entry("ip_mr_vif", net->proc_net); proc_vif_fail: + rtnl_lock(); ipmr_rules_exit(net); + rtnl_unlock(); #endif ipmr_rules_fail: ipmr_notifier_exit(net); @@ -3092,12 +3092,22 @@ static void __net_exit ipmr_net_exit(struct net *net) remove_proc_entry("ip_mr_vif", net->proc_net); #endif ipmr_notifier_exit(net); - ipmr_rules_exit(net); +} + +static void __net_exit ipmr_net_exit_batch(struct list_head *net_list) +{ + struct net *net; + + rtnl_lock(); + list_for_each_entry(net, net_list, exit_list) + ipmr_rules_exit(net); + rtnl_unlock(); } static struct pernet_operations ipmr_net_ops = { .init = ipmr_net_init, .exit = ipmr_net_exit, + .exit_batch = ipmr_net_exit_batch, }; int __init ip_mr_init(void) diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c index 3e2685c120c7..76a411ae9fe6 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -580,7 +580,7 @@ static struct nf_ct_helper_expectfn callforwarding_nat = { }; /****************************************************************************/ -static int __init init(void) +static int __init nf_nat_h323_init(void) { BUG_ON(set_h245_addr_hook != NULL); BUG_ON(set_h225_addr_hook != NULL); @@ -607,7 +607,7 @@ static int __init init(void) } /****************************************************************************/ -static void __exit fini(void) +static void __exit nf_nat_h323_fini(void) { RCU_INIT_POINTER(set_h245_addr_hook, NULL); RCU_INIT_POINTER(set_h225_addr_hook, NULL); @@ -624,8 +624,8 @@ static void __exit fini(void) } /****************************************************************************/ -module_init(init); -module_exit(fini); +module_init(nf_nat_h323_init); +module_exit(nf_nat_h323_fini); MODULE_AUTHOR("Jing Min Zhao "); MODULE_DESCRIPTION("H.323 NAT helper"); diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c index 3f248a19faa3..fab357cc8559 100644 --- a/net/ipv4/netfilter/nf_nat_pptp.c +++ b/net/ipv4/netfilter/nf_nat_pptp.c @@ -295,28 +295,24 @@ pptp_inbound_pkt(struct sk_buff *skb, return NF_ACCEPT; } +static const struct nf_nat_pptp_hook pptp_hooks = { + .outbound = pptp_outbound_pkt, + .inbound = pptp_inbound_pkt, + .exp_gre = pptp_exp_gre, + .expectfn = pptp_nat_expected, +}; + static int __init nf_nat_helper_pptp_init(void) { - BUG_ON(nf_nat_pptp_hook_outbound != NULL); - RCU_INIT_POINTER(nf_nat_pptp_hook_outbound, pptp_outbound_pkt); + WARN_ON(nf_nat_pptp_hook != NULL); + RCU_INIT_POINTER(nf_nat_pptp_hook, &pptp_hooks); - BUG_ON(nf_nat_pptp_hook_inbound != NULL); - RCU_INIT_POINTER(nf_nat_pptp_hook_inbound, pptp_inbound_pkt); - - BUG_ON(nf_nat_pptp_hook_exp_gre != NULL); - RCU_INIT_POINTER(nf_nat_pptp_hook_exp_gre, pptp_exp_gre); - - BUG_ON(nf_nat_pptp_hook_expectfn != NULL); - RCU_INIT_POINTER(nf_nat_pptp_hook_expectfn, pptp_nat_expected); return 0; } static void __exit nf_nat_helper_pptp_fini(void) { - RCU_INIT_POINTER(nf_nat_pptp_hook_expectfn, NULL); - RCU_INIT_POINTER(nf_nat_pptp_hook_exp_gre, NULL); - RCU_INIT_POINTER(nf_nat_pptp_hook_inbound, NULL); - RCU_INIT_POINTER(nf_nat_pptp_hook_outbound, NULL); + RCU_INIT_POINTER(nf_nat_pptp_hook, NULL); synchronize_rcu(); } diff --git a/net/ipv4/netfilter/nft_dup_ipv4.c b/net/ipv4/netfilter/nft_dup_ipv4.c index aeb631760eb9..0bcd6aee6000 100644 --- a/net/ipv4/netfilter/nft_dup_ipv4.c +++ b/net/ipv4/netfilter/nft_dup_ipv4.c @@ -75,6 +75,7 @@ static const struct nft_expr_ops nft_dup_ipv4_ops = { .eval = nft_dup_ipv4_eval, .init = nft_dup_ipv4_init, .dump = nft_dup_ipv4_dump, + .reduce = NFT_REDUCE_READONLY, }; static const struct nla_policy nft_dup_ipv4_policy[NFTA_DUP_MAX + 1] = { diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c index 03df986217b7..4151eb1262dd 100644 --- a/net/ipv4/netfilter/nft_fib_ipv4.c +++ b/net/ipv4/netfilter/nft_fib_ipv4.c @@ -152,6 +152,7 @@ static const struct nft_expr_ops nft_fib4_type_ops = { .init = nft_fib_init, .dump = nft_fib_dump, .validate = nft_fib_validate, + .reduce = nft_fib_reduce, }; static const struct nft_expr_ops nft_fib4_ops = { @@ -161,6 +162,7 @@ static const struct nft_expr_ops nft_fib4_ops = { .init = nft_fib_init, .dump = nft_fib_dump, .validate = nft_fib_validate, + .reduce = nft_fib_reduce, }; static const struct nft_expr_ops * diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c index 55fc23a8f7a7..6cb213bb7256 100644 --- a/net/ipv4/netfilter/nft_reject_ipv4.c +++ b/net/ipv4/netfilter/nft_reject_ipv4.c @@ -45,6 +45,7 @@ static const struct nft_expr_ops nft_reject_ipv4_ops = { .init = nft_reject_init, .dump = nft_reject_dump, .validate = nft_reject_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_reject_ipv4_type __read_mostly = { diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index eeafeccebb8d..e459a391e607 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -3733,12 +3733,16 @@ void nexthop_res_grp_activity_update(struct net *net, u32 id, u16 num_buckets, } EXPORT_SYMBOL(nexthop_res_grp_activity_update); -static void __net_exit nexthop_net_exit(struct net *net) +static void __net_exit nexthop_net_exit_batch(struct list_head *net_list) { + struct net *net; + rtnl_lock(); - flush_all_nexthops(net); + list_for_each_entry(net, net_list, exit_list) { + flush_all_nexthops(net); + kfree(net->nexthop.devhash); + } rtnl_unlock(); - kfree(net->nexthop.devhash); } static int __net_init nexthop_net_init(struct net *net) @@ -3756,7 +3760,7 @@ static int __net_init nexthop_net_init(struct net *net) static struct pernet_operations nexthop_net_ops = { .init = nexthop_net_init, - .exit = nexthop_net_exit, + .exit_batch = nexthop_net_exit_batch, }; static int __init nexthop_init(void) diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index f30273afb539..28836071f0a6 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -59,8 +59,8 @@ static int sockstat_seq_show(struct seq_file *seq, void *v) socket_seq_show(seq); seq_printf(seq, "TCP: inuse %d orphan %d tw %d alloc %d mem %ld\n", sock_prot_inuse_get(net, &tcp_prot), orphans, - atomic_read(&net->ipv4.tcp_death_row.tw_count), sockets, - proto_memory_allocated(&tcp_prot)); + refcount_read(&net->ipv4.tcp_death_row->tw_refcount) - 1, + sockets, proto_memory_allocated(&tcp_prot)); seq_printf(seq, "UDP: inuse %d mem %ld\n", sock_prot_inuse_get(net, &udp_prot), proto_memory_allocated(&udp_prot)); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index f33ad1f383b6..98c6f3429593 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -84,6 +84,7 @@ #include #include #include +#include #include #include #include @@ -112,14 +113,13 @@ #define DEFAULT_MIN_PMTU (512 + 20 + 20) #define DEFAULT_MTU_EXPIRES (10 * 60 * HZ) - +#define DEFAULT_MIN_ADVMSS 256 static int ip_rt_max_size; static int ip_rt_redirect_number __read_mostly = 9; static int ip_rt_redirect_load __read_mostly = HZ / 50; static int ip_rt_redirect_silence __read_mostly = ((HZ / 50) << (9 + 1)); static int ip_rt_error_cost __read_mostly = HZ; static int ip_rt_error_burst __read_mostly = 5 * HZ; -static int ip_rt_min_advmss __read_mostly = 256; static int ip_rt_gc_timeout __read_mostly = RT_GC_TIMEOUT; @@ -458,7 +458,7 @@ static u32 *ip_tstamps __read_mostly; * if one generator is seldom used. This makes hard for an attacker * to infer how many packets were sent between two points in time. */ -u32 ip_idents_reserve(u32 hash, int segs) +static u32 ip_idents_reserve(u32 hash, int segs) { u32 bucket, old, now = (u32)jiffies; atomic_t *p_id; @@ -479,7 +479,6 @@ u32 ip_idents_reserve(u32 hash, int segs) */ return atomic_add_return(segs + delta, p_id) - segs; } -EXPORT_SYMBOL(ip_idents_reserve); void __ip_select_ident(struct net *net, struct iphdr *iph, int segs) { @@ -499,6 +498,15 @@ void __ip_select_ident(struct net *net, struct iphdr *iph, int segs) } EXPORT_SYMBOL(__ip_select_ident); +static void ip_rt_fix_tos(struct flowi4 *fl4) +{ + __u8 tos = RT_FL_TOS(fl4); + + fl4->flowi4_tos = tos & IPTOS_RT_MASK; + fl4->flowi4_scope = tos & RTO_ONLINK ? + RT_SCOPE_LINK : RT_SCOPE_UNIVERSE; +} + static void __build_flow_key(const struct net *net, struct flowi4 *fl4, const struct sock *sk, const struct iphdr *iph, @@ -824,6 +832,7 @@ static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buf rt = (struct rtable *) dst; __build_flow_key(net, &fl4, sk, iph, oif, tos, prot, mark, 0); + ip_rt_fix_tos(&fl4); __ip_do_redirect(rt, skb, &fl4, true); } @@ -1048,6 +1057,7 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, struct flowi4 fl4; ip_rt_build_flow_key(&fl4, sk, skb); + ip_rt_fix_tos(&fl4); /* Don't make lookup fail for bridged encapsulations */ if (skb && netif_is_any_bridge_port(skb->dev)) @@ -1122,6 +1132,8 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) goto out; new = true; + } else { + ip_rt_fix_tos(&fl4); } __ip_rt_update_pmtu((struct rtable *)xfrm_dst_path(&rt->dst), &fl4, mtu); @@ -1298,9 +1310,10 @@ static void set_class_tag(struct rtable *rt, u32 tag) static unsigned int ipv4_default_advmss(const struct dst_entry *dst) { + struct net *net = dev_net(dst->dev); unsigned int header_size = sizeof(struct tcphdr) + sizeof(struct iphdr); unsigned int advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size, - ip_rt_min_advmss); + net->ipv4.ip_rt_min_advmss); return min(advmss, IPV4_MAX_PMTU - header_size); } @@ -1485,6 +1498,7 @@ static bool rt_cache_route(struct fib_nh_common *nhc, struct rtable *rt) struct uncached_list { spinlock_t lock; struct list_head head; + struct list_head quarantine; }; static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt_uncached_list); @@ -1506,7 +1520,7 @@ void rt_del_uncached_list(struct rtable *rt) struct uncached_list *ul = rt->rt_uncached_list; spin_lock_bh(&ul->lock); - list_del(&rt->rt_uncached); + list_del_init(&rt->rt_uncached); spin_unlock_bh(&ul->lock); } } @@ -1521,20 +1535,24 @@ static void ipv4_dst_destroy(struct dst_entry *dst) void rt_flush_dev(struct net_device *dev) { - struct rtable *rt; + struct rtable *rt, *safe; int cpu; for_each_possible_cpu(cpu) { struct uncached_list *ul = &per_cpu(rt_uncached_list, cpu); + if (list_empty(&ul->head)) + continue; + spin_lock_bh(&ul->lock); - list_for_each_entry(rt, &ul->head, rt_uncached) { + list_for_each_entry_safe(rt, safe, &ul->head, rt_uncached) { if (rt->dst.dev != dev) continue; rt->dst.dev = blackhole_netdev; dev_replace_track(dev, blackhole_netdev, &rt->dst.dev_tracker, GFP_ATOMIC); + list_move(&rt->rt_uncached, &ul->quarantine); } spin_unlock_bh(&ul->lock); } @@ -2258,6 +2276,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, /* * Now we are ready to route packet. */ + fl4.flowi4_l3mdev = 0; fl4.flowi4_oif = 0; fl4.flowi4_iif = dev->ifindex; fl4.flowi4_mark = skb->mark; @@ -2603,7 +2622,6 @@ static struct rtable *__mkroute_output(const struct fib_result *res, struct rtable *ip_route_output_key_hash(struct net *net, struct flowi4 *fl4, const struct sk_buff *skb) { - __u8 tos = RT_FL_TOS(fl4); struct fib_result res = { .type = RTN_UNSPEC, .fi = NULL, @@ -2613,9 +2631,7 @@ struct rtable *ip_route_output_key_hash(struct net *net, struct flowi4 *fl4, struct rtable *rth; fl4->flowi4_iif = LOOPBACK_IFINDEX; - fl4->flowi4_tos = tos & IPTOS_RT_MASK; - fl4->flowi4_scope = ((tos & RTO_ONLINK) ? - RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); + ip_rt_fix_tos(fl4); rcu_read_lock(); rth = ip_route_output_key_hash_rcu(net, fl4, &res, skb); @@ -2733,8 +2749,7 @@ struct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *fl4, res->fi = NULL; res->table = NULL; if (fl4->flowi4_oif && - (ipv4_is_multicast(fl4->daddr) || - !netif_index_is_l3_master(net, fl4->flowi4_oif))) { + (ipv4_is_multicast(fl4->daddr) || !fl4->flowi4_l3mdev)) { /* Apparently, routing tables are wrong. Assume, * that the destination is on link. * @@ -3392,7 +3407,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, if (fa->fa_slen == slen && fa->tb_id == fri.tb_id && - fa->fa_tos == fri.tos && + fa->fa_dscp == inet_dsfield_to_dscp(fri.tos) && fa->fa_info == res.fi && fa->fa_type == fri.type) { fri.offload = READ_ONCE(fa->offload); @@ -3535,13 +3550,6 @@ static struct ctl_table ipv4_route_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, - { - .procname = "min_adv_mss", - .data = &ip_rt_min_advmss, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { } }; @@ -3569,6 +3577,13 @@ static struct ctl_table ipv4_route_netns_table[] = { .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, + { + .procname = "min_adv_mss", + .data = &init_net.ipv4.ip_rt_min_advmss, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { }, }; @@ -3631,6 +3646,7 @@ static __net_init int netns_ip_rt_init(struct net *net) /* Set default value for namespaceified sysctls */ net->ipv4.ip_rt_min_pmtu = DEFAULT_MIN_PMTU; net->ipv4.ip_rt_mtu_expires = DEFAULT_MTU_EXPIRES; + net->ipv4.ip_rt_min_advmss = DEFAULT_MIN_ADVMSS; return 0; } @@ -3705,6 +3721,7 @@ int __init ip_rt_init(void) struct uncached_list *ul = &per_cpu(rt_uncached_list, cpu); INIT_LIST_HEAD(&ul->head); + INIT_LIST_HEAD(&ul->quarantine); spin_lock_init(&ul->lock); } #ifdef CONFIG_IP_ROUTE_CLASSID diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 97eb54774924..ad80d180b60b 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -589,6 +589,14 @@ static struct ctl_table ipv4_table[] = { }; static struct ctl_table ipv4_net_table[] = { + /* tcp_max_tw_buckets must be first in this table. */ + { + .procname = "tcp_max_tw_buckets", +/* .data = &init_net.ipv4.tcp_death_row.sysctl_max_tw_buckets, */ + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { .procname = "icmp_echo_ignore_all", .data = &init_net.ipv4.sysctl_icmp_echo_ignore_all, @@ -1000,13 +1008,6 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = SYSCTL_ZERO, .extra2 = &two, }, - { - .procname = "tcp_max_tw_buckets", - .data = &init_net.ipv4.tcp_death_row.sysctl_max_tw_buckets, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_max_syn_backlog", .data = &init_net.ipv4.sysctl_max_syn_backlog, @@ -1270,6 +1271,13 @@ static struct ctl_table ipv4_net_table[] = { .proc_handler = proc_dou8vec_minmax, .extra1 = SYSCTL_ONE, }, + { + .procname = "tcp_tso_rtt_log", + .data = &init_net.ipv4.sysctl_tcp_tso_rtt_log, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + }, { .procname = "tcp_min_rtt_wlen", .data = &init_net.ipv4.sysctl_tcp_min_rtt_wlen, @@ -1400,7 +1408,8 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) if (!table) goto err_alloc; - for (i = 0; i < ARRAY_SIZE(ipv4_net_table) - 1; i++) { + /* skip first entry (sysctl_max_tw_buckets) */ + for (i = 1; i < ARRAY_SIZE(ipv4_net_table) - 1; i++) { if (table[i].data) { /* Update the variables to point into * the current struct net @@ -1415,6 +1424,8 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) } } + table[0].data = &net->ipv4.tcp_death_row->sysctl_max_tw_buckets; + net->ipv4.ipv4_hdr = register_net_sysctl(net, "net/ipv4", table); if (!net->ipv4.ipv4_hdr) goto err_reg; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 28ff2a820f7c..cf18fbcbf123 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -688,7 +688,8 @@ static bool tcp_should_autocork(struct sock *sk, struct sk_buff *skb, return skb->len < size_goal && sock_net(sk)->ipv4.sysctl_tcp_autocorking && !tcp_rtx_queue_empty(sk) && - refcount_read(&sk->sk_wmem_alloc) > skb->truesize; + refcount_read(&sk->sk_wmem_alloc) > skb->truesize && + tcp_skb_can_collapse_to(skb); } void tcp_push(struct sock *sk, int flags, int mss_now, @@ -894,8 +895,7 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, return mss_now; /* Note : tcp_tso_autosize() will eventually split this later */ - new_size_goal = sk->sk_gso_max_size - 1 - MAX_TCP_HEADER; - new_size_goal = tcp_bound_to_half_wnd(tp, new_size_goal); + new_size_goal = tcp_bound_to_half_wnd(tp, sk->sk_gso_max_size); /* We try hard to avoid divides here */ size_goal = tp->gso_segs * mss_now; @@ -4434,6 +4434,73 @@ int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, const struct tcp_md5sig_key *ke } EXPORT_SYMBOL(tcp_md5_hash_key); +/* Called with rcu_read_lock() */ +enum skb_drop_reason +tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, + const void *saddr, const void *daddr, + int family, int dif, int sdif) +{ + /* + * This gets called for each TCP segment that arrives + * so we want to be efficient. + * We have 3 drop cases: + * o No MD5 hash and one expected. + * o MD5 hash and we're not expecting one. + * o MD5 hash and its wrong. + */ + const __u8 *hash_location = NULL; + struct tcp_md5sig_key *hash_expected; + const struct tcphdr *th = tcp_hdr(skb); + struct tcp_sock *tp = tcp_sk(sk); + int genhash, l3index; + u8 newhash[16]; + + /* sdif set, means packet ingressed via a device + * in an L3 domain and dif is set to the l3mdev + */ + l3index = sdif ? dif : 0; + + hash_expected = tcp_md5_do_lookup(sk, l3index, saddr, family); + hash_location = tcp_parse_md5sig_option(th); + + /* We've parsed the options - do we have a hash? */ + if (!hash_expected && !hash_location) + return SKB_NOT_DROPPED_YET; + + if (hash_expected && !hash_location) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); + return SKB_DROP_REASON_TCP_MD5NOTFOUND; + } + + if (!hash_expected && hash_location) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED); + return SKB_DROP_REASON_TCP_MD5UNEXPECTED; + } + + /* check the signature */ + genhash = tp->af_specific->calc_md5_hash(newhash, hash_expected, + NULL, skb); + + if (genhash || memcmp(hash_location, newhash, 16) != 0) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE); + if (family == AF_INET) { + net_info_ratelimited("MD5 Hash failed for (%pI4, %d)->(%pI4, %d)%s L3 index %d\n", + saddr, ntohs(th->source), + daddr, ntohs(th->dest), + genhash ? " tcp_v4_calc_md5_hash failed" + : "", l3index); + } else { + net_info_ratelimited("MD5 Hash %s for [%pI6c]:%u->[%pI6c]:%u L3 index %d\n", + genhash ? "failed" : "mismatch", + saddr, ntohs(th->source), + daddr, ntohs(th->dest), l3index); + } + return SKB_DROP_REASON_TCP_MD5FAILURE; + } + return SKB_NOT_DROPPED_YET; +} +EXPORT_SYMBOL(tcp_inbound_md5_hash); + #endif void tcp_done(struct sock *sk) diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c index ec5550089b4d..02e8626ccb27 100644 --- a/net/ipv4/tcp_bbr.c +++ b/net/ipv4/tcp_bbr.c @@ -1154,7 +1154,7 @@ static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = { .set_state = bbr_set_state, }; -BTF_SET_START(tcp_bbr_kfunc_ids) +BTF_SET_START(tcp_bbr_check_kfunc_ids) #ifdef CONFIG_X86 #ifdef CONFIG_DYNAMIC_FTRACE BTF_ID(func, bbr_init) @@ -1167,25 +1167,27 @@ BTF_ID(func, bbr_min_tso_segs) BTF_ID(func, bbr_set_state) #endif #endif -BTF_SET_END(tcp_bbr_kfunc_ids) +BTF_SET_END(tcp_bbr_check_kfunc_ids) -static DEFINE_KFUNC_BTF_ID_SET(&tcp_bbr_kfunc_ids, tcp_bbr_kfunc_btf_set); +static const struct btf_kfunc_id_set tcp_bbr_kfunc_set = { + .owner = THIS_MODULE, + .check_set = &tcp_bbr_check_kfunc_ids, +}; static int __init bbr_register(void) { int ret; BUILD_BUG_ON(sizeof(struct bbr) > ICSK_CA_PRIV_SIZE); - ret = tcp_register_congestion_control(&tcp_bbr_cong_ops); - if (ret) + + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &tcp_bbr_kfunc_set); + if (ret < 0) return ret; - register_kfunc_btf_id_set(&bpf_tcp_ca_kfunc_list, &tcp_bbr_kfunc_btf_set); - return 0; + return tcp_register_congestion_control(&tcp_bbr_cong_ops); } static void __exit bbr_unregister(void) { - unregister_kfunc_btf_id_set(&bpf_tcp_ca_kfunc_list, &tcp_bbr_kfunc_btf_set); tcp_unregister_congestion_control(&tcp_bbr_cong_ops); } diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 9b9b02052fd3..1cdcb4df0eb7 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -138,10 +138,9 @@ int tcp_bpf_sendmsg_redir(struct sock *sk, struct sk_msg *msg, struct sk_psock *psock = sk_psock_get(sk); int ret; - if (unlikely(!psock)) { - sk_msg_free(sk, msg); - return 0; - } + if (unlikely(!psock)) + return -EPIPE; + ret = ingress ? bpf_tcp_ingress(sk, psock, msg, bytes, flags) : tcp_bpf_push_locked(sk, msg, bytes, flags, false); sk_psock_put(sk, psock); @@ -335,7 +334,7 @@ static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock, cork = true; psock->cork = NULL; } - sk_msg_return(sk, msg, tosend); + sk_msg_return(sk, msg, msg->sg.size); release_sock(sk); ret = tcp_bpf_sendmsg_redir(sk_redir, msg, tosend, flags); @@ -375,8 +374,11 @@ static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock, } if (msg && msg->sg.data[msg->sg.start].page_link && - msg->sg.data[msg->sg.start].length) + msg->sg.data[msg->sg.start].length) { + if (eval == __SK_REDIRECT) + sk_mem_charge(sk, msg->sg.size); goto more_data; + } } return ret; } diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index db5831e6c136..dc95572163df 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -135,7 +135,6 @@ u32 tcp_ca_get_key_by_name(struct net *net, const char *name, bool *ecn_ca) return key; } -EXPORT_SYMBOL_GPL(tcp_ca_get_key_by_name); char *tcp_ca_get_name_by_key(u32 key, char *buffer) { @@ -151,7 +150,6 @@ char *tcp_ca_get_name_by_key(u32 key, char *buffer) return ret; } -EXPORT_SYMBOL_GPL(tcp_ca_get_name_by_key); /* Assign choice of congestion control. */ void tcp_assign_congestion_control(struct sock *sk) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index e07837e23b3f..24d562dd6225 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -485,7 +485,7 @@ static struct tcp_congestion_ops cubictcp __read_mostly = { .name = "cubic", }; -BTF_SET_START(tcp_cubic_kfunc_ids) +BTF_SET_START(tcp_cubic_check_kfunc_ids) #ifdef CONFIG_X86 #ifdef CONFIG_DYNAMIC_FTRACE BTF_ID(func, cubictcp_init) @@ -496,9 +496,12 @@ BTF_ID(func, cubictcp_cwnd_event) BTF_ID(func, cubictcp_acked) #endif #endif -BTF_SET_END(tcp_cubic_kfunc_ids) +BTF_SET_END(tcp_cubic_check_kfunc_ids) -static DEFINE_KFUNC_BTF_ID_SET(&tcp_cubic_kfunc_ids, tcp_cubic_kfunc_btf_set); +static const struct btf_kfunc_id_set tcp_cubic_kfunc_set = { + .owner = THIS_MODULE, + .check_set = &tcp_cubic_check_kfunc_ids, +}; static int __init cubictcp_register(void) { @@ -534,16 +537,14 @@ static int __init cubictcp_register(void) /* divide by bic_scale and by constant Srtt (100ms) */ do_div(cube_factor, bic_scale * 10); - ret = tcp_register_congestion_control(&cubictcp); - if (ret) + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &tcp_cubic_kfunc_set); + if (ret < 0) return ret; - register_kfunc_btf_id_set(&bpf_tcp_ca_kfunc_list, &tcp_cubic_kfunc_btf_set); - return 0; + return tcp_register_congestion_control(&cubictcp); } static void __exit cubictcp_unregister(void) { - unregister_kfunc_btf_id_set(&bpf_tcp_ca_kfunc_list, &tcp_cubic_kfunc_btf_set); tcp_unregister_congestion_control(&cubictcp); } diff --git a/net/ipv4/tcp_dctcp.c b/net/ipv4/tcp_dctcp.c index 0d7ab3cc7b61..1943a6630341 100644 --- a/net/ipv4/tcp_dctcp.c +++ b/net/ipv4/tcp_dctcp.c @@ -238,7 +238,7 @@ static struct tcp_congestion_ops dctcp_reno __read_mostly = { .name = "dctcp-reno", }; -BTF_SET_START(tcp_dctcp_kfunc_ids) +BTF_SET_START(tcp_dctcp_check_kfunc_ids) #ifdef CONFIG_X86 #ifdef CONFIG_DYNAMIC_FTRACE BTF_ID(func, dctcp_init) @@ -249,25 +249,27 @@ BTF_ID(func, dctcp_cwnd_undo) BTF_ID(func, dctcp_state) #endif #endif -BTF_SET_END(tcp_dctcp_kfunc_ids) +BTF_SET_END(tcp_dctcp_check_kfunc_ids) -static DEFINE_KFUNC_BTF_ID_SET(&tcp_dctcp_kfunc_ids, tcp_dctcp_kfunc_btf_set); +static const struct btf_kfunc_id_set tcp_dctcp_kfunc_set = { + .owner = THIS_MODULE, + .check_set = &tcp_dctcp_check_kfunc_ids, +}; static int __init dctcp_register(void) { int ret; BUILD_BUG_ON(sizeof(struct dctcp) > ICSK_CA_PRIV_SIZE); - ret = tcp_register_congestion_control(&dctcp); - if (ret) + + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &tcp_dctcp_kfunc_set); + if (ret < 0) return ret; - register_kfunc_btf_id_set(&bpf_tcp_ca_kfunc_list, &tcp_dctcp_kfunc_btf_set); - return 0; + return tcp_register_congestion_control(&dctcp); } static void __exit dctcp_unregister(void) { - unregister_kfunc_btf_id_set(&bpf_tcp_ca_kfunc_list, &tcp_dctcp_kfunc_btf_set); tcp_unregister_congestion_control(&dctcp); } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index bfe4112e000c..2088f93fa37b 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4684,10 +4684,16 @@ static bool tcp_ooo_try_coalesce(struct sock *sk, return res; } -static void tcp_drop(struct sock *sk, struct sk_buff *skb) +static void tcp_drop_reason(struct sock *sk, struct sk_buff *skb, + enum skb_drop_reason reason) { sk_drops_add(sk, skb); - __kfree_skb(skb); + kfree_skb_reason(skb, reason); +} + +static void tcp_drop(struct sock *sk, struct sk_buff *skb) +{ + tcp_drop_reason(sk, skb, SKB_DROP_REASON_NOT_SPECIFIED); } /* This one checks to see if we can put data from the @@ -4773,7 +4779,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) if (unlikely(tcp_try_rmem_schedule(sk, skb, skb->truesize))) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFODROP); sk->sk_data_ready(sk); - tcp_drop(sk, skb); + tcp_drop_reason(sk, skb, SKB_DROP_REASON_PROTO_MEM); return; } @@ -4836,7 +4842,8 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) /* All the bits are present. Drop. */ NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFOMERGE); - tcp_drop(sk, skb); + tcp_drop_reason(sk, skb, + SKB_DROP_REASON_TCP_OFOMERGE); skb = NULL; tcp_dsack_set(sk, seq, end_seq); goto add_sack; @@ -4855,7 +4862,8 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) TCP_SKB_CB(skb1)->end_seq); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFOMERGE); - tcp_drop(sk, skb1); + tcp_drop_reason(sk, skb1, + SKB_DROP_REASON_TCP_OFOMERGE); goto merge_right; } } else if (tcp_ooo_try_coalesce(sk, skb1, @@ -4883,7 +4891,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq, TCP_SKB_CB(skb1)->end_seq); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFOMERGE); - tcp_drop(sk, skb1); + tcp_drop_reason(sk, skb1, SKB_DROP_REASON_TCP_OFOMERGE); } /* If there is no skb after us, we are the last_skb ! */ if (!skb1) @@ -4982,6 +4990,7 @@ void tcp_data_ready(struct sock *sk) static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); + enum skb_drop_reason reason; bool fragstolen; int eaten; @@ -5000,6 +5009,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) skb_dst_drop(skb); __skb_pull(skb, tcp_hdr(skb)->doff * 4); + reason = SKB_DROP_REASON_NOT_SPECIFIED; tp->rx_opt.dsack = 0; /* Queue data for delivery to the user. @@ -5008,6 +5018,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) */ if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) { if (tcp_receive_window(tp) == 0) { + reason = SKB_DROP_REASON_TCP_ZEROWINDOW; NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPZEROWINDOWDROP); goto out_of_window; } @@ -5017,6 +5028,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) if (skb_queue_len(&sk->sk_receive_queue) == 0) sk_forced_mem_schedule(sk, skb->truesize); else if (tcp_try_rmem_schedule(sk, skb, skb->truesize)) { + reason = SKB_DROP_REASON_PROTO_MEM; NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPRCVQDROP); sk->sk_data_ready(sk); goto drop; @@ -5053,6 +5065,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) { tcp_rcv_spurious_retrans(sk, skb); /* A retransmit, 2nd most common case. Force an immediate ack. */ + reason = SKB_DROP_REASON_TCP_OLD_DATA; NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKLOST); tcp_dsack_set(sk, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq); @@ -5060,13 +5073,16 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) tcp_enter_quickack_mode(sk, TCP_MAX_QUICKACKS); inet_csk_schedule_ack(sk); drop: - tcp_drop(sk, skb); + tcp_drop_reason(sk, skb, reason); return; } /* Out of window. F.e. zero window probe. */ - if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt + tcp_receive_window(tp))) + if (!before(TCP_SKB_CB(skb)->seq, + tp->rcv_nxt + tcp_receive_window(tp))) { + reason = SKB_DROP_REASON_TCP_OVERWINDOW; goto out_of_window; + } if (before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) { /* Partial packet, seq < rcv_next < end_seq */ @@ -5076,6 +5092,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) * remembering D-SACK for its head made in previous line. */ if (!tcp_receive_window(tp)) { + reason = SKB_DROP_REASON_TCP_ZEROWINDOW; NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPZEROWINDOWDROP); goto out_of_window; } @@ -5781,6 +5798,7 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb, */ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb) { + enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED; const struct tcphdr *th = (const struct tcphdr *)skb->data; struct tcp_sock *tp = tcp_sk(sk); unsigned int len = skb->len; @@ -5869,6 +5887,7 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb) tp->rcv_rtt_last_tsecr = tp->rx_opt.rcv_tsecr; return; } else { /* Header too small */ + reason = SKB_DROP_REASON_PKT_TOO_SMALL; TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS); goto discard; } @@ -5924,8 +5943,10 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb) if (len < (th->doff << 2) || tcp_checksum_complete(skb)) goto csum_error; - if (!th->ack && !th->rst && !th->syn) + if (!th->ack && !th->rst && !th->syn) { + reason = SKB_DROP_REASON_TCP_FLAGS; goto discard; + } /* * Standard slow path. @@ -5951,12 +5972,13 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb) return; csum_error: + reason = SKB_DROP_REASON_TCP_CSUM; trace_tcp_bad_csum(skb); TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS); TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS); discard: - tcp_drop(sk, skb); + tcp_drop_reason(sk, skb, reason); } EXPORT_SYMBOL(tcp_rcv_established); @@ -6703,7 +6725,8 @@ static void tcp_openreq_init(struct request_sock *req, ireq->ir_num = ntohs(tcp_hdr(skb)->dest); ireq->ir_mark = inet_request_mark(sk, skb); #if IS_ENABLED(CONFIG_SMC) - ireq->smc_ok = rx_opt->smc_ok; + ireq->smc_ok = rx_opt->smc_ok && !(tcp_sk(sk)->smc_hs_congested && + tcp_sk(sk)->smc_hs_congested(sk)); #endif } @@ -6725,6 +6748,7 @@ struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops, ireq->ireq_state = TCP_NEW_SYN_RECV; write_pnet(&ireq->ireq_net, sock_net(sk_listener)); ireq->ireq_family = sk_listener->sk_family; + req->timeout = TCP_TIMEOUT_INIT; } return req; @@ -6941,9 +6965,10 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, sock_put(fastopen_sk); } else { tcp_rsk(req)->tfo_listener = false; - if (!want_cookie) - inet_csk_reqsk_queue_hash_add(sk, req, - tcp_timeout_init((struct sock *)req)); + if (!want_cookie) { + req->timeout = tcp_timeout_init((struct sock *)req); + inet_csk_reqsk_queue_hash_add(sk, req, req->timeout); + } af_ops->send_synack(sk, dst, &fl, req, &foc, !want_cookie ? TCP_SYNACK_NORMAL : TCP_SYNACK_COOKIE, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index fec656f5a39e..f9cec624068d 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -91,6 +91,8 @@ static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, struct inet_hashinfo tcp_hashinfo; EXPORT_SYMBOL(tcp_hashinfo); +static DEFINE_PER_CPU(struct sock *, ipv4_tcp_sk); + static u32 tcp_v4_init_seq(const struct sk_buff *skb) { return secure_tcp_seq(ip_hdr(skb)->daddr, @@ -206,7 +208,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) struct rtable *rt; int err; struct ip_options_rcu *inet_opt; - struct inet_timewait_death_row *tcp_death_row = &sock_net(sk)->ipv4.tcp_death_row; + struct inet_timewait_death_row *tcp_death_row = sock_net(sk)->ipv4.tcp_death_row; if (addr_len < sizeof(struct sockaddr_in)) return -EINVAL; @@ -810,7 +812,8 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) arg.tos = ip_hdr(skb)->tos; arg.uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL); local_bh_disable(); - ctl_sk = this_cpu_read(*net->ipv4.tcp_sk); + ctl_sk = this_cpu_read(ipv4_tcp_sk); + sock_net_set(ctl_sk, net); if (sk) { ctl_sk->sk_mark = (sk->sk_state == TCP_TIME_WAIT) ? inet_twsk(sk)->tw_mark : sk->sk_mark; @@ -825,6 +828,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) transmit_time); ctl_sk->sk_mark = 0; + sock_net_set(ctl_sk, &init_net); __TCP_INC_STATS(net, TCP_MIB_OUTSEGS); __TCP_INC_STATS(net, TCP_MIB_OUTRSTS); local_bh_enable(); @@ -908,7 +912,8 @@ static void tcp_v4_send_ack(const struct sock *sk, arg.tos = tos; arg.uid = sock_net_uid(net, sk_fullsock(sk) ? sk : NULL); local_bh_disable(); - ctl_sk = this_cpu_read(*net->ipv4.tcp_sk); + ctl_sk = this_cpu_read(ipv4_tcp_sk); + sock_net_set(ctl_sk, net); ctl_sk->sk_mark = (sk->sk_state == TCP_TIME_WAIT) ? inet_twsk(sk)->tw_mark : sk->sk_mark; ctl_sk->sk_priority = (sk->sk_state == TCP_TIME_WAIT) ? @@ -921,6 +926,7 @@ static void tcp_v4_send_ack(const struct sock *sk, transmit_time); ctl_sk->sk_mark = 0; + sock_net_set(ctl_sk, &init_net); __TCP_INC_STATS(net, TCP_MIB_OUTSEGS); local_bh_enable(); } @@ -1403,72 +1409,6 @@ EXPORT_SYMBOL(tcp_v4_md5_hash_skb); #endif -/* Called with rcu_read_lock() */ -static bool tcp_v4_inbound_md5_hash(const struct sock *sk, - const struct sk_buff *skb, - int dif, int sdif) -{ -#ifdef CONFIG_TCP_MD5SIG - /* - * This gets called for each TCP segment that arrives - * so we want to be efficient. - * We have 3 drop cases: - * o No MD5 hash and one expected. - * o MD5 hash and we're not expecting one. - * o MD5 hash and its wrong. - */ - const __u8 *hash_location = NULL; - struct tcp_md5sig_key *hash_expected; - const struct iphdr *iph = ip_hdr(skb); - const struct tcphdr *th = tcp_hdr(skb); - const union tcp_md5_addr *addr; - unsigned char newhash[16]; - int genhash, l3index; - - /* sdif set, means packet ingressed via a device - * in an L3 domain and dif is set to the l3mdev - */ - l3index = sdif ? dif : 0; - - addr = (union tcp_md5_addr *)&iph->saddr; - hash_expected = tcp_md5_do_lookup(sk, l3index, addr, AF_INET); - hash_location = tcp_parse_md5sig_option(th); - - /* We've parsed the options - do we have a hash? */ - if (!hash_expected && !hash_location) - return false; - - if (hash_expected && !hash_location) { - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); - return true; - } - - if (!hash_expected && hash_location) { - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED); - return true; - } - - /* Okay, so this is hash_expected and hash_location - - * so we need to calculate the checksum. - */ - genhash = tcp_v4_md5_hash_skb(newhash, - hash_expected, - NULL, skb); - - if (genhash || memcmp(hash_location, newhash, 16) != 0) { - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE); - net_info_ratelimited("MD5 Hash failed for (%pI4, %d)->(%pI4, %d)%s L3 index %d\n", - &iph->saddr, ntohs(th->source), - &iph->daddr, ntohs(th->dest), - genhash ? " tcp_v4_calc_md5_hash failed" - : "", l3index); - return true; - } - return false; -#endif - return false; -} - static void tcp_v4_init_req(struct request_sock *req, const struct sock *sk_listener, struct sk_buff *skb) @@ -1698,6 +1638,7 @@ INDIRECT_CALLABLE_DECLARE(struct dst_entry *ipv4_dst_check(struct dst_entry *, */ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) { + enum skb_drop_reason reason; struct sock *rsk; if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ @@ -1720,6 +1661,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) return 0; } + reason = SKB_DROP_REASON_NOT_SPECIFIED; if (tcp_checksum_complete(skb)) goto csum_err; @@ -1747,7 +1689,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) reset: tcp_v4_send_reset(rsk, skb); discard: - kfree_skb(skb); + kfree_skb_reason(skb, reason); /* Be careful here. If this function gets more complicated and * gcc suffers from register pressure on the x86, sk (in %ebx) * might be destroyed here. This current version compiles correctly, @@ -1756,6 +1698,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) return 0; csum_err: + reason = SKB_DROP_REASON_TCP_CSUM; trace_tcp_bad_csum(skb); TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS); TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS); @@ -1801,7 +1744,8 @@ int tcp_v4_early_demux(struct sk_buff *skb) return 0; } -bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb) +bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb, + enum skb_drop_reason *reason) { u32 limit, tail_gso_size, tail_gso_segs; struct skb_shared_info *shinfo; @@ -1827,6 +1771,7 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb) if (unlikely(tcp_checksum_complete(skb))) { bh_unlock_sock(sk); trace_tcp_bad_csum(skb); + *reason = SKB_DROP_REASON_TCP_CSUM; __TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS); __TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS); return true; @@ -1915,6 +1860,7 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb) if (unlikely(sk_add_backlog(sk, skb, limit))) { bh_unlock_sock(sk); + *reason = SKB_DROP_REASON_SOCKET_BACKLOG; __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPBACKLOGDROP); return true; } @@ -1965,13 +1911,13 @@ static void tcp_v4_fill_cb(struct sk_buff *skb, const struct iphdr *iph, int tcp_v4_rcv(struct sk_buff *skb) { struct net *net = dev_net(skb->dev); + enum skb_drop_reason drop_reason; int sdif = inet_sdif(skb); int dif = inet_iif(skb); const struct iphdr *iph; const struct tcphdr *th; bool refcounted; struct sock *sk; - int drop_reason; int ret; drop_reason = SKB_DROP_REASON_NOT_SPECIFIED; @@ -2019,7 +1965,10 @@ int tcp_v4_rcv(struct sk_buff *skb) struct sock *nsk; sk = req->rsk_listener; - if (unlikely(tcp_v4_inbound_md5_hash(sk, skb, dif, sdif))) { + drop_reason = tcp_inbound_md5_hash(sk, skb, + &iph->saddr, &iph->daddr, + AF_INET, dif, sdif); + if (unlikely(drop_reason)) { sk_drops_add(sk, skb); reqsk_put(req); goto discard_it; @@ -2051,6 +2000,8 @@ int tcp_v4_rcv(struct sk_buff *skb) iph = ip_hdr(skb); tcp_v4_fill_cb(skb, iph, th); nsk = tcp_check_req(sk, skb, req, false, &req_stolen); + } else { + drop_reason = SKB_DROP_REASON_SOCKET_FILTER; } if (!nsk) { reqsk_put(req); @@ -2086,10 +2037,14 @@ int tcp_v4_rcv(struct sk_buff *skb) } } - if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) + if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) { + drop_reason = SKB_DROP_REASON_XFRM_POLICY; goto discard_and_relse; + } - if (tcp_v4_inbound_md5_hash(sk, skb, dif, sdif)) + drop_reason = tcp_inbound_md5_hash(sk, skb, &iph->saddr, + &iph->daddr, AF_INET, dif, sdif); + if (drop_reason) goto discard_and_relse; nf_reset_ct(skb); @@ -2118,7 +2073,7 @@ int tcp_v4_rcv(struct sk_buff *skb) if (!sock_owned_by_user(sk)) { ret = tcp_v4_do_rcv(sk, skb); } else { - if (tcp_add_backlog(sk, skb)) + if (tcp_add_backlog(sk, skb, &drop_reason)) goto discard_and_relse; } bh_unlock_sock(sk); @@ -2160,6 +2115,7 @@ int tcp_v4_rcv(struct sk_buff *skb) do_time_wait: if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { + drop_reason = SKB_DROP_REASON_XFRM_POLICY; inet_twsk_put(inet_twsk(sk)); goto discard_it; } @@ -3111,41 +3067,18 @@ EXPORT_SYMBOL(tcp_prot); static void __net_exit tcp_sk_exit(struct net *net) { - int cpu; + struct inet_timewait_death_row *tcp_death_row = net->ipv4.tcp_death_row; if (net->ipv4.tcp_congestion_control) bpf_module_put(net->ipv4.tcp_congestion_control, net->ipv4.tcp_congestion_control->owner); - - for_each_possible_cpu(cpu) - inet_ctl_sock_destroy(*per_cpu_ptr(net->ipv4.tcp_sk, cpu)); - free_percpu(net->ipv4.tcp_sk); + if (refcount_dec_and_test(&tcp_death_row->tw_refcount)) + kfree(tcp_death_row); } static int __net_init tcp_sk_init(struct net *net) { - int res, cpu, cnt; - - net->ipv4.tcp_sk = alloc_percpu(struct sock *); - if (!net->ipv4.tcp_sk) - return -ENOMEM; - - for_each_possible_cpu(cpu) { - struct sock *sk; - - res = inet_ctl_sock_create(&sk, PF_INET, SOCK_RAW, - IPPROTO_TCP, net); - if (res) - goto fail; - sock_set_flag(sk, SOCK_USE_WRITE_QUEUE); - - /* Please enforce IP_DF and IPID==0 for RST and - * ACK sent in SYN-RECV and TIME-WAIT state. - */ - inet_sk(sk)->pmtudisc = IP_PMTUDISC_DO; - - *per_cpu_ptr(net->ipv4.tcp_sk, cpu) = sk; - } + int cnt; net->ipv4.sysctl_tcp_ecn = 2; net->ipv4.sysctl_tcp_ecn_fallback = 1; @@ -3172,9 +3105,13 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_tw_reuse = 2; net->ipv4.sysctl_tcp_no_ssthresh_metrics_save = 1; + net->ipv4.tcp_death_row = kzalloc(sizeof(struct inet_timewait_death_row), GFP_KERNEL); + if (!net->ipv4.tcp_death_row) + return -ENOMEM; + refcount_set(&net->ipv4.tcp_death_row->tw_refcount, 1); cnt = tcp_hashinfo.ehash_mask + 1; - net->ipv4.tcp_death_row.sysctl_max_tw_buckets = cnt / 2; - net->ipv4.tcp_death_row.hashinfo = &tcp_hashinfo; + net->ipv4.tcp_death_row->sysctl_max_tw_buckets = cnt / 2; + net->ipv4.tcp_death_row->hashinfo = &tcp_hashinfo; net->ipv4.sysctl_max_syn_backlog = max(128, cnt / 128); net->ipv4.sysctl_tcp_sack = 1; @@ -3200,6 +3137,7 @@ static int __net_init tcp_sk_init(struct net *net) /* rfc5961 challenge ack rate limiting */ net->ipv4.sysctl_tcp_challenge_ack_limit = 1000; net->ipv4.sysctl_tcp_min_tso_segs = 2; + net->ipv4.sysctl_tcp_tso_rtt_log = 9; /* 2^9 = 512 usec */ net->ipv4.sysctl_tcp_min_rtt_wlen = 300; net->ipv4.sysctl_tcp_autocorking = 1; net->ipv4.sysctl_tcp_invalid_ratelimit = HZ/2; @@ -3229,18 +3167,12 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.tcp_congestion_control = &tcp_reno; return 0; -fail: - tcp_sk_exit(net); - - return res; } static void __net_exit tcp_sk_exit_batch(struct list_head *net_exit_list) { struct net *net; - inet_twsk_purge(&tcp_hashinfo, AF_INET); - list_for_each_entry(net, net_exit_list, exit_list) tcp_fastopen_ctx_destroy(net); } @@ -3326,6 +3258,24 @@ static void __init bpf_iter_register(void) void __init tcp_v4_init(void) { + int cpu, res; + + for_each_possible_cpu(cpu) { + struct sock *sk; + + res = inet_ctl_sock_create(&sk, PF_INET, SOCK_RAW, + IPPROTO_TCP, &init_net); + if (res) + panic("Failed to create the TCP control socket.\n"); + sock_set_flag(sk, SOCK_USE_WRITE_QUEUE); + + /* Please enforce IP_DF and IPID==0 for RST and + * ACK sent in SYN-RECV and TIME-WAIT state. + */ + inet_sk(sk)->pmtudisc = IP_PMTUDISC_DO; + + per_cpu(ipv4_tcp_sk, cpu) = sk; + } if (register_pernet_subsys(&tcp_sk_ops)) panic("Failed to create the TCP control socket.\n"); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 7c2d3ac2363a..6366df7aaf2a 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -248,7 +248,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) const struct inet_connection_sock *icsk = inet_csk(sk); const struct tcp_sock *tp = tcp_sk(sk); struct inet_timewait_sock *tw; - struct inet_timewait_death_row *tcp_death_row = &sock_net(sk)->ipv4.tcp_death_row; + struct inet_timewait_death_row *tcp_death_row = sock_net(sk)->ipv4.tcp_death_row; tw = inet_twsk_alloc(sk, tcp_death_row, state); @@ -583,7 +583,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, * it can be estimated (approximately) * from another data. */ - tmp_opt.ts_recent_stamp = ktime_get_seconds() - ((TCP_TIMEOUT_INIT/HZ)<num_timeout); + tmp_opt.ts_recent_stamp = ktime_get_seconds() - reqsk_timeout(req, TCP_RTO_MAX) / HZ; paws_reject = tcp_paws_reject(&tmp_opt, th->rst); } } @@ -622,8 +622,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, !inet_rtx_syn_ack(sk, req)) { unsigned long expires = jiffies; - expires += min(TCP_TIMEOUT_INIT << req->num_timeout, - TCP_RTO_MAX); + expires += reqsk_timeout(req, TCP_RTO_MAX); if (!fastopen) mod_timer_pending(&req->rsk_timer, expires); else diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 5079832af5c1..9ede847f4199 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1253,7 +1253,7 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, tp = tcp_sk(sk); prior_wstamp = tp->tcp_wstamp_ns; tp->tcp_wstamp_ns = max(tp->tcp_wstamp_ns, tp->tcp_clock_cache); - skb->skb_mstamp_ns = tp->tcp_wstamp_ns; + skb_set_delivery_time(skb, tp->tcp_wstamp_ns, true); if (clone_it) { oskb = skb; @@ -1589,7 +1589,7 @@ int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue, skb_split(skb, buff, len); - buff->tstamp = skb->tstamp; + skb_set_delivery_time(buff, skb->tstamp, true); tcp_fragment_tstamp(skb, buff); old_factor = tcp_skb_pcount(skb); @@ -1951,25 +1951,34 @@ static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp, } /* Return how many segs we'd like on a TSO packet, - * to send one TSO packet per ms + * depending on current pacing rate, and how close the peer is. + * + * Rationale is: + * - For close peers, we rather send bigger packets to reduce + * cpu costs, because occasional losses will be repaired fast. + * - For long distance/rtt flows, we would like to get ACK clocking + * with 1 ACK per ms. + * + * Use min_rtt to help adapt TSO burst size, with smaller min_rtt resulting + * in bigger TSO bursts. We we cut the RTT-based allowance in half + * for every 2^9 usec (aka 512 us) of RTT, so that the RTT-based allowance + * is below 1500 bytes after 6 * ~500 usec = 3ms. */ static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now, int min_tso_segs) { - u32 bytes, segs; + unsigned long bytes; + u32 r; - bytes = min_t(unsigned long, - sk->sk_pacing_rate >> READ_ONCE(sk->sk_pacing_shift), - sk->sk_gso_max_size - 1 - MAX_TCP_HEADER); + bytes = sk->sk_pacing_rate >> READ_ONCE(sk->sk_pacing_shift); - /* Goal is to send at least one packet per ms, - * not one big TSO packet every 100 ms. - * This preserves ACK clocking and is consistent - * with tcp_tso_should_defer() heuristic. - */ - segs = max_t(u32, bytes / mss_now, min_tso_segs); + r = tcp_min_rtt(tcp_sk(sk)) >> sock_net(sk)->ipv4.sysctl_tcp_tso_rtt_log; + if (r < BITS_PER_TYPE(sk->sk_gso_max_size)) + bytes += sk->sk_gso_max_size >> r; - return segs; + bytes = min_t(unsigned long, bytes, sk->sk_gso_max_size); + + return max_t(u32, bytes / mss_now, min_tso_segs); } /* Return the number of segments we want in the skb we are transmitting. @@ -2616,7 +2625,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, if (unlikely(tp->repair) && tp->repair_queue == TCP_SEND_QUEUE) { /* "skb_mstamp_ns" is used as a start point for the retransmit timer */ - skb->skb_mstamp_ns = tp->tcp_wstamp_ns = tp->tcp_clock_cache; + tp->tcp_wstamp_ns = tp->tcp_clock_cache; + skb_set_delivery_time(skb, tp->tcp_wstamp_ns, true); list_move_tail(&skb->tcp_tsorted_anchor, &tp->tsorted_sent_queue); tcp_init_tso_segs(skb, mss_now); goto repair; /* Skip network transmission */ @@ -3541,11 +3551,12 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, now = tcp_clock_ns(); #ifdef CONFIG_SYN_COOKIES if (unlikely(synack_type == TCP_SYNACK_COOKIE && ireq->tstamp_ok)) - skb->skb_mstamp_ns = cookie_init_timestamp(req, now); + skb_set_delivery_time(skb, cookie_init_timestamp(req, now), + true); else #endif { - skb->skb_mstamp_ns = now; + skb_set_delivery_time(skb, now, true); if (!tcp_rsk(req)->snt_synack) /* Timestamp first SYNACK */ tcp_rsk(req)->snt_synack = tcp_skb_timestamp_us(skb); } @@ -3594,7 +3605,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, bpf_skops_write_hdr_opt((struct sock *)sk, skb, req, syn_skb, synack_type, &opts); - skb->skb_mstamp_ns = now; + skb_set_delivery_time(skb, now, true); tcp_add_tx_delay(skb, tp); return skb; @@ -3719,6 +3730,7 @@ static void tcp_connect_queue_skb(struct sock *sk, struct sk_buff *skb) */ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn) { + struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); struct tcp_fastopen_request *fo = tp->fastopen_req; int space, err = 0; @@ -3733,8 +3745,10 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn) * private TCP options. The cost is reduced data space in SYN :( */ tp->rx_opt.mss_clamp = tcp_mss_clamp(tp, tp->rx_opt.mss_clamp); + /* Sync mss_cache after updating the mss_clamp */ + tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); - space = __tcp_mtu_to_mss(sk, inet_csk(sk)->icsk_pmtu_cookie) - + space = __tcp_mtu_to_mss(sk, icsk->icsk_pmtu_cookie) - MAX_TCP_OPTION_SPACE; space = min_t(size_t, space, fo->size); @@ -3771,7 +3785,7 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn) err = tcp_transmit_skb(sk, syn_data, 1, sk->sk_allocation); - syn->skb_mstamp_ns = syn_data->skb_mstamp_ns; + skb_set_delivery_time(syn, syn_data->skb_mstamp_ns, true); /* Now full SYN+DATA was cloned and sent (or not), * remove the SYN from the original skb (syn_data) @@ -4092,7 +4106,9 @@ int tcp_rtx_synack(const struct sock *sk, struct request_sock *req) struct flowi fl; int res; - tcp_rsk(req)->txhash = net_tx_rndhash(); + /* Paired with WRITE_ONCE() in sock_setsockopt() */ + if (READ_ONCE(sk->sk_txrehash) == SOCK_TXREHASH_ENABLED) + tcp_rsk(req)->txhash = net_tx_rndhash(); res = af_ops->send_synack(sk, NULL, &fl, req, NULL, TCP_SYNACK_NORMAL, NULL); if (!res) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 090360939401..6b4d8361560f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2093,16 +2093,20 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) rc = __udp_enqueue_schedule_skb(sk, skb); if (rc < 0) { int is_udplite = IS_UDPLITE(sk); + int drop_reason; /* Note that an ENOMEM error is charged twice */ - if (rc == -ENOMEM) + if (rc == -ENOMEM) { UDP_INC_STATS(sock_net(sk), UDP_MIB_RCVBUFERRORS, is_udplite); - else + drop_reason = SKB_DROP_REASON_SOCKET_RCVBUFF; + } else { UDP_INC_STATS(sock_net(sk), UDP_MIB_MEMERRORS, is_udplite); + drop_reason = SKB_DROP_REASON_PROTO_MEM; + } UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite); - kfree_skb(skb); + kfree_skb_reason(skb, drop_reason); trace_udp_fail_queue_rcv_skb(rc, sk); return -1; } @@ -2120,14 +2124,17 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) */ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) { + int drop_reason = SKB_DROP_REASON_NOT_SPECIFIED; struct udp_sock *up = udp_sk(sk); int is_udplite = IS_UDPLITE(sk); /* * Charge it to the socket, dropping if the queue is full. */ - if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) + if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) { + drop_reason = SKB_DROP_REASON_XFRM_POLICY; goto drop; + } nf_reset_ct(skb); if (static_branch_unlikely(&udp_encap_needed_key) && up->encap_type) { @@ -2204,8 +2211,10 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) udp_lib_checksum_complete(skb)) goto csum_error; - if (sk_filter_trim_cap(sk, skb, sizeof(struct udphdr))) + if (sk_filter_trim_cap(sk, skb, sizeof(struct udphdr))) { + drop_reason = SKB_DROP_REASON_SOCKET_FILTER; goto drop; + } udp_csum_pull_header(skb); @@ -2213,11 +2222,12 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) return __udp_queue_rcv_skb(sk, skb); csum_error: + drop_reason = SKB_DROP_REASON_UDP_CSUM; __UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite); drop: __UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite); atomic_inc(&sk->sk_drops); - kfree_skb(skb); + kfree_skb_reason(skb, drop_reason); return -1; } diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 9e83bcb6bc99..6fde0b184791 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -28,13 +28,11 @@ static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, memset(fl4, 0, sizeof(*fl4)); fl4->daddr = daddr->a4; fl4->flowi4_tos = tos; - fl4->flowi4_oif = l3mdev_master_ifindex_by_index(net, oif); + fl4->flowi4_l3mdev = l3mdev_master_ifindex_by_index(net, oif); fl4->flowi4_mark = mark; if (saddr) fl4->saddr = saddr->a4; - fl4->flowi4_flags = FLOWI_FLAG_SKIP_NH_OIF; - rt = __ip_route_output_key(net, fl4); if (!IS_ERR(rt)) return &rt->dst; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index f908e2fd30b2..b22504176588 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -146,18 +146,11 @@ static int ipv6_generate_stable_address(struct in6_addr *addr, #define IN6_ADDR_HSIZE_SHIFT 8 #define IN6_ADDR_HSIZE (1 << IN6_ADDR_HSIZE_SHIFT) -/* - * Configured unicast address hash table - */ -static struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE]; -static DEFINE_SPINLOCK(addrconf_hash_lock); -static void addrconf_verify(void); -static void addrconf_verify_rtnl(void); -static void addrconf_verify_work(struct work_struct *); +static void addrconf_verify(struct net *net); +static void addrconf_verify_rtnl(struct net *net); static struct workqueue_struct *addrconf_wq; -static DECLARE_DELAYED_WORK(addr_chk_work, addrconf_verify_work); static void addrconf_join_anycast(struct inet6_ifaddr *ifp); static void addrconf_leave_anycast(struct inet6_ifaddr *ifp); @@ -379,7 +372,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev) ASSERT_RTNL(); - if (dev->mtu < IPV6_MIN_MTU) + if (dev->mtu < IPV6_MIN_MTU && dev != blackhole_netdev) return ERR_PTR(-EINVAL); ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL); @@ -416,12 +409,13 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev) return ERR_PTR(err); } - if (snmp6_register_dev(ndev) < 0) { - netdev_dbg(dev, "%s: cannot create /proc/net/dev_snmp6/%s\n", - __func__, dev->name); - goto err_release; + if (dev != blackhole_netdev) { + if (snmp6_register_dev(ndev) < 0) { + netdev_dbg(dev, "%s: cannot create /proc/net/dev_snmp6/%s\n", + __func__, dev->name); + goto err_release; + } } - /* One reference from device. */ refcount_set(&ndev->refcnt, 1); @@ -452,25 +446,28 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev) ipv6_mc_init_dev(ndev); ndev->tstamp = jiffies; - err = addrconf_sysctl_register(ndev); - if (err) { - ipv6_mc_destroy_dev(ndev); - snmp6_unregister_dev(ndev); - goto err_release; + if (dev != blackhole_netdev) { + err = addrconf_sysctl_register(ndev); + if (err) { + ipv6_mc_destroy_dev(ndev); + snmp6_unregister_dev(ndev); + goto err_release; + } } /* protected by rtnl_lock */ rcu_assign_pointer(dev->ip6_ptr, ndev); - /* Join interface-local all-node multicast group */ - ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allnodes); + if (dev != blackhole_netdev) { + /* Join interface-local all-node multicast group */ + ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allnodes); - /* Join all-node multicast group */ - ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes); - - /* Join all-router multicast group if forwarding is set */ - if (ndev->cnf.forwarding && (dev->flags & IFF_MULTICAST)) - ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); + /* Join all-node multicast group */ + ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes); + /* Join all-router multicast group if forwarding is set */ + if (ndev->cnf.forwarding && (dev->flags & IFF_MULTICAST)) + ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); + } return ndev; err_release: @@ -554,7 +551,7 @@ static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex, #ifdef CONFIG_IPV6_MROUTE if ((all || type == NETCONFA_MC_FORWARDING) && nla_put_s32(skb, NETCONFA_MC_FORWARDING, - devconf->mc_forwarding) < 0) + atomic_read(&devconf->mc_forwarding)) < 0) goto nla_put_failure; #endif if ((all || type == NETCONFA_PROXY_NEIGH) && @@ -1011,9 +1008,7 @@ static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, { struct inet6_ifaddr *ifp; - hlist_for_each_entry(ifp, &inet6_addr_lst[hash], addr_lst) { - if (!net_eq(dev_net(ifp->idev->dev), net)) - continue; + hlist_for_each_entry(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) { if (ipv6_addr_equal(&ifp->addr, addr)) { if (!dev || ifp->idev->dev == dev) return true; @@ -1024,20 +1019,21 @@ static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, static int ipv6_add_addr_hash(struct net_device *dev, struct inet6_ifaddr *ifa) { - unsigned int hash = inet6_addr_hash(dev_net(dev), &ifa->addr); + struct net *net = dev_net(dev); + unsigned int hash = inet6_addr_hash(net, &ifa->addr); int err = 0; - spin_lock(&addrconf_hash_lock); + spin_lock(&net->ipv6.addrconf_hash_lock); /* Ignore adding duplicate addresses on an interface */ - if (ipv6_chk_same_addr(dev_net(dev), &ifa->addr, dev, hash)) { + if (ipv6_chk_same_addr(net, &ifa->addr, dev, hash)) { netdev_dbg(dev, "ipv6_add_addr: already assigned\n"); err = -EEXIST; } else { - hlist_add_head_rcu(&ifa->addr_lst, &inet6_addr_lst[hash]); + hlist_add_head_rcu(&ifa->addr_lst, &net->ipv6.inet6_addr_lst[hash]); } - spin_unlock(&addrconf_hash_lock); + spin_unlock(&net->ipv6.addrconf_hash_lock); return err; } @@ -1119,6 +1115,7 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg, ifa->prefix_len = cfg->plen; ifa->rt_priority = cfg->rt_priority; ifa->flags = cfg->ifa_flags; + ifa->ifa_proto = cfg->ifa_proto; /* No need to add the TENTATIVE flag for addresses with NODAD */ if (!(cfg->ifa_flags & IFA_F_NODAD)) ifa->flags |= IFA_F_TENTATIVE; @@ -1261,9 +1258,10 @@ cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires, static void ipv6_del_addr(struct inet6_ifaddr *ifp) { - int state; enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_NOP; + struct net *net = dev_net(ifp->idev->dev); unsigned long expires; + int state; ASSERT_RTNL(); @@ -1275,9 +1273,9 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) if (state == INET6_IFADDR_STATE_DEAD) goto out; - spin_lock_bh(&addrconf_hash_lock); + spin_lock_bh(&net->ipv6.addrconf_hash_lock); hlist_del_init_rcu(&ifp->addr_lst); - spin_unlock_bh(&addrconf_hash_lock); + spin_unlock_bh(&net->ipv6.addrconf_hash_lock); write_lock_bh(&ifp->idev->lock); @@ -1920,10 +1918,8 @@ __ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, if (skip_dev_check) dev = NULL; - hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { + hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) { ndev = ifp->idev->dev; - if (!net_eq(dev_net(ndev), net)) - continue; if (l3mdev_master_dev_rcu(ndev) != l3mdev) continue; @@ -2027,9 +2023,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add struct inet6_ifaddr *ifp, *result = NULL; rcu_read_lock(); - hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { - if (!net_eq(dev_net(ifp->idev->dev), net)) - continue; + hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) { if (ipv6_addr_equal(&ifp->addr, addr)) { if (!dev || ifp->idev->dev == dev || !(ifp->scope&(IFA_LINK|IFA_HOST) || strict)) { @@ -2096,7 +2090,7 @@ static int addrconf_dad_end(struct inet6_ifaddr *ifp) void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp) { struct inet6_dev *idev = ifp->idev; - struct net *net = dev_net(ifp->idev->dev); + struct net *net = dev_net(idev->dev); if (addrconf_dad_end(ifp)) { in6_ifa_put(ifp); @@ -2600,6 +2594,7 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, .valid_lft = valid_lft, .preferred_lft = prefered_lft, .scope = addr_type & IPV6_ADDR_SCOPE_MASK, + .ifa_proto = IFAPROT_KERNEL_RA }; #ifdef CONFIG_IPV6_OPTIMISTIC_DAD @@ -2675,7 +2670,7 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, create, now); in6_ifa_put(ifp); - addrconf_verify(); + addrconf_verify(net); } return 0; @@ -2987,7 +2982,7 @@ static int inet6_addr_add(struct net *net, int ifindex, manage_tempaddrs(idev, ifp, cfg->valid_lft, cfg->preferred_lft, true, jiffies); in6_ifa_put(ifp); - addrconf_verify_rtnl(); + addrconf_verify_rtnl(net); return 0; } else if (cfg->ifa_flags & IFA_F_MCAUTOJOIN) { ipv6_mc_config(net->ipv6.mc_autojoin_sk, false, @@ -3027,7 +3022,7 @@ static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags, manage_tempaddrs(idev, ifp, 0, 0, false, jiffies); ipv6_del_addr(ifp); - addrconf_verify_rtnl(); + addrconf_verify_rtnl(net); if (ipv6_addr_is_multicast(pfx)) { ipv6_mc_config(net->ipv6.mc_autojoin_sk, false, pfx, dev->ifindex); @@ -3084,7 +3079,7 @@ int addrconf_del_ifaddr(struct net *net, void __user *arg) } static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr, - int plen, int scope) + int plen, int scope, u8 proto) { struct inet6_ifaddr *ifp; struct ifa6_config cfg = { @@ -3093,7 +3088,8 @@ static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr, .ifa_flags = IFA_F_PERMANENT, .valid_lft = INFINITY_LIFE_TIME, .preferred_lft = INFINITY_LIFE_TIME, - .scope = scope + .scope = scope, + .ifa_proto = proto }; ifp = ipv6_add_addr(idev, &cfg, true, NULL); @@ -3138,7 +3134,7 @@ static void add_v4_addrs(struct inet6_dev *idev) } if (addr.s6_addr32[3]) { - add_addr(idev, &addr, plen, scope); + add_addr(idev, &addr, plen, scope, IFAPROT_UNSPEC); addrconf_prefix_route(&addr, plen, 0, idev->dev, 0, pflags, GFP_KERNEL); return; @@ -3161,7 +3157,8 @@ static void add_v4_addrs(struct inet6_dev *idev) flag |= IFA_HOST; } - add_addr(idev, &addr, plen, flag); + add_addr(idev, &addr, plen, flag, + IFAPROT_UNSPEC); addrconf_prefix_route(&addr, plen, 0, idev->dev, 0, pflags, GFP_KERNEL); } @@ -3184,7 +3181,7 @@ static void init_loopback(struct net_device *dev) return; } - add_addr(idev, &in6addr_loopback, 128, IFA_HOST); + add_addr(idev, &in6addr_loopback, 128, IFA_HOST, IFAPROT_KERNEL_LO); } void addrconf_add_linklocal(struct inet6_dev *idev, @@ -3196,7 +3193,8 @@ void addrconf_add_linklocal(struct inet6_dev *idev, .ifa_flags = flags | IFA_F_PERMANENT, .valid_lft = INFINITY_LIFE_TIME, .preferred_lft = INFINITY_LIFE_TIME, - .scope = IFA_LINK + .scope = IFA_LINK, + .ifa_proto = IFAPROT_KERNEL_LL }; struct inet6_ifaddr *ifp; @@ -3773,9 +3771,9 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister) /* Step 2: clear hash table */ for (i = 0; i < IN6_ADDR_HSIZE; i++) { - struct hlist_head *h = &inet6_addr_lst[i]; + struct hlist_head *h = &net->ipv6.inet6_addr_lst[i]; - spin_lock_bh(&addrconf_hash_lock); + spin_lock_bh(&net->ipv6.addrconf_hash_lock); restart: hlist_for_each_entry_rcu(ifa, h, addr_lst) { if (ifa->idev == idev) { @@ -3791,7 +3789,7 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister) } } } - spin_unlock_bh(&addrconf_hash_lock); + spin_unlock_bh(&net->ipv6.addrconf_hash_lock); } write_lock_bh(&idev->lock); @@ -4250,7 +4248,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id, * before this temporary address becomes deprecated. */ if (ifp->flags & IFA_F_TEMPORARY) - addrconf_verify_rtnl(); + addrconf_verify_rtnl(dev_net(dev)); } static void addrconf_dad_run(struct inet6_dev *idev, bool restart) @@ -4292,10 +4290,8 @@ static struct inet6_ifaddr *if6_get_first(struct seq_file *seq, loff_t pos) } for (; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) { - hlist_for_each_entry_rcu(ifa, &inet6_addr_lst[state->bucket], + hlist_for_each_entry_rcu(ifa, &net->ipv6.inet6_addr_lst[state->bucket], addr_lst) { - if (!net_eq(dev_net(ifa->idev->dev), net)) - continue; /* sync with offset */ if (p < state->offset) { p++; @@ -4318,8 +4314,6 @@ static struct inet6_ifaddr *if6_get_next(struct seq_file *seq, struct net *net = seq_file_net(seq); hlist_for_each_entry_continue_rcu(ifa, addr_lst) { - if (!net_eq(dev_net(ifa->idev->dev), net)) - continue; state->offset++; return ifa; } @@ -4327,9 +4321,7 @@ static struct inet6_ifaddr *if6_get_next(struct seq_file *seq, state->offset = 0; while (++state->bucket < IN6_ADDR_HSIZE) { hlist_for_each_entry_rcu(ifa, - &inet6_addr_lst[state->bucket], addr_lst) { - if (!net_eq(dev_net(ifa->idev->dev), net)) - continue; + &net->ipv6.inet6_addr_lst[state->bucket], addr_lst) { return ifa; } } @@ -4417,9 +4409,7 @@ int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr) int ret = 0; rcu_read_lock(); - hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { - if (!net_eq(dev_net(ifp->idev->dev), net)) - continue; + hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) { if (ipv6_addr_equal(&ifp->addr, addr) && (ifp->flags & IFA_F_HOMEADDRESS)) { ret = 1; @@ -4457,9 +4447,7 @@ int ipv6_chk_rpl_srh_loop(struct net *net, const struct in6_addr *segs, hash = inet6_addr_hash(net, addr); hash_found = false; - hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { - if (!net_eq(dev_net(ifp->idev->dev), net)) - continue; + hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) { if (ipv6_addr_equal(&ifp->addr, addr)) { hash_found = true; @@ -4488,7 +4476,7 @@ int ipv6_chk_rpl_srh_loop(struct net *net, const struct in6_addr *segs, * Periodic address status verification */ -static void addrconf_verify_rtnl(void) +static void addrconf_verify_rtnl(struct net *net) { unsigned long now, next, next_sec, next_sched; struct inet6_ifaddr *ifp; @@ -4500,11 +4488,11 @@ static void addrconf_verify_rtnl(void) now = jiffies; next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); - cancel_delayed_work(&addr_chk_work); + cancel_delayed_work(&net->ipv6.addr_chk_work); for (i = 0; i < IN6_ADDR_HSIZE; i++) { restart: - hlist_for_each_entry_rcu_bh(ifp, &inet6_addr_lst[i], addr_lst) { + hlist_for_each_entry_rcu_bh(ifp, &net->ipv6.inet6_addr_lst[i], addr_lst) { unsigned long age; /* When setting preferred_lft to a value not zero or @@ -4603,20 +4591,23 @@ static void addrconf_verify_rtnl(void) pr_debug("now = %lu, schedule = %lu, rounded schedule = %lu => %lu\n", now, next, next_sec, next_sched); - mod_delayed_work(addrconf_wq, &addr_chk_work, next_sched - now); + mod_delayed_work(addrconf_wq, &net->ipv6.addr_chk_work, next_sched - now); rcu_read_unlock_bh(); } static void addrconf_verify_work(struct work_struct *w) { + struct net *net = container_of(to_delayed_work(w), struct net, + ipv6.addr_chk_work); + rtnl_lock(); - addrconf_verify_rtnl(); + addrconf_verify_rtnl(net); rtnl_unlock(); } -static void addrconf_verify(void) +static void addrconf_verify(struct net *net) { - mod_delayed_work(addrconf_wq, &addr_chk_work, 0); + mod_delayed_work(addrconf_wq, &net->ipv6.addr_chk_work, 0); } static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local, @@ -4645,6 +4636,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = { [IFA_FLAGS] = { .len = sizeof(u32) }, [IFA_RT_PRIORITY] = { .len = sizeof(u32) }, [IFA_TARGET_NETNSID] = { .type = NLA_S32 }, + [IFA_PROTO] = { .type = NLA_U8 }, }; static int @@ -4712,7 +4704,8 @@ static int modify_prefix_route(struct inet6_ifaddr *ifp, return 0; } -static int inet6_addr_modify(struct inet6_ifaddr *ifp, struct ifa6_config *cfg) +static int inet6_addr_modify(struct net *net, struct inet6_ifaddr *ifp, + struct ifa6_config *cfg) { u32 flags; clock_t expires; @@ -4769,6 +4762,7 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, struct ifa6_config *cfg) ifp->tstamp = jiffies; ifp->valid_lft = cfg->valid_lft; ifp->prefered_lft = cfg->preferred_lft; + ifp->ifa_proto = cfg->ifa_proto; if (cfg->rt_priority && cfg->rt_priority != ifp->rt_priority) ifp->rt_priority = cfg->rt_priority; @@ -4826,7 +4820,7 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, struct ifa6_config *cfg) jiffies); } - addrconf_verify_rtnl(); + addrconf_verify_rtnl(net); return 0; } @@ -4862,6 +4856,9 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, if (tb[IFA_RT_PRIORITY]) cfg.rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]); + if (tb[IFA_PROTO]) + cfg.ifa_proto = nla_get_u8(tb[IFA_PROTO]); + cfg.valid_lft = INFINITY_LIFE_TIME; cfg.preferred_lft = INFINITY_LIFE_TIME; @@ -4913,7 +4910,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, !(nlh->nlmsg_flags & NLM_F_REPLACE)) err = -EEXIST; else - err = inet6_addr_modify(ifa, &cfg); + err = inet6_addr_modify(net, ifa, &cfg); in6_ifa_put(ifa); @@ -4965,6 +4962,7 @@ static inline int inet6_ifaddr_msgsize(void) + nla_total_size(16) /* IFA_ADDRESS */ + nla_total_size(sizeof(struct ifa_cacheinfo)) + nla_total_size(4) /* IFA_FLAGS */ + + nla_total_size(1) /* IFA_PROTO */ + nla_total_size(4) /* IFA_RT_PRIORITY */; } @@ -5044,6 +5042,10 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0) goto error; + if (ifa->ifa_proto && + nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) + goto error; + nlmsg_end(skb, nlh); return 0; @@ -5539,7 +5541,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_USE_OPTIMISTIC] = cnf->use_optimistic; #endif #ifdef CONFIG_IPV6_MROUTE - array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding; + array[DEVCONF_MC_FORWARDING] = atomic_read(&cnf->mc_forwarding); #endif array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6; array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad; @@ -5800,7 +5802,7 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token, write_unlock_bh(&idev->lock); inet6_ifinfo_notify(RTM_NEWLINK, idev); - addrconf_verify_rtnl(); + addrconf_verify_rtnl(dev_net(dev)); return 0; } @@ -7117,6 +7119,14 @@ static int __net_init addrconf_init_net(struct net *net) int err = -ENOMEM; struct ipv6_devconf *all, *dflt; + spin_lock_init(&net->ipv6.addrconf_hash_lock); + INIT_DEFERRABLE_WORK(&net->ipv6.addr_chk_work, addrconf_verify_work); + net->ipv6.inet6_addr_lst = kcalloc(IN6_ADDR_HSIZE, + sizeof(struct hlist_head), + GFP_KERNEL); + if (!net->ipv6.inet6_addr_lst) + goto err_alloc_addr; + all = kmemdup(&ipv6_devconf, sizeof(ipv6_devconf), GFP_KERNEL); if (!all) goto err_alloc_all; @@ -7178,11 +7188,15 @@ static int __net_init addrconf_init_net(struct net *net) err_alloc_dflt: kfree(all); err_alloc_all: + kfree(net->ipv6.inet6_addr_lst); +err_alloc_addr: return err; } static void __net_exit addrconf_exit_net(struct net *net) { + int i; + #ifdef CONFIG_SYSCTL __addrconf_sysctl_unregister(net, net->ipv6.devconf_dflt, NETCONFA_IFINDEX_DEFAULT); @@ -7190,7 +7204,19 @@ static void __net_exit addrconf_exit_net(struct net *net) NETCONFA_IFINDEX_ALL); #endif kfree(net->ipv6.devconf_dflt); + net->ipv6.devconf_dflt = NULL; kfree(net->ipv6.devconf_all); + net->ipv6.devconf_all = NULL; + + cancel_delayed_work_sync(&net->ipv6.addr_chk_work); + /* + * Check hash table, then free it. + */ + for (i = 0; i < IN6_ADDR_HSIZE; i++) + WARN_ON_ONCE(!hlist_empty(&net->ipv6.inet6_addr_lst[i])); + + kfree(net->ipv6.inet6_addr_lst); + net->ipv6.inet6_addr_lst = NULL; } static struct pernet_operations addrconf_ops = { @@ -7213,7 +7239,7 @@ static struct rtnl_af_ops inet6_ops __read_mostly = { int __init addrconf_init(void) { struct inet6_dev *idev; - int i, err; + int err; err = ipv6_addr_label_init(); if (err < 0) { @@ -7232,26 +7258,8 @@ int __init addrconf_init(void) goto out_nowq; } - /* The addrconf netdev notifier requires that loopback_dev - * has it's ipv6 private information allocated and setup - * before it can bring up and give link-local addresses - * to other devices which are up. - * - * Unfortunately, loopback_dev is not necessarily the first - * entry in the global dev_base list of net devices. In fact, - * it is likely to be the very last entry on that list. - * So this causes the notifier registry below to try and - * give link-local addresses to all devices besides loopback_dev - * first, then loopback_dev, which cases all the non-loopback_dev - * devices to fail to get a link-local address. - * - * So, as a temporary fix, allocate the ipv6 structure for - * loopback_dev first by hand. - * Longer term, all of the dependencies ipv6 has upon the loopback - * device and it being up should be removed. - */ rtnl_lock(); - idev = ipv6_add_dev(init_net.loopback_dev); + idev = ipv6_add_dev(blackhole_netdev); rtnl_unlock(); if (IS_ERR(idev)) { err = PTR_ERR(idev); @@ -7260,12 +7268,9 @@ int __init addrconf_init(void) ip6_route_init_special_entries(); - for (i = 0; i < IN6_ADDR_HSIZE; i++) - INIT_HLIST_HEAD(&inet6_addr_lst[i]); - register_netdevice_notifier(&ipv6_dev_notf); - addrconf_verify(); + addrconf_verify(&init_net); rtnl_af_register(&inet6_ops); @@ -7323,7 +7328,6 @@ int __init addrconf_init(void) void addrconf_cleanup(void) { struct net_device *dev; - int i; unregister_netdevice_notifier(&ipv6_dev_notf); unregister_pernet_subsys(&addrconf_ops); @@ -7341,14 +7345,6 @@ void addrconf_cleanup(void) } addrconf_ifdown(init_net.loopback_dev, true); - /* - * Check hash table. - */ - spin_lock_bh(&addrconf_hash_lock); - for (i = 0; i < IN6_ADDR_HSIZE; i++) - WARN_ON(!hlist_empty(&inet6_addr_lst[i])); - spin_unlock_bh(&addrconf_hash_lock); - cancel_delayed_work(&addr_chk_work); rtnl_unlock(); destroy_workqueue(addrconf_wq); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 8fe7900f1949..7d7b7523d126 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -441,11 +441,14 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; u32 flags = BIND_WITH_LOCK; + const struct proto *prot; int err = 0; + /* IPV6_ADDRFORM can change sk->sk_prot under us. */ + prot = READ_ONCE(sk->sk_prot); /* If the socket has its own bind function then use it. */ - if (sk->sk_prot->bind) - return sk->sk_prot->bind(sk, uaddr, addr_len); + if (prot->bind) + return prot->bind(sk, uaddr, addr_len); if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; @@ -555,6 +558,7 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) void __user *argp = (void __user *)arg; struct sock *sk = sock->sk; struct net *net = sock_net(sk); + const struct proto *prot; switch (cmd) { case SIOCADDRT: @@ -572,9 +576,11 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCSIFDSTADDR: return addrconf_set_dstaddr(net, argp); default: - if (!sk->sk_prot->ioctl) + /* IPV6_ADDRFORM can change sk->sk_prot under us. */ + prot = READ_ONCE(sk->sk_prot); + if (!prot->ioctl) return -ENOIOCTLCMD; - return sk->sk_prot->ioctl(sk, cmd, arg); + return prot->ioctl(sk, cmd, arg); } /*NOTREACHED*/ return 0; @@ -636,11 +642,14 @@ INDIRECT_CALLABLE_DECLARE(int udpv6_sendmsg(struct sock *, struct msghdr *, int inet6_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { struct sock *sk = sock->sk; + const struct proto *prot; if (unlikely(inet_send_prepare(sk))) return -EAGAIN; - return INDIRECT_CALL_2(sk->sk_prot->sendmsg, tcp_sendmsg, udpv6_sendmsg, + /* IPV6_ADDRFORM can change sk->sk_prot under us. */ + prot = READ_ONCE(sk->sk_prot); + return INDIRECT_CALL_2(prot->sendmsg, tcp_sendmsg, udpv6_sendmsg, sk, msg, size); } @@ -650,13 +659,16 @@ int inet6_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct sock *sk = sock->sk; + const struct proto *prot; int addr_len = 0; int err; if (likely(!(flags & MSG_ERRQUEUE))) sock_rps_record_flow(sk); - err = INDIRECT_CALL_2(sk->sk_prot->recvmsg, tcp_recvmsg, udpv6_recvmsg, + /* IPV6_ADDRFORM can change sk->sk_prot under us. */ + prot = READ_ONCE(sk->sk_prot); + err = INDIRECT_CALL_2(prot->recvmsg, tcp_recvmsg, udpv6_recvmsg, sk, msg, size, flags & MSG_DONTWAIT, flags & ~MSG_DONTWAIT, &addr_len); if (err >= 0) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 77e34aec7e82..658d5eabaf7e 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -1344,14 +1344,14 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, return opt2; } -struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, - struct ipv6_txoptions *opt) +struct ipv6_txoptions *__ipv6_fixup_options(struct ipv6_txoptions *opt_space, + struct ipv6_txoptions *opt) { /* * ignore the dest before srcrt unless srcrt is being included. * --yoshfuji */ - if (opt && opt->dst0opt && !opt->srcrt) { + if (opt->dst0opt && !opt->srcrt) { if (opt_space != opt) { memcpy(opt_space, opt, sizeof(*opt_space)); opt = opt_space; @@ -1362,7 +1362,7 @@ struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, return opt; } -EXPORT_SYMBOL_GPL(ipv6_fixup_options); +EXPORT_SYMBOL_GPL(__ipv6_fixup_options); /** * fl6_update_dst - update flowi destination address with info given diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index ec029c86ae06..7c2003833010 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -25,14 +26,14 @@ struct fib6_rule { struct fib_rule common; struct rt6key src; struct rt6key dst; - u8 tclass; + dscp_t dscp; }; static bool fib6_rule_matchall(const struct fib_rule *rule) { struct fib6_rule *r = container_of(rule, struct fib6_rule, common); - if (r->dst.plen || r->src.plen || r->tclass) + if (r->dst.plen || r->src.plen || r->dscp) return false; return fib_rule_matchall(rule); } @@ -323,7 +324,7 @@ INDIRECT_CALLABLE_SCOPE int fib6_rule_match(struct fib_rule *rule, return 0; } - if (r->tclass && r->tclass != ip6_tclass(fl6->flowlabel)) + if (r->dscp && r->dscp != ip6_dscp(fl6->flowlabel)) return 0; if (rule->ip_proto && (rule->ip_proto != fl6->flowi6_proto)) @@ -349,6 +350,13 @@ static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb, struct net *net = sock_net(skb->sk); struct fib6_rule *rule6 = (struct fib6_rule *) rule; + if (!inet_validate_dscp(frh->tos)) { + NL_SET_ERR_MSG(extack, + "Invalid dsfield (tos): ECN bits must be 0"); + goto errout; + } + rule6->dscp = inet_dsfield_to_dscp(frh->tos); + if (rule->action == FR_ACT_TO_TBL && !rule->l3mdev) { if (rule->table == RT6_TABLE_UNSPEC) { NL_SET_ERR_MSG(extack, "Invalid table"); @@ -369,7 +377,6 @@ static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb, rule6->src.plen = frh->src_len; rule6->dst.plen = frh->dst_len; - rule6->tclass = frh->tos; if (fib_rule_requires_fldissect(rule)) net->ipv6.fib6_rules_require_fldissect++; @@ -402,7 +409,7 @@ static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, if (frh->dst_len && (rule6->dst.plen != frh->dst_len)) return 0; - if (frh->tos && (rule6->tclass != frh->tos)) + if (frh->tos && inet_dscp_to_dsfield(rule6->dscp) != frh->tos) return 0; if (frh->src_len && @@ -423,7 +430,7 @@ static int fib6_rule_fill(struct fib_rule *rule, struct sk_buff *skb, frh->dst_len = rule6->dst.plen; frh->src_len = rule6->src.plen; - frh->tos = rule6->tclass; + frh->tos = inet_dscp_to_dsfield(rule6->dscp); if ((rule6->dst.plen && nla_put_in6_addr(skb, FRA_DST, &rule6->dst.addr)) || @@ -486,16 +493,21 @@ static int __net_init fib6_rules_net_init(struct net *net) goto out; } -static void __net_exit fib6_rules_net_exit(struct net *net) +static void __net_exit fib6_rules_net_exit_batch(struct list_head *net_list) { + struct net *net; + rtnl_lock(); - fib_rules_unregister(net->ipv6.fib6_rules_ops); + list_for_each_entry(net, net_list, exit_list) { + fib_rules_unregister(net->ipv6.fib6_rules_ops); + cond_resched(); + } rtnl_unlock(); } static struct pernet_operations fib6_rules_net_ops = { .init = fib6_rules_net_init, - .exit = fib6_rules_net_exit, + .exit_batch = fib6_rules_net_exit_batch, }; int __init fib6_rules_init(void) diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 96c5cc0f30ce..e6b978ea0e87 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -69,17 +69,7 @@ #include -/* - * The ICMP socket(s). This is the most convenient way to flow control - * our ICMP output as well as maintain a clean interface throughout - * all layers. All Socketless IP sends will soon be gone. - * - * On SMP we have one ICMP socket per-cpu. - */ -static struct sock *icmpv6_sk(struct net *net) -{ - return this_cpu_read(*net->ipv6.icmp_sk); -} +static DEFINE_PER_CPU(struct sock *, ipv6_icmp_sk); static int icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) @@ -110,11 +100,11 @@ static const struct inet6_protocol icmpv6_protocol = { }; /* Called with BH disabled */ -static __inline__ struct sock *icmpv6_xmit_lock(struct net *net) +static struct sock *icmpv6_xmit_lock(struct net *net) { struct sock *sk; - sk = icmpv6_sk(net); + sk = this_cpu_read(ipv6_icmp_sk); if (unlikely(!spin_trylock(&sk->sk_lock.slock))) { /* This can happen if the output path (f.e. SIT or * ip6ip6 tunnel) signals dst_link_failure() for an @@ -122,11 +112,13 @@ static __inline__ struct sock *icmpv6_xmit_lock(struct net *net) */ return NULL; } + sock_net_set(sk, net); return sk; } -static __inline__ void icmpv6_xmit_unlock(struct sock *sk) +static void icmpv6_xmit_unlock(struct sock *sk) { + sock_net_set(sk, &init_net); spin_unlock(&sk->sk_lock.slock); } @@ -1034,59 +1026,27 @@ void icmpv6_flow_init(struct sock *sk, struct flowi6 *fl6, security_sk_classify_flow(sk, flowi6_to_flowi_common(fl6)); } -static void __net_exit icmpv6_sk_exit(struct net *net) -{ - int i; - - for_each_possible_cpu(i) - inet_ctl_sock_destroy(*per_cpu_ptr(net->ipv6.icmp_sk, i)); - free_percpu(net->ipv6.icmp_sk); -} - -static int __net_init icmpv6_sk_init(struct net *net) +int __init icmpv6_init(void) { struct sock *sk; int err, i; - net->ipv6.icmp_sk = alloc_percpu(struct sock *); - if (!net->ipv6.icmp_sk) - return -ENOMEM; - for_each_possible_cpu(i) { err = inet_ctl_sock_create(&sk, PF_INET6, - SOCK_RAW, IPPROTO_ICMPV6, net); + SOCK_RAW, IPPROTO_ICMPV6, &init_net); if (err < 0) { pr_err("Failed to initialize the ICMP6 control socket (err %d)\n", err); - goto fail; + return err; } - *per_cpu_ptr(net->ipv6.icmp_sk, i) = sk; + per_cpu(ipv6_icmp_sk, i) = sk; /* Enough space for 2 64K ICMP packets, including * sk_buff struct overhead. */ sk->sk_sndbuf = 2 * SKB_TRUESIZE(64 * 1024); } - return 0; - - fail: - icmpv6_sk_exit(net); - return err; -} - -static struct pernet_operations icmpv6_sk_ops = { - .init = icmpv6_sk_init, - .exit = icmpv6_sk_exit, -}; - -int __init icmpv6_init(void) -{ - int err; - - err = register_pernet_subsys(&icmpv6_sk_ops); - if (err < 0) - return err; err = -EAGAIN; if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0) @@ -1101,14 +1061,12 @@ int __init icmpv6_init(void) inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6); fail: pr_err("Failed to register ICMP6 protocol\n"); - unregister_pernet_subsys(&icmpv6_sk_ops); return err; } void icmpv6_cleanup(void) { inet6_unregister_icmp_sender(icmp6_send); - unregister_pernet_subsys(&icmpv6_sk_ops); inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6); } diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index 4514444e96c8..4740afecf7c6 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -333,11 +333,8 @@ int inet6_hash(struct sock *sk) { int err = 0; - if (sk->sk_state != TCP_CLOSE) { - local_bh_disable(); + if (sk->sk_state != TCP_CLOSE) err = __inet_hash(sk, NULL); - local_bh_enable(); - } return err; } diff --git a/net/ipv6/ioam6.c b/net/ipv6/ioam6.c index e159eb4328a8..1098131ed90c 100644 --- a/net/ipv6/ioam6.c +++ b/net/ipv6/ioam6.c @@ -635,7 +635,8 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, struct ioam6_schema *sc, u8 sclen, bool is_input) { - struct __kernel_sock_timeval ts; + struct timespec64 ts; + ktime_t tstamp; u64 raw64; u32 raw32; u16 raw16; @@ -680,10 +681,9 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, if (!skb->dev) { *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); } else { - if (!skb->tstamp) - __net_timestamp(skb); + tstamp = skb_tstamp_cond(skb, true); + ts = ktime_to_timespec64(tstamp); - skb_get_new_timestamp(skb, &ts); *(__be32 *)data = cpu_to_be32((u32)ts.tv_sec); } data += sizeof(__be32); @@ -694,13 +694,12 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, if (!skb->dev) { *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); } else { - if (!skb->tstamp) - __net_timestamp(skb); + if (!trace->type.bit2) { + tstamp = skb_tstamp_cond(skb, true); + ts = ktime_to_timespec64(tstamp); + } - if (!trace->type.bit2) - skb_get_new_timestamp(skb, &ts); - - *(__be32 *)data = cpu_to_be32((u32)ts.tv_usec); + *(__be32 *)data = cpu_to_be32((u32)(ts.tv_nsec / NSEC_PER_USEC)); } data += sizeof(__be32); } diff --git a/net/ipv6/ioam6_iptunnel.c b/net/ipv6/ioam6_iptunnel.c index f90a87389fcc..f6f5b83dd954 100644 --- a/net/ipv6/ioam6_iptunnel.c +++ b/net/ipv6/ioam6_iptunnel.c @@ -32,13 +32,25 @@ struct ioam6_lwt_encap { struct ioam6_trace_hdr traceh; } __packed; +struct ioam6_lwt_freq { + u32 k; + u32 n; +}; + struct ioam6_lwt { struct dst_cache cache; + struct ioam6_lwt_freq freq; + atomic_t pkt_cnt; u8 mode; struct in6_addr tundst; struct ioam6_lwt_encap tuninfo; }; +static struct netlink_range_validation freq_range = { + .min = IOAM6_IPTUNNEL_FREQ_MIN, + .max = IOAM6_IPTUNNEL_FREQ_MAX, +}; + static struct ioam6_lwt *ioam6_lwt_state(struct lwtunnel_state *lwt) { return (struct ioam6_lwt *)lwt->data; @@ -55,6 +67,8 @@ static struct ioam6_trace_hdr *ioam6_lwt_trace(struct lwtunnel_state *lwt) } static const struct nla_policy ioam6_iptunnel_policy[IOAM6_IPTUNNEL_MAX + 1] = { + [IOAM6_IPTUNNEL_FREQ_K] = NLA_POLICY_FULL_RANGE(NLA_U32, &freq_range), + [IOAM6_IPTUNNEL_FREQ_N] = NLA_POLICY_FULL_RANGE(NLA_U32, &freq_range), [IOAM6_IPTUNNEL_MODE] = NLA_POLICY_RANGE(NLA_U8, IOAM6_IPTUNNEL_MODE_MIN, IOAM6_IPTUNNEL_MODE_MAX), @@ -96,6 +110,7 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla, struct lwtunnel_state *lwt; struct ioam6_lwt *ilwt; int len_aligned, err; + u32 freq_k, freq_n; u8 mode; if (family != AF_INET6) @@ -106,6 +121,23 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla, if (err < 0) return err; + if ((!tb[IOAM6_IPTUNNEL_FREQ_K] && tb[IOAM6_IPTUNNEL_FREQ_N]) || + (tb[IOAM6_IPTUNNEL_FREQ_K] && !tb[IOAM6_IPTUNNEL_FREQ_N])) { + NL_SET_ERR_MSG(extack, "freq: missing parameter"); + return -EINVAL; + } else if (!tb[IOAM6_IPTUNNEL_FREQ_K] && !tb[IOAM6_IPTUNNEL_FREQ_N]) { + freq_k = IOAM6_IPTUNNEL_FREQ_MIN; + freq_n = IOAM6_IPTUNNEL_FREQ_MIN; + } else { + freq_k = nla_get_u32(tb[IOAM6_IPTUNNEL_FREQ_K]); + freq_n = nla_get_u32(tb[IOAM6_IPTUNNEL_FREQ_N]); + + if (freq_k > freq_n) { + NL_SET_ERR_MSG(extack, "freq: k > n is forbidden"); + return -EINVAL; + } + } + if (!tb[IOAM6_IPTUNNEL_MODE]) mode = IOAM6_IPTUNNEL_MODE_INLINE; else @@ -140,6 +172,10 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla, return err; } + atomic_set(&ilwt->pkt_cnt, 0); + ilwt->freq.k = freq_k; + ilwt->freq.n = freq_n; + ilwt->mode = mode; if (tb[IOAM6_IPTUNNEL_DST]) ilwt->tundst = nla_get_in6_addr(tb[IOAM6_IPTUNNEL_DST]); @@ -263,11 +299,18 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb) struct in6_addr orig_daddr; struct ioam6_lwt *ilwt; int err = -EINVAL; + u32 pkt_cnt; if (skb->protocol != htons(ETH_P_IPV6)) goto drop; ilwt = ioam6_lwt_state(dst->lwtstate); + + /* Check for insertion frequency (i.e., "k over n" insertions) */ + pkt_cnt = atomic_fetch_inc(&ilwt->pkt_cnt); + if (pkt_cnt % ilwt->freq.n >= ilwt->freq.k) + goto out; + orig_daddr = ipv6_hdr(skb)->daddr; switch (ilwt->mode) { @@ -358,6 +401,14 @@ static int ioam6_fill_encap_info(struct sk_buff *skb, struct ioam6_lwt *ilwt = ioam6_lwt_state(lwtstate); int err; + err = nla_put_u32(skb, IOAM6_IPTUNNEL_FREQ_K, ilwt->freq.k); + if (err) + goto ret; + + err = nla_put_u32(skb, IOAM6_IPTUNNEL_FREQ_N, ilwt->freq.n); + if (err) + goto ret; + err = nla_put_u8(skb, IOAM6_IPTUNNEL_MODE, ilwt->mode); if (err) goto ret; @@ -379,7 +430,9 @@ static int ioam6_encap_nlsize(struct lwtunnel_state *lwtstate) struct ioam6_lwt *ilwt = ioam6_lwt_state(lwtstate); int nlsize; - nlsize = nla_total_size(sizeof(ilwt->mode)) + + nlsize = nla_total_size(sizeof(ilwt->freq.k)) + + nla_total_size(sizeof(ilwt->freq.n)) + + nla_total_size(sizeof(ilwt->mode)) + nla_total_size(sizeof(ilwt->tuninfo.traceh)); if (ilwt->mode != IOAM6_IPTUNNEL_MODE_INLINE) @@ -395,7 +448,9 @@ static int ioam6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) struct ioam6_lwt *ilwt_a = ioam6_lwt_state(a); struct ioam6_lwt *ilwt_b = ioam6_lwt_state(b); - return (ilwt_a->mode != ilwt_b->mode || + return (ilwt_a->freq.k != ilwt_b->freq.k || + ilwt_a->freq.n != ilwt_b->freq.n || + ilwt_a->mode != ilwt_b->mode || (ilwt_a->mode != IOAM6_IPTUNNEL_MODE_INLINE && !ipv6_addr_equal(&ilwt_a->tundst, &ilwt_b->tundst)) || trace_a->namespace_id != trace_b->namespace_id); diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 80256717868e..5b5ea35635f9 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -459,6 +459,7 @@ void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr, static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { + skb_clear_delivery_time(skb); rcu_read_lock(); ip6_protocol_deliver_rcu(net, skb, 0, false); rcu_read_unlock(); @@ -508,7 +509,7 @@ int ip6_mc_input(struct sk_buff *skb) /* * IPv6 multicast router mode is now supported ;) */ - if (dev_net(skb->dev)->ipv6.devconf_all->mc_forwarding && + if (atomic_read(&dev_net(skb->dev)->ipv6.devconf_all->mc_forwarding) && !(ipv6_addr_type(&hdr->daddr) & (IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL)) && likely(!(IP6CB(skb)->flags & IP6SKB_FORWARDED))) { diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 5f577e21459b..c4fc03c1ac99 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -251,7 +251,7 @@ INDIRECT_CALLABLE_SCOPE struct sk_buff *ipv6_gro_receive(struct list_head *head, if ((first_word & htonl(0xF00FFFFF)) || !ipv6_addr_equal(&iph->saddr, &iph2->saddr) || !ipv6_addr_equal(&iph->daddr, &iph2->daddr) || - *(u16 *)&iph->nexthdr != *(u16 *)&iph2->nexthdr) { + iph->nexthdr != iph2->nexthdr) { not_same_flow: NAPI_GRO_CB(p)->same_flow = 0; continue; @@ -262,7 +262,8 @@ INDIRECT_CALLABLE_SCOPE struct sk_buff *ipv6_gro_receive(struct list_head *head, goto not_same_flow; } /* flush if Traffic Class fields are different */ - NAPI_GRO_CB(p)->flush |= !!(first_word & htonl(0x0FF00000)); + NAPI_GRO_CB(p)->flush |= !!((first_word & htonl(0x0FF00000)) | + (__force __be32)(iph->hop_limit ^ iph2->hop_limit)); NAPI_GRO_CB(p)->flush |= flush; /* If the previous IP ID value was based on an atomic diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 194832663d85..e23f058166af 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -130,7 +130,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff * rcu_read_unlock_bh(); IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTNOROUTES); - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_CREATEFAIL); return -EINVAL; } @@ -202,7 +202,7 @@ static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *s case NET_XMIT_CN: return __ip6_finish_output(net, sk, skb) ? : ret; default: - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_BPF_CGROUP_EGRESS); return ret; } } @@ -217,7 +217,7 @@ int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb) if (unlikely(idev->cnf.disable_ipv6)) { IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED); return 0; } @@ -440,7 +440,7 @@ static inline int ip6_forward_finish(struct net *net, struct sock *sk, } #endif - skb->tstamp = 0; + skb_clear_tstamp(skb); return dst_output(net, sk, skb); } @@ -813,6 +813,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ? inet6_sk(skb->sk) : NULL; + bool mono_delivery_time = skb->mono_delivery_time; struct ip6_frag_state state; unsigned int mtu, hlen, nexthdr_offset; ktime_t tstamp = skb->tstamp; @@ -903,7 +904,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, if (iter.frag) ip6_fraglist_prepare(skb, &iter); - skb->tstamp = tstamp; + skb_set_delivery_time(skb, tstamp, mono_delivery_time); err = output(net, sk, skb); if (!err) IP6_INC_STATS(net, ip6_dst_idev(&rt->dst), @@ -962,7 +963,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, /* * Put this fragment into the sending queue. */ - frag->tstamp = tstamp; + skb_set_delivery_time(frag, tstamp, mono_delivery_time); err = output(net, sk, frag); if (err) goto fail; @@ -1034,8 +1035,7 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk, #ifdef CONFIG_IPV6_SUBTREES ip6_rt_check(&rt->rt6i_src, &fl6->saddr, np->saddr_cache) || #endif - (!(fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) && - (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex))) { + (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex)) { dst_release(dst); dst = NULL; } @@ -1350,11 +1350,16 @@ static void ip6_append_data_mtu(unsigned int *mtu, static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork, struct inet6_cork *v6_cork, struct ipcm6_cookie *ipc6, - struct rt6_info *rt, struct flowi6 *fl6) + struct rt6_info *rt) { struct ipv6_pinfo *np = inet6_sk(sk); unsigned int mtu; - struct ipv6_txoptions *opt = ipc6->opt; + struct ipv6_txoptions *nopt, *opt = ipc6->opt; + + /* callers pass dst together with a reference, set it first so + * ip6_cork_release() can put it down even in case of an error. + */ + cork->base.dst = &rt->dst; /* * setup for corking @@ -1363,39 +1368,32 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork, if (WARN_ON(v6_cork->opt)) return -EINVAL; - v6_cork->opt = kzalloc(sizeof(*opt), sk->sk_allocation); - if (unlikely(!v6_cork->opt)) + nopt = v6_cork->opt = kzalloc(sizeof(*opt), sk->sk_allocation); + if (unlikely(!nopt)) return -ENOBUFS; - v6_cork->opt->tot_len = sizeof(*opt); - v6_cork->opt->opt_flen = opt->opt_flen; - v6_cork->opt->opt_nflen = opt->opt_nflen; + nopt->tot_len = sizeof(*opt); + nopt->opt_flen = opt->opt_flen; + nopt->opt_nflen = opt->opt_nflen; - v6_cork->opt->dst0opt = ip6_opt_dup(opt->dst0opt, - sk->sk_allocation); - if (opt->dst0opt && !v6_cork->opt->dst0opt) + nopt->dst0opt = ip6_opt_dup(opt->dst0opt, sk->sk_allocation); + if (opt->dst0opt && !nopt->dst0opt) return -ENOBUFS; - v6_cork->opt->dst1opt = ip6_opt_dup(opt->dst1opt, - sk->sk_allocation); - if (opt->dst1opt && !v6_cork->opt->dst1opt) + nopt->dst1opt = ip6_opt_dup(opt->dst1opt, sk->sk_allocation); + if (opt->dst1opt && !nopt->dst1opt) return -ENOBUFS; - v6_cork->opt->hopopt = ip6_opt_dup(opt->hopopt, - sk->sk_allocation); - if (opt->hopopt && !v6_cork->opt->hopopt) + nopt->hopopt = ip6_opt_dup(opt->hopopt, sk->sk_allocation); + if (opt->hopopt && !nopt->hopopt) return -ENOBUFS; - v6_cork->opt->srcrt = ip6_rthdr_dup(opt->srcrt, - sk->sk_allocation); - if (opt->srcrt && !v6_cork->opt->srcrt) + nopt->srcrt = ip6_rthdr_dup(opt->srcrt, sk->sk_allocation); + if (opt->srcrt && !nopt->srcrt) return -ENOBUFS; /* need source address above miyazawa*/ } - dst_hold(&rt->dst); - cork->base.dst = &rt->dst; - cork->fl.u.ip6 = *fl6; v6_cork->hop_limit = ipc6->hlimit; v6_cork->tclass = ipc6->tclass; if (rt->dst.flags & DST_XFRM_TUNNEL) @@ -1424,9 +1422,8 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork, } static int __ip6_append_data(struct sock *sk, - struct flowi6 *fl6, struct sk_buff_head *queue, - struct inet_cork *cork, + struct inet_cork_full *cork_full, struct inet6_cork *v6_cork, struct page_frag *pfrag, int getfrag(void *from, char *to, int offset, @@ -1435,6 +1432,8 @@ static int __ip6_append_data(struct sock *sk, unsigned int flags, struct ipcm6_cookie *ipc6) { struct sk_buff *skb, *skb_prev = NULL; + struct inet_cork *cork = &cork_full->base; + struct flowi6 *fl6 = &cork_full->fl.u.ip6; unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu, pmtu; struct ubuf_info *uarg = NULL; int exthdrlen = 0; @@ -1491,6 +1490,7 @@ static int __ip6_append_data(struct sock *sk, if (cork->length + length > mtu - headersize && ipc6->dontfrag && (sk->sk_protocol == IPPROTO_UDP || + sk->sk_protocol == IPPROTO_ICMPV6 || sk->sk_protocol == IPPROTO_RAW)) { ipv6_local_rxpmtu(sk, fl6, mtu - headersize + sizeof(struct ipv6hdr)); @@ -1791,34 +1791,46 @@ int ip6_append_data(struct sock *sk, /* * setup for corking */ + dst_hold(&rt->dst); err = ip6_setup_cork(sk, &inet->cork, &np->cork, - ipc6, rt, fl6); + ipc6, rt); if (err) return err; + inet->cork.fl.u.ip6 = *fl6; exthdrlen = (ipc6->opt ? ipc6->opt->opt_flen : 0); length += exthdrlen; transhdrlen += exthdrlen; } else { - fl6 = &inet->cork.fl.u.ip6; transhdrlen = 0; } - return __ip6_append_data(sk, fl6, &sk->sk_write_queue, &inet->cork.base, + return __ip6_append_data(sk, &sk->sk_write_queue, &inet->cork, &np->cork, sk_page_frag(sk), getfrag, from, length, transhdrlen, flags, ipc6); } EXPORT_SYMBOL_GPL(ip6_append_data); +static void ip6_cork_steal_dst(struct sk_buff *skb, struct inet_cork_full *cork) +{ + struct dst_entry *dst = cork->base.dst; + + cork->base.dst = NULL; + cork->base.flags &= ~IPCORK_ALLFRAG; + skb_dst_set(skb, dst); +} + static void ip6_cork_release(struct inet_cork_full *cork, struct inet6_cork *v6_cork) { if (v6_cork->opt) { - kfree(v6_cork->opt->dst0opt); - kfree(v6_cork->opt->dst1opt); - kfree(v6_cork->opt->hopopt); - kfree(v6_cork->opt->srcrt); - kfree(v6_cork->opt); + struct ipv6_txoptions *opt = v6_cork->opt; + + kfree(opt->dst0opt); + kfree(opt->dst1opt); + kfree(opt->hopopt); + kfree(opt->srcrt); + kfree(opt); v6_cork->opt = NULL; } @@ -1827,7 +1839,6 @@ static void ip6_cork_release(struct inet_cork_full *cork, cork->base.dst = NULL; cork->base.flags &= ~IPCORK_ALLFRAG; } - memset(&cork->fl, 0, sizeof(cork->fl)); } struct sk_buff *__ip6_make_skb(struct sock *sk, @@ -1837,7 +1848,7 @@ struct sk_buff *__ip6_make_skb(struct sock *sk, { struct sk_buff *skb, *tmp_skb; struct sk_buff **tail_skb; - struct in6_addr final_dst_buf, *final_dst = &final_dst_buf; + struct in6_addr *final_dst; struct ipv6_pinfo *np = inet6_sk(sk); struct net *net = sock_net(sk); struct ipv6hdr *hdr; @@ -1867,9 +1878,9 @@ struct sk_buff *__ip6_make_skb(struct sock *sk, /* Allow local fragmentation. */ skb->ignore_df = ip6_sk_ignore_df(sk); - - *final_dst = fl6->daddr; __skb_pull(skb, skb_network_header_len(skb)); + + final_dst = &fl6->daddr; if (opt && opt->opt_flen) ipv6_push_frag_opts(skb, opt, &proto); if (opt && opt->opt_nflen) @@ -1889,10 +1900,9 @@ struct sk_buff *__ip6_make_skb(struct sock *sk, skb->priority = sk->sk_priority; skb->mark = cork->base.mark; - skb->tstamp = cork->base.transmit_time; - skb_dst_set(skb, dst_clone(&rt->dst)); + ip6_cork_steal_dst(skb, cork); IP6_UPD_PO_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len); if (proto == IPPROTO_ICMPV6) { struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); @@ -1964,26 +1974,26 @@ struct sk_buff *ip6_make_skb(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), void *from, int length, int transhdrlen, - struct ipcm6_cookie *ipc6, struct flowi6 *fl6, - struct rt6_info *rt, unsigned int flags, - struct inet_cork_full *cork) + struct ipcm6_cookie *ipc6, struct rt6_info *rt, + unsigned int flags, struct inet_cork_full *cork) { struct inet6_cork v6_cork; struct sk_buff_head queue; int exthdrlen = (ipc6->opt ? ipc6->opt->opt_flen : 0); int err; - if (flags & MSG_PROBE) + if (flags & MSG_PROBE) { + dst_release(&rt->dst); return NULL; + } __skb_queue_head_init(&queue); cork->base.flags = 0; cork->base.addr = 0; cork->base.opt = NULL; - cork->base.dst = NULL; v6_cork.opt = NULL; - err = ip6_setup_cork(sk, cork, &v6_cork, ipc6, rt, fl6); + err = ip6_setup_cork(sk, cork, &v6_cork, ipc6, rt); if (err) { ip6_cork_release(cork, &v6_cork); return ERR_PTR(err); @@ -1991,7 +2001,7 @@ struct sk_buff *ip6_make_skb(struct sock *sk, if (ipc6->dontfrag < 0) ipc6->dontfrag = inet6_sk(sk)->dontfrag; - err = __ip6_append_data(sk, fl6, &queue, &cork->base, &v6_cork, + err = __ip6_append_data(sk, &queue, cork, &v6_cork, ¤t->task_frag, getfrag, from, length + exthdrlen, transhdrlen + exthdrlen, flags, ipc6); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 97ade833f58c..53f632a560ec 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1121,6 +1121,14 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield, memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr)); neigh_release(neigh); + } else if (skb->protocol == htons(ETH_P_IP)) { + const struct rtable *rt = skb_rtable(skb); + + if (!rt) + goto tx_err_link_failure; + + if (rt->rt_gw_family == AF_INET6) + memcpy(&fl6->daddr, &rt->rt_gw6, sizeof(fl6->daddr)); } } else if (t->parms.proto != 0 && !(t->parms.flags & (IP6_TNL_F_USE_ORIG_TCLASS | diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 8a2db926b5eb..a9775c830194 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -255,13 +255,12 @@ static void __net_exit ip6mr_rules_exit(struct net *net) { struct mr_table *mrt, *next; - rtnl_lock(); + ASSERT_RTNL(); list_for_each_entry_safe(mrt, next, &net->ipv6.mr6_tables, list) { list_del(&mrt->list); ip6mr_free_table(mrt); } fib_rules_unregister(net->ipv6.mr6_rules_ops); - rtnl_unlock(); } static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb, @@ -318,10 +317,9 @@ static int __net_init ip6mr_rules_init(struct net *net) static void __net_exit ip6mr_rules_exit(struct net *net) { - rtnl_lock(); + ASSERT_RTNL(); ip6mr_free_table(net->ipv6.mrt6); net->ipv6.mrt6 = NULL; - rtnl_unlock(); } static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb, @@ -734,7 +732,7 @@ static int mif6_delete(struct mr_table *mrt, int vifi, int notify, in6_dev = __in6_dev_get(dev); if (in6_dev) { - in6_dev->cnf.mc_forwarding--; + atomic_dec(&in6_dev->cnf.mc_forwarding); inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, NETCONFA_MC_FORWARDING, dev->ifindex, &in6_dev->cnf); @@ -902,7 +900,7 @@ static int mif6_add(struct net *net, struct mr_table *mrt, in6_dev = __in6_dev_get(dev); if (in6_dev) { - in6_dev->cnf.mc_forwarding++; + atomic_inc(&in6_dev->cnf.mc_forwarding); inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, NETCONFA_MC_FORWARDING, dev->ifindex, &in6_dev->cnf); @@ -1042,7 +1040,7 @@ static int ip6mr_cache_report(struct mr_table *mrt, struct sk_buff *pkt, int ret; #ifdef CONFIG_IPV6_PIMSM_V2 - if (assert == MRT6MSG_WHOLEPKT) + if (assert == MRT6MSG_WHOLEPKT || assert == MRT6MSG_WRMIFWHOLE) skb = skb_realloc_headroom(pkt, -skb_network_offset(pkt) +sizeof(*msg)); else @@ -1058,7 +1056,7 @@ static int ip6mr_cache_report(struct mr_table *mrt, struct sk_buff *pkt, skb->ip_summed = CHECKSUM_UNNECESSARY; #ifdef CONFIG_IPV6_PIMSM_V2 - if (assert == MRT6MSG_WHOLEPKT) { + if (assert == MRT6MSG_WHOLEPKT || assert == MRT6MSG_WRMIFWHOLE) { /* Ugly, but we have no choice with this interface. Duplicate old header, fix length etc. And all this only to mangle msg->im6_msgtype and @@ -1070,8 +1068,11 @@ static int ip6mr_cache_report(struct mr_table *mrt, struct sk_buff *pkt, skb_reset_transport_header(skb); msg = (struct mrt6msg *)skb_transport_header(skb); msg->im6_mbz = 0; - msg->im6_msgtype = MRT6MSG_WHOLEPKT; - msg->im6_mif = mrt->mroute_reg_vif_num; + msg->im6_msgtype = assert; + if (assert == MRT6MSG_WRMIFWHOLE) + msg->im6_mif = mifi; + else + msg->im6_mif = mrt->mroute_reg_vif_num; msg->im6_pad = 0; msg->im6_src = ipv6_hdr(pkt)->saddr; msg->im6_dst = ipv6_hdr(pkt)->daddr; @@ -1325,7 +1326,9 @@ static int __net_init ip6mr_net_init(struct net *net) proc_cache_fail: remove_proc_entry("ip6_mr_vif", net->proc_net); proc_vif_fail: + rtnl_lock(); ip6mr_rules_exit(net); + rtnl_unlock(); #endif ip6mr_rules_fail: ip6mr_notifier_exit(net); @@ -1338,13 +1341,23 @@ static void __net_exit ip6mr_net_exit(struct net *net) remove_proc_entry("ip6_mr_cache", net->proc_net); remove_proc_entry("ip6_mr_vif", net->proc_net); #endif - ip6mr_rules_exit(net); ip6mr_notifier_exit(net); } +static void __net_exit ip6mr_net_exit_batch(struct list_head *net_list) +{ + struct net *net; + + rtnl_lock(); + list_for_each_entry(net, net_list, exit_list) + ip6mr_rules_exit(net); + rtnl_unlock(); +} + static struct pernet_operations ip6mr_net_ops = { .init = ip6mr_net_init, .exit = ip6mr_net_exit, + .exit_batch = ip6mr_net_exit_batch, }; int __init ip6_mr_init(void) @@ -1553,7 +1566,7 @@ static int ip6mr_sk_init(struct mr_table *mrt, struct sock *sk) } else { rcu_assign_pointer(mrt->mroute_sk, sk); sock_set_flag(sk, SOCK_RCU_FREE); - net->ipv6.devconf_all->mc_forwarding++; + atomic_inc(&net->ipv6.devconf_all->mc_forwarding); } write_unlock_bh(&mrt_lock); @@ -1569,14 +1582,19 @@ static int ip6mr_sk_init(struct mr_table *mrt, struct sock *sk) int ip6mr_sk_done(struct sock *sk) { - int err = -EACCES; struct net *net = sock_net(sk); + struct ipv6_devconf *devconf; struct mr_table *mrt; + int err = -EACCES; if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num != IPPROTO_ICMPV6) return err; + devconf = net->ipv6.devconf_all; + if (!devconf || !atomic_read(&devconf->mc_forwarding)) + return err; + rtnl_lock(); ip6mr_for_each_table(mrt, net) { if (sk == rtnl_dereference(mrt->mroute_sk)) { @@ -1586,7 +1604,7 @@ int ip6mr_sk_done(struct sock *sk) * so the RCU grace period before sk freeing * is guaranteed by sk_destruct() */ - net->ipv6.devconf_all->mc_forwarding--; + atomic_dec(&devconf->mc_forwarding); write_unlock_bh(&mrt_lock); inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_MC_FORWARDING, @@ -1635,6 +1653,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, mifi_t mifi; struct net *net = sock_net(sk); struct mr_table *mrt; + bool do_wrmifwhole; if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num != IPPROTO_ICMPV6) @@ -1748,12 +1767,15 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, return -EINVAL; if (copy_from_sockptr(&v, optval, sizeof(v))) return -EFAULT; + + do_wrmifwhole = (v == MRT6MSG_WRMIFWHOLE); v = !!v; rtnl_lock(); ret = 0; if (v != mrt->mroute_do_pim) { mrt->mroute_do_pim = v; mrt->mroute_do_assert = v; + mrt->mroute_do_wrvifwhole = do_wrmifwhole; } rtnl_unlock(); return ret; @@ -2129,6 +2151,9 @@ static void ip6_mr_forward(struct net *net, struct mr_table *mrt, MFC_ASSERT_THRESH)) { c->_c.mfc_un.res.last_assert = jiffies; ip6mr_cache_report(mrt, skb, true_vifi, MRT6MSG_WRONGMIF); + if (mrt->mroute_do_wrvifwhole) + ip6mr_cache_report(mrt, skb, true_vifi, + MRT6MSG_WRMIFWHOLE); } goto dont_forward; } diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index a733803a710c..222f6bf220ba 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -475,7 +475,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, sock_prot_inuse_add(net, sk->sk_prot, -1); sock_prot_inuse_add(net, &tcp_prot, 1); - sk->sk_prot = &tcp_prot; + /* Paired with READ_ONCE(sk->sk_prot) in net/ipv6/af_inet6.c */ + WRITE_ONCE(sk->sk_prot, &tcp_prot); icsk->icsk_af_ops = &ipv4_specific; sk->sk_socket->ops = &inet_stream_ops; sk->sk_family = PF_INET; @@ -489,7 +490,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, sock_prot_inuse_add(net, sk->sk_prot, -1); sock_prot_inuse_add(net, prot, 1); - sk->sk_prot = prot; + /* Paired with READ_ONCE(sk->sk_prot) in net/ipv6/af_inet6.c */ + WRITE_ONCE(sk->sk_prot, prot); sk->sk_socket->ops = &inet_dgram_ops; sk->sk_family = PF_INET; } diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index f03b597e4121..fcb288b0ae13 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -466,9 +466,8 @@ static void ip6_nd_hdr(struct sk_buff *skb, hdr->daddr = *daddr; } -static void ndisc_send_skb(struct sk_buff *skb, - const struct in6_addr *daddr, - const struct in6_addr *saddr) +void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, + const struct in6_addr *saddr) { struct dst_entry *dst = skb_dst(skb); struct net *net = dev_net(skb->dev); @@ -515,6 +514,7 @@ static void ndisc_send_skb(struct sk_buff *skb, rcu_read_unlock(); } +EXPORT_SYMBOL(ndisc_send_skb); void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr, const struct in6_addr *solicited_addr, @@ -598,22 +598,16 @@ static void ndisc_send_unsol_na(struct net_device *dev) in6_dev_put(idev); } -void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, - const struct in6_addr *daddr, const struct in6_addr *saddr, - u64 nonce) +struct sk_buff *ndisc_ns_create(struct net_device *dev, const struct in6_addr *solicit, + const struct in6_addr *saddr, u64 nonce) { - struct sk_buff *skb; - struct in6_addr addr_buf; int inc_opt = dev->addr_len; - int optlen = 0; + struct sk_buff *skb; struct nd_msg *msg; + int optlen = 0; - if (!saddr) { - if (ipv6_get_lladdr(dev, &addr_buf, - (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC))) - return; - saddr = &addr_buf; - } + if (!saddr) + return NULL; if (ipv6_addr_any(saddr)) inc_opt = false; @@ -625,7 +619,7 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); if (!skb) - return; + return NULL; msg = skb_put(skb, sizeof(*msg)); *msg = (struct nd_msg) { @@ -647,7 +641,28 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, memcpy(opt + 2, &nonce, 6); } - ndisc_send_skb(skb, daddr, saddr); + return skb; +} +EXPORT_SYMBOL(ndisc_ns_create); + +void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, + const struct in6_addr *daddr, const struct in6_addr *saddr, + u64 nonce) +{ + struct in6_addr addr_buf; + struct sk_buff *skb; + + if (!saddr) { + if (ipv6_get_lladdr(dev, &addr_buf, + (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC))) + return; + saddr = &addr_buf; + } + + skb = ndisc_ns_create(dev, solicit, saddr, nonce); + + if (skb) + ndisc_send_skb(skb, daddr, saddr); } void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, @@ -1337,8 +1352,12 @@ static void ndisc_router_discovery(struct sk_buff *skb) return; } neigh->flags |= NTF_ROUTER; - } else if (rt) { + } else if (rt && IPV6_EXTRACT_PREF(rt->fib6_flags) != pref) { + struct nl_info nlinfo = { + .nl_net = net, + }; rt->fib6_flags = (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); + inet6_rt_notify(RTM_NEWROUTE, rt, &nlinfo, NLM_F_REPLACE); } if (rt) diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 6ab710b5a1a8..1da332450d98 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -121,6 +121,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, struct sk_buff *)) { int frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size; + bool mono_delivery_time = skb->mono_delivery_time; ktime_t tstamp = skb->tstamp; struct ip6_frag_state state; u8 *prevhdr, nexthdr = 0; @@ -186,7 +187,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, if (iter.frag) ip6_fraglist_prepare(skb, &iter); - skb->tstamp = tstamp; + skb_set_delivery_time(skb, tstamp, mono_delivery_time); err = output(net, sk, data, skb); if (err || !iter.frag) break; @@ -219,7 +220,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, goto blackhole; } - skb2->tstamp = tstamp; + skb_set_delivery_time(skb2, tstamp, mono_delivery_time); err = output(net, sk, data, skb2); if (err) goto blackhole; diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 5c47be29b9ee..7dd3629dd19e 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -264,6 +264,7 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb, fq->iif = dev->ifindex; fq->q.stamp = skb->tstamp; + fq->q.mono_delivery_time = skb->mono_delivery_time; fq->q.meat += skb->len; fq->ecn |= ecn; if (payload_len > fq->q.max_size) diff --git a/net/ipv6/netfilter/nft_dup_ipv6.c b/net/ipv6/netfilter/nft_dup_ipv6.c index 3a00d95e964e..70a405b4006f 100644 --- a/net/ipv6/netfilter/nft_dup_ipv6.c +++ b/net/ipv6/netfilter/nft_dup_ipv6.c @@ -73,6 +73,7 @@ static const struct nft_expr_ops nft_dup_ipv6_ops = { .eval = nft_dup_ipv6_eval, .init = nft_dup_ipv6_init, .dump = nft_dup_ipv6_dump, + .reduce = NFT_REDUCE_READONLY, }; static const struct nla_policy nft_dup_ipv6_policy[NFTA_DUP_MAX + 1] = { diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c index 92f3235fa287..b3f163b40c2b 100644 --- a/net/ipv6/netfilter/nft_fib_ipv6.c +++ b/net/ipv6/netfilter/nft_fib_ipv6.c @@ -211,6 +211,7 @@ static const struct nft_expr_ops nft_fib6_type_ops = { .init = nft_fib_init, .dump = nft_fib_dump, .validate = nft_fib_validate, + .reduce = nft_fib_reduce, }; static const struct nft_expr_ops nft_fib6_ops = { @@ -220,6 +221,7 @@ static const struct nft_expr_ops nft_fib6_ops = { .init = nft_fib_init, .dump = nft_fib_dump, .validate = nft_fib_validate, + .reduce = nft_fib_reduce, }; static const struct nft_expr_ops * diff --git a/net/ipv6/netfilter/nft_reject_ipv6.c b/net/ipv6/netfilter/nft_reject_ipv6.c index ed69c768797e..5c61294f410e 100644 --- a/net/ipv6/netfilter/nft_reject_ipv6.c +++ b/net/ipv6/netfilter/nft_reject_ipv6.c @@ -46,6 +46,7 @@ static const struct nft_expr_ops nft_reject_ipv6_ops = { .init = nft_reject_init, .dump = nft_reject_dump, .validate = nft_reject_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_reject_ipv6_type __read_mostly = { diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 9256f6ba87ef..ff033d16549e 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -59,8 +59,6 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) struct pingfakehdr pfh; struct ipcm6_cookie ipc6; - pr_debug("ping_v6_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); - err = ping_common_sendmsg(AF_INET6, msg, len, &user_icmph, sizeof(user_icmph)); if (err) @@ -99,7 +97,25 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) (oif && sk->sk_bound_dev_if && oif != sk->sk_bound_dev_if)) return -EINVAL; - /* TODO: use ip6_datagram_send_ctl to get options from cmsg */ + ipcm6_init_sk(&ipc6, np); + ipc6.sockc.tsflags = sk->sk_tsflags; + ipc6.sockc.mark = sk->sk_mark; + + if (msg->msg_controllen) { + struct ipv6_txoptions opt = {}; + + opt.tot_len = sizeof(opt); + ipc6.opt = &opt; + + err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, &ipc6); + if (err < 0) + return err; + + /* Changes to txoptions and flow info are not implemented, yet. + * Drop the options, fl6 is wiped below. + */ + ipc6.opt = NULL; + } memset(&fl6, 0, sizeof(fl6)); @@ -107,14 +123,12 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) fl6.saddr = np->saddr; fl6.daddr = *daddr; fl6.flowi6_oif = oif; - fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_mark = ipc6.sockc.mark; fl6.flowi6_uid = sk->sk_uid; fl6.fl6_icmp_type = user_icmph.icmp6_type; fl6.fl6_icmp_code = user_icmph.icmp6_code; security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6)); - ipcm6_init_sk(&ipc6, np); - ipc6.sockc.mark = sk->sk_mark; fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, false); @@ -136,7 +150,8 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) pfh.wcheck = 0; pfh.family = AF_INET6; - ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); + if (ipc6.hlimit < 0) + ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); lock_sock(sk); err = ip6_append_data(sk, ping_getfrag, &pfh, len, diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 28e44782c94d..ff866f2a879e 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -194,6 +194,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, fq->iif = dev->ifindex; fq->q.stamp = skb->tstamp; + fq->q.mono_delivery_time = skb->mono_delivery_time; fq->q.meat += skb->len; fq->ecn |= ecn; add_frag_mem_limit(fq->q.fqdir, skb->truesize); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index ea1cf414a92e..2fa10e60cccd 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -130,6 +130,7 @@ static struct fib6_info *rt6_get_route_info(struct net *net, struct uncached_list { spinlock_t lock; struct list_head head; + struct list_head quarantine; }; static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list); @@ -149,35 +150,34 @@ void rt6_uncached_list_del(struct rt6_info *rt) { if (!list_empty(&rt->rt6i_uncached)) { struct uncached_list *ul = rt->rt6i_uncached_list; - struct net *net = dev_net(rt->dst.dev); spin_lock_bh(&ul->lock); - list_del(&rt->rt6i_uncached); - atomic_dec(&net->ipv6.rt6_stats->fib_rt_uncache); + list_del_init(&rt->rt6i_uncached); spin_unlock_bh(&ul->lock); } } -static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) +static void rt6_uncached_list_flush_dev(struct net_device *dev) { - struct net_device *loopback_dev = net->loopback_dev; int cpu; - if (dev == loopback_dev) - return; - for_each_possible_cpu(cpu) { struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); - struct rt6_info *rt; + struct rt6_info *rt, *safe; + + if (list_empty(&ul->head)) + continue; spin_lock_bh(&ul->lock); - list_for_each_entry(rt, &ul->head, rt6i_uncached) { + list_for_each_entry_safe(rt, safe, &ul->head, rt6i_uncached) { struct inet6_dev *rt_idev = rt->rt6i_idev; struct net_device *rt_dev = rt->dst.dev; + bool handled = false; if (rt_idev->dev == dev) { - rt->rt6i_idev = in6_dev_get(loopback_dev); + rt->rt6i_idev = in6_dev_get(blackhole_netdev); in6_dev_put(rt_idev); + handled = true; } if (rt_dev == dev) { @@ -185,7 +185,11 @@ static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) dev_replace_track(rt_dev, blackhole_netdev, &rt->dst.dev_tracker, GFP_ATOMIC); + handled = true; } + if (handled) + list_move(&rt->rt6i_uncached, + &ul->quarantine); } spin_unlock_bh(&ul->lock); } @@ -373,13 +377,12 @@ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, { struct rt6_info *rt = (struct rt6_info *)dst; struct inet6_dev *idev = rt->rt6i_idev; - struct net_device *loopback_dev = - dev_net(dev)->loopback_dev; - if (idev && idev->dev != loopback_dev) { - struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev); - if (loopback_idev) { - rt->rt6i_idev = loopback_idev; + if (idev && idev->dev != blackhole_netdev) { + struct inet6_dev *blackhole_idev = in6_dev_get(blackhole_netdev); + + if (blackhole_idev) { + rt->rt6i_idev = blackhole_idev; in6_dev_put(idev); } } @@ -1206,9 +1209,6 @@ INDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_lookup(struct net *net, struct fib6_node *fn; struct rt6_info *rt; - if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) - flags &= ~RT6_LOOKUP_F_IFACE; - rcu_read_lock(); fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); restart: @@ -2178,9 +2178,6 @@ int fib6_table_lookup(struct net *net, struct fib6_table *table, int oif, fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); saved_fn = fn; - if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) - oif = 0; - redo_rt6_select: rt6_select(net, fn, oif, res, strict); if (res->f6i == net->ipv6.fib6_null_entry) { @@ -2244,7 +2241,6 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, * if caller sets RT6_LOOKUP_F_DST_NOREF flag. */ rt6_uncached_list_add(rt); - atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache); rcu_read_unlock(); return rt; @@ -3056,12 +3052,6 @@ INDIRECT_CALLABLE_SCOPE struct rt6_info *__ip6_route_redirect(struct net *net, struct fib6_info *rt; struct fib6_node *fn; - /* l3mdev_update_flow overrides oif if the device is enslaved; in - * this case we must match on the real ingress device, so reset it - */ - if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) - fl6->flowi6_oif = skb->dev->ifindex; - /* Get the "current" route for this destination and * check if the redirect has come from appropriate router. * @@ -3287,7 +3277,6 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, * do proper release of the net_device */ rt6_uncached_list_add(rt); - atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache); dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); @@ -4896,7 +4885,7 @@ void rt6_sync_down_dev(struct net_device *dev, unsigned long event) void rt6_disable_ip(struct net_device *dev, unsigned long event) { rt6_sync_down_dev(dev, event); - rt6_uncached_list_flush_dev(dev_net(dev), dev); + rt6_uncached_list_flush_dev(dev); neigh_ifdown(&nd_tbl, dev); } @@ -5009,6 +4998,12 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, err = -EINVAL; rtm = nlmsg_data(nlh); + if (rtm->rtm_tos) { + NL_SET_ERR_MSG(extack, + "Invalid dsfield (tos): option not available for IPv6"); + goto errout; + } + *cfg = (struct fib6_config){ .fc_table = rtm->rtm_table, .fc_dst_len = rtm->rtm_dst_len, @@ -6731,6 +6726,7 @@ int __init ip6_route_init(void) struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); INIT_LIST_HEAD(&ul->head); + INIT_LIST_HEAD(&ul->quarantine); spin_lock_init(&ul->lock); } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 075ee8a2df3b..13678d3908fa 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -148,6 +148,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr; struct inet_sock *inet = inet_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); + struct inet_timewait_death_row *tcp_death_row; struct ipv6_pinfo *np = tcp_inet6_sk(sk); struct tcp_sock *tp = tcp_sk(sk); struct in6_addr *saddr = NULL, *final_p, final; @@ -156,7 +157,6 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, struct dst_entry *dst; int addr_type; int err; - struct inet_timewait_death_row *tcp_death_row = &sock_net(sk)->ipv4.tcp_death_row; if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; @@ -308,6 +308,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, inet->inet_dport = usin->sin6_port; tcp_set_state(sk, TCP_SYN_SENT); + tcp_death_row = sock_net(sk)->ipv4.tcp_death_row; err = inet6_hash_connect(tcp_death_row, sk); if (err) goto late_failure; @@ -772,57 +773,6 @@ static int tcp_v6_md5_hash_skb(char *md5_hash, #endif -static bool tcp_v6_inbound_md5_hash(const struct sock *sk, - const struct sk_buff *skb, - int dif, int sdif) -{ -#ifdef CONFIG_TCP_MD5SIG - const __u8 *hash_location = NULL; - struct tcp_md5sig_key *hash_expected; - const struct ipv6hdr *ip6h = ipv6_hdr(skb); - const struct tcphdr *th = tcp_hdr(skb); - int genhash, l3index; - u8 newhash[16]; - - /* sdif set, means packet ingressed via a device - * in an L3 domain and dif is set to the l3mdev - */ - l3index = sdif ? dif : 0; - - hash_expected = tcp_v6_md5_do_lookup(sk, &ip6h->saddr, l3index); - hash_location = tcp_parse_md5sig_option(th); - - /* We've parsed the options - do we have a hash? */ - if (!hash_expected && !hash_location) - return false; - - if (hash_expected && !hash_location) { - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); - return true; - } - - if (!hash_expected && hash_location) { - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED); - return true; - } - - /* check the signature */ - genhash = tcp_v6_md5_hash_skb(newhash, - hash_expected, - NULL, skb); - - if (genhash || memcmp(hash_location, newhash, 16) != 0) { - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE); - net_info_ratelimited("MD5 Hash %s for [%pI6c]:%u->[%pI6c]:%u L3 index %d\n", - genhash ? "failed" : "mismatch", - &ip6h->saddr, ntohs(th->source), - &ip6h->daddr, ntohs(th->dest), l3index); - return true; - } -#endif - return false; -} - static void tcp_v6_init_req(struct request_sock *req, const struct sock *sk_listener, struct sk_buff *skb) @@ -920,12 +870,11 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 } #endif - buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) + tot_len, - GFP_ATOMIC); + buff = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC); if (!buff) return; - skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr) + tot_len); + skb_reserve(buff, MAX_TCP_HEADER); t1 = skb_push(buff, tot_len); skb_reset_transport_header(buff); @@ -991,7 +940,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 } else { mark = sk->sk_mark; } - buff->tstamp = tcp_transmit_time(sk); + skb_set_delivery_time(buff, tcp_transmit_time(sk), true); } fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark) ?: mark; fl6.fl6_dport = t1->dest; @@ -1471,6 +1420,7 @@ int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) { struct ipv6_pinfo *np = tcp_inet6_sk(sk); struct sk_buff *opt_skb = NULL; + enum skb_drop_reason reason; struct tcp_sock *tp; /* Imagine: socket is IPv6. IPv4 packet arrives, @@ -1505,6 +1455,7 @@ int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) if (np->rxopt.all) opt_skb = skb_clone(skb, sk_gfp_mask(sk, GFP_ATOMIC)); + reason = SKB_DROP_REASON_NOT_SPECIFIED; if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ struct dst_entry *dst; @@ -1558,9 +1509,10 @@ int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) discard: if (opt_skb) __kfree_skb(opt_skb); - kfree_skb(skb); + kfree_skb_reason(skb, reason); return 0; csum_err: + reason = SKB_DROP_REASON_TCP_CSUM; trace_tcp_bad_csum(skb); TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS); TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS); @@ -1626,6 +1578,7 @@ static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr, INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) { + enum skb_drop_reason drop_reason; int sdif = inet6_sdif(skb); int dif = inet6_iif(skb); const struct tcphdr *th; @@ -1635,6 +1588,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) int ret; struct net *net = dev_net(skb->dev); + drop_reason = SKB_DROP_REASON_NOT_SPECIFIED; if (skb->pkt_type != PACKET_HOST) goto discard_it; @@ -1648,8 +1602,10 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) th = (const struct tcphdr *)skb->data; - if (unlikely(th->doff < sizeof(struct tcphdr)/4)) + if (unlikely(th->doff < sizeof(struct tcphdr) / 4)) { + drop_reason = SKB_DROP_REASON_PKT_TOO_SMALL; goto bad_packet; + } if (!pskb_may_pull(skb, th->doff*4)) goto discard_it; @@ -1676,7 +1632,10 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) struct sock *nsk; sk = req->rsk_listener; - if (tcp_v6_inbound_md5_hash(sk, skb, dif, sdif)) { + drop_reason = tcp_inbound_md5_hash(sk, skb, + &hdr->saddr, &hdr->daddr, + AF_INET6, dif, sdif); + if (drop_reason) { sk_drops_add(sk, skb); reqsk_put(req); goto discard_it; @@ -1705,6 +1664,8 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) hdr = ipv6_hdr(skb); tcp_v6_fill_cb(skb, hdr, th); nsk = tcp_check_req(sk, skb, req, false, &req_stolen); + } else { + drop_reason = SKB_DROP_REASON_SOCKET_FILTER; } if (!nsk) { reqsk_put(req); @@ -1740,14 +1701,20 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) } } - if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) + if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) { + drop_reason = SKB_DROP_REASON_XFRM_POLICY; + goto discard_and_relse; + } + + drop_reason = tcp_inbound_md5_hash(sk, skb, &hdr->saddr, &hdr->daddr, + AF_INET6, dif, sdif); + if (drop_reason) goto discard_and_relse; - if (tcp_v6_inbound_md5_hash(sk, skb, dif, sdif)) - goto discard_and_relse; - - if (tcp_filter(sk, skb)) + if (tcp_filter(sk, skb)) { + drop_reason = SKB_DROP_REASON_SOCKET_FILTER; goto discard_and_relse; + } th = (const struct tcphdr *)skb->data; hdr = ipv6_hdr(skb); tcp_v6_fill_cb(skb, hdr, th); @@ -1768,7 +1735,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) if (!sock_owned_by_user(sk)) { ret = tcp_v6_do_rcv(sk, skb); } else { - if (tcp_add_backlog(sk, skb)) + if (tcp_add_backlog(sk, skb, &drop_reason)) goto discard_and_relse; } bh_unlock_sock(sk); @@ -1778,6 +1745,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) return ret ? -1 : 0; no_tcp_socket: + drop_reason = SKB_DROP_REASON_NO_SOCKET; if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) goto discard_it; @@ -1785,6 +1753,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) if (tcp_checksum_complete(skb)) { csum_error: + drop_reason = SKB_DROP_REASON_TCP_CSUM; trace_tcp_bad_csum(skb); __TCP_INC_STATS(net, TCP_MIB_CSUMERRORS); bad_packet: @@ -1794,7 +1763,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) } discard_it: - kfree_skb(skb); + kfree_skb_reason(skb, drop_reason); return 0; discard_and_relse: @@ -1805,6 +1774,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) do_time_wait: if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { + drop_reason = SKB_DROP_REASON_XFRM_POLICY; inet_twsk_put(inet_twsk(sk)); goto discard_it; } @@ -2237,15 +2207,9 @@ static void __net_exit tcpv6_net_exit(struct net *net) inet_ctl_sock_destroy(net->ipv6.tcp_sk); } -static void __net_exit tcpv6_net_exit_batch(struct list_head *net_exit_list) -{ - inet_twsk_purge(&tcp_hashinfo, AF_INET6); -} - static struct pernet_operations tcpv6_net_ops = { .init = tcpv6_net_init, .exit = tcpv6_net_exit, - .exit_batch = tcpv6_net_exit_batch, }; int __init tcpv6_init(void) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 528b81ef19c9..7f0fa9bd9ffe 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -912,6 +912,7 @@ static int udp6_unicast_rcv_skb(struct sock *sk, struct sk_buff *skb, int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, int proto) { + enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED; const struct in6_addr *saddr, *daddr; struct net *net = dev_net(skb->dev); struct udphdr *uh; @@ -988,6 +989,8 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, return udp6_unicast_rcv_skb(sk, skb, uh); } + reason = SKB_DROP_REASON_NO_SOCKET; + if (!uh->check) goto report_csum_error; @@ -1000,10 +1003,12 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, __UDP6_INC_STATS(net, UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE); icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); - kfree_skb(skb); + kfree_skb_reason(skb, reason); return 0; short_packet: + if (reason == SKB_DROP_REASON_NOT_SPECIFIED) + reason = SKB_DROP_REASON_PKT_TOO_SMALL; net_dbg_ratelimited("UDP%sv6: short packet: From [%pI6c]:%u %d/%d to [%pI6c]:%u\n", proto == IPPROTO_UDPLITE ? "-Lite" : "", saddr, ntohs(uh->source), @@ -1014,10 +1019,12 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, report_csum_error: udp6_csum_zero_error(skb); csum_error: + if (reason == SKB_DROP_REASON_NOT_SPECIFIED) + reason = SKB_DROP_REASON_UDP_CSUM; __UDP6_INC_STATS(net, UDP_MIB_CSUMERRORS, proto == IPPROTO_UDPLITE); discard: __UDP6_INC_STATS(net, UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE); - kfree_skb(skb); + kfree_skb_reason(skb, reason); return 0; } @@ -1266,23 +1273,17 @@ static int udp_v6_push_pending_frames(struct sock *sk) { struct sk_buff *skb; struct udp_sock *up = udp_sk(sk); - struct flowi6 fl6; int err = 0; if (up->pending == AF_INET) return udp_push_pending_frames(sk); - /* ip6_finish_skb will release the cork, so make a copy of - * fl6 here. - */ - fl6 = inet_sk(sk)->cork.fl.u.ip6; - skb = ip6_finish_skb(sk); if (!skb) goto out; - err = udp_v6_send_skb(skb, &fl6, &inet_sk(sk)->cork.base); - + err = udp_v6_send_skb(skb, &inet_sk(sk)->cork.fl.u.ip6, + &inet_sk(sk)->cork.base); out: up->len = 0; up->pending = 0; @@ -1300,7 +1301,8 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) struct ipv6_txoptions *opt = NULL; struct ipv6_txoptions *opt_to_free = NULL; struct ip6_flowlabel *flowlabel = NULL; - struct flowi6 fl6; + struct inet_cork_full cork; + struct flowi6 *fl6 = &cork.fl.u.ip6; struct dst_entry *dst; struct ipcm6_cookie ipc6; int addr_len = msg->msg_namelen; @@ -1363,9 +1365,6 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) } } - if (up->pending == AF_INET) - return udp_sendmsg(sk, msg, len); - /* Rough check on arithmetic overflow, better check is made in ip6_append_data(). */ @@ -1374,6 +1373,8 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag; if (up->pending) { + if (up->pending == AF_INET) + return udp_sendmsg(sk, msg, len); /* * There are pending frames. * The socket lock must be held while it's corked. @@ -1391,19 +1392,19 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) } ulen += sizeof(struct udphdr); - memset(&fl6, 0, sizeof(fl6)); + memset(fl6, 0, sizeof(*fl6)); if (sin6) { if (sin6->sin6_port == 0) return -EINVAL; - fl6.fl6_dport = sin6->sin6_port; + fl6->fl6_dport = sin6->sin6_port; daddr = &sin6->sin6_addr; if (np->sndflow) { - fl6.flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; - if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) { - flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); + fl6->flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; + if (fl6->flowlabel & IPV6_FLOWLABEL_MASK) { + flowlabel = fl6_sock_lookup(sk, fl6->flowlabel); if (IS_ERR(flowlabel)) return -EINVAL; } @@ -1420,24 +1421,24 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) if (addr_len >= sizeof(struct sockaddr_in6) && sin6->sin6_scope_id && __ipv6_addr_needs_scope_id(__ipv6_addr_type(daddr))) - fl6.flowi6_oif = sin6->sin6_scope_id; + fl6->flowi6_oif = sin6->sin6_scope_id; } else { if (sk->sk_state != TCP_ESTABLISHED) return -EDESTADDRREQ; - fl6.fl6_dport = inet->inet_dport; + fl6->fl6_dport = inet->inet_dport; daddr = &sk->sk_v6_daddr; - fl6.flowlabel = np->flow_label; + fl6->flowlabel = np->flow_label; connected = true; } - if (!fl6.flowi6_oif) - fl6.flowi6_oif = sk->sk_bound_dev_if; + if (!fl6->flowi6_oif) + fl6->flowi6_oif = sk->sk_bound_dev_if; - if (!fl6.flowi6_oif) - fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; + if (!fl6->flowi6_oif) + fl6->flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; - fl6.flowi6_uid = sk->sk_uid; + fl6->flowi6_uid = sk->sk_uid; if (msg->msg_controllen) { opt = &opt_space; @@ -1447,14 +1448,14 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) err = udp_cmsg_send(sk, msg, &ipc6.gso_size); if (err > 0) - err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, + err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, fl6, &ipc6); if (err < 0) { fl6_sock_release(flowlabel); return err; } - if ((fl6.flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) { - flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); + if ((fl6->flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) { + flowlabel = fl6_sock_lookup(sk, fl6->flowlabel); if (IS_ERR(flowlabel)) return -EINVAL; } @@ -1471,16 +1472,17 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) opt = ipv6_fixup_options(&opt_space, opt); ipc6.opt = opt; - fl6.flowi6_proto = sk->sk_protocol; - fl6.flowi6_mark = ipc6.sockc.mark; - fl6.daddr = *daddr; - if (ipv6_addr_any(&fl6.saddr) && !ipv6_addr_any(&np->saddr)) - fl6.saddr = np->saddr; - fl6.fl6_sport = inet->inet_sport; + fl6->flowi6_proto = sk->sk_protocol; + fl6->flowi6_mark = ipc6.sockc.mark; + fl6->daddr = *daddr; + if (ipv6_addr_any(&fl6->saddr) && !ipv6_addr_any(&np->saddr)) + fl6->saddr = np->saddr; + fl6->fl6_sport = inet->inet_sport; if (cgroup_bpf_enabled(CGROUP_UDP6_SENDMSG) && !connected) { err = BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK(sk, - (struct sockaddr *)sin6, &fl6.saddr); + (struct sockaddr *)sin6, + &fl6->saddr); if (err) goto out_no_dst; if (sin6) { @@ -1496,32 +1498,32 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) err = -EINVAL; goto out_no_dst; } - fl6.fl6_dport = sin6->sin6_port; - fl6.daddr = sin6->sin6_addr; + fl6->fl6_dport = sin6->sin6_port; + fl6->daddr = sin6->sin6_addr; } } - if (ipv6_addr_any(&fl6.daddr)) - fl6.daddr.s6_addr[15] = 0x1; /* :: means loopback (BSD'ism) */ + if (ipv6_addr_any(&fl6->daddr)) + fl6->daddr.s6_addr[15] = 0x1; /* :: means loopback (BSD'ism) */ - final_p = fl6_update_dst(&fl6, opt, &final); + final_p = fl6_update_dst(fl6, opt, &final); if (final_p) connected = false; - if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) { - fl6.flowi6_oif = np->mcast_oif; + if (!fl6->flowi6_oif && ipv6_addr_is_multicast(&fl6->daddr)) { + fl6->flowi6_oif = np->mcast_oif; connected = false; - } else if (!fl6.flowi6_oif) - fl6.flowi6_oif = np->ucast_oif; + } else if (!fl6->flowi6_oif) + fl6->flowi6_oif = np->ucast_oif; - security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6)); + security_sk_classify_flow(sk, flowi6_to_flowi_common(fl6)); if (ipc6.tclass < 0) ipc6.tclass = np->tclass; - fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); + fl6->flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6->flowlabel); - dst = ip6_sk_dst_lookup_flow(sk, &fl6, final_p, connected); + dst = ip6_sk_dst_lookup_flow(sk, fl6, final_p, connected); if (IS_ERR(dst)) { err = PTR_ERR(dst); dst = NULL; @@ -1529,7 +1531,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) } if (ipc6.hlimit < 0) - ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); + ipc6.hlimit = ip6_sk_dst_hoplimit(np, fl6, dst); if (msg->msg_flags&MSG_CONFIRM) goto do_confirm; @@ -1537,17 +1539,17 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) /* Lockless fast path for the non-corking case */ if (!corkreq) { - struct inet_cork_full cork; struct sk_buff *skb; skb = ip6_make_skb(sk, getfrag, msg, ulen, sizeof(struct udphdr), &ipc6, - &fl6, (struct rt6_info *)dst, + (struct rt6_info *)dst, msg->msg_flags, &cork); err = PTR_ERR(skb); if (!IS_ERR_OR_NULL(skb)) - err = udp_v6_send_skb(skb, &fl6, &cork.base); - goto out; + err = udp_v6_send_skb(skb, fl6, &cork.base); + /* ip6_make_skb steals dst reference */ + goto out_no_dst; } lock_sock(sk); @@ -1568,7 +1570,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ipc6.dontfrag = np->dontfrag; up->len += ulen; err = ip6_append_data(sk, getfrag, msg, ulen, sizeof(struct udphdr), - &ipc6, &fl6, (struct rt6_info *)dst, + &ipc6, fl6, (struct rt6_info *)dst, corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); if (err) udp_v6_flush_pending_frames(sk); @@ -1603,7 +1605,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) do_confirm: if (msg->msg_flags & MSG_PROBE) - dst_confirm_neigh(dst, &fl6.daddr); + dst_confirm_neigh(dst, &fl6->daddr); if (!(msg->msg_flags&MSG_PROBE) || len) goto back_from_confirm; err = 0; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index fad687ee6dd8..e64e427a51cf 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -33,8 +33,7 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, int err; memset(&fl6, 0, sizeof(fl6)); - fl6.flowi6_oif = l3mdev_master_ifindex_by_index(net, oif); - fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF; + fl6.flowi6_l3mdev = l3mdev_master_ifindex_by_index(net, oif); fl6.flowi6_mark = mark; memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr)); if (saddr) @@ -92,7 +91,6 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.rt6.rt6i_src = rt->rt6i_src; INIT_LIST_HEAD(&xdst->u.rt6.rt6i_uncached); rt6_uncached_list_add(&xdst->u.rt6); - atomic_inc(&dev_net(dev)->ipv6.rt6_stats->fib_rt_uncache); return 0; } diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 8f4d49a7d3e8..eb0295d90039 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -319,7 +319,7 @@ static inline int iucv_call_b2f0(int command, union iucv_param *parm) */ static int __iucv_query_maxconn(void *param, unsigned long *max_pathid) { - unsigned long reg1 = (unsigned long)param; + unsigned long reg1 = virt_to_phys(param); int cc; asm volatile ( diff --git a/net/l3mdev/l3mdev.c b/net/l3mdev/l3mdev.c index 17927966abb3..4eb8892fb2ff 100644 --- a/net/l3mdev/l3mdev.c +++ b/net/l3mdev/l3mdev.c @@ -250,25 +250,19 @@ int l3mdev_fib_rule_match(struct net *net, struct flowi *fl, struct net_device *dev; int rc = 0; + /* update flow ensures flowi_l3mdev is set when relevant */ + if (!fl->flowi_l3mdev) + return 0; + rcu_read_lock(); - dev = dev_get_by_index_rcu(net, fl->flowi_oif); + dev = dev_get_by_index_rcu(net, fl->flowi_l3mdev); if (dev && netif_is_l3_master(dev) && dev->l3mdev_ops->l3mdev_fib_table) { arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev); rc = 1; - goto out; } - dev = dev_get_by_index_rcu(net, fl->flowi_iif); - if (dev && netif_is_l3_master(dev) && - dev->l3mdev_ops->l3mdev_fib_table) { - arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev); - rc = 1; - goto out; - } - -out: rcu_read_unlock(); return rc; @@ -277,31 +271,28 @@ int l3mdev_fib_rule_match(struct net *net, struct flowi *fl, void l3mdev_update_flow(struct net *net, struct flowi *fl) { struct net_device *dev; - int ifindex; rcu_read_lock(); if (fl->flowi_oif) { dev = dev_get_by_index_rcu(net, fl->flowi_oif); if (dev) { - ifindex = l3mdev_master_ifindex_rcu(dev); - if (ifindex) { - fl->flowi_oif = ifindex; - fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF; - goto out; - } + if (!fl->flowi_l3mdev) + fl->flowi_l3mdev = l3mdev_master_ifindex_rcu(dev); + + /* oif set to L3mdev directs lookup to its table; + * reset to avoid oif match in fib_lookup + */ + if (netif_is_l3_master(dev)) + fl->flowi_oif = 0; + goto out; } } - if (fl->flowi_iif) { + if (fl->flowi_iif > LOOPBACK_IFINDEX && !fl->flowi_l3mdev) { dev = dev_get_by_index_rcu(net, fl->flowi_iif); - if (dev) { - ifindex = l3mdev_master_ifindex_rcu(dev); - if (ifindex) { - fl->flowi_iif = ifindex; - fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF; - } - } + if (dev) + fl->flowi_l3mdev = l3mdev_master_ifindex_rcu(dev); } out: diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 26c00ebf4fba..c86256064743 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -311,6 +311,10 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr) sock_reset_flag(sk, SOCK_ZAPPED); rc = 0; out: + if (rc) { + dev_put_track(llc->dev, &llc->dev_tracker); + llc->dev = NULL; + } return rc; } @@ -408,6 +412,10 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) out_put: llc_sap_put(sap); out: + if (rc) { + dev_put_track(llc->dev, &llc->dev_tracker); + llc->dev = NULL; + } release_sock(sk); return rc; } diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 23d25e8b2358..af1df3a6bd55 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -34,7 +34,8 @@ mac80211-y := \ trace.o mlme.o \ tdls.o \ ocb.o \ - airtime.o + airtime.o \ + eht.o mac80211-$(CONFIG_MAC80211_LEDS) += led.o mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 7d2925bb966e..218cdc554d71 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -180,7 +180,8 @@ static void sta_rx_agg_reorder_timer_expired(struct timer_list *t) static void ieee80211_add_addbaext(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - const struct ieee80211_addba_ext_ie *req) + const struct ieee80211_addba_ext_ie *req, + u16 buf_size) { struct ieee80211_supported_band *sband; struct ieee80211_addba_ext_ie *resp; @@ -210,6 +211,8 @@ static void ieee80211_add_addbaext(struct ieee80211_sub_if_data *sdata, frag_level = cap_frag_level; resp->data |= u8_encode_bits(frag_level, IEEE80211_ADDBA_EXT_FRAG_LEVEL_MASK); + resp->data |= u8_encode_bits(buf_size >> IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT, + IEEE80211_ADDBA_EXT_BUF_SIZE_MASK); } static void ieee80211_send_addba_resp(struct sta_info *sta, u8 *da, u16 tid, @@ -261,7 +264,7 @@ static void ieee80211_send_addba_resp(struct sta_info *sta, u8 *da, u16 tid, mgmt->u.action.u.addba_resp.status = cpu_to_le16(status); if (sta->sta.he_cap.has_he && addbaext) - ieee80211_add_addbaext(sdata, skb, addbaext); + ieee80211_add_addbaext(sdata, skb, addbaext, buf_size); ieee80211_tx_skb(sdata, skb); } @@ -309,8 +312,10 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta, goto end; } - if (sta->sta.he_cap.has_he) - max_buf_size = IEEE80211_MAX_AMPDU_BUF; + if (sta->sta.eht_cap.has_eht) + max_buf_size = IEEE80211_MAX_AMPDU_BUF_EHT; + else if (sta->sta.he_cap.has_he) + max_buf_size = IEEE80211_MAX_AMPDU_BUF_HE; else max_buf_size = IEEE80211_MAX_AMPDU_BUF_HT; @@ -502,6 +507,13 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, goto free; } + if (sta->sta.eht_cap.has_eht && elems && elems->addba_ext_ie) { + u8 buf_size_1k = u8_get_bits(elems->addba_ext_ie->data, + IEEE80211_ADDBA_EXT_BUF_SIZE_MASK); + + buf_size |= buf_size_1k << IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT; + } + __ieee80211_start_rx_ba_session(sta, dialog_token, timeout, start_seq_num, ba_policy, tid, buf_size, true, false, diff --git a/net/mac80211/airtime.c b/net/mac80211/airtime.c index 26d2f8ba7029..2619e12c8bda 100644 --- a/net/mac80211/airtime.c +++ b/net/mac80211/airtime.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: ISC /* * Copyright (C) 2019 Felix Fietkau + * Copyright (C) 2021 Intel Corporation */ #include @@ -67,17 +68,11 @@ #define IEEE80211_VHT_STREAM_GROUPS 8 /* BW(=4) * SGI(=2) */ #define IEEE80211_HE_MAX_STREAMS 8 -#define IEEE80211_HE_STREAM_GROUPS 12 /* BW(=4) * GI(=3) */ #define IEEE80211_HT_GROUPS_NB (IEEE80211_MAX_STREAMS * \ IEEE80211_HT_STREAM_GROUPS) #define IEEE80211_VHT_GROUPS_NB (IEEE80211_MAX_STREAMS * \ IEEE80211_VHT_STREAM_GROUPS) -#define IEEE80211_HE_GROUPS_NB (IEEE80211_HE_MAX_STREAMS * \ - IEEE80211_HE_STREAM_GROUPS) -#define IEEE80211_GROUPS_NB (IEEE80211_HT_GROUPS_NB + \ - IEEE80211_VHT_GROUPS_NB + \ - IEEE80211_HE_GROUPS_NB) #define IEEE80211_HT_GROUP_0 0 #define IEEE80211_VHT_GROUP_0 (IEEE80211_HT_GROUP_0 + IEEE80211_HT_GROUPS_NB) @@ -477,7 +472,9 @@ u32 ieee80211_calc_rx_airtime(struct ieee80211_hw *hw, bool sp = status->enc_flags & RX_ENC_FLAG_SHORTPRE; bool cck; - if (WARN_ON_ONCE(status->band > NL80211_BAND_5GHZ)) + /* on 60GHz or sub-1GHz band, there are no legacy rates */ + if (WARN_ON_ONCE(status->band == NL80211_BAND_60GHZ || + status->band == NL80211_BAND_S1GHZ)) return 0; sband = hw->wiphy->bands[status->band]; @@ -650,12 +647,12 @@ u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, struct sta_info *sta = container_of(pubsta, struct sta_info, sta); struct ieee80211_rx_status stat; - struct ieee80211_tx_rate *rate = &sta->tx_stats.last_rate; + struct ieee80211_tx_rate *tx_rate = &sta->tx_stats.last_rate; struct rate_info *ri = &sta->tx_stats.last_rate_info; u32 duration, overhead; u8 agg_shift; - if (ieee80211_fill_rx_status(&stat, hw, rate, ri, band, len)) + if (ieee80211_fill_rx_status(&stat, hw, tx_rate, ri, band, len)) return 0; if (stat.encoding == RX_ENC_LEGACY || !ampdu) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 87a208089caf..ba752539d1d9 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -989,11 +989,29 @@ static int ieee80211_set_ftm_responder_params( return 0; } +static int +ieee80211_copy_mbssid_beacon(u8 *pos, struct cfg80211_mbssid_elems *dst, + struct cfg80211_mbssid_elems *src) +{ + int i, offset = 0; + + for (i = 0; i < src->cnt; i++) { + memcpy(pos + offset, src->elem[i].data, src->elem[i].len); + dst->elem[i].len = src->elem[i].len; + dst->elem[i].data = pos + offset; + offset += dst->elem[i].len; + } + dst->cnt = src->cnt; + + return offset; +} + static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, struct cfg80211_beacon_data *params, const struct ieee80211_csa_settings *csa, const struct ieee80211_color_change_settings *cca) { + struct cfg80211_mbssid_elems *mbssid = NULL; struct beacon_data *new, *old; int new_head_len, new_tail_len; int size, err; @@ -1021,6 +1039,17 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, size = sizeof(*new) + new_head_len + new_tail_len; + /* new or old multiple BSSID elements? */ + if (params->mbssid_ies) { + mbssid = params->mbssid_ies; + size += struct_size(new->mbssid_ies, elem, mbssid->cnt); + size += ieee80211_get_mbssid_beacon_len(mbssid); + } else if (old && old->mbssid_ies) { + mbssid = old->mbssid_ies; + size += struct_size(new->mbssid_ies, elem, mbssid->cnt); + size += ieee80211_get_mbssid_beacon_len(mbssid); + } + new = kzalloc(size, GFP_KERNEL); if (!new) return -ENOMEM; @@ -1029,12 +1058,23 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, /* * pointers go into the block we allocated, - * memory is | beacon_data | head | tail | + * memory is | beacon_data | head | tail | mbssid_ies */ new->head = ((u8 *) new) + sizeof(*new); new->tail = new->head + new_head_len; new->head_len = new_head_len; new->tail_len = new_tail_len; + /* copy in optional mbssid_ies */ + if (mbssid) { + u8 *pos = new->tail + new->tail_len; + + new->mbssid_ies = (void *)pos; + pos += struct_size(new->mbssid_ies, elem, mbssid->cnt); + ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies, mbssid); + /* update bssid_indicator */ + sdata->vif.bss_conf.bssid_indicator = + ilog2(__roundup_pow_of_two(mbssid->cnt + 1)); + } if (csa) { new->cntdwn_current_counter = csa->count; @@ -1332,8 +1372,11 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) mutex_unlock(&local->mtx); - kfree(sdata->u.ap.next_beacon); - sdata->u.ap.next_beacon = NULL; + if (sdata->u.ap.next_beacon) { + kfree(sdata->u.ap.next_beacon->mbssid_ies); + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + } /* turn off carrier for this interface and dependent VLANs */ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) @@ -1716,6 +1759,14 @@ static int sta_apply_parameters(struct ieee80211_local *local, (void *)params->he_6ghz_capa, sta); + if (params->eht_capa) + ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband, + (u8 *)params->he_capa, + params->he_capa_len, + params->eht_capa, + params->eht_capa_len, + sta); + if (params->opmode_notif_used) { /* returned value is only needed for rc update, but the * rc isn't initialized here yet, so ignore it @@ -2148,14 +2199,12 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, const struct mesh_setup *setup) { u8 *new_ie; - const u8 *old_ie; struct ieee80211_sub_if_data *sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh); int i; /* allocate information elements */ new_ie = NULL; - old_ie = ifmsh->ie; if (setup->ie_len) { new_ie = kmemdup(setup->ie, setup->ie_len, @@ -2165,7 +2214,6 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, } ifmsh->ie_len = setup->ie_len; ifmsh->ie = new_ie; - kfree(old_ie); /* now copy the rest of the setup parameters */ ifmsh->mesh_id_len = setup->mesh_id_len; @@ -3130,12 +3178,24 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon) len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len + beacon->proberesp_ies_len + beacon->assocresp_ies_len + - beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len; + beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len + + ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies); new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL); if (!new_beacon) return NULL; + if (beacon->mbssid_ies && beacon->mbssid_ies->cnt) { + new_beacon->mbssid_ies = + kzalloc(struct_size(new_beacon->mbssid_ies, + elem, beacon->mbssid_ies->cnt), + GFP_KERNEL); + if (!new_beacon->mbssid_ies) { + kfree(new_beacon); + return NULL; + } + } + pos = (u8 *)(new_beacon + 1); if (beacon->head_len) { new_beacon->head_len = beacon->head_len; @@ -3173,6 +3233,10 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon) memcpy(pos, beacon->probe_resp, beacon->probe_resp_len); pos += beacon->probe_resp_len; } + if (beacon->mbssid_ies && beacon->mbssid_ies->cnt) + pos += ieee80211_copy_mbssid_beacon(pos, + new_beacon->mbssid_ies, + beacon->mbssid_ies); /* might copy -1, meaning no changes requested */ new_beacon->ftm_responder = beacon->ftm_responder; @@ -3195,9 +3259,31 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon) void ieee80211_csa_finish(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; - ieee80211_queue_work(&sdata->local->hw, - &sdata->csa_finalize_work); + rcu_read_lock(); + + if (vif->mbssid_tx_vif == vif) { + /* Trigger ieee80211_csa_finish() on the non-transmitting + * interfaces when channel switch is received on + * transmitting interface + */ + struct ieee80211_sub_if_data *iter; + + list_for_each_entry_rcu(iter, &local->interfaces, list) { + if (!ieee80211_sdata_running(iter)) + continue; + + if (iter == sdata || iter->vif.mbssid_tx_vif != vif) + continue; + + ieee80211_queue_work(&iter->local->hw, + &iter->csa_finalize_work); + } + } + ieee80211_queue_work(&local->hw, &sdata->csa_finalize_work); + + rcu_read_unlock(); } EXPORT_SYMBOL(ieee80211_csa_finish); @@ -3222,8 +3308,11 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP: err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon, NULL, NULL); - kfree(sdata->u.ap.next_beacon); - sdata->u.ap.next_beacon = NULL; + if (sdata->u.ap.next_beacon) { + kfree(sdata->u.ap.next_beacon->mbssid_ies); + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + } if (err < 0) return err; @@ -3378,8 +3467,12 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, if ((params->n_counter_offsets_beacon > IEEE80211_MAX_CNTDWN_COUNTERS_NUM) || (params->n_counter_offsets_presp > - IEEE80211_MAX_CNTDWN_COUNTERS_NUM)) + IEEE80211_MAX_CNTDWN_COUNTERS_NUM)) { + kfree(sdata->u.ap.next_beacon->mbssid_ies); + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; return -EINVAL; + } csa.counter_offsets_beacon = params->counter_offsets_beacon; csa.counter_offsets_presp = params->counter_offsets_presp; @@ -3389,7 +3482,9 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa, &csa, NULL); if (err < 0) { + kfree(sdata->u.ap.next_beacon->mbssid_ies); kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; return err; } *changed |= err; @@ -3479,8 +3574,11 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, static void ieee80211_color_change_abort(struct ieee80211_sub_if_data *sdata) { sdata->vif.color_change_active = false; - kfree(sdata->u.ap.next_beacon); - sdata->u.ap.next_beacon = NULL; + if (sdata->u.ap.next_beacon) { + kfree(sdata->u.ap.next_beacon->mbssid_ies); + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + } cfg80211_color_change_aborted_notify(sdata->dev); } @@ -4218,8 +4316,11 @@ ieee80211_set_after_color_change_beacon(struct ieee80211_sub_if_data *sdata, ret = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon, NULL, NULL); - kfree(sdata->u.ap.next_beacon); - sdata->u.ap.next_beacon = NULL; + if (sdata->u.ap.next_beacon) { + kfree(sdata->u.ap.next_beacon->mbssid_ies); + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + } if (ret < 0) return ret; @@ -4262,7 +4363,11 @@ ieee80211_set_color_change_beacon(struct ieee80211_sub_if_data *sdata, err = ieee80211_assign_beacon(sdata, ¶ms->beacon_color_change, NULL, &color_change); if (err < 0) { - kfree(sdata->u.ap.next_beacon); + if (sdata->u.ap.next_beacon) { + kfree(sdata->u.ap.next_beacon->mbssid_ies); + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + } return err; } *changed |= err; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 76fc36a68750..e26d42de14ec 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -218,6 +218,8 @@ static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta) * might be smaller than the configured bw (160). */ return NL80211_CHAN_WIDTH_160; + case IEEE80211_STA_RX_BW_320: + return NL80211_CHAN_WIDTH_320; default: WARN_ON(1); return NL80211_CHAN_WIDTH_20; @@ -417,7 +419,7 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local, { u32 changed; - /* expected to handle only 20/40/80/160 channel widths */ + /* expected to handle only 20/40/80/160/320 channel widths */ switch (chandef->width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: @@ -425,6 +427,7 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local, case NL80211_CHAN_WIDTH_80: case NL80211_CHAN_WIDTH_80P80: case NL80211_CHAN_WIDTH_160: + case NL80211_CHAN_WIDTH_320: break; default: WARN_ON(1); diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 8dbfe325ee66..f4c9a92f50f9 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -634,8 +634,10 @@ static const struct file_operations stats_ ##name## _ops = { \ .llseek = generic_file_llseek, \ }; +#ifdef CONFIG_MAC80211_DEBUG_COUNTERS #define DEBUGFS_STATS_ADD(name) \ debugfs_create_u32(#name, 0400, statsd, &local->name); +#endif #define DEBUGFS_DEVSTATS_ADD(name) \ debugfs_create_file(#name, 0400, statsd, local, &stats_ ##name## _ops); diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index f53dec8a3d5c..edc7792e1361 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -4,6 +4,7 @@ * Copyright (c) 2006 Jiri Benc * Copyright 2007 Johannes Berg * Copyright (C) 2015 Intel Deutschland GmbH + * Copyright (C) 2021 Intel Corporation */ #include @@ -22,7 +23,6 @@ static ssize_t key_##name##_read(struct file *file, \ return mac80211_format_buffer(userbuf, count, ppos, \ format_string, key->prop); \ } -#define KEY_READ_D(name) KEY_READ(name, name, "%d\n") #define KEY_READ_X(name) KEY_READ(name, name, "0x%x\n") #define KEY_OPS(name) \ diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index db724fc10a5f..e490c3da3aca 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2006 Jiri Benc * Copyright 2007 Johannes Berg - * Copyright (C) 2020 Intel Corporation + * Copyright (C) 2020-2021 Intel Corporation */ #include @@ -77,8 +77,6 @@ static ssize_t ieee80211_if_fmt_##name( \ IEEE80211_IF_FMT(name, field, "%#x\n") #define IEEE80211_IF_FMT_LHEX(name, field) \ IEEE80211_IF_FMT(name, field, "%#lx\n") -#define IEEE80211_IF_FMT_SIZE(name, field) \ - IEEE80211_IF_FMT(name, field, "%zd\n") #define IEEE80211_IF_FMT_HEXARRAY(name, field) \ static ssize_t ieee80211_if_fmt_##name( \ diff --git a/net/mac80211/eht.c b/net/mac80211/eht.c new file mode 100644 index 000000000000..364ad0ef7692 --- /dev/null +++ b/net/mac80211/eht.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * EHT handling + * + * Copyright(c) 2021-2022 Intel Corporation + */ + +#include "ieee80211_i.h" + +void +ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + const u8 *he_cap_ie, u8 he_cap_len, + const struct ieee80211_eht_cap_elem *eht_cap_ie_elem, + u8 eht_cap_len, struct sta_info *sta) +{ + struct ieee80211_sta_eht_cap *eht_cap = &sta->sta.eht_cap; + struct ieee80211_he_cap_elem *he_cap_ie_elem = (void *)he_cap_ie; + u8 eht_ppe_size = 0; + u8 mcs_nss_size; + u8 eht_total_size = sizeof(eht_cap->eht_cap_elem); + u8 *pos = (u8 *)eht_cap_ie_elem; + + memset(eht_cap, 0, sizeof(*eht_cap)); + + if (!eht_cap_ie_elem || + !ieee80211_get_eht_iftype_cap(sband, + ieee80211_vif_type_p2p(&sdata->vif))) + return; + + mcs_nss_size = ieee80211_eht_mcs_nss_size(he_cap_ie_elem, + &eht_cap_ie_elem->fixed); + + eht_total_size += mcs_nss_size; + + /* Calculate the PPE thresholds length only if the header is present */ + if (eht_cap_ie_elem->fixed.phy_cap_info[5] & + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) { + u16 eht_ppe_hdr; + + if (eht_cap_len < eht_total_size + sizeof(u16)) + return; + + eht_ppe_hdr = get_unaligned_le16(eht_cap_ie_elem->optional + mcs_nss_size); + eht_ppe_size = + ieee80211_eht_ppe_size(eht_ppe_hdr, + eht_cap_ie_elem->fixed.phy_cap_info); + eht_total_size += eht_ppe_size; + + /* we calculate as if NSS > 8 are valid, but don't handle that */ + if (eht_ppe_size > sizeof(eht_cap->eht_ppe_thres)) + return; + } + + if (eht_cap_len < eht_total_size) + return; + + /* Copy the static portion of the EHT capabilities */ + memcpy(&eht_cap->eht_cap_elem, pos, sizeof(eht_cap->eht_cap_elem)); + pos += sizeof(eht_cap->eht_cap_elem); + + /* Copy MCS/NSS which depends on the peer capabilities */ + memset(&eht_cap->eht_mcs_nss_supp, 0, + sizeof(eht_cap->eht_mcs_nss_supp)); + memcpy(&eht_cap->eht_mcs_nss_supp, pos, mcs_nss_size); + + if (eht_ppe_size) + memcpy(eht_cap->eht_ppe_thres, + &eht_cap_ie_elem->optional[mcs_nss_size], + eht_ppe_size); + + eht_cap->has_eht = true; + + sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(sta); + sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); +} diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e87bccaab561..d4a7ba4a8202 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -257,6 +257,7 @@ struct beacon_data { struct ieee80211_meshconf_ie *meshconf; u16 cntdwn_counter_offsets[IEEE80211_MAX_CNTDWN_COUNTERS_NUM]; u8 cntdwn_current_counter; + struct cfg80211_mbssid_elems *mbssid_ies; struct rcu_head rcu_head; }; @@ -366,6 +367,8 @@ enum ieee80211_sta_flags { IEEE80211_STA_DISABLE_WMM = BIT(14), IEEE80211_STA_ENABLE_RRM = BIT(15), IEEE80211_STA_DISABLE_HE = BIT(16), + IEEE80211_STA_DISABLE_EHT = BIT(17), + IEEE80211_STA_DISABLE_320MHZ = BIT(18), }; struct ieee80211_mgd_auth_data { @@ -765,6 +768,8 @@ struct ieee80211_if_mesh { * back to wireless media and to the local net stack. * @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume. * @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver + * @IEEE80211_SDATA_DISCONNECT_HW_RESTART: Disconnect after hardware restart + * recovery */ enum ieee80211_sub_if_data_flags { IEEE80211_SDATA_ALLMULTI = BIT(0), @@ -772,6 +777,7 @@ enum ieee80211_sub_if_data_flags { IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3), IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4), IEEE80211_SDATA_IN_DRIVER = BIT(5), + IEEE80211_SDATA_DISCONNECT_HW_RESTART = BIT(6), }; /** @@ -1078,6 +1084,20 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif) return shift; } +static inline int +ieee80211_get_mbssid_beacon_len(struct cfg80211_mbssid_elems *elems) +{ + int i, len = 0; + + if (!elems) + return 0; + + for (i = 0; i < elems->cnt; i++) + len += elems->elem[i].len; + + return len; +} + enum { IEEE80211_RX_MSG = 1, IEEE80211_TX_STATUS_MSG = 2, @@ -1587,6 +1607,8 @@ struct ieee802_11_elems { const struct ieee80211_s1g_oper_ie *s1g_oper; const struct ieee80211_s1g_bcn_compat_ie *s1g_bcn_compat; const struct ieee80211_aid_response_ie *aid_resp; + const struct ieee80211_eht_cap_elem *eht_cap; + const struct ieee80211_eht_operation *eht_operation; /* length of them, respectively */ u8 ext_capab_len; @@ -1608,6 +1630,7 @@ struct ieee802_11_elems { u8 bssid_index_len; u8 tx_pwr_env_len[IEEE80211_TPE_MAX_IE_COUNT]; u8 tx_pwr_env_num; + u8 eht_cap_len; /* whether a parse error occurred while retrieving these elements */ bool parse_error; @@ -2380,7 +2403,7 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, const struct cfg80211_chan_def *chandef); u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype); -u8 *ieee80211_ie_build_he_cap(u8 *pos, +u8 *ieee80211_ie_build_he_cap(u32 disable_flags, u8 *pos, const struct ieee80211_sta_he_cap *he_cap, u8 *end); void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata, @@ -2411,6 +2434,7 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info, struct cfg80211_chan_def *chandef); bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, const struct ieee80211_he_operation *he_oper, + const struct ieee80211_eht_operation *eht_oper, struct cfg80211_chan_def *chandef); bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper, struct cfg80211_chan_def *chandef); @@ -2514,4 +2538,16 @@ u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache); void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache); +u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata, u8 iftype); +u8 *ieee80211_ie_build_eht_cap(u8 *pos, + const struct ieee80211_sta_he_cap *he_cap, + const struct ieee80211_sta_eht_cap *eht_cap, + u8 *end); + +void +ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + const u8 *he_cap_ie, u8 he_cap_len, + const struct ieee80211_eht_cap_elem *eht_cap_ie_elem, + u8 eht_cap_len, struct sta_info *sta); #endif /* IEEE80211_I_H */ diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 5311c3cd3050..a48a32f87897 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -909,7 +909,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) int result, i; enum nl80211_band band; int channels, max_bitrates; - bool supp_ht, supp_vht, supp_he; + bool supp_ht, supp_vht, supp_he, supp_eht; struct cfg80211_chan_def dflt_chandef = {}; if (ieee80211_hw_check(hw, QUEUE_CONTROL) && @@ -978,6 +978,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) supp_ht = false; supp_vht = false; supp_he = false; + supp_eht = false; for (band = 0; band < NUM_NL80211_BANDS; band++) { struct ieee80211_supported_band *sband; @@ -1021,6 +1022,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) iftd = &sband->iftype_data[i]; supp_he = supp_he || iftd->he_cap.has_he; + supp_eht = supp_eht || iftd->eht_cap.has_eht; } /* HT, VHT, HE require QoS, thus >= 4 queues */ @@ -1028,6 +1030,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) (supp_ht || supp_vht || supp_he))) return -EINVAL; + /* EHT requires HE support */ + if (WARN_ON(supp_eht && !supp_he)) + return -EINVAL; + if (!sband->ht_cap.ht_supported) continue; @@ -1138,6 +1144,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) 3 + sizeof(struct ieee80211_he_cap_elem) + sizeof(struct ieee80211_he_mcs_nss_supp) + IEEE80211_HE_PPE_THRES_MAX_LEN; + + if (supp_eht) + local->scan_ies_len += + 3 + sizeof(struct ieee80211_eht_cap_elem) + + sizeof(struct ieee80211_eht_mcs_nss_supp) + + IEEE80211_EHT_PPE_THRES_MAX_LEN; } if (!local->ops->hw_scan) { diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 15ac08d111ea..5275f4f32a78 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2008, 2009 open80211s Ltd. - * Copyright (C) 2018 - 2020 Intel Corporation + * Copyright (C) 2018 - 2021 Intel Corporation * Authors: Luis Carlos Cobo * Javier Cardona */ @@ -104,7 +104,8 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info, ie->vht_operation, ie->ht_operation, &sta_chan_def); - ieee80211_chandef_he_6ghz_oper(sdata, ie->he_operation, &sta_chan_def); + ieee80211_chandef_he_6ghz_oper(sdata, ie->he_operation, NULL, + &sta_chan_def); if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef, &sta_chan_def)) @@ -580,7 +581,7 @@ int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata, return -ENOMEM; pos = skb_put(skb, ie_len); - ieee80211_ie_build_he_cap(pos, he_cap, pos + ie_len); + ieee80211_ie_build_he_cap(0, pos, he_cap, pos + ie_len); return 0; } @@ -852,7 +853,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL); /* need an skb for IE builders to operate on */ - skb = dev_alloc_skb(max(head_len, tail_len)); + skb = __dev_alloc_skb(max(head_len, tail_len), GFP_KERNEL); if (!bcn || !skb) goto out_free; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 744842c4513b..1b30c724ca8d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -150,6 +150,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, const struct ieee80211_ht_operation *ht_oper, const struct ieee80211_vht_operation *vht_oper, const struct ieee80211_he_operation *he_oper, + const struct ieee80211_eht_operation *eht_oper, const struct ieee80211_s1g_oper_ie *s1g_oper, struct cfg80211_chan_def *chandef, bool tracking) { @@ -165,12 +166,14 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, chandef->freq1_offset = channel->freq_offset; if (channel->band == NL80211_BAND_6GHZ) { - if (!ieee80211_chandef_he_6ghz_oper(sdata, he_oper, chandef)) { + if (!ieee80211_chandef_he_6ghz_oper(sdata, he_oper, eht_oper, + chandef)) { mlme_dbg(sdata, - "bad 6 GHz operation, disabling HT/VHT/HE\n"); + "bad 6 GHz operation, disabling HT/VHT/HE/EHT\n"); ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT | - IEEE80211_STA_DISABLE_HE; + IEEE80211_STA_DISABLE_HE | + IEEE80211_STA_DISABLE_EHT; } else { ret = 0; } @@ -197,7 +200,8 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, mlme_dbg(sdata, "HT operation missing / HT not supported\n"); ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT | - IEEE80211_STA_DISABLE_HE; + IEEE80211_STA_DISABLE_HE | + IEEE80211_STA_DISABLE_EHT; goto out; } @@ -220,7 +224,8 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, ht_oper->primary_chan, channel->band); ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT | - IEEE80211_STA_DISABLE_HE; + IEEE80211_STA_DISABLE_HE | + IEEE80211_STA_DISABLE_EHT; goto out; } @@ -261,7 +266,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) sdata_info(sdata, "HE AP VHT information is invalid, disabling HE\n"); - ret = IEEE80211_STA_DISABLE_HE; + ret = IEEE80211_STA_DISABLE_HE | IEEE80211_STA_DISABLE_EHT; goto out; } } else if (!ieee80211_chandef_vht_oper(&sdata->local->hw, @@ -341,7 +346,8 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT | - IEEE80211_STA_DISABLE_HE; + IEEE80211_STA_DISABLE_HE | + IEEE80211_STA_DISABLE_EHT; break; } @@ -350,7 +356,11 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, if (!he_oper || !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef, IEEE80211_CHAN_NO_HE)) - ret |= IEEE80211_STA_DISABLE_HE; + ret |= IEEE80211_STA_DISABLE_HE | IEEE80211_STA_DISABLE_EHT; + + if (!eht_oper || !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef, + IEEE80211_CHAN_NO_EHT)) + ret |= IEEE80211_STA_DISABLE_EHT; if (chandef->width != vht_chandef.width && !tracking) sdata_info(sdata, @@ -367,6 +377,7 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, const struct ieee80211_ht_operation *ht_oper, const struct ieee80211_vht_operation *vht_oper, const struct ieee80211_he_operation *he_oper, + const struct ieee80211_eht_operation *eht_oper, const struct ieee80211_s1g_oper_ie *s1g_oper, const u8 *bssid, u32 *changed) { @@ -392,9 +403,16 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, /* don't check HE if we associated as non-HE station */ if (ifmgd->flags & IEEE80211_STA_DISABLE_HE || !ieee80211_get_he_iftype_cap(sband, - ieee80211_vif_type_p2p(&sdata->vif))) - + ieee80211_vif_type_p2p(&sdata->vif))) { he_oper = NULL; + eht_oper = NULL; + } + + /* don't check EHT if we associated as non-EHT station */ + if (ifmgd->flags & IEEE80211_STA_DISABLE_EHT || + !ieee80211_get_eht_iftype_cap(sband, + ieee80211_vif_type_p2p(&sdata->vif))) + eht_oper = NULL; if (WARN_ON_ONCE(!sta)) return -EINVAL; @@ -414,7 +432,8 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, /* calculate new channel (type) based on HT/VHT/HE operation IEs */ flags = ieee80211_determine_chantype(sdata, sband, chan, vht_cap_info, - ht_oper, vht_oper, he_oper, + ht_oper, vht_oper, + he_oper, eht_oper, s1g_oper, &chandef, true); /* @@ -448,9 +467,11 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, if (flags != (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT | IEEE80211_STA_DISABLE_HE | + IEEE80211_STA_DISABLE_EHT | IEEE80211_STA_DISABLE_40MHZ | IEEE80211_STA_DISABLE_80P80MHZ | - IEEE80211_STA_DISABLE_160MHZ)) || + IEEE80211_STA_DISABLE_160MHZ | + IEEE80211_STA_DISABLE_320MHZ)) || !cfg80211_chandef_valid(&chandef)) { sdata_info(sdata, "AP %pM changed caps/bw in a way we can't support (0x%x/0x%x) - disconnect\n", @@ -636,7 +657,7 @@ static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_supported_band *sband) { - u8 *pos; + u8 *pos, *pre_he_pos; const struct ieee80211_sta_he_cap *he_cap = NULL; struct ieee80211_chanctx_conf *chanctx_conf; u8 he_cap_size; @@ -653,20 +674,67 @@ static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata, he_cap = ieee80211_get_he_iftype_cap(sband, ieee80211_vif_type_p2p(&sdata->vif)); - if (!he_cap || !reg_cap) + if (!he_cap || !chanctx_conf || !reg_cap) return; + /* get a max size estimate */ he_cap_size = 2 + 1 + sizeof(he_cap->he_cap_elem) + ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem) + ieee80211_he_ppe_size(he_cap->ppe_thres[0], he_cap->he_cap_elem.phy_cap_info); pos = skb_put(skb, he_cap_size); - ieee80211_ie_build_he_cap(pos, he_cap, pos + he_cap_size); + pre_he_pos = pos; + pos = ieee80211_ie_build_he_cap(sdata->u.mgd.flags, + pos, he_cap, pos + he_cap_size); + /* trim excess if any */ + skb_trim(skb, skb->len - (pre_he_pos + he_cap_size - pos)); ieee80211_ie_build_he_6ghz_cap(sdata, skb); } +static void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, + struct ieee80211_supported_band *sband) +{ + u8 *pos; + const struct ieee80211_sta_he_cap *he_cap; + const struct ieee80211_sta_eht_cap *eht_cap; + struct ieee80211_chanctx_conf *chanctx_conf; + u8 eht_cap_size; + bool reg_cap = false; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!WARN_ON_ONCE(!chanctx_conf)) + reg_cap = cfg80211_chandef_usable(sdata->wdev.wiphy, + &chanctx_conf->def, + IEEE80211_CHAN_NO_HE | + IEEE80211_CHAN_NO_EHT); + rcu_read_unlock(); + + he_cap = ieee80211_get_he_iftype_cap(sband, + ieee80211_vif_type_p2p(&sdata->vif)); + eht_cap = ieee80211_get_eht_iftype_cap(sband, + ieee80211_vif_type_p2p(&sdata->vif)); + + /* + * EHT capabilities element is only added if the HE capabilities element + * was added so assume that 'he_cap' is valid and don't check it. + */ + if (WARN_ON(!he_cap || !eht_cap || !reg_cap)) + return; + + eht_cap_size = + 2 + 1 + sizeof(eht_cap->eht_cap_elem) + + ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, + &eht_cap->eht_cap_elem) + + ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], + eht_cap->eht_cap_elem.phy_cap_info); + pos = skb_put(skb, eht_cap_size); + ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + eht_cap_size); +} + static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; @@ -987,17 +1055,22 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) &assoc_data->ap_vht_cap); /* - * If AP doesn't support HT, mark HE as disabled. + * If AP doesn't support HT, mark HE and EHT as disabled. * If on the 5GHz band, make sure it supports VHT. */ if (ifmgd->flags & IEEE80211_STA_DISABLE_HT || (sband->band == NL80211_BAND_5GHZ && ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) - ifmgd->flags |= IEEE80211_STA_DISABLE_HE; + ifmgd->flags |= IEEE80211_STA_DISABLE_HE | + IEEE80211_STA_DISABLE_EHT; - if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) { ieee80211_add_he_ie(sdata, skb, sband); + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_EHT)) + ieee80211_add_eht_ie(sdata, skb, sband); + } + /* if present, add any custom non-vendor IEs that go after HE */ if (assoc_data->ie_len) { noffset = ieee80211_ie_split_vendor(assoc_data->ie, @@ -3524,10 +3597,25 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, bss_conf->twt_protected = false; changed |= ieee80211_recalc_twt_req(sdata, sta, elems); + + if (elems->eht_operation && elems->eht_cap && + !(ifmgd->flags & IEEE80211_STA_DISABLE_EHT)) { + ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband, + elems->he_cap, + elems->he_cap_len, + elems->eht_cap, + elems->eht_cap_len, + sta); + + bss_conf->eht_support = sta->sta.eht_cap.has_eht; + } else { + bss_conf->eht_support = false; + } } else { bss_conf->he_support = false; bss_conf->twt_requester = false; bss_conf->twt_protected = false; + bss_conf->eht_support = false; } bss_conf->twt_broadcast = @@ -4271,6 +4359,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (ieee80211_config_bw(sdata, sta, elems->ht_cap_elem, elems->vht_cap_elem, elems->ht_operation, elems->vht_operation, elems->he_operation, + elems->eht_operation, elems->s1g_oper, bssid, &changed)) { mutex_unlock(&local->sta_mtx); sdata_info(sdata, @@ -4845,6 +4934,7 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata) sdata_unlock(sdata); } +#endif void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) { @@ -4866,9 +4956,20 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) sdata_unlock(sdata); return; } + + if (sdata->flags & IEEE80211_SDATA_DISCONNECT_HW_RESTART) { + sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_HW_RESTART; + mlme_dbg(sdata, "driver requested disconnect after hardware restart\n"); + ieee80211_sta_connection_lost(sdata, + ifmgd->associated->bssid, + WLAN_REASON_UNSPECIFIED, + true); + sdata_unlock(sdata); + return; + } + sdata_unlock(sdata); } -#endif /* interface setup */ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) @@ -4923,13 +5024,20 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local) rcu_read_unlock(); } -static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata, - struct cfg80211_bss *cbss) +static u8 ieee80211_max_rx_chains(struct ieee80211_sub_if_data *sdata, + struct cfg80211_bss *cbss) { + struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; const struct element *ht_cap_elem, *vht_cap_elem; + const struct cfg80211_bss_ies *ies; const struct ieee80211_ht_cap *ht_cap; const struct ieee80211_vht_cap *vht_cap; + const struct ieee80211_he_cap_elem *he_cap; + const struct element *he_cap_elem; + u16 mcs_80_map, mcs_160_map; + int i, mcs_nss_size; + bool support_160; u8 chains = 1; if (ifmgd->flags & IEEE80211_STA_DISABLE_HT) @@ -4964,9 +5072,155 @@ static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata, chains = max(chains, nss); } + if (ifmgd->flags & IEEE80211_STA_DISABLE_HE) + return chains; + + ies = rcu_dereference(cbss->ies); + he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, + ies->data, ies->len); + + if (!he_cap_elem || he_cap_elem->datalen < sizeof(*he_cap)) + return chains; + + /* skip one byte ext_tag_id */ + he_cap = (void *)(he_cap_elem->data + 1); + mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap); + + /* invalid HE IE */ + if (he_cap_elem->datalen < 1 + mcs_nss_size + sizeof(*he_cap)) + return chains; + + /* mcs_nss is right after he_cap info */ + he_mcs_nss_supp = (void *)(he_cap + 1); + + mcs_80_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80); + + for (i = 7; i >= 0; i--) { + u8 mcs_80 = mcs_80_map >> (2 * i) & 3; + + if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { + chains = max_t(u8, chains, i + 1); + break; + } + } + + support_160 = he_cap->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + + if (!support_160) + return chains; + + mcs_160_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_160); + for (i = 7; i >= 0; i--) { + u8 mcs_160 = mcs_160_map >> (2 * i) & 3; + + if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { + chains = max_t(u8, chains, i + 1); + break; + } + } + return chains; } +static bool +ieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata, + const struct cfg80211_bss_ies *ies, + const struct ieee80211_he_operation *he_op) +{ + const struct element *he_cap_elem; + const struct ieee80211_he_cap_elem *he_cap; + struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp; + u16 mcs_80_map_tx, mcs_80_map_rx; + u16 ap_min_req_set; + int mcs_nss_size; + int nss; + + he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, + ies->data, ies->len); + + /* invalid HE IE */ + if (!he_cap_elem || he_cap_elem->datalen < 1 + sizeof(*he_cap)) { + sdata_info(sdata, + "Invalid HE elem, Disable HE\n"); + return false; + } + + /* skip one byte ext_tag_id */ + he_cap = (void *)(he_cap_elem->data + 1); + mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap); + + /* invalid HE IE */ + if (he_cap_elem->datalen < 1 + sizeof(*he_cap) + mcs_nss_size) { + sdata_info(sdata, + "Invalid HE elem with nss size, Disable HE\n"); + return false; + } + + /* mcs_nss is right after he_cap info */ + he_mcs_nss_supp = (void *)(he_cap + 1); + + mcs_80_map_tx = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80); + mcs_80_map_rx = le16_to_cpu(he_mcs_nss_supp->rx_mcs_80); + + /* P802.11-REVme/D0.3 + * 27.1.1 Introduction to the HE PHY + * ... + * An HE STA shall support the following features: + * ... + * Single spatial stream HE-MCSs 0 to 7 (transmit and receive) in all + * supported channel widths for HE SU PPDUs + */ + if ((mcs_80_map_tx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED || + (mcs_80_map_rx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED) { + sdata_info(sdata, + "Missing mandatory rates for 1 Nss, rx 0x%x, tx 0x%x, disable HE\n", + mcs_80_map_tx, mcs_80_map_rx); + return false; + } + + if (!he_op) + return true; + + ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set); + + /* make sure the AP is consistent with itself + * + * P802.11-REVme/D0.3 + * 26.17.1 Basic HE BSS operation + * + * A STA that is operating in an HE BSS shall be able to receive and + * transmit at each of the tuple values indicated by the + * Basic HE-MCS And NSS Set field of the HE Operation parameter of the + * MLME-START.request primitive and shall be able to receive at each of + * the tuple values indicated by the Supported HE-MCS and + * NSS Set field in the HE Capabilities parameter of the MLMESTART.request + * primitive + */ + for (nss = 8; nss > 0; nss--) { + u8 ap_op_val = (ap_min_req_set >> (2 * (nss - 1))) & 3; + u8 ap_rx_val; + u8 ap_tx_val; + + if (ap_op_val == IEEE80211_HE_MCS_NOT_SUPPORTED) + continue; + + ap_rx_val = (mcs_80_map_rx >> (2 * (nss - 1))) & 3; + ap_tx_val = (mcs_80_map_tx >> (2 * (nss - 1))) & 3; + + if (ap_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || + ap_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || + ap_rx_val < ap_op_val || ap_tx_val < ap_op_val) { + sdata_info(sdata, + "Invalid rates for %d Nss, rx %d, tx %d oper %d, disable HE\n", + nss, ap_rx_val, ap_rx_val, ap_op_val); + return false; + } + } + + return true; +} + static bool ieee80211_verify_sta_he_mcs_support(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, @@ -5013,7 +5267,15 @@ ieee80211_verify_sta_he_mcs_support(struct ieee80211_sub_if_data *sdata, /* * Make sure the HE AP doesn't require MCSs that aren't - * supported by the client + * supported by the client as required by spec + * + * P802.11-REVme/D0.3 + * 26.17.1 Basic HE BSS operation + * + * An HE STA shall not attempt to join * (MLME-JOIN.request primitive) + * a BSS, unless it supports (i.e., is able to both transmit and + * receive using) all of the tuples in the basic + * HE-MCS and NSS set. */ if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || @@ -5040,6 +5302,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, const struct ieee80211_ht_operation *ht_oper = NULL; const struct ieee80211_vht_operation *vht_oper = NULL; const struct ieee80211_he_operation *he_oper = NULL; + const struct ieee80211_eht_operation *eht_oper = NULL; const struct ieee80211_s1g_oper_ie *s1g_oper = NULL; struct ieee80211_supported_band *sband; struct cfg80211_chan_def chandef; @@ -5070,22 +5333,31 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, /* disable HT/VHT/HE if we don't support them */ if (!sband->ht_cap.ht_supported && !is_6ghz) { - mlme_dbg(sdata, "HT not supported, disabling HT/VHT/HE\n"); + mlme_dbg(sdata, "HT not supported, disabling HT/VHT/HE/EHT\n"); ifmgd->flags |= IEEE80211_STA_DISABLE_HT; ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; ifmgd->flags |= IEEE80211_STA_DISABLE_HE; + ifmgd->flags |= IEEE80211_STA_DISABLE_EHT; } if (!sband->vht_cap.vht_supported && is_5ghz) { - mlme_dbg(sdata, "VHT not supported, disabling VHT/HE\n"); + mlme_dbg(sdata, "VHT not supported, disabling VHT/HE/EHT\n"); ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; ifmgd->flags |= IEEE80211_STA_DISABLE_HE; + ifmgd->flags |= IEEE80211_STA_DISABLE_EHT; } if (!ieee80211_get_he_iftype_cap(sband, ieee80211_vif_type_p2p(&sdata->vif))) { - mlme_dbg(sdata, "HE not supported, disabling it\n"); + mlme_dbg(sdata, "HE not supported, disabling HE and EHT\n"); ifmgd->flags |= IEEE80211_STA_DISABLE_HE; + ifmgd->flags |= IEEE80211_STA_DISABLE_EHT; + } + + if (!ieee80211_get_eht_iftype_cap(sband, + ieee80211_vif_type_p2p(&sdata->vif))) { + mlme_dbg(sdata, "EHT not supported, disabling EHT\n"); + ifmgd->flags |= IEEE80211_STA_DISABLE_EHT; } if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && !is_6ghz) { @@ -5107,6 +5379,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= IEEE80211_STA_DISABLE_HT; ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; ifmgd->flags |= IEEE80211_STA_DISABLE_HE; + ifmgd->flags |= IEEE80211_STA_DISABLE_EHT; } if (!elems->vht_cap_elem) { @@ -5144,8 +5417,31 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, } } - if (!ieee80211_verify_sta_he_mcs_support(sdata, sband, he_oper)) - ifmgd->flags |= IEEE80211_STA_DISABLE_HE; + if (!ieee80211_verify_peer_he_mcs_support(sdata, ies, he_oper) || + !ieee80211_verify_sta_he_mcs_support(sdata, sband, he_oper)) + ifmgd->flags |= IEEE80211_STA_DISABLE_HE | + IEEE80211_STA_DISABLE_EHT; + } + + /* + * EHT requires HE to be supported as well. Specifically for 6 GHz + * channels, the operation channel information can only be deduced from + * both the 6 GHz operation information (from the HE operation IE) and + * EHT operation. + */ + if (!(ifmgd->flags & (IEEE80211_STA_DISABLE_HE | + IEEE80211_STA_DISABLE_EHT)) && he_oper) { + const struct cfg80211_bss_ies *ies; + const u8 *eht_oper_ie; + + ies = rcu_dereference(cbss->ies); + eht_oper_ie = cfg80211_find_ext_ie(WLAN_EID_EXT_EHT_OPERATION, + ies->data, ies->len); + if (eht_oper_ie && eht_oper_ie[1] >= + 1 + sizeof(struct ieee80211_eht_operation)) + eht_oper = (void *)(eht_oper_ie + 3); + else + eht_oper = NULL; } /* Allow VHT if at least one channel on the sband supports 80 MHz */ @@ -5174,11 +5470,12 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, cbss->channel, bss->vht_cap_info, - ht_oper, vht_oper, he_oper, + ht_oper, vht_oper, + he_oper, eht_oper, s1g_oper, &chandef, false); - sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), + sdata->needed_rx_chains = min(ieee80211_max_rx_chains(sdata, cbss), local->rx_chains); rcu_read_unlock(); @@ -5665,6 +5962,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= IEEE80211_STA_DISABLE_HT; ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; ifmgd->flags |= IEEE80211_STA_DISABLE_HE; + ifmgd->flags |= IEEE80211_STA_DISABLE_EHT; netdev_info(sdata->dev, "disabling HT/VHT/HE due to WEP/TKIP use\n"); } @@ -5672,11 +5970,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, sband = local->hw.wiphy->bands[req->bss->channel->band]; - /* also disable HT/VHT/HE if the AP doesn't use WMM */ + /* also disable HT/VHT/HE/EHT if the AP doesn't use WMM */ if (!bss->wmm_used) { ifmgd->flags |= IEEE80211_STA_DISABLE_HT; ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; ifmgd->flags |= IEEE80211_STA_DISABLE_HE; + ifmgd->flags |= IEEE80211_STA_DISABLE_EHT; netdev_info(sdata->dev, "disabling HT/VHT/HE as WMM/QoS is not supported by the AP\n"); } @@ -5730,9 +6029,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, memcpy(&assoc_data->ap_vht_cap, vht_elem->data, sizeof(struct ieee80211_vht_cap)); } else if (is_5ghz) { - sdata_info(sdata, "VHT capa missing/short, disabling VHT/HE\n"); + sdata_info(sdata, + "VHT capa missing/short, disabling VHT/HE/EHT\n"); ifmgd->flags |= IEEE80211_STA_DISABLE_VHT | - IEEE80211_STA_DISABLE_HE; + IEEE80211_STA_DISABLE_HE | + IEEE80211_STA_DISABLE_EHT; } rcu_read_unlock(); @@ -5811,6 +6112,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= IEEE80211_STA_DISABLE_HT; ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; ifmgd->flags |= IEEE80211_STA_DISABLE_HE; + ifmgd->flags |= IEEE80211_STA_DISABLE_EHT; } if (req->flags & ASSOC_REQ_DISABLE_VHT) { @@ -5819,8 +6121,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } if (req->flags & ASSOC_REQ_DISABLE_HE) { - mlme_dbg(sdata, "HE disabled by flag, disabling VHT\n"); + mlme_dbg(sdata, "HE disabled by flag, disabling HE/EHT\n"); ifmgd->flags |= IEEE80211_STA_DISABLE_HE; + ifmgd->flags |= IEEE80211_STA_DISABLE_EHT; } err = ieee80211_prep_connection(sdata, req->bss, true, override); diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 9c3b7fc377c1..9c6ace858107 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2010-2013 Felix Fietkau - * Copyright (C) 2019-2020 Intel Corporation + * Copyright (C) 2019-2021 Intel Corporation */ #include #include diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 48d9553dafe3..beb6b92eb780 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -4625,6 +4625,8 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx, /* do the header conversion - first grab the addresses */ ether_addr_copy(addrs.da, skb->data + fast_rx->da_offs); ether_addr_copy(addrs.sa, skb->data + fast_rx->sa_offs); + skb_postpull_rcsum(skb, skb->data + snap_offs, + sizeof(rfc1042_header) + 2); /* remove the SNAP but leave the ethertype */ skb_pull(skb, snap_offs + sizeof(rfc1042_header)); /* push the addresses in front */ diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 537535a88990..91fbb1ee5c38 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -364,8 +364,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, goto free; sta->mesh->plink_sta = sta; spin_lock_init(&sta->mesh->plink_lock); - if (ieee80211_vif_is_mesh(&sdata->vif) && - !sdata->u.mesh.user_mpm) + if (!sdata->u.mesh.user_mpm) timer_setup(&sta->mesh->plink_timer, mesh_plink_timer, 0); sta->mesh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE; diff --git a/net/mac80211/status.c b/net/mac80211/status.c index f6f63a0b1b72..e81e8a5bb774 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -5,6 +5,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2008-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright 2021-2022 Intel Corporation */ #include @@ -628,6 +629,8 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local, u64 cookie = IEEE80211_SKB_CB(skb)->ack.cookie; struct ieee80211_sub_if_data *sdata; struct ieee80211_hdr *hdr = (void *)skb->data; + bool is_valid_ack_signal = + !!(info->status.flags & IEEE80211_TX_STATUS_ACK_SIGNAL_VALID); rcu_read_lock(); sdata = ieee80211_sdata_from_skb(local, skb); @@ -644,7 +647,7 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local, cfg80211_probe_status(sdata->dev, hdr->addr1, cookie, acked, info->status.ack_signal, - info->status.is_valid_ack_signal, + is_valid_ack_signal, GFP_ATOMIC); else if (ieee80211_is_mgmt(hdr->frame_control)) cfg80211_mgmt_tx_status(&sdata->wdev, cookie, @@ -754,7 +757,6 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local, */ #define STA_LOST_PKT_THRESHOLD 50 #define STA_LOST_PKT_TIME HZ /* 1 sec since last ACK */ -#define STA_LOST_TDLS_PKT_THRESHOLD 10 #define STA_LOST_TDLS_PKT_TIME (10*HZ) /* 10secs since last ACK */ static void ieee80211_lost_packet(struct sta_info *sta, @@ -781,7 +783,7 @@ static void ieee80211_lost_packet(struct sta_info *sta, } /* - * If we're in TDLS mode, make sure that all STA_LOST_TDLS_PKT_THRESHOLD + * If we're in TDLS mode, make sure that all STA_LOST_PKT_THRESHOLD * of the last packets were lost, and that no ACK was received in the * last STA_LOST_TDLS_PKT_TIME ms, before triggering the CQM packet-loss * mechanism. @@ -1102,7 +1104,7 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, struct ieee80211_supported_band *sband; struct sta_info *sta = NULL; int rates_idx, retry_count; - bool acked, noack_success; + bool acked, noack_success, ack_signal_valid; u16 tx_time_est; if (pubsta) { @@ -1133,6 +1135,8 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, acked = !!(info->flags & IEEE80211_TX_STAT_ACK); noack_success = !!(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED); + ack_signal_valid = + !!(info->status.flags & IEEE80211_TX_STATUS_ACK_SIGNAL_VALID); if (pubsta) { struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -1161,7 +1165,7 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, unlikely(sdata->u.mgd.probe_send_count > 0)) sdata->u.mgd.probe_send_count = 0; - if (info->status.is_valid_ack_signal) { + if (ack_signal_valid) { sta->status_stats.last_ack_signal = (s8)info->status.ack_signal; sta->status_stats.ack_signal_filled = true; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 6d054fed062f..b6b20f38de0e 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -5042,6 +5042,19 @@ ieee80211_beacon_get_finish(struct ieee80211_hw *hw, IEEE80211_TX_CTL_FIRST_FRAGMENT; } +static void +ieee80211_beacon_add_mbssid(struct sk_buff *skb, struct beacon_data *beacon) +{ + int i; + + if (!beacon->mbssid_ies) + return; + + for (i = 0; i < beacon->mbssid_ies->cnt; i++) + skb_put_data(skb, beacon->mbssid_ies->elem[i].data, + beacon->mbssid_ies->elem[i].len); +} + static struct sk_buff * ieee80211_beacon_get_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -5055,6 +5068,7 @@ ieee80211_beacon_get_ap(struct ieee80211_hw *hw, struct ieee80211_if_ap *ap = &sdata->u.ap; struct sk_buff *skb = NULL; u16 csa_off_base = 0; + int mbssid_len; if (beacon->cntdwn_counter_offsets[0]) { if (!is_template) @@ -5064,11 +5078,12 @@ ieee80211_beacon_get_ap(struct ieee80211_hw *hw, } /* headroom, head length, - * tail length and maximum TIM length + * tail length, maximum TIM length and multiple BSSID length */ + mbssid_len = ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies); skb = dev_alloc_skb(local->tx_headroom + beacon->head_len + beacon->tail_len + 256 + - local->hw.extra_beacon_tailroom); + local->hw.extra_beacon_tailroom + mbssid_len); if (!skb) return NULL; @@ -5082,6 +5097,11 @@ ieee80211_beacon_get_ap(struct ieee80211_hw *hw, offs->tim_length = skb->len - beacon->head_len; offs->cntdwn_counter_offs[0] = beacon->cntdwn_counter_offsets[0]; + if (mbssid_len) { + ieee80211_beacon_add_mbssid(skb, beacon); + offs->mbssid_off = skb->len - mbssid_len; + } + /* for AP the csa offsets are from tail */ csa_off_base = skb->len; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index f71b042a5c8b..682a164f795a 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -973,8 +973,10 @@ static void ieee80211_parse_extension_element(u32 *crc, } break; case WLAN_EID_EXT_HE_CAPABILITY: - elems->he_cap = data; - elems->he_cap_len = len; + if (ieee80211_he_capa_size_ok(data, len)) { + elems->he_cap = data; + elems->he_cap_len = len; + } break; case WLAN_EID_EXT_HE_OPERATION: if (len >= sizeof(*elems->he_operation) && @@ -1006,6 +1008,17 @@ static void ieee80211_parse_extension_element(u32 *crc, if (len >= sizeof(*elems->he_6ghz_capa)) elems->he_6ghz_capa = data; break; + case WLAN_EID_EXT_EHT_CAPABILITY: + if (ieee80211_eht_capa_size_ok(elems->he_cap, + data, len)) { + elems->eht_cap = data; + elems->eht_cap_len = len; + } + break; + case WLAN_EID_EXT_EHT_OPERATION: + if (ieee80211_eht_oper_size_ok(data, len)) + elems->eht_operation = data; + break; } } @@ -1799,6 +1812,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; const struct ieee80211_sta_he_cap *he_cap; + const struct ieee80211_sta_eht_cap *eht_cap; u8 *pos = buffer, *end = buffer + buffer_len; size_t noffset; int supp_rates_len, i; @@ -1974,7 +1988,19 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, if (he_cap && cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), IEEE80211_CHAN_NO_HE)) { - pos = ieee80211_ie_build_he_cap(pos, he_cap, end); + pos = ieee80211_ie_build_he_cap(0, pos, he_cap, end); + if (!pos) + goto out_err; + } + + eht_cap = ieee80211_get_eht_iftype_cap(sband, + ieee80211_vif_type_p2p(&sdata->vif)); + + if (eht_cap && + cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), + IEEE80211_CHAN_NO_HE | + IEEE80211_CHAN_NO_EHT)) { + pos = ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, end); if (!pos) goto out_err; } @@ -2321,6 +2347,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) struct cfg80211_sched_scan_request *sched_scan_req; bool sched_scan_stopped = false; bool suspended = local->suspended; + bool in_reconfig = false; /* nothing to do if HW shouldn't run */ if (!local->open_count) @@ -2672,6 +2699,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART); if (local->in_reconfig) { + in_reconfig = local->in_reconfig; local->in_reconfig = false; barrier(); @@ -2689,6 +2717,15 @@ int ieee80211_reconfig(struct ieee80211_local *local) IEEE80211_QUEUE_STOP_REASON_SUSPEND, false); + if (in_reconfig) { + list_for_each_entry(sdata, &local->interfaces, list) { + if (!ieee80211_sdata_running(sdata)) + continue; + if (sdata->vif.type == NL80211_IFTYPE_STATION) + ieee80211_sta_restart(sdata); + } + } + if (!suspended) return 0; @@ -2718,7 +2755,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) return 0; } -void ieee80211_resume_disconnect(struct ieee80211_vif *vif) +static void ieee80211_reconfig_disconnect(struct ieee80211_vif *vif, u8 flag) { struct ieee80211_sub_if_data *sdata; struct ieee80211_local *local; @@ -2730,19 +2767,35 @@ void ieee80211_resume_disconnect(struct ieee80211_vif *vif) sdata = vif_to_sdata(vif); local = sdata->local; - if (WARN_ON(!local->resuming)) + if (WARN_ON(flag & IEEE80211_SDATA_DISCONNECT_RESUME && + !local->resuming)) + return; + + if (WARN_ON(flag & IEEE80211_SDATA_DISCONNECT_HW_RESTART && + !local->in_reconfig)) return; if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) return; - sdata->flags |= IEEE80211_SDATA_DISCONNECT_RESUME; + sdata->flags |= flag; mutex_lock(&local->key_mtx); list_for_each_entry(key, &sdata->key_list, list) key->flags |= KEY_FLAG_TAINTED; mutex_unlock(&local->key_mtx); } + +void ieee80211_hw_restart_disconnect(struct ieee80211_vif *vif) +{ + ieee80211_reconfig_disconnect(vif, IEEE80211_SDATA_DISCONNECT_HW_RESTART); +} +EXPORT_SYMBOL_GPL(ieee80211_hw_restart_disconnect); + +void ieee80211_resume_disconnect(struct ieee80211_vif *vif) +{ + ieee80211_reconfig_disconnect(vif, IEEE80211_SDATA_DISCONNECT_RESUME); +} EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata) @@ -2918,10 +2971,11 @@ u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype) he_cap->he_cap_elem.phy_cap_info); } -u8 *ieee80211_ie_build_he_cap(u8 *pos, +u8 *ieee80211_ie_build_he_cap(u32 disable_flags, u8 *pos, const struct ieee80211_sta_he_cap *he_cap, u8 *end) { + struct ieee80211_he_cap_elem elem; u8 n; u8 ie_len; u8 *orig_pos = pos; @@ -2934,7 +2988,23 @@ u8 *ieee80211_ie_build_he_cap(u8 *pos, if (!he_cap) return orig_pos; - n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem); + /* modify on stack first to calculate 'n' and 'ie_len' correctly */ + elem = he_cap->he_cap_elem; + + if (disable_flags & IEEE80211_STA_DISABLE_40MHZ) + elem.phy_cap_info[0] &= + ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G); + + if (disable_flags & IEEE80211_STA_DISABLE_160MHZ) + elem.phy_cap_info[0] &= + ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + + if (disable_flags & IEEE80211_STA_DISABLE_80P80MHZ) + elem.phy_cap_info[0] &= + ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; + + n = ieee80211_he_mcs_nss_size(&elem); ie_len = 2 + 1 + sizeof(he_cap->he_cap_elem) + n + ieee80211_he_ppe_size(he_cap->ppe_thres[0], @@ -2948,8 +3018,8 @@ u8 *ieee80211_ie_build_he_cap(u8 *pos, *pos++ = WLAN_EID_EXT_HE_CAPABILITY; /* Fixed data */ - memcpy(pos, &he_cap->he_cap_elem, sizeof(he_cap->he_cap_elem)); - pos += sizeof(he_cap->he_cap_elem); + memcpy(pos, &elem, sizeof(elem)); + pos += sizeof(elem); memcpy(pos, &he_cap->he_mcs_nss_supp, n); pos += n; @@ -3056,6 +3126,10 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, else ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; break; + case NL80211_CHAN_WIDTH_320: + /* HT information element should not be included on 6GHz */ + WARN_ON(1); + return pos; default: ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; break; @@ -3095,6 +3169,10 @@ void ieee80211_ie_build_wide_bw_cs(u8 *pos, case NL80211_CHAN_WIDTH_80P80: *pos++ = IEEE80211_VHT_CHANWIDTH_80P80MHZ; break; + case NL80211_CHAN_WIDTH_320: + /* The behavior is not defined for 320 MHz channels */ + WARN_ON(1); + fallthrough; default: *pos++ = IEEE80211_VHT_CHANWIDTH_USE_HT; } @@ -3147,6 +3225,10 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, case NL80211_CHAN_WIDTH_80: vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; break; + case NL80211_CHAN_WIDTH_320: + /* VHT information element should not be included on 6GHz */ + WARN_ON(1); + return pos; default: vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; break; @@ -3207,6 +3289,13 @@ u8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef) he_6ghz_op->ccfs1 = 0; switch (chandef->width) { + case NL80211_CHAN_WIDTH_320: + /* + * TODO: mesh operation is not defined over 6GHz 320 MHz + * channels. + */ + WARN_ON(1); + break; case NL80211_CHAN_WIDTH_160: /* Convert 160 MHz channel width to new style as interop * workaround. @@ -3262,7 +3351,6 @@ bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper, channel_type = NL80211_CHAN_HT40MINUS; break; default: - channel_type = NL80211_CHAN_NO_HT; return false; } @@ -3396,17 +3484,19 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info, bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, const struct ieee80211_he_operation *he_oper, + const struct ieee80211_eht_operation *eht_oper, struct cfg80211_chan_def *chandef) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); const struct ieee80211_sta_he_cap *he_cap; + const struct ieee80211_sta_eht_cap *eht_cap; struct cfg80211_chan_def he_chandef = *chandef; const struct ieee80211_he_6ghz_oper *he_6ghz_oper; struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; - bool support_80_80, support_160; - u8 he_phy_cap; + bool support_80_80, support_160, support_320; + u8 he_phy_cap, eht_phy_cap; u32 freq; if (chandef->chan->band != NL80211_BAND_6GHZ) @@ -3435,6 +3525,12 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, return false; } + eht_cap = ieee80211_get_eht_iftype_cap(sband, iftype); + if (!eht_cap) { + sdata_info(sdata, "Missing iftype sband data/EHT cap"); + eht_oper = NULL; + } + he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper); if (!he_6ghz_oper) { @@ -3444,6 +3540,11 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, return false; } + /* + * The EHT operation IE does not contain the primary channel so the + * primary channel frequency should be taken from the 6 GHz operation + * information. + */ freq = ieee80211_channel_to_frequency(he_6ghz_oper->primary, NL80211_BAND_6GHZ); he_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq); @@ -3461,43 +3562,80 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, break; } - switch (u8_get_bits(he_6ghz_oper->control, - IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH)) { - case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ: - he_chandef.width = NL80211_CHAN_WIDTH_20; - break; - case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ: - he_chandef.width = NL80211_CHAN_WIDTH_40; - break; - case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ: - he_chandef.width = NL80211_CHAN_WIDTH_80; - break; - case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ: - he_chandef.width = NL80211_CHAN_WIDTH_80; - if (!he_6ghz_oper->ccfs1) + if (!eht_oper) { + switch (u8_get_bits(he_6ghz_oper->control, + IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH)) { + case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ: + he_chandef.width = NL80211_CHAN_WIDTH_20; + break; + case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ: + he_chandef.width = NL80211_CHAN_WIDTH_40; + break; + case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ: + he_chandef.width = NL80211_CHAN_WIDTH_80; + break; + case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ: + he_chandef.width = NL80211_CHAN_WIDTH_80; + if (!he_6ghz_oper->ccfs1) + break; + if (abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0) == 8) { + if (support_160) + he_chandef.width = NL80211_CHAN_WIDTH_160; + } else { + if (support_80_80) + he_chandef.width = NL80211_CHAN_WIDTH_80P80; + } break; - if (abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0) == 8) { - if (support_160) - he_chandef.width = NL80211_CHAN_WIDTH_160; - } else { - if (support_80_80) - he_chandef.width = NL80211_CHAN_WIDTH_80P80; } - break; - } - if (he_chandef.width == NL80211_CHAN_WIDTH_160) { - he_chandef.center_freq1 = - ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1, - NL80211_BAND_6GHZ); - } else { - he_chandef.center_freq1 = - ieee80211_channel_to_frequency(he_6ghz_oper->ccfs0, - NL80211_BAND_6GHZ); - if (support_80_80 || support_160) - he_chandef.center_freq2 = + if (he_chandef.width == NL80211_CHAN_WIDTH_160) { + he_chandef.center_freq1 = ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1, NL80211_BAND_6GHZ); + } else { + he_chandef.center_freq1 = + ieee80211_channel_to_frequency(he_6ghz_oper->ccfs0, + NL80211_BAND_6GHZ); + if (support_80_80 || support_160) + he_chandef.center_freq2 = + ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1, + NL80211_BAND_6GHZ); + } + } else { + eht_phy_cap = eht_cap->eht_cap_elem.phy_cap_info[0]; + support_320 = + eht_phy_cap & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; + + switch (u8_get_bits(eht_oper->chan_width, + IEEE80211_EHT_OPER_CHAN_WIDTH)) { + case IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ: + he_chandef.width = NL80211_CHAN_WIDTH_20; + break; + case IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ: + he_chandef.width = NL80211_CHAN_WIDTH_40; + break; + case IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ: + he_chandef.width = NL80211_CHAN_WIDTH_80; + break; + case IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ: + if (support_160) + he_chandef.width = NL80211_CHAN_WIDTH_160; + else + he_chandef.width = NL80211_CHAN_WIDTH_80; + break; + case IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ: + if (support_320) + he_chandef.width = NL80211_CHAN_WIDTH_320; + else if (support_160) + he_chandef.width = NL80211_CHAN_WIDTH_160; + else + he_chandef.width = NL80211_CHAN_WIDTH_80; + break; + } + + he_chandef.center_freq1 = + ieee80211_channel_to_frequency(eht_oper->ccfs, + NL80211_BAND_6GHZ); } if (!cfg80211_chandef_valid(&he_chandef)) { @@ -3969,6 +4107,15 @@ u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) ret = IEEE80211_STA_DISABLE_80P80MHZ | IEEE80211_STA_DISABLE_160MHZ; break; + case NL80211_CHAN_WIDTH_320: + /* n_P20 */ + tmp = (150 + c->chan->center_freq - c->center_freq1) / 20; + /* n_P160 */ + tmp /= 80; + c->center_freq1 = c->center_freq1 - 80 + 160 * tmp; + c->width = NL80211_CHAN_WIDTH_160; + ret = IEEE80211_STA_DISABLE_320MHZ; + break; default: case NL80211_CHAN_WIDTH_20_NOHT: WARN_ON_ONCE(1); @@ -4633,3 +4780,69 @@ u16 ieee80211_encode_usf(int listen_interval) return (u16) listen_interval; } + +u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata, u8 iftype) +{ + const struct ieee80211_sta_he_cap *he_cap; + const struct ieee80211_sta_eht_cap *eht_cap; + struct ieee80211_supported_band *sband; + u8 n; + + sband = ieee80211_get_sband(sdata); + if (!sband) + return 0; + + he_cap = ieee80211_get_he_iftype_cap(sband, iftype); + eht_cap = ieee80211_get_eht_iftype_cap(sband, iftype); + if (!he_cap || !eht_cap) + return 0; + + n = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, + &eht_cap->eht_cap_elem); + return 2 + 1 + + sizeof(he_cap->he_cap_elem) + n + + ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], + eht_cap->eht_cap_elem.phy_cap_info); + return 0; +} + +u8 *ieee80211_ie_build_eht_cap(u8 *pos, + const struct ieee80211_sta_he_cap *he_cap, + const struct ieee80211_sta_eht_cap *eht_cap, + u8 *end) +{ + u8 mcs_nss_len, ppet_len; + u8 ie_len; + u8 *orig_pos = pos; + + /* Make sure we have place for the IE */ + if (!he_cap || !eht_cap) + return orig_pos; + + mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, + &eht_cap->eht_cap_elem); + ppet_len = ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], + eht_cap->eht_cap_elem.phy_cap_info); + + ie_len = 2 + 1 + sizeof(eht_cap->eht_cap_elem) + mcs_nss_len + ppet_len; + if ((end - pos) < ie_len) + return orig_pos; + + *pos++ = WLAN_EID_EXTENSION; + *pos++ = ie_len - 2; + *pos++ = WLAN_EID_EXT_EHT_CAPABILITY; + + /* Fixed data */ + memcpy(pos, &eht_cap->eht_cap_elem, sizeof(eht_cap->eht_cap_elem)); + pos += sizeof(eht_cap->eht_cap_elem); + + memcpy(pos, &eht_cap->eht_mcs_nss_supp, mcs_nss_len); + pos += mcs_nss_len; + + if (ppet_len) { + memcpy(pos, &eht_cap->eht_ppe_thres, ppet_len); + pos += ppet_len; + } + + return pos; +} diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index e856f9092137..8f16aa9c725d 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -4,7 +4,7 @@ * * Portions of this file * Copyright(c) 2015 - 2016 Intel Deutschland GmbH - * Copyright (C) 2018 - 2020 Intel Corporation + * Copyright (C) 2018 - 2021 Intel Corporation */ #include @@ -329,15 +329,27 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, } } -/* FIXME: move this to some better location - parses HE now */ +/* FIXME: move this to some better location - parses HE/EHT now */ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta) { struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; struct ieee80211_sta_he_cap *he_cap = &sta->sta.he_cap; + struct ieee80211_sta_eht_cap *eht_cap = &sta->sta.eht_cap; u32 cap_width; if (he_cap->has_he) { - u8 info = he_cap->he_cap_elem.phy_cap_info[0]; + u8 info; + + if (eht_cap->has_eht && + sta->sdata->vif.bss_conf.chandef.chan->band == + NL80211_BAND_6GHZ) { + info = eht_cap->eht_cap_elem.phy_cap_info[0]; + + if (info & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) + return IEEE80211_STA_RX_BW_320; + } + + info = he_cap->he_cap_elem.phy_cap_info[0]; if (sta->sdata->vif.bss_conf.chandef.chan->band == NL80211_BAND_2GHZ) { @@ -445,6 +457,8 @@ ieee80211_chan_width_to_rx_bw(enum nl80211_chan_width width) case NL80211_CHAN_WIDTH_160: case NL80211_CHAN_WIDTH_80P80: return IEEE80211_STA_RX_BW_160; + case NL80211_CHAN_WIDTH_320: + return IEEE80211_STA_RX_BW_320; default: WARN_ON_ONCE(1); return IEEE80211_STA_RX_BW_20; @@ -483,13 +497,24 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) void ieee80211_sta_set_rx_nss(struct sta_info *sta) { - u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, rx_nss; + u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, eht_rx_nss = 0, rx_nss; bool support_160; /* if we received a notification already don't overwrite it */ if (sta->sta.rx_nss) return; + if (sta->sta.eht_cap.has_eht) { + int i; + const u8 *rx_nss_mcs = (void *)&sta->sta.eht_cap.eht_mcs_nss_supp; + + /* get the max nss for EHT over all possible bandwidths and mcs */ + for (i = 0; i < sizeof(struct ieee80211_eht_mcs_nss_supp); i++) + eht_rx_nss = max_t(u8, eht_rx_nss, + u8_get_bits(rx_nss_mcs[i], + IEEE80211_EHT_MCS_NSS_RX)); + } + if (sta->sta.he_cap.has_he) { int i; u8 rx_mcs_80 = 0, rx_mcs_160 = 0; @@ -501,7 +526,7 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta) for (i = 7; i >= 0; i--) { u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3; - if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { + if (mcs_160 != IEEE80211_HE_MCS_NOT_SUPPORTED) { rx_mcs_160 = i + 1; break; } @@ -509,7 +534,7 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta) for (i = 7; i >= 0; i--) { u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3; - if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { + if (mcs_80 != IEEE80211_HE_MCS_NOT_SUPPORTED) { rx_mcs_80 = i + 1; break; } @@ -555,6 +580,7 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta) rx_nss = max(vht_rx_nss, ht_rx_nss); rx_nss = max(he_rx_nss, rx_nss); + rx_nss = max(eht_rx_nss, rx_nss); sta->sta.rx_nss = max_t(u8, 1, rx_nss); } diff --git a/net/mctp/af_mctp.c b/net/mctp/af_mctp.c index c921de63b494..f0702d920d8d 100644 --- a/net/mctp/af_mctp.c +++ b/net/mctp/af_mctp.c @@ -6,6 +6,7 @@ * Copyright (c) 2021 Google */ +#include #include #include #include @@ -21,6 +22,8 @@ /* socket implementation */ +static void mctp_sk_expire_keys(struct timer_list *timer); + static int mctp_release(struct socket *sock) { struct sock *sk = sock->sk; @@ -99,13 +102,20 @@ static int mctp_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) struct sk_buff *skb; if (addr) { + const u8 tagbits = MCTP_TAG_MASK | MCTP_TAG_OWNER | + MCTP_TAG_PREALLOC; + if (addrlen < sizeof(struct sockaddr_mctp)) return -EINVAL; if (addr->smctp_family != AF_MCTP) return -EINVAL; if (!mctp_sockaddr_is_ok(addr)) return -EINVAL; - if (addr->smctp_tag & ~(MCTP_TAG_MASK | MCTP_TAG_OWNER)) + if (addr->smctp_tag & ~tagbits) + return -EINVAL; + /* can't preallocate a non-owned tag */ + if (addr->smctp_tag & MCTP_TAG_PREALLOC && + !(addr->smctp_tag & MCTP_TAG_OWNER)) return -EINVAL; } else { @@ -248,6 +258,32 @@ static int mctp_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, return rc; } +/* We're done with the key; invalidate, stop reassembly, and remove from lists. + */ +static void __mctp_key_remove(struct mctp_sk_key *key, struct net *net, + unsigned long flags, unsigned long reason) +__releases(&key->lock) +__must_hold(&net->mctp.keys_lock) +{ + struct sk_buff *skb; + + trace_mctp_key_release(key, reason); + skb = key->reasm_head; + key->reasm_head = NULL; + key->reasm_dead = true; + key->valid = false; + mctp_dev_release_key(key->dev, key); + spin_unlock_irqrestore(&key->lock, flags); + + hlist_del(&key->hlist); + hlist_del(&key->sklist); + + /* unref for the lists */ + mctp_key_unref(key); + + kfree_skb(skb); +} + static int mctp_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval, unsigned int optlen) { @@ -293,6 +329,115 @@ static int mctp_getsockopt(struct socket *sock, int level, int optname, return -EINVAL; } +static int mctp_ioctl_alloctag(struct mctp_sock *msk, unsigned long arg) +{ + struct net *net = sock_net(&msk->sk); + struct mctp_sk_key *key = NULL; + struct mctp_ioc_tag_ctl ctl; + unsigned long flags; + u8 tag; + + if (copy_from_user(&ctl, (void __user *)arg, sizeof(ctl))) + return -EFAULT; + + if (ctl.tag) + return -EINVAL; + + if (ctl.flags) + return -EINVAL; + + key = mctp_alloc_local_tag(msk, ctl.peer_addr, MCTP_ADDR_ANY, + true, &tag); + if (IS_ERR(key)) + return PTR_ERR(key); + + ctl.tag = tag | MCTP_TAG_OWNER | MCTP_TAG_PREALLOC; + if (copy_to_user((void __user *)arg, &ctl, sizeof(ctl))) { + spin_lock_irqsave(&key->lock, flags); + __mctp_key_remove(key, net, flags, MCTP_TRACE_KEY_DROPPED); + mctp_key_unref(key); + return -EFAULT; + } + + mctp_key_unref(key); + return 0; +} + +static int mctp_ioctl_droptag(struct mctp_sock *msk, unsigned long arg) +{ + struct net *net = sock_net(&msk->sk); + struct mctp_ioc_tag_ctl ctl; + unsigned long flags, fl2; + struct mctp_sk_key *key; + struct hlist_node *tmp; + int rc; + u8 tag; + + if (copy_from_user(&ctl, (void __user *)arg, sizeof(ctl))) + return -EFAULT; + + if (ctl.flags) + return -EINVAL; + + /* Must be a local tag, TO set, preallocated */ + if ((ctl.tag & ~MCTP_TAG_MASK) != (MCTP_TAG_OWNER | MCTP_TAG_PREALLOC)) + return -EINVAL; + + tag = ctl.tag & MCTP_TAG_MASK; + rc = -EINVAL; + + spin_lock_irqsave(&net->mctp.keys_lock, flags); + hlist_for_each_entry_safe(key, tmp, &msk->keys, sklist) { + /* we do an irqsave here, even though we know the irq state, + * so we have the flags to pass to __mctp_key_remove + */ + spin_lock_irqsave(&key->lock, fl2); + if (key->manual_alloc && + ctl.peer_addr == key->peer_addr && + tag == key->tag) { + __mctp_key_remove(key, net, fl2, + MCTP_TRACE_KEY_DROPPED); + rc = 0; + } else { + spin_unlock_irqrestore(&key->lock, fl2); + } + } + spin_unlock_irqrestore(&net->mctp.keys_lock, flags); + + return rc; +} + +static int mctp_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct mctp_sock *msk = container_of(sock->sk, struct mctp_sock, sk); + + switch (cmd) { + case SIOCMCTPALLOCTAG: + return mctp_ioctl_alloctag(msk, arg); + case SIOCMCTPDROPTAG: + return mctp_ioctl_droptag(msk, arg); + } + + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static int mctp_compat_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = compat_ptr(arg); + + switch (cmd) { + /* These have compatible ptr layouts */ + case SIOCMCTPALLOCTAG: + case SIOCMCTPDROPTAG: + return mctp_ioctl(sock, cmd, (unsigned long)argp); + } + + return -ENOIOCTLCMD; +} +#endif + static const struct proto_ops mctp_dgram_ops = { .family = PF_MCTP, .release = mctp_release, @@ -302,7 +447,7 @@ static const struct proto_ops mctp_dgram_ops = { .accept = sock_no_accept, .getname = sock_no_getname, .poll = datagram_poll, - .ioctl = sock_no_ioctl, + .ioctl = mctp_ioctl, .gettstamp = sock_gettstamp, .listen = sock_no_listen, .shutdown = sock_no_shutdown, @@ -312,6 +457,9 @@ static const struct proto_ops mctp_dgram_ops = { .recvmsg = mctp_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, +#ifdef CONFIG_COMPAT + .compat_ioctl = mctp_compat_ioctl, +#endif }; static void mctp_sk_expire_keys(struct timer_list *timer) @@ -319,7 +467,7 @@ static void mctp_sk_expire_keys(struct timer_list *timer) struct mctp_sock *msk = container_of(timer, struct mctp_sock, key_expiry); struct net *net = sock_net(&msk->sk); - unsigned long next_expiry, flags; + unsigned long next_expiry, flags, fl2; struct mctp_sk_key *key; struct hlist_node *tmp; bool next_expiry_valid = false; @@ -327,15 +475,16 @@ static void mctp_sk_expire_keys(struct timer_list *timer) spin_lock_irqsave(&net->mctp.keys_lock, flags); hlist_for_each_entry_safe(key, tmp, &msk->keys, sklist) { - spin_lock(&key->lock); + /* don't expire. manual_alloc is immutable, no locking + * required. + */ + if (key->manual_alloc) + continue; + spin_lock_irqsave(&key->lock, fl2); if (!time_after_eq(key->expiry, jiffies)) { - trace_mctp_key_release(key, MCTP_TRACE_KEY_TIMEOUT); - key->valid = false; - hlist_del_rcu(&key->hlist); - hlist_del_rcu(&key->sklist); - spin_unlock(&key->lock); - mctp_key_unref(key); + __mctp_key_remove(key, net, fl2, + MCTP_TRACE_KEY_TIMEOUT); continue; } @@ -346,7 +495,7 @@ static void mctp_sk_expire_keys(struct timer_list *timer) next_expiry = key->expiry; next_expiry_valid = true; } - spin_unlock(&key->lock); + spin_unlock_irqrestore(&key->lock, fl2); } spin_unlock_irqrestore(&net->mctp.keys_lock, flags); @@ -387,9 +536,9 @@ static void mctp_sk_unhash(struct sock *sk) { struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); struct net *net = sock_net(sk); + unsigned long flags, fl2; struct mctp_sk_key *key; struct hlist_node *tmp; - unsigned long flags; /* remove from any type-based binds */ mutex_lock(&net->mctp.bind_lock); @@ -399,20 +548,8 @@ static void mctp_sk_unhash(struct sock *sk) /* remove tag allocations */ spin_lock_irqsave(&net->mctp.keys_lock, flags); hlist_for_each_entry_safe(key, tmp, &msk->keys, sklist) { - hlist_del(&key->sklist); - hlist_del(&key->hlist); - - trace_mctp_key_release(key, MCTP_TRACE_KEY_CLOSED); - - spin_lock(&key->lock); - kfree_skb(key->reasm_head); - key->reasm_head = NULL; - key->reasm_dead = true; - key->valid = false; - spin_unlock(&key->lock); - - /* key is no longer on the lookup lists, unref */ - mctp_key_unref(key); + spin_lock_irqsave(&key->lock, fl2); + __mctp_key_remove(key, net, fl2, MCTP_TRACE_KEY_CLOSED); } spin_unlock_irqrestore(&net->mctp.keys_lock, flags); } diff --git a/net/mctp/device.c b/net/mctp/device.c index ef2755f82f87..f49be882e98e 100644 --- a/net/mctp/device.c +++ b/net/mctp/device.c @@ -6,6 +6,7 @@ * Copyright (c) 2021 Google */ +#include #include #include #include @@ -24,12 +25,25 @@ struct mctp_dump_cb { size_t a_idx; }; -/* unlocked: caller must hold rcu_read_lock */ +/* unlocked: caller must hold rcu_read_lock. + * Returned mctp_dev has its refcount incremented, or NULL if unset. + */ struct mctp_dev *__mctp_dev_get(const struct net_device *dev) { - return rcu_dereference(dev->mctp_ptr); + struct mctp_dev *mdev = rcu_dereference(dev->mctp_ptr); + + /* RCU guarantees that any mdev is still live. + * Zero refcount implies a pending free, return NULL. + */ + if (mdev) + if (!refcount_inc_not_zero(&mdev->refs)) + return NULL; + return mdev; } +/* Returned mctp_dev does not have refcount incremented. The returned pointer + * remains live while rtnl_lock is held, as that prevents mctp_unregister() + */ struct mctp_dev *mctp_dev_get_rtnl(const struct net_device *dev) { return rtnl_dereference(dev->mctp_ptr); @@ -106,7 +120,7 @@ static int mctp_dump_addrinfo(struct sk_buff *skb, struct netlink_callback *cb) struct ifaddrmsg *hdr; struct mctp_dev *mdev; int ifindex; - int idx, rc; + int idx = 0, rc; hdr = nlmsg_data(cb->nlh); // filter by ifindex if requested @@ -123,6 +137,7 @@ static int mctp_dump_addrinfo(struct sk_buff *skb, struct netlink_callback *cb) if (mdev) { rc = mctp_dump_dev_addrinfo(mdev, skb, cb); + mctp_dev_put(mdev); // Error indicates full buffer, this // callback will get retried. if (rc < 0) @@ -208,7 +223,7 @@ static int mctp_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, if (!mdev) return -ENODEV; - if (!mctp_address_ok(addr->s_addr)) + if (!mctp_address_unicast(addr->s_addr)) return -EINVAL; /* Prevent duplicates. Under RTNL so don't need to lock for reading */ @@ -297,7 +312,7 @@ void mctp_dev_hold(struct mctp_dev *mdev) void mctp_dev_put(struct mctp_dev *mdev) { - if (refcount_dec_and_test(&mdev->refs)) { + if (mdev && refcount_dec_and_test(&mdev->refs)) { dev_put(mdev->dev); kfree_rcu(mdev, rcu); } @@ -369,6 +384,7 @@ static size_t mctp_get_link_af_size(const struct net_device *dev, if (!mdev) return 0; ret = nla_total_size(4); /* IFLA_MCTP_NET */ + mctp_dev_put(mdev); return ret; } @@ -412,10 +428,10 @@ static void mctp_unregister(struct net_device *dev) struct mctp_dev *mdev; mdev = mctp_dev_get_rtnl(dev); - if (mctp_known(dev) != (bool)mdev) { + if (mdev && !mctp_known(dev)) { // Sanity check, should match what was set in mctp_register - netdev_warn(dev, "%s: mdev pointer %d but type (%d) match is %d", - __func__, (bool)mdev, mctp_known(dev), dev->type); + netdev_warn(dev, "%s: BUG mctp_ptr set for unknown type %d", + __func__, dev->type); return; } if (!mdev) @@ -439,7 +455,7 @@ static int mctp_register(struct net_device *dev) if (mdev) { if (!mctp_known(dev)) - netdev_warn(dev, "%s: mctp_dev set for unknown type %d", + netdev_warn(dev, "%s: BUG mctp_ptr set for unknown type %d", __func__, dev->type); return 0; } diff --git a/net/mctp/neigh.c b/net/mctp/neigh.c index 6ad3e33bd4d4..ffa0f9e0983f 100644 --- a/net/mctp/neigh.c +++ b/net/mctp/neigh.c @@ -143,7 +143,7 @@ static int mctp_rtm_newneigh(struct sk_buff *skb, struct nlmsghdr *nlh, } eid = nla_get_u8(tb[NDA_DST]); - if (!mctp_address_ok(eid)) { + if (!mctp_address_unicast(eid)) { NL_SET_ERR_MSG(extack, "Invalid neighbour EID"); return -EINVAL; } diff --git a/net/mctp/route.c b/net/mctp/route.c index e52cef750500..d5e7db83fe9d 100644 --- a/net/mctp/route.c +++ b/net/mctp/route.c @@ -64,8 +64,7 @@ static struct mctp_sock *mctp_lookup_bind(struct net *net, struct sk_buff *skb) if (msk->bind_type != type) continue; - if (msk->bind_addr != MCTP_ADDR_ANY && - msk->bind_addr != mh->dest) + if (!mctp_address_matches(msk->bind_addr, mh->dest)) continue; return msk; @@ -77,7 +76,7 @@ static struct mctp_sock *mctp_lookup_bind(struct net *net, struct sk_buff *skb) static bool mctp_key_match(struct mctp_sk_key *key, mctp_eid_t local, mctp_eid_t peer, u8 tag) { - if (key->local_addr != local) + if (!mctp_address_matches(key->local_addr, local)) return false; if (key->peer_addr != peer) @@ -204,29 +203,38 @@ static int mctp_key_add(struct mctp_sk_key *key, struct mctp_sock *msk) return rc; } -/* We're done with the key; unset valid and remove from lists. There may still - * be outstanding refs on the key though... +/* Helper for mctp_route_input(). + * We're done with the key; unlock and unref the key. + * For the usual case of automatic expiry we remove the key from lists. + * In the case that manual allocation is set on a key we release the lock + * and local ref, reset reassembly, but don't remove from lists. */ -static void __mctp_key_unlock_drop(struct mctp_sk_key *key, struct net *net, - unsigned long flags) - __releases(&key->lock) +static void __mctp_key_done_in(struct mctp_sk_key *key, struct net *net, + unsigned long flags, unsigned long reason) +__releases(&key->lock) { struct sk_buff *skb; + trace_mctp_key_release(key, reason); skb = key->reasm_head; key->reasm_head = NULL; - key->reasm_dead = true; - key->valid = false; - mctp_dev_release_key(key->dev, key); + + if (!key->manual_alloc) { + key->reasm_dead = true; + key->valid = false; + mctp_dev_release_key(key->dev, key); + } spin_unlock_irqrestore(&key->lock, flags); - spin_lock_irqsave(&net->mctp.keys_lock, flags); - hlist_del(&key->hlist); - hlist_del(&key->sklist); - spin_unlock_irqrestore(&net->mctp.keys_lock, flags); + if (!key->manual_alloc) { + spin_lock_irqsave(&net->mctp.keys_lock, flags); + hlist_del(&key->hlist); + hlist_del(&key->sklist); + spin_unlock_irqrestore(&net->mctp.keys_lock, flags); - /* one unref for the lists */ - mctp_key_unref(key); + /* unref for the lists */ + mctp_key_unref(key); + } /* and one for the local reference */ mctp_key_unref(key); @@ -380,9 +388,8 @@ static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb) /* we've hit a pending reassembly; not much we * can do but drop it */ - trace_mctp_key_release(key, - MCTP_TRACE_KEY_REPLIED); - __mctp_key_unlock_drop(key, net, f); + __mctp_key_done_in(key, net, f, + MCTP_TRACE_KEY_REPLIED); key = NULL; } rc = 0; @@ -425,9 +432,8 @@ static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb) } else { if (key->reasm_head || key->reasm_dead) { /* duplicate start? drop everything */ - trace_mctp_key_release(key, - MCTP_TRACE_KEY_INVALIDATED); - __mctp_key_unlock_drop(key, net, f); + __mctp_key_done_in(key, net, f, + MCTP_TRACE_KEY_INVALIDATED); rc = -EEXIST; key = NULL; } else { @@ -452,8 +458,7 @@ static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb) if (!rc && flags & MCTP_HDR_FLAG_EOM) { sock_queue_rcv_skb(key->sk, key->reasm_head); key->reasm_head = NULL; - trace_mctp_key_release(key, MCTP_TRACE_KEY_REPLIED); - __mctp_key_unlock_drop(key, net, f); + __mctp_key_done_in(key, net, f, MCTP_TRACE_KEY_REPLIED); key = NULL; } @@ -581,9 +586,9 @@ static void mctp_reserve_tag(struct net *net, struct mctp_sk_key *key, /* Allocate a locally-owned tag value for (saddr, daddr), and reserve * it for the socket msk */ -static struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk, - mctp_eid_t saddr, - mctp_eid_t daddr, u8 *tagp) +struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk, + mctp_eid_t daddr, mctp_eid_t saddr, + bool manual, u8 *tagp) { struct net *net = sock_net(&msk->sk); struct netns_mctp *mns = &net->mctp; @@ -617,9 +622,8 @@ static struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk, if (tmp->tag & MCTP_HDR_FLAG_TO) continue; - if (!((tmp->peer_addr == daddr || - tmp->peer_addr == MCTP_ADDR_ANY) && - tmp->local_addr == saddr)) + if (!(mctp_address_matches(tmp->peer_addr, daddr) && + mctp_address_matches(tmp->local_addr, saddr))) continue; spin_lock(&tmp->lock); @@ -639,6 +643,7 @@ static struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk, mctp_reserve_tag(net, key, msk); trace_mctp_key_acquire(key); + key->manual_alloc = manual; *tagp = key->tag; } @@ -652,6 +657,50 @@ static struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk, return key; } +static struct mctp_sk_key *mctp_lookup_prealloc_tag(struct mctp_sock *msk, + mctp_eid_t daddr, + u8 req_tag, u8 *tagp) +{ + struct net *net = sock_net(&msk->sk); + struct netns_mctp *mns = &net->mctp; + struct mctp_sk_key *key, *tmp; + unsigned long flags; + + req_tag &= ~(MCTP_TAG_PREALLOC | MCTP_TAG_OWNER); + key = NULL; + + spin_lock_irqsave(&mns->keys_lock, flags); + + hlist_for_each_entry(tmp, &mns->keys, hlist) { + if (tmp->tag != req_tag) + continue; + + if (!mctp_address_matches(tmp->peer_addr, daddr)) + continue; + + if (!tmp->manual_alloc) + continue; + + spin_lock(&tmp->lock); + if (tmp->valid) { + key = tmp; + refcount_inc(&key->refs); + spin_unlock(&tmp->lock); + break; + } + spin_unlock(&tmp->lock); + } + spin_unlock_irqrestore(&mns->keys_lock, flags); + + if (!key) + return ERR_PTR(-ENOENT); + + if (tagp) + *tagp = key->tag; + + return key; +} + /* routing lookups */ static bool mctp_rt_match_eid(struct mctp_route *rt, unsigned int net, mctp_eid_t eid) @@ -786,9 +835,8 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt, { struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); struct mctp_skb_cb *cb = mctp_cb(skb); - struct mctp_route tmp_rt; + struct mctp_route tmp_rt = {0}; struct mctp_sk_key *key; - struct net_device *dev; struct mctp_hdr *hdr; unsigned long flags; unsigned int mtu; @@ -801,12 +849,12 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt, if (rt) { ext_rt = false; - dev = NULL; - if (WARN_ON(!rt->dev)) goto out_release; } else if (cb->ifindex) { + struct net_device *dev; + ext_rt = true; rt = &tmp_rt; @@ -816,7 +864,6 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt, rcu_read_unlock(); return rc; } - rt->dev = __mctp_dev_get(dev); rcu_read_unlock(); @@ -846,8 +893,14 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt, if (rc) goto out_release; - if (req_tag & MCTP_HDR_FLAG_TO) { - key = mctp_alloc_local_tag(msk, saddr, daddr, &tag); + if (req_tag & MCTP_TAG_OWNER) { + if (req_tag & MCTP_TAG_PREALLOC) + key = mctp_lookup_prealloc_tag(msk, daddr, + req_tag, &tag); + else + key = mctp_alloc_local_tag(msk, daddr, saddr, + false, &tag); + if (IS_ERR(key)) { rc = PTR_ERR(key); goto out_release; @@ -858,7 +911,7 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt, tag |= MCTP_HDR_FLAG_TO; } else { key = NULL; - tag = req_tag; + tag = req_tag & MCTP_TAG_MASK; } skb->protocol = htons(ETH_P_MCTP); @@ -891,10 +944,9 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt, if (!ext_rt) mctp_route_release(rt); - dev_put(dev); + mctp_dev_put(tmp_rt.dev); return rc; - } /* route management */ @@ -906,7 +958,7 @@ static int mctp_route_add(struct mctp_dev *mdev, mctp_eid_t daddr_start, struct net *net = dev_net(mdev->dev); struct mctp_route *rt, *ert; - if (!mctp_address_ok(daddr_start)) + if (!mctp_address_unicast(daddr_start)) return -EINVAL; if (daddr_extent > 0xff || daddr_start + daddr_extent >= 255) @@ -1036,6 +1088,17 @@ static int mctp_pkttype_receive(struct sk_buff *skb, struct net_device *dev, if (mh->ver < MCTP_VER_MIN || mh->ver > MCTP_VER_MAX) goto err_drop; + /* source must be valid unicast or null; drop reserved ranges and + * broadcast + */ + if (!(mctp_address_unicast(mh->src) || mctp_address_null(mh->src))) + goto err_drop; + + /* dest address: as above, but allow broadcast */ + if (!(mctp_address_unicast(mh->dest) || mctp_address_null(mh->dest) || + mctp_address_broadcast(mh->dest))) + goto err_drop; + /* MCTP drivers must populate halen/haddr */ if (dev->type == ARPHRD_MCTP) { cb = mctp_cb(skb); @@ -1057,11 +1120,13 @@ static int mctp_pkttype_receive(struct sk_buff *skb, struct net_device *dev, rt->output(rt, skb); mctp_route_release(rt); + mctp_dev_put(mdev); return NET_RX_SUCCESS; err_drop: kfree_skb(skb); + mctp_dev_put(mdev); return NET_RX_DROP; } diff --git a/net/mctp/test/route-test.c b/net/mctp/test/route-test.c index 750f9f9b4daf..61205cf40074 100644 --- a/net/mctp/test/route-test.c +++ b/net/mctp/test/route-test.c @@ -369,14 +369,15 @@ static void mctp_test_route_input_sk(struct kunit *test) #define FL_S (MCTP_HDR_FLAG_SOM) #define FL_E (MCTP_HDR_FLAG_EOM) -#define FL_T (MCTP_HDR_FLAG_TO) +#define FL_TO (MCTP_HDR_FLAG_TO) +#define FL_T(t) ((t) & MCTP_HDR_TAG_MASK) static const struct mctp_route_input_sk_test mctp_route_input_sk_tests[] = { - { .hdr = RX_HDR(1, 10, 8, FL_S | FL_E | FL_T), .type = 0, .deliver = true }, - { .hdr = RX_HDR(1, 10, 8, FL_S | FL_E | FL_T), .type = 1, .deliver = false }, + { .hdr = RX_HDR(1, 10, 8, FL_S | FL_E | FL_TO), .type = 0, .deliver = true }, + { .hdr = RX_HDR(1, 10, 8, FL_S | FL_E | FL_TO), .type = 1, .deliver = false }, { .hdr = RX_HDR(1, 10, 8, FL_S | FL_E), .type = 0, .deliver = false }, - { .hdr = RX_HDR(1, 10, 8, FL_E | FL_T), .type = 0, .deliver = false }, - { .hdr = RX_HDR(1, 10, 8, FL_T), .type = 0, .deliver = false }, + { .hdr = RX_HDR(1, 10, 8, FL_E | FL_TO), .type = 0, .deliver = false }, + { .hdr = RX_HDR(1, 10, 8, FL_TO), .type = 0, .deliver = false }, { .hdr = RX_HDR(1, 10, 8, 0), .type = 0, .deliver = false }, }; @@ -436,7 +437,7 @@ static void mctp_test_route_input_sk_reasm(struct kunit *test) __mctp_route_test_fini(test, dev, rt, sock); } -#define RX_FRAG(f, s) RX_HDR(1, 10, 8, FL_T | (f) | ((s) << MCTP_HDR_SEQ_SHIFT)) +#define RX_FRAG(f, s) RX_HDR(1, 10, 8, FL_TO | (f) | ((s) << MCTP_HDR_SEQ_SHIFT)) static const struct mctp_route_input_sk_reasm_test mctp_route_input_sk_reasm_tests[] = { { @@ -522,12 +523,156 @@ static void mctp_route_input_sk_reasm_to_desc( KUNIT_ARRAY_PARAM(mctp_route_input_sk_reasm, mctp_route_input_sk_reasm_tests, mctp_route_input_sk_reasm_to_desc); +struct mctp_route_input_sk_keys_test { + const char *name; + mctp_eid_t key_peer_addr; + mctp_eid_t key_local_addr; + u8 key_tag; + struct mctp_hdr hdr; + bool deliver; +}; + +/* test packet rx in the presence of various key configurations */ +static void mctp_test_route_input_sk_keys(struct kunit *test) +{ + const struct mctp_route_input_sk_keys_test *params; + struct mctp_test_route *rt; + struct sk_buff *skb, *skb2; + struct mctp_test_dev *dev; + struct mctp_sk_key *key; + struct netns_mctp *mns; + struct mctp_sock *msk; + struct socket *sock; + unsigned long flags; + int rc; + u8 c; + + params = test->param_value; + + dev = mctp_test_create_dev(); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + rt = mctp_test_create_route(&init_net, dev->mdev, 8, 68); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rt); + + rc = sock_create_kern(&init_net, AF_MCTP, SOCK_DGRAM, 0, &sock); + KUNIT_ASSERT_EQ(test, rc, 0); + + msk = container_of(sock->sk, struct mctp_sock, sk); + mns = &sock_net(sock->sk)->mctp; + + /* set the incoming tag according to test params */ + key = mctp_key_alloc(msk, params->key_local_addr, params->key_peer_addr, + params->key_tag, GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, key); + + spin_lock_irqsave(&mns->keys_lock, flags); + mctp_reserve_tag(&init_net, key, msk); + spin_unlock_irqrestore(&mns->keys_lock, flags); + + /* create packet and route */ + c = 0; + skb = mctp_test_create_skb_data(¶ms->hdr, &c); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb); + + skb->dev = dev->ndev; + __mctp_cb(skb); + + rc = mctp_route_input(&rt->rt, skb); + + /* (potentially) receive message */ + skb2 = skb_recv_datagram(sock->sk, 0, 1, &rc); + + if (params->deliver) + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, skb2); + else + KUNIT_EXPECT_PTR_EQ(test, skb2, NULL); + + if (skb2) + skb_free_datagram(sock->sk, skb2); + + mctp_key_unref(key); + __mctp_route_test_fini(test, dev, rt, sock); +} + +static const struct mctp_route_input_sk_keys_test mctp_route_input_sk_keys_tests[] = { + { + .name = "direct match", + .key_peer_addr = 9, + .key_local_addr = 8, + .key_tag = 1, + .hdr = RX_HDR(1, 9, 8, FL_S | FL_E | FL_T(1)), + .deliver = true, + }, + { + .name = "flipped src/dest", + .key_peer_addr = 8, + .key_local_addr = 9, + .key_tag = 1, + .hdr = RX_HDR(1, 9, 8, FL_S | FL_E | FL_T(1)), + .deliver = false, + }, + { + .name = "peer addr mismatch", + .key_peer_addr = 9, + .key_local_addr = 8, + .key_tag = 1, + .hdr = RX_HDR(1, 10, 8, FL_S | FL_E | FL_T(1)), + .deliver = false, + }, + { + .name = "tag value mismatch", + .key_peer_addr = 9, + .key_local_addr = 8, + .key_tag = 1, + .hdr = RX_HDR(1, 9, 8, FL_S | FL_E | FL_T(2)), + .deliver = false, + }, + { + .name = "TO mismatch", + .key_peer_addr = 9, + .key_local_addr = 8, + .key_tag = 1, + .hdr = RX_HDR(1, 9, 8, FL_S | FL_E | FL_T(1) | FL_TO), + .deliver = false, + }, + { + .name = "broadcast response", + .key_peer_addr = MCTP_ADDR_ANY, + .key_local_addr = 8, + .key_tag = 1, + .hdr = RX_HDR(1, 11, 8, FL_S | FL_E | FL_T(1)), + .deliver = true, + }, + { + .name = "any local match", + .key_peer_addr = 12, + .key_local_addr = MCTP_ADDR_ANY, + .key_tag = 1, + .hdr = RX_HDR(1, 12, 8, FL_S | FL_E | FL_T(1)), + .deliver = true, + }, +}; + +static void mctp_route_input_sk_keys_to_desc( + const struct mctp_route_input_sk_keys_test *t, + char *desc) +{ + sprintf(desc, "%s", t->name); +} + +KUNIT_ARRAY_PARAM(mctp_route_input_sk_keys, mctp_route_input_sk_keys_tests, + mctp_route_input_sk_keys_to_desc); + static struct kunit_case mctp_test_cases[] = { KUNIT_CASE_PARAM(mctp_test_fragment, mctp_frag_gen_params), KUNIT_CASE_PARAM(mctp_test_rx_input, mctp_rx_input_gen_params), KUNIT_CASE_PARAM(mctp_test_route_input_sk, mctp_route_input_sk_gen_params), KUNIT_CASE_PARAM(mctp_test_route_input_sk_reasm, mctp_route_input_sk_reasm_gen_params), + KUNIT_CASE_PARAM(mctp_test_route_input_sk_keys, + mctp_route_input_sk_keys_gen_params), {} }; diff --git a/net/mctp/test/utils.c b/net/mctp/test/utils.c index 7b7918702592..e03ba66bbe18 100644 --- a/net/mctp/test/utils.c +++ b/net/mctp/test/utils.c @@ -54,7 +54,6 @@ struct mctp_test_dev *mctp_test_create_dev(void) rcu_read_lock(); dev->mdev = __mctp_dev_get(ndev); - mctp_dev_hold(dev->mdev); rcu_read_unlock(); return dev; diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c index 7558802a1435..e55d3dfbee0c 100644 --- a/net/mptcp/mib.c +++ b/net/mptcp/mib.c @@ -48,6 +48,10 @@ static const struct snmp_mib mptcp_snmp_list[] = { SNMP_MIB_ITEM("MPPrioRx", MPTCP_MIB_MPPRIORX), SNMP_MIB_ITEM("MPFailTx", MPTCP_MIB_MPFAILTX), SNMP_MIB_ITEM("MPFailRx", MPTCP_MIB_MPFAILRX), + SNMP_MIB_ITEM("MPFastcloseTx", MPTCP_MIB_MPFASTCLOSETX), + SNMP_MIB_ITEM("MPFastcloseRx", MPTCP_MIB_MPFASTCLOSERX), + SNMP_MIB_ITEM("MPRstTx", MPTCP_MIB_MPRSTTX), + SNMP_MIB_ITEM("MPRstRx", MPTCP_MIB_MPRSTRX), SNMP_MIB_ITEM("RcvPruned", MPTCP_MIB_RCVPRUNED), SNMP_MIB_ITEM("SubflowStale", MPTCP_MIB_SUBFLOWSTALE), SNMP_MIB_ITEM("SubflowRecover", MPTCP_MIB_SUBFLOWRECOVER), diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h index 2966fcb6548b..00576179a619 100644 --- a/net/mptcp/mib.h +++ b/net/mptcp/mib.h @@ -41,6 +41,10 @@ enum linux_mptcp_mib_field { MPTCP_MIB_MPPRIORX, /* Received a MP_PRIO */ MPTCP_MIB_MPFAILTX, /* Transmit a MP_FAIL */ MPTCP_MIB_MPFAILRX, /* Received a MP_FAIL */ + MPTCP_MIB_MPFASTCLOSETX, /* Transmit a MP_FASTCLOSE */ + MPTCP_MIB_MPFASTCLOSERX, /* Received a MP_FASTCLOSE */ + MPTCP_MIB_MPRSTTX, /* Transmit a MP_RST */ + MPTCP_MIB_MPRSTRX, /* Received a MP_RST */ MPTCP_MIB_RCVPRUNED, /* Incoming packet dropped due to memory limit */ MPTCP_MIB_SUBFLOWSTALE, /* Subflows entered 'stale' status */ MPTCP_MIB_SUBFLOWRECOVER, /* Subflows returned to active status after being stale */ diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 645dd984fef0..325383646f5c 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -323,6 +323,7 @@ static void mptcp_parse_option(const struct sk_buff *skb, mp_opt->rcvr_key = get_unaligned_be64(ptr); ptr += 8; mp_opt->suboptions |= OPTION_MPTCP_FASTCLOSE; + pr_debug("MP_FASTCLOSE: recv_key=%llu", mp_opt->rcvr_key); break; case MPTCPOPT_RST: @@ -336,6 +337,8 @@ static void mptcp_parse_option(const struct sk_buff *skb, flags = *ptr++; mp_opt->reset_transient = flags & MPTCP_RST_TRANSIENT; mp_opt->reset_reason = *ptr; + pr_debug("MP_RST: transient=%u reason=%u", + mp_opt->reset_transient, mp_opt->reset_reason); break; case MPTCPOPT_MP_FAIL: @@ -353,8 +356,7 @@ static void mptcp_parse_option(const struct sk_buff *skb, } } -void mptcp_get_options(const struct sock *sk, - const struct sk_buff *skb, +void mptcp_get_options(const struct sk_buff *skb, struct mptcp_options_received *mp_opt) { const struct tcphdr *th = tcp_hdr(skb); @@ -651,7 +653,6 @@ static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff * bool drop_other_suboptions = false; unsigned int opt_size = *size; bool echo; - bool port; int len; /* add addr will strip the existing options, be sure to avoid breaking @@ -660,12 +661,12 @@ static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff * if (!mptcp_pm_should_add_signal(msk) || (opts->suboptions & (OPTION_MPTCP_MPJ_ACK | OPTION_MPTCP_MPC_ACK)) || !mptcp_pm_add_addr_signal(msk, skb, opt_size, remaining, &opts->addr, - &echo, &port, &drop_other_suboptions)) + &echo, &drop_other_suboptions)) return false; if (drop_other_suboptions) remaining += opt_size; - len = mptcp_add_addr_len(opts->addr.family, echo, port); + len = mptcp_add_addr_len(opts->addr.family, echo, !!opts->addr.port); if (remaining < len) return false; @@ -832,11 +833,13 @@ bool mptcp_established_options(struct sock *sk, struct sk_buff *skb, mptcp_established_options_mp_fail(sk, &opt_size, remaining, opts)) { *size += opt_size; remaining -= opt_size; + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPFASTCLOSETX); } /* MP_RST can be used with MP_FASTCLOSE and MP_FAIL if there is room */ if (mptcp_established_options_rst(sk, skb, &opt_size, remaining, opts)) { *size += opt_size; remaining -= opt_size; + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPRSTTX); } return true; } @@ -1084,8 +1087,7 @@ static bool add_addr_hmac_valid(struct mptcp_sock *msk, &mp_opt->addr); pr_debug("msk=%p, ahmac=%llu, mp_opt->ahmac=%llu\n", - msk, (unsigned long long)hmac, - (unsigned long long)mp_opt->ahmac); + msk, hmac, mp_opt->ahmac); return hmac == mp_opt->ahmac; } @@ -1112,7 +1114,7 @@ bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb) return true; } - mptcp_get_options(sk, skb, &mp_opt); + mptcp_get_options(skb, &mp_opt); /* The subflow can be in close state only if check_fully_established() * just sent a reset. If so, tell the caller to ignore the current packet. @@ -1125,6 +1127,7 @@ bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb) msk->local_key == mp_opt.rcvr_key) { WRITE_ONCE(msk->rcv_fastclose, true); mptcp_schedule_work((struct sock *)msk); + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPFASTCLOSERX); } if ((mp_opt.suboptions & OPTION_MPTCP_ADD_ADDR) && @@ -1159,6 +1162,7 @@ bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb) subflow->reset_seen = 1; subflow->reset_reason = mp_opt.reset_reason; subflow->reset_transient = mp_opt.reset_transient; + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPRSTRX); } if (!(mp_opt.suboptions & OPTION_MPTCP_DSS)) @@ -1264,22 +1268,30 @@ static u16 mptcp_make_csum(const struct mptcp_ext *mpext) void mptcp_write_options(__be32 *ptr, const struct tcp_sock *tp, struct mptcp_out_options *opts) { - if (unlikely(OPTION_MPTCP_FAIL & opts->suboptions)) { - const struct sock *ssk = (const struct sock *)tp; - struct mptcp_subflow_context *subflow; + const struct sock *ssk = (const struct sock *)tp; + struct mptcp_subflow_context *subflow; - subflow = mptcp_subflow_ctx(ssk); - subflow->send_mp_fail = 0; - - *ptr++ = mptcp_option(MPTCPOPT_MP_FAIL, - TCPOLEN_MPTCP_FAIL, - 0, 0); - put_unaligned_be64(opts->fail_seq, ptr); - ptr += 2; - } - - /* DSS, MPC, MPJ, ADD_ADDR, FASTCLOSE and RST are mutually exclusive, - * see mptcp_established_options*() + /* Which options can be used together? + * + * X: mutually exclusive + * O: often used together + * C: can be used together in some cases + * P: could be used together but we prefer not to (optimisations) + * + * Opt: | MPC | MPJ | DSS | ADD | RM | PRIO | FAIL | FC | + * ------|------|------|------|------|------|------|------|------| + * MPC |------|------|------|------|------|------|------|------| + * MPJ | X |------|------|------|------|------|------|------| + * DSS | X | X |------|------|------|------|------|------| + * ADD | X | X | P |------|------|------|------|------| + * RM | C | C | C | P |------|------|------|------| + * PRIO | X | C | C | C | C |------|------|------| + * FAIL | X | X | C | X | X | X |------|------| + * FC | X | X | X | X | X | X | X |------| + * RST | X | X | X | X | X | X | O | O | + * ------|------|------|------|------|------|------|------|------| + * + * The same applies in mptcp_established_options() function. */ if (likely(OPTION_MPTCP_DSS & opts->suboptions)) { struct mptcp_ext *mpext = &opts->ext_copy; @@ -1336,6 +1348,10 @@ void mptcp_write_options(__be32 *ptr, const struct tcp_sock *tp, } ptr += 1; } + + /* We might need to add MP_FAIL options in rare cases */ + if (unlikely(OPTION_MPTCP_FAIL & opts->suboptions)) + goto mp_fail; } else if (OPTIONS_MPTCP_MPC & opts->suboptions) { u8 len, flag = MPTCP_CAP_HMAC_SHA256; @@ -1476,6 +1492,21 @@ void mptcp_write_options(__be32 *ptr, const struct tcp_sock *tp, put_unaligned_be64(opts->rcvr_key, ptr); ptr += 2; + if (OPTION_MPTCP_RST & opts->suboptions) + goto mp_rst; + return; + } else if (unlikely(OPTION_MPTCP_FAIL & opts->suboptions)) { +mp_fail: + /* MP_FAIL is mutually exclusive with others except RST */ + subflow = mptcp_subflow_ctx(ssk); + subflow->send_mp_fail = 0; + + *ptr++ = mptcp_option(MPTCPOPT_MP_FAIL, + TCPOLEN_MPTCP_FAIL, + 0, 0); + put_unaligned_be64(opts->fail_seq, ptr); + ptr += 2; + if (OPTION_MPTCP_RST & opts->suboptions) goto mp_rst; return; @@ -1489,9 +1520,6 @@ void mptcp_write_options(__be32 *ptr, const struct tcp_sock *tp, } if (OPTION_MPTCP_PRIO & opts->suboptions) { - const struct sock *ssk = (const struct sock *)tp; - struct mptcp_subflow_context *subflow; - subflow = mptcp_subflow_ctx(ssk); subflow->send_mp_prio = 0; diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index 7bea318ac5f2..01809eef29b4 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -221,7 +221,7 @@ void mptcp_pm_add_addr_received(struct mptcp_sock *msk, } void mptcp_pm_add_addr_echoed(struct mptcp_sock *msk, - struct mptcp_addr_info *addr) + const struct mptcp_addr_info *addr) { struct mptcp_pm_data *pm = &msk->pm; @@ -279,14 +279,15 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq) /* path manager helpers */ -bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, struct sk_buff *skb, +bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, const struct sk_buff *skb, unsigned int opt_size, unsigned int remaining, struct mptcp_addr_info *addr, bool *echo, - bool *port, bool *drop_other_suboptions) + bool *drop_other_suboptions) { int ret = false; u8 add_addr; u8 family; + bool port; spin_lock_bh(&msk->pm.lock); @@ -304,10 +305,10 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, struct sk_buff *skb, } *echo = mptcp_pm_should_add_signal_echo(msk); - *port = !!(*echo ? msk->pm.remote.port : msk->pm.local.port); + port = !!(*echo ? msk->pm.remote.port : msk->pm.local.port); family = *echo ? msk->pm.remote.family : msk->pm.local.family; - if (remaining < mptcp_add_addr_len(family, *echo, *port)) + if (remaining < mptcp_add_addr_len(family, *echo, port)) goto out_unlock; if (*echo) { diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c index 4b5d795383cd..b5e8de6f7507 100644 --- a/net/mptcp/pm_netlink.c +++ b/net/mptcp/pm_netlink.c @@ -83,16 +83,6 @@ static bool addresses_equal(const struct mptcp_addr_info *a, return a->port == b->port; } -static bool address_zero(const struct mptcp_addr_info *addr) -{ - struct mptcp_addr_info zero; - - memset(&zero, 0, sizeof(zero)); - zero.family = addr->family; - - return addresses_equal(addr, &zero, true); -} - static void local_address(const struct sock_common *skc, struct mptcp_addr_info *addr) { @@ -120,7 +110,7 @@ static void remote_address(const struct sock_common *skc, } static bool lookup_subflow_by_saddr(const struct list_head *list, - struct mptcp_addr_info *saddr) + const struct mptcp_addr_info *saddr) { struct mptcp_subflow_context *subflow; struct mptcp_addr_info cur; @@ -138,7 +128,7 @@ static bool lookup_subflow_by_saddr(const struct list_head *list, } static bool lookup_subflow_by_daddr(const struct list_head *list, - struct mptcp_addr_info *daddr) + const struct mptcp_addr_info *daddr) { struct mptcp_subflow_context *subflow; struct mptcp_addr_info cur; @@ -157,10 +147,10 @@ static bool lookup_subflow_by_daddr(const struct list_head *list, static struct mptcp_pm_addr_entry * select_local_address(const struct pm_nl_pernet *pernet, - struct mptcp_sock *msk) + const struct mptcp_sock *msk) { + const struct sock *sk = (const struct sock *)msk; struct mptcp_pm_addr_entry *entry, *ret = NULL; - struct sock *sk = (struct sock *)msk; msk_owned_by_me(msk); @@ -190,7 +180,7 @@ select_local_address(const struct pm_nl_pernet *pernet, } static struct mptcp_pm_addr_entry * -select_signal_address(struct pm_nl_pernet *pernet, struct mptcp_sock *msk) +select_signal_address(struct pm_nl_pernet *pernet, const struct mptcp_sock *msk) { struct mptcp_pm_addr_entry *entry, *ret = NULL; @@ -214,16 +204,16 @@ select_signal_address(struct pm_nl_pernet *pernet, struct mptcp_sock *msk) return ret; } -unsigned int mptcp_pm_get_add_addr_signal_max(struct mptcp_sock *msk) +unsigned int mptcp_pm_get_add_addr_signal_max(const struct mptcp_sock *msk) { - struct pm_nl_pernet *pernet; + const struct pm_nl_pernet *pernet; - pernet = net_generic(sock_net((struct sock *)msk), pm_nl_pernet_id); + pernet = net_generic(sock_net((const struct sock *)msk), pm_nl_pernet_id); return READ_ONCE(pernet->add_addr_signal_max); } EXPORT_SYMBOL_GPL(mptcp_pm_get_add_addr_signal_max); -unsigned int mptcp_pm_get_add_addr_accept_max(struct mptcp_sock *msk) +unsigned int mptcp_pm_get_add_addr_accept_max(const struct mptcp_sock *msk) { struct pm_nl_pernet *pernet; @@ -232,7 +222,7 @@ unsigned int mptcp_pm_get_add_addr_accept_max(struct mptcp_sock *msk) } EXPORT_SYMBOL_GPL(mptcp_pm_get_add_addr_accept_max); -unsigned int mptcp_pm_get_subflows_max(struct mptcp_sock *msk) +unsigned int mptcp_pm_get_subflows_max(const struct mptcp_sock *msk) { struct pm_nl_pernet *pernet; @@ -241,7 +231,7 @@ unsigned int mptcp_pm_get_subflows_max(struct mptcp_sock *msk) } EXPORT_SYMBOL_GPL(mptcp_pm_get_subflows_max); -unsigned int mptcp_pm_get_local_addr_max(struct mptcp_sock *msk) +unsigned int mptcp_pm_get_local_addr_max(const struct mptcp_sock *msk) { struct pm_nl_pernet *pernet; @@ -264,8 +254,8 @@ bool mptcp_pm_nl_check_work_pending(struct mptcp_sock *msk) } struct mptcp_pm_add_entry * -mptcp_lookup_anno_list_by_saddr(struct mptcp_sock *msk, - struct mptcp_addr_info *addr) +mptcp_lookup_anno_list_by_saddr(const struct mptcp_sock *msk, + const struct mptcp_addr_info *addr) { struct mptcp_pm_add_entry *entry; @@ -346,7 +336,7 @@ static void mptcp_pm_add_timer(struct timer_list *timer) struct mptcp_pm_add_entry * mptcp_pm_del_add_timer(struct mptcp_sock *msk, - struct mptcp_addr_info *addr, bool check_id) + const struct mptcp_addr_info *addr, bool check_id) { struct mptcp_pm_add_entry *entry; struct sock *sk = (struct sock *)msk; @@ -364,7 +354,7 @@ mptcp_pm_del_add_timer(struct mptcp_sock *msk, } static bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk, - struct mptcp_pm_addr_entry *entry) + const struct mptcp_pm_addr_entry *entry) { struct mptcp_pm_add_entry *add_entry = NULL; struct sock *sk = (struct sock *)msk; @@ -410,8 +400,8 @@ void mptcp_pm_free_anno_list(struct mptcp_sock *msk) } } -static bool lookup_address_in_vec(struct mptcp_addr_info *addrs, unsigned int nr, - struct mptcp_addr_info *addr) +static bool lookup_address_in_vec(const struct mptcp_addr_info *addrs, unsigned int nr, + const struct mptcp_addr_info *addr) { int i; @@ -493,9 +483,9 @@ __lookup_addr(struct pm_nl_pernet *pernet, const struct mptcp_addr_info *info, } static int -lookup_id_by_addr(struct pm_nl_pernet *pernet, const struct mptcp_addr_info *addr) +lookup_id_by_addr(const struct pm_nl_pernet *pernet, const struct mptcp_addr_info *addr) { - struct mptcp_pm_addr_entry *entry; + const struct mptcp_pm_addr_entry *entry; int ret = -1; rcu_read_lock(); @@ -660,7 +650,6 @@ static void mptcp_pm_nl_add_addr_received(struct mptcp_sock *msk) unsigned int add_addr_accept_max; struct mptcp_addr_info remote; unsigned int subflows_max; - bool reset_port = false; int i, nr; add_addr_accept_max = mptcp_pm_get_add_addr_accept_max(msk); @@ -671,14 +660,15 @@ static void mptcp_pm_nl_add_addr_received(struct mptcp_sock *msk) msk->pm.remote.family); remote = msk->pm.remote; + mptcp_pm_announce_addr(msk, &remote, true); + mptcp_pm_nl_addr_send_ack(msk); + if (lookup_subflow_by_daddr(&msk->conn_list, &remote)) - goto add_addr_echo; + return; /* pick id 0 port, if none is provided the remote address */ - if (!remote.port) { - reset_port = true; + if (!remote.port) remote.port = sk->sk_dport; - } /* connect to the specified remote address, using whatever * local address the routing configuration will pick. @@ -694,14 +684,6 @@ static void mptcp_pm_nl_add_addr_received(struct mptcp_sock *msk) for (i = 0; i < nr; i++) __mptcp_subflow_connect(sk, &addrs[i], &remote); spin_lock_bh(&msk->pm.lock); - - /* be sure to echo exactly the received address */ - if (reset_port) - remote.port = 0; - -add_addr_echo: - mptcp_pm_announce_addr(msk, &remote, true); - mptcp_pm_nl_addr_send_ack(msk); } void mptcp_pm_nl_addr_send_ack(struct mptcp_sock *msk) @@ -877,10 +859,18 @@ static bool address_use_port(struct mptcp_pm_addr_entry *entry) MPTCP_PM_ADDR_FLAG_SIGNAL; } +/* caller must ensure the RCU grace period is already elapsed */ +static void __mptcp_pm_release_addr_entry(struct mptcp_pm_addr_entry *entry) +{ + if (entry->lsk) + sock_release(entry->lsk); + kfree(entry); +} + static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet, struct mptcp_pm_addr_entry *entry) { - struct mptcp_pm_addr_entry *cur; + struct mptcp_pm_addr_entry *cur, *del_entry = NULL; unsigned int addr_max; int ret = -EINVAL; @@ -901,8 +891,22 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet, list_for_each_entry(cur, &pernet->local_addr_list, list) { if (addresses_equal(&cur->addr, &entry->addr, address_use_port(entry) && - address_use_port(cur))) - goto out; + address_use_port(cur))) { + /* allow replacing the exiting endpoint only if such + * endpoint is an implicit one and the user-space + * did not provide an endpoint id + */ + if (!(cur->flags & MPTCP_PM_ADDR_FLAG_IMPLICIT)) + goto out; + if (entry->addr.id) + goto out; + + pernet->addrs--; + entry->addr.id = cur->addr.id; + list_del_rcu(&cur->list); + del_entry = cur; + break; + } } if (!entry->addr.id) { @@ -938,6 +942,12 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet, out: spin_unlock_bh(&pernet->lock); + + /* just replaced an existing entry, free it */ + if (del_entry) { + synchronize_rcu(); + __mptcp_pm_release_addr_entry(del_entry); + } return ret; } @@ -1011,9 +1021,6 @@ int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct sock_common *skc) if (addresses_equal(&msk_local, &skc_local, false)) return 0; - if (address_zero(&skc_local)) - return 0; - pernet = net_generic(sock_net((struct sock *)msk), pm_nl_pernet_id); rcu_read_lock(); @@ -1036,7 +1043,7 @@ int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct sock_common *skc) entry->addr.id = 0; entry->addr.port = 0; entry->ifindex = 0; - entry->flags = 0; + entry->flags = MPTCP_PM_ADDR_FLAG_IMPLICIT; entry->lsk = NULL; ret = mptcp_pm_nl_append_new_local_addr(pernet, entry); if (ret < 0) @@ -1197,14 +1204,8 @@ static int mptcp_pm_parse_addr(struct nlattr *attr, struct genl_info *info, if (tb[MPTCP_PM_ADDR_ATTR_FLAGS]) entry->flags = nla_get_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]); - if (tb[MPTCP_PM_ADDR_ATTR_PORT]) { - if (!(entry->flags & MPTCP_PM_ADDR_FLAG_SIGNAL)) { - NL_SET_ERR_MSG_ATTR(info->extack, attr, - "flags must have signal when using port"); - return -EINVAL; - } + if (tb[MPTCP_PM_ADDR_ATTR_PORT]) entry->addr.port = htons(nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_PORT])); - } return 0; } @@ -1250,6 +1251,22 @@ static int mptcp_nl_cmd_add_addr(struct sk_buff *skb, struct genl_info *info) if (ret < 0) return ret; + if (addr.addr.port && !(addr.flags & MPTCP_PM_ADDR_FLAG_SIGNAL)) { + GENL_SET_ERR_MSG(info, "flags must have signal when using port"); + return -EINVAL; + } + + if (addr.flags & MPTCP_PM_ADDR_FLAG_SIGNAL && + addr.flags & MPTCP_PM_ADDR_FLAG_FULLMESH) { + GENL_SET_ERR_MSG(info, "flags mustn't have both signal and fullmesh"); + return -EINVAL; + } + + if (addr.flags & MPTCP_PM_ADDR_FLAG_IMPLICIT) { + GENL_SET_ERR_MSG(info, "can't create IMPLICIT endpoint"); + return -EINVAL; + } + entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (!entry) { GENL_SET_ERR_MSG(info, "can't allocate addr"); @@ -1301,7 +1318,7 @@ int mptcp_pm_get_flags_and_ifindex_by_id(struct net *net, unsigned int id, } static bool remove_anno_list_by_saddr(struct mptcp_sock *msk, - struct mptcp_addr_info *addr) + const struct mptcp_addr_info *addr) { struct mptcp_pm_add_entry *entry; @@ -1316,7 +1333,7 @@ static bool remove_anno_list_by_saddr(struct mptcp_sock *msk, } static bool mptcp_pm_remove_anno_addr(struct mptcp_sock *msk, - struct mptcp_addr_info *addr, + const struct mptcp_addr_info *addr, bool force) { struct mptcp_rm_list list = { .nr = 0 }; @@ -1334,11 +1351,12 @@ static bool mptcp_pm_remove_anno_addr(struct mptcp_sock *msk, } static int mptcp_nl_remove_subflow_and_signal_addr(struct net *net, - struct mptcp_addr_info *addr) + const struct mptcp_pm_addr_entry *entry) { - struct mptcp_sock *msk; - long s_slot = 0, s_num = 0; + const struct mptcp_addr_info *addr = &entry->addr; struct mptcp_rm_list list = { .nr = 0 }; + long s_slot = 0, s_num = 0; + struct mptcp_sock *msk; pr_debug("remove_id=%d", addr->id); @@ -1355,7 +1373,8 @@ static int mptcp_nl_remove_subflow_and_signal_addr(struct net *net, lock_sock(sk); remove_subflow = lookup_subflow_by_saddr(&msk->conn_list, addr); - mptcp_pm_remove_anno_addr(msk, addr, remove_subflow); + mptcp_pm_remove_anno_addr(msk, addr, remove_subflow && + !(entry->flags & MPTCP_PM_ADDR_FLAG_IMPLICIT)); if (remove_subflow) mptcp_pm_remove_subflow(msk, &list); release_sock(sk); @@ -1368,14 +1387,6 @@ static int mptcp_nl_remove_subflow_and_signal_addr(struct net *net, return 0; } -/* caller must ensure the RCU grace period is already elapsed */ -static void __mptcp_pm_release_addr_entry(struct mptcp_pm_addr_entry *entry) -{ - if (entry->lsk) - sock_release(entry->lsk); - kfree(entry); -} - static int mptcp_nl_remove_id_zero_address(struct net *net, struct mptcp_addr_info *addr) { @@ -1452,7 +1463,7 @@ static int mptcp_nl_cmd_del_addr(struct sk_buff *skb, struct genl_info *info) __clear_bit(entry->addr.id, pernet->id_bitmap); spin_unlock_bh(&pernet->lock); - mptcp_nl_remove_subflow_and_signal_addr(sock_net(skb->sk), &entry->addr); + mptcp_nl_remove_subflow_and_signal_addr(sock_net(skb->sk), entry); synchronize_rcu(); __mptcp_pm_release_addr_entry(entry); @@ -1467,14 +1478,12 @@ static void mptcp_pm_remove_addrs_and_subflows(struct mptcp_sock *msk, list_for_each_entry(entry, rm_list, list) { if (lookup_subflow_by_saddr(&msk->conn_list, &entry->addr) && - alist.nr < MPTCP_RM_IDS_MAX && - slist.nr < MPTCP_RM_IDS_MAX) { - alist.ids[alist.nr++] = entry->addr.id; + slist.nr < MPTCP_RM_IDS_MAX) slist.ids[slist.nr++] = entry->addr.id; - } else if (remove_anno_list_by_saddr(msk, &entry->addr) && - alist.nr < MPTCP_RM_IDS_MAX) { + + if (remove_anno_list_by_saddr(msk, &entry->addr) && + alist.nr < MPTCP_RM_IDS_MAX) alist.ids[alist.nr++] = entry->addr.id; - } } if (alist.nr) { @@ -1751,9 +1760,20 @@ mptcp_nl_cmd_get_limits(struct sk_buff *skb, struct genl_info *info) return -EMSGSIZE; } -static int mptcp_nl_addr_backup(struct net *net, - struct mptcp_addr_info *addr, - u8 bkup) +static void mptcp_pm_nl_fullmesh(struct mptcp_sock *msk, + struct mptcp_addr_info *addr) +{ + struct mptcp_rm_list list = { .nr = 0 }; + + list.ids[list.nr++] = addr->id; + + mptcp_pm_nl_rm_subflow_received(msk, &list); + mptcp_pm_create_subflow_or_signal_addr(msk); +} + +static int mptcp_nl_set_flags(struct net *net, + struct mptcp_addr_info *addr, + u8 bkup, u8 changed) { long s_slot = 0, s_num = 0; struct mptcp_sock *msk; @@ -1767,7 +1787,10 @@ static int mptcp_nl_addr_backup(struct net *net, lock_sock(sk); spin_lock_bh(&msk->pm.lock); - ret = mptcp_pm_nl_mp_prio_send_ack(msk, addr, bkup); + if (changed & MPTCP_PM_ADDR_FLAG_BACKUP) + ret = mptcp_pm_nl_mp_prio_send_ack(msk, addr, bkup); + if (changed & MPTCP_PM_ADDR_FLAG_FULLMESH) + mptcp_pm_nl_fullmesh(msk, addr); spin_unlock_bh(&msk->pm.lock); release_sock(sk); @@ -1784,6 +1807,8 @@ static int mptcp_nl_cmd_set_flags(struct sk_buff *skb, struct genl_info *info) struct mptcp_pm_addr_entry addr = { .addr = { .family = AF_UNSPEC }, }, *entry; struct nlattr *attr = info->attrs[MPTCP_PM_ATTR_ADDR]; struct pm_nl_pernet *pernet = genl_info_pm_nl(info); + u8 changed, mask = MPTCP_PM_ADDR_FLAG_BACKUP | + MPTCP_PM_ADDR_FLAG_FULLMESH; struct net *net = sock_net(skb->sk); u8 bkup = 0, lookup_by_id = 0; int ret; @@ -1806,15 +1831,18 @@ static int mptcp_nl_cmd_set_flags(struct sk_buff *skb, struct genl_info *info) spin_unlock_bh(&pernet->lock); return -EINVAL; } + if ((addr.flags & MPTCP_PM_ADDR_FLAG_FULLMESH) && + (entry->flags & MPTCP_PM_ADDR_FLAG_SIGNAL)) { + spin_unlock_bh(&pernet->lock); + return -EINVAL; + } - if (bkup) - entry->flags |= MPTCP_PM_ADDR_FLAG_BACKUP; - else - entry->flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP; + changed = (addr.flags ^ entry->flags) & mask; + entry->flags = (entry->flags & ~mask) | (addr.flags & mask); addr = *entry; spin_unlock_bh(&pernet->lock); - mptcp_nl_addr_backup(net, &addr.addr, bkup); + mptcp_nl_set_flags(net, &addr.addr, bkup, changed); return 0; } diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 1c72f25f083e..0cbea3b6d0a4 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -117,6 +117,9 @@ static int __mptcp_socket_create(struct mptcp_sock *msk) list_add(&subflow->node, &msk->conn_list); sock_hold(ssock->sk); subflow->request_mptcp = 1; + + /* This is the first subflow, always with id 0 */ + subflow->local_id_valid = 1; mptcp_sock_graft(msk->first, sk->sk_socket); return 0; @@ -1196,6 +1199,7 @@ static struct sk_buff *__mptcp_alloc_tx_skb(struct sock *sk, struct sock *ssk, g tcp_skb_entail(ssk, skb); return skb; } + tcp_skb_tsorted_anchor_cleanup(skb); kfree_skb(skb); return NULL; } @@ -1356,6 +1360,7 @@ static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk, out: if (READ_ONCE(msk->csum_enabled)) mptcp_update_data_checksum(skb, copy); + trace_mptcp_sendmsg_frag(mpext); mptcp_subflow_ctx(ssk)->rel_write_seq += copy; return copy; } diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 85317ce38e3f..3c1a3036550f 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -442,7 +442,8 @@ struct mptcp_subflow_context { rx_eof : 1, can_ack : 1, /* only after processing the remote a key */ disposable : 1, /* ctx can be free at ulp release time */ - stale : 1; /* unable to snd/rcv data, do not use for xmit */ + stale : 1, /* unable to snd/rcv data, do not use for xmit */ + local_id_valid : 1; /* local_id is correctly initialized */ enum mptcp_data_avail data_avail; u32 remote_nonce; u64 thmac; @@ -468,9 +469,7 @@ struct mptcp_subflow_context { struct sock *tcp_sock; /* tcp sk backpointer */ struct sock *conn; /* parent mptcp_sock */ const struct inet_connection_sock_af_ops *icsk_af_ops; - void (*tcp_data_ready)(struct sock *sk); void (*tcp_state_change)(struct sock *sk); - void (*tcp_write_space)(struct sock *sk); void (*tcp_error_report)(struct sock *sk); struct rcu_head rcu; @@ -614,9 +613,9 @@ bool mptcp_subflow_active(struct mptcp_subflow_context *subflow); static inline void mptcp_subflow_tcp_fallback(struct sock *sk, struct mptcp_subflow_context *ctx) { - sk->sk_data_ready = ctx->tcp_data_ready; + sk->sk_data_ready = sock_def_readable; sk->sk_state_change = ctx->tcp_state_change; - sk->sk_write_space = ctx->tcp_write_space; + sk->sk_write_space = sk_stream_write_space; sk->sk_error_report = ctx->tcp_error_report; inet_csk(sk)->icsk_af_ops = ctx->icsk_af_ops; @@ -643,8 +642,7 @@ int __init mptcp_proto_v6_init(void); struct sock *mptcp_sk_clone(const struct sock *sk, const struct mptcp_options_received *mp_opt, struct request_sock *req); -void mptcp_get_options(const struct sock *sk, - const struct sk_buff *skb, +void mptcp_get_options(const struct sk_buff *skb, struct mptcp_options_received *mp_opt); void mptcp_finish_connect(struct sock *sk); @@ -743,7 +741,7 @@ void mptcp_pm_subflow_check_next(struct mptcp_sock *msk, const struct sock *ssk, void mptcp_pm_add_addr_received(struct mptcp_sock *msk, const struct mptcp_addr_info *addr); void mptcp_pm_add_addr_echoed(struct mptcp_sock *msk, - struct mptcp_addr_info *addr); + const struct mptcp_addr_info *addr); void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk); void mptcp_pm_nl_addr_send_ack(struct mptcp_sock *msk); void mptcp_pm_rm_addr_received(struct mptcp_sock *msk, @@ -754,10 +752,10 @@ void mptcp_pm_free_anno_list(struct mptcp_sock *msk); bool mptcp_pm_sport_in_anno_list(struct mptcp_sock *msk, const struct sock *sk); struct mptcp_pm_add_entry * mptcp_pm_del_add_timer(struct mptcp_sock *msk, - struct mptcp_addr_info *addr, bool check_id); + const struct mptcp_addr_info *addr, bool check_id); struct mptcp_pm_add_entry * -mptcp_lookup_anno_list_by_saddr(struct mptcp_sock *msk, - struct mptcp_addr_info *addr); +mptcp_lookup_anno_list_by_saddr(const struct mptcp_sock *msk, + const struct mptcp_addr_info *addr); int mptcp_pm_get_flags_and_ifindex_by_id(struct net *net, unsigned int id, u8 *flags, int *ifindex); @@ -816,10 +814,10 @@ static inline int mptcp_rm_addr_len(const struct mptcp_rm_list *rm_list) return TCPOLEN_MPTCP_RM_ADDR_BASE + roundup(rm_list->nr - 1, 4) + 1; } -bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, struct sk_buff *skb, +bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, const struct sk_buff *skb, unsigned int opt_size, unsigned int remaining, struct mptcp_addr_info *addr, bool *echo, - bool *port, bool *drop_other_suboptions); + bool *drop_other_suboptions); bool mptcp_pm_rm_addr_signal(struct mptcp_sock *msk, unsigned int remaining, struct mptcp_rm_list *rm_list); int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc); @@ -830,10 +828,10 @@ void mptcp_pm_nl_work(struct mptcp_sock *msk); void mptcp_pm_nl_rm_subflow_received(struct mptcp_sock *msk, const struct mptcp_rm_list *rm_list); int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct sock_common *skc); -unsigned int mptcp_pm_get_add_addr_signal_max(struct mptcp_sock *msk); -unsigned int mptcp_pm_get_add_addr_accept_max(struct mptcp_sock *msk); -unsigned int mptcp_pm_get_subflows_max(struct mptcp_sock *msk); -unsigned int mptcp_pm_get_local_addr_max(struct mptcp_sock *msk); +unsigned int mptcp_pm_get_add_addr_signal_max(const struct mptcp_sock *msk); +unsigned int mptcp_pm_get_add_addr_accept_max(const struct mptcp_sock *msk); +unsigned int mptcp_pm_get_subflows_max(const struct mptcp_sock *msk); +unsigned int mptcp_pm_get_local_addr_max(const struct mptcp_sock *msk); void mptcp_sockopt_sync(struct mptcp_sock *msk, struct sock *ssk); void mptcp_sockopt_sync_locked(struct mptcp_sock *msk, struct sock *ssk); diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index dacf3cee0027..f949d22f52bd 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -343,6 +343,8 @@ static int mptcp_setsockopt_sol_socket(struct mptcp_sock *msk, int optname, case SO_RCVLOWAT: case SO_RCVTIMEO_OLD: case SO_RCVTIMEO_NEW: + case SO_SNDTIMEO_OLD: + case SO_SNDTIMEO_NEW: case SO_BUSY_POLL: case SO_PREFER_BUSY_POLL: case SO_BUSY_POLL_BUDGET: diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index bea47a1180dc..aba260f547da 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -153,7 +153,7 @@ static int subflow_check_req(struct request_sock *req, return -EINVAL; #endif - mptcp_get_options(sk_listener, skb, &mp_opt); + mptcp_get_options(skb, &mp_opt); opt_mp_capable = !!(mp_opt.suboptions & OPTIONS_MPTCP_MPC); opt_mp_join = !!(mp_opt.suboptions & OPTIONS_MPTCP_MPJ); @@ -250,7 +250,7 @@ int mptcp_subflow_init_cookie_req(struct request_sock *req, int err; subflow_init_req(req, sk_listener); - mptcp_get_options(sk_listener, skb, &mp_opt); + mptcp_get_options(skb, &mp_opt); opt_mp_capable = !!(mp_opt.suboptions & OPTIONS_MPTCP_MPC); opt_mp_join = !!(mp_opt.suboptions & OPTIONS_MPTCP_MPJ); @@ -344,9 +344,7 @@ static bool subflow_thmac_valid(struct mptcp_subflow_context *subflow) thmac = get_unaligned_be64(hmac); pr_debug("subflow=%p, token=%u, thmac=%llu, subflow->thmac=%llu\n", - subflow, subflow->token, - (unsigned long long)thmac, - (unsigned long long)subflow->thmac); + subflow, subflow->token, thmac, subflow->thmac); return thmac == subflow->thmac; } @@ -410,7 +408,7 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb) subflow->ssn_offset = TCP_SKB_CB(skb)->seq; pr_debug("subflow=%p synack seq=%x", subflow, subflow->ssn_offset); - mptcp_get_options(sk, skb, &mp_opt); + mptcp_get_options(skb, &mp_opt); if (subflow->request_mptcp) { if (!(mp_opt.suboptions & OPTIONS_MPTCP_MPC)) { MPTCP_INC_STATS(sock_net(sk), @@ -483,9 +481,53 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb) mptcp_subflow_reset(sk); } +static void subflow_set_local_id(struct mptcp_subflow_context *subflow, int local_id) +{ + subflow->local_id = local_id; + subflow->local_id_valid = 1; +} + +static int subflow_chk_local_id(struct sock *sk) +{ + struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); + struct mptcp_sock *msk = mptcp_sk(subflow->conn); + int err; + + if (likely(subflow->local_id_valid)) + return 0; + + err = mptcp_pm_get_local_id(msk, (struct sock_common *)sk); + if (err < 0) + return err; + + subflow_set_local_id(subflow, err); + return 0; +} + +static int subflow_rebuild_header(struct sock *sk) +{ + int err = subflow_chk_local_id(sk); + + if (unlikely(err < 0)) + return err; + + return inet_sk_rebuild_header(sk); +} + +#if IS_ENABLED(CONFIG_MPTCP_IPV6) +static int subflow_v6_rebuild_header(struct sock *sk) +{ + int err = subflow_chk_local_id(sk); + + if (unlikely(err < 0)) + return err; + + return inet6_sk_rebuild_header(sk); +} +#endif + struct request_sock_ops mptcp_subflow_request_sock_ops; -EXPORT_SYMBOL_GPL(mptcp_subflow_request_sock_ops); -static struct tcp_request_sock_ops subflow_request_sock_ipv4_ops; +static struct tcp_request_sock_ops subflow_request_sock_ipv4_ops __ro_after_init; static int subflow_v4_conn_request(struct sock *sk, struct sk_buff *skb) { @@ -506,9 +548,9 @@ static int subflow_v4_conn_request(struct sock *sk, struct sk_buff *skb) } #if IS_ENABLED(CONFIG_MPTCP_IPV6) -static struct tcp_request_sock_ops subflow_request_sock_ipv6_ops; -static struct inet_connection_sock_af_ops subflow_v6_specific; -static struct inet_connection_sock_af_ops subflow_v6m_specific; +static struct tcp_request_sock_ops subflow_request_sock_ipv6_ops __ro_after_init; +static struct inet_connection_sock_af_ops subflow_v6_specific __ro_after_init; +static struct inet_connection_sock_af_ops subflow_v6m_specific __ro_after_init; static struct proto tcpv6_prot_override; static int subflow_v6_conn_request(struct sock *sk, struct sk_buff *skb) @@ -663,7 +705,7 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk, * reordered MPC will cause fallback, but we don't have other * options. */ - mptcp_get_options(sk, skb, &mp_opt); + mptcp_get_options(skb, &mp_opt); if (!(mp_opt.suboptions & OPTIONS_MPTCP_MPC)) { fallback = true; goto create_child; @@ -673,7 +715,7 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk, if (!new_msk) fallback = true; } else if (subflow_req->mp_join) { - mptcp_get_options(sk, skb, &mp_opt); + mptcp_get_options(skb, &mp_opt); if (!(mp_opt.suboptions & OPTIONS_MPTCP_MPJ) || !subflow_hmac_valid(req, &mp_opt) || !mptcp_can_accept_new_subflow(subflow_req->msk)) { @@ -790,7 +832,7 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk, return child; } -static struct inet_connection_sock_af_ops subflow_specific; +static struct inet_connection_sock_af_ops subflow_specific __ro_after_init; static struct proto tcp_prot_override; enum mapping_status { @@ -1107,7 +1149,7 @@ static bool subflow_check_data_avail(struct sock *ssk) struct sk_buff *skb; if (!skb_peek(&ssk->sk_receive_queue)) - WRITE_ONCE(subflow->data_avail, 0); + WRITE_ONCE(subflow->data_avail, MPTCP_SUBFLOW_NODATA); if (subflow->data_avail) return true; @@ -1172,7 +1214,7 @@ static bool subflow_check_data_avail(struct sock *ssk) subflow->reset_transient = 0; subflow->reset_reason = MPTCP_RST_EMIDDLEBOX; tcp_send_active_reset(ssk, GFP_ATOMIC); - WRITE_ONCE(subflow->data_avail, 0); + WRITE_ONCE(subflow->data_avail, MPTCP_SUBFLOW_NODATA); return true; } @@ -1185,7 +1227,7 @@ static bool subflow_check_data_avail(struct sock *ssk) subflow->reset_transient = 0; subflow->reset_reason = MPTCP_RST_EMPTCP; tcp_send_active_reset(ssk, GFP_ATOMIC); - WRITE_ONCE(subflow->data_avail, 0); + WRITE_ONCE(subflow->data_avail, MPTCP_SUBFLOW_NODATA); return false; } @@ -1207,7 +1249,7 @@ bool mptcp_subflow_data_available(struct sock *sk) if (subflow->map_valid && mptcp_subflow_get_map_offset(subflow) >= subflow->map_data_len) { subflow->map_valid = 0; - WRITE_ONCE(subflow->data_avail, 0); + WRITE_ONCE(subflow->data_avail, MPTCP_SUBFLOW_NODATA); pr_debug("Done with mapping: seq=%u data_len=%u", subflow->map_subflow_seq, @@ -1311,7 +1353,7 @@ static void subflow_write_space(struct sock *ssk) mptcp_write_space(sk); } -static struct inet_connection_sock_af_ops * +static const struct inet_connection_sock_af_ops * subflow_default_af_ops(struct sock *sk) { #if IS_ENABLED(CONFIG_MPTCP_IPV6) @@ -1326,7 +1368,7 @@ void mptcpv6_handle_mapped(struct sock *sk, bool mapped) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); struct inet_connection_sock *icsk = inet_csk(sk); - struct inet_connection_sock_af_ops *target; + const struct inet_connection_sock_af_ops *target; target = mapped ? &subflow_v6m_specific : subflow_default_af_ops(sk); @@ -1401,13 +1443,8 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc, get_random_bytes(&subflow->local_nonce, sizeof(u32)); } while (!subflow->local_nonce); - if (!local_id) { - err = mptcp_pm_get_local_id(msk, (struct sock_common *)ssk); - if (err < 0) - goto failed; - - local_id = err; - } + if (local_id) + subflow_set_local_id(subflow, local_id); mptcp_pm_get_flags_and_ifindex_by_id(sock_net(sk), local_id, &flags, &ifindex); @@ -1432,7 +1469,6 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc, pr_debug("msk=%p remote_token=%u local_id=%d remote_id=%d", msk, remote_token, local_id, remote_id); subflow->remote_token = remote_token; - subflow->local_id = local_id; subflow->remote_id = remote_id; subflow->request_join = 1; subflow->request_bkup = !!(flags & MPTCP_PM_ADDR_FLAG_BACKUP); @@ -1657,10 +1693,12 @@ static int subflow_ulp_init(struct sock *sk) tp->is_mptcp = 1; ctx->icsk_af_ops = icsk->icsk_af_ops; icsk->icsk_af_ops = subflow_default_af_ops(sk); - ctx->tcp_data_ready = sk->sk_data_ready; ctx->tcp_state_change = sk->sk_state_change; - ctx->tcp_write_space = sk->sk_write_space; ctx->tcp_error_report = sk->sk_error_report; + + WARN_ON_ONCE(sk->sk_data_ready != sock_def_readable); + WARN_ON_ONCE(sk->sk_write_space != sk_stream_write_space); + sk->sk_data_ready = subflow_data_ready; sk->sk_write_space = subflow_write_space; sk->sk_state_change = subflow_state_change; @@ -1715,9 +1753,7 @@ static void subflow_ulp_clone(const struct request_sock *req, new_ctx->conn_finished = 1; new_ctx->icsk_af_ops = old_ctx->icsk_af_ops; - new_ctx->tcp_data_ready = old_ctx->tcp_data_ready; new_ctx->tcp_state_change = old_ctx->tcp_state_change; - new_ctx->tcp_write_space = old_ctx->tcp_write_space; new_ctx->tcp_error_report = old_ctx->tcp_error_report; new_ctx->rel_write_seq = 1; new_ctx->tcp_sock = newsk; @@ -1731,15 +1767,22 @@ static void subflow_ulp_clone(const struct request_sock *req, new_ctx->token = subflow_req->token; new_ctx->ssn_offset = subflow_req->ssn_offset; new_ctx->idsn = subflow_req->idsn; + + /* this is the first subflow, id is always 0 */ + new_ctx->local_id_valid = 1; } else if (subflow_req->mp_join) { new_ctx->ssn_offset = subflow_req->ssn_offset; new_ctx->mp_join = 1; new_ctx->fully_established = 1; new_ctx->backup = subflow_req->backup; - new_ctx->local_id = subflow_req->local_id; new_ctx->remote_id = subflow_req->remote_id; new_ctx->token = subflow_req->token; new_ctx->thmac = subflow_req->thmac; + + /* the subflow req id is valid, fetched via subflow_check_req() + * and subflow_token_join_request() + */ + subflow_set_local_id(new_ctx, subflow_req->local_id); } } @@ -1792,6 +1835,7 @@ void __init mptcp_subflow_init(void) subflow_specific.conn_request = subflow_v4_conn_request; subflow_specific.syn_recv_sock = subflow_syn_recv_sock; subflow_specific.sk_rx_dst_set = subflow_finish_connect; + subflow_specific.rebuild_header = subflow_rebuild_header; tcp_prot_override = tcp_prot; tcp_prot_override.release_cb = tcp_release_cb_override; @@ -1804,6 +1848,7 @@ void __init mptcp_subflow_init(void) subflow_v6_specific.conn_request = subflow_v6_conn_request; subflow_v6_specific.syn_recv_sock = subflow_syn_recv_sock; subflow_v6_specific.sk_rx_dst_set = subflow_finish_connect; + subflow_v6_specific.rebuild_header = subflow_v6_rebuild_header; subflow_v6m_specific = subflow_v6_specific; subflow_v6m_specific.queue_xmit = ipv4_specific.queue_xmit; @@ -1811,6 +1856,7 @@ void __init mptcp_subflow_init(void) subflow_v6m_specific.net_header_len = ipv4_specific.net_header_len; subflow_v6m_specific.mtu_reduced = ipv4_specific.mtu_reduced; subflow_v6m_specific.net_frag_header_len = 0; + subflow_v6m_specific.rebuild_header = subflow_rebuild_header; tcpv6_prot_override = tcpv6_prot; tcpv6_prot_override.release_cb = tcp_release_cb_override; diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index a135b1a46014..238b6a620e88 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -14,6 +14,11 @@ nf_conntrack-$(CONFIG_NF_CONNTRACK_LABELS) += nf_conntrack_labels.o nf_conntrack-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o nf_conntrack-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o nf_conntrack-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o +ifeq ($(CONFIG_NF_CONNTRACK),m) +nf_conntrack-$(CONFIG_DEBUG_INFO_BTF_MODULES) += nf_conntrack_bpf.o +else ifeq ($(CONFIG_NF_CONNTRACK),y) +nf_conntrack-$(CONFIG_DEBUG_INFO_BTF) += nf_conntrack_bpf.o +endif obj-$(CONFIG_NETFILTER) = netfilter.o diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 8a77a3fd69bc..9a4feb922cf6 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -622,7 +622,8 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, case NF_ACCEPT: break; case NF_DROP: - kfree_skb(skb); + kfree_skb_reason(skb, + SKB_DROP_REASON_NETFILTER_DROP); ret = NF_DROP_GETERR(verdict); if (ret == 0) ret = -EPERM; diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index d2e5a8f644b8..029171379884 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -610,7 +610,7 @@ static inline int ip_vs_tunnel_xmit_prepare(struct sk_buff *skb, nf_reset_ct(skb); skb_forward_csum(skb); if (skb->dev) - skb->tstamp = 0; + skb_clear_tstamp(skb); } return ret; } @@ -652,7 +652,7 @@ static inline int ip_vs_nat_send_or_cont(int pf, struct sk_buff *skb, if (!local) { skb_forward_csum(skb); if (skb->dev) - skb->tstamp = 0; + skb_clear_tstamp(skb); NF_HOOK(pf, NF_INET_LOCAL_OUT, cp->ipvs->net, NULL, skb, NULL, skb_dst(skb)->dev, dst_output); } else @@ -674,7 +674,7 @@ static inline int ip_vs_send_or_cont(int pf, struct sk_buff *skb, ip_vs_drop_early_demux_sk(skb); skb_forward_csum(skb); if (skb->dev) - skb->tstamp = 0; + skb_clear_tstamp(skb); NF_HOOK(pf, NF_INET_LOCAL_OUT, cp->ipvs->net, NULL, skb, NULL, skb_dst(skb)->dev, dst_output); } else diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c index 91bc8df3e4b0..385a5f458aba 100644 --- a/net/netfilter/nf_conntrack_acct.c +++ b/net/netfilter/nf_conntrack_acct.c @@ -22,26 +22,7 @@ static bool nf_ct_acct __read_mostly; module_param_named(acct, nf_ct_acct, bool, 0644); MODULE_PARM_DESC(acct, "Enable connection tracking flow accounting."); -static const struct nf_ct_ext_type acct_extend = { - .len = sizeof(struct nf_conn_acct), - .align = __alignof__(struct nf_conn_acct), - .id = NF_CT_EXT_ACCT, -}; - void nf_conntrack_acct_pernet_init(struct net *net) { net->ct.sysctl_acct = nf_ct_acct; } - -int nf_conntrack_acct_init(void) -{ - int ret = nf_ct_extend_register(&acct_extend); - if (ret < 0) - pr_err("Unable to register extension\n"); - return ret; -} - -void nf_conntrack_acct_fini(void) -{ - nf_ct_extend_unregister(&acct_extend); -} diff --git a/net/netfilter/nf_conntrack_bpf.c b/net/netfilter/nf_conntrack_bpf.c new file mode 100644 index 000000000000..fe98673dd5ac --- /dev/null +++ b/net/netfilter/nf_conntrack_bpf.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Unstable Conntrack Helpers for XDP and TC-BPF hook + * + * These are called from the XDP and SCHED_CLS BPF programs. Note that it is + * allowed to break compatibility for these functions since the interface they + * are exposed through to BPF programs is explicitly unstable. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* bpf_ct_opts - Options for CT lookup helpers + * + * Members: + * @netns_id - Specify the network namespace for lookup + * Values: + * BPF_F_CURRENT_NETNS (-1) + * Use namespace associated with ctx (xdp_md, __sk_buff) + * [0, S32_MAX] + * Network Namespace ID + * @error - Out parameter, set for any errors encountered + * Values: + * -EINVAL - Passed NULL for bpf_tuple pointer + * -EINVAL - opts->reserved is not 0 + * -EINVAL - netns_id is less than -1 + * -EINVAL - opts__sz isn't NF_BPF_CT_OPTS_SZ (12) + * -EPROTO - l4proto isn't one of IPPROTO_TCP or IPPROTO_UDP + * -ENONET - No network namespace found for netns_id + * -ENOENT - Conntrack lookup could not find entry for tuple + * -EAFNOSUPPORT - tuple__sz isn't one of sizeof(tuple->ipv4) + * or sizeof(tuple->ipv6) + * @l4proto - Layer 4 protocol + * Values: + * IPPROTO_TCP, IPPROTO_UDP + * @reserved - Reserved member, will be reused for more options in future + * Values: + * 0 + */ +struct bpf_ct_opts { + s32 netns_id; + s32 error; + u8 l4proto; + u8 reserved[3]; +}; + +enum { + NF_BPF_CT_OPTS_SZ = 12, +}; + +static struct nf_conn *__bpf_nf_ct_lookup(struct net *net, + struct bpf_sock_tuple *bpf_tuple, + u32 tuple_len, u8 protonum, + s32 netns_id) +{ + struct nf_conntrack_tuple_hash *hash; + struct nf_conntrack_tuple tuple; + + if (unlikely(protonum != IPPROTO_TCP && protonum != IPPROTO_UDP)) + return ERR_PTR(-EPROTO); + if (unlikely(netns_id < BPF_F_CURRENT_NETNS)) + return ERR_PTR(-EINVAL); + + memset(&tuple, 0, sizeof(tuple)); + switch (tuple_len) { + case sizeof(bpf_tuple->ipv4): + tuple.src.l3num = AF_INET; + tuple.src.u3.ip = bpf_tuple->ipv4.saddr; + tuple.src.u.tcp.port = bpf_tuple->ipv4.sport; + tuple.dst.u3.ip = bpf_tuple->ipv4.daddr; + tuple.dst.u.tcp.port = bpf_tuple->ipv4.dport; + break; + case sizeof(bpf_tuple->ipv6): + tuple.src.l3num = AF_INET6; + memcpy(tuple.src.u3.ip6, bpf_tuple->ipv6.saddr, sizeof(bpf_tuple->ipv6.saddr)); + tuple.src.u.tcp.port = bpf_tuple->ipv6.sport; + memcpy(tuple.dst.u3.ip6, bpf_tuple->ipv6.daddr, sizeof(bpf_tuple->ipv6.daddr)); + tuple.dst.u.tcp.port = bpf_tuple->ipv6.dport; + break; + default: + return ERR_PTR(-EAFNOSUPPORT); + } + + tuple.dst.protonum = protonum; + + if (netns_id >= 0) { + net = get_net_ns_by_id(net, netns_id); + if (unlikely(!net)) + return ERR_PTR(-ENONET); + } + + hash = nf_conntrack_find_get(net, &nf_ct_zone_dflt, &tuple); + if (netns_id >= 0) + put_net(net); + if (!hash) + return ERR_PTR(-ENOENT); + return nf_ct_tuplehash_to_ctrack(hash); +} + +__diag_push(); +__diag_ignore_all("-Wmissing-prototypes", + "Global functions as their definitions will be in nf_conntrack BTF"); + +/* bpf_xdp_ct_lookup - Lookup CT entry for the given tuple, and acquire a + * reference to it + * + * Parameters: + * @xdp_ctx - Pointer to ctx (xdp_md) in XDP program + * Cannot be NULL + * @bpf_tuple - Pointer to memory representing the tuple to look up + * Cannot be NULL + * @tuple__sz - Length of the tuple structure + * Must be one of sizeof(bpf_tuple->ipv4) or + * sizeof(bpf_tuple->ipv6) + * @opts - Additional options for lookup (documented above) + * Cannot be NULL + * @opts__sz - Length of the bpf_ct_opts structure + * Must be NF_BPF_CT_OPTS_SZ (12) + */ +struct nf_conn * +bpf_xdp_ct_lookup(struct xdp_md *xdp_ctx, struct bpf_sock_tuple *bpf_tuple, + u32 tuple__sz, struct bpf_ct_opts *opts, u32 opts__sz) +{ + struct xdp_buff *ctx = (struct xdp_buff *)xdp_ctx; + struct net *caller_net; + struct nf_conn *nfct; + + BUILD_BUG_ON(sizeof(struct bpf_ct_opts) != NF_BPF_CT_OPTS_SZ); + + if (!opts) + return NULL; + if (!bpf_tuple || opts->reserved[0] || opts->reserved[1] || + opts->reserved[2] || opts__sz != NF_BPF_CT_OPTS_SZ) { + opts->error = -EINVAL; + return NULL; + } + caller_net = dev_net(ctx->rxq->dev); + nfct = __bpf_nf_ct_lookup(caller_net, bpf_tuple, tuple__sz, opts->l4proto, + opts->netns_id); + if (IS_ERR(nfct)) { + opts->error = PTR_ERR(nfct); + return NULL; + } + return nfct; +} + +/* bpf_skb_ct_lookup - Lookup CT entry for the given tuple, and acquire a + * reference to it + * + * Parameters: + * @skb_ctx - Pointer to ctx (__sk_buff) in TC program + * Cannot be NULL + * @bpf_tuple - Pointer to memory representing the tuple to look up + * Cannot be NULL + * @tuple__sz - Length of the tuple structure + * Must be one of sizeof(bpf_tuple->ipv4) or + * sizeof(bpf_tuple->ipv6) + * @opts - Additional options for lookup (documented above) + * Cannot be NULL + * @opts__sz - Length of the bpf_ct_opts structure + * Must be NF_BPF_CT_OPTS_SZ (12) + */ +struct nf_conn * +bpf_skb_ct_lookup(struct __sk_buff *skb_ctx, struct bpf_sock_tuple *bpf_tuple, + u32 tuple__sz, struct bpf_ct_opts *opts, u32 opts__sz) +{ + struct sk_buff *skb = (struct sk_buff *)skb_ctx; + struct net *caller_net; + struct nf_conn *nfct; + + BUILD_BUG_ON(sizeof(struct bpf_ct_opts) != NF_BPF_CT_OPTS_SZ); + + if (!opts) + return NULL; + if (!bpf_tuple || opts->reserved[0] || opts->reserved[1] || + opts->reserved[2] || opts__sz != NF_BPF_CT_OPTS_SZ) { + opts->error = -EINVAL; + return NULL; + } + caller_net = skb->dev ? dev_net(skb->dev) : sock_net(skb->sk); + nfct = __bpf_nf_ct_lookup(caller_net, bpf_tuple, tuple__sz, opts->l4proto, + opts->netns_id); + if (IS_ERR(nfct)) { + opts->error = PTR_ERR(nfct); + return NULL; + } + return nfct; +} + +/* bpf_ct_release - Release acquired nf_conn object + * + * This must be invoked for referenced PTR_TO_BTF_ID, and the verifier rejects + * the program if any references remain in the program in all of the explored + * states. + * + * Parameters: + * @nf_conn - Pointer to referenced nf_conn object, obtained using + * bpf_xdp_ct_lookup or bpf_skb_ct_lookup. + */ +void bpf_ct_release(struct nf_conn *nfct) +{ + if (!nfct) + return; + nf_ct_put(nfct); +} + +__diag_pop() + +BTF_SET_START(nf_ct_xdp_check_kfunc_ids) +BTF_ID(func, bpf_xdp_ct_lookup) +BTF_ID(func, bpf_ct_release) +BTF_SET_END(nf_ct_xdp_check_kfunc_ids) + +BTF_SET_START(nf_ct_tc_check_kfunc_ids) +BTF_ID(func, bpf_skb_ct_lookup) +BTF_ID(func, bpf_ct_release) +BTF_SET_END(nf_ct_tc_check_kfunc_ids) + +BTF_SET_START(nf_ct_acquire_kfunc_ids) +BTF_ID(func, bpf_xdp_ct_lookup) +BTF_ID(func, bpf_skb_ct_lookup) +BTF_SET_END(nf_ct_acquire_kfunc_ids) + +BTF_SET_START(nf_ct_release_kfunc_ids) +BTF_ID(func, bpf_ct_release) +BTF_SET_END(nf_ct_release_kfunc_ids) + +/* Both sets are identical */ +#define nf_ct_ret_null_kfunc_ids nf_ct_acquire_kfunc_ids + +static const struct btf_kfunc_id_set nf_conntrack_xdp_kfunc_set = { + .owner = THIS_MODULE, + .check_set = &nf_ct_xdp_check_kfunc_ids, + .acquire_set = &nf_ct_acquire_kfunc_ids, + .release_set = &nf_ct_release_kfunc_ids, + .ret_null_set = &nf_ct_ret_null_kfunc_ids, +}; + +static const struct btf_kfunc_id_set nf_conntrack_tc_kfunc_set = { + .owner = THIS_MODULE, + .check_set = &nf_ct_tc_check_kfunc_ids, + .acquire_set = &nf_ct_acquire_kfunc_ids, + .release_set = &nf_ct_release_kfunc_ids, + .ret_null_set = &nf_ct_ret_null_kfunc_ids, +}; + +int register_nf_conntrack_bpf(void) +{ + int ret; + + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &nf_conntrack_xdp_kfunc_set); + return ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &nf_conntrack_tc_kfunc_set); +} diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index bf1e17c678f1..0164e5f522e8 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -34,10 +34,10 @@ #include #include +#include #include #include #include -#include #include #include #include @@ -47,7 +47,6 @@ #include #include #include -#include #include #include #include @@ -67,6 +66,8 @@ EXPORT_SYMBOL_GPL(nf_conntrack_hash); struct conntrack_gc_work { struct delayed_work dwork; u32 next_bucket; + u32 avg_timeout; + u32 start_time; bool exiting; bool early_drop; }; @@ -78,8 +79,19 @@ static __read_mostly bool nf_conntrack_locks_all; /* serialize hash resizes and nf_ct_iterate_cleanup */ static DEFINE_MUTEX(nf_conntrack_mutex); -#define GC_SCAN_INTERVAL (120u * HZ) +#define GC_SCAN_INTERVAL_MAX (60ul * HZ) +#define GC_SCAN_INTERVAL_MIN (1ul * HZ) + +/* clamp timeouts to this value (TCP unacked) */ +#define GC_SCAN_INTERVAL_CLAMP (300ul * HZ) + +/* large initial bias so that we don't scan often just because we have + * three entries with a 1s timeout. + */ +#define GC_SCAN_INTERVAL_INIT INT_MAX + #define GC_SCAN_MAX_DURATION msecs_to_jiffies(10) +#define GC_SCAN_EXPIRED_MAX (64000u / HZ) #define MIN_CHAINLEN 8u #define MAX_CHAINLEN (32u - MIN_CHAINLEN) @@ -594,7 +606,7 @@ EXPORT_SYMBOL_GPL(nf_ct_tmpl_alloc); void nf_ct_tmpl_free(struct nf_conn *tmpl) { - nf_ct_ext_destroy(tmpl); + kfree(tmpl->ext); if (ARCH_KMALLOC_MINALIGN <= NFCT_INFOMASK) kfree((char *)tmpl - tmpl->proto.tmpl_padto); @@ -1421,16 +1433,28 @@ static bool gc_worker_can_early_drop(const struct nf_conn *ct) static void gc_worker(struct work_struct *work) { - unsigned long end_time = jiffies + GC_SCAN_MAX_DURATION; unsigned int i, hashsz, nf_conntrack_max95 = 0; - unsigned long next_run = GC_SCAN_INTERVAL; + u32 end_time, start_time = nfct_time_stamp; struct conntrack_gc_work *gc_work; + unsigned int expired_count = 0; + unsigned long next_run; + s32 delta_time; + gc_work = container_of(work, struct conntrack_gc_work, dwork.work); i = gc_work->next_bucket; if (gc_work->early_drop) nf_conntrack_max95 = nf_conntrack_max / 100u * 95u; + if (i == 0) { + gc_work->avg_timeout = GC_SCAN_INTERVAL_INIT; + gc_work->start_time = start_time; + } + + next_run = gc_work->avg_timeout; + + end_time = start_time + GC_SCAN_MAX_DURATION; + do { struct nf_conntrack_tuple_hash *h; struct hlist_nulls_head *ct_hash; @@ -1447,6 +1471,7 @@ static void gc_worker(struct work_struct *work) hlist_nulls_for_each_entry_rcu(h, n, &ct_hash[i], hnnode) { struct nf_conntrack_net *cnet; + unsigned long expires; struct net *net; tmp = nf_ct_tuplehash_to_ctrack(h); @@ -1456,11 +1481,29 @@ static void gc_worker(struct work_struct *work) continue; } + if (expired_count > GC_SCAN_EXPIRED_MAX) { + rcu_read_unlock(); + + gc_work->next_bucket = i; + gc_work->avg_timeout = next_run; + + delta_time = nfct_time_stamp - gc_work->start_time; + + /* re-sched immediately if total cycle time is exceeded */ + next_run = delta_time < (s32)GC_SCAN_INTERVAL_MAX; + goto early_exit; + } + if (nf_ct_is_expired(tmp)) { nf_ct_gc_expired(tmp); + expired_count++; continue; } + expires = clamp(nf_ct_expires(tmp), GC_SCAN_INTERVAL_MIN, GC_SCAN_INTERVAL_CLAMP); + next_run += expires; + next_run /= 2u; + if (nf_conntrack_max95 == 0 || gc_worker_skip_ct(tmp)) continue; @@ -1478,8 +1521,10 @@ static void gc_worker(struct work_struct *work) continue; } - if (gc_worker_can_early_drop(tmp)) + if (gc_worker_can_early_drop(tmp)) { nf_ct_kill(tmp); + expired_count++; + } nf_ct_put(tmp); } @@ -1492,33 +1537,38 @@ static void gc_worker(struct work_struct *work) cond_resched(); i++; - if (time_after(jiffies, end_time) && i < hashsz) { + delta_time = nfct_time_stamp - end_time; + if (delta_time > 0 && i < hashsz) { + gc_work->avg_timeout = next_run; gc_work->next_bucket = i; next_run = 0; - break; + goto early_exit; } } while (i < hashsz); + gc_work->next_bucket = 0; + + next_run = clamp(next_run, GC_SCAN_INTERVAL_MIN, GC_SCAN_INTERVAL_MAX); + + delta_time = max_t(s32, nfct_time_stamp - gc_work->start_time, 1); + if (next_run > (unsigned long)delta_time) + next_run -= delta_time; + else + next_run = 1; + +early_exit: if (gc_work->exiting) return; - /* - * Eviction will normally happen from the packet path, and not - * from this gc worker. - * - * This worker is only here to reap expired entries when system went - * idle after a busy period. - */ - if (next_run) { + if (next_run) gc_work->early_drop = false; - gc_work->next_bucket = 0; - } + queue_delayed_work(system_power_efficient_wq, &gc_work->dwork, next_run); } static void conntrack_gc_work_init(struct conntrack_gc_work *gc_work) { - INIT_DEFERRABLE_WORK(&gc_work->dwork, gc_worker); + INIT_DELAYED_WORK(&gc_work->dwork, gc_worker); gc_work->exiting = false; } @@ -1597,7 +1647,17 @@ void nf_conntrack_free(struct nf_conn *ct) */ WARN_ON(refcount_read(&ct->ct_general.use) != 0); - nf_ct_ext_destroy(ct); + if (ct->status & IPS_SRC_NAT_DONE) { + const struct nf_nat_hook *nat_hook; + + rcu_read_lock(); + nat_hook = rcu_dereference(nf_nat_hook); + if (nat_hook) + nat_hook->remove_nat_bysrc(ct); + rcu_read_unlock(); + } + + kfree(ct->ext); kmem_cache_free(nf_conntrack_cachep, ct); cnet = nf_ct_pernet(net); @@ -2464,13 +2524,7 @@ void nf_conntrack_cleanup_end(void) kvfree(nf_conntrack_hash); nf_conntrack_proto_fini(); - nf_conntrack_seqadj_fini(); - nf_conntrack_labels_fini(); nf_conntrack_helper_fini(); - nf_conntrack_timeout_fini(); - nf_conntrack_ecache_fini(); - nf_conntrack_tstamp_fini(); - nf_conntrack_acct_fini(); nf_conntrack_expect_fini(); kmem_cache_destroy(nf_conntrack_cachep); @@ -2625,39 +2679,6 @@ int nf_conntrack_set_hashsize(const char *val, const struct kernel_param *kp) return nf_conntrack_hash_resize(hashsize); } -static __always_inline unsigned int total_extension_size(void) -{ - /* remember to add new extensions below */ - BUILD_BUG_ON(NF_CT_EXT_NUM > 10); - - return sizeof(struct nf_ct_ext) + - sizeof(struct nf_conn_help) -#if IS_ENABLED(CONFIG_NF_NAT) - + sizeof(struct nf_conn_nat) -#endif - + sizeof(struct nf_conn_seqadj) - + sizeof(struct nf_conn_acct) -#ifdef CONFIG_NF_CONNTRACK_EVENTS - + sizeof(struct nf_conntrack_ecache) -#endif -#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP - + sizeof(struct nf_conn_tstamp) -#endif -#ifdef CONFIG_NF_CONNTRACK_TIMEOUT - + sizeof(struct nf_conn_timeout) -#endif -#ifdef CONFIG_NF_CONNTRACK_LABELS - + sizeof(struct nf_conn_labels) -#endif -#if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY) - + sizeof(struct nf_conn_synproxy) -#endif -#if IS_ENABLED(CONFIG_NET_ACT_CT) - + sizeof(struct nf_conn_act_ct_ext) -#endif - ; -}; - int nf_conntrack_init_start(void) { unsigned long nr_pages = totalram_pages(); @@ -2665,9 +2686,6 @@ int nf_conntrack_init_start(void) int ret = -ENOMEM; int i; - /* struct nf_ct_ext uses u8 to store offsets/size */ - BUILD_BUG_ON(total_extension_size() > 255u); - seqcount_spinlock_init(&nf_conntrack_generation, &nf_conntrack_locks_all_lock); @@ -2712,34 +2730,10 @@ int nf_conntrack_init_start(void) if (ret < 0) goto err_expect; - ret = nf_conntrack_acct_init(); - if (ret < 0) - goto err_acct; - - ret = nf_conntrack_tstamp_init(); - if (ret < 0) - goto err_tstamp; - - ret = nf_conntrack_ecache_init(); - if (ret < 0) - goto err_ecache; - - ret = nf_conntrack_timeout_init(); - if (ret < 0) - goto err_timeout; - ret = nf_conntrack_helper_init(); if (ret < 0) goto err_helper; - ret = nf_conntrack_labels_init(); - if (ret < 0) - goto err_labels; - - ret = nf_conntrack_seqadj_init(); - if (ret < 0) - goto err_seqadj; - ret = nf_conntrack_proto_init(); if (ret < 0) goto err_proto; @@ -2747,23 +2741,18 @@ int nf_conntrack_init_start(void) conntrack_gc_work_init(&conntrack_gc_work); queue_delayed_work(system_power_efficient_wq, &conntrack_gc_work.dwork, HZ); + ret = register_nf_conntrack_bpf(); + if (ret < 0) + goto err_kfunc; + return 0; +err_kfunc: + cancel_delayed_work_sync(&conntrack_gc_work.dwork); + nf_conntrack_proto_fini(); err_proto: - nf_conntrack_seqadj_fini(); -err_seqadj: - nf_conntrack_labels_fini(); -err_labels: nf_conntrack_helper_fini(); err_helper: - nf_conntrack_timeout_fini(); -err_timeout: - nf_conntrack_ecache_fini(); -err_ecache: - nf_conntrack_tstamp_fini(); -err_tstamp: - nf_conntrack_acct_fini(); -err_acct: nf_conntrack_expect_fini(); err_expect: kmem_cache_destroy(nf_conntrack_cachep); diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 41768ff19464..07e65b4e92f8 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -131,13 +131,13 @@ static void ecache_work(struct work_struct *work) } static int __nf_conntrack_eventmask_report(struct nf_conntrack_ecache *e, - const unsigned int events, - const unsigned long missed, + const u32 events, + const u32 missed, const struct nf_ct_event *item) { - struct nf_conn *ct = item->ct; struct net *net = nf_ct_net(item->ct); struct nf_ct_event_notifier *notify; + u32 old, want; int ret; if (!((events | missed) & e->ctmask)) @@ -157,12 +157,13 @@ static int __nf_conntrack_eventmask_report(struct nf_conntrack_ecache *e, if (likely(ret >= 0 && missed == 0)) return 0; - spin_lock_bh(&ct->lock); - if (ret < 0) - e->missed |= events; - else - e->missed &= ~missed; - spin_unlock_bh(&ct->lock); + do { + old = READ_ONCE(e->missed); + if (ret < 0) + want = old | events; + else + want = old & ~missed; + } while (cmpxchg(&e->missed, old, want) != old); return ret; } @@ -172,7 +173,7 @@ int nf_conntrack_eventmask_report(unsigned int events, struct nf_conn *ct, { struct nf_conntrack_ecache *e; struct nf_ct_event item; - unsigned long missed; + unsigned int missed; int ret; if (!nf_ct_is_confirmed(ct)) @@ -211,7 +212,7 @@ void nf_ct_deliver_cached_events(struct nf_conn *ct) { struct nf_conntrack_ecache *e; struct nf_ct_event item; - unsigned long events; + unsigned int events; if (!nf_ct_is_confirmed(ct) || nf_ct_is_dying(ct)) return; @@ -304,12 +305,6 @@ void nf_conntrack_ecache_work(struct net *net, enum nf_ct_ecache_state state) #define NF_CT_EVENTS_DEFAULT 1 static int nf_ct_events __read_mostly = NF_CT_EVENTS_DEFAULT; -static const struct nf_ct_ext_type event_extend = { - .len = sizeof(struct nf_conntrack_ecache), - .align = __alignof__(struct nf_conntrack_ecache), - .id = NF_CT_EXT_ECACHE, -}; - void nf_conntrack_ecache_pernet_init(struct net *net) { struct nf_conntrack_net *cnet = nf_ct_pernet(net); @@ -317,6 +312,8 @@ void nf_conntrack_ecache_pernet_init(struct net *net) net->ct.sysctl_events = nf_ct_events; cnet->ct_net = &net->ct; INIT_DELAYED_WORK(&cnet->ecache_dwork, ecache_work); + + BUILD_BUG_ON(__IPCT_MAX >= 16); /* e->ctmask is u16 */ } void nf_conntrack_ecache_pernet_fini(struct net *net) @@ -325,19 +322,3 @@ void nf_conntrack_ecache_pernet_fini(struct net *net) cancel_delayed_work_sync(&cnet->ecache_dwork); } - -int nf_conntrack_ecache_init(void) -{ - int ret = nf_ct_extend_register(&event_extend); - if (ret < 0) - pr_err("Unable to register event extension\n"); - - BUILD_BUG_ON(__IPCT_MAX >= 16); /* ctmask, missed use u16 */ - - return ret; -} - -void nf_conntrack_ecache_fini(void) -{ - nf_ct_extend_unregister(&event_extend); -} diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 3dbe2329c3f1..1296fda54ac6 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -13,40 +13,90 @@ #include #include -static struct nf_ct_ext_type __rcu *nf_ct_ext_types[NF_CT_EXT_NUM]; -static DEFINE_MUTEX(nf_ct_ext_type_mutex); +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #define NF_CT_EXT_PREALLOC 128u /* conntrack events are on by default */ -void nf_ct_ext_destroy(struct nf_conn *ct) +static const u8 nf_ct_ext_type_len[NF_CT_EXT_NUM] = { + [NF_CT_EXT_HELPER] = sizeof(struct nf_conn_help), +#if IS_ENABLED(CONFIG_NF_NAT) + [NF_CT_EXT_NAT] = sizeof(struct nf_conn_nat), +#endif + [NF_CT_EXT_SEQADJ] = sizeof(struct nf_conn_seqadj), + [NF_CT_EXT_ACCT] = sizeof(struct nf_conn_acct), +#ifdef CONFIG_NF_CONNTRACK_EVENTS + [NF_CT_EXT_ECACHE] = sizeof(struct nf_conntrack_ecache), +#endif +#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP + [NF_CT_EXT_TSTAMP] = sizeof(struct nf_conn_acct), +#endif +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + [NF_CT_EXT_TIMEOUT] = sizeof(struct nf_conn_tstamp), +#endif +#ifdef CONFIG_NF_CONNTRACK_LABELS + [NF_CT_EXT_LABELS] = sizeof(struct nf_conn_labels), +#endif +#if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY) + [NF_CT_EXT_SYNPROXY] = sizeof(struct nf_conn_synproxy), +#endif +#if IS_ENABLED(CONFIG_NET_ACT_CT) + [NF_CT_EXT_ACT_CT] = sizeof(struct nf_conn_act_ct_ext), +#endif +}; + +static __always_inline unsigned int total_extension_size(void) { - unsigned int i; - struct nf_ct_ext_type *t; + /* remember to add new extensions below */ + BUILD_BUG_ON(NF_CT_EXT_NUM > 10); - for (i = 0; i < NF_CT_EXT_NUM; i++) { - rcu_read_lock(); - t = rcu_dereference(nf_ct_ext_types[i]); - - /* Here the nf_ct_ext_type might have been unregisterd. - * I.e., it has responsible to cleanup private - * area in all conntracks when it is unregisterd. - */ - if (t && t->destroy) - t->destroy(ct); - rcu_read_unlock(); - } - - kfree(ct->ext); + return sizeof(struct nf_ct_ext) + + sizeof(struct nf_conn_help) +#if IS_ENABLED(CONFIG_NF_NAT) + + sizeof(struct nf_conn_nat) +#endif + + sizeof(struct nf_conn_seqadj) + + sizeof(struct nf_conn_acct) +#ifdef CONFIG_NF_CONNTRACK_EVENTS + + sizeof(struct nf_conntrack_ecache) +#endif +#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP + + sizeof(struct nf_conn_tstamp) +#endif +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + + sizeof(struct nf_conn_timeout) +#endif +#ifdef CONFIG_NF_CONNTRACK_LABELS + + sizeof(struct nf_conn_labels) +#endif +#if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY) + + sizeof(struct nf_conn_synproxy) +#endif +#if IS_ENABLED(CONFIG_NET_ACT_CT) + + sizeof(struct nf_conn_act_ct_ext) +#endif + ; } void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) { unsigned int newlen, newoff, oldlen, alloc; - struct nf_ct_ext_type *t; struct nf_ct_ext *new; /* Conntrack must not be confirmed to avoid races on reallocation. */ WARN_ON(nf_ct_is_confirmed(ct)); + /* struct nf_ct_ext uses u8 to store offsets/size */ + BUILD_BUG_ON(total_extension_size() > 255u); if (ct->ext) { const struct nf_ct_ext *old = ct->ext; @@ -58,16 +108,8 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) oldlen = sizeof(*new); } - rcu_read_lock(); - t = rcu_dereference(nf_ct_ext_types[id]); - if (!t) { - rcu_read_unlock(); - return NULL; - } - - newoff = ALIGN(oldlen, t->align); - newlen = newoff + t->len; - rcu_read_unlock(); + newoff = ALIGN(oldlen, __alignof__(struct nf_ct_ext)); + newlen = newoff + nf_ct_ext_type_len[id]; alloc = max(newlen, NF_CT_EXT_PREALLOC); new = krealloc(ct->ext, alloc, gfp); @@ -85,31 +127,3 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) return (void *)new + newoff; } EXPORT_SYMBOL(nf_ct_ext_add); - -/* This MUST be called in process context. */ -int nf_ct_extend_register(const struct nf_ct_ext_type *type) -{ - int ret = 0; - - mutex_lock(&nf_ct_ext_type_mutex); - if (nf_ct_ext_types[type->id]) { - ret = -EBUSY; - goto out; - } - - rcu_assign_pointer(nf_ct_ext_types[type->id], type); -out: - mutex_unlock(&nf_ct_ext_type_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(nf_ct_extend_register); - -/* This MUST be called in process context. */ -void nf_ct_extend_unregister(const struct nf_ct_ext_type *type) -{ - mutex_lock(&nf_ct_ext_type_mutex); - RCU_INIT_POINTER(nf_ct_ext_types[type->id], NULL); - mutex_unlock(&nf_ct_ext_type_mutex); - synchronize_rcu(); -} -EXPORT_SYMBOL_GPL(nf_ct_extend_unregister); diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index ae4488a13c70..8dec42ec603e 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -550,11 +550,11 @@ void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat) } EXPORT_SYMBOL_GPL(nf_nat_helper_unregister); -static const struct nf_ct_ext_type helper_extend = { - .len = sizeof(struct nf_conn_help), - .align = __alignof__(struct nf_conn_help), - .id = NF_CT_EXT_HELPER, -}; +void nf_ct_set_auto_assign_helper_warned(struct net *net) +{ + nf_ct_pernet(net)->auto_assign_helper_warned = true; +} +EXPORT_SYMBOL_GPL(nf_ct_set_auto_assign_helper_warned); void nf_conntrack_helper_pernet_init(struct net *net) { @@ -565,28 +565,17 @@ void nf_conntrack_helper_pernet_init(struct net *net) int nf_conntrack_helper_init(void) { - int ret; nf_ct_helper_hsize = 1; /* gets rounded up to use one page */ nf_ct_helper_hash = nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0); if (!nf_ct_helper_hash) return -ENOMEM; - ret = nf_ct_extend_register(&helper_extend); - if (ret < 0) { - pr_err("nf_ct_helper: Unable to register helper extension.\n"); - goto out_extend; - } - INIT_LIST_HEAD(&nf_ct_nat_helpers); return 0; -out_extend: - kvfree(nf_ct_helper_hash); - return ret; } void nf_conntrack_helper_fini(void) { - nf_ct_extend_unregister(&helper_extend); kvfree(nf_ct_helper_hash); } diff --git a/net/netfilter/nf_conntrack_labels.c b/net/netfilter/nf_conntrack_labels.c index 522792556632..6e70e137a0a6 100644 --- a/net/netfilter/nf_conntrack_labels.c +++ b/net/netfilter/nf_conntrack_labels.c @@ -67,6 +67,8 @@ int nf_connlabels_get(struct net *net, unsigned int bits) net->ct.labels_used++; spin_unlock(&nf_connlabels_lock); + BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE / sizeof(long) >= U8_MAX); + return 0; } EXPORT_SYMBOL_GPL(nf_connlabels_get); @@ -78,21 +80,3 @@ void nf_connlabels_put(struct net *net) spin_unlock(&nf_connlabels_lock); } EXPORT_SYMBOL_GPL(nf_connlabels_put); - -static const struct nf_ct_ext_type labels_extend = { - .len = sizeof(struct nf_conn_labels), - .align = __alignof__(struct nf_conn_labels), - .id = NF_CT_EXT_LABELS, -}; - -int nf_conntrack_labels_init(void) -{ - BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE / sizeof(long) >= U8_MAX); - - return nf_ct_extend_register(&labels_extend); -} - -void nf_conntrack_labels_fini(void) -{ - nf_ct_extend_unregister(&labels_extend); -} diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 7032402ffd33..1ea2ad732d57 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -58,6 +58,12 @@ MODULE_LICENSE("GPL"); +struct ctnetlink_list_dump_ctx { + struct nf_conn *last; + unsigned int cpu; + bool done; +}; + static int ctnetlink_dump_tuples_proto(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_l4proto *l4proto) @@ -1694,14 +1700,18 @@ static int ctnetlink_get_conntrack(struct sk_buff *skb, static int ctnetlink_done_list(struct netlink_callback *cb) { - if (cb->args[1]) - nf_ct_put((struct nf_conn *)cb->args[1]); + struct ctnetlink_list_dump_ctx *ctx = (void *)cb->ctx; + + if (ctx->last) + nf_ct_put(ctx->last); + return 0; } static int ctnetlink_dump_list(struct sk_buff *skb, struct netlink_callback *cb, bool dying) { + struct ctnetlink_list_dump_ctx *ctx = (void *)cb->ctx; struct nf_conn *ct, *last; struct nf_conntrack_tuple_hash *h; struct hlist_nulls_node *n; @@ -1712,12 +1722,12 @@ ctnetlink_dump_list(struct sk_buff *skb, struct netlink_callback *cb, bool dying struct hlist_nulls_head *list; struct net *net = sock_net(skb->sk); - if (cb->args[2]) + if (ctx->done) return 0; - last = (struct nf_conn *)cb->args[1]; + last = ctx->last; - for (cpu = cb->args[0]; cpu < nr_cpu_ids; cpu++) { + for (cpu = ctx->cpu; cpu < nr_cpu_ids; cpu++) { struct ct_pcpu *pcpu; if (!cpu_possible(cpu)) @@ -1731,10 +1741,10 @@ ctnetlink_dump_list(struct sk_buff *skb, struct netlink_callback *cb, bool dying ct = nf_ct_tuplehash_to_ctrack(h); if (l3proto && nf_ct_l3num(ct) != l3proto) continue; - if (cb->args[1]) { + if (ctx->last) { if (ct != last) continue; - cb->args[1] = 0; + ctx->last = NULL; } /* We can't dump extension info for the unconfirmed @@ -1751,19 +1761,19 @@ ctnetlink_dump_list(struct sk_buff *skb, struct netlink_callback *cb, bool dying if (res < 0) { if (!refcount_inc_not_zero(&ct->ct_general.use)) continue; - cb->args[0] = cpu; - cb->args[1] = (unsigned long)ct; + ctx->cpu = cpu; + ctx->last = ct; spin_unlock_bh(&pcpu->lock); goto out; } } - if (cb->args[1]) { - cb->args[1] = 0; + if (ctx->last) { + ctx->last = NULL; goto restart; } spin_unlock_bh(&pcpu->lock); } - cb->args[2] = 1; + ctx->done = true; out: if (last) nf_ct_put(last); @@ -3878,6 +3888,8 @@ static int __init ctnetlink_init(void) { int ret; + BUILD_BUG_ON(sizeof(struct ctnetlink_list_dump_ctx) > sizeof_field(struct netlink_callback, ctx)); + ret = nfnetlink_subsys_register(&ctnl_subsys); if (ret < 0) { pr_err("ctnetlink_init: cannot register with nfnetlink.\n"); diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 7d5708b92138..f3fa367b455f 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -45,30 +45,8 @@ MODULE_ALIAS_NFCT_HELPER("pptp"); static DEFINE_SPINLOCK(nf_pptp_lock); -int -(*nf_nat_pptp_hook_outbound)(struct sk_buff *skb, - struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned int protoff, struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq) __read_mostly; -EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_outbound); - -int -(*nf_nat_pptp_hook_inbound)(struct sk_buff *skb, - struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned int protoff, struct PptpControlHeader *ctlh, - union pptp_ctrl_union *pptpReq) __read_mostly; -EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_inbound); - -void -(*nf_nat_pptp_hook_exp_gre)(struct nf_conntrack_expect *expect_orig, - struct nf_conntrack_expect *expect_reply) - __read_mostly; -EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_exp_gre); - -void -(*nf_nat_pptp_hook_expectfn)(struct nf_conn *ct, - struct nf_conntrack_expect *exp) __read_mostly; -EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn); +const struct nf_nat_pptp_hook *nf_nat_pptp_hook; +EXPORT_SYMBOL_GPL(nf_nat_pptp_hook); #if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) /* PptpControlMessageType names */ @@ -111,8 +89,8 @@ EXPORT_SYMBOL(pptp_msg_name); static void pptp_expectfn(struct nf_conn *ct, struct nf_conntrack_expect *exp) { + const struct nf_nat_pptp_hook *hook; struct net *net = nf_ct_net(ct); - typeof(nf_nat_pptp_hook_expectfn) nf_nat_pptp_expectfn; pr_debug("increasing timeouts\n"); /* increase timeout of GRE data channel conntrack entry */ @@ -122,9 +100,9 @@ static void pptp_expectfn(struct nf_conn *ct, /* Can you see how rusty this code is, compared with the pre-2.6.11 * one? That's what happened to my shiny newnat of 2002 ;( -HW */ - nf_nat_pptp_expectfn = rcu_dereference(nf_nat_pptp_hook_expectfn); - if (nf_nat_pptp_expectfn && ct->master->status & IPS_NAT_MASK) - nf_nat_pptp_expectfn(ct, exp); + hook = rcu_dereference(nf_nat_pptp_hook); + if (hook && ct->master->status & IPS_NAT_MASK) + hook->expectfn(ct, exp); else { struct nf_conntrack_tuple inv_t; struct nf_conntrack_expect *exp_other; @@ -209,9 +187,9 @@ static void pptp_destroy_siblings(struct nf_conn *ct) static int exp_gre(struct nf_conn *ct, __be16 callid, __be16 peer_callid) { struct nf_conntrack_expect *exp_orig, *exp_reply; + const struct nf_nat_pptp_hook *hook; enum ip_conntrack_dir dir; int ret = 1; - typeof(nf_nat_pptp_hook_exp_gre) nf_nat_pptp_exp_gre; exp_orig = nf_ct_expect_alloc(ct); if (exp_orig == NULL) @@ -239,9 +217,9 @@ static int exp_gre(struct nf_conn *ct, __be16 callid, __be16 peer_callid) IPPROTO_GRE, &callid, &peer_callid); exp_reply->expectfn = pptp_expectfn; - nf_nat_pptp_exp_gre = rcu_dereference(nf_nat_pptp_hook_exp_gre); - if (nf_nat_pptp_exp_gre && ct->status & IPS_NAT_MASK) - nf_nat_pptp_exp_gre(exp_orig, exp_reply); + hook = rcu_dereference(nf_nat_pptp_hook); + if (hook && ct->status & IPS_NAT_MASK) + hook->exp_gre(exp_orig, exp_reply); if (nf_ct_expect_related(exp_orig, 0) != 0) goto out_put_both; if (nf_ct_expect_related(exp_reply, 0) != 0) @@ -279,9 +257,9 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, enum ip_conntrack_info ctinfo) { struct nf_ct_pptp_master *info = nfct_help_data(ct); + const struct nf_nat_pptp_hook *hook; u_int16_t msg; __be16 cid = 0, pcid = 0; - typeof(nf_nat_pptp_hook_inbound) nf_nat_pptp_inbound; msg = ntohs(ctlh->messageType); pr_debug("inbound control message %s\n", pptp_msg_name(msg)); @@ -383,10 +361,9 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, goto invalid; } - nf_nat_pptp_inbound = rcu_dereference(nf_nat_pptp_hook_inbound); - if (nf_nat_pptp_inbound && ct->status & IPS_NAT_MASK) - return nf_nat_pptp_inbound(skb, ct, ctinfo, - protoff, ctlh, pptpReq); + hook = rcu_dereference(nf_nat_pptp_hook); + if (hook && ct->status & IPS_NAT_MASK) + return hook->inbound(skb, ct, ctinfo, protoff, ctlh, pptpReq); return NF_ACCEPT; invalid: @@ -407,9 +384,9 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff, enum ip_conntrack_info ctinfo) { struct nf_ct_pptp_master *info = nfct_help_data(ct); + const struct nf_nat_pptp_hook *hook; u_int16_t msg; __be16 cid = 0, pcid = 0; - typeof(nf_nat_pptp_hook_outbound) nf_nat_pptp_outbound; msg = ntohs(ctlh->messageType); pr_debug("outbound control message %s\n", pptp_msg_name(msg)); @@ -479,10 +456,9 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff, goto invalid; } - nf_nat_pptp_outbound = rcu_dereference(nf_nat_pptp_hook_outbound); - if (nf_nat_pptp_outbound && ct->status & IPS_NAT_MASK) - return nf_nat_pptp_outbound(skb, ct, ctinfo, - protoff, ctlh, pptpReq); + hook = rcu_dereference(nf_nat_pptp_hook); + if (hook && ct->status & IPS_NAT_MASK) + return hook->outbound(skb, ct, ctinfo, protoff, ctlh, pptpReq); return NF_ACCEPT; invalid: diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c index 3066449f8bd8..7ab2b25b57bc 100644 --- a/net/netfilter/nf_conntrack_seqadj.c +++ b/net/netfilter/nf_conntrack_seqadj.c @@ -232,19 +232,3 @@ s32 nf_ct_seq_offset(const struct nf_conn *ct, this_way->offset_after : this_way->offset_before; } EXPORT_SYMBOL_GPL(nf_ct_seq_offset); - -static const struct nf_ct_ext_type nf_ct_seqadj_extend = { - .len = sizeof(struct nf_conn_seqadj), - .align = __alignof__(struct nf_conn_seqadj), - .id = NF_CT_EXT_SEQADJ, -}; - -int nf_conntrack_seqadj_init(void) -{ - return nf_ct_extend_register(&nf_ct_seqadj_extend); -} - -void nf_conntrack_seqadj_fini(void) -{ - nf_ct_extend_unregister(&nf_ct_seqadj_extend); -} diff --git a/net/netfilter/nf_conntrack_timeout.c b/net/netfilter/nf_conntrack_timeout.c index 14387e0b8008..cec166ecba77 100644 --- a/net/netfilter/nf_conntrack_timeout.c +++ b/net/netfilter/nf_conntrack_timeout.c @@ -22,12 +22,8 @@ #include #include -struct nf_ct_timeout * -(*nf_ct_timeout_find_get_hook)(struct net *net, const char *name) __read_mostly; -EXPORT_SYMBOL_GPL(nf_ct_timeout_find_get_hook); - -void (*nf_ct_timeout_put_hook)(struct nf_ct_timeout *timeout) __read_mostly; -EXPORT_SYMBOL_GPL(nf_ct_timeout_put_hook); +const struct nf_ct_timeout_hooks *nf_ct_timeout_hook __read_mostly; +EXPORT_SYMBOL_GPL(nf_ct_timeout_hook); static int untimeout(struct nf_conn *ct, void *timeout) { @@ -48,31 +44,30 @@ EXPORT_SYMBOL_GPL(nf_ct_untimeout); static void __nf_ct_timeout_put(struct nf_ct_timeout *timeout) { - typeof(nf_ct_timeout_put_hook) timeout_put; + const struct nf_ct_timeout_hooks *h = rcu_dereference(nf_ct_timeout_hook); - timeout_put = rcu_dereference(nf_ct_timeout_put_hook); - if (timeout_put) - timeout_put(timeout); + if (h) + h->timeout_put(timeout); } int nf_ct_set_timeout(struct net *net, struct nf_conn *ct, u8 l3num, u8 l4num, const char *timeout_name) { - typeof(nf_ct_timeout_find_get_hook) timeout_find_get; + const struct nf_ct_timeout_hooks *h; struct nf_ct_timeout *timeout; struct nf_conn_timeout *timeout_ext; const char *errmsg = NULL; int ret = 0; rcu_read_lock(); - timeout_find_get = rcu_dereference(nf_ct_timeout_find_get_hook); - if (!timeout_find_get) { + h = rcu_dereference(nf_ct_timeout_hook); + if (!h) { ret = -ENOENT; errmsg = "Timeout policy base is empty"; goto out; } - timeout = timeout_find_get(net, timeout_name); + timeout = h->timeout_find_get(net, timeout_name); if (!timeout) { ret = -ENOENT; pr_info_ratelimited("No such timeout policy \"%s\"\n", @@ -119,37 +114,18 @@ EXPORT_SYMBOL_GPL(nf_ct_set_timeout); void nf_ct_destroy_timeout(struct nf_conn *ct) { struct nf_conn_timeout *timeout_ext; - typeof(nf_ct_timeout_put_hook) timeout_put; + const struct nf_ct_timeout_hooks *h; rcu_read_lock(); - timeout_put = rcu_dereference(nf_ct_timeout_put_hook); + h = rcu_dereference(nf_ct_timeout_hook); - if (timeout_put) { + if (h) { timeout_ext = nf_ct_timeout_find(ct); if (timeout_ext) { - timeout_put(timeout_ext->timeout); + h->timeout_put(timeout_ext->timeout); RCU_INIT_POINTER(timeout_ext->timeout, NULL); } } rcu_read_unlock(); } EXPORT_SYMBOL_GPL(nf_ct_destroy_timeout); - -static const struct nf_ct_ext_type timeout_extend = { - .len = sizeof(struct nf_conn_timeout), - .align = __alignof__(struct nf_conn_timeout), - .id = NF_CT_EXT_TIMEOUT, -}; - -int nf_conntrack_timeout_init(void) -{ - int ret = nf_ct_extend_register(&timeout_extend); - if (ret < 0) - pr_err("nf_ct_timeout: Unable to register timeout extension.\n"); - return ret; -} - -void nf_conntrack_timeout_fini(void) -{ - nf_ct_extend_unregister(&timeout_extend); -} diff --git a/net/netfilter/nf_conntrack_timestamp.c b/net/netfilter/nf_conntrack_timestamp.c index f656d393fa92..9e43a0a59e73 100644 --- a/net/netfilter/nf_conntrack_timestamp.c +++ b/net/netfilter/nf_conntrack_timestamp.c @@ -19,27 +19,7 @@ static bool nf_ct_tstamp __read_mostly; module_param_named(tstamp, nf_ct_tstamp, bool, 0644); MODULE_PARM_DESC(tstamp, "Enable connection tracking flow timestamping."); -static const struct nf_ct_ext_type tstamp_extend = { - .len = sizeof(struct nf_conn_tstamp), - .align = __alignof__(struct nf_conn_tstamp), - .id = NF_CT_EXT_TSTAMP, -}; - void nf_conntrack_tstamp_pernet_init(struct net *net) { net->ct.sysctl_tstamp = nf_ct_tstamp; } - -int nf_conntrack_tstamp_init(void) -{ - int ret; - ret = nf_ct_extend_register(&tstamp_extend); - if (ret < 0) - pr_err("Unable to register extension\n"); - return ret; -} - -void nf_conntrack_tstamp_fini(void) -{ - nf_ct_extend_unregister(&tstamp_extend); -} diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c index a579e59ee5c5..7873bd1389c3 100644 --- a/net/netfilter/nf_dup_netdev.c +++ b/net/netfilter/nf_dup_netdev.c @@ -19,7 +19,7 @@ static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev) skb_push(skb, skb->mac_len); skb->dev = dev; - skb->tstamp = 0; + skb_clear_tstamp(skb); dev_queue_xmit(skb); } diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index b90eca7a2f22..3db256da919b 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -39,8 +39,14 @@ flow_offload_fill_dir(struct flow_offload *flow, ft->l3proto = ctt->src.l3num; ft->l4proto = ctt->dst.protonum; - ft->src_port = ctt->src.u.tcp.port; - ft->dst_port = ctt->dst.u.tcp.port; + + switch (ctt->dst.protonum) { + case IPPROTO_TCP: + case IPPROTO_UDP: + ft->src_port = ctt->src.u.tcp.port; + ft->dst_port = ctt->dst.u.tcp.port; + break; + } } struct flow_offload *flow_offload_alloc(struct nf_conn *ct) @@ -399,7 +405,8 @@ EXPORT_SYMBOL_GPL(flow_offload_lookup); static int nf_flow_table_iterate(struct nf_flowtable *flow_table, - void (*iter)(struct flow_offload *flow, void *data), + void (*iter)(struct nf_flowtable *flowtable, + struct flow_offload *flow, void *data), void *data) { struct flow_offload_tuple_rhash *tuplehash; @@ -423,7 +430,7 @@ nf_flow_table_iterate(struct nf_flowtable *flow_table, flow = container_of(tuplehash, struct flow_offload, tuplehash[0]); - iter(flow, data); + iter(flow_table, flow, data); } rhashtable_walk_stop(&hti); rhashtable_walk_exit(&hti); @@ -451,10 +458,9 @@ static bool nf_flow_has_stale_dst(struct flow_offload *flow) flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple); } -static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data) +static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table, + struct flow_offload *flow, void *data) { - struct nf_flowtable *flow_table = data; - if (nf_flow_has_expired(flow) || nf_ct_is_dying(flow->ct) || nf_flow_has_stale_dst(flow)) @@ -479,7 +485,7 @@ static void nf_flow_offload_work_gc(struct work_struct *work) struct nf_flowtable *flow_table; flow_table = container_of(work, struct nf_flowtable, gc_work.work); - nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, flow_table); + nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, NULL); queue_delayed_work(system_power_efficient_wq, &flow_table->gc_work, HZ); } @@ -595,7 +601,8 @@ int nf_flow_table_init(struct nf_flowtable *flowtable) } EXPORT_SYMBOL_GPL(nf_flow_table_init); -static void nf_flow_table_do_cleanup(struct flow_offload *flow, void *data) +static void nf_flow_table_do_cleanup(struct nf_flowtable *flow_table, + struct flow_offload *flow, void *data) { struct net_device *dev = data; @@ -637,11 +644,10 @@ void nf_flow_table_free(struct nf_flowtable *flow_table) cancel_delayed_work_sync(&flow_table->gc_work); nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL); - nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, flow_table); + nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, NULL); nf_flow_table_offload_flush(flow_table); if (nf_flowtable_hw_offload(flow_table)) - nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, - flow_table); + nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, NULL); rhashtable_destroy(&flow_table->rhashtable); } EXPORT_SYMBOL_GPL(nf_flow_table_free); diff --git a/net/netfilter/nf_flow_table_inet.c b/net/netfilter/nf_flow_table_inet.c index 5c57ade6bd05..0ccabf3fa6aa 100644 --- a/net/netfilter/nf_flow_table_inet.c +++ b/net/netfilter/nf_flow_table_inet.c @@ -6,12 +6,29 @@ #include #include #include +#include static unsigned int nf_flow_offload_inet_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { + struct vlan_ethhdr *veth; + __be16 proto; + switch (skb->protocol) { + case htons(ETH_P_8021Q): + veth = (struct vlan_ethhdr *)skb_mac_header(skb); + proto = veth->h_vlan_encapsulated_proto; + break; + case htons(ETH_P_PPP_SES): + proto = nf_flow_pppoe_proto(skb); + break; + default: + proto = skb->protocol; + break; + } + + switch (proto) { case htons(ETH_P_IP): return nf_flow_offload_ip_hook(priv, skb, state); case htons(ETH_P_IPV6): diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 889cf88d3dba..32c0eb1b4821 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -8,8 +8,6 @@ #include #include #include -#include -#include #include #include #include @@ -172,6 +170,7 @@ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev, struct flow_ports *ports; unsigned int thoff; struct iphdr *iph; + u8 ipproto; if (!pskb_may_pull(skb, sizeof(*iph) + offset)) return -1; @@ -185,13 +184,19 @@ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev, thoff += offset; - switch (iph->protocol) { + ipproto = iph->protocol; + switch (ipproto) { case IPPROTO_TCP: *hdrsize = sizeof(struct tcphdr); break; case IPPROTO_UDP: *hdrsize = sizeof(struct udphdr); break; +#ifdef CONFIG_NF_CT_PROTO_GRE + case IPPROTO_GRE: + *hdrsize = sizeof(struct gre_base_hdr); + break; +#endif default: return -1; } @@ -202,15 +207,29 @@ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev, if (!pskb_may_pull(skb, thoff + *hdrsize)) return -1; + switch (ipproto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + ports = (struct flow_ports *)(skb_network_header(skb) + thoff); + tuple->src_port = ports->source; + tuple->dst_port = ports->dest; + break; + case IPPROTO_GRE: { + struct gre_base_hdr *greh; + + greh = (struct gre_base_hdr *)(skb_network_header(skb) + thoff); + if ((greh->flags & GRE_VERSION) != GRE_VERSION_0) + return -1; + break; + } + } + iph = (struct iphdr *)(skb_network_header(skb) + offset); - ports = (struct flow_ports *)(skb_network_header(skb) + thoff); tuple->src_v4.s_addr = iph->saddr; tuple->dst_v4.s_addr = iph->daddr; - tuple->src_port = ports->source; - tuple->dst_port = ports->dest; tuple->l3proto = AF_INET; - tuple->l4proto = iph->protocol; + tuple->l4proto = ipproto; tuple->iifidx = dev->ifindex; nf_flow_tuple_encap(skb, tuple); @@ -239,22 +258,6 @@ static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb, return NF_STOLEN; } -static inline __be16 nf_flow_pppoe_proto(const struct sk_buff *skb) -{ - __be16 proto; - - proto = *((__be16 *)(skb_mac_header(skb) + ETH_HLEN + - sizeof(struct pppoe_hdr))); - switch (proto) { - case htons(PPP_IP): - return htons(ETH_P_IP); - case htons(PPP_IPV6): - return htons(ETH_P_IPV6); - } - - return 0; -} - static bool nf_flow_skb_encap_protocol(const struct sk_buff *skb, __be16 proto, u32 *offset) { @@ -376,7 +379,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, nf_flow_nat_ip(flow, skb, thoff, dir, iph); ip_decrease_ttl(iph); - skb->tstamp = 0; + skb_clear_tstamp(skb); if (flow_table->flags & NF_FLOWTABLE_COUNTER) nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len); @@ -521,6 +524,7 @@ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev, struct flow_ports *ports; struct ipv6hdr *ip6h; unsigned int thoff; + u8 nexthdr; thoff = sizeof(*ip6h) + offset; if (!pskb_may_pull(skb, thoff)) @@ -528,13 +532,19 @@ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev, ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset); - switch (ip6h->nexthdr) { + nexthdr = ip6h->nexthdr; + switch (nexthdr) { case IPPROTO_TCP: *hdrsize = sizeof(struct tcphdr); break; case IPPROTO_UDP: *hdrsize = sizeof(struct udphdr); break; +#ifdef CONFIG_NF_CT_PROTO_GRE + case IPPROTO_GRE: + *hdrsize = sizeof(struct gre_base_hdr); + break; +#endif default: return -1; } @@ -545,15 +555,29 @@ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev, if (!pskb_may_pull(skb, thoff + *hdrsize)) return -1; + switch (nexthdr) { + case IPPROTO_TCP: + case IPPROTO_UDP: + ports = (struct flow_ports *)(skb_network_header(skb) + thoff); + tuple->src_port = ports->source; + tuple->dst_port = ports->dest; + break; + case IPPROTO_GRE: { + struct gre_base_hdr *greh; + + greh = (struct gre_base_hdr *)(skb_network_header(skb) + thoff); + if ((greh->flags & GRE_VERSION) != GRE_VERSION_0) + return -1; + break; + } + } + ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset); - ports = (struct flow_ports *)(skb_network_header(skb) + thoff); tuple->src_v6 = ip6h->saddr; tuple->dst_v6 = ip6h->daddr; - tuple->src_port = ports->source; - tuple->dst_port = ports->dest; tuple->l3proto = AF_INET6; - tuple->l4proto = ip6h->nexthdr; + tuple->l4proto = nexthdr; tuple->iifidx = dev->ifindex; nf_flow_tuple_encap(skb, tuple); @@ -611,7 +635,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, nf_flow_nat_ipv6(flow, skb, dir, ip6h); ip6h->hop_limit--; - skb->tstamp = 0; + skb_clear_tstamp(skb); if (flow_table->flags & NF_FLOWTABLE_COUNTER) nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len); diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index fc4265acd9c4..11b6e1942092 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -20,7 +20,6 @@ static struct workqueue_struct *nf_flow_offload_stats_wq; struct flow_offload_work { struct list_head list; enum flow_cls_command cmd; - int priority; struct nf_flowtable *flowtable; struct flow_offload *flow; struct work_struct work; @@ -174,6 +173,7 @@ static int nf_flow_rule_match(struct nf_flow_match *match, match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_TCP); break; case IPPROTO_UDP: + case IPPROTO_GRE: break; default: return -EOPNOTSUPP; @@ -182,15 +182,22 @@ static int nf_flow_rule_match(struct nf_flow_match *match, key->basic.ip_proto = tuple->l4proto; mask->basic.ip_proto = 0xff; - key->tp.src = tuple->src_port; - mask->tp.src = 0xffff; - key->tp.dst = tuple->dst_port; - mask->tp.dst = 0xffff; - match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_META) | BIT(FLOW_DISSECTOR_KEY_CONTROL) | - BIT(FLOW_DISSECTOR_KEY_BASIC) | - BIT(FLOW_DISSECTOR_KEY_PORTS); + BIT(FLOW_DISSECTOR_KEY_BASIC); + + switch (tuple->l4proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + key->tp.src = tuple->src_port; + mask->tp.src = 0xffff; + key->tp.dst = tuple->dst_port; + mask->tp.dst = 0xffff; + + match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_PORTS); + break; + } + return 0; } @@ -866,7 +873,8 @@ static int flow_offload_tuple_add(struct flow_offload_work *offload, enum flow_offload_tuple_dir dir) { return nf_flow_offload_tuple(offload->flowtable, offload->flow, - flow_rule, dir, offload->priority, + flow_rule, dir, + offload->flowtable->priority, FLOW_CLS_REPLACE, NULL, &offload->flowtable->flow_block.cb_list); } @@ -875,7 +883,8 @@ static void flow_offload_tuple_del(struct flow_offload_work *offload, enum flow_offload_tuple_dir dir) { nf_flow_offload_tuple(offload->flowtable, offload->flow, NULL, dir, - offload->priority, FLOW_CLS_DESTROY, NULL, + offload->flowtable->priority, + FLOW_CLS_DESTROY, NULL, &offload->flowtable->flow_block.cb_list); } @@ -926,7 +935,8 @@ static void flow_offload_tuple_stats(struct flow_offload_work *offload, struct flow_stats *stats) { nf_flow_offload_tuple(offload->flowtable, offload->flow, NULL, dir, - offload->priority, FLOW_CLS_STATS, stats, + offload->flowtable->priority, + FLOW_CLS_STATS, stats, &offload->flowtable->flow_block.cb_list); } @@ -1004,7 +1014,6 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable, offload->cmd = cmd; offload->flow = flow; - offload->priority = flowtable->priority; offload->flowtable = flowtable; INIT_WORK(&offload->work, flow_offload_work_handler); diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index ffcf6529afc3..7981be526f26 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -801,7 +801,7 @@ static int nf_nat_proto_remove(struct nf_conn *i, void *data) return i->status & IPS_NAT_MASK ? 1 : 0; } -static void __nf_nat_cleanup_conntrack(struct nf_conn *ct) +static void nf_nat_cleanup_conntrack(struct nf_conn *ct) { unsigned int h; @@ -823,7 +823,7 @@ static int nf_nat_proto_clean(struct nf_conn *ct, void *data) * will delete entry from already-freed table. */ if (test_and_clear_bit(IPS_SRC_NAT_DONE_BIT, &ct->status)) - __nf_nat_cleanup_conntrack(ct); + nf_nat_cleanup_conntrack(ct); /* don't delete conntrack. Although that would make things a lot * simpler, we'd end up flushing all conntracks on nat rmmod. @@ -831,20 +831,6 @@ static int nf_nat_proto_clean(struct nf_conn *ct, void *data) return 0; } -/* No one using conntrack by the time this called. */ -static void nf_nat_cleanup_conntrack(struct nf_conn *ct) -{ - if (ct->status & IPS_SRC_NAT_DONE) - __nf_nat_cleanup_conntrack(ct); -} - -static struct nf_ct_ext_type nat_extend __read_mostly = { - .len = sizeof(struct nf_conn_nat), - .align = __alignof__(struct nf_conn_nat), - .destroy = nf_nat_cleanup_conntrack, - .id = NF_CT_EXT_NAT, -}; - #if IS_ENABLED(CONFIG_NF_CT_NETLINK) #include @@ -1136,6 +1122,7 @@ static const struct nf_nat_hook nat_hook = { .decode_session = __nf_nat_decode_session, #endif .manip_pkt = nf_nat_manip_pkt, + .remove_nat_bysrc = nf_nat_cleanup_conntrack, }; static int __init nf_nat_init(void) @@ -1151,19 +1138,11 @@ static int __init nf_nat_init(void) if (!nf_nat_bysource) return -ENOMEM; - ret = nf_ct_extend_register(&nat_extend); - if (ret < 0) { - kvfree(nf_nat_bysource); - pr_err("Unable to register extension\n"); - return ret; - } - for (i = 0; i < CONNTRACK_LOCKS; i++) spin_lock_init(&nf_nat_locks[i]); ret = register_pernet_subsys(&nat_net_ops); if (ret < 0) { - nf_ct_extend_unregister(&nat_extend); kvfree(nf_nat_bysource); return ret; } @@ -1182,7 +1161,6 @@ static void __exit nf_nat_cleanup(void) nf_ct_iterate_destroy(nf_nat_proto_clean, &clean); - nf_ct_extend_unregister(&nat_extend); nf_ct_helper_expectfn_unregister(&follow_master_nat); RCU_INIT_POINTER(nf_nat_hook, NULL); diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index 2dfc5dae0656..e479dd0561c5 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -236,12 +236,6 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, return 1; } -static struct nf_ct_ext_type nf_ct_synproxy_extend __read_mostly = { - .len = sizeof(struct nf_conn_synproxy), - .align = __alignof__(struct nf_conn_synproxy), - .id = NF_CT_EXT_SYNPROXY, -}; - #ifdef CONFIG_PROC_FS static void *synproxy_cpu_seq_start(struct seq_file *seq, loff_t *pos) { @@ -387,28 +381,12 @@ static struct pernet_operations synproxy_net_ops = { static int __init synproxy_core_init(void) { - int err; - - err = nf_ct_extend_register(&nf_ct_synproxy_extend); - if (err < 0) - goto err1; - - err = register_pernet_subsys(&synproxy_net_ops); - if (err < 0) - goto err2; - - return 0; - -err2: - nf_ct_extend_unregister(&nf_ct_synproxy_extend); -err1: - return err; + return register_pernet_subsys(&synproxy_net_ops); } static void __exit synproxy_core_exit(void) { unregister_pernet_subsys(&synproxy_net_ops); - nf_ct_extend_unregister(&nf_ct_synproxy_extend); } module_init(synproxy_core_init); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index d71a33ae39b3..c55ccd3cf2f8 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -550,6 +550,58 @@ static int nft_delflowtable(struct nft_ctx *ctx, return err; } +static void __nft_reg_track_clobber(struct nft_regs_track *track, u8 dreg) +{ + int i; + + for (i = track->regs[dreg].num_reg; i > 0; i--) + __nft_reg_track_cancel(track, dreg - i); +} + +static void __nft_reg_track_update(struct nft_regs_track *track, + const struct nft_expr *expr, + u8 dreg, u8 num_reg) +{ + track->regs[dreg].selector = expr; + track->regs[dreg].bitwise = NULL; + track->regs[dreg].num_reg = num_reg; +} + +void nft_reg_track_update(struct nft_regs_track *track, + const struct nft_expr *expr, u8 dreg, u8 len) +{ + unsigned int regcount; + int i; + + __nft_reg_track_clobber(track, dreg); + + regcount = DIV_ROUND_UP(len, NFT_REG32_SIZE); + for (i = 0; i < regcount; i++, dreg++) + __nft_reg_track_update(track, expr, dreg, i); +} +EXPORT_SYMBOL_GPL(nft_reg_track_update); + +void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len) +{ + unsigned int regcount; + int i; + + __nft_reg_track_clobber(track, dreg); + + regcount = DIV_ROUND_UP(len, NFT_REG32_SIZE); + for (i = 0; i < regcount; i++, dreg++) + __nft_reg_track_cancel(track, dreg); +} +EXPORT_SYMBOL_GPL(nft_reg_track_cancel); + +void __nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg) +{ + track->regs[dreg].selector = NULL; + track->regs[dreg].bitwise = NULL; + track->regs[dreg].num_reg = 0; +} +EXPORT_SYMBOL_GPL(__nft_reg_track_cancel); + /* * Tables */ @@ -1072,6 +1124,30 @@ static int nft_objname_hash_cmp(struct rhashtable_compare_arg *arg, return strcmp(obj->key.name, k->name); } +static bool nft_supported_family(u8 family) +{ + return false +#ifdef CONFIG_NF_TABLES_INET + || family == NFPROTO_INET +#endif +#ifdef CONFIG_NF_TABLES_IPV4 + || family == NFPROTO_IPV4 +#endif +#ifdef CONFIG_NF_TABLES_ARP + || family == NFPROTO_ARP +#endif +#ifdef CONFIG_NF_TABLES_NETDEV + || family == NFPROTO_NETDEV +#endif +#if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE) + || family == NFPROTO_BRIDGE +#endif +#ifdef CONFIG_NF_TABLES_IPV6 + || family == NFPROTO_IPV6 +#endif + ; +} + static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, const struct nlattr * const nla[]) { @@ -1086,6 +1162,9 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, u32 flags = 0; int err; + if (!nft_supported_family(family)) + return -EOPNOTSUPP; + lockdep_assert_held(&nft_net->commit_mutex); attr = nla[NFTA_TABLE_NAME]; table = nft_table_lookup(net, attr, family, genmask, @@ -8263,7 +8342,16 @@ EXPORT_SYMBOL_GPL(nf_tables_trans_destroy_flush_work); static bool nft_expr_reduce(struct nft_regs_track *track, const struct nft_expr *expr) { - return false; + if (!expr->ops->reduce) { + pr_warn_once("missing reduce for expression %s ", + expr->ops->type->name); + return false; + } + + if (nft_reduce_is_readonly(expr)) + return false; + + return expr->ops->reduce(track, expr); } static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *chain) @@ -9275,17 +9363,23 @@ int nft_parse_u32_check(const struct nlattr *attr, int max, u32 *dest) } EXPORT_SYMBOL_GPL(nft_parse_u32_check); -static unsigned int nft_parse_register(const struct nlattr *attr) +static unsigned int nft_parse_register(const struct nlattr *attr, u32 *preg) { unsigned int reg; reg = ntohl(nla_get_be32(attr)); switch (reg) { case NFT_REG_VERDICT...NFT_REG_4: - return reg * NFT_REG_SIZE / NFT_REG32_SIZE; + *preg = reg * NFT_REG_SIZE / NFT_REG32_SIZE; + break; + case NFT_REG32_00...NFT_REG32_15: + *preg = reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00; + break; default: - return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00; + return -ERANGE; } + + return 0; } /** @@ -9327,7 +9421,10 @@ int nft_parse_register_load(const struct nlattr *attr, u8 *sreg, u32 len) u32 reg; int err; - reg = nft_parse_register(attr); + err = nft_parse_register(attr, ®); + if (err < 0) + return err; + err = nft_validate_register_load(reg, len); if (err < 0) return err; @@ -9382,7 +9479,10 @@ int nft_parse_register_store(const struct nft_ctx *ctx, int err; u32 reg; - reg = nft_parse_register(attr); + err = nft_parse_register(attr, ®); + if (err < 0) + return err; + err = nft_validate_register_store(ctx, reg, data, type, len); if (err < 0) return err; diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 36e73f9828c5..53f40e473855 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -67,6 +67,20 @@ static void nft_cmp_fast_eval(const struct nft_expr *expr, regs->verdict.code = NFT_BREAK; } +static void nft_cmp16_fast_eval(const struct nft_expr *expr, + struct nft_regs *regs) +{ + const struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr); + const u64 *reg_data = (const u64 *)®s->data[priv->sreg]; + const u64 *mask = (const u64 *)&priv->mask; + const u64 *data = (const u64 *)&priv->data; + + if (((reg_data[0] & mask[0]) == data[0] && + ((reg_data[1] & mask[1]) == data[1])) ^ priv->inv) + return; + regs->verdict.code = NFT_BREAK; +} + static noinline void __nft_trace_verdict(struct nft_traceinfo *info, const struct nft_chain *chain, const struct nft_regs *regs) @@ -201,7 +215,7 @@ nft_do_chain(struct nft_pktinfo *pkt, void *priv) const struct nft_rule_dp *rule, *last_rule; const struct net *net = nft_net(pkt); const struct nft_expr *expr, *last; - struct nft_regs regs; + struct nft_regs regs = {}; unsigned int stackptr = 0; struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; bool genbit = READ_ONCE(net->nft.gencursor); @@ -225,6 +239,8 @@ nft_do_chain(struct nft_pktinfo *pkt, void *priv) nft_rule_dp_for_each_expr(expr, last, rule) { if (expr->ops == &nft_cmp_fast_ops) nft_cmp_fast_eval(expr, ®s); + else if (expr->ops == &nft_cmp16_fast_ops) + nft_cmp16_fast_eval(expr, ®s); else if (expr->ops == &nft_bitwise_fast_ops) nft_bitwise_fast_eval(expr, ®s); else if (expr->ops != &nft_payload_fast_ops || diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index c57673d499be..b0d8888a539b 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -605,6 +605,11 @@ static struct pernet_operations cttimeout_ops = { .size = sizeof(struct nfct_timeout_pernet), }; +static const struct nf_ct_timeout_hooks hooks = { + .timeout_find_get = ctnl_timeout_find_get, + .timeout_put = ctnl_timeout_put, +}; + static int __init cttimeout_init(void) { int ret; @@ -619,8 +624,7 @@ static int __init cttimeout_init(void) "nfnetlink.\n"); goto err_out; } - RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, ctnl_timeout_find_get); - RCU_INIT_POINTER(nf_ct_timeout_put_hook, ctnl_timeout_put); + RCU_INIT_POINTER(nf_ct_timeout_hook, &hooks); return 0; err_out: @@ -633,8 +637,7 @@ static void __exit cttimeout_exit(void) nfnetlink_subsys_unregister(&cttimeout_subsys); unregister_pernet_subsys(&cttimeout_ops); - RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL); - RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL); + RCU_INIT_POINTER(nf_ct_timeout_hook, NULL); synchronize_rcu(); } diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index ae9c0756bba5..d97eb280cb2e 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -460,6 +460,7 @@ __build_packet_message(struct nfnl_log_net *log, sk_buff_data_t old_tail = inst->skb->tail; struct sock *sk; const unsigned char *hwhdrp; + ktime_t tstamp; nlh = nfnl_msg_put(inst->skb, 0, 0, nfnl_msg_type(NFNL_SUBSYS_ULOG, NFULNL_MSG_PACKET), @@ -588,9 +589,10 @@ __build_packet_message(struct nfnl_log_net *log, goto nla_put_failure; } - if (hooknum <= NF_INET_FORWARD && skb->tstamp) { + tstamp = skb_tstamp_cond(skb, false); + if (hooknum <= NF_INET_FORWARD && tstamp) { struct nfulnl_msg_packet_timestamp ts; - struct timespec64 kts = ktime_to_timespec64(skb->tstamp); + struct timespec64 kts = ktime_to_timespec64(tstamp); ts.sec = cpu_to_be64(kts.tv_sec); ts.usec = cpu_to_be64(kts.tv_nsec / NSEC_PER_USEC); diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 64a6acb6aeae..a364f8e5e698 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -392,6 +392,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, bool csum_verify; char *secdata = NULL; u32 seclen = 0; + ktime_t tstamp; size = nlmsg_total_size(sizeof(struct nfgenmsg)) + nla_total_size(sizeof(struct nfqnl_msg_packet_hdr)) @@ -402,11 +403,13 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, + nla_total_size(sizeof(u_int32_t)) /* ifindex */ #endif + nla_total_size(sizeof(u_int32_t)) /* mark */ + + nla_total_size(sizeof(u_int32_t)) /* priority */ + nla_total_size(sizeof(struct nfqnl_msg_packet_hw)) + nla_total_size(sizeof(u_int32_t)) /* skbinfo */ + nla_total_size(sizeof(u_int32_t)); /* cap_len */ - if (entskb->tstamp) + tstamp = skb_tstamp_cond(entskb, false); + if (tstamp) size += nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp)); size += nfqnl_get_bridge_size(entry); @@ -559,6 +562,10 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, nla_put_be32(skb, NFQA_MARK, htonl(entskb->mark))) goto nla_put_failure; + if (entskb->priority && + nla_put_be32(skb, NFQA_PRIORITY, htonl(entskb->priority))) + goto nla_put_failure; + if (indev && entskb->dev && skb_mac_header_was_set(entskb) && skb_mac_header_len(entskb) != 0) { @@ -577,9 +584,9 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, if (nfqnl_put_bridge(entry, skb) < 0) goto nla_put_failure; - if (entry->state.hook <= NF_INET_FORWARD && entskb->tstamp) { + if (entry->state.hook <= NF_INET_FORWARD && tstamp) { struct nfqnl_msg_packet_timestamp ts; - struct timespec64 kts = ktime_to_timespec64(entskb->tstamp); + struct timespec64 kts = ktime_to_timespec64(tstamp); ts.sec = cpu_to_be64(kts.tv_sec); ts.usec = cpu_to_be64(kts.tv_nsec / NSEC_PER_USEC); @@ -1020,11 +1027,13 @@ static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = { [NFQA_CT] = { .type = NLA_UNSPEC }, [NFQA_EXP] = { .type = NLA_UNSPEC }, [NFQA_VLAN] = { .type = NLA_NESTED }, + [NFQA_PRIORITY] = { .type = NLA_U32 }, }; static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { [NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) }, [NFQA_MARK] = { .type = NLA_U32 }, + [NFQA_PRIORITY] = { .type = NLA_U32 }, }; static struct nfqnl_instance * @@ -1105,6 +1114,9 @@ static int nfqnl_recv_verdict_batch(struct sk_buff *skb, if (nfqa[NFQA_MARK]) entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); + if (nfqa[NFQA_PRIORITY]) + entry->skb->priority = ntohl(nla_get_be32(nfqa[NFQA_PRIORITY])); + nfqnl_reinject(entry, verdict); } return 0; @@ -1231,6 +1243,9 @@ static int nfqnl_recv_verdict(struct sk_buff *skb, const struct nfnl_info *info, if (nfqa[NFQA_MARK]) entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); + if (nfqa[NFQA_PRIORITY]) + entry->skb->priority = ntohl(nla_get_be32(nfqa[NFQA_PRIORITY])); + nfqnl_reinject(entry, verdict); return 0; } diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index 7b727d3ebf9d..38caa66632b4 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -283,12 +283,16 @@ static bool nft_bitwise_reduce(struct nft_regs_track *track, { const struct nft_bitwise *priv = nft_expr_priv(expr); const struct nft_bitwise *bitwise; + unsigned int regcount; + u8 dreg; + int i; if (!track->regs[priv->sreg].selector) return false; bitwise = nft_expr_priv(expr); if (track->regs[priv->sreg].selector == track->regs[priv->dreg].selector && + track->regs[priv->sreg].num_reg == 0 && track->regs[priv->dreg].bitwise && track->regs[priv->dreg].bitwise->ops == expr->ops && priv->sreg == bitwise->sreg && @@ -302,17 +306,21 @@ static bool nft_bitwise_reduce(struct nft_regs_track *track, return true; } - if (track->regs[priv->sreg].bitwise) { - track->regs[priv->dreg].selector = NULL; - track->regs[priv->dreg].bitwise = NULL; + if (track->regs[priv->sreg].bitwise || + track->regs[priv->sreg].num_reg != 0) { + nft_reg_track_cancel(track, priv->dreg, priv->len); return false; } if (priv->sreg != priv->dreg) { - track->regs[priv->dreg].selector = - track->regs[priv->sreg].selector; + nft_reg_track_update(track, track->regs[priv->sreg].selector, + priv->dreg, priv->len); } - track->regs[priv->dreg].bitwise = expr; + + dreg = priv->dreg; + regcount = DIV_ROUND_UP(priv->len, NFT_REG32_SIZE); + for (i = 0; i < regcount; i++, dreg++) + track->regs[priv->dreg].bitwise = expr; return false; } @@ -447,8 +455,7 @@ static bool nft_bitwise_fast_reduce(struct nft_regs_track *track, } if (track->regs[priv->sreg].bitwise) { - track->regs[priv->dreg].selector = NULL; - track->regs[priv->dreg].bitwise = NULL; + nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE); return false; } @@ -522,3 +529,4 @@ bool nft_expr_reduce_bitwise(struct nft_regs_track *track, return false; } +EXPORT_SYMBOL_GPL(nft_expr_reduce_bitwise); diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c index e646e9ee4a98..d77609144b26 100644 --- a/net/netfilter/nft_byteorder.c +++ b/net/netfilter/nft_byteorder.c @@ -172,8 +172,7 @@ static bool nft_byteorder_reduce(struct nft_regs_track *track, { struct nft_byteorder *priv = nft_expr_priv(expr); - track->regs[priv->dreg].selector = NULL; - track->regs[priv->dreg].bitwise = NULL; + nft_reg_track_cancel(track, priv->dreg, priv->len); return false; } diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c index 47b6d05f1ae6..6528f76ca29e 100644 --- a/net/netfilter/nft_cmp.c +++ b/net/netfilter/nft_cmp.c @@ -193,6 +193,7 @@ static const struct nft_expr_ops nft_cmp_ops = { .eval = nft_cmp_eval, .init = nft_cmp_init, .dump = nft_cmp_dump, + .reduce = NFT_REDUCE_READONLY, .offload = nft_cmp_offload, }; @@ -269,15 +270,108 @@ const struct nft_expr_ops nft_cmp_fast_ops = { .eval = NULL, /* inlined */ .init = nft_cmp_fast_init, .dump = nft_cmp_fast_dump, + .reduce = NFT_REDUCE_READONLY, .offload = nft_cmp_fast_offload, }; +static u32 nft_cmp_mask(u32 bitlen) +{ + return (__force u32)cpu_to_le32(~0U >> (sizeof(u32) * BITS_PER_BYTE - bitlen)); +} + +static void nft_cmp16_fast_mask(struct nft_data *data, unsigned int bitlen) +{ + int len = bitlen / BITS_PER_BYTE; + int i, words = len / sizeof(u32); + + for (i = 0; i < words; i++) { + data->data[i] = 0xffffffff; + bitlen -= sizeof(u32) * BITS_PER_BYTE; + } + + if (len % sizeof(u32)) + data->data[i++] = nft_cmp_mask(bitlen); + + for (; i < 4; i++) + data->data[i] = 0; +} + +static int nft_cmp16_fast_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr); + struct nft_data_desc desc; + int err; + + err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc, + tb[NFTA_CMP_DATA]); + if (err < 0) + return err; + + err = nft_parse_register_load(tb[NFTA_CMP_SREG], &priv->sreg, desc.len); + if (err < 0) + return err; + + nft_cmp16_fast_mask(&priv->mask, desc.len * BITS_PER_BYTE); + priv->inv = ntohl(nla_get_be32(tb[NFTA_CMP_OP])) != NFT_CMP_EQ; + priv->len = desc.len; + + return 0; +} + +static int nft_cmp16_fast_offload(struct nft_offload_ctx *ctx, + struct nft_flow_rule *flow, + const struct nft_expr *expr) +{ + const struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr); + struct nft_cmp_expr cmp = { + .data = priv->data, + .sreg = priv->sreg, + .len = priv->len, + .op = priv->inv ? NFT_CMP_NEQ : NFT_CMP_EQ, + }; + + return __nft_cmp_offload(ctx, flow, &cmp); +} + +static int nft_cmp16_fast_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr); + enum nft_cmp_ops op = priv->inv ? NFT_CMP_NEQ : NFT_CMP_EQ; + + if (nft_dump_register(skb, NFTA_CMP_SREG, priv->sreg)) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_CMP_OP, htonl(op))) + goto nla_put_failure; + + if (nft_data_dump(skb, NFTA_CMP_DATA, &priv->data, + NFT_DATA_VALUE, priv->len) < 0) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + + +const struct nft_expr_ops nft_cmp16_fast_ops = { + .type = &nft_cmp_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp16_fast_expr)), + .eval = NULL, /* inlined */ + .init = nft_cmp16_fast_init, + .dump = nft_cmp16_fast_dump, + .reduce = NFT_REDUCE_READONLY, + .offload = nft_cmp16_fast_offload, +}; + static const struct nft_expr_ops * nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_data_desc desc; struct nft_data data; enum nft_cmp_ops op; + u8 sreg; int err; if (tb[NFTA_CMP_SREG] == NULL || @@ -306,9 +400,16 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) if (desc.type != NFT_DATA_VALUE) goto err1; - if (desc.len <= sizeof(u32) && (op == NFT_CMP_EQ || op == NFT_CMP_NEQ)) - return &nft_cmp_fast_ops; + sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); + if (op == NFT_CMP_EQ || op == NFT_CMP_NEQ) { + if (desc.len <= sizeof(u32)) + return &nft_cmp_fast_ops; + else if (desc.len <= sizeof(data) && + ((sreg >= NFT_REG_1 && sreg <= NFT_REG_4) || + (sreg >= NFT_REG32_00 && sreg <= NFT_REG32_12 && sreg % 2 == 0))) + return &nft_cmp16_fast_ops; + } return &nft_cmp_ops; err1: nft_data_release(&data, desc.type); diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index f69cc73c5813..c16172427622 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -731,6 +731,14 @@ static const struct nfnetlink_subsystem nfnl_compat_subsys = { static struct nft_expr_type nft_match_type; +static bool nft_match_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + const struct xt_match *match = expr->ops->data; + + return strcmp(match->name, "comment") == 0; +} + static const struct nft_expr_ops * nft_match_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) @@ -773,6 +781,7 @@ nft_match_select_ops(const struct nft_ctx *ctx, ops->dump = nft_match_dump; ops->validate = nft_match_validate; ops->data = match; + ops->reduce = nft_match_reduce; matchsize = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize)); if (matchsize > NFT_MATCH_LARGE_THRESH) { @@ -862,6 +871,7 @@ nft_target_select_ops(const struct nft_ctx *ctx, ops->dump = nft_target_dump; ops->validate = nft_target_validate; ops->data = target; + ops->reduce = NFT_REDUCE_READONLY; if (family == NFPROTO_BRIDGE) ops->eval = nft_target_eval_bridge; diff --git a/net/netfilter/nft_connlimit.c b/net/netfilter/nft_connlimit.c index 3362417ebfdb..9de1462e4ac4 100644 --- a/net/netfilter/nft_connlimit.c +++ b/net/netfilter/nft_connlimit.c @@ -257,6 +257,7 @@ static const struct nft_expr_ops nft_connlimit_ops = { .destroy_clone = nft_connlimit_destroy_clone, .dump = nft_connlimit_dump, .gc = nft_connlimit_gc, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_connlimit_type __read_mostly = { diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c index f179e8c3b0ca..da9083605a61 100644 --- a/net/netfilter/nft_counter.c +++ b/net/netfilter/nft_counter.c @@ -293,6 +293,7 @@ static const struct nft_expr_ops nft_counter_ops = { .destroy_clone = nft_counter_destroy, .dump = nft_counter_dump, .clone = nft_counter_clone, + .reduce = NFT_REDUCE_READONLY, .offload = nft_counter_offload, .offload_stats = nft_counter_offload_stats, }; diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 5adf8bb628a8..d8e1614918a1 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -26,6 +26,7 @@ struct nft_ct { enum nft_ct_keys key:8; enum ip_conntrack_dir dir:8; + u8 len; union { u8 dreg; u8 sreg; @@ -500,6 +501,7 @@ static int nft_ct_get_init(const struct nft_ctx *ctx, } } + priv->len = len; err = nft_parse_register_store(ctx, tb[NFTA_CT_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, len); if (err < 0) @@ -608,6 +610,7 @@ static int nft_ct_set_init(const struct nft_ctx *ctx, } } + priv->len = len; err = nft_parse_register_load(tb[NFTA_CT_SREG], &priv->sreg, len); if (err < 0) goto err1; @@ -677,6 +680,29 @@ static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr) return -1; } +static bool nft_ct_get_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + const struct nft_ct *priv = nft_expr_priv(expr); + const struct nft_ct *ct; + + if (!nft_reg_track_cmp(track, expr, priv->dreg)) { + nft_reg_track_update(track, expr, priv->dreg, priv->len); + return false; + } + + ct = nft_expr_priv(track->regs[priv->dreg].selector); + if (priv->key != ct->key) { + nft_reg_track_update(track, expr, priv->dreg, priv->len); + return false; + } + + if (!track->regs[priv->dreg].bitwise) + return true; + + return nft_expr_reduce_bitwise(track, expr); +} + static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr) { const struct nft_ct *priv = nft_expr_priv(expr); @@ -710,8 +736,27 @@ static const struct nft_expr_ops nft_ct_get_ops = { .init = nft_ct_get_init, .destroy = nft_ct_get_destroy, .dump = nft_ct_get_dump, + .reduce = nft_ct_get_reduce, }; +static bool nft_ct_set_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + int i; + + for (i = 0; i < NFT_REG32_NUM; i++) { + if (!track->regs[i].selector) + continue; + + if (track->regs[i].selector->ops != &nft_ct_get_ops) + continue; + + __nft_reg_track_cancel(track, i); + } + + return false; +} + static const struct nft_expr_ops nft_ct_set_ops = { .type = &nft_ct_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), @@ -719,6 +764,7 @@ static const struct nft_expr_ops nft_ct_set_ops = { .init = nft_ct_set_init, .destroy = nft_ct_set_destroy, .dump = nft_ct_set_dump, + .reduce = nft_ct_set_reduce, }; #ifdef CONFIG_NF_CONNTRACK_ZONES @@ -729,6 +775,7 @@ static const struct nft_expr_ops nft_ct_set_zone_ops = { .init = nft_ct_set_init, .destroy = nft_ct_set_destroy, .dump = nft_ct_set_dump, + .reduce = nft_ct_set_reduce, }; #endif @@ -785,6 +832,7 @@ static const struct nft_expr_ops nft_notrack_ops = { .type = &nft_notrack_type, .size = NFT_EXPR_SIZE(0), .eval = nft_notrack_eval, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_notrack_type __read_mostly = { @@ -1041,6 +1089,9 @@ static int nft_ct_helper_obj_init(const struct nft_ctx *ctx, if (err < 0) goto err_put_helper; + /* Avoid the bogus warning, helper will be assigned after CT init */ + nf_ct_set_auto_assign_helper_warned(ctx->net); + return 0; err_put_helper: diff --git a/net/netfilter/nft_dup_netdev.c b/net/netfilter/nft_dup_netdev.c index 5b5c607fbf83..63507402716d 100644 --- a/net/netfilter/nft_dup_netdev.c +++ b/net/netfilter/nft_dup_netdev.c @@ -79,6 +79,7 @@ static const struct nft_expr_ops nft_dup_netdev_ops = { .eval = nft_dup_netdev_eval, .init = nft_dup_netdev_init, .dump = nft_dup_netdev_dump, + .reduce = NFT_REDUCE_READONLY, .offload = nft_dup_netdev_offload, .offload_action = nft_dup_netdev_offload_action, }; diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index 87f3af4645d9..22f70b543fa2 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -413,6 +413,7 @@ static const struct nft_expr_ops nft_dynset_ops = { .activate = nft_dynset_activate, .deactivate = nft_dynset_deactivate, .dump = nft_dynset_dump, + .reduce = NFT_REDUCE_READONLY, }; struct nft_expr_type nft_dynset_type __read_mostly = { diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index 9e927ab4df15..22c3e05b52db 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -308,6 +308,63 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr, regs->verdict.code = NFT_BREAK; } +static void nft_exthdr_tcp_strip_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE]; + struct nft_exthdr *priv = nft_expr_priv(expr); + unsigned int i, tcphdr_len, optl; + struct tcphdr *tcph; + u8 *opt; + + tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len); + if (!tcph) + goto err; + + if (skb_ensure_writable(pkt->skb, nft_thoff(pkt) + tcphdr_len)) + goto drop; + + opt = (u8 *)nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len); + if (!opt) + goto err; + for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) { + unsigned int j; + + optl = optlen(opt, i); + if (priv->type != opt[i]) + continue; + + if (i + optl > tcphdr_len) + goto drop; + + for (j = 0; j < optl; ++j) { + u16 n = TCPOPT_NOP; + u16 o = opt[i+j]; + + if ((i + j) % 2 == 0) { + o <<= 8; + n <<= 8; + } + inet_proto_csum_replace2(&tcph->check, pkt->skb, htons(o), + htons(n), false); + } + memset(opt + i, TCPOPT_NOP, optl); + return; + } + + /* option not found, continue. This allows to do multiple + * option removals per rule. + */ + return; +err: + regs->verdict.code = NFT_BREAK; + return; +drop: + /* can't remove, no choice but to drop */ + regs->verdict.code = NF_DROP; +} + static void nft_exthdr_sctp_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -457,6 +514,28 @@ static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx, priv->len); } +static int nft_exthdr_tcp_strip_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_exthdr *priv = nft_expr_priv(expr); + + if (tb[NFTA_EXTHDR_SREG] || + tb[NFTA_EXTHDR_DREG] || + tb[NFTA_EXTHDR_FLAGS] || + tb[NFTA_EXTHDR_OFFSET] || + tb[NFTA_EXTHDR_LEN]) + return -EINVAL; + + if (!tb[NFTA_EXTHDR_TYPE]) + return -EINVAL; + + priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]); + priv->op = NFT_EXTHDR_OP_TCPOPT; + + return 0; +} + static int nft_exthdr_ipv4_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]) @@ -517,12 +596,47 @@ static int nft_exthdr_dump_set(struct sk_buff *skb, const struct nft_expr *expr) return nft_exthdr_dump_common(skb, priv); } +static int nft_exthdr_dump_strip(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_exthdr *priv = nft_expr_priv(expr); + + return nft_exthdr_dump_common(skb, priv); +} + +static bool nft_exthdr_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + const struct nft_exthdr *priv = nft_expr_priv(expr); + const struct nft_exthdr *exthdr; + + if (!nft_reg_track_cmp(track, expr, priv->dreg)) { + nft_reg_track_update(track, expr, priv->dreg, priv->len); + return false; + } + + exthdr = nft_expr_priv(track->regs[priv->dreg].selector); + if (priv->type != exthdr->type || + priv->op != exthdr->op || + priv->flags != exthdr->flags || + priv->offset != exthdr->offset || + priv->len != exthdr->len) { + nft_reg_track_update(track, expr, priv->dreg, priv->len); + return false; + } + + if (!track->regs[priv->dreg].bitwise) + return true; + + return nft_expr_reduce_bitwise(track, expr); +} + static const struct nft_expr_ops nft_exthdr_ipv6_ops = { .type = &nft_exthdr_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), .eval = nft_exthdr_ipv6_eval, .init = nft_exthdr_init, .dump = nft_exthdr_dump, + .reduce = nft_exthdr_reduce, }; static const struct nft_expr_ops nft_exthdr_ipv4_ops = { @@ -531,6 +645,7 @@ static const struct nft_expr_ops nft_exthdr_ipv4_ops = { .eval = nft_exthdr_ipv4_eval, .init = nft_exthdr_ipv4_init, .dump = nft_exthdr_dump, + .reduce = nft_exthdr_reduce, }; static const struct nft_expr_ops nft_exthdr_tcp_ops = { @@ -539,6 +654,7 @@ static const struct nft_expr_ops nft_exthdr_tcp_ops = { .eval = nft_exthdr_tcp_eval, .init = nft_exthdr_init, .dump = nft_exthdr_dump, + .reduce = nft_exthdr_reduce, }; static const struct nft_expr_ops nft_exthdr_tcp_set_ops = { @@ -547,6 +663,16 @@ static const struct nft_expr_ops nft_exthdr_tcp_set_ops = { .eval = nft_exthdr_tcp_set_eval, .init = nft_exthdr_tcp_set_init, .dump = nft_exthdr_dump_set, + .reduce = NFT_REDUCE_READONLY, +}; + +static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = { + .type = &nft_exthdr_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), + .eval = nft_exthdr_tcp_strip_eval, + .init = nft_exthdr_tcp_strip_init, + .dump = nft_exthdr_dump_strip, + .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops nft_exthdr_sctp_ops = { @@ -555,6 +681,7 @@ static const struct nft_expr_ops nft_exthdr_sctp_ops = { .eval = nft_exthdr_sctp_eval, .init = nft_exthdr_init, .dump = nft_exthdr_dump, + .reduce = nft_exthdr_reduce, }; static const struct nft_expr_ops * @@ -576,7 +703,7 @@ nft_exthdr_select_ops(const struct nft_ctx *ctx, return &nft_exthdr_tcp_set_ops; if (tb[NFTA_EXTHDR_DREG]) return &nft_exthdr_tcp_ops; - break; + return &nft_exthdr_tcp_strip_ops; case NFT_EXTHDR_OP_IPV6: if (tb[NFTA_EXTHDR_DREG]) return &nft_exthdr_ipv6_ops; diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index b10ce732b337..f198f2d9ef90 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -156,5 +156,47 @@ void nft_fib_store_result(void *reg, const struct nft_fib *priv, } EXPORT_SYMBOL_GPL(nft_fib_store_result); +bool nft_fib_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + unsigned int len = NFT_REG32_SIZE; + const struct nft_fib *fib; + + switch (priv->result) { + case NFT_FIB_RESULT_OIF: + break; + case NFT_FIB_RESULT_OIFNAME: + if (priv->flags & NFTA_FIB_F_PRESENT) + len = NFT_REG32_SIZE; + else + len = IFNAMSIZ; + break; + case NFT_FIB_RESULT_ADDRTYPE: + break; + default: + WARN_ON_ONCE(1); + break; + } + + if (!nft_reg_track_cmp(track, expr, priv->dreg)) { + nft_reg_track_update(track, expr, priv->dreg, len); + return false; + } + + fib = nft_expr_priv(track->regs[priv->dreg].selector); + if (priv->result != fib->result || + priv->flags != fib->flags) { + nft_reg_track_update(track, expr, priv->dreg, len); + return false; + } + + if (!track->regs[priv->dreg].bitwise) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(nft_fib_reduce); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Florian Westphal "); diff --git a/net/netfilter/nft_fib_inet.c b/net/netfilter/nft_fib_inet.c index a88d44e163d1..666a3741d20b 100644 --- a/net/netfilter/nft_fib_inet.c +++ b/net/netfilter/nft_fib_inet.c @@ -49,6 +49,7 @@ static const struct nft_expr_ops nft_fib_inet_ops = { .init = nft_fib_init, .dump = nft_fib_dump, .validate = nft_fib_validate, + .reduce = nft_fib_reduce, }; static struct nft_expr_type nft_fib_inet_type __read_mostly = { diff --git a/net/netfilter/nft_fib_netdev.c b/net/netfilter/nft_fib_netdev.c index 3f3478abd845..9121ec64e918 100644 --- a/net/netfilter/nft_fib_netdev.c +++ b/net/netfilter/nft_fib_netdev.c @@ -58,6 +58,7 @@ static const struct nft_expr_ops nft_fib_netdev_ops = { .init = nft_fib_init, .dump = nft_fib_dump, .validate = nft_fib_validate, + .reduce = nft_fib_reduce, }; static struct nft_expr_type nft_fib_netdev_type __read_mostly = { diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c index 0af34ad41479..900d48c810a1 100644 --- a/net/netfilter/nft_flow_offload.c +++ b/net/netfilter/nft_flow_offload.c @@ -298,6 +298,19 @@ static void nft_flow_offload_eval(const struct nft_expr *expr, break; case IPPROTO_UDP: break; +#ifdef CONFIG_NF_CT_PROTO_GRE + case IPPROTO_GRE: { + struct nf_conntrack_tuple *tuple; + + if (ct->status & IPS_NAT_MASK) + goto out; + tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + /* No support for GRE v1 */ + if (tuple->src.u.gre.key || tuple->dst.u.gre.key) + goto out; + break; + } +#endif default: goto out; } @@ -428,6 +441,7 @@ static const struct nft_expr_ops nft_flow_offload_ops = { .destroy = nft_flow_offload_destroy, .validate = nft_flow_offload_validate, .dump = nft_flow_offload_dump, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_flow_offload_type __read_mostly = { diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c index 619e394a91de..7c5876dc9ff2 100644 --- a/net/netfilter/nft_fwd_netdev.c +++ b/net/netfilter/nft_fwd_netdev.c @@ -145,7 +145,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, return; skb->dev = dev; - skb->tstamp = 0; + skb_clear_tstamp(skb); neigh_xmit(neigh_table, dev, addr, skb); out: regs->verdict.code = verdict; @@ -217,6 +217,7 @@ static const struct nft_expr_ops nft_fwd_neigh_netdev_ops = { .init = nft_fwd_neigh_init, .dump = nft_fwd_neigh_dump, .validate = nft_fwd_validate, + .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops nft_fwd_netdev_ops = { @@ -226,6 +227,7 @@ static const struct nft_expr_ops nft_fwd_netdev_ops = { .init = nft_fwd_netdev_init, .dump = nft_fwd_netdev_dump, .validate = nft_fwd_validate, + .reduce = NFT_REDUCE_READONLY, .offload = nft_fwd_netdev_offload, .offload_action = nft_fwd_netdev_offload_action, }; diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index f829f5289e16..e5631e88b285 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -165,6 +165,16 @@ static int nft_jhash_dump(struct sk_buff *skb, return -1; } +static bool nft_jhash_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + const struct nft_jhash *priv = nft_expr_priv(expr); + + nft_reg_track_cancel(track, priv->dreg, sizeof(u32)); + + return false; +} + static int nft_symhash_dump(struct sk_buff *skb, const struct nft_expr *expr) { @@ -185,6 +195,30 @@ static int nft_symhash_dump(struct sk_buff *skb, return -1; } +static bool nft_symhash_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + struct nft_symhash *priv = nft_expr_priv(expr); + struct nft_symhash *symhash; + + if (!nft_reg_track_cmp(track, expr, priv->dreg)) { + nft_reg_track_update(track, expr, priv->dreg, sizeof(u32)); + return false; + } + + symhash = nft_expr_priv(track->regs[priv->dreg].selector); + if (priv->offset != symhash->offset || + priv->modulus != symhash->modulus) { + nft_reg_track_update(track, expr, priv->dreg, sizeof(u32)); + return false; + } + + if (!track->regs[priv->dreg].bitwise) + return true; + + return false; +} + static struct nft_expr_type nft_hash_type; static const struct nft_expr_ops nft_jhash_ops = { .type = &nft_hash_type, @@ -192,6 +226,7 @@ static const struct nft_expr_ops nft_jhash_ops = { .eval = nft_jhash_eval, .init = nft_jhash_init, .dump = nft_jhash_dump, + .reduce = nft_jhash_reduce, }; static const struct nft_expr_ops nft_symhash_ops = { @@ -200,6 +235,7 @@ static const struct nft_expr_ops nft_symhash_ops = { .eval = nft_symhash_eval, .init = nft_symhash_init, .dump = nft_symhash_dump, + .reduce = nft_symhash_reduce, }; static const struct nft_expr_ops * diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index d0f67d325bdf..b80f7b507349 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -223,6 +223,17 @@ static bool nft_immediate_offload_action(const struct nft_expr *expr) return false; } +static bool nft_immediate_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + const struct nft_immediate_expr *priv = nft_expr_priv(expr); + + if (priv->dreg != NFT_REG_VERDICT) + nft_reg_track_cancel(track, priv->dreg, priv->dlen); + + return false; +} + static const struct nft_expr_ops nft_imm_ops = { .type = &nft_imm_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)), @@ -233,6 +244,7 @@ static const struct nft_expr_ops nft_imm_ops = { .destroy = nft_immediate_destroy, .dump = nft_immediate_dump, .validate = nft_immediate_validate, + .reduce = nft_immediate_reduce, .offload = nft_immediate_offload, .offload_action = nft_immediate_offload_action, }; diff --git a/net/netfilter/nft_last.c b/net/netfilter/nft_last.c index 4f745a409d34..43d0d4aadb1f 100644 --- a/net/netfilter/nft_last.c +++ b/net/netfilter/nft_last.c @@ -120,6 +120,7 @@ static const struct nft_expr_ops nft_last_ops = { .destroy = nft_last_destroy, .clone = nft_last_clone, .dump = nft_last_dump, + .reduce = NFT_REDUCE_READONLY, }; struct nft_expr_type nft_last_type __read_mostly = { diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c index a726b623963d..d4a6cf3cd697 100644 --- a/net/netfilter/nft_limit.c +++ b/net/netfilter/nft_limit.c @@ -225,6 +225,7 @@ static const struct nft_expr_ops nft_limit_pkts_ops = { .destroy = nft_limit_pkts_destroy, .clone = nft_limit_pkts_clone, .dump = nft_limit_pkts_dump, + .reduce = NFT_REDUCE_READONLY, }; static void nft_limit_bytes_eval(const struct nft_expr *expr, @@ -279,6 +280,7 @@ static const struct nft_expr_ops nft_limit_bytes_ops = { .dump = nft_limit_bytes_dump, .clone = nft_limit_bytes_clone, .destroy = nft_limit_bytes_destroy, + .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops * diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c index 54f6c2035e84..0e13c003f0c1 100644 --- a/net/netfilter/nft_log.c +++ b/net/netfilter/nft_log.c @@ -290,6 +290,7 @@ static const struct nft_expr_ops nft_log_ops = { .init = nft_log_init, .destroy = nft_log_destroy, .dump = nft_log_dump, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_log_type __read_mostly = { diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index 90becbf5bff3..dfae12759c7c 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -253,6 +253,17 @@ static int nft_lookup_validate(const struct nft_ctx *ctx, return 0; } +static bool nft_lookup_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + const struct nft_lookup *priv = nft_expr_priv(expr); + + if (priv->set->flags & NFT_SET_MAP) + nft_reg_track_cancel(track, priv->dreg, priv->set->dlen); + + return false; +} + static const struct nft_expr_ops nft_lookup_ops = { .type = &nft_lookup_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)), @@ -263,6 +274,7 @@ static const struct nft_expr_ops nft_lookup_ops = { .destroy = nft_lookup_destroy, .dump = nft_lookup_dump, .validate = nft_lookup_validate, + .reduce = nft_lookup_reduce, }; struct nft_expr_type nft_lookup_type __read_mostly = { diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c index 9953e8053753..2a0adc497bbb 100644 --- a/net/netfilter/nft_masq.c +++ b/net/netfilter/nft_masq.c @@ -129,6 +129,7 @@ static const struct nft_expr_ops nft_masq_ipv4_ops = { .destroy = nft_masq_ipv4_destroy, .dump = nft_masq_dump, .validate = nft_masq_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_masq_ipv4_type __read_mostly = { @@ -175,6 +176,7 @@ static const struct nft_expr_ops nft_masq_ipv6_ops = { .destroy = nft_masq_ipv6_destroy, .dump = nft_masq_dump, .validate = nft_masq_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_masq_ipv6_type __read_mostly = { @@ -230,6 +232,7 @@ static const struct nft_expr_ops nft_masq_inet_ops = { .destroy = nft_masq_inet_destroy, .dump = nft_masq_dump, .validate = nft_masq_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_masq_inet_type __read_mostly = { diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 5ab4df56c945..ac4859241e17 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -539,6 +539,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx, return -EOPNOTSUPP; } + priv->len = len; return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, len); } @@ -664,6 +665,7 @@ int nft_meta_set_init(const struct nft_ctx *ctx, return -EOPNOTSUPP; } + priv->len = len; err = nft_parse_register_load(tb[NFTA_META_SREG], &priv->sreg, len); if (err < 0) return err; @@ -750,24 +752,21 @@ static int nft_meta_get_offload(struct nft_offload_ctx *ctx, return 0; } -static bool nft_meta_get_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) +bool nft_meta_get_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) { const struct nft_meta *priv = nft_expr_priv(expr); const struct nft_meta *meta; - if (!track->regs[priv->dreg].selector || - track->regs[priv->dreg].selector->ops != expr->ops) { - track->regs[priv->dreg].selector = expr; - track->regs[priv->dreg].bitwise = NULL; + if (!nft_reg_track_cmp(track, expr, priv->dreg)) { + nft_reg_track_update(track, expr, priv->dreg, priv->len); return false; } meta = nft_expr_priv(track->regs[priv->dreg].selector); if (priv->key != meta->key || priv->dreg != meta->dreg) { - track->regs[priv->dreg].selector = expr; - track->regs[priv->dreg].bitwise = NULL; + nft_reg_track_update(track, expr, priv->dreg, priv->len); return false; } @@ -776,6 +775,7 @@ static bool nft_meta_get_reduce(struct nft_regs_track *track, return nft_expr_reduce_bitwise(track, expr); } +EXPORT_SYMBOL_GPL(nft_meta_get_reduce); static const struct nft_expr_ops nft_meta_get_ops = { .type = &nft_meta_type, @@ -800,8 +800,7 @@ static bool nft_meta_set_reduce(struct nft_regs_track *track, if (track->regs[i].selector->ops != &nft_meta_get_ops) continue; - track->regs[i].selector = NULL; - track->regs[i].bitwise = NULL; + __nft_reg_track_cancel(track, i); } return false; diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c index be1595d6979d..4394df4bc99b 100644 --- a/net/netfilter/nft_nat.c +++ b/net/netfilter/nft_nat.c @@ -317,6 +317,7 @@ static const struct nft_expr_ops nft_nat_ops = { .destroy = nft_nat_destroy, .dump = nft_nat_dump, .validate = nft_nat_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_nat_type __read_mostly = { @@ -346,6 +347,7 @@ static const struct nft_expr_ops nft_nat_inet_ops = { .destroy = nft_nat_destroy, .dump = nft_nat_dump, .validate = nft_nat_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_inet_nat_type __read_mostly = { diff --git a/net/netfilter/nft_numgen.c b/net/netfilter/nft_numgen.c index 1d378efd8823..81b40c663d86 100644 --- a/net/netfilter/nft_numgen.c +++ b/net/netfilter/nft_numgen.c @@ -85,6 +85,16 @@ static int nft_ng_inc_init(const struct nft_ctx *ctx, return err; } +static bool nft_ng_inc_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + const struct nft_ng_inc *priv = nft_expr_priv(expr); + + nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE); + + return false; +} + static int nft_ng_dump(struct sk_buff *skb, enum nft_registers dreg, u32 modulus, enum nft_ng_types type, u32 offset) { @@ -172,6 +182,16 @@ static int nft_ng_random_dump(struct sk_buff *skb, const struct nft_expr *expr) priv->offset); } +static bool nft_ng_random_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + const struct nft_ng_random *priv = nft_expr_priv(expr); + + nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE); + + return false; +} + static struct nft_expr_type nft_ng_type; static const struct nft_expr_ops nft_ng_inc_ops = { .type = &nft_ng_type, @@ -180,6 +200,7 @@ static const struct nft_expr_ops nft_ng_inc_ops = { .init = nft_ng_inc_init, .destroy = nft_ng_inc_destroy, .dump = nft_ng_inc_dump, + .reduce = nft_ng_inc_reduce, }; static const struct nft_expr_ops nft_ng_random_ops = { @@ -188,6 +209,7 @@ static const struct nft_expr_ops nft_ng_random_ops = { .eval = nft_ng_random_eval, .init = nft_ng_random_init, .dump = nft_ng_random_dump, + .reduce = nft_ng_random_reduce, }; static const struct nft_expr_ops * diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c index 94b2327e71dc..5d8d91b3904d 100644 --- a/net/netfilter/nft_objref.c +++ b/net/netfilter/nft_objref.c @@ -91,6 +91,7 @@ static const struct nft_expr_ops nft_objref_ops = { .activate = nft_objref_activate, .deactivate = nft_objref_deactivate, .dump = nft_objref_dump, + .reduce = NFT_REDUCE_READONLY, }; struct nft_objref_map { @@ -204,6 +205,7 @@ static const struct nft_expr_ops nft_objref_map_ops = { .deactivate = nft_objref_map_deactivate, .destroy = nft_objref_map_destroy, .dump = nft_objref_map_dump, + .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops * diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c index d82677e83400..5eed18f90b02 100644 --- a/net/netfilter/nft_osf.c +++ b/net/netfilter/nft_osf.c @@ -120,6 +120,30 @@ static int nft_osf_validate(const struct nft_ctx *ctx, (1 << NF_INET_FORWARD)); } +static bool nft_osf_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + struct nft_osf *priv = nft_expr_priv(expr); + struct nft_osf *osf; + + if (!nft_reg_track_cmp(track, expr, priv->dreg)) { + nft_reg_track_update(track, expr, priv->dreg, NFT_OSF_MAXGENRELEN); + return false; + } + + osf = nft_expr_priv(track->regs[priv->dreg].selector); + if (priv->flags != osf->flags || + priv->ttl != osf->ttl) { + nft_reg_track_update(track, expr, priv->dreg, NFT_OSF_MAXGENRELEN); + return false; + } + + if (!track->regs[priv->dreg].bitwise) + return true; + + return false; +} + static struct nft_expr_type nft_osf_type; static const struct nft_expr_ops nft_osf_op = { .eval = nft_osf_eval, @@ -128,6 +152,7 @@ static const struct nft_expr_ops nft_osf_op = { .dump = nft_osf_dump, .type = &nft_osf_type, .validate = nft_osf_validate, + .reduce = nft_osf_reduce, }; static struct nft_expr_type nft_osf_type __read_mostly = { diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 5cc06aef4345..2e7ac007cb30 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -216,10 +216,8 @@ static bool nft_payload_reduce(struct nft_regs_track *track, const struct nft_payload *priv = nft_expr_priv(expr); const struct nft_payload *payload; - if (!track->regs[priv->dreg].selector || - track->regs[priv->dreg].selector->ops != expr->ops) { - track->regs[priv->dreg].selector = expr; - track->regs[priv->dreg].bitwise = NULL; + if (!nft_reg_track_cmp(track, expr, priv->dreg)) { + nft_reg_track_update(track, expr, priv->dreg, priv->len); return false; } @@ -227,8 +225,7 @@ static bool nft_payload_reduce(struct nft_regs_track *track, if (priv->base != payload->base || priv->offset != payload->offset || priv->len != payload->len) { - track->regs[priv->dreg].selector = expr; - track->regs[priv->dreg].bitwise = NULL; + nft_reg_track_update(track, expr, priv->dreg, priv->len); return false; } @@ -815,8 +812,7 @@ static bool nft_payload_set_reduce(struct nft_regs_track *track, track->regs[i].selector->ops != &nft_payload_fast_ops) continue; - track->regs[i].selector = NULL; - track->regs[i].bitwise = NULL; + __nft_reg_track_cancel(track, i); } return false; diff --git a/net/netfilter/nft_queue.c b/net/netfilter/nft_queue.c index 9ba1de51ac07..15e4b7640dc0 100644 --- a/net/netfilter/nft_queue.c +++ b/net/netfilter/nft_queue.c @@ -164,6 +164,7 @@ static const struct nft_expr_ops nft_queue_ops = { .eval = nft_queue_eval, .init = nft_queue_init, .dump = nft_queue_dump, + .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops nft_queue_sreg_ops = { @@ -172,6 +173,7 @@ static const struct nft_expr_ops nft_queue_sreg_ops = { .eval = nft_queue_sreg_eval, .init = nft_queue_sreg_init, .dump = nft_queue_sreg_dump, + .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops * diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index f394a0b562f6..d7db57ed3bc1 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -254,6 +254,7 @@ static const struct nft_expr_ops nft_quota_ops = { .destroy = nft_quota_destroy, .clone = nft_quota_clone, .dump = nft_quota_dump, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_quota_type __read_mostly = { diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c index e4a1c44d7f51..66f77484c227 100644 --- a/net/netfilter/nft_range.c +++ b/net/netfilter/nft_range.c @@ -140,6 +140,7 @@ static const struct nft_expr_ops nft_range_ops = { .eval = nft_range_eval, .init = nft_range_init, .dump = nft_range_dump, + .reduce = NFT_REDUCE_READONLY, }; struct nft_expr_type nft_range_type __read_mostly = { diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c index ba09890dddb5..5086adfe731c 100644 --- a/net/netfilter/nft_redir.c +++ b/net/netfilter/nft_redir.c @@ -134,6 +134,7 @@ static const struct nft_expr_ops nft_redir_ipv4_ops = { .destroy = nft_redir_ipv4_destroy, .dump = nft_redir_dump, .validate = nft_redir_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_redir_ipv4_type __read_mostly = { @@ -183,6 +184,7 @@ static const struct nft_expr_ops nft_redir_ipv6_ops = { .destroy = nft_redir_ipv6_destroy, .dump = nft_redir_dump, .validate = nft_redir_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_redir_ipv6_type __read_mostly = { @@ -225,6 +227,7 @@ static const struct nft_expr_ops nft_redir_inet_ops = { .destroy = nft_redir_inet_destroy, .dump = nft_redir_dump, .validate = nft_redir_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_redir_inet_type __read_mostly = { diff --git a/net/netfilter/nft_reject_inet.c b/net/netfilter/nft_reject_inet.c index 554caf967baa..973fa31a9dd6 100644 --- a/net/netfilter/nft_reject_inet.c +++ b/net/netfilter/nft_reject_inet.c @@ -80,6 +80,7 @@ static const struct nft_expr_ops nft_reject_inet_ops = { .init = nft_reject_init, .dump = nft_reject_dump, .validate = nft_reject_inet_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_reject_inet_type __read_mostly = { diff --git a/net/netfilter/nft_reject_netdev.c b/net/netfilter/nft_reject_netdev.c index 61cd8c4ac385..7865cd8b11bb 100644 --- a/net/netfilter/nft_reject_netdev.c +++ b/net/netfilter/nft_reject_netdev.c @@ -159,6 +159,7 @@ static const struct nft_expr_ops nft_reject_netdev_ops = { .init = nft_reject_init, .dump = nft_reject_dump, .validate = nft_reject_netdev_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_reject_netdev_type __read_mostly = { diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c index bcd01a63e38f..71931ec91721 100644 --- a/net/netfilter/nft_rt.c +++ b/net/netfilter/nft_rt.c @@ -191,6 +191,7 @@ static const struct nft_expr_ops nft_rt_get_ops = { .init = nft_rt_get_init, .dump = nft_rt_get_dump, .validate = nft_rt_validate, + .reduce = NFT_REDUCE_READONLY, }; struct nft_expr_type nft_rt_type __read_mostly = { diff --git a/net/netfilter/nft_socket.c b/net/netfilter/nft_socket.c index d601974c9d2e..bd3792f080ed 100644 --- a/net/netfilter/nft_socket.c +++ b/net/netfilter/nft_socket.c @@ -10,6 +10,7 @@ struct nft_socket { enum nft_socket_keys key:8; u8 level; + u8 len; union { u8 dreg; }; @@ -179,6 +180,7 @@ static int nft_socket_init(const struct nft_ctx *ctx, return -EOPNOTSUPP; } + priv->len = len; return nft_parse_register_store(ctx, tb[NFTA_SOCKET_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, len); } @@ -198,6 +200,31 @@ static int nft_socket_dump(struct sk_buff *skb, return 0; } +static bool nft_socket_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + const struct nft_socket *priv = nft_expr_priv(expr); + const struct nft_socket *socket; + + if (!nft_reg_track_cmp(track, expr, priv->dreg)) { + nft_reg_track_update(track, expr, priv->dreg, priv->len); + return false; + } + + socket = nft_expr_priv(track->regs[priv->dreg].selector); + if (priv->key != socket->key || + priv->dreg != socket->dreg || + priv->level != socket->level) { + nft_reg_track_update(track, expr, priv->dreg, priv->len); + return false; + } + + if (!track->regs[priv->dreg].bitwise) + return true; + + return nft_expr_reduce_bitwise(track, expr); +} + static struct nft_expr_type nft_socket_type; static const struct nft_expr_ops nft_socket_ops = { .type = &nft_socket_type, @@ -205,6 +232,7 @@ static const struct nft_expr_ops nft_socket_ops = { .eval = nft_socket_eval, .init = nft_socket_init, .dump = nft_socket_dump, + .reduce = nft_socket_reduce, }; static struct nft_expr_type nft_socket_type __read_mostly = { diff --git a/net/netfilter/nft_synproxy.c b/net/netfilter/nft_synproxy.c index 1133e06f3c40..6cf9a04fbfe2 100644 --- a/net/netfilter/nft_synproxy.c +++ b/net/netfilter/nft_synproxy.c @@ -288,6 +288,7 @@ static const struct nft_expr_ops nft_synproxy_ops = { .dump = nft_synproxy_dump, .type = &nft_synproxy_type, .validate = nft_synproxy_validate, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_synproxy_type __read_mostly = { diff --git a/net/netfilter/nft_tproxy.c b/net/netfilter/nft_tproxy.c index b5b09a902c7a..801f013971df 100644 --- a/net/netfilter/nft_tproxy.c +++ b/net/netfilter/nft_tproxy.c @@ -320,6 +320,7 @@ static const struct nft_expr_ops nft_tproxy_ops = { .init = nft_tproxy_init, .destroy = nft_tproxy_destroy, .dump = nft_tproxy_dump, + .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_tproxy_type __read_mostly = { diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index 3b27926d5382..d0f9b1d51b0e 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -17,6 +17,7 @@ struct nft_tunnel { enum nft_tunnel_keys key:8; u8 dreg; enum nft_tunnel_mode mode:8; + u8 len; }; static void nft_tunnel_get_eval(const struct nft_expr *expr, @@ -101,6 +102,7 @@ static int nft_tunnel_get_init(const struct nft_ctx *ctx, priv->mode = NFT_TUNNEL_MODE_NONE; } + priv->len = len; return nft_parse_register_store(ctx, tb[NFTA_TUNNEL_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, len); } @@ -122,6 +124,31 @@ static int nft_tunnel_get_dump(struct sk_buff *skb, return -1; } +static bool nft_tunnel_get_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + const struct nft_tunnel *priv = nft_expr_priv(expr); + const struct nft_tunnel *tunnel; + + if (!nft_reg_track_cmp(track, expr, priv->dreg)) { + nft_reg_track_update(track, expr, priv->dreg, priv->len); + return false; + } + + tunnel = nft_expr_priv(track->regs[priv->dreg].selector); + if (priv->key != tunnel->key || + priv->dreg != tunnel->dreg || + priv->mode != tunnel->mode) { + nft_reg_track_update(track, expr, priv->dreg, priv->len); + return false; + } + + if (!track->regs[priv->dreg].bitwise) + return true; + + return false; +} + static struct nft_expr_type nft_tunnel_type; static const struct nft_expr_ops nft_tunnel_get_ops = { .type = &nft_tunnel_type, @@ -129,6 +156,7 @@ static const struct nft_expr_ops nft_tunnel_get_ops = { .eval = nft_tunnel_get_eval, .init = nft_tunnel_get_init, .dump = nft_tunnel_get_dump, + .reduce = nft_tunnel_get_reduce, }; static struct nft_expr_type nft_tunnel_type __read_mostly = { diff --git a/net/netfilter/nft_xfrm.c b/net/netfilter/nft_xfrm.c index cbbbc4ecad3a..becb88fa4e9b 100644 --- a/net/netfilter/nft_xfrm.c +++ b/net/netfilter/nft_xfrm.c @@ -27,6 +27,7 @@ struct nft_xfrm { u8 dreg; u8 dir; u8 spnum; + u8 len; }; static int nft_xfrm_get_init(const struct nft_ctx *ctx, @@ -86,6 +87,7 @@ static int nft_xfrm_get_init(const struct nft_ctx *ctx, priv->spnum = spnum; + priv->len = len; return nft_parse_register_store(ctx, tb[NFTA_XFRM_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, len); } @@ -252,6 +254,31 @@ static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *e return nft_chain_validate_hooks(ctx->chain, hooks); } +static bool nft_xfrm_reduce(struct nft_regs_track *track, + const struct nft_expr *expr) +{ + const struct nft_xfrm *priv = nft_expr_priv(expr); + const struct nft_xfrm *xfrm; + + if (!nft_reg_track_cmp(track, expr, priv->dreg)) { + nft_reg_track_update(track, expr, priv->dreg, priv->len); + return false; + } + + xfrm = nft_expr_priv(track->regs[priv->dreg].selector); + if (priv->key != xfrm->key || + priv->dreg != xfrm->dreg || + priv->dir != xfrm->dir || + priv->spnum != xfrm->spnum) { + nft_reg_track_update(track, expr, priv->dreg, priv->len); + return false; + } + + if (!track->regs[priv->dreg].bitwise) + return true; + + return nft_expr_reduce_bitwise(track, expr); +} static struct nft_expr_type nft_xfrm_type; static const struct nft_expr_ops nft_xfrm_get_ops = { @@ -261,6 +288,7 @@ static const struct nft_expr_ops nft_xfrm_get_ops = { .init = nft_xfrm_get_init, .dump = nft_xfrm_get_dump, .validate = nft_xfrm_validate, + .reduce = nft_xfrm_reduce, }; static struct nft_expr_type nft_xfrm_type __read_mostly = { diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index beb0e573266d..54c083003947 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -885,6 +885,8 @@ int netlbl_bitmap_walk(const unsigned char *bitmap, u32 bitmap_len, unsigned char bitmask; unsigned char byte; + if (offset >= bitmap_len) + return -1; byte_offset = offset / 8; byte = bitmap[byte_offset]; bit_spot = offset; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 7b344035bfe3..47a876ccd288 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -159,6 +159,8 @@ EXPORT_SYMBOL(do_trace_netlink_extack); static inline u32 netlink_group_mask(u32 group) { + if (group > 32) + return 0; return group ? 1 << (group - 1) : 0; } diff --git a/net/nfc/llcp.h b/net/nfc/llcp.h index d49d4bf2e37c..c1d9be636933 100644 --- a/net/nfc/llcp.h +++ b/net/nfc/llcp.h @@ -6,7 +6,6 @@ enum llcp_state { LLCP_CONNECTED = 1, /* wait_for_packet() wants that */ LLCP_CONNECTING, - LLCP_DISCONNECTING, LLCP_CLOSED, LLCP_BOUND, LLCP_LISTEN, diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index 5ad5157aa9c5..3364caabef8b 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -383,7 +383,7 @@ u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local, pr_debug("WKS %d\n", ssap); /* This is a WKS, let's check if it's free */ - if (local->local_wks & BIT(ssap)) { + if (test_bit(ssap, &local->local_wks)) { mutex_unlock(&local->sdp_lock); return LLCP_SAP_MAX; @@ -737,13 +737,6 @@ static void nfc_llcp_tx_work(struct work_struct *work) print_hex_dump_debug("LLCP Tx: ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb->len, true); - if (ptype == LLCP_PDU_DISC && sk != NULL && - sk->sk_state == LLCP_DISCONNECTING) { - nfc_llcp_sock_unlink(&local->sockets, sk); - sock_orphan(sk); - sock_put(sk); - } - if (ptype == LLCP_PDU_I) copy_skb = skb_copy(skb, GFP_ATOMIC); diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c index 0b93a17b9f11..4ca35791c93b 100644 --- a/net/nfc/llcp_sock.c +++ b/net/nfc/llcp_sock.c @@ -108,21 +108,13 @@ static int llcp_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) llcp_sock->service_name_len, GFP_KERNEL); if (!llcp_sock->service_name) { - nfc_llcp_local_put(llcp_sock->local); - llcp_sock->local = NULL; - llcp_sock->dev = NULL; ret = -ENOMEM; - goto put_dev; + goto sock_llcp_put_local; } llcp_sock->ssap = nfc_llcp_get_sdp_ssap(local, llcp_sock); if (llcp_sock->ssap == LLCP_SAP_MAX) { - nfc_llcp_local_put(llcp_sock->local); - llcp_sock->local = NULL; - kfree(llcp_sock->service_name); - llcp_sock->service_name = NULL; - llcp_sock->dev = NULL; ret = -EADDRINUSE; - goto put_dev; + goto free_service_name; } llcp_sock->reserved_ssap = llcp_sock->ssap; @@ -132,6 +124,19 @@ static int llcp_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) pr_debug("Socket bound to SAP %d\n", llcp_sock->ssap); sk->sk_state = LLCP_BOUND; + nfc_put_device(dev); + release_sock(sk); + + return 0; + +free_service_name: + kfree(llcp_sock->service_name); + llcp_sock->service_name = NULL; + +sock_llcp_put_local: + nfc_llcp_local_put(llcp_sock->local); + llcp_sock->local = NULL; + llcp_sock->dev = NULL; put_dev: nfc_put_device(dev); @@ -626,23 +631,16 @@ static int llcp_sock_release(struct socket *sock) } } - if (llcp_sock->reserved_ssap < LLCP_SAP_MAX) - nfc_llcp_put_ssap(llcp_sock->local, llcp_sock->ssap); - - release_sock(sk); - - /* Keep this sock alive and therefore do not remove it from the sockets - * list until the DISC PDU has been actually sent. Otherwise we would - * reply with DM PDUs before sending the DISC one. - */ - if (sk->sk_state == LLCP_DISCONNECTING) - return err; - if (sock->type == SOCK_RAW) nfc_llcp_sock_unlink(&local->raw_sockets, sk); else nfc_llcp_sock_unlink(&local->sockets, sk); + if (llcp_sock->reserved_ssap < LLCP_SAP_MAX) + nfc_llcp_put_ssap(llcp_sock->local, llcp_sock->ssap); + + release_sock(sk); + out: sock_orphan(sk); sock_put(sk); @@ -712,10 +710,8 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr, llcp_sock->local = nfc_llcp_local_get(local); llcp_sock->ssap = nfc_llcp_get_local_ssap(local); if (llcp_sock->ssap == LLCP_SAP_MAX) { - nfc_llcp_local_put(llcp_sock->local); - llcp_sock->local = NULL; ret = -ENOMEM; - goto put_dev; + goto sock_llcp_put_local; } llcp_sock->reserved_ssap = llcp_sock->ssap; @@ -760,8 +756,11 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr, sock_llcp_release: nfc_llcp_put_ssap(local, llcp_sock->ssap); + +sock_llcp_put_local: nfc_llcp_local_put(llcp_sock->local); llcp_sock->local = NULL; + llcp_sock->dev = NULL; put_dev: nfc_put_device(dev); diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index c07afff57dd3..4a947c13c813 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -734,6 +734,57 @@ static bool skb_nfct_cached(struct net *net, } #if IS_ENABLED(CONFIG_NF_NAT) +static void ovs_nat_update_key(struct sw_flow_key *key, + const struct sk_buff *skb, + enum nf_nat_manip_type maniptype) +{ + if (maniptype == NF_NAT_MANIP_SRC) { + __be16 src; + + key->ct_state |= OVS_CS_F_SRC_NAT; + if (key->eth.type == htons(ETH_P_IP)) + key->ipv4.addr.src = ip_hdr(skb)->saddr; + else if (key->eth.type == htons(ETH_P_IPV6)) + memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr, + sizeof(key->ipv6.addr.src)); + else + return; + + if (key->ip.proto == IPPROTO_UDP) + src = udp_hdr(skb)->source; + else if (key->ip.proto == IPPROTO_TCP) + src = tcp_hdr(skb)->source; + else if (key->ip.proto == IPPROTO_SCTP) + src = sctp_hdr(skb)->source; + else + return; + + key->tp.src = src; + } else { + __be16 dst; + + key->ct_state |= OVS_CS_F_DST_NAT; + if (key->eth.type == htons(ETH_P_IP)) + key->ipv4.addr.dst = ip_hdr(skb)->daddr; + else if (key->eth.type == htons(ETH_P_IPV6)) + memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr, + sizeof(key->ipv6.addr.dst)); + else + return; + + if (key->ip.proto == IPPROTO_UDP) + dst = udp_hdr(skb)->dest; + else if (key->ip.proto == IPPROTO_TCP) + dst = tcp_hdr(skb)->dest; + else if (key->ip.proto == IPPROTO_SCTP) + dst = sctp_hdr(skb)->dest; + else + return; + + key->tp.dst = dst; + } +} + /* Modelled after nf_nat_ipv[46]_fn(). * range is only used for new, uninitialized NAT state. * Returns either NF_ACCEPT or NF_DROP. @@ -741,7 +792,7 @@ static bool skb_nfct_cached(struct net *net, static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, const struct nf_nat_range2 *range, - enum nf_nat_manip_type maniptype) + enum nf_nat_manip_type maniptype, struct sw_flow_key *key) { int hooknum, nh_off, err = NF_ACCEPT; @@ -813,60 +864,13 @@ static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, push: skb_push_rcsum(skb, nh_off); + /* Update the flow key if NAT successful. */ + if (err == NF_ACCEPT) + ovs_nat_update_key(key, skb, maniptype); + return err; } -static void ovs_nat_update_key(struct sw_flow_key *key, - const struct sk_buff *skb, - enum nf_nat_manip_type maniptype) -{ - if (maniptype == NF_NAT_MANIP_SRC) { - __be16 src; - - key->ct_state |= OVS_CS_F_SRC_NAT; - if (key->eth.type == htons(ETH_P_IP)) - key->ipv4.addr.src = ip_hdr(skb)->saddr; - else if (key->eth.type == htons(ETH_P_IPV6)) - memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr, - sizeof(key->ipv6.addr.src)); - else - return; - - if (key->ip.proto == IPPROTO_UDP) - src = udp_hdr(skb)->source; - else if (key->ip.proto == IPPROTO_TCP) - src = tcp_hdr(skb)->source; - else if (key->ip.proto == IPPROTO_SCTP) - src = sctp_hdr(skb)->source; - else - return; - - key->tp.src = src; - } else { - __be16 dst; - - key->ct_state |= OVS_CS_F_DST_NAT; - if (key->eth.type == htons(ETH_P_IP)) - key->ipv4.addr.dst = ip_hdr(skb)->daddr; - else if (key->eth.type == htons(ETH_P_IPV6)) - memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr, - sizeof(key->ipv6.addr.dst)); - else - return; - - if (key->ip.proto == IPPROTO_UDP) - dst = udp_hdr(skb)->dest; - else if (key->ip.proto == IPPROTO_TCP) - dst = tcp_hdr(skb)->dest; - else if (key->ip.proto == IPPROTO_SCTP) - dst = sctp_hdr(skb)->dest; - else - return; - - key->tp.dst = dst; - } -} - /* Returns NF_DROP if the packet should be dropped, NF_ACCEPT otherwise. */ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, const struct ovs_conntrack_info *info, @@ -906,7 +910,7 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, } else { return NF_ACCEPT; /* Connection is not NATed. */ } - err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype); + err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype, key); if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) { if (ct->status & IPS_SRC_NAT) { @@ -916,17 +920,13 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, maniptype = NF_NAT_MANIP_SRC; err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, - maniptype); + maniptype, key); } else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { err = ovs_ct_nat_execute(skb, ct, ctinfo, NULL, - NF_NAT_MANIP_SRC); + NF_NAT_MANIP_SRC, key); } } - /* Mark NAT done if successful and update the flow key. */ - if (err == NF_ACCEPT) - ovs_nat_update_key(key, skb, maniptype); - return err; } #else /* !CONFIG_NF_NAT */ diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 67ad08320886..7e8a39a35627 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "datapath.h" #include "flow.h" @@ -1601,8 +1602,6 @@ static void ovs_dp_reset_user_features(struct sk_buff *skb, dp->user_features = 0; } -DEFINE_STATIC_KEY_FALSE(tc_recirc_sharing_support); - static int ovs_dp_set_upcall_portids(struct datapath *dp, const struct nlattr *ids) { @@ -1657,7 +1656,7 @@ u32 ovs_dp_get_upcall_portid(const struct datapath *dp, uint32_t cpu_id) static int ovs_dp_change(struct datapath *dp, struct nlattr *a[]) { - u32 user_features = 0; + u32 user_features = 0, old_features = dp->user_features; int err; if (a[OVS_DP_ATTR_USER_FEATURES]) { @@ -1696,10 +1695,12 @@ static int ovs_dp_change(struct datapath *dp, struct nlattr *a[]) return err; } - if (dp->user_features & OVS_DP_F_TC_RECIRC_SHARING) - static_branch_enable(&tc_recirc_sharing_support); - else - static_branch_disable(&tc_recirc_sharing_support); + if ((dp->user_features & OVS_DP_F_TC_RECIRC_SHARING) && + !(old_features & OVS_DP_F_TC_RECIRC_SHARING)) + tc_skb_ext_tc_enable(); + else if (!(dp->user_features & OVS_DP_F_TC_RECIRC_SHARING) && + (old_features & OVS_DP_F_TC_RECIRC_SHARING)) + tc_skb_ext_tc_disable(); return 0; } @@ -1839,6 +1840,9 @@ static void __dp_destroy(struct datapath *dp) struct flow_table *table = &dp->table; int i; + if (dp->user_features & OVS_DP_F_TC_RECIRC_SHARING) + tc_skb_ext_tc_disable(); + for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { struct vport *vport; struct hlist_node *n; diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index fcfe6cb46441..0cd29971a907 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -253,8 +253,6 @@ static inline struct datapath *get_dp(struct net *net, int dp_ifindex) extern struct notifier_block ovs_dp_device_notifier; extern struct genl_family dp_vport_genl_family; -DECLARE_STATIC_KEY_FALSE(tc_recirc_sharing_support); - void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key); void ovs_dp_detach_port(struct vport *); int ovs_dp_upcall(struct datapath *, struct sk_buff *, diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 02096f2ec678..372bf54a0ca9 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "conntrack.h" @@ -240,6 +241,144 @@ static bool icmphdr_ok(struct sk_buff *skb) sizeof(struct icmphdr)); } +/** + * get_ipv6_ext_hdrs() - Parses packet and sets IPv6 extension header flags. + * + * @skb: buffer where extension header data starts in packet + * @nh: ipv6 header + * @ext_hdrs: flags are stored here + * + * OFPIEH12_UNREP is set if more than one of a given IPv6 extension header + * is unexpectedly encountered. (Two destination options headers may be + * expected and would not cause this bit to be set.) + * + * OFPIEH12_UNSEQ is set if IPv6 extension headers were not in the order + * preferred (but not required) by RFC 2460: + * + * When more than one extension header is used in the same packet, it is + * recommended that those headers appear in the following order: + * IPv6 header + * Hop-by-Hop Options header + * Destination Options header + * Routing header + * Fragment header + * Authentication header + * Encapsulating Security Payload header + * Destination Options header + * upper-layer header + */ +static void get_ipv6_ext_hdrs(struct sk_buff *skb, struct ipv6hdr *nh, + u16 *ext_hdrs) +{ + u8 next_type = nh->nexthdr; + unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); + int dest_options_header_count = 0; + + *ext_hdrs = 0; + + while (ipv6_ext_hdr(next_type)) { + struct ipv6_opt_hdr _hdr, *hp; + + switch (next_type) { + case IPPROTO_NONE: + *ext_hdrs |= OFPIEH12_NONEXT; + /* stop parsing */ + return; + + case IPPROTO_ESP: + if (*ext_hdrs & OFPIEH12_ESP) + *ext_hdrs |= OFPIEH12_UNREP; + if ((*ext_hdrs & ~(OFPIEH12_HOP | OFPIEH12_DEST | + OFPIEH12_ROUTER | IPPROTO_FRAGMENT | + OFPIEH12_AUTH | OFPIEH12_UNREP)) || + dest_options_header_count >= 2) { + *ext_hdrs |= OFPIEH12_UNSEQ; + } + *ext_hdrs |= OFPIEH12_ESP; + break; + + case IPPROTO_AH: + if (*ext_hdrs & OFPIEH12_AUTH) + *ext_hdrs |= OFPIEH12_UNREP; + if ((*ext_hdrs & + ~(OFPIEH12_HOP | OFPIEH12_DEST | OFPIEH12_ROUTER | + IPPROTO_FRAGMENT | OFPIEH12_UNREP)) || + dest_options_header_count >= 2) { + *ext_hdrs |= OFPIEH12_UNSEQ; + } + *ext_hdrs |= OFPIEH12_AUTH; + break; + + case IPPROTO_DSTOPTS: + if (dest_options_header_count == 0) { + if (*ext_hdrs & + ~(OFPIEH12_HOP | OFPIEH12_UNREP)) + *ext_hdrs |= OFPIEH12_UNSEQ; + *ext_hdrs |= OFPIEH12_DEST; + } else if (dest_options_header_count == 1) { + if (*ext_hdrs & + ~(OFPIEH12_HOP | OFPIEH12_DEST | + OFPIEH12_ROUTER | OFPIEH12_FRAG | + OFPIEH12_AUTH | OFPIEH12_ESP | + OFPIEH12_UNREP)) { + *ext_hdrs |= OFPIEH12_UNSEQ; + } + } else { + *ext_hdrs |= OFPIEH12_UNREP; + } + dest_options_header_count++; + break; + + case IPPROTO_FRAGMENT: + if (*ext_hdrs & OFPIEH12_FRAG) + *ext_hdrs |= OFPIEH12_UNREP; + if ((*ext_hdrs & ~(OFPIEH12_HOP | + OFPIEH12_DEST | + OFPIEH12_ROUTER | + OFPIEH12_UNREP)) || + dest_options_header_count >= 2) { + *ext_hdrs |= OFPIEH12_UNSEQ; + } + *ext_hdrs |= OFPIEH12_FRAG; + break; + + case IPPROTO_ROUTING: + if (*ext_hdrs & OFPIEH12_ROUTER) + *ext_hdrs |= OFPIEH12_UNREP; + if ((*ext_hdrs & ~(OFPIEH12_HOP | + OFPIEH12_DEST | + OFPIEH12_UNREP)) || + dest_options_header_count >= 2) { + *ext_hdrs |= OFPIEH12_UNSEQ; + } + *ext_hdrs |= OFPIEH12_ROUTER; + break; + + case IPPROTO_HOPOPTS: + if (*ext_hdrs & OFPIEH12_HOP) + *ext_hdrs |= OFPIEH12_UNREP; + /* OFPIEH12_HOP is set to 1 if a hop-by-hop IPv6 + * extension header is present as the first + * extension header in the packet. + */ + if (*ext_hdrs == 0) + *ext_hdrs |= OFPIEH12_HOP; + else + *ext_hdrs |= OFPIEH12_UNSEQ; + break; + + default: + return; + } + + hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); + if (!hp) + break; + next_type = hp->nexthdr; + start += ipv6_optlen(hp); + } +} + static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key) { unsigned short frag_off; @@ -255,6 +394,8 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key) nh = ipv6_hdr(skb); + get_ipv6_ext_hdrs(skb, nh, &key->ipv6.exthdrs); + key->ip.proto = NEXTHDR_NONE; key->ip.tos = ipv6_get_dsfield(nh); key->ip.ttl = nh->hop_limit; @@ -895,7 +1036,7 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info, key->mac_proto = res; #if IS_ENABLED(CONFIG_NET_TC_SKB_EXT) - if (static_branch_unlikely(&tc_recirc_sharing_support)) { + if (tc_skb_ext_tc_enabled()) { tc_ext = skb_ext_find(skb, TC_SKB_EXT); key->recirc_id = tc_ext ? tc_ext->chain : 0; OVS_CB(skb)->mru = tc_ext ? tc_ext->mru : 0; diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index 758a8c77f736..073ab73ffeaa 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -32,6 +32,19 @@ enum sw_flow_mac_proto { #define SW_FLOW_KEY_INVALID 0x80 #define MPLS_LABEL_DEPTH 3 +/* Bit definitions for IPv6 Extension Header pseudo-field. */ +enum ofp12_ipv6exthdr_flags { + OFPIEH12_NONEXT = 1 << 0, /* "No next header" encountered. */ + OFPIEH12_ESP = 1 << 1, /* Encrypted Sec Payload header present. */ + OFPIEH12_AUTH = 1 << 2, /* Authentication header present. */ + OFPIEH12_DEST = 1 << 3, /* 1 or 2 dest headers present. */ + OFPIEH12_FRAG = 1 << 4, /* Fragment header present. */ + OFPIEH12_ROUTER = 1 << 5, /* Router header present. */ + OFPIEH12_HOP = 1 << 6, /* Hop-by-hop header present. */ + OFPIEH12_UNREP = 1 << 7, /* Unexpected repeats encountered. */ + OFPIEH12_UNSEQ = 1 << 8 /* Unexpected sequencing encountered. */ +}; + /* Store options at the end of the array if they are less than the * maximum size. This allows us to get the benefits of variable length * matching for small options. @@ -121,6 +134,7 @@ struct sw_flow_key { struct in6_addr dst; /* IPv6 destination address. */ } addr; __be32 label; /* IPv6 flow label. */ + u16 exthdrs; /* IPv6 extension header flags */ union { struct { struct in6_addr src; diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index fd1f809e9bc1..5176f6ccac8e 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -346,7 +346,7 @@ size_t ovs_key_attr_size(void) /* Whenever adding new OVS_KEY_ FIELDS, we should consider * updating this function. */ - BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 29); + BUILD_BUG_ON(OVS_KEY_ATTR_MAX != 32); return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */ + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */ @@ -369,7 +369,8 @@ size_t ovs_key_attr_size(void) + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */ + nla_total_size(40) /* OVS_KEY_ATTR_IPV6 */ + nla_total_size(2) /* OVS_KEY_ATTR_ICMPV6 */ - + nla_total_size(28); /* OVS_KEY_ATTR_ND */ + + nla_total_size(28) /* OVS_KEY_ATTR_ND */ + + nla_total_size(2); /* OVS_KEY_ATTR_IPV6_EXTHDRS */ } static const struct ovs_len_tbl ovs_vxlan_ext_key_lens[OVS_VXLAN_EXT_MAX + 1] = { @@ -437,6 +438,8 @@ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { .len = sizeof(struct ovs_key_ct_tuple_ipv6) }, [OVS_KEY_ATTR_NSH] = { .len = OVS_ATTR_NESTED, .next = ovs_nsh_key_attr_lens, }, + [OVS_KEY_ATTR_IPV6_EXTHDRS] = { + .len = sizeof(struct ovs_key_ipv6_exthdrs) }, }; static bool check_attr_len(unsigned int attr_len, unsigned int expected_len) @@ -479,7 +482,14 @@ static int __parse_flow_nlattrs(const struct nlattr *attr, return -EINVAL; } - if (attrs & (1 << type)) { + if (type == OVS_KEY_ATTR_PACKET_TYPE || + type == OVS_KEY_ATTR_ND_EXTENSIONS || + type == OVS_KEY_ATTR_TUNNEL_INFO) { + OVS_NLERR(log, "Key type %d is not supported", type); + return -EINVAL; + } + + if (attrs & (1ULL << type)) { OVS_NLERR(log, "Duplicate key (type %d).", type); return -EINVAL; } @@ -492,7 +502,7 @@ static int __parse_flow_nlattrs(const struct nlattr *attr, } if (!nz || !is_all_zero(nla_data(nla), nla_len(nla))) { - attrs |= 1 << type; + attrs |= 1ULL << type; a[type] = nla; } } @@ -1597,6 +1607,17 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match, attrs &= ~(1 << OVS_KEY_ATTR_IPV6); } + if (attrs & (1ULL << OVS_KEY_ATTR_IPV6_EXTHDRS)) { + const struct ovs_key_ipv6_exthdrs *ipv6_exthdrs_key; + + ipv6_exthdrs_key = nla_data(a[OVS_KEY_ATTR_IPV6_EXTHDRS]); + + SW_FLOW_KEY_PUT(match, ipv6.exthdrs, + ipv6_exthdrs_key->hdrs, is_mask); + + attrs &= ~(1ULL << OVS_KEY_ATTR_IPV6_EXTHDRS); + } + if (attrs & (1 << OVS_KEY_ATTR_ARP)) { const struct ovs_key_arp *arp_key; @@ -2099,6 +2120,7 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey, ipv4_key->ipv4_frag = output->ip.frag; } else if (swkey->eth.type == htons(ETH_P_IPV6)) { struct ovs_key_ipv6 *ipv6_key; + struct ovs_key_ipv6_exthdrs *ipv6_exthdrs_key; nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6, sizeof(*ipv6_key)); if (!nla) @@ -2113,6 +2135,13 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey, ipv6_key->ipv6_tclass = output->ip.tos; ipv6_key->ipv6_hlimit = output->ip.ttl; ipv6_key->ipv6_frag = output->ip.frag; + + nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6_EXTHDRS, + sizeof(*ipv6_exthdrs_key)); + if (!nla) + goto nla_put_failure; + ipv6_exthdrs_key = nla_data(nla); + ipv6_exthdrs_key->hdrs = output->ipv6.exthdrs; } else if (swkey->eth.type == htons(ETH_P_NSH)) { if (nsh_key_to_nlattr(&output->nsh, is_mask, skb)) goto nla_put_failure; diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index cf2ce5812489..82a74f998966 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -507,7 +507,7 @@ void ovs_vport_send(struct vport *vport, struct sk_buff *skb, u8 mac_proto) } skb->dev = vport->dev; - skb->tstamp = 0; + skb_clear_tstamp(skb); vport->ops->send(skb); return; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index a7273af2d900..c39c09899fd0 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -460,7 +460,7 @@ static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec64 *ts, return TP_STATUS_TS_RAW_HARDWARE; if ((flags & SOF_TIMESTAMPING_SOFTWARE) && - ktime_to_timespec64_cond(skb->tstamp, ts)) + ktime_to_timespec64_cond(skb_tstamp(skb), ts)) return TP_STATUS_TS_SOFTWARE; return 0; @@ -2199,6 +2199,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, spin_lock(&sk->sk_receive_queue.lock); po->stats.stats1.tp_packets++; sock_skb_set_dropcount(sk, skb); + skb_clear_delivery_time(skb); __skb_queue_tail(&sk->sk_receive_queue, skb); spin_unlock(&sk->sk_receive_queue.lock); sk->sk_data_ready(sk); @@ -2380,6 +2381,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, po->stats.stats1.tp_packets++; if (copy_skb) { status |= TP_STATUS_COPY; + skb_clear_delivery_time(copy_skb); __skb_queue_tail(&sk->sk_receive_queue, copy_skb); } spin_unlock(&sk->sk_receive_queue.lock); diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index 65218b7ce9f9..2b582da1e88c 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -146,7 +146,7 @@ EXPORT_SYMBOL(phonet_header_ops); * Prepends an ISI header and sends a datagram. */ static int pn_send(struct sk_buff *skb, struct net_device *dev, - u16 dst, u16 src, u8 res, u8 irq) + u16 dst, u16 src, u8 res) { struct phonethdr *ph; int err; @@ -182,7 +182,7 @@ static int pn_send(struct sk_buff *skb, struct net_device *dev, if (skb->pkt_type == PACKET_LOOPBACK) { skb_reset_mac_header(skb); skb_orphan(skb); - err = (irq ? netif_rx(skb) : netif_rx_ni(skb)) ? -ENOBUFS : 0; + err = netif_rx(skb) ? -ENOBUFS : 0; } else { err = dev_hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL, skb->len); @@ -214,7 +214,7 @@ static int pn_raw_send(const void *data, int len, struct net_device *dev, skb_reserve(skb, MAX_PHONET_HEADER); __skb_put(skb, len); skb_copy_to_linear_data(skb, data, len); - return pn_send(skb, dev, dst, src, res, 1); + return pn_send(skb, dev, dst, src, res); } /* @@ -269,7 +269,7 @@ int pn_skb_send(struct sock *sk, struct sk_buff *skb, if (!pn_addr(src)) src = pn_object(saddr, pn_obj(src)); - err = pn_send(skb, dev, dst, src, res, 0); + err = pn_send(skb, dev, dst, src, res); dev_put(dev); return err; diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 5b1927d66f0d..dac4fdc7488a 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -78,6 +78,7 @@ struct rfkill_data { struct mutex mtx; wait_queue_head_t read_wait; bool input_handler; + u8 max_size; }; @@ -1153,6 +1154,8 @@ static int rfkill_fop_open(struct inode *inode, struct file *file) if (!data) return -ENOMEM; + data->max_size = RFKILL_EVENT_SIZE_V1; + INIT_LIST_HEAD(&data->events); mutex_init(&data->mtx); init_waitqueue_head(&data->read_wait); @@ -1235,6 +1238,7 @@ static ssize_t rfkill_fop_read(struct file *file, char __user *buf, list); sz = min_t(unsigned long, sizeof(ev->ev), count); + sz = min_t(unsigned long, sz, data->max_size); ret = sz; if (copy_to_user(buf, &ev->ev, sz)) ret = -EFAULT; @@ -1249,6 +1253,7 @@ static ssize_t rfkill_fop_read(struct file *file, char __user *buf, static ssize_t rfkill_fop_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { + struct rfkill_data *data = file->private_data; struct rfkill *rfkill; struct rfkill_event_ext ev; int ret; @@ -1263,6 +1268,7 @@ static ssize_t rfkill_fop_write(struct file *file, const char __user *buf, * our API version even in a write() call, if it cares. */ count = min(count, sizeof(ev)); + count = min_t(size_t, count, data->max_size); if (copy_from_user(&ev, buf, count)) return -EFAULT; @@ -1322,31 +1328,47 @@ static int rfkill_fop_release(struct inode *inode, struct file *file) return 0; } -#ifdef CONFIG_RFKILL_INPUT static long rfkill_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct rfkill_data *data = file->private_data; + int ret = -ENOSYS; + u32 size; if (_IOC_TYPE(cmd) != RFKILL_IOC_MAGIC) return -ENOSYS; - if (_IOC_NR(cmd) != RFKILL_IOC_NOINPUT) - return -ENOSYS; - mutex_lock(&data->mtx); - - if (!data->input_handler) { - if (atomic_inc_return(&rfkill_input_disabled) == 1) - printk(KERN_DEBUG "rfkill: input handler disabled\n"); - data->input_handler = true; + switch (_IOC_NR(cmd)) { +#ifdef CONFIG_RFKILL_INPUT + case RFKILL_IOC_NOINPUT: + if (!data->input_handler) { + if (atomic_inc_return(&rfkill_input_disabled) == 1) + printk(KERN_DEBUG "rfkill: input handler disabled\n"); + data->input_handler = true; + } + ret = 0; + break; +#endif + case RFKILL_IOC_MAX_SIZE: + if (get_user(size, (__u32 __user *)arg)) { + ret = -EFAULT; + break; + } + if (size < RFKILL_EVENT_SIZE_V1 || size > U8_MAX) { + ret = -EINVAL; + break; + } + data->max_size = size; + ret = 0; + break; + default: + break; } - mutex_unlock(&data->mtx); - return 0; + return ret; } -#endif static const struct file_operations rfkill_fops = { .owner = THIS_MODULE, @@ -1355,10 +1377,8 @@ static const struct file_operations rfkill_fops = { .write = rfkill_fop_write, .poll = rfkill_fop_poll, .release = rfkill_fop_release, -#ifdef CONFIG_RFKILL_INPUT .unlocked_ioctl = rfkill_fop_ioctl, .compat_ioctl = compat_ptr_ioctl, -#endif .llseek = no_llseek, }; diff --git a/net/sched/act_api.c b/net/sched/act_api.c index ca03e7284254..4f51094da9da 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -1446,6 +1446,8 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla, continue; if (skip_sw != tc_act_skip_sw(act->tcfa_flags) || skip_hw != tc_act_skip_hw(act->tcfa_flags)) { + NL_SET_ERR_MSG(extack, + "Mismatch between action and filter offload flags"); err = -EINVAL; goto err; } diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index a77d8908e737..fea2d78b9ddc 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -53,6 +53,8 @@ static int tcf_bpf_act(struct sk_buff *skb, const struct tc_action *act, bpf_compute_data_pointers(skb); filter_res = bpf_prog_run(filter, skb); } + if (unlikely(!skb->tstamp && skb->mono_delivery_time)) + skb->mono_delivery_time = 0; if (skb_sk_is_prefetched(skb) && filter_res != TC_ACT_OK) skb_orphan(skb); diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index ec19f625863a..6a34f7b80a6d 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -57,12 +57,6 @@ static const struct rhashtable_params zones_params = { .automatic_shrinking = true, }; -static struct nf_ct_ext_type act_ct_extend __read_mostly = { - .len = sizeof(struct nf_conn_act_ct_ext), - .align = __alignof__(struct nf_conn_act_ct_ext), - .id = NF_CT_EXT_ACT_CT, -}; - static struct flow_action_entry * tcf_ct_flow_table_flow_action_get_next(struct flow_action *flow_action) { @@ -426,6 +420,19 @@ static void tcf_ct_flow_table_process_conn(struct tcf_ct_flow_table *ct_ft, break; case IPPROTO_UDP: break; +#ifdef CONFIG_NF_CT_PROTO_GRE + case IPPROTO_GRE: { + struct nf_conntrack_tuple *tuple; + + if (ct->status & IPS_NAT_MASK) + return; + tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + /* No support for GRE v1 */ + if (tuple->src.u.gre.key || tuple->dst.u.gre.key) + return; + break; + } +#endif default: return; } @@ -445,6 +452,8 @@ tcf_ct_flow_table_fill_tuple_ipv4(struct sk_buff *skb, struct flow_ports *ports; unsigned int thoff; struct iphdr *iph; + size_t hdrsize; + u8 ipproto; if (!pskb_network_may_pull(skb, sizeof(*iph))) return false; @@ -456,29 +465,54 @@ tcf_ct_flow_table_fill_tuple_ipv4(struct sk_buff *skb, unlikely(thoff != sizeof(struct iphdr))) return false; - if (iph->protocol != IPPROTO_TCP && - iph->protocol != IPPROTO_UDP) + ipproto = iph->protocol; + switch (ipproto) { + case IPPROTO_TCP: + hdrsize = sizeof(struct tcphdr); + break; + case IPPROTO_UDP: + hdrsize = sizeof(*ports); + break; +#ifdef CONFIG_NF_CT_PROTO_GRE + case IPPROTO_GRE: + hdrsize = sizeof(struct gre_base_hdr); + break; +#endif + default: return false; + } if (iph->ttl <= 1) return false; - if (!pskb_network_may_pull(skb, iph->protocol == IPPROTO_TCP ? - thoff + sizeof(struct tcphdr) : - thoff + sizeof(*ports))) + if (!pskb_network_may_pull(skb, thoff + hdrsize)) return false; - iph = ip_hdr(skb); - if (iph->protocol == IPPROTO_TCP) + switch (ipproto) { + case IPPROTO_TCP: *tcph = (void *)(skb_network_header(skb) + thoff); + fallthrough; + case IPPROTO_UDP: + ports = (struct flow_ports *)(skb_network_header(skb) + thoff); + tuple->src_port = ports->source; + tuple->dst_port = ports->dest; + break; + case IPPROTO_GRE: { + struct gre_base_hdr *greh; + + greh = (struct gre_base_hdr *)(skb_network_header(skb) + thoff); + if ((greh->flags & GRE_VERSION) != GRE_VERSION_0) + return false; + break; + } + } + + iph = ip_hdr(skb); - ports = (struct flow_ports *)(skb_network_header(skb) + thoff); tuple->src_v4.s_addr = iph->saddr; tuple->dst_v4.s_addr = iph->daddr; - tuple->src_port = ports->source; - tuple->dst_port = ports->dest; tuple->l3proto = AF_INET; - tuple->l4proto = iph->protocol; + tuple->l4proto = ipproto; return true; } @@ -491,36 +525,63 @@ tcf_ct_flow_table_fill_tuple_ipv6(struct sk_buff *skb, struct flow_ports *ports; struct ipv6hdr *ip6h; unsigned int thoff; + size_t hdrsize; + u8 nexthdr; if (!pskb_network_may_pull(skb, sizeof(*ip6h))) return false; ip6h = ipv6_hdr(skb); + thoff = sizeof(*ip6h); - if (ip6h->nexthdr != IPPROTO_TCP && - ip6h->nexthdr != IPPROTO_UDP) - return false; + nexthdr = ip6h->nexthdr; + switch (nexthdr) { + case IPPROTO_TCP: + hdrsize = sizeof(struct tcphdr); + break; + case IPPROTO_UDP: + hdrsize = sizeof(*ports); + break; +#ifdef CONFIG_NF_CT_PROTO_GRE + case IPPROTO_GRE: + hdrsize = sizeof(struct gre_base_hdr); + break; +#endif + default: + return -1; + } if (ip6h->hop_limit <= 1) return false; - thoff = sizeof(*ip6h); - if (!pskb_network_may_pull(skb, ip6h->nexthdr == IPPROTO_TCP ? - thoff + sizeof(struct tcphdr) : - thoff + sizeof(*ports))) + if (!pskb_network_may_pull(skb, thoff + hdrsize)) return false; - ip6h = ipv6_hdr(skb); - if (ip6h->nexthdr == IPPROTO_TCP) + switch (nexthdr) { + case IPPROTO_TCP: *tcph = (void *)(skb_network_header(skb) + thoff); + fallthrough; + case IPPROTO_UDP: + ports = (struct flow_ports *)(skb_network_header(skb) + thoff); + tuple->src_port = ports->source; + tuple->dst_port = ports->dest; + break; + case IPPROTO_GRE: { + struct gre_base_hdr *greh; + + greh = (struct gre_base_hdr *)(skb_network_header(skb) + thoff); + if ((greh->flags & GRE_VERSION) != GRE_VERSION_0) + return false; + break; + } + } + + ip6h = ipv6_hdr(skb); - ports = (struct flow_ports *)(skb_network_header(skb) + thoff); tuple->src_v6 = ip6h->saddr; tuple->dst_v6 = ip6h->daddr; - tuple->src_port = ports->source; - tuple->dst_port = ports->dest; tuple->l3proto = AF_INET6; - tuple->l4proto = ip6h->nexthdr; + tuple->l4proto = nexthdr; return true; } @@ -1608,16 +1669,10 @@ static int __init ct_init_module(void) if (err) goto err_register; - err = nf_ct_extend_register(&act_ct_extend); - if (err) - goto err_register_extend; - static_branch_inc(&tcf_frag_xmit_count); return 0; -err_register_extend: - tcf_unregister_action(&act_ct_ops, &ct_net_ops); err_register: tcf_ct_flow_tables_uninit(); err_tbl_init: @@ -1628,7 +1683,6 @@ static int __init ct_init_module(void) static void __exit ct_cleanup_module(void) { static_branch_dec(&tcf_frag_xmit_count); - nf_ct_extend_unregister(&act_ct_extend); tcf_unregister_action(&act_ct_ops, &ct_net_ops); tcf_ct_flow_tables_uninit(); destroy_workqueue(act_ct_wq); diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 0923aa2b8f8a..f4d917705263 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -239,6 +239,20 @@ static int tcf_police_init(struct net *net, struct nlattr *nla, return err; } +static bool tcf_police_mtu_check(struct sk_buff *skb, u32 limit) +{ + u32 len; + + if (skb_is_gso(skb)) + return skb_gso_validate_mac_len(skb, limit); + + len = qdisc_pkt_len(skb); + if (skb_at_tc_ingress(skb)) + len += skb->mac_len; + + return len <= limit; +} + static int tcf_police_act(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { @@ -261,7 +275,7 @@ static int tcf_police_act(struct sk_buff *skb, const struct tc_action *a, goto inc_overlimits; } - if (qdisc_pkt_len(skb) <= p->tcfp_mtu) { + if (tcf_police_mtu_check(skb, p->tcfp_mtu)) { if (!p->rate_present && !p->pps_present) { ret = p->tcfp_result; goto end; @@ -405,20 +419,66 @@ static int tcf_police_search(struct net *net, struct tc_action **a, u32 index) return tcf_idr_search(tn, a, index); } +static int tcf_police_act_to_flow_act(int tc_act, u32 *extval) +{ + int act_id = -EOPNOTSUPP; + + if (!TC_ACT_EXT_OPCODE(tc_act)) { + if (tc_act == TC_ACT_OK) + act_id = FLOW_ACTION_ACCEPT; + else if (tc_act == TC_ACT_SHOT) + act_id = FLOW_ACTION_DROP; + else if (tc_act == TC_ACT_PIPE) + act_id = FLOW_ACTION_PIPE; + } else if (TC_ACT_EXT_CMP(tc_act, TC_ACT_GOTO_CHAIN)) { + act_id = FLOW_ACTION_GOTO; + *extval = tc_act & TC_ACT_EXT_VAL_MASK; + } else if (TC_ACT_EXT_CMP(tc_act, TC_ACT_JUMP)) { + act_id = FLOW_ACTION_JUMP; + *extval = tc_act & TC_ACT_EXT_VAL_MASK; + } + + return act_id; +} + static int tcf_police_offload_act_setup(struct tc_action *act, void *entry_data, u32 *index_inc, bool bind) { if (bind) { struct flow_action_entry *entry = entry_data; + struct tcf_police *police = to_police(act); + struct tcf_police_params *p; + int act_id; + + p = rcu_dereference_protected(police->params, + lockdep_is_held(&police->tcf_lock)); entry->id = FLOW_ACTION_POLICE; entry->police.burst = tcf_police_burst(act); entry->police.rate_bytes_ps = tcf_police_rate_bytes_ps(act); + entry->police.peakrate_bytes_ps = tcf_police_peakrate_bytes_ps(act); + entry->police.avrate = tcf_police_tcfp_ewma_rate(act); + entry->police.overhead = tcf_police_rate_overhead(act); entry->police.burst_pkt = tcf_police_burst_pkt(act); entry->police.rate_pkt_ps = tcf_police_rate_pkt_ps(act); entry->police.mtu = tcf_police_tcfp_mtu(act); + + act_id = tcf_police_act_to_flow_act(police->tcf_action, + &entry->police.exceed.extval); + if (act_id < 0) + return act_id; + + entry->police.exceed.act_id = act_id; + + act_id = tcf_police_act_to_flow_act(p->tcfp_result, + &entry->police.notexceed.extval); + if (act_id < 0) + return act_id; + + entry->police.notexceed.act_id = act_id; + *index_inc = 1; } else { struct flow_offload_action *fl_action = entry_data; diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c index 756e2dcde1cd..883454c4f921 100644 --- a/net/sched/act_vlan.c +++ b/net/sched/act_vlan.c @@ -390,6 +390,13 @@ static int tcf_vlan_offload_act_setup(struct tc_action *act, void *entry_data, entry->vlan.proto = tcf_vlan_push_proto(act); entry->vlan.prio = tcf_vlan_push_prio(act); break; + case TCA_VLAN_ACT_POP_ETH: + entry->id = FLOW_ACTION_VLAN_POP_ETH; + break; + case TCA_VLAN_ACT_PUSH_ETH: + entry->id = FLOW_ACTION_VLAN_PUSH_ETH; + tcf_vlan_push_eth(entry->vlan_push_eth.src, entry->vlan_push_eth.dst, act); + break; default: return -EOPNOTSUPP; } @@ -407,6 +414,12 @@ static int tcf_vlan_offload_act_setup(struct tc_action *act, void *entry_data, case TCA_VLAN_ACT_MODIFY: fl_action->id = FLOW_ACTION_VLAN_MANGLE; break; + case TCA_VLAN_ACT_POP_ETH: + fl_action->id = FLOW_ACTION_VLAN_POP_ETH; + break; + case TCA_VLAN_ACT_PUSH_ETH: + fl_action->id = FLOW_ACTION_VLAN_PUSH_ETH; + break; default: return -EOPNOTSUPP; } diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 5ce1208a6ea3..2957f8f5cea7 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -49,6 +49,23 @@ static LIST_HEAD(tcf_proto_base); /* Protects list of registered TC modules. It is pure SMP lock. */ static DEFINE_RWLOCK(cls_mod_lock); +#ifdef CONFIG_NET_CLS_ACT +DEFINE_STATIC_KEY_FALSE(tc_skb_ext_tc); +EXPORT_SYMBOL(tc_skb_ext_tc); + +void tc_skb_ext_tc_enable(void) +{ + static_branch_inc(&tc_skb_ext_tc); +} +EXPORT_SYMBOL(tc_skb_ext_tc_enable); + +void tc_skb_ext_tc_disable(void) +{ + static_branch_dec(&tc_skb_ext_tc); +} +EXPORT_SYMBOL(tc_skb_ext_tc_disable); +#endif + static u32 destroy_obj_hashfn(const struct tcf_proto *tp) { return jhash_3words(tp->chain->index, tp->prio, @@ -1615,19 +1632,21 @@ int tcf_classify(struct sk_buff *skb, ret = __tcf_classify(skb, tp, orig_tp, res, compat_mode, &last_executed_chain); - /* If we missed on some chain */ - if (ret == TC_ACT_UNSPEC && last_executed_chain) { - struct tc_skb_cb *cb = tc_skb_cb(skb); + if (tc_skb_ext_tc_enabled()) { + /* If we missed on some chain */ + if (ret == TC_ACT_UNSPEC && last_executed_chain) { + struct tc_skb_cb *cb = tc_skb_cb(skb); - ext = tc_skb_ext_alloc(skb); - if (WARN_ON_ONCE(!ext)) - return TC_ACT_SHOT; - ext->chain = last_executed_chain; - ext->mru = cb->mru; - ext->post_ct = cb->post_ct; - ext->post_ct_snat = cb->post_ct_snat; - ext->post_ct_dnat = cb->post_ct_dnat; - ext->zone = cb->zone; + ext = tc_skb_ext_alloc(skb); + if (WARN_ON_ONCE(!ext)) + return TC_ACT_SHOT; + ext->chain = last_executed_chain; + ext->mru = cb->mru; + ext->post_ct = cb->post_ct; + ext->post_ct_snat = cb->post_ct_snat; + ext->post_ct_dnat = cb->post_ct_dnat; + ext->zone = cb->zone; + } } return ret; diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index df19a847829e..c85b85a192bf 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -102,6 +102,8 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, bpf_compute_data_pointers(skb); filter_res = bpf_prog_run(prog->filter, skb); } + if (unlikely(!skb->tstamp && skb->mono_delivery_time)) + skb->mono_delivery_time = 0; if (prog->exts_integrated) { res->class = 0; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 1a9b1f140f9e..c80fc49c0da1 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -723,6 +724,7 @@ enc_opts_policy[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1] = { [TCA_FLOWER_KEY_ENC_OPTS_GENEVE] = { .type = NLA_NESTED }, [TCA_FLOWER_KEY_ENC_OPTS_VXLAN] = { .type = NLA_NESTED }, [TCA_FLOWER_KEY_ENC_OPTS_ERSPAN] = { .type = NLA_NESTED }, + [TCA_FLOWER_KEY_ENC_OPTS_GTP] = { .type = NLA_NESTED }, }; static const struct nla_policy @@ -746,6 +748,12 @@ erspan_opt_policy[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX + 1] = { [TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID] = { .type = NLA_U8 }, }; +static const struct nla_policy +gtp_opt_policy[TCA_FLOWER_KEY_ENC_OPT_GTP_MAX + 1] = { + [TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_ENC_OPT_GTP_QFI] = { .type = NLA_U8 }, +}; + static const struct nla_policy mpls_stack_entry_policy[TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX + 1] = { [TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH] = { .type = NLA_U8 }, @@ -1262,6 +1270,49 @@ static int fl_set_erspan_opt(const struct nlattr *nla, struct fl_flow_key *key, return sizeof(*md); } +static int fl_set_gtp_opt(const struct nlattr *nla, struct fl_flow_key *key, + int depth, int option_len, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[TCA_FLOWER_KEY_ENC_OPT_GTP_MAX + 1]; + struct gtp_pdu_session_info *sinfo; + u8 len = key->enc_opts.len; + int err; + + sinfo = (struct gtp_pdu_session_info *)&key->enc_opts.data[len]; + memset(sinfo, 0xff, option_len); + + if (!depth) + return sizeof(*sinfo); + + if (nla_type(nla) != TCA_FLOWER_KEY_ENC_OPTS_GTP) { + NL_SET_ERR_MSG_MOD(extack, "Non-gtp option type for mask"); + return -EINVAL; + } + + err = nla_parse_nested(tb, TCA_FLOWER_KEY_ENC_OPT_GTP_MAX, nla, + gtp_opt_policy, extack); + if (err < 0) + return err; + + if (!option_len && + (!tb[TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE] || + !tb[TCA_FLOWER_KEY_ENC_OPT_GTP_QFI])) { + NL_SET_ERR_MSG_MOD(extack, + "Missing tunnel key gtp option pdu type or qfi"); + return -EINVAL; + } + + if (tb[TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE]) + sinfo->pdu_type = + nla_get_u8(tb[TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE]); + + if (tb[TCA_FLOWER_KEY_ENC_OPT_GTP_QFI]) + sinfo->qfi = nla_get_u8(tb[TCA_FLOWER_KEY_ENC_OPT_GTP_QFI]); + + return sizeof(*sinfo); +} + static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key, struct fl_flow_key *mask, struct netlink_ext_ack *extack) @@ -1386,6 +1437,38 @@ static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key, return -EINVAL; } break; + case TCA_FLOWER_KEY_ENC_OPTS_GTP: + if (key->enc_opts.dst_opt_type) { + NL_SET_ERR_MSG_MOD(extack, + "Duplicate type for gtp options"); + return -EINVAL; + } + option_len = 0; + key->enc_opts.dst_opt_type = TUNNEL_GTP_OPT; + option_len = fl_set_gtp_opt(nla_opt_key, key, + key_depth, option_len, + extack); + if (option_len < 0) + return option_len; + + key->enc_opts.len += option_len; + /* At the same time we need to parse through the mask + * in order to verify exact and mask attribute lengths. + */ + mask->enc_opts.dst_opt_type = TUNNEL_GTP_OPT; + option_len = fl_set_gtp_opt(nla_opt_msk, mask, + msk_depth, option_len, + extack); + if (option_len < 0) + return option_len; + + mask->enc_opts.len += option_len; + if (key->enc_opts.len != mask->enc_opts.len) { + NL_SET_ERR_MSG_MOD(extack, + "Key and mask miss aligned"); + return -EINVAL; + } + break; default: NL_SET_ERR_MSG(extack, "Unknown tunnel option type"); return -EINVAL; @@ -2761,6 +2844,34 @@ static int fl_dump_key_erspan_opt(struct sk_buff *skb, return -EMSGSIZE; } +static int fl_dump_key_gtp_opt(struct sk_buff *skb, + struct flow_dissector_key_enc_opts *enc_opts) + +{ + struct gtp_pdu_session_info *session_info; + struct nlattr *nest; + + nest = nla_nest_start_noflag(skb, TCA_FLOWER_KEY_ENC_OPTS_GTP); + if (!nest) + goto nla_put_failure; + + session_info = (struct gtp_pdu_session_info *)&enc_opts->data[0]; + + if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE, + session_info->pdu_type)) + goto nla_put_failure; + + if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_GTP_QFI, session_info->qfi)) + goto nla_put_failure; + + nla_nest_end(skb, nest); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nest); + return -EMSGSIZE; +} + static int fl_dump_key_ct(struct sk_buff *skb, struct flow_dissector_key_ct *key, struct flow_dissector_key_ct *mask) @@ -2824,6 +2935,11 @@ static int fl_dump_key_options(struct sk_buff *skb, int enc_opt_type, if (err) goto nla_put_failure; break; + case TUNNEL_GTP_OPT: + err = fl_dump_key_gtp_opt(skb, enc_opts); + if (err) + goto nla_put_failure; + break; default: goto nla_put_failure; } diff --git a/net/smc/Makefile b/net/smc/Makefile index 196fb6f01b14..875efcd126a2 100644 --- a/net/smc/Makefile +++ b/net/smc/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_SMC_DIAG) += smc_diag.o smc-y := af_smc.o smc_pnet.o smc_ib.o smc_clc.o smc_core.o smc_wr.o smc_llc.o smc-y += smc_cdc.o smc_tx.o smc_rx.o smc_close.o smc_ism.o smc_netlink.o smc_stats.o smc-y += smc_tracepoint.o +smc-$(CONFIG_SYSCTL) += smc_sysctl.o diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 284befa90967..f0d118e9f155 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -51,6 +51,7 @@ #include "smc_close.h" #include "smc_stats.h" #include "smc_tracepoint.h" +#include "smc_sysctl.h" static DEFINE_MUTEX(smc_server_lgr_pending); /* serialize link group * creation on server @@ -59,12 +60,52 @@ static DEFINE_MUTEX(smc_client_lgr_pending); /* serialize link group * creation on client */ +static struct workqueue_struct *smc_tcp_ls_wq; /* wq for tcp listen work */ struct workqueue_struct *smc_hs_wq; /* wq for handshake work */ struct workqueue_struct *smc_close_wq; /* wq for close work */ static void smc_tcp_listen_work(struct work_struct *); static void smc_connect_work(struct work_struct *); +int smc_nl_dump_hs_limitation(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb); + void *hdr; + + if (cb_ctx->pos[0]) + goto out; + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &smc_gen_nl_family, NLM_F_MULTI, + SMC_NETLINK_DUMP_HS_LIMITATION); + if (!hdr) + return -ENOMEM; + + if (nla_put_u8(skb, SMC_NLA_HS_LIMITATION_ENABLED, + sock_net(skb->sk)->smc.limit_smc_hs)) + goto err; + + genlmsg_end(skb, hdr); + cb_ctx->pos[0] = 1; +out: + return skb->len; +err: + genlmsg_cancel(skb, hdr); + return -EMSGSIZE; +} + +int smc_nl_enable_hs_limitation(struct sk_buff *skb, struct genl_info *info) +{ + sock_net(skb->sk)->smc.limit_smc_hs = true; + return 0; +} + +int smc_nl_disable_hs_limitation(struct sk_buff *skb, struct genl_info *info) +{ + sock_net(skb->sk)->smc.limit_smc_hs = false; + return 0; +} + static void smc_set_keepalive(struct sock *sk, int val) { struct smc_sock *smc = smc_sk(sk); @@ -72,6 +113,51 @@ static void smc_set_keepalive(struct sock *sk, int val) smc->clcsock->sk->sk_prot->keepalive(smc->clcsock->sk, val); } +static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk, + struct sk_buff *skb, + struct request_sock *req, + struct dst_entry *dst, + struct request_sock *req_unhash, + bool *own_req) +{ + struct smc_sock *smc; + + smc = smc_clcsock_user_data(sk); + + if (READ_ONCE(sk->sk_ack_backlog) + atomic_read(&smc->queued_smc_hs) > + sk->sk_max_ack_backlog) + goto drop; + + if (sk_acceptq_is_full(&smc->sk)) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); + goto drop; + } + + /* passthrough to original syn recv sock fct */ + return smc->ori_af_ops->syn_recv_sock(sk, skb, req, dst, req_unhash, + own_req); + +drop: + dst_release(dst); + tcp_listendrop(sk); + return NULL; +} + +static bool smc_hs_congested(const struct sock *sk) +{ + const struct smc_sock *smc; + + smc = smc_clcsock_user_data(sk); + + if (!smc) + return true; + + if (workqueue_congested(WORK_CPU_UNBOUND, smc_hs_wq)) + return true; + + return false; +} + static struct smc_hashinfo smc_v4_hashinfo = { .lock = __RW_LOCK_UNLOCKED(smc_v4_hashinfo.lock), }; @@ -107,12 +193,27 @@ void smc_unhash_sk(struct sock *sk) } EXPORT_SYMBOL_GPL(smc_unhash_sk); +/* This will be called before user really release sock_lock. So do the + * work which we didn't do because of user hold the sock_lock in the + * BH context + */ +static void smc_release_cb(struct sock *sk) +{ + struct smc_sock *smc = smc_sk(sk); + + if (smc->conn.tx_in_release_sock) { + smc_tx_pending(&smc->conn); + smc->conn.tx_in_release_sock = false; + } +} + struct proto smc_proto = { .name = "SMC", .owner = THIS_MODULE, .keepalive = smc_set_keepalive, .hash = smc_hash_sk, .unhash = smc_unhash_sk, + .release_cb = smc_release_cb, .obj_size = sizeof(struct smc_sock), .h.smc_hash = &smc_v4_hashinfo, .slab_flags = SLAB_TYPESAFE_BY_RCU, @@ -125,6 +226,7 @@ struct proto smc_proto6 = { .keepalive = smc_set_keepalive, .hash = smc_hash_sk, .unhash = smc_unhash_sk, + .release_cb = smc_release_cb, .obj_size = sizeof(struct smc_sock), .h.smc_hash = &smc_v6_hashinfo, .slab_flags = SLAB_TYPESAFE_BY_RCU, @@ -1297,8 +1399,14 @@ static int __smc_connect(struct smc_sock *smc) /* perform CLC handshake */ rc = smc_connect_clc(smc, aclc2, ini); - if (rc) + if (rc) { + /* -EAGAIN on timeout, see tcp_recvmsg() */ + if (rc == -EAGAIN) { + rc = -ETIMEDOUT; + smc->sk.sk_err = ETIMEDOUT; + } goto vlan_cleanup; + } /* check if smc modes and versions of CLC proposal and accept match */ rc = smc_connect_check_aclc(ini, aclc); @@ -1604,6 +1712,9 @@ static void smc_listen_out(struct smc_sock *new_smc) struct smc_sock *lsmc = new_smc->listen_smc; struct sock *newsmcsk = &new_smc->sk; + if (tcp_sk(new_smc->clcsock->sk)->syn_smc) + atomic_dec(&lsmc->queued_smc_hs); + if (lsmc->sk.sk_state == SMC_LISTEN) { lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING); smc_accept_enqueue(&lsmc->sk, newsmcsk); @@ -2209,6 +2320,9 @@ static void smc_tcp_listen_work(struct work_struct *work) if (!new_smc) continue; + if (tcp_sk(new_smc->clcsock->sk)->syn_smc) + atomic_inc(&lsmc->queued_smc_hs); + new_smc->listen_smc = lsmc; new_smc->use_fallback = lsmc->use_fallback; new_smc->fallback_rsn = lsmc->fallback_rsn; @@ -2237,7 +2351,7 @@ static void smc_clcsock_data_ready(struct sock *listen_clcsock) lsmc->clcsk_data_ready(listen_clcsock); if (lsmc->sk.sk_state == SMC_LISTEN) { sock_hold(&lsmc->sk); /* sock_put in smc_tcp_listen_work() */ - if (!queue_work(smc_hs_wq, &lsmc->tcp_listen_work)) + if (!queue_work(smc_tcp_ls_wq, &lsmc->tcp_listen_work)) sock_put(&lsmc->sk); } } @@ -2275,6 +2389,18 @@ static int smc_listen(struct socket *sock, int backlog) smc->clcsock->sk->sk_data_ready = smc_clcsock_data_ready; smc->clcsock->sk->sk_user_data = (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY); + + /* save original ops */ + smc->ori_af_ops = inet_csk(smc->clcsock->sk)->icsk_af_ops; + + smc->af_ops = *smc->ori_af_ops; + smc->af_ops.syn_recv_sock = smc_tcp_syn_recv_sock; + + inet_csk(smc->clcsock->sk)->icsk_af_ops = &smc->af_ops; + + if (smc->limit_smc_hs) + tcp_sk(smc->clcsock->sk)->smc_hs_congested = smc_hs_congested; + rc = kernel_listen(smc->clcsock, backlog); if (rc) { smc->clcsock->sk->sk_data_ready = smc->clcsk_data_ready; @@ -2568,6 +2694,71 @@ static int smc_shutdown(struct socket *sock, int how) return rc ? rc : rc1; } +static int __smc_getsockopt(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) +{ + struct smc_sock *smc; + int val, len; + + smc = smc_sk(sock->sk); + + if (get_user(len, optlen)) + return -EFAULT; + + len = min_t(int, len, sizeof(int)); + + if (len < 0) + return -EINVAL; + + switch (optname) { + case SMC_LIMIT_HS: + val = smc->limit_smc_hs; + break; + default: + return -EOPNOTSUPP; + } + + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + + return 0; +} + +static int __smc_setsockopt(struct socket *sock, int level, int optname, + sockptr_t optval, unsigned int optlen) +{ + struct sock *sk = sock->sk; + struct smc_sock *smc; + int val, rc; + + smc = smc_sk(sk); + + lock_sock(sk); + switch (optname) { + case SMC_LIMIT_HS: + if (optlen < sizeof(int)) { + rc = -EINVAL; + break; + } + if (copy_from_sockptr(&val, optval, sizeof(int))) { + rc = -EFAULT; + break; + } + + smc->limit_smc_hs = !!val; + rc = 0; + break; + default: + rc = -EOPNOTSUPP; + break; + } + release_sock(sk); + + return rc; +} + static int smc_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval, unsigned int optlen) { @@ -2577,6 +2768,8 @@ static int smc_setsockopt(struct socket *sock, int level, int optname, if (level == SOL_TCP && optname == TCP_ULP) return -EOPNOTSUPP; + else if (level == SOL_SMC) + return __smc_setsockopt(sock, level, optname, optval, optlen); smc = smc_sk(sk); @@ -2625,8 +2818,8 @@ static int smc_setsockopt(struct socket *sock, int level, int optname, sk->sk_state != SMC_CLOSED) { if (val) { SMC_STAT_INC(smc, ndly_cnt); - mod_delayed_work(smc->conn.lgr->tx_wq, - &smc->conn.tx_work, 0); + smc_tx_pending(&smc->conn); + cancel_delayed_work(&smc->conn.tx_work); } } break; @@ -2636,8 +2829,8 @@ static int smc_setsockopt(struct socket *sock, int level, int optname, sk->sk_state != SMC_CLOSED) { if (!val) { SMC_STAT_INC(smc, cork_cnt); - mod_delayed_work(smc->conn.lgr->tx_wq, - &smc->conn.tx_work, 0); + smc_tx_pending(&smc->conn); + cancel_delayed_work(&smc->conn.tx_work); } } break; @@ -2659,6 +2852,9 @@ static int smc_getsockopt(struct socket *sock, int level, int optname, struct smc_sock *smc; int rc; + if (level == SOL_SMC) + return __smc_getsockopt(sock, level, optname, optval, optlen); + smc = smc_sk(sock->sk); mutex_lock(&smc->clcsock_release_lock); if (!smc->clcsock) { @@ -2775,8 +2971,10 @@ static ssize_t smc_sendpage(struct socket *sock, struct page *page, rc = kernel_sendpage(smc->clcsock, page, offset, size, flags); } else { + lock_sock(sk); + rc = smc_tx_sendpage(smc, page, offset, size, flags); + release_sock(sk); SMC_STAT_INC(smc, sendpage_cnt); - rc = sock_no_sendpage(sock, page, offset, size, flags); } out: @@ -2885,6 +3083,9 @@ static int __smc_create(struct net *net, struct socket *sock, int protocol, smc->use_fallback = false; /* assume rdma capability first */ smc->fallback_rsn = 0; + /* default behavior from limit_smc_hs in every net namespace */ + smc->limit_smc_hs = net->smc.limit_smc_hs; + rc = 0; if (!clcsock) { rc = sock_create_kern(net, family, SOCK_STREAM, IPPROTO_TCP, @@ -2978,11 +3179,17 @@ unsigned int smc_net_id; static __net_init int smc_net_init(struct net *net) { + int rc; + + rc = smc_sysctl_net_init(net); + if (rc) + return rc; return smc_pnet_net_init(net); } static void __net_exit smc_net_exit(struct net *net) { + smc_sysctl_net_exit(net); smc_pnet_net_exit(net); } @@ -3032,9 +3239,14 @@ static int __init smc_init(void) goto out_nl; rc = -ENOMEM; + + smc_tcp_ls_wq = alloc_workqueue("smc_tcp_ls_wq", 0, 0); + if (!smc_tcp_ls_wq) + goto out_pnet; + smc_hs_wq = alloc_workqueue("smc_hs_wq", 0, 0); if (!smc_hs_wq) - goto out_pnet; + goto out_alloc_tcp_ls_wq; smc_close_wq = alloc_workqueue("smc_close_wq", 0, 0); if (!smc_close_wq) @@ -3107,6 +3319,8 @@ static int __init smc_init(void) destroy_workqueue(smc_close_wq); out_alloc_hs_wq: destroy_workqueue(smc_hs_wq); +out_alloc_tcp_ls_wq: + destroy_workqueue(smc_tcp_ls_wq); out_pnet: smc_pnet_exit(); out_nl: @@ -3125,6 +3339,7 @@ static void __exit smc_exit(void) smc_core_exit(); smc_ib_unregister_client(); destroy_workqueue(smc_close_wq); + destroy_workqueue(smc_tcp_ls_wq); destroy_workqueue(smc_hs_wq); proto_unregister(&smc_proto6); proto_unregister(&smc_proto); diff --git a/net/smc/smc.h b/net/smc/smc.h index 37b2001a0255..ea0620529ebe 100644 --- a/net/smc/smc.h +++ b/net/smc/smc.h @@ -14,6 +14,7 @@ #include #include #include /* __aligned */ +#include #include #include "smc_ib.h" @@ -28,6 +29,7 @@ #define SMC_MAX_ISM_DEVS 8 /* max # of proposed non-native ISM * devices */ +#define SMC_AUTOCORKING_DEFAULT_SIZE 0x10000 /* 64K by default */ extern struct proto smc_proto; extern struct proto smc_proto6; @@ -191,6 +193,7 @@ struct smc_connection { * - dec on polled tx cqe */ wait_queue_head_t cdc_pend_tx_wq; /* wakeup on no cdc_pend_tx_wr*/ + atomic_t tx_pushing; /* nr_threads trying tx push */ struct delayed_work tx_work; /* retry of smc_cdc_msg_send */ u32 tx_off; /* base offset in peer rmb */ @@ -210,6 +213,10 @@ struct smc_connection { * data still pending */ char urg_rx_byte; /* urgent byte */ + bool tx_in_release_sock; + /* flush pending tx data in + * sock release_cb() + */ atomic_t bytes_to_rcv; /* arrived data, * not yet received */ @@ -249,9 +256,14 @@ struct smc_sock { /* smc sock container */ struct work_struct smc_listen_work;/* prepare new accept socket */ struct list_head accept_q; /* sockets to be accepted */ spinlock_t accept_q_lock; /* protects accept_q */ + bool limit_smc_hs; /* put constraint on handshake */ bool use_fallback; /* fallback to tcp */ int fallback_rsn; /* reason for fallback */ u32 peer_diagnosis; /* decline reason from peer */ + atomic_t queued_smc_hs; /* queued smc handshakes */ + struct inet_connection_sock_af_ops af_ops; + const struct inet_connection_sock_af_ops *ori_af_ops; + /* original af ops */ int sockopt_defer_accept; /* sockopt TCP_DEFER_ACCEPT * value @@ -276,7 +288,7 @@ static inline struct smc_sock *smc_sk(const struct sock *sk) return (struct smc_sock *)sk; } -static inline struct smc_sock *smc_clcsock_user_data(struct sock *clcsk) +static inline struct smc_sock *smc_clcsock_user_data(const struct sock *clcsk) { return (struct smc_sock *) ((uintptr_t)clcsk->sk_user_data & ~SK_USER_DATA_NOCOPY); @@ -331,4 +343,9 @@ void smc_fill_gid_list(struct smc_link_group *lgr, struct smc_gidlist *gidlist, struct smc_ib_device *known_dev, u8 *known_gid); +/* smc handshake limitation interface for netlink */ +int smc_nl_dump_hs_limitation(struct sk_buff *skb, struct netlink_callback *cb); +int smc_nl_enable_hs_limitation(struct sk_buff *skb, struct genl_info *info); +int smc_nl_disable_hs_limitation(struct sk_buff *skb, struct genl_info *info); + #endif /* __SMC_H */ diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c index 9d5a97168969..5c731f27996e 100644 --- a/net/smc/smc_cdc.c +++ b/net/smc/smc_cdc.c @@ -48,9 +48,19 @@ static void smc_cdc_tx_handler(struct smc_wr_tx_pend_priv *pnd_snd, conn->tx_cdc_seq_fin = cdcpend->ctrl_seq; } - if (atomic_dec_and_test(&conn->cdc_pend_tx_wr) && - unlikely(wq_has_sleeper(&conn->cdc_pend_tx_wq))) - wake_up(&conn->cdc_pend_tx_wq); + if (atomic_dec_and_test(&conn->cdc_pend_tx_wr)) { + /* If user owns the sock_lock, mark the connection need sending. + * User context will later try to send when it release sock_lock + * in smc_release_cb() + */ + if (sock_owned_by_user(&smc->sk)) + conn->tx_in_release_sock = true; + else + smc_tx_pending(conn); + + if (unlikely(wq_has_sleeper(&conn->cdc_pend_tx_wq))) + wake_up(&conn->cdc_pend_tx_wq); + } WARN_ON(atomic_read(&conn->cdc_pend_tx_wr) < 0); smc_tx_sndbuf_nonfull(smc); @@ -350,8 +360,12 @@ static void smc_cdc_msg_recv_action(struct smc_sock *smc, /* trigger sndbuf consumer: RDMA write into peer RMBE and CDC */ if ((diff_cons && smc_tx_prepared_sends(conn)) || conn->local_rx_ctrl.prod_flags.cons_curs_upd_req || - conn->local_rx_ctrl.prod_flags.urg_data_pending) - smc_tx_sndbuf_nonempty(conn); + conn->local_rx_ctrl.prod_flags.urg_data_pending) { + if (!sock_owned_by_user(&smc->sk)) + smc_tx_pending(conn); + else + conn->tx_in_release_sock = true; + } if (diff_cons && conn->urg_tx_pend && atomic_read(&conn->peer_rmbe_space) == conn->peer_rmbe_size) { diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index be7d704976ff..f40f6ed0fbdb 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -1989,7 +1989,7 @@ static struct smc_buf_desc *smc_buf_get_slot(int compressed_bufsize, */ static inline int smc_rmb_wnd_update_limit(int rmbe_size) { - return min_t(int, rmbe_size / 10, SOCK_MIN_SNDBUF / 2); + return max_t(int, rmbe_size / 10, SOCK_MIN_SNDBUF / 2); } /* map an rmb buf to a link */ diff --git a/net/smc/smc_netlink.c b/net/smc/smc_netlink.c index f13ab0661ed5..c5a62f6f52ba 100644 --- a/net/smc/smc_netlink.c +++ b/net/smc/smc_netlink.c @@ -111,6 +111,21 @@ static const struct genl_ops smc_gen_nl_ops[] = { .flags = GENL_ADMIN_PERM, .doit = smc_nl_disable_seid, }, + { + .cmd = SMC_NETLINK_DUMP_HS_LIMITATION, + /* can be retrieved by unprivileged users */ + .dumpit = smc_nl_dump_hs_limitation, + }, + { + .cmd = SMC_NETLINK_ENABLE_HS_LIMITATION, + .flags = GENL_ADMIN_PERM, + .doit = smc_nl_enable_hs_limitation, + }, + { + .cmd = SMC_NETLINK_DISABLE_HS_LIMITATION, + .flags = GENL_ADMIN_PERM, + .doit = smc_nl_disable_hs_limitation, + }, }; static const struct nla_policy smc_gen_nl_policy[2] = { diff --git a/net/smc/smc_pnet.c b/net/smc/smc_pnet.c index 29f0a559d884..7984f8883472 100644 --- a/net/smc/smc_pnet.c +++ b/net/smc/smc_pnet.c @@ -870,6 +870,9 @@ int smc_pnet_net_init(struct net *net) smc_pnet_create_pnetids_list(net); + /* disable handshake limitation by default */ + net->smc.limit_smc_hs = 0; + return 0; } diff --git a/net/smc/smc_sysctl.c b/net/smc/smc_sysctl.c new file mode 100644 index 000000000000..bae19419e755 --- /dev/null +++ b/net/smc/smc_sysctl.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Shared Memory Communications over RDMA (SMC-R) and RoCE + * + * smc_sysctl.c: sysctl interface to SMC subsystem. + * + * Copyright (c) 2022, Alibaba Inc. + * + * Author: Tony Lu + * + */ + +#include +#include +#include + +#include "smc.h" +#include "smc_sysctl.h" + +static struct ctl_table smc_table[] = { + { + .procname = "autocorking_size", + .data = &init_net.smc.sysctl_autocorking_size, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_douintvec, + }, + { } +}; + +int __net_init smc_sysctl_net_init(struct net *net) +{ + struct ctl_table *table; + + table = smc_table; + if (!net_eq(net, &init_net)) { + int i; + + table = kmemdup(table, sizeof(smc_table), GFP_KERNEL); + if (!table) + goto err_alloc; + + for (i = 0; i < ARRAY_SIZE(smc_table) - 1; i++) + table[i].data += (void *)net - (void *)&init_net; + } + + net->smc.smc_hdr = register_net_sysctl(net, "net/smc", table); + if (!net->smc.smc_hdr) + goto err_reg; + + net->smc.sysctl_autocorking_size = SMC_AUTOCORKING_DEFAULT_SIZE; + + return 0; + +err_reg: + if (!net_eq(net, &init_net)) + kfree(table); +err_alloc: + return -ENOMEM; +} + +void __net_exit smc_sysctl_net_exit(struct net *net) +{ + unregister_net_sysctl_table(net->smc.smc_hdr); +} diff --git a/net/smc/smc_sysctl.h b/net/smc/smc_sysctl.h new file mode 100644 index 000000000000..0becc11bd2f4 --- /dev/null +++ b/net/smc/smc_sysctl.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Shared Memory Communications over RDMA (SMC-R) and RoCE + * + * smc_sysctl.c: sysctl interface to SMC subsystem. + * + * Copyright (c) 2022, Alibaba Inc. + * + * Author: Tony Lu + * + */ + +#ifndef _SMC_SYSCTL_H +#define _SMC_SYSCTL_H + +#ifdef CONFIG_SYSCTL + +int __net_init smc_sysctl_net_init(struct net *net); +void __net_exit smc_sysctl_net_exit(struct net *net); + +#else + +static inline int smc_sysctl_net_init(struct net *net) +{ + net->smc.sysctl_autocorking_size = SMC_AUTOCORKING_DEFAULT_SIZE; + return 0; +} + +static inline void smc_sysctl_net_exit(struct net *net) { } + +#endif /* CONFIG_SYSCTL */ + +#endif /* _SMC_SYSCTL_H */ diff --git a/net/smc/smc_tx.c b/net/smc/smc_tx.c index be241d53020f..98ca9229fe87 100644 --- a/net/smc/smc_tx.c +++ b/net/smc/smc_tx.c @@ -31,7 +31,6 @@ #include "smc_tracepoint.h" #define SMC_TX_WORK_DELAY 0 -#define SMC_TX_CORK_DELAY (HZ >> 2) /* 250 ms */ /***************************** sndbuf producer *******************************/ @@ -132,6 +131,51 @@ static bool smc_tx_is_corked(struct smc_sock *smc) return (tp->nonagle & TCP_NAGLE_CORK) ? true : false; } +/* If we have pending CDC messages, do not send: + * Because CQE of this CDC message will happen shortly, it gives + * a chance to coalesce future sendmsg() payload in to one RDMA Write, + * without need for a timer, and with no latency trade off. + * Algorithm here: + * 1. First message should never cork + * 2. If we have pending Tx CDC messages, wait for the first CDC + * message's completion + * 3. Don't cork to much data in a single RDMA Write to prevent burst + * traffic, total corked message should not exceed sendbuf/2 + */ +static bool smc_should_autocork(struct smc_sock *smc) +{ + struct smc_connection *conn = &smc->conn; + int corking_size; + + corking_size = min_t(unsigned int, conn->sndbuf_desc->len >> 1, + sock_net(&smc->sk)->smc.sysctl_autocorking_size); + + if (atomic_read(&conn->cdc_pend_tx_wr) == 0 || + smc_tx_prepared_sends(conn) > corking_size) + return false; + return true; +} + +static bool smc_tx_should_cork(struct smc_sock *smc, struct msghdr *msg) +{ + struct smc_connection *conn = &smc->conn; + + if (smc_should_autocork(smc)) + return true; + + /* for a corked socket defer the RDMA writes if + * sndbuf_space is still available. The applications + * should known how/when to uncork it. + */ + if ((msg->msg_flags & MSG_MORE || + smc_tx_is_corked(smc) || + msg->msg_flags & MSG_SENDPAGE_NOTLAST) && + atomic_read(&conn->sndbuf_space)) + return true; + + return false; +} + /* sndbuf producer: main API called by socket layer. * called under sock lock. */ @@ -236,15 +280,10 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len) */ if ((msg->msg_flags & MSG_OOB) && !send_remaining) conn->urg_tx_pend = true; - if ((msg->msg_flags & MSG_MORE || smc_tx_is_corked(smc)) && - (atomic_read(&conn->sndbuf_space) > - (conn->sndbuf_desc->len >> 1))) - /* for a corked socket defer the RDMA writes if there - * is still sufficient sndbuf_space available - */ - queue_delayed_work(conn->lgr->tx_wq, &conn->tx_work, - SMC_TX_CORK_DELAY); - else + /* If we need to cork, do nothing and wait for the next + * sendmsg() call or push on tx completion + */ + if (!smc_tx_should_cork(smc, msg)) smc_tx_sndbuf_nonempty(conn); trace_smc_tx_sendmsg(smc, copylen); @@ -260,6 +299,22 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len) return rc; } +int smc_tx_sendpage(struct smc_sock *smc, struct page *page, int offset, + size_t size, int flags) +{ + struct msghdr msg = {.msg_flags = flags}; + char *kaddr = kmap(page); + struct kvec iov; + int rc; + + iov.iov_base = kaddr + offset; + iov.iov_len = size; + iov_iter_kvec(&msg.msg_iter, WRITE, &iov, 1, size); + rc = smc_tx_sendmsg(smc, &msg, size); + kunmap(page); + return rc; +} + /***************************** sndbuf consumer *******************************/ /* sndbuf consumer: actual data transfer of one target chunk with ISM write */ @@ -576,13 +631,26 @@ static int smcd_tx_sndbuf_nonempty(struct smc_connection *conn) return rc; } -int smc_tx_sndbuf_nonempty(struct smc_connection *conn) +static int __smc_tx_sndbuf_nonempty(struct smc_connection *conn) { - int rc; + struct smc_sock *smc = container_of(conn, struct smc_sock, conn); + int rc = 0; + + /* No data in the send queue */ + if (unlikely(smc_tx_prepared_sends(conn) <= 0)) + goto out; + + /* Peer don't have RMBE space */ + if (unlikely(atomic_read(&conn->peer_rmbe_space) <= 0)) { + SMC_STAT_RMB_TX_PEER_FULL(smc, !conn->lnk); + goto out; + } if (conn->killed || - conn->local_rx_ctrl.conn_state_flags.peer_conn_abort) - return -EPIPE; /* connection being aborted */ + conn->local_rx_ctrl.conn_state_flags.peer_conn_abort) { + rc = -EPIPE; /* connection being aborted */ + goto out; + } if (conn->lgr->is_smcd) rc = smcd_tx_sndbuf_nonempty(conn); else @@ -590,15 +658,62 @@ int smc_tx_sndbuf_nonempty(struct smc_connection *conn) if (!rc) { /* trigger socket release if connection is closing */ - struct smc_sock *smc = container_of(conn, struct smc_sock, - conn); smc_close_wake_tx_prepared(smc); } + +out: + return rc; +} + +int smc_tx_sndbuf_nonempty(struct smc_connection *conn) +{ + int rc; + + /* This make sure only one can send simultaneously to prevent wasting + * of CPU and CDC slot. + * Record whether someone has tried to push while we are pushing. + */ + if (atomic_inc_return(&conn->tx_pushing) > 1) + return 0; + +again: + atomic_set(&conn->tx_pushing, 1); + smp_wmb(); /* Make sure tx_pushing is 1 before real send */ + rc = __smc_tx_sndbuf_nonempty(conn); + + /* We need to check whether someone else have added some data into + * the send queue and tried to push but failed after the atomic_set() + * when we are pushing. + * If so, we need to push again to prevent those data hang in the send + * queue. + */ + if (unlikely(!atomic_dec_and_test(&conn->tx_pushing))) + goto again; + return rc; } /* Wakeup sndbuf consumers from process context - * since there is more data to transmit + * since there is more data to transmit. The caller + * must hold sock lock. + */ +void smc_tx_pending(struct smc_connection *conn) +{ + struct smc_sock *smc = container_of(conn, struct smc_sock, conn); + int rc; + + if (smc->sk.sk_err) + return; + + rc = smc_tx_sndbuf_nonempty(conn); + if (!rc && conn->local_rx_ctrl.prod_flags.write_blocked && + !atomic_read(&conn->bytes_to_rcv)) + conn->local_rx_ctrl.prod_flags.write_blocked = 0; +} + +/* Wakeup sndbuf consumers from process context + * since there is more data to transmit in locked + * sock. */ void smc_tx_work(struct work_struct *work) { @@ -606,18 +721,9 @@ void smc_tx_work(struct work_struct *work) struct smc_connection, tx_work); struct smc_sock *smc = container_of(conn, struct smc_sock, conn); - int rc; lock_sock(&smc->sk); - if (smc->sk.sk_err) - goto out; - - rc = smc_tx_sndbuf_nonempty(conn); - if (!rc && conn->local_rx_ctrl.prod_flags.write_blocked && - !atomic_read(&conn->bytes_to_rcv)) - conn->local_rx_ctrl.prod_flags.write_blocked = 0; - -out: + smc_tx_pending(conn); release_sock(&smc->sk); } diff --git a/net/smc/smc_tx.h b/net/smc/smc_tx.h index 07e6ad76224a..34b578498b1f 100644 --- a/net/smc/smc_tx.h +++ b/net/smc/smc_tx.h @@ -27,9 +27,12 @@ static inline int smc_tx_prepared_sends(struct smc_connection *conn) return smc_curs_diff(conn->sndbuf_desc->len, &sent, &prep); } +void smc_tx_pending(struct smc_connection *conn); void smc_tx_work(struct work_struct *work); void smc_tx_init(struct smc_sock *smc); int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len); +int smc_tx_sendpage(struct smc_sock *smc, struct page *page, int offset, + size_t size, int flags); int smc_tx_sndbuf_nonempty(struct smc_connection *conn); void smc_tx_sndbuf_nonfull(struct smc_sock *smc); void smc_tx_consumer_update(struct smc_connection *conn, bool force); diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 5f42aa5fc612..8eb7e8544815 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -72,7 +72,8 @@ struct gss_auth { struct gss_api_mech *mech; enum rpc_gss_svc service; struct rpc_clnt *client; - struct net *net; + struct net *net; + netns_tracker ns_tracker; /* * There are two upcall pipes; dentry[1], named "gssd", is used * for the new text-based upcall; dentry[0] is named after the @@ -1013,7 +1014,8 @@ gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) goto err_free; } gss_auth->client = clnt; - gss_auth->net = get_net(rpc_net_ns(clnt)); + gss_auth->net = get_net_track(rpc_net_ns(clnt), &gss_auth->ns_tracker, + GFP_KERNEL); err = -EINVAL; gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor); if (!gss_auth->mech) @@ -1068,7 +1070,7 @@ gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) err_put_mech: gss_mech_put(gss_auth->mech); err_put_net: - put_net(gss_auth->net); + put_net_track(gss_auth->net, &gss_auth->ns_tracker); err_free: kfree(gss_auth->target_name); kfree(gss_auth); @@ -1084,7 +1086,7 @@ gss_free(struct gss_auth *gss_auth) gss_pipe_free(gss_auth->gss_pipe[0]); gss_pipe_free(gss_auth->gss_pipe[1]); gss_mech_put(gss_auth->mech); - put_net(gss_auth->net); + put_net_track(gss_auth->net, &gss_auth->ns_tracker); kfree(gss_auth->target_name); kfree(gss_auth); diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 0c117d3bfda8..297c49855038 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -162,7 +162,7 @@ static void svc_xprt_free(struct kref *kref) if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) svcauth_unix_info_release(xprt); put_cred(xprt->xpt_cred); - put_net(xprt->xpt_net); + put_net_track(xprt->xpt_net, &xprt->ns_tracker); /* See comment on corresponding get in xs_setup_bc_tcp(): */ if (xprt->xpt_bc_xprt) xprt_put(xprt->xpt_bc_xprt); @@ -198,7 +198,7 @@ void svc_xprt_init(struct net *net, struct svc_xprt_class *xcl, mutex_init(&xprt->xpt_mutex); spin_lock_init(&xprt->xpt_lock); set_bit(XPT_BUSY, &xprt->xpt_flags); - xprt->xpt_net = get_net(net); + xprt->xpt_net = get_net_track(net, &xprt->ns_tracker, GFP_ATOMIC); strcpy(xprt->xpt_remotebuf, "uninitialized"); } EXPORT_SYMBOL_GPL(svc_xprt_init); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index a02de2bddb28..5af484d6ba5e 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1835,7 +1835,7 @@ EXPORT_SYMBOL_GPL(xprt_alloc); void xprt_free(struct rpc_xprt *xprt) { - put_net(xprt->xprt_net); + put_net_track(xprt->xprt_net, &xprt->ns_tracker); xprt_free_all_slots(xprt); xprt_free_id(xprt); rpc_sysfs_xprt_destroy(xprt); @@ -2027,7 +2027,7 @@ static void xprt_init(struct rpc_xprt *xprt, struct net *net) xprt_init_xid(xprt); - xprt->xprt_net = get_net(net); + xprt->xprt_net = get_net_track(net, &xprt->ns_tracker, GFP_KERNEL); } /** diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index b62565278fac..474f76383033 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -85,7 +85,7 @@ static int switchdev_deferred_enqueue(struct net_device *dev, { struct switchdev_deferred_item *dfitem; - dfitem = kmalloc(sizeof(*dfitem) + data_len, GFP_ATOMIC); + dfitem = kmalloc(struct_size(dfitem, data, data_len), GFP_ATOMIC); if (!dfitem) return -ENOMEM; dfitem->dev = dev; @@ -409,10 +409,10 @@ static int switchdev_lower_dev_walk(struct net_device *lower_dev, } static struct net_device * -switchdev_lower_dev_find(struct net_device *dev, - bool (*check_cb)(const struct net_device *dev), - bool (*foreign_dev_check_cb)(const struct net_device *dev, - const struct net_device *foreign_dev)) +switchdev_lower_dev_find_rcu(struct net_device *dev, + bool (*check_cb)(const struct net_device *dev), + bool (*foreign_dev_check_cb)(const struct net_device *dev, + const struct net_device *foreign_dev)) { struct switchdev_nested_priv switchdev_priv = { .check_cb = check_cb, @@ -429,6 +429,27 @@ switchdev_lower_dev_find(struct net_device *dev, return switchdev_priv.lower_dev; } +static struct net_device * +switchdev_lower_dev_find(struct net_device *dev, + bool (*check_cb)(const struct net_device *dev), + bool (*foreign_dev_check_cb)(const struct net_device *dev, + const struct net_device *foreign_dev)) +{ + struct switchdev_nested_priv switchdev_priv = { + .check_cb = check_cb, + .foreign_dev_check_cb = foreign_dev_check_cb, + .dev = dev, + .lower_dev = NULL, + }; + struct netdev_nested_priv priv = { + .data = &switchdev_priv, + }; + + netdev_walk_all_lower_dev(dev, switchdev_lower_dev_walk, &priv); + + return switchdev_priv.lower_dev; +} + static int __switchdev_handle_fdb_event_to_device(struct net_device *dev, struct net_device *orig_dev, unsigned long event, const struct switchdev_notifier_fdb_info *fdb_info, @@ -437,63 +458,40 @@ static int __switchdev_handle_fdb_event_to_device(struct net_device *dev, const struct net_device *foreign_dev), int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev, unsigned long event, const void *ctx, - const struct switchdev_notifier_fdb_info *fdb_info), - int (*lag_mod_cb)(struct net_device *dev, struct net_device *orig_dev, - unsigned long event, const void *ctx, - const struct switchdev_notifier_fdb_info *fdb_info)) + const struct switchdev_notifier_fdb_info *fdb_info)) { const struct switchdev_notifier_info *info = &fdb_info->info; - struct net_device *br, *lower_dev; + struct net_device *br, *lower_dev, *switchdev; struct list_head *iter; int err = -EOPNOTSUPP; if (check_cb(dev)) return mod_cb(dev, orig_dev, event, info->ctx, fdb_info); - if (netif_is_lag_master(dev)) { - if (!switchdev_lower_dev_find(dev, check_cb, foreign_dev_check_cb)) - goto maybe_bridged_with_us; - - /* This is a LAG interface that we offload */ - if (!lag_mod_cb) - return -EOPNOTSUPP; - - return lag_mod_cb(dev, orig_dev, event, info->ctx, fdb_info); - } - /* Recurse through lower interfaces in case the FDB entry is pointing - * towards a bridge device. + * towards a bridge or a LAG device. */ - if (netif_is_bridge_master(dev)) { - if (!switchdev_lower_dev_find(dev, check_cb, foreign_dev_check_cb)) - return 0; + netdev_for_each_lower_dev(dev, lower_dev, iter) { + /* Do not propagate FDB entries across bridges */ + if (netif_is_bridge_master(lower_dev)) + continue; - /* This is a bridge interface that we offload */ - netdev_for_each_lower_dev(dev, lower_dev, iter) { - /* Do not propagate FDB entries across bridges */ - if (netif_is_bridge_master(lower_dev)) - continue; + /* Bridge ports might be either us, or LAG interfaces + * that we offload. + */ + if (!check_cb(lower_dev) && + !switchdev_lower_dev_find_rcu(lower_dev, check_cb, + foreign_dev_check_cb)) + continue; - /* Bridge ports might be either us, or LAG interfaces - * that we offload. - */ - if (!check_cb(lower_dev) && - !switchdev_lower_dev_find(lower_dev, check_cb, - foreign_dev_check_cb)) - continue; - - err = __switchdev_handle_fdb_event_to_device(lower_dev, orig_dev, - event, fdb_info, check_cb, - foreign_dev_check_cb, - mod_cb, lag_mod_cb); - if (err && err != -EOPNOTSUPP) - return err; - } - - return 0; + err = __switchdev_handle_fdb_event_to_device(lower_dev, orig_dev, + event, fdb_info, check_cb, + foreign_dev_check_cb, + mod_cb); + if (err && err != -EOPNOTSUPP) + return err; } -maybe_bridged_with_us: /* Event is neither on a bridge nor a LAG. Check whether it is on an * interface that is in a bridge with us. */ @@ -501,12 +499,16 @@ static int __switchdev_handle_fdb_event_to_device(struct net_device *dev, if (!br || !netif_is_bridge_master(br)) return 0; - if (!switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb)) + switchdev = switchdev_lower_dev_find_rcu(br, check_cb, foreign_dev_check_cb); + if (!switchdev) return 0; + if (!foreign_dev_check_cb(switchdev, dev)) + return err; + return __switchdev_handle_fdb_event_to_device(br, orig_dev, event, fdb_info, check_cb, foreign_dev_check_cb, - mod_cb, lag_mod_cb); + mod_cb); } int switchdev_handle_fdb_event_to_device(struct net_device *dev, unsigned long event, @@ -516,16 +518,13 @@ int switchdev_handle_fdb_event_to_device(struct net_device *dev, unsigned long e const struct net_device *foreign_dev), int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev, unsigned long event, const void *ctx, - const struct switchdev_notifier_fdb_info *fdb_info), - int (*lag_mod_cb)(struct net_device *dev, struct net_device *orig_dev, - unsigned long event, const void *ctx, - const struct switchdev_notifier_fdb_info *fdb_info)) + const struct switchdev_notifier_fdb_info *fdb_info)) { int err; err = __switchdev_handle_fdb_event_to_device(dev, dev, event, fdb_info, check_cb, foreign_dev_check_cb, - mod_cb, lag_mod_cb); + mod_cb); if (err == -EOPNOTSUPP) err = 0; @@ -536,13 +535,15 @@ EXPORT_SYMBOL_GPL(switchdev_handle_fdb_event_to_device); static int __switchdev_handle_port_obj_add(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), + bool (*foreign_dev_check_cb)(const struct net_device *dev, + const struct net_device *foreign_dev), int (*add_cb)(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack)) { struct switchdev_notifier_info *info = &port_obj_info->info; + struct net_device *br, *lower_dev, *switchdev; struct netlink_ext_ack *extack; - struct net_device *lower_dev; struct list_head *iter; int err = -EOPNOTSUPP; @@ -566,15 +567,46 @@ static int __switchdev_handle_port_obj_add(struct net_device *dev, if (netif_is_bridge_master(lower_dev)) continue; + /* When searching for switchdev interfaces that are neighbors + * of foreign ones, and @dev is a bridge, do not recurse on the + * foreign interface again, it was already visited. + */ + if (foreign_dev_check_cb && !check_cb(lower_dev) && + !switchdev_lower_dev_find(lower_dev, check_cb, foreign_dev_check_cb)) + continue; + err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info, - check_cb, add_cb); + check_cb, foreign_dev_check_cb, + add_cb); if (err && err != -EOPNOTSUPP) return err; } - return err; + /* Event is neither on a bridge nor a LAG. Check whether it is on an + * interface that is in a bridge with us. + */ + if (!foreign_dev_check_cb) + return err; + + br = netdev_master_upper_dev_get(dev); + if (!br || !netif_is_bridge_master(br)) + return err; + + switchdev = switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb); + if (!switchdev) + return err; + + if (!foreign_dev_check_cb(switchdev, dev)) + return err; + + return __switchdev_handle_port_obj_add(br, port_obj_info, check_cb, + foreign_dev_check_cb, add_cb); } +/* Pass through a port object addition, if @dev passes @check_cb, or replicate + * it towards all lower interfaces of @dev that pass @check_cb, if @dev is a + * bridge or a LAG. + */ int switchdev_handle_port_obj_add(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), @@ -585,21 +617,46 @@ int switchdev_handle_port_obj_add(struct net_device *dev, int err; err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb, - add_cb); + NULL, add_cb); if (err == -EOPNOTSUPP) err = 0; return err; } EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add); +/* Same as switchdev_handle_port_obj_add(), except if object is notified on a + * @dev that passes @foreign_dev_check_cb, it is replicated towards all devices + * that pass @check_cb and are in the same bridge as @dev. + */ +int switchdev_handle_port_obj_add_foreign(struct net_device *dev, + struct switchdev_notifier_port_obj_info *port_obj_info, + bool (*check_cb)(const struct net_device *dev), + bool (*foreign_dev_check_cb)(const struct net_device *dev, + const struct net_device *foreign_dev), + int (*add_cb)(struct net_device *dev, const void *ctx, + const struct switchdev_obj *obj, + struct netlink_ext_ack *extack)) +{ + int err; + + err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb, + foreign_dev_check_cb, add_cb); + if (err == -EOPNOTSUPP) + err = 0; + return err; +} +EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add_foreign); + static int __switchdev_handle_port_obj_del(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), + bool (*foreign_dev_check_cb)(const struct net_device *dev, + const struct net_device *foreign_dev), int (*del_cb)(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj)) { struct switchdev_notifier_info *info = &port_obj_info->info; - struct net_device *lower_dev; + struct net_device *br, *lower_dev, *switchdev; struct list_head *iter; int err = -EOPNOTSUPP; @@ -621,15 +678,46 @@ static int __switchdev_handle_port_obj_del(struct net_device *dev, if (netif_is_bridge_master(lower_dev)) continue; + /* When searching for switchdev interfaces that are neighbors + * of foreign ones, and @dev is a bridge, do not recurse on the + * foreign interface again, it was already visited. + */ + if (foreign_dev_check_cb && !check_cb(lower_dev) && + !switchdev_lower_dev_find(lower_dev, check_cb, foreign_dev_check_cb)) + continue; + err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info, - check_cb, del_cb); + check_cb, foreign_dev_check_cb, + del_cb); if (err && err != -EOPNOTSUPP) return err; } - return err; + /* Event is neither on a bridge nor a LAG. Check whether it is on an + * interface that is in a bridge with us. + */ + if (!foreign_dev_check_cb) + return err; + + br = netdev_master_upper_dev_get(dev); + if (!br || !netif_is_bridge_master(br)) + return err; + + switchdev = switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb); + if (!switchdev) + return err; + + if (!foreign_dev_check_cb(switchdev, dev)) + return err; + + return __switchdev_handle_port_obj_del(br, port_obj_info, check_cb, + foreign_dev_check_cb, del_cb); } +/* Pass through a port object deletion, if @dev passes @check_cb, or replicate + * it towards all lower interfaces of @dev that pass @check_cb, if @dev is a + * bridge or a LAG. + */ int switchdev_handle_port_obj_del(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), @@ -639,13 +727,35 @@ int switchdev_handle_port_obj_del(struct net_device *dev, int err; err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb, - del_cb); + NULL, del_cb); if (err == -EOPNOTSUPP) err = 0; return err; } EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del); +/* Same as switchdev_handle_port_obj_del(), except if object is notified on a + * @dev that passes @foreign_dev_check_cb, it is replicated towards all devices + * that pass @check_cb and are in the same bridge as @dev. + */ +int switchdev_handle_port_obj_del_foreign(struct net_device *dev, + struct switchdev_notifier_port_obj_info *port_obj_info, + bool (*check_cb)(const struct net_device *dev), + bool (*foreign_dev_check_cb)(const struct net_device *dev, + const struct net_device *foreign_dev), + int (*del_cb)(struct net_device *dev, const void *ctx, + const struct switchdev_obj *obj)) +{ + int err; + + err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb, + foreign_dev_check_cb, del_cb); + if (err == -EOPNOTSUPP) + err = 0; + return err; +} +EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del_foreign); + static int __switchdev_handle_port_attr_set(struct net_device *dev, struct switchdev_notifier_port_attr_info *port_attr_info, bool (*check_cb)(const struct net_device *dev), diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index a2f9c9640716..6d39ca05f249 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -770,7 +770,7 @@ void tipc_clone_to_loopback(struct net *net, struct sk_buff_head *pkts) skb->pkt_type = PACKET_HOST; skb->ip_summed = CHECKSUM_UNNECESSARY; skb->protocol = eth_type_trans(skb, dev); - netif_rx_ni(skb); + netif_rx(skb); } } diff --git a/net/tipc/msg.h b/net/tipc/msg.h index 64ae4c4c44f8..c5eec16213d7 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -226,14 +226,6 @@ static inline void msg_set_bits(struct tipc_msg *m, u32 w, m->hdr[w] |= htonl(val); } -static inline void msg_swap_words(struct tipc_msg *msg, u32 a, u32 b) -{ - u32 temp = msg->hdr[a]; - - msg->hdr[a] = msg->hdr[b]; - msg->hdr[b] = temp; -} - /* * Word 0 */ @@ -480,11 +472,6 @@ static inline void msg_incr_reroute_cnt(struct tipc_msg *m) msg_set_bits(m, 1, 21, 0xf, msg_reroute_cnt(m) + 1); } -static inline void msg_reset_reroute_cnt(struct tipc_msg *m) -{ - msg_set_bits(m, 1, 21, 0xf, 0); -} - static inline u32 msg_lookup_scope(struct tipc_msg *m) { return msg_bits(m, 1, 19, 0x3); @@ -800,11 +787,6 @@ static inline void msg_set_dest_domain(struct tipc_msg *m, u32 n) msg_set_word(m, 2, n); } -static inline u32 msg_bcgap_after(struct tipc_msg *m) -{ - return msg_bits(m, 2, 16, 0xffff); -} - static inline void msg_set_bcgap_after(struct tipc_msg *m, u32 n) { msg_set_bits(m, 2, 16, 0xffff, n); @@ -868,11 +850,6 @@ static inline void msg_set_next_sent(struct tipc_msg *m, u16 n) msg_set_bits(m, 4, 0, 0xffff, n); } -static inline void msg_set_long_msgno(struct tipc_msg *m, u32 n) -{ - msg_set_bits(m, 4, 0, 0xffff, n); -} - static inline u32 msg_bc_netid(struct tipc_msg *m) { return msg_word(m, 4); diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 7545321c3440..17f8c523e33b 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -2852,7 +2852,8 @@ static void tipc_sk_retry_connect(struct sock *sk, struct sk_buff_head *list) /* Try again later if dest link is congested */ if (tsk->cong_link_cnt) { - sk_reset_timer(sk, &sk->sk_timer, msecs_to_jiffies(100)); + sk_reset_timer(sk, &sk->sk_timer, + jiffies + msecs_to_jiffies(100)); return; } /* Prepare SYN for retransmit */ diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index b932469ee69c..12f7b56771d9 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -1028,20 +1028,21 @@ int tls_set_device_offload(struct sock *sk, struct tls_context *ctx) if (ctx->priv_ctx_tx) return -EEXIST; - start_marker_record = kmalloc(sizeof(*start_marker_record), GFP_KERNEL); - if (!start_marker_record) - return -ENOMEM; + netdev = get_netdev_for_sock(sk); + if (!netdev) { + pr_err_ratelimited("%s: netdev not found\n", __func__); + return -EINVAL; + } - offload_ctx = kzalloc(TLS_OFFLOAD_CONTEXT_SIZE_TX, GFP_KERNEL); - if (!offload_ctx) { - rc = -ENOMEM; - goto free_marker_record; + if (!(netdev->features & NETIF_F_HW_TLS_TX)) { + rc = -EOPNOTSUPP; + goto release_netdev; } crypto_info = &ctx->crypto_send.info; if (crypto_info->version != TLS_1_2_VERSION) { rc = -EOPNOTSUPP; - goto free_offload_ctx; + goto release_netdev; } switch (crypto_info->cipher_type) { @@ -1057,13 +1058,13 @@ int tls_set_device_offload(struct sock *sk, struct tls_context *ctx) break; default: rc = -EINVAL; - goto free_offload_ctx; + goto release_netdev; } /* Sanity-check the rec_seq_size for stack allocations */ if (rec_seq_size > TLS_MAX_REC_SEQ_SIZE) { rc = -EINVAL; - goto free_offload_ctx; + goto release_netdev; } prot->version = crypto_info->version; @@ -1077,7 +1078,7 @@ int tls_set_device_offload(struct sock *sk, struct tls_context *ctx) GFP_KERNEL); if (!ctx->tx.iv) { rc = -ENOMEM; - goto free_offload_ctx; + goto release_netdev; } memcpy(ctx->tx.iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, iv, iv_size); @@ -1089,9 +1090,21 @@ int tls_set_device_offload(struct sock *sk, struct tls_context *ctx) goto free_iv; } + start_marker_record = kmalloc(sizeof(*start_marker_record), GFP_KERNEL); + if (!start_marker_record) { + rc = -ENOMEM; + goto free_rec_seq; + } + + offload_ctx = kzalloc(TLS_OFFLOAD_CONTEXT_SIZE_TX, GFP_KERNEL); + if (!offload_ctx) { + rc = -ENOMEM; + goto free_marker_record; + } + rc = tls_sw_fallback_init(sk, offload_ctx, crypto_info); if (rc) - goto free_rec_seq; + goto free_offload_ctx; /* start at rec_seq - 1 to account for the start marker record */ memcpy(&rcd_sn, ctx->tx.rec_seq, sizeof(rcd_sn)); @@ -1118,18 +1131,6 @@ int tls_set_device_offload(struct sock *sk, struct tls_context *ctx) if (skb) TCP_SKB_CB(skb)->eor = 1; - netdev = get_netdev_for_sock(sk); - if (!netdev) { - pr_err_ratelimited("%s: netdev not found\n", __func__); - rc = -EINVAL; - goto disable_cad; - } - - if (!(netdev->features & NETIF_F_HW_TLS_TX)) { - rc = -EOPNOTSUPP; - goto release_netdev; - } - /* Avoid offloading if the device is down * We don't want to offload new flows after * the NETDEV_DOWN event @@ -1167,20 +1168,19 @@ int tls_set_device_offload(struct sock *sk, struct tls_context *ctx) release_lock: up_read(&device_offload_lock); -release_netdev: - dev_put(netdev); -disable_cad: clean_acked_data_disable(inet_csk(sk)); crypto_free_aead(offload_ctx->aead_send); -free_rec_seq: - kfree(ctx->tx.rec_seq); -free_iv: - kfree(ctx->tx.iv); free_offload_ctx: kfree(offload_ctx); ctx->priv_ctx_tx = NULL; free_marker_record: kfree(start_marker_record); +free_rec_seq: + kfree(ctx->tx.rec_seq); +free_iv: + kfree(ctx->tx.iv); +release_netdev: + dev_put(netdev); return rc; } diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index 6bc2879ba637..7b2b0e7ffee4 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -553,10 +553,8 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, int rc = 0; int conf; - if (sockptr_is_null(optval) || (optlen < sizeof(*crypto_info))) { - rc = -EINVAL; - goto out; - } + if (sockptr_is_null(optval) || (optlen < sizeof(*crypto_info))) + return -EINVAL; if (tx) { crypto_info = &ctx->crypto_send.info; @@ -567,10 +565,8 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, } /* Currently we don't support set crypto info more than one time */ - if (TLS_CRYPTO_INFO_READY(crypto_info)) { - rc = -EBUSY; - goto out; - } + if (TLS_CRYPTO_INFO_READY(crypto_info)) + return -EBUSY; rc = copy_from_sockptr(crypto_info, optval, sizeof(*crypto_info)); if (rc) { @@ -672,11 +668,10 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, ctx->sk_write_space = sk->sk_write_space; sk->sk_write_space = tls_write_space; } - goto out; + return 0; err_crypto_info: memzero_explicit(crypto_info, sizeof(union tls_crypto_context)); -out: return rc; } diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index efc84845bb6b..0024a692f0f8 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1433,7 +1433,8 @@ static int decrypt_internal(struct sock *sk, struct sk_buff *skb, if (*zc && (out_iov || out_sg)) { if (out_iov) - n_sgout = iov_iter_npages(out_iov, INT_MAX) + 1; + n_sgout = 1 + + iov_iter_npages_cap(out_iov, INT_MAX, data_len); else n_sgout = sg_nents(out_sg); n_sgin = skb_nsg(skb, rxm->offset + prot->prepend_size, diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index c19569819866..e71a312faa1e 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2049,7 +2049,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, */ #define UNIX_SKB_FRAGS_SZ (PAGE_SIZE << get_order(32768)) -#if (IS_ENABLED(CONFIG_AF_UNIX_OOB)) +#if IS_ENABLED(CONFIG_AF_UNIX_OOB) static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other) { struct unix_sock *ousk = unix_sk(other); @@ -2084,7 +2084,7 @@ static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other if (ousk->oob_skb) consume_skb(ousk->oob_skb); - ousk->oob_skb = skb; + WRITE_ONCE(ousk->oob_skb, skb); scm_stat_add(other, skb); skb_queue_tail(&other->sk_receive_queue, skb); @@ -2115,7 +2115,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, err = -EOPNOTSUPP; if (msg->msg_flags & MSG_OOB) { -#if (IS_ENABLED(CONFIG_AF_UNIX_OOB)) +#if IS_ENABLED(CONFIG_AF_UNIX_OOB) if (len) len--; else @@ -2186,7 +2186,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, sent += size; } -#if (IS_ENABLED(CONFIG_AF_UNIX_OOB)) +#if IS_ENABLED(CONFIG_AF_UNIX_OOB) if (msg->msg_flags & MSG_OOB) { err = queue_oob(sock, msg, other); if (err) @@ -2602,9 +2602,8 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state) oob_skb = u->oob_skb; - if (!(state->flags & MSG_PEEK)) { - u->oob_skb = NULL; - } + if (!(state->flags & MSG_PEEK)) + WRITE_ONCE(u->oob_skb, NULL); unix_state_unlock(sk); @@ -2639,7 +2638,7 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk, skb = NULL; } else if (sock_flag(sk, SOCK_URGINLINE)) { if (!(flags & MSG_PEEK)) { - u->oob_skb = NULL; + WRITE_ONCE(u->oob_skb, NULL); consume_skb(skb); } } else if (!(flags & MSG_PEEK)) { @@ -3094,11 +3093,10 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCATMARK: { struct sk_buff *skb; - struct unix_sock *u = unix_sk(sk); int answ = 0; skb = skb_peek(&sk->sk_receive_queue); - if (skb && skb == u->oob_skb) + if (skb && skb == READ_ONCE(unix_sk(sk)->oob_skb)) answ = 1; err = put_user(answ, (int __user *)arg); } @@ -3139,6 +3137,10 @@ static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wa mask |= EPOLLIN | EPOLLRDNORM; if (sk_is_readable(sk)) mask |= EPOLLIN | EPOLLRDNORM; +#if IS_ENABLED(CONFIG_AF_UNIX_OOB) + if (READ_ONCE(unix_sk(sk)->oob_skb)) + mask |= EPOLLPRI; +#endif /* Connection-based need to check for termination and startup */ if ((sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) && @@ -3240,49 +3242,58 @@ static struct sock *unix_from_bucket(struct seq_file *seq, loff_t *pos) return sk; } -static struct sock *unix_next_socket(struct seq_file *seq, - struct sock *sk, - loff_t *pos) +static struct sock *unix_get_first(struct seq_file *seq, loff_t *pos) { unsigned long bucket = get_bucket(*pos); + struct sock *sk; - while (sk > (struct sock *)SEQ_START_TOKEN) { - sk = sk_next(sk); - if (!sk) - goto next_bucket; - if (sock_net(sk) == seq_file_net(seq)) - return sk; - } - - do { + while (bucket < ARRAY_SIZE(unix_socket_table)) { spin_lock(&unix_table_locks[bucket]); + sk = unix_from_bucket(seq, pos); if (sk) return sk; -next_bucket: - spin_unlock(&unix_table_locks[bucket++]); - *pos = set_bucket_offset(bucket, 1); - } while (bucket < ARRAY_SIZE(unix_socket_table)); + spin_unlock(&unix_table_locks[bucket]); + + *pos = set_bucket_offset(++bucket, 1); + } return NULL; } +static struct sock *unix_get_next(struct seq_file *seq, struct sock *sk, + loff_t *pos) +{ + unsigned long bucket = get_bucket(*pos); + + for (sk = sk_next(sk); sk; sk = sk_next(sk)) + if (sock_net(sk) == seq_file_net(seq)) + return sk; + + spin_unlock(&unix_table_locks[bucket]); + + *pos = set_bucket_offset(++bucket, 1); + + return unix_get_first(seq, pos); +} + static void *unix_seq_start(struct seq_file *seq, loff_t *pos) { if (!*pos) return SEQ_START_TOKEN; - if (get_bucket(*pos) >= ARRAY_SIZE(unix_socket_table)) - return NULL; - - return unix_next_socket(seq, NULL, pos); + return unix_get_first(seq, pos); } static void *unix_seq_next(struct seq_file *seq, void *v, loff_t *pos) { ++*pos; - return unix_next_socket(seq, v, pos); + + if (v == SEQ_START_TOKEN) + return unix_get_first(seq, pos); + + return unix_get_next(seq, v, pos); } static void unix_seq_stop(struct seq_file *seq, void *v) @@ -3347,6 +3358,15 @@ static const struct seq_operations unix_seq_ops = { }; #if IS_BUILTIN(CONFIG_UNIX) && defined(CONFIG_BPF_SYSCALL) +struct bpf_unix_iter_state { + struct seq_net_private p; + unsigned int cur_sk; + unsigned int end_sk; + unsigned int max_sk; + struct sock **batch; + bool st_bucket_done; +}; + struct bpf_iter__unix { __bpf_md_ptr(struct bpf_iter_meta *, meta); __bpf_md_ptr(struct unix_sock *, unix_sk); @@ -3365,24 +3385,156 @@ static int unix_prog_seq_show(struct bpf_prog *prog, struct bpf_iter_meta *meta, return bpf_iter_run_prog(prog, &ctx); } +static int bpf_iter_unix_hold_batch(struct seq_file *seq, struct sock *start_sk) + +{ + struct bpf_unix_iter_state *iter = seq->private; + unsigned int expected = 1; + struct sock *sk; + + sock_hold(start_sk); + iter->batch[iter->end_sk++] = start_sk; + + for (sk = sk_next(start_sk); sk; sk = sk_next(sk)) { + if (sock_net(sk) != seq_file_net(seq)) + continue; + + if (iter->end_sk < iter->max_sk) { + sock_hold(sk); + iter->batch[iter->end_sk++] = sk; + } + + expected++; + } + + spin_unlock(&unix_table_locks[start_sk->sk_hash]); + + return expected; +} + +static void bpf_iter_unix_put_batch(struct bpf_unix_iter_state *iter) +{ + while (iter->cur_sk < iter->end_sk) + sock_put(iter->batch[iter->cur_sk++]); +} + +static int bpf_iter_unix_realloc_batch(struct bpf_unix_iter_state *iter, + unsigned int new_batch_sz) +{ + struct sock **new_batch; + + new_batch = kvmalloc(sizeof(*new_batch) * new_batch_sz, + GFP_USER | __GFP_NOWARN); + if (!new_batch) + return -ENOMEM; + + bpf_iter_unix_put_batch(iter); + kvfree(iter->batch); + iter->batch = new_batch; + iter->max_sk = new_batch_sz; + + return 0; +} + +static struct sock *bpf_iter_unix_batch(struct seq_file *seq, + loff_t *pos) +{ + struct bpf_unix_iter_state *iter = seq->private; + unsigned int expected; + bool resized = false; + struct sock *sk; + + if (iter->st_bucket_done) + *pos = set_bucket_offset(get_bucket(*pos) + 1, 1); + +again: + /* Get a new batch */ + iter->cur_sk = 0; + iter->end_sk = 0; + + sk = unix_get_first(seq, pos); + if (!sk) + return NULL; /* Done */ + + expected = bpf_iter_unix_hold_batch(seq, sk); + + if (iter->end_sk == expected) { + iter->st_bucket_done = true; + return sk; + } + + if (!resized && !bpf_iter_unix_realloc_batch(iter, expected * 3 / 2)) { + resized = true; + goto again; + } + + return sk; +} + +static void *bpf_iter_unix_seq_start(struct seq_file *seq, loff_t *pos) +{ + if (!*pos) + return SEQ_START_TOKEN; + + /* bpf iter does not support lseek, so it always + * continue from where it was stop()-ped. + */ + return bpf_iter_unix_batch(seq, pos); +} + +static void *bpf_iter_unix_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct bpf_unix_iter_state *iter = seq->private; + struct sock *sk; + + /* Whenever seq_next() is called, the iter->cur_sk is + * done with seq_show(), so advance to the next sk in + * the batch. + */ + if (iter->cur_sk < iter->end_sk) + sock_put(iter->batch[iter->cur_sk++]); + + ++*pos; + + if (iter->cur_sk < iter->end_sk) + sk = iter->batch[iter->cur_sk]; + else + sk = bpf_iter_unix_batch(seq, pos); + + return sk; +} + static int bpf_iter_unix_seq_show(struct seq_file *seq, void *v) { struct bpf_iter_meta meta; struct bpf_prog *prog; struct sock *sk = v; uid_t uid; + bool slow; + int ret; if (v == SEQ_START_TOKEN) return 0; + slow = lock_sock_fast(sk); + + if (unlikely(sk_unhashed(sk))) { + ret = SEQ_SKIP; + goto unlock; + } + uid = from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)); meta.seq = seq; prog = bpf_iter_get_info(&meta, false); - return unix_prog_seq_show(prog, &meta, v, uid); + ret = unix_prog_seq_show(prog, &meta, v, uid); +unlock: + unlock_sock_fast(sk, slow); + return ret; } static void bpf_iter_unix_seq_stop(struct seq_file *seq, void *v) { + struct bpf_unix_iter_state *iter = seq->private; struct bpf_iter_meta meta; struct bpf_prog *prog; @@ -3393,12 +3545,13 @@ static void bpf_iter_unix_seq_stop(struct seq_file *seq, void *v) (void)unix_prog_seq_show(prog, &meta, v, 0); } - unix_seq_stop(seq, v); + if (iter->cur_sk < iter->end_sk) + bpf_iter_unix_put_batch(iter); } static const struct seq_operations bpf_iter_unix_seq_ops = { - .start = unix_seq_start, - .next = unix_seq_next, + .start = bpf_iter_unix_seq_start, + .next = bpf_iter_unix_seq_next, .stop = bpf_iter_unix_seq_stop, .show = bpf_iter_unix_seq_show, }; @@ -3447,13 +3600,55 @@ static struct pernet_operations unix_net_ops = { DEFINE_BPF_ITER_FUNC(unix, struct bpf_iter_meta *meta, struct unix_sock *unix_sk, uid_t uid) +#define INIT_BATCH_SZ 16 + +static int bpf_iter_init_unix(void *priv_data, struct bpf_iter_aux_info *aux) +{ + struct bpf_unix_iter_state *iter = priv_data; + int err; + + err = bpf_iter_init_seq_net(priv_data, aux); + if (err) + return err; + + err = bpf_iter_unix_realloc_batch(iter, INIT_BATCH_SZ); + if (err) { + bpf_iter_fini_seq_net(priv_data); + return err; + } + + return 0; +} + +static void bpf_iter_fini_unix(void *priv_data) +{ + struct bpf_unix_iter_state *iter = priv_data; + + bpf_iter_fini_seq_net(priv_data); + kvfree(iter->batch); +} + static const struct bpf_iter_seq_info unix_seq_info = { .seq_ops = &bpf_iter_unix_seq_ops, - .init_seq_private = bpf_iter_init_seq_net, - .fini_seq_private = bpf_iter_fini_seq_net, - .seq_priv_size = sizeof(struct seq_net_private), + .init_seq_private = bpf_iter_init_unix, + .fini_seq_private = bpf_iter_fini_unix, + .seq_priv_size = sizeof(struct bpf_unix_iter_state), }; +static const struct bpf_func_proto * +bpf_iter_unix_get_func_proto(enum bpf_func_id func_id, + const struct bpf_prog *prog) +{ + switch (func_id) { + case BPF_FUNC_setsockopt: + return &bpf_sk_setsockopt_proto; + case BPF_FUNC_getsockopt: + return &bpf_sk_getsockopt_proto; + default: + return NULL; + } +} + static struct bpf_iter_reg unix_reg_info = { .target = "unix", .ctx_arg_info_size = 1, @@ -3461,6 +3656,7 @@ static struct bpf_iter_reg unix_reg_info = { { offsetof(struct bpf_iter__unix, unix_sk), PTR_TO_BTF_ID_OR_NULL }, }, + .get_func_proto = bpf_iter_unix_get_func_proto, .seq_info = &unix_seq_info, }; diff --git a/net/wireless/chan.c b/net/wireless/chan.c index eb822052d344..8b7fb4a9e07b 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -181,6 +181,9 @@ static int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width) case NL80211_CHAN_WIDTH_160: mhz = 160; break; + case NL80211_CHAN_WIDTH_320: + mhz = 320; + break; default: WARN_ON_ONCE(1); return -1; @@ -271,6 +274,17 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) case NL80211_CHAN_WIDTH_16: /* all checked above */ break; + case NL80211_CHAN_WIDTH_320: + if (chandef->center_freq1 == control_freq + 150 || + chandef->center_freq1 == control_freq + 130 || + chandef->center_freq1 == control_freq + 110 || + chandef->center_freq1 == control_freq + 90 || + chandef->center_freq1 == control_freq - 90 || + chandef->center_freq1 == control_freq - 110 || + chandef->center_freq1 == control_freq - 130 || + chandef->center_freq1 == control_freq - 150) + break; + fallthrough; case NL80211_CHAN_WIDTH_160: if (chandef->center_freq1 == control_freq + 70 || chandef->center_freq1 == control_freq + 50 || @@ -307,7 +321,7 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) EXPORT_SYMBOL(cfg80211_chandef_valid); static void chandef_primary_freqs(const struct cfg80211_chan_def *c, - u32 *pri40, u32 *pri80) + u32 *pri40, u32 *pri80, u32 *pri160) { int tmp; @@ -315,9 +329,11 @@ static void chandef_primary_freqs(const struct cfg80211_chan_def *c, case NL80211_CHAN_WIDTH_40: *pri40 = c->center_freq1; *pri80 = 0; + *pri160 = 0; break; case NL80211_CHAN_WIDTH_80: case NL80211_CHAN_WIDTH_80P80: + *pri160 = 0; *pri80 = c->center_freq1; /* n_P20 */ tmp = (30 + c->chan->center_freq - c->center_freq1)/20; @@ -327,6 +343,7 @@ static void chandef_primary_freqs(const struct cfg80211_chan_def *c, *pri40 = c->center_freq1 - 20 + 40 * tmp; break; case NL80211_CHAN_WIDTH_160: + *pri160 = c->center_freq1; /* n_P20 */ tmp = (70 + c->chan->center_freq - c->center_freq1)/20; /* n_P40 */ @@ -337,6 +354,20 @@ static void chandef_primary_freqs(const struct cfg80211_chan_def *c, tmp /= 2; *pri80 = c->center_freq1 - 40 + 80 * tmp; break; + case NL80211_CHAN_WIDTH_320: + /* n_P20 */ + tmp = (150 + c->chan->center_freq - c->center_freq1) / 20; + /* n_P40 */ + tmp /= 2; + /* freq_P40 */ + *pri40 = c->center_freq1 - 140 + 40 * tmp; + /* n_P80 */ + tmp /= 2; + *pri80 = c->center_freq1 - 120 + 80 * tmp; + /* n_P160 */ + tmp /= 2; + *pri160 = c->center_freq1 - 80 + 160 * tmp; + break; default: WARN_ON_ONCE(1); } @@ -346,7 +377,7 @@ const struct cfg80211_chan_def * cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, const struct cfg80211_chan_def *c2) { - u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80; + u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80, c1_pri160, c2_pri160; /* If they are identical, return */ if (cfg80211_chandef_identical(c1, c2)) @@ -381,14 +412,31 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, c2->width == NL80211_CHAN_WIDTH_20) return c1; - chandef_primary_freqs(c1, &c1_pri40, &c1_pri80); - chandef_primary_freqs(c2, &c2_pri40, &c2_pri80); + chandef_primary_freqs(c1, &c1_pri40, &c1_pri80, &c1_pri160); + chandef_primary_freqs(c2, &c2_pri40, &c2_pri80, &c2_pri160); if (c1_pri40 != c2_pri40) return NULL; - WARN_ON(!c1_pri80 && !c2_pri80); - if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80) + if (c1->width == NL80211_CHAN_WIDTH_40) + return c2; + + if (c2->width == NL80211_CHAN_WIDTH_40) + return c1; + + if (c1_pri80 != c2_pri80) + return NULL; + + if (c1->width == NL80211_CHAN_WIDTH_80 && + c2->width > NL80211_CHAN_WIDTH_80) + return c2; + + if (c2->width == NL80211_CHAN_WIDTH_80 && + c1->width > NL80211_CHAN_WIDTH_80) + return c1; + + WARN_ON(!c1_pri160 && !c2_pri160); + if (c1_pri160 && c2_pri160 && c1_pri160 != c2_pri160) return NULL; if (c1->width > c2->width) @@ -960,7 +1008,10 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, struct ieee80211_sta_vht_cap *vht_cap; struct ieee80211_edmg *edmg_cap; u32 width, control_freq, cap; - bool ext_nss_cap, support_80_80 = false; + bool ext_nss_cap, support_80_80 = false, support_320 = false; + const struct ieee80211_sband_iftype_data *iftd; + struct ieee80211_supported_band *sband; + int i; if (WARN_ON(!cfg80211_chandef_valid(chandef))) return false; @@ -1062,6 +1113,32 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK))) return false; break; + case NL80211_CHAN_WIDTH_320: + prohibited_flags |= IEEE80211_CHAN_NO_320MHZ; + width = 320; + + if (chandef->chan->band != NL80211_BAND_6GHZ) + return false; + + sband = wiphy->bands[NL80211_BAND_6GHZ]; + if (!sband) + return false; + + for (i = 0; i < sband->n_iftype_data; i++) { + iftd = &sband->iftype_data[i]; + if (!iftd->eht_cap.has_eht) + continue; + + if (iftd->eht_cap.eht_cap_elem.phy_cap_info[0] & + IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) { + support_320 = true; + break; + } + } + + if (!support_320) + return false; + break; default: WARN_ON_ONCE(1); return false; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c01fbcc848e8..ee1c2b6b6971 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5,7 +5,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2022 Intel Corporation */ #include @@ -285,6 +285,15 @@ static int validate_ie_attr(const struct nlattr *attr, return -EINVAL; } +static int validate_he_capa(const struct nlattr *attr, + struct netlink_ext_ack *extack) +{ + if (!ieee80211_he_capa_size_ok(nla_data(attr), nla_len(attr))) + return -EINVAL; + + return 0; +} + /* policy for the attributes */ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR]; @@ -730,9 +739,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_TXQ_MEMORY_LIMIT] = { .type = NLA_U32 }, [NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 }, [NL80211_ATTR_HE_CAPABILITY] = - NLA_POLICY_RANGE(NLA_BINARY, - NL80211_HE_MIN_CAPABILITY_LEN, - NL80211_HE_MAX_CAPABILITY_LEN), + NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_he_capa, + NL80211_HE_MAX_CAPABILITY_LEN), [NL80211_ATTR_FTM_RESPONDER] = NLA_POLICY_NESTED(nl80211_ftm_responder_policy), [NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1), @@ -778,6 +786,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED }, [NL80211_ATTR_RADAR_BACKGROUND] = { .type = NLA_FLAG }, [NL80211_ATTR_AP_SETTINGS_FLAGS] = { .type = NLA_U32 }, + [NL80211_ATTR_EHT_CAPABILITY] = + NLA_POLICY_RANGE(NLA_BINARY, + NL80211_EHT_MIN_CAPABILITY_LEN, + NL80211_EHT_MAX_CAPABILITY_LEN), }; /* policy for the key attributes */ @@ -1148,6 +1160,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, if ((chan->flags & IEEE80211_CHAN_16MHZ) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_16MHZ)) goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_NO_320MHZ) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_320MHZ)) + goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_NO_EHT) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_EHT)) + goto nla_put_failure; } if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, @@ -1729,6 +1747,7 @@ nl80211_send_iftype_data(struct sk_buff *msg, const struct ieee80211_sband_iftype_data *iftdata) { const struct ieee80211_sta_he_cap *he_cap = &iftdata->he_cap; + const struct ieee80211_sta_eht_cap *eht_cap = &iftdata->eht_cap; if (nl80211_put_iftypes(msg, NL80211_BAND_IFTYPE_ATTR_IFTYPES, iftdata->types_mask)) @@ -1749,6 +1768,32 @@ nl80211_send_iftype_data(struct sk_buff *msg, return -ENOBUFS; } + if (eht_cap->has_eht && he_cap->has_he) { + u8 mcs_nss_size, ppe_thresh_size; + u16 ppe_thres_hdr; + + mcs_nss_size = + ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, + &eht_cap->eht_cap_elem); + + ppe_thres_hdr = get_unaligned_le16(&eht_cap->eht_ppe_thres[0]); + ppe_thresh_size = + ieee80211_eht_ppe_size(ppe_thres_hdr, + eht_cap->eht_cap_elem.phy_cap_info); + + if (nla_put(msg, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC, + sizeof(eht_cap->eht_cap_elem.mac_cap_info), + eht_cap->eht_cap_elem.mac_cap_info) || + nla_put(msg, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY, + sizeof(eht_cap->eht_cap_elem.phy_cap_info), + eht_cap->eht_cap_elem.phy_cap_info) || + nla_put(msg, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET, + mcs_nss_size, &eht_cap->eht_mcs_nss_supp) || + nla_put(msg, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE, + ppe_thresh_size, eht_cap->eht_ppe_thres)) + return -ENOBUFS; + } + if (sband->band == NL80211_BAND_6GHZ && nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA, sizeof(iftdata->he_6ghz_capa), @@ -5919,6 +5964,14 @@ bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr) case RATE_INFO_BW_HE_RU: rate_flg = 0; WARN_ON(!(info->flags & RATE_INFO_FLAGS_HE_MCS)); + break; + case RATE_INFO_BW_320: + rate_flg = NL80211_RATE_INFO_320_MHZ_WIDTH; + break; + case RATE_INFO_BW_EHT_RU: + rate_flg = 0; + WARN_ON(!(info->flags & RATE_INFO_FLAGS_EHT_MCS)); + break; } if (rate_flg && nla_put_flag(msg, rate_flg)) @@ -5951,6 +6004,17 @@ bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr) nla_put_u8(msg, NL80211_RATE_INFO_HE_RU_ALLOC, info->he_ru_alloc)) return false; + } else if (info->flags & RATE_INFO_FLAGS_EHT_MCS) { + if (nla_put_u8(msg, NL80211_RATE_INFO_EHT_MCS, info->mcs)) + return false; + if (nla_put_u8(msg, NL80211_RATE_INFO_EHT_NSS, info->nss)) + return false; + if (nla_put_u8(msg, NL80211_RATE_INFO_EHT_GI, info->eht_gi)) + return false; + if (info->bw == RATE_INFO_BW_EHT_RU && + nla_put_u8(msg, NL80211_RATE_INFO_EHT_RU_ALLOC, + info->eht_ru_alloc)) + return false; } nla_nest_end(msg, rate); @@ -6365,7 +6429,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy, if (params->supported_rates) return -EINVAL; if (params->ext_capab || params->ht_capa || params->vht_capa || - params->he_capa) + params->he_capa || params->eht_capa) return -EINVAL; } @@ -6568,6 +6632,18 @@ static int nl80211_set_station_tdls(struct genl_info *info, nla_data(info->attrs[NL80211_ATTR_HE_CAPABILITY]); params->he_capa_len = nla_len(info->attrs[NL80211_ATTR_HE_CAPABILITY]); + + if (info->attrs[NL80211_ATTR_EHT_CAPABILITY]) { + params->eht_capa = + nla_data(info->attrs[NL80211_ATTR_EHT_CAPABILITY]); + params->eht_capa_len = + nla_len(info->attrs[NL80211_ATTR_EHT_CAPABILITY]); + + if (!ieee80211_eht_capa_size_ok((const u8 *)params->he_capa, + (const u8 *)params->eht_capa, + params->eht_capa_len)) + return -EINVAL; + } } err = nl80211_parse_sta_channel_info(info, params); @@ -6825,6 +6901,18 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) nla_data(info->attrs[NL80211_ATTR_HE_CAPABILITY]); params.he_capa_len = nla_len(info->attrs[NL80211_ATTR_HE_CAPABILITY]); + + if (info->attrs[NL80211_ATTR_EHT_CAPABILITY]) { + params.eht_capa = + nla_data(info->attrs[NL80211_ATTR_EHT_CAPABILITY]); + params.eht_capa_len = + nla_len(info->attrs[NL80211_ATTR_EHT_CAPABILITY]); + + if (!ieee80211_eht_capa_size_ok((const u8 *)params.he_capa, + (const u8 *)params.eht_capa, + params.eht_capa_len)) + return -EINVAL; + } } if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]) @@ -6874,8 +6962,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.ht_capa = NULL; params.vht_capa = NULL; - /* HE requires WME */ - if (params.he_capa_len || params.he_6ghz_capa) + /* HE and EHT require WME */ + if (params.he_capa_len || params.he_6ghz_capa || + params.eht_capa_len) return -EINVAL; } @@ -7948,6 +8037,7 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev; struct wiphy *wiphy = NULL; struct sk_buff *msg; + int err = -EMSGSIZE; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); @@ -7966,34 +8056,35 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info) rdev = cfg80211_get_dev_from_info(genl_info_net(info), info); if (IS_ERR(rdev)) { - nlmsg_free(msg); - rtnl_unlock(); - return PTR_ERR(rdev); + err = PTR_ERR(rdev); + goto nla_put_failure; } wiphy = &rdev->wiphy; self_managed = wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED; + + rcu_read_lock(); + regdom = get_wiphy_regdom(wiphy); /* a self-managed-reg device must have a private regdom */ if (WARN_ON(!regdom && self_managed)) { - nlmsg_free(msg); - rtnl_unlock(); - return -EINVAL; + err = -EINVAL; + goto nla_put_failure_rcu; } if (regdom && nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy))) - goto nla_put_failure; + goto nla_put_failure_rcu; + } else { + rcu_read_lock(); } if (!wiphy && reg_last_request_cell_base() && nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, NL80211_USER_REG_HINT_CELL_BASE)) - goto nla_put_failure; - - rcu_read_lock(); + goto nla_put_failure_rcu; if (!regdom) regdom = rcu_dereference(cfg80211_regdomain); @@ -8013,7 +8104,7 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info) rtnl_unlock(); put_failure: nlmsg_free(msg); - return -EMSGSIZE; + return err; } static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb, @@ -8059,19 +8150,19 @@ static int nl80211_get_reg_dump(struct sk_buff *skb, struct cfg80211_registered_device *rdev; int err, reg_idx, start = cb->args[2]; - rtnl_lock(); + rcu_read_lock(); if (cfg80211_regdomain && start == 0) { err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq, NLM_F_MULTI, NULL, - rtnl_dereference(cfg80211_regdomain)); + rcu_dereference(cfg80211_regdomain)); if (err < 0) goto out_err; } /* the global regdom is idx 0 */ reg_idx = 1; - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { regdom = get_wiphy_regdom(&rdev->wiphy); if (!regdom) continue; @@ -8090,7 +8181,7 @@ static int nl80211_get_reg_dump(struct sk_buff *skb, cb->args[2] = reg_idx; err = skb->len; out_err: - rtnl_unlock(); + rcu_read_unlock(); return err; } @@ -10552,6 +10643,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) NL80211_EXT_FEATURE_VHT_IBSS)) return -EINVAL; break; + case NL80211_CHAN_WIDTH_320: + return -EINVAL; default: return -EINVAL; } diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index 328cf54bda82..2bc647720cda 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -2,8 +2,6 @@ /* * Copyright (C) 2018 - 2021 Intel Corporation */ -#ifndef __PMSR_H -#define __PMSR_H #include #include "core.h" #include "nl80211.h" @@ -661,5 +659,3 @@ void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid) } spin_unlock_bh(&wdev->pmsr_lock); } - -#endif /* __PMSR_H */ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index ec25924a1c26..c76cd973f06e 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1238,6 +1238,8 @@ unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd, { unsigned int bw = reg_get_max_bandwidth_from_range(rd, rule); + if (rule->flags & NL80211_RRF_NO_320MHZ) + bw = min_t(unsigned int, bw, MHZ_TO_KHZ(160)); if (rule->flags & NL80211_RRF_NO_160MHZ) bw = min_t(unsigned int, bw, MHZ_TO_KHZ(80)); if (rule->flags & NL80211_RRF_NO_80MHZ) @@ -1611,6 +1613,8 @@ static u32 map_regdom_flags(u32 rd_flags) channel_flags |= IEEE80211_CHAN_NO_160MHZ; if (rd_flags & NL80211_RRF_NO_HE) channel_flags |= IEEE80211_CHAN_NO_HE; + if (rd_flags & NL80211_RRF_NO_320MHZ) + channel_flags |= IEEE80211_CHAN_NO_320MHZ; return channel_flags; } @@ -1773,6 +1777,8 @@ static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd bw_flags |= IEEE80211_CHAN_NO_80MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(160)) bw_flags |= IEEE80211_CHAN_NO_160MHZ; + if (max_bandwidth_khz < MHZ_TO_KHZ(320)) + bw_flags |= IEEE80211_CHAN_NO_320MHZ; } return bw_flags; } diff --git a/net/wireless/scan.c b/net/wireless/scan.c index b888522f133b..b2fdac96bab0 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -700,8 +700,12 @@ static bool cfg80211_find_ssid_match(struct cfg80211_colocated_ap *ap, for (i = 0; i < request->n_ssids; i++) { /* wildcard ssid in the scan request */ - if (!request->ssids[i].ssid_len) + if (!request->ssids[i].ssid_len) { + if (ap->multi_bss && !ap->transmitted_bssid) + continue; + return true; + } if (ap->ssid_len && ap->ssid_len == request->ssids[i].ssid_len) { @@ -827,6 +831,9 @@ static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev) !cfg80211_find_ssid_match(ap, request)) continue; + if (!request->n_ssids && ap->multi_bss && !ap->transmitted_bssid) + continue; + cfg80211_scan_req_add_chan(request, chan, true); memcpy(scan_6ghz_params->bssid, ap->bssid, ETH_ALEN); scan_6ghz_params->short_ssid = ap->short_ssid; diff --git a/net/wireless/util.c b/net/wireless/util.c index 41ea65deb6e1..a60d7d638e72 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -5,7 +5,7 @@ * Copyright 2007-2009 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2020 Intel Corporation + * Copyright (C) 2018-2021 Intel Corporation */ #include #include @@ -634,12 +634,14 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, if (likely((!is_amsdu && ether_addr_equal(payload.hdr, rfc1042_header) && tmp.h_proto != htons(ETH_P_AARP) && tmp.h_proto != htons(ETH_P_IPX)) || - ether_addr_equal(payload.hdr, bridge_tunnel_header))) + ether_addr_equal(payload.hdr, bridge_tunnel_header))) { /* remove RFC1042 or Bridge-Tunnel encapsulation and * replace EtherType */ hdrlen += ETH_ALEN + 2; - else + skb_postpull_rcsum(skb, &payload, ETH_ALEN + 2); + } else { tmp.h_proto = htons(skb->len - hdrlen); + } pskb_pull(skb, hdrlen); @@ -1428,6 +1430,135 @@ static u32 cfg80211_calculate_bitrate_he(struct rate_info *rate) return result / 10000; } +static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate) +{ +#define SCALE 6144 + static const u32 mcs_divisors[16] = { + 102399, /* 16.666666... */ + 51201, /* 8.333333... */ + 34134, /* 5.555555... */ + 25599, /* 4.166666... */ + 17067, /* 2.777777... */ + 12801, /* 2.083333... */ + 11769, /* 1.851851... */ + 10239, /* 1.666666... */ + 8532, /* 1.388888... */ + 7680, /* 1.250000... */ + 6828, /* 1.111111... */ + 6144, /* 1.000000... */ + 5690, /* 0.926106... */ + 5120, /* 0.833333... */ + 409600, /* 66.666666... */ + 204800, /* 33.333333... */ + }; + static const u32 rates_996[3] = { 480388888, 453700000, 408333333 }; + static const u32 rates_484[3] = { 229411111, 216666666, 195000000 }; + static const u32 rates_242[3] = { 114711111, 108333333, 97500000 }; + static const u32 rates_106[3] = { 40000000, 37777777, 34000000 }; + static const u32 rates_52[3] = { 18820000, 17777777, 16000000 }; + static const u32 rates_26[3] = { 9411111, 8888888, 8000000 }; + u64 tmp; + u32 result; + + if (WARN_ON_ONCE(rate->mcs > 15)) + return 0; + if (WARN_ON_ONCE(rate->eht_gi > NL80211_RATE_INFO_EHT_GI_3_2)) + return 0; + if (WARN_ON_ONCE(rate->eht_ru_alloc > + NL80211_RATE_INFO_EHT_RU_ALLOC_4x996)) + return 0; + if (WARN_ON_ONCE(rate->nss < 1 || rate->nss > 8)) + return 0; + + /* Bandwidth checks for MCS 14 */ + if (rate->mcs == 14) { + if ((rate->bw != RATE_INFO_BW_EHT_RU && + rate->bw != RATE_INFO_BW_80 && + rate->bw != RATE_INFO_BW_160 && + rate->bw != RATE_INFO_BW_320) || + (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc != NL80211_RATE_INFO_EHT_RU_ALLOC_996 && + rate->eht_ru_alloc != NL80211_RATE_INFO_EHT_RU_ALLOC_2x996 && + rate->eht_ru_alloc != NL80211_RATE_INFO_EHT_RU_ALLOC_4x996)) { + WARN(1, "invalid EHT BW for MCS 14: bw:%d, ru:%d\n", + rate->bw, rate->eht_ru_alloc); + return 0; + } + } + + if (rate->bw == RATE_INFO_BW_320 || + (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_4x996)) + result = 4 * rates_996[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484) + result = 3 * rates_996[rate->eht_gi] + rates_484[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_3x996) + result = 3 * rates_996[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484) + result = 2 * rates_996[rate->eht_gi] + rates_484[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_160 || + (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_2x996)) + result = 2 * rates_996[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == + NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242) + result = rates_996[rate->eht_gi] + rates_484[rate->eht_gi] + + rates_242[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_996P484) + result = rates_996[rate->eht_gi] + rates_484[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_80 || + (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_996)) + result = rates_996[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_484P242) + result = rates_484[rate->eht_gi] + rates_242[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_40 || + (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_484)) + result = rates_484[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_20 || + (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_242)) + result = rates_242[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_106P26) + result = rates_106[rate->eht_gi] + rates_26[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_106) + result = rates_106[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_52P26) + result = rates_52[rate->eht_gi] + rates_26[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_52) + result = rates_52[rate->eht_gi]; + else if (rate->bw == RATE_INFO_BW_EHT_RU && + rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_26) + result = rates_26[rate->eht_gi]; + else { + WARN(1, "invalid EHT MCS: bw:%d, ru:%d\n", + rate->bw, rate->eht_ru_alloc); + return 0; + } + + /* now scale to the appropriate MCS */ + tmp = result; + tmp *= SCALE; + do_div(tmp, mcs_divisors[rate->mcs]); + result = tmp; + + /* and take NSS */ + result = (result * rate->nss) / 8; + + return result / 10000; +} + u32 cfg80211_calculate_bitrate(struct rate_info *rate) { if (rate->flags & RATE_INFO_FLAGS_MCS) @@ -1442,6 +1573,8 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate) return cfg80211_calculate_bitrate_vht(rate); if (rate->flags & RATE_INFO_FLAGS_HE_MCS) return cfg80211_calculate_bitrate_he(rate); + if (rate->flags & RATE_INFO_FLAGS_EHT_MCS) + return cfg80211_calculate_bitrate_eht(rate); return rate->legacy; } @@ -2151,7 +2284,7 @@ void cfg80211_send_layer2_update(struct net_device *dev, const u8 *addr) skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); memset(skb->cb, 0, sizeof(skb->cb)); - netif_rx_ni(skb); + netif_rx(skb); } EXPORT_SYMBOL(cfg80211_send_layer2_update); diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 28ef3f4465ae..2c34caee0fd1 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -343,9 +343,9 @@ bool xsk_tx_peek_desc(struct xsk_buff_pool *pool, struct xdp_desc *desc) } EXPORT_SYMBOL(xsk_tx_peek_desc); -static u32 xsk_tx_peek_release_fallback(struct xsk_buff_pool *pool, struct xdp_desc *descs, - u32 max_entries) +static u32 xsk_tx_peek_release_fallback(struct xsk_buff_pool *pool, u32 max_entries) { + struct xdp_desc *descs = pool->tx_descs; u32 nb_pkts = 0; while (nb_pkts < max_entries && xsk_tx_peek_desc(pool, &descs[nb_pkts])) @@ -355,8 +355,7 @@ static u32 xsk_tx_peek_release_fallback(struct xsk_buff_pool *pool, struct xdp_d return nb_pkts; } -u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, struct xdp_desc *descs, - u32 max_entries) +u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 max_entries) { struct xdp_sock *xs; u32 nb_pkts; @@ -365,7 +364,7 @@ u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, struct xdp_desc * if (!list_is_singular(&pool->xsk_tx_list)) { /* Fallback to the non-batched version */ rcu_read_unlock(); - return xsk_tx_peek_release_fallback(pool, descs, max_entries); + return xsk_tx_peek_release_fallback(pool, max_entries); } xs = list_first_or_null_rcu(&pool->xsk_tx_list, struct xdp_sock, tx_list); @@ -374,7 +373,7 @@ u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, struct xdp_desc * goto out; } - nb_pkts = xskq_cons_peek_desc_batch(xs->tx, descs, pool, max_entries); + nb_pkts = xskq_cons_peek_desc_batch(xs->tx, pool, max_entries); if (!nb_pkts) { xs->tx->queue_empty_descs++; goto out; @@ -386,7 +385,7 @@ u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, struct xdp_desc * * packets. This avoids having to implement any buffering in * the Tx path. */ - nb_pkts = xskq_prod_reserve_addr_batch(pool->cq, descs, nb_pkts); + nb_pkts = xskq_prod_reserve_addr_batch(pool->cq, pool->tx_descs, nb_pkts); if (!nb_pkts) goto out; @@ -403,18 +402,8 @@ EXPORT_SYMBOL(xsk_tx_peek_release_desc_batch); static int xsk_wakeup(struct xdp_sock *xs, u8 flags) { struct net_device *dev = xs->dev; - int err; - rcu_read_lock(); - err = dev->netdev_ops->ndo_xsk_wakeup(dev, xs->queue_id, flags); - rcu_read_unlock(); - - return err; -} - -static int xsk_zc_xmit(struct xdp_sock *xs) -{ - return xsk_wakeup(xs, XDP_WAKEUP_TX); + return dev->netdev_ops->ndo_xsk_wakeup(dev, xs->queue_id, flags); } static void xsk_destruct_skb(struct sk_buff *skb) @@ -533,6 +522,12 @@ static int xsk_generic_xmit(struct sock *sk) mutex_lock(&xs->mutex); + /* Since we dropped the RCU read lock, the socket state might have changed. */ + if (unlikely(!xsk_is_bound(xs))) { + err = -ENXIO; + goto out; + } + if (xs->queue_id >= xs->dev->real_num_tx_queues) goto out; @@ -596,16 +591,26 @@ static int xsk_generic_xmit(struct sock *sk) return err; } -static int __xsk_sendmsg(struct sock *sk) +static int xsk_xmit(struct sock *sk) { struct xdp_sock *xs = xdp_sk(sk); + int ret; if (unlikely(!(xs->dev->flags & IFF_UP))) return -ENETDOWN; if (unlikely(!xs->tx)) return -ENOBUFS; - return xs->zc ? xsk_zc_xmit(xs) : xsk_generic_xmit(sk); + if (xs->zc) + return xsk_wakeup(xs, XDP_WAKEUP_TX); + + /* Drop the RCU lock since the SKB path might sleep. */ + rcu_read_unlock(); + ret = xsk_generic_xmit(sk); + /* Reaquire RCU lock before going into common code. */ + rcu_read_lock(); + + return ret; } static bool xsk_no_wakeup(struct sock *sk) @@ -619,7 +624,7 @@ static bool xsk_no_wakeup(struct sock *sk) #endif } -static int xsk_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len) +static int __xsk_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len) { bool need_wait = !(m->msg_flags & MSG_DONTWAIT); struct sock *sk = sock->sk; @@ -639,11 +644,22 @@ static int xsk_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len) pool = xs->pool; if (pool->cached_need_wakeup & XDP_WAKEUP_TX) - return __xsk_sendmsg(sk); + return xsk_xmit(sk); return 0; } -static int xsk_recvmsg(struct socket *sock, struct msghdr *m, size_t len, int flags) +static int xsk_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len) +{ + int ret; + + rcu_read_lock(); + ret = __xsk_sendmsg(sock, m, total_len); + rcu_read_unlock(); + + return ret; +} + +static int __xsk_recvmsg(struct socket *sock, struct msghdr *m, size_t len, int flags) { bool need_wait = !(flags & MSG_DONTWAIT); struct sock *sk = sock->sk; @@ -669,6 +685,17 @@ static int xsk_recvmsg(struct socket *sock, struct msghdr *m, size_t len, int fl return 0; } +static int xsk_recvmsg(struct socket *sock, struct msghdr *m, size_t len, int flags) +{ + int ret; + + rcu_read_lock(); + ret = __xsk_recvmsg(sock, m, len, flags); + rcu_read_unlock(); + + return ret; +} + static __poll_t xsk_poll(struct file *file, struct socket *sock, struct poll_table_struct *wait) { @@ -679,8 +706,11 @@ static __poll_t xsk_poll(struct file *file, struct socket *sock, sock_poll_wait(file, sock, wait); - if (unlikely(!xsk_is_bound(xs))) + rcu_read_lock(); + if (unlikely(!xsk_is_bound(xs))) { + rcu_read_unlock(); return mask; + } pool = xs->pool; @@ -689,7 +719,7 @@ static __poll_t xsk_poll(struct file *file, struct socket *sock, xsk_wakeup(xs, pool->cached_need_wakeup); else /* Poll needs to drive Tx also in copy mode */ - __xsk_sendmsg(sk); + xsk_xmit(sk); } if (xs->rx && !xskq_prod_is_empty(xs->rx)) @@ -697,6 +727,7 @@ static __poll_t xsk_poll(struct file *file, struct socket *sock, if (xs->tx && xsk_tx_writeable(xs)) mask |= EPOLLOUT | EPOLLWRNORM; + rcu_read_unlock(); return mask; } @@ -728,7 +759,6 @@ static void xsk_unbind_dev(struct xdp_sock *xs) /* Wait for driver to stop using the xdp socket. */ xp_del_xsk(xs->pool, xs); - xs->dev = NULL; synchronize_net(); dev_put(dev); } diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index fd39bb660ebc..b34fca6ada86 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -37,6 +37,7 @@ void xp_destroy(struct xsk_buff_pool *pool) if (!pool) return; + kvfree(pool->tx_descs); kvfree(pool->heads); kvfree(pool); } @@ -58,6 +59,12 @@ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs, if (!pool->heads) goto out; + if (xs->tx) { + pool->tx_descs = kcalloc(xs->tx->nentries, sizeof(*pool->tx_descs), GFP_KERNEL); + if (!pool->tx_descs) + goto out; + } + pool->chunk_mask = ~((u64)umem->chunk_size - 1); pool->addrs_cnt = umem->size; pool->heads_cnt = umem->chunks; diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index e9aa2c236356..801cda5d1938 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -205,11 +205,11 @@ static inline bool xskq_cons_read_desc(struct xsk_queue *q, return false; } -static inline u32 xskq_cons_read_desc_batch(struct xsk_queue *q, - struct xdp_desc *descs, - struct xsk_buff_pool *pool, u32 max) +static inline u32 xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool, + u32 max) { u32 cached_cons = q->cached_cons, nb_entries = 0; + struct xdp_desc *descs = pool->tx_descs; while (cached_cons != q->cached_prod && nb_entries < max) { struct xdp_rxtx_ring *ring = (struct xdp_rxtx_ring *)q->ring; @@ -282,12 +282,12 @@ static inline bool xskq_cons_peek_desc(struct xsk_queue *q, return xskq_cons_read_desc(q, desc, pool); } -static inline u32 xskq_cons_peek_desc_batch(struct xsk_queue *q, struct xdp_desc *descs, - struct xsk_buff_pool *pool, u32 max) +static inline u32 xskq_cons_peek_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool, + u32 max) { u32 entries = xskq_cons_nb_entries(q, max); - return xskq_cons_read_desc_batch(q, descs, pool, entries); + return xskq_cons_read_desc_batch(q, pool, entries); } /* To improve performance in the xskq_cons_release functions, only update local state here. @@ -304,13 +304,6 @@ static inline void xskq_cons_release_n(struct xsk_queue *q, u32 cnt) q->cached_cons += cnt; } -static inline bool xskq_cons_is_full(struct xsk_queue *q) -{ - /* No barriers needed since data is not accessed */ - return READ_ONCE(q->ring->producer) - READ_ONCE(q->ring->consumer) == - q->nentries; -} - static inline u32 xskq_cons_present_entries(struct xsk_queue *q) { /* No barriers needed since data is not accessed */ diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 39bce5d764de..36aa01d92b65 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -143,7 +143,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur segs = skb_gso_segment(skb, esp_features); if (IS_ERR(segs)) { kfree_skb(skb); - atomic_long_inc(&dev->tx_dropped); + dev_core_stats_tx_dropped_inc(dev); return NULL; } else { consume_skb(skb); @@ -384,16 +384,6 @@ static int xfrm_api_check(struct net_device *dev) return NOTIFY_DONE; } -static int xfrm_dev_register(struct net_device *dev) -{ - return xfrm_api_check(dev); -} - -static int xfrm_dev_feat_change(struct net_device *dev) -{ - return xfrm_api_check(dev); -} - static int xfrm_dev_down(struct net_device *dev) { if (dev->features & NETIF_F_HW_ESP) @@ -408,10 +398,10 @@ static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void switch (event) { case NETDEV_REGISTER: - return xfrm_dev_register(dev); + return xfrm_api_check(dev); case NETDEV_FEAT_CHANGE: - return xfrm_dev_feat_change(dev); + return xfrm_api_check(dev); case NETDEV_DOWN: case NETDEV_UNREGISTER: diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface.c index 1e8b26eecb3f..5113fa0fbcee 100644 --- a/net/xfrm/xfrm_interface.c +++ b/net/xfrm/xfrm_interface.c @@ -190,7 +190,7 @@ static void xfrmi_dev_uninit(struct net_device *dev) static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet) { - skb->tstamp = 0; + skb_clear_tstamp(skb); skb->pkt_type = PACKET_HOST; skb->skb_iif = 0; skb->ignore_df = 0; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 882526159d3a..19aa994f5d2c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3158,7 +3158,7 @@ struct dst_entry *xfrm_lookup_with_ifid(struct net *net, nopol: if (!(dst_orig->dev->flags & IFF_LOOPBACK) && - !xfrm_default_allow(net, dir)) { + net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) { err = -EPERM; goto error; } @@ -3569,7 +3569,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, } if (!pol) { - if (!xfrm_default_allow(net, dir)) { + if (net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS); return 0; } @@ -3629,7 +3629,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, } xfrm_nr = ti; - if (!xfrm_default_allow(net, dir) && !xfrm_nr) { + if (net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK && + !xfrm_nr) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES); goto reject; } @@ -4118,6 +4119,9 @@ static int __net_init xfrm_net_init(struct net *net) spin_lock_init(&net->xfrm.xfrm_policy_lock); seqcount_spinlock_init(&net->xfrm.xfrm_policy_hash_generation, &net->xfrm.xfrm_policy_lock); mutex_init(&net->xfrm.xfrm_cfg_mutex); + net->xfrm.policy_default[XFRM_POLICY_IN] = XFRM_USERPOLICY_ACCEPT; + net->xfrm.policy_default[XFRM_POLICY_FWD] = XFRM_USERPOLICY_ACCEPT; + net->xfrm.policy_default[XFRM_POLICY_OUT] = XFRM_USERPOLICY_ACCEPT; rv = xfrm_statistics_init(net); if (rv < 0) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 72b2f173aac8..64fa8fdd6bbd 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1994,12 +1994,9 @@ static int xfrm_notify_userpolicy(struct net *net) } up = nlmsg_data(nlh); - up->in = net->xfrm.policy_default & XFRM_POL_DEFAULT_IN ? - XFRM_USERPOLICY_BLOCK : XFRM_USERPOLICY_ACCEPT; - up->fwd = net->xfrm.policy_default & XFRM_POL_DEFAULT_FWD ? - XFRM_USERPOLICY_BLOCK : XFRM_USERPOLICY_ACCEPT; - up->out = net->xfrm.policy_default & XFRM_POL_DEFAULT_OUT ? - XFRM_USERPOLICY_BLOCK : XFRM_USERPOLICY_ACCEPT; + up->in = net->xfrm.policy_default[XFRM_POLICY_IN]; + up->fwd = net->xfrm.policy_default[XFRM_POLICY_FWD]; + up->out = net->xfrm.policy_default[XFRM_POLICY_OUT]; nlmsg_end(skb, nlh); @@ -2010,26 +2007,26 @@ static int xfrm_notify_userpolicy(struct net *net) return err; } +static bool xfrm_userpolicy_is_valid(__u8 policy) +{ + return policy == XFRM_USERPOLICY_BLOCK || + policy == XFRM_USERPOLICY_ACCEPT; +} + static int xfrm_set_default(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr **attrs) { struct net *net = sock_net(skb->sk); struct xfrm_userpolicy_default *up = nlmsg_data(nlh); - if (up->in == XFRM_USERPOLICY_BLOCK) - net->xfrm.policy_default |= XFRM_POL_DEFAULT_IN; - else if (up->in == XFRM_USERPOLICY_ACCEPT) - net->xfrm.policy_default &= ~XFRM_POL_DEFAULT_IN; + if (xfrm_userpolicy_is_valid(up->in)) + net->xfrm.policy_default[XFRM_POLICY_IN] = up->in; - if (up->fwd == XFRM_USERPOLICY_BLOCK) - net->xfrm.policy_default |= XFRM_POL_DEFAULT_FWD; - else if (up->fwd == XFRM_USERPOLICY_ACCEPT) - net->xfrm.policy_default &= ~XFRM_POL_DEFAULT_FWD; + if (xfrm_userpolicy_is_valid(up->fwd)) + net->xfrm.policy_default[XFRM_POLICY_FWD] = up->fwd; - if (up->out == XFRM_USERPOLICY_BLOCK) - net->xfrm.policy_default |= XFRM_POL_DEFAULT_OUT; - else if (up->out == XFRM_USERPOLICY_ACCEPT) - net->xfrm.policy_default &= ~XFRM_POL_DEFAULT_OUT; + if (xfrm_userpolicy_is_valid(up->out)) + net->xfrm.policy_default[XFRM_POLICY_OUT] = up->out; rt_genid_bump_all(net); @@ -2059,13 +2056,9 @@ static int xfrm_get_default(struct sk_buff *skb, struct nlmsghdr *nlh, } r_up = nlmsg_data(r_nlh); - - r_up->in = net->xfrm.policy_default & XFRM_POL_DEFAULT_IN ? - XFRM_USERPOLICY_BLOCK : XFRM_USERPOLICY_ACCEPT; - r_up->fwd = net->xfrm.policy_default & XFRM_POL_DEFAULT_FWD ? - XFRM_USERPOLICY_BLOCK : XFRM_USERPOLICY_ACCEPT; - r_up->out = net->xfrm.policy_default & XFRM_POL_DEFAULT_OUT ? - XFRM_USERPOLICY_BLOCK : XFRM_USERPOLICY_ACCEPT; + r_up->in = net->xfrm.policy_default[XFRM_POLICY_IN]; + r_up->fwd = net->xfrm.policy_default[XFRM_POLICY_FWD]; + r_up->out = net->xfrm.policy_default[XFRM_POLICY_OUT]; nlmsg_end(r_skb, r_nlh); return nlmsg_unicast(net->xfrm.nlsk, r_skb, portid); diff --git a/samples/Kconfig b/samples/Kconfig index 10e021c72282..470ee3baf2e1 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -79,6 +79,13 @@ config SAMPLE_HW_BREAKPOINT help This builds kernel hardware breakpoint example modules. +config SAMPLE_FPROBE + tristate "Build fprobe examples -- loadable modules only" + depends on FPROBE && m + help + This builds a fprobe example module. This module has an option 'symbol'. + You can specify a probed symbol or symbols separated with ','. + config SAMPLE_KFIFO tristate "Build kfifo examples -- loadable modules only" depends on m diff --git a/samples/Makefile b/samples/Makefile index 448343e8faeb..701e912ab5af 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -34,3 +34,4 @@ subdir-$(CONFIG_SAMPLE_WATCHDOG) += watchdog subdir-$(CONFIG_SAMPLE_WATCH_QUEUE) += watch_queue obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak/ obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight/ +obj-$(CONFIG_SAMPLE_FPROBE) += fprobe/ diff --git a/samples/bpf/map_perf_test_user.c b/samples/bpf/map_perf_test_user.c index 319fd31522f3..e69651a6902f 100644 --- a/samples/bpf/map_perf_test_user.c +++ b/samples/bpf/map_perf_test_user.c @@ -413,7 +413,7 @@ static void fixup_map(struct bpf_object *obj) for (i = 0; i < NR_TESTS; i++) { if (!strcmp(test_map_names[i], name) && (check_test_flags(i))) { - bpf_map__resize(map, num_map_entries); + bpf_map__set_max_entries(map, num_map_entries); continue; } } diff --git a/samples/bpf/xdp1_user.c b/samples/bpf/xdp1_user.c index 8675fa5273df..631f0cabe139 100644 --- a/samples/bpf/xdp1_user.c +++ b/samples/bpf/xdp1_user.c @@ -26,12 +26,12 @@ static void int_exit(int sig) { __u32 curr_prog_id = 0; - if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) { - printf("bpf_get_link_xdp_id failed\n"); + if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) { + printf("bpf_xdp_query_id failed\n"); exit(1); } if (prog_id == curr_prog_id) - bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + bpf_xdp_detach(ifindex, xdp_flags, NULL); else if (!curr_prog_id) printf("couldn't find a prog id on a given interface\n"); else @@ -79,13 +79,11 @@ static void usage(const char *prog) int main(int argc, char **argv) { - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; struct bpf_prog_info info = {}; __u32 info_len = sizeof(info); const char *optstr = "FSN"; int prog_fd, map_fd, opt; + struct bpf_program *prog; struct bpf_object *obj; struct bpf_map *map; char filename[256]; @@ -123,11 +121,19 @@ int main(int argc, char **argv) } snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); - prog_load_attr.file = filename; - - if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) + obj = bpf_object__open_file(filename, NULL); + if (libbpf_get_error(obj)) return 1; + prog = bpf_object__next_program(obj, NULL); + bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); + + err = bpf_object__load(obj); + if (err) + return 1; + + prog_fd = bpf_program__fd(prog); + map = bpf_object__next_map(obj, NULL); if (!map) { printf("finding a map in obj file failed\n"); @@ -143,7 +149,7 @@ int main(int argc, char **argv) signal(SIGINT, int_exit); signal(SIGTERM, int_exit); - if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) { + if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) { printf("link set xdp fd failed\n"); return 1; } diff --git a/samples/bpf/xdp_adjust_tail_user.c b/samples/bpf/xdp_adjust_tail_user.c index a70b094c8ec5..b3f6e49676ed 100644 --- a/samples/bpf/xdp_adjust_tail_user.c +++ b/samples/bpf/xdp_adjust_tail_user.c @@ -34,12 +34,12 @@ static void int_exit(int sig) __u32 curr_prog_id = 0; if (ifindex > -1) { - if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) { - printf("bpf_get_link_xdp_id failed\n"); + if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) { + printf("bpf_xdp_query_id failed\n"); exit(1); } if (prog_id == curr_prog_id) - bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + bpf_xdp_detach(ifindex, xdp_flags, NULL); else if (!curr_prog_id) printf("couldn't find a prog id on a given iface\n"); else @@ -82,15 +82,13 @@ static void usage(const char *cmd) int main(int argc, char **argv) { - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; unsigned char opt_flags[256] = {}; const char *optstr = "i:T:P:SNFh"; struct bpf_prog_info info = {}; __u32 info_len = sizeof(info); unsigned int kill_after_s = 0; int i, prog_fd, map_fd, opt; + struct bpf_program *prog; struct bpf_object *obj; __u32 max_pckt_size = 0; __u32 key = 0; @@ -148,11 +146,20 @@ int main(int argc, char **argv) } snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); - prog_load_attr.file = filename; - if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) + obj = bpf_object__open_file(filename, NULL); + if (libbpf_get_error(obj)) return 1; + prog = bpf_object__next_program(obj, NULL); + bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); + + err = bpf_object__load(obj); + if (err) + return 1; + + prog_fd = bpf_program__fd(prog); + /* static global var 'max_pcktsz' is accessible from .data section */ if (max_pckt_size) { map_fd = bpf_object__find_map_fd_by_name(obj, "xdp_adju.data"); @@ -173,7 +180,7 @@ int main(int argc, char **argv) signal(SIGINT, int_exit); signal(SIGTERM, int_exit); - if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) { + if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) { printf("link set xdp fd failed\n"); return 1; } diff --git a/samples/bpf/xdp_fwd_user.c b/samples/bpf/xdp_fwd_user.c index 4ad896782f77..1828487bae9a 100644 --- a/samples/bpf/xdp_fwd_user.c +++ b/samples/bpf/xdp_fwd_user.c @@ -33,7 +33,7 @@ static int do_attach(int idx, int prog_fd, int map_fd, const char *name) { int err; - err = bpf_set_link_xdp_fd(idx, prog_fd, xdp_flags); + err = bpf_xdp_attach(idx, prog_fd, xdp_flags, NULL); if (err < 0) { printf("ERROR: failed to attach program to %s\n", name); return err; @@ -51,7 +51,7 @@ static int do_detach(int idx, const char *name) { int err; - err = bpf_set_link_xdp_fd(idx, -1, xdp_flags); + err = bpf_xdp_detach(idx, xdp_flags, NULL); if (err < 0) printf("ERROR: failed to detach program from %s\n", name); @@ -75,14 +75,11 @@ static void usage(const char *prog) int main(int argc, char **argv) { - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; const char *prog_name = "xdp_fwd"; struct bpf_program *prog = NULL; struct bpf_program *pos; const char *sec_name; - int prog_fd, map_fd = -1; + int prog_fd = -1, map_fd = -1; char filename[PATH_MAX]; struct bpf_object *obj; int opt, i, idx, err; @@ -119,7 +116,6 @@ int main(int argc, char **argv) if (attach) { snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); - prog_load_attr.file = filename; if (access(filename, O_RDONLY) < 0) { printf("error accessing file %s: %s\n", @@ -127,7 +123,14 @@ int main(int argc, char **argv) return 1; } - err = bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd); + obj = bpf_object__open_file(filename, NULL); + if (libbpf_get_error(obj)) + return 1; + + prog = bpf_object__next_program(obj, NULL); + bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); + + err = bpf_object__load(obj); if (err) { printf("Does kernel support devmap lookup?\n"); /* If not, the error message will be: diff --git a/samples/bpf/xdp_redirect_cpu.bpf.c b/samples/bpf/xdp_redirect_cpu.bpf.c index 25e3a405375f..87c54bfdbb70 100644 --- a/samples/bpf/xdp_redirect_cpu.bpf.c +++ b/samples/bpf/xdp_redirect_cpu.bpf.c @@ -491,7 +491,7 @@ int xdp_prognum5_lb_hash_ip_pairs(struct xdp_md *ctx) return bpf_redirect_map(&cpu_map, cpu_dest, 0); } -SEC("xdp_cpumap/redirect") +SEC("xdp/cpumap") int xdp_redirect_cpu_devmap(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; @@ -507,19 +507,19 @@ int xdp_redirect_cpu_devmap(struct xdp_md *ctx) return bpf_redirect_map(&tx_port, 0, 0); } -SEC("xdp_cpumap/pass") +SEC("xdp/cpumap") int xdp_redirect_cpu_pass(struct xdp_md *ctx) { return XDP_PASS; } -SEC("xdp_cpumap/drop") +SEC("xdp/cpumap") int xdp_redirect_cpu_drop(struct xdp_md *ctx) { return XDP_DROP; } -SEC("xdp_devmap/egress") +SEC("xdp/devmap") int xdp_redirect_egress_prog(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; diff --git a/samples/bpf/xdp_redirect_cpu_user.c b/samples/bpf/xdp_redirect_cpu_user.c index a81704d3317b..5f74a70a9021 100644 --- a/samples/bpf/xdp_redirect_cpu_user.c +++ b/samples/bpf/xdp_redirect_cpu_user.c @@ -70,7 +70,7 @@ static void print_avail_progs(struct bpf_object *obj) printf(" Programs to be used for -p/--progname:\n"); bpf_object__for_each_program(pos, obj) { - if (bpf_program__is_xdp(pos)) { + if (bpf_program__type(pos) == BPF_PROG_TYPE_XDP) { if (!strncmp(bpf_program__name(pos), "xdp_prognum", sizeof("xdp_prognum") - 1)) printf(" %s\n", bpf_program__name(pos)); diff --git a/samples/bpf/xdp_redirect_map.bpf.c b/samples/bpf/xdp_redirect_map.bpf.c index 59efd656e1b2..415bac1758e3 100644 --- a/samples/bpf/xdp_redirect_map.bpf.c +++ b/samples/bpf/xdp_redirect_map.bpf.c @@ -68,7 +68,7 @@ int xdp_redirect_map_native(struct xdp_md *ctx) return xdp_redirect_map(ctx, &tx_port_native); } -SEC("xdp_devmap/egress") +SEC("xdp/devmap") int xdp_redirect_map_egress(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; diff --git a/samples/bpf/xdp_redirect_map_multi.bpf.c b/samples/bpf/xdp_redirect_map_multi.bpf.c index bb0a5a3bfcf0..8b2fd4ec2c76 100644 --- a/samples/bpf/xdp_redirect_map_multi.bpf.c +++ b/samples/bpf/xdp_redirect_map_multi.bpf.c @@ -53,7 +53,7 @@ int xdp_redirect_map_native(struct xdp_md *ctx) return xdp_redirect_map(ctx, &forward_map_native); } -SEC("xdp_devmap/egress") +SEC("xdp/devmap") int xdp_devmap_prog(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; diff --git a/samples/bpf/xdp_router_ipv4_user.c b/samples/bpf/xdp_router_ipv4_user.c index cfaf7e50e431..6dae87d83e1c 100644 --- a/samples/bpf/xdp_router_ipv4_user.c +++ b/samples/bpf/xdp_router_ipv4_user.c @@ -43,13 +43,13 @@ static void int_exit(int sig) int i = 0; for (i = 0; i < total_ifindex; i++) { - if (bpf_get_link_xdp_id(ifindex_list[i], &prog_id, flags)) { - printf("bpf_get_link_xdp_id on iface %d failed\n", + if (bpf_xdp_query_id(ifindex_list[i], flags, &prog_id)) { + printf("bpf_xdp_query_id on iface %d failed\n", ifindex_list[i]); exit(1); } if (prog_id_list[i] == prog_id) - bpf_set_link_xdp_fd(ifindex_list[i], -1, flags); + bpf_xdp_detach(ifindex_list[i], flags, NULL); else if (!prog_id) printf("couldn't find a prog id on iface %d\n", ifindex_list[i]); @@ -640,12 +640,10 @@ static void usage(const char *prog) int main(int ac, char **argv) { - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; struct bpf_prog_info info = {}; __u32 info_len = sizeof(info); const char *optstr = "SF"; + struct bpf_program *prog; struct bpf_object *obj; char filename[256]; char **ifname_list; @@ -653,7 +651,6 @@ int main(int ac, char **argv) int err, i = 1; snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); - prog_load_attr.file = filename; total_ifindex = ac - 1; ifname_list = (argv + 1); @@ -684,14 +681,20 @@ int main(int ac, char **argv) return 1; } - if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) + obj = bpf_object__open_file(filename, NULL); + if (libbpf_get_error(obj)) return 1; + prog = bpf_object__next_program(obj, NULL); + bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); + printf("\n******************loading bpf file*********************\n"); - if (!prog_fd) { - printf("bpf_prog_load_xattr: %s\n", strerror(errno)); + err = bpf_object__load(obj); + if (err) { + printf("bpf_object__load(): %s\n", strerror(errno)); return 1; } + prog_fd = bpf_program__fd(prog); lpm_map_fd = bpf_object__find_map_fd_by_name(obj, "lpm_map"); rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt"); @@ -716,12 +719,12 @@ int main(int ac, char **argv) } prog_id_list = (__u32 *)calloc(total_ifindex, sizeof(__u32 *)); for (i = 0; i < total_ifindex; i++) { - if (bpf_set_link_xdp_fd(ifindex_list[i], prog_fd, flags) < 0) { + if (bpf_xdp_attach(ifindex_list[i], prog_fd, flags, NULL) < 0) { printf("link set xdp fd failed\n"); int recovery_index = i; for (i = 0; i < recovery_index; i++) - bpf_set_link_xdp_fd(ifindex_list[i], -1, flags); + bpf_xdp_detach(ifindex_list[i], flags, NULL); return 1; } diff --git a/samples/bpf/xdp_rxq_info_user.c b/samples/bpf/xdp_rxq_info_user.c index 74a2926eba08..f2d90cba5164 100644 --- a/samples/bpf/xdp_rxq_info_user.c +++ b/samples/bpf/xdp_rxq_info_user.c @@ -62,15 +62,15 @@ static void int_exit(int sig) __u32 curr_prog_id = 0; if (ifindex > -1) { - if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) { - printf("bpf_get_link_xdp_id failed\n"); + if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) { + printf("bpf_xdp_query_id failed\n"); exit(EXIT_FAIL); } if (prog_id == curr_prog_id) { fprintf(stderr, "Interrupted: Removing XDP program on ifindex:%d device:%s\n", ifindex, ifname); - bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + bpf_xdp_detach(ifindex, xdp_flags, NULL); } else if (!curr_prog_id) { printf("couldn't find a prog id on a given iface\n"); } else { @@ -209,7 +209,7 @@ static struct datarec *alloc_record_per_cpu(void) static struct record *alloc_record_per_rxq(void) { - unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; + unsigned int nr_rxqs = bpf_map__max_entries(rx_queue_index_map); struct record *array; array = calloc(nr_rxqs, sizeof(struct record)); @@ -222,7 +222,7 @@ static struct record *alloc_record_per_rxq(void) static struct stats_record *alloc_stats_record(void) { - unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; + unsigned int nr_rxqs = bpf_map__max_entries(rx_queue_index_map); struct stats_record *rec; int i; @@ -241,7 +241,7 @@ static struct stats_record *alloc_stats_record(void) static void free_stats_record(struct stats_record *r) { - unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; + unsigned int nr_rxqs = bpf_map__max_entries(rx_queue_index_map); int i; for (i = 0; i < nr_rxqs; i++) @@ -289,7 +289,7 @@ static void stats_collect(struct stats_record *rec) map_collect_percpu(fd, 0, &rec->stats); fd = bpf_map__fd(rx_queue_index_map); - max_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; + max_rxqs = bpf_map__max_entries(rx_queue_index_map); for (i = 0; i < max_rxqs; i++) map_collect_percpu(fd, i, &rec->rxq[i]); } @@ -335,7 +335,7 @@ static void stats_print(struct stats_record *stats_rec, struct stats_record *stats_prev, int action, __u32 cfg_opt) { - unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; + unsigned int nr_rxqs = bpf_map__max_entries(rx_queue_index_map); unsigned int nr_cpus = bpf_num_possible_cpus(); double pps = 0, err = 0; struct record *rec, *prev; @@ -450,14 +450,12 @@ static void stats_poll(int interval, int action, __u32 cfg_opt) int main(int argc, char **argv) { __u32 cfg_options= NO_TOUCH ; /* Default: Don't touch packet memory */ - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; struct bpf_prog_info info = {}; __u32 info_len = sizeof(info); int prog_fd, map_fd, opt, err; bool use_separators = true; struct config cfg = { 0 }; + struct bpf_program *prog; struct bpf_object *obj; struct bpf_map *map; char filename[256]; @@ -471,11 +469,19 @@ int main(int argc, char **argv) char *action_str = NULL; snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); - prog_load_attr.file = filename; - if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) + obj = bpf_object__open_file(filename, NULL); + if (libbpf_get_error(obj)) return EXIT_FAIL; + prog = bpf_object__next_program(obj, NULL); + bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); + + err = bpf_object__load(obj); + if (err) + return EXIT_FAIL; + prog_fd = bpf_program__fd(prog); + map = bpf_object__find_map_by_name(obj, "config_map"); stats_global_map = bpf_object__find_map_by_name(obj, "stats_global_map"); rx_queue_index_map = bpf_object__find_map_by_name(obj, "rx_queue_index_map"); @@ -582,7 +588,7 @@ int main(int argc, char **argv) signal(SIGINT, int_exit); signal(SIGTERM, int_exit); - if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) { + if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) { fprintf(stderr, "link set xdp fd failed\n"); return EXIT_FAIL_XDP; } diff --git a/samples/bpf/xdp_sample_pkts_user.c b/samples/bpf/xdp_sample_pkts_user.c index 587eacb49103..0a2b3e997aed 100644 --- a/samples/bpf/xdp_sample_pkts_user.c +++ b/samples/bpf/xdp_sample_pkts_user.c @@ -30,7 +30,7 @@ static int do_attach(int idx, int fd, const char *name) __u32 info_len = sizeof(info); int err; - err = bpf_set_link_xdp_fd(idx, fd, xdp_flags); + err = bpf_xdp_attach(idx, fd, xdp_flags, NULL); if (err < 0) { printf("ERROR: failed to attach program to %s\n", name); return err; @@ -51,13 +51,13 @@ static int do_detach(int idx, const char *name) __u32 curr_prog_id = 0; int err = 0; - err = bpf_get_link_xdp_id(idx, &curr_prog_id, xdp_flags); + err = bpf_xdp_query_id(idx, xdp_flags, &curr_prog_id); if (err) { - printf("bpf_get_link_xdp_id failed\n"); + printf("bpf_xdp_query_id failed\n"); return err; } if (prog_id == curr_prog_id) { - err = bpf_set_link_xdp_fd(idx, -1, xdp_flags); + err = bpf_xdp_detach(idx, xdp_flags, NULL); if (err < 0) printf("ERROR: failed to detach prog from %s\n", name); } else if (!curr_prog_id) { diff --git a/samples/bpf/xdp_sample_user.c b/samples/bpf/xdp_sample_user.c index 8740838e7767..c4332d068b91 100644 --- a/samples/bpf/xdp_sample_user.c +++ b/samples/bpf/xdp_sample_user.c @@ -1218,7 +1218,7 @@ int sample_setup_maps(struct bpf_map **maps) default: return -EINVAL; } - if (bpf_map__resize(sample_map[i], sample_map_count[i]) < 0) + if (bpf_map__set_max_entries(sample_map[i], sample_map_count[i]) < 0) return -errno; } sample_map[MAP_DEVMAP_XMIT_MULTI] = maps[MAP_DEVMAP_XMIT_MULTI]; @@ -1265,7 +1265,7 @@ static int __sample_remove_xdp(int ifindex, __u32 prog_id, int xdp_flags) int ret; if (prog_id) { - ret = bpf_get_link_xdp_id(ifindex, &cur_prog_id, xdp_flags); + ret = bpf_xdp_query_id(ifindex, xdp_flags, &cur_prog_id); if (ret < 0) return -errno; @@ -1278,7 +1278,7 @@ static int __sample_remove_xdp(int ifindex, __u32 prog_id, int xdp_flags) } } - return bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + return bpf_xdp_detach(ifindex, xdp_flags, NULL); } int sample_install_xdp(struct bpf_program *xdp_prog, int ifindex, bool generic, @@ -1295,8 +1295,7 @@ int sample_install_xdp(struct bpf_program *xdp_prog, int ifindex, bool generic, xdp_flags |= !force ? XDP_FLAGS_UPDATE_IF_NOEXIST : 0; xdp_flags |= generic ? XDP_FLAGS_SKB_MODE : XDP_FLAGS_DRV_MODE; - ret = bpf_set_link_xdp_fd(ifindex, bpf_program__fd(xdp_prog), - xdp_flags); + ret = bpf_xdp_attach(ifindex, bpf_program__fd(xdp_prog), xdp_flags, NULL); if (ret < 0) { ret = -errno; fprintf(stderr, @@ -1308,7 +1307,7 @@ int sample_install_xdp(struct bpf_program *xdp_prog, int ifindex, bool generic, return ret; } - ret = bpf_get_link_xdp_id(ifindex, &prog_id, xdp_flags); + ret = bpf_xdp_query_id(ifindex, xdp_flags, &prog_id); if (ret < 0) { ret = -errno; fprintf(stderr, diff --git a/samples/bpf/xdp_sample_user.h b/samples/bpf/xdp_sample_user.h index 5f44b877ecf5..f45051679977 100644 --- a/samples/bpf/xdp_sample_user.h +++ b/samples/bpf/xdp_sample_user.h @@ -61,7 +61,7 @@ static inline char *safe_strncpy(char *dst, const char *src, size_t size) #define __attach_tp(name) \ ({ \ - if (!bpf_program__is_tracing(skel->progs.name)) \ + if (bpf_program__type(skel->progs.name) != BPF_PROG_TYPE_TRACING)\ return -EINVAL; \ skel->links.name = bpf_program__attach(skel->progs.name); \ if (!skel->links.name) \ diff --git a/samples/bpf/xdp_tx_iptunnel_user.c b/samples/bpf/xdp_tx_iptunnel_user.c index 1d4f305d02aa..2e811e4331cc 100644 --- a/samples/bpf/xdp_tx_iptunnel_user.c +++ b/samples/bpf/xdp_tx_iptunnel_user.c @@ -32,12 +32,12 @@ static void int_exit(int sig) __u32 curr_prog_id = 0; if (ifindex > -1) { - if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) { - printf("bpf_get_link_xdp_id failed\n"); + if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) { + printf("bpf_xdp_query_id failed\n"); exit(1); } if (prog_id == curr_prog_id) - bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + bpf_xdp_detach(ifindex, xdp_flags, NULL); else if (!curr_prog_id) printf("couldn't find a prog id on a given iface\n"); else @@ -152,9 +152,6 @@ static int parse_ports(const char *port_str, int *min_port, int *max_port) int main(int argc, char **argv) { - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; int min_port = 0, max_port = 0, vip2tnl_map_fd; const char *optstr = "i:a:p:s:d:m:T:P:FSNh"; unsigned char opt_flags[256] = {}; @@ -162,6 +159,7 @@ int main(int argc, char **argv) __u32 info_len = sizeof(info); unsigned int kill_after_s = 0; struct iptnl_info tnl = {}; + struct bpf_program *prog; struct bpf_object *obj; struct vip vip = {}; char filename[256]; @@ -259,15 +257,20 @@ int main(int argc, char **argv) } snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); - prog_load_attr.file = filename; - if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) + obj = bpf_object__open_file(filename, NULL); + if (libbpf_get_error(obj)) return 1; - if (!prog_fd) { - printf("bpf_prog_load_xattr: %s\n", strerror(errno)); + prog = bpf_object__next_program(obj, NULL); + bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); + + err = bpf_object__load(obj); + if (err) { + printf("bpf_object__load(): %s\n", strerror(errno)); return 1; } + prog_fd = bpf_program__fd(prog); rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt"); vip2tnl_map_fd = bpf_object__find_map_fd_by_name(obj, "vip2tnl"); @@ -288,7 +291,7 @@ int main(int argc, char **argv) } } - if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) { + if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) { printf("link set xdp fd failed\n"); return 1; } @@ -302,7 +305,7 @@ int main(int argc, char **argv) poll_stats(kill_after_s); - bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + bpf_xdp_detach(ifindex, xdp_flags, NULL); return 0; } diff --git a/samples/bpf/xdpsock_ctrl_proc.c b/samples/bpf/xdpsock_ctrl_proc.c index cc4408797ab7..28b5f2a9fa08 100644 --- a/samples/bpf/xdpsock_ctrl_proc.c +++ b/samples/bpf/xdpsock_ctrl_proc.c @@ -173,7 +173,7 @@ main(int argc, char **argv) unlink(SOCKET_NAME); /* Unset fd for given ifindex */ - err = bpf_set_link_xdp_fd(ifindex, -1, 0); + err = bpf_xdp_detach(ifindex, 0, NULL); if (err) { fprintf(stderr, "Error when unsetting bpf prog_fd for ifindex(%d)\n", ifindex); return err; diff --git a/samples/bpf/xdpsock_user.c b/samples/bpf/xdpsock_user.c index aa50864e4415..6f3fe30ad283 100644 --- a/samples/bpf/xdpsock_user.c +++ b/samples/bpf/xdpsock_user.c @@ -571,13 +571,13 @@ static void remove_xdp_program(void) { u32 curr_prog_id = 0; - if (bpf_get_link_xdp_id(opt_ifindex, &curr_prog_id, opt_xdp_flags)) { - printf("bpf_get_link_xdp_id failed\n"); + if (bpf_xdp_query_id(opt_ifindex, opt_xdp_flags, &curr_prog_id)) { + printf("bpf_xdp_query_id failed\n"); exit(EXIT_FAILURE); } if (prog_id == curr_prog_id) - bpf_set_link_xdp_fd(opt_ifindex, -1, opt_xdp_flags); + bpf_xdp_detach(opt_ifindex, opt_xdp_flags, NULL); else if (!curr_prog_id) printf("couldn't find a prog id on a given interface\n"); else @@ -1027,7 +1027,7 @@ static struct xsk_socket_info *xsk_configure_socket(struct xsk_umem_info *umem, if (ret) exit_with_error(-ret); - ret = bpf_get_link_xdp_id(opt_ifindex, &prog_id, opt_xdp_flags); + ret = bpf_xdp_query_id(opt_ifindex, opt_xdp_flags, &prog_id); if (ret) exit_with_error(-ret); @@ -1760,7 +1760,7 @@ static void load_xdp_program(char **argv, struct bpf_object **obj) exit(EXIT_FAILURE); } - if (bpf_set_link_xdp_fd(opt_ifindex, prog_fd, opt_xdp_flags) < 0) { + if (bpf_xdp_attach(opt_ifindex, prog_fd, opt_xdp_flags, NULL) < 0) { fprintf(stderr, "ERROR: link set xdp fd failed\n"); exit(EXIT_FAILURE); } @@ -1984,15 +1984,15 @@ int main(int argc, char **argv) setlocale(LC_ALL, ""); + prev_time = get_nsecs(); + start_time = prev_time; + if (!opt_quiet) { ret = pthread_create(&pt, NULL, poller, NULL); if (ret) exit_with_error(ret); } - prev_time = get_nsecs(); - start_time = prev_time; - /* Configure sched priority for better wake-up accuracy */ memset(&schparam, 0, sizeof(schparam)); schparam.sched_priority = opt_schprio; diff --git a/samples/bpf/xsk_fwd.c b/samples/bpf/xsk_fwd.c index 52e7c4ffd228..2220509588a0 100644 --- a/samples/bpf/xsk_fwd.c +++ b/samples/bpf/xsk_fwd.c @@ -974,8 +974,8 @@ static void remove_xdp_program(void) int i; for (i = 0 ; i < n_ports; i++) - bpf_set_link_xdp_fd(if_nametoindex(port_params[i].iface), -1, - port_params[i].xsk_cfg.xdp_flags); + bpf_xdp_detach(if_nametoindex(port_params[i].iface), + port_params[i].xsk_cfg.xdp_flags, NULL); } int main(int argc, char **argv) diff --git a/samples/fprobe/Makefile b/samples/fprobe/Makefile new file mode 100644 index 000000000000..ecccbfa6e99b --- /dev/null +++ b/samples/fprobe/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_SAMPLE_FPROBE) += fprobe_example.o diff --git a/samples/fprobe/fprobe_example.c b/samples/fprobe/fprobe_example.c new file mode 100644 index 000000000000..24d3cf109140 --- /dev/null +++ b/samples/fprobe/fprobe_example.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Here's a sample kernel module showing the use of fprobe to dump a + * stack trace and selected registers when kernel_clone() is called. + * + * For more information on theory of operation of kprobes, see + * Documentation/trace/kprobes.rst + * + * You will see the trace data in /var/log/messages and on the console + * whenever kernel_clone() is invoked to create a new process. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include + +#define BACKTRACE_DEPTH 16 +#define MAX_SYMBOL_LEN 4096 +struct fprobe sample_probe; + +static char symbol[MAX_SYMBOL_LEN] = "kernel_clone"; +module_param_string(symbol, symbol, sizeof(symbol), 0644); +static char nosymbol[MAX_SYMBOL_LEN] = ""; +module_param_string(nosymbol, nosymbol, sizeof(nosymbol), 0644); +static bool stackdump = true; +module_param(stackdump, bool, 0644); + +static void show_backtrace(void) +{ + unsigned long stacks[BACKTRACE_DEPTH]; + unsigned int len; + + len = stack_trace_save(stacks, BACKTRACE_DEPTH, 2); + stack_trace_print(stacks, len, 24); +} + +static void sample_entry_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs) +{ + pr_info("Enter <%pS> ip = 0x%p\n", (void *)ip, (void *)ip); + if (stackdump) + show_backtrace(); +} + +static void sample_exit_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs) +{ + unsigned long rip = instruction_pointer(regs); + + pr_info("Return from <%pS> ip = 0x%p to rip = 0x%p (%pS)\n", + (void *)ip, (void *)ip, (void *)rip, (void *)rip); + if (stackdump) + show_backtrace(); +} + +static int __init fprobe_init(void) +{ + char *p, *symbuf = NULL; + const char **syms; + int ret, count, i; + + sample_probe.entry_handler = sample_entry_handler; + sample_probe.exit_handler = sample_exit_handler; + + if (strchr(symbol, '*')) { + /* filter based fprobe */ + ret = register_fprobe(&sample_probe, symbol, + nosymbol[0] == '\0' ? NULL : nosymbol); + goto out; + } else if (!strchr(symbol, ',')) { + symbuf = symbol; + ret = register_fprobe_syms(&sample_probe, (const char **)&symbuf, 1); + goto out; + } + + /* Comma separated symbols */ + symbuf = kstrdup(symbol, GFP_KERNEL); + if (!symbuf) + return -ENOMEM; + p = symbuf; + count = 1; + while ((p = strchr(++p, ',')) != NULL) + count++; + + pr_info("%d symbols found\n", count); + + syms = kcalloc(count, sizeof(char *), GFP_KERNEL); + if (!syms) { + kfree(symbuf); + return -ENOMEM; + } + + p = symbuf; + for (i = 0; i < count; i++) + syms[i] = strsep(&p, ","); + + ret = register_fprobe_syms(&sample_probe, syms, count); + kfree(syms); + kfree(symbuf); +out: + if (ret < 0) + pr_err("register_fprobe failed, returned %d\n", ret); + else + pr_info("Planted fprobe at %s\n", symbol); + + return ret; +} + +static void __exit fprobe_exit(void) +{ + unregister_fprobe(&sample_probe); + + pr_info("fprobe at %s unregistered\n", symbol); +} + +module_init(fprobe_init) +module_exit(fprobe_exit) +MODULE_LICENSE("GPL"); diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py index a6403ddf5de7..096625242475 100755 --- a/scripts/bpf_doc.py +++ b/scripts/bpf_doc.py @@ -87,21 +87,25 @@ class HeaderParser(object): self.line = '' self.helpers = [] self.commands = [] + self.desc_unique_helpers = set() + self.define_unique_helpers = [] + self.desc_syscalls = [] + self.enum_syscalls = [] def parse_element(self): proto = self.parse_symbol() - desc = self.parse_desc() - ret = self.parse_ret() + desc = self.parse_desc(proto) + ret = self.parse_ret(proto) return APIElement(proto=proto, desc=desc, ret=ret) def parse_helper(self): proto = self.parse_proto() - desc = self.parse_desc() - ret = self.parse_ret() + desc = self.parse_desc(proto) + ret = self.parse_ret(proto) return Helper(proto=proto, desc=desc, ret=ret) def parse_symbol(self): - p = re.compile(' \* ?(.+)$') + p = re.compile(' \* ?(BPF\w+)$') capture = p.match(self.line) if not capture: raise NoSyscallCommandFound @@ -127,16 +131,15 @@ class HeaderParser(object): self.line = self.reader.readline() return capture.group(1) - def parse_desc(self): + def parse_desc(self, proto): p = re.compile(' \* ?(?:\t| {5,8})Description$') capture = p.match(self.line) if not capture: - # Helper can have empty description and we might be parsing another - # attribute: return but do not consume. - return '' + raise Exception("No description section found for " + proto) # Description can be several lines, some of them possibly empty, and it # stops when another subsection title is met. desc = '' + desc_present = False while True: self.line = self.reader.readline() if self.line == ' *\n': @@ -145,21 +148,24 @@ class HeaderParser(object): p = re.compile(' \* ?(?:\t| {5,8})(?:\t| {8})(.*)') capture = p.match(self.line) if capture: + desc_present = True desc += capture.group(1) + '\n' else: break + + if not desc_present: + raise Exception("No description found for " + proto) return desc - def parse_ret(self): + def parse_ret(self, proto): p = re.compile(' \* ?(?:\t| {5,8})Return$') capture = p.match(self.line) if not capture: - # Helper can have empty retval and we might be parsing another - # attribute: return but do not consume. - return '' + raise Exception("No return section found for " + proto) # Return value description can be several lines, some of them possibly # empty, and it stops when another subsection title is met. ret = '' + ret_present = False while True: self.line = self.reader.readline() if self.line == ' *\n': @@ -168,44 +174,101 @@ class HeaderParser(object): p = re.compile(' \* ?(?:\t| {5,8})(?:\t| {8})(.*)') capture = p.match(self.line) if capture: + ret_present = True ret += capture.group(1) + '\n' else: break + + if not ret_present: + raise Exception("No return found for " + proto) return ret - def seek_to(self, target, help_message): + def seek_to(self, target, help_message, discard_lines = 1): self.reader.seek(0) offset = self.reader.read().find(target) if offset == -1: raise Exception(help_message) self.reader.seek(offset) self.reader.readline() - self.reader.readline() + for _ in range(discard_lines): + self.reader.readline() self.line = self.reader.readline() - def parse_syscall(self): + def parse_desc_syscall(self): self.seek_to('* DOC: eBPF Syscall Commands', 'Could not find start of eBPF syscall descriptions list') while True: try: command = self.parse_element() self.commands.append(command) + self.desc_syscalls.append(command.proto) + except NoSyscallCommandFound: break - def parse_helpers(self): + def parse_enum_syscall(self): + self.seek_to('enum bpf_cmd {', + 'Could not find start of bpf_cmd enum', 0) + # Searches for either one or more BPF\w+ enums + bpf_p = re.compile('\s*(BPF\w+)+') + # Searches for an enum entry assigned to another entry, + # for e.g. BPF_PROG_RUN = BPF_PROG_TEST_RUN, which is + # not documented hence should be skipped in check to + # determine if the right number of syscalls are documented + assign_p = re.compile('\s*(BPF\w+)\s*=\s*(BPF\w+)') + bpf_cmd_str = '' + while True: + capture = assign_p.match(self.line) + if capture: + # Skip line if an enum entry is assigned to another entry + self.line = self.reader.readline() + continue + capture = bpf_p.match(self.line) + if capture: + bpf_cmd_str += self.line + else: + break + self.line = self.reader.readline() + # Find the number of occurences of BPF\w+ + self.enum_syscalls = re.findall('(BPF\w+)+', bpf_cmd_str) + + def parse_desc_helpers(self): self.seek_to('* Start of BPF helper function descriptions:', 'Could not find start of eBPF helper descriptions list') while True: try: helper = self.parse_helper() self.helpers.append(helper) + proto = helper.proto_break_down() + self.desc_unique_helpers.add(proto['name']) except NoHelperFound: break + def parse_define_helpers(self): + # Parse the number of FN(...) in #define __BPF_FUNC_MAPPER to compare + # later with the number of unique function names present in description. + # Note: seek_to(..) discards the first line below the target search text, + # resulting in FN(unspec) being skipped and not added to self.define_unique_helpers. + self.seek_to('#define __BPF_FUNC_MAPPER(FN)', + 'Could not find start of eBPF helper definition list') + # Searches for either one or more FN(\w+) defines or a backslash for newline + p = re.compile('\s*(FN\(\w+\))+|\\\\') + fn_defines_str = '' + while True: + capture = p.match(self.line) + if capture: + fn_defines_str += self.line + else: + break + self.line = self.reader.readline() + # Find the number of occurences of FN(\w+) + self.define_unique_helpers = re.findall('FN\(\w+\)', fn_defines_str) + def run(self): - self.parse_syscall() - self.parse_helpers() + self.parse_desc_syscall() + self.parse_enum_syscall() + self.parse_desc_helpers() + self.parse_define_helpers() self.reader.close() ############################################################################### @@ -235,6 +298,25 @@ class Printer(object): self.print_one(elem) self.print_footer() + def elem_number_check(self, desc_unique_elem, define_unique_elem, type, instance): + """ + Checks the number of helpers/syscalls documented within the header file + description with those defined as part of enum/macro and raise an + Exception if they don't match. + """ + nr_desc_unique_elem = len(desc_unique_elem) + nr_define_unique_elem = len(define_unique_elem) + if nr_desc_unique_elem != nr_define_unique_elem: + exception_msg = ''' +The number of unique %s in description (%d) doesn\'t match the number of unique %s defined in %s (%d) +''' % (type, nr_desc_unique_elem, type, instance, nr_define_unique_elem) + if nr_desc_unique_elem < nr_define_unique_elem: + # Function description is parsed until no helper is found (which can be due to + # misformatting). Hence, only print the first missing/misformatted helper/enum. + exception_msg += ''' +The description for %s is not present or formatted correctly. +''' % (define_unique_elem[nr_desc_unique_elem]) + raise Exception(exception_msg) class PrinterRST(Printer): """ @@ -295,7 +377,6 @@ class PrinterRST(Printer): print('') - class PrinterHelpersRST(PrinterRST): """ A printer for dumping collected information about helpers as a ReStructured @@ -305,6 +386,7 @@ class PrinterHelpersRST(PrinterRST): """ def __init__(self, parser): self.elements = parser.helpers + self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '__BPF_FUNC_MAPPER') def print_header(self): header = '''\ @@ -478,6 +560,7 @@ class PrinterSyscallRST(PrinterRST): """ def __init__(self, parser): self.elements = parser.commands + self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd') def print_header(self): header = '''\ @@ -509,6 +592,7 @@ class PrinterHelpers(Printer): """ def __init__(self, parser): self.elements = parser.helpers + self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '__BPF_FUNC_MAPPER') type_fwds = [ 'struct bpf_fib_lookup', diff --git a/scripts/pahole-flags.sh b/scripts/pahole-flags.sh index e6093adf4c06..0d99ef17e4a5 100755 --- a/scripts/pahole-flags.sh +++ b/scripts/pahole-flags.sh @@ -7,7 +7,7 @@ if ! [ -x "$(command -v ${PAHOLE})" ]; then exit 0 fi -pahole_ver=$(${PAHOLE} --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/') +pahole_ver=$($(dirname $0)/pahole-version.sh ${PAHOLE}) if [ "${pahole_ver}" -ge "118" ] && [ "${pahole_ver}" -le "121" ]; then # pahole 1.18 through 1.21 can't handle zero-sized per-CPU vars @@ -16,5 +16,8 @@ fi if [ "${pahole_ver}" -ge "121" ]; then extra_paholeopt="${extra_paholeopt} --btf_gen_floats" fi +if [ "${pahole_ver}" -ge "122" ]; then + extra_paholeopt="${extra_paholeopt} -j" +fi echo ${extra_paholeopt} diff --git a/scripts/pahole-version.sh b/scripts/pahole-version.sh new file mode 100755 index 000000000000..f8a32ab93ad1 --- /dev/null +++ b/scripts/pahole-version.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Usage: $ ./pahole-version.sh pahole +# +# Prints pahole's version in a 3-digit form, such as 119 for v1.19. + +if [ ! -x "$(command -v "$@")" ]; then + echo 0 + exit 1 +fi + +"$@" --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/' diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 842889f3dcb7..a9f8c63a96d1 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -838,7 +838,7 @@ int devcgroup_check_permission(short type, u32 major, u32 minor, short access) int rc = BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access); if (rc) - return -EPERM; + return rc; #ifdef CONFIG_CGROUP_DEVICE return devcgroup_legacy_check_permission(type, major, minor, access); diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index c6412dec3810..3d3f8c5c502b 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -418,6 +418,7 @@ int ima_file_mmap(struct file *file, unsigned long prot) /** * ima_file_mprotect - based on policy, limit mprotect change + * @vma: vm_area_struct protection is set to * @prot: contains the protection that will be applied by the kernel. * * Files can be mmap'ed read/write and later changed to execute to circumvent @@ -519,20 +520,38 @@ int ima_file_check(struct file *file, int mask) } EXPORT_SYMBOL_GPL(ima_file_check); -static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size) +static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf, + size_t buf_size) { - struct integrity_iint_cache *iint; - int hash_algo; + struct integrity_iint_cache *iint = NULL, tmp_iint; + int rc, hash_algo; - if (!ima_policy_flag) - return -EOPNOTSUPP; + if (ima_policy_flag) { + iint = integrity_iint_find(inode); + if (iint) + mutex_lock(&iint->mutex); + } + + if ((!iint || !(iint->flags & IMA_COLLECTED)) && file) { + if (iint) + mutex_unlock(&iint->mutex); + + memset(&tmp_iint, 0, sizeof(tmp_iint)); + tmp_iint.inode = inode; + mutex_init(&tmp_iint.mutex); + + rc = ima_collect_measurement(&tmp_iint, file, NULL, 0, + ima_hash_algo, NULL); + if (rc < 0) + return -EOPNOTSUPP; + + iint = &tmp_iint; + mutex_lock(&iint->mutex); + } - iint = integrity_iint_find(inode); if (!iint) return -EOPNOTSUPP; - mutex_lock(&iint->mutex); - /* * ima_file_hash can be called when ima_collect_measurement has still * not been called, we might not always have a hash. @@ -551,12 +570,14 @@ static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size) hash_algo = iint->ima_hash->algo; mutex_unlock(&iint->mutex); + if (iint == &tmp_iint) + kfree(iint->ima_hash); + return hash_algo; } /** - * ima_file_hash - return the stored measurement if a file has been hashed and - * is in the iint cache. + * ima_file_hash - return a measurement of the file * @file: pointer to the file * @buf: buffer in which to store the hash * @buf_size: length of the buffer @@ -569,7 +590,7 @@ static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size) * The file hash returned is based on the entire file, including the appended * signature. * - * If IMA is disabled or if no measurement is available, return -EOPNOTSUPP. + * If the measurement cannot be performed, return -EOPNOTSUPP. * If the parameters are incorrect, return -EINVAL. */ int ima_file_hash(struct file *file, char *buf, size_t buf_size) @@ -577,7 +598,7 @@ int ima_file_hash(struct file *file, char *buf, size_t buf_size) if (!file) return -EINVAL; - return __ima_inode_hash(file_inode(file), buf, buf_size); + return __ima_inode_hash(file_inode(file), file, buf, buf_size); } EXPORT_SYMBOL_GPL(ima_file_hash); @@ -604,14 +625,14 @@ int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size) if (!inode) return -EINVAL; - return __ima_inode_hash(inode, buf, buf_size); + return __ima_inode_hash(inode, NULL, buf, buf_size); } EXPORT_SYMBOL_GPL(ima_inode_hash); /** * ima_post_create_tmpfile - mark newly created tmpfile as new - * @mnt_userns: user namespace of the mount the inode was found from - * @file : newly created tmpfile + * @mnt_userns: user namespace of the mount the inode was found from + * @inode: inode of the newly created tmpfile * * No measuring, appraising or auditing of newly created tmpfiles is needed. * Skip calling process_measurement(), but indicate which newly, created @@ -643,7 +664,7 @@ void ima_post_create_tmpfile(struct user_namespace *mnt_userns, /** * ima_post_path_mknod - mark as a new inode - * @mnt_userns: user namespace of the mount the inode was found from + * @mnt_userns: user namespace of the mount the inode was found from * @dentry: newly created dentry * * Mark files created via the mknodat syscall as new, so that the @@ -814,8 +835,8 @@ int ima_load_data(enum kernel_load_data_id id, bool contents) * ima_post_load_data - appraise decision based on policy * @buf: pointer to in memory file contents * @size: size of in memory file contents - * @id: kernel load data caller identifier - * @description: @id-specific description of contents + * @load_id: kernel load data caller identifier + * @description: @load_id-specific description of contents * * Measure/appraise/audit in memory buffer based on policy. Policy rules * are written in terms of a policy identifier. diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 94ea2a8b2bb7..d8ceee9e0d6f 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -76,6 +76,7 @@ static const struct nlmsg_perm nlmsg_route_perms[] = { RTM_GETNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_NEWSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_GETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_SETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, { RTM_NEWCACHEREPORT, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_NEWCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, { RTM_DELCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, @@ -91,6 +92,9 @@ static const struct nlmsg_perm nlmsg_route_perms[] = { RTM_NEWNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, { RTM_DELNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, { RTM_GETNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, }; static const struct nlmsg_perm nlmsg_tcpdiag_perms[] = @@ -176,7 +180,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) * structures at the top of this file with the new mappings * before updating the BUILD_BUG_ON() macro! */ - BUILD_BUG_ON(RTM_MAX != (RTM_NEWNEXTHOPBUCKET + 3)); + BUILD_BUG_ON(RTM_MAX != (RTM_NEWTUNNEL + 3)); err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms, sizeof(nlmsg_route_perms)); break; diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst index bc276388f432..68454ef28f58 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst @@ -25,6 +25,8 @@ GEN COMMANDS | **bpftool** **gen object** *OUTPUT_FILE* *INPUT_FILE* [*INPUT_FILE*...] | **bpftool** **gen skeleton** *FILE* [**name** *OBJECT_NAME*] +| **bpftool** **gen subskeleton** *FILE* [**name** *OBJECT_NAME*] +| **bpftool** **gen min_core_btf** *INPUT* *OUTPUT* *OBJECT* [*OBJECT*...] | **bpftool** **gen help** DESCRIPTION @@ -149,6 +151,50 @@ DESCRIPTION (non-read-only) data from userspace, with same simplicity as for BPF side. + **bpftool gen subskeleton** *FILE* + Generate BPF subskeleton C header file for a given *FILE*. + + Subskeletons are similar to skeletons, except they do not own + the corresponding maps, programs, or global variables. They + require that the object file used to generate them is already + loaded into a *bpf_object* by some other means. + + This functionality is useful when a library is included into a + larger BPF program. A subskeleton for the library would have + access to all objects and globals defined in it, without + having to know about the larger program. + + Consequently, there are only two functions defined + for subskeletons: + + - **example__open(bpf_object\*)** + Instantiates a subskeleton from an already opened (but not + necessarily loaded) **bpf_object**. + + - **example__destroy()** + Frees the storage for the subskeleton but *does not* unload + any BPF programs or maps. + + **bpftool** **gen min_core_btf** *INPUT* *OUTPUT* *OBJECT* [*OBJECT*...] + Generate a minimum BTF file as *OUTPUT*, derived from a given + *INPUT* BTF file, containing all needed BTF types so one, or + more, given eBPF objects CO-RE relocations may be satisfied. + + When kernels aren't compiled with CONFIG_DEBUG_INFO_BTF, + libbpf, when loading an eBPF object, has to rely on external + BTF files to be able to calculate CO-RE relocations. + + Usually, an external BTF file is built from existing kernel + DWARF data using pahole. It contains all the types used by + its respective kernel image and, because of that, is big. + + The min_core_btf feature builds smaller BTF files, customized + to one or multiple eBPF objects, so they can be distributed + together with an eBPF CO-RE based application, turning the + application portable to different kernel versions. + + Check examples bellow for more information how to use it. + **bpftool gen help** Print short help message. @@ -215,7 +261,9 @@ This is example BPF application with two BPF programs and a mix of BPF maps and global variables. Source code is split across two source code files. **$ clang -target bpf -g example1.bpf.c -o example1.bpf.o** + **$ clang -target bpf -g example2.bpf.c -o example2.bpf.o** + **$ bpftool gen object example.bpf.o example1.bpf.o example2.bpf.o** This set of commands compiles *example1.bpf.c* and *example2.bpf.c* @@ -329,3 +377,70 @@ BPF ELF object file *example.bpf.o*. my_static_var: 7 This is a stripped-out version of skeleton generated for above example code. + +min_core_btf +------------ + +**$ bpftool btf dump file 5.4.0-example.btf format raw** + +:: + + [1] INT 'long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none) + [2] CONST '(anon)' type_id=1 + [3] VOLATILE '(anon)' type_id=1 + [4] ARRAY '(anon)' type_id=1 index_type_id=21 nr_elems=2 + [5] PTR '(anon)' type_id=8 + [6] CONST '(anon)' type_id=5 + [7] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=(none) + [8] CONST '(anon)' type_id=7 + [9] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none) + + +**$ bpftool btf dump file one.bpf.o format raw** + +:: + + [1] PTR '(anon)' type_id=2 + [2] STRUCT 'trace_event_raw_sys_enter' size=64 vlen=4 + 'ent' type_id=3 bits_offset=0 + 'id' type_id=7 bits_offset=64 + 'args' type_id=9 bits_offset=128 + '__data' type_id=12 bits_offset=512 + [3] STRUCT 'trace_entry' size=8 vlen=4 + 'type' type_id=4 bits_offset=0 + 'flags' type_id=5 bits_offset=16 + 'preempt_count' type_id=5 bits_offset=24 + + +**$ bpftool gen min_core_btf 5.4.0-example.btf 5.4.0-smaller.btf one.bpf.o** + +**$ bpftool btf dump file 5.4.0-smaller.btf format raw** + +:: + + [1] TYPEDEF 'pid_t' type_id=6 + [2] STRUCT 'trace_event_raw_sys_enter' size=64 vlen=1 + 'args' type_id=4 bits_offset=128 + [3] STRUCT 'task_struct' size=9216 vlen=2 + 'pid' type_id=1 bits_offset=17920 + 'real_parent' type_id=7 bits_offset=18048 + [4] ARRAY '(anon)' type_id=5 index_type_id=8 nr_elems=6 + [5] INT 'long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none) + [6] TYPEDEF '__kernel_pid_t' type_id=8 + [7] PTR '(anon)' type_id=3 + [8] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED + + +Now, the "5.4.0-smaller.btf" file may be used by libbpf as an external BTF file +when loading the "one.bpf.o" object into the "5.4.0-example" kernel. Note that +the generated BTF file won't allow other eBPF objects to be loaded, just the +ones given to min_core_btf. + +:: + + LIBBPF_OPTS(bpf_object_open_opts, opts, .btf_custom_path = "5.4.0-smaller.btf"); + struct bpf_object *obj; + + obj = bpf_object__open_file("one.bpf.o", &opts); + + ... diff --git a/tools/bpf/bpftool/Documentation/bpftool.rst b/tools/bpf/bpftool/Documentation/bpftool.rst index 7084dd9fa2f8..6965c94dfdaf 100644 --- a/tools/bpf/bpftool/Documentation/bpftool.rst +++ b/tools/bpf/bpftool/Documentation/bpftool.rst @@ -20,7 +20,8 @@ SYNOPSIS **bpftool** **version** - *OBJECT* := { **map** | **program** | **cgroup** | **perf** | **net** | **feature** } + *OBJECT* := { **map** | **program** | **link** | **cgroup** | **perf** | **net** | **feature** | + **btf** | **gen** | **struct_ops** | **iter** } *OPTIONS* := { { **-V** | **--version** } | |COMMON_OPTIONS| } @@ -31,6 +32,8 @@ SYNOPSIS *PROG-COMMANDS* := { **show** | **list** | **dump jited** | **dump xlated** | **pin** | **load** | **attach** | **detach** | **help** } + *LINK-COMMANDS* := { **show** | **list** | **pin** | **detach** | **help** } + *CGROUP-COMMANDS* := { **show** | **list** | **attach** | **detach** | **help** } *PERF-COMMANDS* := { **show** | **list** | **help** } @@ -39,6 +42,14 @@ SYNOPSIS *FEATURE-COMMANDS* := { **probe** | **help** } + *BTF-COMMANDS* := { **show** | **list** | **dump** | **help** } + + *GEN-COMMANDS* := { **object** | **skeleton** | **min_core_btf** | **help** } + + *STRUCT-OPS-COMMANDS* := { **show** | **list** | **dump** | **register** | **unregister** | **help** } + + *ITER-COMMANDS* := { **pin** | **help** } + DESCRIPTION =========== *bpftool* allows for inspection and simple modification of BPF objects diff --git a/tools/bpf/bpftool/Documentation/common_options.rst b/tools/bpf/bpftool/Documentation/common_options.rst index 908487b9c2ad..4107a586b68b 100644 --- a/tools/bpf/bpftool/Documentation/common_options.rst +++ b/tools/bpf/bpftool/Documentation/common_options.rst @@ -4,12 +4,13 @@ Print short help message (similar to **bpftool help**). -V, --version - Print version number (similar to **bpftool version**), and optional - features that were included when bpftool was compiled. Optional - features include linking against libbfd to provide the disassembler - for JIT-ted programs (**bpftool prog dump jited**) and usage of BPF - skeletons (some features like **bpftool prog profile** or showing - pids associated to BPF objects may rely on it). + Print bpftool's version number (similar to **bpftool version**), the + number of the libbpf version in use, and optional features that were + included when bpftool was compiled. Optional features include linking + against libbfd to provide the disassembler for JIT-ted programs + (**bpftool prog dump jited**) and usage of BPF skeletons (some + features like **bpftool prog profile** or showing pids associated to + BPF objects may rely on it). -j, --json Generate JSON output. For commands that cannot produce JSON, this diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index 83369f55df61..9800f966fd51 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -18,37 +18,33 @@ BPF_DIR = $(srctree)/tools/lib/bpf ifneq ($(OUTPUT),) _OUTPUT := $(OUTPUT) else - _OUTPUT := $(CURDIR) + _OUTPUT := $(CURDIR)/ endif -BOOTSTRAP_OUTPUT := $(_OUTPUT)/bootstrap/ +BOOTSTRAP_OUTPUT := $(_OUTPUT)bootstrap/ -LIBBPF_OUTPUT := $(_OUTPUT)/libbpf/ +LIBBPF_OUTPUT := $(_OUTPUT)libbpf/ LIBBPF_DESTDIR := $(LIBBPF_OUTPUT) -LIBBPF_INCLUDE := $(LIBBPF_DESTDIR)/include +LIBBPF_INCLUDE := $(LIBBPF_DESTDIR)include LIBBPF_HDRS_DIR := $(LIBBPF_INCLUDE)/bpf LIBBPF := $(LIBBPF_OUTPUT)libbpf.a LIBBPF_BOOTSTRAP_OUTPUT := $(BOOTSTRAP_OUTPUT)libbpf/ LIBBPF_BOOTSTRAP_DESTDIR := $(LIBBPF_BOOTSTRAP_OUTPUT) -LIBBPF_BOOTSTRAP_INCLUDE := $(LIBBPF_BOOTSTRAP_DESTDIR)/include +LIBBPF_BOOTSTRAP_INCLUDE := $(LIBBPF_BOOTSTRAP_DESTDIR)include LIBBPF_BOOTSTRAP_HDRS_DIR := $(LIBBPF_BOOTSTRAP_INCLUDE)/bpf LIBBPF_BOOTSTRAP := $(LIBBPF_BOOTSTRAP_OUTPUT)libbpf.a -# We need to copy hashmap.h and nlattr.h which is not otherwise exported by -# libbpf, but still required by bpftool. -LIBBPF_INTERNAL_HDRS := $(addprefix $(LIBBPF_HDRS_DIR)/,hashmap.h nlattr.h) -LIBBPF_BOOTSTRAP_INTERNAL_HDRS := $(addprefix $(LIBBPF_BOOTSTRAP_HDRS_DIR)/,hashmap.h) - -ifeq ($(BPFTOOL_VERSION),) -BPFTOOL_VERSION := $(shell make -rR --no-print-directory -sC ../../.. kernelversion) -endif +# We need to copy hashmap.h, nlattr.h, relo_core.h and libbpf_internal.h +# which are not otherwise exported by libbpf, but still required by bpftool. +LIBBPF_INTERNAL_HDRS := $(addprefix $(LIBBPF_HDRS_DIR)/,hashmap.h nlattr.h relo_core.h libbpf_internal.h) +LIBBPF_BOOTSTRAP_INTERNAL_HDRS := $(addprefix $(LIBBPF_BOOTSTRAP_HDRS_DIR)/,hashmap.h relo_core.h libbpf_internal.h) $(LIBBPF_OUTPUT) $(BOOTSTRAP_OUTPUT) $(LIBBPF_BOOTSTRAP_OUTPUT) $(LIBBPF_HDRS_DIR) $(LIBBPF_BOOTSTRAP_HDRS_DIR): $(QUIET_MKDIR)mkdir -p $@ $(LIBBPF): $(wildcard $(BPF_DIR)/*.[ch] $(BPF_DIR)/Makefile) | $(LIBBPF_OUTPUT) $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_OUTPUT) \ - DESTDIR=$(LIBBPF_DESTDIR) prefix= $(LIBBPF) install_headers + DESTDIR=$(LIBBPF_DESTDIR:/=) prefix= $(LIBBPF) install_headers $(LIBBPF_INTERNAL_HDRS): $(LIBBPF_HDRS_DIR)/%.h: $(BPF_DIR)/%.h | $(LIBBPF_HDRS_DIR) $(call QUIET_INSTALL, $@) @@ -56,7 +52,7 @@ $(LIBBPF_INTERNAL_HDRS): $(LIBBPF_HDRS_DIR)/%.h: $(BPF_DIR)/%.h | $(LIBBPF_HDRS_ $(LIBBPF_BOOTSTRAP): $(wildcard $(BPF_DIR)/*.[ch] $(BPF_DIR)/Makefile) | $(LIBBPF_BOOTSTRAP_OUTPUT) $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_BOOTSTRAP_OUTPUT) \ - DESTDIR=$(LIBBPF_BOOTSTRAP_DESTDIR) prefix= \ + DESTDIR=$(LIBBPF_BOOTSTRAP_DESTDIR:/=) prefix= \ ARCH= CROSS_COMPILE= CC=$(HOSTCC) LD=$(HOSTLD) $@ install_headers $(LIBBPF_BOOTSTRAP_INTERNAL_HDRS): $(LIBBPF_BOOTSTRAP_HDRS_DIR)/%.h: $(BPF_DIR)/%.h | $(LIBBPF_BOOTSTRAP_HDRS_DIR) @@ -83,7 +79,9 @@ CFLAGS += -DPACKAGE='"bpftool"' -D__EXPORTED_HEADERS__ \ -I$(srctree)/kernel/bpf/ \ -I$(srctree)/tools/include \ -I$(srctree)/tools/include/uapi +ifneq ($(BPFTOOL_VERSION),) CFLAGS += -DBPFTOOL_VERSION='"$(BPFTOOL_VERSION)"' +endif ifneq ($(EXTRA_CFLAGS),) CFLAGS += $(EXTRA_CFLAGS) endif @@ -95,7 +93,7 @@ INSTALL ?= install RM ?= rm -f FEATURE_USER = .bpftool -FEATURE_TESTS = libbfd disassembler-four-args reallocarray zlib libcap \ +FEATURE_TESTS = libbfd disassembler-four-args zlib libcap \ clang-bpf-co-re FEATURE_DISPLAY = libbfd disassembler-four-args zlib libcap \ clang-bpf-co-re @@ -120,10 +118,6 @@ ifeq ($(feature-disassembler-four-args), 1) CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE endif -ifeq ($(feature-reallocarray), 0) -CFLAGS += -DCOMPAT_NEED_REALLOCARRAY -endif - LIBS = $(LIBBPF) -lelf -lz LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz ifeq ($(feature-libcap), 1) diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 493753a4962e..5df8d72c5179 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -1003,9 +1003,25 @@ _bpftool() ;; esac ;; + subskeleton) + case $prev in + $command) + _filedir + return 0 + ;; + *) + _bpftool_once_attr 'name' + return 0 + ;; + esac + ;; + min_core_btf) + _filedir + return 0 + ;; *) [[ $prev == $object ]] && \ - COMPREPLY=( $( compgen -W 'object skeleton help' -- "$cur" ) ) + COMPREPLY=( $( compgen -W 'object skeleton subskeleton help min_core_btf' -- "$cur" ) ) ;; esac ;; diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c index 59833125ac0a..a2c665beda87 100644 --- a/tools/bpf/bpftool/btf.c +++ b/tools/bpf/bpftool/btf.c @@ -902,7 +902,7 @@ static int do_show(int argc, char **argv) equal_fn_for_key_as_id, NULL); btf_map_table = hashmap__new(hash_fn_for_key_as_id, equal_fn_for_key_as_id, NULL); - if (!btf_prog_table || !btf_map_table) { + if (IS_ERR(btf_prog_table) || IS_ERR(btf_map_table)) { hashmap__free(btf_prog_table); hashmap__free(btf_map_table); if (fd >= 0) diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c index 3571a281c43f..effe136119d7 100644 --- a/tools/bpf/bpftool/cgroup.c +++ b/tools/bpf/bpftool/cgroup.c @@ -50,6 +50,7 @@ static int show_bpf_prog(int id, enum bpf_attach_type attach_type, const char *attach_flags_str, int level) { + char prog_name[MAX_PROG_FULL_NAME]; struct bpf_prog_info info = {}; __u32 info_len = sizeof(info); int prog_fd; @@ -63,6 +64,7 @@ static int show_bpf_prog(int id, enum bpf_attach_type attach_type, return -1; } + get_prog_full_name(&info, prog_fd, prog_name, sizeof(prog_name)); if (json_output) { jsonw_start_object(json_wtr); jsonw_uint_field(json_wtr, "id", info.id); @@ -73,7 +75,7 @@ static int show_bpf_prog(int id, enum bpf_attach_type attach_type, jsonw_uint_field(json_wtr, "attach_type", attach_type); jsonw_string_field(json_wtr, "attach_flags", attach_flags_str); - jsonw_string_field(json_wtr, "name", info.name); + jsonw_string_field(json_wtr, "name", prog_name); jsonw_end_object(json_wtr); } else { printf("%s%-8u ", level ? " " : "", info.id); @@ -81,7 +83,7 @@ static int show_bpf_prog(int id, enum bpf_attach_type attach_type, printf("%-15s", attach_type_name[attach_type]); else printf("type %-10u", attach_type); - printf(" %-15s %-15s\n", attach_flags_str, info.name); + printf(" %-15s %-15s\n", attach_flags_str, prog_name); } close(prog_fd); diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index fa8eb8134344..0c1e06cf50b9 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -24,6 +24,7 @@ #include #include #include /* libbpf_num_possible_cpus */ +#include #include "main.h" @@ -55,7 +56,6 @@ const char * const attach_type_name[__MAX_BPF_ATTACH_TYPE] = { [BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6", [BPF_CGROUP_GETSOCKOPT] = "getsockopt", [BPF_CGROUP_SETSOCKOPT] = "setsockopt", - [BPF_SK_SKB_STREAM_PARSER] = "sk_skb_stream_parser", [BPF_SK_SKB_STREAM_VERDICT] = "sk_skb_stream_verdict", [BPF_SK_SKB_VERDICT] = "sk_skb_verdict", @@ -75,6 +75,7 @@ const char * const attach_type_name[__MAX_BPF_ATTACH_TYPE] = { [BPF_SK_REUSEPORT_SELECT] = "sk_skb_reuseport_select", [BPF_SK_REUSEPORT_SELECT_OR_MIGRATE] = "sk_skb_reuseport_select_or_migrate", [BPF_PERF_EVENT] = "perf_event", + [BPF_TRACE_KPROBE_MULTI] = "trace_kprobe_multi", }; void p_err(const char *fmt, ...) @@ -304,6 +305,49 @@ const char *get_fd_type_name(enum bpf_obj_type type) return names[type]; } +void get_prog_full_name(const struct bpf_prog_info *prog_info, int prog_fd, + char *name_buff, size_t buff_len) +{ + const char *prog_name = prog_info->name; + const struct btf_type *func_type; + const struct bpf_func_info finfo = {}; + struct bpf_prog_info info = {}; + __u32 info_len = sizeof(info); + struct btf *prog_btf = NULL; + + if (buff_len <= BPF_OBJ_NAME_LEN || + strlen(prog_info->name) < BPF_OBJ_NAME_LEN - 1) + goto copy_name; + + if (!prog_info->btf_id || prog_info->nr_func_info == 0) + goto copy_name; + + info.nr_func_info = 1; + info.func_info_rec_size = prog_info->func_info_rec_size; + if (info.func_info_rec_size > sizeof(finfo)) + info.func_info_rec_size = sizeof(finfo); + info.func_info = ptr_to_u64(&finfo); + + if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) + goto copy_name; + + prog_btf = btf__load_from_kernel_by_id(info.btf_id); + if (!prog_btf) + goto copy_name; + + func_type = btf__type_by_id(prog_btf, finfo.type_id); + if (!func_type || !btf_is_func(func_type)) + goto copy_name; + + prog_name = btf__name_by_offset(prog_btf, func_type->name_off); + +copy_name: + snprintf(name_buff, buff_len, "%s", prog_name); + + if (prog_btf) + btf__free(prog_btf); +} + int get_fd_type(int fd) { char path[PATH_MAX]; diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index e999159fa28d..c2f43a5d38e0 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -45,6 +46,11 @@ static bool run_as_unprivileged; /* Miscellaneous utility functions */ +static bool grep(const char *buffer, const char *pattern) +{ + return !!strstr(buffer, pattern); +} + static bool check_procfs(void) { struct statfs st_fs; @@ -135,6 +141,32 @@ static void print_end_section(void) /* Probing functions */ +static int get_vendor_id(int ifindex) +{ + char ifname[IF_NAMESIZE], path[64], buf[8]; + ssize_t len; + int fd; + + if (!if_indextoname(ifindex, ifname)) + return -1; + + snprintf(path, sizeof(path), "/sys/class/net/%s/device/vendor", ifname); + + fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd < 0) + return -1; + + len = read(fd, buf, sizeof(buf)); + close(fd); + if (len < 0) + return -1; + if (len >= (ssize_t)sizeof(buf)) + return -1; + buf[len] = '\0'; + + return strtol(buf, NULL, 0); +} + static int read_procfs(const char *path) { char *endptr, *line = NULL; @@ -478,6 +510,40 @@ static bool probe_bpf_syscall(const char *define_prefix) return res; } +static bool +probe_prog_load_ifindex(enum bpf_prog_type prog_type, + const struct bpf_insn *insns, size_t insns_cnt, + char *log_buf, size_t log_buf_sz, + __u32 ifindex) +{ + LIBBPF_OPTS(bpf_prog_load_opts, opts, + .log_buf = log_buf, + .log_size = log_buf_sz, + .log_level = log_buf ? 1 : 0, + .prog_ifindex = ifindex, + ); + int fd; + + errno = 0; + fd = bpf_prog_load(prog_type, NULL, "GPL", insns, insns_cnt, &opts); + if (fd >= 0) + close(fd); + + return fd >= 0 && errno != EINVAL && errno != EOPNOTSUPP; +} + +static bool probe_prog_type_ifindex(enum bpf_prog_type prog_type, __u32 ifindex) +{ + /* nfp returns -EINVAL on exit(0) with TC offload */ + struct bpf_insn insns[2] = { + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN() + }; + + return probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns), + NULL, 0, ifindex); +} + static void probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types, const char *define_prefix, __u32 ifindex) @@ -487,8 +553,7 @@ probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types, size_t maxlen; bool res; - if (ifindex) - /* Only test offload-able program types */ + if (ifindex) { switch (prog_type) { case BPF_PROG_TYPE_SCHED_CLS: case BPF_PROG_TYPE_XDP: @@ -497,7 +562,11 @@ probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types, return; } - res = bpf_probe_prog_type(prog_type, ifindex); + res = probe_prog_type_ifindex(prog_type, ifindex); + } else { + res = libbpf_probe_bpf_prog_type(prog_type, NULL); + } + #ifdef USE_LIBCAP /* Probe may succeed even if program load fails, for unprivileged users * check that we did not fail because of insufficient permissions @@ -526,6 +595,26 @@ probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types, define_prefix); } +static bool probe_map_type_ifindex(enum bpf_map_type map_type, __u32 ifindex) +{ + LIBBPF_OPTS(bpf_map_create_opts, opts); + int key_size, value_size, max_entries; + int fd; + + opts.map_ifindex = ifindex; + + key_size = sizeof(__u32); + value_size = sizeof(__u32); + max_entries = 1; + + fd = bpf_map_create(map_type, NULL, key_size, value_size, max_entries, + &opts); + if (fd >= 0) + close(fd); + + return fd >= 0; +} + static void probe_map_type(enum bpf_map_type map_type, const char *define_prefix, __u32 ifindex) @@ -535,7 +624,19 @@ probe_map_type(enum bpf_map_type map_type, const char *define_prefix, size_t maxlen; bool res; - res = bpf_probe_map_type(map_type, ifindex); + if (ifindex) { + switch (map_type) { + case BPF_MAP_TYPE_HASH: + case BPF_MAP_TYPE_ARRAY: + break; + default: + return; + } + + res = probe_map_type_ifindex(map_type, ifindex); + } else { + res = libbpf_probe_bpf_map_type(map_type, NULL); + } /* Probe result depends on the success of map creation, no additional * check required for unprivileged users @@ -559,6 +660,33 @@ probe_map_type(enum bpf_map_type map_type, const char *define_prefix, define_prefix); } +static bool +probe_helper_ifindex(enum bpf_func_id id, enum bpf_prog_type prog_type, + __u32 ifindex) +{ + struct bpf_insn insns[2] = { + BPF_EMIT_CALL(id), + BPF_EXIT_INSN() + }; + char buf[4096] = {}; + bool res; + + probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns), buf, + sizeof(buf), ifindex); + res = !grep(buf, "invalid func ") && !grep(buf, "unknown func "); + + switch (get_vendor_id(ifindex)) { + case 0x19ee: /* Netronome specific */ + res = res && !grep(buf, "not supported by FW") && + !grep(buf, "unsupported function id"); + break; + default: + break; + } + + return res; +} + static void probe_helper_for_progtype(enum bpf_prog_type prog_type, bool supported_type, const char *define_prefix, unsigned int id, @@ -567,7 +695,10 @@ probe_helper_for_progtype(enum bpf_prog_type prog_type, bool supported_type, bool res = false; if (supported_type) { - res = bpf_probe_helper(id, prog_type, ifindex); + if (ifindex) + res = probe_helper_ifindex(id, prog_type, ifindex); + else + res = libbpf_probe_bpf_helper(prog_type, id, NULL); #ifdef USE_LIBCAP /* Probe may succeed even if program load fails, for * unprivileged users check that we did not fail because of diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index b4695df2ea3d..7ba7ff55d2ea 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -63,11 +64,11 @@ static void get_obj_name(char *name, const char *file) sanitize_identifier(name); } -static void get_header_guard(char *guard, const char *obj_name) +static void get_header_guard(char *guard, const char *obj_name, const char *suffix) { int i; - sprintf(guard, "__%s_SKEL_H__", obj_name); + sprintf(guard, "__%s_%s__", obj_name, suffix); for (i = 0; guard[i]; i++) guard[i] = toupper(guard[i]); } @@ -208,15 +209,47 @@ static int codegen_datasec_def(struct bpf_object *obj, return 0; } +static const struct btf_type *find_type_for_map(struct btf *btf, const char *map_ident) +{ + int n = btf__type_cnt(btf), i; + char sec_ident[256]; + + for (i = 1; i < n; i++) { + const struct btf_type *t = btf__type_by_id(btf, i); + const char *name; + + if (!btf_is_datasec(t)) + continue; + + name = btf__str_by_offset(btf, t->name_off); + if (!get_datasec_ident(name, sec_ident, sizeof(sec_ident))) + continue; + + if (strcmp(sec_ident, map_ident) == 0) + return t; + } + return NULL; +} + +static bool is_internal_mmapable_map(const struct bpf_map *map, char *buf, size_t sz) +{ + if (!bpf_map__is_internal(map) || !(bpf_map__map_flags(map) & BPF_F_MMAPABLE)) + return false; + + if (!get_map_ident(map, buf, sz)) + return false; + + return true; +} + static int codegen_datasecs(struct bpf_object *obj, const char *obj_name) { struct btf *btf = bpf_object__btf(obj); - int n = btf__type_cnt(btf); struct btf_dump *d; struct bpf_map *map; const struct btf_type *sec; - char sec_ident[256], map_ident[256]; - int i, err = 0; + char map_ident[256]; + int err = 0; d = btf_dump__new(btf, codegen_btf_dump_printf, NULL, NULL); err = libbpf_get_error(d); @@ -225,31 +258,10 @@ static int codegen_datasecs(struct bpf_object *obj, const char *obj_name) bpf_object__for_each_map(map, obj) { /* only generate definitions for memory-mapped internal maps */ - if (!bpf_map__is_internal(map)) - continue; - if (!(bpf_map__def(map)->map_flags & BPF_F_MMAPABLE)) + if (!is_internal_mmapable_map(map, map_ident, sizeof(map_ident))) continue; - if (!get_map_ident(map, map_ident, sizeof(map_ident))) - continue; - - sec = NULL; - for (i = 1; i < n; i++) { - const struct btf_type *t = btf__type_by_id(btf, i); - const char *name; - - if (!btf_is_datasec(t)) - continue; - - name = btf__str_by_offset(btf, t->name_off); - if (!get_datasec_ident(name, sec_ident, sizeof(sec_ident))) - continue; - - if (strcmp(sec_ident, map_ident) == 0) { - sec = t; - break; - } - } + sec = find_type_for_map(btf, map_ident); /* In some cases (e.g., sections like .rodata.cst16 containing * compiler allocated string constants only) there will be @@ -274,6 +286,96 @@ static int codegen_datasecs(struct bpf_object *obj, const char *obj_name) return err; } +static bool btf_is_ptr_to_func_proto(const struct btf *btf, + const struct btf_type *v) +{ + return btf_is_ptr(v) && btf_is_func_proto(btf__type_by_id(btf, v->type)); +} + +static int codegen_subskel_datasecs(struct bpf_object *obj, const char *obj_name) +{ + struct btf *btf = bpf_object__btf(obj); + struct btf_dump *d; + struct bpf_map *map; + const struct btf_type *sec, *var; + const struct btf_var_secinfo *sec_var; + int i, err = 0, vlen; + char map_ident[256], sec_ident[256]; + bool strip_mods = false, needs_typeof = false; + const char *sec_name, *var_name; + __u32 var_type_id; + + d = btf_dump__new(btf, codegen_btf_dump_printf, NULL, NULL); + if (!d) + return -errno; + + bpf_object__for_each_map(map, obj) { + /* only generate definitions for memory-mapped internal maps */ + if (!is_internal_mmapable_map(map, map_ident, sizeof(map_ident))) + continue; + + sec = find_type_for_map(btf, map_ident); + if (!sec) + continue; + + sec_name = btf__name_by_offset(btf, sec->name_off); + if (!get_datasec_ident(sec_name, sec_ident, sizeof(sec_ident))) + continue; + + strip_mods = strcmp(sec_name, ".kconfig") != 0; + printf(" struct %s__%s {\n", obj_name, sec_ident); + + sec_var = btf_var_secinfos(sec); + vlen = btf_vlen(sec); + for (i = 0; i < vlen; i++, sec_var++) { + DECLARE_LIBBPF_OPTS(btf_dump_emit_type_decl_opts, opts, + .indent_level = 2, + .strip_mods = strip_mods, + /* we'll print the name separately */ + .field_name = "", + ); + + var = btf__type_by_id(btf, sec_var->type); + var_name = btf__name_by_offset(btf, var->name_off); + var_type_id = var->type; + + /* static variables are not exposed through BPF skeleton */ + if (btf_var(var)->linkage == BTF_VAR_STATIC) + continue; + + /* The datasec member has KIND_VAR but we want the + * underlying type of the variable (e.g. KIND_INT). + */ + var = skip_mods_and_typedefs(btf, var->type, NULL); + + printf("\t\t"); + /* Func and array members require special handling. + * Instead of producing `typename *var`, they produce + * `typeof(typename) *var`. This allows us to keep a + * similar syntax where the identifier is just prefixed + * by *, allowing us to ignore C declaration minutiae. + */ + needs_typeof = btf_is_array(var) || btf_is_ptr_to_func_proto(btf, var); + if (needs_typeof) + printf("typeof("); + + err = btf_dump__emit_type_decl(d, var_type_id, &opts); + if (err) + goto out; + + if (needs_typeof) + printf(")"); + + printf(" *%s;\n", var_name); + } + printf(" } %s;\n", sec_ident); + } + +out: + btf_dump__free(d); + return err; +} + static void codegen(const char *template, ...) { const char *src, *end; @@ -362,6 +464,69 @@ static size_t bpf_map_mmap_sz(const struct bpf_map *map) return map_sz; } +/* Emit type size asserts for all top-level fields in memory-mapped internal maps. */ +static void codegen_asserts(struct bpf_object *obj, const char *obj_name) +{ + struct btf *btf = bpf_object__btf(obj); + struct bpf_map *map; + struct btf_var_secinfo *sec_var; + int i, vlen; + const struct btf_type *sec; + char map_ident[256], var_ident[256]; + + codegen("\ + \n\ + __attribute__((unused)) static void \n\ + %1$s__assert(struct %1$s *s) \n\ + { \n\ + #ifdef __cplusplus \n\ + #define _Static_assert static_assert \n\ + #endif \n\ + ", obj_name); + + bpf_object__for_each_map(map, obj) { + if (!is_internal_mmapable_map(map, map_ident, sizeof(map_ident))) + continue; + + sec = find_type_for_map(btf, map_ident); + if (!sec) { + /* best effort, couldn't find the type for this map */ + continue; + } + + sec_var = btf_var_secinfos(sec); + vlen = btf_vlen(sec); + + for (i = 0; i < vlen; i++, sec_var++) { + const struct btf_type *var = btf__type_by_id(btf, sec_var->type); + const char *var_name = btf__name_by_offset(btf, var->name_off); + long var_size; + + /* static variables are not exposed through BPF skeleton */ + if (btf_var(var)->linkage == BTF_VAR_STATIC) + continue; + + var_size = btf__resolve_size(btf, var->type); + if (var_size < 0) + continue; + + var_ident[0] = '\0'; + strncat(var_ident, var_name, sizeof(var_ident) - 1); + sanitize_identifier(var_ident); + + printf("\t_Static_assert(sizeof(s->%s->%s) == %ld, \"unexpected size of '%s'\");\n", + map_ident, var_ident, var_size, var_ident); + } + } + codegen("\ + \n\ + #ifdef __cplusplus \n\ + #undef _Static_assert \n\ + #endif \n\ + } \n\ + "); +} + static void codegen_attach_detach(struct bpf_object *obj, const char *obj_name) { struct bpf_program *prog; @@ -378,13 +543,16 @@ static void codegen_attach_detach(struct bpf_object *obj, const char *obj_name) int prog_fd = skel->progs.%2$s.prog_fd; \n\ ", obj_name, bpf_program__name(prog)); - switch (bpf_program__get_type(prog)) { + switch (bpf_program__type(prog)) { case BPF_PROG_TYPE_RAW_TRACEPOINT: tp_name = strchr(bpf_program__section_name(prog), '/') + 1; - printf("\tint fd = bpf_raw_tracepoint_open(\"%s\", prog_fd);\n", tp_name); + printf("\tint fd = skel_raw_tracepoint_open(\"%s\", prog_fd);\n", tp_name); break; case BPF_PROG_TYPE_TRACING: - printf("\tint fd = bpf_raw_tracepoint_open(NULL, prog_fd);\n"); + if (bpf_program__expected_attach_type(prog) == BPF_TRACE_ITER) + printf("\tint fd = skel_link_create(prog_fd, 0, BPF_TRACE_ITER);\n"); + else + printf("\tint fd = skel_raw_tracepoint_open(NULL, prog_fd);\n"); break; default: printf("\tint fd = ((void)prog_fd, 0); /* auto-attach not supported */\n"); @@ -468,8 +636,8 @@ static void codegen_destroy(struct bpf_object *obj, const char *obj_name) if (!get_map_ident(map, ident, sizeof(ident))) continue; if (bpf_map__is_internal(map) && - (bpf_map__def(map)->map_flags & BPF_F_MMAPABLE)) - printf("\tmunmap(skel->%1$s, %2$zd);\n", + (bpf_map__map_flags(map) & BPF_F_MMAPABLE)) + printf("\tskel_free_map_data(skel->%1$s, skel->maps.%1$s.initial_value, %2$zd);\n", ident, bpf_map_mmap_sz(map)); codegen("\ \n\ @@ -478,7 +646,7 @@ static void codegen_destroy(struct bpf_object *obj, const char *obj_name) } codegen("\ \n\ - free(skel); \n\ + skel_free(skel); \n\ } \n\ ", obj_name); @@ -522,7 +690,7 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h { \n\ struct %1$s *skel; \n\ \n\ - skel = calloc(sizeof(*skel), 1); \n\ + skel = skel_alloc(sizeof(*skel)); \n\ if (!skel) \n\ goto cleanup; \n\ skel->ctx.sz = (void *)&skel->links - (void *)skel; \n\ @@ -532,27 +700,22 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h const void *mmap_data = NULL; size_t mmap_size = 0; - if (!get_map_ident(map, ident, sizeof(ident))) - continue; - - if (!bpf_map__is_internal(map) || - !(bpf_map__def(map)->map_flags & BPF_F_MMAPABLE)) + if (!is_internal_mmapable_map(map, ident, sizeof(ident))) continue; codegen("\ - \n\ - skel->%1$s = \n\ - mmap(NULL, %2$zd, PROT_READ | PROT_WRITE,\n\ - MAP_SHARED | MAP_ANONYMOUS, -1, 0); \n\ - if (skel->%1$s == (void *) -1) \n\ - goto cleanup; \n\ - memcpy(skel->%1$s, (void *)\"\\ \n\ - ", ident, bpf_map_mmap_sz(map)); + \n\ + skel->%1$s = skel_prep_map_data((void *)\"\\ \n\ + ", ident); mmap_data = bpf_map__initial_value(map, &mmap_size); print_hex(mmap_data, mmap_size); - printf("\", %2$zd);\n" - "\tskel->maps.%1$s.initial_value = (__u64)(long)skel->%1$s;\n", - ident, mmap_size); + codegen("\ + \n\ + \", %1$zd, %2$zd); \n\ + if (!skel->%3$s) \n\ + goto cleanup; \n\ + skel->maps.%3$s.initial_value = (__u64) (long) skel->%3$s;\n\ + ", bpf_map_mmap_sz(map), mmap_size, ident); } codegen("\ \n\ @@ -596,21 +759,21 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h bpf_object__for_each_map(map, obj) { const char *mmap_flags; - if (!get_map_ident(map, ident, sizeof(ident))) + if (!is_internal_mmapable_map(map, ident, sizeof(ident))) continue; - if (!bpf_map__is_internal(map) || - !(bpf_map__def(map)->map_flags & BPF_F_MMAPABLE)) - continue; - - if (bpf_map__def(map)->map_flags & BPF_F_RDONLY_PROG) + if (bpf_map__map_flags(map) & BPF_F_RDONLY_PROG) mmap_flags = "PROT_READ"; else mmap_flags = "PROT_READ | PROT_WRITE"; - printf("\tskel->%1$s =\n" - "\t\tmmap(skel->%1$s, %2$zd, %3$s, MAP_SHARED | MAP_FIXED,\n" - "\t\t\tskel->maps.%1$s.map_fd, 0);\n", + codegen("\ + \n\ + skel->%1$s = skel_finalize_map_data(&skel->maps.%1$s.initial_value, \n\ + %2$zd, %3$s, skel->maps.%1$s.map_fd);\n\ + if (!skel->%1$s) \n\ + return -ENOMEM; \n\ + ", ident, bpf_map_mmap_sz(map), mmap_flags); } codegen("\ @@ -632,8 +795,11 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h } \n\ return skel; \n\ } \n\ + \n\ ", obj_name); + codegen_asserts(obj, obj_name); + codegen("\ \n\ \n\ @@ -645,10 +811,95 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h return err; } +static void +codegen_maps_skeleton(struct bpf_object *obj, size_t map_cnt, bool mmaped) +{ + struct bpf_map *map; + char ident[256]; + size_t i; + + if (!map_cnt) + return; + + codegen("\ + \n\ + \n\ + /* maps */ \n\ + s->map_cnt = %zu; \n\ + s->map_skel_sz = sizeof(*s->maps); \n\ + s->maps = (struct bpf_map_skeleton *)calloc(s->map_cnt, s->map_skel_sz);\n\ + if (!s->maps) \n\ + goto err; \n\ + ", + map_cnt + ); + i = 0; + bpf_object__for_each_map(map, obj) { + if (!get_map_ident(map, ident, sizeof(ident))) + continue; + + codegen("\ + \n\ + \n\ + s->maps[%zu].name = \"%s\"; \n\ + s->maps[%zu].map = &obj->maps.%s; \n\ + ", + i, bpf_map__name(map), i, ident); + /* memory-mapped internal maps */ + if (mmaped && is_internal_mmapable_map(map, ident, sizeof(ident))) { + printf("\ts->maps[%zu].mmaped = (void **)&obj->%s;\n", + i, ident); + } + i++; + } +} + +static void +codegen_progs_skeleton(struct bpf_object *obj, size_t prog_cnt, bool populate_links) +{ + struct bpf_program *prog; + int i; + + if (!prog_cnt) + return; + + codegen("\ + \n\ + \n\ + /* programs */ \n\ + s->prog_cnt = %zu; \n\ + s->prog_skel_sz = sizeof(*s->progs); \n\ + s->progs = (struct bpf_prog_skeleton *)calloc(s->prog_cnt, s->prog_skel_sz);\n\ + if (!s->progs) \n\ + goto err; \n\ + ", + prog_cnt + ); + i = 0; + bpf_object__for_each_program(prog, obj) { + codegen("\ + \n\ + \n\ + s->progs[%1$zu].name = \"%2$s\"; \n\ + s->progs[%1$zu].prog = &obj->progs.%2$s;\n\ + ", + i, bpf_program__name(prog)); + + if (populate_links) { + codegen("\ + \n\ + s->progs[%1$zu].link = &obj->links.%2$s;\n\ + ", + i, bpf_program__name(prog)); + } + i++; + } +} + static int do_skeleton(int argc, char **argv) { char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")]; - size_t i, map_cnt = 0, prog_cnt = 0, file_sz, mmap_sz; + size_t map_cnt = 0, prog_cnt = 0, file_sz, mmap_sz; DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts); char obj_name[MAX_OBJ_NAME_LEN] = "", *obj_data; struct bpf_object *obj = NULL; @@ -739,7 +990,7 @@ static int do_skeleton(int argc, char **argv) prog_cnt++; } - get_header_guard(header_guard, obj_name); + get_header_guard(header_guard, obj_name, "SKEL_H"); if (use_loader) { codegen("\ \n\ @@ -748,8 +999,6 @@ static int do_skeleton(int argc, char **argv) #ifndef %2$s \n\ #define %2$s \n\ \n\ - #include \n\ - #include \n\ #include \n\ \n\ struct %1$s { \n\ @@ -827,6 +1076,16 @@ static int do_skeleton(int argc, char **argv) codegen("\ \n\ + \n\ + #ifdef __cplusplus \n\ + static inline struct %1$s *open(const struct bpf_object_open_opts *opts = nullptr);\n\ + static inline struct %1$s *open_and_load(); \n\ + static inline int load(struct %1$s *skel); \n\ + static inline int attach(struct %1$s *skel); \n\ + static inline void detach(struct %1$s *skel); \n\ + static inline void destroy(struct %1$s *skel); \n\ + static inline const void *elf_bytes(size_t *sz); \n\ + #endif /* __cplusplus */ \n\ }; \n\ \n\ static void \n\ @@ -927,7 +1186,6 @@ static int do_skeleton(int argc, char **argv) s = (struct bpf_object_skeleton *)calloc(1, sizeof(*s));\n\ if (!s) \n\ goto err; \n\ - obj->skeleton = s; \n\ \n\ s->sz = sizeof(*s); \n\ s->name = \"%1$s\"; \n\ @@ -935,71 +1193,16 @@ static int do_skeleton(int argc, char **argv) ", obj_name ); - if (map_cnt) { - codegen("\ - \n\ - \n\ - /* maps */ \n\ - s->map_cnt = %zu; \n\ - s->map_skel_sz = sizeof(*s->maps); \n\ - s->maps = (struct bpf_map_skeleton *)calloc(s->map_cnt, s->map_skel_sz);\n\ - if (!s->maps) \n\ - goto err; \n\ - ", - map_cnt - ); - i = 0; - bpf_object__for_each_map(map, obj) { - if (!get_map_ident(map, ident, sizeof(ident))) - continue; - codegen("\ - \n\ - \n\ - s->maps[%zu].name = \"%s\"; \n\ - s->maps[%zu].map = &obj->maps.%s; \n\ - ", - i, bpf_map__name(map), i, ident); - /* memory-mapped internal maps */ - if (bpf_map__is_internal(map) && - (bpf_map__def(map)->map_flags & BPF_F_MMAPABLE)) { - printf("\ts->maps[%zu].mmaped = (void **)&obj->%s;\n", - i, ident); - } - i++; - } - } - if (prog_cnt) { - codegen("\ - \n\ - \n\ - /* programs */ \n\ - s->prog_cnt = %zu; \n\ - s->prog_skel_sz = sizeof(*s->progs); \n\ - s->progs = (struct bpf_prog_skeleton *)calloc(s->prog_cnt, s->prog_skel_sz);\n\ - if (!s->progs) \n\ - goto err; \n\ - ", - prog_cnt - ); - i = 0; - bpf_object__for_each_program(prog, obj) { - codegen("\ - \n\ - \n\ - s->progs[%1$zu].name = \"%2$s\"; \n\ - s->progs[%1$zu].prog = &obj->progs.%2$s;\n\ - s->progs[%1$zu].link = &obj->links.%2$s;\n\ - ", - i, bpf_program__name(prog)); - i++; - } - } + codegen_maps_skeleton(obj, map_cnt, true /*mmaped*/); + codegen_progs_skeleton(obj, prog_cnt, true /*populate_links*/); + codegen("\ \n\ \n\ s->data = (void *)%2$s__elf_bytes(&s->data_sz); \n\ \n\ + obj->skeleton = s; \n\ return 0; \n\ err: \n\ bpf_object__destroy_skeleton(s); \n\ @@ -1021,7 +1224,25 @@ static int do_skeleton(int argc, char **argv) \"; \n\ } \n\ \n\ - #endif /* %s */ \n\ + #ifdef __cplusplus \n\ + struct %1$s *%1$s::open(const struct bpf_object_open_opts *opts) { return %1$s__open_opts(opts); }\n\ + struct %1$s *%1$s::open_and_load() { return %1$s__open_and_load(); } \n\ + int %1$s::load(struct %1$s *skel) { return %1$s__load(skel); } \n\ + int %1$s::attach(struct %1$s *skel) { return %1$s__attach(skel); } \n\ + void %1$s::detach(struct %1$s *skel) { %1$s__detach(skel); } \n\ + void %1$s::destroy(struct %1$s *skel) { %1$s__destroy(skel); } \n\ + const void *%1$s::elf_bytes(size_t *sz) { return %1$s__elf_bytes(sz); } \n\ + #endif /* __cplusplus */ \n\ + \n\ + ", + obj_name); + + codegen_asserts(obj, obj_name); + + codegen("\ + \n\ + \n\ + #endif /* %1$s */ \n\ ", header_guard); err = 0; @@ -1033,6 +1254,310 @@ static int do_skeleton(int argc, char **argv) return err; } +/* Subskeletons are like skeletons, except they don't own the bpf_object, + * associated maps, links, etc. Instead, they know about the existence of + * variables, maps, programs and are able to find their locations + * _at runtime_ from an already loaded bpf_object. + * + * This allows for library-like BPF objects to have userspace counterparts + * with access to their own items without having to know anything about the + * final BPF object that the library was linked into. + */ +static int do_subskeleton(int argc, char **argv) +{ + char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SUBSKEL_H__")]; + size_t i, len, file_sz, map_cnt = 0, prog_cnt = 0, mmap_sz, var_cnt = 0, var_idx = 0; + DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts); + char obj_name[MAX_OBJ_NAME_LEN] = "", *obj_data; + struct bpf_object *obj = NULL; + const char *file, *var_name; + char ident[256]; + int fd, err = -1, map_type_id; + const struct bpf_map *map; + struct bpf_program *prog; + struct btf *btf; + const struct btf_type *map_type, *var_type; + const struct btf_var_secinfo *var; + struct stat st; + + if (!REQ_ARGS(1)) { + usage(); + return -1; + } + file = GET_ARG(); + + while (argc) { + if (!REQ_ARGS(2)) + return -1; + + if (is_prefix(*argv, "name")) { + NEXT_ARG(); + + if (obj_name[0] != '\0') { + p_err("object name already specified"); + return -1; + } + + strncpy(obj_name, *argv, MAX_OBJ_NAME_LEN - 1); + obj_name[MAX_OBJ_NAME_LEN - 1] = '\0'; + } else { + p_err("unknown arg %s", *argv); + return -1; + } + + NEXT_ARG(); + } + + if (argc) { + p_err("extra unknown arguments"); + return -1; + } + + if (use_loader) { + p_err("cannot use loader for subskeletons"); + return -1; + } + + if (stat(file, &st)) { + p_err("failed to stat() %s: %s", file, strerror(errno)); + return -1; + } + file_sz = st.st_size; + mmap_sz = roundup(file_sz, sysconf(_SC_PAGE_SIZE)); + fd = open(file, O_RDONLY); + if (fd < 0) { + p_err("failed to open() %s: %s", file, strerror(errno)); + return -1; + } + obj_data = mmap(NULL, mmap_sz, PROT_READ, MAP_PRIVATE, fd, 0); + if (obj_data == MAP_FAILED) { + obj_data = NULL; + p_err("failed to mmap() %s: %s", file, strerror(errno)); + goto out; + } + if (obj_name[0] == '\0') + get_obj_name(obj_name, file); + + /* The empty object name allows us to use bpf_map__name and produce + * ELF section names out of it. (".data" instead of "obj.data") + */ + opts.object_name = ""; + obj = bpf_object__open_mem(obj_data, file_sz, &opts); + if (!obj) { + char err_buf[256]; + + libbpf_strerror(errno, err_buf, sizeof(err_buf)); + p_err("failed to open BPF object file: %s", err_buf); + obj = NULL; + goto out; + } + + btf = bpf_object__btf(obj); + if (!btf) { + err = -1; + p_err("need btf type information for %s", obj_name); + goto out; + } + + bpf_object__for_each_program(prog, obj) { + prog_cnt++; + } + + /* First, count how many variables we have to find. + * We need this in advance so the subskel can allocate the right + * amount of storage. + */ + bpf_object__for_each_map(map, obj) { + if (!get_map_ident(map, ident, sizeof(ident))) + continue; + + /* Also count all maps that have a name */ + map_cnt++; + + if (!is_internal_mmapable_map(map, ident, sizeof(ident))) + continue; + + map_type_id = bpf_map__btf_value_type_id(map); + if (map_type_id <= 0) { + err = map_type_id; + goto out; + } + map_type = btf__type_by_id(btf, map_type_id); + + var = btf_var_secinfos(map_type); + len = btf_vlen(map_type); + for (i = 0; i < len; i++, var++) { + var_type = btf__type_by_id(btf, var->type); + + if (btf_var(var_type)->linkage == BTF_VAR_STATIC) + continue; + + var_cnt++; + } + } + + get_header_guard(header_guard, obj_name, "SUBSKEL_H"); + codegen("\ + \n\ + /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ \n\ + \n\ + /* THIS FILE IS AUTOGENERATED! */ \n\ + #ifndef %2$s \n\ + #define %2$s \n\ + \n\ + #include \n\ + #include \n\ + #include \n\ + \n\ + struct %1$s { \n\ + struct bpf_object *obj; \n\ + struct bpf_object_subskeleton *subskel; \n\ + ", obj_name, header_guard); + + if (map_cnt) { + printf("\tstruct {\n"); + bpf_object__for_each_map(map, obj) { + if (!get_map_ident(map, ident, sizeof(ident))) + continue; + printf("\t\tstruct bpf_map *%s;\n", ident); + } + printf("\t} maps;\n"); + } + + if (prog_cnt) { + printf("\tstruct {\n"); + bpf_object__for_each_program(prog, obj) { + printf("\t\tstruct bpf_program *%s;\n", + bpf_program__name(prog)); + } + printf("\t} progs;\n"); + } + + err = codegen_subskel_datasecs(obj, obj_name); + if (err) + goto out; + + /* emit code that will allocate enough storage for all symbols */ + codegen("\ + \n\ + \n\ + #ifdef __cplusplus \n\ + static inline struct %1$s *open(const struct bpf_object *src);\n\ + static inline void destroy(struct %1$s *skel); \n\ + #endif /* __cplusplus */ \n\ + }; \n\ + \n\ + static inline void \n\ + %1$s__destroy(struct %1$s *skel) \n\ + { \n\ + if (!skel) \n\ + return; \n\ + if (skel->subskel) \n\ + bpf_object__destroy_subskeleton(skel->subskel);\n\ + free(skel); \n\ + } \n\ + \n\ + static inline struct %1$s * \n\ + %1$s__open(const struct bpf_object *src) \n\ + { \n\ + struct %1$s *obj; \n\ + struct bpf_object_subskeleton *s; \n\ + int err; \n\ + \n\ + obj = (struct %1$s *)calloc(1, sizeof(*obj)); \n\ + if (!obj) { \n\ + errno = ENOMEM; \n\ + goto err; \n\ + } \n\ + s = (struct bpf_object_subskeleton *)calloc(1, sizeof(*s));\n\ + if (!s) { \n\ + errno = ENOMEM; \n\ + goto err; \n\ + } \n\ + s->sz = sizeof(*s); \n\ + s->obj = src; \n\ + s->var_skel_sz = sizeof(*s->vars); \n\ + obj->subskel = s; \n\ + \n\ + /* vars */ \n\ + s->var_cnt = %2$d; \n\ + s->vars = (struct bpf_var_skeleton *)calloc(%2$d, sizeof(*s->vars));\n\ + if (!s->vars) { \n\ + errno = ENOMEM; \n\ + goto err; \n\ + } \n\ + ", + obj_name, var_cnt + ); + + /* walk through each symbol and emit the runtime representation */ + bpf_object__for_each_map(map, obj) { + if (!is_internal_mmapable_map(map, ident, sizeof(ident))) + continue; + + map_type_id = bpf_map__btf_value_type_id(map); + if (map_type_id <= 0) + /* skip over internal maps with no type*/ + continue; + + map_type = btf__type_by_id(btf, map_type_id); + var = btf_var_secinfos(map_type); + len = btf_vlen(map_type); + for (i = 0; i < len; i++, var++) { + var_type = btf__type_by_id(btf, var->type); + var_name = btf__name_by_offset(btf, var_type->name_off); + + if (btf_var(var_type)->linkage == BTF_VAR_STATIC) + continue; + + /* Note that we use the dot prefix in .data as the + * field access operator i.e. maps%s becomes maps.data + */ + codegen("\ + \n\ + \n\ + s->vars[%3$d].name = \"%1$s\"; \n\ + s->vars[%3$d].map = &obj->maps.%2$s; \n\ + s->vars[%3$d].addr = (void **) &obj->%2$s.%1$s;\n\ + ", var_name, ident, var_idx); + + var_idx++; + } + } + + codegen_maps_skeleton(obj, map_cnt, false /*mmaped*/); + codegen_progs_skeleton(obj, prog_cnt, false /*links*/); + + codegen("\ + \n\ + \n\ + err = bpf_object__open_subskeleton(s); \n\ + if (err) \n\ + goto err; \n\ + \n\ + return obj; \n\ + err: \n\ + %1$s__destroy(obj); \n\ + return NULL; \n\ + } \n\ + \n\ + #ifdef __cplusplus \n\ + struct %1$s *%1$s::open(const struct bpf_object *src) { return %1$s__open(src); }\n\ + void %1$s::destroy(struct %1$s *skel) { %1$s__destroy(skel); }\n\ + #endif /* __cplusplus */ \n\ + \n\ + #endif /* %2$s */ \n\ + ", + obj_name, header_guard); + err = 0; +out: + bpf_object__close(obj); + if (obj_data) + munmap(obj_data, mmap_sz); + close(fd); + return err; +} + static int do_object(int argc, char **argv) { struct bpf_linker *linker; @@ -1084,6 +1609,8 @@ static int do_help(int argc, char **argv) fprintf(stderr, "Usage: %1$s %2$s object OUTPUT_FILE INPUT_FILE [INPUT_FILE...]\n" " %1$s %2$s skeleton FILE [name OBJECT_NAME]\n" + " %1$s %2$s subskeleton FILE [name OBJECT_NAME]\n" + " %1$s %2$s min_core_btf INPUT OUTPUT OBJECT [OBJECT...]\n" " %1$s %2$s help\n" "\n" " " HELP_SPEC_OPTIONS " |\n" @@ -1094,10 +1621,594 @@ static int do_help(int argc, char **argv) return 0; } +static int btf_save_raw(const struct btf *btf, const char *path) +{ + const void *data; + FILE *f = NULL; + __u32 data_sz; + int err = 0; + + data = btf__raw_data(btf, &data_sz); + if (!data) + return -ENOMEM; + + f = fopen(path, "wb"); + if (!f) + return -errno; + + if (fwrite(data, 1, data_sz, f) != data_sz) + err = -errno; + + fclose(f); + return err; +} + +struct btfgen_info { + struct btf *src_btf; + struct btf *marked_btf; /* btf structure used to mark used types */ +}; + +static size_t btfgen_hash_fn(const void *key, void *ctx) +{ + return (size_t)key; +} + +static bool btfgen_equal_fn(const void *k1, const void *k2, void *ctx) +{ + return k1 == k2; +} + +static void *u32_as_hash_key(__u32 x) +{ + return (void *)(uintptr_t)x; +} + +static void btfgen_free_info(struct btfgen_info *info) +{ + if (!info) + return; + + btf__free(info->src_btf); + btf__free(info->marked_btf); + + free(info); +} + +static struct btfgen_info * +btfgen_new_info(const char *targ_btf_path) +{ + struct btfgen_info *info; + int err; + + info = calloc(1, sizeof(*info)); + if (!info) + return NULL; + + info->src_btf = btf__parse(targ_btf_path, NULL); + if (!info->src_btf) { + err = -errno; + p_err("failed parsing '%s' BTF file: %s", targ_btf_path, strerror(errno)); + goto err_out; + } + + info->marked_btf = btf__parse(targ_btf_path, NULL); + if (!info->marked_btf) { + err = -errno; + p_err("failed parsing '%s' BTF file: %s", targ_btf_path, strerror(errno)); + goto err_out; + } + + return info; + +err_out: + btfgen_free_info(info); + errno = -err; + return NULL; +} + +#define MARKED UINT32_MAX + +static void btfgen_mark_member(struct btfgen_info *info, int type_id, int idx) +{ + const struct btf_type *t = btf__type_by_id(info->marked_btf, type_id); + struct btf_member *m = btf_members(t) + idx; + + m->name_off = MARKED; +} + +static int +btfgen_mark_type(struct btfgen_info *info, unsigned int type_id, bool follow_pointers) +{ + const struct btf_type *btf_type = btf__type_by_id(info->src_btf, type_id); + struct btf_type *cloned_type; + struct btf_param *param; + struct btf_array *array; + int err, i; + + if (type_id == 0) + return 0; + + /* mark type on cloned BTF as used */ + cloned_type = (struct btf_type *) btf__type_by_id(info->marked_btf, type_id); + cloned_type->name_off = MARKED; + + /* recursively mark other types needed by it */ + switch (btf_kind(btf_type)) { + case BTF_KIND_UNKN: + case BTF_KIND_INT: + case BTF_KIND_FLOAT: + case BTF_KIND_ENUM: + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + break; + case BTF_KIND_PTR: + if (follow_pointers) { + err = btfgen_mark_type(info, btf_type->type, follow_pointers); + if (err) + return err; + } + break; + case BTF_KIND_CONST: + case BTF_KIND_VOLATILE: + case BTF_KIND_TYPEDEF: + err = btfgen_mark_type(info, btf_type->type, follow_pointers); + if (err) + return err; + break; + case BTF_KIND_ARRAY: + array = btf_array(btf_type); + + /* mark array type */ + err = btfgen_mark_type(info, array->type, follow_pointers); + /* mark array's index type */ + err = err ? : btfgen_mark_type(info, array->index_type, follow_pointers); + if (err) + return err; + break; + case BTF_KIND_FUNC_PROTO: + /* mark ret type */ + err = btfgen_mark_type(info, btf_type->type, follow_pointers); + if (err) + return err; + + /* mark parameters types */ + param = btf_params(btf_type); + for (i = 0; i < btf_vlen(btf_type); i++) { + err = btfgen_mark_type(info, param->type, follow_pointers); + if (err) + return err; + param++; + } + break; + /* tells if some other type needs to be handled */ + default: + p_err("unsupported kind: %s (%d)", btf_kind_str(btf_type), type_id); + return -EINVAL; + } + + return 0; +} + +static int btfgen_record_field_relo(struct btfgen_info *info, struct bpf_core_spec *targ_spec) +{ + struct btf *btf = info->src_btf; + const struct btf_type *btf_type; + struct btf_member *btf_member; + struct btf_array *array; + unsigned int type_id = targ_spec->root_type_id; + int idx, err; + + /* mark root type */ + btf_type = btf__type_by_id(btf, type_id); + err = btfgen_mark_type(info, type_id, false); + if (err) + return err; + + /* mark types for complex types (arrays, unions, structures) */ + for (int i = 1; i < targ_spec->raw_len; i++) { + /* skip typedefs and mods */ + while (btf_is_mod(btf_type) || btf_is_typedef(btf_type)) { + type_id = btf_type->type; + btf_type = btf__type_by_id(btf, type_id); + } + + switch (btf_kind(btf_type)) { + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + idx = targ_spec->raw_spec[i]; + btf_member = btf_members(btf_type) + idx; + + /* mark member */ + btfgen_mark_member(info, type_id, idx); + + /* mark member's type */ + type_id = btf_member->type; + btf_type = btf__type_by_id(btf, type_id); + err = btfgen_mark_type(info, type_id, false); + if (err) + return err; + break; + case BTF_KIND_ARRAY: + array = btf_array(btf_type); + type_id = array->type; + btf_type = btf__type_by_id(btf, type_id); + break; + default: + p_err("unsupported kind: %s (%d)", + btf_kind_str(btf_type), btf_type->type); + return -EINVAL; + } + } + + return 0; +} + +static int btfgen_record_type_relo(struct btfgen_info *info, struct bpf_core_spec *targ_spec) +{ + return btfgen_mark_type(info, targ_spec->root_type_id, true); +} + +static int btfgen_record_enumval_relo(struct btfgen_info *info, struct bpf_core_spec *targ_spec) +{ + return btfgen_mark_type(info, targ_spec->root_type_id, false); +} + +static int btfgen_record_reloc(struct btfgen_info *info, struct bpf_core_spec *res) +{ + switch (res->relo_kind) { + case BPF_CORE_FIELD_BYTE_OFFSET: + case BPF_CORE_FIELD_BYTE_SIZE: + case BPF_CORE_FIELD_EXISTS: + case BPF_CORE_FIELD_SIGNED: + case BPF_CORE_FIELD_LSHIFT_U64: + case BPF_CORE_FIELD_RSHIFT_U64: + return btfgen_record_field_relo(info, res); + case BPF_CORE_TYPE_ID_LOCAL: /* BPF_CORE_TYPE_ID_LOCAL doesn't require kernel BTF */ + return 0; + case BPF_CORE_TYPE_ID_TARGET: + case BPF_CORE_TYPE_EXISTS: + case BPF_CORE_TYPE_SIZE: + return btfgen_record_type_relo(info, res); + case BPF_CORE_ENUMVAL_EXISTS: + case BPF_CORE_ENUMVAL_VALUE: + return btfgen_record_enumval_relo(info, res); + default: + return -EINVAL; + } +} + +static struct bpf_core_cand_list * +btfgen_find_cands(const struct btf *local_btf, const struct btf *targ_btf, __u32 local_id) +{ + const struct btf_type *local_type; + struct bpf_core_cand_list *cands = NULL; + struct bpf_core_cand local_cand = {}; + size_t local_essent_len; + const char *local_name; + int err; + + local_cand.btf = local_btf; + local_cand.id = local_id; + + local_type = btf__type_by_id(local_btf, local_id); + if (!local_type) { + err = -EINVAL; + goto err_out; + } + + local_name = btf__name_by_offset(local_btf, local_type->name_off); + if (!local_name) { + err = -EINVAL; + goto err_out; + } + local_essent_len = bpf_core_essential_name_len(local_name); + + cands = calloc(1, sizeof(*cands)); + if (!cands) + return NULL; + + err = bpf_core_add_cands(&local_cand, local_essent_len, targ_btf, "vmlinux", 1, cands); + if (err) + goto err_out; + + return cands; + +err_out: + bpf_core_free_cands(cands); + errno = -err; + return NULL; +} + +/* Record relocation information for a single BPF object */ +static int btfgen_record_obj(struct btfgen_info *info, const char *obj_path) +{ + const struct btf_ext_info_sec *sec; + const struct bpf_core_relo *relo; + const struct btf_ext_info *seg; + struct hashmap_entry *entry; + struct hashmap *cand_cache = NULL; + struct btf_ext *btf_ext = NULL; + unsigned int relo_idx; + struct btf *btf = NULL; + size_t i; + int err; + + btf = btf__parse(obj_path, &btf_ext); + if (!btf) { + err = -errno; + p_err("failed to parse BPF object '%s': %s", obj_path, strerror(errno)); + return err; + } + + if (!btf_ext) { + p_err("failed to parse BPF object '%s': section %s not found", + obj_path, BTF_EXT_ELF_SEC); + err = -EINVAL; + goto out; + } + + if (btf_ext->core_relo_info.len == 0) { + err = 0; + goto out; + } + + cand_cache = hashmap__new(btfgen_hash_fn, btfgen_equal_fn, NULL); + if (IS_ERR(cand_cache)) { + err = PTR_ERR(cand_cache); + goto out; + } + + seg = &btf_ext->core_relo_info; + for_each_btf_ext_sec(seg, sec) { + for_each_btf_ext_rec(seg, sec, relo_idx, relo) { + struct bpf_core_spec specs_scratch[3] = {}; + struct bpf_core_relo_res targ_res = {}; + struct bpf_core_cand_list *cands = NULL; + const void *type_key = u32_as_hash_key(relo->type_id); + const char *sec_name = btf__name_by_offset(btf, sec->sec_name_off); + + if (relo->kind != BPF_CORE_TYPE_ID_LOCAL && + !hashmap__find(cand_cache, type_key, (void **)&cands)) { + cands = btfgen_find_cands(btf, info->src_btf, relo->type_id); + if (!cands) { + err = -errno; + goto out; + } + + err = hashmap__set(cand_cache, type_key, cands, NULL, NULL); + if (err) + goto out; + } + + err = bpf_core_calc_relo_insn(sec_name, relo, relo_idx, btf, cands, + specs_scratch, &targ_res); + if (err) + goto out; + + /* specs_scratch[2] is the target spec */ + err = btfgen_record_reloc(info, &specs_scratch[2]); + if (err) + goto out; + } + } + +out: + btf__free(btf); + btf_ext__free(btf_ext); + + if (!IS_ERR_OR_NULL(cand_cache)) { + hashmap__for_each_entry(cand_cache, entry, i) { + bpf_core_free_cands(entry->value); + } + hashmap__free(cand_cache); + } + + return err; +} + +static int btfgen_remap_id(__u32 *type_id, void *ctx) +{ + unsigned int *ids = ctx; + + *type_id = ids[*type_id]; + + return 0; +} + +/* Generate BTF from relocation information previously recorded */ +static struct btf *btfgen_get_btf(struct btfgen_info *info) +{ + struct btf *btf_new = NULL; + unsigned int *ids = NULL; + unsigned int i, n = btf__type_cnt(info->marked_btf); + int err = 0; + + btf_new = btf__new_empty(); + if (!btf_new) { + err = -errno; + goto err_out; + } + + ids = calloc(n, sizeof(*ids)); + if (!ids) { + err = -errno; + goto err_out; + } + + /* first pass: add all marked types to btf_new and add their new ids to the ids map */ + for (i = 1; i < n; i++) { + const struct btf_type *cloned_type, *type; + const char *name; + int new_id; + + cloned_type = btf__type_by_id(info->marked_btf, i); + + if (cloned_type->name_off != MARKED) + continue; + + type = btf__type_by_id(info->src_btf, i); + + /* add members for struct and union */ + if (btf_is_composite(type)) { + struct btf_member *cloned_m, *m; + unsigned short vlen; + int idx_src; + + name = btf__str_by_offset(info->src_btf, type->name_off); + + if (btf_is_struct(type)) + err = btf__add_struct(btf_new, name, type->size); + else + err = btf__add_union(btf_new, name, type->size); + + if (err < 0) + goto err_out; + new_id = err; + + cloned_m = btf_members(cloned_type); + m = btf_members(type); + vlen = btf_vlen(cloned_type); + for (idx_src = 0; idx_src < vlen; idx_src++, cloned_m++, m++) { + /* add only members that are marked as used */ + if (cloned_m->name_off != MARKED) + continue; + + name = btf__str_by_offset(info->src_btf, m->name_off); + err = btf__add_field(btf_new, name, m->type, + btf_member_bit_offset(cloned_type, idx_src), + btf_member_bitfield_size(cloned_type, idx_src)); + if (err < 0) + goto err_out; + } + } else { + err = btf__add_type(btf_new, info->src_btf, type); + if (err < 0) + goto err_out; + new_id = err; + } + + /* add ID mapping */ + ids[i] = new_id; + } + + /* second pass: fix up type ids */ + for (i = 1; i < btf__type_cnt(btf_new); i++) { + struct btf_type *btf_type = (struct btf_type *) btf__type_by_id(btf_new, i); + + err = btf_type_visit_type_ids(btf_type, btfgen_remap_id, ids); + if (err) + goto err_out; + } + + free(ids); + return btf_new; + +err_out: + btf__free(btf_new); + free(ids); + errno = -err; + return NULL; +} + +/* Create minimized BTF file for a set of BPF objects. + * + * The BTFGen algorithm is divided in two main parts: (1) collect the + * BTF types that are involved in relocations and (2) generate the BTF + * object using the collected types. + * + * In order to collect the types involved in the relocations, we parse + * the BTF and BTF.ext sections of the BPF objects and use + * bpf_core_calc_relo_insn() to get the target specification, this + * indicates how the types and fields are used in a relocation. + * + * Types are recorded in different ways according to the kind of the + * relocation. For field-based relocations only the members that are + * actually used are saved in order to reduce the size of the generated + * BTF file. For type-based relocations empty struct / unions are + * generated and for enum-based relocations the whole type is saved. + * + * The second part of the algorithm generates the BTF object. It creates + * an empty BTF object and fills it with the types recorded in the + * previous step. This function takes care of only adding the structure + * and union members that were marked as used and it also fixes up the + * type IDs on the generated BTF object. + */ +static int minimize_btf(const char *src_btf, const char *dst_btf, const char *objspaths[]) +{ + struct btfgen_info *info; + struct btf *btf_new = NULL; + int err, i; + + info = btfgen_new_info(src_btf); + if (!info) { + err = -errno; + p_err("failed to allocate info structure: %s", strerror(errno)); + goto out; + } + + for (i = 0; objspaths[i] != NULL; i++) { + err = btfgen_record_obj(info, objspaths[i]); + if (err) { + p_err("error recording relocations for %s: %s", objspaths[i], + strerror(errno)); + goto out; + } + } + + btf_new = btfgen_get_btf(info); + if (!btf_new) { + err = -errno; + p_err("error generating BTF: %s", strerror(errno)); + goto out; + } + + err = btf_save_raw(btf_new, dst_btf); + if (err) { + p_err("error saving btf file: %s", strerror(errno)); + goto out; + } + +out: + btf__free(btf_new); + btfgen_free_info(info); + + return err; +} + +static int do_min_core_btf(int argc, char **argv) +{ + const char *input, *output, **objs; + int i, err; + + if (!REQ_ARGS(3)) { + usage(); + return -1; + } + + input = GET_ARG(); + output = GET_ARG(); + + objs = (const char **) calloc(argc + 1, sizeof(*objs)); + if (!objs) { + p_err("failed to allocate array for object names"); + return -ENOMEM; + } + + i = 0; + while (argc) + objs[i++] = GET_ARG(); + + err = minimize_btf(input, output, objs); + free(objs); + return err; +} + static const struct cmd cmds[] = { - { "object", do_object }, - { "skeleton", do_skeleton }, - { "help", do_help }, + { "object", do_object }, + { "skeleton", do_skeleton }, + { "subskeleton", do_subskeleton }, + { "min_core_btf", do_min_core_btf}, + { "help", do_help }, { 0 } }; diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c index 2c258db0d352..97dec81950e5 100644 --- a/tools/bpf/bpftool/link.c +++ b/tools/bpf/bpftool/link.c @@ -2,6 +2,7 @@ /* Copyright (C) 2020 Facebook */ #include +#include #include #include #include @@ -306,7 +307,7 @@ static int do_show(int argc, char **argv) if (show_pinned) { link_table = hashmap__new(hash_fn_for_key_as_id, equal_fn_for_key_as_id, NULL); - if (!link_table) { + if (IS_ERR(link_table)) { p_err("failed to create hashmap for pinned paths"); return -1; } diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 020e91a542d5..e81227761f5d 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -71,6 +71,17 @@ static int do_help(int argc, char **argv) return 0; } +#ifndef BPFTOOL_VERSION +/* bpftool's major and minor version numbers are aligned on libbpf's. There is + * an offset of 6 for the version number, because bpftool's version was higher + * than libbpf's when we adopted this scheme. The patch number remains at 0 + * for now. Set BPFTOOL_VERSION to override. + */ +#define BPFTOOL_MAJOR_VERSION (LIBBPF_MAJOR_VERSION + 6) +#define BPFTOOL_MINOR_VERSION LIBBPF_MINOR_VERSION +#define BPFTOOL_PATCH_VERSION 0 +#endif + static int do_version(int argc, char **argv) { #ifdef HAVE_LIBBFD_SUPPORT @@ -88,7 +99,15 @@ static int do_version(int argc, char **argv) jsonw_start_object(json_wtr); /* root object */ jsonw_name(json_wtr, "version"); +#ifdef BPFTOOL_VERSION jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION); +#else + jsonw_printf(json_wtr, "\"%d.%d.%d\"", BPFTOOL_MAJOR_VERSION, + BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION); +#endif + jsonw_name(json_wtr, "libbpf_version"); + jsonw_printf(json_wtr, "\"%d.%d\"", + libbpf_major_version(), libbpf_minor_version()); jsonw_name(json_wtr, "features"); jsonw_start_object(json_wtr); /* features */ @@ -101,7 +120,13 @@ static int do_version(int argc, char **argv) } else { unsigned int nb_features = 0; +#ifdef BPFTOOL_VERSION printf("%s v%s\n", bin_name, BPFTOOL_VERSION); +#else + printf("%s v%d.%d.%d\n", bin_name, BPFTOOL_MAJOR_VERSION, + BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION); +#endif + printf("using libbpf %s\n", libbpf_version_string()); printf("features:"); if (has_libbfd) { printf(" libbfd"); @@ -478,7 +503,11 @@ int main(int argc, char **argv) } if (!legacy_libbpf) { - ret = libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + /* Allow legacy map definitions for skeleton generation. + * It will still be rejected if users use LIBBPF_STRICT_ALL + * mode for loading generated skeleton. + */ + ret = libbpf_set_strict_mode(LIBBPF_STRICT_ALL & ~LIBBPF_STRICT_MAP_DEFINITIONS); if (ret) p_err("failed to enable libbpf strict mode: %d", ret); } diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 8d76d937a62b..6e9277ffc68c 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -8,10 +8,10 @@ #undef GCC_VERSION #include #include +#include #include #include #include -#include #include #include @@ -113,7 +113,9 @@ struct obj_ref { struct obj_refs { int ref_cnt; + bool has_bpf_cookie; struct obj_ref *refs; + __u64 bpf_cookie; }; struct btf; @@ -140,6 +142,10 @@ struct cmd { int cmd_select(const struct cmd *cmds, int argc, char **argv, int (*help)(int argc, char **argv)); +#define MAX_PROG_FULL_NAME 128 +void get_prog_full_name(const struct bpf_prog_info *prog_info, int prog_fd, + char *name_buff, size_t buff_len); + int get_fd_type(int fd); const char *get_fd_type_name(enum bpf_obj_type type); char *get_fdinfo(int fd, const char *key); diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index cc530a229812..c26378f20831 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -504,7 +504,7 @@ static int show_map_close_json(int fd, struct bpf_map_info *info) jsonw_uint_field(json_wtr, "max_entries", info->max_entries); if (memlock) - jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); + jsonw_int_field(json_wtr, "bytes_memlock", atoll(memlock)); free(memlock); if (info->type == BPF_MAP_TYPE_PROG_ARRAY) { @@ -620,17 +620,14 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info) u32_as_hash_field(info->id)) printf("\n\tpinned %s", (char *)entry->value); } - printf("\n"); if (frozen_str) { frozen = atoi(frozen_str); free(frozen_str); } - if (!info->btf_id && !frozen) - return 0; - - printf("\t"); + if (info->btf_id || frozen) + printf("\n\t"); if (info->btf_id) printf("btf_id %d", info->btf_id); @@ -699,7 +696,7 @@ static int do_show(int argc, char **argv) if (show_pinned) { map_table = hashmap__new(hash_fn_for_key_as_id, equal_fn_for_key_as_id, NULL); - if (!map_table) { + if (IS_ERR(map_table)) { p_err("failed to create hashmap for pinned paths"); return -1; } @@ -805,29 +802,30 @@ static int maps_have_btf(int *fds, int nb_fds) static struct btf *btf_vmlinux; -static struct btf *get_map_kv_btf(const struct bpf_map_info *info) +static int get_map_kv_btf(const struct bpf_map_info *info, struct btf **btf) { - struct btf *btf = NULL; + int err = 0; if (info->btf_vmlinux_value_type_id) { if (!btf_vmlinux) { btf_vmlinux = libbpf_find_kernel_btf(); - if (libbpf_get_error(btf_vmlinux)) + err = libbpf_get_error(btf_vmlinux); + if (err) { p_err("failed to get kernel btf"); + return err; + } } - return btf_vmlinux; + *btf = btf_vmlinux; } else if (info->btf_value_type_id) { - int err; - - btf = btf__load_from_kernel_by_id(info->btf_id); - err = libbpf_get_error(btf); - if (err) { + *btf = btf__load_from_kernel_by_id(info->btf_id); + err = libbpf_get_error(*btf); + if (err) p_err("failed to get btf"); - btf = ERR_PTR(err); - } + } else { + *btf = NULL; } - return btf; + return err; } static void free_map_kv_btf(struct btf *btf) @@ -862,8 +860,7 @@ map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr, prev_key = NULL; if (wtr) { - btf = get_map_kv_btf(info); - err = libbpf_get_error(btf); + err = get_map_kv_btf(info, &btf); if (err) { goto exit_free; } @@ -1054,11 +1051,8 @@ static void print_key_value(struct bpf_map_info *info, void *key, json_writer_t *btf_wtr; struct btf *btf; - btf = btf__load_from_kernel_by_id(info->btf_id); - if (libbpf_get_error(btf)) { - p_err("failed to get btf"); + if (get_map_kv_btf(info, &btf)) return; - } if (json_output) { print_entry_json(info, key, value, btf); diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c index 649053704bd7..526a332c48e6 100644 --- a/tools/bpf/bpftool/net.c +++ b/tools/bpf/bpftool/net.c @@ -551,7 +551,7 @@ static int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type, if (attach_type == NET_ATTACH_TYPE_XDP_OFFLOAD) flags |= XDP_FLAGS_HW_MODE; - return bpf_set_link_xdp_fd(ifindex, progfd, flags); + return bpf_xdp_attach(ifindex, progfd, flags, NULL); } static int do_attach(int argc, char **argv) diff --git a/tools/bpf/bpftool/pids.c b/tools/bpf/bpftool/pids.c index 56b598eee043..bb6c969a114a 100644 --- a/tools/bpf/bpftool/pids.c +++ b/tools/bpf/bpftool/pids.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (C) 2020 Facebook */ #include +#include #include #include #include @@ -77,6 +78,8 @@ static void add_ref(struct hashmap *map, struct pid_iter_entry *e) ref->pid = e->pid; memcpy(ref->comm, e->comm, sizeof(ref->comm)); refs->ref_cnt = 1; + refs->has_bpf_cookie = e->has_bpf_cookie; + refs->bpf_cookie = e->bpf_cookie; err = hashmap__append(map, u32_as_hash_field(e->id), refs); if (err) @@ -101,7 +104,7 @@ int build_obj_refs_table(struct hashmap **map, enum bpf_obj_type type) libbpf_print_fn_t default_print; *map = hashmap__new(hash_fn_for_key_as_id, equal_fn_for_key_as_id, NULL); - if (!*map) { + if (IS_ERR(*map)) { p_err("failed to create hashmap for PID references"); return -1; } @@ -204,6 +207,9 @@ void emit_obj_refs_json(struct hashmap *map, __u32 id, if (refs->ref_cnt == 0) break; + if (refs->has_bpf_cookie) + jsonw_lluint_field(json_writer, "bpf_cookie", refs->bpf_cookie); + jsonw_name(json_writer, "pids"); jsonw_start_array(json_writer); for (i = 0; i < refs->ref_cnt; i++) { @@ -233,6 +239,9 @@ void emit_obj_refs_plain(struct hashmap *map, __u32 id, const char *prefix) if (refs->ref_cnt == 0) break; + if (refs->has_bpf_cookie) + printf("\n\tbpf_cookie %llu", (unsigned long long) refs->bpf_cookie); + printf("%s", prefix); for (i = 0; i < refs->ref_cnt; i++) { struct obj_ref *ref = &refs->refs[i]; diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 2a21d50516bc..bc4e05542c2b 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "cfg.h" @@ -424,8 +425,10 @@ static void show_prog_metadata(int fd, __u32 num_maps) free(value); } -static void print_prog_header_json(struct bpf_prog_info *info) +static void print_prog_header_json(struct bpf_prog_info *info, int fd) { + char prog_name[MAX_PROG_FULL_NAME]; + jsonw_uint_field(json_wtr, "id", info->id); if (info->type < ARRAY_SIZE(prog_type_name)) jsonw_string_field(json_wtr, "type", @@ -433,8 +436,10 @@ static void print_prog_header_json(struct bpf_prog_info *info) else jsonw_uint_field(json_wtr, "type", info->type); - if (*info->name) - jsonw_string_field(json_wtr, "name", info->name); + if (*info->name) { + get_prog_full_name(info, fd, prog_name, sizeof(prog_name)); + jsonw_string_field(json_wtr, "name", prog_name); + } jsonw_name(json_wtr, "tag"); jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"", @@ -455,7 +460,7 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) char *memlock; jsonw_start_object(json_wtr); - print_prog_header_json(info); + print_prog_header_json(info, fd); print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); if (info->load_time) { @@ -480,7 +485,7 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) memlock = get_fdinfo(fd, "memlock"); if (memlock) - jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); + jsonw_int_field(json_wtr, "bytes_memlock", atoll(memlock)); free(memlock); if (info->nr_map_ids) @@ -507,16 +512,20 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) jsonw_end_object(json_wtr); } -static void print_prog_header_plain(struct bpf_prog_info *info) +static void print_prog_header_plain(struct bpf_prog_info *info, int fd) { + char prog_name[MAX_PROG_FULL_NAME]; + printf("%u: ", info->id); if (info->type < ARRAY_SIZE(prog_type_name)) printf("%s ", prog_type_name[info->type]); else printf("type %u ", info->type); - if (*info->name) - printf("name %s ", info->name); + if (*info->name) { + get_prog_full_name(info, fd, prog_name, sizeof(prog_name)); + printf("name %s ", prog_name); + } printf("tag "); fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); @@ -534,7 +543,7 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd) { char *memlock; - print_prog_header_plain(info); + print_prog_header_plain(info, fd); if (info->load_time) { char buf[32]; @@ -641,7 +650,7 @@ static int do_show(int argc, char **argv) if (show_pinned) { prog_table = hashmap__new(hash_fn_for_key_as_id, equal_fn_for_key_as_id, NULL); - if (!prog_table) { + if (IS_ERR(prog_table)) { p_err("failed to create hashmap for pinned paths"); return -1; } @@ -972,10 +981,10 @@ static int do_dump(int argc, char **argv) if (json_output && nb_fds > 1) { jsonw_start_object(json_wtr); /* prog object */ - print_prog_header_json(&info); + print_prog_header_json(&info, fds[i]); jsonw_name(json_wtr, "insns"); } else if (nb_fds > 1) { - print_prog_header_plain(&info); + print_prog_header_plain(&info, fds[i]); } err = prog_dump(&info, mode, filepath, opcodes, visual, linum); @@ -1264,12 +1273,12 @@ static int do_run(int argc, char **argv) { char *data_fname_in = NULL, *data_fname_out = NULL; char *ctx_fname_in = NULL, *ctx_fname_out = NULL; - struct bpf_prog_test_run_attr test_attr = {0}; const unsigned int default_size = SZ_32K; void *data_in = NULL, *data_out = NULL; void *ctx_in = NULL, *ctx_out = NULL; unsigned int repeat = 1; int fd, err; + LIBBPF_OPTS(bpf_test_run_opts, test_attr); if (!REQ_ARGS(4)) return -1; @@ -1387,14 +1396,13 @@ static int do_run(int argc, char **argv) goto free_ctx_in; } - test_attr.prog_fd = fd; test_attr.repeat = repeat; test_attr.data_in = data_in; test_attr.data_out = data_out; test_attr.ctx_in = ctx_in; test_attr.ctx_out = ctx_out; - err = bpf_prog_test_run_xattr(&test_attr); + err = bpf_prog_test_run_opts(fd, &test_attr); if (err) { p_err("failed to run program: %s", strerror(errno)); goto free_ctx_out; @@ -1551,9 +1559,9 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) if (fd < 0) goto err_free_reuse_maps; - new_map_replace = reallocarray(map_replace, - old_map_fds + 1, - sizeof(*map_replace)); + new_map_replace = libbpf_reallocarray(map_replace, + old_map_fds + 1, + sizeof(*map_replace)); if (!new_map_replace) { p_err("mem alloc failed"); goto err_free_reuse_maps; @@ -2275,10 +2283,10 @@ static int do_profile(int argc, char **argv) profile_obj->rodata->num_metric = num_metric; /* adjust map sizes */ - bpf_map__resize(profile_obj->maps.events, num_metric * num_cpu); - bpf_map__resize(profile_obj->maps.fentry_readings, num_metric); - bpf_map__resize(profile_obj->maps.accum_readings, num_metric); - bpf_map__resize(profile_obj->maps.counts, 1); + bpf_map__set_max_entries(profile_obj->maps.events, num_metric * num_cpu); + bpf_map__set_max_entries(profile_obj->maps.fentry_readings, num_metric); + bpf_map__set_max_entries(profile_obj->maps.accum_readings, num_metric); + bpf_map__set_max_entries(profile_obj->maps.counts, 1); /* change target name */ profile_tgt_name = profile_target_name(profile_tgt_fd); diff --git a/tools/bpf/bpftool/skeleton/pid_iter.bpf.c b/tools/bpf/bpftool/skeleton/pid_iter.bpf.c index f70702fcb224..eb05ea53afb1 100644 --- a/tools/bpf/bpftool/skeleton/pid_iter.bpf.c +++ b/tools/bpf/bpftool/skeleton/pid_iter.bpf.c @@ -38,6 +38,17 @@ static __always_inline __u32 get_obj_id(void *ent, enum bpf_obj_type type) } } +/* could be used only with BPF_LINK_TYPE_PERF_EVENT links */ +static __u64 get_bpf_cookie(struct bpf_link *link) +{ + struct bpf_perf_link *perf_link; + struct perf_event *event; + + perf_link = container_of(link, struct bpf_perf_link, link); + event = BPF_CORE_READ(perf_link, perf_file, private_data); + return BPF_CORE_READ(event, bpf_cookie); +} + SEC("iter/task_file") int iter(struct bpf_iter__task_file *ctx) { @@ -69,8 +80,19 @@ int iter(struct bpf_iter__task_file *ctx) if (file->f_op != fops) return 0; + __builtin_memset(&e, 0, sizeof(e)); e.pid = task->tgid; e.id = get_obj_id(file->private_data, obj_type); + + if (obj_type == BPF_OBJ_LINK) { + struct bpf_link *link = (struct bpf_link *) file->private_data; + + if (BPF_CORE_READ(link, type) == BPF_LINK_TYPE_PERF_EVENT) { + e.has_bpf_cookie = true; + e.bpf_cookie = get_bpf_cookie(link); + } + } + bpf_probe_read_kernel_str(&e.comm, sizeof(e.comm), task->group_leader->comm); bpf_seq_write(ctx->meta->seq, &e, sizeof(e)); diff --git a/tools/bpf/bpftool/skeleton/pid_iter.h b/tools/bpf/bpftool/skeleton/pid_iter.h index 5692cf257adb..bbb570d4cca6 100644 --- a/tools/bpf/bpftool/skeleton/pid_iter.h +++ b/tools/bpf/bpftool/skeleton/pid_iter.h @@ -6,6 +6,8 @@ struct pid_iter_entry { __u32 id; int pid; + __u64 bpf_cookie; + bool has_bpf_cookie; char comm[16]; }; diff --git a/tools/bpf/bpftool/struct_ops.c b/tools/bpf/bpftool/struct_ops.c index 2f693b082bdb..e08a6ff2866c 100644 --- a/tools/bpf/bpftool/struct_ops.c +++ b/tools/bpf/bpftool/struct_ops.c @@ -480,7 +480,6 @@ static int do_unregister(int argc, char **argv) static int do_register(int argc, char **argv) { LIBBPF_OPTS(bpf_object_open_opts, open_opts); - const struct bpf_map_def *def; struct bpf_map_info info = {}; __u32 info_len = sizeof(info); int nr_errs = 0, nr_maps = 0; @@ -510,8 +509,7 @@ static int do_register(int argc, char **argv) } bpf_object__for_each_map(map, obj) { - def = bpf_map__def(map); - if (def->type != BPF_MAP_TYPE_STRUCT_OPS) + if (bpf_map__type(map) != BPF_MAP_TYPE_STRUCT_OPS) continue; link = bpf_map__attach_struct_ops(map); diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c index f1f32e21d5cd..2d9cd6a7b3c8 100644 --- a/tools/bpf/bpftool/xlated_dumper.c +++ b/tools/bpf/bpftool/xlated_dumper.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "disasm.h" #include "json_writer.h" @@ -32,8 +33,8 @@ void kernel_syms_load(struct dump_data *dd) return; while (fgets(buff, sizeof(buff), fp)) { - tmp = reallocarray(dd->sym_mapping, dd->sym_count + 1, - sizeof(*dd->sym_mapping)); + tmp = libbpf_reallocarray(dd->sym_mapping, dd->sym_count + 1, + sizeof(*dd->sym_mapping)); if (!tmp) { out: free(dd->sym_mapping); diff --git a/tools/bpf/resolve_btfids/Makefile b/tools/bpf/resolve_btfids/Makefile index 320a88ac28c9..19a3112e271a 100644 --- a/tools/bpf/resolve_btfids/Makefile +++ b/tools/bpf/resolve_btfids/Makefile @@ -24,6 +24,8 @@ LD = $(HOSTLD) ARCH = $(HOSTARCH) RM ?= rm CROSS_COMPILE = +CFLAGS := $(KBUILD_HOSTCFLAGS) +LDFLAGS := $(KBUILD_HOSTLDFLAGS) OUTPUT ?= $(srctree)/tools/bpf/resolve_btfids/ @@ -51,10 +53,10 @@ $(SUBCMDOBJ): fixdep FORCE | $(OUTPUT)/libsubcmd $(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(LIBBPF_OUT) $(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) OUTPUT=$(LIBBPF_OUT) \ - DESTDIR=$(LIBBPF_DESTDIR) prefix= \ + DESTDIR=$(LIBBPF_DESTDIR) prefix= EXTRA_CFLAGS="$(CFLAGS)" \ $(abspath $@) install_headers -CFLAGS := -g \ +CFLAGS += -g \ -I$(srctree)/tools/include \ -I$(srctree)/tools/include/uapi \ -I$(LIBBPF_INCLUDE) \ diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index b0383d371b9a..7604e7d5438f 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -330,6 +330,8 @@ union bpf_iter_link_info { * *ctx_out*, *data_in* and *data_out* must be NULL. * *repeat* must be zero. * + * BPF_PROG_RUN is an alias for BPF_PROG_TEST_RUN. + * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. @@ -995,6 +997,7 @@ enum bpf_attach_type { BPF_SK_REUSEPORT_SELECT, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, BPF_PERF_EVENT, + BPF_TRACE_KPROBE_MULTI, __MAX_BPF_ATTACH_TYPE }; @@ -1009,6 +1012,7 @@ enum bpf_link_type { BPF_LINK_TYPE_NETNS = 5, BPF_LINK_TYPE_XDP = 6, BPF_LINK_TYPE_PERF_EVENT = 7, + BPF_LINK_TYPE_KPROBE_MULTI = 8, MAX_BPF_LINK_TYPE, }; @@ -1111,6 +1115,16 @@ enum bpf_link_type { */ #define BPF_F_SLEEPABLE (1U << 4) +/* If BPF_F_XDP_HAS_FRAGS is used in BPF_PROG_LOAD command, the loaded program + * fully support xdp frags. + */ +#define BPF_F_XDP_HAS_FRAGS (1U << 5) + +/* link_create.kprobe_multi.flags used in LINK_CREATE command for + * BPF_TRACE_KPROBE_MULTI attach type to create return probe. + */ +#define BPF_F_KPROBE_MULTI_RETURN (1U << 0) + /* When BPF ldimm64's insn[0].src_reg != 0 then this can have * the following extensions: * @@ -1225,6 +1239,8 @@ enum { /* If set, run the test on the cpu specified by bpf_attr.test.cpu */ #define BPF_F_TEST_RUN_ON_CPU (1U << 0) +/* If set, XDP frames will be transmitted after processing */ +#define BPF_F_TEST_XDP_LIVE_FRAMES (1U << 1) /* type for BPF_ENABLE_STATS */ enum bpf_stats_type { @@ -1386,6 +1402,7 @@ union bpf_attr { __aligned_u64 ctx_out; __u32 flags; __u32 cpu; + __u32 batch_size; } test; struct { /* anonymous struct used by BPF_*_GET_*_ID */ @@ -1465,6 +1482,13 @@ union bpf_attr { */ __u64 bpf_cookie; } perf_event; + struct { + __u32 flags; + __u32 cnt; + __aligned_u64 syms; + __aligned_u64 addrs; + __aligned_u64 cookies; + } kprobe_multi; }; } link_create; @@ -1775,6 +1799,8 @@ union bpf_attr { * 0 on success, or a negative error in case of failure. * * u64 bpf_get_current_pid_tgid(void) + * Description + * Get the current pid and tgid. * Return * A 64-bit integer containing the current tgid and pid, and * created as such: @@ -1782,6 +1808,8 @@ union bpf_attr { * *current_task*\ **->pid**. * * u64 bpf_get_current_uid_gid(void) + * Description + * Get the current uid and gid. * Return * A 64-bit integer containing the current GID and UID, and * created as such: *current_gid* **<< 32 \|** *current_uid*. @@ -2256,6 +2284,8 @@ union bpf_attr { * The 32-bit hash. * * u64 bpf_get_current_task(void) + * Description + * Get the current task. * Return * A pointer to the current task struct. * @@ -2286,8 +2316,8 @@ union bpf_attr { * Return * The return value depends on the result of the test, and can be: * - * * 0, if current task belongs to the cgroup2. - * * 1, if current task does not belong to the cgroup2. + * * 1, if current task belongs to the cgroup2. + * * 0, if current task does not belong to the cgroup2. * * A negative error code, if an error occurred. * * long bpf_skb_change_tail(struct sk_buff *skb, u32 len, u64 flags) @@ -2369,6 +2399,8 @@ union bpf_attr { * indicate that the hash is outdated and to trigger a * recalculation the next time the kernel tries to access this * hash or when the **bpf_get_hash_recalc**\ () helper is called. + * Return + * void. * * long bpf_get_numa_node_id(void) * Description @@ -2466,6 +2498,8 @@ union bpf_attr { * A 8-byte long unique number or 0 if *sk* is NULL. * * u32 bpf_get_socket_uid(struct sk_buff *skb) + * Description + * Get the owner UID of the socked associated to *skb*. * Return * The owner UID of the socket associated to *skb*. If the socket * is **NULL**, or if it is not a full socket (i.e. if it is a @@ -3240,6 +3274,9 @@ union bpf_attr { * The id is returned or 0 in case the id could not be retrieved. * * u64 bpf_get_current_cgroup_id(void) + * Description + * Get the current cgroup id based on the cgroup within which + * the current task is running. * Return * A 64-bit integer containing the current cgroup id based * on the cgroup within which the current task is running. @@ -5018,6 +5055,94 @@ union bpf_attr { * * Return * The number of arguments of the traced function. + * + * int bpf_get_retval(void) + * Description + * Get the syscall's return value that will be returned to userspace. + * + * This helper is currently supported by cgroup programs only. + * Return + * The syscall's return value. + * + * int bpf_set_retval(int retval) + * Description + * Set the syscall's return value that will be returned to userspace. + * + * This helper is currently supported by cgroup programs only. + * Return + * 0 on success, or a negative error in case of failure. + * + * u64 bpf_xdp_get_buff_len(struct xdp_buff *xdp_md) + * Description + * Get the total size of a given xdp buff (linear and paged area) + * Return + * The total size of a given xdp buffer. + * + * long bpf_xdp_load_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len) + * Description + * This helper is provided as an easy way to load data from a + * xdp buffer. It can be used to load *len* bytes from *offset* from + * the frame associated to *xdp_md*, into the buffer pointed by + * *buf*. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_xdp_store_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len) + * Description + * Store *len* bytes from buffer *buf* into the frame + * associated to *xdp_md*, at *offset*. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_copy_from_user_task(void *dst, u32 size, const void *user_ptr, struct task_struct *tsk, u64 flags) + * Description + * Read *size* bytes from user space address *user_ptr* in *tsk*'s + * address space, and stores the data in *dst*. *flags* is not + * used yet and is provided for future extensibility. This helper + * can only be used by sleepable programs. + * Return + * 0 on success, or a negative error in case of failure. On error + * *dst* buffer is zeroed out. + * + * long bpf_skb_set_tstamp(struct sk_buff *skb, u64 tstamp, u32 tstamp_type) + * Description + * Change the __sk_buff->tstamp_type to *tstamp_type* + * and set *tstamp* to the __sk_buff->tstamp together. + * + * If there is no need to change the __sk_buff->tstamp_type, + * the tstamp value can be directly written to __sk_buff->tstamp + * instead. + * + * BPF_SKB_TSTAMP_DELIVERY_MONO is the only tstamp that + * will be kept during bpf_redirect_*(). A non zero + * *tstamp* must be used with the BPF_SKB_TSTAMP_DELIVERY_MONO + * *tstamp_type*. + * + * A BPF_SKB_TSTAMP_UNSPEC *tstamp_type* can only be used + * with a zero *tstamp*. + * + * Only IPv4 and IPv6 skb->protocol are supported. + * + * This function is most useful when it needs to set a + * mono delivery time to __sk_buff->tstamp and then + * bpf_redirect_*() to the egress of an iface. For example, + * changing the (rcv) timestamp in __sk_buff->tstamp at + * ingress to a mono delivery time and then bpf_redirect_*() + * to sch_fq@phy-dev. + * Return + * 0 on success. + * **-EINVAL** for invalid input + * **-EOPNOTSUPP** for unsupported protocol + * + * long bpf_ima_file_hash(struct file *file, void *dst, u32 size) + * Description + * Returns a calculated IMA hash of the *file*. + * If the hash is larger than *size*, then only *size* + * bytes will be copied to *dst* + * Return + * The **hash_algo** is returned on success, + * **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if + * invalid arguments are passed. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -5206,6 +5331,14 @@ union bpf_attr { FN(get_func_arg), \ FN(get_func_ret), \ FN(get_func_arg_cnt), \ + FN(get_retval), \ + FN(set_retval), \ + FN(xdp_get_buff_len), \ + FN(xdp_load_bytes), \ + FN(xdp_store_bytes), \ + FN(copy_from_user_task), \ + FN(skb_set_tstamp), \ + FN(ima_file_hash), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper @@ -5395,6 +5528,15 @@ union { \ __u64 :64; \ } __attribute__((aligned(8))) +enum { + BPF_SKB_TSTAMP_UNSPEC, + BPF_SKB_TSTAMP_DELIVERY_MONO, /* tstamp has mono delivery time */ + /* For any BPF_SKB_TSTAMP_* that the bpf prog cannot handle, + * the bpf prog should handle it like BPF_SKB_TSTAMP_UNSPEC + * and try to deduce it by ingress, egress or skb->sk->sk_clockid. + */ +}; + /* user accessible mirror of in-kernel sk_buff. * new fields can only be added to the end of this structure */ @@ -5435,7 +5577,8 @@ struct __sk_buff { __u32 gso_segs; __bpf_md_ptr(struct bpf_sock *, sk); __u32 gso_size; - __u32 :32; /* Padding, future use. */ + __u8 tstamp_type; + __u32 :24; /* Padding, future use. */ __u64 hwtstamp; }; @@ -5500,7 +5643,8 @@ struct bpf_sock { __u32 src_ip4; __u32 src_ip6[4]; __u32 src_port; /* host byte order */ - __u32 dst_port; /* network byte order */ + __be16 dst_port; /* network byte order */ + __u16 :16; /* zero padding */ __u32 dst_ip4; __u32 dst_ip6[4]; __u32 state; @@ -6378,7 +6522,8 @@ struct bpf_sk_lookup { __u32 protocol; /* IP protocol (IPPROTO_TCP, IPPROTO_UDP) */ __u32 remote_ip4; /* Network byte order */ __u32 remote_ip6[4]; /* Network byte order */ - __u32 remote_port; /* Network byte order */ + __be16 remote_port; /* Network byte order */ + __u16 :16; /* Zero padding */ __u32 local_ip4; /* Network byte order */ __u32 local_ip6[4]; /* Network byte order */ __u32 local_port; /* Host byte order */ diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h index 6218f93f5c1a..e1ba2d51b717 100644 --- a/tools/include/uapi/linux/if_link.h +++ b/tools/include/uapi/linux/if_link.h @@ -860,6 +860,7 @@ enum { IFLA_BOND_PEER_NOTIF_DELAY, IFLA_BOND_AD_LACP_ACTIVE, IFLA_BOND_MISSED_MAX, + IFLA_BOND_NS_IP6_TARGET, __IFLA_BOND_MAX, }; diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index f947b61b2107..b8b37fe76006 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile @@ -131,7 +131,7 @@ GLOBAL_SYM_COUNT = $(shell readelf -s --wide $(BPF_IN_SHARED) | \ sort -u | wc -l) VERSIONED_SYM_COUNT = $(shell readelf --dyn-syms --wide $(OUTPUT)libbpf.so | \ sed 's/\[.*\]//' | \ - awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}' | \ + awk '/GLOBAL/ && /DEFAULT/ && !/UND|ABS/ {print $$NF}' | \ grep -Eo '[^ ]+@LIBBPF_' | cut -d@ -f1 | sort -u | wc -l) CMD_TARGETS = $(LIB_TARGET) $(PC_FILE) @@ -194,7 +194,7 @@ check_abi: $(OUTPUT)libbpf.so $(VERSION_SCRIPT) sort -u > $(OUTPUT)libbpf_global_syms.tmp; \ readelf --dyn-syms --wide $(OUTPUT)libbpf.so | \ sed 's/\[.*\]//' | \ - awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}'| \ + awk '/GLOBAL/ && /DEFAULT/ && !/UND|ABS/ {print $$NF}'| \ grep -Eo '[^ ]+@LIBBPF_' | cut -d@ -f1 | \ sort -u > $(OUTPUT)libbpf_versioned_syms.tmp; \ diff -u $(OUTPUT)libbpf_global_syms.tmp \ diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 550b4cbb6c99..cf27251adb92 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "bpf.h" @@ -111,7 +112,7 @@ int probe_memcg_account(void) BPF_EMIT_CALL(BPF_FUNC_ktime_get_coarse_ns), BPF_EXIT_INSN(), }; - size_t insn_cnt = sizeof(insns) / sizeof(insns[0]); + size_t insn_cnt = ARRAY_SIZE(insns); union bpf_attr attr; int prog_fd; @@ -754,10 +755,10 @@ int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type, .flags = flags, ); - return bpf_prog_attach_xattr(prog_fd, target_fd, type, &opts); + return bpf_prog_attach_opts(prog_fd, target_fd, type, &opts); } -int bpf_prog_attach_xattr(int prog_fd, int target_fd, +int bpf_prog_attach_opts(int prog_fd, int target_fd, enum bpf_attach_type type, const struct bpf_prog_attach_opts *opts) { @@ -778,6 +779,11 @@ int bpf_prog_attach_xattr(int prog_fd, int target_fd, return libbpf_err_errno(ret); } +__attribute__((alias("bpf_prog_attach_opts"))) +int bpf_prog_attach_xattr(int prog_fd, int target_fd, + enum bpf_attach_type type, + const struct bpf_prog_attach_opts *opts); + int bpf_prog_detach(int target_fd, enum bpf_attach_type type) { union bpf_attr attr; @@ -848,6 +854,15 @@ int bpf_link_create(int prog_fd, int target_fd, if (!OPTS_ZEROED(opts, perf_event)) return libbpf_err(-EINVAL); break; + case BPF_TRACE_KPROBE_MULTI: + attr.link_create.kprobe_multi.flags = OPTS_GET(opts, kprobe_multi.flags, 0); + attr.link_create.kprobe_multi.cnt = OPTS_GET(opts, kprobe_multi.cnt, 0); + attr.link_create.kprobe_multi.syms = ptr_to_u64(OPTS_GET(opts, kprobe_multi.syms, 0)); + attr.link_create.kprobe_multi.addrs = ptr_to_u64(OPTS_GET(opts, kprobe_multi.addrs, 0)); + attr.link_create.kprobe_multi.cookies = ptr_to_u64(OPTS_GET(opts, kprobe_multi.cookies, 0)); + if (!OPTS_ZEROED(opts, kprobe_multi)) + return libbpf_err(-EINVAL); + break; default: if (!OPTS_ZEROED(opts, flags)) return libbpf_err(-EINVAL); @@ -989,6 +1004,7 @@ int bpf_prog_test_run_opts(int prog_fd, struct bpf_test_run_opts *opts) memset(&attr, 0, sizeof(attr)); attr.test.prog_fd = prog_fd; + attr.test.batch_size = OPTS_GET(opts, batch_size, 0); attr.test.cpu = OPTS_GET(opts, cpu, 0); attr.test.flags = OPTS_GET(opts, flags, 0); attr.test.repeat = OPTS_GET(opts, repeat, 0); diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 14e0d97ad2cf..f4b4afb6d4ba 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -391,6 +391,10 @@ struct bpf_prog_attach_opts { LIBBPF_API int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type, unsigned int flags); +LIBBPF_API int bpf_prog_attach_opts(int prog_fd, int attachable_fd, + enum bpf_attach_type type, + const struct bpf_prog_attach_opts *opts); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_prog_attach_opts() instead") LIBBPF_API int bpf_prog_attach_xattr(int prog_fd, int attachable_fd, enum bpf_attach_type type, const struct bpf_prog_attach_opts *opts); @@ -409,10 +413,17 @@ struct bpf_link_create_opts { struct { __u64 bpf_cookie; } perf_event; + struct { + __u32 flags; + __u32 cnt; + const char **syms; + const unsigned long *addrs; + const __u64 *cookies; + } kprobe_multi; }; size_t :0; }; -#define bpf_link_create_opts__last_field perf_event +#define bpf_link_create_opts__last_field kprobe_multi.cookies LIBBPF_API int bpf_link_create(int prog_fd, int target_fd, enum bpf_attach_type attach_type, @@ -449,12 +460,14 @@ struct bpf_prog_test_run_attr { * out: length of cxt_out */ }; +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_prog_test_run_opts() instead") LIBBPF_API int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr); /* * bpf_prog_test_run does not check that data_out is large enough. Consider - * using bpf_prog_test_run_xattr instead. + * using bpf_prog_test_run_opts instead. */ +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_prog_test_run_opts() instead") LIBBPF_API int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size, void *data_out, __u32 *size_out, __u32 *retval, __u32 *duration); @@ -506,8 +519,9 @@ struct bpf_test_run_opts { __u32 duration; /* out: average per repetition in ns */ __u32 flags; __u32 cpu; + __u32 batch_size; }; -#define bpf_test_run_opts__last_field cpu +#define bpf_test_run_opts__last_field batch_size LIBBPF_API int bpf_prog_test_run_opts(int prog_fd, struct bpf_test_run_opts *opts); diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h index 963b1060d944..44df982d2a5c 100644 --- a/tools/lib/bpf/bpf_helpers.h +++ b/tools/lib/bpf/bpf_helpers.h @@ -133,7 +133,7 @@ struct bpf_map_def { unsigned int value_size; unsigned int max_entries; unsigned int map_flags; -}; +} __attribute__((deprecated("use BTF-defined maps in .maps section"))); enum libbpf_pin_type { LIBBPF_PIN_NONE, diff --git a/tools/lib/bpf/bpf_tracing.h b/tools/lib/bpf/bpf_tracing.h index 90f56b0f585f..e3a8c947e89f 100644 --- a/tools/lib/bpf/bpf_tracing.h +++ b/tools/lib/bpf/bpf_tracing.h @@ -76,6 +76,9 @@ #define __PT_RC_REG ax #define __PT_SP_REG sp #define __PT_IP_REG ip +/* syscall uses r10 for PARM4 */ +#define PT_REGS_PARM4_SYSCALL(x) ((x)->r10) +#define PT_REGS_PARM4_CORE_SYSCALL(x) BPF_CORE_READ(x, r10) #else @@ -105,6 +108,9 @@ #define __PT_RC_REG rax #define __PT_SP_REG rsp #define __PT_IP_REG rip +/* syscall uses r10 for PARM4 */ +#define PT_REGS_PARM4_SYSCALL(x) ((x)->r10) +#define PT_REGS_PARM4_CORE_SYSCALL(x) BPF_CORE_READ(x, r10) #endif /* __i386__ */ @@ -112,6 +118,10 @@ #elif defined(bpf_target_s390) +struct pt_regs___s390 { + unsigned long orig_gpr2; +}; + /* s390 provides user_pt_regs instead of struct pt_regs to userspace */ #define __PT_REGS_CAST(x) ((const user_pt_regs *)(x)) #define __PT_PARM1_REG gprs[2] @@ -124,6 +134,8 @@ #define __PT_RC_REG gprs[2] #define __PT_SP_REG gprs[15] #define __PT_IP_REG psw.addr +#define PT_REGS_PARM1_SYSCALL(x) ({ _Pragma("GCC error \"use PT_REGS_PARM1_CORE_SYSCALL() instead\""); 0l; }) +#define PT_REGS_PARM1_CORE_SYSCALL(x) BPF_CORE_READ((const struct pt_regs___s390 *)(x), orig_gpr2) #elif defined(bpf_target_arm) @@ -140,6 +152,10 @@ #elif defined(bpf_target_arm64) +struct pt_regs___arm64 { + unsigned long orig_x0; +}; + /* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */ #define __PT_REGS_CAST(x) ((const struct user_pt_regs *)(x)) #define __PT_PARM1_REG regs[0] @@ -152,6 +168,8 @@ #define __PT_RC_REG regs[0] #define __PT_SP_REG sp #define __PT_IP_REG pc +#define PT_REGS_PARM1_SYSCALL(x) ({ _Pragma("GCC error \"use PT_REGS_PARM1_CORE_SYSCALL() instead\""); 0l; }) +#define PT_REGS_PARM1_CORE_SYSCALL(x) BPF_CORE_READ((const struct pt_regs___arm64 *)(x), orig_x0) #elif defined(bpf_target_mips) @@ -178,6 +196,8 @@ #define __PT_RC_REG gpr[3] #define __PT_SP_REG sp #define __PT_IP_REG nip +/* powerpc does not select ARCH_HAS_SYSCALL_WRAPPER. */ +#define PT_REGS_SYSCALL_REGS(ctx) ctx #elif defined(bpf_target_sparc) @@ -206,10 +226,12 @@ #define __PT_PARM4_REG a3 #define __PT_PARM5_REG a4 #define __PT_RET_REG ra -#define __PT_FP_REG fp +#define __PT_FP_REG s0 #define __PT_RC_REG a5 #define __PT_SP_REG sp -#define __PT_IP_REG epc +#define __PT_IP_REG pc +/* riscv does not select ARCH_HAS_SYSCALL_WRAPPER. */ +#define PT_REGS_SYSCALL_REGS(ctx) ctx #endif @@ -263,6 +285,26 @@ struct pt_regs; #endif +#ifndef PT_REGS_PARM1_SYSCALL +#define PT_REGS_PARM1_SYSCALL(x) PT_REGS_PARM1(x) +#endif +#define PT_REGS_PARM2_SYSCALL(x) PT_REGS_PARM2(x) +#define PT_REGS_PARM3_SYSCALL(x) PT_REGS_PARM3(x) +#ifndef PT_REGS_PARM4_SYSCALL +#define PT_REGS_PARM4_SYSCALL(x) PT_REGS_PARM4(x) +#endif +#define PT_REGS_PARM5_SYSCALL(x) PT_REGS_PARM5(x) + +#ifndef PT_REGS_PARM1_CORE_SYSCALL +#define PT_REGS_PARM1_CORE_SYSCALL(x) PT_REGS_PARM1_CORE(x) +#endif +#define PT_REGS_PARM2_CORE_SYSCALL(x) PT_REGS_PARM2_CORE(x) +#define PT_REGS_PARM3_CORE_SYSCALL(x) PT_REGS_PARM3_CORE(x) +#ifndef PT_REGS_PARM4_CORE_SYSCALL +#define PT_REGS_PARM4_CORE_SYSCALL(x) PT_REGS_PARM4_CORE(x) +#endif +#define PT_REGS_PARM5_CORE_SYSCALL(x) PT_REGS_PARM5_CORE(x) + #else /* defined(bpf_target_defined) */ #define PT_REGS_PARM1(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) @@ -290,8 +332,30 @@ struct pt_regs; #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM1_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM2_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM3_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM4_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM5_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) + +#define PT_REGS_PARM1_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM2_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM3_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM4_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM5_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) + #endif /* defined(bpf_target_defined) */ +/* + * When invoked from a syscall handler kprobe, returns a pointer to a + * struct pt_regs containing syscall arguments and suitable for passing to + * PT_REGS_PARMn_SYSCALL() and PT_REGS_PARMn_CORE_SYSCALL(). + */ +#ifndef PT_REGS_SYSCALL_REGS +/* By default, assume that the arch selects ARCH_HAS_SYSCALL_WRAPPER. */ +#define PT_REGS_SYSCALL_REGS(ctx) ((struct pt_regs *)PT_REGS_PARM1(ctx)) +#endif + #ifndef ___bpf_concat #define ___bpf_concat(a, b) a ## b #endif @@ -406,4 +470,39 @@ typeof(name(0)) name(struct pt_regs *ctx) \ } \ static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args) +#define ___bpf_syscall_args0() ctx +#define ___bpf_syscall_args1(x) ___bpf_syscall_args0(), (void *)PT_REGS_PARM1_CORE_SYSCALL(regs) +#define ___bpf_syscall_args2(x, args...) ___bpf_syscall_args1(args), (void *)PT_REGS_PARM2_CORE_SYSCALL(regs) +#define ___bpf_syscall_args3(x, args...) ___bpf_syscall_args2(args), (void *)PT_REGS_PARM3_CORE_SYSCALL(regs) +#define ___bpf_syscall_args4(x, args...) ___bpf_syscall_args3(args), (void *)PT_REGS_PARM4_CORE_SYSCALL(regs) +#define ___bpf_syscall_args5(x, args...) ___bpf_syscall_args4(args), (void *)PT_REGS_PARM5_CORE_SYSCALL(regs) +#define ___bpf_syscall_args(args...) ___bpf_apply(___bpf_syscall_args, ___bpf_narg(args))(args) + +/* + * BPF_KPROBE_SYSCALL is a variant of BPF_KPROBE, which is intended for + * tracing syscall functions, like __x64_sys_close. It hides the underlying + * platform-specific low-level way of getting syscall input arguments from + * struct pt_regs, and provides a familiar typed and named function arguments + * syntax and semantics of accessing syscall input parameters. + * + * Original struct pt_regs* context is preserved as 'ctx' argument. This might + * be necessary when using BPF helpers like bpf_perf_event_output(). + * + * This macro relies on BPF CO-RE support. + */ +#define BPF_KPROBE_SYSCALL(name, args...) \ +name(struct pt_regs *ctx); \ +static __attribute__((always_inline)) typeof(name(0)) \ +____##name(struct pt_regs *ctx, ##args); \ +typeof(name(0)) name(struct pt_regs *ctx) \ +{ \ + struct pt_regs *regs = PT_REGS_SYSCALL_REGS(ctx); \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ + return ____##name(___bpf_syscall_args(args)); \ + _Pragma("GCC diagnostic pop") \ +} \ +static __attribute__((always_inline)) typeof(name(0)) \ +____##name(struct pt_regs *ctx, ##args) + #endif diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 9aa19c89f758..1383e26c5d1f 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -1620,20 +1620,37 @@ static int btf_commit_type(struct btf *btf, int data_sz) struct btf_pipe { const struct btf *src; struct btf *dst; + struct hashmap *str_off_map; /* map string offsets from src to dst */ }; static int btf_rewrite_str(__u32 *str_off, void *ctx) { struct btf_pipe *p = ctx; - int off; + void *mapped_off; + int off, err; if (!*str_off) /* nothing to do for empty strings */ return 0; + if (p->str_off_map && + hashmap__find(p->str_off_map, (void *)(long)*str_off, &mapped_off)) { + *str_off = (__u32)(long)mapped_off; + return 0; + } + off = btf__add_str(p->dst, btf__str_by_offset(p->src, *str_off)); if (off < 0) return off; + /* Remember string mapping from src to dst. It avoids + * performing expensive string comparisons. + */ + if (p->str_off_map) { + err = hashmap__append(p->str_off_map, (void *)(long)*str_off, (void *)(long)off); + if (err) + return err; + } + *str_off = off; return 0; } @@ -1680,6 +1697,9 @@ static int btf_rewrite_type_ids(__u32 *type_id, void *ctx) return 0; } +static size_t btf_dedup_identity_hash_fn(const void *key, void *ctx); +static bool btf_dedup_equal_fn(const void *k1, const void *k2, void *ctx); + int btf__add_btf(struct btf *btf, const struct btf *src_btf) { struct btf_pipe p = { .src = src_btf, .dst = btf }; @@ -1713,6 +1733,11 @@ int btf__add_btf(struct btf *btf, const struct btf *src_btf) if (!off) return libbpf_err(-ENOMEM); + /* Map the string offsets from src_btf to the offsets from btf to improve performance */ + p.str_off_map = hashmap__new(btf_dedup_identity_hash_fn, btf_dedup_equal_fn, NULL); + if (IS_ERR(p.str_off_map)) + return libbpf_err(-ENOMEM); + /* bulk copy types data for all types from src_btf */ memcpy(t, src_btf->types_data, data_sz); @@ -1754,6 +1779,8 @@ int btf__add_btf(struct btf *btf, const struct btf *src_btf) btf->hdr->str_off += data_sz; btf->nr_types += cnt; + hashmap__free(p.str_off_map); + /* return type ID of the first added BTF type */ return btf->start_id + btf->nr_types - cnt; err_out: @@ -1767,6 +1794,8 @@ int btf__add_btf(struct btf *btf, const struct btf *src_btf) * wasn't modified, so doesn't need restoring, see big comment above */ btf->hdr->str_len = old_strs_len; + hashmap__free(p.str_off_map); + return libbpf_err(err); } diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index 061839f04525..951ac7475794 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -147,11 +147,10 @@ LIBBPF_API int btf__resolve_type(const struct btf *btf, __u32 type_id); LIBBPF_API int btf__align_of(const struct btf *btf, __u32 id); LIBBPF_API int btf__fd(const struct btf *btf); LIBBPF_API void btf__set_fd(struct btf *btf, int fd); -LIBBPF_DEPRECATED_SINCE(0, 7, "use btf__raw_data() instead") -LIBBPF_API const void *btf__get_raw_data(const struct btf *btf, __u32 *size); LIBBPF_API const void *btf__raw_data(const struct btf *btf, __u32 *size); LIBBPF_API const char *btf__name_by_offset(const struct btf *btf, __u32 offset); LIBBPF_API const char *btf__str_by_offset(const struct btf *btf, __u32 offset); +LIBBPF_DEPRECATED_SINCE(0, 7, "this API is not necessary when BTF-defined maps are used") LIBBPF_API int btf__get_map_kv_tids(const struct btf *btf, const char *map_name, __u32 expected_key_size, __u32 expected_value_size, @@ -159,8 +158,7 @@ LIBBPF_API int btf__get_map_kv_tids(const struct btf *btf, const char *map_name, LIBBPF_API struct btf_ext *btf_ext__new(const __u8 *data, __u32 size); LIBBPF_API void btf_ext__free(struct btf_ext *btf_ext); -LIBBPF_API const void *btf_ext__get_raw_data(const struct btf_ext *btf_ext, - __u32 *size); +LIBBPF_API const void *btf_ext__raw_data(const struct btf_ext *btf_ext, __u32 *size); LIBBPF_API LIBBPF_DEPRECATED("btf_ext__reloc_func_info was never meant as a public API and has wrong assumptions embedded in it; it will be removed in the future libbpf versions") int btf_ext__reloc_func_info(const struct btf *btf, const struct btf_ext *btf_ext, @@ -171,8 +169,10 @@ int btf_ext__reloc_line_info(const struct btf *btf, const struct btf_ext *btf_ext, const char *sec_name, __u32 insns_cnt, void **line_info, __u32 *cnt); -LIBBPF_API __u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext); -LIBBPF_API __u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext); +LIBBPF_API LIBBPF_DEPRECATED("btf_ext__reloc_func_info is deprecated; write custom func_info parsing to fetch rec_size") +__u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext); +LIBBPF_API LIBBPF_DEPRECATED("btf_ext__reloc_line_info is deprecated; write custom line_info parsing to fetch rec_size") +__u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext); LIBBPF_API int btf__find_str(struct btf *btf, const char *s); LIBBPF_API int btf__add_str(struct btf *btf, const char *s); @@ -375,8 +375,28 @@ btf_dump__dump_type_data(struct btf_dump *d, __u32 id, const struct btf_dump_type_data_opts *opts); /* - * A set of helpers for easier BTF types handling + * A set of helpers for easier BTF types handling. + * + * The inline functions below rely on constants from the kernel headers which + * may not be available for applications including this header file. To avoid + * compilation errors, we define all the constants here that were added after + * the initial introduction of the BTF_KIND* constants. */ +#ifndef BTF_KIND_FUNC +#define BTF_KIND_FUNC 12 /* Function */ +#define BTF_KIND_FUNC_PROTO 13 /* Function Proto */ +#endif +#ifndef BTF_KIND_VAR +#define BTF_KIND_VAR 14 /* Variable */ +#define BTF_KIND_DATASEC 15 /* Section */ +#endif +#ifndef BTF_KIND_FLOAT +#define BTF_KIND_FLOAT 16 /* Floating point */ +#endif +/* The kernel header switched to enums, so these two were never #defined */ +#define BTF_KIND_DECL_TAG 17 /* Decl Tag */ +#define BTF_KIND_TYPE_TAG 18 /* Type Tag */ + static inline __u16 btf_kind(const struct btf_type *t) { return BTF_INFO_KIND(t->info); diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index b9a3260c83cb..6b1bc1f43728 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -1505,6 +1505,11 @@ static const char *btf_dump_resolve_name(struct btf_dump *d, __u32 id, if (s->name_resolved) return *cached_name ? *cached_name : orig_name; + if (btf_is_fwd(t) || (btf_is_enum(t) && btf_vlen(t) == 0)) { + s->name_resolved = 1; + return orig_name; + } + dup_cnt = btf_dump_name_dups(d, name_map, orig_name); if (dup_cnt > 1) { const size_t max_len = 256; @@ -1861,14 +1866,16 @@ static int btf_dump_array_data(struct btf_dump *d, { const struct btf_array *array = btf_array(t); const struct btf_type *elem_type; - __u32 i, elem_size = 0, elem_type_id; + __u32 i, elem_type_id; + __s64 elem_size; bool is_array_member; elem_type_id = array->type; elem_type = skip_mods_and_typedefs(d->btf, elem_type_id, NULL); elem_size = btf__resolve_size(d->btf, elem_type_id); if (elem_size <= 0) { - pr_warn("unexpected elem size %d for array type [%u]\n", elem_size, id); + pr_warn("unexpected elem size %zd for array type [%u]\n", + (ssize_t)elem_size, id); return -EINVAL; } diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c index 8ecef1088ba2..927745b08014 100644 --- a/tools/lib/bpf/gen_loader.c +++ b/tools/lib/bpf/gen_loader.c @@ -1043,18 +1043,27 @@ void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *pvalue, value = add_data(gen, pvalue, value_size); key = add_data(gen, &zero, sizeof(zero)); - /* if (map_desc[map_idx].initial_value) - * copy_from_user(value, initial_value, value_size); + /* if (map_desc[map_idx].initial_value) { + * if (ctx->flags & BPF_SKEL_KERNEL) + * bpf_probe_read_kernel(value, value_size, initial_value); + * else + * bpf_copy_from_user(value, value_size, initial_value); + * } */ emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, sizeof(struct bpf_loader_ctx) + sizeof(struct bpf_map_desc) * map_idx + offsetof(struct bpf_map_desc, initial_value))); - emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 4)); + emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8)); emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, 0, 0, 0, value)); emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size)); + emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, + offsetof(struct bpf_loader_ctx, flags))); + emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2)); emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user)); + emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1)); + emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel)); map_update_attr = add_data(gen, &attr, attr_size); move_blob2blob(gen, attr_field(map_update_attr, map_fd), 4, diff --git a/tools/lib/bpf/hashmap.c b/tools/lib/bpf/hashmap.c index 3c20b126d60d..aeb09c288716 100644 --- a/tools/lib/bpf/hashmap.c +++ b/tools/lib/bpf/hashmap.c @@ -75,7 +75,7 @@ void hashmap__clear(struct hashmap *map) void hashmap__free(struct hashmap *map) { - if (!map) + if (IS_ERR_OR_NULL(map)) return; hashmap__clear(map); @@ -238,4 +238,3 @@ bool hashmap__delete(struct hashmap *map, const void *key, return true; } - diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 7f10dd501a52..809fe209cdcc 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -156,14 +156,6 @@ enum libbpf_strict_mode libbpf_mode = LIBBPF_STRICT_NONE; int libbpf_set_strict_mode(enum libbpf_strict_mode mode) { - /* __LIBBPF_STRICT_LAST is the last power-of-2 value used + 1, so to - * get all possible values we compensate last +1, and then (2*x - 1) - * to get the bit mask - */ - if (mode != LIBBPF_STRICT_ALL - && (mode & ~((__LIBBPF_STRICT_LAST - 1) * 2 - 1))) - return errno = EINVAL, -EINVAL; - libbpf_mode = mode; return 0; } @@ -209,12 +201,6 @@ struct reloc_desc { }; }; -struct bpf_sec_def; - -typedef int (*init_fn_t)(struct bpf_program *prog, long cookie); -typedef int (*preload_fn_t)(struct bpf_program *prog, struct bpf_prog_load_opts *opts, long cookie); -typedef struct bpf_link *(*attach_fn_t)(const struct bpf_program *prog, long cookie); - /* stored as sec_def->cookie for all libbpf-supported SEC()s */ enum sec_def_flags { SEC_NONE = 0, @@ -235,17 +221,22 @@ enum sec_def_flags { SEC_SLEEPABLE = 8, /* allow non-strict prefix matching */ SEC_SLOPPY_PFX = 16, + /* BPF program support non-linear XDP buffer */ + SEC_XDP_FRAGS = 32, + /* deprecated sec definitions not supposed to be used */ + SEC_DEPRECATED = 64, }; struct bpf_sec_def { - const char *sec; + char *sec; enum bpf_prog_type prog_type; enum bpf_attach_type expected_attach_type; long cookie; + int handler_id; - init_fn_t init_fn; - preload_fn_t preload_fn; - attach_fn_t attach_fn; + libbpf_prog_setup_fn_t prog_setup_fn; + libbpf_prog_prepare_load_fn_t prog_prepare_load_fn; + libbpf_prog_attach_fn_t prog_attach_fn; }; /* @@ -1378,22 +1369,20 @@ static bool bpf_map_type__is_map_in_map(enum bpf_map_type type) static int find_elf_sec_sz(const struct bpf_object *obj, const char *name, __u32 *size) { - int ret = -ENOENT; Elf_Data *data; Elf_Scn *scn; - *size = 0; if (!name) return -EINVAL; scn = elf_sec_by_name(obj, name); data = elf_sec_data(obj, scn); if (data) { - ret = 0; /* found it */ *size = data->d_size; + return 0; /* found it */ } - return *size ? 0 : ret; + return -ENOENT; } static int find_elf_var_offset(const struct bpf_object *obj, const char *name, __u32 *off) @@ -1528,6 +1517,9 @@ static char *internal_map_name(struct bpf_object *obj, const char *real_name) return strdup(map_name); } +static int +bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map); + static int bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, const char *real_name, int sec_idx, void *data, size_t data_sz) @@ -1575,6 +1567,9 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, return err; } + /* failures are fine because of maps like .rodata.str1.1 */ + (void) bpf_map_find_btf_info(obj, map); + if (data) memcpy(map->mmaped, data, data_sz); @@ -1937,6 +1932,11 @@ static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict) if (obj->efile.maps_shndx < 0) return 0; + if (libbpf_mode & LIBBPF_STRICT_MAP_DEFINITIONS) { + pr_warn("legacy map definitions in SEC(\"maps\") are not supported\n"); + return -EOPNOTSUPP; + } + if (!symbols) return -EINVAL; @@ -1999,6 +1999,8 @@ static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict) return -LIBBPF_ERRNO__FORMAT; } + pr_warn("map '%s' (legacy): legacy map definitions are deprecated, use BTF-defined maps instead\n", map_name); + if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL) { pr_warn("map '%s' (legacy): static maps are not supported\n", map_name); return -ENOTSUP; @@ -2050,6 +2052,9 @@ static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict) } memcpy(&map->def, def, sizeof(struct bpf_map_def)); } + + /* btf info may not exist but fill it in if it does exist */ + (void) bpf_map_find_btf_info(obj, map); } return 0; } @@ -2538,6 +2543,10 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, fill_map_from_def(map->inner_map, &inner_def); } + err = bpf_map_find_btf_info(obj, map); + if (err) + return err; + return 0; } @@ -2792,7 +2801,7 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf, goto sort_vars; ret = find_elf_sec_sz(obj, name, &size); - if (ret || !size || (t->size && t->size != size)) { + if (ret || !size) { pr_debug("Invalid size for section %s: %u bytes\n", name, size); return -ENOENT; } @@ -3836,7 +3845,14 @@ static bool prog_is_subprog(const struct bpf_object *obj, * .text programs are subprograms (even if they are not called from * other programs), because libbpf never explicitly supported mixing * SEC()-designated BPF programs and .text entry-point BPF programs. + * + * In libbpf 1.0 strict mode, we always consider .text + * programs to be subprograms. */ + + if (libbpf_mode & LIBBPF_STRICT_SEC_NAME) + return prog->sec_idx == obj->efile.text_shndx; + return prog->sec_idx == obj->efile.text_shndx && obj->nr_programs > 1; } @@ -4181,6 +4197,9 @@ static int bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map) __u32 key_type_id = 0, value_type_id = 0; int ret; + if (!obj->btf) + return -ENOENT; + /* if it's BTF-defined map, we don't need to search for type IDs. * For struct_ops map, it does not need btf_key_type_id and * btf_value_type_id. @@ -4190,9 +4209,13 @@ static int bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map) return 0; if (!bpf_map__is_internal(map)) { + pr_warn("Use of BPF_ANNOTATE_KV_PAIR is deprecated, use BTF-defined maps in .maps section instead\n"); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" ret = btf__get_map_kv_tids(obj->btf, map->name, def->key_size, def->value_size, &key_type_id, &value_type_id); +#pragma GCC diagnostic pop } else { /* * LLVM annotates global data differently in BTF, that is, @@ -4800,8 +4823,8 @@ bpf_object__reuse_map(struct bpf_map *map) } err = bpf_map__reuse_fd(map, pin_fd); + close(pin_fd); if (err) { - close(pin_fd); return err; } map->pinned = true; @@ -4854,7 +4877,6 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b LIBBPF_OPTS(bpf_map_create_opts, create_attr); struct bpf_map_def *def = &map->def; const char *map_name = NULL; - __u32 max_entries; int err = 0; if (kernel_supports(obj, FEAT_PROG_NAME)) @@ -4864,25 +4886,10 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b create_attr.numa_node = map->numa_node; create_attr.map_extra = map->map_extra; - if (def->type == BPF_MAP_TYPE_PERF_EVENT_ARRAY && !def->max_entries) { - int nr_cpus; - - nr_cpus = libbpf_num_possible_cpus(); - if (nr_cpus < 0) { - pr_warn("map '%s': failed to determine number of system CPUs: %d\n", - map->name, nr_cpus); - return nr_cpus; - } - pr_debug("map '%s': setting size to %d\n", map->name, nr_cpus); - max_entries = nr_cpus; - } else { - max_entries = def->max_entries; - } - if (bpf_map__is_struct_ops(map)) create_attr.btf_vmlinux_value_type_id = map->btf_vmlinux_value_type_id; - if (obj->btf && btf__fd(obj->btf) >= 0 && !bpf_map_find_btf_info(obj, map)) { + if (obj->btf && btf__fd(obj->btf) >= 0) { create_attr.btf_fd = btf__fd(obj->btf); create_attr.btf_key_type_id = map->btf_key_type_id; create_attr.btf_value_type_id = map->btf_value_type_id; @@ -4928,7 +4935,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b if (obj->gen_loader) { bpf_gen__map_create(obj->gen_loader, def->type, map_name, - def->key_size, def->value_size, max_entries, + def->key_size, def->value_size, def->max_entries, &create_attr, is_inner ? -1 : map - obj->maps); /* Pretend to have valid FD to pass various fd >= 0 checks. * This fd == 0 will not be used with any syscall and will be reset to -1 eventually. @@ -4937,7 +4944,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b } else { map->fd = bpf_map_create(def->type, map_name, def->key_size, def->value_size, - max_entries, &create_attr); + def->max_entries, &create_attr); } if (map->fd < 0 && (create_attr.btf_key_type_id || create_attr.btf_value_type_id)) { @@ -4954,7 +4961,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b map->btf_value_type_id = 0; map->fd = bpf_map_create(def->type, map_name, def->key_size, def->value_size, - max_entries, &create_attr); + def->max_entries, &create_attr); } err = map->fd < 0 ? -errno : 0; @@ -5058,6 +5065,24 @@ static int bpf_object_init_prog_arrays(struct bpf_object *obj) return 0; } +static int map_set_def_max_entries(struct bpf_map *map) +{ + if (map->def.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY && !map->def.max_entries) { + int nr_cpus; + + nr_cpus = libbpf_num_possible_cpus(); + if (nr_cpus < 0) { + pr_warn("map '%s': failed to determine number of system CPUs: %d\n", + map->name, nr_cpus); + return nr_cpus; + } + pr_debug("map '%s': setting size to %d\n", map->name, nr_cpus); + map->def.max_entries = nr_cpus; + } + + return 0; +} + static int bpf_object__create_maps(struct bpf_object *obj) { @@ -5090,6 +5115,10 @@ bpf_object__create_maps(struct bpf_object *obj) continue; } + err = map_set_def_max_entries(map); + if (err) + goto err_out; + retried = false; retry: if (map->pin_path) { @@ -5185,18 +5214,21 @@ size_t bpf_core_essential_name_len(const char *name) return n; } -static void bpf_core_free_cands(struct bpf_core_cand_list *cands) +void bpf_core_free_cands(struct bpf_core_cand_list *cands) { + if (!cands) + return; + free(cands->cands); free(cands); } -static int bpf_core_add_cands(struct bpf_core_cand *local_cand, - size_t local_essent_len, - const struct btf *targ_btf, - const char *targ_btf_name, - int targ_start_id, - struct bpf_core_cand_list *cands) +int bpf_core_add_cands(struct bpf_core_cand *local_cand, + size_t local_essent_len, + const struct btf *targ_btf, + const char *targ_btf_name, + int targ_start_id, + struct bpf_core_cand_list *cands) { struct bpf_core_cand *new_cands, *cand; const struct btf_type *t, *local_t; @@ -5523,11 +5555,12 @@ static int record_relo_core(struct bpf_program *prog, return 0; } -static int bpf_core_apply_relo(struct bpf_program *prog, - const struct bpf_core_relo *relo, - int relo_idx, - const struct btf *local_btf, - struct hashmap *cand_cache) +static int bpf_core_resolve_relo(struct bpf_program *prog, + const struct bpf_core_relo *relo, + int relo_idx, + const struct btf *local_btf, + struct hashmap *cand_cache, + struct bpf_core_relo_res *targ_res) { struct bpf_core_spec specs_scratch[3] = {}; const void *type_key = u32_as_hash_key(relo->type_id); @@ -5536,20 +5569,7 @@ static int bpf_core_apply_relo(struct bpf_program *prog, const struct btf_type *local_type; const char *local_name; __u32 local_id = relo->type_id; - struct bpf_insn *insn; - int insn_idx, err; - - if (relo->insn_off % BPF_INSN_SZ) - return -EINVAL; - insn_idx = relo->insn_off / BPF_INSN_SZ; - /* adjust insn_idx from section frame of reference to the local - * program's frame of reference; (sub-)program code is not yet - * relocated, so it's enough to just subtract in-section offset - */ - insn_idx = insn_idx - prog->sec_insn_off; - if (insn_idx >= prog->insns_cnt) - return -EINVAL; - insn = &prog->insns[insn_idx]; + int err; local_type = btf__type_by_id(local_btf, local_id); if (!local_type) @@ -5559,15 +5579,6 @@ static int bpf_core_apply_relo(struct bpf_program *prog, if (!local_name) return -EINVAL; - if (prog->obj->gen_loader) { - const char *spec_str = btf__name_by_offset(local_btf, relo->access_str_off); - - pr_debug("record_relo_core: prog %td insn[%d] %s %s %s final insn_idx %d\n", - prog - prog->obj->programs, relo->insn_off / 8, - btf_kind_str(local_type), local_name, spec_str, insn_idx); - return record_relo_core(prog, relo, insn_idx); - } - if (relo->kind != BPF_CORE_TYPE_ID_LOCAL && !hashmap__find(cand_cache, type_key, (void **)&cands)) { cands = bpf_core_find_cands(prog->obj, local_btf, local_id); @@ -5584,19 +5595,21 @@ static int bpf_core_apply_relo(struct bpf_program *prog, } } - return bpf_core_apply_relo_insn(prog_name, insn, insn_idx, relo, - relo_idx, local_btf, cands, specs_scratch); + return bpf_core_calc_relo_insn(prog_name, relo, relo_idx, local_btf, cands, specs_scratch, + targ_res); } static int bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path) { const struct btf_ext_info_sec *sec; + struct bpf_core_relo_res targ_res; const struct bpf_core_relo *rec; const struct btf_ext_info *seg; struct hashmap_entry *entry; struct hashmap *cand_cache = NULL; struct bpf_program *prog; + struct bpf_insn *insn; const char *sec_name; int i, err = 0, insn_idx, sec_idx; @@ -5647,6 +5660,8 @@ bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path) sec_name, sec->num_info); for_each_btf_ext_rec(seg, sec, i, rec) { + if (rec->insn_off % BPF_INSN_SZ) + return -EINVAL; insn_idx = rec->insn_off / BPF_INSN_SZ; prog = find_prog_by_sec_insn(obj, sec_idx, insn_idx); if (!prog) { @@ -5661,12 +5676,38 @@ bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path) if (!prog->load) continue; - err = bpf_core_apply_relo(prog, rec, i, obj->btf, cand_cache); + /* adjust insn_idx from section frame of reference to the local + * program's frame of reference; (sub-)program code is not yet + * relocated, so it's enough to just subtract in-section offset + */ + insn_idx = insn_idx - prog->sec_insn_off; + if (insn_idx >= prog->insns_cnt) + return -EINVAL; + insn = &prog->insns[insn_idx]; + + if (prog->obj->gen_loader) { + err = record_relo_core(prog, rec, insn_idx); + if (err) { + pr_warn("prog '%s': relo #%d: failed to record relocation: %d\n", + prog->name, i, err); + goto out; + } + continue; + } + + err = bpf_core_resolve_relo(prog, rec, i, obj->btf, cand_cache, &targ_res); if (err) { pr_warn("prog '%s': relo #%d: failed to relocate: %d\n", prog->name, i, err); goto out; } + + err = bpf_core_patch_insn(prog->name, insn, insn_idx, rec, i, &targ_res); + if (err) { + pr_warn("prog '%s': relo #%d: failed to patch insn #%u: %d\n", + prog->name, i, insn_idx, err); + goto out; + } } } @@ -6549,9 +6590,9 @@ static int bpf_object__sanitize_prog(struct bpf_object *obj, struct bpf_program static int libbpf_find_attach_btf_id(struct bpf_program *prog, const char *attach_name, int *btf_obj_fd, int *btf_type_id); -/* this is called as prog->sec_def->preload_fn for libbpf-supported sec_defs */ -static int libbpf_preload_prog(struct bpf_program *prog, - struct bpf_prog_load_opts *opts, long cookie) +/* this is called as prog->sec_def->prog_prepare_load_fn for libbpf-supported sec_defs */ +static int libbpf_prepare_prog_load(struct bpf_program *prog, + struct bpf_prog_load_opts *opts, long cookie) { enum sec_def_flags def = cookie; @@ -6562,6 +6603,13 @@ static int libbpf_preload_prog(struct bpf_program *prog, if (def & SEC_SLEEPABLE) opts->prog_flags |= BPF_F_SLEEPABLE; + if (prog->type == BPF_PROG_TYPE_XDP && (def & SEC_XDP_FRAGS)) + opts->prog_flags |= BPF_F_XDP_HAS_FRAGS; + + if (def & SEC_DEPRECATED) + pr_warn("SEC(\"%s\") is deprecated, please see https://github.com/libbpf/libbpf/wiki/Libbpf-1.0-migration-guide#bpf-program-sec-annotation-deprecations for details\n", + prog->sec_name); + if ((prog->type == BPF_PROG_TYPE_TRACING || prog->type == BPF_PROG_TYPE_LSM || prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) { @@ -6640,8 +6688,8 @@ static int bpf_object_load_prog_instance(struct bpf_object *obj, struct bpf_prog load_attr.fd_array = obj->fd_array; /* adjust load_attr if sec_def provides custom preload callback */ - if (prog->sec_def && prog->sec_def->preload_fn) { - err = prog->sec_def->preload_fn(prog, &load_attr, prog->sec_def->cookie); + if (prog->sec_def && prog->sec_def->prog_prepare_load_fn) { + err = prog->sec_def->prog_prepare_load_fn(prog, &load_attr, prog->sec_def->cookie); if (err < 0) { pr_warn("prog '%s': failed to prepare load attributes: %d\n", prog->name, err); @@ -6941,8 +6989,8 @@ static int bpf_object_init_progs(struct bpf_object *obj, const struct bpf_object /* sec_def can have custom callback which should be called * after bpf_program is initialized to adjust its properties */ - if (prog->sec_def->init_fn) { - err = prog->sec_def->init_fn(prog, prog->sec_def->cookie); + if (prog->sec_def->prog_setup_fn) { + err = prog->sec_def->prog_setup_fn(prog, prog->sec_def->cookie); if (err < 0) { pr_warn("prog '%s': failed to initialize: %d\n", prog->name, err); @@ -7146,12 +7194,10 @@ static int bpf_object__sanitize_maps(struct bpf_object *obj) return 0; } -static int bpf_object__read_kallsyms_file(struct bpf_object *obj) +int libbpf_kallsyms_parse(kallsyms_cb_t cb, void *ctx) { char sym_type, sym_name[500]; unsigned long long sym_addr; - const struct btf_type *t; - struct extern_desc *ext; int ret, err = 0; FILE *f; @@ -7170,35 +7216,51 @@ static int bpf_object__read_kallsyms_file(struct bpf_object *obj) if (ret != 3) { pr_warn("failed to read kallsyms entry: %d\n", ret); err = -EINVAL; - goto out; + break; } - ext = find_extern_by_name(obj, sym_name); - if (!ext || ext->type != EXT_KSYM) - continue; - - t = btf__type_by_id(obj->btf, ext->btf_id); - if (!btf_is_var(t)) - continue; - - if (ext->is_set && ext->ksym.addr != sym_addr) { - pr_warn("extern (ksym) '%s' resolution is ambiguous: 0x%llx or 0x%llx\n", - sym_name, ext->ksym.addr, sym_addr); - err = -EINVAL; - goto out; - } - if (!ext->is_set) { - ext->is_set = true; - ext->ksym.addr = sym_addr; - pr_debug("extern (ksym) %s=0x%llx\n", sym_name, sym_addr); - } + err = cb(sym_addr, sym_type, sym_name, ctx); + if (err) + break; } -out: fclose(f); return err; } +static int kallsyms_cb(unsigned long long sym_addr, char sym_type, + const char *sym_name, void *ctx) +{ + struct bpf_object *obj = ctx; + const struct btf_type *t; + struct extern_desc *ext; + + ext = find_extern_by_name(obj, sym_name); + if (!ext || ext->type != EXT_KSYM) + return 0; + + t = btf__type_by_id(obj->btf, ext->btf_id); + if (!btf_is_var(t)) + return 0; + + if (ext->is_set && ext->ksym.addr != sym_addr) { + pr_warn("extern (ksym) '%s' resolution is ambiguous: 0x%llx or 0x%llx\n", + sym_name, ext->ksym.addr, sym_addr); + return -EINVAL; + } + if (!ext->is_set) { + ext->is_set = true; + ext->ksym.addr = sym_addr; + pr_debug("extern (ksym) %s=0x%llx\n", sym_name, sym_addr); + } + return 0; +} + +static int bpf_object__read_kallsyms_file(struct bpf_object *obj) +{ + return libbpf_kallsyms_parse(kallsyms_cb, obj); +} + static int find_ksym_btf_id(struct bpf_object *obj, const char *ksym_name, __u16 kind, struct btf **res_btf, struct module_btf **res_mod_btf) @@ -7883,10 +7945,8 @@ int bpf_map__set_pin_path(struct bpf_map *map, const char *path) return 0; } -const char *bpf_map__get_pin_path(const struct bpf_map *map) -{ - return map->pin_path; -} +__alias(bpf_map__pin_path) +const char *bpf_map__get_pin_path(const struct bpf_map *map); const char *bpf_map__pin_path(const struct bpf_map *map) { @@ -8451,7 +8511,10 @@ static int bpf_program_nth_fd(const struct bpf_program *prog, int n) return fd; } -enum bpf_prog_type bpf_program__get_type(const struct bpf_program *prog) +__alias(bpf_program__type) +enum bpf_prog_type bpf_program__get_type(const struct bpf_program *prog); + +enum bpf_prog_type bpf_program__type(const struct bpf_program *prog) { return prog->type; } @@ -8495,8 +8558,10 @@ BPF_PROG_TYPE_FNS(struct_ops, BPF_PROG_TYPE_STRUCT_OPS); BPF_PROG_TYPE_FNS(extension, BPF_PROG_TYPE_EXT); BPF_PROG_TYPE_FNS(sk_lookup, BPF_PROG_TYPE_SK_LOOKUP); -enum bpf_attach_type -bpf_program__get_expected_attach_type(const struct bpf_program *prog) +__alias(bpf_program__expected_attach_type) +enum bpf_attach_type bpf_program__get_expected_attach_type(const struct bpf_program *prog); + +enum bpf_attach_type bpf_program__expected_attach_type(const struct bpf_program *prog) { return prog->expected_attach_type; } @@ -8556,20 +8621,21 @@ int bpf_program__set_log_buf(struct bpf_program *prog, char *log_buf, size_t log } #define SEC_DEF(sec_pfx, ptype, atype, flags, ...) { \ - .sec = sec_pfx, \ + .sec = (char *)sec_pfx, \ .prog_type = BPF_PROG_TYPE_##ptype, \ .expected_attach_type = atype, \ .cookie = (long)(flags), \ - .preload_fn = libbpf_preload_prog, \ + .prog_prepare_load_fn = libbpf_prepare_prog_load, \ __VA_ARGS__ \ } -static struct bpf_link *attach_kprobe(const struct bpf_program *prog, long cookie); -static struct bpf_link *attach_tp(const struct bpf_program *prog, long cookie); -static struct bpf_link *attach_raw_tp(const struct bpf_program *prog, long cookie); -static struct bpf_link *attach_trace(const struct bpf_program *prog, long cookie); -static struct bpf_link *attach_lsm(const struct bpf_program *prog, long cookie); -static struct bpf_link *attach_iter(const struct bpf_program *prog, long cookie); +static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link); +static int attach_tp(const struct bpf_program *prog, long cookie, struct bpf_link **link); +static int attach_raw_tp(const struct bpf_program *prog, long cookie, struct bpf_link **link); +static int attach_trace(const struct bpf_program *prog, long cookie, struct bpf_link **link); +static int attach_kprobe_multi(const struct bpf_program *prog, long cookie, struct bpf_link **link); +static int attach_lsm(const struct bpf_program *prog, long cookie, struct bpf_link **link); +static int attach_iter(const struct bpf_program *prog, long cookie, struct bpf_link **link); static const struct bpf_sec_def section_defs[] = { SEC_DEF("socket", SOCKET_FILTER, 0, SEC_NONE | SEC_SLOPPY_PFX), @@ -8579,8 +8645,10 @@ static const struct bpf_sec_def section_defs[] = { SEC_DEF("uprobe/", KPROBE, 0, SEC_NONE), SEC_DEF("kretprobe/", KPROBE, 0, SEC_NONE, attach_kprobe), SEC_DEF("uretprobe/", KPROBE, 0, SEC_NONE), + SEC_DEF("kprobe.multi/", KPROBE, BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi), + SEC_DEF("kretprobe.multi/", KPROBE, BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi), SEC_DEF("tc", SCHED_CLS, 0, SEC_NONE), - SEC_DEF("classifier", SCHED_CLS, 0, SEC_NONE | SEC_SLOPPY_PFX), + SEC_DEF("classifier", SCHED_CLS, 0, SEC_NONE | SEC_SLOPPY_PFX | SEC_DEPRECATED), SEC_DEF("action", SCHED_ACT, 0, SEC_NONE | SEC_SLOPPY_PFX), SEC_DEF("tracepoint/", TRACEPOINT, 0, SEC_NONE, attach_tp), SEC_DEF("tp/", TRACEPOINT, 0, SEC_NONE, attach_tp), @@ -8599,9 +8667,15 @@ static const struct bpf_sec_def section_defs[] = { SEC_DEF("lsm/", LSM, BPF_LSM_MAC, SEC_ATTACH_BTF, attach_lsm), SEC_DEF("lsm.s/", LSM, BPF_LSM_MAC, SEC_ATTACH_BTF | SEC_SLEEPABLE, attach_lsm), SEC_DEF("iter/", TRACING, BPF_TRACE_ITER, SEC_ATTACH_BTF, attach_iter), + SEC_DEF("iter.s/", TRACING, BPF_TRACE_ITER, SEC_ATTACH_BTF | SEC_SLEEPABLE, attach_iter), SEC_DEF("syscall", SYSCALL, 0, SEC_SLEEPABLE), - SEC_DEF("xdp_devmap/", XDP, BPF_XDP_DEVMAP, SEC_ATTACHABLE), - SEC_DEF("xdp_cpumap/", XDP, BPF_XDP_CPUMAP, SEC_ATTACHABLE), + SEC_DEF("xdp.frags/devmap", XDP, BPF_XDP_DEVMAP, SEC_XDP_FRAGS), + SEC_DEF("xdp/devmap", XDP, BPF_XDP_DEVMAP, SEC_ATTACHABLE), + SEC_DEF("xdp_devmap/", XDP, BPF_XDP_DEVMAP, SEC_ATTACHABLE | SEC_DEPRECATED), + SEC_DEF("xdp.frags/cpumap", XDP, BPF_XDP_CPUMAP, SEC_XDP_FRAGS), + SEC_DEF("xdp/cpumap", XDP, BPF_XDP_CPUMAP, SEC_ATTACHABLE), + SEC_DEF("xdp_cpumap/", XDP, BPF_XDP_CPUMAP, SEC_ATTACHABLE | SEC_DEPRECATED), + SEC_DEF("xdp.frags", XDP, BPF_XDP, SEC_XDP_FRAGS), SEC_DEF("xdp", XDP, BPF_XDP, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX), SEC_DEF("perf_event", PERF_EVENT, 0, SEC_NONE | SEC_SLOPPY_PFX), SEC_DEF("lwt_in", LWT_IN, 0, SEC_NONE | SEC_SLOPPY_PFX), @@ -8643,61 +8717,167 @@ static const struct bpf_sec_def section_defs[] = { SEC_DEF("sk_lookup", SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE | SEC_SLOPPY_PFX), }; -#define MAX_TYPE_NAME_SIZE 32 +static size_t custom_sec_def_cnt; +static struct bpf_sec_def *custom_sec_defs; +static struct bpf_sec_def custom_fallback_def; +static bool has_custom_fallback_def; + +static int last_custom_sec_def_handler_id; + +int libbpf_register_prog_handler(const char *sec, + enum bpf_prog_type prog_type, + enum bpf_attach_type exp_attach_type, + const struct libbpf_prog_handler_opts *opts) +{ + struct bpf_sec_def *sec_def; + + if (!OPTS_VALID(opts, libbpf_prog_handler_opts)) + return libbpf_err(-EINVAL); + + if (last_custom_sec_def_handler_id == INT_MAX) /* prevent overflow */ + return libbpf_err(-E2BIG); + + if (sec) { + sec_def = libbpf_reallocarray(custom_sec_defs, custom_sec_def_cnt + 1, + sizeof(*sec_def)); + if (!sec_def) + return libbpf_err(-ENOMEM); + + custom_sec_defs = sec_def; + sec_def = &custom_sec_defs[custom_sec_def_cnt]; + } else { + if (has_custom_fallback_def) + return libbpf_err(-EBUSY); + + sec_def = &custom_fallback_def; + } + + sec_def->sec = sec ? strdup(sec) : NULL; + if (sec && !sec_def->sec) + return libbpf_err(-ENOMEM); + + sec_def->prog_type = prog_type; + sec_def->expected_attach_type = exp_attach_type; + sec_def->cookie = OPTS_GET(opts, cookie, 0); + + sec_def->prog_setup_fn = OPTS_GET(opts, prog_setup_fn, NULL); + sec_def->prog_prepare_load_fn = OPTS_GET(opts, prog_prepare_load_fn, NULL); + sec_def->prog_attach_fn = OPTS_GET(opts, prog_attach_fn, NULL); + + sec_def->handler_id = ++last_custom_sec_def_handler_id; + + if (sec) + custom_sec_def_cnt++; + else + has_custom_fallback_def = true; + + return sec_def->handler_id; +} + +int libbpf_unregister_prog_handler(int handler_id) +{ + struct bpf_sec_def *sec_defs; + int i; + + if (handler_id <= 0) + return libbpf_err(-EINVAL); + + if (has_custom_fallback_def && custom_fallback_def.handler_id == handler_id) { + memset(&custom_fallback_def, 0, sizeof(custom_fallback_def)); + has_custom_fallback_def = false; + return 0; + } + + for (i = 0; i < custom_sec_def_cnt; i++) { + if (custom_sec_defs[i].handler_id == handler_id) + break; + } + + if (i == custom_sec_def_cnt) + return libbpf_err(-ENOENT); + + free(custom_sec_defs[i].sec); + for (i = i + 1; i < custom_sec_def_cnt; i++) + custom_sec_defs[i - 1] = custom_sec_defs[i]; + custom_sec_def_cnt--; + + /* try to shrink the array, but it's ok if we couldn't */ + sec_defs = libbpf_reallocarray(custom_sec_defs, custom_sec_def_cnt, sizeof(*sec_defs)); + if (sec_defs) + custom_sec_defs = sec_defs; + + return 0; +} + +static bool sec_def_matches(const struct bpf_sec_def *sec_def, const char *sec_name, + bool allow_sloppy) +{ + size_t len = strlen(sec_def->sec); + + /* "type/" always has to have proper SEC("type/extras") form */ + if (sec_def->sec[len - 1] == '/') { + if (str_has_pfx(sec_name, sec_def->sec)) + return true; + return false; + } + + /* "type+" means it can be either exact SEC("type") or + * well-formed SEC("type/extras") with proper '/' separator + */ + if (sec_def->sec[len - 1] == '+') { + len--; + /* not even a prefix */ + if (strncmp(sec_name, sec_def->sec, len) != 0) + return false; + /* exact match or has '/' separator */ + if (sec_name[len] == '\0' || sec_name[len] == '/') + return true; + return false; + } + + /* SEC_SLOPPY_PFX definitions are allowed to be just prefix + * matches, unless strict section name mode + * (LIBBPF_STRICT_SEC_NAME) is enabled, in which case the + * match has to be exact. + */ + if (allow_sloppy && str_has_pfx(sec_name, sec_def->sec)) + return true; + + /* Definitions not marked SEC_SLOPPY_PFX (e.g., + * SEC("syscall")) are exact matches in both modes. + */ + return strcmp(sec_name, sec_def->sec) == 0; +} static const struct bpf_sec_def *find_sec_def(const char *sec_name) { const struct bpf_sec_def *sec_def; - enum sec_def_flags sec_flags; - int i, n = ARRAY_SIZE(section_defs), len; - bool strict = libbpf_mode & LIBBPF_STRICT_SEC_NAME; + int i, n; + bool strict = libbpf_mode & LIBBPF_STRICT_SEC_NAME, allow_sloppy; + n = custom_sec_def_cnt; for (i = 0; i < n; i++) { - sec_def = §ion_defs[i]; - sec_flags = sec_def->cookie; - len = strlen(sec_def->sec); - - /* "type/" always has to have proper SEC("type/extras") form */ - if (sec_def->sec[len - 1] == '/') { - if (str_has_pfx(sec_name, sec_def->sec)) - return sec_def; - continue; - } - - /* "type+" means it can be either exact SEC("type") or - * well-formed SEC("type/extras") with proper '/' separator - */ - if (sec_def->sec[len - 1] == '+') { - len--; - /* not even a prefix */ - if (strncmp(sec_name, sec_def->sec, len) != 0) - continue; - /* exact match or has '/' separator */ - if (sec_name[len] == '\0' || sec_name[len] == '/') - return sec_def; - continue; - } - - /* SEC_SLOPPY_PFX definitions are allowed to be just prefix - * matches, unless strict section name mode - * (LIBBPF_STRICT_SEC_NAME) is enabled, in which case the - * match has to be exact. - */ - if ((sec_flags & SEC_SLOPPY_PFX) && !strict) { - if (str_has_pfx(sec_name, sec_def->sec)) - return sec_def; - continue; - } - - /* Definitions not marked SEC_SLOPPY_PFX (e.g., - * SEC("syscall")) are exact matches in both modes. - */ - if (strcmp(sec_name, sec_def->sec) == 0) + sec_def = &custom_sec_defs[i]; + if (sec_def_matches(sec_def, sec_name, false)) return sec_def; } + + n = ARRAY_SIZE(section_defs); + for (i = 0; i < n; i++) { + sec_def = §ion_defs[i]; + allow_sloppy = (sec_def->cookie & SEC_SLOPPY_PFX) && !strict; + if (sec_def_matches(sec_def, sec_name, allow_sloppy)) + return sec_def; + } + + if (has_custom_fallback_def) + return &custom_fallback_def; + return NULL; } +#define MAX_TYPE_NAME_SIZE 32 + static char *libbpf_get_type_names(bool attach_type) { int i, len = ARRAY_SIZE(section_defs) * MAX_TYPE_NAME_SIZE; @@ -8713,7 +8893,7 @@ static char *libbpf_get_type_names(bool attach_type) const struct bpf_sec_def *sec_def = §ion_defs[i]; if (attach_type) { - if (sec_def->preload_fn != libbpf_preload_prog) + if (sec_def->prog_prepare_load_fn != libbpf_prepare_prog_load) continue; if (!(sec_def->cookie & SEC_ATTACHABLE)) @@ -9096,7 +9276,7 @@ int libbpf_attach_type_by_name(const char *name, return libbpf_err(-EINVAL); } - if (sec_def->preload_fn != libbpf_preload_prog) + if (sec_def->prog_prepare_load_fn != libbpf_prepare_prog_load) return libbpf_err(-EINVAL); if (!(sec_def->cookie & SEC_ATTACHABLE)) return libbpf_err(-EINVAL); @@ -9443,7 +9623,7 @@ static int bpf_prog_load_xattr2(const struct bpf_prog_load_attr *attr, open_attr.file = attr->file; open_attr.prog_type = attr->prog_type; - obj = bpf_object__open_xattr(&open_attr); + obj = __bpf_object__open_xattr(&open_attr, 0); err = libbpf_get_error(obj); if (err) return libbpf_err(-ENOENT); @@ -9460,7 +9640,7 @@ static int bpf_prog_load_xattr2(const struct bpf_prog_load_attr *attr, bpf_program__set_expected_attach_type(prog, attach_type); } - if (bpf_program__get_type(prog) == BPF_PROG_TYPE_UNSPEC) { + if (bpf_program__type(prog) == BPF_PROG_TYPE_UNSPEC) { /* * we haven't guessed from section name and user * didn't provide a fallback type, too bad... @@ -9477,7 +9657,7 @@ static int bpf_prog_load_xattr2(const struct bpf_prog_load_attr *attr, } bpf_object__for_each_map(map, obj) { - if (!bpf_map__is_offload_neutral(map)) + if (map->def.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) map->map_ifindex = attr->ifindex; } @@ -10070,14 +10250,146 @@ struct bpf_link *bpf_program__attach_kprobe(const struct bpf_program *prog, return bpf_program__attach_kprobe_opts(prog, func_name, &opts); } -static struct bpf_link *attach_kprobe(const struct bpf_program *prog, long cookie) +/* Adapted from perf/util/string.c */ +static bool glob_match(const char *str, const char *pat) +{ + while (*str && *pat && *pat != '*') { + if (*pat == '?') { /* Matches any single character */ + str++; + pat++; + continue; + } + if (*str != *pat) + return false; + str++; + pat++; + } + /* Check wild card */ + if (*pat == '*') { + while (*pat == '*') + pat++; + if (!*pat) /* Tail wild card matches all */ + return true; + while (*str) + if (glob_match(str++, pat)) + return true; + } + return !*str && !*pat; +} + +struct kprobe_multi_resolve { + const char *pattern; + unsigned long *addrs; + size_t cap; + size_t cnt; +}; + +static int +resolve_kprobe_multi_cb(unsigned long long sym_addr, char sym_type, + const char *sym_name, void *ctx) +{ + struct kprobe_multi_resolve *res = ctx; + int err; + + if (!glob_match(sym_name, res->pattern)) + return 0; + + err = libbpf_ensure_mem((void **) &res->addrs, &res->cap, sizeof(unsigned long), + res->cnt + 1); + if (err) + return err; + + res->addrs[res->cnt++] = (unsigned long) sym_addr; + return 0; +} + +struct bpf_link * +bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog, + const char *pattern, + const struct bpf_kprobe_multi_opts *opts) +{ + LIBBPF_OPTS(bpf_link_create_opts, lopts); + struct kprobe_multi_resolve res = { + .pattern = pattern, + }; + struct bpf_link *link = NULL; + char errmsg[STRERR_BUFSIZE]; + const unsigned long *addrs; + int err, link_fd, prog_fd; + const __u64 *cookies; + const char **syms; + bool retprobe; + size_t cnt; + + if (!OPTS_VALID(opts, bpf_kprobe_multi_opts)) + return libbpf_err_ptr(-EINVAL); + + syms = OPTS_GET(opts, syms, false); + addrs = OPTS_GET(opts, addrs, false); + cnt = OPTS_GET(opts, cnt, false); + cookies = OPTS_GET(opts, cookies, false); + + if (!pattern && !addrs && !syms) + return libbpf_err_ptr(-EINVAL); + if (pattern && (addrs || syms || cookies || cnt)) + return libbpf_err_ptr(-EINVAL); + if (!pattern && !cnt) + return libbpf_err_ptr(-EINVAL); + if (addrs && syms) + return libbpf_err_ptr(-EINVAL); + + if (pattern) { + err = libbpf_kallsyms_parse(resolve_kprobe_multi_cb, &res); + if (err) + goto error; + if (!res.cnt) { + err = -ENOENT; + goto error; + } + addrs = res.addrs; + cnt = res.cnt; + } + + retprobe = OPTS_GET(opts, retprobe, false); + + lopts.kprobe_multi.syms = syms; + lopts.kprobe_multi.addrs = addrs; + lopts.kprobe_multi.cookies = cookies; + lopts.kprobe_multi.cnt = cnt; + lopts.kprobe_multi.flags = retprobe ? BPF_F_KPROBE_MULTI_RETURN : 0; + + link = calloc(1, sizeof(*link)); + if (!link) { + err = -ENOMEM; + goto error; + } + link->detach = &bpf_link__detach_fd; + + prog_fd = bpf_program__fd(prog); + link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_KPROBE_MULTI, &lopts); + if (link_fd < 0) { + err = -errno; + pr_warn("prog '%s': failed to attach: %s\n", + prog->name, libbpf_strerror_r(err, errmsg, sizeof(errmsg))); + goto error; + } + link->fd = link_fd; + free(res.addrs); + return link; + +error: + free(link); + free(res.addrs); + return libbpf_err_ptr(err); +} + +static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link) { DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, opts); unsigned long offset = 0; - struct bpf_link *link; const char *func_name; char *func; - int n, err; + int n; opts.retprobe = str_has_pfx(prog->sec_name, "kretprobe/"); if (opts.retprobe) @@ -10087,21 +10399,43 @@ static struct bpf_link *attach_kprobe(const struct bpf_program *prog, long cooki n = sscanf(func_name, "%m[a-zA-Z0-9_.]+%li", &func, &offset); if (n < 1) { - err = -EINVAL; pr_warn("kprobe name is invalid: %s\n", func_name); - return libbpf_err_ptr(err); + return -EINVAL; } if (opts.retprobe && offset != 0) { free(func); - err = -EINVAL; pr_warn("kretprobes do not support offset specification\n"); - return libbpf_err_ptr(err); + return -EINVAL; } opts.offset = offset; - link = bpf_program__attach_kprobe_opts(prog, func, &opts); + *link = bpf_program__attach_kprobe_opts(prog, func, &opts); free(func); - return link; + return libbpf_get_error(*link); +} + +static int attach_kprobe_multi(const struct bpf_program *prog, long cookie, struct bpf_link **link) +{ + LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); + const char *spec; + char *pattern; + int n; + + opts.retprobe = str_has_pfx(prog->sec_name, "kretprobe.multi/"); + if (opts.retprobe) + spec = prog->sec_name + sizeof("kretprobe.multi/") - 1; + else + spec = prog->sec_name + sizeof("kprobe.multi/") - 1; + + n = sscanf(spec, "%m[a-zA-Z0-9_.*?]", &pattern); + if (n < 1) { + pr_warn("kprobe multi pattern is invalid: %s\n", pattern); + return -EINVAL; + } + + *link = bpf_program__attach_kprobe_multi_opts(prog, pattern, &opts); + free(pattern); + return libbpf_get_error(*link); } static void gen_uprobe_legacy_event_name(char *buf, size_t buf_sz, @@ -10356,14 +10690,13 @@ struct bpf_link *bpf_program__attach_tracepoint(const struct bpf_program *prog, return bpf_program__attach_tracepoint_opts(prog, tp_category, tp_name, NULL); } -static struct bpf_link *attach_tp(const struct bpf_program *prog, long cookie) +static int attach_tp(const struct bpf_program *prog, long cookie, struct bpf_link **link) { char *sec_name, *tp_cat, *tp_name; - struct bpf_link *link; sec_name = strdup(prog->sec_name); if (!sec_name) - return libbpf_err_ptr(-ENOMEM); + return -ENOMEM; /* extract "tp//" or "tracepoint//" */ if (str_has_pfx(prog->sec_name, "tp/")) @@ -10373,14 +10706,14 @@ static struct bpf_link *attach_tp(const struct bpf_program *prog, long cookie) tp_name = strchr(tp_cat, '/'); if (!tp_name) { free(sec_name); - return libbpf_err_ptr(-EINVAL); + return -EINVAL; } *tp_name = '\0'; tp_name++; - link = bpf_program__attach_tracepoint(prog, tp_cat, tp_name); + *link = bpf_program__attach_tracepoint(prog, tp_cat, tp_name); free(sec_name); - return link; + return libbpf_get_error(*link); } struct bpf_link *bpf_program__attach_raw_tracepoint(const struct bpf_program *prog, @@ -10413,7 +10746,7 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(const struct bpf_program *pr return link; } -static struct bpf_link *attach_raw_tp(const struct bpf_program *prog, long cookie) +static int attach_raw_tp(const struct bpf_program *prog, long cookie, struct bpf_link **link) { static const char *const prefixes[] = { "raw_tp/", @@ -10433,10 +10766,11 @@ static struct bpf_link *attach_raw_tp(const struct bpf_program *prog, long cooki if (!tp_name) { pr_warn("prog '%s': invalid section name '%s'\n", prog->name, prog->sec_name); - return libbpf_err_ptr(-EINVAL); + return -EINVAL; } - return bpf_program__attach_raw_tracepoint(prog, tp_name); + *link = bpf_program__attach_raw_tracepoint(prog, tp_name); + return libbpf_get_error(link); } /* Common logic for all BPF program types that attach to a btf_id */ @@ -10479,14 +10813,16 @@ struct bpf_link *bpf_program__attach_lsm(const struct bpf_program *prog) return bpf_program__attach_btf_id(prog); } -static struct bpf_link *attach_trace(const struct bpf_program *prog, long cookie) +static int attach_trace(const struct bpf_program *prog, long cookie, struct bpf_link **link) { - return bpf_program__attach_trace(prog); + *link = bpf_program__attach_trace(prog); + return libbpf_get_error(*link); } -static struct bpf_link *attach_lsm(const struct bpf_program *prog, long cookie) +static int attach_lsm(const struct bpf_program *prog, long cookie, struct bpf_link **link) { - return bpf_program__attach_lsm(prog); + *link = bpf_program__attach_lsm(prog); + return libbpf_get_error(*link); } static struct bpf_link * @@ -10511,7 +10847,7 @@ bpf_program__attach_fd(const struct bpf_program *prog, int target_fd, int btf_id return libbpf_err_ptr(-ENOMEM); link->detach = &bpf_link__detach_fd; - attach_type = bpf_program__get_expected_attach_type(prog); + attach_type = bpf_program__expected_attach_type(prog); link_fd = bpf_link_create(prog_fd, target_fd, attach_type, &opts); if (link_fd < 0) { link_fd = -errno; @@ -10615,17 +10951,33 @@ bpf_program__attach_iter(const struct bpf_program *prog, return link; } -static struct bpf_link *attach_iter(const struct bpf_program *prog, long cookie) +static int attach_iter(const struct bpf_program *prog, long cookie, struct bpf_link **link) { - return bpf_program__attach_iter(prog, NULL); + *link = bpf_program__attach_iter(prog, NULL); + return libbpf_get_error(*link); } struct bpf_link *bpf_program__attach(const struct bpf_program *prog) { - if (!prog->sec_def || !prog->sec_def->attach_fn) - return libbpf_err_ptr(-ESRCH); + struct bpf_link *link = NULL; + int err; - return prog->sec_def->attach_fn(prog, prog->sec_def->cookie); + if (!prog->sec_def || !prog->sec_def->prog_attach_fn) + return libbpf_err_ptr(-EOPNOTSUPP); + + err = prog->sec_def->prog_attach_fn(prog, prog->sec_def->cookie, &link); + if (err) + return libbpf_err_ptr(err); + + /* When calling bpf_program__attach() explicitly, auto-attach support + * is expected to work, so NULL returned link is considered an error. + * This is different for skeleton's attach, see comment in + * bpf_object__attach_skeleton(). + */ + if (!link) + return libbpf_err_ptr(-EOPNOTSUPP); + + return link; } static int bpf_link__detach_struct_ops(struct bpf_link *link) @@ -10912,7 +11264,7 @@ struct perf_buffer *perf_buffer__new_raw_v0_6_0(int map_fd, size_t page_cnt, { struct perf_buffer_params p = {}; - if (page_cnt == 0 || !attr) + if (!attr) return libbpf_err_ptr(-EINVAL); if (!OPTS_VALID(opts, perf_buffer_raw_opts)) @@ -10953,7 +11305,7 @@ static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt, __u32 map_info_len; int err, i, j, n; - if (page_cnt & (page_cnt - 1)) { + if (page_cnt == 0 || (page_cnt & (page_cnt - 1))) { pr_warn("page count should be power of two, but is %zu\n", page_cnt); return ERR_PTR(-EINVAL); @@ -11640,6 +11992,49 @@ int libbpf_num_possible_cpus(void) return tmp_cpus; } +static int populate_skeleton_maps(const struct bpf_object *obj, + struct bpf_map_skeleton *maps, + size_t map_cnt) +{ + int i; + + for (i = 0; i < map_cnt; i++) { + struct bpf_map **map = maps[i].map; + const char *name = maps[i].name; + void **mmaped = maps[i].mmaped; + + *map = bpf_object__find_map_by_name(obj, name); + if (!*map) { + pr_warn("failed to find skeleton map '%s'\n", name); + return -ESRCH; + } + + /* externs shouldn't be pre-setup from user code */ + if (mmaped && (*map)->libbpf_type != LIBBPF_MAP_KCONFIG) + *mmaped = (*map)->mmaped; + } + return 0; +} + +static int populate_skeleton_progs(const struct bpf_object *obj, + struct bpf_prog_skeleton *progs, + size_t prog_cnt) +{ + int i; + + for (i = 0; i < prog_cnt; i++) { + struct bpf_program **prog = progs[i].prog; + const char *name = progs[i].name; + + *prog = bpf_object__find_program_by_name(obj, name); + if (!*prog) { + pr_warn("failed to find skeleton program '%s'\n", name); + return -ESRCH; + } + } + return 0; +} + int bpf_object__open_skeleton(struct bpf_object_skeleton *s, const struct bpf_object_open_opts *opts) { @@ -11647,7 +12042,7 @@ int bpf_object__open_skeleton(struct bpf_object_skeleton *s, .object_name = s->name, ); struct bpf_object *obj; - int i, err; + int err; /* Attempt to preserve opts->object_name, unless overriden by user * explicitly. Overwriting object name for skeletons is discouraged, @@ -11670,37 +12065,91 @@ int bpf_object__open_skeleton(struct bpf_object_skeleton *s, } *s->obj = obj; - - for (i = 0; i < s->map_cnt; i++) { - struct bpf_map **map = s->maps[i].map; - const char *name = s->maps[i].name; - void **mmaped = s->maps[i].mmaped; - - *map = bpf_object__find_map_by_name(obj, name); - if (!*map) { - pr_warn("failed to find skeleton map '%s'\n", name); - return libbpf_err(-ESRCH); - } - - /* externs shouldn't be pre-setup from user code */ - if (mmaped && (*map)->libbpf_type != LIBBPF_MAP_KCONFIG) - *mmaped = (*map)->mmaped; + err = populate_skeleton_maps(obj, s->maps, s->map_cnt); + if (err) { + pr_warn("failed to populate skeleton maps for '%s': %d\n", s->name, err); + return libbpf_err(err); } - for (i = 0; i < s->prog_cnt; i++) { - struct bpf_program **prog = s->progs[i].prog; - const char *name = s->progs[i].name; - - *prog = bpf_object__find_program_by_name(obj, name); - if (!*prog) { - pr_warn("failed to find skeleton program '%s'\n", name); - return libbpf_err(-ESRCH); - } + err = populate_skeleton_progs(obj, s->progs, s->prog_cnt); + if (err) { + pr_warn("failed to populate skeleton progs for '%s': %d\n", s->name, err); + return libbpf_err(err); } return 0; } +int bpf_object__open_subskeleton(struct bpf_object_subskeleton *s) +{ + int err, len, var_idx, i; + const char *var_name; + const struct bpf_map *map; + struct btf *btf; + __u32 map_type_id; + const struct btf_type *map_type, *var_type; + const struct bpf_var_skeleton *var_skel; + struct btf_var_secinfo *var; + + if (!s->obj) + return libbpf_err(-EINVAL); + + btf = bpf_object__btf(s->obj); + if (!btf) { + pr_warn("subskeletons require BTF at runtime (object %s)\n", + bpf_object__name(s->obj)); + return libbpf_err(-errno); + } + + err = populate_skeleton_maps(s->obj, s->maps, s->map_cnt); + if (err) { + pr_warn("failed to populate subskeleton maps: %d\n", err); + return libbpf_err(err); + } + + err = populate_skeleton_progs(s->obj, s->progs, s->prog_cnt); + if (err) { + pr_warn("failed to populate subskeleton maps: %d\n", err); + return libbpf_err(err); + } + + for (var_idx = 0; var_idx < s->var_cnt; var_idx++) { + var_skel = &s->vars[var_idx]; + map = *var_skel->map; + map_type_id = bpf_map__btf_value_type_id(map); + map_type = btf__type_by_id(btf, map_type_id); + + if (!btf_is_datasec(map_type)) { + pr_warn("type for map '%1$s' is not a datasec: %2$s", + bpf_map__name(map), + __btf_kind_str(btf_kind(map_type))); + return libbpf_err(-EINVAL); + } + + len = btf_vlen(map_type); + var = btf_var_secinfos(map_type); + for (i = 0; i < len; i++, var++) { + var_type = btf__type_by_id(btf, var->type); + var_name = btf__name_by_offset(btf, var_type->name_off); + if (strcmp(var_name, var_skel->name) == 0) { + *var_skel->addr = map->mmaped + var->offset; + break; + } + } + } + return 0; +} + +void bpf_object__destroy_subskeleton(struct bpf_object_subskeleton *s) +{ + if (!s) + return; + free(s->maps); + free(s->progs); + free(s->vars); + free(s); +} + int bpf_object__load_skeleton(struct bpf_object_skeleton *s) { int i, err; @@ -11766,16 +12215,30 @@ int bpf_object__attach_skeleton(struct bpf_object_skeleton *s) continue; /* auto-attaching not supported for this program */ - if (!prog->sec_def || !prog->sec_def->attach_fn) + if (!prog->sec_def || !prog->sec_def->prog_attach_fn) continue; - *link = bpf_program__attach(prog); - err = libbpf_get_error(*link); + /* if user already set the link manually, don't attempt auto-attach */ + if (*link) + continue; + + err = prog->sec_def->prog_attach_fn(prog, prog->sec_def->cookie, link); if (err) { - pr_warn("failed to auto-attach program '%s': %d\n", + pr_warn("prog '%s': failed to auto-attach: %d\n", bpf_program__name(prog), err); return libbpf_err(err); } + + /* It's possible that for some SEC() definitions auto-attach + * is supported in some cases (e.g., if definition completely + * specifies target information), but is not in other cases. + * SEC("uprobe") is one such case. If user specified target + * binary and function name, such BPF program can be + * auto-attached. But if not, it shouldn't trigger skeleton's + * attach to fail. It should just be skipped. + * attach_fn signals such case with returning 0 (no error) and + * setting link to NULL. + */ } return 0; @@ -11795,6 +12258,9 @@ void bpf_object__detach_skeleton(struct bpf_object_skeleton *s) void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s) { + if (!s) + return; + if (s->progs) bpf_object__detach_skeleton(s); if (s->obj) diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 8b9bc5e90c2b..05dde85e19a6 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -180,9 +180,11 @@ bpf_object__open_mem(const void *obj_buf, size_t obj_buf_sz, const struct bpf_object_open_opts *opts); /* deprecated bpf_object__open variants */ +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_object__open_mem() instead") LIBBPF_API struct bpf_object * bpf_object__open_buffer(const void *obj_buf, size_t obj_buf_sz, const char *name); +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_object__open_file() instead") LIBBPF_API struct bpf_object * bpf_object__open_xattr(struct bpf_object_open_attr *attr); @@ -244,8 +246,10 @@ struct bpf_object *bpf_object__next(struct bpf_object *prev); (pos) = (tmp), (tmp) = bpf_object__next(tmp)) typedef void (*bpf_object_clear_priv_t)(struct bpf_object *, void *); +LIBBPF_DEPRECATED_SINCE(0, 7, "storage via set_priv/priv is deprecated") LIBBPF_API int bpf_object__set_priv(struct bpf_object *obj, void *priv, bpf_object_clear_priv_t clear_priv); +LIBBPF_DEPRECATED_SINCE(0, 7, "storage via set_priv/priv is deprecated") LIBBPF_API void *bpf_object__priv(const struct bpf_object *prog); LIBBPF_API int @@ -277,9 +281,10 @@ bpf_object__prev_program(const struct bpf_object *obj, struct bpf_program *prog) typedef void (*bpf_program_clear_priv_t)(struct bpf_program *, void *); +LIBBPF_DEPRECATED_SINCE(0, 7, "storage via set_priv/priv is deprecated") LIBBPF_API int bpf_program__set_priv(struct bpf_program *prog, void *priv, bpf_program_clear_priv_t clear_priv); - +LIBBPF_DEPRECATED_SINCE(0, 7, "storage via set_priv/priv is deprecated") LIBBPF_API void *bpf_program__priv(const struct bpf_program *prog); LIBBPF_API void bpf_program__set_ifindex(struct bpf_program *prog, __u32 ifindex); @@ -420,6 +425,29 @@ bpf_program__attach_kprobe_opts(const struct bpf_program *prog, const char *func_name, const struct bpf_kprobe_opts *opts); +struct bpf_kprobe_multi_opts { + /* size of this struct, for forward/backward compatibility */ + size_t sz; + /* array of function symbols to attach */ + const char **syms; + /* array of function addresses to attach */ + const unsigned long *addrs; + /* array of user-provided values fetchable through bpf_get_attach_cookie */ + const __u64 *cookies; + /* number of elements in syms/addrs/cookies arrays */ + size_t cnt; + /* create return kprobes */ + bool retprobe; + size_t :0; +}; + +#define bpf_kprobe_multi_opts__last_field retprobe + +LIBBPF_API struct bpf_link * +bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog, + const char *pattern, + const struct bpf_kprobe_multi_opts *opts); + struct bpf_uprobe_opts { /* size of this struct, for forward/backward compatiblity */ size_t sz; @@ -591,26 +619,39 @@ LIBBPF_API int bpf_program__nth_fd(const struct bpf_program *prog, int n); /* * Adjust type of BPF program. Default is kprobe. */ +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_socket_filter(struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_tracepoint(struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_raw_tracepoint(struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_kprobe(struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_lsm(struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_sched_cls(struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_sched_act(struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_xdp(struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_perf_event(struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_tracing(struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_struct_ops(struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_extension(struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__set_type() instead") LIBBPF_API int bpf_program__set_sk_lookup(struct bpf_program *prog); -LIBBPF_API enum bpf_prog_type bpf_program__get_type(const struct bpf_program *prog); +LIBBPF_API enum bpf_prog_type bpf_program__type(const struct bpf_program *prog); LIBBPF_API void bpf_program__set_type(struct bpf_program *prog, enum bpf_prog_type type); LIBBPF_API enum bpf_attach_type -bpf_program__get_expected_attach_type(const struct bpf_program *prog); +bpf_program__expected_attach_type(const struct bpf_program *prog); LIBBPF_API void bpf_program__set_expected_attach_type(struct bpf_program *prog, enum bpf_attach_type type); @@ -631,18 +672,31 @@ LIBBPF_API int bpf_program__set_attach_target(struct bpf_program *prog, int attach_prog_fd, const char *attach_func_name); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_socket_filter(const struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_tracepoint(const struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_raw_tracepoint(const struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_kprobe(const struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_lsm(const struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_sched_cls(const struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_sched_act(const struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_perf_event(const struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_tracing(const struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_struct_ops(const struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_extension(const struct bpf_program *prog); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_program__type() instead") LIBBPF_API bool bpf_program__is_sk_lookup(const struct bpf_program *prog); /* @@ -706,7 +760,8 @@ bpf_object__prev_map(const struct bpf_object *obj, const struct bpf_map *map); LIBBPF_API int bpf_map__fd(const struct bpf_map *map); LIBBPF_API int bpf_map__reuse_fd(struct bpf_map *map, int fd); /* get map definition */ -LIBBPF_API const struct bpf_map_def *bpf_map__def(const struct bpf_map *map); +LIBBPF_API LIBBPF_DEPRECATED_SINCE(0, 8, "use appropriate getters or setters instead") +const struct bpf_map_def *bpf_map__def(const struct bpf_map *map); /* get map name */ LIBBPF_API const char *bpf_map__name(const struct bpf_map *map); /* get/set map type */ @@ -715,6 +770,7 @@ LIBBPF_API int bpf_map__set_type(struct bpf_map *map, enum bpf_map_type type); /* get/set map size (max_entries) */ LIBBPF_API __u32 bpf_map__max_entries(const struct bpf_map *map); LIBBPF_API int bpf_map__set_max_entries(struct bpf_map *map, __u32 max_entries); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_map__set_max_entries() instead") LIBBPF_API int bpf_map__resize(struct bpf_map *map, __u32 max_entries); /* get/set map flags */ LIBBPF_API __u32 bpf_map__map_flags(const struct bpf_map *map); @@ -739,8 +795,10 @@ LIBBPF_API __u64 bpf_map__map_extra(const struct bpf_map *map); LIBBPF_API int bpf_map__set_map_extra(struct bpf_map *map, __u64 map_extra); typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *); +LIBBPF_DEPRECATED_SINCE(0, 7, "storage via set_priv/priv is deprecated") LIBBPF_API int bpf_map__set_priv(struct bpf_map *map, void *priv, bpf_map_clear_priv_t clear_priv); +LIBBPF_DEPRECATED_SINCE(0, 7, "storage via set_priv/priv is deprecated") LIBBPF_API void *bpf_map__priv(const struct bpf_map *map); LIBBPF_API int bpf_map__set_initial_value(struct bpf_map *map, const void *data, size_t size); @@ -757,7 +815,6 @@ LIBBPF_API bool bpf_map__is_offload_neutral(const struct bpf_map *map); */ LIBBPF_API bool bpf_map__is_internal(const struct bpf_map *map); LIBBPF_API int bpf_map__set_pin_path(struct bpf_map *map, const char *path); -LIBBPF_API const char *bpf_map__get_pin_path(const struct bpf_map *map); LIBBPF_API const char *bpf_map__pin_path(const struct bpf_map *map); LIBBPF_API bool bpf_map__is_pinned(const struct bpf_map *map); LIBBPF_API int bpf_map__pin(struct bpf_map *map, const char *path); @@ -832,13 +889,42 @@ struct bpf_xdp_set_link_opts { }; #define bpf_xdp_set_link_opts__last_field old_fd +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_xdp_attach() instead") LIBBPF_API int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_xdp_attach() instead") LIBBPF_API int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags, const struct bpf_xdp_set_link_opts *opts); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_xdp_query_id() instead") LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags); +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_xdp_query() instead") LIBBPF_API int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, size_t info_size, __u32 flags); +struct bpf_xdp_attach_opts { + size_t sz; + int old_prog_fd; + size_t :0; +}; +#define bpf_xdp_attach_opts__last_field old_prog_fd + +struct bpf_xdp_query_opts { + size_t sz; + __u32 prog_id; /* output */ + __u32 drv_prog_id; /* output */ + __u32 hw_prog_id; /* output */ + __u32 skb_prog_id; /* output */ + __u8 attach_mode; /* output */ + size_t :0; +}; +#define bpf_xdp_query_opts__last_field attach_mode + +LIBBPF_API int bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags, + const struct bpf_xdp_attach_opts *opts); +LIBBPF_API int bpf_xdp_detach(int ifindex, __u32 flags, + const struct bpf_xdp_attach_opts *opts); +LIBBPF_API int bpf_xdp_query(int ifindex, int flags, struct bpf_xdp_query_opts *opts); +LIBBPF_API int bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id); + /* TC related API */ enum bpf_tc_attach_point { BPF_TC_INGRESS = 1 << 0, @@ -1226,6 +1312,35 @@ LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s); LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s); LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s); +struct bpf_var_skeleton { + const char *name; + struct bpf_map **map; + void **addr; +}; + +struct bpf_object_subskeleton { + size_t sz; /* size of this struct, for forward/backward compatibility */ + + const struct bpf_object *obj; + + int map_cnt; + int map_skel_sz; /* sizeof(struct bpf_map_skeleton) */ + struct bpf_map_skeleton *maps; + + int prog_cnt; + int prog_skel_sz; /* sizeof(struct bpf_prog_skeleton) */ + struct bpf_prog_skeleton *progs; + + int var_cnt; + int var_skel_sz; /* sizeof(struct bpf_var_skeleton) */ + struct bpf_var_skeleton *vars; +}; + +LIBBPF_API int +bpf_object__open_subskeleton(struct bpf_object_subskeleton *s); +LIBBPF_API void +bpf_object__destroy_subskeleton(struct bpf_object_subskeleton *s); + struct gen_loader_opts { size_t sz; /* size of this struct, for forward/backward compatiblity */ const char *data; @@ -1265,6 +1380,115 @@ LIBBPF_API int bpf_linker__add_file(struct bpf_linker *linker, LIBBPF_API int bpf_linker__finalize(struct bpf_linker *linker); LIBBPF_API void bpf_linker__free(struct bpf_linker *linker); +/* + * Custom handling of BPF program's SEC() definitions + */ + +struct bpf_prog_load_opts; /* defined in bpf.h */ + +/* Called during bpf_object__open() for each recognized BPF program. Callback + * can use various bpf_program__set_*() setters to adjust whatever properties + * are necessary. + */ +typedef int (*libbpf_prog_setup_fn_t)(struct bpf_program *prog, long cookie); + +/* Called right before libbpf performs bpf_prog_load() to load BPF program + * into the kernel. Callback can adjust opts as necessary. + */ +typedef int (*libbpf_prog_prepare_load_fn_t)(struct bpf_program *prog, + struct bpf_prog_load_opts *opts, long cookie); + +/* Called during skeleton attach or through bpf_program__attach(). If + * auto-attach is not supported, callback should return 0 and set link to + * NULL (it's not considered an error during skeleton attach, but it will be + * an error for bpf_program__attach() calls). On error, error should be + * returned directly and link set to NULL. On success, return 0 and set link + * to a valid struct bpf_link. + */ +typedef int (*libbpf_prog_attach_fn_t)(const struct bpf_program *prog, long cookie, + struct bpf_link **link); + +struct libbpf_prog_handler_opts { + /* size of this struct, for forward/backward compatiblity */ + size_t sz; + /* User-provided value that is passed to prog_setup_fn, + * prog_prepare_load_fn, and prog_attach_fn callbacks. Allows user to + * register one set of callbacks for multiple SEC() definitions and + * still be able to distinguish them, if necessary. For example, + * libbpf itself is using this to pass necessary flags (e.g., + * sleepable flag) to a common internal SEC() handler. + */ + long cookie; + /* BPF program initialization callback (see libbpf_prog_setup_fn_t). + * Callback is optional, pass NULL if it's not necessary. + */ + libbpf_prog_setup_fn_t prog_setup_fn; + /* BPF program loading callback (see libbpf_prog_prepare_load_fn_t). + * Callback is optional, pass NULL if it's not necessary. + */ + libbpf_prog_prepare_load_fn_t prog_prepare_load_fn; + /* BPF program attach callback (see libbpf_prog_attach_fn_t). + * Callback is optional, pass NULL if it's not necessary. + */ + libbpf_prog_attach_fn_t prog_attach_fn; +}; +#define libbpf_prog_handler_opts__last_field prog_attach_fn + +/** + * @brief **libbpf_register_prog_handler()** registers a custom BPF program + * SEC() handler. + * @param sec section prefix for which custom handler is registered + * @param prog_type BPF program type associated with specified section + * @param exp_attach_type Expected BPF attach type associated with specified section + * @param opts optional cookie, callbacks, and other extra options + * @return Non-negative handler ID is returned on success. This handler ID has + * to be passed to *libbpf_unregister_prog_handler()* to unregister such + * custom handler. Negative error code is returned on error. + * + * *sec* defines which SEC() definitions are handled by this custom handler + * registration. *sec* can have few different forms: + * - if *sec* is just a plain string (e.g., "abc"), it will match only + * SEC("abc"). If BPF program specifies SEC("abc/whatever") it will result + * in an error; + * - if *sec* is of the form "abc/", proper SEC() form is + * SEC("abc/something"), where acceptable "something" should be checked by + * *prog_init_fn* callback, if there are additional restrictions; + * - if *sec* is of the form "abc+", it will successfully match both + * SEC("abc") and SEC("abc/whatever") forms; + * - if *sec* is NULL, custom handler is registered for any BPF program that + * doesn't match any of the registered (custom or libbpf's own) SEC() + * handlers. There could be only one such generic custom handler registered + * at any given time. + * + * All custom handlers (except the one with *sec* == NULL) are processed + * before libbpf's own SEC() handlers. It is allowed to "override" libbpf's + * SEC() handlers by registering custom ones for the same section prefix + * (i.e., it's possible to have custom SEC("perf_event/LLC-load-misses") + * handler). + * + * Note, like much of global libbpf APIs (e.g., libbpf_set_print(), + * libbpf_set_strict_mode(), etc)) these APIs are not thread-safe. User needs + * to ensure synchronization if there is a risk of running this API from + * multiple threads simultaneously. + */ +LIBBPF_API int libbpf_register_prog_handler(const char *sec, + enum bpf_prog_type prog_type, + enum bpf_attach_type exp_attach_type, + const struct libbpf_prog_handler_opts *opts); +/** + * @brief *libbpf_unregister_prog_handler()* unregisters previously registered + * custom BPF program SEC() handler. + * @param handler_id handler ID returned by *libbpf_register_prog_handler()* + * after successful registration + * @return 0 on success, negative error code if handler isn't found + * + * Note, like much of global libbpf APIs (e.g., libbpf_set_print(), + * libbpf_set_strict_mode(), etc)) these APIs are not thread-safe. User needs + * to ensure synchronization if there is a risk of running this API from + * multiple threads simultaneously. + */ +LIBBPF_API int libbpf_unregister_prog_handler(int handler_id); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 529783967793..dd35ee58bfaa 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -247,6 +247,7 @@ LIBBPF_0.0.8 { bpf_link_create; bpf_link_update; bpf_map__set_initial_value; + bpf_prog_attach_opts; bpf_program__attach_cgroup; bpf_program__attach_lsm; bpf_program__is_lsm; @@ -423,12 +424,27 @@ LIBBPF_0.6.0 { LIBBPF_0.7.0 { global: bpf_btf_load; + bpf_program__expected_attach_type; bpf_program__log_buf; bpf_program__log_level; bpf_program__set_log_buf; bpf_program__set_log_level; + bpf_program__type; + bpf_xdp_attach; + bpf_xdp_detach; + bpf_xdp_query; + bpf_xdp_query_id; libbpf_probe_bpf_helper; libbpf_probe_bpf_map_type; libbpf_probe_bpf_prog_type; libbpf_set_memlock_rlim_max; -}; +} LIBBPF_0.6.0; + +LIBBPF_0.8.0 { + global: + bpf_object__destroy_subskeleton; + bpf_object__open_subskeleton; + libbpf_register_prog_handler; + libbpf_unregister_prog_handler; + bpf_program__attach_kprobe_multi_opts; +} LIBBPF_0.7.0; diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h index 1565679eb432..b6247dc7f8eb 100644 --- a/tools/lib/bpf/libbpf_internal.h +++ b/tools/lib/bpf/libbpf_internal.h @@ -92,6 +92,9 @@ # define offsetofend(TYPE, FIELD) \ (offsetof(TYPE, FIELD) + sizeof(((TYPE *)0)->FIELD)) #endif +#ifndef __alias +#define __alias(symbol) __attribute__((alias(#symbol))) +#endif /* Check whether a string `str` has prefix `pfx`, regardless if `pfx` is * a string literal known at compilation time or char * pointer known only at @@ -446,6 +449,11 @@ __s32 btf__find_by_name_kind_own(const struct btf *btf, const char *type_name, extern enum libbpf_strict_mode libbpf_mode; +typedef int (*kallsyms_cb_t)(unsigned long long sym_addr, char sym_type, + const char *sym_name, void *ctx); + +int libbpf_kallsyms_parse(kallsyms_cb_t cb, void *arg); + /* handle direct returned errors */ static inline int libbpf_err(int ret) { @@ -526,4 +534,13 @@ static inline int ensure_good_fd(int fd) return fd; } +/* The following two functions are exposed to bpftool */ +int bpf_core_add_cands(struct bpf_core_cand *local_cand, + size_t local_essent_len, + const struct btf *targ_btf, + const char *targ_btf_name, + int targ_start_id, + struct bpf_core_cand_list *cands); +void bpf_core_free_cands(struct bpf_core_cand_list *cands); + #endif /* __LIBBPF_LIBBPF_INTERNAL_H */ diff --git a/tools/lib/bpf/libbpf_legacy.h b/tools/lib/bpf/libbpf_legacy.h index 79131f761a27..d7bcbd01f66f 100644 --- a/tools/lib/bpf/libbpf_legacy.h +++ b/tools/lib/bpf/libbpf_legacy.h @@ -54,6 +54,10 @@ enum libbpf_strict_mode { * * Note, in this mode the program pin path will be based on the * function name instead of section name. + * + * Additionally, routines in the .text section are always considered + * sub-programs. Legacy behavior allows for a single routine in .text + * to be a program. */ LIBBPF_STRICT_SEC_NAME = 0x04, /* @@ -73,6 +77,11 @@ enum libbpf_strict_mode { * operation. */ LIBBPF_STRICT_AUTO_RLIMIT_MEMLOCK = 0x10, + /* + * Error out on any SEC("maps") map definition, which are deprecated + * in favor of BTF-defined map definitions in SEC(".maps"). + */ + LIBBPF_STRICT_MAP_DEFINITIONS = 0x20, __LIBBPF_STRICT_LAST, }; @@ -81,6 +90,23 @@ LIBBPF_API int libbpf_set_strict_mode(enum libbpf_strict_mode mode); #define DECLARE_LIBBPF_OPTS LIBBPF_OPTS +/* "Discouraged" APIs which don't follow consistent libbpf naming patterns. + * They are normally a trivial aliases or wrappers for proper APIs and are + * left to minimize unnecessary disruption for users of libbpf. But they + * shouldn't be used going forward. + */ + +struct bpf_program; +struct bpf_map; +struct btf; +struct btf_ext; + +LIBBPF_API enum bpf_prog_type bpf_program__get_type(const struct bpf_program *prog); +LIBBPF_API enum bpf_attach_type bpf_program__get_expected_attach_type(const struct bpf_program *prog); +LIBBPF_API const char *bpf_map__get_pin_path(const struct bpf_map *map); +LIBBPF_API const void *btf__get_raw_data(const struct btf *btf, __u32 *size); +LIBBPF_API const void *btf_ext__get_raw_data(const struct btf_ext *btf_ext, __u32 *size); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/tools/lib/bpf/libbpf_version.h b/tools/lib/bpf/libbpf_version.h index 0fefefc3500b..61f2039404b6 100644 --- a/tools/lib/bpf/libbpf_version.h +++ b/tools/lib/bpf/libbpf_version.h @@ -4,6 +4,6 @@ #define __LIBBPF_VERSION_H #define LIBBPF_MAJOR_VERSION 0 -#define LIBBPF_MINOR_VERSION 7 +#define LIBBPF_MINOR_VERSION 8 #endif /* __LIBBPF_VERSION_H */ diff --git a/tools/lib/bpf/netlink.c b/tools/lib/bpf/netlink.c index 39f25e09b51e..cbc8967d5402 100644 --- a/tools/lib/bpf/netlink.c +++ b/tools/lib/bpf/netlink.c @@ -87,29 +87,75 @@ enum { NL_DONE, }; +static int netlink_recvmsg(int sock, struct msghdr *mhdr, int flags) +{ + int len; + + do { + len = recvmsg(sock, mhdr, flags); + } while (len < 0 && (errno == EINTR || errno == EAGAIN)); + + if (len < 0) + return -errno; + return len; +} + +static int alloc_iov(struct iovec *iov, int len) +{ + void *nbuf; + + nbuf = realloc(iov->iov_base, len); + if (!nbuf) + return -ENOMEM; + + iov->iov_base = nbuf; + iov->iov_len = len; + return 0; +} + static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq, __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn, void *cookie) { + struct iovec iov = {}; + struct msghdr mhdr = { + .msg_iov = &iov, + .msg_iovlen = 1, + }; bool multipart = true; struct nlmsgerr *err; struct nlmsghdr *nh; - char buf[4096]; int len, ret; + ret = alloc_iov(&iov, 4096); + if (ret) + goto done; + while (multipart) { start: multipart = false; - len = recv(sock, buf, sizeof(buf), 0); + len = netlink_recvmsg(sock, &mhdr, MSG_PEEK | MSG_TRUNC); if (len < 0) { - ret = -errno; + ret = len; + goto done; + } + + if (len > iov.iov_len) { + ret = alloc_iov(&iov, len); + if (ret) + goto done; + } + + len = netlink_recvmsg(sock, &mhdr, 0); + if (len < 0) { + ret = len; goto done; } if (len == 0) break; - for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); + for (nh = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { if (nh->nlmsg_pid != nl_pid) { ret = -LIBBPF_ERRNO__WRNGPID; @@ -130,7 +176,8 @@ static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq, libbpf_nla_dump_errormsg(nh); goto done; case NLMSG_DONE: - return 0; + ret = 0; + goto done; default: break; } @@ -142,15 +189,17 @@ static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq, case NL_NEXT: goto start; case NL_DONE: - return 0; + ret = 0; + goto done; default: - return ret; + goto done; } } } } ret = 0; done: + free(iov.iov_base); return ret; } @@ -217,6 +266,28 @@ static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd, return libbpf_netlink_send_recv(&req, NULL, NULL, NULL); } +int bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags, const struct bpf_xdp_attach_opts *opts) +{ + int old_prog_fd, err; + + if (!OPTS_VALID(opts, bpf_xdp_attach_opts)) + return libbpf_err(-EINVAL); + + old_prog_fd = OPTS_GET(opts, old_prog_fd, 0); + if (old_prog_fd) + flags |= XDP_FLAGS_REPLACE; + else + old_prog_fd = -1; + + err = __bpf_set_link_xdp_fd_replace(ifindex, prog_fd, old_prog_fd, flags); + return libbpf_err(err); +} + +int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *opts) +{ + return bpf_xdp_attach(ifindex, -1, flags, opts); +} + int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags, const struct bpf_xdp_set_link_opts *opts) { @@ -303,69 +374,98 @@ static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb) return 0; } -int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, - size_t info_size, __u32 flags) +int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts) { - struct xdp_id_md xdp_id = {}; - __u32 mask; - int ret; struct libbpf_nla_req req = { .nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), .nh.nlmsg_type = RTM_GETLINK, .nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, .ifinfo.ifi_family = AF_PACKET, }; + struct xdp_id_md xdp_id = {}; + int err; - if (flags & ~XDP_FLAGS_MASK || !info_size) + if (!OPTS_VALID(opts, bpf_xdp_query_opts)) + return libbpf_err(-EINVAL); + + if (xdp_flags & ~XDP_FLAGS_MASK) return libbpf_err(-EINVAL); /* Check whether the single {HW,DRV,SKB} mode is set */ - flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE); - mask = flags - 1; - if (flags && flags & mask) + xdp_flags &= XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE; + if (xdp_flags & (xdp_flags - 1)) return libbpf_err(-EINVAL); xdp_id.ifindex = ifindex; - xdp_id.flags = flags; + xdp_id.flags = xdp_flags; - ret = libbpf_netlink_send_recv(&req, __dump_link_nlmsg, + err = libbpf_netlink_send_recv(&req, __dump_link_nlmsg, get_xdp_info, &xdp_id); - if (!ret) { - size_t sz = min(info_size, sizeof(xdp_id.info)); + if (err) + return libbpf_err(err); - memcpy(info, &xdp_id.info, sz); - memset((void *) info + sz, 0, info_size - sz); - } - - return libbpf_err(ret); -} - -static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags) -{ - flags &= XDP_FLAGS_MODES; - - if (info->attach_mode != XDP_ATTACHED_MULTI && !flags) - return info->prog_id; - if (flags & XDP_FLAGS_DRV_MODE) - return info->drv_prog_id; - if (flags & XDP_FLAGS_HW_MODE) - return info->hw_prog_id; - if (flags & XDP_FLAGS_SKB_MODE) - return info->skb_prog_id; + OPTS_SET(opts, prog_id, xdp_id.info.prog_id); + OPTS_SET(opts, drv_prog_id, xdp_id.info.drv_prog_id); + OPTS_SET(opts, hw_prog_id, xdp_id.info.hw_prog_id); + OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id); + OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode); return 0; } -int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags) +int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, + size_t info_size, __u32 flags) { - struct xdp_link_info info; + LIBBPF_OPTS(bpf_xdp_query_opts, opts); + size_t sz; + int err; + + if (!info_size) + return libbpf_err(-EINVAL); + + err = bpf_xdp_query(ifindex, flags, &opts); + if (err) + return libbpf_err(err); + + /* struct xdp_link_info field layout matches struct bpf_xdp_query_opts + * layout after sz field + */ + sz = min(info_size, offsetofend(struct xdp_link_info, attach_mode)); + memcpy(info, &opts.prog_id, sz); + memset((void *)info + sz, 0, info_size - sz); + + return 0; +} + +int bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id) +{ + LIBBPF_OPTS(bpf_xdp_query_opts, opts); int ret; - ret = bpf_get_link_xdp_info(ifindex, &info, sizeof(info), flags); - if (!ret) - *prog_id = get_xdp_id(&info, flags); + ret = bpf_xdp_query(ifindex, flags, &opts); + if (ret) + return libbpf_err(ret); - return libbpf_err(ret); + flags &= XDP_FLAGS_MODES; + + if (opts.attach_mode != XDP_ATTACHED_MULTI && !flags) + *prog_id = opts.prog_id; + else if (flags & XDP_FLAGS_DRV_MODE) + *prog_id = opts.drv_prog_id; + else if (flags & XDP_FLAGS_HW_MODE) + *prog_id = opts.hw_prog_id; + else if (flags & XDP_FLAGS_SKB_MODE) + *prog_id = opts.skb_prog_id; + else + *prog_id = 0; + + return 0; +} + + +int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags) +{ + return bpf_xdp_query_id(ifindex, flags, prog_id); } typedef int (*qdisc_config_t)(struct libbpf_nla_req *req); diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c index 910865e29edc..f946f23eab20 100644 --- a/tools/lib/bpf/relo_core.c +++ b/tools/lib/bpf/relo_core.c @@ -775,31 +775,6 @@ static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo, return 0; } -struct bpf_core_relo_res -{ - /* expected value in the instruction, unless validate == false */ - __u32 orig_val; - /* new value that needs to be patched up to */ - __u32 new_val; - /* relocation unsuccessful, poison instruction, but don't fail load */ - bool poison; - /* some relocations can't be validated against orig_val */ - bool validate; - /* for field byte offset relocations or the forms: - * *(T *)(rX + ) = rY - * rX = *(T *)(rY + ), - * we remember original and resolved field size to adjust direct - * memory loads of pointers and integers; this is necessary for 32-bit - * host kernel architectures, but also allows to automatically - * relocate fields that were resized from, e.g., u32 to u64, etc. - */ - bool fail_memsz_adjust; - __u32 orig_sz; - __u32 orig_type_id; - __u32 new_sz; - __u32 new_type_id; -}; - /* Calculate original and target relocation values, given local and target * specs and relocation kind. These values are calculated for each candidate. * If there are multiple candidates, resulting values should all be consistent @@ -951,9 +926,9 @@ static int insn_bytes_to_bpf_size(__u32 sz) * 5. *(T *)(rX + ) = rY, where T is one of {u8, u16, u32, u64}; * 6. *(T *)(rX + ) = , where T is one of {u8, u16, u32, u64}. */ -static int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn, - int insn_idx, const struct bpf_core_relo *relo, - int relo_idx, const struct bpf_core_relo_res *res) +int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn, + int insn_idx, const struct bpf_core_relo *relo, + int relo_idx, const struct bpf_core_relo_res *res) { __u32 orig_val, new_val; __u8 class; @@ -1128,7 +1103,7 @@ static void bpf_core_dump_spec(const char *prog_name, int level, const struct bp } /* - * CO-RE relocate single instruction. + * Calculate CO-RE relocation target result. * * The outline and important points of the algorithm: * 1. For given local type, find corresponding candidate target types. @@ -1177,18 +1152,18 @@ static void bpf_core_dump_spec(const char *prog_name, int level, const struct bp * between multiple relocations for the same type ID and is updated as some * of the candidates are pruned due to structural incompatibility. */ -int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn, - int insn_idx, - const struct bpf_core_relo *relo, - int relo_idx, - const struct btf *local_btf, - struct bpf_core_cand_list *cands, - struct bpf_core_spec *specs_scratch) +int bpf_core_calc_relo_insn(const char *prog_name, + const struct bpf_core_relo *relo, + int relo_idx, + const struct btf *local_btf, + struct bpf_core_cand_list *cands, + struct bpf_core_spec *specs_scratch, + struct bpf_core_relo_res *targ_res) { struct bpf_core_spec *local_spec = &specs_scratch[0]; struct bpf_core_spec *cand_spec = &specs_scratch[1]; struct bpf_core_spec *targ_spec = &specs_scratch[2]; - struct bpf_core_relo_res cand_res, targ_res; + struct bpf_core_relo_res cand_res; const struct btf_type *local_type; const char *local_name; __u32 local_id; @@ -1223,12 +1198,12 @@ int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn, /* TYPE_ID_LOCAL relo is special and doesn't need candidate search */ if (relo->kind == BPF_CORE_TYPE_ID_LOCAL) { /* bpf_insn's imm value could get out of sync during linking */ - memset(&targ_res, 0, sizeof(targ_res)); - targ_res.validate = false; - targ_res.poison = false; - targ_res.orig_val = local_spec->root_type_id; - targ_res.new_val = local_spec->root_type_id; - goto patch_insn; + memset(targ_res, 0, sizeof(*targ_res)); + targ_res->validate = false; + targ_res->poison = false; + targ_res->orig_val = local_spec->root_type_id; + targ_res->new_val = local_spec->root_type_id; + return 0; } /* libbpf doesn't support candidate search for anonymous types */ @@ -1262,7 +1237,7 @@ int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn, return err; if (j == 0) { - targ_res = cand_res; + *targ_res = cand_res; *targ_spec = *cand_spec; } else if (cand_spec->bit_offset != targ_spec->bit_offset) { /* if there are many field relo candidates, they @@ -1272,7 +1247,8 @@ int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn, prog_name, relo_idx, cand_spec->bit_offset, targ_spec->bit_offset); return -EINVAL; - } else if (cand_res.poison != targ_res.poison || cand_res.new_val != targ_res.new_val) { + } else if (cand_res.poison != targ_res->poison || + cand_res.new_val != targ_res->new_val) { /* all candidates should result in the same relocation * decision and value, otherwise it's dangerous to * proceed due to ambiguity @@ -1280,7 +1256,7 @@ int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn, pr_warn("prog '%s': relo #%d: relocation decision ambiguity: %s %u != %s %u\n", prog_name, relo_idx, cand_res.poison ? "failure" : "success", cand_res.new_val, - targ_res.poison ? "failure" : "success", targ_res.new_val); + targ_res->poison ? "failure" : "success", targ_res->new_val); return -EINVAL; } @@ -1314,19 +1290,10 @@ int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn, prog_name, relo_idx); /* calculate single target relo result explicitly */ - err = bpf_core_calc_relo(prog_name, relo, relo_idx, local_spec, NULL, &targ_res); + err = bpf_core_calc_relo(prog_name, relo, relo_idx, local_spec, NULL, targ_res); if (err) return err; } -patch_insn: - /* bpf_core_patch_insn() should know how to handle missing targ_spec */ - err = bpf_core_patch_insn(prog_name, insn, insn_idx, relo, relo_idx, &targ_res); - if (err) { - pr_warn("prog '%s': relo #%d: failed to patch insn #%u: %d\n", - prog_name, relo_idx, relo->insn_off / 8, err); - return -EINVAL; - } - return 0; } diff --git a/tools/lib/bpf/relo_core.h b/tools/lib/bpf/relo_core.h index 17799819ad7c..a28bf3711ce2 100644 --- a/tools/lib/bpf/relo_core.h +++ b/tools/lib/bpf/relo_core.h @@ -44,14 +44,44 @@ struct bpf_core_spec { __u32 bit_offset; }; -int bpf_core_apply_relo_insn(const char *prog_name, - struct bpf_insn *insn, int insn_idx, - const struct bpf_core_relo *relo, int relo_idx, - const struct btf *local_btf, - struct bpf_core_cand_list *cands, - struct bpf_core_spec *specs_scratch); +struct bpf_core_relo_res { + /* expected value in the instruction, unless validate == false */ + __u32 orig_val; + /* new value that needs to be patched up to */ + __u32 new_val; + /* relocation unsuccessful, poison instruction, but don't fail load */ + bool poison; + /* some relocations can't be validated against orig_val */ + bool validate; + /* for field byte offset relocations or the forms: + * *(T *)(rX + ) = rY + * rX = *(T *)(rY + ), + * we remember original and resolved field size to adjust direct + * memory loads of pointers and integers; this is necessary for 32-bit + * host kernel architectures, but also allows to automatically + * relocate fields that were resized from, e.g., u32 to u64, etc. + */ + bool fail_memsz_adjust; + __u32 orig_sz; + __u32 orig_type_id; + __u32 new_sz; + __u32 new_type_id; +}; + int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id, const struct btf *targ_btf, __u32 targ_id); size_t bpf_core_essential_name_len(const char *name); + +int bpf_core_calc_relo_insn(const char *prog_name, + const struct bpf_core_relo *relo, int relo_idx, + const struct btf *local_btf, + struct bpf_core_cand_list *cands, + struct bpf_core_spec *specs_scratch, + struct bpf_core_relo_res *targ_res); + +int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn, + int insn_idx, const struct bpf_core_relo *relo, + int relo_idx, const struct bpf_core_relo_res *res); + #endif diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h index 0b84d8e6b72a..bd6f4505e7b1 100644 --- a/tools/lib/bpf/skel_internal.h +++ b/tools/lib/bpf/skel_internal.h @@ -3,9 +3,19 @@ #ifndef __SKEL_INTERNAL_H #define __SKEL_INTERNAL_H +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#else #include #include #include +#include +#include "bpf.h" +#endif #ifndef __NR_bpf # if defined(__mips__) && defined(_ABIO32) @@ -25,24 +35,23 @@ * requested during loader program generation. */ struct bpf_map_desc { - union { - /* input for the loader prog */ - struct { - __aligned_u64 initial_value; - __u32 max_entries; - }; - /* output of the loader prog */ - struct { - int map_fd; - }; - }; + /* output of the loader prog */ + int map_fd; + /* input for the loader prog */ + __u32 max_entries; + __aligned_u64 initial_value; }; struct bpf_prog_desc { int prog_fd; }; +enum { + BPF_SKEL_KERNEL = (1ULL << 0), +}; + struct bpf_loader_ctx { - size_t sz; + __u32 sz; + __u32 flags; __u32 log_level; __u32 log_size; __u64 log_buf; @@ -57,12 +66,144 @@ struct bpf_load_and_run_opts { const char *errstr; }; +long bpf_sys_bpf(__u32 cmd, void *attr, __u32 attr_size); + static inline int skel_sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size) { +#ifdef __KERNEL__ + return bpf_sys_bpf(cmd, attr, size); +#else return syscall(__NR_bpf, cmd, attr, size); +#endif } +#ifdef __KERNEL__ +static inline int close(int fd) +{ + return close_fd(fd); +} + +static inline void *skel_alloc(size_t size) +{ + struct bpf_loader_ctx *ctx = kzalloc(size, GFP_KERNEL); + + if (!ctx) + return NULL; + ctx->flags |= BPF_SKEL_KERNEL; + return ctx; +} + +static inline void skel_free(const void *p) +{ + kfree(p); +} + +/* skel->bss/rodata maps are populated the following way: + * + * For kernel use: + * skel_prep_map_data() allocates kernel memory that kernel module can directly access. + * Generated lskel stores the pointer in skel->rodata and in skel->maps.rodata.initial_value. + * The loader program will perform probe_read_kernel() from maps.rodata.initial_value. + * skel_finalize_map_data() sets skel->rodata to point to actual value in a bpf map and + * does maps.rodata.initial_value = ~0ULL to signal skel_free_map_data() that kvfree + * is not nessary. + * + * For user space: + * skel_prep_map_data() mmaps anon memory into skel->rodata that can be accessed directly. + * Generated lskel stores the pointer in skel->rodata and in skel->maps.rodata.initial_value. + * The loader program will perform copy_from_user() from maps.rodata.initial_value. + * skel_finalize_map_data() remaps bpf array map value from the kernel memory into + * skel->rodata address. + * + * The "bpftool gen skeleton -L" command generates lskel.h that is suitable for + * both kernel and user space. The generated loader program does + * either bpf_probe_read_kernel() or bpf_copy_from_user() from initial_value + * depending on bpf_loader_ctx->flags. + */ +static inline void skel_free_map_data(void *p, __u64 addr, size_t sz) +{ + if (addr != ~0ULL) + kvfree(p); + /* When addr == ~0ULL the 'p' points to + * ((struct bpf_array *)map)->value. See skel_finalize_map_data. + */ +} + +static inline void *skel_prep_map_data(const void *val, size_t mmap_sz, size_t val_sz) +{ + void *addr; + + addr = kvmalloc(val_sz, GFP_KERNEL); + if (!addr) + return NULL; + memcpy(addr, val, val_sz); + return addr; +} + +static inline void *skel_finalize_map_data(__u64 *init_val, size_t mmap_sz, int flags, int fd) +{ + struct bpf_map *map; + void *addr = NULL; + + kvfree((void *) (long) *init_val); + *init_val = ~0ULL; + + /* At this point bpf_load_and_run() finished without error and + * 'fd' is a valid bpf map FD. All sanity checks below should succeed. + */ + map = bpf_map_get(fd); + if (IS_ERR(map)) + return NULL; + if (map->map_type != BPF_MAP_TYPE_ARRAY) + goto out; + addr = ((struct bpf_array *)map)->value; + /* the addr stays valid, since FD is not closed */ +out: + bpf_map_put(map); + return addr; +} + +#else + +static inline void *skel_alloc(size_t size) +{ + return calloc(1, size); +} + +static inline void skel_free(void *p) +{ + free(p); +} + +static inline void skel_free_map_data(void *p, __u64 addr, size_t sz) +{ + munmap(p, sz); +} + +static inline void *skel_prep_map_data(const void *val, size_t mmap_sz, size_t val_sz) +{ + void *addr; + + addr = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (addr == (void *) -1) + return NULL; + memcpy(addr, val, val_sz); + return addr; +} + +static inline void *skel_finalize_map_data(__u64 *init_val, size_t mmap_sz, int flags, int fd) +{ + void *addr; + + addr = mmap((void *) (long) *init_val, mmap_sz, flags, MAP_SHARED | MAP_FIXED, fd, 0); + if (addr == (void *) -1) + return NULL; + return addr; +} +#endif + static inline int skel_closenz(int fd) { if (fd > 0) @@ -70,22 +211,94 @@ static inline int skel_closenz(int fd) return -EINVAL; } +#ifndef offsetofend +#define offsetofend(TYPE, MEMBER) \ + (offsetof(TYPE, MEMBER) + sizeof((((TYPE *)0)->MEMBER))) +#endif + +static inline int skel_map_create(enum bpf_map_type map_type, + const char *map_name, + __u32 key_size, + __u32 value_size, + __u32 max_entries) +{ + const size_t attr_sz = offsetofend(union bpf_attr, map_extra); + union bpf_attr attr; + + memset(&attr, 0, attr_sz); + + attr.map_type = map_type; + strncpy(attr.map_name, map_name, sizeof(attr.map_name)); + attr.key_size = key_size; + attr.value_size = value_size; + attr.max_entries = max_entries; + + return skel_sys_bpf(BPF_MAP_CREATE, &attr, attr_sz); +} + +static inline int skel_map_update_elem(int fd, const void *key, + const void *value, __u64 flags) +{ + const size_t attr_sz = offsetofend(union bpf_attr, flags); + union bpf_attr attr; + + memset(&attr, 0, attr_sz); + attr.map_fd = fd; + attr.key = (long) key; + attr.value = (long) value; + attr.flags = flags; + + return skel_sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, attr_sz); +} + +static inline int skel_raw_tracepoint_open(const char *name, int prog_fd) +{ + const size_t attr_sz = offsetofend(union bpf_attr, raw_tracepoint.prog_fd); + union bpf_attr attr; + + memset(&attr, 0, attr_sz); + attr.raw_tracepoint.name = (long) name; + attr.raw_tracepoint.prog_fd = prog_fd; + + return skel_sys_bpf(BPF_RAW_TRACEPOINT_OPEN, &attr, attr_sz); +} + +static inline int skel_link_create(int prog_fd, int target_fd, + enum bpf_attach_type attach_type) +{ + const size_t attr_sz = offsetofend(union bpf_attr, link_create.iter_info_len); + union bpf_attr attr; + + memset(&attr, 0, attr_sz); + attr.link_create.prog_fd = prog_fd; + attr.link_create.target_fd = target_fd; + attr.link_create.attach_type = attach_type; + + return skel_sys_bpf(BPF_LINK_CREATE, &attr, attr_sz); +} + +#ifdef __KERNEL__ +#define set_err +#else +#define set_err err = -errno +#endif + static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts) { int map_fd = -1, prog_fd = -1, key = 0, err; union bpf_attr attr; - map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4, opts->data_sz, 1, NULL); + err = map_fd = skel_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4, opts->data_sz, 1); if (map_fd < 0) { opts->errstr = "failed to create loader map"; - err = -errno; + set_err; goto out; } - err = bpf_map_update_elem(map_fd, &key, opts->data, 0); + err = skel_map_update_elem(map_fd, &key, opts->data, 0); if (err < 0) { opts->errstr = "failed to update loader map"; - err = -errno; + set_err; goto out; } @@ -100,10 +313,10 @@ static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts) attr.log_size = opts->ctx->log_size; attr.log_buf = opts->ctx->log_buf; attr.prog_flags = BPF_F_SLEEPABLE; - prog_fd = skel_sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); + err = prog_fd = skel_sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); if (prog_fd < 0) { opts->errstr = "failed to load loader prog"; - err = -errno; + set_err; goto out; } @@ -115,10 +328,12 @@ static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts) if (err < 0 || (int)attr.test.retval < 0) { opts->errstr = "failed to execute loader prog"; if (err < 0) { - err = -errno; + set_err; } else { err = (int)attr.test.retval; +#ifndef __KERNEL__ errno = -err; +#endif } goto out; } diff --git a/tools/lib/bpf/xsk.c b/tools/lib/bpf/xsk.c index edafe56664f3..af136f73b09d 100644 --- a/tools/lib/bpf/xsk.c +++ b/tools/lib/bpf/xsk.c @@ -481,8 +481,8 @@ static int xsk_load_xdp_prog(struct xsk_socket *xsk) BPF_EMIT_CALL(BPF_FUNC_redirect_map), BPF_EXIT_INSN(), }; - size_t insns_cnt[] = {sizeof(prog) / sizeof(struct bpf_insn), - sizeof(prog_redirect_flags) / sizeof(struct bpf_insn), + size_t insns_cnt[] = {ARRAY_SIZE(prog), + ARRAY_SIZE(prog_redirect_flags), }; struct bpf_insn *progs[] = {prog, prog_redirect_flags}; enum xsk_prog option = get_xsk_prog(); @@ -1193,12 +1193,23 @@ int xsk_socket__create(struct xsk_socket **xsk_ptr, const char *ifname, int xsk_umem__delete(struct xsk_umem *umem) { + struct xdp_mmap_offsets off; + int err; + if (!umem) return 0; if (umem->refcount) return -EBUSY; + err = xsk_get_mmap_offsets(umem->fd, &off); + if (!err && umem->fill_save && umem->comp_save) { + munmap(umem->fill_save->ring - off.fr.desc, + off.fr.desc + umem->config.fill_size * sizeof(__u64)); + munmap(umem->comp_save->ring - off.cr.desc, + off.cr.desc + umem->config.comp_size * sizeof(__u64)); + } + close(umem->fd); free(umem); diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c index 8ac0a3a457ef..0bc25a56cfef 100644 --- a/tools/perf/tests/llvm.c +++ b/tools/perf/tests/llvm.c @@ -13,7 +13,7 @@ static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz) { struct bpf_object *obj; - obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL); + obj = bpf_object__open_mem(obj_buf, obj_buf_sz, NULL); if (libbpf_get_error(obj)) return TEST_FAIL; bpf_object__close(obj); diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 16ec605a9fe4..ec6d9e7b446d 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -54,6 +54,7 @@ static bool libbpf_initialized; struct bpf_object * bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name) { + LIBBPF_OPTS(bpf_object_open_opts, opts, .object_name = name); struct bpf_object *obj; if (!libbpf_initialized) { @@ -61,7 +62,7 @@ bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name) libbpf_initialized = true; } - obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, name); + obj = bpf_object__open_mem(obj_buf, obj_buf_sz, &opts); if (IS_ERR_OR_NULL(obj)) { pr_debug("bpf: failed to load buffer\n"); return ERR_PTR(-EINVAL); @@ -72,6 +73,7 @@ bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name) struct bpf_object *bpf__prepare_load(const char *filename, bool source) { + LIBBPF_OPTS(bpf_object_open_opts, opts, .object_name = filename); struct bpf_object *obj; if (!libbpf_initialized) { @@ -94,7 +96,7 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source) return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE); } else pr_debug("bpf: successful builtin compilation\n"); - obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename); + obj = bpf_object__open_mem(obj_buf, obj_buf_sz, &opts); if (!IS_ERR_OR_NULL(obj) && llvm_param.dump_obj) llvm__dump_obj(filename, obj_buf, obj_buf_sz); @@ -654,11 +656,11 @@ int bpf__probe(struct bpf_object *obj) } if (priv->is_tp) { - bpf_program__set_tracepoint(prog); + bpf_program__set_type(prog, BPF_PROG_TYPE_TRACEPOINT); continue; } - bpf_program__set_kprobe(prog); + bpf_program__set_type(prog, BPF_PROG_TYPE_KPROBE); pev = &priv->pev; err = convert_perf_probe_events(pev, 1); @@ -1005,24 +1007,22 @@ __bpf_map__config_value(struct bpf_map *map, { struct bpf_map_op *op; const char *map_name = bpf_map__name(map); - const struct bpf_map_def *def = bpf_map__def(map); - if (IS_ERR(def)) { - pr_debug("Unable to get map definition from '%s'\n", - map_name); + if (!map) { + pr_debug("Map '%s' is invalid\n", map_name); return -BPF_LOADER_ERRNO__INTERNAL; } - if (def->type != BPF_MAP_TYPE_ARRAY) { + if (bpf_map__type(map) != BPF_MAP_TYPE_ARRAY) { pr_debug("Map %s type is not BPF_MAP_TYPE_ARRAY\n", map_name); return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE; } - if (def->key_size < sizeof(unsigned int)) { + if (bpf_map__key_size(map) < sizeof(unsigned int)) { pr_debug("Map %s has incorrect key size\n", map_name); return -BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE; } - switch (def->value_size) { + switch (bpf_map__value_size(map)) { case 1: case 2: case 4: @@ -1064,7 +1064,6 @@ __bpf_map__config_event(struct bpf_map *map, struct parse_events_term *term, struct evlist *evlist) { - const struct bpf_map_def *def; struct bpf_map_op *op; const char *map_name = bpf_map__name(map); struct evsel *evsel = evlist__find_evsel_by_str(evlist, term->val.str); @@ -1075,18 +1074,16 @@ __bpf_map__config_event(struct bpf_map *map, return -BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT; } - def = bpf_map__def(map); - if (IS_ERR(def)) { - pr_debug("Unable to get map definition from '%s'\n", - map_name); - return PTR_ERR(def); + if (!map) { + pr_debug("Map '%s' is invalid\n", map_name); + return PTR_ERR(map); } /* * No need to check key_size and value_size: * kernel has already checked them. */ - if (def->type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) { + if (bpf_map__type(map) != BPF_MAP_TYPE_PERF_EVENT_ARRAY) { pr_debug("Map %s type is not BPF_MAP_TYPE_PERF_EVENT_ARRAY\n", map_name); return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE; @@ -1135,7 +1132,6 @@ config_map_indices_range_check(struct parse_events_term *term, const char *map_name) { struct parse_events_array *array = &term->array; - const struct bpf_map_def *def; unsigned int i; if (!array->nr_ranges) @@ -1146,10 +1142,8 @@ config_map_indices_range_check(struct parse_events_term *term, return -BPF_LOADER_ERRNO__INTERNAL; } - def = bpf_map__def(map); - if (IS_ERR(def)) { - pr_debug("ERROR: Unable to get map definition from '%s'\n", - map_name); + if (!map) { + pr_debug("Map '%s' is invalid\n", map_name); return -BPF_LOADER_ERRNO__INTERNAL; } @@ -1158,7 +1152,7 @@ config_map_indices_range_check(struct parse_events_term *term, size_t length = array->ranges[i].length; unsigned int idx = start + length - 1; - if (idx >= def->max_entries) { + if (idx >= bpf_map__max_entries(map)) { pr_debug("ERROR: index %d too large\n", idx); return -BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG; } @@ -1252,21 +1246,21 @@ int bpf__config_obj(struct bpf_object *obj, } typedef int (*map_config_func_t)(const char *name, int map_fd, - const struct bpf_map_def *pdef, + const struct bpf_map *map, struct bpf_map_op *op, void *pkey, void *arg); static int foreach_key_array_all(map_config_func_t func, void *arg, const char *name, - int map_fd, const struct bpf_map_def *pdef, + int map_fd, const struct bpf_map *map, struct bpf_map_op *op) { unsigned int i; int err; - for (i = 0; i < pdef->max_entries; i++) { - err = func(name, map_fd, pdef, op, &i, arg); + for (i = 0; i < bpf_map__max_entries(map); i++) { + err = func(name, map_fd, map, op, &i, arg); if (err) { pr_debug("ERROR: failed to insert value to %s[%u]\n", name, i); @@ -1279,7 +1273,7 @@ foreach_key_array_all(map_config_func_t func, static int foreach_key_array_ranges(map_config_func_t func, void *arg, const char *name, int map_fd, - const struct bpf_map_def *pdef, + const struct bpf_map *map, struct bpf_map_op *op) { unsigned int i, j; @@ -1292,7 +1286,7 @@ foreach_key_array_ranges(map_config_func_t func, void *arg, for (j = 0; j < length; j++) { unsigned int idx = start + j; - err = func(name, map_fd, pdef, op, &idx, arg); + err = func(name, map_fd, map, op, &idx, arg); if (err) { pr_debug("ERROR: failed to insert value to %s[%u]\n", name, idx); @@ -1308,9 +1302,8 @@ bpf_map_config_foreach_key(struct bpf_map *map, map_config_func_t func, void *arg) { - int err, map_fd; + int err, map_fd, type; struct bpf_map_op *op; - const struct bpf_map_def *def; const char *name = bpf_map__name(map); struct bpf_map_priv *priv = bpf_map__priv(map); @@ -1323,9 +1316,8 @@ bpf_map_config_foreach_key(struct bpf_map *map, return 0; } - def = bpf_map__def(map); - if (IS_ERR(def)) { - pr_debug("ERROR: failed to get definition from map %s\n", name); + if (!map) { + pr_debug("Map '%s' is invalid\n", name); return -BPF_LOADER_ERRNO__INTERNAL; } map_fd = bpf_map__fd(map); @@ -1334,19 +1326,19 @@ bpf_map_config_foreach_key(struct bpf_map *map, return map_fd; } + type = bpf_map__type(map); list_for_each_entry(op, &priv->ops_list, list) { - switch (def->type) { + switch (type) { case BPF_MAP_TYPE_ARRAY: case BPF_MAP_TYPE_PERF_EVENT_ARRAY: switch (op->key_type) { case BPF_MAP_KEY_ALL: err = foreach_key_array_all(func, arg, name, - map_fd, def, op); + map_fd, map, op); break; case BPF_MAP_KEY_RANGES: err = foreach_key_array_ranges(func, arg, name, - map_fd, def, - op); + map_fd, map, op); break; default: pr_debug("ERROR: keytype for map '%s' invalid\n", @@ -1455,7 +1447,7 @@ apply_config_evsel_for_key(const char *name, int map_fd, void *pkey, static int apply_obj_config_map_for_key(const char *name, int map_fd, - const struct bpf_map_def *pdef, + const struct bpf_map *map, struct bpf_map_op *op, void *pkey, void *arg __maybe_unused) { @@ -1464,7 +1456,7 @@ apply_obj_config_map_for_key(const char *name, int map_fd, switch (op->op_type) { case BPF_MAP_OP_SET_VALUE: err = apply_config_value_for_key(map_fd, pkey, - pdef->value_size, + bpf_map__value_size(map), op->v.value); break; case BPF_MAP_OP_SET_EVSEL: diff --git a/tools/perf/util/bpf_map.c b/tools/perf/util/bpf_map.c index eb853ca67cf4..c863ae0c5cb5 100644 --- a/tools/perf/util/bpf_map.c +++ b/tools/perf/util/bpf_map.c @@ -9,25 +9,25 @@ #include #include -static bool bpf_map_def__is_per_cpu(const struct bpf_map_def *def) +static bool bpf_map__is_per_cpu(enum bpf_map_type type) { - return def->type == BPF_MAP_TYPE_PERCPU_HASH || - def->type == BPF_MAP_TYPE_PERCPU_ARRAY || - def->type == BPF_MAP_TYPE_LRU_PERCPU_HASH || - def->type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE; + return type == BPF_MAP_TYPE_PERCPU_HASH || + type == BPF_MAP_TYPE_PERCPU_ARRAY || + type == BPF_MAP_TYPE_LRU_PERCPU_HASH || + type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE; } -static void *bpf_map_def__alloc_value(const struct bpf_map_def *def) +static void *bpf_map__alloc_value(const struct bpf_map *map) { - if (bpf_map_def__is_per_cpu(def)) - return malloc(round_up(def->value_size, 8) * sysconf(_SC_NPROCESSORS_CONF)); + if (bpf_map__is_per_cpu(bpf_map__type(map))) + return malloc(round_up(bpf_map__value_size(map), 8) * + sysconf(_SC_NPROCESSORS_CONF)); - return malloc(def->value_size); + return malloc(bpf_map__value_size(map)); } int bpf_map__fprintf(struct bpf_map *map, FILE *fp) { - const struct bpf_map_def *def = bpf_map__def(map); void *prev_key = NULL, *key, *value; int fd = bpf_map__fd(map), err; int printed = 0; @@ -35,15 +35,15 @@ int bpf_map__fprintf(struct bpf_map *map, FILE *fp) if (fd < 0) return fd; - if (IS_ERR(def)) - return PTR_ERR(def); + if (!map) + return PTR_ERR(map); err = -ENOMEM; - key = malloc(def->key_size); + key = malloc(bpf_map__key_size(map)); if (key == NULL) goto out; - value = bpf_map_def__alloc_value(def); + value = bpf_map__alloc_value(map); if (value == NULL) goto out_free_key; diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include index 79d102304470..a2335e402145 100644 --- a/tools/scripts/Makefile.include +++ b/tools/scripts/Makefile.include @@ -89,6 +89,9 @@ ifeq ($(CC_NO_CLANG), 1) EXTRA_WARNINGS += -Wstrict-aliasing=3 else ifneq ($(CROSS_COMPILE),) +# Allow userspace to override CLANG_CROSS_FLAGS to specify their own +# sysroots and flags or to avoid the GCC call in pure Clang builds. +ifeq ($(CLANG_CROSS_FLAGS),) CLANG_CROSS_FLAGS := --target=$(notdir $(CROSS_COMPILE:%-=%)) GCC_TOOLCHAIN_DIR := $(dir $(shell which $(CROSS_COMPILE)gcc 2>/dev/null)) ifneq ($(GCC_TOOLCHAIN_DIR),) @@ -96,6 +99,7 @@ CLANG_CROSS_FLAGS += --prefix=$(GCC_TOOLCHAIN_DIR)$(notdir $(CROSS_COMPILE)) CLANG_CROSS_FLAGS += --sysroot=$(shell $(CROSS_COMPILE)gcc -print-sysroot) CLANG_CROSS_FLAGS += --gcc-toolchain=$(realpath $(GCC_TOOLCHAIN_DIR)/..) endif # GCC_TOOLCHAIN_DIR +endif # CLANG_CROSS_FLAGS CFLAGS += $(CLANG_CROSS_FLAGS) AFLAGS += $(CLANG_CROSS_FLAGS) endif # CROSS_COMPILE diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index 1dad8d617da8..595565eb68c0 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +bpftool bpf-helpers* bpf-syscall* test_verifier @@ -30,6 +31,7 @@ test_tcp_check_syncookie_user test_sysctl xdping test_cpp +*.subskel.h *.skel.h *.lskel.h /no_alu32 diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 42ffc24e9e71..3820608faf57 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -21,11 +21,11 @@ endif BPF_GCC ?= $(shell command -v bpf-gcc;) SAN_CFLAGS ?= -CFLAGS += -g -O0 -rdynamic -Wall $(GENFLAGS) $(SAN_CFLAGS) \ +CFLAGS += -g -O0 -rdynamic -Wall -Werror $(GENFLAGS) $(SAN_CFLAGS) \ -I$(CURDIR) -I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR) \ -I$(TOOLSINCDIR) -I$(APIDIR) -I$(OUTPUT) LDFLAGS += $(SAN_CFLAGS) -LDLIBS += -lcap -lelf -lz -lrt -lpthread +LDLIBS += -lelf -lz -lrt -lpthread # Silence some warnings when compiled with clang ifneq ($(LLVM),) @@ -195,6 +195,7 @@ $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED): $(BPFOBJ) CGROUP_HELPERS := $(OUTPUT)/cgroup_helpers.o TESTING_HELPERS := $(OUTPUT)/testing_helpers.o TRACE_HELPERS := $(OUTPUT)/trace_helpers.o +CAP_HELPERS := $(OUTPUT)/cap_helpers.o $(OUTPUT)/test_dev_cgroup: $(CGROUP_HELPERS) $(TESTING_HELPERS) $(OUTPUT)/test_skb_cgroup_id_user: $(CGROUP_HELPERS) $(TESTING_HELPERS) @@ -211,7 +212,7 @@ $(OUTPUT)/test_lirc_mode2_user: $(TESTING_HELPERS) $(OUTPUT)/xdping: $(TESTING_HELPERS) $(OUTPUT)/flow_dissector_load: $(TESTING_HELPERS) $(OUTPUT)/test_maps: $(TESTING_HELPERS) -$(OUTPUT)/test_verifier: $(TESTING_HELPERS) +$(OUTPUT)/test_verifier: $(TESTING_HELPERS) $(CAP_HELPERS) BPFTOOL ?= $(DEFAULT_BPFTOOL) $(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile) \ @@ -292,7 +293,7 @@ IS_LITTLE_ENDIAN = $(shell $(CC) -dM -E - $$@ + $(Q)$$(BPFTOOL) gen subskeleton $$(<:.o=.linked3.o) name $$(notdir $$(<:.o=)) > $$(@:.skel.h=.subskel.h) $(TRUNNER_BPF_LSKELS): %.lskel.h: %.o $(BPFTOOL) | $(TRUNNER_OUTPUT) $$(call msg,GEN-SKEL,$(TRUNNER_BINARY),$$@) @@ -421,6 +429,7 @@ $(TRUNNER_BPF_SKELS_LINKED): $(TRUNNER_BPF_OBJS) $(BPFTOOL) | $(TRUNNER_OUTPUT) $(Q)diff $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked3.o) $$(call msg,GEN-SKEL,$(TRUNNER_BINARY),$$@) $(Q)$$(BPFTOOL) gen skeleton $$(@:.skel.h=.linked3.o) name $$(notdir $$(@:.skel.h=)) > $$@ + $(Q)$$(BPFTOOL) gen subskeleton $$(@:.skel.h=.linked3.o) name $$(notdir $$(@:.skel.h=)) > $$(@:.skel.h=.subskel.h) endif # ensure we set up tests.h header generation rule just once @@ -470,6 +479,7 @@ $(OUTPUT)/$(TRUNNER_BINARY): $(TRUNNER_TEST_OBJS) \ $$(call msg,BINARY,,$$@) $(Q)$$(CC) $$(CFLAGS) $$(filter %.a %.o,$$^) $$(LDLIBS) -o $$@ $(Q)$(RESOLVE_BTFIDS) --btf $(TRUNNER_OUTPUT)/btf_data.o $$@ + $(Q)ln -sf $(if $2,..,.)/tools/build/bpftool/bootstrap/bpftool $(if $2,$2/)bpftool endef @@ -478,7 +488,8 @@ TRUNNER_TESTS_DIR := prog_tests TRUNNER_BPF_PROGS_DIR := progs TRUNNER_EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c \ network_helpers.c testing_helpers.c \ - btf_helpers.c flow_dissector_load.h + btf_helpers.c flow_dissector_load.h \ + cap_helpers.c TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko \ ima_setup.sh \ $(wildcard progs/btf_dump_test_case_*.c) @@ -555,7 +566,7 @@ $(OUTPUT)/bench: $(OUTPUT)/bench.o \ EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(SCRATCH_DIR) $(HOST_SCRATCH_DIR) \ prog_tests/tests.h map_tests/tests.h verifier/tests.h \ - feature \ - $(addprefix $(OUTPUT)/,*.o *.skel.h *.lskel.h no_alu32 bpf_gcc bpf_testmod.ko) + feature bpftool \ + $(addprefix $(OUTPUT)/,*.o *.skel.h *.lskel.h *.subskel.h no_alu32 bpf_gcc bpf_testmod.ko) .PHONY: docs docs-clean diff --git a/tools/testing/selftests/bpf/README.rst b/tools/testing/selftests/bpf/README.rst index 42ef250c7acc..eb1b7541f39d 100644 --- a/tools/testing/selftests/bpf/README.rst +++ b/tools/testing/selftests/bpf/README.rst @@ -32,11 +32,19 @@ For more information on about using the script, run: $ tools/testing/selftests/bpf/vmtest.sh -h +In case of linker errors when running selftests, try using static linking: + +.. code-block:: console + + $ LDLIBS=-static vmtest.sh + +.. note:: Some distros may not support static linking. + .. note:: The script uses pahole and clang based on host environment setting. If you want to change pahole and llvm, you can change `PATH` environment variable in the beginning of script. -.. note:: The script currently only supports x86_64. +.. note:: The script currently only supports x86_64 and s390x architectures. Additional information about selftest failures are documented here. @@ -206,6 +214,8 @@ btf_tag test and Clang version The btf_tag selftest requires LLVM support to recognize the btf_decl_tag and btf_type_tag attributes. They are introduced in `Clang 14` [0_, 1_]. +The subtests ``btf_type_tag_user_{mod1, mod2, vmlinux}`` also requires +pahole version ``1.23``. Without them, the btf_tag selftest will be skipped and you will observe: diff --git a/tools/testing/selftests/bpf/benchs/bench_ringbufs.c b/tools/testing/selftests/bpf/benchs/bench_ringbufs.c index da8593b3494a..c2554f9695ff 100644 --- a/tools/testing/selftests/bpf/benchs/bench_ringbufs.c +++ b/tools/testing/selftests/bpf/benchs/bench_ringbufs.c @@ -151,7 +151,7 @@ static struct ringbuf_bench *ringbuf_setup_skeleton(void) /* record data + header take 16 bytes */ skel->rodata->wakeup_data_size = args.sample_rate * 16; - bpf_map__resize(skel->maps.ringbuf, args.ringbuf_sz); + bpf_map__set_max_entries(skel->maps.ringbuf, args.ringbuf_sz); if (ringbuf_bench__load(skel)) { fprintf(stderr, "failed to load skeleton\n"); diff --git a/tools/testing/selftests/bpf/benchs/bench_trigger.c b/tools/testing/selftests/bpf/benchs/bench_trigger.c index 7f957c55a3ca..0c481de2833d 100644 --- a/tools/testing/selftests/bpf/benchs/bench_trigger.c +++ b/tools/testing/selftests/bpf/benchs/bench_trigger.c @@ -154,7 +154,6 @@ static void *uprobe_producer_without_nop(void *input) static void usetup(bool use_retprobe, bool use_nop) { size_t uprobe_offset; - ssize_t base_addr; struct bpf_link *link; setup_libbpf(); @@ -165,11 +164,10 @@ static void usetup(bool use_retprobe, bool use_nop) exit(1); } - base_addr = get_base_addr(); if (use_nop) - uprobe_offset = get_uprobe_offset(&uprobe_target_with_nop, base_addr); + uprobe_offset = get_uprobe_offset(&uprobe_target_with_nop); else - uprobe_offset = get_uprobe_offset(&uprobe_target_without_nop, base_addr); + uprobe_offset = get_uprobe_offset(&uprobe_target_without_nop); link = bpf_program__attach_uprobe(ctx.skel->progs.bench_trigger_uprobe, use_retprobe, diff --git a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c index df3b292a8ffe..e585e1cefc77 100644 --- a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +++ b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c @@ -13,6 +13,10 @@ #define CREATE_TRACE_POINTS #include "bpf_testmod-events.h" +typedef int (*func_proto_typedef)(long); +typedef int (*func_proto_typedef_nested1)(func_proto_typedef); +typedef int (*func_proto_typedef_nested2)(func_proto_typedef_nested1); + DEFINE_PER_CPU(int, bpf_testmod_ksym_percpu) = 123; noinline void @@ -21,6 +25,41 @@ bpf_testmod_test_mod_kfunc(int i) *(int *)this_cpu_ptr(&bpf_testmod_ksym_percpu) = i; } +struct bpf_testmod_btf_type_tag_1 { + int a; +}; + +struct bpf_testmod_btf_type_tag_2 { + struct bpf_testmod_btf_type_tag_1 __user *p; +}; + +struct bpf_testmod_btf_type_tag_3 { + struct bpf_testmod_btf_type_tag_1 __percpu *p; +}; + +noinline int +bpf_testmod_test_btf_type_tag_user_1(struct bpf_testmod_btf_type_tag_1 __user *arg) { + BTF_TYPE_EMIT(func_proto_typedef); + BTF_TYPE_EMIT(func_proto_typedef_nested1); + BTF_TYPE_EMIT(func_proto_typedef_nested2); + return arg->a; +} + +noinline int +bpf_testmod_test_btf_type_tag_user_2(struct bpf_testmod_btf_type_tag_2 *arg) { + return arg->p->a; +} + +noinline int +bpf_testmod_test_btf_type_tag_percpu_1(struct bpf_testmod_btf_type_tag_1 __percpu *arg) { + return arg->a; +} + +noinline int +bpf_testmod_test_btf_type_tag_percpu_2(struct bpf_testmod_btf_type_tag_3 *arg) { + return arg->p->a; +} + noinline int bpf_testmod_loop_test(int n) { int i, sum = 0; @@ -109,26 +148,31 @@ static struct bin_attribute bin_attr_bpf_testmod_file __ro_after_init = { .write = bpf_testmod_test_write, }; -BTF_SET_START(bpf_testmod_kfunc_ids) +BTF_SET_START(bpf_testmod_check_kfunc_ids) BTF_ID(func, bpf_testmod_test_mod_kfunc) -BTF_SET_END(bpf_testmod_kfunc_ids) +BTF_SET_END(bpf_testmod_check_kfunc_ids) -static DEFINE_KFUNC_BTF_ID_SET(&bpf_testmod_kfunc_ids, bpf_testmod_kfunc_btf_set); +static const struct btf_kfunc_id_set bpf_testmod_kfunc_set = { + .owner = THIS_MODULE, + .check_set = &bpf_testmod_check_kfunc_ids, +}; + +extern int bpf_fentry_test1(int a); static int bpf_testmod_init(void) { int ret; - ret = sysfs_create_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file); - if (ret) + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_testmod_kfunc_set); + if (ret < 0) return ret; - register_kfunc_btf_id_set(&prog_test_kfunc_list, &bpf_testmod_kfunc_btf_set); - return 0; + if (bpf_fentry_test1(0) < 0) + return -EINVAL; + return sysfs_create_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file); } static void bpf_testmod_exit(void) { - unregister_kfunc_btf_id_set(&prog_test_kfunc_list, &bpf_testmod_kfunc_btf_set); return sysfs_remove_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file); } diff --git a/tools/testing/selftests/bpf/cap_helpers.c b/tools/testing/selftests/bpf/cap_helpers.c new file mode 100644 index 000000000000..d5ac507401d7 --- /dev/null +++ b/tools/testing/selftests/bpf/cap_helpers.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "cap_helpers.h" + +/* Avoid including from the libcap-devel package, + * so directly declare them here and use them from glibc. + */ +int capget(cap_user_header_t header, cap_user_data_t data); +int capset(cap_user_header_t header, const cap_user_data_t data); + +int cap_enable_effective(__u64 caps, __u64 *old_caps) +{ + struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3]; + struct __user_cap_header_struct hdr = { + .version = _LINUX_CAPABILITY_VERSION_3, + }; + __u32 cap0 = caps; + __u32 cap1 = caps >> 32; + int err; + + err = capget(&hdr, data); + if (err) + return err; + + if (old_caps) + *old_caps = (__u64)(data[1].effective) << 32 | data[0].effective; + + if ((data[0].effective & cap0) == cap0 && + (data[1].effective & cap1) == cap1) + return 0; + + data[0].effective |= cap0; + data[1].effective |= cap1; + err = capset(&hdr, data); + if (err) + return err; + + return 0; +} + +int cap_disable_effective(__u64 caps, __u64 *old_caps) +{ + struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3]; + struct __user_cap_header_struct hdr = { + .version = _LINUX_CAPABILITY_VERSION_3, + }; + __u32 cap0 = caps; + __u32 cap1 = caps >> 32; + int err; + + err = capget(&hdr, data); + if (err) + return err; + + if (old_caps) + *old_caps = (__u64)(data[1].effective) << 32 | data[0].effective; + + if (!(data[0].effective & cap0) && !(data[1].effective & cap1)) + return 0; + + data[0].effective &= ~cap0; + data[1].effective &= ~cap1; + err = capset(&hdr, data); + if (err) + return err; + + return 0; +} diff --git a/tools/testing/selftests/bpf/cap_helpers.h b/tools/testing/selftests/bpf/cap_helpers.h new file mode 100644 index 000000000000..6d163530cb0f --- /dev/null +++ b/tools/testing/selftests/bpf/cap_helpers.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __CAP_HELPERS_H +#define __CAP_HELPERS_H + +#include +#include + +#ifndef CAP_PERFMON +#define CAP_PERFMON 38 +#endif + +#ifndef CAP_BPF +#define CAP_BPF 39 +#endif + +int cap_enable_effective(__u64 caps, __u64 *old_caps); +int cap_disable_effective(__u64 caps, __u64 *old_caps); + +#endif diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index f6287132fa89..763db63a3890 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -48,3 +48,8 @@ CONFIG_IMA_READ_POLICY=y CONFIG_BLK_DEV_LOOP=y CONFIG_FUNCTION_TRACER=y CONFIG_DYNAMIC_FTRACE=y +CONFIG_NETFILTER=y +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK=y +CONFIG_USERFAULTFD=y diff --git a/tools/testing/selftests/bpf/ima_setup.sh b/tools/testing/selftests/bpf/ima_setup.sh index 8e62581113a3..8ecead4ccad0 100755 --- a/tools/testing/selftests/bpf/ima_setup.sh +++ b/tools/testing/selftests/bpf/ima_setup.sh @@ -12,7 +12,7 @@ LOG_FILE="$(mktemp /tmp/ima_setup.XXXX.log)" usage() { - echo "Usage: $0 " + echo "Usage: $0 " exit 1 } @@ -51,6 +51,7 @@ setup() ensure_mount_securityfs echo "measure func=BPRM_CHECK fsuuid=${mount_uuid}" > ${IMA_POLICY_FILE} + echo "measure func=BPRM_CHECK fsuuid=${mount_uuid}" > ${mount_dir}/policy_test } cleanup() { @@ -77,6 +78,32 @@ run() exec "${copied_bin_path}" } +modify_bin() +{ + local tmp_dir="$1" + local mount_dir="${tmp_dir}/mnt" + local copied_bin_path="${mount_dir}/$(basename ${TEST_BINARY})" + + echo "mod" >> "${copied_bin_path}" +} + +restore_bin() +{ + local tmp_dir="$1" + local mount_dir="${tmp_dir}/mnt" + local copied_bin_path="${mount_dir}/$(basename ${TEST_BINARY})" + + truncate -s -4 "${copied_bin_path}" +} + +load_policy() +{ + local tmp_dir="$1" + local mount_dir="${tmp_dir}/mnt" + + echo ${mount_dir}/policy_test > ${IMA_POLICY_FILE} 2> /dev/null +} + catch() { local exit_code="$1" @@ -105,6 +132,12 @@ main() cleanup "${tmp_dir}" elif [[ "${action}" == "run" ]]; then run "${tmp_dir}" + elif [[ "${action}" == "modify-bin" ]]; then + modify_bin "${tmp_dir}" + elif [[ "${action}" == "restore-bin" ]]; then + restore_bin "${tmp_dir}" + elif [[ "${action}" == "load-policy" ]]; then + load_policy "${tmp_dir}" else echo "Unknown action: ${action}" exit 1 diff --git a/tools/testing/selftests/bpf/network_helpers.c b/tools/testing/selftests/bpf/network_helpers.c index 6db1af8fdee7..2bb1f9b3841d 100644 --- a/tools/testing/selftests/bpf/network_helpers.c +++ b/tools/testing/selftests/bpf/network_helpers.c @@ -1,18 +1,25 @@ // SPDX-License-Identifier: GPL-2.0-only +#define _GNU_SOURCE + #include #include #include #include #include +#include #include +#include +#include #include #include #include +#include #include "bpf_util.h" #include "network_helpers.h" +#include "test_progs.h" #define clean_errno() (errno == 0 ? "None" : strerror(errno)) #define log_err(MSG, ...) ({ \ @@ -356,3 +363,82 @@ char *ping_command(int family) } return "ping"; } + +struct nstoken { + int orig_netns_fd; +}; + +static int setns_by_fd(int nsfd) +{ + int err; + + err = setns(nsfd, CLONE_NEWNET); + close(nsfd); + + if (!ASSERT_OK(err, "setns")) + return err; + + /* Switch /sys to the new namespace so that e.g. /sys/class/net + * reflects the devices in the new namespace. + */ + err = unshare(CLONE_NEWNS); + if (!ASSERT_OK(err, "unshare")) + return err; + + /* Make our /sys mount private, so the following umount won't + * trigger the global umount in case it's shared. + */ + err = mount("none", "/sys", NULL, MS_PRIVATE, NULL); + if (!ASSERT_OK(err, "remount private /sys")) + return err; + + err = umount2("/sys", MNT_DETACH); + if (!ASSERT_OK(err, "umount2 /sys")) + return err; + + err = mount("sysfs", "/sys", "sysfs", 0, NULL); + if (!ASSERT_OK(err, "mount /sys")) + return err; + + err = mount("bpffs", "/sys/fs/bpf", "bpf", 0, NULL); + if (!ASSERT_OK(err, "mount /sys/fs/bpf")) + return err; + + return 0; +} + +struct nstoken *open_netns(const char *name) +{ + int nsfd; + char nspath[PATH_MAX]; + int err; + struct nstoken *token; + + token = malloc(sizeof(struct nstoken)); + if (!ASSERT_OK_PTR(token, "malloc token")) + return NULL; + + token->orig_netns_fd = open("/proc/self/ns/net", O_RDONLY); + if (!ASSERT_GE(token->orig_netns_fd, 0, "open /proc/self/ns/net")) + goto fail; + + snprintf(nspath, sizeof(nspath), "%s/%s", "/var/run/netns", name); + nsfd = open(nspath, O_RDONLY | O_CLOEXEC); + if (!ASSERT_GE(nsfd, 0, "open netns fd")) + goto fail; + + err = setns_by_fd(nsfd); + if (!ASSERT_OK(err, "setns_by_fd")) + goto fail; + + return token; +fail: + free(token); + return NULL; +} + +void close_netns(struct nstoken *token) +{ + ASSERT_OK(setns_by_fd(token->orig_netns_fd), "setns_by_fd"); + free(token); +} diff --git a/tools/testing/selftests/bpf/network_helpers.h b/tools/testing/selftests/bpf/network_helpers.h index d198181a5648..a4b3b2f9877b 100644 --- a/tools/testing/selftests/bpf/network_helpers.h +++ b/tools/testing/selftests/bpf/network_helpers.h @@ -55,4 +55,13 @@ int make_sockaddr(int family, const char *addr_str, __u16 port, struct sockaddr_storage *addr, socklen_t *len); char *ping_command(int family); +struct nstoken; +/** + * open_netns() - Switch to specified network namespace by name. + * + * Returns token with which to restore the original namespace + * using close_netns(). + */ +struct nstoken *open_netns(const char *name); +void close_netns(struct nstoken *token); #endif diff --git a/tools/testing/selftests/bpf/prog_tests/align.c b/tools/testing/selftests/bpf/prog_tests/align.c index 0ee29e11eaee..970f09156eb4 100644 --- a/tools/testing/selftests/bpf/prog_tests/align.c +++ b/tools/testing/selftests/bpf/prog_tests/align.c @@ -39,13 +39,13 @@ static struct bpf_align_test tests[] = { }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .matches = { - {0, "R1=ctx(id=0,off=0,imm=0)"}, + {0, "R1=ctx(off=0,imm=0)"}, {0, "R10=fp0"}, - {0, "R3_w=inv2"}, - {1, "R3_w=inv4"}, - {2, "R3_w=inv8"}, - {3, "R3_w=inv16"}, - {4, "R3_w=inv32"}, + {0, "R3_w=2"}, + {1, "R3_w=4"}, + {2, "R3_w=8"}, + {3, "R3_w=16"}, + {4, "R3_w=32"}, }, }, { @@ -67,19 +67,19 @@ static struct bpf_align_test tests[] = { }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .matches = { - {0, "R1=ctx(id=0,off=0,imm=0)"}, + {0, "R1=ctx(off=0,imm=0)"}, {0, "R10=fp0"}, - {0, "R3_w=inv1"}, - {1, "R3_w=inv2"}, - {2, "R3_w=inv4"}, - {3, "R3_w=inv8"}, - {4, "R3_w=inv16"}, - {5, "R3_w=inv1"}, - {6, "R4_w=inv32"}, - {7, "R4_w=inv16"}, - {8, "R4_w=inv8"}, - {9, "R4_w=inv4"}, - {10, "R4_w=inv2"}, + {0, "R3_w=1"}, + {1, "R3_w=2"}, + {2, "R3_w=4"}, + {3, "R3_w=8"}, + {4, "R3_w=16"}, + {5, "R3_w=1"}, + {6, "R4_w=32"}, + {7, "R4_w=16"}, + {8, "R4_w=8"}, + {9, "R4_w=4"}, + {10, "R4_w=2"}, }, }, { @@ -96,14 +96,14 @@ static struct bpf_align_test tests[] = { }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .matches = { - {0, "R1=ctx(id=0,off=0,imm=0)"}, + {0, "R1=ctx(off=0,imm=0)"}, {0, "R10=fp0"}, - {0, "R3_w=inv4"}, - {1, "R3_w=inv8"}, - {2, "R3_w=inv10"}, - {3, "R4_w=inv8"}, - {4, "R4_w=inv12"}, - {5, "R4_w=inv14"}, + {0, "R3_w=4"}, + {1, "R3_w=8"}, + {2, "R3_w=10"}, + {3, "R4_w=8"}, + {4, "R4_w=12"}, + {5, "R4_w=14"}, }, }, { @@ -118,12 +118,12 @@ static struct bpf_align_test tests[] = { }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .matches = { - {0, "R1=ctx(id=0,off=0,imm=0)"}, + {0, "R1=ctx(off=0,imm=0)"}, {0, "R10=fp0"}, - {0, "R3_w=inv7"}, - {1, "R3_w=inv7"}, - {2, "R3_w=inv14"}, - {3, "R3_w=inv56"}, + {0, "R3_w=7"}, + {1, "R3_w=7"}, + {2, "R3_w=14"}, + {3, "R3_w=56"}, }, }, @@ -161,19 +161,19 @@ static struct bpf_align_test tests[] = { }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .matches = { - {6, "R0_w=pkt(id=0,off=8,r=8,imm=0)"}, - {6, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, - {7, "R3_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"}, - {8, "R3_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, - {9, "R3_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, - {10, "R3_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"}, - {12, "R3_w=pkt_end(id=0,off=0,imm=0)"}, - {17, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, - {18, "R4_w=inv(id=0,umax_value=8160,var_off=(0x0; 0x1fe0))"}, - {19, "R4_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"}, - {20, "R4_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, - {21, "R4_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, - {22, "R4_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"}, + {6, "R0_w=pkt(off=8,r=8,imm=0)"}, + {6, "R3_w=scalar(umax=255,var_off=(0x0; 0xff))"}, + {7, "R3_w=scalar(umax=510,var_off=(0x0; 0x1fe))"}, + {8, "R3_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"}, + {9, "R3_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"}, + {10, "R3_w=scalar(umax=4080,var_off=(0x0; 0xff0))"}, + {12, "R3_w=pkt_end(off=0,imm=0)"}, + {17, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"}, + {18, "R4_w=scalar(umax=8160,var_off=(0x0; 0x1fe0))"}, + {19, "R4_w=scalar(umax=4080,var_off=(0x0; 0xff0))"}, + {20, "R4_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"}, + {21, "R4_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"}, + {22, "R4_w=scalar(umax=510,var_off=(0x0; 0x1fe))"}, }, }, { @@ -194,16 +194,16 @@ static struct bpf_align_test tests[] = { }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .matches = { - {6, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, - {7, "R4_w=inv(id=1,umax_value=255,var_off=(0x0; 0xff))"}, - {8, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, - {9, "R4_w=inv(id=1,umax_value=255,var_off=(0x0; 0xff))"}, - {10, "R4_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"}, - {11, "R4_w=inv(id=1,umax_value=255,var_off=(0x0; 0xff))"}, - {12, "R4_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, - {13, "R4_w=inv(id=1,umax_value=255,var_off=(0x0; 0xff))"}, - {14, "R4_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, - {15, "R4_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"}, + {6, "R3_w=scalar(umax=255,var_off=(0x0; 0xff))"}, + {7, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"}, + {8, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"}, + {9, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"}, + {10, "R4_w=scalar(umax=510,var_off=(0x0; 0x1fe))"}, + {11, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"}, + {12, "R4_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"}, + {13, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"}, + {14, "R4_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"}, + {15, "R4_w=scalar(umax=4080,var_off=(0x0; 0xff0))"}, }, }, { @@ -234,14 +234,14 @@ static struct bpf_align_test tests[] = { }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .matches = { - {2, "R5_w=pkt(id=0,off=0,r=0,imm=0)"}, - {4, "R5_w=pkt(id=0,off=14,r=0,imm=0)"}, - {5, "R4_w=pkt(id=0,off=14,r=0,imm=0)"}, - {9, "R2=pkt(id=0,off=0,r=18,imm=0)"}, - {10, "R5=pkt(id=0,off=14,r=18,imm=0)"}, - {10, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, - {13, "R4_w=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"}, - {14, "R4_w=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"}, + {2, "R5_w=pkt(off=0,r=0,imm=0)"}, + {4, "R5_w=pkt(off=14,r=0,imm=0)"}, + {5, "R4_w=pkt(off=14,r=0,imm=0)"}, + {9, "R2=pkt(off=0,r=18,imm=0)"}, + {10, "R5=pkt(off=14,r=18,imm=0)"}, + {10, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"}, + {13, "R4_w=scalar(umax=65535,var_off=(0x0; 0xffff))"}, + {14, "R4_w=scalar(umax=65535,var_off=(0x0; 0xffff))"}, }, }, { @@ -296,59 +296,59 @@ static struct bpf_align_test tests[] = { /* Calculated offset in R6 has unknown value, but known * alignment of 4. */ - {6, "R2_w=pkt(id=0,off=0,r=8,imm=0)"}, - {7, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, + {6, "R2_w=pkt(off=0,r=8,imm=0)"}, + {7, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"}, /* Offset is added to packet pointer R5, resulting in * known fixed offset, and variable offset from R6. */ - {11, "R5_w=pkt(id=1,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, + {11, "R5_w=pkt(id=1,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, /* At the time the word size load is performed from R5, * it's total offset is NET_IP_ALIGN + reg->off (0) + * reg->aux_off (14) which is 16. Then the variable * offset is considered using reg->aux_off_align which * is 4 and meets the load's requirements. */ - {15, "R4=pkt(id=1,off=18,r=18,umax_value=1020,var_off=(0x0; 0x3fc))"}, - {15, "R5=pkt(id=1,off=14,r=18,umax_value=1020,var_off=(0x0; 0x3fc))"}, + {15, "R4=pkt(id=1,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"}, + {15, "R5=pkt(id=1,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"}, /* Variable offset is added to R5 packet pointer, * resulting in auxiliary alignment of 4. */ - {17, "R5_w=pkt(id=2,off=0,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, + {17, "R5_w=pkt(id=2,off=0,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, /* Constant offset is added to R5, resulting in * reg->off of 14. */ - {18, "R5_w=pkt(id=2,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, + {18, "R5_w=pkt(id=2,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, /* At the time the word size load is performed from R5, * its total fixed offset is NET_IP_ALIGN + reg->off * (14) which is 16. Then the variable offset is 4-byte * aligned, so the total offset is 4-byte aligned and * meets the load's requirements. */ - {23, "R4=pkt(id=2,off=18,r=18,umax_value=1020,var_off=(0x0; 0x3fc))"}, - {23, "R5=pkt(id=2,off=14,r=18,umax_value=1020,var_off=(0x0; 0x3fc))"}, + {23, "R4=pkt(id=2,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"}, + {23, "R5=pkt(id=2,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"}, /* Constant offset is added to R5 packet pointer, * resulting in reg->off value of 14. */ - {25, "R5_w=pkt(id=0,off=14,r=8"}, + {25, "R5_w=pkt(off=14,r=8"}, /* Variable offset is added to R5, resulting in a * variable offset of (4n). */ - {26, "R5_w=pkt(id=3,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, + {26, "R5_w=pkt(id=3,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, /* Constant is added to R5 again, setting reg->off to 18. */ - {27, "R5_w=pkt(id=3,off=18,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, + {27, "R5_w=pkt(id=3,off=18,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, /* And once more we add a variable; resulting var_off * is still (4n), fixed offset is not changed. * Also, we create a new reg->id. */ - {28, "R5_w=pkt(id=4,off=18,r=0,umax_value=2040,var_off=(0x0; 0x7fc)"}, + {28, "R5_w=pkt(id=4,off=18,r=0,umax=2040,var_off=(0x0; 0x7fc)"}, /* At the time the word size load is performed from R5, * its total fixed offset is NET_IP_ALIGN + reg->off (18) * which is 20. Then the variable offset is (4n), so * the total offset is 4-byte aligned and meets the * load's requirements. */ - {33, "R4=pkt(id=4,off=22,r=22,umax_value=2040,var_off=(0x0; 0x7fc)"}, - {33, "R5=pkt(id=4,off=18,r=22,umax_value=2040,var_off=(0x0; 0x7fc)"}, + {33, "R4=pkt(id=4,off=22,r=22,umax=2040,var_off=(0x0; 0x7fc)"}, + {33, "R5=pkt(id=4,off=18,r=22,umax=2040,var_off=(0x0; 0x7fc)"}, }, }, { @@ -386,36 +386,36 @@ static struct bpf_align_test tests[] = { /* Calculated offset in R6 has unknown value, but known * alignment of 4. */ - {6, "R2_w=pkt(id=0,off=0,r=8,imm=0)"}, - {7, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, + {6, "R2_w=pkt(off=0,r=8,imm=0)"}, + {7, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"}, /* Adding 14 makes R6 be (4n+2) */ - {8, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"}, + {8, "R6_w=scalar(umin=14,umax=1034,var_off=(0x2; 0x7fc))"}, /* Packet pointer has (4n+2) offset */ - {11, "R5_w=pkt(id=1,off=0,r=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc)"}, - {12, "R4=pkt(id=1,off=4,r=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc)"}, + {11, "R5_w=pkt(id=1,off=0,r=0,umin=14,umax=1034,var_off=(0x2; 0x7fc)"}, + {12, "R4=pkt(id=1,off=4,r=0,umin=14,umax=1034,var_off=(0x2; 0x7fc)"}, /* At the time the word size load is performed from R5, * its total fixed offset is NET_IP_ALIGN + reg->off (0) * which is 2. Then the variable offset is (4n+2), so * the total offset is 4-byte aligned and meets the * load's requirements. */ - {15, "R5=pkt(id=1,off=0,r=4,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc)"}, + {15, "R5=pkt(id=1,off=0,r=4,umin=14,umax=1034,var_off=(0x2; 0x7fc)"}, /* Newly read value in R6 was shifted left by 2, so has * known alignment of 4. */ - {17, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, + {17, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"}, /* Added (4n) to packet pointer's (4n+2) var_off, giving * another (4n+2). */ - {19, "R5_w=pkt(id=2,off=0,r=0,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc)"}, - {20, "R4=pkt(id=2,off=4,r=0,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc)"}, + {19, "R5_w=pkt(id=2,off=0,r=0,umin=14,umax=2054,var_off=(0x2; 0xffc)"}, + {20, "R4=pkt(id=2,off=4,r=0,umin=14,umax=2054,var_off=(0x2; 0xffc)"}, /* At the time the word size load is performed from R5, * its total fixed offset is NET_IP_ALIGN + reg->off (0) * which is 2. Then the variable offset is (4n+2), so * the total offset is 4-byte aligned and meets the * load's requirements. */ - {23, "R5=pkt(id=2,off=0,r=4,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc)"}, + {23, "R5=pkt(id=2,off=0,r=4,umin=14,umax=2054,var_off=(0x2; 0xffc)"}, }, }, { @@ -448,18 +448,18 @@ static struct bpf_align_test tests[] = { .prog_type = BPF_PROG_TYPE_SCHED_CLS, .result = REJECT, .matches = { - {3, "R5_w=pkt_end(id=0,off=0,imm=0)"}, + {3, "R5_w=pkt_end(off=0,imm=0)"}, /* (ptr - ptr) << 2 == unknown, (4n) */ - {5, "R5_w=inv(id=0,smax_value=9223372036854775804,umax_value=18446744073709551612,var_off=(0x0; 0xfffffffffffffffc)"}, + {5, "R5_w=scalar(smax=9223372036854775804,umax=18446744073709551612,var_off=(0x0; 0xfffffffffffffffc)"}, /* (4n) + 14 == (4n+2). We blow our bounds, because * the add could overflow. */ - {6, "R5_w=inv(id=0,smin_value=-9223372036854775806,smax_value=9223372036854775806,umin_value=2,umax_value=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"}, + {6, "R5_w=scalar(smin=-9223372036854775806,smax=9223372036854775806,umin=2,umax=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"}, /* Checked s>=0 */ - {9, "R5=inv(id=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"}, + {9, "R5=scalar(umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"}, /* packet pointer + nonnegative (4n+2) */ - {11, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"}, - {12, "R4_w=pkt(id=1,off=4,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"}, + {11, "R6_w=pkt(id=1,off=0,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"}, + {12, "R4_w=pkt(id=1,off=4,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"}, /* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine. * We checked the bounds, but it might have been able * to overflow if the packet pointer started in the @@ -467,7 +467,7 @@ static struct bpf_align_test tests[] = { * So we did not get a 'range' on R6, and the access * attempt will fail. */ - {15, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"}, + {15, "R6_w=pkt(id=1,off=0,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"}, } }, { @@ -502,23 +502,23 @@ static struct bpf_align_test tests[] = { /* Calculated offset in R6 has unknown value, but known * alignment of 4. */ - {6, "R2_w=pkt(id=0,off=0,r=8,imm=0)"}, - {8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, + {6, "R2_w=pkt(off=0,r=8,imm=0)"}, + {8, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"}, /* Adding 14 makes R6 be (4n+2) */ - {9, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"}, + {9, "R6_w=scalar(umin=14,umax=1034,var_off=(0x2; 0x7fc))"}, /* New unknown value in R7 is (4n) */ - {10, "R7_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, + {10, "R7_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"}, /* Subtracting it from R6 blows our unsigned bounds */ - {11, "R6=inv(id=0,smin_value=-1006,smax_value=1034,umin_value=2,umax_value=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"}, + {11, "R6=scalar(smin=-1006,smax=1034,umin=2,umax=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"}, /* Checked s>= 0 */ - {14, "R6=inv(id=0,umin_value=2,umax_value=1034,var_off=(0x2; 0x7fc))"}, + {14, "R6=scalar(umin=2,umax=1034,var_off=(0x2; 0x7fc))"}, /* At the time the word size load is performed from R5, * its total fixed offset is NET_IP_ALIGN + reg->off (0) * which is 2. Then the variable offset is (4n+2), so * the total offset is 4-byte aligned and meets the * load's requirements. */ - {20, "R5=pkt(id=2,off=0,r=4,umin_value=2,umax_value=1034,var_off=(0x2; 0x7fc)"}, + {20, "R5=pkt(id=2,off=0,r=4,umin=2,umax=1034,var_off=(0x2; 0x7fc)"}, }, }, @@ -556,23 +556,23 @@ static struct bpf_align_test tests[] = { /* Calculated offset in R6 has unknown value, but known * alignment of 4. */ - {6, "R2_w=pkt(id=0,off=0,r=8,imm=0)"}, - {9, "R6_w=inv(id=0,umax_value=60,var_off=(0x0; 0x3c))"}, + {6, "R2_w=pkt(off=0,r=8,imm=0)"}, + {9, "R6_w=scalar(umax=60,var_off=(0x0; 0x3c))"}, /* Adding 14 makes R6 be (4n+2) */ - {10, "R6_w=inv(id=0,umin_value=14,umax_value=74,var_off=(0x2; 0x7c))"}, + {10, "R6_w=scalar(umin=14,umax=74,var_off=(0x2; 0x7c))"}, /* Subtracting from packet pointer overflows ubounds */ - {13, "R5_w=pkt(id=2,off=0,r=8,umin_value=18446744073709551542,umax_value=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c)"}, + {13, "R5_w=pkt(id=2,off=0,r=8,umin=18446744073709551542,umax=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c)"}, /* New unknown value in R7 is (4n), >= 76 */ - {14, "R7_w=inv(id=0,umin_value=76,umax_value=1096,var_off=(0x0; 0x7fc))"}, + {14, "R7_w=scalar(umin=76,umax=1096,var_off=(0x0; 0x7fc))"}, /* Adding it to packet pointer gives nice bounds again */ - {16, "R5_w=pkt(id=3,off=0,r=0,umin_value=2,umax_value=1082,var_off=(0x2; 0xfffffffc)"}, + {16, "R5_w=pkt(id=3,off=0,r=0,umin=2,umax=1082,var_off=(0x2; 0xfffffffc)"}, /* At the time the word size load is performed from R5, * its total fixed offset is NET_IP_ALIGN + reg->off (0) * which is 2. Then the variable offset is (4n+2), so * the total offset is 4-byte aligned and meets the * load's requirements. */ - {20, "R5=pkt(id=3,off=0,r=4,umin_value=2,umax_value=1082,var_off=(0x2; 0xfffffffc)"}, + {20, "R5=pkt(id=3,off=0,r=4,umin=2,umax=1082,var_off=(0x2; 0xfffffffc)"}, }, }, }; @@ -648,8 +648,8 @@ static int do_test_single(struct bpf_align_test *test) /* Check the next line as well in case the previous line * did not have a corresponding bpf insn. Example: * func#0 @0 - * 0: R1=ctx(id=0,off=0,imm=0) R10=fp0 - * 0: (b7) r3 = 2 ; R3_w=inv2 + * 0: R1=ctx(off=0,imm=0) R10=fp0 + * 0: (b7) r3 = 2 ; R3_w=2 */ if (!strstr(line_ptr, m.match)) { cur_line = -1; diff --git a/tools/testing/selftests/bpf/prog_tests/atomics.c b/tools/testing/selftests/bpf/prog_tests/atomics.c index 86b7d5d84eec..13e101f370a1 100644 --- a/tools/testing/selftests/bpf/prog_tests/atomics.c +++ b/tools/testing/selftests/bpf/prog_tests/atomics.c @@ -7,19 +7,15 @@ static void test_add(struct atomics_lskel *skel) { int err, prog_fd; - __u32 duration = 0, retval; - int link_fd; - - link_fd = atomics_lskel__add__attach(skel); - if (!ASSERT_GT(link_fd, 0, "attach(add)")) - return; + LIBBPF_OPTS(bpf_test_run_opts, topts); + /* No need to attach it, just run it directly */ prog_fd = skel->progs.add.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); - if (CHECK(err || retval, "test_run add", - "err %d errno %d retval %d duration %d\n", err, errno, retval, duration)) - goto cleanup; + err = bpf_prog_test_run_opts(prog_fd, &topts); + if (!ASSERT_OK(err, "test_run_opts err")) + return; + if (!ASSERT_OK(topts.retval, "test_run_opts retval")) + return; ASSERT_EQ(skel->data->add64_value, 3, "add64_value"); ASSERT_EQ(skel->bss->add64_result, 1, "add64_result"); @@ -31,28 +27,20 @@ static void test_add(struct atomics_lskel *skel) ASSERT_EQ(skel->bss->add_stack_result, 1, "add_stack_result"); ASSERT_EQ(skel->data->add_noreturn_value, 3, "add_noreturn_value"); - -cleanup: - close(link_fd); } static void test_sub(struct atomics_lskel *skel) { int err, prog_fd; - __u32 duration = 0, retval; - int link_fd; - - link_fd = atomics_lskel__sub__attach(skel); - if (!ASSERT_GT(link_fd, 0, "attach(sub)")) - return; + LIBBPF_OPTS(bpf_test_run_opts, topts); + /* No need to attach it, just run it directly */ prog_fd = skel->progs.sub.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); - if (CHECK(err || retval, "test_run sub", - "err %d errno %d retval %d duration %d\n", - err, errno, retval, duration)) - goto cleanup; + err = bpf_prog_test_run_opts(prog_fd, &topts); + if (!ASSERT_OK(err, "test_run_opts err")) + return; + if (!ASSERT_OK(topts.retval, "test_run_opts retval")) + return; ASSERT_EQ(skel->data->sub64_value, -1, "sub64_value"); ASSERT_EQ(skel->bss->sub64_result, 1, "sub64_result"); @@ -64,27 +52,20 @@ static void test_sub(struct atomics_lskel *skel) ASSERT_EQ(skel->bss->sub_stack_result, 1, "sub_stack_result"); ASSERT_EQ(skel->data->sub_noreturn_value, -1, "sub_noreturn_value"); - -cleanup: - close(link_fd); } static void test_and(struct atomics_lskel *skel) { int err, prog_fd; - __u32 duration = 0, retval; - int link_fd; - - link_fd = atomics_lskel__and__attach(skel); - if (!ASSERT_GT(link_fd, 0, "attach(and)")) - return; + LIBBPF_OPTS(bpf_test_run_opts, topts); + /* No need to attach it, just run it directly */ prog_fd = skel->progs.and.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); - if (CHECK(err || retval, "test_run and", - "err %d errno %d retval %d duration %d\n", err, errno, retval, duration)) - goto cleanup; + err = bpf_prog_test_run_opts(prog_fd, &topts); + if (!ASSERT_OK(err, "test_run_opts err")) + return; + if (!ASSERT_OK(topts.retval, "test_run_opts retval")) + return; ASSERT_EQ(skel->data->and64_value, 0x010ull << 32, "and64_value"); ASSERT_EQ(skel->bss->and64_result, 0x110ull << 32, "and64_result"); @@ -93,27 +74,20 @@ static void test_and(struct atomics_lskel *skel) ASSERT_EQ(skel->bss->and32_result, 0x110, "and32_result"); ASSERT_EQ(skel->data->and_noreturn_value, 0x010ull << 32, "and_noreturn_value"); -cleanup: - close(link_fd); } static void test_or(struct atomics_lskel *skel) { int err, prog_fd; - __u32 duration = 0, retval; - int link_fd; - - link_fd = atomics_lskel__or__attach(skel); - if (!ASSERT_GT(link_fd, 0, "attach(or)")) - return; + LIBBPF_OPTS(bpf_test_run_opts, topts); + /* No need to attach it, just run it directly */ prog_fd = skel->progs.or.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); - if (CHECK(err || retval, "test_run or", - "err %d errno %d retval %d duration %d\n", - err, errno, retval, duration)) - goto cleanup; + err = bpf_prog_test_run_opts(prog_fd, &topts); + if (!ASSERT_OK(err, "test_run_opts err")) + return; + if (!ASSERT_OK(topts.retval, "test_run_opts retval")) + return; ASSERT_EQ(skel->data->or64_value, 0x111ull << 32, "or64_value"); ASSERT_EQ(skel->bss->or64_result, 0x110ull << 32, "or64_result"); @@ -122,26 +96,20 @@ static void test_or(struct atomics_lskel *skel) ASSERT_EQ(skel->bss->or32_result, 0x110, "or32_result"); ASSERT_EQ(skel->data->or_noreturn_value, 0x111ull << 32, "or_noreturn_value"); -cleanup: - close(link_fd); } static void test_xor(struct atomics_lskel *skel) { int err, prog_fd; - __u32 duration = 0, retval; - int link_fd; - - link_fd = atomics_lskel__xor__attach(skel); - if (!ASSERT_GT(link_fd, 0, "attach(xor)")) - return; + LIBBPF_OPTS(bpf_test_run_opts, topts); + /* No need to attach it, just run it directly */ prog_fd = skel->progs.xor.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); - if (CHECK(err || retval, "test_run xor", - "err %d errno %d retval %d duration %d\n", err, errno, retval, duration)) - goto cleanup; + err = bpf_prog_test_run_opts(prog_fd, &topts); + if (!ASSERT_OK(err, "test_run_opts err")) + return; + if (!ASSERT_OK(topts.retval, "test_run_opts retval")) + return; ASSERT_EQ(skel->data->xor64_value, 0x101ull << 32, "xor64_value"); ASSERT_EQ(skel->bss->xor64_result, 0x110ull << 32, "xor64_result"); @@ -150,26 +118,20 @@ static void test_xor(struct atomics_lskel *skel) ASSERT_EQ(skel->bss->xor32_result, 0x110, "xor32_result"); ASSERT_EQ(skel->data->xor_noreturn_value, 0x101ull << 32, "xor_nxoreturn_value"); -cleanup: - close(link_fd); } static void test_cmpxchg(struct atomics_lskel *skel) { int err, prog_fd; - __u32 duration = 0, retval; - int link_fd; - - link_fd = atomics_lskel__cmpxchg__attach(skel); - if (!ASSERT_GT(link_fd, 0, "attach(cmpxchg)")) - return; + LIBBPF_OPTS(bpf_test_run_opts, topts); + /* No need to attach it, just run it directly */ prog_fd = skel->progs.cmpxchg.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); - if (CHECK(err || retval, "test_run cmpxchg", - "err %d errno %d retval %d duration %d\n", err, errno, retval, duration)) - goto cleanup; + err = bpf_prog_test_run_opts(prog_fd, &topts); + if (!ASSERT_OK(err, "test_run_opts err")) + return; + if (!ASSERT_OK(topts.retval, "test_run_opts retval")) + return; ASSERT_EQ(skel->data->cmpxchg64_value, 2, "cmpxchg64_value"); ASSERT_EQ(skel->bss->cmpxchg64_result_fail, 1, "cmpxchg_result_fail"); @@ -178,45 +140,34 @@ static void test_cmpxchg(struct atomics_lskel *skel) ASSERT_EQ(skel->data->cmpxchg32_value, 2, "lcmpxchg32_value"); ASSERT_EQ(skel->bss->cmpxchg32_result_fail, 1, "cmpxchg_result_fail"); ASSERT_EQ(skel->bss->cmpxchg32_result_succeed, 1, "cmpxchg_result_succeed"); - -cleanup: - close(link_fd); } static void test_xchg(struct atomics_lskel *skel) { int err, prog_fd; - __u32 duration = 0, retval; - int link_fd; - - link_fd = atomics_lskel__xchg__attach(skel); - if (!ASSERT_GT(link_fd, 0, "attach(xchg)")) - return; + LIBBPF_OPTS(bpf_test_run_opts, topts); + /* No need to attach it, just run it directly */ prog_fd = skel->progs.xchg.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); - if (CHECK(err || retval, "test_run xchg", - "err %d errno %d retval %d duration %d\n", err, errno, retval, duration)) - goto cleanup; + err = bpf_prog_test_run_opts(prog_fd, &topts); + if (!ASSERT_OK(err, "test_run_opts err")) + return; + if (!ASSERT_OK(topts.retval, "test_run_opts retval")) + return; ASSERT_EQ(skel->data->xchg64_value, 2, "xchg64_value"); ASSERT_EQ(skel->bss->xchg64_result, 1, "xchg64_result"); ASSERT_EQ(skel->data->xchg32_value, 2, "xchg32_value"); ASSERT_EQ(skel->bss->xchg32_result, 1, "xchg32_result"); - -cleanup: - close(link_fd); } void test_atomics(void) { struct atomics_lskel *skel; - __u32 duration = 0; skel = atomics_lskel__open_and_load(); - if (CHECK(!skel, "skel_load", "atomics skeleton failed\n")) + if (!ASSERT_OK_PTR(skel, "atomics skeleton load")) return; if (skel->data->skip_tests) { diff --git a/tools/testing/selftests/bpf/prog_tests/attach_probe.c b/tools/testing/selftests/bpf/prog_tests/attach_probe.c index d0bd51eb23c8..d48f6e533e1e 100644 --- a/tools/testing/selftests/bpf/prog_tests/attach_probe.c +++ b/tools/testing/selftests/bpf/prog_tests/attach_probe.c @@ -5,9 +5,10 @@ /* this is how USDT semaphore is actually defined, except volatile modifier */ volatile unsigned short uprobe_ref_ctr __attribute__((unused)) __attribute((section(".probes"))); -/* attach point */ -static void method(void) { - return ; +/* uprobe attach point */ +static void trigger_func(void) +{ + asm volatile (""); } void test_attach_probe(void) @@ -17,8 +18,7 @@ void test_attach_probe(void) struct bpf_link *kprobe_link, *kretprobe_link; struct bpf_link *uprobe_link, *uretprobe_link; struct test_attach_probe* skel; - size_t uprobe_offset; - ssize_t base_addr, ref_ctr_offset; + ssize_t uprobe_offset, ref_ctr_offset; bool legacy; /* Check if new-style kprobe/uprobe API is supported. @@ -34,11 +34,9 @@ void test_attach_probe(void) */ legacy = access("/sys/bus/event_source/devices/kprobe/type", F_OK) != 0; - base_addr = get_base_addr(); - if (CHECK(base_addr < 0, "get_base_addr", - "failed to find base addr: %zd", base_addr)) + uprobe_offset = get_uprobe_offset(&trigger_func); + if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset")) return; - uprobe_offset = get_uprobe_offset(&method, base_addr); ref_ctr_offset = get_rel_offset((uintptr_t)&uprobe_ref_ctr); if (!ASSERT_GE(ref_ctr_offset, 0, "ref_ctr_offset")) @@ -103,7 +101,7 @@ void test_attach_probe(void) goto cleanup; /* trigger & validate uprobe & uretprobe */ - method(); + trigger_func(); if (CHECK(skel->bss->uprobe_res != 3, "check_uprobe_res", "wrong uprobe res: %d\n", skel->bss->uprobe_res)) diff --git a/tools/testing/selftests/bpf/prog_tests/bind_perm.c b/tools/testing/selftests/bpf/prog_tests/bind_perm.c index d0f06e40c16d..a1766a298bb7 100644 --- a/tools/testing/selftests/bpf/prog_tests/bind_perm.c +++ b/tools/testing/selftests/bpf/prog_tests/bind_perm.c @@ -1,13 +1,24 @@ // SPDX-License-Identifier: GPL-2.0 -#include -#include "bind_perm.skel.h" - +#define _GNU_SOURCE +#include +#include #include #include -#include + +#include "test_progs.h" +#include "cap_helpers.h" +#include "bind_perm.skel.h" static int duration; +static int create_netns(void) +{ + if (!ASSERT_OK(unshare(CLONE_NEWNET), "create netns")) + return -1; + + return 0; +} + void try_bind(int family, int port, int expected_errno) { struct sockaddr_storage addr = {}; @@ -38,43 +49,16 @@ void try_bind(int family, int port, int expected_errno) close(fd); } -bool cap_net_bind_service(cap_flag_value_t flag) -{ - const cap_value_t cap_net_bind_service = CAP_NET_BIND_SERVICE; - cap_flag_value_t original_value; - bool was_effective = false; - cap_t caps; - - caps = cap_get_proc(); - if (CHECK(!caps, "cap_get_proc", "errno %d", errno)) - goto free_caps; - - if (CHECK(cap_get_flag(caps, CAP_NET_BIND_SERVICE, CAP_EFFECTIVE, - &original_value), - "cap_get_flag", "errno %d", errno)) - goto free_caps; - - was_effective = (original_value == CAP_SET); - - if (CHECK(cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_net_bind_service, - flag), - "cap_set_flag", "errno %d", errno)) - goto free_caps; - - if (CHECK(cap_set_proc(caps), "cap_set_proc", "errno %d", errno)) - goto free_caps; - -free_caps: - CHECK(cap_free(caps), "cap_free", "errno %d", errno); - return was_effective; -} - void test_bind_perm(void) { - bool cap_was_effective; + const __u64 net_bind_svc_cap = 1ULL << CAP_NET_BIND_SERVICE; struct bind_perm *skel; + __u64 old_caps = 0; int cgroup_fd; + if (create_netns()) + return; + cgroup_fd = test__join_cgroup("/bind_perm"); if (CHECK(cgroup_fd < 0, "cg-join", "errno %d", errno)) return; @@ -91,7 +75,8 @@ void test_bind_perm(void) if (!ASSERT_OK_PTR(skel, "bind_v6_prog")) goto close_skeleton; - cap_was_effective = cap_net_bind_service(CAP_CLEAR); + ASSERT_OK(cap_disable_effective(net_bind_svc_cap, &old_caps), + "cap_disable_effective"); try_bind(AF_INET, 110, EACCES); try_bind(AF_INET6, 110, EACCES); @@ -99,8 +84,9 @@ void test_bind_perm(void) try_bind(AF_INET, 111, 0); try_bind(AF_INET6, 111, 0); - if (cap_was_effective) - cap_net_bind_service(CAP_SET); + if (old_caps & net_bind_svc_cap) + ASSERT_OK(cap_enable_effective(net_bind_svc_cap, NULL), + "cap_enable_effective"); close_skeleton: bind_perm__destroy(skel); diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c b/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c index 5eea3c3a40fe..923a6139b2d8 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c @@ -7,6 +7,13 @@ #include #include #include "test_bpf_cookie.skel.h" +#include "kprobe_multi.skel.h" + +/* uprobe attach point */ +static void trigger_func(void) +{ + asm volatile (""); +} static void kprobe_subtest(struct test_bpf_cookie *skel) { @@ -57,16 +64,188 @@ static void kprobe_subtest(struct test_bpf_cookie *skel) bpf_link__destroy(retlink2); } +static void kprobe_multi_test_run(struct kprobe_multi *skel) +{ + LIBBPF_OPTS(bpf_test_run_opts, topts); + int err, prog_fd; + + prog_fd = bpf_program__fd(skel->progs.trigger); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.retval, 0, "test_run"); + + ASSERT_EQ(skel->bss->kprobe_test1_result, 1, "kprobe_test1_result"); + ASSERT_EQ(skel->bss->kprobe_test2_result, 1, "kprobe_test2_result"); + ASSERT_EQ(skel->bss->kprobe_test3_result, 1, "kprobe_test3_result"); + ASSERT_EQ(skel->bss->kprobe_test4_result, 1, "kprobe_test4_result"); + ASSERT_EQ(skel->bss->kprobe_test5_result, 1, "kprobe_test5_result"); + ASSERT_EQ(skel->bss->kprobe_test6_result, 1, "kprobe_test6_result"); + ASSERT_EQ(skel->bss->kprobe_test7_result, 1, "kprobe_test7_result"); + ASSERT_EQ(skel->bss->kprobe_test8_result, 1, "kprobe_test8_result"); + + ASSERT_EQ(skel->bss->kretprobe_test1_result, 1, "kretprobe_test1_result"); + ASSERT_EQ(skel->bss->kretprobe_test2_result, 1, "kretprobe_test2_result"); + ASSERT_EQ(skel->bss->kretprobe_test3_result, 1, "kretprobe_test3_result"); + ASSERT_EQ(skel->bss->kretprobe_test4_result, 1, "kretprobe_test4_result"); + ASSERT_EQ(skel->bss->kretprobe_test5_result, 1, "kretprobe_test5_result"); + ASSERT_EQ(skel->bss->kretprobe_test6_result, 1, "kretprobe_test6_result"); + ASSERT_EQ(skel->bss->kretprobe_test7_result, 1, "kretprobe_test7_result"); + ASSERT_EQ(skel->bss->kretprobe_test8_result, 1, "kretprobe_test8_result"); +} + +static void kprobe_multi_link_api_subtest(void) +{ + int prog_fd, link1_fd = -1, link2_fd = -1; + struct kprobe_multi *skel = NULL; + LIBBPF_OPTS(bpf_link_create_opts, opts); + unsigned long long addrs[8]; + __u64 cookies[8]; + + if (!ASSERT_OK(load_kallsyms(), "load_kallsyms")) + goto cleanup; + + skel = kprobe_multi__open_and_load(); + if (!ASSERT_OK_PTR(skel, "fentry_raw_skel_load")) + goto cleanup; + + skel->bss->pid = getpid(); + skel->bss->test_cookie = true; + +#define GET_ADDR(__sym, __addr) ({ \ + __addr = ksym_get_addr(__sym); \ + if (!ASSERT_NEQ(__addr, 0, "ksym_get_addr " #__sym)) \ + goto cleanup; \ +}) + + GET_ADDR("bpf_fentry_test1", addrs[0]); + GET_ADDR("bpf_fentry_test2", addrs[1]); + GET_ADDR("bpf_fentry_test3", addrs[2]); + GET_ADDR("bpf_fentry_test4", addrs[3]); + GET_ADDR("bpf_fentry_test5", addrs[4]); + GET_ADDR("bpf_fentry_test6", addrs[5]); + GET_ADDR("bpf_fentry_test7", addrs[6]); + GET_ADDR("bpf_fentry_test8", addrs[7]); + +#undef GET_ADDR + + cookies[0] = 1; + cookies[1] = 2; + cookies[2] = 3; + cookies[3] = 4; + cookies[4] = 5; + cookies[5] = 6; + cookies[6] = 7; + cookies[7] = 8; + + opts.kprobe_multi.addrs = (const unsigned long *) &addrs; + opts.kprobe_multi.cnt = ARRAY_SIZE(addrs); + opts.kprobe_multi.cookies = (const __u64 *) &cookies; + prog_fd = bpf_program__fd(skel->progs.test_kprobe); + + link1_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_KPROBE_MULTI, &opts); + if (!ASSERT_GE(link1_fd, 0, "link1_fd")) + goto cleanup; + + cookies[0] = 8; + cookies[1] = 7; + cookies[2] = 6; + cookies[3] = 5; + cookies[4] = 4; + cookies[5] = 3; + cookies[6] = 2; + cookies[7] = 1; + + opts.kprobe_multi.flags = BPF_F_KPROBE_MULTI_RETURN; + prog_fd = bpf_program__fd(skel->progs.test_kretprobe); + + link2_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_KPROBE_MULTI, &opts); + if (!ASSERT_GE(link2_fd, 0, "link2_fd")) + goto cleanup; + + kprobe_multi_test_run(skel); + +cleanup: + close(link1_fd); + close(link2_fd); + kprobe_multi__destroy(skel); +} + +static void kprobe_multi_attach_api_subtest(void) +{ + struct bpf_link *link1 = NULL, *link2 = NULL; + LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); + LIBBPF_OPTS(bpf_test_run_opts, topts); + struct kprobe_multi *skel = NULL; + const char *syms[8] = { + "bpf_fentry_test1", + "bpf_fentry_test2", + "bpf_fentry_test3", + "bpf_fentry_test4", + "bpf_fentry_test5", + "bpf_fentry_test6", + "bpf_fentry_test7", + "bpf_fentry_test8", + }; + __u64 cookies[8]; + + skel = kprobe_multi__open_and_load(); + if (!ASSERT_OK_PTR(skel, "fentry_raw_skel_load")) + goto cleanup; + + skel->bss->pid = getpid(); + skel->bss->test_cookie = true; + + cookies[0] = 1; + cookies[1] = 2; + cookies[2] = 3; + cookies[3] = 4; + cookies[4] = 5; + cookies[5] = 6; + cookies[6] = 7; + cookies[7] = 8; + + opts.syms = syms; + opts.cnt = ARRAY_SIZE(syms); + opts.cookies = cookies; + + link1 = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kprobe, + NULL, &opts); + if (!ASSERT_OK_PTR(link1, "bpf_program__attach_kprobe_multi_opts")) + goto cleanup; + + cookies[0] = 8; + cookies[1] = 7; + cookies[2] = 6; + cookies[3] = 5; + cookies[4] = 4; + cookies[5] = 3; + cookies[6] = 2; + cookies[7] = 1; + + opts.retprobe = true; + + link2 = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kretprobe, + NULL, &opts); + if (!ASSERT_OK_PTR(link2, "bpf_program__attach_kprobe_multi_opts")) + goto cleanup; + + kprobe_multi_test_run(skel); + +cleanup: + bpf_link__destroy(link2); + bpf_link__destroy(link1); + kprobe_multi__destroy(skel); +} static void uprobe_subtest(struct test_bpf_cookie *skel) { DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts); struct bpf_link *link1 = NULL, *link2 = NULL; struct bpf_link *retlink1 = NULL, *retlink2 = NULL; - size_t uprobe_offset; - ssize_t base_addr; + ssize_t uprobe_offset; - base_addr = get_base_addr(); - uprobe_offset = get_uprobe_offset(&get_base_addr, base_addr); + uprobe_offset = get_uprobe_offset(&trigger_func); + if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset")) + goto cleanup; /* attach two uprobes */ opts.bpf_cookie = 0x100; @@ -99,7 +278,7 @@ static void uprobe_subtest(struct test_bpf_cookie *skel) goto cleanup; /* trigger uprobe && uretprobe */ - get_base_addr(); + trigger_func(); ASSERT_EQ(skel->bss->uprobe_res, 0x100 | 0x200, "uprobe_res"); ASSERT_EQ(skel->bss->uretprobe_res, 0x1000 | 0x2000, "uretprobe_res"); @@ -193,7 +372,7 @@ static void pe_subtest(struct test_bpf_cookie *skel) attr.type = PERF_TYPE_SOFTWARE; attr.config = PERF_COUNT_SW_CPU_CLOCK; attr.freq = 1; - attr.sample_freq = 4000; + attr.sample_freq = 1000; pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC); if (!ASSERT_GE(pfd, 0, "perf_fd")) goto cleanup; @@ -243,6 +422,10 @@ void test_bpf_cookie(void) if (test__start_subtest("kprobe")) kprobe_subtest(skel); + if (test__start_subtest("multi_kprobe_link_api")) + kprobe_multi_link_api_subtest(); + if (test__start_subtest("multi_kprobe_attach_api")) + kprobe_multi_attach_api_subtest(); if (test__start_subtest("uprobe")) uprobe_subtest(skel); if (test__start_subtest("tracepoint")) diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c index b84f859b1267..5142a7d130b2 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c @@ -138,6 +138,24 @@ static void test_task(void) bpf_iter_task__destroy(skel); } +static void test_task_sleepable(void) +{ + struct bpf_iter_task *skel; + + skel = bpf_iter_task__open_and_load(); + if (!ASSERT_OK_PTR(skel, "bpf_iter_task__open_and_load")) + return; + + do_dummy_read(skel->progs.dump_task_sleepable); + + ASSERT_GT(skel->bss->num_expected_failure_copy_from_user_task, 0, + "num_expected_failure_copy_from_user_task"); + ASSERT_GT(skel->bss->num_success_copy_from_user_task, 0, + "num_success_copy_from_user_task"); + + bpf_iter_task__destroy(skel); +} + static void test_task_stack(void) { struct bpf_iter_task_stack *skel; @@ -1252,6 +1270,8 @@ void test_bpf_iter(void) test_bpf_map(); if (test__start_subtest("task")) test_task(); + if (test__start_subtest("task_sleepable")) + test_task_sleepable(); if (test__start_subtest("task_stack")) test_task_stack(); if (test__start_subtest("task_file")) diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter_setsockopt_unix.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter_setsockopt_unix.c new file mode 100644 index 000000000000..ee725d4d98a5 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter_setsockopt_unix.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright Amazon.com Inc. or its affiliates. */ +#include +#include +#include +#include "bpf_iter_setsockopt_unix.skel.h" + +#define NR_CASES 5 + +static int create_unix_socket(struct bpf_iter_setsockopt_unix *skel) +{ + struct sockaddr_un addr = { + .sun_family = AF_UNIX, + .sun_path = "", + }; + socklen_t len; + int fd, err; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (!ASSERT_NEQ(fd, -1, "socket")) + return -1; + + len = offsetof(struct sockaddr_un, sun_path); + err = bind(fd, (struct sockaddr *)&addr, len); + if (!ASSERT_OK(err, "bind")) + return -1; + + len = sizeof(addr); + err = getsockname(fd, (struct sockaddr *)&addr, &len); + if (!ASSERT_OK(err, "getsockname")) + return -1; + + memcpy(&skel->bss->sun_path, &addr.sun_path, + len - offsetof(struct sockaddr_un, sun_path)); + + return fd; +} + +static void test_sndbuf(struct bpf_iter_setsockopt_unix *skel, int fd) +{ + socklen_t optlen; + int i, err; + + for (i = 0; i < NR_CASES; i++) { + if (!ASSERT_NEQ(skel->data->sndbuf_getsockopt[i], -1, + "bpf_(get|set)sockopt")) + return; + + err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, + &(skel->data->sndbuf_setsockopt[i]), + sizeof(skel->data->sndbuf_setsockopt[i])); + if (!ASSERT_OK(err, "setsockopt")) + return; + + optlen = sizeof(skel->bss->sndbuf_getsockopt_expected[i]); + err = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, + &(skel->bss->sndbuf_getsockopt_expected[i]), + &optlen); + if (!ASSERT_OK(err, "getsockopt")) + return; + + if (!ASSERT_EQ(skel->data->sndbuf_getsockopt[i], + skel->bss->sndbuf_getsockopt_expected[i], + "bpf_(get|set)sockopt")) + return; + } +} + +void test_bpf_iter_setsockopt_unix(void) +{ + struct bpf_iter_setsockopt_unix *skel; + int err, unix_fd, iter_fd; + char buf; + + skel = bpf_iter_setsockopt_unix__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + + unix_fd = create_unix_socket(skel); + if (!ASSERT_NEQ(unix_fd, -1, "create_unix_server")) + goto destroy; + + skel->links.change_sndbuf = bpf_program__attach_iter(skel->progs.change_sndbuf, NULL); + if (!ASSERT_OK_PTR(skel->links.change_sndbuf, "bpf_program__attach_iter")) + goto destroy; + + iter_fd = bpf_iter_create(bpf_link__fd(skel->links.change_sndbuf)); + if (!ASSERT_GE(iter_fd, 0, "bpf_iter_create")) + goto destroy; + + while ((err = read(iter_fd, &buf, sizeof(buf))) == -1 && + errno == EAGAIN) + ; + if (!ASSERT_OK(err, "read iter error")) + goto destroy; + + test_sndbuf(skel, unix_fd); +destroy: + bpf_iter_setsockopt_unix__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c b/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c new file mode 100644 index 000000000000..d43f548c572c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ksym_race.skel.h" +#include "bpf_mod_race.skel.h" +#include "kfunc_call_race.skel.h" + +/* This test crafts a race between btf_try_get_module and do_init_module, and + * checks whether btf_try_get_module handles the invocation for a well-formed + * but uninitialized module correctly. Unless the module has completed its + * initcalls, the verifier should fail the program load and return ENXIO. + * + * userfaultfd is used to trigger a fault in an fmod_ret program, and make it + * sleep, then the BPF program is loaded and the return value from verifier is + * inspected. After this, the userfaultfd is closed so that the module loading + * thread makes forward progress, and fmod_ret injects an error so that the + * module load fails and it is freed. + * + * If the verifier succeeded in loading the supplied program, it will end up + * taking reference to freed module, and trigger a crash when the program fd + * is closed later. This is true for both kfuncs and ksyms. In both cases, + * the crash is triggered inside bpf_prog_free_deferred, when module reference + * is finally released. + */ + +struct test_config { + const char *str_open; + void *(*bpf_open_and_load)(); + void (*bpf_destroy)(void *); +}; + +enum test_state { + _TS_INVALID, + TS_MODULE_LOAD, + TS_MODULE_LOAD_FAIL, +}; + +static _Atomic enum test_state state = _TS_INVALID; + +static int sys_finit_module(int fd, const char *param_values, int flags) +{ + return syscall(__NR_finit_module, fd, param_values, flags); +} + +static int sys_delete_module(const char *name, unsigned int flags) +{ + return syscall(__NR_delete_module, name, flags); +} + +static int load_module(const char *mod) +{ + int ret, fd; + + fd = open("bpf_testmod.ko", O_RDONLY); + if (fd < 0) + return fd; + + ret = sys_finit_module(fd, "", 0); + close(fd); + if (ret < 0) + return ret; + return 0; +} + +static void *load_module_thread(void *p) +{ + + if (!ASSERT_NEQ(load_module("bpf_testmod.ko"), 0, "load_module_thread must fail")) + atomic_store(&state, TS_MODULE_LOAD); + else + atomic_store(&state, TS_MODULE_LOAD_FAIL); + return p; +} + +static int sys_userfaultfd(int flags) +{ + return syscall(__NR_userfaultfd, flags); +} + +static int test_setup_uffd(void *fault_addr) +{ + struct uffdio_register uffd_register = {}; + struct uffdio_api uffd_api = {}; + int uffd; + + uffd = sys_userfaultfd(O_CLOEXEC); + if (uffd < 0) + return -errno; + + uffd_api.api = UFFD_API; + uffd_api.features = 0; + if (ioctl(uffd, UFFDIO_API, &uffd_api)) { + close(uffd); + return -1; + } + + uffd_register.range.start = (unsigned long)fault_addr; + uffd_register.range.len = 4096; + uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING; + if (ioctl(uffd, UFFDIO_REGISTER, &uffd_register)) { + close(uffd); + return -1; + } + return uffd; +} + +static void test_bpf_mod_race_config(const struct test_config *config) +{ + void *fault_addr, *skel_fail; + struct bpf_mod_race *skel; + struct uffd_msg uffd_msg; + pthread_t load_mod_thrd; + _Atomic int *blockingp; + int uffd, ret; + + fault_addr = mmap(0, 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (!ASSERT_NEQ(fault_addr, MAP_FAILED, "mmap for uffd registration")) + return; + + if (!ASSERT_OK(sys_delete_module("bpf_testmod", 0), "unload bpf_testmod")) + goto end_mmap; + + skel = bpf_mod_race__open(); + if (!ASSERT_OK_PTR(skel, "bpf_mod_kfunc_race__open")) + goto end_module; + + skel->rodata->bpf_mod_race_config.tgid = getpid(); + skel->rodata->bpf_mod_race_config.inject_error = -4242; + skel->rodata->bpf_mod_race_config.fault_addr = fault_addr; + if (!ASSERT_OK(bpf_mod_race__load(skel), "bpf_mod___load")) + goto end_destroy; + blockingp = (_Atomic int *)&skel->bss->bpf_blocking; + + if (!ASSERT_OK(bpf_mod_race__attach(skel), "bpf_mod_kfunc_race__attach")) + goto end_destroy; + + uffd = test_setup_uffd(fault_addr); + if (!ASSERT_GE(uffd, 0, "userfaultfd open + register address")) + goto end_destroy; + + if (!ASSERT_OK(pthread_create(&load_mod_thrd, NULL, load_module_thread, NULL), + "load module thread")) + goto end_uffd; + + /* Now, we either fail loading module, or block in bpf prog, spin to find out */ + while (!atomic_load(&state) && !atomic_load(blockingp)) + ; + if (!ASSERT_EQ(state, _TS_INVALID, "module load should block")) + goto end_join; + if (!ASSERT_EQ(*blockingp, 1, "module load blocked")) { + pthread_kill(load_mod_thrd, SIGKILL); + goto end_uffd; + } + + /* We might have set bpf_blocking to 1, but may have not blocked in + * bpf_copy_from_user. Read userfaultfd descriptor to verify that. + */ + if (!ASSERT_EQ(read(uffd, &uffd_msg, sizeof(uffd_msg)), sizeof(uffd_msg), + "read uffd block event")) + goto end_join; + if (!ASSERT_EQ(uffd_msg.event, UFFD_EVENT_PAGEFAULT, "read uffd event is pagefault")) + goto end_join; + + /* We know that load_mod_thrd is blocked in the fmod_ret program, the + * module state is still MODULE_STATE_COMING because mod->init hasn't + * returned. This is the time we try to load a program calling kfunc and + * check if we get ENXIO from verifier. + */ + skel_fail = config->bpf_open_and_load(); + ret = errno; + if (!ASSERT_EQ(skel_fail, NULL, config->str_open)) { + /* Close uffd to unblock load_mod_thrd */ + close(uffd); + uffd = -1; + while (atomic_load(blockingp) != 2) + ; + ASSERT_OK(kern_sync_rcu(), "kern_sync_rcu"); + config->bpf_destroy(skel_fail); + goto end_join; + + } + ASSERT_EQ(ret, ENXIO, "verifier returns ENXIO"); + ASSERT_EQ(skel->data->res_try_get_module, false, "btf_try_get_module == false"); + + close(uffd); + uffd = -1; +end_join: + pthread_join(load_mod_thrd, NULL); + if (uffd < 0) + ASSERT_EQ(atomic_load(&state), TS_MODULE_LOAD_FAIL, "load_mod_thrd success"); +end_uffd: + if (uffd >= 0) + close(uffd); +end_destroy: + bpf_mod_race__destroy(skel); + ASSERT_OK(kern_sync_rcu(), "kern_sync_rcu"); +end_module: + sys_delete_module("bpf_testmod", 0); + ASSERT_OK(load_module("bpf_testmod.ko"), "restore bpf_testmod"); +end_mmap: + munmap(fault_addr, 4096); + atomic_store(&state, _TS_INVALID); +} + +static const struct test_config ksym_config = { + .str_open = "ksym_race__open_and_load", + .bpf_open_and_load = (void *)ksym_race__open_and_load, + .bpf_destroy = (void *)ksym_race__destroy, +}; + +static const struct test_config kfunc_config = { + .str_open = "kfunc_call_race__open_and_load", + .bpf_open_and_load = (void *)kfunc_call_race__open_and_load, + .bpf_destroy = (void *)kfunc_call_race__destroy, +}; + +void serial_test_bpf_mod_race(void) +{ + if (test__start_subtest("ksym (used_btfs UAF)")) + test_bpf_mod_race_config(&ksym_config); + if (test__start_subtest("kfunc (kfunc_btf_tab UAF)")) + test_bpf_mod_race_config(&kfunc_config); +} diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c new file mode 100644 index 000000000000..dd30b1e3a67c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include "test_bpf_nf.skel.h" + +enum { + TEST_XDP, + TEST_TC_BPF, +}; + +void test_bpf_nf_ct(int mode) +{ + struct test_bpf_nf *skel; + int prog_fd, err; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); + + skel = test_bpf_nf__open_and_load(); + if (!ASSERT_OK_PTR(skel, "test_bpf_nf__open_and_load")) + return; + + if (mode == TEST_XDP) + prog_fd = bpf_program__fd(skel->progs.nf_xdp_ct_test); + else + prog_fd = bpf_program__fd(skel->progs.nf_skb_ct_test); + + err = bpf_prog_test_run_opts(prog_fd, &topts); + if (!ASSERT_OK(err, "bpf_prog_test_run")) + goto end; + + ASSERT_EQ(skel->bss->test_einval_bpf_tuple, -EINVAL, "Test EINVAL for NULL bpf_tuple"); + ASSERT_EQ(skel->bss->test_einval_reserved, -EINVAL, "Test EINVAL for reserved not set to 0"); + ASSERT_EQ(skel->bss->test_einval_netns_id, -EINVAL, "Test EINVAL for netns_id < -1"); + ASSERT_EQ(skel->bss->test_einval_len_opts, -EINVAL, "Test EINVAL for len__opts != NF_BPF_CT_OPTS_SZ"); + ASSERT_EQ(skel->bss->test_eproto_l4proto, -EPROTO, "Test EPROTO for l4proto != TCP or UDP"); + ASSERT_EQ(skel->bss->test_enonet_netns_id, -ENONET, "Test ENONET for bad but valid netns_id"); + ASSERT_EQ(skel->bss->test_enoent_lookup, -ENOENT, "Test ENOENT for failed lookup"); + ASSERT_EQ(skel->bss->test_eafnosupport, -EAFNOSUPPORT, "Test EAFNOSUPPORT for invalid len__tuple"); +end: + test_bpf_nf__destroy(skel); +} + +void test_bpf_nf(void) +{ + if (test__start_subtest("xdp-ct")) + test_bpf_nf_ct(TEST_XDP); + if (test__start_subtest("tc-bpf-ct")) + test_bpf_nf_ct(TEST_TC_BPF); +} diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c index 8ba53acf9eb4..ec823561b912 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf.c +++ b/tools/testing/selftests/bpf/prog_tests/btf.c @@ -3938,6 +3938,25 @@ static struct btf_raw_test raw_tests[] = { .btf_load_err = true, .err_str = "Invalid component_idx", }, +{ + .descr = "decl_tag test #15, func, invalid func proto", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_DECL_TAG_ENC(NAME_TBD, 3, 0), /* [2] */ + BTF_FUNC_ENC(NAME_TBD, 8), /* [3] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0tag\0func"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "tag_type_check_btf", + .key_size = sizeof(int), + .value_size = 4, + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 1, + .btf_load_err = true, + .err_str = "Invalid type_id", +}, { .descr = "type_tag test #1", .raw_types = { @@ -4560,6 +4579,8 @@ static void do_test_file(unsigned int test_num) has_btf_ext = btf_ext != NULL; btf_ext__free(btf_ext); + /* temporary disable LIBBPF_STRICT_MAP_DEFINITIONS to test legacy maps */ + libbpf_set_strict_mode(LIBBPF_STRICT_ALL & ~LIBBPF_STRICT_MAP_DEFINITIONS); obj = bpf_object__open(test->file); err = libbpf_get_error(obj); if (CHECK(err, "obj: %d", err)) @@ -4684,6 +4705,8 @@ static void do_test_file(unsigned int test_num) fprintf(stderr, "OK"); done: + libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + btf__free(btf); free(func_info); bpf_object__close(obj); @@ -6533,7 +6556,7 @@ static int test_get_linfo(const struct prog_info_raw_test *test, static void do_test_info_raw(unsigned int test_num) { const struct prog_info_raw_test *test = &info_raw_tests[test_num - 1]; - unsigned int raw_btf_size, linfo_str_off, linfo_size; + unsigned int raw_btf_size, linfo_str_off, linfo_size = 0; int btf_fd = -1, prog_fd = -1, err = 0; void *raw_btf, *patched_linfo = NULL; const char *ret_next_str; diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c index 9e26903f9170..5fce7008d1ff 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c +++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c @@ -148,22 +148,38 @@ static void test_btf_dump_incremental(void) /* First, generate BTF corresponding to the following C code: * - * enum { VAL = 1 }; + * enum x; + * + * enum x { X = 1 }; + * + * enum { Y = 1 }; + * + * struct s; * * struct s { int x; }; * */ + id = btf__add_enum(btf, "x", 4); + ASSERT_EQ(id, 1, "enum_declaration_id"); + id = btf__add_enum(btf, "x", 4); + ASSERT_EQ(id, 2, "named_enum_id"); + err = btf__add_enum_value(btf, "X", 1); + ASSERT_OK(err, "named_enum_val_ok"); + id = btf__add_enum(btf, NULL, 4); - ASSERT_EQ(id, 1, "enum_id"); - err = btf__add_enum_value(btf, "VAL", 1); - ASSERT_OK(err, "enum_val_ok"); + ASSERT_EQ(id, 3, "anon_enum_id"); + err = btf__add_enum_value(btf, "Y", 1); + ASSERT_OK(err, "anon_enum_val_ok"); id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED); - ASSERT_EQ(id, 2, "int_id"); + ASSERT_EQ(id, 4, "int_id"); + + id = btf__add_fwd(btf, "s", BTF_FWD_STRUCT); + ASSERT_EQ(id, 5, "fwd_id"); id = btf__add_struct(btf, "s", 4); - ASSERT_EQ(id, 3, "struct_id"); - err = btf__add_field(btf, "x", 2, 0, 0); + ASSERT_EQ(id, 6, "struct_id"); + err = btf__add_field(btf, "x", 4, 0, 0); ASSERT_OK(err, "field_ok"); for (i = 1; i < btf__type_cnt(btf); i++) { @@ -173,11 +189,20 @@ static void test_btf_dump_incremental(void) fflush(dump_buf_file); dump_buf[dump_buf_sz] = 0; /* some libc implementations don't do this */ + ASSERT_STREQ(dump_buf, -"enum {\n" -" VAL = 1,\n" +"enum x;\n" +"\n" +"enum x {\n" +" X = 1,\n" "};\n" "\n" +"enum {\n" +" Y = 1,\n" +"};\n" +"\n" +"struct s;\n" +"\n" "struct s {\n" " int x;\n" "};\n\n", "c_dump1"); @@ -199,10 +224,12 @@ static void test_btf_dump_incremental(void) fseek(dump_buf_file, 0, SEEK_SET); id = btf__add_struct(btf, "s", 4); - ASSERT_EQ(id, 4, "struct_id"); - err = btf__add_field(btf, "x", 1, 0, 0); + ASSERT_EQ(id, 7, "struct_id"); + err = btf__add_field(btf, "x", 2, 0, 0); ASSERT_OK(err, "field_ok"); - err = btf__add_field(btf, "s", 3, 32, 0); + err = btf__add_field(btf, "y", 3, 32, 0); + ASSERT_OK(err, "field_ok"); + err = btf__add_field(btf, "s", 6, 64, 0); ASSERT_OK(err, "field_ok"); for (i = 1; i < btf__type_cnt(btf); i++) { @@ -214,9 +241,10 @@ static void test_btf_dump_incremental(void) dump_buf[dump_buf_sz] = 0; /* some libc implementations don't do this */ ASSERT_STREQ(dump_buf, "struct s___2 {\n" +" enum x x;\n" " enum {\n" -" VAL___2 = 1,\n" -" } x;\n" +" Y___2 = 1,\n" +" } y;\n" " struct s s;\n" "};\n\n" , "c_dump1"); diff --git a/tools/testing/selftests/bpf/prog_tests/btf_tag.c b/tools/testing/selftests/bpf/prog_tests/btf_tag.c index 88d63e23e35f..071430cd54de 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf_tag.c +++ b/tools/testing/selftests/bpf/prog_tests/btf_tag.c @@ -1,19 +1,22 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2021 Facebook */ #include -#include "btf_decl_tag.skel.h" +#include +#include "test_btf_decl_tag.skel.h" /* struct btf_type_tag_test is referenced in btf_type_tag.skel.h */ struct btf_type_tag_test { int **p; }; #include "btf_type_tag.skel.h" +#include "btf_type_tag_user.skel.h" +#include "btf_type_tag_percpu.skel.h" static void test_btf_decl_tag(void) { - struct btf_decl_tag *skel; + struct test_btf_decl_tag *skel; - skel = btf_decl_tag__open_and_load(); + skel = test_btf_decl_tag__open_and_load(); if (!ASSERT_OK_PTR(skel, "btf_decl_tag")) return; @@ -22,7 +25,7 @@ static void test_btf_decl_tag(void) test__skip(); } - btf_decl_tag__destroy(skel); + test_btf_decl_tag__destroy(skel); } static void test_btf_type_tag(void) @@ -41,10 +44,206 @@ static void test_btf_type_tag(void) btf_type_tag__destroy(skel); } +/* loads vmlinux_btf as well as module_btf. If the caller passes NULL as + * module_btf, it will not load module btf. + * + * Returns 0 on success. + * Return -1 On error. In case of error, the loaded btf will be freed and the + * input parameters will be set to pointing to NULL. + */ +static int load_btfs(struct btf **vmlinux_btf, struct btf **module_btf, + bool needs_vmlinux_tag) +{ + const char *module_name = "bpf_testmod"; + __s32 type_id; + + if (!env.has_testmod) { + test__skip(); + return -1; + } + + *vmlinux_btf = btf__load_vmlinux_btf(); + if (!ASSERT_OK_PTR(*vmlinux_btf, "could not load vmlinux BTF")) + return -1; + + if (!needs_vmlinux_tag) + goto load_module_btf; + + /* skip the test if the vmlinux does not have __user tags */ + type_id = btf__find_by_name_kind(*vmlinux_btf, "user", BTF_KIND_TYPE_TAG); + if (type_id <= 0) { + printf("%s:SKIP: btf_type_tag attribute not in vmlinux btf", __func__); + test__skip(); + goto free_vmlinux_btf; + } + +load_module_btf: + /* skip loading module_btf, if not requested by caller */ + if (!module_btf) + return 0; + + *module_btf = btf__load_module_btf(module_name, *vmlinux_btf); + if (!ASSERT_OK_PTR(*module_btf, "could not load module BTF")) + goto free_vmlinux_btf; + + /* skip the test if the module does not have __user tags */ + type_id = btf__find_by_name_kind(*module_btf, "user", BTF_KIND_TYPE_TAG); + if (type_id <= 0) { + printf("%s:SKIP: btf_type_tag attribute not in %s", __func__, module_name); + test__skip(); + goto free_module_btf; + } + + return 0; + +free_module_btf: + btf__free(*module_btf); +free_vmlinux_btf: + btf__free(*vmlinux_btf); + + *vmlinux_btf = NULL; + if (module_btf) + *module_btf = NULL; + return -1; +} + +static void test_btf_type_tag_mod_user(bool load_test_user1) +{ + struct btf *vmlinux_btf = NULL, *module_btf = NULL; + struct btf_type_tag_user *skel; + int err; + + if (load_btfs(&vmlinux_btf, &module_btf, /*needs_vmlinux_tag=*/false)) + return; + + skel = btf_type_tag_user__open(); + if (!ASSERT_OK_PTR(skel, "btf_type_tag_user")) + goto cleanup; + + bpf_program__set_autoload(skel->progs.test_sys_getsockname, false); + if (load_test_user1) + bpf_program__set_autoload(skel->progs.test_user2, false); + else + bpf_program__set_autoload(skel->progs.test_user1, false); + + err = btf_type_tag_user__load(skel); + ASSERT_ERR(err, "btf_type_tag_user"); + + btf_type_tag_user__destroy(skel); + +cleanup: + btf__free(module_btf); + btf__free(vmlinux_btf); +} + +static void test_btf_type_tag_vmlinux_user(void) +{ + struct btf_type_tag_user *skel; + struct btf *vmlinux_btf = NULL; + int err; + + if (load_btfs(&vmlinux_btf, NULL, /*needs_vmlinux_tag=*/true)) + return; + + skel = btf_type_tag_user__open(); + if (!ASSERT_OK_PTR(skel, "btf_type_tag_user")) + goto cleanup; + + bpf_program__set_autoload(skel->progs.test_user2, false); + bpf_program__set_autoload(skel->progs.test_user1, false); + + err = btf_type_tag_user__load(skel); + ASSERT_ERR(err, "btf_type_tag_user"); + + btf_type_tag_user__destroy(skel); + +cleanup: + btf__free(vmlinux_btf); +} + +static void test_btf_type_tag_mod_percpu(bool load_test_percpu1) +{ + struct btf *vmlinux_btf, *module_btf; + struct btf_type_tag_percpu *skel; + int err; + + if (load_btfs(&vmlinux_btf, &module_btf, /*needs_vmlinux_tag=*/false)) + return; + + skel = btf_type_tag_percpu__open(); + if (!ASSERT_OK_PTR(skel, "btf_type_tag_percpu")) + goto cleanup; + + bpf_program__set_autoload(skel->progs.test_percpu_load, false); + bpf_program__set_autoload(skel->progs.test_percpu_helper, false); + if (load_test_percpu1) + bpf_program__set_autoload(skel->progs.test_percpu2, false); + else + bpf_program__set_autoload(skel->progs.test_percpu1, false); + + err = btf_type_tag_percpu__load(skel); + ASSERT_ERR(err, "btf_type_tag_percpu"); + + btf_type_tag_percpu__destroy(skel); + +cleanup: + btf__free(module_btf); + btf__free(vmlinux_btf); +} + +static void test_btf_type_tag_vmlinux_percpu(bool load_test) +{ + struct btf_type_tag_percpu *skel; + struct btf *vmlinux_btf = NULL; + int err; + + if (load_btfs(&vmlinux_btf, NULL, /*needs_vmlinux_tag=*/true)) + return; + + skel = btf_type_tag_percpu__open(); + if (!ASSERT_OK_PTR(skel, "btf_type_tag_percpu")) + goto cleanup; + + bpf_program__set_autoload(skel->progs.test_percpu2, false); + bpf_program__set_autoload(skel->progs.test_percpu1, false); + if (load_test) { + bpf_program__set_autoload(skel->progs.test_percpu_helper, false); + + err = btf_type_tag_percpu__load(skel); + ASSERT_ERR(err, "btf_type_tag_percpu_load"); + } else { + bpf_program__set_autoload(skel->progs.test_percpu_load, false); + + err = btf_type_tag_percpu__load(skel); + ASSERT_OK(err, "btf_type_tag_percpu_helper"); + } + + btf_type_tag_percpu__destroy(skel); + +cleanup: + btf__free(vmlinux_btf); +} + void test_btf_tag(void) { if (test__start_subtest("btf_decl_tag")) test_btf_decl_tag(); if (test__start_subtest("btf_type_tag")) test_btf_type_tag(); + + if (test__start_subtest("btf_type_tag_user_mod1")) + test_btf_type_tag_mod_user(true); + if (test__start_subtest("btf_type_tag_user_mod2")) + test_btf_type_tag_mod_user(false); + if (test__start_subtest("btf_type_tag_sys_user_vmlinux")) + test_btf_type_tag_vmlinux_user(); + + if (test__start_subtest("btf_type_tag_percpu_mod1")) + test_btf_type_tag_mod_percpu(true); + if (test__start_subtest("btf_type_tag_percpu_mod2")) + test_btf_type_tag_mod_percpu(false); + if (test__start_subtest("btf_type_tag_percpu_vmlinux_load")) + test_btf_type_tag_vmlinux_percpu(true); + if (test__start_subtest("btf_type_tag_percpu_vmlinux_helper")) + test_btf_type_tag_vmlinux_percpu(false); } diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c index 858916d11e2e..9367bd2f0ae1 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c @@ -14,7 +14,7 @@ static int prog_load(void) BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = 1 */ BPF_EXIT_INSN(), }; - size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); + size_t insns_cnt = ARRAY_SIZE(prog); return bpf_test_load_program(BPF_PROG_TYPE_CGROUP_SKB, prog, insns_cnt, "GPL", 0, diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c index d3e8f729c623..db0b7bac78d1 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c @@ -63,7 +63,7 @@ static int prog_load_cnt(int verdict, int val) BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ BPF_EXIT_INSN(), }; - size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); + size_t insns_cnt = ARRAY_SIZE(prog); int ret; ret = bpf_test_load_program(BPF_PROG_TYPE_CGROUP_SKB, @@ -194,14 +194,14 @@ void serial_test_cgroup_attach_multi(void) attach_opts.flags = BPF_F_ALLOW_OVERRIDE | BPF_F_REPLACE; attach_opts.replace_prog_fd = allow_prog[0]; - if (CHECK(!bpf_prog_attach_xattr(allow_prog[6], cg1, + if (CHECK(!bpf_prog_attach_opts(allow_prog[6], cg1, BPF_CGROUP_INET_EGRESS, &attach_opts), "fail_prog_replace_override", "unexpected success\n")) goto err; CHECK_FAIL(errno != EINVAL); attach_opts.flags = BPF_F_REPLACE; - if (CHECK(!bpf_prog_attach_xattr(allow_prog[6], cg1, + if (CHECK(!bpf_prog_attach_opts(allow_prog[6], cg1, BPF_CGROUP_INET_EGRESS, &attach_opts), "fail_prog_replace_no_multi", "unexpected success\n")) goto err; @@ -209,7 +209,7 @@ void serial_test_cgroup_attach_multi(void) attach_opts.flags = BPF_F_ALLOW_MULTI | BPF_F_REPLACE; attach_opts.replace_prog_fd = -1; - if (CHECK(!bpf_prog_attach_xattr(allow_prog[6], cg1, + if (CHECK(!bpf_prog_attach_opts(allow_prog[6], cg1, BPF_CGROUP_INET_EGRESS, &attach_opts), "fail_prog_replace_bad_fd", "unexpected success\n")) goto err; @@ -217,7 +217,7 @@ void serial_test_cgroup_attach_multi(void) /* replacing a program that is not attached to cgroup should fail */ attach_opts.replace_prog_fd = allow_prog[3]; - if (CHECK(!bpf_prog_attach_xattr(allow_prog[6], cg1, + if (CHECK(!bpf_prog_attach_opts(allow_prog[6], cg1, BPF_CGROUP_INET_EGRESS, &attach_opts), "fail_prog_replace_no_ent", "unexpected success\n")) goto err; @@ -225,14 +225,14 @@ void serial_test_cgroup_attach_multi(void) /* replace 1st from the top program */ attach_opts.replace_prog_fd = allow_prog[0]; - if (CHECK(bpf_prog_attach_xattr(allow_prog[6], cg1, + if (CHECK(bpf_prog_attach_opts(allow_prog[6], cg1, BPF_CGROUP_INET_EGRESS, &attach_opts), "prog_replace", "errno=%d\n", errno)) goto err; /* replace program with itself */ attach_opts.replace_prog_fd = allow_prog[6]; - if (CHECK(bpf_prog_attach_xattr(allow_prog[6], cg1, + if (CHECK(bpf_prog_attach_opts(allow_prog[6], cg1, BPF_CGROUP_INET_EGRESS, &attach_opts), "prog_replace", "errno=%d\n", errno)) goto err; diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c index 356547e849e2..9421a5b7f4e1 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c @@ -16,7 +16,7 @@ static int prog_load(int verdict) BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ BPF_EXIT_INSN(), }; - size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); + size_t insns_cnt = ARRAY_SIZE(prog); return bpf_test_load_program(BPF_PROG_TYPE_CGROUP_SKB, prog, insns_cnt, "GPL", 0, diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_getset_retval.c b/tools/testing/selftests/bpf/prog_tests/cgroup_getset_retval.c new file mode 100644 index 000000000000..0b47c3c000c7 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_getset_retval.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright 2021 Google LLC. + */ + +#include +#include +#include + +#include "cgroup_getset_retval_setsockopt.skel.h" +#include "cgroup_getset_retval_getsockopt.skel.h" + +#define SOL_CUSTOM 0xdeadbeef + +static int zero; + +static void test_setsockopt_set(int cgroup_fd, int sock_fd) +{ + struct cgroup_getset_retval_setsockopt *obj; + struct bpf_link *link_set_eunatch = NULL; + + obj = cgroup_getset_retval_setsockopt__open_and_load(); + if (!ASSERT_OK_PTR(obj, "skel-load")) + return; + + /* Attach setsockopt that sets EUNATCH, assert that + * we actually get that error when we run setsockopt() + */ + link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch, + cgroup_fd); + if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch")) + goto close_bpf_object; + + if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, + &zero, sizeof(int)), "setsockopt")) + goto close_bpf_object; + if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno")) + goto close_bpf_object; + + if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations")) + goto close_bpf_object; + if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) + goto close_bpf_object; + +close_bpf_object: + bpf_link__destroy(link_set_eunatch); + + cgroup_getset_retval_setsockopt__destroy(obj); +} + +static void test_setsockopt_set_and_get(int cgroup_fd, int sock_fd) +{ + struct cgroup_getset_retval_setsockopt *obj; + struct bpf_link *link_set_eunatch = NULL, *link_get_retval = NULL; + + obj = cgroup_getset_retval_setsockopt__open_and_load(); + if (!ASSERT_OK_PTR(obj, "skel-load")) + return; + + /* Attach setsockopt that sets EUNATCH, and one that gets the + * previously set errno. Assert that we get the same errno back. + */ + link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch, + cgroup_fd); + if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch")) + goto close_bpf_object; + link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, + cgroup_fd); + if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) + goto close_bpf_object; + + if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, + &zero, sizeof(int)), "setsockopt")) + goto close_bpf_object; + if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno")) + goto close_bpf_object; + + if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations")) + goto close_bpf_object; + if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) + goto close_bpf_object; + if (!ASSERT_EQ(obj->bss->retval_value, -EUNATCH, "retval_value")) + goto close_bpf_object; + +close_bpf_object: + bpf_link__destroy(link_set_eunatch); + bpf_link__destroy(link_get_retval); + + cgroup_getset_retval_setsockopt__destroy(obj); +} + +static void test_setsockopt_default_zero(int cgroup_fd, int sock_fd) +{ + struct cgroup_getset_retval_setsockopt *obj; + struct bpf_link *link_get_retval = NULL; + + obj = cgroup_getset_retval_setsockopt__open_and_load(); + if (!ASSERT_OK_PTR(obj, "skel-load")) + return; + + /* Attach setsockopt that gets the previously set errno. + * Assert that, without anything setting one, we get 0. + */ + link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, + cgroup_fd); + if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) + goto close_bpf_object; + + if (!ASSERT_OK(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, + &zero, sizeof(int)), "setsockopt")) + goto close_bpf_object; + + if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations")) + goto close_bpf_object; + if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) + goto close_bpf_object; + if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value")) + goto close_bpf_object; + +close_bpf_object: + bpf_link__destroy(link_get_retval); + + cgroup_getset_retval_setsockopt__destroy(obj); +} + +static void test_setsockopt_default_zero_and_set(int cgroup_fd, int sock_fd) +{ + struct cgroup_getset_retval_setsockopt *obj; + struct bpf_link *link_get_retval = NULL, *link_set_eunatch = NULL; + + obj = cgroup_getset_retval_setsockopt__open_and_load(); + if (!ASSERT_OK_PTR(obj, "skel-load")) + return; + + /* Attach setsockopt that gets the previously set errno, and then + * one that sets the errno to EUNATCH. Assert that the get does not + * see EUNATCH set later, and does not prevent EUNATCH from being set. + */ + link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, + cgroup_fd); + if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) + goto close_bpf_object; + link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch, + cgroup_fd); + if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch")) + goto close_bpf_object; + + if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, + &zero, sizeof(int)), "setsockopt")) + goto close_bpf_object; + if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno")) + goto close_bpf_object; + + if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations")) + goto close_bpf_object; + if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) + goto close_bpf_object; + if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value")) + goto close_bpf_object; + +close_bpf_object: + bpf_link__destroy(link_get_retval); + bpf_link__destroy(link_set_eunatch); + + cgroup_getset_retval_setsockopt__destroy(obj); +} + +static void test_setsockopt_override(int cgroup_fd, int sock_fd) +{ + struct cgroup_getset_retval_setsockopt *obj; + struct bpf_link *link_set_eunatch = NULL, *link_set_eisconn = NULL; + struct bpf_link *link_get_retval = NULL; + + obj = cgroup_getset_retval_setsockopt__open_and_load(); + if (!ASSERT_OK_PTR(obj, "skel-load")) + return; + + /* Attach setsockopt that sets EUNATCH, then one that sets EISCONN, + * and then one that gets the exported errno. Assert both the syscall + * and the helper sees the last set errno. + */ + link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch, + cgroup_fd); + if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch")) + goto close_bpf_object; + link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn, + cgroup_fd); + if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn")) + goto close_bpf_object; + link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, + cgroup_fd); + if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) + goto close_bpf_object; + + if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, + &zero, sizeof(int)), "setsockopt")) + goto close_bpf_object; + if (!ASSERT_EQ(errno, EISCONN, "setsockopt-errno")) + goto close_bpf_object; + + if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations")) + goto close_bpf_object; + if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) + goto close_bpf_object; + if (!ASSERT_EQ(obj->bss->retval_value, -EISCONN, "retval_value")) + goto close_bpf_object; + +close_bpf_object: + bpf_link__destroy(link_set_eunatch); + bpf_link__destroy(link_set_eisconn); + bpf_link__destroy(link_get_retval); + + cgroup_getset_retval_setsockopt__destroy(obj); +} + +static void test_setsockopt_legacy_eperm(int cgroup_fd, int sock_fd) +{ + struct cgroup_getset_retval_setsockopt *obj; + struct bpf_link *link_legacy_eperm = NULL, *link_get_retval = NULL; + + obj = cgroup_getset_retval_setsockopt__open_and_load(); + if (!ASSERT_OK_PTR(obj, "skel-load")) + return; + + /* Attach setsockopt that return a reject without setting errno + * (legacy reject), and one that gets the errno. Assert that for + * backward compatibility the syscall result in EPERM, and this + * is also visible to the helper. + */ + link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm, + cgroup_fd); + if (!ASSERT_OK_PTR(link_legacy_eperm, "cg-attach-legacy_eperm")) + goto close_bpf_object; + link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, + cgroup_fd); + if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) + goto close_bpf_object; + + if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, + &zero, sizeof(int)), "setsockopt")) + goto close_bpf_object; + if (!ASSERT_EQ(errno, EPERM, "setsockopt-errno")) + goto close_bpf_object; + + if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations")) + goto close_bpf_object; + if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) + goto close_bpf_object; + if (!ASSERT_EQ(obj->bss->retval_value, -EPERM, "retval_value")) + goto close_bpf_object; + +close_bpf_object: + bpf_link__destroy(link_legacy_eperm); + bpf_link__destroy(link_get_retval); + + cgroup_getset_retval_setsockopt__destroy(obj); +} + +static void test_setsockopt_legacy_no_override(int cgroup_fd, int sock_fd) +{ + struct cgroup_getset_retval_setsockopt *obj; + struct bpf_link *link_set_eunatch = NULL, *link_legacy_eperm = NULL; + struct bpf_link *link_get_retval = NULL; + + obj = cgroup_getset_retval_setsockopt__open_and_load(); + if (!ASSERT_OK_PTR(obj, "skel-load")) + return; + + /* Attach setsockopt that sets EUNATCH, then one that return a reject + * without setting errno, and then one that gets the exported errno. + * Assert both the syscall and the helper's errno are unaffected by + * the second prog (i.e. legacy rejects does not override the errno + * to EPERM). + */ + link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch, + cgroup_fd); + if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch")) + goto close_bpf_object; + link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm, + cgroup_fd); + if (!ASSERT_OK_PTR(link_legacy_eperm, "cg-attach-legacy_eperm")) + goto close_bpf_object; + link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, + cgroup_fd); + if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) + goto close_bpf_object; + + if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, + &zero, sizeof(int)), "setsockopt")) + goto close_bpf_object; + if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno")) + goto close_bpf_object; + + if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations")) + goto close_bpf_object; + if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) + goto close_bpf_object; + if (!ASSERT_EQ(obj->bss->retval_value, -EUNATCH, "retval_value")) + goto close_bpf_object; + +close_bpf_object: + bpf_link__destroy(link_set_eunatch); + bpf_link__destroy(link_legacy_eperm); + bpf_link__destroy(link_get_retval); + + cgroup_getset_retval_setsockopt__destroy(obj); +} + +static void test_getsockopt_get(int cgroup_fd, int sock_fd) +{ + struct cgroup_getset_retval_getsockopt *obj; + struct bpf_link *link_get_retval = NULL; + int buf; + socklen_t optlen = sizeof(buf); + + obj = cgroup_getset_retval_getsockopt__open_and_load(); + if (!ASSERT_OK_PTR(obj, "skel-load")) + return; + + /* Attach getsockopt that gets previously set errno. Assert that the + * error from kernel is in both ctx_retval_value and retval_value. + */ + link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, + cgroup_fd); + if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) + goto close_bpf_object; + + if (!ASSERT_ERR(getsockopt(sock_fd, SOL_CUSTOM, 0, + &buf, &optlen), "getsockopt")) + goto close_bpf_object; + if (!ASSERT_EQ(errno, EOPNOTSUPP, "getsockopt-errno")) + goto close_bpf_object; + + if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations")) + goto close_bpf_object; + if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) + goto close_bpf_object; + if (!ASSERT_EQ(obj->bss->retval_value, -EOPNOTSUPP, "retval_value")) + goto close_bpf_object; + if (!ASSERT_EQ(obj->bss->ctx_retval_value, -EOPNOTSUPP, "ctx_retval_value")) + goto close_bpf_object; + +close_bpf_object: + bpf_link__destroy(link_get_retval); + + cgroup_getset_retval_getsockopt__destroy(obj); +} + +static void test_getsockopt_override(int cgroup_fd, int sock_fd) +{ + struct cgroup_getset_retval_getsockopt *obj; + struct bpf_link *link_set_eisconn = NULL; + int buf; + socklen_t optlen = sizeof(buf); + + obj = cgroup_getset_retval_getsockopt__open_and_load(); + if (!ASSERT_OK_PTR(obj, "skel-load")) + return; + + /* Attach getsockopt that sets retval to -EISCONN. Assert that this + * overrides the value from kernel. + */ + link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn, + cgroup_fd); + if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn")) + goto close_bpf_object; + + if (!ASSERT_ERR(getsockopt(sock_fd, SOL_CUSTOM, 0, + &buf, &optlen), "getsockopt")) + goto close_bpf_object; + if (!ASSERT_EQ(errno, EISCONN, "getsockopt-errno")) + goto close_bpf_object; + + if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations")) + goto close_bpf_object; + if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) + goto close_bpf_object; + +close_bpf_object: + bpf_link__destroy(link_set_eisconn); + + cgroup_getset_retval_getsockopt__destroy(obj); +} + +static void test_getsockopt_retval_sync(int cgroup_fd, int sock_fd) +{ + struct cgroup_getset_retval_getsockopt *obj; + struct bpf_link *link_set_eisconn = NULL, *link_clear_retval = NULL; + struct bpf_link *link_get_retval = NULL; + int buf; + socklen_t optlen = sizeof(buf); + + obj = cgroup_getset_retval_getsockopt__open_and_load(); + if (!ASSERT_OK_PTR(obj, "skel-load")) + return; + + /* Attach getsockopt that sets retval to -EISCONN, and one that clears + * ctx retval. Assert that the clearing ctx retval is synced to helper + * and clears any errors both from kernel and BPF.. + */ + link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn, + cgroup_fd); + if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn")) + goto close_bpf_object; + link_clear_retval = bpf_program__attach_cgroup(obj->progs.clear_retval, + cgroup_fd); + if (!ASSERT_OK_PTR(link_clear_retval, "cg-attach-clear_retval")) + goto close_bpf_object; + link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval, + cgroup_fd); + if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval")) + goto close_bpf_object; + + if (!ASSERT_OK(getsockopt(sock_fd, SOL_CUSTOM, 0, + &buf, &optlen), "getsockopt")) + goto close_bpf_object; + + if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations")) + goto close_bpf_object; + if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error")) + goto close_bpf_object; + if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value")) + goto close_bpf_object; + if (!ASSERT_EQ(obj->bss->ctx_retval_value, 0, "ctx_retval_value")) + goto close_bpf_object; + +close_bpf_object: + bpf_link__destroy(link_set_eisconn); + bpf_link__destroy(link_clear_retval); + bpf_link__destroy(link_get_retval); + + cgroup_getset_retval_getsockopt__destroy(obj); +} + +void test_cgroup_getset_retval(void) +{ + int cgroup_fd = -1; + int sock_fd = -1; + + cgroup_fd = test__join_cgroup("/cgroup_getset_retval"); + if (!ASSERT_GE(cgroup_fd, 0, "cg-create")) + goto close_fd; + + sock_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0); + if (!ASSERT_GE(sock_fd, 0, "start-server")) + goto close_fd; + + if (test__start_subtest("setsockopt-set")) + test_setsockopt_set(cgroup_fd, sock_fd); + + if (test__start_subtest("setsockopt-set_and_get")) + test_setsockopt_set_and_get(cgroup_fd, sock_fd); + + if (test__start_subtest("setsockopt-default_zero")) + test_setsockopt_default_zero(cgroup_fd, sock_fd); + + if (test__start_subtest("setsockopt-default_zero_and_set")) + test_setsockopt_default_zero_and_set(cgroup_fd, sock_fd); + + if (test__start_subtest("setsockopt-override")) + test_setsockopt_override(cgroup_fd, sock_fd); + + if (test__start_subtest("setsockopt-legacy_eperm")) + test_setsockopt_legacy_eperm(cgroup_fd, sock_fd); + + if (test__start_subtest("setsockopt-legacy_no_override")) + test_setsockopt_legacy_no_override(cgroup_fd, sock_fd); + + if (test__start_subtest("getsockopt-get")) + test_getsockopt_get(cgroup_fd, sock_fd); + + if (test__start_subtest("getsockopt-override")) + test_getsockopt_override(cgroup_fd, sock_fd); + + if (test__start_subtest("getsockopt-retval_sync")) + test_getsockopt_retval_sync(cgroup_fd, sock_fd); + +close_fd: + close(cgroup_fd); +} diff --git a/tools/testing/selftests/bpf/prog_tests/check_mtu.c b/tools/testing/selftests/bpf/prog_tests/check_mtu.c index f73e6e36b74d..12f4395f18b3 100644 --- a/tools/testing/selftests/bpf/prog_tests/check_mtu.c +++ b/tools/testing/selftests/bpf/prog_tests/check_mtu.c @@ -79,28 +79,21 @@ static void test_check_mtu_run_xdp(struct test_check_mtu *skel, struct bpf_program *prog, __u32 mtu_expect) { - const char *prog_name = bpf_program__name(prog); int retval_expect = XDP_PASS; __u32 mtu_result = 0; char buf[256] = {}; - int err; - struct bpf_prog_test_run_attr tattr = { + int err, prog_fd = bpf_program__fd(prog); + LIBBPF_OPTS(bpf_test_run_opts, topts, .repeat = 1, .data_in = &pkt_v4, .data_size_in = sizeof(pkt_v4), .data_out = buf, .data_size_out = sizeof(buf), - .prog_fd = bpf_program__fd(prog), - }; + ); - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err != 0, "bpf_prog_test_run", - "prog_name:%s (err %d errno %d retval %d)\n", - prog_name, err, errno, tattr.retval); - - CHECK(tattr.retval != retval_expect, "retval", - "progname:%s unexpected retval=%d expected=%d\n", - prog_name, tattr.retval, retval_expect); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.retval, retval_expect, "retval"); /* Extract MTU that BPF-prog got */ mtu_result = skel->bss->global_bpf_mtu_xdp; @@ -139,28 +132,21 @@ static void test_check_mtu_run_tc(struct test_check_mtu *skel, struct bpf_program *prog, __u32 mtu_expect) { - const char *prog_name = bpf_program__name(prog); int retval_expect = BPF_OK; __u32 mtu_result = 0; char buf[256] = {}; - int err; - struct bpf_prog_test_run_attr tattr = { - .repeat = 1, + int err, prog_fd = bpf_program__fd(prog); + LIBBPF_OPTS(bpf_test_run_opts, topts, .data_in = &pkt_v4, .data_size_in = sizeof(pkt_v4), .data_out = buf, .data_size_out = sizeof(buf), - .prog_fd = bpf_program__fd(prog), - }; + .repeat = 1, + ); - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err != 0, "bpf_prog_test_run", - "prog_name:%s (err %d errno %d retval %d)\n", - prog_name, err, errno, tattr.retval); - - CHECK(tattr.retval != retval_expect, "retval", - "progname:%s unexpected retval=%d expected=%d\n", - prog_name, tattr.retval, retval_expect); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.retval, retval_expect, "retval"); /* Extract MTU that BPF-prog got */ mtu_result = skel->bss->global_bpf_mtu_tc; diff --git a/tools/testing/selftests/bpf/prog_tests/cls_redirect.c b/tools/testing/selftests/bpf/prog_tests/cls_redirect.c index e075d03ab630..224f016b0a53 100644 --- a/tools/testing/selftests/bpf/prog_tests/cls_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/cls_redirect.c @@ -161,7 +161,7 @@ static socklen_t prepare_addr(struct sockaddr_storage *addr, int family) } } -static bool was_decapsulated(struct bpf_prog_test_run_attr *tattr) +static bool was_decapsulated(struct bpf_test_run_opts *tattr) { return tattr->data_size_out < tattr->data_size_in; } @@ -367,12 +367,12 @@ static void close_fds(int *fds, int n) static void test_cls_redirect_common(struct bpf_program *prog) { - struct bpf_prog_test_run_attr tattr = {}; + LIBBPF_OPTS(bpf_test_run_opts, tattr); int families[] = { AF_INET, AF_INET6 }; struct sockaddr_storage ss; struct sockaddr *addr; socklen_t slen; - int i, j, err; + int i, j, err, prog_fd; int servers[__NR_KIND][ARRAY_SIZE(families)] = {}; int conns[__NR_KIND][ARRAY_SIZE(families)] = {}; struct tuple tuples[__NR_KIND][ARRAY_SIZE(families)]; @@ -394,7 +394,7 @@ static void test_cls_redirect_common(struct bpf_program *prog) goto cleanup; } - tattr.prog_fd = bpf_program__fd(prog); + prog_fd = bpf_program__fd(prog); for (i = 0; i < ARRAY_SIZE(tests); i++) { struct test_cfg *test = &tests[i]; @@ -415,7 +415,7 @@ static void test_cls_redirect_common(struct bpf_program *prog) if (CHECK_FAIL(!tattr.data_size_in)) continue; - err = bpf_prog_test_run_xattr(&tattr); + err = bpf_prog_test_run_opts(prog_fd, &tattr); if (CHECK_FAIL(err)) continue; diff --git a/tools/testing/selftests/bpf/prog_tests/core_kern.c b/tools/testing/selftests/bpf/prog_tests/core_kern.c index 561c5185d886..6a5a1c019a5d 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_kern.c +++ b/tools/testing/selftests/bpf/prog_tests/core_kern.c @@ -7,8 +7,22 @@ void test_core_kern_lskel(void) { struct core_kern_lskel *skel; + int link_fd; skel = core_kern_lskel__open_and_load(); - ASSERT_OK_PTR(skel, "open_and_load"); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + + link_fd = core_kern_lskel__core_relo_proto__attach(skel); + if (!ASSERT_GT(link_fd, 0, "attach(core_relo_proto)")) + goto cleanup; + + /* trigger tracepoints */ + usleep(1); + ASSERT_TRUE(skel->bss->proto_out[0], "bpf_core_type_exists"); + ASSERT_FALSE(skel->bss->proto_out[1], "!bpf_core_type_exists"); + ASSERT_TRUE(skel->bss->proto_out[2], "bpf_core_type_exists. nested"); + +cleanup: core_kern_lskel__destroy(skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/core_kern_overflow.c b/tools/testing/selftests/bpf/prog_tests/core_kern_overflow.c new file mode 100644 index 000000000000..04cc145bc26a --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/core_kern_overflow.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "test_progs.h" +#include "core_kern_overflow.lskel.h" + +void test_core_kern_overflow_lskel(void) +{ + struct core_kern_overflow_lskel *skel; + + skel = core_kern_overflow_lskel__open_and_load(); + if (!ASSERT_NULL(skel, "open_and_load")) + core_kern_overflow_lskel__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c index b8bdd1c3efca..f28f75aa9154 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c @@ -2,6 +2,7 @@ #include #include "progs/core_reloc_types.h" #include "bpf_testmod/bpf_testmod.h" +#include #include #include #include @@ -511,7 +512,7 @@ static int __trigger_module_test_read(const struct core_reloc_test_case *test) } -static struct core_reloc_test_case test_cases[] = { +static const struct core_reloc_test_case test_cases[] = { /* validate we can find kernel image and use its BTF for relocs */ { .case_name = "kernel", @@ -836,13 +837,27 @@ static size_t roundup_page(size_t sz) return (sz + page_size - 1) / page_size * page_size; } -void test_core_reloc(void) +static int run_btfgen(const char *src_btf, const char *dst_btf, const char *objpath) +{ + char command[4096]; + int n; + + n = snprintf(command, sizeof(command), + "./bpftool gen min_core_btf %s %s %s", + src_btf, dst_btf, objpath); + if (n < 0 || n >= sizeof(command)) + return -1; + + return system(command); +} + +static void run_core_reloc_tests(bool use_btfgen) { const size_t mmap_sz = roundup_page(sizeof(struct data)); DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts); - struct core_reloc_test_case *test_case; + struct core_reloc_test_case *test_case, test_case_copy; const char *tp_name, *probe_name; - int err, i, equal; + int err, i, equal, fd; struct bpf_link *link = NULL; struct bpf_map *data_map; struct bpf_program *prog; @@ -854,7 +869,11 @@ void test_core_reloc(void) my_pid_tgid = getpid() | ((uint64_t)syscall(SYS_gettid) << 32); for (i = 0; i < ARRAY_SIZE(test_cases); i++) { - test_case = &test_cases[i]; + char btf_file[] = "/tmp/core_reloc.btf.XXXXXX"; + + test_case_copy = test_cases[i]; + test_case = &test_case_copy; + if (!test__start_subtest(test_case->case_name)) continue; @@ -863,6 +882,26 @@ void test_core_reloc(void) continue; } + /* generate a "minimal" BTF file and use it as source */ + if (use_btfgen) { + + if (!test_case->btf_src_file || test_case->fails) { + test__skip(); + continue; + } + + fd = mkstemp(btf_file); + if (!ASSERT_GE(fd, 0, "btf_tmp")) + continue; + close(fd); /* we only need the path */ + err = run_btfgen(test_case->btf_src_file, btf_file, + test_case->bpf_obj_file); + if (!ASSERT_OK(err, "run_btfgen")) + continue; + + test_case->btf_src_file = btf_file; + } + if (test_case->setup) { err = test_case->setup(test_case); if (CHECK(err, "test_setup", "test #%d setup failed: %d\n", i, err)) @@ -872,7 +911,7 @@ void test_core_reloc(void) if (test_case->btf_src_file) { err = access(test_case->btf_src_file, R_OK); if (!ASSERT_OK(err, "btf_src_file")) - goto cleanup; + continue; } open_opts.btf_custom_path = test_case->btf_src_file; @@ -954,8 +993,20 @@ void test_core_reloc(void) CHECK_FAIL(munmap(mmap_data, mmap_sz)); mmap_data = NULL; } + if (use_btfgen) + remove(test_case->btf_src_file); bpf_link__destroy(link); link = NULL; bpf_object__close(obj); } } + +void test_core_reloc(void) +{ + run_core_reloc_tests(false); +} + +void test_core_reloc_btfgen(void) +{ + run_core_reloc_tests(true); +} diff --git a/tools/testing/selftests/bpf/prog_tests/custom_sec_handlers.c b/tools/testing/selftests/bpf/prog_tests/custom_sec_handlers.c new file mode 100644 index 000000000000..b2dfc5954aea --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/custom_sec_handlers.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Facebook */ + +#include +#include "test_custom_sec_handlers.skel.h" + +#define COOKIE_ABC1 1 +#define COOKIE_ABC2 2 +#define COOKIE_CUSTOM 3 +#define COOKIE_FALLBACK 4 +#define COOKIE_KPROBE 5 + +static int custom_setup_prog(struct bpf_program *prog, long cookie) +{ + if (cookie == COOKIE_ABC1) + bpf_program__set_autoload(prog, false); + + return 0; +} + +static int custom_prepare_load_prog(struct bpf_program *prog, + struct bpf_prog_load_opts *opts, long cookie) +{ + if (cookie == COOKIE_FALLBACK) + opts->prog_flags |= BPF_F_SLEEPABLE; + else if (cookie == COOKIE_ABC1) + ASSERT_FALSE(true, "unexpected preload for abc"); + + return 0; +} + +static int custom_attach_prog(const struct bpf_program *prog, long cookie, + struct bpf_link **link) +{ + switch (cookie) { + case COOKIE_ABC2: + *link = bpf_program__attach_raw_tracepoint(prog, "sys_enter"); + return libbpf_get_error(*link); + case COOKIE_CUSTOM: + *link = bpf_program__attach_tracepoint(prog, "syscalls", "sys_enter_nanosleep"); + return libbpf_get_error(*link); + case COOKIE_KPROBE: + case COOKIE_FALLBACK: + /* no auto-attach for SEC("xyz") and SEC("kprobe") */ + *link = NULL; + return 0; + default: + ASSERT_FALSE(true, "unexpected cookie"); + return -EINVAL; + } +} + +static int abc1_id; +static int abc2_id; +static int custom_id; +static int fallback_id; +static int kprobe_id; + +__attribute__((constructor)) +static void register_sec_handlers(void) +{ + LIBBPF_OPTS(libbpf_prog_handler_opts, abc1_opts, + .cookie = COOKIE_ABC1, + .prog_setup_fn = custom_setup_prog, + .prog_prepare_load_fn = custom_prepare_load_prog, + .prog_attach_fn = NULL, + ); + LIBBPF_OPTS(libbpf_prog_handler_opts, abc2_opts, + .cookie = COOKIE_ABC2, + .prog_setup_fn = custom_setup_prog, + .prog_prepare_load_fn = custom_prepare_load_prog, + .prog_attach_fn = custom_attach_prog, + ); + LIBBPF_OPTS(libbpf_prog_handler_opts, custom_opts, + .cookie = COOKIE_CUSTOM, + .prog_setup_fn = NULL, + .prog_prepare_load_fn = NULL, + .prog_attach_fn = custom_attach_prog, + ); + + abc1_id = libbpf_register_prog_handler("abc", BPF_PROG_TYPE_RAW_TRACEPOINT, 0, &abc1_opts); + abc2_id = libbpf_register_prog_handler("abc/", BPF_PROG_TYPE_RAW_TRACEPOINT, 0, &abc2_opts); + custom_id = libbpf_register_prog_handler("custom+", BPF_PROG_TYPE_TRACEPOINT, 0, &custom_opts); +} + +__attribute__((destructor)) +static void unregister_sec_handlers(void) +{ + libbpf_unregister_prog_handler(abc1_id); + libbpf_unregister_prog_handler(abc2_id); + libbpf_unregister_prog_handler(custom_id); +} + +void test_custom_sec_handlers(void) +{ + LIBBPF_OPTS(libbpf_prog_handler_opts, opts, + .prog_setup_fn = custom_setup_prog, + .prog_prepare_load_fn = custom_prepare_load_prog, + .prog_attach_fn = custom_attach_prog, + ); + struct test_custom_sec_handlers* skel; + int err; + + ASSERT_GT(abc1_id, 0, "abc1_id"); + ASSERT_GT(abc2_id, 0, "abc2_id"); + ASSERT_GT(custom_id, 0, "custom_id"); + + /* override libbpf's handle of SEC("kprobe/...") but also allow pure + * SEC("kprobe") due to "kprobe+" specifier. Register it as + * TRACEPOINT, just for fun. + */ + opts.cookie = COOKIE_KPROBE; + kprobe_id = libbpf_register_prog_handler("kprobe+", BPF_PROG_TYPE_TRACEPOINT, 0, &opts); + /* fallback treats everything as BPF_PROG_TYPE_SYSCALL program to test + * setting custom BPF_F_SLEEPABLE bit in preload handler + */ + opts.cookie = COOKIE_FALLBACK; + fallback_id = libbpf_register_prog_handler(NULL, BPF_PROG_TYPE_SYSCALL, 0, &opts); + + if (!ASSERT_GT(fallback_id, 0, "fallback_id") /* || !ASSERT_GT(kprobe_id, 0, "kprobe_id")*/) { + if (fallback_id > 0) + libbpf_unregister_prog_handler(fallback_id); + if (kprobe_id > 0) + libbpf_unregister_prog_handler(kprobe_id); + return; + } + + /* open skeleton and validate assumptions */ + skel = test_custom_sec_handlers__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + goto cleanup; + + ASSERT_EQ(bpf_program__type(skel->progs.abc1), BPF_PROG_TYPE_RAW_TRACEPOINT, "abc1_type"); + ASSERT_FALSE(bpf_program__autoload(skel->progs.abc1), "abc1_autoload"); + + ASSERT_EQ(bpf_program__type(skel->progs.abc2), BPF_PROG_TYPE_RAW_TRACEPOINT, "abc2_type"); + ASSERT_EQ(bpf_program__type(skel->progs.custom1), BPF_PROG_TYPE_TRACEPOINT, "custom1_type"); + ASSERT_EQ(bpf_program__type(skel->progs.custom2), BPF_PROG_TYPE_TRACEPOINT, "custom2_type"); + ASSERT_EQ(bpf_program__type(skel->progs.kprobe1), BPF_PROG_TYPE_TRACEPOINT, "kprobe1_type"); + ASSERT_EQ(bpf_program__type(skel->progs.xyz), BPF_PROG_TYPE_SYSCALL, "xyz_type"); + + skel->rodata->my_pid = getpid(); + + /* now attempt to load everything */ + err = test_custom_sec_handlers__load(skel); + if (!ASSERT_OK(err, "skel_load")) + goto cleanup; + + /* now try to auto-attach everything */ + err = test_custom_sec_handlers__attach(skel); + if (!ASSERT_OK(err, "skel_attach")) + goto cleanup; + + skel->links.xyz = bpf_program__attach(skel->progs.kprobe1); + ASSERT_EQ(errno, EOPNOTSUPP, "xyz_attach_err"); + ASSERT_ERR_PTR(skel->links.xyz, "xyz_attach"); + + /* trigger programs */ + usleep(1); + + /* SEC("abc") is set to not auto-loaded */ + ASSERT_FALSE(skel->bss->abc1_called, "abc1_called"); + ASSERT_TRUE(skel->bss->abc2_called, "abc2_called"); + ASSERT_TRUE(skel->bss->custom1_called, "custom1_called"); + ASSERT_TRUE(skel->bss->custom2_called, "custom2_called"); + /* SEC("kprobe") shouldn't be auto-attached */ + ASSERT_FALSE(skel->bss->kprobe1_called, "kprobe1_called"); + /* SEC("xyz") shouldn't be auto-attached */ + ASSERT_FALSE(skel->bss->xyz_called, "xyz_called"); + +cleanup: + test_custom_sec_handlers__destroy(skel); + + ASSERT_OK(libbpf_unregister_prog_handler(fallback_id), "unregister_fallback"); + ASSERT_OK(libbpf_unregister_prog_handler(kprobe_id), "unregister_kprobe"); +} diff --git a/tools/testing/selftests/bpf/prog_tests/dummy_st_ops.c b/tools/testing/selftests/bpf/prog_tests/dummy_st_ops.c index cbaa44ffb8c6..5aa52cc31dc2 100644 --- a/tools/testing/selftests/bpf/prog_tests/dummy_st_ops.c +++ b/tools/testing/selftests/bpf/prog_tests/dummy_st_ops.c @@ -26,10 +26,10 @@ static void test_dummy_st_ops_attach(void) static void test_dummy_init_ret_value(void) { __u64 args[1] = {0}; - struct bpf_prog_test_run_attr attr = { - .ctx_size_in = sizeof(args), + LIBBPF_OPTS(bpf_test_run_opts, attr, .ctx_in = args, - }; + .ctx_size_in = sizeof(args), + ); struct dummy_st_ops *skel; int fd, err; @@ -38,8 +38,7 @@ static void test_dummy_init_ret_value(void) return; fd = bpf_program__fd(skel->progs.test_1); - attr.prog_fd = fd; - err = bpf_prog_test_run_xattr(&attr); + err = bpf_prog_test_run_opts(fd, &attr); ASSERT_OK(err, "test_run"); ASSERT_EQ(attr.retval, 0xf2f3f4f5, "test_ret"); @@ -53,10 +52,10 @@ static void test_dummy_init_ptr_arg(void) .val = exp_retval, }; __u64 args[1] = {(unsigned long)&in_state}; - struct bpf_prog_test_run_attr attr = { - .ctx_size_in = sizeof(args), + LIBBPF_OPTS(bpf_test_run_opts, attr, .ctx_in = args, - }; + .ctx_size_in = sizeof(args), + ); struct dummy_st_ops *skel; int fd, err; @@ -65,8 +64,7 @@ static void test_dummy_init_ptr_arg(void) return; fd = bpf_program__fd(skel->progs.test_1); - attr.prog_fd = fd; - err = bpf_prog_test_run_xattr(&attr); + err = bpf_prog_test_run_opts(fd, &attr); ASSERT_OK(err, "test_run"); ASSERT_EQ(in_state.val, 0x5a, "test_ptr_ret"); ASSERT_EQ(attr.retval, exp_retval, "test_ret"); @@ -77,10 +75,10 @@ static void test_dummy_init_ptr_arg(void) static void test_dummy_multiple_args(void) { __u64 args[5] = {0, -100, 0x8a5f, 'c', 0x1234567887654321ULL}; - struct bpf_prog_test_run_attr attr = { - .ctx_size_in = sizeof(args), + LIBBPF_OPTS(bpf_test_run_opts, attr, .ctx_in = args, - }; + .ctx_size_in = sizeof(args), + ); struct dummy_st_ops *skel; int fd, err; size_t i; @@ -91,8 +89,7 @@ static void test_dummy_multiple_args(void) return; fd = bpf_program__fd(skel->progs.test_2); - attr.prog_fd = fd; - err = bpf_prog_test_run_xattr(&attr); + err = bpf_prog_test_run_opts(fd, &attr); ASSERT_OK(err, "test_run"); for (i = 0; i < ARRAY_SIZE(args); i++) { snprintf(name, sizeof(name), "arg %zu", i); diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c index 4374ac8a8a91..130f5b82d2e6 100644 --- a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c +++ b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c @@ -9,38 +9,34 @@ void test_fentry_fexit(void) struct fentry_test_lskel *fentry_skel = NULL; struct fexit_test_lskel *fexit_skel = NULL; __u64 *fentry_res, *fexit_res; - __u32 duration = 0, retval; int err, prog_fd, i; + LIBBPF_OPTS(bpf_test_run_opts, topts); fentry_skel = fentry_test_lskel__open_and_load(); - if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n")) + if (!ASSERT_OK_PTR(fentry_skel, "fentry_skel_load")) goto close_prog; fexit_skel = fexit_test_lskel__open_and_load(); - if (CHECK(!fexit_skel, "fexit_skel_load", "fexit skeleton failed\n")) + if (!ASSERT_OK_PTR(fexit_skel, "fexit_skel_load")) goto close_prog; err = fentry_test_lskel__attach(fentry_skel); - if (CHECK(err, "fentry_attach", "fentry attach failed: %d\n", err)) + if (!ASSERT_OK(err, "fentry_attach")) goto close_prog; err = fexit_test_lskel__attach(fexit_skel); - if (CHECK(err, "fexit_attach", "fexit attach failed: %d\n", err)) + if (!ASSERT_OK(err, "fexit_attach")) goto close_prog; prog_fd = fexit_skel->progs.test1.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); - CHECK(err || retval, "ipv6", - "err %d errno %d retval %d duration %d\n", - err, errno, retval, duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "ipv6 test_run"); + ASSERT_OK(topts.retval, "ipv6 test retval"); fentry_res = (__u64 *)fentry_skel->bss; fexit_res = (__u64 *)fexit_skel->bss; printf("%lld\n", fentry_skel->bss->test1_result); for (i = 0; i < 8; i++) { - CHECK(fentry_res[i] != 1, "result", - "fentry_test%d failed err %lld\n", i + 1, fentry_res[i]); - CHECK(fexit_res[i] != 1, "result", - "fexit_test%d failed err %lld\n", i + 1, fexit_res[i]); + ASSERT_EQ(fentry_res[i], 1, "fentry result"); + ASSERT_EQ(fexit_res[i], 1, "fexit result"); } close_prog: diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_test.c b/tools/testing/selftests/bpf/prog_tests/fentry_test.c index 12921b3850d2..c0d1d61d5f66 100644 --- a/tools/testing/selftests/bpf/prog_tests/fentry_test.c +++ b/tools/testing/selftests/bpf/prog_tests/fentry_test.c @@ -6,9 +6,9 @@ static int fentry_test(struct fentry_test_lskel *fentry_skel) { int err, prog_fd, i; - __u32 duration = 0, retval; int link_fd; __u64 *result; + LIBBPF_OPTS(bpf_test_run_opts, topts); err = fentry_test_lskel__attach(fentry_skel); if (!ASSERT_OK(err, "fentry_attach")) @@ -20,10 +20,9 @@ static int fentry_test(struct fentry_test_lskel *fentry_skel) return -1; prog_fd = fentry_skel->progs.test1.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "test_run"); - ASSERT_EQ(retval, 0, "test_run"); + ASSERT_EQ(topts.retval, 0, "test_run"); result = (__u64 *)fentry_skel->bss; for (i = 0; i < sizeof(*fentry_skel->bss) / sizeof(__u64); i++) { diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c b/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c index c52f99f6a909..d9aad15e0d24 100644 --- a/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c +++ b/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c @@ -58,12 +58,17 @@ static void test_fexit_bpf2bpf_common(const char *obj_file, test_cb cb) { struct bpf_object *obj = NULL, *tgt_obj; - __u32 retval, tgt_prog_id, info_len; + __u32 tgt_prog_id, info_len; struct bpf_prog_info prog_info = {}; struct bpf_program **prog = NULL, *p; struct bpf_link **link = NULL; int err, tgt_fd, i; struct btf *btf; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v6, + .data_size_in = sizeof(pkt_v6), + .repeat = 1, + ); err = bpf_prog_test_load(target_obj_file, BPF_PROG_TYPE_UNSPEC, &tgt_obj, &tgt_fd); @@ -132,7 +137,7 @@ static void test_fexit_bpf2bpf_common(const char *obj_file, &link_info, &info_len); ASSERT_OK(err, "link_fd_get_info"); ASSERT_EQ(link_info.tracing.attach_type, - bpf_program__get_expected_attach_type(prog[i]), + bpf_program__expected_attach_type(prog[i]), "link_attach_type"); ASSERT_EQ(link_info.tracing.target_obj_id, tgt_prog_id, "link_tgt_obj_id"); ASSERT_EQ(link_info.tracing.target_btf_id, btf_id, "link_tgt_btf_id"); @@ -147,10 +152,9 @@ static void test_fexit_bpf2bpf_common(const char *obj_file, if (!run_prog) goto close_prog; - err = bpf_prog_test_run(tgt_fd, 1, &pkt_v6, sizeof(pkt_v6), - NULL, NULL, &retval, NULL); + err = bpf_prog_test_run_opts(tgt_fd, &topts); ASSERT_OK(err, "prog_run"); - ASSERT_EQ(retval, 0, "prog_run_ret"); + ASSERT_EQ(topts.retval, 0, "prog_run_ret"); if (check_data_map(obj, prog_cnt, false)) goto close_prog; @@ -225,29 +229,31 @@ static int test_second_attach(struct bpf_object *obj) const char *tgt_obj_file = "./test_pkt_access.o"; struct bpf_program *prog = NULL; struct bpf_object *tgt_obj; - __u32 duration = 0, retval; struct bpf_link *link; int err = 0, tgt_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v6, + .data_size_in = sizeof(pkt_v6), + .repeat = 1, + ); prog = bpf_object__find_program_by_name(obj, prog_name); - if (CHECK(!prog, "find_prog", "prog %s not found\n", prog_name)) + if (!ASSERT_OK_PTR(prog, "find_prog")) return -ENOENT; err = bpf_prog_test_load(tgt_obj_file, BPF_PROG_TYPE_UNSPEC, &tgt_obj, &tgt_fd); - if (CHECK(err, "second_prog_load", "file %s err %d errno %d\n", - tgt_obj_file, err, errno)) + if (!ASSERT_OK(err, "second_prog_load")) return err; link = bpf_program__attach_freplace(prog, tgt_fd, tgt_name); if (!ASSERT_OK_PTR(link, "second_link")) goto out; - err = bpf_prog_test_run(tgt_fd, 1, &pkt_v6, sizeof(pkt_v6), - NULL, NULL, &retval, &duration); - if (CHECK(err || retval, "ipv6", - "err %d errno %d retval %d duration %d\n", - err, errno, retval, duration)) + err = bpf_prog_test_run_opts(tgt_fd, &topts); + if (!ASSERT_OK(err, "ipv6 test_run")) + goto out; + if (!ASSERT_OK(topts.retval, "ipv6 retval")) goto out; err = check_data_map(obj, 1, true); diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_stress.c b/tools/testing/selftests/bpf/prog_tests/fexit_stress.c index e4cede6b4b2d..3ee2107bbf7a 100644 --- a/tools/testing/selftests/bpf/prog_tests/fexit_stress.c +++ b/tools/testing/selftests/bpf/prog_tests/fexit_stress.c @@ -10,9 +10,7 @@ void test_fexit_stress(void) char test_skb[128] = {}; int fexit_fd[CNT] = {}; int link_fd[CNT] = {}; - __u32 duration = 0; char error[4096]; - __u32 prog_ret; int err, i, filter_fd; const struct bpf_insn trace_program[] = { @@ -36,9 +34,15 @@ void test_fexit_stress(void) .log_size = sizeof(error), ); + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = test_skb, + .data_size_in = sizeof(test_skb), + .repeat = 1, + ); + err = libbpf_find_vmlinux_btf_id("bpf_fentry_test1", trace_opts.expected_attach_type); - if (CHECK(err <= 0, "find_vmlinux_btf_id", "failed: %d\n", err)) + if (!ASSERT_GT(err, 0, "find_vmlinux_btf_id")) goto out; trace_opts.attach_btf_id = err; @@ -47,24 +51,20 @@ void test_fexit_stress(void) trace_program, sizeof(trace_program) / sizeof(struct bpf_insn), &trace_opts); - if (CHECK(fexit_fd[i] < 0, "fexit loaded", - "failed: %d errno %d\n", fexit_fd[i], errno)) + if (!ASSERT_GE(fexit_fd[i], 0, "fexit load")) goto out; link_fd[i] = bpf_raw_tracepoint_open(NULL, fexit_fd[i]); - if (CHECK(link_fd[i] < 0, "fexit attach failed", - "prog %d failed: %d err %d\n", i, link_fd[i], errno)) + if (!ASSERT_GE(link_fd[i], 0, "fexit attach")) goto out; } filter_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", skb_program, sizeof(skb_program) / sizeof(struct bpf_insn), &skb_opts); - if (CHECK(filter_fd < 0, "test_program_loaded", "failed: %d errno %d\n", - filter_fd, errno)) + if (!ASSERT_GE(filter_fd, 0, "test_program_loaded")) goto out; - err = bpf_prog_test_run(filter_fd, 1, test_skb, sizeof(test_skb), 0, - 0, &prog_ret, 0); + err = bpf_prog_test_run_opts(filter_fd, &topts); close(filter_fd); CHECK_FAIL(err); out: diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_test.c b/tools/testing/selftests/bpf/prog_tests/fexit_test.c index d4887d8bb396..101b7343036b 100644 --- a/tools/testing/selftests/bpf/prog_tests/fexit_test.c +++ b/tools/testing/selftests/bpf/prog_tests/fexit_test.c @@ -6,9 +6,9 @@ static int fexit_test(struct fexit_test_lskel *fexit_skel) { int err, prog_fd, i; - __u32 duration = 0, retval; int link_fd; __u64 *result; + LIBBPF_OPTS(bpf_test_run_opts, topts); err = fexit_test_lskel__attach(fexit_skel); if (!ASSERT_OK(err, "fexit_attach")) @@ -20,10 +20,9 @@ static int fexit_test(struct fexit_test_lskel *fexit_skel) return -1; prog_fd = fexit_skel->progs.test1.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "test_run"); - ASSERT_EQ(retval, 0, "test_run"); + ASSERT_EQ(topts.retval, 0, "test_run"); result = (__u64 *)fexit_skel->bss; for (i = 0; i < sizeof(*fexit_skel->bss) / sizeof(__u64); i++) { diff --git a/tools/testing/selftests/bpf/prog_tests/find_vma.c b/tools/testing/selftests/bpf/prog_tests/find_vma.c index b74b3c0c555a..5165b38f0e59 100644 --- a/tools/testing/selftests/bpf/prog_tests/find_vma.c +++ b/tools/testing/selftests/bpf/prog_tests/find_vma.c @@ -7,12 +7,14 @@ #include "find_vma_fail1.skel.h" #include "find_vma_fail2.skel.h" -static void test_and_reset_skel(struct find_vma *skel, int expected_find_zero_ret) +static void test_and_reset_skel(struct find_vma *skel, int expected_find_zero_ret, bool need_test) { - ASSERT_EQ(skel->bss->found_vm_exec, 1, "found_vm_exec"); - ASSERT_EQ(skel->data->find_addr_ret, 0, "find_addr_ret"); - ASSERT_EQ(skel->data->find_zero_ret, expected_find_zero_ret, "find_zero_ret"); - ASSERT_OK_PTR(strstr(skel->bss->d_iname, "test_progs"), "find_test_progs"); + if (need_test) { + ASSERT_EQ(skel->bss->found_vm_exec, 1, "found_vm_exec"); + ASSERT_EQ(skel->data->find_addr_ret, 0, "find_addr_ret"); + ASSERT_EQ(skel->data->find_zero_ret, expected_find_zero_ret, "find_zero_ret"); + ASSERT_OK_PTR(strstr(skel->bss->d_iname, "test_progs"), "find_test_progs"); + } skel->bss->found_vm_exec = 0; skel->data->find_addr_ret = -1; @@ -30,17 +32,26 @@ static int open_pe(void) attr.type = PERF_TYPE_HARDWARE; attr.config = PERF_COUNT_HW_CPU_CYCLES; attr.freq = 1; - attr.sample_freq = 4000; + attr.sample_freq = 1000; pfd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, PERF_FLAG_FD_CLOEXEC); return pfd >= 0 ? pfd : -errno; } +static bool find_vma_pe_condition(struct find_vma *skel) +{ + return skel->bss->found_vm_exec == 0 || + skel->data->find_addr_ret != 0 || + skel->data->find_zero_ret == -1 || + strcmp(skel->bss->d_iname, "test_progs") != 0; +} + static void test_find_vma_pe(struct find_vma *skel) { struct bpf_link *link = NULL; volatile int j = 0; int pfd, i; + const int one_bn = 1000000000; pfd = open_pe(); if (pfd < 0) { @@ -57,10 +68,10 @@ static void test_find_vma_pe(struct find_vma *skel) if (!ASSERT_OK_PTR(link, "attach_perf_event")) goto cleanup; - for (i = 0; i < 1000000; ++i) + for (i = 0; i < one_bn && find_vma_pe_condition(skel); ++i) ++j; - test_and_reset_skel(skel, -EBUSY /* in nmi, irq_work is busy */); + test_and_reset_skel(skel, -EBUSY /* in nmi, irq_work is busy */, i == one_bn); cleanup: bpf_link__destroy(link); close(pfd); @@ -75,7 +86,7 @@ static void test_find_vma_kprobe(struct find_vma *skel) return; getpgid(skel->bss->target_pid); - test_and_reset_skel(skel, -ENOENT /* could not find vma for ptr 0 */); + test_and_reset_skel(skel, -ENOENT /* could not find vma for ptr 0 */, true); } static void test_illegal_write_vma(void) @@ -108,7 +119,6 @@ void serial_test_find_vma(void) skel->bss->addr = (__u64)(uintptr_t)test_find_vma_pe; test_find_vma_pe(skel); - usleep(100000); /* allow the irq_work to finish */ test_find_vma_kprobe(skel); find_vma__destroy(skel); diff --git a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c index ac54e3f91d42..0c1661ea996e 100644 --- a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c +++ b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c @@ -13,8 +13,9 @@ #endif #define CHECK_FLOW_KEYS(desc, got, expected) \ - CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0, \ + _CHECK(memcmp(&got, &expected, sizeof(got)) != 0, \ desc, \ + topts.duration, \ "nhoff=%u/%u " \ "thoff=%u/%u " \ "addr_proto=0x%x/0x%x " \ @@ -457,7 +458,7 @@ static int init_prog_array(struct bpf_object *obj, struct bpf_map *prog_array) if (map_fd < 0) return -1; - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { snprintf(prog_name, sizeof(prog_name), "flow_dissector_%d", i); prog = bpf_object__find_program_by_name(obj, prog_name); @@ -487,7 +488,7 @@ static void run_tests_skb_less(int tap_fd, struct bpf_map *keys) /* Keep in sync with 'flags' from eth_get_headlen. */ __u32 eth_get_headlen_flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG; - struct bpf_prog_test_run_attr tattr = {}; + LIBBPF_OPTS(bpf_test_run_opts, topts); struct bpf_flow_keys flow_keys = {}; __u32 key = (__u32)(tests[i].keys.sport) << 16 | tests[i].keys.dport; @@ -503,13 +504,12 @@ static void run_tests_skb_less(int tap_fd, struct bpf_map *keys) CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno); err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys); - CHECK_ATTR(err, tests[i].name, "bpf_map_lookup_elem %d\n", err); + ASSERT_OK(err, "bpf_map_lookup_elem"); - CHECK_ATTR(err, tests[i].name, "skb-less err %d\n", err); CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys); err = bpf_map_delete_elem(keys_fd, &key); - CHECK_ATTR(err, tests[i].name, "bpf_map_delete_elem %d\n", err); + ASSERT_OK(err, "bpf_map_delete_elem"); } } @@ -573,27 +573,24 @@ void test_flow_dissector(void) for (i = 0; i < ARRAY_SIZE(tests); i++) { struct bpf_flow_keys flow_keys; - struct bpf_prog_test_run_attr tattr = { - .prog_fd = prog_fd, + LIBBPF_OPTS(bpf_test_run_opts, topts, .data_in = &tests[i].pkt, .data_size_in = sizeof(tests[i].pkt), .data_out = &flow_keys, - }; + ); static struct bpf_flow_keys ctx = {}; if (tests[i].flags) { - tattr.ctx_in = &ctx; - tattr.ctx_size_in = sizeof(ctx); + topts.ctx_in = &ctx; + topts.ctx_size_in = sizeof(ctx); ctx.flags = tests[i].flags; } - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) || - err || tattr.retval != 1, - tests[i].name, - "err %d errno %d retval %d duration %d size %u/%zu\n", - err, errno, tattr.retval, tattr.duration, - tattr.data_size_out, sizeof(flow_keys)); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.retval, 1, "test_run retval"); + ASSERT_EQ(topts.data_size_out, sizeof(flow_keys), + "test_run data_size_out"); CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys); } diff --git a/tools/testing/selftests/bpf/prog_tests/flow_dissector_load_bytes.c b/tools/testing/selftests/bpf/prog_tests/flow_dissector_load_bytes.c index 93ac3f28226c..36afb409c25f 100644 --- a/tools/testing/selftests/bpf/prog_tests/flow_dissector_load_bytes.c +++ b/tools/testing/selftests/bpf/prog_tests/flow_dissector_load_bytes.c @@ -5,7 +5,6 @@ void serial_test_flow_dissector_load_bytes(void) { struct bpf_flow_keys flow_keys; - __u32 duration = 0, retval, size; struct bpf_insn prog[] = { // BPF_REG_1 - 1st argument: context // BPF_REG_2 - 2nd argument: offset, start at first byte @@ -27,22 +26,25 @@ void serial_test_flow_dissector_load_bytes(void) BPF_EXIT_INSN(), }; int fd, err; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .data_out = &flow_keys, + .data_size_out = sizeof(flow_keys), + .repeat = 1, + ); /* make sure bpf_skb_load_bytes is not allowed from skb-less context */ fd = bpf_test_load_program(BPF_PROG_TYPE_FLOW_DISSECTOR, prog, ARRAY_SIZE(prog), "GPL", 0, NULL, 0); - CHECK(fd < 0, - "flow_dissector-bpf_skb_load_bytes-load", - "fd %d errno %d\n", - fd, errno); + ASSERT_GE(fd, 0, "bpf_test_load_program good fd"); - err = bpf_prog_test_run(fd, 1, &pkt_v4, sizeof(pkt_v4), - &flow_keys, &size, &retval, &duration); - CHECK(size != sizeof(flow_keys) || err || retval != 1, - "flow_dissector-bpf_skb_load_bytes", - "err %d errno %d retval %d duration %d size %u/%zu\n", - err, errno, retval, duration, size, sizeof(flow_keys)); + err = bpf_prog_test_run_opts(fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.data_size_out, sizeof(flow_keys), + "test_run data_size_out"); + ASSERT_EQ(topts.retval, 1, "test_run retval"); if (fd >= -1) close(fd); diff --git a/tools/testing/selftests/bpf/prog_tests/for_each.c b/tools/testing/selftests/bpf/prog_tests/for_each.c index 68eb12a287d4..044df13ee069 100644 --- a/tools/testing/selftests/bpf/prog_tests/for_each.c +++ b/tools/testing/selftests/bpf/prog_tests/for_each.c @@ -12,8 +12,13 @@ static void test_hash_map(void) int i, err, hashmap_fd, max_entries, percpu_map_fd; struct for_each_hash_map_elem *skel; __u64 *percpu_valbuf = NULL; - __u32 key, num_cpus, retval; + __u32 key, num_cpus; __u64 val; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); skel = for_each_hash_map_elem__open_and_load(); if (!ASSERT_OK_PTR(skel, "for_each_hash_map_elem__open_and_load")) @@ -42,11 +47,10 @@ static void test_hash_map(void) if (!ASSERT_OK(err, "percpu_map_update")) goto out; - err = bpf_prog_test_run(bpf_program__fd(skel->progs.test_pkt_access), - 1, &pkt_v4, sizeof(pkt_v4), NULL, NULL, - &retval, &duration); - if (CHECK(err || retval, "ipv4", "err %d errno %d retval %d\n", - err, errno, retval)) + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_pkt_access), &topts); + duration = topts.duration; + if (CHECK(err || topts.retval, "ipv4", "err %d errno %d retval %d\n", + err, errno, topts.retval)) goto out; ASSERT_EQ(skel->bss->hashmap_output, 4, "hashmap_output"); @@ -69,11 +73,16 @@ static void test_hash_map(void) static void test_array_map(void) { - __u32 key, num_cpus, max_entries, retval; + __u32 key, num_cpus, max_entries; int i, arraymap_fd, percpu_map_fd, err; struct for_each_array_map_elem *skel; __u64 *percpu_valbuf = NULL; __u64 val, expected_total; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); skel = for_each_array_map_elem__open_and_load(); if (!ASSERT_OK_PTR(skel, "for_each_array_map_elem__open_and_load")) @@ -106,11 +115,10 @@ static void test_array_map(void) if (!ASSERT_OK(err, "percpu_map_update")) goto out; - err = bpf_prog_test_run(bpf_program__fd(skel->progs.test_pkt_access), - 1, &pkt_v4, sizeof(pkt_v4), NULL, NULL, - &retval, &duration); - if (CHECK(err || retval, "ipv4", "err %d errno %d retval %d\n", - err, errno, retval)) + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_pkt_access), &topts); + duration = topts.duration; + if (CHECK(err || topts.retval, "ipv4", "err %d errno %d retval %d\n", + err, errno, topts.retval)) goto out; ASSERT_EQ(skel->bss->arraymap_output, expected_total, "array_output"); diff --git a/tools/testing/selftests/bpf/prog_tests/get_func_args_test.c b/tools/testing/selftests/bpf/prog_tests/get_func_args_test.c index 85c427119fe9..28cf63963cb7 100644 --- a/tools/testing/selftests/bpf/prog_tests/get_func_args_test.c +++ b/tools/testing/selftests/bpf/prog_tests/get_func_args_test.c @@ -5,8 +5,8 @@ void test_get_func_args_test(void) { struct get_func_args_test *skel = NULL; - __u32 duration = 0, retval; int err, prog_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts); skel = get_func_args_test__open_and_load(); if (!ASSERT_OK_PTR(skel, "get_func_args_test__open_and_load")) @@ -20,19 +20,17 @@ void test_get_func_args_test(void) * fentry/fexit programs. */ prog_fd = bpf_program__fd(skel->progs.test1); - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "test_run"); - ASSERT_EQ(retval, 0, "test_run"); + ASSERT_EQ(topts.retval, 0, "test_run"); /* This runs bpf_modify_return_test function and triggers * fmod_ret_test and fexit_test programs. */ prog_fd = bpf_program__fd(skel->progs.fmod_ret_test); - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "test_run"); - ASSERT_EQ(retval, 1234, "test_run"); + ASSERT_EQ(topts.retval, 1234, "test_run"); ASSERT_EQ(skel->bss->test1_result, 1, "test1_result"); ASSERT_EQ(skel->bss->test2_result, 1, "test2_result"); diff --git a/tools/testing/selftests/bpf/prog_tests/get_func_ip_test.c b/tools/testing/selftests/bpf/prog_tests/get_func_ip_test.c index 02a465f36d59..938dbd4d7c2f 100644 --- a/tools/testing/selftests/bpf/prog_tests/get_func_ip_test.c +++ b/tools/testing/selftests/bpf/prog_tests/get_func_ip_test.c @@ -5,8 +5,8 @@ void test_get_func_ip_test(void) { struct get_func_ip_test *skel = NULL; - __u32 duration = 0, retval; int err, prog_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts); skel = get_func_ip_test__open(); if (!ASSERT_OK_PTR(skel, "get_func_ip_test__open")) @@ -29,14 +29,12 @@ void test_get_func_ip_test(void) goto cleanup; prog_fd = bpf_program__fd(skel->progs.test1); - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "test_run"); - ASSERT_EQ(retval, 0, "test_run"); + ASSERT_EQ(topts.retval, 0, "test_run"); prog_fd = bpf_program__fd(skel->progs.test5); - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "test_run"); diff --git a/tools/testing/selftests/bpf/prog_tests/get_stackid_cannot_attach.c b/tools/testing/selftests/bpf/prog_tests/get_stackid_cannot_attach.c index 8d5a6023a1bb..5308de1ed478 100644 --- a/tools/testing/selftests/bpf/prog_tests/get_stackid_cannot_attach.c +++ b/tools/testing/selftests/bpf/prog_tests/get_stackid_cannot_attach.c @@ -27,7 +27,7 @@ void test_get_stackid_cannot_attach(void) return; /* override program type */ - bpf_program__set_perf_event(skel->progs.oncpu); + bpf_program__set_type(skel->progs.oncpu, BPF_PROG_TYPE_PERF_EVENT); err = test_stacktrace_build_id__load(skel); if (CHECK(err, "skel_load", "skeleton load failed: %d\n", err)) diff --git a/tools/testing/selftests/bpf/prog_tests/global_data.c b/tools/testing/selftests/bpf/prog_tests/global_data.c index 9da131b32e13..027685858925 100644 --- a/tools/testing/selftests/bpf/prog_tests/global_data.c +++ b/tools/testing/selftests/bpf/prog_tests/global_data.c @@ -29,7 +29,7 @@ static void test_global_data_number(struct bpf_object *obj, __u32 duration) { "relocate .rodata reference", 10, ~0 }, }; - for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + for (i = 0; i < ARRAY_SIZE(tests); i++) { err = bpf_map_lookup_elem(map_fd, &tests[i].key, &num); CHECK(err || num != tests[i].num, tests[i].name, "err %d result %llx expected %llx\n", @@ -58,7 +58,7 @@ static void test_global_data_string(struct bpf_object *obj, __u32 duration) { "relocate .bss reference", 4, "\0\0hello" }, }; - for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + for (i = 0; i < ARRAY_SIZE(tests); i++) { err = bpf_map_lookup_elem(map_fd, &tests[i].key, str); CHECK(err || memcmp(str, tests[i].str, sizeof(str)), tests[i].name, "err %d result \'%s\' expected \'%s\'\n", @@ -92,7 +92,7 @@ static void test_global_data_struct(struct bpf_object *obj, __u32 duration) { "relocate .data reference", 3, { 41, 0xeeeeefef, 0x2111111111111111ULL, } }, }; - for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + for (i = 0; i < ARRAY_SIZE(tests); i++) { err = bpf_map_lookup_elem(map_fd, &tests[i].key, &val); CHECK(err || memcmp(&val, &tests[i].val, sizeof(val)), tests[i].name, "err %d result { %u, %u, %llu } expected { %u, %u, %llu }\n", @@ -121,7 +121,7 @@ static void test_global_data_rdonly(struct bpf_object *obj, __u32 duration) if (CHECK_FAIL(map_fd < 0)) return; - buff = malloc(bpf_map__def(map)->value_size); + buff = malloc(bpf_map__value_size(map)); if (buff) err = bpf_map_update_elem(map_fd, &zero, buff, 0); free(buff); @@ -132,24 +132,26 @@ static void test_global_data_rdonly(struct bpf_object *obj, __u32 duration) void test_global_data(void) { const char *file = "./test_global_data.o"; - __u32 duration = 0, retval; struct bpf_object *obj; int err, prog_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); err = bpf_prog_test_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); - if (CHECK(err, "load program", "error %d loading %s\n", err, file)) + if (!ASSERT_OK(err, "load program")) return; - err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, &retval, &duration); - CHECK(err || retval, "pass global data run", - "err %d errno %d retval %d duration %d\n", - err, errno, retval, duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "pass global data run err"); + ASSERT_OK(topts.retval, "pass global data run retval"); - test_global_data_number(obj, duration); - test_global_data_string(obj, duration); - test_global_data_struct(obj, duration); - test_global_data_rdonly(obj, duration); + test_global_data_number(obj, topts.duration); + test_global_data_string(obj, topts.duration); + test_global_data_struct(obj, topts.duration); + test_global_data_rdonly(obj, topts.duration); bpf_object__close(obj); } diff --git a/tools/testing/selftests/bpf/prog_tests/global_data_init.c b/tools/testing/selftests/bpf/prog_tests/global_data_init.c index 1db86eab101b..57331c606964 100644 --- a/tools/testing/selftests/bpf/prog_tests/global_data_init.c +++ b/tools/testing/selftests/bpf/prog_tests/global_data_init.c @@ -20,7 +20,7 @@ void test_global_data_init(void) if (CHECK_FAIL(!map || !bpf_map__is_internal(map))) goto out; - sz = bpf_map__def(map)->value_size; + sz = bpf_map__value_size(map); newval = malloc(sz); if (CHECK_FAIL(!newval)) goto out; diff --git a/tools/testing/selftests/bpf/prog_tests/global_func_args.c b/tools/testing/selftests/bpf/prog_tests/global_func_args.c index 93a2439237b0..29039a36cce5 100644 --- a/tools/testing/selftests/bpf/prog_tests/global_func_args.c +++ b/tools/testing/selftests/bpf/prog_tests/global_func_args.c @@ -40,19 +40,21 @@ static void test_global_func_args0(struct bpf_object *obj) void test_global_func_args(void) { const char *file = "./test_global_func_args.o"; - __u32 retval; struct bpf_object *obj; int err, prog_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); err = bpf_prog_test_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd); if (CHECK(err, "load program", "error %d loading %s\n", err, file)) return; - err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, &retval, &duration); - CHECK(err || retval, "pass global func args run", - "err %d errno %d retval %d duration %d\n", - err, errno, retval, duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_OK(topts.retval, "test_run retval"); test_global_func_args0(obj); diff --git a/tools/testing/selftests/bpf/prog_tests/kfree_skb.c b/tools/testing/selftests/bpf/prog_tests/kfree_skb.c index ce10d2fc3a6c..1cee6957285e 100644 --- a/tools/testing/selftests/bpf/prog_tests/kfree_skb.c +++ b/tools/testing/selftests/bpf/prog_tests/kfree_skb.c @@ -53,24 +53,24 @@ static void on_sample(void *ctx, int cpu, void *data, __u32 size) void serial_test_kfree_skb(void) { struct __sk_buff skb = {}; - struct bpf_prog_test_run_attr tattr = { + LIBBPF_OPTS(bpf_test_run_opts, topts, .data_in = &pkt_v6, .data_size_in = sizeof(pkt_v6), .ctx_in = &skb, .ctx_size_in = sizeof(skb), - }; + ); struct kfree_skb *skel = NULL; struct bpf_link *link; struct bpf_object *obj; struct perf_buffer *pb = NULL; - int err; + int err, prog_fd; bool passed = false; __u32 duration = 0; const int zero = 0; bool test_ok[2]; err = bpf_prog_test_load("./test_pkt_access.o", BPF_PROG_TYPE_SCHED_CLS, - &obj, &tattr.prog_fd); + &obj, &prog_fd); if (CHECK(err, "prog_load sched cls", "err %d errno %d\n", err, errno)) return; @@ -100,11 +100,9 @@ void serial_test_kfree_skb(void) goto close_prog; memcpy(skb.cb, &cb, sizeof(cb)); - err = bpf_prog_test_run_xattr(&tattr); - duration = tattr.duration; - CHECK(err || tattr.retval, "ipv6", - "err %d errno %d retval %d duration %d\n", - err, errno, tattr.retval, duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "ipv6 test_run"); + ASSERT_OK(topts.retval, "ipv6 test_run retval"); /* read perf buffer */ err = perf_buffer__poll(pb, 100); diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c index 7d7445ccc141..c00eb974eb85 100644 --- a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c +++ b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c @@ -9,23 +9,31 @@ static void test_main(void) { struct kfunc_call_test_lskel *skel; - int prog_fd, retval, err; + int prog_fd, err; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); skel = kfunc_call_test_lskel__open_and_load(); if (!ASSERT_OK_PTR(skel, "skel")) return; prog_fd = skel->progs.kfunc_call_test1.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, (__u32 *)&retval, NULL); + err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "bpf_prog_test_run(test1)"); - ASSERT_EQ(retval, 12, "test1-retval"); + ASSERT_EQ(topts.retval, 12, "test1-retval"); prog_fd = skel->progs.kfunc_call_test2.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, (__u32 *)&retval, NULL); + err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "bpf_prog_test_run(test2)"); - ASSERT_EQ(retval, 3, "test2-retval"); + ASSERT_EQ(topts.retval, 3, "test2-retval"); + + prog_fd = skel->progs.kfunc_call_test_ref_btf_id.prog_fd; + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "bpf_prog_test_run(test_ref_btf_id)"); + ASSERT_EQ(topts.retval, 0, "test_ref_btf_id-retval"); kfunc_call_test_lskel__destroy(skel); } @@ -33,17 +41,21 @@ static void test_main(void) static void test_subprog(void) { struct kfunc_call_test_subprog *skel; - int prog_fd, retval, err; + int prog_fd, err; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); skel = kfunc_call_test_subprog__open_and_load(); if (!ASSERT_OK_PTR(skel, "skel")) return; prog_fd = bpf_program__fd(skel->progs.kfunc_call_test1); - err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, (__u32 *)&retval, NULL); + err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "bpf_prog_test_run(test1)"); - ASSERT_EQ(retval, 10, "test1-retval"); + ASSERT_EQ(topts.retval, 10, "test1-retval"); ASSERT_NEQ(skel->data->active_res, -1, "active_res"); ASSERT_EQ(skel->data->sk_state_res, BPF_TCP_CLOSE, "sk_state_res"); @@ -53,17 +65,21 @@ static void test_subprog(void) static void test_subprog_lskel(void) { struct kfunc_call_test_subprog_lskel *skel; - int prog_fd, retval, err; + int prog_fd, err; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); skel = kfunc_call_test_subprog_lskel__open_and_load(); if (!ASSERT_OK_PTR(skel, "skel")) return; prog_fd = skel->progs.kfunc_call_test1.prog_fd; - err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, (__u32 *)&retval, NULL); + err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "bpf_prog_test_run(test1)"); - ASSERT_EQ(retval, 10, "test1-retval"); + ASSERT_EQ(topts.retval, 10, "test1-retval"); ASSERT_NEQ(skel->data->active_res, -1, "active_res"); ASSERT_EQ(skel->data->sk_state_res, BPF_TCP_CLOSE, "sk_state_res"); diff --git a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c new file mode 100644 index 000000000000..b9876b55fc0c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include "kprobe_multi.skel.h" +#include "trace_helpers.h" + +static void kprobe_multi_test_run(struct kprobe_multi *skel, bool test_return) +{ + LIBBPF_OPTS(bpf_test_run_opts, topts); + int err, prog_fd; + + prog_fd = bpf_program__fd(skel->progs.trigger); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.retval, 0, "test_run"); + + ASSERT_EQ(skel->bss->kprobe_test1_result, 1, "kprobe_test1_result"); + ASSERT_EQ(skel->bss->kprobe_test2_result, 1, "kprobe_test2_result"); + ASSERT_EQ(skel->bss->kprobe_test3_result, 1, "kprobe_test3_result"); + ASSERT_EQ(skel->bss->kprobe_test4_result, 1, "kprobe_test4_result"); + ASSERT_EQ(skel->bss->kprobe_test5_result, 1, "kprobe_test5_result"); + ASSERT_EQ(skel->bss->kprobe_test6_result, 1, "kprobe_test6_result"); + ASSERT_EQ(skel->bss->kprobe_test7_result, 1, "kprobe_test7_result"); + ASSERT_EQ(skel->bss->kprobe_test8_result, 1, "kprobe_test8_result"); + + if (test_return) { + ASSERT_EQ(skel->bss->kretprobe_test1_result, 1, "kretprobe_test1_result"); + ASSERT_EQ(skel->bss->kretprobe_test2_result, 1, "kretprobe_test2_result"); + ASSERT_EQ(skel->bss->kretprobe_test3_result, 1, "kretprobe_test3_result"); + ASSERT_EQ(skel->bss->kretprobe_test4_result, 1, "kretprobe_test4_result"); + ASSERT_EQ(skel->bss->kretprobe_test5_result, 1, "kretprobe_test5_result"); + ASSERT_EQ(skel->bss->kretprobe_test6_result, 1, "kretprobe_test6_result"); + ASSERT_EQ(skel->bss->kretprobe_test7_result, 1, "kretprobe_test7_result"); + ASSERT_EQ(skel->bss->kretprobe_test8_result, 1, "kretprobe_test8_result"); + } +} + +static void test_skel_api(void) +{ + struct kprobe_multi *skel = NULL; + int err; + + skel = kprobe_multi__open_and_load(); + if (!ASSERT_OK_PTR(skel, "kprobe_multi__open_and_load")) + goto cleanup; + + skel->bss->pid = getpid(); + err = kprobe_multi__attach(skel); + if (!ASSERT_OK(err, "kprobe_multi__attach")) + goto cleanup; + + kprobe_multi_test_run(skel, true); + +cleanup: + kprobe_multi__destroy(skel); +} + +static void test_link_api(struct bpf_link_create_opts *opts) +{ + int prog_fd, link1_fd = -1, link2_fd = -1; + struct kprobe_multi *skel = NULL; + + skel = kprobe_multi__open_and_load(); + if (!ASSERT_OK_PTR(skel, "fentry_raw_skel_load")) + goto cleanup; + + skel->bss->pid = getpid(); + prog_fd = bpf_program__fd(skel->progs.test_kprobe); + link1_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_KPROBE_MULTI, opts); + if (!ASSERT_GE(link1_fd, 0, "link_fd")) + goto cleanup; + + opts->kprobe_multi.flags = BPF_F_KPROBE_MULTI_RETURN; + prog_fd = bpf_program__fd(skel->progs.test_kretprobe); + link2_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_KPROBE_MULTI, opts); + if (!ASSERT_GE(link2_fd, 0, "link_fd")) + goto cleanup; + + kprobe_multi_test_run(skel, true); + +cleanup: + if (link1_fd != -1) + close(link1_fd); + if (link2_fd != -1) + close(link2_fd); + kprobe_multi__destroy(skel); +} + +#define GET_ADDR(__sym, __addr) ({ \ + __addr = ksym_get_addr(__sym); \ + if (!ASSERT_NEQ(__addr, 0, "kallsyms load failed for " #__sym)) \ + return; \ +}) + +static void test_link_api_addrs(void) +{ + LIBBPF_OPTS(bpf_link_create_opts, opts); + unsigned long long addrs[8]; + + GET_ADDR("bpf_fentry_test1", addrs[0]); + GET_ADDR("bpf_fentry_test2", addrs[1]); + GET_ADDR("bpf_fentry_test3", addrs[2]); + GET_ADDR("bpf_fentry_test4", addrs[3]); + GET_ADDR("bpf_fentry_test5", addrs[4]); + GET_ADDR("bpf_fentry_test6", addrs[5]); + GET_ADDR("bpf_fentry_test7", addrs[6]); + GET_ADDR("bpf_fentry_test8", addrs[7]); + + opts.kprobe_multi.addrs = (const unsigned long*) addrs; + opts.kprobe_multi.cnt = ARRAY_SIZE(addrs); + test_link_api(&opts); +} + +static void test_link_api_syms(void) +{ + LIBBPF_OPTS(bpf_link_create_opts, opts); + const char *syms[8] = { + "bpf_fentry_test1", + "bpf_fentry_test2", + "bpf_fentry_test3", + "bpf_fentry_test4", + "bpf_fentry_test5", + "bpf_fentry_test6", + "bpf_fentry_test7", + "bpf_fentry_test8", + }; + + opts.kprobe_multi.syms = syms; + opts.kprobe_multi.cnt = ARRAY_SIZE(syms); + test_link_api(&opts); +} + +static void +test_attach_api(const char *pattern, struct bpf_kprobe_multi_opts *opts) +{ + struct bpf_link *link1 = NULL, *link2 = NULL; + struct kprobe_multi *skel = NULL; + + skel = kprobe_multi__open_and_load(); + if (!ASSERT_OK_PTR(skel, "fentry_raw_skel_load")) + goto cleanup; + + skel->bss->pid = getpid(); + link1 = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kprobe, + pattern, opts); + if (!ASSERT_OK_PTR(link1, "bpf_program__attach_kprobe_multi_opts")) + goto cleanup; + + if (opts) { + opts->retprobe = true; + link2 = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kretprobe, + pattern, opts); + if (!ASSERT_OK_PTR(link2, "bpf_program__attach_kprobe_multi_opts")) + goto cleanup; + } + + kprobe_multi_test_run(skel, !!opts); + +cleanup: + bpf_link__destroy(link2); + bpf_link__destroy(link1); + kprobe_multi__destroy(skel); +} + +static void test_attach_api_pattern(void) +{ + LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); + + test_attach_api("bpf_fentry_test*", &opts); + test_attach_api("bpf_fentry_test?", NULL); +} + +static void test_attach_api_addrs(void) +{ + LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); + unsigned long long addrs[8]; + + GET_ADDR("bpf_fentry_test1", addrs[0]); + GET_ADDR("bpf_fentry_test2", addrs[1]); + GET_ADDR("bpf_fentry_test3", addrs[2]); + GET_ADDR("bpf_fentry_test4", addrs[3]); + GET_ADDR("bpf_fentry_test5", addrs[4]); + GET_ADDR("bpf_fentry_test6", addrs[5]); + GET_ADDR("bpf_fentry_test7", addrs[6]); + GET_ADDR("bpf_fentry_test8", addrs[7]); + + opts.addrs = (const unsigned long *) addrs; + opts.cnt = ARRAY_SIZE(addrs); + test_attach_api(NULL, &opts); +} + +static void test_attach_api_syms(void) +{ + LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); + const char *syms[8] = { + "bpf_fentry_test1", + "bpf_fentry_test2", + "bpf_fentry_test3", + "bpf_fentry_test4", + "bpf_fentry_test5", + "bpf_fentry_test6", + "bpf_fentry_test7", + "bpf_fentry_test8", + }; + + opts.syms = syms; + opts.cnt = ARRAY_SIZE(syms); + test_attach_api(NULL, &opts); +} + +static void test_attach_api_fails(void) +{ + LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); + struct kprobe_multi *skel = NULL; + struct bpf_link *link = NULL; + unsigned long long addrs[2]; + const char *syms[2] = { + "bpf_fentry_test1", + "bpf_fentry_test2", + }; + __u64 cookies[2]; + + addrs[0] = ksym_get_addr("bpf_fentry_test1"); + addrs[1] = ksym_get_addr("bpf_fentry_test2"); + + if (!ASSERT_FALSE(!addrs[0] || !addrs[1], "ksym_get_addr")) + goto cleanup; + + skel = kprobe_multi__open_and_load(); + if (!ASSERT_OK_PTR(skel, "fentry_raw_skel_load")) + goto cleanup; + + skel->bss->pid = getpid(); + + /* fail_1 - pattern and opts NULL */ + link = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kprobe, + NULL, NULL); + if (!ASSERT_ERR_PTR(link, "fail_1")) + goto cleanup; + + if (!ASSERT_EQ(libbpf_get_error(link), -EINVAL, "fail_1_error")) + goto cleanup; + + /* fail_2 - both addrs and syms set */ + opts.addrs = (const unsigned long *) addrs; + opts.syms = syms; + opts.cnt = ARRAY_SIZE(syms); + opts.cookies = NULL; + + link = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kprobe, + NULL, &opts); + if (!ASSERT_ERR_PTR(link, "fail_2")) + goto cleanup; + + if (!ASSERT_EQ(libbpf_get_error(link), -EINVAL, "fail_2_error")) + goto cleanup; + + /* fail_3 - pattern and addrs set */ + opts.addrs = (const unsigned long *) addrs; + opts.syms = NULL; + opts.cnt = ARRAY_SIZE(syms); + opts.cookies = NULL; + + link = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kprobe, + "ksys_*", &opts); + if (!ASSERT_ERR_PTR(link, "fail_3")) + goto cleanup; + + if (!ASSERT_EQ(libbpf_get_error(link), -EINVAL, "fail_3_error")) + goto cleanup; + + /* fail_4 - pattern and cnt set */ + opts.addrs = NULL; + opts.syms = NULL; + opts.cnt = ARRAY_SIZE(syms); + opts.cookies = NULL; + + link = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kprobe, + "ksys_*", &opts); + if (!ASSERT_ERR_PTR(link, "fail_4")) + goto cleanup; + + if (!ASSERT_EQ(libbpf_get_error(link), -EINVAL, "fail_4_error")) + goto cleanup; + + /* fail_5 - pattern and cookies */ + opts.addrs = NULL; + opts.syms = NULL; + opts.cnt = 0; + opts.cookies = cookies; + + link = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kprobe, + "ksys_*", &opts); + if (!ASSERT_ERR_PTR(link, "fail_5")) + goto cleanup; + + if (!ASSERT_EQ(libbpf_get_error(link), -EINVAL, "fail_5_error")) + goto cleanup; + +cleanup: + bpf_link__destroy(link); + kprobe_multi__destroy(skel); +} + +void test_kprobe_multi_test(void) +{ + if (!ASSERT_OK(load_kallsyms(), "load_kallsyms")) + return; + + if (test__start_subtest("skel_api")) + test_skel_api(); + if (test__start_subtest("link_api_addrs")) + test_link_api_syms(); + if (test__start_subtest("link_api_syms")) + test_link_api_addrs(); + if (test__start_subtest("attach_api_pattern")) + test_attach_api_pattern(); + if (test__start_subtest("attach_api_addrs")) + test_attach_api_addrs(); + if (test__start_subtest("attach_api_syms")) + test_attach_api_syms(); + if (test__start_subtest("attach_api_fails")) + test_attach_api_fails(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/ksyms_module.c b/tools/testing/selftests/bpf/prog_tests/ksyms_module.c index d490ad80eccb..a1ebac70ec29 100644 --- a/tools/testing/selftests/bpf/prog_tests/ksyms_module.c +++ b/tools/testing/selftests/bpf/prog_tests/ksyms_module.c @@ -6,11 +6,15 @@ #include "test_ksyms_module.lskel.h" #include "test_ksyms_module.skel.h" -void test_ksyms_module_lskel(void) +static void test_ksyms_module_lskel(void) { struct test_ksyms_module_lskel *skel; - int retval; int err; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); if (!env.has_testmod) { test__skip(); @@ -20,20 +24,24 @@ void test_ksyms_module_lskel(void) skel = test_ksyms_module_lskel__open_and_load(); if (!ASSERT_OK_PTR(skel, "test_ksyms_module_lskel__open_and_load")) return; - err = bpf_prog_test_run(skel->progs.load.prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, (__u32 *)&retval, NULL); + err = bpf_prog_test_run_opts(skel->progs.load.prog_fd, &topts); if (!ASSERT_OK(err, "bpf_prog_test_run")) goto cleanup; - ASSERT_EQ(retval, 0, "retval"); + ASSERT_EQ(topts.retval, 0, "retval"); ASSERT_EQ(skel->bss->out_bpf_testmod_ksym, 42, "bpf_testmod_ksym"); cleanup: test_ksyms_module_lskel__destroy(skel); } -void test_ksyms_module_libbpf(void) +static void test_ksyms_module_libbpf(void) { struct test_ksyms_module *skel; - int retval, err; + int err; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); if (!env.has_testmod) { test__skip(); @@ -43,11 +51,10 @@ void test_ksyms_module_libbpf(void) skel = test_ksyms_module__open_and_load(); if (!ASSERT_OK_PTR(skel, "test_ksyms_module__open")) return; - err = bpf_prog_test_run(bpf_program__fd(skel->progs.load), 1, &pkt_v4, - sizeof(pkt_v4), NULL, NULL, (__u32 *)&retval, NULL); + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.load), &topts); if (!ASSERT_OK(err, "bpf_prog_test_run")) goto cleanup; - ASSERT_EQ(retval, 0, "retval"); + ASSERT_EQ(topts.retval, 0, "retval"); ASSERT_EQ(skel->bss->out_bpf_testmod_ksym, 42, "bpf_testmod_ksym"); cleanup: test_ksyms_module__destroy(skel); diff --git a/tools/testing/selftests/bpf/prog_tests/l4lb_all.c b/tools/testing/selftests/bpf/prog_tests/l4lb_all.c index 540ef28fabff..55f733ff4109 100644 --- a/tools/testing/selftests/bpf/prog_tests/l4lb_all.c +++ b/tools/testing/selftests/bpf/prog_tests/l4lb_all.c @@ -23,12 +23,16 @@ static void test_l4lb(const char *file) __u8 flags; } real_def = {.dst = MAGIC_VAL}; __u32 ch_key = 11, real_num = 3; - __u32 duration, retval, size; int err, i, prog_fd, map_fd; __u64 bytes = 0, pkts = 0; struct bpf_object *obj; char buf[128]; u32 *magic = (u32 *)buf; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_out = buf, + .data_size_out = sizeof(buf), + .repeat = NUM_ITER, + ); err = bpf_prog_test_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); if (CHECK_FAIL(err)) @@ -49,19 +53,24 @@ static void test_l4lb(const char *file) goto out; bpf_map_update_elem(map_fd, &real_num, &real_def, 0); - err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v4, sizeof(pkt_v4), - buf, &size, &retval, &duration); - CHECK(err || retval != 7/*TC_ACT_REDIRECT*/ || size != 54 || - *magic != MAGIC_VAL, "ipv4", - "err %d errno %d retval %d size %d magic %x\n", - err, errno, retval, size, *magic); + topts.data_in = &pkt_v4; + topts.data_size_in = sizeof(pkt_v4); - err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v6, sizeof(pkt_v6), - buf, &size, &retval, &duration); - CHECK(err || retval != 7/*TC_ACT_REDIRECT*/ || size != 74 || - *magic != MAGIC_VAL, "ipv6", - "err %d errno %d retval %d size %d magic %x\n", - err, errno, retval, size, *magic); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.retval, 7 /*TC_ACT_REDIRECT*/, "ipv4 test_run retval"); + ASSERT_EQ(topts.data_size_out, 54, "ipv4 test_run data_size_out"); + ASSERT_EQ(*magic, MAGIC_VAL, "ipv4 magic"); + + topts.data_in = &pkt_v6; + topts.data_size_in = sizeof(pkt_v6); + topts.data_size_out = sizeof(buf); /* reset out size */ + + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.retval, 7 /*TC_ACT_REDIRECT*/, "ipv6 test_run retval"); + ASSERT_EQ(topts.data_size_out, 74, "ipv6 test_run data_size_out"); + ASSERT_EQ(*magic, MAGIC_VAL, "ipv6 magic"); map_fd = bpf_find_map(__func__, obj, "stats"); if (map_fd < 0) diff --git a/tools/testing/selftests/bpf/prog_tests/log_buf.c b/tools/testing/selftests/bpf/prog_tests/log_buf.c index e469b023962b..fe9a23e65ef4 100644 --- a/tools/testing/selftests/bpf/prog_tests/log_buf.c +++ b/tools/testing/selftests/bpf/prog_tests/log_buf.c @@ -78,7 +78,7 @@ static void obj_load_log_buf(void) ASSERT_OK_PTR(strstr(libbpf_log_buf, "prog 'bad_prog': BPF program load failed"), "libbpf_log_not_empty"); ASSERT_OK_PTR(strstr(obj_log_buf, "DATASEC license"), "obj_log_not_empty"); - ASSERT_OK_PTR(strstr(good_log_buf, "0: R1=ctx(id=0,off=0,imm=0) R10=fp0"), + ASSERT_OK_PTR(strstr(good_log_buf, "0: R1=ctx(off=0,imm=0) R10=fp0"), "good_log_verbose"); ASSERT_OK_PTR(strstr(bad_log_buf, "invalid access to map value, value_size=16 off=16000 size=4"), "bad_log_not_empty"); @@ -175,7 +175,7 @@ static void bpf_prog_load_log_buf(void) opts.log_level = 2; fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, "good_prog", "GPL", good_prog_insns, good_prog_insn_cnt, &opts); - ASSERT_OK_PTR(strstr(log_buf, "0: R1=ctx(id=0,off=0,imm=0) R10=fp0"), "good_log_2"); + ASSERT_OK_PTR(strstr(log_buf, "0: R1=ctx(off=0,imm=0) R10=fp0"), "good_log_2"); ASSERT_GE(fd, 0, "good_fd2"); if (fd >= 0) close(fd); @@ -202,7 +202,7 @@ static void bpf_btf_load_log_buf(void) const void *raw_btf_data; __u32 raw_btf_size; struct btf *btf; - char *log_buf; + char *log_buf = NULL; int fd = -1; btf = btf__new_empty(); diff --git a/tools/testing/selftests/bpf/prog_tests/map_lock.c b/tools/testing/selftests/bpf/prog_tests/map_lock.c index 23d19e9cf26a..e4e99b37df64 100644 --- a/tools/testing/selftests/bpf/prog_tests/map_lock.c +++ b/tools/testing/selftests/bpf/prog_tests/map_lock.c @@ -4,14 +4,17 @@ static void *spin_lock_thread(void *arg) { - __u32 duration, retval; int err, prog_fd = *(u32 *) arg; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 10000, + ); + + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run_opts err"); + ASSERT_OK(topts.retval, "test_run_opts retval"); - err = bpf_prog_test_run(prog_fd, 10000, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, &retval, &duration); - CHECK(err || retval, "", - "err %d errno %d retval %d duration %d\n", - err, errno, retval, duration); pthread_exit(arg); } diff --git a/tools/testing/selftests/bpf/prog_tests/map_ptr.c b/tools/testing/selftests/bpf/prog_tests/map_ptr.c index 273725504f11..43e502acf050 100644 --- a/tools/testing/selftests/bpf/prog_tests/map_ptr.c +++ b/tools/testing/selftests/bpf/prog_tests/map_ptr.c @@ -9,10 +9,16 @@ void test_map_ptr(void) { struct map_ptr_kern_lskel *skel; - __u32 duration = 0, retval; char buf[128]; int err; int page_size = getpagesize(); + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .data_out = buf, + .data_size_out = sizeof(buf), + .repeat = 1, + ); skel = map_ptr_kern_lskel__open(); if (!ASSERT_OK_PTR(skel, "skel_open")) @@ -26,14 +32,12 @@ void test_map_ptr(void) skel->bss->page_size = page_size; - err = bpf_prog_test_run(skel->progs.cg_skb.prog_fd, 1, &pkt_v4, - sizeof(pkt_v4), buf, NULL, &retval, NULL); + err = bpf_prog_test_run_opts(skel->progs.cg_skb.prog_fd, &topts); - if (CHECK(err, "test_run", "err=%d errno=%d\n", err, errno)) + if (!ASSERT_OK(err, "test_run")) goto cleanup; - if (CHECK(!retval, "retval", "retval=%d map_type=%u line=%u\n", retval, - skel->bss->g_map_type, skel->bss->g_line)) + if (!ASSERT_NEQ(topts.retval, 0, "test_run retval")) goto cleanup; cleanup: diff --git a/tools/testing/selftests/bpf/prog_tests/modify_return.c b/tools/testing/selftests/bpf/prog_tests/modify_return.c index b772fe30ce9b..5d9955af6247 100644 --- a/tools/testing/selftests/bpf/prog_tests/modify_return.c +++ b/tools/testing/selftests/bpf/prog_tests/modify_return.c @@ -15,39 +15,31 @@ static void run_test(__u32 input_retval, __u16 want_side_effect, __s16 want_ret) { struct modify_return *skel = NULL; int err, prog_fd; - __u32 duration = 0, retval; __u16 side_effect; __s16 ret; + LIBBPF_OPTS(bpf_test_run_opts, topts); skel = modify_return__open_and_load(); - if (CHECK(!skel, "skel_load", "modify_return skeleton failed\n")) + if (!ASSERT_OK_PTR(skel, "skel_load")) goto cleanup; err = modify_return__attach(skel); - if (CHECK(err, "modify_return", "attach failed: %d\n", err)) + if (!ASSERT_OK(err, "modify_return__attach failed")) goto cleanup; skel->bss->input_retval = input_retval; prog_fd = bpf_program__fd(skel->progs.fmod_ret_test); - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, NULL, 0, - &retval, &duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); - CHECK(err, "test_run", "err %d errno %d\n", err, errno); + side_effect = UPPER(topts.retval); + ret = LOWER(topts.retval); - side_effect = UPPER(retval); - ret = LOWER(retval); - - CHECK(ret != want_ret, "test_run", - "unexpected ret: %d, expected: %d\n", ret, want_ret); - CHECK(side_effect != want_side_effect, "modify_return", - "unexpected side_effect: %d\n", side_effect); - - CHECK(skel->bss->fentry_result != 1, "modify_return", - "fentry failed\n"); - CHECK(skel->bss->fexit_result != 1, "modify_return", - "fexit failed\n"); - CHECK(skel->bss->fmod_ret_result != 1, "modify_return", - "fmod_ret failed\n"); + ASSERT_EQ(ret, want_ret, "test_run ret"); + ASSERT_EQ(side_effect, want_side_effect, "modify_return side_effect"); + ASSERT_EQ(skel->bss->fentry_result, 1, "modify_return fentry_result"); + ASSERT_EQ(skel->bss->fexit_result, 1, "modify_return fexit_result"); + ASSERT_EQ(skel->bss->fmod_ret_result, 1, "modify_return fmod_ret_result"); cleanup: modify_return__destroy(skel); @@ -63,4 +55,3 @@ void serial_test_modify_return(void) 0 /* want_side_effect */, -EINVAL /* want_ret */); } - diff --git a/tools/testing/selftests/bpf/prog_tests/obj_name.c b/tools/testing/selftests/bpf/prog_tests/obj_name.c index 6194b776a28b..7093edca6e08 100644 --- a/tools/testing/selftests/bpf/prog_tests/obj_name.c +++ b/tools/testing/selftests/bpf/prog_tests/obj_name.c @@ -20,7 +20,7 @@ void test_obj_name(void) __u32 duration = 0; int i; - for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + for (i = 0; i < ARRAY_SIZE(tests); i++) { size_t name_len = strlen(tests[i].name) + 1; union bpf_attr attr; size_t ncopy; diff --git a/tools/testing/selftests/bpf/prog_tests/perf_branches.c b/tools/testing/selftests/bpf/prog_tests/perf_branches.c index 12c4f45cee1a..bc24f83339d6 100644 --- a/tools/testing/selftests/bpf/prog_tests/perf_branches.c +++ b/tools/testing/selftests/bpf/prog_tests/perf_branches.c @@ -110,7 +110,7 @@ static void test_perf_branches_hw(void) attr.type = PERF_TYPE_HARDWARE; attr.config = PERF_COUNT_HW_CPU_CYCLES; attr.freq = 1; - attr.sample_freq = 4000; + attr.sample_freq = 1000; attr.sample_type = PERF_SAMPLE_BRANCH_STACK; attr.branch_sample_type = PERF_SAMPLE_BRANCH_USER | PERF_SAMPLE_BRANCH_ANY; pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC); @@ -151,7 +151,7 @@ static void test_perf_branches_no_hw(void) attr.type = PERF_TYPE_SOFTWARE; attr.config = PERF_COUNT_SW_CPU_CLOCK; attr.freq = 1; - attr.sample_freq = 4000; + attr.sample_freq = 1000; pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC); if (CHECK(pfd < 0, "perf_event_open", "err %d\n", pfd)) return; diff --git a/tools/testing/selftests/bpf/prog_tests/perf_link.c b/tools/testing/selftests/bpf/prog_tests/perf_link.c index ede07344f264..224eba6fef2e 100644 --- a/tools/testing/selftests/bpf/prog_tests/perf_link.c +++ b/tools/testing/selftests/bpf/prog_tests/perf_link.c @@ -39,7 +39,7 @@ void serial_test_perf_link(void) attr.type = PERF_TYPE_SOFTWARE; attr.config = PERF_COUNT_SW_CPU_CLOCK; attr.freq = 1; - attr.sample_freq = 4000; + attr.sample_freq = 1000; pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC); if (!ASSERT_GE(pfd, 0, "perf_fd")) goto cleanup; diff --git a/tools/testing/selftests/bpf/prog_tests/pkt_access.c b/tools/testing/selftests/bpf/prog_tests/pkt_access.c index 6628710ec3c6..0bcccdc34fbc 100644 --- a/tools/testing/selftests/bpf/prog_tests/pkt_access.c +++ b/tools/testing/selftests/bpf/prog_tests/pkt_access.c @@ -6,23 +6,27 @@ void test_pkt_access(void) { const char *file = "./test_pkt_access.o"; struct bpf_object *obj; - __u32 duration, retval; int err, prog_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 100000, + ); err = bpf_prog_test_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); if (CHECK_FAIL(err)) return; - err = bpf_prog_test_run(prog_fd, 100000, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, &retval, &duration); - CHECK(err || retval, "ipv4", - "err %d errno %d retval %d duration %d\n", - err, errno, retval, duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "ipv4 test_run_opts err"); + ASSERT_OK(topts.retval, "ipv4 test_run_opts retval"); + + topts.data_in = &pkt_v6; + topts.data_size_in = sizeof(pkt_v6); + topts.data_size_out = 0; /* reset from last call */ + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "ipv6 test_run_opts err"); + ASSERT_OK(topts.retval, "ipv6 test_run_opts retval"); - err = bpf_prog_test_run(prog_fd, 100000, &pkt_v6, sizeof(pkt_v6), - NULL, NULL, &retval, &duration); - CHECK(err || retval, "ipv6", - "err %d errno %d retval %d duration %d\n", - err, errno, retval, duration); bpf_object__close(obj); } diff --git a/tools/testing/selftests/bpf/prog_tests/pkt_md_access.c b/tools/testing/selftests/bpf/prog_tests/pkt_md_access.c index c9d2d6a1bfcc..00ee1dd792aa 100644 --- a/tools/testing/selftests/bpf/prog_tests/pkt_md_access.c +++ b/tools/testing/selftests/bpf/prog_tests/pkt_md_access.c @@ -6,18 +6,20 @@ void test_pkt_md_access(void) { const char *file = "./test_pkt_md_access.o"; struct bpf_object *obj; - __u32 duration, retval; int err, prog_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 10, + ); err = bpf_prog_test_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); if (CHECK_FAIL(err)) return; - err = bpf_prog_test_run(prog_fd, 10, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, &retval, &duration); - CHECK(err || retval, "", - "err %d errno %d retval %d duration %d\n", - err, errno, retval, duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run_opts err"); + ASSERT_OK(topts.retval, "test_run_opts retval"); bpf_object__close(obj); } diff --git a/tools/testing/selftests/bpf/prog_tests/prog_run_opts.c b/tools/testing/selftests/bpf/prog_tests/prog_run_opts.c new file mode 100644 index 000000000000..1ccd2bdf8fa8 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/prog_run_opts.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +#include "test_pkt_access.skel.h" + +static const __u32 duration; + +static void check_run_cnt(int prog_fd, __u64 run_cnt) +{ + struct bpf_prog_info info = {}; + __u32 info_len = sizeof(info); + int err; + + err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len); + if (CHECK(err, "get_prog_info", "failed to get bpf_prog_info for fd %d\n", prog_fd)) + return; + + CHECK(run_cnt != info.run_cnt, "run_cnt", + "incorrect number of repetitions, want %llu have %llu\n", run_cnt, info.run_cnt); +} + +void test_prog_run_opts(void) +{ + struct test_pkt_access *skel; + int err, stats_fd = -1, prog_fd; + char buf[10] = {}; + __u64 run_cnt = 0; + + LIBBPF_OPTS(bpf_test_run_opts, topts, + .repeat = 1, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .data_out = buf, + .data_size_out = 5, + ); + + stats_fd = bpf_enable_stats(BPF_STATS_RUN_TIME); + if (!ASSERT_GE(stats_fd, 0, "enable_stats good fd")) + return; + + skel = test_pkt_access__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + goto cleanup; + + prog_fd = bpf_program__fd(skel->progs.test_pkt_access); + + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_EQ(errno, ENOSPC, "test_run errno"); + ASSERT_ERR(err, "test_run"); + ASSERT_OK(topts.retval, "test_run retval"); + + ASSERT_EQ(topts.data_size_out, sizeof(pkt_v4), "test_run data_size_out"); + ASSERT_EQ(buf[5], 0, "overflow, BPF_PROG_TEST_RUN ignored size hint"); + + run_cnt += topts.repeat; + check_run_cnt(prog_fd, run_cnt); + + topts.data_out = NULL; + topts.data_size_out = 0; + topts.repeat = 2; + errno = 0; + + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(errno, "run_no_output errno"); + ASSERT_OK(err, "run_no_output err"); + ASSERT_OK(topts.retval, "run_no_output retval"); + + run_cnt += topts.repeat; + check_run_cnt(prog_fd, run_cnt); + +cleanup: + if (skel) + test_pkt_access__destroy(skel); + if (stats_fd >= 0) + close(stats_fd); +} diff --git a/tools/testing/selftests/bpf/prog_tests/prog_run_xattr.c b/tools/testing/selftests/bpf/prog_tests/prog_run_xattr.c deleted file mode 100644 index 89fc98faf19e..000000000000 --- a/tools/testing/selftests/bpf/prog_tests/prog_run_xattr.c +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -#include "test_pkt_access.skel.h" - -static const __u32 duration; - -static void check_run_cnt(int prog_fd, __u64 run_cnt) -{ - struct bpf_prog_info info = {}; - __u32 info_len = sizeof(info); - int err; - - err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len); - if (CHECK(err, "get_prog_info", "failed to get bpf_prog_info for fd %d\n", prog_fd)) - return; - - CHECK(run_cnt != info.run_cnt, "run_cnt", - "incorrect number of repetitions, want %llu have %llu\n", run_cnt, info.run_cnt); -} - -void test_prog_run_xattr(void) -{ - struct test_pkt_access *skel; - int err, stats_fd = -1; - char buf[10] = {}; - __u64 run_cnt = 0; - - struct bpf_prog_test_run_attr tattr = { - .repeat = 1, - .data_in = &pkt_v4, - .data_size_in = sizeof(pkt_v4), - .data_out = buf, - .data_size_out = 5, - }; - - stats_fd = bpf_enable_stats(BPF_STATS_RUN_TIME); - if (CHECK_ATTR(stats_fd < 0, "enable_stats", "failed %d\n", errno)) - return; - - skel = test_pkt_access__open_and_load(); - if (CHECK_ATTR(!skel, "open_and_load", "failed\n")) - goto cleanup; - - tattr.prog_fd = bpf_program__fd(skel->progs.test_pkt_access); - - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err >= 0 || errno != ENOSPC || tattr.retval, "run", - "err %d errno %d retval %d\n", err, errno, tattr.retval); - - CHECK_ATTR(tattr.data_size_out != sizeof(pkt_v4), "data_size_out", - "incorrect output size, want %zu have %u\n", - sizeof(pkt_v4), tattr.data_size_out); - - CHECK_ATTR(buf[5] != 0, "overflow", - "BPF_PROG_TEST_RUN ignored size hint\n"); - - run_cnt += tattr.repeat; - check_run_cnt(tattr.prog_fd, run_cnt); - - tattr.data_out = NULL; - tattr.data_size_out = 0; - tattr.repeat = 2; - errno = 0; - - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err || errno || tattr.retval, "run_no_output", - "err %d errno %d retval %d\n", err, errno, tattr.retval); - - tattr.data_size_out = 1; - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err != -EINVAL, "run_wrong_size_out", "err %d\n", err); - - run_cnt += tattr.repeat; - check_run_cnt(tattr.prog_fd, run_cnt); - -cleanup: - if (skel) - test_pkt_access__destroy(skel); - if (stats_fd >= 0) - close(stats_fd); -} diff --git a/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c b/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c index b9822f914eeb..d2743fc10032 100644 --- a/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c +++ b/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c @@ -10,11 +10,18 @@ enum { static void test_queue_stack_map_by_type(int type) { const int MAP_SIZE = 32; - __u32 vals[MAP_SIZE], duration, retval, size, val; + __u32 vals[MAP_SIZE], val; int i, err, prog_fd, map_in_fd, map_out_fd; char file[32], buf[128]; struct bpf_object *obj; struct iphdr iph; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .data_out = buf, + .data_size_out = sizeof(buf), + .repeat = 1, + ); /* Fill test values to be used */ for (i = 0; i < MAP_SIZE; i++) @@ -58,38 +65,37 @@ static void test_queue_stack_map_by_type(int type) pkt_v4.iph.saddr = vals[MAP_SIZE - 1 - i] * 5; } - err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - buf, &size, &retval, &duration); - if (err || retval || size != sizeof(pkt_v4)) + topts.data_size_out = sizeof(buf); + err = bpf_prog_test_run_opts(prog_fd, &topts); + if (err || topts.retval || + topts.data_size_out != sizeof(pkt_v4)) break; memcpy(&iph, buf + sizeof(struct ethhdr), sizeof(iph)); if (iph.daddr != val) break; } - CHECK(err || retval || size != sizeof(pkt_v4) || iph.daddr != val, - "bpf_map_pop_elem", - "err %d errno %d retval %d size %d iph->daddr %u\n", - err, errno, retval, size, iph.daddr); + ASSERT_OK(err, "bpf_map_pop_elem"); + ASSERT_OK(topts.retval, "bpf_map_pop_elem test retval"); + ASSERT_EQ(topts.data_size_out, sizeof(pkt_v4), + "bpf_map_pop_elem data_size_out"); + ASSERT_EQ(iph.daddr, val, "bpf_map_pop_elem iph.daddr"); /* Queue is empty, program should return TC_ACT_SHOT */ - err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - buf, &size, &retval, &duration); - CHECK(err || retval != 2 /* TC_ACT_SHOT */|| size != sizeof(pkt_v4), - "check-queue-stack-map-empty", - "err %d errno %d retval %d size %d\n", - err, errno, retval, size); + topts.data_size_out = sizeof(buf); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "check-queue-stack-map-empty"); + ASSERT_EQ(topts.retval, 2 /* TC_ACT_SHOT */, + "check-queue-stack-map-empty test retval"); + ASSERT_EQ(topts.data_size_out, sizeof(pkt_v4), + "check-queue-stack-map-empty data_size_out"); /* Check that the program pushed elements correctly */ for (i = 0; i < MAP_SIZE; i++) { err = bpf_map_lookup_and_delete_elem(map_out_fd, NULL, &val); - if (err || val != vals[i] * 5) - break; + ASSERT_OK(err, "bpf_map_lookup_and_delete_elem"); + ASSERT_EQ(val, vals[i] * 5, "bpf_map_push_elem val"); } - - CHECK(i != MAP_SIZE && (err || val != vals[i] * 5), - "bpf_map_push_elem", "err %d value %u\n", err, val); - out: pkt_v4.iph.saddr = 0; bpf_object__close(obj); diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_test_run.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_test_run.c index 41720a62c4fa..fe5b8fae2c36 100644 --- a/tools/testing/selftests/bpf/prog_tests/raw_tp_test_run.c +++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_test_run.c @@ -5,59 +5,54 @@ #include "bpf/libbpf_internal.h" #include "test_raw_tp_test_run.skel.h" -static int duration; - void test_raw_tp_test_run(void) { - struct bpf_prog_test_run_attr test_attr = {}; int comm_fd = -1, err, nr_online, i, prog_fd; __u64 args[2] = {0x1234ULL, 0x5678ULL}; int expected_retval = 0x1234 + 0x5678; struct test_raw_tp_test_run *skel; char buf[] = "new_name"; bool *online = NULL; - DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts, - .ctx_in = args, - .ctx_size_in = sizeof(args), - .flags = BPF_F_TEST_RUN_ON_CPU, - ); + LIBBPF_OPTS(bpf_test_run_opts, opts, + .ctx_in = args, + .ctx_size_in = sizeof(args), + .flags = BPF_F_TEST_RUN_ON_CPU, + ); err = parse_cpu_mask_file("/sys/devices/system/cpu/online", &online, &nr_online); - if (CHECK(err, "parse_cpu_mask_file", "err %d\n", err)) + if (!ASSERT_OK(err, "parse_cpu_mask_file")) return; skel = test_raw_tp_test_run__open_and_load(); - if (CHECK(!skel, "skel_open", "failed to open skeleton\n")) + if (!ASSERT_OK_PTR(skel, "skel_open")) goto cleanup; err = test_raw_tp_test_run__attach(skel); - if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err)) + if (!ASSERT_OK(err, "skel_attach")) goto cleanup; comm_fd = open("/proc/self/comm", O_WRONLY|O_TRUNC); - if (CHECK(comm_fd < 0, "open /proc/self/comm", "err %d\n", errno)) + if (!ASSERT_GE(comm_fd, 0, "open /proc/self/comm")) goto cleanup; err = write(comm_fd, buf, sizeof(buf)); - CHECK(err < 0, "task rename", "err %d", errno); + ASSERT_GE(err, 0, "task rename"); - CHECK(skel->bss->count == 0, "check_count", "didn't increase\n"); - CHECK(skel->data->on_cpu != 0xffffffff, "check_on_cpu", "got wrong value\n"); + ASSERT_NEQ(skel->bss->count, 0, "check_count"); + ASSERT_EQ(skel->data->on_cpu, 0xffffffff, "check_on_cpu"); prog_fd = bpf_program__fd(skel->progs.rename); - test_attr.prog_fd = prog_fd; - test_attr.ctx_in = args; - test_attr.ctx_size_in = sizeof(__u64); + opts.ctx_in = args; + opts.ctx_size_in = sizeof(__u64); - err = bpf_prog_test_run_xattr(&test_attr); - CHECK(err == 0, "test_run", "should fail for too small ctx\n"); + err = bpf_prog_test_run_opts(prog_fd, &opts); + ASSERT_NEQ(err, 0, "test_run should fail for too small ctx"); - test_attr.ctx_size_in = sizeof(args); - err = bpf_prog_test_run_xattr(&test_attr); - CHECK(err < 0, "test_run", "err %d\n", errno); - CHECK(test_attr.retval != expected_retval, "check_retval", - "expect 0x%x, got 0x%x\n", expected_retval, test_attr.retval); + opts.ctx_size_in = sizeof(args); + err = bpf_prog_test_run_opts(prog_fd, &opts); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(opts.retval, expected_retval, "check_retval"); for (i = 0; i < nr_online; i++) { if (!online[i]) @@ -66,28 +61,23 @@ void test_raw_tp_test_run(void) opts.cpu = i; opts.retval = 0; err = bpf_prog_test_run_opts(prog_fd, &opts); - CHECK(err < 0, "test_run_opts", "err %d\n", errno); - CHECK(skel->data->on_cpu != i, "check_on_cpu", - "expect %d got %d\n", i, skel->data->on_cpu); - CHECK(opts.retval != expected_retval, - "check_retval", "expect 0x%x, got 0x%x\n", - expected_retval, opts.retval); + ASSERT_OK(err, "test_run_opts"); + ASSERT_EQ(skel->data->on_cpu, i, "check_on_cpu"); + ASSERT_EQ(opts.retval, expected_retval, "check_retval"); } /* invalid cpu ID should fail with ENXIO */ opts.cpu = 0xffffffff; err = bpf_prog_test_run_opts(prog_fd, &opts); - CHECK(err >= 0 || errno != ENXIO, - "test_run_opts_fail", - "should failed with ENXIO\n"); + ASSERT_EQ(errno, ENXIO, "test_run_opts should fail with ENXIO"); + ASSERT_ERR(err, "test_run_opts_fail"); /* non-zero cpu w/o BPF_F_TEST_RUN_ON_CPU should fail with EINVAL */ opts.cpu = 1; opts.flags = 0; err = bpf_prog_test_run_opts(prog_fd, &opts); - CHECK(err >= 0 || errno != EINVAL, - "test_run_opts_fail", - "should failed with EINVAL\n"); + ASSERT_EQ(errno, EINVAL, "test_run_opts should fail with EINVAL"); + ASSERT_ERR(err, "test_run_opts_fail"); cleanup: close(comm_fd); diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c index 239baccabccb..f4aa7dab4766 100644 --- a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c +++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c @@ -56,21 +56,23 @@ void serial_test_raw_tp_writable_test_run(void) 0, }; - __u32 prog_ret; - int err = bpf_prog_test_run(filter_fd, 1, test_skb, sizeof(test_skb), 0, - 0, &prog_ret, 0); + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = test_skb, + .data_size_in = sizeof(test_skb), + .repeat = 1, + ); + int err = bpf_prog_test_run_opts(filter_fd, &topts); CHECK(err != 42, "test_run", "tracepoint did not modify return value\n"); - CHECK(prog_ret != 0, "test_run_ret", + CHECK(topts.retval != 0, "test_run_ret", "socket_filter did not return 0\n"); close(tp_fd); - err = bpf_prog_test_run(filter_fd, 1, test_skb, sizeof(test_skb), 0, 0, - &prog_ret, 0); + err = bpf_prog_test_run_opts(filter_fd, &topts); CHECK(err != 0, "test_run_notrace", "test_run failed with %d errno %d\n", err, errno); - CHECK(prog_ret != 0, "test_run_ret_notrace", + CHECK(topts.retval != 0, "test_run_ret_notrace", "socket_filter did not return 0\n"); out_filterfd: diff --git a/tools/testing/selftests/bpf/prog_tests/send_signal.c b/tools/testing/selftests/bpf/prog_tests/send_signal.c index 776916b61c40..d71226e34c34 100644 --- a/tools/testing/selftests/bpf/prog_tests/send_signal.c +++ b/tools/testing/selftests/bpf/prog_tests/send_signal.c @@ -4,11 +4,11 @@ #include #include "test_send_signal_kern.skel.h" -int sigusr1_received = 0; +static int sigusr1_received; static void sigusr1_handler(int signum) { - sigusr1_received++; + sigusr1_received = 1; } static void test_send_signal_common(struct perf_event_attr *attr, @@ -40,9 +40,10 @@ static void test_send_signal_common(struct perf_event_attr *attr, if (pid == 0) { int old_prio; + volatile int j = 0; /* install signal handler and notify parent */ - signal(SIGUSR1, sigusr1_handler); + ASSERT_NEQ(signal(SIGUSR1, sigusr1_handler), SIG_ERR, "signal"); close(pipe_c2p[0]); /* close read */ close(pipe_p2c[1]); /* close write */ @@ -63,9 +64,11 @@ static void test_send_signal_common(struct perf_event_attr *attr, ASSERT_EQ(read(pipe_p2c[0], buf, 1), 1, "pipe_read"); /* wait a little for signal handler */ - sleep(1); + for (int i = 0; i < 100000000 && !sigusr1_received; i++) + j /= i + j + 1; buf[0] = sigusr1_received ? '2' : '0'; + ASSERT_EQ(sigusr1_received, 1, "sigusr1_received"); ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write"); /* wait for parent notification and exit */ @@ -93,7 +96,7 @@ static void test_send_signal_common(struct perf_event_attr *attr, goto destroy_skel; } } else { - pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1, + pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1 /* cpu */, -1 /* group id */, 0 /* flags */); if (!ASSERT_GE(pmu_fd, 0, "perf_event_open")) { err = -1; @@ -110,9 +113,9 @@ static void test_send_signal_common(struct perf_event_attr *attr, ASSERT_EQ(read(pipe_c2p[0], buf, 1), 1, "pipe_read"); /* trigger the bpf send_signal */ - skel->bss->pid = pid; - skel->bss->sig = SIGUSR1; skel->bss->signal_thread = signal_thread; + skel->bss->sig = SIGUSR1; + skel->bss->pid = pid; /* notify child that bpf program can send_signal now */ ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write"); diff --git a/tools/testing/selftests/bpf/prog_tests/signal_pending.c b/tools/testing/selftests/bpf/prog_tests/signal_pending.c index aecfe662c070..70b49da5ca0a 100644 --- a/tools/testing/selftests/bpf/prog_tests/signal_pending.c +++ b/tools/testing/selftests/bpf/prog_tests/signal_pending.c @@ -13,10 +13,14 @@ static void test_signal_pending_by_type(enum bpf_prog_type prog_type) struct itimerval timeo = { .it_value.tv_usec = 100000, /* 100ms */ }; - __u32 duration = 0, retval; int prog_fd; int err; int i; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 0xffffffff, + ); for (i = 0; i < ARRAY_SIZE(prog); i++) prog[i] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0); @@ -24,20 +28,17 @@ static void test_signal_pending_by_type(enum bpf_prog_type prog_type) prog_fd = bpf_test_load_program(prog_type, prog, ARRAY_SIZE(prog), "GPL", 0, NULL, 0); - CHECK(prog_fd < 0, "test-run", "errno %d\n", errno); + ASSERT_GE(prog_fd, 0, "test-run load"); err = sigaction(SIGALRM, &sigalrm_action, NULL); - CHECK(err, "test-run-signal-sigaction", "errno %d\n", errno); + ASSERT_OK(err, "test-run-signal-sigaction"); err = setitimer(ITIMER_REAL, &timeo, NULL); - CHECK(err, "test-run-signal-timer", "errno %d\n", errno); + ASSERT_OK(err, "test-run-signal-timer"); - err = bpf_prog_test_run(prog_fd, 0xffffffff, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, &retval, &duration); - CHECK(duration > 500000000, /* 500ms */ - "test-run-signal-duration", - "duration %dns > 500ms\n", - duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_LE(topts.duration, 500000000 /* 500ms */, + "test-run-signal-duration"); signal(SIGALRM, SIG_DFL); } diff --git a/tools/testing/selftests/bpf/prog_tests/skb_ctx.c b/tools/testing/selftests/bpf/prog_tests/skb_ctx.c index b5319ba2ee27..ce0e555b5e38 100644 --- a/tools/testing/selftests/bpf/prog_tests/skb_ctx.c +++ b/tools/testing/selftests/bpf/prog_tests/skb_ctx.c @@ -20,97 +20,72 @@ void test_skb_ctx(void) .gso_size = 10, .hwtstamp = 11, }; - struct bpf_prog_test_run_attr tattr = { + LIBBPF_OPTS(bpf_test_run_opts, tattr, .data_in = &pkt_v4, .data_size_in = sizeof(pkt_v4), .ctx_in = &skb, .ctx_size_in = sizeof(skb), .ctx_out = &skb, .ctx_size_out = sizeof(skb), - }; + ); struct bpf_object *obj; - int err; - int i; + int err, prog_fd, i; - err = bpf_prog_test_load("./test_skb_ctx.o", BPF_PROG_TYPE_SCHED_CLS, &obj, - &tattr.prog_fd); - if (CHECK_ATTR(err, "load", "err %d errno %d\n", err, errno)) + err = bpf_prog_test_load("./test_skb_ctx.o", BPF_PROG_TYPE_SCHED_CLS, + &obj, &prog_fd); + if (!ASSERT_OK(err, "load")) return; /* ctx_in != NULL, ctx_size_in == 0 */ tattr.ctx_size_in = 0; - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err == 0, "ctx_size_in", "err %d errno %d\n", err, errno); + err = bpf_prog_test_run_opts(prog_fd, &tattr); + ASSERT_NEQ(err, 0, "ctx_size_in"); tattr.ctx_size_in = sizeof(skb); /* ctx_out != NULL, ctx_size_out == 0 */ tattr.ctx_size_out = 0; - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err == 0, "ctx_size_out", "err %d errno %d\n", err, errno); + err = bpf_prog_test_run_opts(prog_fd, &tattr); + ASSERT_NEQ(err, 0, "ctx_size_out"); tattr.ctx_size_out = sizeof(skb); /* non-zero [len, tc_index] fields should be rejected*/ skb.len = 1; - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err == 0, "len", "err %d errno %d\n", err, errno); + err = bpf_prog_test_run_opts(prog_fd, &tattr); + ASSERT_NEQ(err, 0, "len"); skb.len = 0; skb.tc_index = 1; - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err == 0, "tc_index", "err %d errno %d\n", err, errno); + err = bpf_prog_test_run_opts(prog_fd, &tattr); + ASSERT_NEQ(err, 0, "tc_index"); skb.tc_index = 0; /* non-zero [hash, sk] fields should be rejected */ skb.hash = 1; - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err == 0, "hash", "err %d errno %d\n", err, errno); + err = bpf_prog_test_run_opts(prog_fd, &tattr); + ASSERT_NEQ(err, 0, "hash"); skb.hash = 0; skb.sk = (struct bpf_sock *)1; - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err == 0, "sk", "err %d errno %d\n", err, errno); + err = bpf_prog_test_run_opts(prog_fd, &tattr); + ASSERT_NEQ(err, 0, "sk"); skb.sk = 0; - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err != 0 || tattr.retval, - "run", - "err %d errno %d retval %d\n", - err, errno, tattr.retval); - - CHECK_ATTR(tattr.ctx_size_out != sizeof(skb), - "ctx_size_out", - "incorrect output size, want %zu have %u\n", - sizeof(skb), tattr.ctx_size_out); + err = bpf_prog_test_run_opts(prog_fd, &tattr); + ASSERT_OK(err, "test_run"); + ASSERT_OK(tattr.retval, "test_run retval"); + ASSERT_EQ(tattr.ctx_size_out, sizeof(skb), "ctx_size_out"); for (i = 0; i < 5; i++) - CHECK_ATTR(skb.cb[i] != i + 2, - "ctx_out_cb", - "skb->cb[i] == %d, expected %d\n", - skb.cb[i], i + 2); - CHECK_ATTR(skb.priority != 7, - "ctx_out_priority", - "skb->priority == %d, expected %d\n", - skb.priority, 7); - CHECK_ATTR(skb.ifindex != 1, - "ctx_out_ifindex", - "skb->ifindex == %d, expected %d\n", - skb.ifindex, 1); - CHECK_ATTR(skb.ingress_ifindex != 11, - "ctx_out_ingress_ifindex", - "skb->ingress_ifindex == %d, expected %d\n", - skb.ingress_ifindex, 11); - CHECK_ATTR(skb.tstamp != 8, - "ctx_out_tstamp", - "skb->tstamp == %lld, expected %d\n", - skb.tstamp, 8); - CHECK_ATTR(skb.mark != 10, - "ctx_out_mark", - "skb->mark == %u, expected %d\n", - skb.mark, 10); + ASSERT_EQ(skb.cb[i], i + 2, "ctx_out_cb"); + ASSERT_EQ(skb.priority, 7, "ctx_out_priority"); + ASSERT_EQ(skb.ifindex, 1, "ctx_out_ifindex"); + ASSERT_EQ(skb.ingress_ifindex, 11, "ctx_out_ingress_ifindex"); + ASSERT_EQ(skb.tstamp, 8, "ctx_out_tstamp"); + ASSERT_EQ(skb.mark, 10, "ctx_out_mark"); bpf_object__close(obj); } diff --git a/tools/testing/selftests/bpf/prog_tests/skb_helpers.c b/tools/testing/selftests/bpf/prog_tests/skb_helpers.c index 6f802a1c0800..97dc8b14be48 100644 --- a/tools/testing/selftests/bpf/prog_tests/skb_helpers.c +++ b/tools/testing/selftests/bpf/prog_tests/skb_helpers.c @@ -9,22 +9,22 @@ void test_skb_helpers(void) .gso_segs = 8, .gso_size = 10, }; - struct bpf_prog_test_run_attr tattr = { + LIBBPF_OPTS(bpf_test_run_opts, topts, .data_in = &pkt_v4, .data_size_in = sizeof(pkt_v4), .ctx_in = &skb, .ctx_size_in = sizeof(skb), .ctx_out = &skb, .ctx_size_out = sizeof(skb), - }; + ); struct bpf_object *obj; - int err; + int err, prog_fd; - err = bpf_prog_test_load("./test_skb_helpers.o", BPF_PROG_TYPE_SCHED_CLS, &obj, - &tattr.prog_fd); - if (CHECK_ATTR(err, "load", "err %d errno %d\n", err, errno)) + err = bpf_prog_test_load("./test_skb_helpers.o", + BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); + if (!ASSERT_OK(err, "load")) return; - err = bpf_prog_test_run_xattr(&tattr); - CHECK_ATTR(err, "len", "err %d errno %d\n", err, errno); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); bpf_object__close(obj); } diff --git a/tools/testing/selftests/bpf/prog_tests/sock_fields.c b/tools/testing/selftests/bpf/prog_tests/sock_fields.c index 9fc040eaa482..9d211b5c22c4 100644 --- a/tools/testing/selftests/bpf/prog_tests/sock_fields.c +++ b/tools/testing/selftests/bpf/prog_tests/sock_fields.c @@ -1,9 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2019 Facebook */ +#define _GNU_SOURCE #include #include #include +#include #include #include #include @@ -20,6 +22,7 @@ enum bpf_linum_array_idx { EGRESS_LINUM_IDX, INGRESS_LINUM_IDX, + READ_SK_DST_PORT_LINUM_IDX, __NR_BPF_LINUM_ARRAY_IDX, }; @@ -42,8 +45,16 @@ static __u64 child_cg_id; static int linum_map_fd; static __u32 duration; -static __u32 egress_linum_idx = EGRESS_LINUM_IDX; -static __u32 ingress_linum_idx = INGRESS_LINUM_IDX; +static bool create_netns(void) +{ + if (!ASSERT_OK(unshare(CLONE_NEWNET), "create netns")) + return false; + + if (!ASSERT_OK(system("ip link set dev lo up"), "bring up lo")) + return false; + + return true; +} static void print_sk(const struct bpf_sock *sk, const char *prefix) { @@ -91,19 +102,24 @@ static void check_result(void) { struct bpf_tcp_sock srv_tp, cli_tp, listen_tp; struct bpf_sock srv_sk, cli_sk, listen_sk; - __u32 ingress_linum, egress_linum; + __u32 idx, ingress_linum, egress_linum, linum; int err; - err = bpf_map_lookup_elem(linum_map_fd, &egress_linum_idx, - &egress_linum); + idx = EGRESS_LINUM_IDX; + err = bpf_map_lookup_elem(linum_map_fd, &idx, &egress_linum); CHECK(err < 0, "bpf_map_lookup_elem(linum_map_fd)", "err:%d errno:%d\n", err, errno); - err = bpf_map_lookup_elem(linum_map_fd, &ingress_linum_idx, - &ingress_linum); + idx = INGRESS_LINUM_IDX; + err = bpf_map_lookup_elem(linum_map_fd, &idx, &ingress_linum); CHECK(err < 0, "bpf_map_lookup_elem(linum_map_fd)", "err:%d errno:%d\n", err, errno); + idx = READ_SK_DST_PORT_LINUM_IDX; + err = bpf_map_lookup_elem(linum_map_fd, &idx, &linum); + ASSERT_OK(err, "bpf_map_lookup_elem(linum_map_fd, READ_SK_DST_PORT_IDX)"); + ASSERT_EQ(linum, 0, "failure in read_sk_dst_port on line"); + memcpy(&srv_sk, &skel->bss->srv_sk, sizeof(srv_sk)); memcpy(&srv_tp, &skel->bss->srv_tp, sizeof(srv_tp)); memcpy(&cli_sk, &skel->bss->cli_sk, sizeof(cli_sk)); @@ -262,7 +278,7 @@ static void test(void) char buf[DATA_LEN]; /* Prepare listen_fd */ - listen_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0); + listen_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0xcafe, 0); /* start_server() has logged the error details */ if (CHECK_FAIL(listen_fd == -1)) goto done; @@ -330,8 +346,12 @@ static void test(void) void serial_test_sock_fields(void) { - struct bpf_link *egress_link = NULL, *ingress_link = NULL; int parent_cg_fd = -1, child_cg_fd = -1; + struct bpf_link *link; + + /* Use a dedicated netns to have a fixed listen port */ + if (!create_netns()) + return; /* Create a cgroup, get fd, and join it */ parent_cg_fd = test__join_cgroup(PARENT_CGROUP); @@ -352,15 +372,20 @@ void serial_test_sock_fields(void) if (CHECK(!skel, "test_sock_fields__open_and_load", "failed\n")) goto done; - egress_link = bpf_program__attach_cgroup(skel->progs.egress_read_sock_fields, - child_cg_fd); - if (!ASSERT_OK_PTR(egress_link, "attach_cgroup(egress)")) + link = bpf_program__attach_cgroup(skel->progs.egress_read_sock_fields, child_cg_fd); + if (!ASSERT_OK_PTR(link, "attach_cgroup(egress_read_sock_fields)")) goto done; + skel->links.egress_read_sock_fields = link; - ingress_link = bpf_program__attach_cgroup(skel->progs.ingress_read_sock_fields, - child_cg_fd); - if (!ASSERT_OK_PTR(ingress_link, "attach_cgroup(ingress)")) + link = bpf_program__attach_cgroup(skel->progs.ingress_read_sock_fields, child_cg_fd); + if (!ASSERT_OK_PTR(link, "attach_cgroup(ingress_read_sock_fields)")) goto done; + skel->links.ingress_read_sock_fields = link; + + link = bpf_program__attach_cgroup(skel->progs.read_sk_dst_port, child_cg_fd); + if (!ASSERT_OK_PTR(link, "attach_cgroup(read_sk_dst_port")) + goto done; + skel->links.read_sk_dst_port = link; linum_map_fd = bpf_map__fd(skel->maps.linum_map); sk_pkt_out_cnt_fd = bpf_map__fd(skel->maps.sk_pkt_out_cnt); @@ -369,8 +394,7 @@ void serial_test_sock_fields(void) test(); done: - bpf_link__destroy(egress_link); - bpf_link__destroy(ingress_link); + test_sock_fields__detach(skel); test_sock_fields__destroy(skel); if (child_cg_fd >= 0) close(child_cg_fd); diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c index 85db0f4cdd95..cec5c0882372 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c @@ -8,6 +8,7 @@ #include "test_sockmap_update.skel.h" #include "test_sockmap_invalid_update.skel.h" #include "test_sockmap_skb_verdict_attach.skel.h" +#include "test_sockmap_progs_query.skel.h" #include "bpf_iter_sockmap.skel.h" #define TCP_REPAIR 19 /* TCP sock is under repair right now */ @@ -139,12 +140,16 @@ static void test_skmsg_helpers(enum bpf_map_type map_type) static void test_sockmap_update(enum bpf_map_type map_type) { - struct bpf_prog_test_run_attr tattr; int err, prog, src, duration = 0; struct test_sockmap_update *skel; struct bpf_map *dst_map; const __u32 zero = 0; char dummy[14] = {0}; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = dummy, + .data_size_in = sizeof(dummy), + .repeat = 1, + ); __s64 sk; sk = connected_socket_v4(); @@ -166,16 +171,10 @@ static void test_sockmap_update(enum bpf_map_type map_type) if (CHECK(err, "update_elem(src)", "errno=%u\n", errno)) goto out; - tattr = (struct bpf_prog_test_run_attr){ - .prog_fd = prog, - .repeat = 1, - .data_in = dummy, - .data_size_in = sizeof(dummy), - }; - - err = bpf_prog_test_run_xattr(&tattr); - if (CHECK_ATTR(err || !tattr.retval, "bpf_prog_test_run", - "errno=%u retval=%u\n", errno, tattr.retval)) + err = bpf_prog_test_run_opts(prog, &topts); + if (!ASSERT_OK(err, "test_run")) + goto out; + if (!ASSERT_NEQ(topts.retval, 0, "test_run retval")) goto out; compare_cookies(skel->maps.src, dst_map); @@ -315,6 +314,63 @@ static void test_sockmap_skb_verdict_attach(enum bpf_attach_type first, test_sockmap_skb_verdict_attach__destroy(skel); } +static __u32 query_prog_id(int prog_fd) +{ + struct bpf_prog_info info = {}; + __u32 info_len = sizeof(info); + int err; + + err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len); + if (!ASSERT_OK(err, "bpf_obj_get_info_by_fd") || + !ASSERT_EQ(info_len, sizeof(info), "bpf_obj_get_info_by_fd")) + return 0; + + return info.id; +} + +static void test_sockmap_progs_query(enum bpf_attach_type attach_type) +{ + struct test_sockmap_progs_query *skel; + int err, map_fd, verdict_fd; + __u32 attach_flags = 0; + __u32 prog_ids[3] = {}; + __u32 prog_cnt = 3; + + skel = test_sockmap_progs_query__open_and_load(); + if (!ASSERT_OK_PTR(skel, "test_sockmap_progs_query__open_and_load")) + return; + + map_fd = bpf_map__fd(skel->maps.sock_map); + + if (attach_type == BPF_SK_MSG_VERDICT) + verdict_fd = bpf_program__fd(skel->progs.prog_skmsg_verdict); + else + verdict_fd = bpf_program__fd(skel->progs.prog_skb_verdict); + + err = bpf_prog_query(map_fd, attach_type, 0 /* query flags */, + &attach_flags, prog_ids, &prog_cnt); + ASSERT_OK(err, "bpf_prog_query failed"); + ASSERT_EQ(attach_flags, 0, "wrong attach_flags on query"); + ASSERT_EQ(prog_cnt, 0, "wrong program count on query"); + + err = bpf_prog_attach(verdict_fd, map_fd, attach_type, 0); + if (!ASSERT_OK(err, "bpf_prog_attach failed")) + goto out; + + prog_cnt = 1; + err = bpf_prog_query(map_fd, attach_type, 0 /* query flags */, + &attach_flags, prog_ids, &prog_cnt); + ASSERT_OK(err, "bpf_prog_query failed"); + ASSERT_EQ(attach_flags, 0, "wrong attach_flags on query"); + ASSERT_EQ(prog_cnt, 1, "wrong program count on query"); + ASSERT_EQ(prog_ids[0], query_prog_id(verdict_fd), + "wrong prog_ids on query"); + + bpf_prog_detach2(verdict_fd, map_fd, attach_type); +out: + test_sockmap_progs_query__destroy(skel); +} + void test_sockmap_basic(void) { if (test__start_subtest("sockmap create_update_free")) @@ -341,4 +397,12 @@ void test_sockmap_basic(void) test_sockmap_skb_verdict_attach(BPF_SK_SKB_STREAM_VERDICT, BPF_SK_SKB_VERDICT); } + if (test__start_subtest("sockmap msg_verdict progs query")) + test_sockmap_progs_query(BPF_SK_MSG_VERDICT); + if (test__start_subtest("sockmap stream_parser progs query")) + test_sockmap_progs_query(BPF_SK_SKB_STREAM_PARSER); + if (test__start_subtest("sockmap stream_verdict progs query")) + test_sockmap_progs_query(BPF_SK_SKB_STREAM_VERDICT); + if (test__start_subtest("sockmap skb_verdict progs query")) + test_sockmap_progs_query(BPF_SK_SKB_VERDICT); } diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c index 7e21bfab6358..2cf0c7a3fe23 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c @@ -1413,14 +1413,12 @@ static void test_reuseport_mixed_groups(int family, int sotype, int sock_map, static void test_ops_cleanup(const struct bpf_map *map) { - const struct bpf_map_def *def; int err, mapfd; u32 key; - def = bpf_map__def(map); mapfd = bpf_map__fd(map); - for (key = 0; key < def->max_entries; key++) { + for (key = 0; key < bpf_map__max_entries(map); key++) { err = bpf_map_delete_elem(mapfd, &key); if (err && errno != EINVAL && errno != ENOENT) FAIL_ERRNO("map_delete: expected EINVAL/ENOENT"); @@ -1443,13 +1441,13 @@ static const char *family_str(sa_family_t family) static const char *map_type_str(const struct bpf_map *map) { - const struct bpf_map_def *def; + int type; - def = bpf_map__def(map); - if (IS_ERR(def)) + if (!map) return "invalid"; + type = bpf_map__type(map); - switch (def->type) { + switch (type) { case BPF_MAP_TYPE_SOCKMAP: return "sockmap"; case BPF_MAP_TYPE_SOCKHASH: diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c index 4b937e5dbaca..30a99d2ed5c6 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c +++ b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c @@ -173,11 +173,11 @@ static int getsetsockopt(void) } memset(&buf, 0, sizeof(buf)); - buf.zc.address = 12345; /* rejected by BPF */ + buf.zc.address = 12345; /* Not page aligned. Rejected by tcp_zerocopy_receive() */ optlen = sizeof(buf.zc); errno = 0; err = getsockopt(fd, SOL_TCP, TCP_ZEROCOPY_RECEIVE, &buf, &optlen); - if (errno != EPERM) { + if (errno != EINVAL) { log_err("Unexpected getsockopt(TCP_ZEROCOPY_RECEIVE) err=%d errno=%d", err, errno); goto err; diff --git a/tools/testing/selftests/bpf/prog_tests/spinlock.c b/tools/testing/selftests/bpf/prog_tests/spinlock.c index 6307f5d2b417..8e329eaee6d7 100644 --- a/tools/testing/selftests/bpf/prog_tests/spinlock.c +++ b/tools/testing/selftests/bpf/prog_tests/spinlock.c @@ -4,14 +4,16 @@ static void *spin_lock_thread(void *arg) { - __u32 duration, retval; int err, prog_fd = *(u32 *) arg; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 10000, + ); - err = bpf_prog_test_run(prog_fd, 10000, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, &retval, &duration); - CHECK(err || retval, "", - "err %d errno %d retval %d duration %d\n", - err, errno, retval, duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_OK(topts.retval, "test_run retval"); pthread_exit(arg); } diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c index 0a91d8d9954b..f45a1d7b0a28 100644 --- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c +++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c @@ -42,7 +42,7 @@ void test_stacktrace_build_id_nmi(void) return; /* override program type */ - bpf_program__set_perf_event(skel->progs.oncpu); + bpf_program__set_type(skel->progs.oncpu, BPF_PROG_TYPE_PERF_EVENT); err = test_stacktrace_build_id__load(skel); if (CHECK(err, "skel_load", "skeleton load failed: %d\n", err)) diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_map_skip.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_map_skip.c new file mode 100644 index 000000000000..1932b1e0685c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_map_skip.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include "stacktrace_map_skip.skel.h" + +#define TEST_STACK_DEPTH 2 + +void test_stacktrace_map_skip(void) +{ + struct stacktrace_map_skip *skel; + int stackid_hmap_fd, stackmap_fd, stack_amap_fd; + int err, stack_trace_len; + + skel = stacktrace_map_skip__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) + return; + + /* find map fds */ + stackid_hmap_fd = bpf_map__fd(skel->maps.stackid_hmap); + if (!ASSERT_GE(stackid_hmap_fd, 0, "stackid_hmap fd")) + goto out; + + stackmap_fd = bpf_map__fd(skel->maps.stackmap); + if (!ASSERT_GE(stackmap_fd, 0, "stackmap fd")) + goto out; + + stack_amap_fd = bpf_map__fd(skel->maps.stack_amap); + if (!ASSERT_GE(stack_amap_fd, 0, "stack_amap fd")) + goto out; + + skel->bss->pid = getpid(); + + err = stacktrace_map_skip__attach(skel); + if (!ASSERT_OK(err, "skel_attach")) + goto out; + + /* give some time for bpf program run */ + sleep(1); + + /* disable stack trace collection */ + skel->bss->control = 1; + + /* for every element in stackid_hmap, we can find a corresponding one + * in stackmap, and vise versa. + */ + err = compare_map_keys(stackid_hmap_fd, stackmap_fd); + if (!ASSERT_OK(err, "compare_map_keys stackid_hmap vs. stackmap")) + goto out; + + err = compare_map_keys(stackmap_fd, stackid_hmap_fd); + if (!ASSERT_OK(err, "compare_map_keys stackmap vs. stackid_hmap")) + goto out; + + stack_trace_len = TEST_STACK_DEPTH * sizeof(__u64); + err = compare_stack_ips(stackmap_fd, stack_amap_fd, stack_trace_len); + if (!ASSERT_OK(err, "compare_stack_ips stackmap vs. stack_amap")) + goto out; + + if (!ASSERT_EQ(skel->bss->failed, 0, "skip_failed")) + goto out; + +out: + stacktrace_map_skip__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/subprogs.c b/tools/testing/selftests/bpf/prog_tests/subprogs.c index 3f3d2ac4dd57..903f35a9e62e 100644 --- a/tools/testing/selftests/bpf/prog_tests/subprogs.c +++ b/tools/testing/selftests/bpf/prog_tests/subprogs.c @@ -1,32 +1,83 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2020 Facebook */ #include -#include #include "test_subprogs.skel.h" #include "test_subprogs_unused.skel.h" -static int duration; +struct toggler_ctx { + int fd; + bool stop; +}; -void test_subprogs(void) +static void *toggle_jit_harden(void *arg) +{ + struct toggler_ctx *ctx = arg; + char two = '2'; + char zero = '0'; + + while (!ctx->stop) { + lseek(ctx->fd, SEEK_SET, 0); + write(ctx->fd, &two, sizeof(two)); + lseek(ctx->fd, SEEK_SET, 0); + write(ctx->fd, &zero, sizeof(zero)); + } + + return NULL; +} + +static void test_subprogs_with_jit_harden_toggling(void) +{ + struct toggler_ctx ctx; + pthread_t toggler; + int err; + unsigned int i, loop = 10; + + ctx.fd = open("/proc/sys/net/core/bpf_jit_harden", O_RDWR); + if (!ASSERT_GE(ctx.fd, 0, "open bpf_jit_harden")) + return; + + ctx.stop = false; + err = pthread_create(&toggler, NULL, toggle_jit_harden, &ctx); + if (!ASSERT_OK(err, "new toggler")) + goto out; + + /* Make toggler thread to run */ + usleep(1); + + for (i = 0; i < loop; i++) { + struct test_subprogs *skel = test_subprogs__open_and_load(); + + if (!ASSERT_OK_PTR(skel, "skel open")) + break; + test_subprogs__destroy(skel); + } + + ctx.stop = true; + pthread_join(toggler, NULL); +out: + close(ctx.fd); +} + +static void test_subprogs_alone(void) { struct test_subprogs *skel; struct test_subprogs_unused *skel2; int err; skel = test_subprogs__open_and_load(); - if (CHECK(!skel, "skel_open", "failed to open skeleton\n")) + if (!ASSERT_OK_PTR(skel, "skel_open")) return; err = test_subprogs__attach(skel); - if (CHECK(err, "skel_attach", "failed to attach skeleton: %d\n", err)) + if (!ASSERT_OK(err, "skel attach")) goto cleanup; usleep(1); - CHECK(skel->bss->res1 != 12, "res1", "got %d, exp %d\n", skel->bss->res1, 12); - CHECK(skel->bss->res2 != 17, "res2", "got %d, exp %d\n", skel->bss->res2, 17); - CHECK(skel->bss->res3 != 19, "res3", "got %d, exp %d\n", skel->bss->res3, 19); - CHECK(skel->bss->res4 != 36, "res4", "got %d, exp %d\n", skel->bss->res4, 36); + ASSERT_EQ(skel->bss->res1, 12, "res1"); + ASSERT_EQ(skel->bss->res2, 17, "res2"); + ASSERT_EQ(skel->bss->res3, 19, "res3"); + ASSERT_EQ(skel->bss->res4, 36, "res4"); skel2 = test_subprogs_unused__open_and_load(); ASSERT_OK_PTR(skel2, "unused_progs_skel"); @@ -35,3 +86,11 @@ void test_subprogs(void) cleanup: test_subprogs__destroy(skel); } + +void test_subprogs(void) +{ + if (test__start_subtest("subprogs_alone")) + test_subprogs_alone(); + if (test__start_subtest("subprogs_and_jit_harden")) + test_subprogs_with_jit_harden_toggling(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/subskeleton.c b/tools/testing/selftests/bpf/prog_tests/subskeleton.c new file mode 100644 index 000000000000..9c31b7004f9c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/subskeleton.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) Meta Platforms, Inc. and affiliates. */ + +#include +#include "test_subskeleton.skel.h" +#include "test_subskeleton_lib.subskel.h" + +static void subskeleton_lib_setup(struct bpf_object *obj) +{ + struct test_subskeleton_lib *lib = test_subskeleton_lib__open(obj); + + if (!ASSERT_OK_PTR(lib, "open subskeleton")) + return; + + *lib->rodata.var1 = 1; + *lib->data.var2 = 2; + lib->bss.var3->var3_1 = 3; + lib->bss.var3->var3_2 = 4; + + test_subskeleton_lib__destroy(lib); +} + +static int subskeleton_lib_subresult(struct bpf_object *obj) +{ + struct test_subskeleton_lib *lib = test_subskeleton_lib__open(obj); + int result; + + if (!ASSERT_OK_PTR(lib, "open subskeleton")) + return -EINVAL; + + result = *lib->bss.libout1; + ASSERT_EQ(result, 1 + 2 + 3 + 4 + 5 + 6, "lib subresult"); + + ASSERT_OK_PTR(lib->progs.lib_perf_handler, "lib_perf_handler"); + ASSERT_STREQ(bpf_program__name(lib->progs.lib_perf_handler), + "lib_perf_handler", "program name"); + + ASSERT_OK_PTR(lib->maps.map1, "map1"); + ASSERT_STREQ(bpf_map__name(lib->maps.map1), "map1", "map name"); + + ASSERT_EQ(*lib->data.var5, 5, "__weak var5"); + ASSERT_EQ(*lib->data.var6, 6, "extern var6"); + ASSERT_TRUE(*lib->kconfig.CONFIG_BPF_SYSCALL, "CONFIG_BPF_SYSCALL"); + + test_subskeleton_lib__destroy(lib); + return result; +} + +void test_subskeleton(void) +{ + int err, result; + struct test_subskeleton *skel; + + skel = test_subskeleton__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + skel->rodata->rovar1 = 10; + skel->rodata->var1 = 1; + subskeleton_lib_setup(skel->obj); + + err = test_subskeleton__load(skel); + if (!ASSERT_OK(err, "skel_load")) + goto cleanup; + + err = test_subskeleton__attach(skel); + if (!ASSERT_OK(err, "skel_attach")) + goto cleanup; + + /* trigger tracepoint */ + usleep(1); + + result = subskeleton_lib_subresult(skel->obj) * 10; + ASSERT_EQ(skel->bss->out1, result, "unexpected calculation"); + +cleanup: + test_subskeleton__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/syscall.c b/tools/testing/selftests/bpf/prog_tests/syscall.c index 81e997a69f7a..f4d40001155a 100644 --- a/tools/testing/selftests/bpf/prog_tests/syscall.c +++ b/tools/testing/selftests/bpf/prog_tests/syscall.c @@ -20,20 +20,20 @@ void test_syscall(void) .log_buf = (uintptr_t) verifier_log, .log_size = sizeof(verifier_log), }; - struct bpf_prog_test_run_attr tattr = { + LIBBPF_OPTS(bpf_test_run_opts, tattr, .ctx_in = &ctx, .ctx_size_in = sizeof(ctx), - }; + ); struct syscall *skel = NULL; __u64 key = 12, value = 0; - int err; + int err, prog_fd; skel = syscall__open_and_load(); if (!ASSERT_OK_PTR(skel, "skel_load")) goto cleanup; - tattr.prog_fd = bpf_program__fd(skel->progs.bpf_prog); - err = bpf_prog_test_run_xattr(&tattr); + prog_fd = bpf_program__fd(skel->progs.bpf_prog); + err = bpf_prog_test_run_opts(prog_fd, &tattr); ASSERT_EQ(err, 0, "err"); ASSERT_EQ(tattr.retval, 1, "retval"); ASSERT_GT(ctx.map_fd, 0, "ctx.map_fd"); diff --git a/tools/testing/selftests/bpf/prog_tests/tailcalls.c b/tools/testing/selftests/bpf/prog_tests/tailcalls.c index 5dc0f425bd11..c4da87ec3ba4 100644 --- a/tools/testing/selftests/bpf/prog_tests/tailcalls.c +++ b/tools/testing/selftests/bpf/prog_tests/tailcalls.c @@ -12,9 +12,13 @@ static void test_tailcall_1(void) struct bpf_map *prog_array; struct bpf_program *prog; struct bpf_object *obj; - __u32 retval, duration; char prog_name[32]; char buff[128] = {}; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = buff, + .data_size_in = sizeof(buff), + .repeat = 1, + ); err = bpf_prog_test_load("tailcall1.o", BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); @@ -37,7 +41,7 @@ static void test_tailcall_1(void) if (CHECK_FAIL(map_fd < 0)) goto out; - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); prog = bpf_object__find_program_by_name(obj, prog_name); @@ -53,23 +57,21 @@ static void test_tailcall_1(void) goto out; } - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != i, "tailcall", - "err %d errno %d retval %d\n", err, errno, retval); + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, i, "tailcall retval"); err = bpf_map_delete_elem(map_fd, &i); if (CHECK_FAIL(err)) goto out; } - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 3, "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, 3, "tailcall retval"); - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); prog = bpf_object__find_program_by_name(obj, prog_name); @@ -85,13 +87,12 @@ static void test_tailcall_1(void) goto out; } - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 0, "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_OK(topts.retval, "tailcall retval"); - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { - j = bpf_map__def(prog_array)->max_entries - 1 - i; + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { + j = bpf_map__max_entries(prog_array) - 1 - i; snprintf(prog_name, sizeof(prog_name), "classifier_%d", j); prog = bpf_object__find_program_by_name(obj, prog_name); @@ -107,33 +108,30 @@ static void test_tailcall_1(void) goto out; } - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { - j = bpf_map__def(prog_array)->max_entries - 1 - i; + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { + j = bpf_map__max_entries(prog_array) - 1 - i; - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != j, "tailcall", - "err %d errno %d retval %d\n", err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, j, "tailcall retval"); err = bpf_map_delete_elem(map_fd, &i); if (CHECK_FAIL(err)) goto out; } - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 3, "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, 3, "tailcall retval"); - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { err = bpf_map_delete_elem(map_fd, &i); if (CHECK_FAIL(err >= 0 || errno != ENOENT)) goto out; - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 3, "tailcall", - "err %d errno %d retval %d\n", err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, 3, "tailcall retval"); } out: @@ -150,9 +148,13 @@ static void test_tailcall_2(void) struct bpf_map *prog_array; struct bpf_program *prog; struct bpf_object *obj; - __u32 retval, duration; char prog_name[32]; char buff[128] = {}; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = buff, + .data_size_in = sizeof(buff), + .repeat = 1, + ); err = bpf_prog_test_load("tailcall2.o", BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); @@ -175,7 +177,7 @@ static void test_tailcall_2(void) if (CHECK_FAIL(map_fd < 0)) goto out; - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); prog = bpf_object__find_program_by_name(obj, prog_name); @@ -191,30 +193,27 @@ static void test_tailcall_2(void) goto out; } - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 2, "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, 2, "tailcall retval"); i = 2; err = bpf_map_delete_elem(map_fd, &i); if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 1, "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, 1, "tailcall retval"); i = 0; err = bpf_map_delete_elem(map_fd, &i); if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 3, "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, 3, "tailcall retval"); out: bpf_object__close(obj); } @@ -225,8 +224,12 @@ static void test_tailcall_count(const char *which) struct bpf_map *prog_array, *data_map; struct bpf_program *prog; struct bpf_object *obj; - __u32 retval, duration; char buff[128] = {}; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = buff, + .data_size_in = sizeof(buff), + .repeat = 1, + ); err = bpf_prog_test_load(which, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); @@ -262,10 +265,9 @@ static void test_tailcall_count(const char *which) if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 1, "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, 1, "tailcall retval"); data_map = bpf_object__find_map_by_name(obj, "tailcall.bss"); if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map))) @@ -277,18 +279,17 @@ static void test_tailcall_count(const char *which) i = 0; err = bpf_map_lookup_elem(data_fd, &i, &val); - CHECK(err || val != 33, "tailcall count", "err %d errno %d count %d\n", - err, errno, val); + ASSERT_OK(err, "tailcall count"); + ASSERT_EQ(val, 33, "tailcall count"); i = 0; err = bpf_map_delete_elem(map_fd, &i); if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 0, "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_OK(topts.retval, "tailcall retval"); out: bpf_object__close(obj); } @@ -319,10 +320,14 @@ static void test_tailcall_4(void) struct bpf_map *prog_array, *data_map; struct bpf_program *prog; struct bpf_object *obj; - __u32 retval, duration; static const int zero = 0; char buff[128] = {}; char prog_name[32]; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = buff, + .data_size_in = sizeof(buff), + .repeat = 1, + ); err = bpf_prog_test_load("tailcall4.o", BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); @@ -353,7 +358,7 @@ static void test_tailcall_4(void) if (CHECK_FAIL(map_fd < 0)) return; - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); prog = bpf_object__find_program_by_name(obj, prog_name); @@ -369,18 +374,17 @@ static void test_tailcall_4(void) goto out; } - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { err = bpf_map_update_elem(data_fd, &zero, &i, BPF_ANY); if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != i, "tailcall", - "err %d errno %d retval %d\n", err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, i, "tailcall retval"); } - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { err = bpf_map_update_elem(data_fd, &zero, &i, BPF_ANY); if (CHECK_FAIL(err)) goto out; @@ -389,10 +393,9 @@ static void test_tailcall_4(void) if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 3, "tailcall", - "err %d errno %d retval %d\n", err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, 3, "tailcall retval"); } out: bpf_object__close(obj); @@ -407,10 +410,14 @@ static void test_tailcall_5(void) struct bpf_map *prog_array, *data_map; struct bpf_program *prog; struct bpf_object *obj; - __u32 retval, duration; static const int zero = 0; char buff[128] = {}; char prog_name[32]; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = buff, + .data_size_in = sizeof(buff), + .repeat = 1, + ); err = bpf_prog_test_load("tailcall5.o", BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); @@ -441,7 +448,7 @@ static void test_tailcall_5(void) if (CHECK_FAIL(map_fd < 0)) return; - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); prog = bpf_object__find_program_by_name(obj, prog_name); @@ -457,18 +464,17 @@ static void test_tailcall_5(void) goto out; } - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { err = bpf_map_update_elem(data_fd, &zero, &key[i], BPF_ANY); if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != i, "tailcall", - "err %d errno %d retval %d\n", err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, i, "tailcall retval"); } - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { err = bpf_map_update_elem(data_fd, &zero, &key[i], BPF_ANY); if (CHECK_FAIL(err)) goto out; @@ -477,10 +483,9 @@ static void test_tailcall_5(void) if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 3, "tailcall", - "err %d errno %d retval %d\n", err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, 3, "tailcall retval"); } out: bpf_object__close(obj); @@ -495,8 +500,12 @@ static void test_tailcall_bpf2bpf_1(void) struct bpf_map *prog_array; struct bpf_program *prog; struct bpf_object *obj; - __u32 retval, duration; char prog_name[32]; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); err = bpf_prog_test_load("tailcall_bpf2bpf1.o", BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); @@ -520,7 +529,7 @@ static void test_tailcall_bpf2bpf_1(void) goto out; /* nop -> jmp */ - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); prog = bpf_object__find_program_by_name(obj, prog_name); @@ -536,10 +545,9 @@ static void test_tailcall_bpf2bpf_1(void) goto out; } - err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0, - 0, &retval, &duration); - CHECK(err || retval != 1, "tailcall", - "err %d errno %d retval %d\n", err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, 1, "tailcall retval"); /* jmp -> nop, call subprog that will do tailcall */ i = 1; @@ -547,10 +555,9 @@ static void test_tailcall_bpf2bpf_1(void) if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0, - 0, &retval, &duration); - CHECK(err || retval != 0, "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_OK(topts.retval, "tailcall retval"); /* make sure that subprog can access ctx and entry prog that * called this subprog can properly return @@ -560,11 +567,9 @@ static void test_tailcall_bpf2bpf_1(void) if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0, - 0, &retval, &duration); - CHECK(err || retval != sizeof(pkt_v4) * 2, - "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 2, "tailcall retval"); out: bpf_object__close(obj); } @@ -579,8 +584,12 @@ static void test_tailcall_bpf2bpf_2(void) struct bpf_map *prog_array, *data_map; struct bpf_program *prog; struct bpf_object *obj; - __u32 retval, duration; char buff[128] = {}; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = buff, + .data_size_in = sizeof(buff), + .repeat = 1, + ); err = bpf_prog_test_load("tailcall_bpf2bpf2.o", BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); @@ -616,10 +625,9 @@ static void test_tailcall_bpf2bpf_2(void) if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 1, "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, 1, "tailcall retval"); data_map = bpf_object__find_map_by_name(obj, "tailcall.bss"); if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map))) @@ -631,18 +639,17 @@ static void test_tailcall_bpf2bpf_2(void) i = 0; err = bpf_map_lookup_elem(data_fd, &i, &val); - CHECK(err || val != 33, "tailcall count", "err %d errno %d count %d\n", - err, errno, val); + ASSERT_OK(err, "tailcall count"); + ASSERT_EQ(val, 33, "tailcall count"); i = 0; err = bpf_map_delete_elem(map_fd, &i); if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0, - &duration, &retval, NULL); - CHECK(err || retval != 0, "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_OK(topts.retval, "tailcall retval"); out: bpf_object__close(obj); } @@ -657,8 +664,12 @@ static void test_tailcall_bpf2bpf_3(void) struct bpf_map *prog_array; struct bpf_program *prog; struct bpf_object *obj; - __u32 retval, duration; char prog_name[32]; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); err = bpf_prog_test_load("tailcall_bpf2bpf3.o", BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); @@ -681,7 +692,7 @@ static void test_tailcall_bpf2bpf_3(void) if (CHECK_FAIL(map_fd < 0)) goto out; - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); prog = bpf_object__find_program_by_name(obj, prog_name); @@ -697,33 +708,27 @@ static void test_tailcall_bpf2bpf_3(void) goto out; } - err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0, - &duration, &retval, NULL); - CHECK(err || retval != sizeof(pkt_v4) * 3, - "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 3, "tailcall retval"); i = 1; err = bpf_map_delete_elem(map_fd, &i); if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0, - &duration, &retval, NULL); - CHECK(err || retval != sizeof(pkt_v4), - "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, sizeof(pkt_v4), "tailcall retval"); i = 0; err = bpf_map_delete_elem(map_fd, &i); if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0, - &duration, &retval, NULL); - CHECK(err || retval != sizeof(pkt_v4) * 2, - "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 2, "tailcall retval"); out: bpf_object__close(obj); } @@ -754,8 +759,12 @@ static void test_tailcall_bpf2bpf_4(bool noise) struct bpf_map *prog_array, *data_map; struct bpf_program *prog; struct bpf_object *obj; - __u32 retval, duration; char prog_name[32]; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); err = bpf_prog_test_load("tailcall_bpf2bpf4.o", BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); @@ -778,7 +787,7 @@ static void test_tailcall_bpf2bpf_4(bool noise) if (CHECK_FAIL(map_fd < 0)) goto out; - for (i = 0; i < bpf_map__def(prog_array)->max_entries; i++) { + for (i = 0; i < bpf_map__max_entries(prog_array); i++) { snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); prog = bpf_object__find_program_by_name(obj, prog_name); @@ -809,15 +818,14 @@ static void test_tailcall_bpf2bpf_4(bool noise) if (CHECK_FAIL(err)) goto out; - err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0, - &duration, &retval, NULL); - CHECK(err || retval != sizeof(pkt_v4) * 3, "tailcall", "err %d errno %d retval %d\n", - err, errno, retval); + err = bpf_prog_test_run_opts(main_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 3, "tailcall retval"); i = 0; err = bpf_map_lookup_elem(data_fd, &i, &val); - CHECK(err || val.count != 31, "tailcall count", "err %d errno %d count %d\n", - err, errno, val.count); + ASSERT_OK(err, "tailcall count"); + ASSERT_EQ(val.count, 31, "tailcall count"); out: bpf_object__close(obj); diff --git a/tools/testing/selftests/bpf/prog_tests/task_pt_regs.c b/tools/testing/selftests/bpf/prog_tests/task_pt_regs.c index 37c20b5ffa70..61935e7e056a 100644 --- a/tools/testing/selftests/bpf/prog_tests/task_pt_regs.c +++ b/tools/testing/selftests/bpf/prog_tests/task_pt_regs.c @@ -3,18 +3,22 @@ #include #include "test_task_pt_regs.skel.h" +/* uprobe attach point */ +static void trigger_func(void) +{ + asm volatile (""); +} + void test_task_pt_regs(void) { struct test_task_pt_regs *skel; struct bpf_link *uprobe_link; - size_t uprobe_offset; - ssize_t base_addr; + ssize_t uprobe_offset; bool match; - base_addr = get_base_addr(); - if (!ASSERT_GT(base_addr, 0, "get_base_addr")) + uprobe_offset = get_uprobe_offset(&trigger_func); + if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset")) return; - uprobe_offset = get_uprobe_offset(&get_base_addr, base_addr); skel = test_task_pt_regs__open_and_load(); if (!ASSERT_OK_PTR(skel, "skel_open")) @@ -32,7 +36,7 @@ void test_task_pt_regs(void) skel->links.handle_uprobe = uprobe_link; /* trigger & validate uprobe */ - get_base_addr(); + trigger_func(); if (!ASSERT_EQ(skel->bss->uprobe_res, 1, "check_uprobe_res")) goto cleanup; diff --git a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c index c2426df58e17..7ad66a247c02 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c @@ -10,17 +10,15 @@ * to drop unexpected traffic. */ -#define _GNU_SOURCE - #include #include #include #include #include -#include +#include +#include #include #include -#include #include #include @@ -29,6 +27,11 @@ #include "test_tc_neigh_fib.skel.h" #include "test_tc_neigh.skel.h" #include "test_tc_peer.skel.h" +#include "test_tc_dtime.skel.h" + +#ifndef TCP_TX_DELAY +#define TCP_TX_DELAY 37 +#endif #define NS_SRC "ns_src" #define NS_FWD "ns_fwd" @@ -61,6 +64,7 @@ #define CHK_PROG_PIN_FILE "/sys/fs/bpf/test_tc_chk" #define TIMEOUT_MILLIS 10000 +#define NSEC_PER_SEC 1000000000ULL #define log_err(MSG, ...) \ fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \ @@ -84,91 +88,6 @@ static int write_file(const char *path, const char *newval) return 0; } -struct nstoken { - int orig_netns_fd; -}; - -static int setns_by_fd(int nsfd) -{ - int err; - - err = setns(nsfd, CLONE_NEWNET); - close(nsfd); - - if (!ASSERT_OK(err, "setns")) - return err; - - /* Switch /sys to the new namespace so that e.g. /sys/class/net - * reflects the devices in the new namespace. - */ - err = unshare(CLONE_NEWNS); - if (!ASSERT_OK(err, "unshare")) - return err; - - /* Make our /sys mount private, so the following umount won't - * trigger the global umount in case it's shared. - */ - err = mount("none", "/sys", NULL, MS_PRIVATE, NULL); - if (!ASSERT_OK(err, "remount private /sys")) - return err; - - err = umount2("/sys", MNT_DETACH); - if (!ASSERT_OK(err, "umount2 /sys")) - return err; - - err = mount("sysfs", "/sys", "sysfs", 0, NULL); - if (!ASSERT_OK(err, "mount /sys")) - return err; - - err = mount("bpffs", "/sys/fs/bpf", "bpf", 0, NULL); - if (!ASSERT_OK(err, "mount /sys/fs/bpf")) - return err; - - return 0; -} - -/** - * open_netns() - Switch to specified network namespace by name. - * - * Returns token with which to restore the original namespace - * using close_netns(). - */ -static struct nstoken *open_netns(const char *name) -{ - int nsfd; - char nspath[PATH_MAX]; - int err; - struct nstoken *token; - - token = malloc(sizeof(struct nstoken)); - if (!ASSERT_OK_PTR(token, "malloc token")) - return NULL; - - token->orig_netns_fd = open("/proc/self/ns/net", O_RDONLY); - if (!ASSERT_GE(token->orig_netns_fd, 0, "open /proc/self/ns/net")) - goto fail; - - snprintf(nspath, sizeof(nspath), "%s/%s", "/var/run/netns", name); - nsfd = open(nspath, O_RDONLY | O_CLOEXEC); - if (!ASSERT_GE(nsfd, 0, "open netns fd")) - goto fail; - - err = setns_by_fd(nsfd); - if (!ASSERT_OK(err, "setns_by_fd")) - goto fail; - - return token; -fail: - free(token); - return NULL; -} - -static void close_netns(struct nstoken *token) -{ - ASSERT_OK(setns_by_fd(token->orig_netns_fd), "setns_by_fd"); - free(token); -} - static int netns_setup_namespaces(const char *verb) { const char * const *ns = namespaces; @@ -440,6 +359,431 @@ static int set_forwarding(bool enable) return 0; } +static void rcv_tstamp(int fd, const char *expected, size_t s) +{ + struct __kernel_timespec pkt_ts = {}; + char ctl[CMSG_SPACE(sizeof(pkt_ts))]; + struct timespec now_ts; + struct msghdr msg = {}; + __u64 now_ns, pkt_ns; + struct cmsghdr *cmsg; + struct iovec iov; + char data[32]; + int ret; + + iov.iov_base = data; + iov.iov_len = sizeof(data); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &ctl; + msg.msg_controllen = sizeof(ctl); + + ret = recvmsg(fd, &msg, 0); + if (!ASSERT_EQ(ret, s, "recvmsg")) + return; + ASSERT_STRNEQ(data, expected, s, "expected rcv data"); + + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg && cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TIMESTAMPNS_NEW) + memcpy(&pkt_ts, CMSG_DATA(cmsg), sizeof(pkt_ts)); + + pkt_ns = pkt_ts.tv_sec * NSEC_PER_SEC + pkt_ts.tv_nsec; + ASSERT_NEQ(pkt_ns, 0, "pkt rcv tstamp"); + + ret = clock_gettime(CLOCK_REALTIME, &now_ts); + ASSERT_OK(ret, "clock_gettime"); + now_ns = now_ts.tv_sec * NSEC_PER_SEC + now_ts.tv_nsec; + + if (ASSERT_GE(now_ns, pkt_ns, "check rcv tstamp")) + ASSERT_LT(now_ns - pkt_ns, 5 * NSEC_PER_SEC, + "check rcv tstamp"); +} + +static void snd_tstamp(int fd, char *b, size_t s) +{ + struct sock_txtime opt = { .clockid = CLOCK_TAI }; + char ctl[CMSG_SPACE(sizeof(__u64))]; + struct timespec now_ts; + struct msghdr msg = {}; + struct cmsghdr *cmsg; + struct iovec iov; + __u64 now_ns; + int ret; + + ret = clock_gettime(CLOCK_TAI, &now_ts); + ASSERT_OK(ret, "clock_get_time(CLOCK_TAI)"); + now_ns = now_ts.tv_sec * NSEC_PER_SEC + now_ts.tv_nsec; + + iov.iov_base = b; + iov.iov_len = s; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &ctl; + msg.msg_controllen = sizeof(ctl); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_TXTIME; + cmsg->cmsg_len = CMSG_LEN(sizeof(now_ns)); + *(__u64 *)CMSG_DATA(cmsg) = now_ns; + + ret = setsockopt(fd, SOL_SOCKET, SO_TXTIME, &opt, sizeof(opt)); + ASSERT_OK(ret, "setsockopt(SO_TXTIME)"); + + ret = sendmsg(fd, &msg, 0); + ASSERT_EQ(ret, s, "sendmsg"); +} + +static void test_inet_dtime(int family, int type, const char *addr, __u16 port) +{ + int opt = 1, accept_fd = -1, client_fd = -1, listen_fd, err; + char buf[] = "testing testing"; + struct nstoken *nstoken; + + nstoken = open_netns(NS_DST); + if (!ASSERT_OK_PTR(nstoken, "setns dst")) + return; + listen_fd = start_server(family, type, addr, port, 0); + close_netns(nstoken); + + if (!ASSERT_GE(listen_fd, 0, "listen")) + return; + + /* Ensure the kernel puts the (rcv) timestamp for all skb */ + err = setsockopt(listen_fd, SOL_SOCKET, SO_TIMESTAMPNS_NEW, + &opt, sizeof(opt)); + if (!ASSERT_OK(err, "setsockopt(SO_TIMESTAMPNS_NEW)")) + goto done; + + if (type == SOCK_STREAM) { + /* Ensure the kernel set EDT when sending out rst/ack + * from the kernel's ctl_sk. + */ + err = setsockopt(listen_fd, SOL_TCP, TCP_TX_DELAY, &opt, + sizeof(opt)); + if (!ASSERT_OK(err, "setsockopt(TCP_TX_DELAY)")) + goto done; + } + + nstoken = open_netns(NS_SRC); + if (!ASSERT_OK_PTR(nstoken, "setns src")) + goto done; + client_fd = connect_to_fd(listen_fd, TIMEOUT_MILLIS); + close_netns(nstoken); + + if (!ASSERT_GE(client_fd, 0, "connect_to_fd")) + goto done; + + if (type == SOCK_STREAM) { + int n; + + accept_fd = accept(listen_fd, NULL, NULL); + if (!ASSERT_GE(accept_fd, 0, "accept")) + goto done; + + n = write(client_fd, buf, sizeof(buf)); + if (!ASSERT_EQ(n, sizeof(buf), "send to server")) + goto done; + rcv_tstamp(accept_fd, buf, sizeof(buf)); + } else { + snd_tstamp(client_fd, buf, sizeof(buf)); + rcv_tstamp(listen_fd, buf, sizeof(buf)); + } + +done: + close(listen_fd); + if (accept_fd != -1) + close(accept_fd); + if (client_fd != -1) + close(client_fd); +} + +static int netns_load_dtime_bpf(struct test_tc_dtime *skel) +{ + struct nstoken *nstoken; + +#define PIN_FNAME(__file) "/sys/fs/bpf/" #__file +#define PIN(__prog) ({ \ + int err = bpf_program__pin(skel->progs.__prog, PIN_FNAME(__prog)); \ + if (!ASSERT_OK(err, "pin " #__prog)) \ + goto fail; \ + }) + + /* setup ns_src tc progs */ + nstoken = open_netns(NS_SRC); + if (!ASSERT_OK_PTR(nstoken, "setns " NS_SRC)) + return -1; + PIN(egress_host); + PIN(ingress_host); + SYS("tc qdisc add dev veth_src clsact"); + SYS("tc filter add dev veth_src ingress bpf da object-pinned " + PIN_FNAME(ingress_host)); + SYS("tc filter add dev veth_src egress bpf da object-pinned " + PIN_FNAME(egress_host)); + close_netns(nstoken); + + /* setup ns_dst tc progs */ + nstoken = open_netns(NS_DST); + if (!ASSERT_OK_PTR(nstoken, "setns " NS_DST)) + return -1; + PIN(egress_host); + PIN(ingress_host); + SYS("tc qdisc add dev veth_dst clsact"); + SYS("tc filter add dev veth_dst ingress bpf da object-pinned " + PIN_FNAME(ingress_host)); + SYS("tc filter add dev veth_dst egress bpf da object-pinned " + PIN_FNAME(egress_host)); + close_netns(nstoken); + + /* setup ns_fwd tc progs */ + nstoken = open_netns(NS_FWD); + if (!ASSERT_OK_PTR(nstoken, "setns " NS_FWD)) + return -1; + PIN(ingress_fwdns_prio100); + PIN(egress_fwdns_prio100); + PIN(ingress_fwdns_prio101); + PIN(egress_fwdns_prio101); + SYS("tc qdisc add dev veth_dst_fwd clsact"); + SYS("tc filter add dev veth_dst_fwd ingress prio 100 bpf da object-pinned " + PIN_FNAME(ingress_fwdns_prio100)); + SYS("tc filter add dev veth_dst_fwd ingress prio 101 bpf da object-pinned " + PIN_FNAME(ingress_fwdns_prio101)); + SYS("tc filter add dev veth_dst_fwd egress prio 100 bpf da object-pinned " + PIN_FNAME(egress_fwdns_prio100)); + SYS("tc filter add dev veth_dst_fwd egress prio 101 bpf da object-pinned " + PIN_FNAME(egress_fwdns_prio101)); + SYS("tc qdisc add dev veth_src_fwd clsact"); + SYS("tc filter add dev veth_src_fwd ingress prio 100 bpf da object-pinned " + PIN_FNAME(ingress_fwdns_prio100)); + SYS("tc filter add dev veth_src_fwd ingress prio 101 bpf da object-pinned " + PIN_FNAME(ingress_fwdns_prio101)); + SYS("tc filter add dev veth_src_fwd egress prio 100 bpf da object-pinned " + PIN_FNAME(egress_fwdns_prio100)); + SYS("tc filter add dev veth_src_fwd egress prio 101 bpf da object-pinned " + PIN_FNAME(egress_fwdns_prio101)); + close_netns(nstoken); + +#undef PIN + + return 0; + +fail: + close_netns(nstoken); + return -1; +} + +enum { + INGRESS_FWDNS_P100, + INGRESS_FWDNS_P101, + EGRESS_FWDNS_P100, + EGRESS_FWDNS_P101, + INGRESS_ENDHOST, + EGRESS_ENDHOST, + SET_DTIME, + __MAX_CNT, +}; + +const char *cnt_names[] = { + "ingress_fwdns_p100", + "ingress_fwdns_p101", + "egress_fwdns_p100", + "egress_fwdns_p101", + "ingress_endhost", + "egress_endhost", + "set_dtime", +}; + +enum { + TCP_IP6_CLEAR_DTIME, + TCP_IP4, + TCP_IP6, + UDP_IP4, + UDP_IP6, + TCP_IP4_RT_FWD, + TCP_IP6_RT_FWD, + UDP_IP4_RT_FWD, + UDP_IP6_RT_FWD, + UKN_TEST, + __NR_TESTS, +}; + +const char *test_names[] = { + "tcp ip6 clear dtime", + "tcp ip4", + "tcp ip6", + "udp ip4", + "udp ip6", + "tcp ip4 rt fwd", + "tcp ip6 rt fwd", + "udp ip4 rt fwd", + "udp ip6 rt fwd", +}; + +static const char *dtime_cnt_str(int test, int cnt) +{ + static char name[64]; + + snprintf(name, sizeof(name), "%s %s", test_names[test], cnt_names[cnt]); + + return name; +} + +static const char *dtime_err_str(int test, int cnt) +{ + static char name[64]; + + snprintf(name, sizeof(name), "%s %s errs", test_names[test], + cnt_names[cnt]); + + return name; +} + +static void test_tcp_clear_dtime(struct test_tc_dtime *skel) +{ + int i, t = TCP_IP6_CLEAR_DTIME; + __u32 *dtimes = skel->bss->dtimes[t]; + __u32 *errs = skel->bss->errs[t]; + + skel->bss->test = t; + test_inet_dtime(AF_INET6, SOCK_STREAM, IP6_DST, 0); + + ASSERT_EQ(dtimes[INGRESS_FWDNS_P100], 0, + dtime_cnt_str(t, INGRESS_FWDNS_P100)); + ASSERT_EQ(dtimes[INGRESS_FWDNS_P101], 0, + dtime_cnt_str(t, INGRESS_FWDNS_P101)); + ASSERT_GT(dtimes[EGRESS_FWDNS_P100], 0, + dtime_cnt_str(t, EGRESS_FWDNS_P100)); + ASSERT_EQ(dtimes[EGRESS_FWDNS_P101], 0, + dtime_cnt_str(t, EGRESS_FWDNS_P101)); + ASSERT_GT(dtimes[EGRESS_ENDHOST], 0, + dtime_cnt_str(t, EGRESS_ENDHOST)); + ASSERT_GT(dtimes[INGRESS_ENDHOST], 0, + dtime_cnt_str(t, INGRESS_ENDHOST)); + + for (i = INGRESS_FWDNS_P100; i < __MAX_CNT; i++) + ASSERT_EQ(errs[i], 0, dtime_err_str(t, i)); +} + +static void test_tcp_dtime(struct test_tc_dtime *skel, int family, bool bpf_fwd) +{ + __u32 *dtimes, *errs; + const char *addr; + int i, t; + + if (family == AF_INET) { + t = bpf_fwd ? TCP_IP4 : TCP_IP4_RT_FWD; + addr = IP4_DST; + } else { + t = bpf_fwd ? TCP_IP6 : TCP_IP6_RT_FWD; + addr = IP6_DST; + } + + dtimes = skel->bss->dtimes[t]; + errs = skel->bss->errs[t]; + + skel->bss->test = t; + test_inet_dtime(family, SOCK_STREAM, addr, 0); + + /* fwdns_prio100 prog does not read delivery_time_type, so + * kernel puts the (rcv) timetamp in __sk_buff->tstamp + */ + ASSERT_EQ(dtimes[INGRESS_FWDNS_P100], 0, + dtime_cnt_str(t, INGRESS_FWDNS_P100)); + for (i = INGRESS_FWDNS_P101; i < SET_DTIME; i++) + ASSERT_GT(dtimes[i], 0, dtime_cnt_str(t, i)); + + for (i = INGRESS_FWDNS_P100; i < __MAX_CNT; i++) + ASSERT_EQ(errs[i], 0, dtime_err_str(t, i)); +} + +static void test_udp_dtime(struct test_tc_dtime *skel, int family, bool bpf_fwd) +{ + __u32 *dtimes, *errs; + const char *addr; + int i, t; + + if (family == AF_INET) { + t = bpf_fwd ? UDP_IP4 : UDP_IP4_RT_FWD; + addr = IP4_DST; + } else { + t = bpf_fwd ? UDP_IP6 : UDP_IP6_RT_FWD; + addr = IP6_DST; + } + + dtimes = skel->bss->dtimes[t]; + errs = skel->bss->errs[t]; + + skel->bss->test = t; + test_inet_dtime(family, SOCK_DGRAM, addr, 0); + + ASSERT_EQ(dtimes[INGRESS_FWDNS_P100], 0, + dtime_cnt_str(t, INGRESS_FWDNS_P100)); + /* non mono delivery time is not forwarded */ + ASSERT_EQ(dtimes[INGRESS_FWDNS_P101], 0, + dtime_cnt_str(t, INGRESS_FWDNS_P100)); + for (i = EGRESS_FWDNS_P100; i < SET_DTIME; i++) + ASSERT_GT(dtimes[i], 0, dtime_cnt_str(t, i)); + + for (i = INGRESS_FWDNS_P100; i < __MAX_CNT; i++) + ASSERT_EQ(errs[i], 0, dtime_err_str(t, i)); +} + +static void test_tc_redirect_dtime(struct netns_setup_result *setup_result) +{ + struct test_tc_dtime *skel; + struct nstoken *nstoken; + int err; + + skel = test_tc_dtime__open(); + if (!ASSERT_OK_PTR(skel, "test_tc_dtime__open")) + return; + + skel->rodata->IFINDEX_SRC = setup_result->ifindex_veth_src_fwd; + skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd; + + err = test_tc_dtime__load(skel); + if (!ASSERT_OK(err, "test_tc_dtime__load")) + goto done; + + if (netns_load_dtime_bpf(skel)) + goto done; + + nstoken = open_netns(NS_FWD); + if (!ASSERT_OK_PTR(nstoken, "setns fwd")) + goto done; + err = set_forwarding(false); + close_netns(nstoken); + if (!ASSERT_OK(err, "disable forwarding")) + goto done; + + test_tcp_clear_dtime(skel); + + test_tcp_dtime(skel, AF_INET, true); + test_tcp_dtime(skel, AF_INET6, true); + test_udp_dtime(skel, AF_INET, true); + test_udp_dtime(skel, AF_INET6, true); + + /* Test the kernel ip[6]_forward path instead + * of bpf_redirect_neigh(). + */ + nstoken = open_netns(NS_FWD); + if (!ASSERT_OK_PTR(nstoken, "setns fwd")) + goto done; + err = set_forwarding(true); + close_netns(nstoken); + if (!ASSERT_OK(err, "enable forwarding")) + goto done; + + test_tcp_dtime(skel, AF_INET, false); + test_tcp_dtime(skel, AF_INET6, false); + test_udp_dtime(skel, AF_INET, false); + test_udp_dtime(skel, AF_INET6, false); + +done: + test_tc_dtime__destroy(skel); +} + static void test_tc_redirect_neigh_fib(struct netns_setup_result *setup_result) { struct nstoken *nstoken = NULL; @@ -787,6 +1131,7 @@ static void *test_tc_redirect_run_tests(void *arg) RUN_TEST(tc_redirect_peer_l3); RUN_TEST(tc_redirect_neigh); RUN_TEST(tc_redirect_neigh_fib); + RUN_TEST(tc_redirect_dtime); return NULL; } diff --git a/tools/testing/selftests/bpf/prog_tests/test_bpf_syscall_macro.c b/tools/testing/selftests/bpf/prog_tests/test_bpf_syscall_macro.c new file mode 100644 index 000000000000..c381faaae741 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/test_bpf_syscall_macro.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2022 Sony Group Corporation */ +#include +#include +#include "bpf_syscall_macro.skel.h" + +void test_bpf_syscall_macro(void) +{ + struct bpf_syscall_macro *skel = NULL; + int err; + int exp_arg1 = 1001; + unsigned long exp_arg2 = 12; + unsigned long exp_arg3 = 13; + unsigned long exp_arg4 = 14; + unsigned long exp_arg5 = 15; + + /* check whether it can open program */ + skel = bpf_syscall_macro__open(); + if (!ASSERT_OK_PTR(skel, "bpf_syscall_macro__open")) + return; + + skel->rodata->filter_pid = getpid(); + + /* check whether it can load program */ + err = bpf_syscall_macro__load(skel); + if (!ASSERT_OK(err, "bpf_syscall_macro__load")) + goto cleanup; + + /* check whether it can attach kprobe */ + err = bpf_syscall_macro__attach(skel); + if (!ASSERT_OK(err, "bpf_syscall_macro__attach")) + goto cleanup; + + /* check whether args of syscall are copied correctly */ + prctl(exp_arg1, exp_arg2, exp_arg3, exp_arg4, exp_arg5); +#if defined(__aarch64__) || defined(__s390__) + ASSERT_NEQ(skel->bss->arg1, exp_arg1, "syscall_arg1"); +#else + ASSERT_EQ(skel->bss->arg1, exp_arg1, "syscall_arg1"); +#endif + ASSERT_EQ(skel->bss->arg2, exp_arg2, "syscall_arg2"); + ASSERT_EQ(skel->bss->arg3, exp_arg3, "syscall_arg3"); + /* it cannot copy arg4 when uses PT_REGS_PARM4 on x86_64 */ +#ifdef __x86_64__ + ASSERT_NEQ(skel->bss->arg4_cx, exp_arg4, "syscall_arg4_from_cx"); +#else + ASSERT_EQ(skel->bss->arg4_cx, exp_arg4, "syscall_arg4_from_cx"); +#endif + ASSERT_EQ(skel->bss->arg4, exp_arg4, "syscall_arg4"); + ASSERT_EQ(skel->bss->arg5, exp_arg5, "syscall_arg5"); + + /* check whether args of syscall are copied correctly for CORE variants */ + ASSERT_EQ(skel->bss->arg1_core, exp_arg1, "syscall_arg1_core_variant"); + ASSERT_EQ(skel->bss->arg2_core, exp_arg2, "syscall_arg2_core_variant"); + ASSERT_EQ(skel->bss->arg3_core, exp_arg3, "syscall_arg3_core_variant"); + /* it cannot copy arg4 when uses PT_REGS_PARM4_CORE on x86_64 */ +#ifdef __x86_64__ + ASSERT_NEQ(skel->bss->arg4_core_cx, exp_arg4, "syscall_arg4_from_cx_core_variant"); +#else + ASSERT_EQ(skel->bss->arg4_core_cx, exp_arg4, "syscall_arg4_from_cx_core_variant"); +#endif + ASSERT_EQ(skel->bss->arg4_core, exp_arg4, "syscall_arg4_core_variant"); + ASSERT_EQ(skel->bss->arg5_core, exp_arg5, "syscall_arg5_core_variant"); + + ASSERT_EQ(skel->bss->option_syscall, exp_arg1, "BPF_KPROBE_SYSCALL_option"); + ASSERT_EQ(skel->bss->arg2_syscall, exp_arg2, "BPF_KPROBE_SYSCALL_arg2"); + ASSERT_EQ(skel->bss->arg3_syscall, exp_arg3, "BPF_KPROBE_SYSCALL_arg3"); + ASSERT_EQ(skel->bss->arg4_syscall, exp_arg4, "BPF_KPROBE_SYSCALL_arg4"); + ASSERT_EQ(skel->bss->arg5_syscall, exp_arg5, "BPF_KPROBE_SYSCALL_arg5"); + +cleanup: + bpf_syscall_macro__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/test_ima.c b/tools/testing/selftests/bpf/prog_tests/test_ima.c index 97d8a6f84f4a..b13feceb38f1 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_ima.c +++ b/tools/testing/selftests/bpf/prog_tests/test_ima.c @@ -13,14 +13,17 @@ #include "ima.skel.h" -static int run_measured_process(const char *measured_dir, u32 *monitored_pid) +#define MAX_SAMPLES 4 + +static int _run_measured_process(const char *measured_dir, u32 *monitored_pid, + const char *cmd) { int child_pid, child_status; child_pid = fork(); if (child_pid == 0) { *monitored_pid = getpid(); - execlp("./ima_setup.sh", "./ima_setup.sh", "run", measured_dir, + execlp("./ima_setup.sh", "./ima_setup.sh", cmd, measured_dir, NULL); exit(errno); @@ -32,19 +35,39 @@ static int run_measured_process(const char *measured_dir, u32 *monitored_pid) return -EINVAL; } -static u64 ima_hash_from_bpf; +static int run_measured_process(const char *measured_dir, u32 *monitored_pid) +{ + return _run_measured_process(measured_dir, monitored_pid, "run"); +} + +static u64 ima_hash_from_bpf[MAX_SAMPLES]; +static int ima_hash_from_bpf_idx; static int process_sample(void *ctx, void *data, size_t len) { - ima_hash_from_bpf = *((u64 *)data); + if (ima_hash_from_bpf_idx >= MAX_SAMPLES) + return -ENOSPC; + + ima_hash_from_bpf[ima_hash_from_bpf_idx++] = *((u64 *)data); return 0; } +static void test_init(struct ima__bss *bss) +{ + ima_hash_from_bpf_idx = 0; + + bss->use_ima_file_hash = false; + bss->enable_bprm_creds_for_exec = false; + bss->enable_kernel_read_file = false; + bss->test_deny = false; +} + void test_test_ima(void) { char measured_dir_template[] = "/tmp/ima_measuredXXXXXX"; struct ring_buffer *ringbuf = NULL; const char *measured_dir; + u64 bin_true_sample; char cmd[256]; int err, duration = 0; @@ -72,13 +95,127 @@ void test_test_ima(void) if (CHECK(err, "failed to run command", "%s, errno = %d\n", cmd, errno)) goto close_clean; + /* + * Test #1 + * - Goal: obtain a sample with the bpf_ima_inode_hash() helper + * - Expected result: 1 sample (/bin/true) + */ + test_init(skel->bss); err = run_measured_process(measured_dir, &skel->bss->monitored_pid); - if (CHECK(err, "run_measured_process", "err = %d\n", err)) + if (CHECK(err, "run_measured_process #1", "err = %d\n", err)) goto close_clean; err = ring_buffer__consume(ringbuf); ASSERT_EQ(err, 1, "num_samples_or_err"); - ASSERT_NEQ(ima_hash_from_bpf, 0, "ima_hash"); + ASSERT_NEQ(ima_hash_from_bpf[0], 0, "ima_hash"); + + /* + * Test #2 + * - Goal: obtain samples with the bpf_ima_file_hash() helper + * - Expected result: 2 samples (./ima_setup.sh, /bin/true) + */ + test_init(skel->bss); + skel->bss->use_ima_file_hash = true; + err = run_measured_process(measured_dir, &skel->bss->monitored_pid); + if (CHECK(err, "run_measured_process #2", "err = %d\n", err)) + goto close_clean; + + err = ring_buffer__consume(ringbuf); + ASSERT_EQ(err, 2, "num_samples_or_err"); + ASSERT_NEQ(ima_hash_from_bpf[0], 0, "ima_hash"); + ASSERT_NEQ(ima_hash_from_bpf[1], 0, "ima_hash"); + bin_true_sample = ima_hash_from_bpf[1]; + + /* + * Test #3 + * - Goal: confirm that bpf_ima_inode_hash() returns a non-fresh digest + * - Expected result: 2 samples (/bin/true: non-fresh, fresh) + */ + test_init(skel->bss); + + err = _run_measured_process(measured_dir, &skel->bss->monitored_pid, + "modify-bin"); + if (CHECK(err, "modify-bin #3", "err = %d\n", err)) + goto close_clean; + + skel->bss->enable_bprm_creds_for_exec = true; + err = run_measured_process(measured_dir, &skel->bss->monitored_pid); + if (CHECK(err, "run_measured_process #3", "err = %d\n", err)) + goto close_clean; + + err = ring_buffer__consume(ringbuf); + ASSERT_EQ(err, 2, "num_samples_or_err"); + ASSERT_NEQ(ima_hash_from_bpf[0], 0, "ima_hash"); + ASSERT_NEQ(ima_hash_from_bpf[1], 0, "ima_hash"); + ASSERT_EQ(ima_hash_from_bpf[0], bin_true_sample, "sample_equal_or_err"); + /* IMA refreshed the digest. */ + ASSERT_NEQ(ima_hash_from_bpf[1], bin_true_sample, + "sample_different_or_err"); + + /* + * Test #4 + * - Goal: verify that bpf_ima_file_hash() returns a fresh digest + * - Expected result: 4 samples (./ima_setup.sh: fresh, fresh; + * /bin/true: fresh, fresh) + */ + test_init(skel->bss); + skel->bss->use_ima_file_hash = true; + skel->bss->enable_bprm_creds_for_exec = true; + err = run_measured_process(measured_dir, &skel->bss->monitored_pid); + if (CHECK(err, "run_measured_process #4", "err = %d\n", err)) + goto close_clean; + + err = ring_buffer__consume(ringbuf); + ASSERT_EQ(err, 4, "num_samples_or_err"); + ASSERT_NEQ(ima_hash_from_bpf[0], 0, "ima_hash"); + ASSERT_NEQ(ima_hash_from_bpf[1], 0, "ima_hash"); + ASSERT_NEQ(ima_hash_from_bpf[2], 0, "ima_hash"); + ASSERT_NEQ(ima_hash_from_bpf[3], 0, "ima_hash"); + ASSERT_NEQ(ima_hash_from_bpf[2], bin_true_sample, + "sample_different_or_err"); + ASSERT_EQ(ima_hash_from_bpf[3], ima_hash_from_bpf[2], + "sample_equal_or_err"); + + skel->bss->use_ima_file_hash = false; + skel->bss->enable_bprm_creds_for_exec = false; + err = _run_measured_process(measured_dir, &skel->bss->monitored_pid, + "restore-bin"); + if (CHECK(err, "restore-bin #3", "err = %d\n", err)) + goto close_clean; + + /* + * Test #5 + * - Goal: obtain a sample from the kernel_read_file hook + * - Expected result: 2 samples (./ima_setup.sh, policy_test) + */ + test_init(skel->bss); + skel->bss->use_ima_file_hash = true; + skel->bss->enable_kernel_read_file = true; + err = _run_measured_process(measured_dir, &skel->bss->monitored_pid, + "load-policy"); + if (CHECK(err, "run_measured_process #5", "err = %d\n", err)) + goto close_clean; + + err = ring_buffer__consume(ringbuf); + ASSERT_EQ(err, 2, "num_samples_or_err"); + ASSERT_NEQ(ima_hash_from_bpf[0], 0, "ima_hash"); + ASSERT_NEQ(ima_hash_from_bpf[1], 0, "ima_hash"); + + /* + * Test #6 + * - Goal: ensure that the kernel_read_file hook denies an operation + * - Expected result: 0 samples + */ + test_init(skel->bss); + skel->bss->enable_kernel_read_file = true; + skel->bss->test_deny = true; + err = _run_measured_process(measured_dir, &skel->bss->monitored_pid, + "load-policy"); + if (CHECK(!err, "run_measured_process #6", "err = %d\n", err)) + goto close_clean; + + err = ring_buffer__consume(ringbuf); + ASSERT_EQ(err, 0, "num_samples_or_err"); close_clean: snprintf(cmd, sizeof(cmd), "./ima_setup.sh cleanup %s", measured_dir); diff --git a/tools/testing/selftests/bpf/prog_tests/test_profiler.c b/tools/testing/selftests/bpf/prog_tests/test_profiler.c index 4ca275101ee0..de24e8f0e738 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_profiler.c +++ b/tools/testing/selftests/bpf/prog_tests/test_profiler.c @@ -8,20 +8,20 @@ static int sanity_run(struct bpf_program *prog) { - struct bpf_prog_test_run_attr test_attr = {}; + LIBBPF_OPTS(bpf_test_run_opts, test_attr); __u64 args[] = {1, 2, 3}; - __u32 duration = 0; int err, prog_fd; prog_fd = bpf_program__fd(prog); - test_attr.prog_fd = prog_fd; test_attr.ctx_in = args; test_attr.ctx_size_in = sizeof(args); - err = bpf_prog_test_run_xattr(&test_attr); - if (CHECK(err || test_attr.retval, "test_run", - "err %d errno %d retval %d duration %d\n", - err, errno, test_attr.retval, duration)) + err = bpf_prog_test_run_opts(prog_fd, &test_attr); + if (!ASSERT_OK(err, "test_run")) return -1; + + if (!ASSERT_OK(test_attr.retval, "test_run retval")) + return -1; + return 0; } diff --git a/tools/testing/selftests/bpf/prog_tests/test_skb_pkt_end.c b/tools/testing/selftests/bpf/prog_tests/test_skb_pkt_end.c index cf1215531920..ae93411fd582 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_skb_pkt_end.c +++ b/tools/testing/selftests/bpf/prog_tests/test_skb_pkt_end.c @@ -6,15 +6,18 @@ static int sanity_run(struct bpf_program *prog) { - __u32 duration, retval; int err, prog_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); prog_fd = bpf_program__fd(prog); - err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, &retval, &duration); - if (CHECK(err || retval != 123, "test_run", - "err %d errno %d retval %d duration %d\n", - err, errno, retval, duration)) + err = bpf_prog_test_run_opts(prog_fd, &topts); + if (!ASSERT_OK(err, "test_run")) + return -1; + if (!ASSERT_EQ(topts.retval, 123, "test_run retval")) return -1; return 0; } diff --git a/tools/testing/selftests/bpf/prog_tests/timer.c b/tools/testing/selftests/bpf/prog_tests/timer.c index 0f4e49e622cd..7eb049214859 100644 --- a/tools/testing/selftests/bpf/prog_tests/timer.c +++ b/tools/testing/selftests/bpf/prog_tests/timer.c @@ -6,7 +6,7 @@ static int timer(struct timer *timer_skel) { int err, prog_fd; - __u32 duration = 0, retval; + LIBBPF_OPTS(bpf_test_run_opts, topts); err = timer__attach(timer_skel); if (!ASSERT_OK(err, "timer_attach")) @@ -16,10 +16,9 @@ static int timer(struct timer *timer_skel) ASSERT_EQ(timer_skel->data->callback2_check, 52, "callback2_check1"); prog_fd = bpf_program__fd(timer_skel->progs.test1); - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "test_run"); - ASSERT_EQ(retval, 0, "test_run"); + ASSERT_EQ(topts.retval, 0, "test_run"); timer__detach(timer_skel); usleep(50); /* 10 usecs should be enough, but give it extra */ diff --git a/tools/testing/selftests/bpf/prog_tests/timer_mim.c b/tools/testing/selftests/bpf/prog_tests/timer_mim.c index 949a0617869d..2ee5f5ae11d4 100644 --- a/tools/testing/selftests/bpf/prog_tests/timer_mim.c +++ b/tools/testing/selftests/bpf/prog_tests/timer_mim.c @@ -6,19 +6,18 @@ static int timer_mim(struct timer_mim *timer_skel) { - __u32 duration = 0, retval; __u64 cnt1, cnt2; int err, prog_fd, key1 = 1; + LIBBPF_OPTS(bpf_test_run_opts, topts); err = timer_mim__attach(timer_skel); if (!ASSERT_OK(err, "timer_attach")) return err; prog_fd = bpf_program__fd(timer_skel->progs.test1); - err = bpf_prog_test_run(prog_fd, 1, NULL, 0, - NULL, NULL, &retval, &duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "test_run"); - ASSERT_EQ(retval, 0, "test_run"); + ASSERT_EQ(topts.retval, 0, "test_run"); timer_mim__detach(timer_skel); /* check that timer_cb[12] are incrementing 'cnt' */ diff --git a/tools/testing/selftests/bpf/prog_tests/trace_ext.c b/tools/testing/selftests/bpf/prog_tests/trace_ext.c index 924441d4362d..aabdff7bea3e 100644 --- a/tools/testing/selftests/bpf/prog_tests/trace_ext.c +++ b/tools/testing/selftests/bpf/prog_tests/trace_ext.c @@ -23,8 +23,12 @@ void test_trace_ext(void) int err, pkt_fd, ext_fd; struct bpf_program *prog; char buf[100]; - __u32 retval; __u64 len; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); /* open/load/attach test_pkt_md_access */ skel_pkt = test_pkt_md_access__open_and_load(); @@ -77,32 +81,32 @@ void test_trace_ext(void) /* load/attach tracing */ err = test_trace_ext_tracing__load(skel_trace); - if (CHECK(err, "setup", "tracing/test_pkt_md_access_new load failed\n")) { + if (!ASSERT_OK(err, "tracing/test_pkt_md_access_new load")) { libbpf_strerror(err, buf, sizeof(buf)); fprintf(stderr, "%s\n", buf); goto cleanup; } err = test_trace_ext_tracing__attach(skel_trace); - if (CHECK(err, "setup", "tracing/test_pkt_md_access_new attach failed: %d\n", err)) + if (!ASSERT_OK(err, "tracing/test_pkt_md_access_new attach")) goto cleanup; /* trigger the test */ - err = bpf_prog_test_run(pkt_fd, 1, &pkt_v4, sizeof(pkt_v4), - NULL, NULL, &retval, &duration); - CHECK(err || retval, "run", "err %d errno %d retval %d\n", err, errno, retval); + err = bpf_prog_test_run_opts(pkt_fd, &topts); + ASSERT_OK(err, "test_run_opts err"); + ASSERT_OK(topts.retval, "test_run_opts retval"); bss_ext = skel_ext->bss; bss_trace = skel_trace->bss; len = bss_ext->ext_called; - CHECK(bss_ext->ext_called == 0, - "check", "failed to trigger freplace/test_pkt_md_access\n"); - CHECK(bss_trace->fentry_called != len, - "check", "failed to trigger fentry/test_pkt_md_access_new\n"); - CHECK(bss_trace->fexit_called != len, - "check", "failed to trigger fexit/test_pkt_md_access_new\n"); + ASSERT_NEQ(bss_ext->ext_called, 0, + "failed to trigger freplace/test_pkt_md_access"); + ASSERT_EQ(bss_trace->fentry_called, len, + "failed to trigger fentry/test_pkt_md_access_new"); + ASSERT_EQ(bss_trace->fexit_called, len, + "failed to trigger fexit/test_pkt_md_access_new"); cleanup: test_trace_ext_tracing__destroy(skel_trace); diff --git a/tools/testing/selftests/bpf/prog_tests/xdp.c b/tools/testing/selftests/bpf/prog_tests/xdp.c index ac65456b7ab8..ec21c53cb1da 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp.c @@ -13,8 +13,14 @@ void test_xdp(void) char buf[128]; struct ipv6hdr iph6; struct iphdr iph; - __u32 duration, retval, size; int err, prog_fd, map_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .data_out = buf, + .data_size_out = sizeof(buf), + .repeat = 1, + ); err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); if (CHECK_FAIL(err)) @@ -26,21 +32,23 @@ void test_xdp(void) bpf_map_update_elem(map_fd, &key4, &value4, 0); bpf_map_update_elem(map_fd, &key6, &value6, 0); - err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - buf, &size, &retval, &duration); + err = bpf_prog_test_run_opts(prog_fd, &topts); memcpy(&iph, buf + sizeof(struct ethhdr), sizeof(iph)); - CHECK(err || retval != XDP_TX || size != 74 || - iph.protocol != IPPROTO_IPIP, "ipv4", - "err %d errno %d retval %d size %d\n", - err, errno, retval, size); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.retval, XDP_TX, "ipv4 test_run retval"); + ASSERT_EQ(topts.data_size_out, 74, "ipv4 test_run data_size_out"); + ASSERT_EQ(iph.protocol, IPPROTO_IPIP, "ipv4 test_run iph.protocol"); - err = bpf_prog_test_run(prog_fd, 1, &pkt_v6, sizeof(pkt_v6), - buf, &size, &retval, &duration); + topts.data_in = &pkt_v6; + topts.data_size_in = sizeof(pkt_v6); + topts.data_size_out = sizeof(buf); + + err = bpf_prog_test_run_opts(prog_fd, &topts); memcpy(&iph6, buf + sizeof(struct ethhdr), sizeof(iph6)); - CHECK(err || retval != XDP_TX || size != 114 || - iph6.nexthdr != IPPROTO_IPV6, "ipv6", - "err %d errno %d retval %d size %d\n", - err, errno, retval, size); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.retval, XDP_TX, "ipv6 test_run retval"); + ASSERT_EQ(topts.data_size_out, 114, "ipv6 test_run data_size_out"); + ASSERT_EQ(iph6.nexthdr, IPPROTO_IPV6, "ipv6 test_run iph6.nexthdr"); out: bpf_object__close(obj); } diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_frags.c b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_frags.c new file mode 100644 index 000000000000..2f033da4cd45 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_frags.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +static void test_xdp_update_frags(void) +{ + const char *file = "./test_xdp_update_frags.o"; + int err, prog_fd, max_skb_frags, buf_size, num; + struct bpf_program *prog; + struct bpf_object *obj; + __u32 *offset; + __u8 *buf; + FILE *f; + LIBBPF_OPTS(bpf_test_run_opts, topts); + + obj = bpf_object__open(file); + if (libbpf_get_error(obj)) + return; + + prog = bpf_object__next_program(obj, NULL); + if (bpf_object__load(obj)) + return; + + prog_fd = bpf_program__fd(prog); + + buf = malloc(128); + if (!ASSERT_OK_PTR(buf, "alloc buf 128b")) + goto out; + + memset(buf, 0, 128); + offset = (__u32 *)buf; + *offset = 16; + buf[*offset] = 0xaa; /* marker at offset 16 (head) */ + buf[*offset + 15] = 0xaa; /* marker at offset 31 (head) */ + + topts.data_in = buf; + topts.data_out = buf; + topts.data_size_in = 128; + topts.data_size_out = 128; + + err = bpf_prog_test_run_opts(prog_fd, &topts); + + /* test_xdp_update_frags: buf[16,31]: 0xaa -> 0xbb */ + ASSERT_OK(err, "xdp_update_frag"); + ASSERT_EQ(topts.retval, XDP_PASS, "xdp_update_frag retval"); + ASSERT_EQ(buf[16], 0xbb, "xdp_update_frag buf[16]"); + ASSERT_EQ(buf[31], 0xbb, "xdp_update_frag buf[31]"); + + free(buf); + + buf = malloc(9000); + if (!ASSERT_OK_PTR(buf, "alloc buf 9Kb")) + goto out; + + memset(buf, 0, 9000); + offset = (__u32 *)buf; + *offset = 5000; + buf[*offset] = 0xaa; /* marker at offset 5000 (frag0) */ + buf[*offset + 15] = 0xaa; /* marker at offset 5015 (frag0) */ + + topts.data_in = buf; + topts.data_out = buf; + topts.data_size_in = 9000; + topts.data_size_out = 9000; + + err = bpf_prog_test_run_opts(prog_fd, &topts); + + /* test_xdp_update_frags: buf[5000,5015]: 0xaa -> 0xbb */ + ASSERT_OK(err, "xdp_update_frag"); + ASSERT_EQ(topts.retval, XDP_PASS, "xdp_update_frag retval"); + ASSERT_EQ(buf[5000], 0xbb, "xdp_update_frag buf[5000]"); + ASSERT_EQ(buf[5015], 0xbb, "xdp_update_frag buf[5015]"); + + memset(buf, 0, 9000); + offset = (__u32 *)buf; + *offset = 3510; + buf[*offset] = 0xaa; /* marker at offset 3510 (head) */ + buf[*offset + 15] = 0xaa; /* marker at offset 3525 (frag0) */ + + err = bpf_prog_test_run_opts(prog_fd, &topts); + + /* test_xdp_update_frags: buf[3510,3525]: 0xaa -> 0xbb */ + ASSERT_OK(err, "xdp_update_frag"); + ASSERT_EQ(topts.retval, XDP_PASS, "xdp_update_frag retval"); + ASSERT_EQ(buf[3510], 0xbb, "xdp_update_frag buf[3510]"); + ASSERT_EQ(buf[3525], 0xbb, "xdp_update_frag buf[3525]"); + + memset(buf, 0, 9000); + offset = (__u32 *)buf; + *offset = 7606; + buf[*offset] = 0xaa; /* marker at offset 7606 (frag0) */ + buf[*offset + 15] = 0xaa; /* marker at offset 7621 (frag1) */ + + err = bpf_prog_test_run_opts(prog_fd, &topts); + + /* test_xdp_update_frags: buf[7606,7621]: 0xaa -> 0xbb */ + ASSERT_OK(err, "xdp_update_frag"); + ASSERT_EQ(topts.retval, XDP_PASS, "xdp_update_frag retval"); + ASSERT_EQ(buf[7606], 0xbb, "xdp_update_frag buf[7606]"); + ASSERT_EQ(buf[7621], 0xbb, "xdp_update_frag buf[7621]"); + + free(buf); + + /* test_xdp_update_frags: unsupported buffer size */ + f = fopen("/proc/sys/net/core/max_skb_frags", "r"); + if (!ASSERT_OK_PTR(f, "max_skb_frag file pointer")) + goto out; + + num = fscanf(f, "%d", &max_skb_frags); + fclose(f); + + if (!ASSERT_EQ(num, 1, "max_skb_frags read failed")) + goto out; + + /* xdp_buff linear area size is always set to 4096 in the + * bpf_prog_test_run_xdp routine. + */ + buf_size = 4096 + (max_skb_frags + 1) * sysconf(_SC_PAGE_SIZE); + buf = malloc(buf_size); + if (!ASSERT_OK_PTR(buf, "alloc buf")) + goto out; + + memset(buf, 0, buf_size); + offset = (__u32 *)buf; + *offset = 16; + buf[*offset] = 0xaa; + buf[*offset + 15] = 0xaa; + + topts.data_in = buf; + topts.data_out = buf; + topts.data_size_in = buf_size; + topts.data_size_out = buf_size; + + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_EQ(err, -ENOMEM, + "unsupported buf size, possible non-default /proc/sys/net/core/max_skb_flags?"); + free(buf); +out: + bpf_object__close(obj); +} + +void test_xdp_adjust_frags(void) +{ + if (test__start_subtest("xdp_adjust_frags")) + test_xdp_update_frags(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c index 3f5a17c38be5..21ceac24e174 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c @@ -5,28 +5,35 @@ static void test_xdp_adjust_tail_shrink(void) { const char *file = "./test_xdp_adjust_tail_shrink.o"; - __u32 duration, retval, size, expect_sz; + __u32 expect_sz; struct bpf_object *obj; int err, prog_fd; char buf[128]; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .data_out = buf, + .data_size_out = sizeof(buf), + .repeat = 1, + ); err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); - if (CHECK_FAIL(err)) + if (ASSERT_OK(err, "test_xdp_adjust_tail_shrink")) return; - err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - buf, &size, &retval, &duration); - - CHECK(err || retval != XDP_DROP, - "ipv4", "err %d errno %d retval %d size %d\n", - err, errno, retval, size); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "ipv4"); + ASSERT_EQ(topts.retval, XDP_DROP, "ipv4 retval"); expect_sz = sizeof(pkt_v6) - 20; /* Test shrink with 20 bytes */ - err = bpf_prog_test_run(prog_fd, 1, &pkt_v6, sizeof(pkt_v6), - buf, &size, &retval, &duration); - CHECK(err || retval != XDP_TX || size != expect_sz, - "ipv6", "err %d errno %d retval %d size %d expect-size %d\n", - err, errno, retval, size, expect_sz); + topts.data_in = &pkt_v6; + topts.data_size_in = sizeof(pkt_v6); + topts.data_size_out = sizeof(buf); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "ipv6"); + ASSERT_EQ(topts.retval, XDP_TX, "ipv6 retval"); + ASSERT_EQ(topts.data_size_out, expect_sz, "ipv6 size"); + bpf_object__close(obj); } @@ -35,25 +42,31 @@ static void test_xdp_adjust_tail_grow(void) const char *file = "./test_xdp_adjust_tail_grow.o"; struct bpf_object *obj; char buf[4096]; /* avoid segfault: large buf to hold grow results */ - __u32 duration, retval, size, expect_sz; + __u32 expect_sz; int err, prog_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .data_out = buf, + .data_size_out = sizeof(buf), + .repeat = 1, + ); err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); - if (CHECK_FAIL(err)) + if (ASSERT_OK(err, "test_xdp_adjust_tail_grow")) return; - err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), - buf, &size, &retval, &duration); - CHECK(err || retval != XDP_DROP, - "ipv4", "err %d errno %d retval %d size %d\n", - err, errno, retval, size); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "ipv4"); + ASSERT_EQ(topts.retval, XDP_DROP, "ipv4 retval"); expect_sz = sizeof(pkt_v6) + 40; /* Test grow with 40 bytes */ - err = bpf_prog_test_run(prog_fd, 1, &pkt_v6, sizeof(pkt_v6) /* 74 */, - buf, &size, &retval, &duration); - CHECK(err || retval != XDP_TX || size != expect_sz, - "ipv6", "err %d errno %d retval %d size %d expect-size %d\n", - err, errno, retval, size, expect_sz); + topts.data_in = &pkt_v6; + topts.data_size_in = sizeof(pkt_v6); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "ipv6"); + ASSERT_EQ(topts.retval, XDP_TX, "ipv6 retval"); + ASSERT_EQ(topts.data_size_out, expect_sz, "ipv6 size"); bpf_object__close(obj); } @@ -65,18 +78,18 @@ static void test_xdp_adjust_tail_grow2(void) int tailroom = 320; /* SKB_DATA_ALIGN(sizeof(struct skb_shared_info))*/; struct bpf_object *obj; int err, cnt, i; - int max_grow; + int max_grow, prog_fd; - struct bpf_prog_test_run_attr tattr = { + LIBBPF_OPTS(bpf_test_run_opts, tattr, .repeat = 1, .data_in = &buf, .data_out = &buf, .data_size_in = 0, /* Per test */ .data_size_out = 0, /* Per test */ - }; + ); - err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &tattr.prog_fd); - if (CHECK_ATTR(err, "load", "err %d errno %d\n", err, errno)) + err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); + if (ASSERT_OK(err, "test_xdp_adjust_tail_grow")) return; /* Test case-64 */ @@ -84,52 +97,174 @@ static void test_xdp_adjust_tail_grow2(void) tattr.data_size_in = 64; /* Determine test case via pkt size */ tattr.data_size_out = 128; /* Limit copy_size */ /* Kernel side alloc packet memory area that is zero init */ - err = bpf_prog_test_run_xattr(&tattr); + err = bpf_prog_test_run_opts(prog_fd, &tattr); - CHECK_ATTR(errno != ENOSPC /* Due limit copy_size in bpf_test_finish */ - || tattr.retval != XDP_TX - || tattr.data_size_out != 192, /* Expected grow size */ - "case-64", - "err %d errno %d retval %d size %d\n", - err, errno, tattr.retval, tattr.data_size_out); + ASSERT_EQ(errno, ENOSPC, "case-64 errno"); /* Due limit copy_size in bpf_test_finish */ + ASSERT_EQ(tattr.retval, XDP_TX, "case-64 retval"); + ASSERT_EQ(tattr.data_size_out, 192, "case-64 data_size_out"); /* Expected grow size */ /* Extra checks for data contents */ - CHECK_ATTR(tattr.data_size_out != 192 - || buf[0] != 1 || buf[63] != 1 /* 0-63 memset to 1 */ - || buf[64] != 0 || buf[127] != 0 /* 64-127 memset to 0 */ - || buf[128] != 1 || buf[191] != 1, /*128-191 memset to 1 */ - "case-64-data", - "err %d errno %d retval %d size %d\n", - err, errno, tattr.retval, tattr.data_size_out); + ASSERT_EQ(buf[0], 1, "case-64-data buf[0]"); /* 0-63 memset to 1 */ + ASSERT_EQ(buf[63], 1, "case-64-data buf[63]"); + ASSERT_EQ(buf[64], 0, "case-64-data buf[64]"); /* 64-127 memset to 0 */ + ASSERT_EQ(buf[127], 0, "case-64-data buf[127]"); + ASSERT_EQ(buf[128], 1, "case-64-data buf[128]"); /* 128-191 memset to 1 */ + ASSERT_EQ(buf[191], 1, "case-64-data buf[191]"); /* Test case-128 */ memset(buf, 2, sizeof(buf)); tattr.data_size_in = 128; /* Determine test case via pkt size */ tattr.data_size_out = sizeof(buf); /* Copy everything */ - err = bpf_prog_test_run_xattr(&tattr); + err = bpf_prog_test_run_opts(prog_fd, &tattr); max_grow = 4096 - XDP_PACKET_HEADROOM - tailroom; /* 3520 */ - CHECK_ATTR(err - || tattr.retval != XDP_TX - || tattr.data_size_out != max_grow,/* Expect max grow size */ - "case-128", - "err %d errno %d retval %d size %d expect-size %d\n", - err, errno, tattr.retval, tattr.data_size_out, max_grow); + ASSERT_OK(err, "case-128"); + ASSERT_EQ(tattr.retval, XDP_TX, "case-128 retval"); + ASSERT_EQ(tattr.data_size_out, max_grow, "case-128 data_size_out"); /* Expect max grow */ /* Extra checks for data content: Count grow size, will contain zeros */ for (i = 0, cnt = 0; i < sizeof(buf); i++) { if (buf[i] == 0) cnt++; } - CHECK_ATTR((cnt != (max_grow - tattr.data_size_in)) /* Grow increase */ - || tattr.data_size_out != max_grow, /* Total grow size */ - "case-128-data", - "err %d errno %d retval %d size %d grow-size %d\n", - err, errno, tattr.retval, tattr.data_size_out, cnt); + ASSERT_EQ(cnt, max_grow - tattr.data_size_in, "case-128-data cnt"); /* Grow increase */ + ASSERT_EQ(tattr.data_size_out, max_grow, "case-128-data data_size_out"); /* Total grow */ bpf_object__close(obj); } +static void test_xdp_adjust_frags_tail_shrink(void) +{ + const char *file = "./test_xdp_adjust_tail_shrink.o"; + __u32 exp_size; + struct bpf_program *prog; + struct bpf_object *obj; + int err, prog_fd; + __u8 *buf; + LIBBPF_OPTS(bpf_test_run_opts, topts); + + /* For the individual test cases, the first byte in the packet + * indicates which test will be run. + */ + obj = bpf_object__open(file); + if (libbpf_get_error(obj)) + return; + + prog = bpf_object__next_program(obj, NULL); + if (bpf_object__load(obj)) + return; + + prog_fd = bpf_program__fd(prog); + + buf = malloc(9000); + if (!ASSERT_OK_PTR(buf, "alloc buf 9Kb")) + goto out; + + memset(buf, 0, 9000); + + /* Test case removing 10 bytes from last frag, NOT freeing it */ + exp_size = 8990; /* 9000 - 10 */ + topts.data_in = buf; + topts.data_out = buf; + topts.data_size_in = 9000; + topts.data_size_out = 9000; + err = bpf_prog_test_run_opts(prog_fd, &topts); + + ASSERT_OK(err, "9Kb-10b"); + ASSERT_EQ(topts.retval, XDP_TX, "9Kb-10b retval"); + ASSERT_EQ(topts.data_size_out, exp_size, "9Kb-10b size"); + + /* Test case removing one of two pages, assuming 4K pages */ + buf[0] = 1; + exp_size = 4900; /* 9000 - 4100 */ + + topts.data_size_out = 9000; /* reset from previous invocation */ + err = bpf_prog_test_run_opts(prog_fd, &topts); + + ASSERT_OK(err, "9Kb-4Kb"); + ASSERT_EQ(topts.retval, XDP_TX, "9Kb-4Kb retval"); + ASSERT_EQ(topts.data_size_out, exp_size, "9Kb-4Kb size"); + + /* Test case removing two pages resulting in a linear xdp_buff */ + buf[0] = 2; + exp_size = 800; /* 9000 - 8200 */ + topts.data_size_out = 9000; /* reset from previous invocation */ + err = bpf_prog_test_run_opts(prog_fd, &topts); + + ASSERT_OK(err, "9Kb-9Kb"); + ASSERT_EQ(topts.retval, XDP_TX, "9Kb-9Kb retval"); + ASSERT_EQ(topts.data_size_out, exp_size, "9Kb-9Kb size"); + + free(buf); +out: + bpf_object__close(obj); +} + +static void test_xdp_adjust_frags_tail_grow(void) +{ + const char *file = "./test_xdp_adjust_tail_grow.o"; + __u32 exp_size; + struct bpf_program *prog; + struct bpf_object *obj; + int err, i, prog_fd; + __u8 *buf; + LIBBPF_OPTS(bpf_test_run_opts, topts); + + obj = bpf_object__open(file); + if (libbpf_get_error(obj)) + return; + + prog = bpf_object__next_program(obj, NULL); + if (bpf_object__load(obj)) + return; + + prog_fd = bpf_program__fd(prog); + + buf = malloc(16384); + if (!ASSERT_OK_PTR(buf, "alloc buf 16Kb")) + goto out; + + /* Test case add 10 bytes to last frag */ + memset(buf, 1, 16384); + exp_size = 9000 + 10; + + topts.data_in = buf; + topts.data_out = buf; + topts.data_size_in = 9000; + topts.data_size_out = 16384; + err = bpf_prog_test_run_opts(prog_fd, &topts); + + ASSERT_OK(err, "9Kb+10b"); + ASSERT_EQ(topts.retval, XDP_TX, "9Kb+10b retval"); + ASSERT_EQ(topts.data_size_out, exp_size, "9Kb+10b size"); + + for (i = 0; i < 9000; i++) + ASSERT_EQ(buf[i], 1, "9Kb+10b-old"); + + for (i = 9000; i < 9010; i++) + ASSERT_EQ(buf[i], 0, "9Kb+10b-new"); + + for (i = 9010; i < 16384; i++) + ASSERT_EQ(buf[i], 1, "9Kb+10b-untouched"); + + /* Test a too large grow */ + memset(buf, 1, 16384); + exp_size = 9001; + + topts.data_in = topts.data_out = buf; + topts.data_size_in = 9001; + topts.data_size_out = 16384; + err = bpf_prog_test_run_opts(prog_fd, &topts); + + ASSERT_OK(err, "9Kb+10b"); + ASSERT_EQ(topts.retval, XDP_DROP, "9Kb+10b retval"); + ASSERT_EQ(topts.data_size_out, exp_size, "9Kb+10b size"); + + free(buf); +out: + bpf_object__close(obj); +} + void test_xdp_adjust_tail(void) { if (test__start_subtest("xdp_adjust_tail_shrink")) @@ -138,4 +273,8 @@ void test_xdp_adjust_tail(void) test_xdp_adjust_tail_grow(); if (test__start_subtest("xdp_adjust_tail_grow2")) test_xdp_adjust_tail_grow2(); + if (test__start_subtest("xdp_adjust_frags_tail_shrink")) + test_xdp_adjust_frags_tail_shrink(); + if (test__start_subtest("xdp_adjust_frags_tail_grow")) + test_xdp_adjust_frags_tail_grow(); } diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_attach.c b/tools/testing/selftests/bpf/prog_tests/xdp_attach.c index c6fa390e3aa1..62aa3edda5e6 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_attach.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_attach.c @@ -11,8 +11,7 @@ void serial_test_xdp_attach(void) const char *file = "./test_xdp.o"; struct bpf_prog_info info = {}; int err, fd1, fd2, fd3; - DECLARE_LIBBPF_OPTS(bpf_xdp_set_link_opts, opts, - .old_fd = -1); + LIBBPF_OPTS(bpf_xdp_attach_opts, opts); len = sizeof(info); @@ -38,49 +37,47 @@ void serial_test_xdp_attach(void) if (CHECK_FAIL(err)) goto out_2; - err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd1, XDP_FLAGS_REPLACE, - &opts); + err = bpf_xdp_attach(IFINDEX_LO, fd1, XDP_FLAGS_REPLACE, &opts); if (CHECK(err, "load_ok", "initial load failed")) goto out_close; - err = bpf_get_link_xdp_id(IFINDEX_LO, &id0, 0); + err = bpf_xdp_query_id(IFINDEX_LO, 0, &id0); if (CHECK(err || id0 != id1, "id1_check", "loaded prog id %u != id1 %u, err %d", id0, id1, err)) goto out_close; - err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd2, XDP_FLAGS_REPLACE, - &opts); + err = bpf_xdp_attach(IFINDEX_LO, fd2, XDP_FLAGS_REPLACE, &opts); if (CHECK(!err, "load_fail", "load with expected id didn't fail")) goto out; - opts.old_fd = fd1; - err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd2, 0, &opts); + opts.old_prog_fd = fd1; + err = bpf_xdp_attach(IFINDEX_LO, fd2, 0, &opts); if (CHECK(err, "replace_ok", "replace valid old_fd failed")) goto out; - err = bpf_get_link_xdp_id(IFINDEX_LO, &id0, 0); + err = bpf_xdp_query_id(IFINDEX_LO, 0, &id0); if (CHECK(err || id0 != id2, "id2_check", "loaded prog id %u != id2 %u, err %d", id0, id2, err)) goto out_close; - err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd3, 0, &opts); + err = bpf_xdp_attach(IFINDEX_LO, fd3, 0, &opts); if (CHECK(!err, "replace_fail", "replace invalid old_fd didn't fail")) goto out; - err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, 0, &opts); + err = bpf_xdp_detach(IFINDEX_LO, 0, &opts); if (CHECK(!err, "remove_fail", "remove invalid old_fd didn't fail")) goto out; - opts.old_fd = fd2; - err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, 0, &opts); + opts.old_prog_fd = fd2; + err = bpf_xdp_detach(IFINDEX_LO, 0, &opts); if (CHECK(err, "remove_ok", "remove valid old_fd failed")) goto out; - err = bpf_get_link_xdp_id(IFINDEX_LO, &id0, 0); + err = bpf_xdp_query_id(IFINDEX_LO, 0, &id0); if (CHECK(err || id0 != 0, "unload_check", "loaded prog id %u != 0, err %d", id0, err)) goto out_close; out: - bpf_set_link_xdp_fd(IFINDEX_LO, -1, 0); + bpf_xdp_detach(IFINDEX_LO, 0, NULL); out_close: bpf_object__close(obj3); out_2: diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c b/tools/testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c index c98a897ad692..76967d8ace9c 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c @@ -10,40 +10,101 @@ struct meta { int pkt_len; }; +struct test_ctx_s { + bool passed; + int pkt_size; +}; + +struct test_ctx_s test_ctx; + static void on_sample(void *ctx, int cpu, void *data, __u32 size) { - int duration = 0; struct meta *meta = (struct meta *)data; struct ipv4_packet *trace_pkt_v4 = data + sizeof(*meta); + unsigned char *raw_pkt = data + sizeof(*meta); + struct test_ctx_s *tst_ctx = ctx; - if (CHECK(size < sizeof(pkt_v4) + sizeof(*meta), - "check_size", "size %u < %zu\n", - size, sizeof(pkt_v4) + sizeof(*meta))) + ASSERT_GE(size, sizeof(pkt_v4) + sizeof(*meta), "check_size"); + ASSERT_EQ(meta->ifindex, if_nametoindex("lo"), "check_meta_ifindex"); + ASSERT_EQ(meta->pkt_len, tst_ctx->pkt_size, "check_meta_pkt_len"); + ASSERT_EQ(memcmp(trace_pkt_v4, &pkt_v4, sizeof(pkt_v4)), 0, + "check_packet_content"); + + if (meta->pkt_len > sizeof(pkt_v4)) { + for (int i = 0; i < meta->pkt_len - sizeof(pkt_v4); i++) + ASSERT_EQ(raw_pkt[i + sizeof(pkt_v4)], (unsigned char)i, + "check_packet_content"); + } + + tst_ctx->passed = true; +} + +#define BUF_SZ 9000 + +static void run_xdp_bpf2bpf_pkt_size(int pkt_fd, struct perf_buffer *pb, + struct test_xdp_bpf2bpf *ftrace_skel, + int pkt_size) +{ + __u8 *buf, *buf_in; + int err; + LIBBPF_OPTS(bpf_test_run_opts, topts); + + if (!ASSERT_LE(pkt_size, BUF_SZ, "pkt_size") || + !ASSERT_GE(pkt_size, sizeof(pkt_v4), "pkt_size")) return; - if (CHECK(meta->ifindex != if_nametoindex("lo"), "check_meta_ifindex", - "meta->ifindex = %d\n", meta->ifindex)) + buf_in = malloc(BUF_SZ); + if (!ASSERT_OK_PTR(buf_in, "buf_in malloc()")) return; - if (CHECK(meta->pkt_len != sizeof(pkt_v4), "check_meta_pkt_len", - "meta->pkt_len = %zd\n", sizeof(pkt_v4))) + buf = malloc(BUF_SZ); + if (!ASSERT_OK_PTR(buf, "buf malloc()")) { + free(buf_in); return; + } - if (CHECK(memcmp(trace_pkt_v4, &pkt_v4, sizeof(pkt_v4)), - "check_packet_content", "content not the same\n")) - return; + test_ctx.passed = false; + test_ctx.pkt_size = pkt_size; - *(bool *)ctx = true; + memcpy(buf_in, &pkt_v4, sizeof(pkt_v4)); + if (pkt_size > sizeof(pkt_v4)) { + for (int i = 0; i < (pkt_size - sizeof(pkt_v4)); i++) + buf_in[i + sizeof(pkt_v4)] = i; + } + + /* Run test program */ + topts.data_in = buf_in; + topts.data_size_in = pkt_size; + topts.data_out = buf; + topts.data_size_out = BUF_SZ; + + err = bpf_prog_test_run_opts(pkt_fd, &topts); + + ASSERT_OK(err, "ipv4"); + ASSERT_EQ(topts.retval, XDP_PASS, "ipv4 retval"); + ASSERT_EQ(topts.data_size_out, pkt_size, "ipv4 size"); + + /* Make sure bpf_xdp_output() was triggered and it sent the expected + * data to the perf ring buffer. + */ + err = perf_buffer__poll(pb, 100); + + ASSERT_GE(err, 0, "perf_buffer__poll"); + ASSERT_TRUE(test_ctx.passed, "test passed"); + /* Verify test results */ + ASSERT_EQ(ftrace_skel->bss->test_result_fentry, if_nametoindex("lo"), + "fentry result"); + ASSERT_EQ(ftrace_skel->bss->test_result_fexit, XDP_PASS, "fexit result"); + + free(buf); + free(buf_in); } void test_xdp_bpf2bpf(void) { - __u32 duration = 0, retval, size; - char buf[128]; int err, pkt_fd, map_fd; - bool passed = false; - struct iphdr iph; - struct iptnl_info value4 = {.family = AF_INET}; + int pkt_sizes[] = {sizeof(pkt_v4), 1024, 4100, 8200}; + struct iptnl_info value4 = {.family = AF_INET6}; struct test_xdp *pkt_skel = NULL; struct test_xdp_bpf2bpf *ftrace_skel = NULL; struct vip key4 = {.protocol = 6, .family = AF_INET}; @@ -52,7 +113,7 @@ void test_xdp_bpf2bpf(void) /* Load XDP program to introspect */ pkt_skel = test_xdp__open_and_load(); - if (CHECK(!pkt_skel, "pkt_skel_load", "test_xdp skeleton failed\n")) + if (!ASSERT_OK_PTR(pkt_skel, "test_xdp__open_and_load")) return; pkt_fd = bpf_program__fd(pkt_skel->progs._xdp_tx_iptunnel); @@ -62,7 +123,7 @@ void test_xdp_bpf2bpf(void) /* Load trace program */ ftrace_skel = test_xdp_bpf2bpf__open(); - if (CHECK(!ftrace_skel, "__open", "ftrace skeleton failed\n")) + if (!ASSERT_OK_PTR(ftrace_skel, "test_xdp_bpf2bpf__open")) goto out; /* Demonstrate the bpf_program__set_attach_target() API rather than @@ -77,50 +138,24 @@ void test_xdp_bpf2bpf(void) bpf_program__set_attach_target(prog, pkt_fd, "_xdp_tx_iptunnel"); err = test_xdp_bpf2bpf__load(ftrace_skel); - if (CHECK(err, "__load", "ftrace skeleton failed\n")) + if (!ASSERT_OK(err, "test_xdp_bpf2bpf__load")) goto out; err = test_xdp_bpf2bpf__attach(ftrace_skel); - if (CHECK(err, "ftrace_attach", "ftrace attach failed: %d\n", err)) + if (!ASSERT_OK(err, "test_xdp_bpf2bpf__attach")) goto out; /* Set up perf buffer */ - pb = perf_buffer__new(bpf_map__fd(ftrace_skel->maps.perf_buf_map), 1, - on_sample, NULL, &passed, NULL); + pb = perf_buffer__new(bpf_map__fd(ftrace_skel->maps.perf_buf_map), 8, + on_sample, NULL, &test_ctx, NULL); if (!ASSERT_OK_PTR(pb, "perf_buf__new")) goto out; - /* Run test program */ - err = bpf_prog_test_run(pkt_fd, 1, &pkt_v4, sizeof(pkt_v4), - buf, &size, &retval, &duration); - memcpy(&iph, buf + sizeof(struct ethhdr), sizeof(iph)); - if (CHECK(err || retval != XDP_TX || size != 74 || - iph.protocol != IPPROTO_IPIP, "ipv4", - "err %d errno %d retval %d size %d\n", - err, errno, retval, size)) - goto out; - - /* Make sure bpf_xdp_output() was triggered and it sent the expected - * data to the perf ring buffer. - */ - err = perf_buffer__poll(pb, 100); - if (CHECK(err < 0, "perf_buffer__poll", "err %d\n", err)) - goto out; - - CHECK_FAIL(!passed); - - /* Verify test results */ - if (CHECK(ftrace_skel->bss->test_result_fentry != if_nametoindex("lo"), - "result", "fentry failed err %llu\n", - ftrace_skel->bss->test_result_fentry)) - goto out; - - CHECK(ftrace_skel->bss->test_result_fexit != XDP_TX, "result", - "fexit failed err %llu\n", ftrace_skel->bss->test_result_fexit); - + for (int i = 0; i < ARRAY_SIZE(pkt_sizes); i++) + run_xdp_bpf2bpf_pkt_size(pkt_fd, pb, ftrace_skel, + pkt_sizes[i]); out: - if (pb) - perf_buffer__free(pb); + perf_buffer__free(pb); test_xdp__destroy(pkt_skel); test_xdp_bpf2bpf__destroy(ftrace_skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_cpumap_attach.c b/tools/testing/selftests/bpf/prog_tests/xdp_cpumap_attach.c index fd812bd43600..f775a1613833 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_cpumap_attach.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_cpumap_attach.c @@ -3,11 +3,12 @@ #include #include +#include "test_xdp_with_cpumap_frags_helpers.skel.h" #include "test_xdp_with_cpumap_helpers.skel.h" #define IFINDEX_LO 1 -void serial_test_xdp_cpumap_attach(void) +static void test_xdp_with_cpumap_helpers(void) { struct test_xdp_with_cpumap_helpers *skel; struct bpf_prog_info info = {}; @@ -23,11 +24,11 @@ void serial_test_xdp_cpumap_attach(void) return; prog_fd = bpf_program__fd(skel->progs.xdp_redir_prog); - err = bpf_set_link_xdp_fd(IFINDEX_LO, prog_fd, XDP_FLAGS_SKB_MODE); + err = bpf_xdp_attach(IFINDEX_LO, prog_fd, XDP_FLAGS_SKB_MODE, NULL); if (!ASSERT_OK(err, "Generic attach of program with 8-byte CPUMAP")) goto out_close; - err = bpf_set_link_xdp_fd(IFINDEX_LO, -1, XDP_FLAGS_SKB_MODE); + err = bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_SKB_MODE, NULL); ASSERT_OK(err, "XDP program detach"); prog_fd = bpf_program__fd(skel->progs.xdp_dummy_cm); @@ -45,15 +46,76 @@ void serial_test_xdp_cpumap_attach(void) ASSERT_EQ(info.id, val.bpf_prog.id, "Match program id to cpumap entry prog_id"); /* can not attach BPF_XDP_CPUMAP program to a device */ - err = bpf_set_link_xdp_fd(IFINDEX_LO, prog_fd, XDP_FLAGS_SKB_MODE); + err = bpf_xdp_attach(IFINDEX_LO, prog_fd, XDP_FLAGS_SKB_MODE, NULL); if (!ASSERT_NEQ(err, 0, "Attach of BPF_XDP_CPUMAP program")) - bpf_set_link_xdp_fd(IFINDEX_LO, -1, XDP_FLAGS_SKB_MODE); + bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_SKB_MODE, NULL); val.qsize = 192; val.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_dummy_prog); err = bpf_map_update_elem(map_fd, &idx, &val, 0); ASSERT_NEQ(err, 0, "Add non-BPF_XDP_CPUMAP program to cpumap entry"); + /* Try to attach BPF_XDP program with frags to cpumap when we have + * already loaded a BPF_XDP program on the map + */ + idx = 1; + val.qsize = 192; + val.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_dummy_cm_frags); + err = bpf_map_update_elem(map_fd, &idx, &val, 0); + ASSERT_NEQ(err, 0, "Add BPF_XDP program with frags to cpumap entry"); + out_close: test_xdp_with_cpumap_helpers__destroy(skel); } + +static void test_xdp_with_cpumap_frags_helpers(void) +{ + struct test_xdp_with_cpumap_frags_helpers *skel; + struct bpf_prog_info info = {}; + __u32 len = sizeof(info); + struct bpf_cpumap_val val = { + .qsize = 192, + }; + int err, frags_prog_fd, map_fd; + __u32 idx = 0; + + skel = test_xdp_with_cpumap_frags_helpers__open_and_load(); + if (!ASSERT_OK_PTR(skel, "test_xdp_with_cpumap_helpers__open_and_load")) + return; + + frags_prog_fd = bpf_program__fd(skel->progs.xdp_dummy_cm_frags); + map_fd = bpf_map__fd(skel->maps.cpu_map); + err = bpf_obj_get_info_by_fd(frags_prog_fd, &info, &len); + if (!ASSERT_OK(err, "bpf_obj_get_info_by_fd")) + goto out_close; + + val.bpf_prog.fd = frags_prog_fd; + err = bpf_map_update_elem(map_fd, &idx, &val, 0); + ASSERT_OK(err, "Add program to cpumap entry"); + + err = bpf_map_lookup_elem(map_fd, &idx, &val); + ASSERT_OK(err, "Read cpumap entry"); + ASSERT_EQ(info.id, val.bpf_prog.id, + "Match program id to cpumap entry prog_id"); + + /* Try to attach BPF_XDP program to cpumap when we have + * already loaded a BPF_XDP program with frags on the map + */ + idx = 1; + val.qsize = 192; + val.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_dummy_cm); + err = bpf_map_update_elem(map_fd, &idx, &val, 0); + ASSERT_NEQ(err, 0, "Add BPF_XDP program to cpumap entry"); + +out_close: + test_xdp_with_cpumap_frags_helpers__destroy(skel); +} + +void serial_test_xdp_cpumap_attach(void) +{ + if (test__start_subtest("CPUMAP with programs in entries")) + test_xdp_with_cpumap_helpers(); + + if (test__start_subtest("CPUMAP with frags programs in entries")) + test_xdp_with_cpumap_frags_helpers(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c b/tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c index 3079d5568f8f..ead40016c324 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c @@ -4,6 +4,7 @@ #include #include "test_xdp_devmap_helpers.skel.h" +#include "test_xdp_with_devmap_frags_helpers.skel.h" #include "test_xdp_with_devmap_helpers.skel.h" #define IFINDEX_LO 1 @@ -25,11 +26,11 @@ static void test_xdp_with_devmap_helpers(void) return; dm_fd = bpf_program__fd(skel->progs.xdp_redir_prog); - err = bpf_set_link_xdp_fd(IFINDEX_LO, dm_fd, XDP_FLAGS_SKB_MODE); + err = bpf_xdp_attach(IFINDEX_LO, dm_fd, XDP_FLAGS_SKB_MODE, NULL); if (!ASSERT_OK(err, "Generic attach of program with 8-byte devmap")) goto out_close; - err = bpf_set_link_xdp_fd(IFINDEX_LO, -1, XDP_FLAGS_SKB_MODE); + err = bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_SKB_MODE, NULL); ASSERT_OK(err, "XDP program detach"); dm_fd = bpf_program__fd(skel->progs.xdp_dummy_dm); @@ -47,15 +48,24 @@ static void test_xdp_with_devmap_helpers(void) ASSERT_EQ(info.id, val.bpf_prog.id, "Match program id to devmap entry prog_id"); /* can not attach BPF_XDP_DEVMAP program to a device */ - err = bpf_set_link_xdp_fd(IFINDEX_LO, dm_fd, XDP_FLAGS_SKB_MODE); + err = bpf_xdp_attach(IFINDEX_LO, dm_fd, XDP_FLAGS_SKB_MODE, NULL); if (!ASSERT_NEQ(err, 0, "Attach of BPF_XDP_DEVMAP program")) - bpf_set_link_xdp_fd(IFINDEX_LO, -1, XDP_FLAGS_SKB_MODE); + bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_SKB_MODE, NULL); val.ifindex = 1; val.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_dummy_prog); err = bpf_map_update_elem(map_fd, &idx, &val, 0); ASSERT_NEQ(err, 0, "Add non-BPF_XDP_DEVMAP program to devmap entry"); + /* Try to attach BPF_XDP program with frags to devmap when we have + * already loaded a BPF_XDP program on the map + */ + idx = 1; + val.ifindex = 1; + val.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_dummy_dm_frags); + err = bpf_map_update_elem(map_fd, &idx, &val, 0); + ASSERT_NEQ(err, 0, "Add BPF_XDP program with frags to devmap entry"); + out_close: test_xdp_with_devmap_helpers__destroy(skel); } @@ -71,12 +81,57 @@ static void test_neg_xdp_devmap_helpers(void) } } +static void test_xdp_with_devmap_frags_helpers(void) +{ + struct test_xdp_with_devmap_frags_helpers *skel; + struct bpf_prog_info info = {}; + struct bpf_devmap_val val = { + .ifindex = IFINDEX_LO, + }; + __u32 len = sizeof(info); + int err, dm_fd_frags, map_fd; + __u32 idx = 0; + + skel = test_xdp_with_devmap_frags_helpers__open_and_load(); + if (!ASSERT_OK_PTR(skel, "test_xdp_with_devmap_helpers__open_and_load")) + return; + + dm_fd_frags = bpf_program__fd(skel->progs.xdp_dummy_dm_frags); + map_fd = bpf_map__fd(skel->maps.dm_ports); + err = bpf_obj_get_info_by_fd(dm_fd_frags, &info, &len); + if (!ASSERT_OK(err, "bpf_obj_get_info_by_fd")) + goto out_close; + + val.bpf_prog.fd = dm_fd_frags; + err = bpf_map_update_elem(map_fd, &idx, &val, 0); + ASSERT_OK(err, "Add frags program to devmap entry"); + + err = bpf_map_lookup_elem(map_fd, &idx, &val); + ASSERT_OK(err, "Read devmap entry"); + ASSERT_EQ(info.id, val.bpf_prog.id, + "Match program id to devmap entry prog_id"); + + /* Try to attach BPF_XDP program to devmap when we have + * already loaded a BPF_XDP program with frags on the map + */ + idx = 1; + val.ifindex = 1; + val.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_dummy_dm); + err = bpf_map_update_elem(map_fd, &idx, &val, 0); + ASSERT_NEQ(err, 0, "Add BPF_XDP program to devmap entry"); + +out_close: + test_xdp_with_devmap_frags_helpers__destroy(skel); +} void serial_test_xdp_devmap_attach(void) { if (test__start_subtest("DEVMAP with programs in entries")) test_xdp_with_devmap_helpers(); + if (test__start_subtest("DEVMAP with frags programs in entries")) + test_xdp_with_devmap_frags_helpers(); + if (test__start_subtest("Verifier check of DEVMAP programs")) test_neg_xdp_devmap_helpers(); } diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c new file mode 100644 index 000000000000..a50971c6cf4a --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "test_xdp_do_redirect.skel.h" + +#define SYS(fmt, ...) \ + ({ \ + char cmd[1024]; \ + snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \ + if (!ASSERT_OK(system(cmd), cmd)) \ + goto out; \ + }) + +struct udp_packet { + struct ethhdr eth; + struct ipv6hdr iph; + struct udphdr udp; + __u8 payload[64 - sizeof(struct udphdr) + - sizeof(struct ethhdr) - sizeof(struct ipv6hdr)]; +} __packed; + +static struct udp_packet pkt_udp = { + .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), + .eth.h_dest = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, + .eth.h_source = {0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb}, + .iph.version = 6, + .iph.nexthdr = IPPROTO_UDP, + .iph.payload_len = bpf_htons(sizeof(struct udp_packet) + - offsetof(struct udp_packet, udp)), + .iph.hop_limit = 2, + .iph.saddr.s6_addr16 = {bpf_htons(0xfc00), 0, 0, 0, 0, 0, 0, bpf_htons(1)}, + .iph.daddr.s6_addr16 = {bpf_htons(0xfc00), 0, 0, 0, 0, 0, 0, bpf_htons(2)}, + .udp.source = bpf_htons(1), + .udp.dest = bpf_htons(1), + .udp.len = bpf_htons(sizeof(struct udp_packet) + - offsetof(struct udp_packet, udp)), + .payload = {0x42}, /* receiver XDP program matches on this */ +}; + +static int attach_tc_prog(struct bpf_tc_hook *hook, int fd) +{ + DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts, .handle = 1, .priority = 1, .prog_fd = fd); + int ret; + + ret = bpf_tc_hook_create(hook); + if (!ASSERT_OK(ret, "create tc hook")) + return ret; + + ret = bpf_tc_attach(hook, &opts); + if (!ASSERT_OK(ret, "bpf_tc_attach")) { + bpf_tc_hook_destroy(hook); + return ret; + } + + return 0; +} + +/* The maximum permissible size is: PAGE_SIZE - sizeof(struct xdp_page_head) - + * sizeof(struct skb_shared_info) - XDP_PACKET_HEADROOM = 3368 bytes + */ +#define MAX_PKT_SIZE 3368 +static void test_max_pkt_size(int fd) +{ + char data[MAX_PKT_SIZE + 1] = {}; + int err; + DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts, + .data_in = &data, + .data_size_in = MAX_PKT_SIZE, + .flags = BPF_F_TEST_XDP_LIVE_FRAMES, + .repeat = 1, + ); + err = bpf_prog_test_run_opts(fd, &opts); + ASSERT_OK(err, "prog_run_max_size"); + + opts.data_size_in += 1; + err = bpf_prog_test_run_opts(fd, &opts); + ASSERT_EQ(err, -EINVAL, "prog_run_too_big"); +} + +#define NUM_PKTS 10000 +void test_xdp_do_redirect(void) +{ + int err, xdp_prog_fd, tc_prog_fd, ifindex_src, ifindex_dst; + char data[sizeof(pkt_udp) + sizeof(__u32)]; + struct test_xdp_do_redirect *skel = NULL; + struct nstoken *nstoken = NULL; + struct bpf_link *link; + + struct xdp_md ctx_in = { .data = sizeof(__u32), + .data_end = sizeof(data) }; + DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts, + .data_in = &data, + .data_size_in = sizeof(data), + .ctx_in = &ctx_in, + .ctx_size_in = sizeof(ctx_in), + .flags = BPF_F_TEST_XDP_LIVE_FRAMES, + .repeat = NUM_PKTS, + .batch_size = 64, + ); + DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook, + .attach_point = BPF_TC_INGRESS); + + memcpy(&data[sizeof(__u32)], &pkt_udp, sizeof(pkt_udp)); + *((__u32 *)data) = 0x42; /* metadata test value */ + + skel = test_xdp_do_redirect__open(); + if (!ASSERT_OK_PTR(skel, "skel")) + return; + + /* The XDP program we run with bpf_prog_run() will cycle through all + * three xmit (PASS/TX/REDIRECT) return codes starting from above, and + * ending up with PASS, so we should end up with two packets on the dst + * iface and NUM_PKTS-2 in the TC hook. We match the packets on the UDP + * payload. + */ + SYS("ip netns add testns"); + nstoken = open_netns("testns"); + if (!ASSERT_OK_PTR(nstoken, "setns")) + goto out; + + SYS("ip link add veth_src type veth peer name veth_dst"); + SYS("ip link set dev veth_src address 00:11:22:33:44:55"); + SYS("ip link set dev veth_dst address 66:77:88:99:aa:bb"); + SYS("ip link set dev veth_src up"); + SYS("ip link set dev veth_dst up"); + SYS("ip addr add dev veth_src fc00::1/64"); + SYS("ip addr add dev veth_dst fc00::2/64"); + SYS("ip neigh add fc00::2 dev veth_src lladdr 66:77:88:99:aa:bb"); + + /* We enable forwarding in the test namespace because that will cause + * the packets that go through the kernel stack (with XDP_PASS) to be + * forwarded back out the same interface (because of the packet dst + * combined with the interface addresses). When this happens, the + * regular forwarding path will end up going through the same + * veth_xdp_xmit() call as the XDP_REDIRECT code, which can cause a + * deadlock if it happens on the same CPU. There's a local_bh_disable() + * in the test_run code to prevent this, but an earlier version of the + * code didn't have this, so we keep the test behaviour to make sure the + * bug doesn't resurface. + */ + SYS("sysctl -qw net.ipv6.conf.all.forwarding=1"); + + ifindex_src = if_nametoindex("veth_src"); + ifindex_dst = if_nametoindex("veth_dst"); + if (!ASSERT_NEQ(ifindex_src, 0, "ifindex_src") || + !ASSERT_NEQ(ifindex_dst, 0, "ifindex_dst")) + goto out; + + memcpy(skel->rodata->expect_dst, &pkt_udp.eth.h_dest, ETH_ALEN); + skel->rodata->ifindex_out = ifindex_src; /* redirect back to the same iface */ + skel->rodata->ifindex_in = ifindex_src; + ctx_in.ingress_ifindex = ifindex_src; + tc_hook.ifindex = ifindex_src; + + if (!ASSERT_OK(test_xdp_do_redirect__load(skel), "load")) + goto out; + + link = bpf_program__attach_xdp(skel->progs.xdp_count_pkts, ifindex_dst); + if (!ASSERT_OK_PTR(link, "prog_attach")) + goto out; + skel->links.xdp_count_pkts = link; + + tc_prog_fd = bpf_program__fd(skel->progs.tc_count_pkts); + if (attach_tc_prog(&tc_hook, tc_prog_fd)) + goto out; + + xdp_prog_fd = bpf_program__fd(skel->progs.xdp_redirect); + err = bpf_prog_test_run_opts(xdp_prog_fd, &opts); + if (!ASSERT_OK(err, "prog_run")) + goto out_tc; + + /* wait for the packets to be flushed */ + kern_sync_rcu(); + + /* There will be one packet sent through XDP_REDIRECT and one through + * XDP_TX; these will show up on the XDP counting program, while the + * rest will be counted at the TC ingress hook (and the counting program + * resets the packet payload so they don't get counted twice even though + * they are re-xmited out the veth device + */ + ASSERT_EQ(skel->bss->pkts_seen_xdp, 2, "pkt_count_xdp"); + ASSERT_EQ(skel->bss->pkts_seen_zero, 2, "pkt_count_zero"); + ASSERT_EQ(skel->bss->pkts_seen_tc, NUM_PKTS - 2, "pkt_count_tc"); + + test_max_pkt_size(bpf_program__fd(skel->progs.xdp_count_pkts)); + +out_tc: + bpf_tc_hook_destroy(&tc_hook); +out: + if (nstoken) + close_netns(nstoken); + system("ip netns del testns"); + test_xdp_do_redirect__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_info.c b/tools/testing/selftests/bpf/prog_tests/xdp_info.c index abe48e82e1dc..0d01ff6cb91a 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_info.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_info.c @@ -14,13 +14,13 @@ void serial_test_xdp_info(void) /* Get prog_id for XDP_ATTACHED_NONE mode */ - err = bpf_get_link_xdp_id(IFINDEX_LO, &prog_id, 0); + err = bpf_xdp_query_id(IFINDEX_LO, 0, &prog_id); if (CHECK(err, "get_xdp_none", "errno=%d\n", errno)) return; if (CHECK(prog_id, "prog_id_none", "unexpected prog_id=%u\n", prog_id)) return; - err = bpf_get_link_xdp_id(IFINDEX_LO, &prog_id, XDP_FLAGS_SKB_MODE); + err = bpf_xdp_query_id(IFINDEX_LO, XDP_FLAGS_SKB_MODE, &prog_id); if (CHECK(err, "get_xdp_none_skb", "errno=%d\n", errno)) return; if (CHECK(prog_id, "prog_id_none_skb", "unexpected prog_id=%u\n", @@ -37,32 +37,32 @@ void serial_test_xdp_info(void) if (CHECK(err, "get_prog_info", "errno=%d\n", errno)) goto out_close; - err = bpf_set_link_xdp_fd(IFINDEX_LO, prog_fd, XDP_FLAGS_SKB_MODE); + err = bpf_xdp_attach(IFINDEX_LO, prog_fd, XDP_FLAGS_SKB_MODE, NULL); if (CHECK(err, "set_xdp_skb", "errno=%d\n", errno)) goto out_close; /* Get prog_id for single prog mode */ - err = bpf_get_link_xdp_id(IFINDEX_LO, &prog_id, 0); + err = bpf_xdp_query_id(IFINDEX_LO, 0, &prog_id); if (CHECK(err, "get_xdp", "errno=%d\n", errno)) goto out; if (CHECK(prog_id != info.id, "prog_id", "prog_id not available\n")) goto out; - err = bpf_get_link_xdp_id(IFINDEX_LO, &prog_id, XDP_FLAGS_SKB_MODE); + err = bpf_xdp_query_id(IFINDEX_LO, XDP_FLAGS_SKB_MODE, &prog_id); if (CHECK(err, "get_xdp_skb", "errno=%d\n", errno)) goto out; if (CHECK(prog_id != info.id, "prog_id_skb", "prog_id not available\n")) goto out; - err = bpf_get_link_xdp_id(IFINDEX_LO, &prog_id, XDP_FLAGS_DRV_MODE); + err = bpf_xdp_query_id(IFINDEX_LO, XDP_FLAGS_DRV_MODE, &prog_id); if (CHECK(err, "get_xdp_drv", "errno=%d\n", errno)) goto out; if (CHECK(prog_id, "prog_id_drv", "unexpected prog_id=%u\n", prog_id)) goto out; out: - bpf_set_link_xdp_fd(IFINDEX_LO, -1, 0); + bpf_xdp_detach(IFINDEX_LO, 0, NULL); out_close: bpf_object__close(obj); } diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_link.c b/tools/testing/selftests/bpf/prog_tests/xdp_link.c index b2b357f8c74c..3e9d5c5521f0 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_link.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_link.c @@ -8,9 +8,9 @@ void serial_test_xdp_link(void) { - DECLARE_LIBBPF_OPTS(bpf_xdp_set_link_opts, opts, .old_fd = -1); struct test_xdp_link *skel1 = NULL, *skel2 = NULL; __u32 id1, id2, id0 = 0, prog_fd1, prog_fd2; + LIBBPF_OPTS(bpf_xdp_attach_opts, opts); struct bpf_link_info link_info; struct bpf_prog_info prog_info; struct bpf_link *link; @@ -41,12 +41,12 @@ void serial_test_xdp_link(void) id2 = prog_info.id; /* set initial prog attachment */ - err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, prog_fd1, XDP_FLAGS_REPLACE, &opts); + err = bpf_xdp_attach(IFINDEX_LO, prog_fd1, XDP_FLAGS_REPLACE, &opts); if (!ASSERT_OK(err, "fd_attach")) goto cleanup; /* validate prog ID */ - err = bpf_get_link_xdp_id(IFINDEX_LO, &id0, 0); + err = bpf_xdp_query_id(IFINDEX_LO, 0, &id0); if (!ASSERT_OK(err, "id1_check_err") || !ASSERT_EQ(id0, id1, "id1_check_val")) goto cleanup; @@ -55,14 +55,14 @@ void serial_test_xdp_link(void) if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) { bpf_link__destroy(link); /* best-effort detach prog */ - opts.old_fd = prog_fd1; - bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, XDP_FLAGS_REPLACE, &opts); + opts.old_prog_fd = prog_fd1; + bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_REPLACE, &opts); goto cleanup; } /* detach BPF program */ - opts.old_fd = prog_fd1; - err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, XDP_FLAGS_REPLACE, &opts); + opts.old_prog_fd = prog_fd1; + err = bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_REPLACE, &opts); if (!ASSERT_OK(err, "prog_detach")) goto cleanup; @@ -73,23 +73,23 @@ void serial_test_xdp_link(void) skel1->links.xdp_handler = link; /* validate prog ID */ - err = bpf_get_link_xdp_id(IFINDEX_LO, &id0, 0); + err = bpf_xdp_query_id(IFINDEX_LO, 0, &id0); if (!ASSERT_OK(err, "id1_check_err") || !ASSERT_EQ(id0, id1, "id1_check_val")) goto cleanup; /* BPF prog attach is not allowed to replace BPF link */ - opts.old_fd = prog_fd1; - err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, prog_fd2, XDP_FLAGS_REPLACE, &opts); + opts.old_prog_fd = prog_fd1; + err = bpf_xdp_attach(IFINDEX_LO, prog_fd2, XDP_FLAGS_REPLACE, &opts); if (!ASSERT_ERR(err, "prog_attach_fail")) goto cleanup; /* Can't force-update when BPF link is active */ - err = bpf_set_link_xdp_fd(IFINDEX_LO, prog_fd2, 0); + err = bpf_xdp_attach(IFINDEX_LO, prog_fd2, 0, NULL); if (!ASSERT_ERR(err, "prog_update_fail")) goto cleanup; /* Can't force-detach when BPF link is active */ - err = bpf_set_link_xdp_fd(IFINDEX_LO, -1, 0); + err = bpf_xdp_detach(IFINDEX_LO, 0, NULL); if (!ASSERT_ERR(err, "prog_detach_fail")) goto cleanup; @@ -109,7 +109,7 @@ void serial_test_xdp_link(void) goto cleanup; skel2->links.xdp_handler = link; - err = bpf_get_link_xdp_id(IFINDEX_LO, &id0, 0); + err = bpf_xdp_query_id(IFINDEX_LO, 0, &id0); if (!ASSERT_OK(err, "id2_check_err") || !ASSERT_EQ(id0, id2, "id2_check_val")) goto cleanup; diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_noinline.c b/tools/testing/selftests/bpf/prog_tests/xdp_noinline.c index 0281095de266..92ef0aa50866 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_noinline.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_noinline.c @@ -25,43 +25,49 @@ void test_xdp_noinline(void) __u8 flags; } real_def = {.dst = MAGIC_VAL}; __u32 ch_key = 11, real_num = 3; - __u32 duration = 0, retval, size; int err, i; __u64 bytes = 0, pkts = 0; char buf[128]; u32 *magic = (u32 *)buf; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .data_out = buf, + .data_size_out = sizeof(buf), + .repeat = NUM_ITER, + ); skel = test_xdp_noinline__open_and_load(); - if (CHECK(!skel, "skel_open_and_load", "failed\n")) + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) return; bpf_map_update_elem(bpf_map__fd(skel->maps.vip_map), &key, &value, 0); bpf_map_update_elem(bpf_map__fd(skel->maps.ch_rings), &ch_key, &real_num, 0); bpf_map_update_elem(bpf_map__fd(skel->maps.reals), &real_num, &real_def, 0); - err = bpf_prog_test_run(bpf_program__fd(skel->progs.balancer_ingress_v4), - NUM_ITER, &pkt_v4, sizeof(pkt_v4), - buf, &size, &retval, &duration); - CHECK(err || retval != 1 || size != 54 || - *magic != MAGIC_VAL, "ipv4", - "err %d errno %d retval %d size %d magic %x\n", - err, errno, retval, size, *magic); + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.balancer_ingress_v4), &topts); + ASSERT_OK(err, "ipv4 test_run"); + ASSERT_EQ(topts.retval, 1, "ipv4 test_run retval"); + ASSERT_EQ(topts.data_size_out, 54, "ipv4 test_run data_size_out"); + ASSERT_EQ(*magic, MAGIC_VAL, "ipv4 test_run magic"); - err = bpf_prog_test_run(bpf_program__fd(skel->progs.balancer_ingress_v6), - NUM_ITER, &pkt_v6, sizeof(pkt_v6), - buf, &size, &retval, &duration); - CHECK(err || retval != 1 || size != 74 || - *magic != MAGIC_VAL, "ipv6", - "err %d errno %d retval %d size %d magic %x\n", - err, errno, retval, size, *magic); + topts.data_in = &pkt_v6; + topts.data_size_in = sizeof(pkt_v6); + topts.data_out = buf; + topts.data_size_out = sizeof(buf); + + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.balancer_ingress_v6), &topts); + ASSERT_OK(err, "ipv6 test_run"); + ASSERT_EQ(topts.retval, 1, "ipv6 test_run retval"); + ASSERT_EQ(topts.data_size_out, 74, "ipv6 test_run data_size_out"); + ASSERT_EQ(*magic, MAGIC_VAL, "ipv6 test_run magic"); bpf_map_lookup_elem(bpf_map__fd(skel->maps.stats), &stats_key, stats); for (i = 0; i < nr_cpus; i++) { bytes += stats[i].bytes; pkts += stats[i].pkts; } - CHECK(bytes != MAGIC_BYTES * NUM_ITER * 2 || pkts != NUM_ITER * 2, - "stats", "bytes %lld pkts %lld\n", - (unsigned long long)bytes, (unsigned long long)pkts); + ASSERT_EQ(bytes, MAGIC_BYTES * NUM_ITER * 2, "stats bytes"); + ASSERT_EQ(pkts, NUM_ITER * 2, "stats pkts"); test_xdp_noinline__destroy(skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_perf.c b/tools/testing/selftests/bpf/prog_tests/xdp_perf.c index 15a3900e4370..f543d1bd21b8 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_perf.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_perf.c @@ -4,22 +4,25 @@ void test_xdp_perf(void) { const char *file = "./xdp_dummy.o"; - __u32 duration, retval, size; struct bpf_object *obj; char in[128], out[128]; int err, prog_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = in, + .data_size_in = sizeof(in), + .data_out = out, + .data_size_out = sizeof(out), + .repeat = 1000000, + ); err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); if (CHECK_FAIL(err)) return; - err = bpf_prog_test_run(prog_fd, 1000000, &in[0], 128, - out, &size, &retval, &duration); - - CHECK(err || retval != XDP_PASS || size != 128, - "xdp-perf", - "err %d errno %d retval %d size %d\n", - err, errno, retval, size); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.retval, XDP_PASS, "test_run retval"); + ASSERT_EQ(topts.data_size_out, 128, "test_run data_size_out"); bpf_object__close(obj); } diff --git a/tools/testing/selftests/bpf/progs/atomics.c b/tools/testing/selftests/bpf/progs/atomics.c index 16e57313204a..f89c7f0cc53b 100644 --- a/tools/testing/selftests/bpf/progs/atomics.c +++ b/tools/testing/selftests/bpf/progs/atomics.c @@ -20,8 +20,8 @@ __u64 add_stack_value_copy = 0; __u64 add_stack_result = 0; __u64 add_noreturn_value = 1; -SEC("fentry/bpf_fentry_test1") -int BPF_PROG(add, int a) +SEC("raw_tp/sys_enter") +int add(const void *ctx) { if (pid != (bpf_get_current_pid_tgid() >> 32)) return 0; @@ -46,8 +46,8 @@ __s64 sub_stack_value_copy = 0; __s64 sub_stack_result = 0; __s64 sub_noreturn_value = 1; -SEC("fentry/bpf_fentry_test1") -int BPF_PROG(sub, int a) +SEC("raw_tp/sys_enter") +int sub(const void *ctx) { if (pid != (bpf_get_current_pid_tgid() >> 32)) return 0; @@ -70,8 +70,8 @@ __u32 and32_value = 0x110; __u32 and32_result = 0; __u64 and_noreturn_value = (0x110ull << 32); -SEC("fentry/bpf_fentry_test1") -int BPF_PROG(and, int a) +SEC("raw_tp/sys_enter") +int and(const void *ctx) { if (pid != (bpf_get_current_pid_tgid() >> 32)) return 0; @@ -91,8 +91,8 @@ __u32 or32_value = 0x110; __u32 or32_result = 0; __u64 or_noreturn_value = (0x110ull << 32); -SEC("fentry/bpf_fentry_test1") -int BPF_PROG(or, int a) +SEC("raw_tp/sys_enter") +int or(const void *ctx) { if (pid != (bpf_get_current_pid_tgid() >> 32)) return 0; @@ -111,8 +111,8 @@ __u32 xor32_value = 0x110; __u32 xor32_result = 0; __u64 xor_noreturn_value = (0x110ull << 32); -SEC("fentry/bpf_fentry_test1") -int BPF_PROG(xor, int a) +SEC("raw_tp/sys_enter") +int xor(const void *ctx) { if (pid != (bpf_get_current_pid_tgid() >> 32)) return 0; @@ -132,8 +132,8 @@ __u32 cmpxchg32_value = 1; __u32 cmpxchg32_result_fail = 0; __u32 cmpxchg32_result_succeed = 0; -SEC("fentry/bpf_fentry_test1") -int BPF_PROG(cmpxchg, int a) +SEC("raw_tp/sys_enter") +int cmpxchg(const void *ctx) { if (pid != (bpf_get_current_pid_tgid() >> 32)) return 0; @@ -153,8 +153,8 @@ __u64 xchg64_result = 0; __u32 xchg32_value = 1; __u32 xchg32_result = 0; -SEC("fentry/bpf_fentry_test1") -int BPF_PROG(xchg, int a) +SEC("raw_tp/sys_enter") +int xchg(const void *ctx) { if (pid != (bpf_get_current_pid_tgid() >> 32)) return 0; diff --git a/tools/testing/selftests/bpf/progs/bloom_filter_bench.c b/tools/testing/selftests/bpf/progs/bloom_filter_bench.c index d9a88dd1ea65..7efcbdbe772d 100644 --- a/tools/testing/selftests/bpf/progs/bloom_filter_bench.c +++ b/tools/testing/selftests/bpf/progs/bloom_filter_bench.c @@ -5,6 +5,7 @@ #include #include #include +#include "bpf_misc.h" char _license[] SEC("license") = "GPL"; @@ -87,7 +88,7 @@ bloom_callback(struct bpf_map *map, __u32 *key, void *val, return 0; } -SEC("fentry/__x64_sys_getpgid") +SEC("fentry/" SYS_PREFIX "sys_getpgid") int bloom_lookup(void *ctx) { struct callback_ctx data; @@ -100,7 +101,7 @@ int bloom_lookup(void *ctx) return 0; } -SEC("fentry/__x64_sys_getpgid") +SEC("fentry/" SYS_PREFIX "sys_getpgid") int bloom_update(void *ctx) { struct callback_ctx data; @@ -113,7 +114,7 @@ int bloom_update(void *ctx) return 0; } -SEC("fentry/__x64_sys_getpgid") +SEC("fentry/" SYS_PREFIX "sys_getpgid") int bloom_hashmap_lookup(void *ctx) { __u64 *result; diff --git a/tools/testing/selftests/bpf/progs/bloom_filter_map.c b/tools/testing/selftests/bpf/progs/bloom_filter_map.c index 1316f3db79d9..f245fcfe0c61 100644 --- a/tools/testing/selftests/bpf/progs/bloom_filter_map.c +++ b/tools/testing/selftests/bpf/progs/bloom_filter_map.c @@ -3,6 +3,7 @@ #include #include +#include "bpf_misc.h" char _license[] SEC("license") = "GPL"; @@ -51,7 +52,7 @@ check_elem(struct bpf_map *map, __u32 *key, __u32 *val, return 0; } -SEC("fentry/__x64_sys_getpgid") +SEC("fentry/" SYS_PREFIX "sys_getpgid") int inner_map(void *ctx) { struct bpf_map *inner_map; @@ -70,7 +71,7 @@ int inner_map(void *ctx) return 0; } -SEC("fentry/__x64_sys_getpgid") +SEC("fentry/" SYS_PREFIX "sys_getpgid") int check_bloom(void *ctx) { struct callback_ctx data; diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_setsockopt_unix.c b/tools/testing/selftests/bpf/progs/bpf_iter_setsockopt_unix.c new file mode 100644 index 000000000000..eafc877ea460 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/bpf_iter_setsockopt_unix.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright Amazon.com Inc. or its affiliates. */ +#include "bpf_iter.h" +#include "bpf_tracing_net.h" +#include +#include + +#define AUTOBIND_LEN 6 +char sun_path[AUTOBIND_LEN]; + +#define NR_CASES 5 +int sndbuf_setsockopt[NR_CASES] = {-1, 0, 8192, INT_MAX / 2, INT_MAX}; +int sndbuf_getsockopt[NR_CASES] = {-1, -1, -1, -1, -1}; +int sndbuf_getsockopt_expected[NR_CASES]; + +static inline int cmpname(struct unix_sock *unix_sk) +{ + int i; + + for (i = 0; i < AUTOBIND_LEN; i++) { + if (unix_sk->addr->name->sun_path[i] != sun_path[i]) + return -1; + } + + return 0; +} + +SEC("iter/unix") +int change_sndbuf(struct bpf_iter__unix *ctx) +{ + struct unix_sock *unix_sk = ctx->unix_sk; + int i, err; + + if (!unix_sk || !unix_sk->addr) + return 0; + + if (unix_sk->addr->name->sun_path[0]) + return 0; + + if (cmpname(unix_sk)) + return 0; + + for (i = 0; i < NR_CASES; i++) { + err = bpf_setsockopt(unix_sk, SOL_SOCKET, SO_SNDBUF, + &sndbuf_setsockopt[i], + sizeof(sndbuf_setsockopt[i])); + if (err) + break; + + err = bpf_getsockopt(unix_sk, SOL_SOCKET, SO_SNDBUF, + &sndbuf_getsockopt[i], + sizeof(sndbuf_getsockopt[i])); + if (err) + break; + } + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_task.c b/tools/testing/selftests/bpf/progs/bpf_iter_task.c index c86b93f33b32..d22741272692 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter_task.c +++ b/tools/testing/selftests/bpf/progs/bpf_iter_task.c @@ -2,6 +2,7 @@ /* Copyright (c) 2020 Facebook */ #include "bpf_iter.h" #include +#include char _license[] SEC("license") = "GPL"; @@ -23,3 +24,56 @@ int dump_task(struct bpf_iter__task *ctx) BPF_SEQ_PRINTF(seq, "%8d %8d\n", task->tgid, task->pid); return 0; } + +int num_expected_failure_copy_from_user_task = 0; +int num_success_copy_from_user_task = 0; + +SEC("iter.s/task") +int dump_task_sleepable(struct bpf_iter__task *ctx) +{ + struct seq_file *seq = ctx->meta->seq; + struct task_struct *task = ctx->task; + static const char info[] = " === END ==="; + struct pt_regs *regs; + void *ptr; + uint32_t user_data = 0; + int ret; + + if (task == (void *)0) { + BPF_SEQ_PRINTF(seq, "%s\n", info); + return 0; + } + + /* Read an invalid pointer and ensure we get an error */ + ptr = NULL; + ret = bpf_copy_from_user_task(&user_data, sizeof(uint32_t), ptr, task, 0); + if (ret) { + ++num_expected_failure_copy_from_user_task; + } else { + BPF_SEQ_PRINTF(seq, "%s\n", info); + return 0; + } + + /* Try to read the contents of the task's instruction pointer from the + * remote task's address space. + */ + regs = (struct pt_regs *)bpf_task_pt_regs(task); + if (regs == (void *)0) { + BPF_SEQ_PRINTF(seq, "%s\n", info); + return 0; + } + ptr = (void *)PT_REGS_IP(regs); + + ret = bpf_copy_from_user_task(&user_data, sizeof(uint32_t), ptr, task, 0); + if (ret) { + BPF_SEQ_PRINTF(seq, "%s\n", info); + return 0; + } + ++num_success_copy_from_user_task; + + if (ctx->meta->seq_num == 0) + BPF_SEQ_PRINTF(seq, " tgid gid data\n"); + + BPF_SEQ_PRINTF(seq, "%8d %8d %8d\n", task->tgid, task->pid, user_data); + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_unix.c b/tools/testing/selftests/bpf/progs/bpf_iter_unix.c index c21e3f545371..e6aefae38894 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter_unix.c +++ b/tools/testing/selftests/bpf/progs/bpf_iter_unix.c @@ -63,7 +63,7 @@ int dump_unix(struct bpf_iter__unix *ctx) BPF_SEQ_PRINTF(seq, " @"); for (i = 1; i < len; i++) { - /* unix_mkname() tests this upper bound. */ + /* unix_validate_addr() tests this upper bound. */ if (i >= sizeof(struct sockaddr_un)) break; diff --git a/tools/testing/selftests/bpf/progs/bpf_loop.c b/tools/testing/selftests/bpf/progs/bpf_loop.c index 12349e4601e8..e08565282759 100644 --- a/tools/testing/selftests/bpf/progs/bpf_loop.c +++ b/tools/testing/selftests/bpf/progs/bpf_loop.c @@ -3,6 +3,7 @@ #include "vmlinux.h" #include +#include "bpf_misc.h" char _license[] SEC("license") = "GPL"; @@ -53,7 +54,7 @@ static int nested_callback1(__u32 index, void *data) return 0; } -SEC("fentry/__x64_sys_nanosleep") +SEC("fentry/" SYS_PREFIX "sys_nanosleep") int test_prog(void *ctx) { struct callback_ctx data = {}; @@ -71,7 +72,7 @@ int test_prog(void *ctx) return 0; } -SEC("fentry/__x64_sys_nanosleep") +SEC("fentry/" SYS_PREFIX "sys_nanosleep") int prog_null_ctx(void *ctx) { if (bpf_get_current_pid_tgid() >> 32 != pid) @@ -82,7 +83,7 @@ int prog_null_ctx(void *ctx) return 0; } -SEC("fentry/__x64_sys_nanosleep") +SEC("fentry/" SYS_PREFIX "sys_nanosleep") int prog_invalid_flags(void *ctx) { struct callback_ctx data = {}; @@ -95,7 +96,7 @@ int prog_invalid_flags(void *ctx) return 0; } -SEC("fentry/__x64_sys_nanosleep") +SEC("fentry/" SYS_PREFIX "sys_nanosleep") int prog_nested_calls(void *ctx) { struct callback_ctx data = {}; diff --git a/tools/testing/selftests/bpf/progs/bpf_loop_bench.c b/tools/testing/selftests/bpf/progs/bpf_loop_bench.c index 9dafdc244462..4ce76eb064c4 100644 --- a/tools/testing/selftests/bpf/progs/bpf_loop_bench.c +++ b/tools/testing/selftests/bpf/progs/bpf_loop_bench.c @@ -3,6 +3,7 @@ #include "vmlinux.h" #include +#include "bpf_misc.h" char _license[] SEC("license") = "GPL"; @@ -14,7 +15,7 @@ static int empty_callback(__u32 index, void *data) return 0; } -SEC("fentry/__x64_sys_getpgid") +SEC("fentry/" SYS_PREFIX "sys_getpgid") int benchmark(void *ctx) { for (int i = 0; i < 1000; i++) { diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h new file mode 100644 index 000000000000..5bb11fe595a4 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __BPF_MISC_H__ +#define __BPF_MISC_H__ + +#if defined(__TARGET_ARCH_x86) +#define SYSCALL_WRAPPER 1 +#define SYS_PREFIX "__x64_" +#elif defined(__TARGET_ARCH_s390) +#define SYSCALL_WRAPPER 1 +#define SYS_PREFIX "__s390x_" +#elif defined(__TARGET_ARCH_arm64) +#define SYSCALL_WRAPPER 1 +#define SYS_PREFIX "__arm64_" +#else +#define SYSCALL_WRAPPER 0 +#define SYS_PREFIX "__se_" +#endif + +#endif diff --git a/tools/testing/selftests/bpf/progs/bpf_mod_race.c b/tools/testing/selftests/bpf/progs/bpf_mod_race.c new file mode 100644 index 000000000000..82a5c6c6ba83 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/bpf_mod_race.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +const volatile struct { + /* thread to activate trace programs for */ + pid_t tgid; + /* return error from __init function */ + int inject_error; + /* uffd monitored range start address */ + void *fault_addr; +} bpf_mod_race_config = { -1 }; + +int bpf_blocking = 0; +int res_try_get_module = -1; + +static __always_inline bool check_thread_id(void) +{ + struct task_struct *task = bpf_get_current_task_btf(); + + return task->tgid == bpf_mod_race_config.tgid; +} + +/* The trace of execution is something like this: + * + * finit_module() + * load_module() + * prepare_coming_module() + * notifier_call(MODULE_STATE_COMING) + * btf_parse_module() + * btf_alloc_id() // Visible to userspace at this point + * list_add(btf_mod->list, &btf_modules) + * do_init_module() + * freeinit = kmalloc() + * ret = mod->init() + * bpf_prog_widen_race() + * bpf_copy_from_user() + * ...... + * if (ret < 0) + * ... + * free_module() + * return ret + * + * At this point, module loading thread is blocked, we now load the program: + * + * bpf_check + * add_kfunc_call/check_pseudo_btf_id + * btf_try_get_module + * try_get_module_live == false + * return -ENXIO + * + * Without the fix (try_get_module_live in btf_try_get_module): + * + * bpf_check + * add_kfunc_call/check_pseudo_btf_id + * btf_try_get_module + * try_get_module == true + * + * ... + * return fd + * + * Now, if we inject an error in the blocked program, our module will be freed + * (going straight from MODULE_STATE_COMING to MODULE_STATE_GOING). + * Later, when bpf program is freed, it will try to module_put already freed + * module. This is why try_get_module_live returns false if mod->state is not + * MODULE_STATE_LIVE. + */ + +SEC("fmod_ret.s/bpf_fentry_test1") +int BPF_PROG(widen_race, int a, int ret) +{ + char dst; + + if (!check_thread_id()) + return 0; + /* Indicate that we will attempt to block */ + bpf_blocking = 1; + bpf_copy_from_user(&dst, 1, bpf_mod_race_config.fault_addr); + return bpf_mod_race_config.inject_error; +} + +SEC("fexit/do_init_module") +int BPF_PROG(fexit_init_module, struct module *mod, int ret) +{ + if (!check_thread_id()) + return 0; + /* Indicate that we finished blocking */ + bpf_blocking = 2; + return 0; +} + +SEC("fexit/btf_try_get_module") +int BPF_PROG(fexit_module_get, const struct btf *btf, struct module *mod) +{ + res_try_get_module = !!mod; + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/bpf_syscall_macro.c b/tools/testing/selftests/bpf/progs/bpf_syscall_macro.c new file mode 100644 index 000000000000..05838ed9b89c --- /dev/null +++ b/tools/testing/selftests/bpf/progs/bpf_syscall_macro.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2022 Sony Group Corporation */ +#include + +#include +#include +#include +#include "bpf_misc.h" + +int arg1 = 0; +unsigned long arg2 = 0; +unsigned long arg3 = 0; +unsigned long arg4_cx = 0; +unsigned long arg4 = 0; +unsigned long arg5 = 0; + +int arg1_core = 0; +unsigned long arg2_core = 0; +unsigned long arg3_core = 0; +unsigned long arg4_core_cx = 0; +unsigned long arg4_core = 0; +unsigned long arg5_core = 0; + +int option_syscall = 0; +unsigned long arg2_syscall = 0; +unsigned long arg3_syscall = 0; +unsigned long arg4_syscall = 0; +unsigned long arg5_syscall = 0; + +const volatile pid_t filter_pid = 0; + +SEC("kprobe/" SYS_PREFIX "sys_prctl") +int BPF_KPROBE(handle_sys_prctl) +{ + struct pt_regs *real_regs; + pid_t pid = bpf_get_current_pid_tgid() >> 32; + unsigned long tmp = 0; + + if (pid != filter_pid) + return 0; + + real_regs = PT_REGS_SYSCALL_REGS(ctx); + + /* test for PT_REGS_PARM */ + +#if !defined(bpf_target_arm64) && !defined(bpf_target_s390) + bpf_probe_read_kernel(&tmp, sizeof(tmp), &PT_REGS_PARM1_SYSCALL(real_regs)); +#endif + arg1 = tmp; + bpf_probe_read_kernel(&arg2, sizeof(arg2), &PT_REGS_PARM2_SYSCALL(real_regs)); + bpf_probe_read_kernel(&arg3, sizeof(arg3), &PT_REGS_PARM3_SYSCALL(real_regs)); + bpf_probe_read_kernel(&arg4_cx, sizeof(arg4_cx), &PT_REGS_PARM4(real_regs)); + bpf_probe_read_kernel(&arg4, sizeof(arg4), &PT_REGS_PARM4_SYSCALL(real_regs)); + bpf_probe_read_kernel(&arg5, sizeof(arg5), &PT_REGS_PARM5_SYSCALL(real_regs)); + + /* test for the CORE variant of PT_REGS_PARM */ + arg1_core = PT_REGS_PARM1_CORE_SYSCALL(real_regs); + arg2_core = PT_REGS_PARM2_CORE_SYSCALL(real_regs); + arg3_core = PT_REGS_PARM3_CORE_SYSCALL(real_regs); + arg4_core_cx = PT_REGS_PARM4_CORE(real_regs); + arg4_core = PT_REGS_PARM4_CORE_SYSCALL(real_regs); + arg5_core = PT_REGS_PARM5_CORE_SYSCALL(real_regs); + + return 0; +} + +SEC("kprobe/" SYS_PREFIX "sys_prctl") +int BPF_KPROBE_SYSCALL(prctl_enter, int option, unsigned long arg2, + unsigned long arg3, unsigned long arg4, unsigned long arg5) +{ + pid_t pid = bpf_get_current_pid_tgid() >> 32; + + if (pid != filter_pid) + return 0; + + option_syscall = option; + arg2_syscall = arg2; + arg3_syscall = arg3; + arg4_syscall = arg4; + arg5_syscall = arg5; + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/bpf_tracing_net.h b/tools/testing/selftests/bpf/progs/bpf_tracing_net.h index e0f42601be9b..1c1289ba5fc5 100644 --- a/tools/testing/selftests/bpf/progs/bpf_tracing_net.h +++ b/tools/testing/selftests/bpf/progs/bpf_tracing_net.h @@ -5,6 +5,8 @@ #define AF_INET 2 #define AF_INET6 10 +#define SOL_SOCKET 1 +#define SO_SNDBUF 7 #define __SO_ACCEPTCON (1 << 16) #define SOL_TCP 6 diff --git a/tools/testing/selftests/bpf/progs/btf_type_tag_percpu.c b/tools/testing/selftests/bpf/progs/btf_type_tag_percpu.c new file mode 100644 index 000000000000..8feddb8289cf --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf_type_tag_percpu.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Google */ +#include "vmlinux.h" +#include +#include + +struct bpf_testmod_btf_type_tag_1 { + int a; +}; + +struct bpf_testmod_btf_type_tag_2 { + struct bpf_testmod_btf_type_tag_1 *p; +}; + +__u64 g; + +SEC("fentry/bpf_testmod_test_btf_type_tag_percpu_1") +int BPF_PROG(test_percpu1, struct bpf_testmod_btf_type_tag_1 *arg) +{ + g = arg->a; + return 0; +} + +SEC("fentry/bpf_testmod_test_btf_type_tag_percpu_2") +int BPF_PROG(test_percpu2, struct bpf_testmod_btf_type_tag_2 *arg) +{ + g = arg->p->a; + return 0; +} + +/* trace_cgroup_mkdir(struct cgroup *cgrp, const char *path) + * + * struct cgroup_rstat_cpu { + * ... + * struct cgroup *updated_children; + * ... + * }; + * + * struct cgroup { + * ... + * struct cgroup_rstat_cpu __percpu *rstat_cpu; + * ... + * }; + */ +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(test_percpu_load, struct cgroup *cgrp, const char *path) +{ + g = (__u64)cgrp->rstat_cpu->updated_children; + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(test_percpu_helper, struct cgroup *cgrp, const char *path) +{ + struct cgroup_rstat_cpu *rstat; + __u32 cpu; + + cpu = bpf_get_smp_processor_id(); + rstat = (struct cgroup_rstat_cpu *)bpf_per_cpu_ptr(cgrp->rstat_cpu, cpu); + if (rstat) { + /* READ_ONCE */ + *(volatile int *)rstat; + } + + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/btf_type_tag_user.c b/tools/testing/selftests/bpf/progs/btf_type_tag_user.c new file mode 100644 index 000000000000..5523f77c5a44 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf_type_tag_user.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Facebook */ +#include "vmlinux.h" +#include +#include + +struct bpf_testmod_btf_type_tag_1 { + int a; +}; + +struct bpf_testmod_btf_type_tag_2 { + struct bpf_testmod_btf_type_tag_1 *p; +}; + +int g; + +SEC("fentry/bpf_testmod_test_btf_type_tag_user_1") +int BPF_PROG(test_user1, struct bpf_testmod_btf_type_tag_1 *arg) +{ + g = arg->a; + return 0; +} + +SEC("fentry/bpf_testmod_test_btf_type_tag_user_2") +int BPF_PROG(test_user2, struct bpf_testmod_btf_type_tag_2 *arg) +{ + g = arg->p->a; + return 0; +} + +/* int __sys_getsockname(int fd, struct sockaddr __user *usockaddr, + * int __user *usockaddr_len); + */ +SEC("fentry/__sys_getsockname") +int BPF_PROG(test_sys_getsockname, int fd, struct sockaddr *usockaddr, + int *usockaddr_len) +{ + g = usockaddr->sa_family; + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/cgroup_getset_retval_getsockopt.c b/tools/testing/selftests/bpf/progs/cgroup_getset_retval_getsockopt.c new file mode 100644 index 000000000000..b2a409e6382a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgroup_getset_retval_getsockopt.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright 2021 Google LLC. + */ + +#include +#include +#include + +__u32 invocations = 0; +__u32 assertion_error = 0; +__u32 retval_value = 0; +__u32 ctx_retval_value = 0; + +SEC("cgroup/getsockopt") +int get_retval(struct bpf_sockopt *ctx) +{ + retval_value = bpf_get_retval(); + ctx_retval_value = ctx->retval; + __sync_fetch_and_add(&invocations, 1); + + return 1; +} + +SEC("cgroup/getsockopt") +int set_eisconn(struct bpf_sockopt *ctx) +{ + __sync_fetch_and_add(&invocations, 1); + + if (bpf_set_retval(-EISCONN)) + assertion_error = 1; + + return 1; +} + +SEC("cgroup/getsockopt") +int clear_retval(struct bpf_sockopt *ctx) +{ + __sync_fetch_and_add(&invocations, 1); + + ctx->retval = 0; + + return 1; +} diff --git a/tools/testing/selftests/bpf/progs/cgroup_getset_retval_setsockopt.c b/tools/testing/selftests/bpf/progs/cgroup_getset_retval_setsockopt.c new file mode 100644 index 000000000000..d6e5903e06ba --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgroup_getset_retval_setsockopt.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright 2021 Google LLC. + */ + +#include +#include +#include + +__u32 invocations = 0; +__u32 assertion_error = 0; +__u32 retval_value = 0; + +SEC("cgroup/setsockopt") +int get_retval(struct bpf_sockopt *ctx) +{ + retval_value = bpf_get_retval(); + __sync_fetch_and_add(&invocations, 1); + + return 1; +} + +SEC("cgroup/setsockopt") +int set_eunatch(struct bpf_sockopt *ctx) +{ + __sync_fetch_and_add(&invocations, 1); + + if (bpf_set_retval(-EUNATCH)) + assertion_error = 1; + + return 0; +} + +SEC("cgroup/setsockopt") +int set_eisconn(struct bpf_sockopt *ctx) +{ + __sync_fetch_and_add(&invocations, 1); + + if (bpf_set_retval(-EISCONN)) + assertion_error = 1; + + return 0; +} + +SEC("cgroup/setsockopt") +int legacy_eperm(struct bpf_sockopt *ctx) +{ + __sync_fetch_and_add(&invocations, 1); + + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/core_kern.c b/tools/testing/selftests/bpf/progs/core_kern.c index 13499cc15c7d..2715fe27d4cf 100644 --- a/tools/testing/selftests/bpf/progs/core_kern.c +++ b/tools/testing/selftests/bpf/progs/core_kern.c @@ -101,4 +101,20 @@ int balancer_ingress(struct __sk_buff *ctx) return 0; } +typedef int (*func_proto_typedef___match)(long); +typedef int (*func_proto_typedef___doesnt_match)(char *); +typedef int (*func_proto_typedef_nested1)(func_proto_typedef___match); + +int proto_out[3]; + +SEC("raw_tracepoint/sys_enter") +int core_relo_proto(void *ctx) +{ + proto_out[0] = bpf_core_type_exists(func_proto_typedef___match); + proto_out[1] = bpf_core_type_exists(func_proto_typedef___doesnt_match); + proto_out[2] = bpf_core_type_exists(func_proto_typedef_nested1); + + return 0; +} + char LICENSE[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/core_kern_overflow.c b/tools/testing/selftests/bpf/progs/core_kern_overflow.c new file mode 100644 index 000000000000..f0d5652256ba --- /dev/null +++ b/tools/testing/selftests/bpf/progs/core_kern_overflow.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "vmlinux.h" + +#include +#include +#include + +typedef int (*func_proto_typedef)(long); +typedef int (*func_proto_typedef_nested1)(func_proto_typedef); +typedef int (*func_proto_typedef_nested2)(func_proto_typedef_nested1); + +int proto_out; + +SEC("raw_tracepoint/sys_enter") +int core_relo_proto(void *ctx) +{ + proto_out = bpf_core_type_exists(func_proto_typedef_nested2); + + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/fexit_sleep.c b/tools/testing/selftests/bpf/progs/fexit_sleep.c index bca92c9bd29a..106dc75efcc4 100644 --- a/tools/testing/selftests/bpf/progs/fexit_sleep.c +++ b/tools/testing/selftests/bpf/progs/fexit_sleep.c @@ -3,6 +3,7 @@ #include "vmlinux.h" #include #include +#include "bpf_misc.h" char LICENSE[] SEC("license") = "GPL"; @@ -10,8 +11,8 @@ int pid = 0; int fentry_cnt = 0; int fexit_cnt = 0; -SEC("fentry/__x64_sys_nanosleep") -int BPF_PROG(nanosleep_fentry, const struct pt_regs *regs) +SEC("fentry/" SYS_PREFIX "sys_nanosleep") +int nanosleep_fentry(void *ctx) { if (bpf_get_current_pid_tgid() >> 32 != pid) return 0; @@ -20,8 +21,8 @@ int BPF_PROG(nanosleep_fentry, const struct pt_regs *regs) return 0; } -SEC("fexit/__x64_sys_nanosleep") -int BPF_PROG(nanosleep_fexit, const struct pt_regs *regs, int ret) +SEC("fexit/" SYS_PREFIX "sys_nanosleep") +int nanosleep_fexit(void *ctx) { if (bpf_get_current_pid_tgid() >> 32 != pid) return 0; diff --git a/tools/testing/selftests/bpf/progs/freplace_cls_redirect.c b/tools/testing/selftests/bpf/progs/freplace_cls_redirect.c index 68a5a9db928a..7e94412d47a5 100644 --- a/tools/testing/selftests/bpf/progs/freplace_cls_redirect.c +++ b/tools/testing/selftests/bpf/progs/freplace_cls_redirect.c @@ -7,12 +7,12 @@ #include #include -struct bpf_map_def SEC("maps") sock_map = { - .type = BPF_MAP_TYPE_SOCKMAP, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 2, -}; +struct { + __uint(type, BPF_MAP_TYPE_SOCKMAP); + __type(key, int); + __type(value, int); + __uint(max_entries, 2); +} sock_map SEC(".maps"); SEC("freplace/cls_redirect") int freplace_cls_redirect_test(struct __sk_buff *skb) diff --git a/tools/testing/selftests/bpf/progs/ima.c b/tools/testing/selftests/bpf/progs/ima.c index 96060ff4ffc6..e16a2c208481 100644 --- a/tools/testing/selftests/bpf/progs/ima.c +++ b/tools/testing/selftests/bpf/progs/ima.c @@ -18,8 +18,12 @@ struct { char _license[] SEC("license") = "GPL"; -SEC("lsm.s/bprm_committed_creds") -void BPF_PROG(ima, struct linux_binprm *bprm) +bool use_ima_file_hash; +bool enable_bprm_creds_for_exec; +bool enable_kernel_read_file; +bool test_deny; + +static void ima_test_common(struct file *file) { u64 ima_hash = 0; u64 *sample; @@ -28,8 +32,12 @@ void BPF_PROG(ima, struct linux_binprm *bprm) pid = bpf_get_current_pid_tgid() >> 32; if (pid == monitored_pid) { - ret = bpf_ima_inode_hash(bprm->file->f_inode, &ima_hash, - sizeof(ima_hash)); + if (!use_ima_file_hash) + ret = bpf_ima_inode_hash(file->f_inode, &ima_hash, + sizeof(ima_hash)); + else + ret = bpf_ima_file_hash(file, &ima_hash, + sizeof(ima_hash)); if (ret < 0 || ima_hash == 0) return; @@ -43,3 +51,53 @@ void BPF_PROG(ima, struct linux_binprm *bprm) return; } + +static int ima_test_deny(void) +{ + u32 pid; + + pid = bpf_get_current_pid_tgid() >> 32; + if (pid == monitored_pid && test_deny) + return -EPERM; + + return 0; +} + +SEC("lsm.s/bprm_committed_creds") +void BPF_PROG(bprm_committed_creds, struct linux_binprm *bprm) +{ + ima_test_common(bprm->file); +} + +SEC("lsm.s/bprm_creds_for_exec") +int BPF_PROG(bprm_creds_for_exec, struct linux_binprm *bprm) +{ + if (!enable_bprm_creds_for_exec) + return 0; + + ima_test_common(bprm->file); + return 0; +} + +SEC("lsm.s/kernel_read_file") +int BPF_PROG(kernel_read_file, struct file *file, enum kernel_read_file_id id, + bool contents) +{ + int ret; + + if (!enable_kernel_read_file) + return 0; + + if (!contents) + return 0; + + if (id != READING_POLICY) + return 0; + + ret = ima_test_deny(); + if (ret < 0) + return ret; + + ima_test_common(file); + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/kfunc_call_race.c b/tools/testing/selftests/bpf/progs/kfunc_call_race.c new file mode 100644 index 000000000000..4e8fed75a4e0 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/kfunc_call_race.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +extern void bpf_testmod_test_mod_kfunc(int i) __ksym; + +SEC("tc") +int kfunc_call_fail(struct __sk_buff *ctx) +{ + bpf_testmod_test_mod_kfunc(0); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/kfunc_call_test.c b/tools/testing/selftests/bpf/progs/kfunc_call_test.c index 8a8cf59017aa..5aecbb9fdc68 100644 --- a/tools/testing/selftests/bpf/progs/kfunc_call_test.c +++ b/tools/testing/selftests/bpf/progs/kfunc_call_test.c @@ -1,13 +1,20 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2021 Facebook */ -#include +#include #include -#include "bpf_tcp_helpers.h" extern int bpf_kfunc_call_test2(struct sock *sk, __u32 a, __u32 b) __ksym; extern __u64 bpf_kfunc_call_test1(struct sock *sk, __u32 a, __u64 b, __u32 c, __u64 d) __ksym; +extern struct prog_test_ref_kfunc *bpf_kfunc_call_test_acquire(unsigned long *sp) __ksym; +extern void bpf_kfunc_call_test_release(struct prog_test_ref_kfunc *p) __ksym; +extern void bpf_kfunc_call_test_pass_ctx(struct __sk_buff *skb) __ksym; +extern void bpf_kfunc_call_test_pass1(struct prog_test_pass1 *p) __ksym; +extern void bpf_kfunc_call_test_pass2(struct prog_test_pass2 *p) __ksym; +extern void bpf_kfunc_call_test_mem_len_pass1(void *mem, int len) __ksym; +extern void bpf_kfunc_call_test_mem_len_fail2(__u64 *mem, int len) __ksym; + SEC("tc") int kfunc_call_test2(struct __sk_buff *skb) { @@ -44,4 +51,45 @@ int kfunc_call_test1(struct __sk_buff *skb) return ret; } +SEC("tc") +int kfunc_call_test_ref_btf_id(struct __sk_buff *skb) +{ + struct prog_test_ref_kfunc *pt; + unsigned long s = 0; + int ret = 0; + + pt = bpf_kfunc_call_test_acquire(&s); + if (pt) { + if (pt->a != 42 || pt->b != 108) + ret = -1; + bpf_kfunc_call_test_release(pt); + } + return ret; +} + +SEC("tc") +int kfunc_call_test_pass(struct __sk_buff *skb) +{ + struct prog_test_pass1 p1 = {}; + struct prog_test_pass2 p2 = {}; + short a = 0; + __u64 b = 0; + long c = 0; + char d = 0; + int e = 0; + + bpf_kfunc_call_test_pass_ctx(skb); + bpf_kfunc_call_test_pass1(&p1); + bpf_kfunc_call_test_pass2(&p2); + + bpf_kfunc_call_test_mem_len_pass1(&a, sizeof(a)); + bpf_kfunc_call_test_mem_len_pass1(&b, sizeof(b)); + bpf_kfunc_call_test_mem_len_pass1(&c, sizeof(c)); + bpf_kfunc_call_test_mem_len_pass1(&d, sizeof(d)); + bpf_kfunc_call_test_mem_len_pass1(&e, sizeof(e)); + bpf_kfunc_call_test_mem_len_fail2(&b, -1); + + return 0; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/kprobe_multi.c b/tools/testing/selftests/bpf/progs/kprobe_multi.c new file mode 100644 index 000000000000..600be50800f8 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/kprobe_multi.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include + +char _license[] SEC("license") = "GPL"; + +extern const void bpf_fentry_test1 __ksym; +extern const void bpf_fentry_test2 __ksym; +extern const void bpf_fentry_test3 __ksym; +extern const void bpf_fentry_test4 __ksym; +extern const void bpf_fentry_test5 __ksym; +extern const void bpf_fentry_test6 __ksym; +extern const void bpf_fentry_test7 __ksym; +extern const void bpf_fentry_test8 __ksym; + +int pid = 0; +bool test_cookie = false; + +__u64 kprobe_test1_result = 0; +__u64 kprobe_test2_result = 0; +__u64 kprobe_test3_result = 0; +__u64 kprobe_test4_result = 0; +__u64 kprobe_test5_result = 0; +__u64 kprobe_test6_result = 0; +__u64 kprobe_test7_result = 0; +__u64 kprobe_test8_result = 0; + +__u64 kretprobe_test1_result = 0; +__u64 kretprobe_test2_result = 0; +__u64 kretprobe_test3_result = 0; +__u64 kretprobe_test4_result = 0; +__u64 kretprobe_test5_result = 0; +__u64 kretprobe_test6_result = 0; +__u64 kretprobe_test7_result = 0; +__u64 kretprobe_test8_result = 0; + +extern bool CONFIG_X86_KERNEL_IBT __kconfig __weak; + +static void kprobe_multi_check(void *ctx, bool is_return) +{ + if (bpf_get_current_pid_tgid() >> 32 != pid) + return; + + __u64 cookie = test_cookie ? bpf_get_attach_cookie(ctx) : 0; + __u64 addr = bpf_get_func_ip(ctx) - (CONFIG_X86_KERNEL_IBT ? 4 : 0); + +#define SET(__var, __addr, __cookie) ({ \ + if (((const void *) addr == __addr) && \ + (!test_cookie || (cookie == __cookie))) \ + __var = 1; \ +}) + + if (is_return) { + SET(kretprobe_test1_result, &bpf_fentry_test1, 8); + SET(kretprobe_test2_result, &bpf_fentry_test2, 7); + SET(kretprobe_test3_result, &bpf_fentry_test3, 6); + SET(kretprobe_test4_result, &bpf_fentry_test4, 5); + SET(kretprobe_test5_result, &bpf_fentry_test5, 4); + SET(kretprobe_test6_result, &bpf_fentry_test6, 3); + SET(kretprobe_test7_result, &bpf_fentry_test7, 2); + SET(kretprobe_test8_result, &bpf_fentry_test8, 1); + } else { + SET(kprobe_test1_result, &bpf_fentry_test1, 1); + SET(kprobe_test2_result, &bpf_fentry_test2, 2); + SET(kprobe_test3_result, &bpf_fentry_test3, 3); + SET(kprobe_test4_result, &bpf_fentry_test4, 4); + SET(kprobe_test5_result, &bpf_fentry_test5, 5); + SET(kprobe_test6_result, &bpf_fentry_test6, 6); + SET(kprobe_test7_result, &bpf_fentry_test7, 7); + SET(kprobe_test8_result, &bpf_fentry_test8, 8); + } + +#undef SET +} + +/* + * No tests in here, just to trigger 'bpf_fentry_test*' + * through tracing test_run + */ +SEC("fentry/bpf_modify_return_test") +int BPF_PROG(trigger) +{ + return 0; +} + +SEC("kprobe.multi/bpf_fentry_tes??") +int test_kprobe(struct pt_regs *ctx) +{ + kprobe_multi_check(ctx, false); + return 0; +} + +SEC("kretprobe.multi/bpf_fentry_test*") +int test_kretprobe(struct pt_regs *ctx) +{ + kprobe_multi_check(ctx, true); + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/ksym_race.c b/tools/testing/selftests/bpf/progs/ksym_race.c new file mode 100644 index 000000000000..def97f2fed90 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/ksym_race.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +extern int bpf_testmod_ksym_percpu __ksym; + +SEC("tc") +int ksym_fail(struct __sk_buff *ctx) +{ + return *(int *)bpf_this_cpu_ptr(&bpf_testmod_ksym_percpu); +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/local_storage.c b/tools/testing/selftests/bpf/progs/local_storage.c index 9b1f9b75d5c2..19423ed862e3 100644 --- a/tools/testing/selftests/bpf/progs/local_storage.c +++ b/tools/testing/selftests/bpf/progs/local_storage.c @@ -36,6 +36,13 @@ struct { __type(value, struct local_storage); } sk_storage_map SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_SK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE); + __type(key, int); + __type(value, struct local_storage); +} sk_storage_map2 SEC(".maps"); + struct { __uint(type, BPF_MAP_TYPE_TASK_STORAGE); __uint(map_flags, BPF_F_NO_PREALLOC); @@ -115,7 +122,19 @@ int BPF_PROG(socket_bind, struct socket *sock, struct sockaddr *address, if (storage->value != DUMMY_STORAGE_VALUE) sk_storage_result = -1; + /* This tests that we can associate multiple elements + * with the local storage. + */ + storage = bpf_sk_storage_get(&sk_storage_map2, sock->sk, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!storage) + return 0; + err = bpf_sk_storage_delete(&sk_storage_map, sock->sk); + if (err) + return 0; + + err = bpf_sk_storage_delete(&sk_storage_map2, sock->sk); if (!err) sk_storage_result = err; diff --git a/tools/testing/selftests/bpf/progs/perfbuf_bench.c b/tools/testing/selftests/bpf/progs/perfbuf_bench.c index e5ab4836a641..45204fe0c570 100644 --- a/tools/testing/selftests/bpf/progs/perfbuf_bench.c +++ b/tools/testing/selftests/bpf/progs/perfbuf_bench.c @@ -4,6 +4,7 @@ #include #include #include +#include "bpf_misc.h" char _license[] SEC("license") = "GPL"; @@ -18,7 +19,7 @@ const volatile int batch_cnt = 0; long sample_val = 42; long dropped __attribute__((aligned(128))) = 0; -SEC("fentry/__x64_sys_getpgid") +SEC("fentry/" SYS_PREFIX "sys_getpgid") int bench_perfbuf(void *ctx) { __u64 *sample; diff --git a/tools/testing/selftests/bpf/progs/ringbuf_bench.c b/tools/testing/selftests/bpf/progs/ringbuf_bench.c index 123607d314d6..6a468496f539 100644 --- a/tools/testing/selftests/bpf/progs/ringbuf_bench.c +++ b/tools/testing/selftests/bpf/progs/ringbuf_bench.c @@ -4,6 +4,7 @@ #include #include #include +#include "bpf_misc.h" char _license[] SEC("license") = "GPL"; @@ -30,7 +31,7 @@ static __always_inline long get_flags() return sz >= wakeup_data_size ? BPF_RB_FORCE_WAKEUP : BPF_RB_NO_WAKEUP; } -SEC("fentry/__x64_sys_getpgid") +SEC("fentry/" SYS_PREFIX "sys_getpgid") int bench_ringbuf(void *ctx) { long *sample, flags; diff --git a/tools/testing/selftests/bpf/progs/sample_map_ret0.c b/tools/testing/selftests/bpf/progs/sample_map_ret0.c index 1612a32007b6..495990d355ef 100644 --- a/tools/testing/selftests/bpf/progs/sample_map_ret0.c +++ b/tools/testing/selftests/bpf/progs/sample_map_ret0.c @@ -2,19 +2,19 @@ #include #include -struct bpf_map_def SEC("maps") htab = { - .type = BPF_MAP_TYPE_HASH, - .key_size = sizeof(__u32), - .value_size = sizeof(long), - .max_entries = 2, -}; +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u32); + __type(value, long); + __uint(max_entries, 2); +} htab SEC(".maps"); -struct bpf_map_def SEC("maps") array = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(__u32), - .value_size = sizeof(long), - .max_entries = 2, -}; +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, long); + __uint(max_entries, 2); +} array SEC(".maps"); /* Sample program which should always load for testing control paths. */ SEC(".text") int func() diff --git a/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c b/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c index 95d5b941bc1f..c9abfe3a11af 100644 --- a/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c +++ b/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c @@ -7,8 +7,6 @@ int bpf_prog1(struct __sk_buff *skb) { void *data_end = (void *)(long) skb->data_end; void *data = (void *)(long) skb->data; - __u32 lport = skb->local_port; - __u32 rport = skb->remote_port; __u8 *d = data; int err; diff --git a/tools/testing/selftests/bpf/progs/sockopt_sk.c b/tools/testing/selftests/bpf/progs/sockopt_sk.c index 79c8139b63b8..c8d810010a94 100644 --- a/tools/testing/selftests/bpf/progs/sockopt_sk.c +++ b/tools/testing/selftests/bpf/progs/sockopt_sk.c @@ -72,18 +72,19 @@ int _getsockopt(struct bpf_sockopt *ctx) * reasons. */ - if (optval + sizeof(struct tcp_zerocopy_receive) > optval_end) - return 0; /* EPERM, bounds check */ + /* Check that optval contains address (__u64) */ + if (optval + sizeof(__u64) > optval_end) + return 0; /* bounds check */ if (((struct tcp_zerocopy_receive *)optval)->address != 0) - return 0; /* EPERM, unexpected data */ + return 0; /* unexpected data */ return 1; } if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) { if (optval + 1 > optval_end) - return 0; /* EPERM, bounds check */ + return 0; /* bounds check */ ctx->retval = 0; /* Reset system call return value to zero */ @@ -96,24 +97,24 @@ int _getsockopt(struct bpf_sockopt *ctx) * bytes of data. */ if (optval_end - optval != page_size) - return 0; /* EPERM, unexpected data size */ + return 0; /* unexpected data size */ return 1; } if (ctx->level != SOL_CUSTOM) - return 0; /* EPERM, deny everything except custom level */ + return 0; /* deny everything except custom level */ if (optval + 1 > optval_end) - return 0; /* EPERM, bounds check */ + return 0; /* bounds check */ storage = bpf_sk_storage_get(&socket_storage_map, ctx->sk, 0, BPF_SK_STORAGE_GET_F_CREATE); if (!storage) - return 0; /* EPERM, couldn't get sk storage */ + return 0; /* couldn't get sk storage */ if (!ctx->retval) - return 0; /* EPERM, kernel should not have handled + return 0; /* kernel should not have handled * SOL_CUSTOM, something is wrong! */ ctx->retval = 0; /* Reset system call return value to zero */ @@ -152,7 +153,7 @@ int _setsockopt(struct bpf_sockopt *ctx) /* Overwrite SO_SNDBUF value */ if (optval + sizeof(__u32) > optval_end) - return 0; /* EPERM, bounds check */ + return 0; /* bounds check */ *(__u32 *)optval = 0x55AA; ctx->optlen = 4; @@ -164,7 +165,7 @@ int _setsockopt(struct bpf_sockopt *ctx) /* Always use cubic */ if (optval + 5 > optval_end) - return 0; /* EPERM, bounds check */ + return 0; /* bounds check */ memcpy(optval, "cubic", 5); ctx->optlen = 5; @@ -175,10 +176,10 @@ int _setsockopt(struct bpf_sockopt *ctx) if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) { /* Original optlen is larger than PAGE_SIZE. */ if (ctx->optlen != page_size * 2) - return 0; /* EPERM, unexpected data size */ + return 0; /* unexpected data size */ if (optval + 1 > optval_end) - return 0; /* EPERM, bounds check */ + return 0; /* bounds check */ /* Make sure we can trim the buffer. */ optval[0] = 0; @@ -189,21 +190,21 @@ int _setsockopt(struct bpf_sockopt *ctx) * bytes of data. */ if (optval_end - optval != page_size) - return 0; /* EPERM, unexpected data size */ + return 0; /* unexpected data size */ return 1; } if (ctx->level != SOL_CUSTOM) - return 0; /* EPERM, deny everything except custom level */ + return 0; /* deny everything except custom level */ if (optval + 1 > optval_end) - return 0; /* EPERM, bounds check */ + return 0; /* bounds check */ storage = bpf_sk_storage_get(&socket_storage_map, ctx->sk, 0, BPF_SK_STORAGE_GET_F_CREATE); if (!storage) - return 0; /* EPERM, couldn't get sk storage */ + return 0; /* couldn't get sk storage */ storage->val = optval[0]; ctx->optlen = -1; /* BPF has consumed this option, don't call kernel diff --git a/tools/testing/selftests/bpf/progs/stacktrace_map_skip.c b/tools/testing/selftests/bpf/progs/stacktrace_map_skip.c new file mode 100644 index 000000000000..2eb297df3dd6 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/stacktrace_map_skip.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +#define TEST_STACK_DEPTH 2 +#define TEST_MAX_ENTRIES 16384 + +typedef __u64 stack_trace_t[TEST_STACK_DEPTH]; + +struct { + __uint(type, BPF_MAP_TYPE_STACK_TRACE); + __uint(max_entries, TEST_MAX_ENTRIES); + __type(key, __u32); + __type(value, stack_trace_t); +} stackmap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, TEST_MAX_ENTRIES); + __type(key, __u32); + __type(value, __u32); +} stackid_hmap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, TEST_MAX_ENTRIES); + __type(key, __u32); + __type(value, stack_trace_t); +} stack_amap SEC(".maps"); + +int pid = 0; +int control = 0; +int failed = 0; + +SEC("tracepoint/sched/sched_switch") +int oncpu(struct trace_event_raw_sched_switch *ctx) +{ + __u32 max_len = TEST_STACK_DEPTH * sizeof(__u64); + __u32 key = 0, val = 0; + __u64 *stack_p; + + if (pid != (bpf_get_current_pid_tgid() >> 32)) + return 0; + + if (control) + return 0; + + /* it should allow skipping whole buffer size entries */ + key = bpf_get_stackid(ctx, &stackmap, TEST_STACK_DEPTH); + if ((int)key >= 0) { + /* The size of stackmap and stack_amap should be the same */ + bpf_map_update_elem(&stackid_hmap, &key, &val, 0); + stack_p = bpf_map_lookup_elem(&stack_amap, &key); + if (stack_p) { + bpf_get_stack(ctx, stack_p, max_len, TEST_STACK_DEPTH); + /* it wrongly skipped all the entries and filled zero */ + if (stack_p[0] == 0) + failed = 1; + } + } else { + /* old kernel doesn't support skipping that many entries */ + failed = 2; + } + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_bpf_nf.c b/tools/testing/selftests/bpf/progs/test_bpf_nf.c new file mode 100644 index 000000000000..f00a9731930e --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_bpf_nf.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +#define EAFNOSUPPORT 97 +#define EPROTO 71 +#define ENONET 64 +#define EINVAL 22 +#define ENOENT 2 + +int test_einval_bpf_tuple = 0; +int test_einval_reserved = 0; +int test_einval_netns_id = 0; +int test_einval_len_opts = 0; +int test_eproto_l4proto = 0; +int test_enonet_netns_id = 0; +int test_enoent_lookup = 0; +int test_eafnosupport = 0; + +struct nf_conn; + +struct bpf_ct_opts___local { + s32 netns_id; + s32 error; + u8 l4proto; + u8 reserved[3]; +} __attribute__((preserve_access_index)); + +struct nf_conn *bpf_xdp_ct_lookup(struct xdp_md *, struct bpf_sock_tuple *, u32, + struct bpf_ct_opts___local *, u32) __ksym; +struct nf_conn *bpf_skb_ct_lookup(struct __sk_buff *, struct bpf_sock_tuple *, u32, + struct bpf_ct_opts___local *, u32) __ksym; +void bpf_ct_release(struct nf_conn *) __ksym; + +static __always_inline void +nf_ct_test(struct nf_conn *(*func)(void *, struct bpf_sock_tuple *, u32, + struct bpf_ct_opts___local *, u32), + void *ctx) +{ + struct bpf_ct_opts___local opts_def = { .l4proto = IPPROTO_TCP, .netns_id = -1 }; + struct bpf_sock_tuple bpf_tuple; + struct nf_conn *ct; + + __builtin_memset(&bpf_tuple, 0, sizeof(bpf_tuple.ipv4)); + + ct = func(ctx, NULL, 0, &opts_def, sizeof(opts_def)); + if (ct) + bpf_ct_release(ct); + else + test_einval_bpf_tuple = opts_def.error; + + opts_def.reserved[0] = 1; + ct = func(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, sizeof(opts_def)); + opts_def.reserved[0] = 0; + opts_def.l4proto = IPPROTO_TCP; + if (ct) + bpf_ct_release(ct); + else + test_einval_reserved = opts_def.error; + + opts_def.netns_id = -2; + ct = func(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, sizeof(opts_def)); + opts_def.netns_id = -1; + if (ct) + bpf_ct_release(ct); + else + test_einval_netns_id = opts_def.error; + + ct = func(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, sizeof(opts_def) - 1); + if (ct) + bpf_ct_release(ct); + else + test_einval_len_opts = opts_def.error; + + opts_def.l4proto = IPPROTO_ICMP; + ct = func(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, sizeof(opts_def)); + opts_def.l4proto = IPPROTO_TCP; + if (ct) + bpf_ct_release(ct); + else + test_eproto_l4proto = opts_def.error; + + opts_def.netns_id = 0xf00f; + ct = func(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, sizeof(opts_def)); + opts_def.netns_id = -1; + if (ct) + bpf_ct_release(ct); + else + test_enonet_netns_id = opts_def.error; + + ct = func(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, sizeof(opts_def)); + if (ct) + bpf_ct_release(ct); + else + test_enoent_lookup = opts_def.error; + + ct = func(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4) - 1, &opts_def, sizeof(opts_def)); + if (ct) + bpf_ct_release(ct); + else + test_eafnosupport = opts_def.error; +} + +SEC("xdp") +int nf_xdp_ct_test(struct xdp_md *ctx) +{ + nf_ct_test((void *)bpf_xdp_ct_lookup, ctx); + return 0; +} + +SEC("tc") +int nf_skb_ct_test(struct __sk_buff *ctx) +{ + nf_ct_test((void *)bpf_skb_ct_lookup, ctx); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/btf_decl_tag.c b/tools/testing/selftests/bpf/progs/test_btf_decl_tag.c similarity index 100% rename from tools/testing/selftests/bpf/progs/btf_decl_tag.c rename to tools/testing/selftests/bpf/progs/test_btf_decl_tag.c diff --git a/tools/testing/selftests/bpf/progs/test_btf_haskv.c b/tools/testing/selftests/bpf/progs/test_btf_haskv.c index 160ead6c67b2..07c94df13660 100644 --- a/tools/testing/selftests/bpf/progs/test_btf_haskv.c +++ b/tools/testing/selftests/bpf/progs/test_btf_haskv.c @@ -9,12 +9,15 @@ struct ipv_counts { unsigned int v6; }; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" struct bpf_map_def SEC("maps") btf_map = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(int), .value_size = sizeof(struct ipv_counts), .max_entries = 4, }; +#pragma GCC diagnostic pop BPF_ANNOTATE_KV_PAIR(btf_map, int, struct ipv_counts); diff --git a/tools/testing/selftests/bpf/progs/test_btf_newkv.c b/tools/testing/selftests/bpf/progs/test_btf_newkv.c index 1884a5bd10f5..762671a2e90c 100644 --- a/tools/testing/selftests/bpf/progs/test_btf_newkv.c +++ b/tools/testing/selftests/bpf/progs/test_btf_newkv.c @@ -9,6 +9,8 @@ struct ipv_counts { unsigned int v6; }; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" /* just to validate we can handle maps in multiple sections */ struct bpf_map_def SEC("maps") btf_map_legacy = { .type = BPF_MAP_TYPE_ARRAY, @@ -16,6 +18,7 @@ struct bpf_map_def SEC("maps") btf_map_legacy = { .value_size = sizeof(long long), .max_entries = 4, }; +#pragma GCC diagnostic pop BPF_ANNOTATE_KV_PAIR(btf_map_legacy, int, struct ipv_counts); diff --git a/tools/testing/selftests/bpf/progs/test_btf_nokv.c b/tools/testing/selftests/bpf/progs/test_btf_nokv.c index 15e0f9945fe4..1dabb88f8cb4 100644 --- a/tools/testing/selftests/bpf/progs/test_btf_nokv.c +++ b/tools/testing/selftests/bpf/progs/test_btf_nokv.c @@ -8,12 +8,12 @@ struct ipv_counts { unsigned int v6; }; -struct bpf_map_def SEC("maps") btf_map = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(int), - .value_size = sizeof(struct ipv_counts), - .max_entries = 4, -}; +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(struct ipv_counts)); + __uint(max_entries, 4); +} btf_map SEC(".maps"); __attribute__((noinline)) int test_long_fname_2(void) diff --git a/tools/testing/selftests/bpf/progs/test_custom_sec_handlers.c b/tools/testing/selftests/bpf/progs/test_custom_sec_handlers.c new file mode 100644 index 000000000000..4061f701ca50 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_custom_sec_handlers.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Facebook */ + +#include "vmlinux.h" +#include +#include + +const volatile int my_pid; + +bool abc1_called; +bool abc2_called; +bool custom1_called; +bool custom2_called; +bool kprobe1_called; +bool xyz_called; + +SEC("abc") +int abc1(void *ctx) +{ + abc1_called = true; + return 0; +} + +SEC("abc/whatever") +int abc2(void *ctx) +{ + abc2_called = true; + return 0; +} + +SEC("custom") +int custom1(void *ctx) +{ + custom1_called = true; + return 0; +} + +SEC("custom/something") +int custom2(void *ctx) +{ + custom2_called = true; + return 0; +} + +SEC("kprobe") +int kprobe1(void *ctx) +{ + kprobe1_called = true; + return 0; +} + +SEC("xyz/blah") +int xyz(void *ctx) +{ + int whatever; + + /* use sleepable helper, custom handler should set sleepable flag */ + bpf_copy_from_user(&whatever, sizeof(whatever), NULL); + xyz_called = true; + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_probe_user.c b/tools/testing/selftests/bpf/progs/test_probe_user.c index 8812a90da4eb..702578a5e496 100644 --- a/tools/testing/selftests/bpf/progs/test_probe_user.c +++ b/tools/testing/selftests/bpf/progs/test_probe_user.c @@ -7,20 +7,7 @@ #include #include - -#if defined(__TARGET_ARCH_x86) -#define SYSCALL_WRAPPER 1 -#define SYS_PREFIX "__x64_" -#elif defined(__TARGET_ARCH_s390) -#define SYSCALL_WRAPPER 1 -#define SYS_PREFIX "__s390x_" -#elif defined(__TARGET_ARCH_arm64) -#define SYSCALL_WRAPPER 1 -#define SYS_PREFIX "__arm64_" -#else -#define SYSCALL_WRAPPER 0 -#define SYS_PREFIX "" -#endif +#include "bpf_misc.h" static struct sockaddr_in old; diff --git a/tools/testing/selftests/bpf/progs/test_ringbuf.c b/tools/testing/selftests/bpf/progs/test_ringbuf.c index eaa7d9dba0be..5bdc0d38efc0 100644 --- a/tools/testing/selftests/bpf/progs/test_ringbuf.c +++ b/tools/testing/selftests/bpf/progs/test_ringbuf.c @@ -3,6 +3,7 @@ #include #include +#include "bpf_misc.h" char _license[] SEC("license") = "GPL"; @@ -35,7 +36,7 @@ long prod_pos = 0; /* inner state */ long seq = 0; -SEC("fentry/__x64_sys_getpgid") +SEC("fentry/" SYS_PREFIX "sys_getpgid") int test_ringbuf(void *ctx) { int cur_pid = bpf_get_current_pid_tgid() >> 32; diff --git a/tools/testing/selftests/bpf/progs/test_send_signal_kern.c b/tools/testing/selftests/bpf/progs/test_send_signal_kern.c index b4233d3efac2..92354cd72044 100644 --- a/tools/testing/selftests/bpf/progs/test_send_signal_kern.c +++ b/tools/testing/selftests/bpf/progs/test_send_signal_kern.c @@ -10,7 +10,7 @@ static __always_inline int bpf_send_signal_test(void *ctx) { int ret; - if (status != 0 || sig == 0 || pid == 0) + if (status != 0 || pid == 0) return 0; if ((bpf_get_current_pid_tgid() >> 32) == pid) { diff --git a/tools/testing/selftests/bpf/progs/test_sk_lookup.c b/tools/testing/selftests/bpf/progs/test_sk_lookup.c index 83b0aaa52ef7..6058dcb11b36 100644 --- a/tools/testing/selftests/bpf/progs/test_sk_lookup.c +++ b/tools/testing/selftests/bpf/progs/test_sk_lookup.c @@ -392,6 +392,7 @@ int ctx_narrow_access(struct bpf_sk_lookup *ctx) { struct bpf_sock *sk; int err, family; + __u32 val_u32; bool v4; v4 = (ctx->family == AF_INET); @@ -412,12 +413,22 @@ int ctx_narrow_access(struct bpf_sk_lookup *ctx) /* Narrow loads from remote_port field. Expect SRC_PORT. */ if (LSB(ctx->remote_port, 0) != ((SRC_PORT >> 0) & 0xff) || - LSB(ctx->remote_port, 1) != ((SRC_PORT >> 8) & 0xff) || - LSB(ctx->remote_port, 2) != 0 || LSB(ctx->remote_port, 3) != 0) + LSB(ctx->remote_port, 1) != ((SRC_PORT >> 8) & 0xff)) return SK_DROP; if (LSW(ctx->remote_port, 0) != SRC_PORT) return SK_DROP; + /* + * NOTE: 4-byte load from bpf_sk_lookup at remote_port offset + * is quirky. It gets rewritten by the access converter to a + * 2-byte load for backward compatibility. Treating the load + * result as a be16 value makes the code portable across + * little- and big-endian platforms. + */ + val_u32 = *(__u32 *)&ctx->remote_port; + if (val_u32 != SRC_PORT) + return SK_DROP; + /* Narrow loads from local_port field. Expect DST_PORT. */ if (LSB(ctx->local_port, 0) != ((DST_PORT >> 0) & 0xff) || LSB(ctx->local_port, 1) != ((DST_PORT >> 8) & 0xff) || diff --git a/tools/testing/selftests/bpf/progs/test_skb_cgroup_id_kern.c b/tools/testing/selftests/bpf/progs/test_skb_cgroup_id_kern.c index c304cd5b8cad..37aacc66cd68 100644 --- a/tools/testing/selftests/bpf/progs/test_skb_cgroup_id_kern.c +++ b/tools/testing/selftests/bpf/progs/test_skb_cgroup_id_kern.c @@ -10,12 +10,12 @@ #define NUM_CGROUP_LEVELS 4 -struct bpf_map_def SEC("maps") cgroup_ids = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(__u32), - .value_size = sizeof(__u64), - .max_entries = NUM_CGROUP_LEVELS, -}; +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, __u64); + __uint(max_entries, NUM_CGROUP_LEVELS); +} cgroup_ids SEC(".maps"); static __always_inline void log_nth_level(struct __sk_buff *skb, __u32 level) { diff --git a/tools/testing/selftests/bpf/progs/test_sock_fields.c b/tools/testing/selftests/bpf/progs/test_sock_fields.c index 81b57b9aaaea..9f4b8f9f1181 100644 --- a/tools/testing/selftests/bpf/progs/test_sock_fields.c +++ b/tools/testing/selftests/bpf/progs/test_sock_fields.c @@ -12,6 +12,7 @@ enum bpf_linum_array_idx { EGRESS_LINUM_IDX, INGRESS_LINUM_IDX, + READ_SK_DST_PORT_LINUM_IDX, __NR_BPF_LINUM_ARRAY_IDX, }; @@ -113,7 +114,7 @@ static void tpcpy(struct bpf_tcp_sock *dst, #define RET_LOG() ({ \ linum = __LINE__; \ - bpf_map_update_elem(&linum_map, &linum_idx, &linum, BPF_NOEXIST); \ + bpf_map_update_elem(&linum_map, &linum_idx, &linum, BPF_ANY); \ return CG_OK; \ }) @@ -133,11 +134,11 @@ int egress_read_sock_fields(struct __sk_buff *skb) if (!sk) RET_LOG(); - /* Not the testing egress traffic or - * TCP_LISTEN (10) socket will be copied at the ingress side. + /* Not testing the egress traffic or the listening socket, + * which are covered by the cgroup_skb/ingress test program. */ if (sk->family != AF_INET6 || !is_loopback6(sk->src_ip6) || - sk->state == 10) + sk->state == BPF_TCP_LISTEN) return CG_OK; if (sk->src_port == bpf_ntohs(srv_sa6.sin6_port)) { @@ -231,8 +232,8 @@ int ingress_read_sock_fields(struct __sk_buff *skb) sk->src_port != bpf_ntohs(srv_sa6.sin6_port)) return CG_OK; - /* Only interested in TCP_LISTEN */ - if (sk->state != 10) + /* Only interested in the listening socket */ + if (sk->state != BPF_TCP_LISTEN) return CG_OK; /* It must be a fullsock for cgroup_skb/ingress prog */ @@ -250,4 +251,54 @@ int ingress_read_sock_fields(struct __sk_buff *skb) return CG_OK; } +/* + * NOTE: 4-byte load from bpf_sock at dst_port offset is quirky. It + * gets rewritten by the access converter to a 2-byte load for + * backward compatibility. Treating the load result as a be16 value + * makes the code portable across little- and big-endian platforms. + */ +static __noinline bool sk_dst_port__load_word(struct bpf_sock *sk) +{ + __u32 *word = (__u32 *)&sk->dst_port; + return word[0] == bpf_htons(0xcafe); +} + +static __noinline bool sk_dst_port__load_half(struct bpf_sock *sk) +{ + __u16 *half = (__u16 *)&sk->dst_port; + return half[0] == bpf_htons(0xcafe); +} + +static __noinline bool sk_dst_port__load_byte(struct bpf_sock *sk) +{ + __u8 *byte = (__u8 *)&sk->dst_port; + return byte[0] == 0xca && byte[1] == 0xfe; +} + +SEC("cgroup_skb/egress") +int read_sk_dst_port(struct __sk_buff *skb) +{ + __u32 linum, linum_idx; + struct bpf_sock *sk; + + linum_idx = READ_SK_DST_PORT_LINUM_IDX; + + sk = skb->sk; + if (!sk) + RET_LOG(); + + /* Ignore everything but the SYN from the client socket */ + if (sk->state != BPF_TCP_SYN_SENT) + return CG_OK; + + if (!sk_dst_port__load_word(sk)) + RET_LOG(); + if (!sk_dst_port__load_half(sk)) + RET_LOG(); + if (!sk_dst_port__load_byte(sk)) + RET_LOG(); + + return CG_OK; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_progs_query.c b/tools/testing/selftests/bpf/progs/test_sockmap_progs_query.c new file mode 100644 index 000000000000..9d58d61c0dee --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_sockmap_progs_query.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "vmlinux.h" +#include + +struct { + __uint(type, BPF_MAP_TYPE_SOCKMAP); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} sock_map SEC(".maps"); + +SEC("sk_skb") +int prog_skb_verdict(struct __sk_buff *skb) +{ + return SK_PASS; +} + +SEC("sk_msg") +int prog_skmsg_verdict(struct sk_msg_md *msg) +{ + return SK_PASS; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_subskeleton.c b/tools/testing/selftests/bpf/progs/test_subskeleton.c new file mode 100644 index 000000000000..006417974372 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_subskeleton.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include + +/* volatile to force a read, compiler may assume 0 otherwise */ +const volatile int rovar1; +int out1; + +/* Override weak symbol in test_subskeleton_lib */ +int var5 = 5; + +extern volatile bool CONFIG_BPF_SYSCALL __kconfig; + +extern int lib_routine(void); + +SEC("raw_tp/sys_enter") +int handler1(const void *ctx) +{ + (void) CONFIG_BPF_SYSCALL; + + out1 = lib_routine() * rovar1; + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_subskeleton_lib.c b/tools/testing/selftests/bpf/progs/test_subskeleton_lib.c new file mode 100644 index 000000000000..ecfafe812c36 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_subskeleton_lib.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include + +/* volatile to force a read */ +const volatile int var1; +volatile int var2 = 1; +struct { + int var3_1; + __s64 var3_2; +} var3; +int libout1; + +extern volatile bool CONFIG_BPF_SYSCALL __kconfig; + +int var4[4]; + +__weak int var5 SEC(".data"); + +/* Fully contained within library extern-and-definition */ +extern int var6; + +int var7 SEC(".data.custom"); + +int (*fn_ptr)(void); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 16); +} map1 SEC(".maps"); + +extern struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 16); +} map2 SEC(".maps"); + +int lib_routine(void) +{ + __u32 key = 1, value = 2; + + (void) CONFIG_BPF_SYSCALL; + bpf_map_update_elem(&map2, &key, &value, BPF_ANY); + + libout1 = var1 + var2 + var3.var3_1 + var3.var3_2 + var5 + var6; + return libout1; +} + +SEC("perf_event") +int lib_perf_handler(struct pt_regs *ctx) +{ + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_subskeleton_lib2.c b/tools/testing/selftests/bpf/progs/test_subskeleton_lib2.c new file mode 100644 index 000000000000..80238486b7ce --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_subskeleton_lib2.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) Meta Platforms, Inc. and affiliates. */ + +#include +#include + +int var6 = 6; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 16); +} map2 SEC(".maps"); + +char LICENSE[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_tc_dtime.c b/tools/testing/selftests/bpf/progs/test_tc_dtime.c new file mode 100644 index 000000000000..06f300d06dbd --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_tc_dtime.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 Meta + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* veth_src --- veth_src_fwd --- veth_det_fwd --- veth_dst + * | | + * ns_src | ns_fwd | ns_dst + * + * ns_src and ns_dst: ENDHOST namespace + * ns_fwd: Fowarding namespace + */ + +#define ctx_ptr(field) (void *)(long)(field) + +#define ip4_src __bpf_htonl(0xac100164) /* 172.16.1.100 */ +#define ip4_dst __bpf_htonl(0xac100264) /* 172.16.2.100 */ + +#define ip6_src { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x01, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe } +#define ip6_dst { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x02, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe } + +#define v6_equal(a, b) (a.s6_addr32[0] == b.s6_addr32[0] && \ + a.s6_addr32[1] == b.s6_addr32[1] && \ + a.s6_addr32[2] == b.s6_addr32[2] && \ + a.s6_addr32[3] == b.s6_addr32[3]) + +volatile const __u32 IFINDEX_SRC; +volatile const __u32 IFINDEX_DST; + +#define EGRESS_ENDHOST_MAGIC 0x0b9fbeef +#define INGRESS_FWDNS_MAGIC 0x1b9fbeef +#define EGRESS_FWDNS_MAGIC 0x2b9fbeef + +enum { + INGRESS_FWDNS_P100, + INGRESS_FWDNS_P101, + EGRESS_FWDNS_P100, + EGRESS_FWDNS_P101, + INGRESS_ENDHOST, + EGRESS_ENDHOST, + SET_DTIME, + __MAX_CNT, +}; + +enum { + TCP_IP6_CLEAR_DTIME, + TCP_IP4, + TCP_IP6, + UDP_IP4, + UDP_IP6, + TCP_IP4_RT_FWD, + TCP_IP6_RT_FWD, + UDP_IP4_RT_FWD, + UDP_IP6_RT_FWD, + UKN_TEST, + __NR_TESTS, +}; + +enum { + SRC_NS = 1, + DST_NS, +}; + +__u32 dtimes[__NR_TESTS][__MAX_CNT] = {}; +__u32 errs[__NR_TESTS][__MAX_CNT] = {}; +__u32 test = 0; + +static void inc_dtimes(__u32 idx) +{ + if (test < __NR_TESTS) + dtimes[test][idx]++; + else + dtimes[UKN_TEST][idx]++; +} + +static void inc_errs(__u32 idx) +{ + if (test < __NR_TESTS) + errs[test][idx]++; + else + errs[UKN_TEST][idx]++; +} + +static int skb_proto(int type) +{ + return type & 0xff; +} + +static int skb_ns(int type) +{ + return (type >> 8) & 0xff; +} + +static bool fwdns_clear_dtime(void) +{ + return test == TCP_IP6_CLEAR_DTIME; +} + +static bool bpf_fwd(void) +{ + return test < TCP_IP4_RT_FWD; +} + +/* -1: parse error: TC_ACT_SHOT + * 0: not testing traffic: TC_ACT_OK + * >0: first byte is the inet_proto, second byte has the netns + * of the sender + */ +static int skb_get_type(struct __sk_buff *skb) +{ + void *data_end = ctx_ptr(skb->data_end); + void *data = ctx_ptr(skb->data); + __u8 inet_proto = 0, ns = 0; + struct ipv6hdr *ip6h; + struct iphdr *iph; + + switch (skb->protocol) { + case __bpf_htons(ETH_P_IP): + iph = data + sizeof(struct ethhdr); + if (iph + 1 > data_end) + return -1; + if (iph->saddr == ip4_src) + ns = SRC_NS; + else if (iph->saddr == ip4_dst) + ns = DST_NS; + inet_proto = iph->protocol; + break; + case __bpf_htons(ETH_P_IPV6): + ip6h = data + sizeof(struct ethhdr); + if (ip6h + 1 > data_end) + return -1; + if (v6_equal(ip6h->saddr, (struct in6_addr)ip6_src)) + ns = SRC_NS; + else if (v6_equal(ip6h->saddr, (struct in6_addr)ip6_dst)) + ns = DST_NS; + inet_proto = ip6h->nexthdr; + break; + default: + return 0; + } + + if ((inet_proto != IPPROTO_TCP && inet_proto != IPPROTO_UDP) || !ns) + return 0; + + return (ns << 8 | inet_proto); +} + +/* format: direction@iface@netns + * egress@veth_(src|dst)@ns_(src|dst) + */ +SEC("tc") +int egress_host(struct __sk_buff *skb) +{ + int skb_type; + + skb_type = skb_get_type(skb); + if (skb_type == -1) + return TC_ACT_SHOT; + if (!skb_type) + return TC_ACT_OK; + + if (skb_proto(skb_type) == IPPROTO_TCP) { + if (skb->tstamp_type == BPF_SKB_TSTAMP_DELIVERY_MONO && + skb->tstamp) + inc_dtimes(EGRESS_ENDHOST); + else + inc_errs(EGRESS_ENDHOST); + } else { + if (skb->tstamp_type == BPF_SKB_TSTAMP_UNSPEC && + skb->tstamp) + inc_dtimes(EGRESS_ENDHOST); + else + inc_errs(EGRESS_ENDHOST); + } + + skb->tstamp = EGRESS_ENDHOST_MAGIC; + + return TC_ACT_OK; +} + +/* ingress@veth_(src|dst)@ns_(src|dst) */ +SEC("tc") +int ingress_host(struct __sk_buff *skb) +{ + int skb_type; + + skb_type = skb_get_type(skb); + if (skb_type == -1) + return TC_ACT_SHOT; + if (!skb_type) + return TC_ACT_OK; + + if (skb->tstamp_type == BPF_SKB_TSTAMP_DELIVERY_MONO && + skb->tstamp == EGRESS_FWDNS_MAGIC) + inc_dtimes(INGRESS_ENDHOST); + else + inc_errs(INGRESS_ENDHOST); + + return TC_ACT_OK; +} + +/* ingress@veth_(src|dst)_fwd@ns_fwd priority 100 */ +SEC("tc") +int ingress_fwdns_prio100(struct __sk_buff *skb) +{ + int skb_type; + + skb_type = skb_get_type(skb); + if (skb_type == -1) + return TC_ACT_SHOT; + if (!skb_type) + return TC_ACT_OK; + + /* delivery_time is only available to the ingress + * if the tc-bpf checks the skb->tstamp_type. + */ + if (skb->tstamp == EGRESS_ENDHOST_MAGIC) + inc_errs(INGRESS_FWDNS_P100); + + if (fwdns_clear_dtime()) + skb->tstamp = 0; + + return TC_ACT_UNSPEC; +} + +/* egress@veth_(src|dst)_fwd@ns_fwd priority 100 */ +SEC("tc") +int egress_fwdns_prio100(struct __sk_buff *skb) +{ + int skb_type; + + skb_type = skb_get_type(skb); + if (skb_type == -1) + return TC_ACT_SHOT; + if (!skb_type) + return TC_ACT_OK; + + /* delivery_time is always available to egress even + * the tc-bpf did not use the tstamp_type. + */ + if (skb->tstamp == INGRESS_FWDNS_MAGIC) + inc_dtimes(EGRESS_FWDNS_P100); + else + inc_errs(EGRESS_FWDNS_P100); + + if (fwdns_clear_dtime()) + skb->tstamp = 0; + + return TC_ACT_UNSPEC; +} + +/* ingress@veth_(src|dst)_fwd@ns_fwd priority 101 */ +SEC("tc") +int ingress_fwdns_prio101(struct __sk_buff *skb) +{ + __u64 expected_dtime = EGRESS_ENDHOST_MAGIC; + int skb_type; + + skb_type = skb_get_type(skb); + if (skb_type == -1 || !skb_type) + /* Should have handled in prio100 */ + return TC_ACT_SHOT; + + if (skb_proto(skb_type) == IPPROTO_UDP) + expected_dtime = 0; + + if (skb->tstamp_type) { + if (fwdns_clear_dtime() || + skb->tstamp_type != BPF_SKB_TSTAMP_DELIVERY_MONO || + skb->tstamp != expected_dtime) + inc_errs(INGRESS_FWDNS_P101); + else + inc_dtimes(INGRESS_FWDNS_P101); + } else { + if (!fwdns_clear_dtime() && expected_dtime) + inc_errs(INGRESS_FWDNS_P101); + } + + if (skb->tstamp_type == BPF_SKB_TSTAMP_DELIVERY_MONO) { + skb->tstamp = INGRESS_FWDNS_MAGIC; + } else { + if (bpf_skb_set_tstamp(skb, INGRESS_FWDNS_MAGIC, + BPF_SKB_TSTAMP_DELIVERY_MONO)) + inc_errs(SET_DTIME); + if (!bpf_skb_set_tstamp(skb, INGRESS_FWDNS_MAGIC, + BPF_SKB_TSTAMP_UNSPEC)) + inc_errs(SET_DTIME); + } + + if (skb_ns(skb_type) == SRC_NS) + return bpf_fwd() ? + bpf_redirect_neigh(IFINDEX_DST, NULL, 0, 0) : TC_ACT_OK; + else + return bpf_fwd() ? + bpf_redirect_neigh(IFINDEX_SRC, NULL, 0, 0) : TC_ACT_OK; +} + +/* egress@veth_(src|dst)_fwd@ns_fwd priority 101 */ +SEC("tc") +int egress_fwdns_prio101(struct __sk_buff *skb) +{ + int skb_type; + + skb_type = skb_get_type(skb); + if (skb_type == -1 || !skb_type) + /* Should have handled in prio100 */ + return TC_ACT_SHOT; + + if (skb->tstamp_type) { + if (fwdns_clear_dtime() || + skb->tstamp_type != BPF_SKB_TSTAMP_DELIVERY_MONO || + skb->tstamp != INGRESS_FWDNS_MAGIC) + inc_errs(EGRESS_FWDNS_P101); + else + inc_dtimes(EGRESS_FWDNS_P101); + } else { + if (!fwdns_clear_dtime()) + inc_errs(EGRESS_FWDNS_P101); + } + + if (skb->tstamp_type == BPF_SKB_TSTAMP_DELIVERY_MONO) { + skb->tstamp = EGRESS_FWDNS_MAGIC; + } else { + if (bpf_skb_set_tstamp(skb, EGRESS_FWDNS_MAGIC, + BPF_SKB_TSTAMP_DELIVERY_MONO)) + inc_errs(SET_DTIME); + if (!bpf_skb_set_tstamp(skb, INGRESS_FWDNS_MAGIC, + BPF_SKB_TSTAMP_UNSPEC)) + inc_errs(SET_DTIME); + } + + return TC_ACT_OK; +} + +char __license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_tc_edt.c b/tools/testing/selftests/bpf/progs/test_tc_edt.c index bf28814bfde5..950a70b61e74 100644 --- a/tools/testing/selftests/bpf/progs/test_tc_edt.c +++ b/tools/testing/selftests/bpf/progs/test_tc_edt.c @@ -17,12 +17,12 @@ #define THROTTLE_RATE_BPS (5 * 1000 * 1000) /* flow_key => last_tstamp timestamp used */ -struct bpf_map_def SEC("maps") flow_map = { - .type = BPF_MAP_TYPE_HASH, - .key_size = sizeof(uint32_t), - .value_size = sizeof(uint64_t), - .max_entries = 1, -}; +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, uint32_t); + __type(value, uint64_t); + __uint(max_entries, 1); +} flow_map SEC(".maps"); static inline int throttle_flow(struct __sk_buff *skb) { diff --git a/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c b/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c index cd747cd93dbe..6edebce563b5 100644 --- a/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c +++ b/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c @@ -16,12 +16,12 @@ #include #include -struct bpf_map_def SEC("maps") results = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(__u32), - .value_size = sizeof(__u32), - .max_entries = 3, -}; +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 3); +} results SEC(".maps"); static __always_inline __s64 gen_syncookie(void *data_end, struct bpf_sock *sk, void *iph, __u32 ip_size, diff --git a/tools/testing/selftests/bpf/progs/test_xdp_adjust_tail_grow.c b/tools/testing/selftests/bpf/progs/test_xdp_adjust_tail_grow.c index 199c61b7d062..53b64c999450 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_adjust_tail_grow.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_adjust_tail_grow.c @@ -7,11 +7,10 @@ int _xdp_adjust_tail_grow(struct xdp_md *xdp) { void *data_end = (void *)(long)xdp->data_end; void *data = (void *)(long)xdp->data; - unsigned int data_len; + int data_len = bpf_xdp_get_buff_len(xdp); int offset = 0; /* Data length determine test case */ - data_len = data_end - data; if (data_len == 54) { /* sizeof(pkt_v4) */ offset = 4096; /* test too large offset */ @@ -20,7 +19,12 @@ int _xdp_adjust_tail_grow(struct xdp_md *xdp) } else if (data_len == 64) { offset = 128; } else if (data_len == 128) { - offset = 4096 - 256 - 320 - data_len; /* Max tail grow 3520 */ + /* Max tail grow 3520 */ + offset = 4096 - 256 - 320 - data_len; + } else if (data_len == 9000) { + offset = 10; + } else if (data_len == 9001) { + offset = 4096; } else { return XDP_ABORTED; /* No matching test */ } diff --git a/tools/testing/selftests/bpf/progs/test_xdp_adjust_tail_shrink.c b/tools/testing/selftests/bpf/progs/test_xdp_adjust_tail_shrink.c index b7448253d135..ca68c038357c 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_adjust_tail_shrink.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_adjust_tail_shrink.c @@ -12,14 +12,38 @@ SEC("xdp") int _xdp_adjust_tail_shrink(struct xdp_md *xdp) { - void *data_end = (void *)(long)xdp->data_end; - void *data = (void *)(long)xdp->data; + __u8 *data_end = (void *)(long)xdp->data_end; + __u8 *data = (void *)(long)xdp->data; int offset = 0; - if (data_end - data == 54) /* sizeof(pkt_v4) */ + switch (bpf_xdp_get_buff_len(xdp)) { + case 54: + /* sizeof(pkt_v4) */ offset = 256; /* shrink too much */ - else + break; + case 9000: + /* non-linear buff test cases */ + if (data + 1 > data_end) + return XDP_DROP; + + switch (data[0]) { + case 0: + offset = 10; + break; + case 1: + offset = 4100; + break; + case 2: + offset = 8200; + break; + default: + return XDP_DROP; + } + break; + default: offset = 20; + break; + } if (bpf_xdp_adjust_tail(xdp, 0 - offset)) return XDP_DROP; return XDP_TX; diff --git a/tools/testing/selftests/bpf/progs/test_xdp_bpf2bpf.c b/tools/testing/selftests/bpf/progs/test_xdp_bpf2bpf.c index 58cf4345f5cc..3379d303f41a 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_bpf2bpf.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_bpf2bpf.c @@ -49,7 +49,7 @@ int BPF_PROG(trace_on_entry, struct xdp_buff *xdp) void *data = (void *)(long)xdp->data; meta.ifindex = xdp->rxq->dev->ifindex; - meta.pkt_len = data_end - data; + meta.pkt_len = bpf_xdp_get_buff_len((struct xdp_md *)xdp); bpf_xdp_output(xdp, &perf_buf_map, ((__u64) meta.pkt_len << 32) | BPF_F_CURRENT_CPU, diff --git a/tools/testing/selftests/bpf/progs/test_xdp_do_redirect.c b/tools/testing/selftests/bpf/progs/test_xdp_do_redirect.c new file mode 100644 index 000000000000..77a123071940 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_xdp_do_redirect.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +#define ETH_ALEN 6 +#define HDR_SZ (sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + sizeof(struct udphdr)) +const volatile int ifindex_out; +const volatile int ifindex_in; +const volatile __u8 expect_dst[ETH_ALEN]; +volatile int pkts_seen_xdp = 0; +volatile int pkts_seen_zero = 0; +volatile int pkts_seen_tc = 0; +volatile int retcode = XDP_REDIRECT; + +SEC("xdp") +int xdp_redirect(struct xdp_md *xdp) +{ + __u32 *metadata = (void *)(long)xdp->data_meta; + void *data_end = (void *)(long)xdp->data_end; + void *data = (void *)(long)xdp->data; + + __u8 *payload = data + HDR_SZ; + int ret = retcode; + + if (payload + 1 > data_end) + return XDP_ABORTED; + + if (xdp->ingress_ifindex != ifindex_in) + return XDP_ABORTED; + + if (metadata + 1 > data) + return XDP_ABORTED; + + if (*metadata != 0x42) + return XDP_ABORTED; + + if (*payload == 0) { + *payload = 0x42; + pkts_seen_zero++; + } + + if (bpf_xdp_adjust_meta(xdp, 4)) + return XDP_ABORTED; + + if (retcode > XDP_PASS) + retcode--; + + if (ret == XDP_REDIRECT) + return bpf_redirect(ifindex_out, 0); + + return ret; +} + +static bool check_pkt(void *data, void *data_end) +{ + struct ipv6hdr *iph = data + sizeof(struct ethhdr); + __u8 *payload = data + HDR_SZ; + + if (payload + 1 > data_end) + return false; + + if (iph->nexthdr != IPPROTO_UDP || *payload != 0x42) + return false; + + /* reset the payload so the same packet doesn't get counted twice when + * it cycles back through the kernel path and out the dst veth + */ + *payload = 0; + return true; +} + +SEC("xdp") +int xdp_count_pkts(struct xdp_md *xdp) +{ + void *data = (void *)(long)xdp->data; + void *data_end = (void *)(long)xdp->data_end; + + if (check_pkt(data, data_end)) + pkts_seen_xdp++; + + /* Return XDP_DROP to make sure the data page is recycled, like when it + * exits a physical NIC. Recycled pages will be counted in the + * pkts_seen_zero counter above. + */ + return XDP_DROP; +} + +SEC("tc") +int tc_count_pkts(struct __sk_buff *skb) +{ + void *data = (void *)(long)skb->data; + void *data_end = (void *)(long)skb->data_end; + + if (check_pkt(data, data_end)) + pkts_seen_tc++; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_xdp_update_frags.c b/tools/testing/selftests/bpf/progs/test_xdp_update_frags.c new file mode 100644 index 000000000000..2a3496d8e327 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_xdp_update_frags.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include +#include + +int _version SEC("version") = 1; + +SEC("xdp.frags") +int xdp_adjust_frags(struct xdp_md *xdp) +{ + __u8 *data_end = (void *)(long)xdp->data_end; + __u8 *data = (void *)(long)xdp->data; + __u8 val[16] = {}; + __u32 offset; + int err; + + if (data + sizeof(__u32) > data_end) + return XDP_DROP; + + offset = *(__u32 *)data; + err = bpf_xdp_load_bytes(xdp, offset, val, sizeof(val)); + if (err < 0) + return XDP_DROP; + + if (val[0] != 0xaa || val[15] != 0xaa) /* marker */ + return XDP_DROP; + + val[0] = 0xbb; /* update the marker */ + val[15] = 0xbb; + err = bpf_xdp_store_bytes(xdp, offset, val, sizeof(val)); + if (err < 0) + return XDP_DROP; + + return XDP_PASS; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_xdp_with_cpumap_frags_helpers.c b/tools/testing/selftests/bpf/progs/test_xdp_with_cpumap_frags_helpers.c new file mode 100644 index 000000000000..97ed625bb70a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_xdp_with_cpumap_frags_helpers.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +#define IFINDEX_LO 1 + +struct { + __uint(type, BPF_MAP_TYPE_CPUMAP); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(struct bpf_cpumap_val)); + __uint(max_entries, 4); +} cpu_map SEC(".maps"); + +SEC("xdp/cpumap") +int xdp_dummy_cm(struct xdp_md *ctx) +{ + return XDP_PASS; +} + +SEC("xdp.frags/cpumap") +int xdp_dummy_cm_frags(struct xdp_md *ctx) +{ + return XDP_PASS; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_xdp_with_cpumap_helpers.c b/tools/testing/selftests/bpf/progs/test_xdp_with_cpumap_helpers.c index 532025057711..20ec6723df18 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_with_cpumap_helpers.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_with_cpumap_helpers.c @@ -24,7 +24,7 @@ int xdp_dummy_prog(struct xdp_md *ctx) return XDP_PASS; } -SEC("xdp_cpumap/dummy_cm") +SEC("xdp/cpumap") int xdp_dummy_cm(struct xdp_md *ctx) { if (ctx->ingress_ifindex == IFINDEX_LO) @@ -33,4 +33,10 @@ int xdp_dummy_cm(struct xdp_md *ctx) return XDP_PASS; } +SEC("xdp.frags/cpumap") +int xdp_dummy_cm_frags(struct xdp_md *ctx) +{ + return XDP_PASS; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_xdp_with_devmap_frags_helpers.c b/tools/testing/selftests/bpf/progs/test_xdp_with_devmap_frags_helpers.c new file mode 100644 index 000000000000..cdcf7de7ec8c --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_xdp_with_devmap_frags_helpers.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_DEVMAP); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(struct bpf_devmap_val)); + __uint(max_entries, 4); +} dm_ports SEC(".maps"); + +/* valid program on DEVMAP entry via SEC name; + * has access to egress and ingress ifindex + */ +SEC("xdp/devmap") +int xdp_dummy_dm(struct xdp_md *ctx) +{ + return XDP_PASS; +} + +SEC("xdp.frags/devmap") +int xdp_dummy_dm_frags(struct xdp_md *ctx) +{ + return XDP_PASS; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_xdp_with_devmap_helpers.c b/tools/testing/selftests/bpf/progs/test_xdp_with_devmap_helpers.c index 1e6b9c38ea6d..4139a14f9996 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_with_devmap_helpers.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_with_devmap_helpers.c @@ -27,7 +27,7 @@ int xdp_dummy_prog(struct xdp_md *ctx) /* valid program on DEVMAP entry via SEC name; * has access to egress and ingress ifindex */ -SEC("xdp_devmap/map_prog") +SEC("xdp/devmap") int xdp_dummy_dm(struct xdp_md *ctx) { char fmt[] = "devmap redirect: dev %u -> dev %u len %u\n"; @@ -40,4 +40,11 @@ int xdp_dummy_dm(struct xdp_md *ctx) return XDP_PASS; } + +SEC("xdp.frags/devmap") +int xdp_dummy_dm_frags(struct xdp_md *ctx) +{ + return XDP_PASS; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/trace_printk.c b/tools/testing/selftests/bpf/progs/trace_printk.c index 119582aa105a..6695478c2b25 100644 --- a/tools/testing/selftests/bpf/progs/trace_printk.c +++ b/tools/testing/selftests/bpf/progs/trace_printk.c @@ -4,6 +4,7 @@ #include "vmlinux.h" #include #include +#include "bpf_misc.h" char _license[] SEC("license") = "GPL"; @@ -12,7 +13,7 @@ int trace_printk_ran = 0; const char fmt[] = "Testing,testing %d\n"; -SEC("fentry/__x64_sys_nanosleep") +SEC("fentry/" SYS_PREFIX "sys_nanosleep") int sys_enter(void *ctx) { trace_printk_ret = bpf_trace_printk(fmt, sizeof(fmt), diff --git a/tools/testing/selftests/bpf/progs/trace_vprintk.c b/tools/testing/selftests/bpf/progs/trace_vprintk.c index d327241ba047..969306cd4f33 100644 --- a/tools/testing/selftests/bpf/progs/trace_vprintk.c +++ b/tools/testing/selftests/bpf/progs/trace_vprintk.c @@ -4,6 +4,7 @@ #include "vmlinux.h" #include #include +#include "bpf_misc.h" char _license[] SEC("license") = "GPL"; @@ -11,7 +12,7 @@ int null_data_vprintk_ret = 0; int trace_vprintk_ret = 0; int trace_vprintk_ran = 0; -SEC("fentry/__x64_sys_nanosleep") +SEC("fentry/" SYS_PREFIX "sys_nanosleep") int sys_enter(void *ctx) { static const char one[] = "1"; diff --git a/tools/testing/selftests/bpf/progs/trigger_bench.c b/tools/testing/selftests/bpf/progs/trigger_bench.c index 2098f3f27f18..2ab049b54d6c 100644 --- a/tools/testing/selftests/bpf/progs/trigger_bench.c +++ b/tools/testing/selftests/bpf/progs/trigger_bench.c @@ -5,6 +5,7 @@ #include #include #include +#include "bpf_misc.h" char _license[] SEC("license") = "GPL"; @@ -25,28 +26,28 @@ int BPF_PROG(bench_trigger_raw_tp, struct pt_regs *regs, long id) return 0; } -SEC("kprobe/__x64_sys_getpgid") +SEC("kprobe/" SYS_PREFIX "sys_getpgid") int bench_trigger_kprobe(void *ctx) { __sync_add_and_fetch(&hits, 1); return 0; } -SEC("fentry/__x64_sys_getpgid") +SEC("fentry/" SYS_PREFIX "sys_getpgid") int bench_trigger_fentry(void *ctx) { __sync_add_and_fetch(&hits, 1); return 0; } -SEC("fentry.s/__x64_sys_getpgid") +SEC("fentry.s/" SYS_PREFIX "sys_getpgid") int bench_trigger_fentry_sleep(void *ctx) { __sync_add_and_fetch(&hits, 1); return 0; } -SEC("fmod_ret/__x64_sys_getpgid") +SEC("fmod_ret/" SYS_PREFIX "sys_getpgid") int bench_trigger_fmodret(void *ctx) { __sync_add_and_fetch(&hits, 1); diff --git a/tools/testing/selftests/bpf/progs/xdp_redirect_multi_kern.c b/tools/testing/selftests/bpf/progs/xdp_redirect_multi_kern.c index 8395782b6e0a..97b26a30b59a 100644 --- a/tools/testing/selftests/bpf/progs/xdp_redirect_multi_kern.c +++ b/tools/testing/selftests/bpf/progs/xdp_redirect_multi_kern.c @@ -70,7 +70,7 @@ int xdp_redirect_map_all_prog(struct xdp_md *ctx) BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS); } -SEC("xdp_devmap/map_prog") +SEC("xdp/devmap") int xdp_devmap_prog(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; diff --git a/tools/testing/selftests/bpf/test_cgroup_storage.c b/tools/testing/selftests/bpf/test_cgroup_storage.c index 5b8314cd77fd..d6a1be4d8020 100644 --- a/tools/testing/selftests/bpf/test_cgroup_storage.c +++ b/tools/testing/selftests/bpf/test_cgroup_storage.c @@ -36,7 +36,7 @@ int main(int argc, char **argv) BPF_MOV64_REG(BPF_REG_0, BPF_REG_1), BPF_EXIT_INSN(), }; - size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); + size_t insns_cnt = ARRAY_SIZE(prog); int error = EXIT_FAILURE; int map_fd, percpu_map_fd, prog_fd, cgroup_fd; struct bpf_cgroup_storage_key key; diff --git a/tools/testing/selftests/bpf/test_cpp.cpp b/tools/testing/selftests/bpf/test_cpp.cpp index e00201de2890..19ad172036da 100644 --- a/tools/testing/selftests/bpf/test_cpp.cpp +++ b/tools/testing/selftests/bpf/test_cpp.cpp @@ -1,22 +1,107 @@ /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" #include +#pragma GCC diagnostic pop #include #include #include "test_core_extern.skel.h" -/* do nothing, just make sure we can link successfully */ +template +class Skeleton { +private: + T *skel; +public: + Skeleton(): skel(nullptr) { } + + ~Skeleton() { if (skel) T::destroy(skel); } + + int open(const struct bpf_object_open_opts *opts = nullptr) + { + int err; + + if (skel) + return -EBUSY; + + skel = T::open(opts); + err = libbpf_get_error(skel); + if (err) { + skel = nullptr; + return err; + } + + return 0; + } + + int load() { return T::load(skel); } + + int attach() { return T::attach(skel); } + + void detach() { return T::detach(skel); } + + const T* operator->() const { return skel; } + + T* operator->() { return skel; } + + const T *get() const { return skel; } +}; static void dump_printf(void *ctx, const char *fmt, va_list args) { } +static void try_skeleton_template() +{ + Skeleton skel; + std::string prog_name; + int err; + LIBBPF_OPTS(bpf_object_open_opts, opts); + + err = skel.open(&opts); + if (err) { + fprintf(stderr, "Skeleton open failed: %d\n", err); + return; + } + + skel->data->kern_ver = 123; + skel->data->int_val = skel->data->ushort_val; + + err = skel.load(); + if (err) { + fprintf(stderr, "Skeleton load failed: %d\n", err); + return; + } + + if (!skel->kconfig->CONFIG_BPF_SYSCALL) + fprintf(stderr, "Seems like CONFIG_BPF_SYSCALL isn't set?!\n"); + + err = skel.attach(); + if (err) { + fprintf(stderr, "Skeleton attach failed: %d\n", err); + return; + } + + prog_name = bpf_program__name(skel->progs.handle_sys_enter); + if (prog_name != "handle_sys_enter") + fprintf(stderr, "Unexpected program name: %s\n", prog_name.c_str()); + + bpf_link__destroy(skel->links.handle_sys_enter); + skel->links.handle_sys_enter = bpf_program__attach(skel->progs.handle_sys_enter); + + skel.detach(); + + /* destructor will destory underlying skeleton */ +} + int main(int argc, char *argv[]) { struct btf_dump_opts opts = { }; struct test_core_extern *skel; struct btf *btf; + try_skeleton_template(); + /* libbpf.h */ libbpf_set_print(NULL); @@ -25,7 +110,8 @@ int main(int argc, char *argv[]) /* btf.h */ btf = btf__new(NULL, 0); - btf_dump__new(btf, dump_printf, nullptr, &opts); + if (!libbpf_get_error(btf)) + btf_dump__new(btf, dump_printf, nullptr, &opts); /* BPF skeleton */ skel = test_core_extern__open_and_load(); diff --git a/tools/testing/selftests/bpf/test_lirc_mode2.sh b/tools/testing/selftests/bpf/test_lirc_mode2.sh index ec4e15948e40..5252b91f48a1 100755 --- a/tools/testing/selftests/bpf/test_lirc_mode2.sh +++ b/tools/testing/selftests/bpf/test_lirc_mode2.sh @@ -3,6 +3,7 @@ # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 +ret=$ksft_skip msg="skip all tests:" if [ $UID != 0 ]; then @@ -25,7 +26,7 @@ do fi done -if [ -n $LIRCDEV ]; +if [ -n "$LIRCDEV" ]; then TYPE=lirc_mode2 ./test_lirc_mode2_user $LIRCDEV $INPUTDEV @@ -36,3 +37,5 @@ then echo -e ${GREEN}"PASS: $TYPE"${NC} fi fi + +exit $ret diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c index b9f1bbbc8aba..563bbe18c172 100644 --- a/tools/testing/selftests/bpf/test_lru_map.c +++ b/tools/testing/selftests/bpf/test_lru_map.c @@ -61,7 +61,11 @@ static int bpf_map_lookup_elem_with_ref_bit(int fd, unsigned long long key, }; __u8 data[64] = {}; int mfd, pfd, ret, zero = 0; - __u32 retval = 0; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = data, + .data_size_in = sizeof(data), + .repeat = 1, + ); mfd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, sizeof(int), sizeof(__u64), 1, NULL); if (mfd < 0) @@ -75,9 +79,8 @@ static int bpf_map_lookup_elem_with_ref_bit(int fd, unsigned long long key, return -1; } - ret = bpf_prog_test_run(pfd, 1, data, sizeof(data), - NULL, NULL, &retval, NULL); - if (ret < 0 || retval != 42) { + ret = bpf_prog_test_run_opts(pfd, &topts); + if (ret < 0 || topts.retval != 42) { ret = -1; } else { assert(!bpf_map_lookup_elem(mfd, &zero, value)); @@ -875,11 +878,11 @@ int main(int argc, char **argv) assert(nr_cpus != -1); printf("nr_cpus:%d\n\n", nr_cpus); - for (f = 0; f < sizeof(map_flags) / sizeof(*map_flags); f++) { + for (f = 0; f < ARRAY_SIZE(map_flags); f++) { unsigned int tgt_free = (map_flags[f] & BPF_F_NO_COMMON_LRU) ? PERCPU_FREE_TARGET : LOCAL_FREE_TARGET; - for (t = 0; t < sizeof(map_types) / sizeof(*map_types); t++) { + for (t = 0; t < ARRAY_SIZE(map_types); t++) { test_lru_sanity0(map_types[t], map_flags[f]); test_lru_sanity1(map_types[t], map_flags[f], tgt_free); test_lru_sanity2(map_types[t], map_flags[f], tgt_free); diff --git a/tools/testing/selftests/bpf/test_lwt_ip_encap.sh b/tools/testing/selftests/bpf/test_lwt_ip_encap.sh index b497bb85b667..6c69c42b1d60 100755 --- a/tools/testing/selftests/bpf/test_lwt_ip_encap.sh +++ b/tools/testing/selftests/bpf/test_lwt_ip_encap.sh @@ -120,6 +120,14 @@ setup() ip netns exec ${NS2} sysctl -wq net.ipv4.conf.default.rp_filter=0 ip netns exec ${NS3} sysctl -wq net.ipv4.conf.default.rp_filter=0 + # disable IPv6 DAD because it sometimes takes too long and fails tests + ip netns exec ${NS1} sysctl -wq net.ipv6.conf.all.accept_dad=0 + ip netns exec ${NS2} sysctl -wq net.ipv6.conf.all.accept_dad=0 + ip netns exec ${NS3} sysctl -wq net.ipv6.conf.all.accept_dad=0 + ip netns exec ${NS1} sysctl -wq net.ipv6.conf.default.accept_dad=0 + ip netns exec ${NS2} sysctl -wq net.ipv6.conf.default.accept_dad=0 + ip netns exec ${NS3} sysctl -wq net.ipv6.conf.default.accept_dad=0 + ip link add veth1 type veth peer name veth2 ip link add veth3 type veth peer name veth4 ip link add veth5 type veth peer name veth6 @@ -289,7 +297,7 @@ test_ping() ip netns exec ${NS1} ping -c 1 -W 1 -I veth1 ${IPv4_DST} 2>&1 > /dev/null RET=$? elif [ "${PROTO}" == "IPv6" ] ; then - ip netns exec ${NS1} ping6 -c 1 -W 6 -I veth1 ${IPv6_DST} 2>&1 > /dev/null + ip netns exec ${NS1} ping6 -c 1 -W 1 -I veth1 ${IPv6_DST} 2>&1 > /dev/null RET=$? else echo " test_ping: unknown PROTO: ${PROTO}" diff --git a/tools/testing/selftests/bpf/test_lwt_seg6local.sh b/tools/testing/selftests/bpf/test_lwt_seg6local.sh index 5620919fde9e..826f4423ce02 100755 --- a/tools/testing/selftests/bpf/test_lwt_seg6local.sh +++ b/tools/testing/selftests/bpf/test_lwt_seg6local.sh @@ -23,6 +23,12 @@ # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 +readonly NS1="ns1-$(mktemp -u XXXXXX)" +readonly NS2="ns2-$(mktemp -u XXXXXX)" +readonly NS3="ns3-$(mktemp -u XXXXXX)" +readonly NS4="ns4-$(mktemp -u XXXXXX)" +readonly NS5="ns5-$(mktemp -u XXXXXX)" +readonly NS6="ns6-$(mktemp -u XXXXXX)" msg="skip all tests:" if [ $UID != 0 ]; then @@ -41,23 +47,23 @@ cleanup() fi set +e - ip netns del ns1 2> /dev/null - ip netns del ns2 2> /dev/null - ip netns del ns3 2> /dev/null - ip netns del ns4 2> /dev/null - ip netns del ns5 2> /dev/null - ip netns del ns6 2> /dev/null + ip netns del ${NS1} 2> /dev/null + ip netns del ${NS2} 2> /dev/null + ip netns del ${NS3} 2> /dev/null + ip netns del ${NS4} 2> /dev/null + ip netns del ${NS5} 2> /dev/null + ip netns del ${NS6} 2> /dev/null rm -f $TMP_FILE } set -e -ip netns add ns1 -ip netns add ns2 -ip netns add ns3 -ip netns add ns4 -ip netns add ns5 -ip netns add ns6 +ip netns add ${NS1} +ip netns add ${NS2} +ip netns add ${NS3} +ip netns add ${NS4} +ip netns add ${NS5} +ip netns add ${NS6} trap cleanup 0 2 3 6 9 @@ -67,78 +73,78 @@ ip link add veth5 type veth peer name veth6 ip link add veth7 type veth peer name veth8 ip link add veth9 type veth peer name veth10 -ip link set veth1 netns ns1 -ip link set veth2 netns ns2 -ip link set veth3 netns ns2 -ip link set veth4 netns ns3 -ip link set veth5 netns ns3 -ip link set veth6 netns ns4 -ip link set veth7 netns ns4 -ip link set veth8 netns ns5 -ip link set veth9 netns ns5 -ip link set veth10 netns ns6 +ip link set veth1 netns ${NS1} +ip link set veth2 netns ${NS2} +ip link set veth3 netns ${NS2} +ip link set veth4 netns ${NS3} +ip link set veth5 netns ${NS3} +ip link set veth6 netns ${NS4} +ip link set veth7 netns ${NS4} +ip link set veth8 netns ${NS5} +ip link set veth9 netns ${NS5} +ip link set veth10 netns ${NS6} -ip netns exec ns1 ip link set dev veth1 up -ip netns exec ns2 ip link set dev veth2 up -ip netns exec ns2 ip link set dev veth3 up -ip netns exec ns3 ip link set dev veth4 up -ip netns exec ns3 ip link set dev veth5 up -ip netns exec ns4 ip link set dev veth6 up -ip netns exec ns4 ip link set dev veth7 up -ip netns exec ns5 ip link set dev veth8 up -ip netns exec ns5 ip link set dev veth9 up -ip netns exec ns6 ip link set dev veth10 up -ip netns exec ns6 ip link set dev lo up +ip netns exec ${NS1} ip link set dev veth1 up +ip netns exec ${NS2} ip link set dev veth2 up +ip netns exec ${NS2} ip link set dev veth3 up +ip netns exec ${NS3} ip link set dev veth4 up +ip netns exec ${NS3} ip link set dev veth5 up +ip netns exec ${NS4} ip link set dev veth6 up +ip netns exec ${NS4} ip link set dev veth7 up +ip netns exec ${NS5} ip link set dev veth8 up +ip netns exec ${NS5} ip link set dev veth9 up +ip netns exec ${NS6} ip link set dev veth10 up +ip netns exec ${NS6} ip link set dev lo up # All link scope addresses and routes required between veths -ip netns exec ns1 ip -6 addr add fb00::12/16 dev veth1 scope link -ip netns exec ns1 ip -6 route add fb00::21 dev veth1 scope link -ip netns exec ns2 ip -6 addr add fb00::21/16 dev veth2 scope link -ip netns exec ns2 ip -6 addr add fb00::34/16 dev veth3 scope link -ip netns exec ns2 ip -6 route add fb00::43 dev veth3 scope link -ip netns exec ns3 ip -6 route add fb00::65 dev veth5 scope link -ip netns exec ns3 ip -6 addr add fb00::43/16 dev veth4 scope link -ip netns exec ns3 ip -6 addr add fb00::56/16 dev veth5 scope link -ip netns exec ns4 ip -6 addr add fb00::65/16 dev veth6 scope link -ip netns exec ns4 ip -6 addr add fb00::78/16 dev veth7 scope link -ip netns exec ns4 ip -6 route add fb00::87 dev veth7 scope link -ip netns exec ns5 ip -6 addr add fb00::87/16 dev veth8 scope link -ip netns exec ns5 ip -6 addr add fb00::910/16 dev veth9 scope link -ip netns exec ns5 ip -6 route add fb00::109 dev veth9 scope link -ip netns exec ns5 ip -6 route add fb00::109 table 117 dev veth9 scope link -ip netns exec ns6 ip -6 addr add fb00::109/16 dev veth10 scope link +ip netns exec ${NS1} ip -6 addr add fb00::12/16 dev veth1 scope link +ip netns exec ${NS1} ip -6 route add fb00::21 dev veth1 scope link +ip netns exec ${NS2} ip -6 addr add fb00::21/16 dev veth2 scope link +ip netns exec ${NS2} ip -6 addr add fb00::34/16 dev veth3 scope link +ip netns exec ${NS2} ip -6 route add fb00::43 dev veth3 scope link +ip netns exec ${NS3} ip -6 route add fb00::65 dev veth5 scope link +ip netns exec ${NS3} ip -6 addr add fb00::43/16 dev veth4 scope link +ip netns exec ${NS3} ip -6 addr add fb00::56/16 dev veth5 scope link +ip netns exec ${NS4} ip -6 addr add fb00::65/16 dev veth6 scope link +ip netns exec ${NS4} ip -6 addr add fb00::78/16 dev veth7 scope link +ip netns exec ${NS4} ip -6 route add fb00::87 dev veth7 scope link +ip netns exec ${NS5} ip -6 addr add fb00::87/16 dev veth8 scope link +ip netns exec ${NS5} ip -6 addr add fb00::910/16 dev veth9 scope link +ip netns exec ${NS5} ip -6 route add fb00::109 dev veth9 scope link +ip netns exec ${NS5} ip -6 route add fb00::109 table 117 dev veth9 scope link +ip netns exec ${NS6} ip -6 addr add fb00::109/16 dev veth10 scope link -ip netns exec ns1 ip -6 addr add fb00::1/16 dev lo -ip netns exec ns1 ip -6 route add fb00::6 dev veth1 via fb00::21 +ip netns exec ${NS1} ip -6 addr add fb00::1/16 dev lo +ip netns exec ${NS1} ip -6 route add fb00::6 dev veth1 via fb00::21 -ip netns exec ns2 ip -6 route add fb00::6 encap bpf in obj test_lwt_seg6local.o sec encap_srh dev veth2 -ip netns exec ns2 ip -6 route add fd00::1 dev veth3 via fb00::43 scope link +ip netns exec ${NS2} ip -6 route add fb00::6 encap bpf in obj test_lwt_seg6local.o sec encap_srh dev veth2 +ip netns exec ${NS2} ip -6 route add fd00::1 dev veth3 via fb00::43 scope link -ip netns exec ns3 ip -6 route add fc42::1 dev veth5 via fb00::65 -ip netns exec ns3 ip -6 route add fd00::1 encap seg6local action End.BPF endpoint obj test_lwt_seg6local.o sec add_egr_x dev veth4 +ip netns exec ${NS3} ip -6 route add fc42::1 dev veth5 via fb00::65 +ip netns exec ${NS3} ip -6 route add fd00::1 encap seg6local action End.BPF endpoint obj test_lwt_seg6local.o sec add_egr_x dev veth4 -ip netns exec ns4 ip -6 route add fd00::2 encap seg6local action End.BPF endpoint obj test_lwt_seg6local.o sec pop_egr dev veth6 -ip netns exec ns4 ip -6 addr add fc42::1 dev lo -ip netns exec ns4 ip -6 route add fd00::3 dev veth7 via fb00::87 +ip netns exec ${NS4} ip -6 route add fd00::2 encap seg6local action End.BPF endpoint obj test_lwt_seg6local.o sec pop_egr dev veth6 +ip netns exec ${NS4} ip -6 addr add fc42::1 dev lo +ip netns exec ${NS4} ip -6 route add fd00::3 dev veth7 via fb00::87 -ip netns exec ns5 ip -6 route add fd00::4 table 117 dev veth9 via fb00::109 -ip netns exec ns5 ip -6 route add fd00::3 encap seg6local action End.BPF endpoint obj test_lwt_seg6local.o sec inspect_t dev veth8 +ip netns exec ${NS5} ip -6 route add fd00::4 table 117 dev veth9 via fb00::109 +ip netns exec ${NS5} ip -6 route add fd00::3 encap seg6local action End.BPF endpoint obj test_lwt_seg6local.o sec inspect_t dev veth8 -ip netns exec ns6 ip -6 addr add fb00::6/16 dev lo -ip netns exec ns6 ip -6 addr add fd00::4/16 dev lo +ip netns exec ${NS6} ip -6 addr add fb00::6/16 dev lo +ip netns exec ${NS6} ip -6 addr add fd00::4/16 dev lo -ip netns exec ns1 sysctl net.ipv6.conf.all.forwarding=1 > /dev/null -ip netns exec ns2 sysctl net.ipv6.conf.all.forwarding=1 > /dev/null -ip netns exec ns3 sysctl net.ipv6.conf.all.forwarding=1 > /dev/null -ip netns exec ns4 sysctl net.ipv6.conf.all.forwarding=1 > /dev/null -ip netns exec ns5 sysctl net.ipv6.conf.all.forwarding=1 > /dev/null +ip netns exec ${NS1} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null +ip netns exec ${NS2} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null +ip netns exec ${NS3} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null +ip netns exec ${NS4} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null +ip netns exec ${NS5} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null -ip netns exec ns6 sysctl net.ipv6.conf.all.seg6_enabled=1 > /dev/null -ip netns exec ns6 sysctl net.ipv6.conf.lo.seg6_enabled=1 > /dev/null -ip netns exec ns6 sysctl net.ipv6.conf.veth10.seg6_enabled=1 > /dev/null +ip netns exec ${NS6} sysctl net.ipv6.conf.all.seg6_enabled=1 > /dev/null +ip netns exec ${NS6} sysctl net.ipv6.conf.lo.seg6_enabled=1 > /dev/null +ip netns exec ${NS6} sysctl net.ipv6.conf.veth10.seg6_enabled=1 > /dev/null -ip netns exec ns6 nc -l -6 -u -d 7330 > $TMP_FILE & -ip netns exec ns1 bash -c "echo 'foobar' | nc -w0 -6 -u -p 2121 -s fb00::1 fb00::6 7330" +ip netns exec ${NS6} nc -l -6 -u -d 7330 > $TMP_FILE & +ip netns exec ${NS1} bash -c "echo 'foobar' | nc -w0 -6 -u -p 2121 -s fb00::1 fb00::6 7330" sleep 5 # wait enough time to ensure the UDP datagram arrived to the last segment kill -TERM $! diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index 50f7e74ca0b9..cbebfaa7c1e8 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -738,7 +738,7 @@ static void test_sockmap(unsigned int tasks, void *data) sizeof(key), sizeof(value), 6, NULL); if (fd < 0) { - if (!bpf_probe_map_type(BPF_MAP_TYPE_SOCKMAP, 0)) { + if (!libbpf_probe_bpf_map_type(BPF_MAP_TYPE_SOCKMAP, NULL)) { printf("%s SKIP (unsupported map type BPF_MAP_TYPE_SOCKMAP)\n", __func__); skips++; diff --git a/tools/testing/selftests/bpf/test_sock_addr.c b/tools/testing/selftests/bpf/test_sock_addr.c index f0c8d05ba6d1..f3d5d7ac6505 100644 --- a/tools/testing/selftests/bpf/test_sock_addr.c +++ b/tools/testing/selftests/bpf/test_sock_addr.c @@ -723,7 +723,7 @@ static int xmsg_ret_only_prog_load(const struct sock_addr_test *test, BPF_MOV64_IMM(BPF_REG_0, rc), BPF_EXIT_INSN(), }; - return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn)); + return load_insns(test, insns, ARRAY_SIZE(insns)); } static int sendmsg_allow_prog_load(const struct sock_addr_test *test) @@ -795,7 +795,7 @@ static int sendmsg4_rw_asm_prog_load(const struct sock_addr_test *test) BPF_EXIT_INSN(), }; - return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn)); + return load_insns(test, insns, ARRAY_SIZE(insns)); } static int recvmsg4_rw_c_prog_load(const struct sock_addr_test *test) @@ -858,7 +858,7 @@ static int sendmsg6_rw_dst_asm_prog_load(const struct sock_addr_test *test, BPF_EXIT_INSN(), }; - return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn)); + return load_insns(test, insns, ARRAY_SIZE(insns)); } static int sendmsg6_rw_asm_prog_load(const struct sock_addr_test *test) diff --git a/tools/testing/selftests/bpf/test_sockmap.c b/tools/testing/selftests/bpf/test_sockmap.c index 1ba7e7346afb..dfb4f5c0fcb9 100644 --- a/tools/testing/selftests/bpf/test_sockmap.c +++ b/tools/testing/selftests/bpf/test_sockmap.c @@ -1786,7 +1786,7 @@ static int populate_progs(char *bpf_file) i++; } - for (i = 0; i < sizeof(map_fd)/sizeof(int); i++) { + for (i = 0; i < ARRAY_SIZE(map_fd); i++) { maps[i] = bpf_object__find_map_by_name(obj, map_names[i]); map_fd[i] = bpf_map__fd(maps[i]); if (map_fd[i] < 0) { @@ -1867,7 +1867,7 @@ static int __test_selftests(int cg_fd, struct sockmap_options *opt) } /* Tests basic commands and APIs */ - for (i = 0; i < sizeof(test)/sizeof(struct _test); i++) { + for (i = 0; i < ARRAY_SIZE(test); i++) { struct _test t = test[i]; if (check_whitelist(&t, opt) != 0) diff --git a/tools/testing/selftests/bpf/test_tcp_check_syncookie.sh b/tools/testing/selftests/bpf/test_tcp_check_syncookie.sh index 6413c1472554..102e6588e2fe 100755 --- a/tools/testing/selftests/bpf/test_tcp_check_syncookie.sh +++ b/tools/testing/selftests/bpf/test_tcp_check_syncookie.sh @@ -4,6 +4,7 @@ # Copyright (c) 2019 Cloudflare set -eu +readonly NS1="ns1-$(mktemp -u XXXXXX)" wait_for_ip() { @@ -28,12 +29,12 @@ get_prog_id() ns1_exec() { - ip netns exec ns1 "$@" + ip netns exec ${NS1} "$@" } setup() { - ip netns add ns1 + ip netns add ${NS1} ns1_exec ip link set lo up ns1_exec sysctl -w net.ipv4.tcp_syncookies=2 diff --git a/tools/testing/selftests/bpf/test_tunnel.sh b/tools/testing/selftests/bpf/test_tunnel.sh index ca1372924023..2817d9948d59 100755 --- a/tools/testing/selftests/bpf/test_tunnel.sh +++ b/tools/testing/selftests/bpf/test_tunnel.sh @@ -39,7 +39,7 @@ # from root namespace, the following operations happen: # 1) Route lookup shows 10.1.1.100/24 belongs to tnl dev, fwd to tnl dev. # 2) Tnl device's egress BPF program is triggered and set the tunnel metadata, -# with remote_ip=172.16.1.200 and others. +# with remote_ip=172.16.1.100 and others. # 3) Outer tunnel header is prepended and route the packet to veth1's egress # 4) veth0's ingress queue receive the tunneled packet at namespace at_ns0 # 5) Tunnel protocol handler, ex: vxlan_rcv, decap the packet diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 76cd903117af..a2cd236c32eb 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -22,8 +22,6 @@ #include #include -#include - #include #include #include @@ -31,6 +29,7 @@ #include #include +#include #include #include @@ -41,6 +40,7 @@ # define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 1 # endif #endif +#include "cap_helpers.h" #include "bpf_rand.h" #include "bpf_util.h" #include "test_btf.h" @@ -61,11 +61,20 @@ #define F_NEEDS_EFFICIENT_UNALIGNED_ACCESS (1 << 0) #define F_LOAD_WITH_STRICT_ALIGNMENT (1 << 1) +/* need CAP_BPF, CAP_NET_ADMIN, CAP_PERFMON to load progs */ +#define ADMIN_CAPS (1ULL << CAP_NET_ADMIN | \ + 1ULL << CAP_PERFMON | \ + 1ULL << CAP_BPF) #define UNPRIV_SYSCTL "kernel/unprivileged_bpf_disabled" static bool unpriv_disabled = false; static int skips; static bool verbose = false; +struct kfunc_btf_id_pair { + const char *kfunc; + int insn_idx; +}; + struct bpf_test { const char *descr; struct bpf_insn insns[MAX_INSNS]; @@ -92,6 +101,7 @@ struct bpf_test { int fixup_map_reuseport_array[MAX_FIXUPS]; int fixup_map_ringbuf[MAX_FIXUPS]; int fixup_map_timer[MAX_FIXUPS]; + struct kfunc_btf_id_pair fixup_kfunc_btf_id[MAX_FIXUPS]; /* Expected verifier log output for result REJECT or VERBOSE_ACCEPT. * Can be a tab-separated sequence of expected strings. An empty string * means no log verification. @@ -449,7 +459,7 @@ static int probe_filter_length(const struct bpf_insn *fp) static bool skip_unsupported_map(enum bpf_map_type map_type) { - if (!bpf_probe_map_type(map_type, 0)) { + if (!libbpf_probe_bpf_map_type(map_type, NULL)) { printf("SKIP (unsupported map type %d)\n", map_type); skips++; return true; @@ -744,6 +754,7 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type, int *fixup_map_reuseport_array = test->fixup_map_reuseport_array; int *fixup_map_ringbuf = test->fixup_map_ringbuf; int *fixup_map_timer = test->fixup_map_timer; + struct kfunc_btf_id_pair *fixup_kfunc_btf_id = test->fixup_kfunc_btf_id; if (test->fill_helper) { test->fill_insns = calloc(MAX_TEST_INSNS, sizeof(struct bpf_insn)); @@ -936,6 +947,26 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type, fixup_map_timer++; } while (*fixup_map_timer); } + + /* Patch in kfunc BTF IDs */ + if (fixup_kfunc_btf_id->kfunc) { + struct btf *btf; + int btf_id; + + do { + btf_id = 0; + btf = btf__load_vmlinux_btf(); + if (btf) { + btf_id = btf__find_by_name_kind(btf, + fixup_kfunc_btf_id->kfunc, + BTF_KIND_FUNC); + btf_id = btf_id < 0 ? 0 : btf_id; + } + btf__free(btf); + prog[fixup_kfunc_btf_id->insn_idx].imm = btf_id; + fixup_kfunc_btf_id++; + } while (fixup_kfunc_btf_id->kfunc); + } } struct libcap { @@ -945,47 +976,19 @@ struct libcap { static int set_admin(bool admin) { - cap_t caps; - /* need CAP_BPF, CAP_NET_ADMIN, CAP_PERFMON to load progs */ - const cap_value_t cap_net_admin = CAP_NET_ADMIN; - const cap_value_t cap_sys_admin = CAP_SYS_ADMIN; - struct libcap *cap; - int ret = -1; + int err; - caps = cap_get_proc(); - if (!caps) { - perror("cap_get_proc"); - return -1; - } - cap = (struct libcap *)caps; - if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_sys_admin, CAP_CLEAR)) { - perror("cap_set_flag clear admin"); - goto out; - } - if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_net_admin, - admin ? CAP_SET : CAP_CLEAR)) { - perror("cap_set_flag set_or_clear net"); - goto out; - } - /* libcap is likely old and simply ignores CAP_BPF and CAP_PERFMON, - * so update effective bits manually - */ if (admin) { - cap->data[1].effective |= 1 << (38 /* CAP_PERFMON */ - 32); - cap->data[1].effective |= 1 << (39 /* CAP_BPF */ - 32); + err = cap_enable_effective(ADMIN_CAPS, NULL); + if (err) + perror("cap_enable_effective(ADMIN_CAPS)"); } else { - cap->data[1].effective &= ~(1 << (38 - 32)); - cap->data[1].effective &= ~(1 << (39 - 32)); + err = cap_disable_effective(ADMIN_CAPS, NULL); + if (err) + perror("cap_disable_effective(ADMIN_CAPS)"); } - if (cap_set_proc(caps)) { - perror("cap_set_proc"); - goto out; - } - ret = 0; -out: - if (cap_free(caps)) - perror("cap_free"); - return ret; + + return err; } static int do_prog_test_run(int fd_prog, bool unpriv, uint32_t expected_val, @@ -993,13 +996,18 @@ static int do_prog_test_run(int fd_prog, bool unpriv, uint32_t expected_val, { __u8 tmp[TEST_DATA_LEN << 2]; __u32 size_tmp = sizeof(tmp); - uint32_t retval; int err, saved_errno; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = data, + .data_size_in = size_data, + .data_out = tmp, + .data_size_out = size_tmp, + .repeat = 1, + ); if (unpriv) set_admin(true); - err = bpf_prog_test_run(fd_prog, 1, data, size_data, - tmp, &size_tmp, &retval, NULL); + err = bpf_prog_test_run_opts(fd_prog, &topts); saved_errno = errno; if (unpriv) @@ -1023,9 +1031,8 @@ static int do_prog_test_run(int fd_prog, bool unpriv, uint32_t expected_val, } } - if (retval != expected_val && - expected_val != POINTER_VALUE) { - printf("FAIL retval %d != %d ", retval, expected_val); + if (topts.retval != expected_val && expected_val != POINTER_VALUE) { + printf("FAIL retval %d != %d ", topts.retval, expected_val); return 1; } @@ -1148,7 +1155,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv, * bpf_probe_prog_type won't give correct answer */ if (fd_prog < 0 && prog_type != BPF_PROG_TYPE_TRACING && - !bpf_probe_prog_type(prog_type, 0)) { + !libbpf_probe_bpf_prog_type(prog_type, NULL)) { printf("SKIP (unsupported program type %d)\n", prog_type); skips++; goto close_fds; @@ -1259,31 +1266,18 @@ static void do_test_single(struct bpf_test *test, bool unpriv, static bool is_admin(void) { - cap_flag_value_t net_priv = CAP_CLEAR; - bool perfmon_priv = false; - bool bpf_priv = false; - struct libcap *cap; - cap_t caps; + __u64 caps; -#ifdef CAP_IS_SUPPORTED - if (!CAP_IS_SUPPORTED(CAP_SETFCAP)) { - perror("cap_get_flag"); + /* The test checks for finer cap as CAP_NET_ADMIN, + * CAP_PERFMON, and CAP_BPF instead of CAP_SYS_ADMIN. + * Thus, disable CAP_SYS_ADMIN at the beginning. + */ + if (cap_disable_effective(1ULL << CAP_SYS_ADMIN, &caps)) { + perror("cap_disable_effective(CAP_SYS_ADMIN)"); return false; } -#endif - caps = cap_get_proc(); - if (!caps) { - perror("cap_get_proc"); - return false; - } - cap = (struct libcap *)caps; - bpf_priv = cap->data[1].effective & (1 << (39/* CAP_BPF */ - 32)); - perfmon_priv = cap->data[1].effective & (1 << (38/* CAP_PERFMON */ - 32)); - if (cap_get_flag(caps, CAP_NET_ADMIN, CAP_EFFECTIVE, &net_priv)) - perror("cap_get_flag NET"); - if (cap_free(caps)) - perror("cap_free"); - return bpf_priv && perfmon_priv && net_priv == CAP_SET; + + return (caps & ADMIN_CAPS) == ADMIN_CAPS; } static void get_unpriv_disabled() diff --git a/tools/testing/selftests/bpf/test_xdp_meta.sh b/tools/testing/selftests/bpf/test_xdp_meta.sh index d10cefd6eb09..ea69370caae3 100755 --- a/tools/testing/selftests/bpf/test_xdp_meta.sh +++ b/tools/testing/selftests/bpf/test_xdp_meta.sh @@ -2,6 +2,8 @@ # Kselftest framework requirement - SKIP code is 4. readonly KSFT_SKIP=4 +readonly NS1="ns1-$(mktemp -u XXXXXX)" +readonly NS2="ns2-$(mktemp -u XXXXXX)" cleanup() { @@ -13,8 +15,8 @@ cleanup() set +e ip link del veth1 2> /dev/null - ip netns del ns1 2> /dev/null - ip netns del ns2 2> /dev/null + ip netns del ${NS1} 2> /dev/null + ip netns del ${NS2} 2> /dev/null } ip link set dev lo xdp off 2>/dev/null > /dev/null @@ -24,32 +26,32 @@ if [ $? -ne 0 ];then fi set -e -ip netns add ns1 -ip netns add ns2 +ip netns add ${NS1} +ip netns add ${NS2} trap cleanup 0 2 3 6 9 ip link add veth1 type veth peer name veth2 -ip link set veth1 netns ns1 -ip link set veth2 netns ns2 +ip link set veth1 netns ${NS1} +ip link set veth2 netns ${NS2} -ip netns exec ns1 ip addr add 10.1.1.11/24 dev veth1 -ip netns exec ns2 ip addr add 10.1.1.22/24 dev veth2 +ip netns exec ${NS1} ip addr add 10.1.1.11/24 dev veth1 +ip netns exec ${NS2} ip addr add 10.1.1.22/24 dev veth2 -ip netns exec ns1 tc qdisc add dev veth1 clsact -ip netns exec ns2 tc qdisc add dev veth2 clsact +ip netns exec ${NS1} tc qdisc add dev veth1 clsact +ip netns exec ${NS2} tc qdisc add dev veth2 clsact -ip netns exec ns1 tc filter add dev veth1 ingress bpf da obj test_xdp_meta.o sec t -ip netns exec ns2 tc filter add dev veth2 ingress bpf da obj test_xdp_meta.o sec t +ip netns exec ${NS1} tc filter add dev veth1 ingress bpf da obj test_xdp_meta.o sec t +ip netns exec ${NS2} tc filter add dev veth2 ingress bpf da obj test_xdp_meta.o sec t -ip netns exec ns1 ip link set dev veth1 xdp obj test_xdp_meta.o sec x -ip netns exec ns2 ip link set dev veth2 xdp obj test_xdp_meta.o sec x +ip netns exec ${NS1} ip link set dev veth1 xdp obj test_xdp_meta.o sec x +ip netns exec ${NS2} ip link set dev veth2 xdp obj test_xdp_meta.o sec x -ip netns exec ns1 ip link set dev veth1 up -ip netns exec ns2 ip link set dev veth2 up +ip netns exec ${NS1} ip link set dev veth1 up +ip netns exec ${NS2} ip link set dev veth2 up -ip netns exec ns1 ping -c 1 10.1.1.22 -ip netns exec ns2 ping -c 1 10.1.1.11 +ip netns exec ${NS1} ping -c 1 10.1.1.22 +ip netns exec ${NS2} ping -c 1 10.1.1.11 exit 0 diff --git a/tools/testing/selftests/bpf/test_xdp_redirect.sh b/tools/testing/selftests/bpf/test_xdp_redirect.sh index 57c8db9972a6..1d79f31480ad 100755 --- a/tools/testing/selftests/bpf/test_xdp_redirect.sh +++ b/tools/testing/selftests/bpf/test_xdp_redirect.sh @@ -10,6 +10,8 @@ # | xdp forwarding | # ------------------ +readonly NS1="ns1-$(mktemp -u XXXXXX)" +readonly NS2="ns2-$(mktemp -u XXXXXX)" ret=0 setup() @@ -17,27 +19,27 @@ setup() local xdpmode=$1 - ip netns add ns1 - ip netns add ns2 + ip netns add ${NS1} + ip netns add ${NS2} - ip link add veth1 index 111 type veth peer name veth11 netns ns1 - ip link add veth2 index 222 type veth peer name veth22 netns ns2 + ip link add veth1 index 111 type veth peer name veth11 netns ${NS1} + ip link add veth2 index 222 type veth peer name veth22 netns ${NS2} ip link set veth1 up ip link set veth2 up - ip -n ns1 link set dev veth11 up - ip -n ns2 link set dev veth22 up + ip -n ${NS1} link set dev veth11 up + ip -n ${NS2} link set dev veth22 up - ip -n ns1 addr add 10.1.1.11/24 dev veth11 - ip -n ns2 addr add 10.1.1.22/24 dev veth22 + ip -n ${NS1} addr add 10.1.1.11/24 dev veth11 + ip -n ${NS2} addr add 10.1.1.22/24 dev veth22 } cleanup() { ip link del veth1 2> /dev/null ip link del veth2 2> /dev/null - ip netns del ns1 2> /dev/null - ip netns del ns2 2> /dev/null + ip netns del ${NS1} 2> /dev/null + ip netns del ${NS2} 2> /dev/null } test_xdp_redirect() @@ -52,13 +54,13 @@ test_xdp_redirect() return 0 fi - ip -n ns1 link set veth11 $xdpmode obj xdp_dummy.o sec xdp &> /dev/null - ip -n ns2 link set veth22 $xdpmode obj xdp_dummy.o sec xdp &> /dev/null + ip -n ${NS1} link set veth11 $xdpmode obj xdp_dummy.o sec xdp &> /dev/null + ip -n ${NS2} link set veth22 $xdpmode obj xdp_dummy.o sec xdp &> /dev/null ip link set dev veth1 $xdpmode obj test_xdp_redirect.o sec redirect_to_222 &> /dev/null ip link set dev veth2 $xdpmode obj test_xdp_redirect.o sec redirect_to_111 &> /dev/null - if ip netns exec ns1 ping -c 1 10.1.1.22 &> /dev/null && - ip netns exec ns2 ping -c 1 10.1.1.11 &> /dev/null; then + if ip netns exec ${NS1} ping -c 1 10.1.1.22 &> /dev/null && + ip netns exec ${NS2} ping -c 1 10.1.1.11 &> /dev/null; then echo "selftests: test_xdp_redirect $xdpmode [PASS]"; else ret=1 diff --git a/tools/testing/selftests/bpf/test_xdp_redirect_multi.sh b/tools/testing/selftests/bpf/test_xdp_redirect_multi.sh index 05f872740999..cc57cb87e65f 100755 --- a/tools/testing/selftests/bpf/test_xdp_redirect_multi.sh +++ b/tools/testing/selftests/bpf/test_xdp_redirect_multi.sh @@ -32,6 +32,11 @@ DRV_MODE="xdpgeneric xdpdrv xdpegress" PASS=0 FAIL=0 LOG_DIR=$(mktemp -d) +declare -a NS +NS[0]="ns0-$(mktemp -u XXXXXX)" +NS[1]="ns1-$(mktemp -u XXXXXX)" +NS[2]="ns2-$(mktemp -u XXXXXX)" +NS[3]="ns3-$(mktemp -u XXXXXX)" test_pass() { @@ -47,11 +52,9 @@ test_fail() clean_up() { - for i in $(seq $NUM); do - ip link del veth$i 2> /dev/null - ip netns del ns$i 2> /dev/null + for i in $(seq 0 $NUM); do + ip netns del ${NS[$i]} 2> /dev/null done - ip netns del ns0 2> /dev/null } # Kselftest framework requirement - SKIP code is 4. @@ -79,23 +82,22 @@ setup_ns() mode="xdpdrv" fi - ip netns add ns0 + ip netns add ${NS[0]} for i in $(seq $NUM); do - ip netns add ns$i - ip -n ns$i link add veth0 index 2 type veth \ - peer name veth$i netns ns0 index $((1 + $i)) - ip -n ns0 link set veth$i up - ip -n ns$i link set veth0 up + ip netns add ${NS[$i]} + ip -n ${NS[$i]} link add veth0 type veth peer name veth$i netns ${NS[0]} + ip -n ${NS[$i]} link set veth0 up + ip -n ${NS[0]} link set veth$i up - ip -n ns$i addr add 192.0.2.$i/24 dev veth0 - ip -n ns$i addr add 2001:db8::$i/64 dev veth0 + ip -n ${NS[$i]} addr add 192.0.2.$i/24 dev veth0 + ip -n ${NS[$i]} addr add 2001:db8::$i/64 dev veth0 # Add a neigh entry for IPv4 ping test - ip -n ns$i neigh add 192.0.2.253 lladdr 00:00:00:00:00:01 dev veth0 - ip -n ns$i link set veth0 $mode obj \ + ip -n ${NS[$i]} neigh add 192.0.2.253 lladdr 00:00:00:00:00:01 dev veth0 + ip -n ${NS[$i]} link set veth0 $mode obj \ xdp_dummy.o sec xdp &> /dev/null || \ { test_fail "Unable to load dummy xdp" && exit 1; } IFACES="$IFACES veth$i" - veth_mac[$i]=$(ip -n ns0 link show veth$i | awk '/link\/ether/ {print $2}') + veth_mac[$i]=$(ip -n ${NS[0]} link show veth$i | awk '/link\/ether/ {print $2}') done } @@ -104,10 +106,10 @@ do_egress_tests() local mode=$1 # mac test - ip netns exec ns2 tcpdump -e -i veth0 -nn -l -e &> ${LOG_DIR}/mac_ns1-2_${mode}.log & - ip netns exec ns3 tcpdump -e -i veth0 -nn -l -e &> ${LOG_DIR}/mac_ns1-3_${mode}.log & + ip netns exec ${NS[2]} tcpdump -e -i veth0 -nn -l -e &> ${LOG_DIR}/mac_ns1-2_${mode}.log & + ip netns exec ${NS[3]} tcpdump -e -i veth0 -nn -l -e &> ${LOG_DIR}/mac_ns1-3_${mode}.log & sleep 0.5 - ip netns exec ns1 ping 192.0.2.254 -i 0.1 -c 4 &> /dev/null + ip netns exec ${NS[1]} ping 192.0.2.254 -i 0.1 -c 4 &> /dev/null sleep 0.5 pkill tcpdump @@ -123,18 +125,18 @@ do_ping_tests() local mode=$1 # ping6 test: echo request should be redirect back to itself, not others - ip netns exec ns1 ip neigh add 2001:db8::2 dev veth0 lladdr 00:00:00:00:00:02 + ip netns exec ${NS[1]} ip neigh add 2001:db8::2 dev veth0 lladdr 00:00:00:00:00:02 - ip netns exec ns1 tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-1_${mode}.log & - ip netns exec ns2 tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-2_${mode}.log & - ip netns exec ns3 tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-3_${mode}.log & + ip netns exec ${NS[1]} tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-1_${mode}.log & + ip netns exec ${NS[2]} tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-2_${mode}.log & + ip netns exec ${NS[3]} tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-3_${mode}.log & sleep 0.5 # ARP test - ip netns exec ns1 arping -q -c 2 -I veth0 192.0.2.254 + ip netns exec ${NS[1]} arping -q -c 2 -I veth0 192.0.2.254 # IPv4 test - ip netns exec ns1 ping 192.0.2.253 -i 0.1 -c 4 &> /dev/null + ip netns exec ${NS[1]} ping 192.0.2.253 -i 0.1 -c 4 &> /dev/null # IPv6 test - ip netns exec ns1 ping6 2001:db8::2 -i 0.1 -c 2 &> /dev/null + ip netns exec ${NS[1]} ping6 2001:db8::2 -i 0.1 -c 2 &> /dev/null sleep 0.5 pkill tcpdump @@ -180,7 +182,7 @@ do_tests() xdpgeneric) drv_p="-S";; esac - ip netns exec ns0 ./xdp_redirect_multi $drv_p $IFACES &> ${LOG_DIR}/xdp_redirect_${mode}.log & + ip netns exec ${NS[0]} ./xdp_redirect_multi $drv_p $IFACES &> ${LOG_DIR}/xdp_redirect_${mode}.log & xdp_pid=$! sleep 1 if ! ps -p $xdp_pid > /dev/null; then @@ -197,10 +199,10 @@ do_tests() kill $xdp_pid } -trap clean_up EXIT - check_env +trap clean_up EXIT + for mode in ${DRV_MODE}; do setup_ns $mode do_tests $mode diff --git a/tools/testing/selftests/bpf/test_xdp_veth.sh b/tools/testing/selftests/bpf/test_xdp_veth.sh index a3a1eaee26ea..392d28cc4e58 100755 --- a/tools/testing/selftests/bpf/test_xdp_veth.sh +++ b/tools/testing/selftests/bpf/test_xdp_veth.sh @@ -22,6 +22,9 @@ ksft_skip=4 TESTNAME=xdp_veth BPF_FS=$(awk '$3 == "bpf" {print $2; exit}' /proc/mounts) BPF_DIR=$BPF_FS/test_$TESTNAME +readonly NS1="ns1-$(mktemp -u XXXXXX)" +readonly NS2="ns2-$(mktemp -u XXXXXX)" +readonly NS3="ns3-$(mktemp -u XXXXXX)" _cleanup() { @@ -29,9 +32,9 @@ _cleanup() ip link del veth1 2> /dev/null ip link del veth2 2> /dev/null ip link del veth3 2> /dev/null - ip netns del ns1 2> /dev/null - ip netns del ns2 2> /dev/null - ip netns del ns3 2> /dev/null + ip netns del ${NS1} 2> /dev/null + ip netns del ${NS2} 2> /dev/null + ip netns del ${NS3} 2> /dev/null rm -rf $BPF_DIR 2> /dev/null } @@ -77,24 +80,24 @@ set -e trap cleanup_skip EXIT -ip netns add ns1 -ip netns add ns2 -ip netns add ns3 +ip netns add ${NS1} +ip netns add ${NS2} +ip netns add ${NS3} -ip link add veth1 index 111 type veth peer name veth11 netns ns1 -ip link add veth2 index 122 type veth peer name veth22 netns ns2 -ip link add veth3 index 133 type veth peer name veth33 netns ns3 +ip link add veth1 index 111 type veth peer name veth11 netns ${NS1} +ip link add veth2 index 122 type veth peer name veth22 netns ${NS2} +ip link add veth3 index 133 type veth peer name veth33 netns ${NS3} ip link set veth1 up ip link set veth2 up ip link set veth3 up -ip -n ns1 addr add 10.1.1.11/24 dev veth11 -ip -n ns3 addr add 10.1.1.33/24 dev veth33 +ip -n ${NS1} addr add 10.1.1.11/24 dev veth11 +ip -n ${NS3} addr add 10.1.1.33/24 dev veth33 -ip -n ns1 link set dev veth11 up -ip -n ns2 link set dev veth22 up -ip -n ns3 link set dev veth33 up +ip -n ${NS1} link set dev veth11 up +ip -n ${NS2} link set dev veth22 up +ip -n ${NS3} link set dev veth33 up mkdir $BPF_DIR bpftool prog loadall \ @@ -107,12 +110,12 @@ ip link set dev veth1 xdp pinned $BPF_DIR/progs/redirect_map_0 ip link set dev veth2 xdp pinned $BPF_DIR/progs/redirect_map_1 ip link set dev veth3 xdp pinned $BPF_DIR/progs/redirect_map_2 -ip -n ns1 link set dev veth11 xdp obj xdp_dummy.o sec xdp -ip -n ns2 link set dev veth22 xdp obj xdp_tx.o sec xdp -ip -n ns3 link set dev veth33 xdp obj xdp_dummy.o sec xdp +ip -n ${NS1} link set dev veth11 xdp obj xdp_dummy.o sec xdp +ip -n ${NS2} link set dev veth22 xdp obj xdp_tx.o sec xdp +ip -n ${NS3} link set dev veth33 xdp obj xdp_dummy.o sec xdp trap cleanup EXIT -ip netns exec ns1 ping -c 1 -W 1 10.1.1.33 +ip netns exec ${NS1} ping -c 1 -W 1 10.1.1.33 exit 0 diff --git a/tools/testing/selftests/bpf/test_xdp_vlan.sh b/tools/testing/selftests/bpf/test_xdp_vlan.sh index 0cbc7604a2f8..810c407e0286 100755 --- a/tools/testing/selftests/bpf/test_xdp_vlan.sh +++ b/tools/testing/selftests/bpf/test_xdp_vlan.sh @@ -4,6 +4,8 @@ # Kselftest framework requirement - SKIP code is 4. readonly KSFT_SKIP=4 +readonly NS1="ns1-$(mktemp -u XXXXXX)" +readonly NS2="ns2-$(mktemp -u XXXXXX)" # Allow wrapper scripts to name test if [ -z "$TESTNAME" ]; then @@ -49,15 +51,15 @@ cleanup() if [ -n "$INTERACTIVE" ]; then echo "Namespace setup still active explore with:" - echo " ip netns exec ns1 bash" - echo " ip netns exec ns2 bash" + echo " ip netns exec ${NS1} bash" + echo " ip netns exec ${NS2} bash" exit $status fi set +e ip link del veth1 2> /dev/null - ip netns del ns1 2> /dev/null - ip netns del ns2 2> /dev/null + ip netns del ${NS1} 2> /dev/null + ip netns del ${NS2} 2> /dev/null } # Using external program "getopt" to get --long-options @@ -126,8 +128,8 @@ fi # Interactive mode likely require us to cleanup netns if [ -n "$INTERACTIVE" ]; then ip link del veth1 2> /dev/null - ip netns del ns1 2> /dev/null - ip netns del ns2 2> /dev/null + ip netns del ${NS1} 2> /dev/null + ip netns del ${NS2} 2> /dev/null fi # Exit on failure @@ -144,8 +146,8 @@ if [ -n "$VERBOSE" ]; then fi # Create two namespaces -ip netns add ns1 -ip netns add ns2 +ip netns add ${NS1} +ip netns add ${NS2} # Run cleanup if failing or on kill trap cleanup 0 2 3 6 9 @@ -154,44 +156,44 @@ trap cleanup 0 2 3 6 9 ip link add veth1 type veth peer name veth2 # Move veth1 and veth2 into the respective namespaces -ip link set veth1 netns ns1 -ip link set veth2 netns ns2 +ip link set veth1 netns ${NS1} +ip link set veth2 netns ${NS2} # NOTICE: XDP require VLAN header inside packet payload # - Thus, disable VLAN offloading driver features # - For veth REMEMBER TX side VLAN-offload # # Disable rx-vlan-offload (mostly needed on ns1) -ip netns exec ns1 ethtool -K veth1 rxvlan off -ip netns exec ns2 ethtool -K veth2 rxvlan off +ip netns exec ${NS1} ethtool -K veth1 rxvlan off +ip netns exec ${NS2} ethtool -K veth2 rxvlan off # # Disable tx-vlan-offload (mostly needed on ns2) -ip netns exec ns2 ethtool -K veth2 txvlan off -ip netns exec ns1 ethtool -K veth1 txvlan off +ip netns exec ${NS2} ethtool -K veth2 txvlan off +ip netns exec ${NS1} ethtool -K veth1 txvlan off export IPADDR1=100.64.41.1 export IPADDR2=100.64.41.2 # In ns1/veth1 add IP-addr on plain net_device -ip netns exec ns1 ip addr add ${IPADDR1}/24 dev veth1 -ip netns exec ns1 ip link set veth1 up +ip netns exec ${NS1} ip addr add ${IPADDR1}/24 dev veth1 +ip netns exec ${NS1} ip link set veth1 up # In ns2/veth2 create VLAN device export VLAN=4011 export DEVNS2=veth2 -ip netns exec ns2 ip link add link $DEVNS2 name $DEVNS2.$VLAN type vlan id $VLAN -ip netns exec ns2 ip addr add ${IPADDR2}/24 dev $DEVNS2.$VLAN -ip netns exec ns2 ip link set $DEVNS2 up -ip netns exec ns2 ip link set $DEVNS2.$VLAN up +ip netns exec ${NS2} ip link add link $DEVNS2 name $DEVNS2.$VLAN type vlan id $VLAN +ip netns exec ${NS2} ip addr add ${IPADDR2}/24 dev $DEVNS2.$VLAN +ip netns exec ${NS2} ip link set $DEVNS2 up +ip netns exec ${NS2} ip link set $DEVNS2.$VLAN up # Bringup lo in netns (to avoids confusing people using --interactive) -ip netns exec ns1 ip link set lo up -ip netns exec ns2 ip link set lo up +ip netns exec ${NS1} ip link set lo up +ip netns exec ${NS2} ip link set lo up # At this point, the hosts cannot reach each-other, # because ns2 are using VLAN tags on the packets. -ip netns exec ns2 sh -c 'ping -W 1 -c 1 100.64.41.1 || echo "Success: First ping must fail"' +ip netns exec ${NS2} sh -c 'ping -W 1 -c 1 100.64.41.1 || echo "Success: First ping must fail"' # Now we can use the test_xdp_vlan.c program to pop/push these VLAN tags @@ -202,19 +204,19 @@ export FILE=test_xdp_vlan.o # First test: Remove VLAN by setting VLAN ID 0, using "xdp_vlan_change" export XDP_PROG=xdp_vlan_change -ip netns exec ns1 ip link set $DEVNS1 $XDP_MODE object $FILE section $XDP_PROG +ip netns exec ${NS1} ip link set $DEVNS1 $XDP_MODE object $FILE section $XDP_PROG # In ns1: egress use TC to add back VLAN tag 4011 # (del cmd) # tc qdisc del dev $DEVNS1 clsact 2> /dev/null # -ip netns exec ns1 tc qdisc add dev $DEVNS1 clsact -ip netns exec ns1 tc filter add dev $DEVNS1 egress \ +ip netns exec ${NS1} tc qdisc add dev $DEVNS1 clsact +ip netns exec ${NS1} tc filter add dev $DEVNS1 egress \ prio 1 handle 1 bpf da obj $FILE sec tc_vlan_push # Now the namespaces can reach each-other, test with ping: -ip netns exec ns2 ping -i 0.2 -W 2 -c 2 $IPADDR1 -ip netns exec ns1 ping -i 0.2 -W 2 -c 2 $IPADDR2 +ip netns exec ${NS2} ping -i 0.2 -W 2 -c 2 $IPADDR1 +ip netns exec ${NS1} ping -i 0.2 -W 2 -c 2 $IPADDR2 # Second test: Replace xdp prog, that fully remove vlan header # @@ -223,9 +225,9 @@ ip netns exec ns1 ping -i 0.2 -W 2 -c 2 $IPADDR2 # ETH_P_8021Q indication, and this cause overwriting of our changes. # export XDP_PROG=xdp_vlan_remove_outer2 -ip netns exec ns1 ip link set $DEVNS1 $XDP_MODE off -ip netns exec ns1 ip link set $DEVNS1 $XDP_MODE object $FILE section $XDP_PROG +ip netns exec ${NS1} ip link set $DEVNS1 $XDP_MODE off +ip netns exec ${NS1} ip link set $DEVNS1 $XDP_MODE object $FILE section $XDP_PROG # Now the namespaces should still be able reach each-other, test with ping: -ip netns exec ns2 ping -i 0.2 -W 2 -c 2 $IPADDR1 -ip netns exec ns1 ping -i 0.2 -W 2 -c 2 $IPADDR2 +ip netns exec ${NS2} ping -i 0.2 -W 2 -c 2 $IPADDR1 +ip netns exec ${NS1} ping -i 0.2 -W 2 -c 2 $IPADDR2 diff --git a/tools/testing/selftests/bpf/trace_helpers.c b/tools/testing/selftests/bpf/trace_helpers.c index 7b7f918eda77..3d6217e3aff7 100644 --- a/tools/testing/selftests/bpf/trace_helpers.c +++ b/tools/testing/selftests/bpf/trace_helpers.c @@ -34,6 +34,13 @@ int load_kallsyms(void) if (!f) return -ENOENT; + /* + * This is called/used from multiplace places, + * load symbols just once. + */ + if (sym_cnt) + return 0; + while (fgets(buf, sizeof(buf), f)) { if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3) break; @@ -138,6 +145,29 @@ void read_trace_pipe(void) } } +ssize_t get_uprobe_offset(const void *addr) +{ + size_t start, end, base; + char buf[256]; + bool found = false; + FILE *f; + + f = fopen("/proc/self/maps", "r"); + if (!f) + return -errno; + + while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) { + if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) { + found = true; + break; + } + } + + fclose(f); + + if (!found) + return -ESRCH; + #if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2 #define OP_RT_RA_MASK 0xffff0000UL @@ -145,10 +175,6 @@ void read_trace_pipe(void) #define ADDIS_R2_R12 0x3c4c0000UL #define ADDI_R2_R2 0x38420000UL -ssize_t get_uprobe_offset(const void *addr, ssize_t base) -{ - u32 *insn = (u32 *)(uintptr_t)addr; - /* * A PPC64 ABIv2 function may have a local and a global entry * point. We need to use the local entry point when patching @@ -165,43 +191,16 @@ ssize_t get_uprobe_offset(const void *addr, ssize_t base) * lis r2,XXXX * addi r2,r2,XXXX */ - if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) || - ((*insn & OP_RT_RA_MASK) == LIS_R2)) && - ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2)) - return (ssize_t)(insn + 2) - base; - else - return (uintptr_t)addr - base; -} + { + const u32 *insn = (const u32 *)(uintptr_t)addr; -#else - -ssize_t get_uprobe_offset(const void *addr, ssize_t base) -{ - return (uintptr_t)addr - base; -} - -#endif - -ssize_t get_base_addr(void) -{ - size_t start, offset; - char buf[256]; - FILE *f; - - f = fopen("/proc/self/maps", "r"); - if (!f) - return -errno; - - while (fscanf(f, "%zx-%*x %s %zx %*[^\n]\n", - &start, buf, &offset) == 3) { - if (strcmp(buf, "r-xp") == 0) { - fclose(f); - return start - offset; - } + if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) || + ((*insn & OP_RT_RA_MASK) == LIS_R2)) && + ((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2)) + return (uintptr_t)(insn + 2) - start + base; } - - fclose(f); - return -EINVAL; +#endif + return (uintptr_t)addr - start + base; } ssize_t get_rel_offset(uintptr_t addr) diff --git a/tools/testing/selftests/bpf/trace_helpers.h b/tools/testing/selftests/bpf/trace_helpers.h index d907b445524d..238a9c98cde2 100644 --- a/tools/testing/selftests/bpf/trace_helpers.h +++ b/tools/testing/selftests/bpf/trace_helpers.h @@ -18,8 +18,7 @@ int kallsyms_find(const char *sym, unsigned long long *addr); void read_trace_pipe(void); -ssize_t get_uprobe_offset(const void *addr, ssize_t base); -ssize_t get_base_addr(void); +ssize_t get_uprobe_offset(const void *addr); ssize_t get_rel_offset(uintptr_t addr); #endif diff --git a/tools/testing/selftests/bpf/verifier/atomic_invalid.c b/tools/testing/selftests/bpf/verifier/atomic_invalid.c index 39272720b2f6..25f4ac1c69ab 100644 --- a/tools/testing/selftests/bpf/verifier/atomic_invalid.c +++ b/tools/testing/selftests/bpf/verifier/atomic_invalid.c @@ -1,6 +1,6 @@ -#define __INVALID_ATOMIC_ACCESS_TEST(op) \ +#define __INVALID_ATOMIC_ACCESS_TEST(op) \ { \ - "atomic " #op " access through non-pointer ", \ + "atomic " #op " access through non-pointer ", \ .insns = { \ BPF_MOV64_IMM(BPF_REG_0, 1), \ BPF_MOV64_IMM(BPF_REG_1, 0), \ @@ -9,7 +9,7 @@ BPF_EXIT_INSN(), \ }, \ .result = REJECT, \ - .errstr = "R1 invalid mem access 'inv'" \ + .errstr = "R1 invalid mem access 'scalar'" \ } __INVALID_ATOMIC_ACCESS_TEST(BPF_ADD), __INVALID_ATOMIC_ACCESS_TEST(BPF_ADD | BPF_FETCH), diff --git a/tools/testing/selftests/bpf/verifier/bounds.c b/tools/testing/selftests/bpf/verifier/bounds.c index e061e8799ce2..33125d5f6772 100644 --- a/tools/testing/selftests/bpf/verifier/bounds.c +++ b/tools/testing/selftests/bpf/verifier/bounds.c @@ -508,7 +508,7 @@ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, -1), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'inv'", + .errstr_unpriv = "R0 invalid mem access 'scalar'", .result_unpriv = REJECT, .result = ACCEPT }, @@ -530,7 +530,7 @@ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, -1), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'inv'", + .errstr_unpriv = "R0 invalid mem access 'scalar'", .result_unpriv = REJECT, .result = ACCEPT }, diff --git a/tools/testing/selftests/bpf/verifier/bounds_deduction.c b/tools/testing/selftests/bpf/verifier/bounds_deduction.c index 91869aea6d64..3931c481e30c 100644 --- a/tools/testing/selftests/bpf/verifier/bounds_deduction.c +++ b/tools/testing/selftests/bpf/verifier/bounds_deduction.c @@ -105,7 +105,7 @@ BPF_EXIT_INSN(), }, .errstr_unpriv = "R1 has pointer with unsupported alu operation", - .errstr = "dereference of modified ctx ptr", + .errstr = "negative offset ctx ptr R1 off=-1 disallowed", .result = REJECT, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c index d7b74eb28333..2e03decb11b6 100644 --- a/tools/testing/selftests/bpf/verifier/calls.c +++ b/tools/testing/selftests/bpf/verifier/calls.c @@ -21,6 +21,183 @@ .prog_type = BPF_PROG_TYPE_TRACEPOINT, .result = ACCEPT, }, +{ + "calls: invalid kfunc call: ptr_to_mem to struct with non-scalar", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = REJECT, + .errstr = "arg#0 pointer type STRUCT prog_test_fail1 must point to scalar", + .fixup_kfunc_btf_id = { + { "bpf_kfunc_call_test_fail1", 2 }, + }, +}, +{ + "calls: invalid kfunc call: ptr_to_mem to struct with nesting depth > 4", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = REJECT, + .errstr = "max struct nesting depth exceeded\narg#0 pointer type STRUCT prog_test_fail2", + .fixup_kfunc_btf_id = { + { "bpf_kfunc_call_test_fail2", 2 }, + }, +}, +{ + "calls: invalid kfunc call: ptr_to_mem to struct with FAM", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = REJECT, + .errstr = "arg#0 pointer type STRUCT prog_test_fail3 must point to scalar", + .fixup_kfunc_btf_id = { + { "bpf_kfunc_call_test_fail3", 2 }, + }, +}, +{ + "calls: invalid kfunc call: reg->type != PTR_TO_CTX", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = REJECT, + .errstr = "arg#0 expected pointer to ctx, but got PTR", + .fixup_kfunc_btf_id = { + { "bpf_kfunc_call_test_pass_ctx", 2 }, + }, +}, +{ + "calls: invalid kfunc call: void * not allowed in func proto without mem size arg", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = REJECT, + .errstr = "arg#0 pointer type UNKNOWN must point to scalar", + .fixup_kfunc_btf_id = { + { "bpf_kfunc_call_test_mem_len_fail1", 2 }, + }, +}, +{ + "calls: trigger reg2btf_ids[reg->type] for reg->type > __BPF_REG_TYPE_MAX", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = REJECT, + .errstr = "arg#0 pointer type STRUCT prog_test_ref_kfunc must point", + .fixup_kfunc_btf_id = { + { "bpf_kfunc_call_test_acquire", 3 }, + { "bpf_kfunc_call_test_release", 5 }, + }, +}, +{ + "calls: invalid kfunc call: reg->off must be zero when passed to release kfunc", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = REJECT, + .errstr = "R1 must have zero offset when passed to release func", + .fixup_kfunc_btf_id = { + { "bpf_kfunc_call_test_acquire", 3 }, + { "bpf_kfunc_call_memb_release", 8 }, + }, +}, +{ + "calls: invalid kfunc call: PTR_TO_BTF_ID with negative offset", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 16), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -4), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .fixup_kfunc_btf_id = { + { "bpf_kfunc_call_test_acquire", 3 }, + { "bpf_kfunc_call_test_release", 9 }, + }, + .result_unpriv = REJECT, + .result = REJECT, + .errstr = "negative offset ptr_ ptr R1 off=-4 disallowed", +}, +{ + "calls: invalid kfunc call: PTR_TO_BTF_ID with variable offset", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_0, 4), + BPF_JMP_IMM(BPF_JLE, BPF_REG_2, 4, 3), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_JMP_IMM(BPF_JGE, BPF_REG_2, 0, 3), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_2), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .fixup_kfunc_btf_id = { + { "bpf_kfunc_call_test_acquire", 3 }, + { "bpf_kfunc_call_test_release", 9 }, + { "bpf_kfunc_call_test_release", 13 }, + { "bpf_kfunc_call_test_release", 17 }, + }, + .result_unpriv = REJECT, + .result = REJECT, + .errstr = "variable ptr_ access var_off=(0x0; 0x7) disallowed", +}, { "calls: basic sanity", .insns = { @@ -94,7 +271,7 @@ }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .result = REJECT, - .errstr = "R0 invalid mem access 'inv'", + .errstr = "R0 invalid mem access 'scalar'", }, { "calls: multiple ret types in subprog 2", @@ -397,7 +574,7 @@ BPF_EXIT_INSN(), }, .result = REJECT, - .errstr = "R6 invalid mem access 'inv'", + .errstr = "R6 invalid mem access 'scalar'", .prog_type = BPF_PROG_TYPE_XDP, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, @@ -1603,7 +1780,7 @@ .prog_type = BPF_PROG_TYPE_SCHED_CLS, .fixup_map_hash_8b = { 12, 22 }, .result = REJECT, - .errstr = "R0 invalid mem access 'inv'", + .errstr = "R0 invalid mem access 'scalar'", }, { "calls: pkt_ptr spill into caller stack", diff --git a/tools/testing/selftests/bpf/verifier/ctx.c b/tools/testing/selftests/bpf/verifier/ctx.c index 23080862aafd..c8eaf0536c24 100644 --- a/tools/testing/selftests/bpf/verifier/ctx.c +++ b/tools/testing/selftests/bpf/verifier/ctx.c @@ -58,7 +58,7 @@ }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .result = REJECT, - .errstr = "dereference of modified ctx ptr", + .errstr = "negative offset ctx ptr R1 off=-612 disallowed", }, { "pass modified ctx pointer to helper, 2", @@ -71,8 +71,8 @@ }, .result_unpriv = REJECT, .result = REJECT, - .errstr_unpriv = "dereference of modified ctx ptr", - .errstr = "dereference of modified ctx ptr", + .errstr_unpriv = "negative offset ctx ptr R1 off=-612 disallowed", + .errstr = "negative offset ctx ptr R1 off=-612 disallowed", }, { "pass modified ctx pointer to helper, 3", @@ -127,7 +127,7 @@ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR, .expected_attach_type = BPF_CGROUP_UDP6_SENDMSG, .result = REJECT, - .errstr = "R1 type=inv expected=ctx", + .errstr = "R1 type=scalar expected=ctx", }, { "pass ctx or null check, 4: ctx - const", @@ -141,7 +141,7 @@ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR, .expected_attach_type = BPF_CGROUP_UDP6_SENDMSG, .result = REJECT, - .errstr = "dereference of modified ctx ptr", + .errstr = "negative offset ctx ptr R1 off=-612 disallowed", }, { "pass ctx or null check, 5: null (connect)", @@ -193,5 +193,5 @@ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, .result = REJECT, - .errstr = "R1 type=inv expected=ctx", + .errstr = "R1 type=scalar expected=ctx", }, diff --git a/tools/testing/selftests/bpf/verifier/direct_packet_access.c b/tools/testing/selftests/bpf/verifier/direct_packet_access.c index ac1e19d0f520..11acd1855acf 100644 --- a/tools/testing/selftests/bpf/verifier/direct_packet_access.c +++ b/tools/testing/selftests/bpf/verifier/direct_packet_access.c @@ -339,7 +339,7 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr = "R2 invalid mem access 'inv'", + .errstr = "R2 invalid mem access 'scalar'", .result = REJECT, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, diff --git a/tools/testing/selftests/bpf/verifier/helper_access_var_len.c b/tools/testing/selftests/bpf/verifier/helper_access_var_len.c index 0ab7f1dfc97a..a6c869a7319c 100644 --- a/tools/testing/selftests/bpf/verifier/helper_access_var_len.c +++ b/tools/testing/selftests/bpf/verifier/helper_access_var_len.c @@ -350,7 +350,7 @@ BPF_EMIT_CALL(BPF_FUNC_csum_diff), BPF_EXIT_INSN(), }, - .errstr = "R1 type=inv expected=fp", + .errstr = "R1 type=scalar expected=fp", .result = REJECT, .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, @@ -471,7 +471,7 @@ BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel), BPF_EXIT_INSN(), }, - .errstr = "R1 type=inv expected=fp", + .errstr = "R1 type=scalar expected=fp", .result = REJECT, .prog_type = BPF_PROG_TYPE_TRACEPOINT, }, @@ -484,7 +484,7 @@ BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel), BPF_EXIT_INSN(), }, - .errstr = "R1 type=inv expected=fp", + .errstr = "R1 type=scalar expected=fp", .result = REJECT, .prog_type = BPF_PROG_TYPE_TRACEPOINT, }, diff --git a/tools/testing/selftests/bpf/verifier/jmp32.c b/tools/testing/selftests/bpf/verifier/jmp32.c index 1c857b2fbdf0..6ddc418fdfaf 100644 --- a/tools/testing/selftests/bpf/verifier/jmp32.c +++ b/tools/testing/selftests/bpf/verifier/jmp32.c @@ -286,7 +286,7 @@ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'inv'", + .errstr_unpriv = "R0 invalid mem access 'scalar'", .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, @@ -356,7 +356,7 @@ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'inv'", + .errstr_unpriv = "R0 invalid mem access 'scalar'", .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, @@ -426,7 +426,7 @@ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'inv'", + .errstr_unpriv = "R0 invalid mem access 'scalar'", .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, @@ -496,7 +496,7 @@ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'inv'", + .errstr_unpriv = "R0 invalid mem access 'scalar'", .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, @@ -566,7 +566,7 @@ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'inv'", + .errstr_unpriv = "R0 invalid mem access 'scalar'", .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, @@ -636,7 +636,7 @@ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'inv'", + .errstr_unpriv = "R0 invalid mem access 'scalar'", .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, @@ -706,7 +706,7 @@ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'inv'", + .errstr_unpriv = "R0 invalid mem access 'scalar'", .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, @@ -776,7 +776,7 @@ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R0 invalid mem access 'inv'", + .errstr_unpriv = "R0 invalid mem access 'scalar'", .result_unpriv = REJECT, .result = ACCEPT, .retval = 2, diff --git a/tools/testing/selftests/bpf/verifier/precise.c b/tools/testing/selftests/bpf/verifier/precise.c index 6dc8003ffc70..9e754423fa8b 100644 --- a/tools/testing/selftests/bpf/verifier/precise.c +++ b/tools/testing/selftests/bpf/verifier/precise.c @@ -27,7 +27,7 @@ BPF_JMP_IMM(BPF_JLT, BPF_REG_2, 8, 1), BPF_EXIT_INSN(), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), /* R2=inv(umin=1, umax=8) */ + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), /* R2=scalar(umin=1, umax=8) */ BPF_MOV64_REG(BPF_REG_1, BPF_REG_FP), BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), BPF_MOV64_IMM(BPF_REG_3, 0), @@ -87,7 +87,7 @@ BPF_JMP_IMM(BPF_JLT, BPF_REG_2, 8, 1), BPF_EXIT_INSN(), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), /* R2=inv(umin=1, umax=8) */ + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), /* R2=scalar(umin=1, umax=8) */ BPF_MOV64_REG(BPF_REG_1, BPF_REG_FP), BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), BPF_MOV64_IMM(BPF_REG_3, 0), diff --git a/tools/testing/selftests/bpf/verifier/raw_stack.c b/tools/testing/selftests/bpf/verifier/raw_stack.c index cc8e8c3cdc03..eb5ed936580b 100644 --- a/tools/testing/selftests/bpf/verifier/raw_stack.c +++ b/tools/testing/selftests/bpf/verifier/raw_stack.c @@ -132,7 +132,7 @@ BPF_EXIT_INSN(), }, .result = REJECT, - .errstr = "R0 invalid mem access 'inv'", + .errstr = "R0 invalid mem access 'scalar'", .prog_type = BPF_PROG_TYPE_SCHED_CLS, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, @@ -162,7 +162,7 @@ BPF_EXIT_INSN(), }, .result = REJECT, - .errstr = "R3 invalid mem access 'inv'", + .errstr = "R3 invalid mem access 'scalar'", .prog_type = BPF_PROG_TYPE_SCHED_CLS, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, diff --git a/tools/testing/selftests/bpf/verifier/ref_tracking.c b/tools/testing/selftests/bpf/verifier/ref_tracking.c index 3b6ee009c00b..fbd682520e47 100644 --- a/tools/testing/selftests/bpf/verifier/ref_tracking.c +++ b/tools/testing/selftests/bpf/verifier/ref_tracking.c @@ -162,7 +162,7 @@ BPF_EXIT_INSN(), }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, - .errstr = "type=inv expected=sock", + .errstr = "type=scalar expected=sock", .result = REJECT, }, { @@ -178,7 +178,7 @@ BPF_EXIT_INSN(), }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, - .errstr = "type=inv expected=sock", + .errstr = "type=scalar expected=sock", .result = REJECT, }, { @@ -274,7 +274,7 @@ BPF_EXIT_INSN(), }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, - .errstr = "type=inv expected=sock", + .errstr = "type=scalar expected=sock", .result = REJECT, }, { diff --git a/tools/testing/selftests/bpf/verifier/search_pruning.c b/tools/testing/selftests/bpf/verifier/search_pruning.c index 682519769fe3..68b14fdfebdb 100644 --- a/tools/testing/selftests/bpf/verifier/search_pruning.c +++ b/tools/testing/selftests/bpf/verifier/search_pruning.c @@ -104,7 +104,7 @@ BPF_EXIT_INSN(), }, .fixup_map_hash_8b = { 3 }, - .errstr = "R6 invalid mem access 'inv'", + .errstr = "R6 invalid mem access 'scalar'", .result = REJECT, .prog_type = BPF_PROG_TYPE_TRACEPOINT, }, diff --git a/tools/testing/selftests/bpf/verifier/sock.c b/tools/testing/selftests/bpf/verifier/sock.c index ce13ece08d51..86b24cad27a7 100644 --- a/tools/testing/selftests/bpf/verifier/sock.c +++ b/tools/testing/selftests/bpf/verifier/sock.c @@ -121,7 +121,25 @@ .result = ACCEPT, }, { - "sk_fullsock(skb->sk): sk->dst_port [narrow load]", + "sk_fullsock(skb->sk): sk->dst_port [word load] (backward compatibility)", + .insns = { + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)), + BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, dst_port)), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + .result = ACCEPT, +}, +{ + "sk_fullsock(skb->sk): sk->dst_port [half load]", .insns = { BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)), BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2), @@ -139,7 +157,7 @@ .result = ACCEPT, }, { - "sk_fullsock(skb->sk): sk->dst_port [load 2nd byte]", + "sk_fullsock(skb->sk): sk->dst_port [half load] (invalid)", .insns = { BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)), BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2), @@ -149,7 +167,64 @@ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, dst_port) + 1), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, dst_port) + 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + .result = REJECT, + .errstr = "invalid sock access", +}, +{ + "sk_fullsock(skb->sk): sk->dst_port [byte load]", + .insns = { + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)), + BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_LDX_MEM(BPF_B, BPF_REG_2, BPF_REG_0, offsetof(struct bpf_sock, dst_port)), + BPF_LDX_MEM(BPF_B, BPF_REG_2, BPF_REG_0, offsetof(struct bpf_sock, dst_port) + 1), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + .result = ACCEPT, +}, +{ + "sk_fullsock(skb->sk): sk->dst_port [byte load] (invalid)", + .insns = { + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)), + BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, dst_port) + 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + .result = REJECT, + .errstr = "invalid sock access", +}, +{ + "sk_fullsock(skb->sk): past sk->dst_port [half load] (invalid)", + .insns = { + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)), + BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_0, offsetofend(struct bpf_sock, dst_port)), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, @@ -427,7 +502,7 @@ .fixup_sk_storage_map = { 11 }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .result = REJECT, - .errstr = "R3 type=inv expected=fp", + .errstr = "R3 type=scalar expected=fp", }, { "sk_storage_get(map, skb->sk, &stack_value, 1): stack_value", diff --git a/tools/testing/selftests/bpf/verifier/spill_fill.c b/tools/testing/selftests/bpf/verifier/spill_fill.c index 8cfc5349d2a8..e23f07175e1b 100644 --- a/tools/testing/selftests/bpf/verifier/spill_fill.c +++ b/tools/testing/selftests/bpf/verifier/spill_fill.c @@ -102,7 +102,7 @@ BPF_EXIT_INSN(), }, .errstr_unpriv = "attempt to corrupt spilled", - .errstr = "R0 invalid mem access 'inv", + .errstr = "R0 invalid mem access 'scalar'", .result = REJECT, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, @@ -147,11 +147,11 @@ BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_10, -8), /* r0 = r2 */ BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv20 */ + /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=20 */ BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4), - /* if (r0 > r3) R0=pkt,off=20 R2=pkt R3=pkt_end R4=inv20 */ + /* if (r0 > r3) R0=pkt,off=20 R2=pkt R3=pkt_end R4=20 */ BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), - /* r0 = *(u32 *)r2 R0=pkt,off=20,r=20 R2=pkt,r=20 R3=pkt_end R4=inv20 */ + /* r0 = *(u32 *)r2 R0=pkt,off=20,r=20 R2=pkt,r=20 R3=pkt_end R4=20 */ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), @@ -190,11 +190,11 @@ BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_10, -8), /* r0 = r2 */ BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv,umax=65535 */ + /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=umax=65535 */ BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4), - /* if (r0 > r3) R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv,umax=65535 */ + /* if (r0 > r3) R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=umax=65535 */ BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), - /* r0 = *(u32 *)r2 R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv20 */ + /* r0 = *(u32 *)r2 R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=20 */ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), @@ -222,11 +222,11 @@ BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_10, -8), /* r0 = r2 */ BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv,umax=65535 */ + /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=umax=65535 */ BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4), - /* if (r0 > r3) R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv,umax=65535 */ + /* if (r0 > r3) R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=umax=65535 */ BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), - /* r0 = *(u32 *)r2 R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv20 */ + /* r0 = *(u32 *)r2 R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=20 */ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), @@ -250,11 +250,11 @@ BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_10, -6), /* r0 = r2 */ BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv,umax=65535 */ + /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=umax=65535 */ BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4), - /* if (r0 > r3) R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv,umax=65535 */ + /* if (r0 > r3) R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=umax=65535 */ BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), - /* r0 = *(u32 *)r2 R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv20 */ + /* r0 = *(u32 *)r2 R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=20 */ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), @@ -280,11 +280,11 @@ BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_10, -4), /* r0 = r2 */ BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv,umax=U32_MAX */ + /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=umax=U32_MAX */ BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4), - /* if (r0 > r3) R0=pkt,umax=U32_MAX R2=pkt R3=pkt_end R4=inv */ + /* if (r0 > r3) R0=pkt,umax=U32_MAX R2=pkt R3=pkt_end R4= */ BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), - /* r0 = *(u32 *)r2 R0=pkt,umax=U32_MAX R2=pkt R3=pkt_end R4=inv */ + /* r0 = *(u32 *)r2 R0=pkt,umax=U32_MAX R2=pkt R3=pkt_end R4= */ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), @@ -305,13 +305,13 @@ BPF_JMP_IMM(BPF_JLE, BPF_REG_4, 40, 2), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), - /* *(u32 *)(r10 -8) = r4 R4=inv,umax=40 */ + /* *(u32 *)(r10 -8) = r4 R4=umax=40 */ BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_4, -8), /* r4 = (*u32 *)(r10 - 8) */ BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_10, -8), - /* r2 += r4 R2=pkt R4=inv,umax=40 */ + /* r2 += r4 R2=pkt R4=umax=40 */ BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_4), - /* r0 = r2 R2=pkt,umax=40 R4=inv,umax=40 */ + /* r0 = r2 R2=pkt,umax=40 R4=umax=40 */ BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), /* r2 += 20 R0=pkt,umax=40 R2=pkt,umax=40 */ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 20), diff --git a/tools/testing/selftests/bpf/verifier/unpriv.c b/tools/testing/selftests/bpf/verifier/unpriv.c index 111801aea5e3..878ca26c3f0a 100644 --- a/tools/testing/selftests/bpf/verifier/unpriv.c +++ b/tools/testing/selftests/bpf/verifier/unpriv.c @@ -214,7 +214,7 @@ BPF_EXIT_INSN(), }, .result = REJECT, - .errstr = "R1 type=inv expected=ctx", + .errstr = "R1 type=scalar expected=ctx", .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { @@ -420,7 +420,7 @@ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_7, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R7 invalid mem access 'inv'", + .errstr_unpriv = "R7 invalid mem access 'scalar'", .result_unpriv = REJECT, .result = ACCEPT, .retval = 0, diff --git a/tools/testing/selftests/bpf/verifier/value_illegal_alu.c b/tools/testing/selftests/bpf/verifier/value_illegal_alu.c index 489062867218..d6f29eb4bd57 100644 --- a/tools/testing/selftests/bpf/verifier/value_illegal_alu.c +++ b/tools/testing/selftests/bpf/verifier/value_illegal_alu.c @@ -64,7 +64,7 @@ }, .fixup_map_hash_48b = { 3 }, .errstr_unpriv = "R0 pointer arithmetic prohibited", - .errstr = "invalid mem access 'inv'", + .errstr = "invalid mem access 'scalar'", .result = REJECT, .result_unpriv = REJECT, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -89,7 +89,7 @@ }, .fixup_map_hash_48b = { 3 }, .errstr_unpriv = "leaking pointer from stack off -8", - .errstr = "R0 invalid mem access 'inv'", + .errstr = "R0 invalid mem access 'scalar'", .result = REJECT, .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, diff --git a/tools/testing/selftests/bpf/verifier/value_ptr_arith.c b/tools/testing/selftests/bpf/verifier/value_ptr_arith.c index 359f3e8f8b60..249187d3c530 100644 --- a/tools/testing/selftests/bpf/verifier/value_ptr_arith.c +++ b/tools/testing/selftests/bpf/verifier/value_ptr_arith.c @@ -397,7 +397,7 @@ .fixup_map_array_48b = { 1 }, .result = ACCEPT, .result_unpriv = REJECT, - .errstr_unpriv = "R0 invalid mem access 'inv'", + .errstr_unpriv = "R0 invalid mem access 'scalar'", .retval = 0, }, { @@ -1074,7 +1074,7 @@ }, .fixup_map_array_48b = { 3 }, .result = REJECT, - .errstr = "R0 invalid mem access 'inv'", + .errstr = "R0 invalid mem access 'scalar'", .errstr_unpriv = "R0 pointer -= pointer prohibited", }, { diff --git a/tools/testing/selftests/bpf/verifier/var_off.c b/tools/testing/selftests/bpf/verifier/var_off.c index eab1f7f56e2f..187c6f6e32bc 100644 --- a/tools/testing/selftests/bpf/verifier/var_off.c +++ b/tools/testing/selftests/bpf/verifier/var_off.c @@ -131,7 +131,7 @@ * write might have overwritten the spilled pointer (i.e. we lose track * of the spilled register when we analyze the write). */ - .errstr = "R2 invalid mem access 'inv'", + .errstr = "R2 invalid mem access 'scalar'", .result = REJECT, }, { diff --git a/tools/testing/selftests/bpf/vmtest.sh b/tools/testing/selftests/bpf/vmtest.sh index b3afd43549fa..e0bb04a97e10 100755 --- a/tools/testing/selftests/bpf/vmtest.sh +++ b/tools/testing/selftests/bpf/vmtest.sh @@ -241,7 +241,7 @@ EOF -nodefaults \ -display none \ -serial mon:stdio \ - "${qemu_flags[@]}" \ + "${QEMU_FLAGS[@]}" \ -enable-kvm \ -m 4G \ -drive file="${rootfs_img}",format=raw,index=1,media=disk,if=virtio,cache=none \ diff --git a/tools/testing/selftests/bpf/xdp_redirect_multi.c b/tools/testing/selftests/bpf/xdp_redirect_multi.c index 51c8224b4ccc..aaedbf4955c3 100644 --- a/tools/testing/selftests/bpf/xdp_redirect_multi.c +++ b/tools/testing/selftests/bpf/xdp_redirect_multi.c @@ -32,12 +32,12 @@ static void int_exit(int sig) int i; for (i = 0; ifaces[i] > 0; i++) { - if (bpf_get_link_xdp_id(ifaces[i], &prog_id, xdp_flags)) { - printf("bpf_get_link_xdp_id failed\n"); + if (bpf_xdp_query_id(ifaces[i], xdp_flags, &prog_id)) { + printf("bpf_xdp_query_id failed\n"); exit(1); } if (prog_id) - bpf_set_link_xdp_fd(ifaces[i], -1, xdp_flags); + bpf_xdp_detach(ifaces[i], xdp_flags, NULL); } exit(0); @@ -210,7 +210,7 @@ int main(int argc, char **argv) } /* bind prog_fd to each interface */ - ret = bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags); + ret = bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL); if (ret) { printf("Set xdp fd failed on %d\n", ifindex); goto err_out; diff --git a/tools/testing/selftests/bpf/xdping.c b/tools/testing/selftests/bpf/xdping.c index baa870a759a2..c567856fd1bc 100644 --- a/tools/testing/selftests/bpf/xdping.c +++ b/tools/testing/selftests/bpf/xdping.c @@ -29,7 +29,7 @@ static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; static void cleanup(int sig) { - bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + bpf_xdp_detach(ifindex, xdp_flags, NULL); if (sig) exit(1); } @@ -203,7 +203,7 @@ int main(int argc, char **argv) printf("XDP setup disrupts network connectivity, hit Ctrl+C to quit\n"); - if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) { + if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) { fprintf(stderr, "Link set xdp fd failed for %s\n", ifname); goto done; } diff --git a/tools/testing/selftests/bpf/xdpxceiver.c b/tools/testing/selftests/bpf/xdpxceiver.c index 0a5d23da486d..5f8296d29e77 100644 --- a/tools/testing/selftests/bpf/xdpxceiver.c +++ b/tools/testing/selftests/bpf/xdpxceiver.c @@ -266,22 +266,24 @@ static int xsk_configure_umem(struct xsk_umem_info *umem, void *buffer, u64 size } static int xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem, - struct ifobject *ifobject, u32 qid) + struct ifobject *ifobject, bool shared) { - struct xsk_socket_config cfg; + struct xsk_socket_config cfg = {}; struct xsk_ring_cons *rxr; struct xsk_ring_prod *txr; xsk->umem = umem; cfg.rx_size = xsk->rxqsize; cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS; - cfg.libbpf_flags = 0; + cfg.libbpf_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD; cfg.xdp_flags = ifobject->xdp_flags; cfg.bind_flags = ifobject->bind_flags; + if (shared) + cfg.bind_flags |= XDP_SHARED_UMEM; txr = ifobject->tx_on ? &xsk->tx : NULL; rxr = ifobject->rx_on ? &xsk->rx : NULL; - return xsk_socket__create(&xsk->xsk, ifobject->ifname, qid, umem->umem, rxr, txr, &cfg); + return xsk_socket__create(&xsk->xsk, ifobject->ifname, 0, umem->umem, rxr, txr, &cfg); } static struct option long_options[] = { @@ -387,7 +389,6 @@ static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx, for (i = 0; i < MAX_INTERFACES; i++) { struct ifobject *ifobj = i ? ifobj_rx : ifobj_tx; - ifobj->umem = &ifobj->umem_arr[0]; ifobj->xsk = &ifobj->xsk_arr[0]; ifobj->use_poll = false; ifobj->pacing_on = true; @@ -401,11 +402,12 @@ static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx, ifobj->tx_on = false; } + memset(ifobj->umem, 0, sizeof(*ifobj->umem)); + ifobj->umem->num_frames = DEFAULT_UMEM_BUFFERS; + ifobj->umem->frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE; + for (j = 0; j < MAX_SOCKETS; j++) { - memset(&ifobj->umem_arr[j], 0, sizeof(ifobj->umem_arr[j])); memset(&ifobj->xsk_arr[j], 0, sizeof(ifobj->xsk_arr[j])); - ifobj->umem_arr[j].num_frames = DEFAULT_UMEM_BUFFERS; - ifobj->umem_arr[j].frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE; ifobj->xsk_arr[j].rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS; } } @@ -906,7 +908,10 @@ static bool rx_stats_are_valid(struct ifobject *ifobject) return true; case STAT_TEST_RX_FULL: xsk_stat = stats.rx_ring_full; - expected_stat -= RX_FULL_RXQSIZE; + if (ifobject->umem->num_frames < XSK_RING_PROD__DEFAULT_NUM_DESCS) + expected_stat = ifobject->umem->num_frames - RX_FULL_RXQSIZE; + else + expected_stat = XSK_RING_PROD__DEFAULT_NUM_DESCS - RX_FULL_RXQSIZE; break; case STAT_TEST_RX_FILL_EMPTY: xsk_stat = stats.rx_fill_ring_empty_descs; @@ -947,7 +952,10 @@ static void tx_stats_validate(struct ifobject *ifobject) static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject) { + u64 umem_sz = ifobject->umem->num_frames * ifobject->umem->frame_size; int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; + int ret, ifindex; + void *bufs; u32 i; ifobject->ns_fd = switch_namespace(ifobject->nsname); @@ -955,23 +963,20 @@ static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject) if (ifobject->umem->unaligned_mode) mmap_flags |= MAP_HUGETLB; + bufs = mmap(NULL, umem_sz, PROT_READ | PROT_WRITE, mmap_flags, -1, 0); + if (bufs == MAP_FAILED) + exit_with_error(errno); + + ret = xsk_configure_umem(ifobject->umem, bufs, umem_sz); + if (ret) + exit_with_error(-ret); + for (i = 0; i < test->nb_sockets; i++) { - u64 umem_sz = ifobject->umem->num_frames * ifobject->umem->frame_size; u32 ctr = 0; - void *bufs; - int ret; - - bufs = mmap(NULL, umem_sz, PROT_READ | PROT_WRITE, mmap_flags, -1, 0); - if (bufs == MAP_FAILED) - exit_with_error(errno); - - ret = xsk_configure_umem(&ifobject->umem_arr[i], bufs, umem_sz); - if (ret) - exit_with_error(-ret); while (ctr++ < SOCK_RECONF_CTR) { - ret = xsk_configure_socket(&ifobject->xsk_arr[i], &ifobject->umem_arr[i], - ifobject, i); + ret = xsk_configure_socket(&ifobject->xsk_arr[i], ifobject->umem, + ifobject, !!i); if (!ret) break; @@ -982,8 +987,22 @@ static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject) } } - ifobject->umem = &ifobject->umem_arr[0]; ifobject->xsk = &ifobject->xsk_arr[0]; + + if (!ifobject->rx_on) + return; + + ifindex = if_nametoindex(ifobject->ifname); + if (!ifindex) + exit_with_error(errno); + + ret = xsk_setup_xdp_prog(ifindex, &ifobject->xsk_map_fd); + if (ret) + exit_with_error(-ret); + + ret = xsk_socket__update_xskmap(ifobject->xsk->xsk, ifobject->xsk_map_fd); + if (ret) + exit_with_error(-ret); } static void testapp_cleanup_xsk_res(struct ifobject *ifobj) @@ -1139,14 +1158,16 @@ static void testapp_bidi(struct test_spec *test) static void swap_xsk_resources(struct ifobject *ifobj_tx, struct ifobject *ifobj_rx) { + int ret; + xsk_socket__delete(ifobj_tx->xsk->xsk); - xsk_umem__delete(ifobj_tx->umem->umem); xsk_socket__delete(ifobj_rx->xsk->xsk); - xsk_umem__delete(ifobj_rx->umem->umem); - ifobj_tx->umem = &ifobj_tx->umem_arr[1]; ifobj_tx->xsk = &ifobj_tx->xsk_arr[1]; - ifobj_rx->umem = &ifobj_rx->umem_arr[1]; ifobj_rx->xsk = &ifobj_rx->xsk_arr[1]; + + ret = xsk_socket__update_xskmap(ifobj_rx->xsk->xsk, ifobj_rx->xsk_map_fd); + if (ret) + exit_with_error(-ret); } static void testapp_bpf_res(struct test_spec *test) @@ -1405,13 +1426,13 @@ static struct ifobject *ifobject_create(void) if (!ifobj->xsk_arr) goto out_xsk_arr; - ifobj->umem_arr = calloc(MAX_SOCKETS, sizeof(*ifobj->umem_arr)); - if (!ifobj->umem_arr) - goto out_umem_arr; + ifobj->umem = calloc(1, sizeof(*ifobj->umem)); + if (!ifobj->umem) + goto out_umem; return ifobj; -out_umem_arr: +out_umem: free(ifobj->xsk_arr); out_xsk_arr: free(ifobj); @@ -1420,7 +1441,7 @@ static struct ifobject *ifobject_create(void) static void ifobject_delete(struct ifobject *ifobj) { - free(ifobj->umem_arr); + free(ifobj->umem); free(ifobj->xsk_arr); free(ifobj); } diff --git a/tools/testing/selftests/bpf/xdpxceiver.h b/tools/testing/selftests/bpf/xdpxceiver.h index 2f705f44b748..62a3e6388632 100644 --- a/tools/testing/selftests/bpf/xdpxceiver.h +++ b/tools/testing/selftests/bpf/xdpxceiver.h @@ -125,10 +125,10 @@ struct ifobject { struct xsk_socket_info *xsk; struct xsk_socket_info *xsk_arr; struct xsk_umem_info *umem; - struct xsk_umem_info *umem_arr; thread_func_t func_ptr; struct pkt_stream *pkt_stream; int ns_fd; + int xsk_map_fd; u32 dst_ip; u32 src_ip; u32 xdp_flags; diff --git a/tools/testing/selftests/drivers/net/mlxsw/hw_stats_l3.sh b/tools/testing/selftests/drivers/net/mlxsw/hw_stats_l3.sh new file mode 100755 index 000000000000..941ba4c485c9 --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/hw_stats_l3.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +lib_dir=$(dirname $0)/../../../net/forwarding + +ALL_TESTS=" + l3_monitor_test +" +NUM_NETIFS=0 +source $lib_dir/lib.sh + +swp=$NETIF_NO_CABLE + +cleanup() +{ + pre_cleanup +} + +l3_monitor_test() +{ + hw_stats_monitor_test $swp l3 \ + "ip addr add dev $swp 192.0.2.1/28" \ + "ip addr del dev $swp 192.0.2.1/28" +} + +trap cleanup EXIT + +setup_wait +tests_run + +exit $EXIT_STATUS diff --git a/tools/testing/selftests/drivers/net/netdevsim/hw_stats_l3.sh b/tools/testing/selftests/drivers/net/netdevsim/hw_stats_l3.sh new file mode 100755 index 000000000000..fe1898402987 --- /dev/null +++ b/tools/testing/selftests/drivers/net/netdevsim/hw_stats_l3.sh @@ -0,0 +1,421 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +lib_dir=$(dirname $0)/../../../net/forwarding + +ALL_TESTS=" + l3_reporting_test + l3_fail_next_test + l3_counter_test + l3_rollback_test + l3_monitor_test +" + +NETDEVSIM_PATH=/sys/bus/netdevsim/ +DEV_ADDR_1=1337 +DEV_ADDR_2=1057 +DEV_ADDR_3=5417 +NUM_NETIFS=0 +source $lib_dir/lib.sh + +DUMMY_IFINDEX= + +DEV_ADDR() +{ + local n=$1; shift + local var=DEV_ADDR_$n + + echo ${!var} +} + +DEV() +{ + echo netdevsim$(DEV_ADDR $1) +} + +DEVLINK_DEV() +{ + echo netdevsim/$(DEV $1) +} + +SYSFS_NET_DIR() +{ + echo /sys/bus/netdevsim/devices/$(DEV $1)/net/ +} + +DEBUGFS_DIR() +{ + echo /sys/kernel/debug/netdevsim/$(DEV $1)/ +} + +nsim_add() +{ + local n=$1; shift + + echo "$(DEV_ADDR $n) 1" > ${NETDEVSIM_PATH}/new_device + while [ ! -d $(SYSFS_NET_DIR $n) ] ; do :; done +} + +nsim_reload() +{ + local n=$1; shift + local ns=$1; shift + + devlink dev reload $(DEVLINK_DEV $n) netns $ns + + if [ $? -ne 0 ]; then + echo "Failed to reload $(DEV $n) into netns \"testns1\"" + exit 1 + fi + +} + +nsim_del() +{ + local n=$1; shift + + echo "$(DEV_ADDR $n)" > ${NETDEVSIM_PATH}/del_device +} + +nsim_hwstats_toggle() +{ + local action=$1; shift + local instance=$1; shift + local netdev=$1; shift + local type=$1; shift + + local ifindex=$($IP -j link show dev $netdev | jq '.[].ifindex') + + echo $ifindex > $(DEBUGFS_DIR $instance)/hwstats/$type/$action +} + +nsim_hwstats_enable() +{ + nsim_hwstats_toggle enable_ifindex "$@" +} + +nsim_hwstats_disable() +{ + nsim_hwstats_toggle disable_ifindex "$@" +} + +nsim_hwstats_fail_next_enable() +{ + nsim_hwstats_toggle fail_next_enable "$@" +} + +setup_prepare() +{ + modprobe netdevsim &> /dev/null + nsim_add 1 + nsim_add 2 + nsim_add 3 + + ip netns add testns1 + + if [ $? -ne 0 ]; then + echo "Failed to add netns \"testns1\"" + exit 1 + fi + + nsim_reload 1 testns1 + nsim_reload 2 testns1 + nsim_reload 3 testns1 + + IP="ip -n testns1" + + $IP link add name dummy1 type dummy + $IP link set dev dummy1 up + DUMMY_IFINDEX=$($IP -j link show dev dummy1 | jq '.[].ifindex') +} + +cleanup() +{ + pre_cleanup + + $IP link del name dummy1 + ip netns del testns1 + nsim_del 3 + nsim_del 2 + nsim_del 1 + modprobe -r netdevsim &> /dev/null +} + +netdev_hwstats_used() +{ + local netdev=$1; shift + local type=$1; shift + + $IP -j stats show dev "$netdev" group offload subgroup hw_stats_info | + jq '.[].info.l3_stats.used' +} + +netdev_check_used() +{ + local netdev=$1; shift + local type=$1; shift + + [[ $(netdev_hwstats_used $netdev $type) == "true" ]] +} + +netdev_check_unused() +{ + local netdev=$1; shift + local type=$1; shift + + [[ $(netdev_hwstats_used $netdev $type) == "false" ]] +} + +netdev_hwstats_request() +{ + local netdev=$1; shift + local type=$1; shift + + $IP -j stats show dev "$netdev" group offload subgroup hw_stats_info | + jq ".[].info.${type}_stats.request" +} + +netdev_check_requested() +{ + local netdev=$1; shift + local type=$1; shift + + [[ $(netdev_hwstats_request $netdev $type) == "true" ]] +} + +netdev_check_unrequested() +{ + local netdev=$1; shift + local type=$1; shift + + [[ $(netdev_hwstats_request $netdev $type) == "false" ]] +} + +reporting_test() +{ + local type=$1; shift + local instance=1 + + RET=0 + + [[ -n $(netdev_hwstats_used dummy1 $type) ]] + check_err $? "$type stats not reported" + + netdev_check_unused dummy1 $type + check_err $? "$type stats reported as used before either device or netdevsim request" + + nsim_hwstats_enable $instance dummy1 $type + netdev_check_unused dummy1 $type + check_err $? "$type stats reported as used before device request" + netdev_check_unrequested dummy1 $type + check_err $? "$type stats reported as requested before device request" + + $IP stats set dev dummy1 ${type}_stats on + netdev_check_used dummy1 $type + check_err $? "$type stats reported as not used after both device and netdevsim request" + netdev_check_requested dummy1 $type + check_err $? "$type stats reported as not requested after device request" + + nsim_hwstats_disable $instance dummy1 $type + netdev_check_unused dummy1 $type + check_err $? "$type stats reported as used after netdevsim request withdrawn" + + nsim_hwstats_enable $instance dummy1 $type + netdev_check_used dummy1 $type + check_err $? "$type stats reported as not used after netdevsim request reenabled" + + $IP stats set dev dummy1 ${type}_stats off + netdev_check_unused dummy1 $type + check_err $? "$type stats reported as used after device request withdrawn" + netdev_check_unrequested dummy1 $type + check_err $? "$type stats reported as requested after device request withdrawn" + + nsim_hwstats_disable $instance dummy1 $type + netdev_check_unused dummy1 $type + check_err $? "$type stats reported as used after both requests withdrawn" + + log_test "Reporting of $type stats usage" +} + +l3_reporting_test() +{ + reporting_test l3 +} + +__fail_next_test() +{ + local instance=$1; shift + local type=$1; shift + + RET=0 + + netdev_check_unused dummy1 $type + check_err $? "$type stats reported as used before either device or netdevsim request" + + nsim_hwstats_enable $instance dummy1 $type + nsim_hwstats_fail_next_enable $instance dummy1 $type + netdev_check_unused dummy1 $type + check_err $? "$type stats reported as used before device request" + netdev_check_unrequested dummy1 $type + check_err $? "$type stats reported as requested before device request" + + $IP stats set dev dummy1 ${type}_stats on 2>/dev/null + check_fail $? "$type stats request not bounced as it should have been" + netdev_check_unused dummy1 $type + check_err $? "$type stats reported as used after bounce" + netdev_check_unrequested dummy1 $type + check_err $? "$type stats reported as requested after bounce" + + $IP stats set dev dummy1 ${type}_stats on + check_err $? "$type stats request failed when it shouldn't have" + netdev_check_used dummy1 $type + check_err $? "$type stats reported as not used after both device and netdevsim request" + netdev_check_requested dummy1 $type + check_err $? "$type stats reported as not requested after device request" + + $IP stats set dev dummy1 ${type}_stats off + nsim_hwstats_disable $instance dummy1 $type + + log_test "Injected failure of $type stats enablement (netdevsim #$instance)" +} + +fail_next_test() +{ + __fail_next_test 1 "$@" + __fail_next_test 2 "$@" + __fail_next_test 3 "$@" +} + +l3_fail_next_test() +{ + fail_next_test l3 +} + +get_hwstat() +{ + local netdev=$1; shift + local type=$1; shift + local selector=$1; shift + + $IP -j stats show dev $netdev group offload subgroup ${type}_stats | + jq ".[0].stats64.${selector}" +} + +counter_test() +{ + local type=$1; shift + local instance=1 + + RET=0 + + nsim_hwstats_enable $instance dummy1 $type + $IP stats set dev dummy1 ${type}_stats on + netdev_check_used dummy1 $type + check_err $? "$type stats reported as not used after both device and netdevsim request" + + # Netdevsim counts 10pps on ingress. We should see maybe a couple + # packets, unless things take a reeealy long time. + local pkts=$(get_hwstat dummy1 l3 rx.packets) + ((pkts < 10)) + check_err $? "$type stats show >= 10 packets after first enablement" + + sleep 2 + + local pkts=$(get_hwstat dummy1 l3 rx.packets) + ((pkts >= 20)) + check_err $? "$type stats show < 20 packets after 2s passed" + + $IP stats set dev dummy1 ${type}_stats off + + sleep 2 + + $IP stats set dev dummy1 ${type}_stats on + local pkts=$(get_hwstat dummy1 l3 rx.packets) + ((pkts < 10)) + check_err $? "$type stats show >= 10 packets after second enablement" + + $IP stats set dev dummy1 ${type}_stats off + nsim_hwstats_fail_next_enable $instance dummy1 $type + $IP stats set dev dummy1 ${type}_stats on 2>/dev/null + check_fail $? "$type stats request not bounced as it should have been" + + sleep 2 + + $IP stats set dev dummy1 ${type}_stats on + local pkts=$(get_hwstat dummy1 l3 rx.packets) + ((pkts < 10)) + check_err $? "$type stats show >= 10 packets after post-fail enablement" + + $IP stats set dev dummy1 ${type}_stats off + + log_test "Counter values in $type stats" +} + +l3_counter_test() +{ + counter_test l3 +} + +rollback_test() +{ + local type=$1; shift + + RET=0 + + nsim_hwstats_enable 1 dummy1 l3 + nsim_hwstats_enable 2 dummy1 l3 + nsim_hwstats_enable 3 dummy1 l3 + + # The three netdevsim instances are registered in order of their number + # one after another. It is reasonable to expect that whatever + # notifications take place hit no. 2 in between hitting nos. 1 and 3, + # whatever the actual order. This allows us to test that a fail caused + # by no. 2 does not leave the system in a partial state, and rolls + # everything back. + + nsim_hwstats_fail_next_enable 2 dummy1 l3 + $IP stats set dev dummy1 ${type}_stats on 2>/dev/null + check_fail $? "$type stats request not bounced as it should have been" + + netdev_check_unused dummy1 $type + check_err $? "$type stats reported as used after bounce" + netdev_check_unrequested dummy1 $type + check_err $? "$type stats reported as requested after bounce" + + sleep 2 + + $IP stats set dev dummy1 ${type}_stats on + check_err $? "$type stats request not upheld as it should have been" + + local pkts=$(get_hwstat dummy1 l3 rx.packets) + ((pkts < 10)) + check_err $? "$type stats show $pkts packets after post-fail enablement" + + $IP stats set dev dummy1 ${type}_stats off + + nsim_hwstats_disable 3 dummy1 l3 + nsim_hwstats_disable 2 dummy1 l3 + nsim_hwstats_disable 1 dummy1 l3 + + log_test "Failure in $type stats enablement rolled back" +} + +l3_rollback_test() +{ + rollback_test l3 +} + +l3_monitor_test() +{ + hw_stats_monitor_test dummy1 l3 \ + "nsim_hwstats_enable 1 dummy1 l3" \ + "nsim_hwstats_disable 1 dummy1 l3" \ + "$IP" +} + +trap cleanup EXIT + +setup_prepare +tests_run + +exit $EXIT_STATUS diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 7581a7348e1b..21a411b04890 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -35,4 +35,4 @@ test_unix_oob gro ioam6_parser toeplitz -cmsg_so_mark +cmsg_sender diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 0b1488616c55..3fe2515aa616 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -30,6 +30,7 @@ TEST_PROGS += ioam6.sh TEST_PROGS += gro.sh TEST_PROGS += gre_gso.sh TEST_PROGS += cmsg_so_mark.sh +TEST_PROGS += cmsg_time.sh TEST_PROGS += srv6_end_dt46_l3vpn_test.sh TEST_PROGS += srv6_end_dt4_l3vpn_test.sh TEST_PROGS += srv6_end_dt6_l3vpn_test.sh @@ -52,7 +53,7 @@ TEST_GEN_FILES += gro TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls TEST_GEN_FILES += toeplitz -TEST_GEN_FILES += cmsg_so_mark +TEST_GEN_FILES += cmsg_sender TEST_FILES := settings diff --git a/tools/testing/selftests/net/af_unix/test_unix_oob.c b/tools/testing/selftests/net/af_unix/test_unix_oob.c index 3dece8b29253..b57e91e1c3f2 100644 --- a/tools/testing/selftests/net/af_unix/test_unix_oob.c +++ b/tools/testing/selftests/net/af_unix/test_unix_oob.c @@ -218,10 +218,10 @@ main(int argc, char **argv) /* Test 1: * veriyf that SIGURG is - * delivered and 63 bytes are - * read and oob is '@' + * delivered, 63 bytes are + * read, oob is '@', and POLLPRI works. */ - wait_for_data(pfd, POLLIN | POLLPRI); + wait_for_data(pfd, POLLPRI); read_oob(pfd, &oob); len = read_data(pfd, buf, 1024); if (!signal_recvd || len != 63 || oob != '@') { diff --git a/tools/testing/selftests/net/cmsg_ipv6.sh b/tools/testing/selftests/net/cmsg_ipv6.sh new file mode 100755 index 000000000000..2d89cb0ad288 --- /dev/null +++ b/tools/testing/selftests/net/cmsg_ipv6.sh @@ -0,0 +1,156 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +ksft_skip=4 + +NS=ns +IP6=2001:db8:1::1/64 +TGT6=2001:db8:1::2 +TMPF=`mktemp` + +cleanup() +{ + rm -f $TMPF + ip netns del $NS +} + +trap cleanup EXIT + +NSEXE="ip netns exec $NS" + +tcpdump -h | grep immediate-mode >> /dev/null +if [ $? -ne 0 ]; then + echo "SKIP - tcpdump with --immediate-mode option required" + exit $ksft_skip +fi + +# Namespaces +ip netns add $NS + +$NSEXE sysctl -w net.ipv4.ping_group_range='0 2147483647' > /dev/null + +# Connectivity +ip -netns $NS link add type dummy +ip -netns $NS link set dev dummy0 up +ip -netns $NS addr add $IP6 dev dummy0 + +# Test +BAD=0 +TOTAL=0 + +check_result() { + ((TOTAL++)) + if [ $1 -ne $2 ]; then + echo " Case $3 returned $1, expected $2" + ((BAD++)) + fi +} + +# IPV6_DONTFRAG +for ovr in setsock cmsg both diff; do + for df in 0 1; do + for p in u i r; do + [ $p == "u" ] && prot=UDP + [ $p == "i" ] && prot=ICMP + [ $p == "r" ] && prot=RAW + + [ $ovr == "setsock" ] && m="-F $df" + [ $ovr == "cmsg" ] && m="-f $df" + [ $ovr == "both" ] && m="-F $df -f $df" + [ $ovr == "diff" ] && m="-F $((1 - df)) -f $df" + + $NSEXE ./cmsg_sender -s -S 2000 -6 -p $p $m $TGT6 1234 + check_result $? $df "DONTFRAG $prot $ovr" + done + done +done + +# IPV6_TCLASS +TOS=0x10 +TOS2=0x20 + +ip -6 -netns $NS rule add tos $TOS lookup 300 +ip -6 -netns $NS route add table 300 prohibit any + +for ovr in setsock cmsg both diff; do + for p in u i r; do + [ $p == "u" ] && prot=UDP + [ $p == "i" ] && prot=ICMP + [ $p == "r" ] && prot=RAW + + [ $ovr == "setsock" ] && m="-C" + [ $ovr == "cmsg" ] && m="-c" + [ $ovr == "both" ] && m="-C $((TOS2)) -c" + [ $ovr == "diff" ] && m="-C $((TOS )) -c" + + $NSEXE nohup tcpdump --immediate-mode -p -ni dummy0 -w $TMPF -c 4 2> /dev/null & + BG=$! + sleep 0.05 + + $NSEXE ./cmsg_sender -6 -p $p $m $((TOS2)) $TGT6 1234 + check_result $? 0 "TCLASS $prot $ovr - pass" + + while [ -d /proc/$BG ]; do + $NSEXE ./cmsg_sender -6 -p u $TGT6 1234 + done + + tcpdump -r $TMPF -v 2>&1 | grep "class $TOS2" >> /dev/null + check_result $? 0 "TCLASS $prot $ovr - packet data" + rm $TMPF + + [ $ovr == "both" ] && m="-C $((TOS )) -c" + [ $ovr == "diff" ] && m="-C $((TOS2)) -c" + + $NSEXE ./cmsg_sender -6 -p $p $m $((TOS)) -s $TGT6 1234 + check_result $? 1 "TCLASS $prot $ovr - rejection" + done +done + +# IPV6_HOPLIMIT +LIM=4 + +for ovr in setsock cmsg both diff; do + for p in u i r; do + [ $p == "u" ] && prot=UDP + [ $p == "i" ] && prot=ICMP + [ $p == "r" ] && prot=RAW + + [ $ovr == "setsock" ] && m="-L" + [ $ovr == "cmsg" ] && m="-l" + [ $ovr == "both" ] && m="-L $LIM -l" + [ $ovr == "diff" ] && m="-L $((LIM + 1)) -l" + + $NSEXE nohup tcpdump --immediate-mode -p -ni dummy0 -w $TMPF -c 4 2> /dev/null & + BG=$! + sleep 0.05 + + $NSEXE ./cmsg_sender -6 -p $p $m $LIM $TGT6 1234 + check_result $? 0 "HOPLIMIT $prot $ovr - pass" + + while [ -d /proc/$BG ]; do + $NSEXE ./cmsg_sender -6 -p u $TGT6 1234 + done + + tcpdump -r $TMPF -v 2>&1 | grep "hlim $LIM[^0-9]" >> /dev/null + check_result $? 0 "HOPLIMIT $prot $ovr - packet data" + rm $TMPF + done +done + +# IPV6 exthdr +for p in u i r; do + # Very basic "does it crash" test + for h in h d r; do + $NSEXE ./cmsg_sender -p $p -6 -H $h $TGT6 1234 + check_result $? 0 "ExtHdr $prot $ovr - pass" + done +done + +# Summary +if [ $BAD -ne 0 ]; then + echo "FAIL - $BAD/$TOTAL cases failed" + exit 1 +else + echo "OK" + exit 0 +fi diff --git a/tools/testing/selftests/net/cmsg_sender.c b/tools/testing/selftests/net/cmsg_sender.c new file mode 100644 index 000000000000..bc2162909a1a --- /dev/null +++ b/tools/testing/selftests/net/cmsg_sender.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest.h" + +enum { + ERN_SUCCESS = 0, + /* Well defined errors, callers may depend on these */ + ERN_SEND = 1, + /* Informational, can reorder */ + ERN_HELP, + ERN_SEND_SHORT, + ERN_SOCK_CREATE, + ERN_RESOLVE, + ERN_CMSG_WR, + ERN_SOCKOPT, + ERN_GETTIME, + ERN_RECVERR, + ERN_CMSG_RD, + ERN_CMSG_RCV, +}; + +struct option_cmsg_u32 { + bool ena; + unsigned int val; +}; + +struct options { + bool silent_send; + const char *host; + const char *service; + unsigned int size; + struct { + unsigned int mark; + unsigned int dontfrag; + unsigned int tclass; + unsigned int hlimit; + } sockopt; + struct { + unsigned int family; + unsigned int type; + unsigned int proto; + } sock; + struct option_cmsg_u32 mark; + struct { + bool ena; + unsigned int delay; + } txtime; + struct { + bool ena; + } ts; + struct { + struct option_cmsg_u32 dontfrag; + struct option_cmsg_u32 tclass; + struct option_cmsg_u32 hlimit; + struct option_cmsg_u32 exthdr; + } v6; +} opt = { + .size = 13, + .sock = { + .family = AF_UNSPEC, + .type = SOCK_DGRAM, + .proto = IPPROTO_UDP, + }, +}; + +static struct timespec time_start_real; +static struct timespec time_start_mono; + +static void __attribute__((noreturn)) cs_usage(const char *bin) +{ + printf("Usage: %s [opts] \n", bin); + printf("Options:\n" + "\t\t-s Silent send() failures\n" + "\t\t-S send() size\n" + "\t\t-4/-6 Force IPv4 / IPv6 only\n" + "\t\t-p prot Socket protocol\n" + "\t\t (u = UDP (default); i = ICMP; r = RAW)\n" + "\n" + "\t\t-m val Set SO_MARK with given value\n" + "\t\t-M val Set SO_MARK via setsockopt\n" + "\t\t-d val Set SO_TXTIME with given delay (usec)\n" + "\t\t-t Enable time stamp reporting\n" + "\t\t-f val Set don't fragment via cmsg\n" + "\t\t-F val Set don't fragment via setsockopt\n" + "\t\t-c val Set TCLASS via cmsg\n" + "\t\t-C val Set TCLASS via setsockopt\n" + "\t\t-l val Set HOPLIMIT via cmsg\n" + "\t\t-L val Set HOPLIMIT via setsockopt\n" + "\t\t-H type Add an IPv6 header option\n" + "\t\t (h = HOP; d = DST; r = RTDST)" + ""); + exit(ERN_HELP); +} + +static void cs_parse_args(int argc, char *argv[]) +{ + char o; + + while ((o = getopt(argc, argv, "46sS:p:m:M:d:tf:F:c:C:l:L:H:")) != -1) { + switch (o) { + case 's': + opt.silent_send = true; + break; + case 'S': + opt.size = atoi(optarg); + break; + case '4': + opt.sock.family = AF_INET; + break; + case '6': + opt.sock.family = AF_INET6; + break; + case 'p': + if (*optarg == 'u' || *optarg == 'U') { + opt.sock.proto = IPPROTO_UDP; + } else if (*optarg == 'i' || *optarg == 'I') { + opt.sock.proto = IPPROTO_ICMP; + } else if (*optarg == 'r') { + opt.sock.type = SOCK_RAW; + } else { + printf("Error: unknown protocol: %s\n", optarg); + cs_usage(argv[0]); + } + break; + + case 'm': + opt.mark.ena = true; + opt.mark.val = atoi(optarg); + break; + case 'M': + opt.sockopt.mark = atoi(optarg); + break; + case 'd': + opt.txtime.ena = true; + opt.txtime.delay = atoi(optarg); + break; + case 't': + opt.ts.ena = true; + break; + case 'f': + opt.v6.dontfrag.ena = true; + opt.v6.dontfrag.val = atoi(optarg); + break; + case 'F': + opt.sockopt.dontfrag = atoi(optarg); + break; + case 'c': + opt.v6.tclass.ena = true; + opt.v6.tclass.val = atoi(optarg); + break; + case 'C': + opt.sockopt.tclass = atoi(optarg); + break; + case 'l': + opt.v6.hlimit.ena = true; + opt.v6.hlimit.val = atoi(optarg); + break; + case 'L': + opt.sockopt.hlimit = atoi(optarg); + break; + case 'H': + opt.v6.exthdr.ena = true; + switch (optarg[0]) { + case 'h': + opt.v6.exthdr.val = IPV6_HOPOPTS; + break; + case 'd': + opt.v6.exthdr.val = IPV6_DSTOPTS; + break; + case 'r': + opt.v6.exthdr.val = IPV6_RTHDRDSTOPTS; + break; + default: + printf("Error: hdr type: %s\n", optarg); + break; + } + break; + } + } + + if (optind != argc - 2) + cs_usage(argv[0]); + + opt.host = argv[optind]; + opt.service = argv[optind + 1]; +} + +static void memrnd(void *s, size_t n) +{ + int *dword = s; + char *byte; + + for (; n >= 4; n -= 4) + *dword++ = rand(); + byte = (void *)dword; + while (n--) + *byte++ = rand(); +} + +static void +ca_write_cmsg_u32(char *cbuf, size_t cbuf_sz, size_t *cmsg_len, + int level, int optname, struct option_cmsg_u32 *uopt) +{ + struct cmsghdr *cmsg; + + if (!uopt->ena) + return; + + cmsg = (struct cmsghdr *)(cbuf + *cmsg_len); + *cmsg_len += CMSG_SPACE(sizeof(__u32)); + if (cbuf_sz < *cmsg_len) + error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small"); + + cmsg->cmsg_level = level; + cmsg->cmsg_type = optname; + cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); + *(__u32 *)CMSG_DATA(cmsg) = uopt->val; +} + +static void +cs_write_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz) +{ + struct cmsghdr *cmsg; + size_t cmsg_len; + + msg->msg_control = cbuf; + cmsg_len = 0; + + ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len, + SOL_SOCKET, SO_MARK, &opt.mark); + ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len, + SOL_IPV6, IPV6_DONTFRAG, &opt.v6.dontfrag); + ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len, + SOL_IPV6, IPV6_TCLASS, &opt.v6.tclass); + ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len, + SOL_IPV6, IPV6_HOPLIMIT, &opt.v6.hlimit); + + if (opt.txtime.ena) { + struct sock_txtime so_txtime = { + .clockid = CLOCK_MONOTONIC, + }; + __u64 txtime; + + if (setsockopt(fd, SOL_SOCKET, SO_TXTIME, + &so_txtime, sizeof(so_txtime))) + error(ERN_SOCKOPT, errno, "setsockopt TXTIME"); + + txtime = time_start_mono.tv_sec * (1000ULL * 1000 * 1000) + + time_start_mono.tv_nsec + + opt.txtime.delay * 1000; + + cmsg = (struct cmsghdr *)(cbuf + cmsg_len); + cmsg_len += CMSG_SPACE(sizeof(txtime)); + if (cbuf_sz < cmsg_len) + error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small"); + + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_TXTIME; + cmsg->cmsg_len = CMSG_LEN(sizeof(txtime)); + memcpy(CMSG_DATA(cmsg), &txtime, sizeof(txtime)); + } + if (opt.ts.ena) { + __u32 val = SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_OPT_TSONLY; + + if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, + &val, sizeof(val))) + error(ERN_SOCKOPT, errno, "setsockopt TIMESTAMPING"); + + cmsg = (struct cmsghdr *)(cbuf + cmsg_len); + cmsg_len += CMSG_SPACE(sizeof(__u32)); + if (cbuf_sz < cmsg_len) + error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small"); + + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SO_TIMESTAMPING; + cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); + *(__u32 *)CMSG_DATA(cmsg) = SOF_TIMESTAMPING_TX_SCHED | + SOF_TIMESTAMPING_TX_SOFTWARE; + } + if (opt.v6.exthdr.ena) { + cmsg = (struct cmsghdr *)(cbuf + cmsg_len); + cmsg_len += CMSG_SPACE(8); + if (cbuf_sz < cmsg_len) + error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small"); + + cmsg->cmsg_level = SOL_IPV6; + cmsg->cmsg_type = opt.v6.exthdr.val; + cmsg->cmsg_len = CMSG_LEN(8); + *(__u64 *)CMSG_DATA(cmsg) = 0; + } + + if (cmsg_len) + msg->msg_controllen = cmsg_len; + else + msg->msg_control = NULL; +} + +static const char *cs_ts_info2str(unsigned int info) +{ + static const char *names[] = { + [SCM_TSTAMP_SND] = "SND", + [SCM_TSTAMP_SCHED] = "SCHED", + [SCM_TSTAMP_ACK] = "ACK", + }; + + if (info < ARRAY_SIZE(names)) + return names[info]; + return "unknown"; +} + +static void +cs_read_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz) +{ + struct sock_extended_err *see; + struct scm_timestamping *ts; + struct cmsghdr *cmsg; + int i, err; + + if (!opt.ts.ena) + return; + msg->msg_control = cbuf; + msg->msg_controllen = cbuf_sz; + + while (true) { + ts = NULL; + see = NULL; + memset(cbuf, 0, cbuf_sz); + + err = recvmsg(fd, msg, MSG_ERRQUEUE); + if (err < 0) { + if (errno == EAGAIN) + break; + error(ERN_RECVERR, errno, "recvmsg ERRQ"); + } + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TIMESTAMPING_OLD) { + if (cmsg->cmsg_len < sizeof(*ts)) + error(ERN_CMSG_RD, EINVAL, "TS cmsg"); + + ts = (void *)CMSG_DATA(cmsg); + } + if ((cmsg->cmsg_level == SOL_IP && + cmsg->cmsg_type == IP_RECVERR) || + (cmsg->cmsg_level == SOL_IPV6 && + cmsg->cmsg_type == IPV6_RECVERR)) { + if (cmsg->cmsg_len < sizeof(*see)) + error(ERN_CMSG_RD, EINVAL, "sock_err cmsg"); + + see = (void *)CMSG_DATA(cmsg); + } + } + + if (!ts) + error(ERN_CMSG_RCV, ENOENT, "TS cmsg not found"); + if (!see) + error(ERN_CMSG_RCV, ENOENT, "sock_err cmsg not found"); + + for (i = 0; i < 3; i++) { + unsigned long long rel_time; + + if (!ts->ts[i].tv_sec && !ts->ts[i].tv_nsec) + continue; + + rel_time = (ts->ts[i].tv_sec - time_start_real.tv_sec) * + (1000ULL * 1000) + + (ts->ts[i].tv_nsec - time_start_real.tv_nsec) / + 1000; + printf(" %5s ts%d %lluus\n", + cs_ts_info2str(see->ee_info), + i, rel_time); + } + } +} + +static void ca_set_sockopts(int fd) +{ + if (opt.sockopt.mark && + setsockopt(fd, SOL_SOCKET, SO_MARK, + &opt.sockopt.mark, sizeof(opt.sockopt.mark))) + error(ERN_SOCKOPT, errno, "setsockopt SO_MARK"); + if (opt.sockopt.dontfrag && + setsockopt(fd, SOL_IPV6, IPV6_DONTFRAG, + &opt.sockopt.dontfrag, sizeof(opt.sockopt.dontfrag))) + error(ERN_SOCKOPT, errno, "setsockopt IPV6_DONTFRAG"); + if (opt.sockopt.tclass && + setsockopt(fd, SOL_IPV6, IPV6_TCLASS, + &opt.sockopt.tclass, sizeof(opt.sockopt.tclass))) + error(ERN_SOCKOPT, errno, "setsockopt IPV6_TCLASS"); + if (opt.sockopt.hlimit && + setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, + &opt.sockopt.hlimit, sizeof(opt.sockopt.hlimit))) + error(ERN_SOCKOPT, errno, "setsockopt IPV6_HOPLIMIT"); +} + +int main(int argc, char *argv[]) +{ + struct addrinfo hints, *ai; + struct iovec iov[1]; + struct msghdr msg; + char cbuf[1024]; + char *buf; + int err; + int fd; + + cs_parse_args(argc, argv); + + buf = malloc(opt.size); + memrnd(buf, opt.size); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = opt.sock.family; + + ai = NULL; + err = getaddrinfo(opt.host, opt.service, &hints, &ai); + if (err) { + fprintf(stderr, "Can't resolve address [%s]:%s\n", + opt.host, opt.service); + return ERN_SOCK_CREATE; + } + + if (ai->ai_family == AF_INET6 && opt.sock.proto == IPPROTO_ICMP) + opt.sock.proto = IPPROTO_ICMPV6; + + fd = socket(ai->ai_family, opt.sock.type, opt.sock.proto); + if (fd < 0) { + fprintf(stderr, "Can't open socket: %s\n", strerror(errno)); + freeaddrinfo(ai); + return ERN_RESOLVE; + } + + if (opt.sock.proto == IPPROTO_ICMP) { + buf[0] = ICMP_ECHO; + buf[1] = 0; + } else if (opt.sock.proto == IPPROTO_ICMPV6) { + buf[0] = ICMPV6_ECHO_REQUEST; + buf[1] = 0; + } else if (opt.sock.type == SOCK_RAW) { + struct udphdr hdr = { 1, 2, htons(opt.size), 0 }; + struct sockaddr_in6 *sin6 = (void *)ai->ai_addr;; + + memcpy(buf, &hdr, sizeof(hdr)); + sin6->sin6_port = htons(opt.sock.proto); + } + + ca_set_sockopts(fd); + + if (clock_gettime(CLOCK_REALTIME, &time_start_real)) + error(ERN_GETTIME, errno, "gettime REALTIME"); + if (clock_gettime(CLOCK_MONOTONIC, &time_start_mono)) + error(ERN_GETTIME, errno, "gettime MONOTONIC"); + + iov[0].iov_base = buf; + iov[0].iov_len = opt.size; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = ai->ai_addr; + msg.msg_namelen = ai->ai_addrlen; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + cs_write_cmsg(fd, &msg, cbuf, sizeof(cbuf)); + + err = sendmsg(fd, &msg, 0); + if (err < 0) { + if (!opt.silent_send) + fprintf(stderr, "send failed: %s\n", strerror(errno)); + err = ERN_SEND; + goto err_out; + } else if (err != (int)opt.size) { + fprintf(stderr, "short send\n"); + err = ERN_SEND_SHORT; + goto err_out; + } else { + err = ERN_SUCCESS; + } + + /* Make sure all timestamps have time to loop back */ + usleep(opt.txtime.delay); + + cs_read_cmsg(fd, &msg, cbuf, sizeof(cbuf)); + +err_out: + close(fd); + freeaddrinfo(ai); + return err; +} diff --git a/tools/testing/selftests/net/cmsg_so_mark.c b/tools/testing/selftests/net/cmsg_so_mark.c deleted file mode 100644 index 27f2804892a7..000000000000 --- a/tools/testing/selftests/net/cmsg_so_mark.c +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, const char **argv) -{ - char cbuf[CMSG_SPACE(sizeof(__u32))]; - struct addrinfo hints, *ai; - struct cmsghdr *cmsg; - struct iovec iov[1]; - struct msghdr msg; - int mark; - int err; - int fd; - - if (argc != 4) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; - } - mark = atoi(argv[3]); - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_DGRAM; - - ai = NULL; - err = getaddrinfo(argv[1], argv[2], &hints, &ai); - if (err) { - fprintf(stderr, "Can't resolve address: %s\n", strerror(errno)); - return 1; - } - - fd = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP); - if (fd < 0) { - fprintf(stderr, "Can't open socket: %s\n", strerror(errno)); - freeaddrinfo(ai); - return 1; - } - - iov[0].iov_base = "bla"; - iov[0].iov_len = 4; - - msg.msg_name = ai->ai_addr; - msg.msg_namelen = ai->ai_addrlen; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = cbuf; - msg.msg_controllen = sizeof(cbuf); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SO_MARK; - cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); - *(__u32 *)CMSG_DATA(cmsg) = mark; - - err = sendmsg(fd, &msg, 0); - - close(fd); - freeaddrinfo(ai); - return err != 4; -} diff --git a/tools/testing/selftests/net/cmsg_so_mark.sh b/tools/testing/selftests/net/cmsg_so_mark.sh index 19c6aab8d0e9..1650b8622f2f 100755 --- a/tools/testing/selftests/net/cmsg_so_mark.sh +++ b/tools/testing/selftests/net/cmsg_so_mark.sh @@ -18,6 +18,8 @@ trap cleanup EXIT # Namespaces ip netns add $NS +ip netns exec $NS sysctl -w net.ipv4.ping_group_range='0 2147483647' > /dev/null + # Connectivity ip -netns $NS link add type dummy ip -netns $NS link set dev dummy0 up @@ -41,15 +43,29 @@ check_result() { fi } -ip netns exec $NS ./cmsg_so_mark $TGT4 1234 $((MARK + 1)) -check_result $? 0 "IPv4 pass" -ip netns exec $NS ./cmsg_so_mark $TGT6 1234 $((MARK + 1)) -check_result $? 0 "IPv6 pass" +for ovr in setsock cmsg both; do + for i in 4 6; do + [ $i == 4 ] && TGT=$TGT4 || TGT=$TGT6 -ip netns exec $NS ./cmsg_so_mark $TGT4 1234 $MARK -check_result $? 1 "IPv4 rejection" -ip netns exec $NS ./cmsg_so_mark $TGT6 1234 $MARK -check_result $? 1 "IPv6 rejection" + for p in u i r; do + [ $p == "u" ] && prot=UDP + [ $p == "i" ] && prot=ICMP + [ $p == "r" ] && prot=RAW + + [ $ovr == "setsock" ] && m="-M" + [ $ovr == "cmsg" ] && m="-m" + [ $ovr == "both" ] && m="-M $MARK -m" + + ip netns exec $NS ./cmsg_sender -$i -p $p $m $((MARK + 1)) $TGT 1234 + check_result $? 0 "$prot $ovr - pass" + + [ $ovr == "diff" ] && m="-M $((MARK + 1)) -m" + + ip netns exec $NS ./cmsg_sender -$i -p $p $m $MARK -s $TGT 1234 + check_result $? 1 "$prot $ovr - rejection" + done + done +done # Summary if [ $BAD -ne 0 ]; then diff --git a/tools/testing/selftests/net/cmsg_time.sh b/tools/testing/selftests/net/cmsg_time.sh new file mode 100755 index 000000000000..91161e1da734 --- /dev/null +++ b/tools/testing/selftests/net/cmsg_time.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +NS=ns +IP4=172.16.0.1/24 +TGT4=172.16.0.2 +IP6=2001:db8:1::1/64 +TGT6=2001:db8:1::2 + +cleanup() +{ + ip netns del $NS +} + +trap cleanup EXIT + +# Namespaces +ip netns add $NS + +ip netns exec $NS sysctl -w net.ipv4.ping_group_range='0 2147483647' > /dev/null + +# Connectivity +ip -netns $NS link add type dummy +ip -netns $NS link set dev dummy0 up +ip -netns $NS addr add $IP4 dev dummy0 +ip -netns $NS addr add $IP6 dev dummy0 + +# Need FQ for TXTIME +ip netns exec $NS tc qdisc replace dev dummy0 root fq + +# Test +BAD=0 +TOTAL=0 + +check_result() { + ((TOTAL++)) + if [ $1 -ne 0 ]; then + echo " Case $4 returned $1, expected 0" + ((BAD++)) + elif [ "$2" != "$3" ]; then + echo " Case $4 returned '$2', expected '$3'" + ((BAD++)) + fi +} + +for i in "-4 $TGT4" "-6 $TGT6"; do + for p in u i r; do + [ $p == "u" ] && prot=UDPv${i:1:2} + [ $p == "i" ] && prot=ICMPv${i:1:2} + [ $p == "r" ] && prot=RAWv${i:1:2} + + ts=$(ip netns exec $NS ./cmsg_sender -p $p $i 1234) + check_result $? "$ts" "" "$prot - no options" + + ts=$(ip netns exec $NS ./cmsg_sender -p $p $i 1234 -t | wc -l) + check_result $? "$ts" "2" "$prot - ts cnt" + ts=$(ip netns exec $NS ./cmsg_sender -p $p $i 1234 -t | + sed -n "s/.*SCHED ts0 [0-9].*/OK/p") + check_result $? "$ts" "OK" "$prot - ts0 SCHED" + ts=$(ip netns exec $NS ./cmsg_sender -p $p $i 1234 -t | + sed -n "s/.*SND ts0 [0-9].*/OK/p") + check_result $? "$ts" "OK" "$prot - ts0 SND" + + ts=$(ip netns exec $NS ./cmsg_sender -p $p $i 1234 -t -d 1000 | + awk '/SND/ { if ($3 > 1000) print "OK"; }') + check_result $? "$ts" "OK" "$prot - TXTIME abs" + + ts=$(ip netns exec $NS ./cmsg_sender -p $p $i 1234 -t -d 1000 | + awk '/SND/ {snd=$3} + /SCHED/ {sch=$3} + END { if (snd - sch > 500) print "OK"; }') + check_result $? "$ts" "OK" "$prot - TXTIME rel" + done +done + +# Summary +if [ $BAD -ne 0 ]; then + echo "FAIL - $BAD/$TOTAL cases failed" + exit 1 +else + echo "OK" + exit 0 +fi diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh index 3f4c8cfe7aca..47c4d4b4a44a 100755 --- a/tools/testing/selftests/net/fcnal-test.sh +++ b/tools/testing/selftests/net/fcnal-test.sh @@ -750,7 +750,7 @@ ipv4_ping_vrf() log_start show_hint "Fails since address on vrf device is out of device scope" run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a} - log_test_addr ${a} $? 1 "ping local, device bind" + log_test_addr ${a} $? 2 "ping local, device bind" done # diff --git a/tools/testing/selftests/net/fib_rule_tests.sh b/tools/testing/selftests/net/fib_rule_tests.sh index 43ea8407a82e..4f70baad867d 100755 --- a/tools/testing/selftests/net/fib_rule_tests.sh +++ b/tools/testing/selftests/net/fib_rule_tests.sh @@ -96,7 +96,7 @@ fib_rule6_del() fib_rule6_del_by_pref() { - pref=$($IP -6 rule show | grep "$1 lookup $TABLE" | cut -d ":" -f 1) + pref=$($IP -6 rule show $1 table $RTABLE | cut -d ":" -f 1) $IP -6 rule del pref $pref } @@ -104,17 +104,36 @@ fib_rule6_test_match_n_redirect() { local match="$1" local getmatch="$2" + local description="$3" $IP -6 rule add $match table $RTABLE $IP -6 route get $GW_IP6 $getmatch | grep -q "table $RTABLE" - log_test $? 0 "rule6 check: $1" + log_test $? 0 "rule6 check: $description" fib_rule6_del_by_pref "$match" - log_test $? 0 "rule6 del by pref: $match" + log_test $? 0 "rule6 del by pref: $description" +} + +fib_rule6_test_reject() +{ + local match="$1" + local rc + + $IP -6 rule add $match table $RTABLE 2>/dev/null + rc=$? + log_test $rc 2 "rule6 check: $match" + + if [ $rc -eq 0 ]; then + $IP -6 rule del $match table $RTABLE + fi } fib_rule6_test() { + local getmatch + local match + local cnt + # setup the fib rule redirect route $IP -6 route add table $RTABLE default via $GW_IP6 dev $DEV onlink @@ -124,8 +143,21 @@ fib_rule6_test() match="from $SRC_IP6 iif $DEV" fib_rule6_test_match_n_redirect "$match" "$match" "iif redirect to table" + # Reject dsfield (tos) options which have ECN bits set + for cnt in $(seq 1 3); do + match="dsfield $cnt" + fib_rule6_test_reject "$match" + done + + # Don't take ECN bits into account when matching on dsfield match="tos 0x10" - fib_rule6_test_match_n_redirect "$match" "$match" "tos redirect to table" + for cnt in "0x10" "0x11" "0x12" "0x13"; do + # Using option 'tos' instead of 'dsfield' as old iproute2 + # versions don't support 'dsfield' in ip rule show. + getmatch="tos $cnt" + fib_rule6_test_match_n_redirect "$match" "$getmatch" \ + "$getmatch redirect to table" + done match="fwmark 0x64" getmatch="mark 0x64" @@ -165,7 +197,7 @@ fib_rule4_del() fib_rule4_del_by_pref() { - pref=$($IP rule show | grep "$1 lookup $TABLE" | cut -d ":" -f 1) + pref=$($IP rule show $1 table $RTABLE | cut -d ":" -f 1) $IP rule del pref $pref } @@ -173,17 +205,36 @@ fib_rule4_test_match_n_redirect() { local match="$1" local getmatch="$2" + local description="$3" $IP rule add $match table $RTABLE $IP route get $GW_IP4 $getmatch | grep -q "table $RTABLE" - log_test $? 0 "rule4 check: $1" + log_test $? 0 "rule4 check: $description" fib_rule4_del_by_pref "$match" - log_test $? 0 "rule4 del by pref: $match" + log_test $? 0 "rule4 del by pref: $description" +} + +fib_rule4_test_reject() +{ + local match="$1" + local rc + + $IP rule add $match table $RTABLE 2>/dev/null + rc=$? + log_test $rc 2 "rule4 check: $match" + + if [ $rc -eq 0 ]; then + $IP rule del $match table $RTABLE + fi } fib_rule4_test() { + local getmatch + local match + local cnt + # setup the fib rule redirect route $IP route add table $RTABLE default via $GW_IP4 dev $DEV onlink @@ -192,14 +243,27 @@ fib_rule4_test() # need enable forwarding and disable rp_filter temporarily as all the # addresses are in the same subnet and egress device == ingress device. - ip netns exec testns sysctl -w net.ipv4.ip_forward=1 - ip netns exec testns sysctl -w net.ipv4.conf.$DEV.rp_filter=0 + ip netns exec testns sysctl -qw net.ipv4.ip_forward=1 + ip netns exec testns sysctl -qw net.ipv4.conf.$DEV.rp_filter=0 match="from $SRC_IP iif $DEV" fib_rule4_test_match_n_redirect "$match" "$match" "iif redirect to table" - ip netns exec testns sysctl -w net.ipv4.ip_forward=0 + ip netns exec testns sysctl -qw net.ipv4.ip_forward=0 + # Reject dsfield (tos) options which have ECN bits set + for cnt in $(seq 1 3); do + match="dsfield $cnt" + fib_rule4_test_reject "$match" + done + + # Don't take ECN bits into account when matching on dsfield match="tos 0x10" - fib_rule4_test_match_n_redirect "$match" "$match" "tos redirect to table" + for cnt in "0x10" "0x11" "0x12" "0x13"; do + # Using option 'tos' instead of 'dsfield' as old iproute2 + # versions don't support 'dsfield' in ip rule show. + getmatch="tos $cnt" + fib_rule4_test_match_n_redirect "$match" "$getmatch" \ + "$getmatch redirect to table" + done match="fwmark 0x64" getmatch="mark 0x64" diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh index 996af1ae3d3d..2271a8727f62 100755 --- a/tools/testing/selftests/net/fib_tests.sh +++ b/tools/testing/selftests/net/fib_tests.sh @@ -9,7 +9,7 @@ ret=0 ksft_skip=4 # all tests in this script. Can be overridden with -t option -TESTS="unregister down carrier nexthop suppress ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw rp_filter ipv4_del_addr ipv4_mangle ipv6_mangle" +TESTS="unregister down carrier nexthop suppress ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw rp_filter ipv4_del_addr ipv4_mangle ipv6_mangle ipv4_bcast_neigh" VERBOSE=0 PAUSE_ON_FAIL=no @@ -988,12 +988,25 @@ ipv6_rt_replace() ipv6_rt_replace_mpath } +ipv6_rt_dsfield() +{ + echo + echo "IPv6 route with dsfield tests" + + run_cmd "$IP -6 route flush 2001:db8:102::/64" + + # IPv6 doesn't support routing based on dsfield + run_cmd "$IP -6 route add 2001:db8:102::/64 dsfield 0x04 via 2001:db8:101::2" + log_test $? 2 "Reject route with dsfield" +} + ipv6_route_test() { route_setup ipv6_rt_add ipv6_rt_replace + ipv6_rt_dsfield route_cleanup } @@ -1447,6 +1460,81 @@ ipv4_local_rt_cache() log_test $? 0 "Cached route removed from VRF port device" } +ipv4_rt_dsfield() +{ + echo + echo "IPv4 route with dsfield tests" + + run_cmd "$IP route flush 172.16.102.0/24" + + # New routes should reject dsfield options that interfere with ECN + run_cmd "$IP route add 172.16.102.0/24 dsfield 0x01 via 172.16.101.2" + log_test $? 2 "Reject route with dsfield 0x01" + + run_cmd "$IP route add 172.16.102.0/24 dsfield 0x02 via 172.16.101.2" + log_test $? 2 "Reject route with dsfield 0x02" + + run_cmd "$IP route add 172.16.102.0/24 dsfield 0x03 via 172.16.101.2" + log_test $? 2 "Reject route with dsfield 0x03" + + # A generic route that doesn't take DSCP into account + run_cmd "$IP route add 172.16.102.0/24 via 172.16.101.2" + + # A more specific route for DSCP 0x10 + run_cmd "$IP route add 172.16.102.0/24 dsfield 0x10 via 172.16.103.2" + + # DSCP 0x10 should match the specific route, no matter the ECN bits + $IP route get fibmatch 172.16.102.1 dsfield 0x10 | \ + grep -q "via 172.16.103.2" + log_test $? 0 "IPv4 route with DSCP and ECN:Not-ECT" + + $IP route get fibmatch 172.16.102.1 dsfield 0x11 | \ + grep -q "via 172.16.103.2" + log_test $? 0 "IPv4 route with DSCP and ECN:ECT(1)" + + $IP route get fibmatch 172.16.102.1 dsfield 0x12 | \ + grep -q "via 172.16.103.2" + log_test $? 0 "IPv4 route with DSCP and ECN:ECT(0)" + + $IP route get fibmatch 172.16.102.1 dsfield 0x13 | \ + grep -q "via 172.16.103.2" + log_test $? 0 "IPv4 route with DSCP and ECN:CE" + + # Unknown DSCP should match the generic route, no matter the ECN bits + $IP route get fibmatch 172.16.102.1 dsfield 0x14 | \ + grep -q "via 172.16.101.2" + log_test $? 0 "IPv4 route with unknown DSCP and ECN:Not-ECT" + + $IP route get fibmatch 172.16.102.1 dsfield 0x15 | \ + grep -q "via 172.16.101.2" + log_test $? 0 "IPv4 route with unknown DSCP and ECN:ECT(1)" + + $IP route get fibmatch 172.16.102.1 dsfield 0x16 | \ + grep -q "via 172.16.101.2" + log_test $? 0 "IPv4 route with unknown DSCP and ECN:ECT(0)" + + $IP route get fibmatch 172.16.102.1 dsfield 0x17 | \ + grep -q "via 172.16.101.2" + log_test $? 0 "IPv4 route with unknown DSCP and ECN:CE" + + # Null DSCP should match the generic route, no matter the ECN bits + $IP route get fibmatch 172.16.102.1 dsfield 0x00 | \ + grep -q "via 172.16.101.2" + log_test $? 0 "IPv4 route with no DSCP and ECN:Not-ECT" + + $IP route get fibmatch 172.16.102.1 dsfield 0x01 | \ + grep -q "via 172.16.101.2" + log_test $? 0 "IPv4 route with no DSCP and ECN:ECT(1)" + + $IP route get fibmatch 172.16.102.1 dsfield 0x02 | \ + grep -q "via 172.16.101.2" + log_test $? 0 "IPv4 route with no DSCP and ECN:ECT(0)" + + $IP route get fibmatch 172.16.102.1 dsfield 0x03 | \ + grep -q "via 172.16.101.2" + log_test $? 0 "IPv4 route with no DSCP and ECN:CE" +} + ipv4_route_test() { route_setup @@ -1454,6 +1542,7 @@ ipv4_route_test() ipv4_rt_add ipv4_rt_replace ipv4_local_rt_cache + ipv4_rt_dsfield route_cleanup } @@ -1865,6 +1954,61 @@ ipv6_mangle_test() route_cleanup } +ip_neigh_get_check() +{ + ip neigh help 2>&1 | grep -q 'ip neigh get' + if [ $? -ne 0 ]; then + echo "iproute2 command does not support neigh get. Skipping test" + return 1 + fi + + return 0 +} + +ipv4_bcast_neigh_test() +{ + local rc + + echo + echo "IPv4 broadcast neighbour tests" + + ip_neigh_get_check || return 1 + + setup + + set -e + run_cmd "$IP neigh add 192.0.2.111 lladdr 00:11:22:33:44:55 nud perm dev dummy0" + run_cmd "$IP neigh add 192.0.2.255 lladdr 00:11:22:33:44:55 nud perm dev dummy0" + + run_cmd "$IP neigh get 192.0.2.111 dev dummy0" + run_cmd "$IP neigh get 192.0.2.255 dev dummy0" + + run_cmd "$IP address add 192.0.2.1/24 broadcast 192.0.2.111 dev dummy0" + + run_cmd "$IP neigh add 203.0.113.111 nud failed dev dummy0" + run_cmd "$IP neigh add 203.0.113.255 nud failed dev dummy0" + + run_cmd "$IP neigh get 203.0.113.111 dev dummy0" + run_cmd "$IP neigh get 203.0.113.255 dev dummy0" + + run_cmd "$IP address add 203.0.113.1/24 broadcast 203.0.113.111 dev dummy0" + set +e + + run_cmd "$IP neigh get 192.0.2.111 dev dummy0" + log_test $? 0 "Resolved neighbour for broadcast address" + + run_cmd "$IP neigh get 192.0.2.255 dev dummy0" + log_test $? 0 "Resolved neighbour for network broadcast address" + + run_cmd "$IP neigh get 203.0.113.111 dev dummy0" + log_test $? 2 "Unresolved neighbour for broadcast address" + + run_cmd "$IP neigh get 203.0.113.255 dev dummy0" + log_test $? 2 "Unresolved neighbour for network broadcast address" + + cleanup +} + ################################################################################ # usage @@ -1939,6 +2083,7 @@ do ipv4_route_v6_gw) ipv4_route_v6_gw_test;; ipv4_mangle) ipv4_mangle_test;; ipv6_mangle) ipv6_mangle_test;; + ipv4_bcast_neigh) ipv4_bcast_neigh_test;; help) echo "Test names: $TESTS"; exit 0;; esac diff --git a/tools/testing/selftests/net/forwarding/Makefile b/tools/testing/selftests/net/forwarding/Makefile index 72ee644d47bf..8fa97ae9af9e 100644 --- a/tools/testing/selftests/net/forwarding/Makefile +++ b/tools/testing/selftests/net/forwarding/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ OR MIT TEST_PROGS = bridge_igmp.sh \ + bridge_locked_port.sh \ bridge_port_isolation.sh \ bridge_sticky_fdb.sh \ bridge_vlan_aware.sh \ diff --git a/tools/testing/selftests/net/forwarding/bridge_locked_port.sh b/tools/testing/selftests/net/forwarding/bridge_locked_port.sh new file mode 100755 index 000000000000..5b02b6b60ce7 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/bridge_locked_port.sh @@ -0,0 +1,176 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +ALL_TESTS="locked_port_ipv4 locked_port_ipv6 locked_port_vlan" +NUM_NETIFS=4 +CHECK_TC="no" +source lib.sh + +h1_create() +{ + simple_if_init $h1 192.0.2.1/24 2001:db8:1::1/64 + vlan_create $h1 100 v$h1 198.51.100.1/24 +} + +h1_destroy() +{ + vlan_destroy $h1 100 + simple_if_fini $h1 192.0.2.1/24 2001:db8:1::1/64 +} + +h2_create() +{ + simple_if_init $h2 192.0.2.2/24 2001:db8:1::2/64 + vlan_create $h2 100 v$h2 198.51.100.2/24 +} + +h2_destroy() +{ + vlan_destroy $h2 100 + simple_if_fini $h2 192.0.2.2/24 2001:db8:1::2/64 +} + +switch_create() +{ + ip link add dev br0 type bridge vlan_filtering 1 + + ip link set dev $swp1 master br0 + ip link set dev $swp2 master br0 + + bridge link set dev $swp1 learning off + + ip link set dev br0 up + ip link set dev $swp1 up + ip link set dev $swp2 up +} + +switch_destroy() +{ + ip link set dev $swp2 down + ip link set dev $swp1 down + + ip link del dev br0 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + vrf_prepare + + h1_create + h2_create + + switch_create +} + +cleanup() +{ + pre_cleanup + + switch_destroy + + h2_destroy + h1_destroy + + vrf_cleanup +} + +locked_port_ipv4() +{ + RET=0 + + check_locked_port_support || return 0 + + ping_do $h1 192.0.2.2 + check_err $? "Ping did not work before locking port" + + bridge link set dev $swp1 locked on + + ping_do $h1 192.0.2.2 + check_fail $? "Ping worked after locking port, but before adding FDB entry" + + bridge fdb add `mac_get $h1` dev $swp1 master static + + ping_do $h1 192.0.2.2 + check_err $? "Ping did not work after locking port and adding FDB entry" + + bridge link set dev $swp1 locked off + bridge fdb del `mac_get $h1` dev $swp1 master static + + ping_do $h1 192.0.2.2 + check_err $? "Ping did not work after unlocking port and removing FDB entry." + + log_test "Locked port ipv4" +} + +locked_port_vlan() +{ + RET=0 + + check_locked_port_support || return 0 + + bridge vlan add vid 100 dev $swp1 + bridge vlan add vid 100 dev $swp2 + + ping_do $h1.100 198.51.100.2 + check_err $? "Ping through vlan did not work before locking port" + + bridge link set dev $swp1 locked on + ping_do $h1.100 198.51.100.2 + check_fail $? "Ping through vlan worked after locking port, but before adding FDB entry" + + bridge fdb add `mac_get $h1` dev $swp1 vlan 100 master static + + ping_do $h1.100 198.51.100.2 + check_err $? "Ping through vlan did not work after locking port and adding FDB entry" + + bridge link set dev $swp1 locked off + bridge fdb del `mac_get $h1` dev $swp1 vlan 100 master static + + ping_do $h1.100 198.51.100.2 + check_err $? "Ping through vlan did not work after unlocking port and removing FDB entry" + + bridge vlan del vid 100 dev $swp1 + bridge vlan del vid 100 dev $swp2 + log_test "Locked port vlan" +} + +locked_port_ipv6() +{ + RET=0 + check_locked_port_support || return 0 + + ping6_do $h1 2001:db8:1::2 + check_err $? "Ping6 did not work before locking port" + + bridge link set dev $swp1 locked on + + ping6_do $h1 2001:db8:1::2 + check_fail $? "Ping6 worked after locking port, but before adding FDB entry" + + bridge fdb add `mac_get $h1` dev $swp1 master static + ping6_do $h1 2001:db8:1::2 + check_err $? "Ping6 did not work after locking port and adding FDB entry" + + bridge link set dev $swp1 locked off + bridge fdb del `mac_get $h1` dev $swp1 master static + + ping6_do $h1 2001:db8:1::2 + check_err $? "Ping6 did not work after unlocking port and removing FDB entry" + + log_test "Locked port ipv6" +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tests_run + +exit $EXIT_STATUS diff --git a/tools/testing/selftests/net/forwarding/bridge_vlan_aware.sh b/tools/testing/selftests/net/forwarding/bridge_vlan_aware.sh index b90dff8d3a94..64bd00fe9a4f 100755 --- a/tools/testing/selftests/net/forwarding/bridge_vlan_aware.sh +++ b/tools/testing/selftests/net/forwarding/bridge_vlan_aware.sh @@ -28,8 +28,9 @@ h2_destroy() switch_create() { - # 10 Seconds ageing time. - ip link add dev br0 type bridge vlan_filtering 1 ageing_time 1000 \ + ip link add dev br0 type bridge \ + vlan_filtering 1 \ + ageing_time $LOW_AGEING_TIME \ mcast_snooping 0 ip link set dev $swp1 master br0 diff --git a/tools/testing/selftests/net/forwarding/bridge_vlan_unaware.sh b/tools/testing/selftests/net/forwarding/bridge_vlan_unaware.sh index c15c6c85c984..1c8a26046589 100755 --- a/tools/testing/selftests/net/forwarding/bridge_vlan_unaware.sh +++ b/tools/testing/selftests/net/forwarding/bridge_vlan_unaware.sh @@ -27,8 +27,9 @@ h2_destroy() switch_create() { - # 10 Seconds ageing time. - ip link add dev br0 type bridge ageing_time 1000 mcast_snooping 0 + ip link add dev br0 type bridge \ + ageing_time $LOW_AGEING_TIME \ + mcast_snooping 0 ip link set dev $swp1 master br0 ip link set dev $swp2 master br0 diff --git a/tools/testing/selftests/net/forwarding/fib_offload_lib.sh b/tools/testing/selftests/net/forwarding/fib_offload_lib.sh index e134a5f529c9..1b3b46292179 100644 --- a/tools/testing/selftests/net/forwarding/fib_offload_lib.sh +++ b/tools/testing/selftests/net/forwarding/fib_offload_lib.sh @@ -99,15 +99,15 @@ fib_ipv4_tos_test() fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 0 metric 1024" false check_err $? "Route not in hardware when should" - ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 2 metric 1024 - fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 2 metric 1024" false + ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 8 metric 1024 + fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 8 metric 1024" false check_err $? "Highest TOS route not in hardware when should" fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 0 metric 1024" true check_err $? "Lowest TOS route still in hardware when should not" - ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 1 metric 1024 - fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 1 metric 1024" true + ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 4 metric 1024 + fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 4 metric 1024" true check_err $? "Middle TOS route in hardware when should not" log_test "IPv4 routes with TOS" @@ -277,11 +277,11 @@ fib_ipv4_replay_tos_test() ip -n $ns link set dev dummy1 up ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 0 - ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 1 + ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 4 devlink -N $ns dev reload $devlink_dev - fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 1" false + fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 4" false check_err $? "Highest TOS route not in hardware when should" fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 0" true diff --git a/tools/testing/selftests/net/forwarding/forwarding.config.sample b/tools/testing/selftests/net/forwarding/forwarding.config.sample index b0980a2efa31..4a546509de90 100644 --- a/tools/testing/selftests/net/forwarding/forwarding.config.sample +++ b/tools/testing/selftests/net/forwarding/forwarding.config.sample @@ -41,6 +41,8 @@ NETIF_CREATE=yes # Timeout (in seconds) before ping exits regardless of how many packets have # been sent or received PING_TIMEOUT=5 +# Minimum ageing_time (in centiseconds) supported by hardware +LOW_AGEING_TIME=1000 # Flag for tc match, supposed to be skip_sw/skip_hw which means do not process # filter by software/hardware TC_FLAG=skip_hw diff --git a/tools/testing/selftests/net/forwarding/hw_stats_l3.sh b/tools/testing/selftests/net/forwarding/hw_stats_l3.sh new file mode 100755 index 000000000000..1c11c4256d06 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/hw_stats_l3.sh @@ -0,0 +1,332 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# +--------------------+ +----------------------+ +# | H1 | | H2 | +# | | | | +# | $h1.200 + | | + $h2.200 | +# | 192.0.2.1/28 | | | | 192.0.2.18/28 | +# | 2001:db8:1::1/64 | | | | 2001:db8:2::1/64 | +# | | | | | | +# | $h1 + | | + $h2 | +# | | | | | | +# +------------------|-+ +-|--------------------+ +# | | +# +------------------|-------------------------|--------------------+ +# | SW | | | +# | | | | +# | $rp1 + + $rp2 | +# | | | | +# | $rp1.200 + + $rp2.200 | +# | 192.0.2.2/28 192.0.2.17/28 | +# | 2001:db8:1::2/64 2001:db8:2::2/64 | +# | | +# +-----------------------------------------------------------------+ + +ALL_TESTS=" + ping_ipv4 + ping_ipv6 + test_stats_rx_ipv4 + test_stats_tx_ipv4 + test_stats_rx_ipv6 + test_stats_tx_ipv6 + respin_enablement + test_stats_rx_ipv4 + test_stats_tx_ipv4 + test_stats_rx_ipv6 + test_stats_tx_ipv6 + reapply_config + ping_ipv4 + ping_ipv6 + test_stats_rx_ipv4 + test_stats_tx_ipv4 + test_stats_rx_ipv6 + test_stats_tx_ipv6 + test_stats_report_rx + test_stats_report_tx + test_destroy_enabled + test_double_enable +" +NUM_NETIFS=4 +source lib.sh + +h1_create() +{ + simple_if_init $h1 + vlan_create $h1 200 v$h1 192.0.2.1/28 2001:db8:1::1/64 + ip route add 192.0.2.16/28 vrf v$h1 nexthop via 192.0.2.2 + ip -6 route add 2001:db8:2::/64 vrf v$h1 nexthop via 2001:db8:1::2 +} + +h1_destroy() +{ + ip -6 route del 2001:db8:2::/64 vrf v$h1 nexthop via 2001:db8:1::2 + ip route del 192.0.2.16/28 vrf v$h1 nexthop via 192.0.2.2 + vlan_destroy $h1 200 + simple_if_fini $h1 +} + +h2_create() +{ + simple_if_init $h2 + vlan_create $h2 200 v$h2 192.0.2.18/28 2001:db8:2::1/64 + ip route add 192.0.2.0/28 vrf v$h2 nexthop via 192.0.2.17 + ip -6 route add 2001:db8:1::/64 vrf v$h2 nexthop via 2001:db8:2::2 +} + +h2_destroy() +{ + ip -6 route del 2001:db8:1::/64 vrf v$h2 nexthop via 2001:db8:2::2 + ip route del 192.0.2.0/28 vrf v$h2 nexthop via 192.0.2.17 + vlan_destroy $h2 200 + simple_if_fini $h2 +} + +router_rp1_200_create() +{ + ip link add name $rp1.200 up \ + link $rp1 addrgenmode eui64 type vlan id 200 + ip address add dev $rp1.200 192.0.2.2/28 + ip address add dev $rp1.200 2001:db8:1::2/64 + ip stats set dev $rp1.200 l3_stats on +} + +router_rp1_200_destroy() +{ + ip stats set dev $rp1.200 l3_stats off + ip address del dev $rp1.200 2001:db8:1::2/64 + ip address del dev $rp1.200 192.0.2.2/28 + ip link del dev $rp1.200 +} + +router_create() +{ + ip link set dev $rp1 up + router_rp1_200_create + + ip link set dev $rp2 up + vlan_create $rp2 200 "" 192.0.2.17/28 2001:db8:2::2/64 +} + +router_destroy() +{ + vlan_destroy $rp2 200 + ip link set dev $rp2 down + + router_rp1_200_destroy + ip link set dev $rp1 down +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + rp1=${NETIFS[p2]} + + rp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + rp1mac=$(mac_get $rp1) + rp2mac=$(mac_get $rp2) + + vrf_prepare + + h1_create + h2_create + + router_create + + forwarding_enable +} + +cleanup() +{ + pre_cleanup + + forwarding_restore + + router_destroy + + h2_destroy + h1_destroy + + vrf_cleanup +} + +ping_ipv4() +{ + ping_test $h1.200 192.0.2.18 " IPv4" +} + +ping_ipv6() +{ + ping_test $h1.200 2001:db8:2::1 " IPv6" +} + +get_l3_stat() +{ + local selector=$1; shift + + ip -j stats show dev $rp1.200 group offload subgroup l3_stats | + jq '.[0].stats64.'$selector +} + +send_packets_rx_ipv4() +{ + # Send 21 packets instead of 20, because the first one might trap and go + # through the SW datapath, which might not bump the HW counter. + $MZ $h1.200 -c 21 -d 20msec -p 100 \ + -a own -b $rp1mac -A 192.0.2.1 -B 192.0.2.18 \ + -q -t udp sp=54321,dp=12345 +} + +send_packets_rx_ipv6() +{ + $MZ $h1.200 -6 -c 21 -d 20msec -p 100 \ + -a own -b $rp1mac -A 2001:db8:1::1 -B 2001:db8:2::1 \ + -q -t udp sp=54321,dp=12345 +} + +send_packets_tx_ipv4() +{ + $MZ $h2.200 -c 21 -d 20msec -p 100 \ + -a own -b $rp2mac -A 192.0.2.18 -B 192.0.2.1 \ + -q -t udp sp=54321,dp=12345 +} + +send_packets_tx_ipv6() +{ + $MZ $h2.200 -6 -c 21 -d 20msec -p 100 \ + -a own -b $rp2mac -A 2001:db8:2::1 -B 2001:db8:1::1 \ + -q -t udp sp=54321,dp=12345 +} + +___test_stats() +{ + local dir=$1; shift + local prot=$1; shift + + local a + local b + + a=$(get_l3_stat ${dir}.packets) + send_packets_${dir}_${prot} + "$@" + b=$(busywait "$TC_HIT_TIMEOUT" until_counter_is ">= $a + 20" \ + get_l3_stat ${dir}.packets) + check_err $? "Traffic not reflected in the counter: $a -> $b" +} + +__test_stats() +{ + local dir=$1; shift + local prot=$1; shift + + RET=0 + ___test_stats "$dir" "$prot" + log_test "Test $dir packets: $prot" +} + +test_stats_rx_ipv4() +{ + __test_stats rx ipv4 +} + +test_stats_tx_ipv4() +{ + __test_stats tx ipv4 +} + +test_stats_rx_ipv6() +{ + __test_stats rx ipv6 +} + +test_stats_tx_ipv6() +{ + __test_stats tx ipv6 +} + +# Make sure everything works well even after stats have been disabled and +# reenabled on the same device without touching the L3 configuration. +respin_enablement() +{ + log_info "Turning stats off and on again" + ip stats set dev $rp1.200 l3_stats off + ip stats set dev $rp1.200 l3_stats on +} + +# For the initial run, l3_stats is enabled on a completely set up netdevice. Now +# do it the other way around: enabling the L3 stats on an L2 netdevice, and only +# then apply the L3 configuration. +reapply_config() +{ + log_info "Reapplying configuration" + + router_rp1_200_destroy + + ip link add name $rp1.200 link $rp1 addrgenmode none type vlan id 200 + ip stats set dev $rp1.200 l3_stats on + ip link set dev $rp1.200 up addrgenmode eui64 + ip address add dev $rp1.200 192.0.2.2/28 + ip address add dev $rp1.200 2001:db8:1::2/64 +} + +__test_stats_report() +{ + local dir=$1; shift + local prot=$1; shift + + local a + local b + + RET=0 + + a=$(get_l3_stat ${dir}.packets) + send_packets_${dir}_${prot} + ip address flush dev $rp1.200 + b=$(busywait "$TC_HIT_TIMEOUT" until_counter_is ">= $a + 20" \ + get_l3_stat ${dir}.packets) + check_err $? "Traffic not reflected in the counter: $a -> $b" + log_test "Test ${dir} packets: stats pushed on loss of L3" + + ip stats set dev $rp1.200 l3_stats off + ip link del dev $rp1.200 + router_rp1_200_create +} + +test_stats_report_rx() +{ + __test_stats_report rx ipv4 +} + +test_stats_report_tx() +{ + __test_stats_report tx ipv4 +} + +test_destroy_enabled() +{ + RET=0 + + ip link del dev $rp1.200 + router_rp1_200_create + + log_test "Destroy l3_stats-enabled netdev" +} + +test_double_enable() +{ + RET=0 + ___test_stats rx ipv4 \ + ip stats set dev $rp1.200 l3_stats on + log_test "Test stat retention across a spurious enablement" +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tests_run + +exit $EXIT_STATUS diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 7da783d6f453..664b9ecaf228 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -24,6 +24,7 @@ PING_COUNT=${PING_COUNT:=10} PING_TIMEOUT=${PING_TIMEOUT:=5} WAIT_TIMEOUT=${WAIT_TIMEOUT:=20} INTERFACE_TIMEOUT=${INTERFACE_TIMEOUT:=600} +LOW_AGEING_TIME=${LOW_AGEING_TIME:=1000} REQUIRE_JQ=${REQUIRE_JQ:=yes} REQUIRE_MZ=${REQUIRE_MZ:=yes} @@ -125,6 +126,14 @@ check_ethtool_lanes_support() fi } +check_locked_port_support() +{ + if ! bridge -d link show | grep -q " locked"; then + echo "SKIP: iproute2 too old; Locked port feature not supported." + return $ksft_skip + fi +} + if [[ "$(id -u)" -ne 0 ]]; then echo "SKIP: need root privileges" exit $ksft_skip @@ -1489,3 +1498,63 @@ brmcast_check_sg_state() check_err_fail $should_fail $? "Entry $src has blocked flag" done } + +start_ip_monitor() +{ + local mtype=$1; shift + local ip=${1-ip}; shift + + # start the monitor in the background + tmpfile=`mktemp /var/run/nexthoptestXXX` + mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null` + sleep 0.2 + echo "$mpid $tmpfile" +} + +stop_ip_monitor() +{ + local mpid=$1; shift + local tmpfile=$1; shift + local el=$1; shift + local what=$1; shift + + sleep 0.2 + kill $mpid + local lines=`grep '^\w' $tmpfile | wc -l` + test $lines -eq $el + check_err $? "$what: $lines lines of events, expected $el" + rm -rf $tmpfile +} + +hw_stats_monitor_test() +{ + local dev=$1; shift + local type=$1; shift + local make_suitable=$1; shift + local make_unsuitable=$1; shift + local ip=${1-ip}; shift + + RET=0 + + # Expect a notification about enablement. + local ipmout=$(start_ip_monitor stats "$ip") + $ip stats set dev $dev ${type}_stats on + stop_ip_monitor $ipmout 1 "${type}_stats enablement" + + # Expect a notification about offload. + local ipmout=$(start_ip_monitor stats "$ip") + $make_suitable + stop_ip_monitor $ipmout 1 "${type}_stats installation" + + # Expect a notification about loss of offload. + local ipmout=$(start_ip_monitor stats "$ip") + $make_unsuitable + stop_ip_monitor $ipmout 1 "${type}_stats deinstallation" + + # Expect a notification about disablement + local ipmout=$(start_ip_monitor stats "$ip") + $ip stats set dev $dev ${type}_stats off + stop_ip_monitor $ipmout 1 "${type}_stats disablement" + + log_test "${type}_stats notifications" +} diff --git a/tools/testing/selftests/net/forwarding/pedit_ip.sh b/tools/testing/selftests/net/forwarding/pedit_ip.sh new file mode 100755 index 000000000000..d14efb2d23b2 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/pedit_ip.sh @@ -0,0 +1,201 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# This test sends traffic from H1 to H2. Either on ingress of $swp1, or on +# egress of $swp2, the traffic is acted upon by a pedit action. An ingress +# filter installed on $h2 verifies that the packet looks like expected. +# +# +----------------------+ +----------------------+ +# | H1 | | H2 | +# | + $h1 | | $h2 + | +# | | 192.0.2.1/28 | | 192.0.2.2/28 | | +# +----|-----------------+ +----------------|-----+ +# | | +# +----|----------------------------------------------------------------|-----+ +# | SW | | | +# | +-|----------------------------------------------------------------|-+ | +# | | + $swp1 BR $swp2 + | | +# | +--------------------------------------------------------------------+ | +# +---------------------------------------------------------------------------+ + +ALL_TESTS=" + ping_ipv4 + ping_ipv6 + test_ip4_src + test_ip4_dst + test_ip6_src + test_ip6_dst +" + +NUM_NETIFS=4 +source lib.sh +source tc_common.sh + +h1_create() +{ + simple_if_init $h1 192.0.2.1/28 2001:db8:1::1/64 +} + +h1_destroy() +{ + simple_if_fini $h1 192.0.2.1/28 2001:db8:1::1/64 +} + +h2_create() +{ + simple_if_init $h2 192.0.2.2/28 2001:db8:1::2/64 + tc qdisc add dev $h2 clsact +} + +h2_destroy() +{ + tc qdisc del dev $h2 clsact + simple_if_fini $h2 192.0.2.2/28 2001:db8:1::2/64 +} + +switch_create() +{ + ip link add name br1 up type bridge vlan_filtering 1 + ip link set dev $swp1 master br1 + ip link set dev $swp1 up + ip link set dev $swp2 master br1 + ip link set dev $swp2 up + + tc qdisc add dev $swp1 clsact + tc qdisc add dev $swp2 clsact +} + +switch_destroy() +{ + tc qdisc del dev $swp2 clsact + tc qdisc del dev $swp1 clsact + + ip link set dev $swp2 down + ip link set dev $swp2 nomaster + ip link set dev $swp1 down + ip link set dev $swp1 nomaster + ip link del dev br1 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + h2mac=$(mac_get $h2) + + vrf_prepare + h1_create + h2_create + switch_create +} + +cleanup() +{ + pre_cleanup + + switch_destroy + h2_destroy + h1_destroy + vrf_cleanup +} + +ping_ipv4() +{ + ping_test $h1 192.0.2.2 +} + +ping_ipv6() +{ + ping6_test $h1 2001:db8:1::2 +} + +do_test_pedit_ip() +{ + local pedit_locus=$1; shift + local pedit_action=$1; shift + local match_prot=$1; shift + local match_flower=$1; shift + local mz_flags=$1; shift + + tc filter add $pedit_locus handle 101 pref 1 \ + flower action pedit ex munge $pedit_action + tc filter add dev $h2 ingress handle 101 pref 1 prot $match_prot \ + flower skip_hw $match_flower action pass + + RET=0 + + $MZ $mz_flags $h1 -c 10 -d 20msec -p 100 -a own -b $h2mac -q -t ip + + local pkts + pkts=$(busywait "$TC_HIT_TIMEOUT" until_counter_is ">= 10" \ + tc_rule_handle_stats_get "dev $h2 ingress" 101) + check_err $? "Expected to get 10 packets, but got $pkts." + + pkts=$(tc_rule_handle_stats_get "$pedit_locus" 101) + ((pkts >= 10)) + check_err $? "Expected to get 10 packets on pedit rule, but got $pkts." + + log_test "$pedit_locus pedit $pedit_action" + + tc filter del dev $h2 ingress pref 1 + tc filter del $pedit_locus pref 1 +} + +do_test_pedit_ip6() +{ + local locus=$1; shift + local pedit_addr=$1; shift + local flower_addr=$1; shift + + do_test_pedit_ip "$locus" "$pedit_addr set 2001:db8:2::1" ipv6 \ + "$flower_addr 2001:db8:2::1" \ + "-6 -A 2001:db8:1::1 -B 2001:db8:1::2" +} + +do_test_pedit_ip4() +{ + local locus=$1; shift + local pedit_addr=$1; shift + local flower_addr=$1; shift + + do_test_pedit_ip "$locus" "$pedit_addr set 198.51.100.1" ip \ + "$flower_addr 198.51.100.1" \ + "-A 192.0.2.1 -B 192.0.2.2" +} + +test_ip4_src() +{ + do_test_pedit_ip4 "dev $swp1 ingress" "ip src" src_ip + do_test_pedit_ip4 "dev $swp2 egress" "ip src" src_ip +} + +test_ip4_dst() +{ + do_test_pedit_ip4 "dev $swp1 ingress" "ip dst" dst_ip + do_test_pedit_ip4 "dev $swp2 egress" "ip dst" dst_ip +} + +test_ip6_src() +{ + do_test_pedit_ip6 "dev $swp1 ingress" "ip6 src" src_ip + do_test_pedit_ip6 "dev $swp2 egress" "ip6 src" src_ip +} + +test_ip6_dst() +{ + do_test_pedit_ip6 "dev $swp1 ingress" "ip6 dst" dst_ip + do_test_pedit_ip6 "dev $swp2 egress" "ip6 dst" dst_ip +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +tests_run + +exit $EXIT_STATUS diff --git a/tools/testing/selftests/net/forwarding/tc_police.sh b/tools/testing/selftests/net/forwarding/tc_police.sh index 4f9f17cb45d6..0a51eef21b9e 100755 --- a/tools/testing/selftests/net/forwarding/tc_police.sh +++ b/tools/testing/selftests/net/forwarding/tc_police.sh @@ -37,6 +37,8 @@ ALL_TESTS=" police_tx_mirror_test police_pps_rx_test police_pps_tx_test + police_mtu_rx_test + police_mtu_tx_test " NUM_NETIFS=6 source tc_common.sh @@ -346,6 +348,56 @@ police_pps_tx_test() tc filter del dev $rp2 egress protocol ip pref 1 handle 101 flower } +police_mtu_common_test() { + RET=0 + + local test_name=$1; shift + local dev=$1; shift + local direction=$1; shift + + tc filter add dev $dev $direction protocol ip pref 1 handle 101 flower \ + dst_ip 198.51.100.1 ip_proto udp dst_port 54321 \ + action police mtu 1042 conform-exceed drop/ok + + # to count "conform" packets + tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ + dst_ip 198.51.100.1 ip_proto udp dst_port 54321 \ + action drop + + mausezahn $h1 -a own -b $(mac_get $rp1) -A 192.0.2.1 -B 198.51.100.1 \ + -t udp sp=12345,dp=54321 -p 1001 -c 10 -q + + mausezahn $h1 -a own -b $(mac_get $rp1) -A 192.0.2.1 -B 198.51.100.1 \ + -t udp sp=12345,dp=54321 -p 1000 -c 3 -q + + tc_check_packets "dev $dev $direction" 101 13 + check_err $? "wrong packet counter" + + # "exceed" packets + local overlimits_t0=$(tc_rule_stats_get ${dev} 1 ${direction} .overlimits) + test ${overlimits_t0} = 10 + check_err $? "wrong overlimits, expected 10 got ${overlimits_t0}" + + # "conform" packets + tc_check_packets "dev $h2 ingress" 101 3 + check_err $? "forwarding error" + + tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower + tc filter del dev $dev $direction protocol ip pref 1 handle 101 flower + + log_test "$test_name" +} + +police_mtu_rx_test() +{ + police_mtu_common_test "police mtu (rx)" $rp1 ingress +} + +police_mtu_tx_test() +{ + police_mtu_common_test "police mtu (tx)" $rp2 egress +} + setup_prepare() { h1=${NETIFS[p1]} diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh index f0f4ab96b8f3..621af6895f4d 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh @@ -432,6 +432,8 @@ do_transfer() local stat_ackrx_last_l=$(get_mib_counter "${listener_ns}" "MPTcpExtMPCapableACKRX") local stat_cookietx_last=$(get_mib_counter "${listener_ns}" "TcpExtSyncookiesSent") local stat_cookierx_last=$(get_mib_counter "${listener_ns}" "TcpExtSyncookiesRecv") + local stat_csum_err_s=$(get_mib_counter "${listener_ns}" "MPTcpExtDataCsumErr") + local stat_csum_err_c=$(get_mib_counter "${connector_ns}" "MPTcpExtDataCsumErr") timeout ${timeout_test} \ ip netns exec ${listener_ns} \ @@ -524,6 +526,23 @@ do_transfer() fi fi + if $checksum; then + local csum_err_s=$(get_mib_counter "${listener_ns}" "MPTcpExtDataCsumErr") + local csum_err_c=$(get_mib_counter "${connector_ns}" "MPTcpExtDataCsumErr") + + local csum_err_s_nr=$((csum_err_s - stat_csum_err_s)) + if [ $csum_err_s_nr -gt 0 ]; then + printf "[ FAIL ]\nserver got $csum_err_s_nr data checksum error[s]" + rets=1 + fi + + local csum_err_c_nr=$((csum_err_c - stat_csum_err_c)) + if [ $csum_err_c_nr -gt 0 ]; then + printf "[ FAIL ]\nclient got $csum_err_c_nr data checksum error[s]" + retc=1 + fi + fi + if [ $retc -eq 0 ] && [ $rets -eq 0 ]; then printf "[ OK ]" fi diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 0c8a2a20b96c..7314257d248a 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -1,6 +1,11 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +# Double quotes to prevent globbing and word splitting is recommended in new +# code but we accept it, especially because there were too many before having +# address all other issues detected by shellcheck. +#shellcheck disable=SC2086 + ret=0 sin="" sinfail="" @@ -9,15 +14,28 @@ cin="" cinfail="" cinsent="" cout="" +capout="" +ns1="" +ns2="" ksft_skip=4 timeout_poll=30 timeout_test=$((timeout_poll * 2 + 1)) -mptcp_connect="" capture=0 checksum=0 -do_all_tests=1 +ip_mptcp=0 +check_invert=0 +validate_checksum=0 +init=0 +declare -A all_tests +declare -a only_tests_ids +declare -a only_tests_names +declare -A failed_tests TEST_COUNT=0 +TEST_NAME="" +nr_blank=40 + +export FAILING_LINKS="" # generated using "nfbpf_compile '(ip && (ip[54] & 0xf0) == 0x30) || # (ip6 && (ip6[74] & 0xf0) == 0x30)'" @@ -37,16 +55,18 @@ CBPF_MPTCP_SUBOPTION_ADD_ADDR="14, 6 0 0 65535, 6 0 0 0" -init() +init_partial() { capout=$(mktemp) - rndh=$(printf %x $sec)-$(mktemp -u XXXXXX) + local rndh + rndh=$(mktemp -u XXXXXX) ns1="ns1-$rndh" ns2="ns2-$rndh" - for netns in "$ns1" "$ns2";do + local netns + for netns in "$ns1" "$ns2"; do ip netns add $netns || exit $ksft_skip ip -net $netns link set lo up ip netns exec $netns sysctl -q net.mptcp.enabled=1 @@ -57,13 +77,18 @@ init() fi done - # ns1 ns2 + check_invert=0 + validate_checksum=$checksum + FAILING_LINKS="" + + # ns1 ns2 # ns1eth1 ns2eth1 # ns1eth2 ns2eth2 # ns1eth3 ns2eth3 # ns1eth4 ns2eth4 - for i in `seq 1 4`; do + local i + for i in $(seq 1 4); do ip link add ns1eth$i netns "$ns1" type veth peer name ns2eth$i netns "$ns2" ip -net "$ns1" addr add 10.0.$i.1/24 dev ns1eth$i ip -net "$ns1" addr add dead:beef:$i::1/64 dev ns1eth$i nodad @@ -81,7 +106,8 @@ init() init_shapers() { - for i in `seq 1 4`; do + local i + for i in $(seq 1 4); do tc -n $ns1 qdisc add dev ns1eth$i root netem rate 20mbit delay 1 tc -n $ns2 qdisc add dev ns2eth$i root netem rate 20mbit delay 1 done @@ -91,12 +117,48 @@ cleanup_partial() { rm -f "$capout" + local netns for netns in "$ns1" "$ns2"; do ip netns del $netns rm -f /tmp/$netns.{nstat,out} done } +check_tools() +{ + if ! ip -Version &> /dev/null; then + echo "SKIP: Could not run test without ip tool" + exit $ksft_skip + fi + + if ! iptables -V &> /dev/null; then + echo "SKIP: Could not run all tests without iptables tool" + exit $ksft_skip + fi + + if ! ip6tables -V &> /dev/null; then + echo "SKIP: Could not run all tests without ip6tables tool" + exit $ksft_skip + fi +} + +init() { + init=1 + + check_tools + + sin=$(mktemp) + sout=$(mktemp) + cin=$(mktemp) + cinsent=$(mktemp) + cout=$(mktemp) + + trap cleanup EXIT + + make_file "$cin" "client" 1 + make_file "$sin" "server" 1 +} + cleanup() { rm -f "$cin" "$cout" "$sinfail" @@ -104,33 +166,73 @@ cleanup() cleanup_partial } -reset() +skip_test() { - cleanup_partial - init + if [ "${#only_tests_ids[@]}" -eq 0 ] && [ "${#only_tests_names[@]}" -eq 0 ]; then + return 1 + fi + + local i + for i in "${only_tests_ids[@]}"; do + if [ "${TEST_COUNT}" -eq "${i}" ]; then + return 1 + fi + done + for i in "${only_tests_names[@]}"; do + if [ "${TEST_NAME}" = "${i}" ]; then + return 1 + fi + done + + return 0 } +# $1: test name +reset() +{ + TEST_NAME="${1}" + + TEST_COUNT=$((TEST_COUNT+1)) + + if skip_test; then + return 1 + fi + + if [ "${init}" != "1" ]; then + init + else + cleanup_partial + fi + + init_partial + + return 0 +} + +# $1: test name reset_with_cookies() { - reset + reset "${1}" || return 1 - for netns in "$ns1" "$ns2";do + local netns + for netns in "$ns1" "$ns2"; do ip netns exec $netns sysctl -q net.ipv4.tcp_syncookies=2 done } +# $1: test name reset_with_add_addr_timeout() { - local ip="${1:-4}" + local ip="${2:-4}" local tables + reset "${1}" || return 1 + tables="iptables" if [ $ip -eq 6 ]; then tables="ip6tables" fi - reset - ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=1 ip netns exec $ns2 $tables -A OUTPUT -p tcp \ -m tcp --tcp-option 30 \ @@ -139,45 +241,45 @@ reset_with_add_addr_timeout() -j DROP } +# $1: test name reset_with_checksum() { local ns1_enable=$1 local ns2_enable=$2 - reset + reset "checksum test ${1} ${2}" || return 1 ip netns exec $ns1 sysctl -q net.mptcp.checksum_enabled=$ns1_enable ip netns exec $ns2 sysctl -q net.mptcp.checksum_enabled=$ns2_enable + + validate_checksum=1 } reset_with_allow_join_id0() { - local ns1_enable=$1 - local ns2_enable=$2 + local ns1_enable=$2 + local ns2_enable=$3 - reset + reset "${1}" || return 1 ip netns exec $ns1 sysctl -q net.mptcp.allow_join_initial_addr_port=$ns1_enable ip netns exec $ns2 sysctl -q net.mptcp.allow_join_initial_addr_port=$ns2_enable } -ip -Version > /dev/null 2>&1 -if [ $? -ne 0 ];then - echo "SKIP: Could not run test without ip tool" - exit $ksft_skip -fi +fail_test() +{ + ret=1 + failed_tests[${TEST_COUNT}]="${TEST_NAME}" +} -iptables -V > /dev/null 2>&1 -if [ $? -ne 0 ];then - echo "SKIP: Could not run all tests without iptables tool" - exit $ksft_skip -fi - -ip6tables -V > /dev/null 2>&1 -if [ $? -ne 0 ];then - echo "SKIP: Could not run all tests without ip6tables tool" - exit $ksft_skip -fi +get_failed_tests_ids() +{ + # sorted + local i + for i in "${!failed_tests[@]}"; do + echo "${i}" + done | sort -n +} print_file_err() { @@ -188,47 +290,53 @@ print_file_err() check_transfer() { - in=$1 - out=$2 - what=$3 + local in=$1 + local out=$2 + local what=$3 + local i a b - cmp "$in" "$out" > /dev/null 2>&1 - if [ $? -ne 0 ] ;then - echo "[ FAIL ] $what does not match (in, out):" - print_file_err "$in" - print_file_err "$out" - ret=1 + local line + cmp -l "$in" "$out" | while read -r i a b; do + local sum=$((0${a} + 0${b})) + if [ $check_invert -eq 0 ] || [ $sum -ne $((0xff)) ]; then + echo "[ FAIL ] $what does not match (in, out):" + print_file_err "$in" + print_file_err "$out" + fail_test - return 1 - fi + return 1 + else + echo "$what has inverted byte at ${i}" + fi + done return 0 } do_ping() { - listener_ns="$1" - connector_ns="$2" - connect_addr="$3" + local listener_ns="$1" + local connector_ns="$2" + local connect_addr="$3" - ip netns exec ${connector_ns} ping -q -c 1 $connect_addr >/dev/null - if [ $? -ne 0 ] ; then + if ! ip netns exec ${connector_ns} ping -q -c 1 $connect_addr >/dev/null; then echo "$listener_ns -> $connect_addr connectivity [ FAIL ]" 1>&2 - ret=1 + fail_test fi } link_failure() { - ns="$1" + local ns="$1" if [ -z "$FAILING_LINKS" ]; then l=$((RANDOM%4)) FAILING_LINKS=$((l+1)) fi + local l for l in $FAILING_LINKS; do - veth="ns1eth$l" + local veth="ns1eth$l" ip -net "$ns" link set "$veth" down done } @@ -245,9 +353,10 @@ wait_local_port_listen() local listener_ns="${1}" local port="${2}" - local port_hex i - + local port_hex port_hex="$(printf "%04X" "${port}")" + + local i for i in $(seq 10); do ip netns exec "${listener_ns}" cat /proc/net/tcp* | \ awk "BEGIN {rc=1} {if (\$2 ~ /:${port_hex}\$/ && \$4 ~ /0A/) {rc=0; exit}} END {exit rc}" && @@ -258,7 +367,7 @@ wait_local_port_listen() rm_addr_count() { - ns=${1} + local ns=${1} ip netns exec ${ns} nstat -as | grep MPTcpExtRmAddr | awk '{print $2}' } @@ -269,8 +378,8 @@ wait_rm_addr() local ns="${1}" local old_cnt="${2}" local cnt - local i + local i for i in $(seq 10); do cnt=$(rm_addr_count ${ns}) [ "$cnt" = "${old_cnt}" ] || break @@ -278,27 +387,227 @@ wait_rm_addr() done } +wait_mpj() +{ + local ns="${1}" + local cnt old_cnt + + old_cnt=$(ip netns exec ${ns} nstat -as | grep MPJoinAckRx | awk '{print $2}') + + local i + for i in $(seq 10); do + cnt=$(ip netns exec ${ns} nstat -as | grep MPJoinAckRx | awk '{print $2}') + [ "$cnt" = "${old_cnt}" ] || break + sleep 0.1 + done +} + +pm_nl_set_limits() +{ + local ns=$1 + local addrs=$2 + local subflows=$3 + + if [ $ip_mptcp -eq 1 ]; then + ip -n $ns mptcp limits set add_addr_accepted $addrs subflows $subflows + else + ip netns exec $ns ./pm_nl_ctl limits $addrs $subflows + fi +} + +pm_nl_add_endpoint() +{ + local ns=$1 + local addr=$2 + local flags _flags + local port _port + local dev _dev + local id _id + local nr=2 + + local p + for p in "${@}" + do + if [ $p = "flags" ]; then + eval _flags=\$"$nr" + [ -n "$_flags" ]; flags="flags $_flags" + fi + if [ $p = "dev" ]; then + eval _dev=\$"$nr" + [ -n "$_dev" ]; dev="dev $_dev" + fi + if [ $p = "id" ]; then + eval _id=\$"$nr" + [ -n "$_id" ]; id="id $_id" + fi + if [ $p = "port" ]; then + eval _port=\$"$nr" + [ -n "$_port" ]; port="port $_port" + fi + + nr=$((nr + 1)) + done + + if [ $ip_mptcp -eq 1 ]; then + ip -n $ns mptcp endpoint add $addr ${_flags//","/" "} $dev $id $port + else + ip netns exec $ns ./pm_nl_ctl add $addr $flags $dev $id $port + fi +} + +pm_nl_del_endpoint() +{ + local ns=$1 + local id=$2 + local addr=$3 + + if [ $ip_mptcp -eq 1 ]; then + ip -n $ns mptcp endpoint delete id $id $addr + else + ip netns exec $ns ./pm_nl_ctl del $id $addr + fi +} + +pm_nl_flush_endpoint() +{ + local ns=$1 + + if [ $ip_mptcp -eq 1 ]; then + ip -n $ns mptcp endpoint flush + else + ip netns exec $ns ./pm_nl_ctl flush + fi +} + +pm_nl_show_endpoints() +{ + local ns=$1 + + if [ $ip_mptcp -eq 1 ]; then + ip -n $ns mptcp endpoint show + else + ip netns exec $ns ./pm_nl_ctl dump + fi +} + +pm_nl_change_endpoint() +{ + local ns=$1 + local id=$2 + local flags=$3 + + if [ $ip_mptcp -eq 1 ]; then + ip -n $ns mptcp endpoint change id $id ${flags//","/" "} + else + ip netns exec $ns ./pm_nl_ctl set id $id flags $flags + fi +} + +pm_nl_check_endpoint() +{ + local line expected_line + local need_title=$1 + local msg="$2" + local ns=$3 + local addr=$4 + local _flags="" + local flags + local _port + local port + local dev + local _id + local id + + if [ "${need_title}" = 1 ]; then + printf "%03u %-36s %s" "${TEST_COUNT}" "${TEST_NAME}" "${msg}" + else + printf "%-${nr_blank}s %s" " " "${msg}" + fi + + shift 4 + while [ -n "$1" ]; do + if [ $1 = "flags" ]; then + _flags=$2 + [ -n "$_flags" ]; flags="flags $_flags" + shift + elif [ $1 = "dev" ]; then + [ -n "$2" ]; dev="dev $1" + shift + elif [ $1 = "id" ]; then + _id=$2 + [ -n "$_id" ]; id="id $_id" + shift + elif [ $1 = "port" ]; then + _port=$2 + [ -n "$_port" ]; port=" port $_port" + shift + fi + + shift + done + + if [ -z "$id" ]; then + echo "[skip] bad test - missing endpoint id" + return + fi + + if [ $ip_mptcp -eq 1 ]; then + line=$(ip -n $ns mptcp endpoint show $id) + # the dump order is: address id flags port dev + expected_line="$addr" + [ -n "$addr" ] && expected_line="$expected_line $addr" + expected_line="$expected_line $id" + [ -n "$_flags" ] && expected_line="$expected_line ${_flags//","/" "}" + [ -n "$dev" ] && expected_line="$expected_line $dev" + [ -n "$port" ] && expected_line="$expected_line $port" + else + line=$(ip netns exec $ns ./pm_nl_ctl get $_id) + # the dump order is: id flags dev address port + expected_line="$id" + [ -n "$flags" ] && expected_line="$expected_line $flags" + [ -n "$dev" ] && expected_line="$expected_line $dev" + [ -n "$addr" ] && expected_line="$expected_line $addr" + [ -n "$_port" ] && expected_line="$expected_line $_port" + fi + if [ "$line" = "$expected_line" ]; then + echo "[ ok ]" + else + echo "[fail] expected '$expected_line' found '$line'" + fail_test + fi +} + +filter_tcp_from() +{ + local ns="${1}" + local src="${2}" + local target="${3}" + + ip netns exec "${ns}" iptables -A INPUT -s "${src}" -p tcp -j "${target}" +} + do_transfer() { - listener_ns="$1" - connector_ns="$2" - cl_proto="$3" - srv_proto="$4" - connect_addr="$5" - test_link_fail="$6" - addr_nr_ns1="$7" - addr_nr_ns2="$8" - speed="$9" - bkup="${10}" + local listener_ns="$1" + local connector_ns="$2" + local cl_proto="$3" + local srv_proto="$4" + local connect_addr="$5" + local test_link_fail="$6" + local addr_nr_ns1="$7" + local addr_nr_ns2="$8" + local speed="$9" + local sflags="${10}" - port=$((10000+$TEST_COUNT)) - TEST_COUNT=$((TEST_COUNT+1)) + local port=$((10000 + TEST_COUNT - 1)) + local cappid :> "$cout" :> "$sout" :> "$capout" if [ $capture -eq 1 ]; then + local capuser if [ -z $SUDO_USER ] ; then capuser="" else @@ -319,12 +628,19 @@ do_transfer() NSTAT_HISTORY=/tmp/${connector_ns}.nstat ip netns exec ${connector_ns} \ nstat -n + local extra_args if [ $speed = "fast" ]; then - mptcp_connect="./mptcp_connect -j" + extra_args="-j" elif [ $speed = "slow" ]; then - mptcp_connect="./mptcp_connect -r 50" - elif [ $speed = "least" ]; then - mptcp_connect="./mptcp_connect -r 10" + extra_args="-r 50" + elif [[ $speed = "speed_"* ]]; then + extra_args="-r ${speed:6}" + fi + + if [[ "${addr_nr_ns2}" = "fastclose_"* ]]; then + # disconnect + extra_args="$extra_args -I ${addr_nr_ns2:10}" + addr_nr_ns2=0 fi local local_addr @@ -334,43 +650,51 @@ do_transfer() local_addr="0.0.0.0" fi - if [ "$test_link_fail" -eq 2 ];then + if [ "$test_link_fail" -gt 1 ];then timeout ${timeout_test} \ ip netns exec ${listener_ns} \ - $mptcp_connect -t ${timeout_poll} -l -p $port -s ${srv_proto} \ - ${local_addr} < "$sinfail" > "$sout" & + ./mptcp_connect -t ${timeout_poll} -l -p $port -s ${srv_proto} \ + $extra_args ${local_addr} < "$sinfail" > "$sout" & else timeout ${timeout_test} \ ip netns exec ${listener_ns} \ - $mptcp_connect -t ${timeout_poll} -l -p $port -s ${srv_proto} \ - ${local_addr} < "$sin" > "$sout" & + ./mptcp_connect -t ${timeout_poll} -l -p $port -s ${srv_proto} \ + $extra_args ${local_addr} < "$sin" > "$sout" & fi - spid=$! + local spid=$! wait_local_port_listen "${listener_ns}" "${port}" if [ "$test_link_fail" -eq 0 ];then timeout ${timeout_test} \ ip netns exec ${connector_ns} \ - $mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \ - $connect_addr < "$cin" > "$cout" & - else + ./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \ + $extra_args $connect_addr < "$cin" > "$cout" & + elif [ "$test_link_fail" -eq 1 ] || [ "$test_link_fail" -eq 2 ];then ( cat "$cinfail" ; sleep 2; link_failure $listener_ns ; cat "$cinfail" ) | \ tee "$cinsent" | \ timeout ${timeout_test} \ ip netns exec ${connector_ns} \ - $mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \ - $connect_addr > "$cout" & + ./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \ + $extra_args $connect_addr > "$cout" & + else + tee "$cinsent" < "$cinfail" | \ + timeout ${timeout_test} \ + ip netns exec ${connector_ns} \ + ./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \ + $extra_args $connect_addr > "$cout" & fi - cpid=$! + local cpid=$! # let the mptcp subflow be established in background before # do endpoint manipulation - [ $addr_nr_ns1 = "0" -a $addr_nr_ns2 = "0" ] || sleep 1 + if [ $addr_nr_ns1 != "0" ] || [ $addr_nr_ns2 != "0" ]; then + sleep 1 + fi if [ $addr_nr_ns1 -gt 0 ]; then - let add_nr_ns1=addr_nr_ns1 - counter=2 + local counter=2 + local add_nr_ns1=${addr_nr_ns1} while [ $add_nr_ns1 -gt 0 ]; do local addr if is_v6 "${connect_addr}"; then @@ -378,35 +702,43 @@ do_transfer() else addr="10.0.$counter.1" fi - ip netns exec $ns1 ./pm_nl_ctl add $addr flags signal - let counter+=1 - let add_nr_ns1-=1 + pm_nl_add_endpoint $ns1 $addr flags signal + counter=$((counter + 1)) + add_nr_ns1=$((add_nr_ns1 - 1)) done elif [ $addr_nr_ns1 -lt 0 ]; then - let rm_nr_ns1=-addr_nr_ns1 + local rm_nr_ns1=$((-addr_nr_ns1)) if [ $rm_nr_ns1 -lt 8 ]; then - counter=1 - pos=1 - dump=(`ip netns exec ${listener_ns} ./pm_nl_ctl dump`) - if [ ${#dump[@]} -gt 0 ]; then - while [ $counter -le $rm_nr_ns1 ] - do - id=${dump[$pos]} - rm_addr=$(rm_addr_count ${connector_ns}) - ip netns exec ${listener_ns} ./pm_nl_ctl del $id - wait_rm_addr ${connector_ns} ${rm_addr} - let counter+=1 - let pos+=5 + local counter=0 + local line + pm_nl_show_endpoints ${listener_ns} | while read -r line; do + # shellcheck disable=SC2206 # we do want to split per word + local arr=($line) + local nr=0 + + local i + for i in "${arr[@]}"; do + if [ $i = "id" ]; then + if [ $counter -eq $rm_nr_ns1 ]; then + break + fi + id=${arr[$nr+1]} + rm_addr=$(rm_addr_count ${connector_ns}) + pm_nl_del_endpoint ${listener_ns} $id + wait_rm_addr ${connector_ns} ${rm_addr} + counter=$((counter + 1)) + fi + nr=$((nr + 1)) done - fi + done elif [ $rm_nr_ns1 -eq 8 ]; then - ip netns exec ${listener_ns} ./pm_nl_ctl flush + pm_nl_flush_endpoint ${listener_ns} elif [ $rm_nr_ns1 -eq 9 ]; then - ip netns exec ${listener_ns} ./pm_nl_ctl del 0 ${connect_addr} + pm_nl_del_endpoint ${listener_ns} 0 ${connect_addr} fi fi - flags="subflow" + local flags="subflow" if [[ "${addr_nr_ns2}" = "fullmesh_"* ]]; then flags="${flags},fullmesh" addr_nr_ns2=${addr_nr_ns2:9} @@ -414,11 +746,11 @@ do_transfer() # if newly added endpoints must be deleted, give the background msk # some time to created them - [ $addr_nr_ns1 -gt 0 -a $addr_nr_ns2 -lt 0 ] && sleep 1 + [ $addr_nr_ns1 -gt 0 ] && [ $addr_nr_ns2 -lt 0 ] && sleep 1 if [ $addr_nr_ns2 -gt 0 ]; then - let add_nr_ns2=addr_nr_ns2 - counter=3 + local add_nr_ns2=${addr_nr_ns2} + local counter=3 while [ $add_nr_ns2 -gt 0 ]; do local addr if is_v6 "${connect_addr}"; then @@ -426,30 +758,40 @@ do_transfer() else addr="10.0.$counter.2" fi - ip netns exec $ns2 ./pm_nl_ctl add $addr flags $flags - let counter+=1 - let add_nr_ns2-=1 + pm_nl_add_endpoint $ns2 $addr flags $flags + counter=$((counter + 1)) + add_nr_ns2=$((add_nr_ns2 - 1)) done elif [ $addr_nr_ns2 -lt 0 ]; then - let rm_nr_ns2=-addr_nr_ns2 + local rm_nr_ns2=$((-addr_nr_ns2)) if [ $rm_nr_ns2 -lt 8 ]; then - counter=1 - pos=1 - dump=(`ip netns exec ${connector_ns} ./pm_nl_ctl dump`) - if [ ${#dump[@]} -gt 0 ]; then - while [ $counter -le $rm_nr_ns2 ] - do - # rm_addr are serialized, allow the previous one to complete - id=${dump[$pos]} - rm_addr=$(rm_addr_count ${listener_ns}) - ip netns exec ${connector_ns} ./pm_nl_ctl del $id - wait_rm_addr ${listener_ns} ${rm_addr} - let counter+=1 - let pos+=5 + local counter=0 + local line + pm_nl_show_endpoints ${connector_ns} | while read -r line; do + # shellcheck disable=SC2206 # we do want to split per word + local arr=($line) + local nr=0 + + local i + for i in "${arr[@]}"; do + if [ $i = "id" ]; then + if [ $counter -eq $rm_nr_ns2 ]; then + break + fi + local id rm_addr + # rm_addr are serialized, allow the previous one to + # complete + id=${arr[$nr+1]} + rm_addr=$(rm_addr_count ${listener_ns}) + pm_nl_del_endpoint ${connector_ns} $id + wait_rm_addr ${listener_ns} ${rm_addr} + counter=$((counter + 1)) + fi + nr=$((nr + 1)) done - fi + done elif [ $rm_nr_ns2 -eq 8 ]; then - ip netns exec ${connector_ns} ./pm_nl_ctl flush + pm_nl_flush_endpoint ${connector_ns} elif [ $rm_nr_ns2 -eq 9 ]; then local addr if is_v6 "${connect_addr}"; then @@ -457,26 +799,38 @@ do_transfer() else addr="10.0.1.2" fi - ip netns exec ${connector_ns} ./pm_nl_ctl del 0 $addr + pm_nl_del_endpoint ${connector_ns} 0 $addr fi fi - if [ ! -z $bkup ]; then + if [ -n "${sflags}" ]; then sleep 1 + + local netns for netns in "$ns1" "$ns2"; do - dump=(`ip netns exec $netns ./pm_nl_ctl dump`) - if [ ${#dump[@]} -gt 0 ]; then - addr=${dump[${#dump[@]} - 1]} - backup="ip netns exec $netns ./pm_nl_ctl set $addr flags $bkup" - $backup - fi + local line + pm_nl_show_endpoints $netns | while read -r line; do + # shellcheck disable=SC2206 # we do want to split per word + local arr=($line) + local nr=0 + local id + + local i + for i in "${arr[@]}"; do + if [ $i = "id" ]; then + id=${arr[$nr+1]} + fi + nr=$((nr + 1)) + done + pm_nl_change_endpoint $netns $id $sflags + done done fi wait $cpid - retc=$? + local retc=$? wait $spid - rets=$? + local rets=$? if [ $capture -eq 1 ]; then sleep 1 @@ -498,11 +852,11 @@ do_transfer() cat /tmp/${connector_ns}.out cat "$capout" - ret=1 + fail_test return 1 fi - if [ "$test_link_fail" -eq 2 ];then + if [ "$test_link_fail" -gt 1 ];then check_transfer $sinfail $cout "file received by client" else check_transfer $sin $cout "file received by client" @@ -526,9 +880,9 @@ do_transfer() make_file() { - name=$1 - who=$2 - size=$3 + local name=$1 + local who=$2 + local size=$3 dd if=/dev/urandom of="$name" bs=1024 count=$size 2> /dev/null echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name" @@ -538,33 +892,49 @@ make_file() run_tests() { - listener_ns="$1" - connector_ns="$2" - connect_addr="$3" - test_linkfail="${4:-0}" - addr_nr_ns1="${5:-0}" - addr_nr_ns2="${6:-0}" - speed="${7:-fast}" - bkup="${8:-""}" - lret=0 - oldin="" + local listener_ns="$1" + local connector_ns="$2" + local connect_addr="$3" + local test_linkfail="${4:-0}" + local addr_nr_ns1="${5:-0}" + local addr_nr_ns2="${6:-0}" + local speed="${7:-fast}" + local sflags="${8:-""}" + local size + + # The values above 2 are reused to make test files + # with the given sizes (KB) + if [ "$test_linkfail" -gt 2 ]; then + size=$test_linkfail + + if [ -z "$cinfail" ]; then + cinfail=$(mktemp) + fi + make_file "$cinfail" "client" $size # create the input file for the failure test when # the first failure test run - if [ "$test_linkfail" -ne 0 -a -z "$cinfail" ]; then + elif [ "$test_linkfail" -ne 0 ] && [ -z "$cinfail" ]; then # the client file must be considerably larger # of the maximum expected cwin value, or the # link utilization will be not predicable size=$((RANDOM%2)) size=$((size+1)) size=$((size*8192)) - size=$((size + ( $RANDOM % 8192) )) + size=$((size + ( RANDOM % 8192) )) cinfail=$(mktemp) make_file "$cinfail" "client" $size fi - if [ "$test_linkfail" -eq 2 -a -z "$sinfail" ]; then + if [ "$test_linkfail" -gt 2 ]; then + size=$test_linkfail + + if [ -z "$sinfail" ]; then + sinfail=$(mktemp) + fi + make_file "$sinfail" "server" $size + elif [ "$test_linkfail" -eq 2 ] && [ -z "$sinfail" ]; then size=$((RANDOM%16)) size=$((size+1)) size=$((size*2048)) @@ -574,8 +944,7 @@ run_tests() fi do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP ${connect_addr} \ - ${test_linkfail} ${addr_nr_ns1} ${addr_nr_ns2} ${speed} ${bkup} - lret=$? + ${test_linkfail} ${addr_nr_ns1} ${addr_nr_ns2} ${speed} ${sflags} } dump_stats() @@ -588,31 +957,40 @@ dump_stats() chk_csum_nr() { - local msg=${1:-""} + local csum_ns1=${1:-0} + local csum_ns2=${2:-0} local count local dump_stats + local allow_multi_errors_ns1=0 + local allow_multi_errors_ns2=0 - if [ ! -z "$msg" ]; then - printf "%02u" "$TEST_COUNT" - else - echo -n " " + if [[ "${csum_ns1}" = "+"* ]]; then + allow_multi_errors_ns1=1 + csum_ns1=${csum_ns1:1} fi - printf " %-36s %s" "$msg" "sum" - count=`ip netns exec $ns1 nstat -as | grep MPTcpExtDataCsumErr | awk '{print $2}'` + if [[ "${csum_ns2}" = "+"* ]]; then + allow_multi_errors_ns2=1 + csum_ns2=${csum_ns2:1} + fi + + printf "%-${nr_blank}s %s" " " "sum" + count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtDataCsumErr | awk '{print $2}') [ -z "$count" ] && count=0 - if [ "$count" != 0 ]; then - echo "[fail] got $count data checksum error[s] expected 0" - ret=1 + if { [ "$count" != $csum_ns1 ] && [ $allow_multi_errors_ns1 -eq 0 ]; } || + { [ "$count" -lt $csum_ns1 ] && [ $allow_multi_errors_ns1 -eq 1 ]; }; then + echo "[fail] got $count data checksum error[s] expected $csum_ns1" + fail_test dump_stats=1 else echo -n "[ ok ]" fi echo -n " - csum " - count=`ip netns exec $ns2 nstat -as | grep MPTcpExtDataCsumErr | awk '{print $2}'` + count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtDataCsumErr | awk '{print $2}') [ -z "$count" ] && count=0 - if [ "$count" != 0 ]; then - echo "[fail] got $count data checksum error[s] expected 0" - ret=1 + if { [ "$count" != $csum_ns2 ] && [ $allow_multi_errors_ns2 -eq 0 ]; } || + { [ "$count" -lt $csum_ns2 ] && [ $allow_multi_errors_ns2 -eq 1 ]; }; then + echo "[fail] got $count data checksum error[s] expected $csum_ns2" + fail_test dump_stats=1 else echo "[ ok ]" @@ -622,28 +1000,28 @@ chk_csum_nr() chk_fail_nr() { - local mp_fail_nr_tx=$1 - local mp_fail_nr_rx=$2 + local fail_tx=$1 + local fail_rx=$2 local count local dump_stats - printf "%-39s %s" " " "ftx" - count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMPFailTx | awk '{print $2}'` + printf "%-${nr_blank}s %s" " " "ftx" + count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPFailTx | awk '{print $2}') [ -z "$count" ] && count=0 - if [ "$count" != "$mp_fail_nr_tx" ]; then - echo "[fail] got $count MP_FAIL[s] TX expected $mp_fail_nr_tx" - ret=1 + if [ "$count" != "$fail_tx" ]; then + echo "[fail] got $count MP_FAIL[s] TX expected $fail_tx" + fail_test dump_stats=1 else echo -n "[ ok ]" fi - echo -n " - frx " - count=`ip netns exec $ns2 nstat -as | grep MPTcpExtMPFailRx | awk '{print $2}'` + echo -n " - failrx" + count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtMPFailRx | awk '{print $2}') [ -z "$count" ] && count=0 - if [ "$count" != "$mp_fail_nr_rx" ]; then - echo "[fail] got $count MP_FAIL[s] RX expected $mp_fail_nr_rx" - ret=1 + if [ "$count" != "$fail_rx" ]; then + echo "[fail] got $count MP_FAIL[s] RX expected $fail_rx" + fail_test dump_stats=1 else echo "[ ok ]" @@ -652,30 +1030,115 @@ chk_fail_nr() [ "${dump_stats}" = 1 ] && dump_stats } +chk_fclose_nr() +{ + local fclose_tx=$1 + local fclose_rx=$2 + local count + local dump_stats + + printf "%-${nr_blank}s %s" " " "ctx" + count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtMPFastcloseTx | awk '{print $2}') + [ -z "$count" ] && count=0 + if [ "$count" != "$fclose_tx" ]; then + echo "[fail] got $count MP_FASTCLOSE[s] TX expected $fclose_tx" + fail_test + dump_stats=1 + else + echo -n "[ ok ]" + fi + + echo -n " - fclzrx" + count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPFastcloseRx | awk '{print $2}') + [ -z "$count" ] && count=0 + if [ "$count" != "$fclose_rx" ]; then + echo "[fail] got $count MP_FASTCLOSE[s] RX expected $fclose_rx" + fail_test + dump_stats=1 + else + echo "[ ok ]" + fi + + [ "${dump_stats}" = 1 ] && dump_stats +} + +chk_rst_nr() +{ + local rst_tx=$1 + local rst_rx=$2 + local ns_invert=${3:-""} + local count + local dump_stats + local ns_tx=$ns1 + local ns_rx=$ns2 + local extra_msg="" + + if [[ $ns_invert = "invert" ]]; then + ns_tx=$ns2 + ns_rx=$ns1 + extra_msg=" invert" + fi + + printf "%-${nr_blank}s %s" " " "rtx" + count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPRstTx | awk '{print $2}') + [ -z "$count" ] && count=0 + if [ "$count" != "$rst_tx" ]; then + echo "[fail] got $count MP_RST[s] TX expected $rst_tx" + fail_test + dump_stats=1 + else + echo -n "[ ok ]" + fi + + echo -n " - rstrx " + count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPRstRx | awk '{print $2}') + [ -z "$count" ] && count=0 + if [ "$count" != "$rst_rx" ]; then + echo "[fail] got $count MP_RST[s] RX expected $rst_rx" + fail_test + dump_stats=1 + else + echo -n "[ ok ]" + fi + + [ "${dump_stats}" = 1 ] && dump_stats + + echo "$extra_msg" +} + chk_join_nr() { - local msg="$1" - local syn_nr=$2 - local syn_ack_nr=$3 - local ack_nr=$4 + local syn_nr=$1 + local syn_ack_nr=$2 + local ack_nr=$3 + local csum_ns1=${4:-0} + local csum_ns2=${5:-0} + local fail_nr=${6:-0} + local rst_nr=${7:-0} + local corrupted_pkts=${8:-0} local count local dump_stats local with_cookie + local title="${TEST_NAME}" - printf "%02u %-36s %s" "$TEST_COUNT" "$msg" "syn" - count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinSynRx | awk '{print $2}'` + if [ "${corrupted_pkts}" -gt 0 ]; then + title+=": ${corrupted_pkts} corrupted pkts" + fi + + printf "%03u %-36s %s" "${TEST_COUNT}" "${title}" "syn" + count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinSynRx | awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$syn_nr" ]; then echo "[fail] got $count JOIN[s] syn expected $syn_nr" - ret=1 + fail_test dump_stats=1 else echo -n "[ ok ]" fi echo -n " - synack" - with_cookie=`ip netns exec $ns2 sysctl -n net.ipv4.tcp_syncookies` - count=`ip netns exec $ns2 nstat -as | grep MPTcpExtMPJoinSynAckRx | awk '{print $2}'` + with_cookie=$(ip netns exec $ns2 sysctl -n net.ipv4.tcp_syncookies) + count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtMPJoinSynAckRx | awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$syn_ack_nr" ]; then # simult connections exceeding the limit with cookie enabled could go up to @@ -685,7 +1148,7 @@ chk_join_nr() echo -n "[ ok ]" else echo "[fail] got $count JOIN[s] synack expected $syn_ack_nr" - ret=1 + fail_test dump_stats=1 fi else @@ -693,19 +1156,20 @@ chk_join_nr() fi echo -n " - ack" - count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinAckRx | awk '{print $2}'` + count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinAckRx | awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$ack_nr" ]; then echo "[fail] got $count JOIN[s] ack expected $ack_nr" - ret=1 + fail_test dump_stats=1 else echo "[ ok ]" fi [ "${dump_stats}" = 1 ] && dump_stats if [ $checksum -eq 1 ]; then - chk_csum_nr - chk_fail_nr 0 0 + chk_csum_nr $csum_ns1 $csum_ns2 + chk_fail_nr $fail_nr $fail_nr + chk_rst_nr $rst_nr $rst_nr fi } @@ -724,19 +1188,19 @@ chk_stale_nr() local stale_nr local recover_nr - printf "%-39s %-18s" " " "stale" - stale_nr=`ip netns exec $ns nstat -as | grep MPTcpExtSubflowStale | awk '{print $2}'` + printf "%-${nr_blank}s %-18s" " " "stale" + stale_nr=$(ip netns exec $ns nstat -as | grep MPTcpExtSubflowStale | awk '{print $2}') [ -z "$stale_nr" ] && stale_nr=0 - recover_nr=`ip netns exec $ns nstat -as | grep MPTcpExtSubflowRecover | awk '{print $2}'` + recover_nr=$(ip netns exec $ns nstat -as | grep MPTcpExtSubflowRecover | awk '{print $2}') [ -z "$recover_nr" ] && recover_nr=0 if [ $stale_nr -lt $stale_min ] || - [ $stale_max -gt 0 -a $stale_nr -gt $stale_max ] || - [ $((stale_nr - $recover_nr)) -ne $stale_delta ]; then + { [ $stale_max -gt 0 ] && [ $stale_nr -gt $stale_max ]; } || + [ $((stale_nr - recover_nr)) -ne $stale_delta ]; then echo "[fail] got $stale_nr stale[s] $recover_nr recover[s], " \ " expected stale in range [$stale_min..$stale_max]," \ " stale-recover delta $stale_delta " - ret=1 + fail_test dump_stats=1 else echo "[ ok ]" @@ -763,28 +1227,28 @@ chk_add_nr() local dump_stats local timeout - timeout=`ip netns exec $ns1 sysctl -n net.mptcp.add_addr_timeout` + timeout=$(ip netns exec $ns1 sysctl -n net.mptcp.add_addr_timeout) - printf "%-39s %s" " " "add" - count=`ip netns exec $ns2 nstat -as MPTcpExtAddAddr | grep MPTcpExtAddAddr | awk '{print $2}'` + printf "%-${nr_blank}s %s" " " "add" + count=$(ip netns exec $ns2 nstat -as MPTcpExtAddAddr | grep MPTcpExtAddAddr | awk '{print $2}') [ -z "$count" ] && count=0 # if the test configured a short timeout tolerate greater then expected # add addrs options, due to retransmissions - if [ "$count" != "$add_nr" ] && [ "$timeout" -gt 1 -o "$count" -lt "$add_nr" ]; then + if [ "$count" != "$add_nr" ] && { [ "$timeout" -gt 1 ] || [ "$count" -lt "$add_nr" ]; }; then echo "[fail] got $count ADD_ADDR[s] expected $add_nr" - ret=1 + fail_test dump_stats=1 else echo -n "[ ok ]" fi echo -n " - echo " - count=`ip netns exec $ns1 nstat -as | grep MPTcpExtEchoAdd | awk '{print $2}'` + count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtEchoAdd | awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$echo_nr" ]; then echo "[fail] got $count ADD_ADDR echo[s] expected $echo_nr" - ret=1 + fail_test dump_stats=1 else echo -n "[ ok ]" @@ -792,76 +1256,76 @@ chk_add_nr() if [ $port_nr -gt 0 ]; then echo -n " - pt " - count=`ip netns exec $ns2 nstat -as | grep MPTcpExtPortAdd | awk '{print $2}'` + count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtPortAdd | awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$port_nr" ]; then echo "[fail] got $count ADD_ADDR[s] with a port-number expected $port_nr" - ret=1 + fail_test dump_stats=1 else echo "[ ok ]" fi - printf "%-39s %s" " " "syn" - count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinPortSynRx | - awk '{print $2}'` + printf "%-${nr_blank}s %s" " " "syn" + count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinPortSynRx | + awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$syn_nr" ]; then echo "[fail] got $count JOIN[s] syn with a different \ port-number expected $syn_nr" - ret=1 + fail_test dump_stats=1 else echo -n "[ ok ]" fi echo -n " - synack" - count=`ip netns exec $ns2 nstat -as | grep MPTcpExtMPJoinPortSynAckRx | - awk '{print $2}'` + count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtMPJoinPortSynAckRx | + awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$syn_ack_nr" ]; then echo "[fail] got $count JOIN[s] synack with a different \ port-number expected $syn_ack_nr" - ret=1 + fail_test dump_stats=1 else echo -n "[ ok ]" fi echo -n " - ack" - count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinPortAckRx | - awk '{print $2}'` + count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinPortAckRx | + awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$ack_nr" ]; then echo "[fail] got $count JOIN[s] ack with a different \ port-number expected $ack_nr" - ret=1 + fail_test dump_stats=1 else echo "[ ok ]" fi - printf "%-39s %s" " " "syn" - count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMismatchPortSynRx | - awk '{print $2}'` + printf "%-${nr_blank}s %s" " " "syn" + count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMismatchPortSynRx | + awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$mis_syn_nr" ]; then echo "[fail] got $count JOIN[s] syn with a mismatched \ port-number expected $mis_syn_nr" - ret=1 + fail_test dump_stats=1 else echo -n "[ ok ]" fi echo -n " - ack " - count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMismatchPortAckRx | - awk '{print $2}'` + count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMismatchPortAckRx | + awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$mis_ack_nr" ]; then echo "[fail] got $count JOIN[s] ack with a mismatched \ port-number expected $mis_ack_nr" - ret=1 + fail_test dump_stats=1 else echo "[ ok ]" @@ -877,43 +1341,75 @@ chk_rm_nr() { local rm_addr_nr=$1 local rm_subflow_nr=$2 - local invert=${3:-""} + local invert + local simult local count local dump_stats - local addr_ns - local subflow_ns + local addr_ns=$ns1 + local subflow_ns=$ns2 + local extra_msg="" + + shift 2 + while [ -n "$1" ]; do + [ "$1" = "invert" ] && invert=true + [ "$1" = "simult" ] && simult=true + shift + done if [ -z $invert ]; then addr_ns=$ns1 subflow_ns=$ns2 - elif [ $invert = "invert" ]; then + elif [ $invert = "true" ]; then addr_ns=$ns2 subflow_ns=$ns1 + extra_msg=" invert" fi - printf "%-39s %s" " " "rm " - count=`ip netns exec $addr_ns nstat -as | grep MPTcpExtRmAddr | awk '{print $2}'` + printf "%-${nr_blank}s %s" " " "rm " + count=$(ip netns exec $addr_ns nstat -as | grep MPTcpExtRmAddr | awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$rm_addr_nr" ]; then echo "[fail] got $count RM_ADDR[s] expected $rm_addr_nr" - ret=1 + fail_test dump_stats=1 else echo -n "[ ok ]" fi - echo -n " - sf " - count=`ip netns exec $subflow_ns nstat -as | grep MPTcpExtRmSubflow | awk '{print $2}'` + echo -n " - rmsf " + count=$(ip netns exec $subflow_ns nstat -as | grep MPTcpExtRmSubflow | awk '{print $2}') [ -z "$count" ] && count=0 + if [ -n "$simult" ]; then + local cnt suffix + + cnt=$(ip netns exec $addr_ns nstat -as | grep MPTcpExtRmSubflow | awk '{print $2}') + + # in case of simult flush, the subflow removal count on each side is + # unreliable + [ -z "$cnt" ] && cnt=0 + count=$((count + cnt)) + [ "$count" != "$rm_subflow_nr" ] && suffix="$count in [$rm_subflow_nr:$((rm_subflow_nr*2))]" + if [ $count -ge "$rm_subflow_nr" ] && \ + [ "$count" -le "$((rm_subflow_nr *2 ))" ]; then + echo "[ ok ] $suffix" + else + echo "[fail] got $count RM_SUBFLOW[s] expected in range [$rm_subflow_nr:$((rm_subflow_nr*2))]" + fail_test + dump_stats=1 + fi + return + fi if [ "$count" != "$rm_subflow_nr" ]; then echo "[fail] got $count RM_SUBFLOW[s] expected $rm_subflow_nr" - ret=1 + fail_test dump_stats=1 else - echo "[ ok ]" + echo -n "[ ok ]" fi [ "${dump_stats}" = 1 ] && dump_stats + + echo "$extra_msg" } chk_prio_nr() @@ -923,23 +1419,23 @@ chk_prio_nr() local count local dump_stats - printf "%-39s %s" " " "ptx" - count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioTx | awk '{print $2}'` + printf "%-${nr_blank}s %s" " " "ptx" + count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioTx | awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$mp_prio_nr_tx" ]; then echo "[fail] got $count MP_PRIO[s] TX expected $mp_prio_nr_tx" - ret=1 + fail_test dump_stats=1 else echo -n "[ ok ]" fi echo -n " - prx " - count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioRx | awk '{print $2}'` + count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioRx | awk '{print $2}') [ -z "$count" ] && count=0 if [ "$count" != "$mp_prio_nr_rx" ]; then echo "[fail] got $count MP_PRIO[s] RX expected $mp_prio_nr_rx" - ret=1 + fail_test dump_stats=1 else echo "[ ok ]" @@ -954,29 +1450,33 @@ chk_link_usage() local link=$2 local out=$3 local expected_rate=$4 - local tx_link=`ip netns exec $ns cat /sys/class/net/$link/statistics/tx_bytes` - local tx_total=`ls -l $out | awk '{print $5}'` - local tx_rate=$((tx_link * 100 / $tx_total)) + + local tx_link tx_total + tx_link=$(ip netns exec $ns cat /sys/class/net/$link/statistics/tx_bytes) + tx_total=$(stat --format=%s $out) + local tx_rate=$((tx_link * 100 / tx_total)) local tolerance=5 - printf "%-39s %-18s" " " "link usage" - if [ $tx_rate -lt $((expected_rate - $tolerance)) -o \ - $tx_rate -gt $((expected_rate + $tolerance)) ]; then + printf "%-${nr_blank}s %-18s" " " "link usage" + if [ $tx_rate -lt $((expected_rate - tolerance)) ] || \ + [ $tx_rate -gt $((expected_rate + tolerance)) ]; then echo "[fail] got $tx_rate% usage, expected $expected_rate%" - ret=1 + fail_test else echo "[ ok ]" fi } -wait_for_tw() +wait_attempt_fail() { local timeout_ms=$((timeout_poll * 1000)) local time=0 local ns=$1 while [ $time -lt $timeout_ms ]; do - local cnt=$(ip netns exec $ns nstat -as TcpAttemptFails | grep TcpAttemptFails | awk '{print $2}') + local cnt + + cnt=$(ip netns exec $ns nstat -as TcpAttemptFails | grep TcpAttemptFails | awk '{print $2}') [ "$cnt" = 1 ] && return 1 time=$((time + 100)) @@ -987,877 +1487,968 @@ wait_for_tw() subflows_tests() { - reset - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "no JOIN" "0" "0" "0" + if reset "no JOIN"; then + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 0 0 0 + fi # subflow limited by client - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 0 - ip netns exec $ns2 ./pm_nl_ctl limits 0 0 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "single subflow, limited by client" 0 0 0 + if reset "single subflow, limited by client"; then + pm_nl_set_limits $ns1 0 0 + pm_nl_set_limits $ns2 0 0 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 0 0 0 + fi # subflow limited by server - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 0 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "single subflow, limited by server" 1 1 0 + if reset "single subflow, limited by server"; then + pm_nl_set_limits $ns1 0 0 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 0 + fi # subflow - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "single subflow" 1 1 1 + if reset "single subflow"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + fi # multiple subflows - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.2.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "multiple subflows" 2 2 2 + if reset "multiple subflows"; then + pm_nl_set_limits $ns1 0 2 + pm_nl_set_limits $ns2 0 2 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 2 2 2 + fi # multiple subflows limited by server - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.2.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "multiple subflows, limited by server" 2 2 1 + if reset "multiple subflows, limited by server"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 2 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 2 2 1 + fi # single subflow, dev - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow dev ns2eth3 - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "single subflow, dev" 1 1 1 + if reset "single subflow, dev"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow dev ns2eth3 + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + fi } subflows_error_tests() { # If a single subflow is configured, and matches the MPC src # address, no additional subflow should be created - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.1.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow - chk_join_nr "no MPC reuse with single endpoint" 0 0 0 + if reset "no MPC reuse with single endpoint"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow + chk_join_nr 0 0 0 + fi # multiple subflows, with subflow creation error - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.2.2 flags subflow - ip netns exec $ns1 iptables -A INPUT -s 10.0.3.2 -p tcp -j REJECT - run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow - chk_join_nr "multi subflows, with failing subflow" 1 1 1 + if reset "multi subflows, with failing subflow"; then + pm_nl_set_limits $ns1 0 2 + pm_nl_set_limits $ns2 0 2 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow + filter_tcp_from $ns1 10.0.3.2 REJECT + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow + chk_join_nr 1 1 1 + fi # multiple subflows, with subflow timeout on MPJ - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.2.2 flags subflow - ip netns exec $ns1 iptables -A INPUT -s 10.0.3.2 -p tcp -j DROP - run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow - chk_join_nr "multi subflows, with subflow timeout" 1 1 1 + if reset "multi subflows, with subflow timeout"; then + pm_nl_set_limits $ns1 0 2 + pm_nl_set_limits $ns2 0 2 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow + filter_tcp_from $ns1 10.0.3.2 DROP + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow + chk_join_nr 1 1 1 + fi # multiple subflows, check that the endpoint corresponding to # closed subflow (due to reset) is not reused if additional # subflows are added later - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns1 iptables -A INPUT -s 10.0.3.2 -p tcp -j REJECT - run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow & + if reset "multi subflows, fair usage on close"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + filter_tcp_from $ns1 10.0.3.2 REJECT + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow & - # updates in the child shell do not have any effect here, we - # need to bump the test counter for the above case - TEST_COUNT=$((TEST_COUNT+1)) + # mpj subflow will be in TW after the reset + wait_attempt_fail $ns2 + pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow + wait - # mpj subflow will be in TW after the reset - wait_for_tw $ns2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.2.2 flags subflow - wait - - # additional subflow could be created only if the PM select - # the later endpoint, skipping the already used one - chk_join_nr "multi subflows, fair usage on close" 1 1 1 + # additional subflow could be created only if the PM select + # the later endpoint, skipping the already used one + chk_join_nr 1 1 1 + fi } signal_address_tests() { # add_address, unused - reset - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "unused signal address" 0 0 0 - chk_add_nr 1 1 + if reset "unused signal address"; then + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 0 0 0 + chk_add_nr 1 1 + fi # accept and use add_addr - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "signal address" 1 1 1 - chk_add_nr 1 1 + if reset "signal address"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 1 1 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + chk_add_nr 1 1 + fi # accept and use add_addr with an additional subflow # note: signal address in server ns and local addresses in client ns must # belong to different subnets or one of the listed local address could be # used for 'add_addr' subflow - reset - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl limits 1 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "subflow and signal" 2 2 2 - chk_add_nr 1 1 + if reset "subflow and signal"; then + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_set_limits $ns1 0 2 + pm_nl_set_limits $ns2 1 2 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 2 2 2 + chk_add_nr 1 1 + fi # accept and use add_addr with additional subflows - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 3 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.4.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "multiple subflows and signal" 3 3 3 - chk_add_nr 1 1 + if reset "multiple subflows and signal"; then + pm_nl_set_limits $ns1 0 3 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_set_limits $ns2 1 3 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.4.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 3 3 3 + chk_add_nr 1 1 + fi # signal addresses - reset - ip netns exec $ns1 ./pm_nl_ctl limits 3 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.3.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.4.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 3 3 - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "signal addresses" 3 3 3 - chk_add_nr 3 3 + if reset "signal addresses"; then + pm_nl_set_limits $ns1 3 3 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_add_endpoint $ns1 10.0.3.1 flags signal + pm_nl_add_endpoint $ns1 10.0.4.1 flags signal + pm_nl_set_limits $ns2 3 3 + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 3 3 3 + chk_add_nr 3 3 + fi # signal invalid addresses - reset - ip netns exec $ns1 ./pm_nl_ctl limits 3 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.12.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.3.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.14.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 3 3 - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "signal invalid addresses" 1 1 1 - chk_add_nr 3 3 + if reset "signal invalid addresses"; then + pm_nl_set_limits $ns1 3 3 + pm_nl_add_endpoint $ns1 10.0.12.1 flags signal + pm_nl_add_endpoint $ns1 10.0.3.1 flags signal + pm_nl_add_endpoint $ns1 10.0.14.1 flags signal + pm_nl_set_limits $ns2 3 3 + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + chk_add_nr 3 3 + fi # signal addresses race test - reset - ip netns exec $ns1 ./pm_nl_ctl limits 4 4 - ip netns exec $ns2 ./pm_nl_ctl limits 4 4 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.3.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.4.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl add 10.0.1.2 flags signal - ip netns exec $ns2 ./pm_nl_ctl add 10.0.2.2 flags signal - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags signal - ip netns exec $ns2 ./pm_nl_ctl add 10.0.4.2 flags signal + if reset "signal addresses race test"; then + pm_nl_set_limits $ns1 4 4 + pm_nl_set_limits $ns2 4 4 + pm_nl_add_endpoint $ns1 10.0.1.1 flags signal + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_add_endpoint $ns1 10.0.3.1 flags signal + pm_nl_add_endpoint $ns1 10.0.4.1 flags signal + pm_nl_add_endpoint $ns2 10.0.1.2 flags signal + pm_nl_add_endpoint $ns2 10.0.2.2 flags signal + pm_nl_add_endpoint $ns2 10.0.3.2 flags signal + pm_nl_add_endpoint $ns2 10.0.4.2 flags signal - # the peer could possibly miss some addr notification, allow retransmission - ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=1 - run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow - chk_join_nr "signal addresses race test" 3 3 3 + # the peer could possibly miss some addr notification, allow retransmission + ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=1 + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow + chk_join_nr 3 3 3 - # the server will not signal the address terminating - # the MPC subflow - chk_add_nr 3 3 + # the server will not signal the address terminating + # the MPC subflow + chk_add_nr 3 3 + fi } link_failure_tests() { # accept and use add_addr with additional subflows and link loss - reset - - # without any b/w limit each veth could spool the packets and get - # them acked at xmit time, so that the corresponding subflow will - # have almost always no outstanding pkts, the scheduler will pick - # always the first subflow and we will have hard time testing - # active backup and link switch-over. - # Let's set some arbitrary (low) virtual link limits. - init_shapers - ip netns exec $ns1 ./pm_nl_ctl limits 0 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 dev ns1eth2 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 3 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 dev ns2eth3 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.4.2 dev ns2eth4 flags subflow - run_tests $ns1 $ns2 10.0.1.1 1 - chk_join_nr "multiple flows, signal, link failure" 3 3 3 - chk_add_nr 1 1 - chk_stale_nr $ns2 1 5 1 + if reset "multiple flows, signal, link failure"; then + # without any b/w limit each veth could spool the packets and get + # them acked at xmit time, so that the corresponding subflow will + # have almost always no outstanding pkts, the scheduler will pick + # always the first subflow and we will have hard time testing + # active backup and link switch-over. + # Let's set some arbitrary (low) virtual link limits. + init_shapers + pm_nl_set_limits $ns1 0 3 + pm_nl_add_endpoint $ns1 10.0.2.1 dev ns1eth2 flags signal + pm_nl_set_limits $ns2 1 3 + pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow + pm_nl_add_endpoint $ns2 10.0.4.2 dev ns2eth4 flags subflow + run_tests $ns1 $ns2 10.0.1.1 1 + chk_join_nr 3 3 3 + chk_add_nr 1 1 + chk_stale_nr $ns2 1 5 1 + fi # accept and use add_addr with additional subflows and link loss # for bidirectional transfer - reset - init_shapers - ip netns exec $ns1 ./pm_nl_ctl limits 0 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 dev ns1eth2 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 3 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 dev ns2eth3 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.4.2 dev ns2eth4 flags subflow - run_tests $ns1 $ns2 10.0.1.1 2 - chk_join_nr "multi flows, signal, bidi, link fail" 3 3 3 - chk_add_nr 1 1 - chk_stale_nr $ns2 1 -1 1 + if reset "multi flows, signal, bidi, link fail"; then + init_shapers + pm_nl_set_limits $ns1 0 3 + pm_nl_add_endpoint $ns1 10.0.2.1 dev ns1eth2 flags signal + pm_nl_set_limits $ns2 1 3 + pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow + pm_nl_add_endpoint $ns2 10.0.4.2 dev ns2eth4 flags subflow + run_tests $ns1 $ns2 10.0.1.1 2 + chk_join_nr 3 3 3 + chk_add_nr 1 1 + chk_stale_nr $ns2 1 -1 1 + fi # 2 subflows plus 1 backup subflow with a lossy link, backup # will never be used - reset - init_shapers - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 dev ns1eth2 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 2 - export FAILING_LINKS="1" - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 dev ns2eth3 flags subflow,backup - run_tests $ns1 $ns2 10.0.1.1 1 - chk_join_nr "backup subflow unused, link failure" 2 2 2 - chk_add_nr 1 1 - chk_link_usage $ns2 ns2eth3 $cinsent 0 + if reset "backup subflow unused, link failure"; then + init_shapers + pm_nl_set_limits $ns1 0 2 + pm_nl_add_endpoint $ns1 10.0.2.1 dev ns1eth2 flags signal + pm_nl_set_limits $ns2 1 2 + FAILING_LINKS="1" + pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow,backup + run_tests $ns1 $ns2 10.0.1.1 1 + chk_join_nr 2 2 2 + chk_add_nr 1 1 + chk_link_usage $ns2 ns2eth3 $cinsent 0 + fi # 2 lossy links after half transfer, backup will get half of # the traffic - reset - init_shapers - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 dev ns1eth2 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 dev ns2eth3 flags subflow,backup - export FAILING_LINKS="1 2" - run_tests $ns1 $ns2 10.0.1.1 1 - chk_join_nr "backup flow used, multi links fail" 2 2 2 - chk_add_nr 1 1 - chk_stale_nr $ns2 2 4 2 - chk_link_usage $ns2 ns2eth3 $cinsent 50 + if reset "backup flow used, multi links fail"; then + init_shapers + pm_nl_set_limits $ns1 0 2 + pm_nl_add_endpoint $ns1 10.0.2.1 dev ns1eth2 flags signal + pm_nl_set_limits $ns2 1 2 + pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow,backup + FAILING_LINKS="1 2" + run_tests $ns1 $ns2 10.0.1.1 1 + chk_join_nr 2 2 2 + chk_add_nr 1 1 + chk_stale_nr $ns2 2 4 2 + chk_link_usage $ns2 ns2eth3 $cinsent 50 + fi # use a backup subflow with the first subflow on a lossy link # for bidirectional transfer - reset - init_shapers - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 dev ns1eth2 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 3 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 dev ns2eth3 flags subflow,backup - run_tests $ns1 $ns2 10.0.1.1 2 - chk_join_nr "backup flow used, bidi, link failure" 2 2 2 - chk_add_nr 1 1 - chk_stale_nr $ns2 1 -1 2 - chk_link_usage $ns2 ns2eth3 $cinsent 50 + if reset "backup flow used, bidi, link failure"; then + init_shapers + pm_nl_set_limits $ns1 0 2 + pm_nl_add_endpoint $ns1 10.0.2.1 dev ns1eth2 flags signal + pm_nl_set_limits $ns2 1 3 + pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow,backup + FAILING_LINKS="1 2" + run_tests $ns1 $ns2 10.0.1.1 2 + chk_join_nr 2 2 2 + chk_add_nr 1 1 + chk_stale_nr $ns2 1 -1 2 + chk_link_usage $ns2 ns2eth3 $cinsent 50 + fi } add_addr_timeout_tests() { # add_addr timeout - reset_with_add_addr_timeout - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow - chk_join_nr "signal address, ADD_ADDR timeout" 1 1 1 - chk_add_nr 4 0 + if reset_with_add_addr_timeout "signal address, ADD_ADDR timeout"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 1 1 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow + chk_join_nr 1 1 1 + chk_add_nr 4 0 + fi # add_addr timeout IPv6 - reset_with_add_addr_timeout 6 - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - ip netns exec $ns1 ./pm_nl_ctl add dead:beef:2::1 flags signal - run_tests $ns1 $ns2 dead:beef:1::1 0 0 0 slow - chk_join_nr "signal address, ADD_ADDR6 timeout" 1 1 1 - chk_add_nr 4 0 + if reset_with_add_addr_timeout "signal address, ADD_ADDR6 timeout" 6; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 1 1 + pm_nl_add_endpoint $ns1 dead:beef:2::1 flags signal + run_tests $ns1 $ns2 dead:beef:1::1 0 0 0 slow + chk_join_nr 1 1 1 + chk_add_nr 4 0 + fi # signal addresses timeout - reset_with_add_addr_timeout - ip netns exec $ns1 ./pm_nl_ctl limits 2 2 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.3.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 2 2 - run_tests $ns1 $ns2 10.0.1.1 0 0 0 least - chk_join_nr "signal addresses, ADD_ADDR timeout" 2 2 2 - chk_add_nr 8 0 + if reset_with_add_addr_timeout "signal addresses, ADD_ADDR timeout"; then + pm_nl_set_limits $ns1 2 2 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_add_endpoint $ns1 10.0.3.1 flags signal + pm_nl_set_limits $ns2 2 2 + run_tests $ns1 $ns2 10.0.1.1 0 0 0 speed_10 + chk_join_nr 2 2 2 + chk_add_nr 8 0 + fi # signal invalid addresses timeout - reset_with_add_addr_timeout - ip netns exec $ns1 ./pm_nl_ctl limits 2 2 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.12.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.3.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 2 2 - run_tests $ns1 $ns2 10.0.1.1 0 0 0 least - chk_join_nr "invalid address, ADD_ADDR timeout" 1 1 1 - chk_add_nr 8 0 + if reset_with_add_addr_timeout "invalid address, ADD_ADDR timeout"; then + pm_nl_set_limits $ns1 2 2 + pm_nl_add_endpoint $ns1 10.0.12.1 flags signal + pm_nl_add_endpoint $ns1 10.0.3.1 flags signal + pm_nl_set_limits $ns2 2 2 + run_tests $ns1 $ns2 10.0.1.1 0 0 0 speed_10 + chk_join_nr 1 1 1 + chk_add_nr 8 0 + fi } remove_tests() { # single subflow, remove - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 0 0 -1 slow - chk_join_nr "remove single subflow" 1 1 1 - chk_rm_nr 1 1 + if reset "remove single subflow"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 0 0 -1 slow + chk_join_nr 1 1 1 + chk_rm_nr 1 1 + fi # multiple subflows, remove - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.2.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 0 0 -2 slow - chk_join_nr "remove multiple subflows" 2 2 2 - chk_rm_nr 2 2 + if reset "remove multiple subflows"; then + pm_nl_set_limits $ns1 0 2 + pm_nl_set_limits $ns2 0 2 + pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 0 0 -2 slow + chk_join_nr 2 2 2 + chk_rm_nr 2 2 + fi # single address, remove - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - run_tests $ns1 $ns2 10.0.1.1 0 -1 0 slow - chk_join_nr "remove single address" 1 1 1 - chk_add_nr 1 1 - chk_rm_nr 1 1 invert + if reset "remove single address"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_set_limits $ns2 1 1 + run_tests $ns1 $ns2 10.0.1.1 0 -1 0 slow + chk_join_nr 1 1 1 + chk_add_nr 1 1 + chk_rm_nr 1 1 invert + fi # subflow and signal, remove - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 0 -1 -1 slow - chk_join_nr "remove subflow and signal" 2 2 2 - chk_add_nr 1 1 - chk_rm_nr 1 1 + if reset "remove subflow and signal"; then + pm_nl_set_limits $ns1 0 2 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_set_limits $ns2 1 2 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 0 -1 -1 slow + chk_join_nr 2 2 2 + chk_add_nr 1 1 + chk_rm_nr 1 1 + fi # subflows and signal, remove - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 3 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.4.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 0 -1 -2 slow - chk_join_nr "remove subflows and signal" 3 3 3 - chk_add_nr 1 1 - chk_rm_nr 2 2 + if reset "remove subflows and signal"; then + pm_nl_set_limits $ns1 0 3 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_set_limits $ns2 1 3 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.4.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 0 -1 -2 slow + chk_join_nr 3 3 3 + chk_add_nr 1 1 + chk_rm_nr 2 2 + fi # addresses remove - reset - ip netns exec $ns1 ./pm_nl_ctl limits 3 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal id 250 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.3.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.4.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 3 3 - run_tests $ns1 $ns2 10.0.1.1 0 -3 0 slow - chk_join_nr "remove addresses" 3 3 3 - chk_add_nr 3 3 - chk_rm_nr 3 3 invert + if reset "remove addresses"; then + pm_nl_set_limits $ns1 3 3 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal id 250 + pm_nl_add_endpoint $ns1 10.0.3.1 flags signal + pm_nl_add_endpoint $ns1 10.0.4.1 flags signal + pm_nl_set_limits $ns2 3 3 + run_tests $ns1 $ns2 10.0.1.1 0 -3 0 slow + chk_join_nr 3 3 3 + chk_add_nr 3 3 + chk_rm_nr 3 3 invert + fi # invalid addresses remove - reset - ip netns exec $ns1 ./pm_nl_ctl limits 3 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.12.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.3.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.14.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 3 3 - run_tests $ns1 $ns2 10.0.1.1 0 -3 0 slow - chk_join_nr "remove invalid addresses" 1 1 1 - chk_add_nr 3 3 - chk_rm_nr 3 1 invert + if reset "remove invalid addresses"; then + pm_nl_set_limits $ns1 3 3 + pm_nl_add_endpoint $ns1 10.0.12.1 flags signal + pm_nl_add_endpoint $ns1 10.0.3.1 flags signal + pm_nl_add_endpoint $ns1 10.0.14.1 flags signal + pm_nl_set_limits $ns2 3 3 + run_tests $ns1 $ns2 10.0.1.1 0 -3 0 slow + chk_join_nr 1 1 1 + chk_add_nr 3 3 + chk_rm_nr 3 1 invert + fi # subflows and signal, flush - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 3 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.4.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 0 -8 -8 slow - chk_join_nr "flush subflows and signal" 3 3 3 - chk_add_nr 1 1 - chk_rm_nr 2 2 + if reset "flush subflows and signal"; then + pm_nl_set_limits $ns1 0 3 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_set_limits $ns2 1 3 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.4.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 0 -8 -8 slow + chk_join_nr 3 3 3 + chk_add_nr 1 1 + chk_rm_nr 1 3 invert simult + fi # subflows flush - reset - ip netns exec $ns1 ./pm_nl_ctl limits 3 3 - ip netns exec $ns2 ./pm_nl_ctl limits 3 3 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.2.2 flags subflow id 150 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.4.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 0 -8 -8 slow - chk_join_nr "flush subflows" 3 3 3 - chk_rm_nr 3 3 + if reset "flush subflows"; then + pm_nl_set_limits $ns1 3 3 + pm_nl_set_limits $ns2 3 3 + pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow id 150 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.4.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 0 -8 -8 slow + chk_join_nr 3 3 3 + chk_rm_nr 0 3 simult + fi # addresses flush - reset - ip netns exec $ns1 ./pm_nl_ctl limits 3 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal id 250 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.3.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.4.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 3 3 - run_tests $ns1 $ns2 10.0.1.1 0 -8 -8 slow - chk_join_nr "flush addresses" 3 3 3 - chk_add_nr 3 3 - chk_rm_nr 3 3 invert + if reset "flush addresses"; then + pm_nl_set_limits $ns1 3 3 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal id 250 + pm_nl_add_endpoint $ns1 10.0.3.1 flags signal + pm_nl_add_endpoint $ns1 10.0.4.1 flags signal + pm_nl_set_limits $ns2 3 3 + run_tests $ns1 $ns2 10.0.1.1 0 -8 -8 slow + chk_join_nr 3 3 3 + chk_add_nr 3 3 + chk_rm_nr 3 3 invert simult + fi # invalid addresses flush - reset - ip netns exec $ns1 ./pm_nl_ctl limits 3 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.12.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.3.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl add 10.0.14.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 3 3 - run_tests $ns1 $ns2 10.0.1.1 0 -8 0 slow - chk_join_nr "flush invalid addresses" 1 1 1 - chk_add_nr 3 3 - chk_rm_nr 3 1 invert + if reset "flush invalid addresses"; then + pm_nl_set_limits $ns1 3 3 + pm_nl_add_endpoint $ns1 10.0.12.1 flags signal + pm_nl_add_endpoint $ns1 10.0.3.1 flags signal + pm_nl_add_endpoint $ns1 10.0.14.1 flags signal + pm_nl_set_limits $ns2 3 3 + run_tests $ns1 $ns2 10.0.1.1 0 -8 0 slow + chk_join_nr 1 1 1 + chk_add_nr 3 3 + chk_rm_nr 3 1 invert + fi # remove id 0 subflow - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 0 0 -9 slow - chk_join_nr "remove id 0 subflow" 1 1 1 - chk_rm_nr 1 1 + if reset "remove id 0 subflow"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 0 0 -9 slow + chk_join_nr 1 1 1 + chk_rm_nr 1 1 + fi # remove id 0 address - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - run_tests $ns1 $ns2 10.0.1.1 0 -9 0 slow - chk_join_nr "remove id 0 address" 1 1 1 - chk_add_nr 1 1 - chk_rm_nr 1 1 invert + if reset "remove id 0 address"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_set_limits $ns2 1 1 + run_tests $ns1 $ns2 10.0.1.1 0 -9 0 slow + chk_join_nr 1 1 1 + chk_add_nr 1 1 + chk_rm_nr 1 1 invert + fi } add_tests() { # add single subflow - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - run_tests $ns1 $ns2 10.0.1.1 0 0 1 slow - chk_join_nr "add single subflow" 1 1 1 + if reset "add single subflow"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + run_tests $ns1 $ns2 10.0.1.1 0 0 1 slow + chk_join_nr 1 1 1 + fi # add signal address - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - run_tests $ns1 $ns2 10.0.1.1 0 1 0 slow - chk_join_nr "add signal address" 1 1 1 - chk_add_nr 1 1 + if reset "add signal address"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 1 1 + run_tests $ns1 $ns2 10.0.1.1 0 1 0 slow + chk_join_nr 1 1 1 + chk_add_nr 1 1 + fi # add multiple subflows - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl limits 0 2 - run_tests $ns1 $ns2 10.0.1.1 0 0 2 slow - chk_join_nr "add multiple subflows" 2 2 2 + if reset "add multiple subflows"; then + pm_nl_set_limits $ns1 0 2 + pm_nl_set_limits $ns2 0 2 + run_tests $ns1 $ns2 10.0.1.1 0 0 2 slow + chk_join_nr 2 2 2 + fi # add multiple subflows IPv6 - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl limits 0 2 - run_tests $ns1 $ns2 dead:beef:1::1 0 0 2 slow - chk_join_nr "add multiple subflows IPv6" 2 2 2 + if reset "add multiple subflows IPv6"; then + pm_nl_set_limits $ns1 0 2 + pm_nl_set_limits $ns2 0 2 + run_tests $ns1 $ns2 dead:beef:1::1 0 0 2 slow + chk_join_nr 2 2 2 + fi # add multiple addresses IPv6 - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl limits 2 2 - run_tests $ns1 $ns2 dead:beef:1::1 0 2 0 slow - chk_join_nr "add multiple addresses IPv6" 2 2 2 - chk_add_nr 2 2 + if reset "add multiple addresses IPv6"; then + pm_nl_set_limits $ns1 0 2 + pm_nl_set_limits $ns2 2 2 + run_tests $ns1 $ns2 dead:beef:1::1 0 2 0 slow + chk_join_nr 2 2 2 + chk_add_nr 2 2 + fi } ipv6_tests() { # subflow IPv6 - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add dead:beef:3::2 dev ns2eth3 flags subflow - run_tests $ns1 $ns2 dead:beef:1::1 0 0 0 slow - chk_join_nr "single subflow IPv6" 1 1 1 + if reset "single subflow IPv6"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 dead:beef:3::2 dev ns2eth3 flags subflow + run_tests $ns1 $ns2 dead:beef:1::1 0 0 0 slow + chk_join_nr 1 1 1 + fi # add_address, unused IPv6 - reset - ip netns exec $ns1 ./pm_nl_ctl add dead:beef:2::1 flags signal - run_tests $ns1 $ns2 dead:beef:1::1 0 0 0 slow - chk_join_nr "unused signal address IPv6" 0 0 0 - chk_add_nr 1 1 + if reset "unused signal address IPv6"; then + pm_nl_add_endpoint $ns1 dead:beef:2::1 flags signal + run_tests $ns1 $ns2 dead:beef:1::1 0 0 0 slow + chk_join_nr 0 0 0 + chk_add_nr 1 1 + fi # signal address IPv6 - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns1 ./pm_nl_ctl add dead:beef:2::1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - run_tests $ns1 $ns2 dead:beef:1::1 0 0 0 slow - chk_join_nr "single address IPv6" 1 1 1 - chk_add_nr 1 1 + if reset "single address IPv6"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_add_endpoint $ns1 dead:beef:2::1 flags signal + pm_nl_set_limits $ns2 1 1 + run_tests $ns1 $ns2 dead:beef:1::1 0 0 0 slow + chk_join_nr 1 1 1 + chk_add_nr 1 1 + fi # single address IPv6, remove - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns1 ./pm_nl_ctl add dead:beef:2::1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - run_tests $ns1 $ns2 dead:beef:1::1 0 -1 0 slow - chk_join_nr "remove single address IPv6" 1 1 1 - chk_add_nr 1 1 - chk_rm_nr 1 1 invert + if reset "remove single address IPv6"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_add_endpoint $ns1 dead:beef:2::1 flags signal + pm_nl_set_limits $ns2 1 1 + run_tests $ns1 $ns2 dead:beef:1::1 0 -1 0 slow + chk_join_nr 1 1 1 + chk_add_nr 1 1 + chk_rm_nr 1 1 invert + fi # subflow and signal IPv6, remove - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns1 ./pm_nl_ctl add dead:beef:2::1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 2 - ip netns exec $ns2 ./pm_nl_ctl add dead:beef:3::2 dev ns2eth3 flags subflow - run_tests $ns1 $ns2 dead:beef:1::1 0 -1 -1 slow - chk_join_nr "remove subflow and signal IPv6" 2 2 2 - chk_add_nr 1 1 - chk_rm_nr 1 1 + if reset "remove subflow and signal IPv6"; then + pm_nl_set_limits $ns1 0 2 + pm_nl_add_endpoint $ns1 dead:beef:2::1 flags signal + pm_nl_set_limits $ns2 1 2 + pm_nl_add_endpoint $ns2 dead:beef:3::2 dev ns2eth3 flags subflow + run_tests $ns1 $ns2 dead:beef:1::1 0 -1 -1 slow + chk_join_nr 2 2 2 + chk_add_nr 1 1 + chk_rm_nr 1 1 + fi } v4mapped_tests() { # subflow IPv4-mapped to IPv4-mapped - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add "::ffff:10.0.3.2" flags subflow - run_tests $ns1 $ns2 "::ffff:10.0.1.1" - chk_join_nr "single subflow IPv4-mapped" 1 1 1 + if reset "single subflow IPv4-mapped"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 "::ffff:10.0.3.2" flags subflow + run_tests $ns1 $ns2 "::ffff:10.0.1.1" + chk_join_nr 1 1 1 + fi # signal address IPv4-mapped with IPv4-mapped sk - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - ip netns exec $ns1 ./pm_nl_ctl add "::ffff:10.0.2.1" flags signal - run_tests $ns1 $ns2 "::ffff:10.0.1.1" - chk_join_nr "signal address IPv4-mapped" 1 1 1 - chk_add_nr 1 1 + if reset "signal address IPv4-mapped"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 1 1 + pm_nl_add_endpoint $ns1 "::ffff:10.0.2.1" flags signal + run_tests $ns1 $ns2 "::ffff:10.0.1.1" + chk_join_nr 1 1 1 + chk_add_nr 1 1 + fi # subflow v4-map-v6 - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 "::ffff:10.0.1.1" - chk_join_nr "single subflow v4-map-v6" 1 1 1 + if reset "single subflow v4-map-v6"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 "::ffff:10.0.1.1" + chk_join_nr 1 1 1 + fi # signal address v4-map-v6 - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - run_tests $ns1 $ns2 "::ffff:10.0.1.1" - chk_join_nr "signal address v4-map-v6" 1 1 1 - chk_add_nr 1 1 + if reset "signal address v4-map-v6"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 1 1 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + run_tests $ns1 $ns2 "::ffff:10.0.1.1" + chk_join_nr 1 1 1 + chk_add_nr 1 1 + fi # subflow v6-map-v4 - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add "::ffff:10.0.3.2" flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "single subflow v6-map-v4" 1 1 1 + if reset "single subflow v6-map-v4"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 "::ffff:10.0.3.2" flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + fi # signal address v6-map-v4 - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - ip netns exec $ns1 ./pm_nl_ctl add "::ffff:10.0.2.1" flags signal - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "signal address v6-map-v4" 1 1 1 - chk_add_nr 1 1 + if reset "signal address v6-map-v4"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 1 1 + pm_nl_add_endpoint $ns1 "::ffff:10.0.2.1" flags signal + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + chk_add_nr 1 1 + fi # no subflow IPv6 to v4 address - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add dead:beef:2::2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "no JOIN with diff families v4-v6" 0 0 0 + if reset "no JOIN with diff families v4-v6"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 dead:beef:2::2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 0 0 0 + fi # no subflow IPv6 to v4 address even if v6 has a valid v4 at the end - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add dead:beef:2::10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "no JOIN with diff families v4-v6-2" 0 0 0 + if reset "no JOIN with diff families v4-v6-2"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 dead:beef:2::10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 0 0 0 + fi # no subflow IPv4 to v6 address, no need to slow down too then - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 dead:beef:1::1 - chk_join_nr "no JOIN with diff families v6-v4" 0 0 0 + if reset "no JOIN with diff families v6-v4"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 dead:beef:1::1 + chk_join_nr 0 0 0 + fi } backup_tests() { # single subflow, backup - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow,backup - run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow nobackup - chk_join_nr "single subflow, backup" 1 1 1 - chk_prio_nr 0 1 + if reset "single subflow, backup"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow,backup + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow nobackup + chk_join_nr 1 1 1 + chk_prio_nr 0 1 + fi # single address, backup - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup - chk_join_nr "single address, backup" 1 1 1 - chk_add_nr 1 1 - chk_prio_nr 1 0 + if reset "single address, backup"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_set_limits $ns2 1 1 + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup + chk_join_nr 1 1 1 + chk_add_nr 1 1 + chk_prio_nr 1 1 + fi + + # single address with port, backup + if reset "single address with port, backup"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100 + pm_nl_set_limits $ns2 1 1 + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup + chk_join_nr 1 1 1 + chk_add_nr 1 1 + chk_prio_nr 1 1 + fi } add_addr_ports_tests() { # signal address with port - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal port 10100 - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "signal address with port" 1 1 1 - chk_add_nr 1 1 1 + if reset "signal address with port"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 1 1 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100 + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + chk_add_nr 1 1 1 + fi # subflow and signal with port - reset - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal port 10100 - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl limits 1 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "subflow and signal with port" 2 2 2 - chk_add_nr 1 1 1 + if reset "subflow and signal with port"; then + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100 + pm_nl_set_limits $ns1 0 2 + pm_nl_set_limits $ns2 1 2 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 2 2 2 + chk_add_nr 1 1 1 + fi # single address with port, remove - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal port 10100 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - run_tests $ns1 $ns2 10.0.1.1 0 -1 0 slow - chk_join_nr "remove single address with port" 1 1 1 - chk_add_nr 1 1 1 - chk_rm_nr 1 1 invert + if reset "remove single address with port"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100 + pm_nl_set_limits $ns2 1 1 + run_tests $ns1 $ns2 10.0.1.1 0 -1 0 slow + chk_join_nr 1 1 1 + chk_add_nr 1 1 1 + chk_rm_nr 1 1 invert + fi # subflow and signal with port, remove - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal port 10100 - ip netns exec $ns2 ./pm_nl_ctl limits 1 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 0 -1 -1 slow - chk_join_nr "remove subflow and signal with port" 2 2 2 - chk_add_nr 1 1 1 - chk_rm_nr 1 1 + if reset "remove subflow and signal with port"; then + pm_nl_set_limits $ns1 0 2 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100 + pm_nl_set_limits $ns2 1 2 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 0 -1 -1 slow + chk_join_nr 2 2 2 + chk_add_nr 1 1 1 + chk_rm_nr 1 1 + fi # subflows and signal with port, flush - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal port 10100 - ip netns exec $ns2 ./pm_nl_ctl limits 1 3 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.4.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 0 -8 -2 slow - chk_join_nr "flush subflows and signal with port" 3 3 3 - chk_add_nr 1 1 - chk_rm_nr 2 2 + if reset "flush subflows and signal with port"; then + pm_nl_set_limits $ns1 0 3 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100 + pm_nl_set_limits $ns2 1 3 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.4.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 0 -8 -2 slow + chk_join_nr 3 3 3 + chk_add_nr 1 1 + chk_rm_nr 1 3 invert simult + fi # multiple addresses with port - reset - ip netns exec $ns1 ./pm_nl_ctl limits 2 2 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal port 10100 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.3.1 flags signal port 10100 - ip netns exec $ns2 ./pm_nl_ctl limits 2 2 - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "multiple addresses with port" 2 2 2 - chk_add_nr 2 2 2 + if reset "multiple addresses with port"; then + pm_nl_set_limits $ns1 2 2 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100 + pm_nl_add_endpoint $ns1 10.0.3.1 flags signal port 10100 + pm_nl_set_limits $ns2 2 2 + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 2 2 2 + chk_add_nr 2 2 2 + fi # multiple addresses with ports - reset - ip netns exec $ns1 ./pm_nl_ctl limits 2 2 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal port 10100 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.3.1 flags signal port 10101 - ip netns exec $ns2 ./pm_nl_ctl limits 2 2 - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "multiple addresses with ports" 2 2 2 - chk_add_nr 2 2 2 + if reset "multiple addresses with ports"; then + pm_nl_set_limits $ns1 2 2 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100 + pm_nl_add_endpoint $ns1 10.0.3.1 flags signal port 10101 + pm_nl_set_limits $ns2 2 2 + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 2 2 2 + chk_add_nr 2 2 2 + fi } syncookies_tests() { # single subflow, syncookies - reset_with_cookies - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "single subflow with syn cookies" 1 1 1 + if reset_with_cookies "single subflow with syn cookies"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + fi # multiple subflows with syn cookies - reset_with_cookies - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.2.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "multiple subflows with syn cookies" 2 2 2 + if reset_with_cookies "multiple subflows with syn cookies"; then + pm_nl_set_limits $ns1 0 2 + pm_nl_set_limits $ns2 0 2 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 2 2 2 + fi # multiple subflows limited by server - reset_with_cookies - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.2.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "subflows limited by server w cookies" 2 1 1 + if reset_with_cookies "subflows limited by server w cookies"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 2 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 2 1 1 + fi # test signal address with cookies - reset_with_cookies - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "signal address with syn cookies" 1 1 1 - chk_add_nr 1 1 + if reset_with_cookies "signal address with syn cookies"; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 1 1 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + chk_add_nr 1 1 + fi # test cookie with subflow and signal - reset_with_cookies - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns1 ./pm_nl_ctl limits 0 2 - ip netns exec $ns2 ./pm_nl_ctl limits 1 2 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "subflow and signal w cookies" 2 2 2 - chk_add_nr 1 1 + if reset_with_cookies "subflow and signal w cookies"; then + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_set_limits $ns1 0 2 + pm_nl_set_limits $ns2 1 2 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 2 2 2 + chk_add_nr 1 1 + fi # accept and use add_addr with additional subflows - reset_with_cookies - ip netns exec $ns1 ./pm_nl_ctl limits 0 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl limits 1 3 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - ip netns exec $ns2 ./pm_nl_ctl add 10.0.4.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "subflows and signal w. cookies" 3 3 3 - chk_add_nr 1 1 + if reset_with_cookies "subflows and signal w. cookies"; then + pm_nl_set_limits $ns1 0 3 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_set_limits $ns2 1 3 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + pm_nl_add_endpoint $ns2 10.0.4.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 3 3 3 + chk_add_nr 1 1 + fi } checksum_tests() { # checksum test 0 0 - reset_with_checksum 0 0 - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - run_tests $ns1 $ns2 10.0.1.1 - chk_csum_nr "checksum test 0 0" + if reset_with_checksum 0 0; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 0 0 0 + fi # checksum test 1 1 - reset_with_checksum 1 1 - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - run_tests $ns1 $ns2 10.0.1.1 - chk_csum_nr "checksum test 1 1" + if reset_with_checksum 1 1; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 0 0 0 + fi # checksum test 0 1 - reset_with_checksum 0 1 - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - run_tests $ns1 $ns2 10.0.1.1 - chk_csum_nr "checksum test 0 1" + if reset_with_checksum 0 1; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 0 0 0 + fi # checksum test 1 0 - reset_with_checksum 1 0 - ip netns exec $ns1 ./pm_nl_ctl limits 0 1 - ip netns exec $ns2 ./pm_nl_ctl limits 0 1 - run_tests $ns1 $ns2 10.0.1.1 - chk_csum_nr "checksum test 1 0" + if reset_with_checksum 1 0; then + pm_nl_set_limits $ns1 0 1 + pm_nl_set_limits $ns2 0 1 + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 0 0 0 + fi } deny_join_id0_tests() { # subflow allow join id0 ns1 - reset_with_allow_join_id0 1 0 - ip netns exec $ns1 ./pm_nl_ctl limits 1 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "single subflow allow join id0 ns1" 1 1 1 + if reset_with_allow_join_id0 "single subflow allow join id0 ns1" 1 0; then + pm_nl_set_limits $ns1 1 1 + pm_nl_set_limits $ns2 1 1 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + fi # subflow allow join id0 ns2 - reset_with_allow_join_id0 0 1 - ip netns exec $ns1 ./pm_nl_ctl limits 1 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "single subflow allow join id0 ns2" 0 0 0 + if reset_with_allow_join_id0 "single subflow allow join id0 ns2" 0 1; then + pm_nl_set_limits $ns1 1 1 + pm_nl_set_limits $ns2 1 1 + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 0 0 0 + fi # signal address allow join id0 ns1 # ADD_ADDRs are not affected by allow_join_id0 value. - reset_with_allow_join_id0 1 0 - ip netns exec $ns1 ./pm_nl_ctl limits 1 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "signal address allow join id0 ns1" 1 1 1 - chk_add_nr 1 1 + if reset_with_allow_join_id0 "signal address allow join id0 ns1" 1 0; then + pm_nl_set_limits $ns1 1 1 + pm_nl_set_limits $ns2 1 1 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + chk_add_nr 1 1 + fi # signal address allow join id0 ns2 # ADD_ADDRs are not affected by allow_join_id0 value. - reset_with_allow_join_id0 0 1 - ip netns exec $ns1 ./pm_nl_ctl limits 1 1 - ip netns exec $ns2 ./pm_nl_ctl limits 1 1 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "signal address allow join id0 ns2" 1 1 1 - chk_add_nr 1 1 + if reset_with_allow_join_id0 "signal address allow join id0 ns2" 0 1; then + pm_nl_set_limits $ns1 1 1 + pm_nl_set_limits $ns2 1 1 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + chk_add_nr 1 1 + fi # subflow and address allow join id0 ns1 - reset_with_allow_join_id0 1 0 - ip netns exec $ns1 ./pm_nl_ctl limits 2 2 - ip netns exec $ns2 ./pm_nl_ctl limits 2 2 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "subflow and address allow join id0 1" 2 2 2 + if reset_with_allow_join_id0 "subflow and address allow join id0 1" 1 0; then + pm_nl_set_limits $ns1 2 2 + pm_nl_set_limits $ns2 2 2 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 2 2 2 + fi # subflow and address allow join id0 ns2 - reset_with_allow_join_id0 0 1 - ip netns exec $ns1 ./pm_nl_ctl limits 2 2 - ip netns exec $ns2 ./pm_nl_ctl limits 2 2 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow - run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr "subflow and address allow join id0 2" 1 1 1 + if reset_with_allow_join_id0 "subflow and address allow join id0 2" 0 1; then + pm_nl_set_limits $ns1 2 2 + pm_nl_set_limits $ns2 2 2 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 1 1 1 + fi } fullmesh_tests() @@ -1865,177 +2456,237 @@ fullmesh_tests() # fullmesh 1 # 2 fullmesh addrs in ns2, added before the connection, # 1 non-fullmesh addr in ns1, added during the connection. - reset - ip netns exec $ns1 ./pm_nl_ctl limits 0 4 - ip netns exec $ns2 ./pm_nl_ctl limits 1 4 - ip netns exec $ns2 ./pm_nl_ctl add 10.0.2.2 flags subflow,fullmesh - ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow,fullmesh - run_tests $ns1 $ns2 10.0.1.1 0 1 0 slow - chk_join_nr "fullmesh test 2x1" 4 4 4 - chk_add_nr 1 1 + if reset "fullmesh test 2x1"; then + pm_nl_set_limits $ns1 0 4 + pm_nl_set_limits $ns2 1 4 + pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow,fullmesh + pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow,fullmesh + run_tests $ns1 $ns2 10.0.1.1 0 1 0 slow + chk_join_nr 4 4 4 + chk_add_nr 1 1 + fi # fullmesh 2 # 1 non-fullmesh addr in ns1, added before the connection, # 1 fullmesh addr in ns2, added during the connection. - reset - ip netns exec $ns1 ./pm_nl_ctl limits 1 3 - ip netns exec $ns2 ./pm_nl_ctl limits 1 3 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - run_tests $ns1 $ns2 10.0.1.1 0 0 fullmesh_1 slow - chk_join_nr "fullmesh test 1x1" 3 3 3 - chk_add_nr 1 1 + if reset "fullmesh test 1x1"; then + pm_nl_set_limits $ns1 1 3 + pm_nl_set_limits $ns2 1 3 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + run_tests $ns1 $ns2 10.0.1.1 0 0 fullmesh_1 slow + chk_join_nr 3 3 3 + chk_add_nr 1 1 + fi # fullmesh 3 # 1 non-fullmesh addr in ns1, added before the connection, # 2 fullmesh addrs in ns2, added during the connection. - reset - ip netns exec $ns1 ./pm_nl_ctl limits 2 5 - ip netns exec $ns2 ./pm_nl_ctl limits 1 5 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - run_tests $ns1 $ns2 10.0.1.1 0 0 fullmesh_2 slow - chk_join_nr "fullmesh test 1x2" 5 5 5 - chk_add_nr 1 1 + if reset "fullmesh test 1x2"; then + pm_nl_set_limits $ns1 2 5 + pm_nl_set_limits $ns2 1 5 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + run_tests $ns1 $ns2 10.0.1.1 0 0 fullmesh_2 slow + chk_join_nr 5 5 5 + chk_add_nr 1 1 + fi # fullmesh 4 # 1 non-fullmesh addr in ns1, added before the connection, # 2 fullmesh addrs in ns2, added during the connection, # limit max_subflows to 4. - reset - ip netns exec $ns1 ./pm_nl_ctl limits 2 4 - ip netns exec $ns2 ./pm_nl_ctl limits 1 4 - ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal - run_tests $ns1 $ns2 10.0.1.1 0 0 fullmesh_2 slow - chk_join_nr "fullmesh test 1x2, limited" 4 4 4 - chk_add_nr 1 1 + if reset "fullmesh test 1x2, limited"; then + pm_nl_set_limits $ns1 2 4 + pm_nl_set_limits $ns2 1 4 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + run_tests $ns1 $ns2 10.0.1.1 0 0 fullmesh_2 slow + chk_join_nr 4 4 4 + chk_add_nr 1 1 + fi + + # set fullmesh flag + if reset "set fullmesh flag test"; then + pm_nl_set_limits $ns1 4 4 + pm_nl_add_endpoint $ns1 10.0.2.1 flags subflow + pm_nl_set_limits $ns2 4 4 + run_tests $ns1 $ns2 10.0.1.1 0 0 1 slow fullmesh + chk_join_nr 2 2 2 + chk_rm_nr 0 1 + fi + + # set nofullmesh flag + if reset "set nofullmesh flag test"; then + pm_nl_set_limits $ns1 4 4 + pm_nl_add_endpoint $ns1 10.0.2.1 flags subflow,fullmesh + pm_nl_set_limits $ns2 4 4 + run_tests $ns1 $ns2 10.0.1.1 0 0 fullmesh_1 slow nofullmesh + chk_join_nr 2 2 2 + chk_rm_nr 0 1 + fi + + # set backup,fullmesh flags + if reset "set backup,fullmesh flags test"; then + pm_nl_set_limits $ns1 4 4 + pm_nl_add_endpoint $ns1 10.0.2.1 flags subflow + pm_nl_set_limits $ns2 4 4 + run_tests $ns1 $ns2 10.0.1.1 0 0 1 slow backup,fullmesh + chk_join_nr 2 2 2 + chk_prio_nr 0 1 + chk_rm_nr 0 1 + fi + + # set nobackup,nofullmesh flags + if reset "set nobackup,nofullmesh flags test"; then + pm_nl_set_limits $ns1 4 4 + pm_nl_set_limits $ns2 4 4 + pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow,backup,fullmesh + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow nobackup,nofullmesh + chk_join_nr 2 2 2 + chk_prio_nr 0 1 + chk_rm_nr 0 1 + fi } -all_tests() +fastclose_tests() { - subflows_tests - subflows_error_tests - signal_address_tests - link_failure_tests - add_addr_timeout_tests - remove_tests - add_tests - ipv6_tests - v4mapped_tests - backup_tests - add_addr_ports_tests - syncookies_tests - checksum_tests - deny_join_id0_tests - fullmesh_tests + if reset "fastclose test"; then + run_tests $ns1 $ns2 10.0.1.1 1024 0 fastclose_2 + chk_join_nr 0 0 0 + chk_fclose_nr 1 1 + chk_rst_nr 1 1 invert + fi } +implicit_tests() +{ + # userspace pm type prevents add_addr + if reset "implicit EP"; then + pm_nl_set_limits $ns1 2 2 + pm_nl_set_limits $ns2 2 2 + pm_nl_add_endpoint $ns1 10.0.2.1 flags signal + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow & + + wait_mpj $ns1 + pm_nl_check_endpoint 1 "creation" \ + $ns2 10.0.2.2 id 1 flags implicit + + pm_nl_add_endpoint $ns2 10.0.2.2 id 33 + pm_nl_check_endpoint 0 "ID change is prevented" \ + $ns2 10.0.2.2 id 1 flags implicit + + pm_nl_add_endpoint $ns2 10.0.2.2 flags signal + pm_nl_check_endpoint 0 "modif is allowed" \ + $ns2 10.0.2.2 id 1 flags signal + wait + fi +} + +# [$1: error message] usage() { + if [ -n "${1}" ]; then + echo "${1}" + ret=1 + fi + echo "mptcp_join usage:" - echo " -f subflows_tests" - echo " -e subflows_error_tests" - echo " -s signal_address_tests" - echo " -l link_failure_tests" - echo " -t add_addr_timeout_tests" - echo " -r remove_tests" - echo " -a add_tests" - echo " -6 ipv6_tests" - echo " -4 v4mapped_tests" - echo " -b backup_tests" - echo " -p add_addr_ports_tests" - echo " -k syncookies_tests" - echo " -S checksum_tests" - echo " -d deny_join_id0_tests" - echo " -m fullmesh_tests" + + local key + for key in "${!all_tests[@]}"; do + echo " -${key} ${all_tests[${key}]}" + done + echo " -c capture pcap files" echo " -C enable data checksum" + echo " -i use ip mptcp" echo " -h help" + + echo "[test ids|names]" + + exit ${ret} } -sin=$(mktemp) -sout=$(mktemp) -cin=$(mktemp) -cinsent=$(mktemp) -cout=$(mktemp) -init -make_file "$cin" "client" 1 -make_file "$sin" "server" 1 -trap cleanup EXIT -for arg in "$@"; do - # check for "capture/checksum" args before launching tests - if [[ "${arg}" =~ ^"-"[0-9a-zA-Z]*"c"[0-9a-zA-Z]*$ ]]; then - capture=1 - fi - if [[ "${arg}" =~ ^"-"[0-9a-zA-Z]*"C"[0-9a-zA-Z]*$ ]]; then - checksum=1 - fi +# Use a "simple" array to force an specific order we cannot have with an associative one +all_tests_sorted=( + f@subflows_tests + e@subflows_error_tests + s@signal_address_tests + l@link_failure_tests + t@add_addr_timeout_tests + r@remove_tests + a@add_tests + 6@ipv6_tests + 4@v4mapped_tests + b@backup_tests + p@add_addr_ports_tests + k@syncookies_tests + S@checksum_tests + d@deny_join_id0_tests + m@fullmesh_tests + z@fastclose_tests + I@implicit_tests +) - # exception for the capture/checksum options, the rest means: a part of the tests - if [ "${arg}" != "-c" ] && [ "${arg}" != "-C" ]; then - do_all_tests=0 - fi +all_tests_args="" +all_tests_names=() +for subtests in "${all_tests_sorted[@]}"; do + key="${subtests%@*}" + value="${subtests#*@}" + + all_tests_args+="${key}" + all_tests_names+=("${value}") + all_tests[${key}]="${value}" done -if [ $do_all_tests -eq 1 ]; then - all_tests - exit $ret -fi - -while getopts 'fesltra64bpkdmchCS' opt; do +tests=() +while getopts "${all_tests_args}cCih" opt; do case $opt in - f) - subflows_tests - ;; - e) - subflows_error_tests - ;; - s) - signal_address_tests - ;; - l) - link_failure_tests - ;; - t) - add_addr_timeout_tests - ;; - r) - remove_tests - ;; - a) - add_tests - ;; - 6) - ipv6_tests - ;; - 4) - v4mapped_tests - ;; - b) - backup_tests - ;; - p) - add_addr_ports_tests - ;; - k) - syncookies_tests - ;; - S) - checksum_tests - ;; - d) - deny_join_id0_tests - ;; - m) - fullmesh_tests + ["${all_tests_args}"]) + tests+=("${all_tests[${opt}]}") ;; c) + capture=1 ;; C) + checksum=1 ;; - h | *) + i) + ip_mptcp=1 + ;; + h) usage ;; + *) + usage "Unknown option: -${opt}" + ;; esac done +shift $((OPTIND - 1)) + +for arg in "${@}"; do + if [[ "${arg}" =~ ^[0-9]+$ ]]; then + only_tests_ids+=("${arg}") + else + only_tests_names+=("${arg}") + fi +done + +if [ ${#tests[@]} -eq 0 ]; then + tests=("${all_tests_names[@]}") +fi + +for subtests in "${tests[@]}"; do + "${subtests}" +done + +if [ ${ret} -ne 0 ]; then + echo + echo "${#failed_tests[@]} failure(s) has(ve) been detected:" + for i in $(get_failed_tests_ids); do + echo -e "\t- ${i}: ${failed_tests[${i}]}" + done + echo +fi + exit $ret diff --git a/tools/testing/selftests/net/mptcp/pm_netlink.sh b/tools/testing/selftests/net/mptcp/pm_netlink.sh index cbacf9f6538b..89839d1ff9d8 100755 --- a/tools/testing/selftests/net/mptcp/pm_netlink.sh +++ b/tools/testing/selftests/net/mptcp/pm_netlink.sh @@ -164,4 +164,22 @@ id 253 flags 10.0.0.5 id 254 flags 10.0.0.2 id 255 flags 10.0.0.3" "wrap-around ids" +ip netns exec $ns1 ./pm_nl_ctl flush +ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.1 flags subflow +ip netns exec $ns1 ./pm_nl_ctl set 10.0.1.1 flags backup +check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ +subflow,backup 10.0.1.1" "set flags (backup)" +ip netns exec $ns1 ./pm_nl_ctl set 10.0.1.1 flags nobackup +check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ +subflow 10.0.1.1" " (nobackup)" +ip netns exec $ns1 ./pm_nl_ctl set id 1 flags fullmesh +check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ +subflow,fullmesh 10.0.1.1" " (fullmesh)" +ip netns exec $ns1 ./pm_nl_ctl set id 1 flags nofullmesh +check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ +subflow 10.0.1.1" " (nofullmesh)" +ip netns exec $ns1 ./pm_nl_ctl set id 1 flags backup,fullmesh +check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ +subflow,backup,fullmesh 10.0.1.1" " (backup,fullmesh)" + exit $ret diff --git a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c index 354784512748..a75a68ad652e 100644 --- a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c +++ b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c @@ -28,7 +28,7 @@ static void syntax(char *argv[]) fprintf(stderr, "\tadd [flags signal|subflow|backup|fullmesh] [id ] [dev ] \n"); fprintf(stderr, "\tdel []\n"); fprintf(stderr, "\tget \n"); - fprintf(stderr, "\tset [flags backup|nobackup]\n"); + fprintf(stderr, "\tset [] [id ] flags [no]backup|[no]fullmesh [port ]\n"); fprintf(stderr, "\tflush\n"); fprintf(stderr, "\tdump\n"); fprintf(stderr, "\tlimits [ ]\n"); @@ -436,6 +436,13 @@ static void print_addr(struct rtattr *attrs, int len) printf(","); } + if (flags & MPTCP_PM_ADDR_FLAG_IMPLICIT) { + printf("implicit"); + flags &= ~MPTCP_PM_ADDR_FLAG_IMPLICIT; + if (flags) + printf(","); + } + /* bump unknown flags, if any */ if (flags) printf("0x%x", flags); @@ -657,8 +664,10 @@ int set_flags(int fd, int pm_family, int argc, char *argv[]) u_int32_t flags = 0; u_int16_t family; int nest_start; + int use_id = 0; + u_int8_t id; int off = 0; - int arg; + int arg = 2; memset(data, 0, sizeof(data)); nh = (void *)data; @@ -674,29 +683,45 @@ int set_flags(int fd, int pm_family, int argc, char *argv[]) nest->rta_len = RTA_LENGTH(0); off += NLMSG_ALIGN(nest->rta_len); - /* addr data */ - rta = (void *)(data + off); - if (inet_pton(AF_INET, argv[2], RTA_DATA(rta))) { - family = AF_INET; - rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; - rta->rta_len = RTA_LENGTH(4); - } else if (inet_pton(AF_INET6, argv[2], RTA_DATA(rta))) { - family = AF_INET6; - rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; - rta->rta_len = RTA_LENGTH(16); + if (!strcmp(argv[arg], "id")) { + if (++arg >= argc) + error(1, 0, " missing id value"); + + use_id = 1; + id = atoi(argv[arg]); + rta = (void *)(data + off); + rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; + rta->rta_len = RTA_LENGTH(1); + memcpy(RTA_DATA(rta), &id, 1); + off += NLMSG_ALIGN(rta->rta_len); } else { - error(1, errno, "can't parse ip %s", argv[2]); + /* addr data */ + rta = (void *)(data + off); + if (inet_pton(AF_INET, argv[arg], RTA_DATA(rta))) { + family = AF_INET; + rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; + rta->rta_len = RTA_LENGTH(4); + } else if (inet_pton(AF_INET6, argv[arg], RTA_DATA(rta))) { + family = AF_INET6; + rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; + rta->rta_len = RTA_LENGTH(16); + } else { + error(1, errno, "can't parse ip %s", argv[arg]); + } + off += NLMSG_ALIGN(rta->rta_len); + + /* family */ + rta = (void *)(data + off); + rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; + rta->rta_len = RTA_LENGTH(2); + memcpy(RTA_DATA(rta), &family, 2); + off += NLMSG_ALIGN(rta->rta_len); } - off += NLMSG_ALIGN(rta->rta_len); - /* family */ - rta = (void *)(data + off); - rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; - rta->rta_len = RTA_LENGTH(2); - memcpy(RTA_DATA(rta), &family, 2); - off += NLMSG_ALIGN(rta->rta_len); + if (++arg >= argc) + error(1, 0, " missing flags keyword"); - for (arg = 3; arg < argc; arg++) { + for (; arg < argc; arg++) { if (!strcmp(argv[arg], "flags")) { char *tok, *str; @@ -704,12 +729,14 @@ int set_flags(int fd, int pm_family, int argc, char *argv[]) if (++arg >= argc) error(1, 0, " missing flags value"); - /* do not support flag list yet */ for (str = argv[arg]; (tok = strtok(str, ",")); str = NULL) { if (!strcmp(tok, "backup")) flags |= MPTCP_PM_ADDR_FLAG_BACKUP; - else if (strcmp(tok, "nobackup")) + else if (!strcmp(tok, "fullmesh")) + flags |= MPTCP_PM_ADDR_FLAG_FULLMESH; + else if (strcmp(tok, "nobackup") && + strcmp(tok, "nofullmesh")) error(1, errno, "unknown flag %s", argv[arg]); } @@ -719,6 +746,21 @@ int set_flags(int fd, int pm_family, int argc, char *argv[]) rta->rta_len = RTA_LENGTH(4); memcpy(RTA_DATA(rta), &flags, 4); off += NLMSG_ALIGN(rta->rta_len); + } else if (!strcmp(argv[arg], "port")) { + u_int16_t port; + + if (use_id) + error(1, 0, " port can't be used with id"); + + if (++arg >= argc) + error(1, 0, " missing port value"); + + port = atoi(argv[arg]); + rta = (void *)(data + off); + rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; + rta->rta_len = RTA_LENGTH(2); + memcpy(RTA_DATA(rta), &port, 2); + off += NLMSG_ALIGN(rta->rta_len); } else { error(1, 0, "unknown keyword %s", argv[arg]); } diff --git a/tools/testing/selftests/net/mptcp/settings b/tools/testing/selftests/net/mptcp/settings index a62d2fa1275c..79b65bdf05db 100644 --- a/tools/testing/selftests/net/mptcp/settings +++ b/tools/testing/selftests/net/mptcp/settings @@ -1 +1 @@ -timeout=600 +timeout=1200 diff --git a/tools/testing/selftests/net/pmtu.sh b/tools/testing/selftests/net/pmtu.sh index 694732e4b344..736e358dc549 100755 --- a/tools/testing/selftests/net/pmtu.sh +++ b/tools/testing/selftests/net/pmtu.sh @@ -26,6 +26,15 @@ # - pmtu_ipv6 # Same as pmtu_ipv4, except for locked PMTU tests, using IPv6 # +# - pmtu_ipv4_dscp_icmp_exception +# Set up the same network topology as pmtu_ipv4, but use non-default +# routing table in A. A fib-rule is used to jump to this routing table +# based on DSCP. Send ICMPv4 packets with the expected DSCP value and +# verify that ECN doesn't interfere with the creation of PMTU exceptions. +# +# - pmtu_ipv4_dscp_udp_exception +# Same as pmtu_ipv4_dscp_icmp_exception, but use UDP instead of ICMP. +# # - pmtu_ipv4_vxlan4_exception # Set up the same network topology as pmtu_ipv4, create a VXLAN tunnel # over IPv4 between A and B, routed via R1. On the link between R1 and B, @@ -203,6 +212,8 @@ which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping) tests=" pmtu_ipv4_exception ipv4: PMTU exceptions 1 pmtu_ipv6_exception ipv6: PMTU exceptions 1 + pmtu_ipv4_dscp_icmp_exception ICMPv4 with DSCP and ECN: PMTU exceptions 1 + pmtu_ipv4_dscp_udp_exception UDPv4 with DSCP and ECN: PMTU exceptions 1 pmtu_ipv4_vxlan4_exception IPv4 over vxlan4: PMTU exceptions 1 pmtu_ipv6_vxlan4_exception IPv6 over vxlan4: PMTU exceptions 1 pmtu_ipv4_vxlan6_exception IPv4 over vxlan6: PMTU exceptions 1 @@ -323,6 +334,9 @@ routes_nh=" B 6 default 61 " +policy_mark=0x04 +rt_table=main + veth4_a_addr="192.168.1.1" veth4_b_addr="192.168.1.2" veth4_c_addr="192.168.2.10" @@ -346,6 +360,7 @@ dummy6_mask="64" err_buf= tcpdump_pids= nettest_pids= +socat_pids= err() { err_buf="${err_buf}${1} @@ -723,7 +738,7 @@ setup_routing_old() { ns_name="$(nsname ${ns})" - ip -n ${ns_name} route add ${addr} via ${gw} + ip -n "${ns_name}" route add "${addr}" table "${rt_table}" via "${gw}" ns=""; addr=""; gw="" done @@ -753,7 +768,7 @@ setup_routing_new() { ns_name="$(nsname ${ns})" - ip -n ${ns_name} -${fam} route add ${addr} nhid ${nhid} + ip -n "${ns_name}" -"${fam}" route add "${addr}" table "${rt_table}" nhid "${nhid}" ns=""; fam=""; addr=""; nhid="" done @@ -798,6 +813,24 @@ setup_routing() { return 0 } +setup_policy_routing() { + setup_routing + + ip -netns "${NS_A}" -4 rule add dsfield "${policy_mark}" \ + table "${rt_table}" + + # Set the IPv4 Don't Fragment bit with tc, since socat doesn't seem to + # have an option do to it. + tc -netns "${NS_A}" qdisc replace dev veth_A-R1 root prio + tc -netns "${NS_A}" qdisc replace dev veth_A-R2 root prio + tc -netns "${NS_A}" filter add dev veth_A-R1 \ + protocol ipv4 flower ip_proto udp \ + action pedit ex munge ip df set 0x40 pipe csum ip and udp + tc -netns "${NS_A}" filter add dev veth_A-R2 \ + protocol ipv4 flower ip_proto udp \ + action pedit ex munge ip df set 0x40 pipe csum ip and udp +} + setup_bridge() { run_cmd ${ns_a} ip link add br0 type bridge || return $ksft_skip run_cmd ${ns_a} ip link set br0 up @@ -903,6 +936,11 @@ cleanup() { done nettest_pids= + for pid in ${socat_pids}; do + kill "${pid}" + done + socat_pids= + for n in ${NS_A} ${NS_B} ${NS_C} ${NS_R1} ${NS_R2}; do ip netns del ${n} 2> /dev/null done @@ -950,15 +988,21 @@ link_get_mtu() { route_get_dst_exception() { ns_cmd="${1}" dst="${2}" + dsfield="${3}" - ${ns_cmd} ip route get "${dst}" + if [ -z "${dsfield}" ]; then + dsfield=0 + fi + + ${ns_cmd} ip route get "${dst}" dsfield "${dsfield}" } route_get_dst_pmtu_from_exception() { ns_cmd="${1}" dst="${2}" + dsfield="${3}" - mtu_parse "$(route_get_dst_exception "${ns_cmd}" ${dst})" + mtu_parse "$(route_get_dst_exception "${ns_cmd}" "${dst}" "${dsfield}")" } check_pmtu_value() { @@ -1068,6 +1112,95 @@ test_pmtu_ipv6_exception() { test_pmtu_ipvX 6 } +test_pmtu_ipv4_dscp_icmp_exception() { + rt_table=100 + + setup namespaces policy_routing || return $ksft_skip + trace "${ns_a}" veth_A-R1 "${ns_r1}" veth_R1-A \ + "${ns_r1}" veth_R1-B "${ns_b}" veth_B-R1 \ + "${ns_a}" veth_A-R2 "${ns_r2}" veth_R2-A \ + "${ns_r2}" veth_R2-B "${ns_b}" veth_B-R2 + + # Set up initial MTU values + mtu "${ns_a}" veth_A-R1 2000 + mtu "${ns_r1}" veth_R1-A 2000 + mtu "${ns_r1}" veth_R1-B 1400 + mtu "${ns_b}" veth_B-R1 1400 + + mtu "${ns_a}" veth_A-R2 2000 + mtu "${ns_r2}" veth_R2-A 2000 + mtu "${ns_r2}" veth_R2-B 1500 + mtu "${ns_b}" veth_B-R2 1500 + + len=$((2000 - 20 - 8)) # Fills MTU of veth_A-R1 + + dst1="${prefix4}.${b_r1}.1" + dst2="${prefix4}.${b_r2}.1" + + # Create route exceptions + dsfield=${policy_mark} # No ECN bit set (Not-ECT) + run_cmd "${ns_a}" ping -q -M want -Q "${dsfield}" -c 1 -w 1 -s "${len}" "${dst1}" + + dsfield=$(printf "%#x" $((policy_mark + 0x02))) # ECN=2 (ECT(0)) + run_cmd "${ns_a}" ping -q -M want -Q "${dsfield}" -c 1 -w 1 -s "${len}" "${dst2}" + + # Check that exceptions have been created with the correct PMTU + pmtu_1="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst1}" "${policy_mark}")" + check_pmtu_value "1400" "${pmtu_1}" "exceeding MTU" || return 1 + + pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst2}" "${policy_mark}")" + check_pmtu_value "1500" "${pmtu_2}" "exceeding MTU" || return 1 +} + +test_pmtu_ipv4_dscp_udp_exception() { + rt_table=100 + + if ! which socat > /dev/null 2>&1; then + echo "'socat' command not found; skipping tests" + return $ksft_skip + fi + + setup namespaces policy_routing || return $ksft_skip + trace "${ns_a}" veth_A-R1 "${ns_r1}" veth_R1-A \ + "${ns_r1}" veth_R1-B "${ns_b}" veth_B-R1 \ + "${ns_a}" veth_A-R2 "${ns_r2}" veth_R2-A \ + "${ns_r2}" veth_R2-B "${ns_b}" veth_B-R2 + + # Set up initial MTU values + mtu "${ns_a}" veth_A-R1 2000 + mtu "${ns_r1}" veth_R1-A 2000 + mtu "${ns_r1}" veth_R1-B 1400 + mtu "${ns_b}" veth_B-R1 1400 + + mtu "${ns_a}" veth_A-R2 2000 + mtu "${ns_r2}" veth_R2-A 2000 + mtu "${ns_r2}" veth_R2-B 1500 + mtu "${ns_b}" veth_B-R2 1500 + + len=$((2000 - 20 - 8)) # Fills MTU of veth_A-R1 + + dst1="${prefix4}.${b_r1}.1" + dst2="${prefix4}.${b_r2}.1" + + # Create route exceptions + run_cmd_bg "${ns_b}" socat UDP-LISTEN:50000 OPEN:/dev/null,wronly=1 + socat_pids="${socat_pids} $!" + + dsfield=${policy_mark} # No ECN bit set (Not-ECT) + run_cmd "${ns_a}" socat OPEN:/dev/zero,rdonly=1,readbytes="${len}" \ + UDP:"${dst1}":50000,tos="${dsfield}" + + dsfield=$(printf "%#x" $((policy_mark + 0x02))) # ECN=2 (ECT(0)) + run_cmd "${ns_a}" socat OPEN:/dev/zero,rdonly=1,readbytes="${len}" \ + UDP:"${dst2}":50000,tos="${dsfield}" + + # Check that exceptions have been created with the correct PMTU + pmtu_1="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst1}" "${policy_mark}")" + check_pmtu_value "1400" "${pmtu_1}" "exceeding MTU" || return 1 + pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst2}" "${policy_mark}")" + check_pmtu_value "1500" "${pmtu_2}" "exceeding MTU" || return 1 +} + test_pmtu_ipvX_over_vxlanY_or_geneveY_exception() { type=${1} family=${2} diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c index 3653d6468c67..1a736f700be4 100644 --- a/tools/testing/selftests/net/psock_fanout.c +++ b/tools/testing/selftests/net/psock_fanout.c @@ -53,6 +53,7 @@ #include #include "psock_lib.h" +#include "../kselftest.h" #define RING_NUM_FRAMES 20 @@ -117,7 +118,7 @@ static void sock_fanout_set_cbpf(int fd) struct sock_fprog bpf_prog; bpf_prog.filter = bpf_filter; - bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); + bpf_prog.len = ARRAY_SIZE(bpf_filter); if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT_DATA, &bpf_prog, sizeof(bpf_prog))) { @@ -162,7 +163,7 @@ static void sock_fanout_set_ebpf(int fd) memset(&attr, 0, sizeof(attr)); attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER; attr.insns = (unsigned long) prog; - attr.insn_cnt = sizeof(prog) / sizeof(prog[0]); + attr.insn_cnt = ARRAY_SIZE(prog); attr.license = (unsigned long) "GPL"; attr.log_buf = (unsigned long) log_buf, attr.log_size = sizeof(log_buf), diff --git a/tools/testing/selftests/net/reuseport_bpf_numa.c b/tools/testing/selftests/net/reuseport_bpf_numa.c index b2eebf669b8c..c9ba36aa688e 100644 --- a/tools/testing/selftests/net/reuseport_bpf_numa.c +++ b/tools/testing/selftests/net/reuseport_bpf_numa.c @@ -86,7 +86,7 @@ static void attach_bpf(int fd) memset(&attr, 0, sizeof(attr)); attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER; - attr.insn_cnt = sizeof(prog) / sizeof(prog[0]); + attr.insn_cnt = ARRAY_SIZE(prog); attr.insns = (unsigned long) &prog; attr.license = (unsigned long) &bpf_license; attr.log_buf = (unsigned long) &bpf_log_buf; diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index c9ce3dfa42ee..0900c5438fbb 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -216,9 +216,9 @@ kci_test_route_get() check_err $? ip route get fe80::1 dev "$devdummy" > /dev/null check_err $? - ip route get 127.0.0.1 from 127.0.0.1 oif lo tos 0x1 mark 0x1 > /dev/null + ip route get 127.0.0.1 from 127.0.0.1 oif lo tos 0x10 mark 0x1 > /dev/null check_err $? - ip route get ::1 from ::1 iif lo oif lo tos 0x1 mark 0x1 > /dev/null + ip route get ::1 from ::1 iif lo oif lo tos 0x10 mark 0x1 > /dev/null check_err $? ip addr add dev "$devdummy" 10.23.7.11/24 check_err $? diff --git a/tools/testing/selftests/net/test_vxlan_vnifiltering.sh b/tools/testing/selftests/net/test_vxlan_vnifiltering.sh new file mode 100755 index 000000000000..704997ffc244 --- /dev/null +++ b/tools/testing/selftests/net/test_vxlan_vnifiltering.sh @@ -0,0 +1,579 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# This test is for checking the VXLAN vni filtering api and +# datapath. +# It simulates two hypervisors running two VMs each using four network +# six namespaces: two for the HVs, four for the VMs. Each VM is +# connected to a separate bridge. The VM's use overlapping vlans and +# hence the separate bridge domain. Each vxlan device is a collect +# metadata device with vni filtering and hence has the ability to +# terminate configured vni's only. + +# +--------------------------------+ +------------------------------------+ +# | vm-11 netns | | vm-21 netns | +# | | | | +# |+------------+ +-------------+ | |+-------------+ +----------------+ | +# ||veth-11.10 | |veth-11.20 | | ||veth-21.10 | | veth-21.20 | | +# ||10.0.10.11/24 |10.0.20.11/24| | ||10.0.10.21/24| | 10.0.20.21/24 | | +# |+------|-----+ +|------------+ | |+-----------|-+ +---|------------+ | +# | | | | | | | | +# | | | | | +------------+ | +# | +------------+ | | | veth-21 | | +# | | veth-11 | | | | | | +# | | | | | +-----|------+ | +# | +-----|------+ | | | | +# | | | | | | +# +------------|-------------------+ +---------------|--------------------+ +# +------------|-----------------------------------------|-------------------+ +# | +-----|------+ +-----|------+ | +# | |vethhv-11 | |vethhv-21 | | +# | +----|-------+ +-----|------+ | +# | +---|---+ +---|--+ | +# | | br1 | | br2 | | +# | +---|---+ +---|--+ | +# | +---|----+ +---|--+ | +# | | vxlan1| |vxlan2| | +# | +--|-----+ +--|---+ | +# | | | | +# | | +---------------------+ | | +# | | |veth0 | | | +# | +---------|172.16.0.1/24 -----------+ | +# | |2002:fee1::1/64 | | +# | hv-1 netns +--------|------------+ | +# +-----------------------------|--------------------------------------------+ +# | +# +-----------------------------|--------------------------------------------+ +# | hv-2 netns +--------|-------------+ | +# | | veth0 | | +# | +------| 172.16.0.2/24 |---+ | +# | | | 2002:fee1::2/64 | | | +# | | | | | | +# | | +----------------------+ | - | +# | | | | +# | +-|-------+ +--------|-+ | +# | | vxlan1 | | vxlan2 | | +# | +----|----+ +---|------+ | +# | +--|--+ +-|---+ | +# | | br1 | | br2 | | +# | +--|--+ +--|--+ | +# | +-----|-------+ +----|-------+ | +# | | vethhv-12 | |vethhv-22 | | +# | +------|------+ +-------|----+ | +# +-----------------|----------------------------|---------------------------+ +# | | +# +-----------------|-----------------+ +--------|---------------------------+ +# | +-------|---+ | | +--|---------+ | +# | | veth-12 | | | |veth-22 | | +# | +-|--------|+ | | +--|--------|+ | +# | | | | | | | | +# |+----------|--+ +---|-----------+ | |+-------|-----+ +|---------------+ | +# ||veth-12.10 | |veth-12.20 | | ||veth-22.10 | |veth-22.20 | | +# ||10.0.10.12/24| |10.0.20.12/24 | | ||10.0.10.22/24| |10.0.20.22/24 | | +# |+-------------+ +---------------+ | |+-------------+ +----------------+ | +# | | | | +# | | | | +# | vm-12 netns | |vm-22 netns | +# +-----------------------------------+ +------------------------------------+ +# +# +# This test tests the new vxlan vnifiltering api + +ret=0 +# Kselftest framework requirement - SKIP code is 4. +ksft_skip=4 + +# all tests in this script. Can be overridden with -t option +TESTS=" + vxlan_vnifilter_api + vxlan_vnifilter_datapath + vxlan_vnifilter_datapath_pervni + vxlan_vnifilter_datapath_mgroup + vxlan_vnifilter_datapath_mgroup_pervni + vxlan_vnifilter_metadata_and_traditional_mix +" +VERBOSE=0 +PAUSE_ON_FAIL=no +PAUSE=no + +which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping) + +log_test() +{ + local rc=$1 + local expected=$2 + local msg="$3" + + if [ ${rc} -eq ${expected} ]; then + printf " TEST: %-60s [ OK ]\n" "${msg}" + nsuccess=$((nsuccess+1)) + else + ret=1 + nfail=$((nfail+1)) + printf " TEST: %-60s [FAIL]\n" "${msg}" + if [ "${PAUSE_ON_FAIL}" = "yes" ]; then + echo + echo "hit enter to continue, 'q' to quit" + read a + [ "$a" = "q" ] && exit 1 + fi + fi + + if [ "${PAUSE}" = "yes" ]; then + echo + echo "hit enter to continue, 'q' to quit" + read a + [ "$a" = "q" ] && exit 1 + fi +} + +run_cmd() +{ + local cmd="$1" + local out + local stderr="2>/dev/null" + + if [ "$VERBOSE" = "1" ]; then + printf "COMMAND: $cmd\n" + stderr= + fi + + out=$(eval $cmd $stderr) + rc=$? + if [ "$VERBOSE" = "1" -a -n "$out" ]; then + echo " $out" + fi + + return $rc +} + +check_hv_connectivity() { + ip netns exec hv-1 ping -c 1 -W 1 $1 &>/dev/null + sleep 1 + ip netns exec hv-1 ping -c 1 -W 1 $2 &>/dev/null + + return $? +} + +check_vm_connectivity() { + run_cmd "ip netns exec vm-11 ping -c 1 -W 1 10.0.10.12" + log_test $? 0 "VM connectivity over $1 (ipv4 default rdst)" + + run_cmd "ip netns exec vm-21 ping -c 1 -W 1 10.0.10.22" + log_test $? 0 "VM connectivity over $1 (ipv6 default rdst)" +} + +cleanup() { + ip link del veth-hv-1 2>/dev/null || true + ip link del vethhv-11 vethhv-12 vethhv-21 vethhv-22 2>/dev/null || true + + for ns in hv-1 hv-2 vm-11 vm-21 vm-12 vm-22 vm-31 vm-32; do + ip netns del $ns 2>/dev/null || true + done +} + +trap cleanup EXIT + +setup-hv-networking() { + hv=$1 + local1=$2 + mask1=$3 + local2=$4 + mask2=$5 + + ip netns add hv-$hv + ip link set veth-hv-$hv netns hv-$hv + ip -netns hv-$hv link set veth-hv-$hv name veth0 + ip -netns hv-$hv addr add $local1/$mask1 dev veth0 + ip -netns hv-$hv addr add $local2/$mask2 dev veth0 + ip -netns hv-$hv link set veth0 up +} + +# Setups a "VM" simulated by a netns an a veth pair +# example: setup-vm +# VATTRS = comma separated "-----" +# VTYPE = vxlan device type. "default = traditional device, metadata = metadata device +# vnifilter = vnifiltering device, +# vnifilterg = vnifiltering device with per vni group/remote" +# example: +# setup-vm 1 11 1 \ +# 10-v4-172.16.0.1-239.1.1.100-vnifilterg,20-v4-172.16.0.1-239.1.1.100-vnifilterg 1 +# +setup-vm() { + hvid=$1 + vmid=$2 + brid=$3 + vattrs=$4 + mcast=$5 + lastvxlandev="" + + # create bridge + ip -netns hv-$hvid link add br$brid type bridge vlan_filtering 1 vlan_default_pvid 0 \ + mcast_snooping 0 + ip -netns hv-$hvid link set br$brid up + + # create vm namespace and interfaces and connect to hypervisor + # namespace + ip netns add vm-$vmid + hvvethif="vethhv-$vmid" + vmvethif="veth-$vmid" + ip link add $hvvethif type veth peer name $vmvethif + ip link set $hvvethif netns hv-$hvid + ip link set $vmvethif netns vm-$vmid + ip -netns hv-$hvid link set $hvvethif up + ip -netns vm-$vmid link set $vmvethif up + ip -netns hv-$hvid link set $hvvethif master br$brid + + # configure VM vlan/vni filtering on hypervisor + for vmap in $(echo $vattrs | cut -d "," -f1- --output-delimiter=' ') + do + local vid=$(echo $vmap | awk -F'-' '{print ($1)}') + local family=$(echo $vmap | awk -F'-' '{print ($2)}') + local localip=$(echo $vmap | awk -F'-' '{print ($3)}') + local group=$(echo $vmap | awk -F'-' '{print ($4)}') + local vtype=$(echo $vmap | awk -F'-' '{print ($5)}') + local port=$(echo $vmap | awk -F'-' '{print ($6)}') + + ip -netns vm-$vmid link add name $vmvethif.$vid link $vmvethif type vlan id $vid + ip -netns vm-$vmid addr add 10.0.$vid.$vmid/24 dev $vmvethif.$vid + ip -netns vm-$vmid link set $vmvethif.$vid up + + tid=$vid + vxlandev="vxlan$brid" + vxlandevflags="" + + if [[ -n $vtype && $vtype == "metadata" ]]; then + vxlandevflags="$vxlandevflags external" + elif [[ -n $vtype && $vtype == "vnifilter" || $vtype == "vnifilterg" ]]; then + vxlandevflags="$vxlandevflags external vnifilter" + tid=$((vid+brid)) + else + vxlandevflags="$vxlandevflags id $tid" + vxlandev="vxlan$tid" + fi + + if [[ -n $vtype && $vtype != "vnifilterg" ]]; then + if [[ -n "$group" && "$group" != "null" ]]; then + if [ $mcast -eq 1 ]; then + vxlandevflags="$vxlandevflags group $group" + else + vxlandevflags="$vxlandevflags remote $group" + fi + fi + fi + + if [[ -n "$port" && "$port" != "default" ]]; then + vxlandevflags="$vxlandevflags dstport $port" + fi + + # create vxlan device + if [ "$vxlandev" != "$lastvxlandev" ]; then + ip -netns hv-$hvid link add $vxlandev type vxlan local $localip $vxlandevflags dev veth0 2>/dev/null + ip -netns hv-$hvid link set $vxlandev master br$brid + ip -netns hv-$hvid link set $vxlandev up + lastvxlandev=$vxlandev + fi + + # add vlan + bridge -netns hv-$hvid vlan add vid $vid dev $hvvethif + bridge -netns hv-$hvid vlan add vid $vid pvid dev $vxlandev + + # Add bridge vni filter for tx + if [[ -n $vtype && $vtype == "metadata" || $vtype == "vnifilter" || $vtype == "vnifilterg" ]]; then + bridge -netns hv-$hvid link set dev $vxlandev vlan_tunnel on + bridge -netns hv-$hvid vlan add dev $vxlandev vid $vid tunnel_info id $tid + fi + + if [[ -n $vtype && $vtype == "metadata" ]]; then + bridge -netns hv-$hvid fdb add 00:00:00:00:00:00 dev $vxlandev \ + src_vni $tid vni $tid dst $group self + elif [[ -n $vtype && $vtype == "vnifilter" ]]; then + # Add per vni rx filter with 'bridge vni' api + bridge -netns hv-$hvid vni add dev $vxlandev vni $tid + elif [[ -n $vtype && $vtype == "vnifilterg" ]]; then + # Add per vni group config with 'bridge vni' api + if [ -n "$group" ]; then + if [ "$family" == "v4" ]; then + if [ $mcast -eq 1 ]; then + bridge -netns hv-$hvid vni add dev $vxlandev vni $tid group $group + else + bridge -netns hv-$hvid vni add dev $vxlandev vni $tid remote $group + fi + else + if [ $mcast -eq 1 ]; then + bridge -netns hv-$hvid vni add dev $vxlandev vni $tid group6 $group + else + bridge -netns hv-$hvid vni add dev $vxlandev vni $tid remote6 $group + fi + fi + fi + fi + done +} + +setup_vnifilter_api() +{ + ip link add veth-host type veth peer name veth-testns + ip netns add testns + ip link set veth-testns netns testns +} + +cleanup_vnifilter_api() +{ + ip link del veth-host 2>/dev/null || true + ip netns del testns 2>/dev/null || true +} + +# tests vxlan filtering api +vxlan_vnifilter_api() +{ + hv1addr1="172.16.0.1" + hv2addr1="172.16.0.2" + hv1addr2="2002:fee1::1" + hv2addr2="2002:fee1::2" + localip="172.16.0.1" + group="239.1.1.101" + + cleanup_vnifilter_api &>/dev/null + setup_vnifilter_api + + # Duplicate vni test + # create non-vnifiltering traditional vni device + run_cmd "ip -netns testns link add vxlan100 type vxlan id 100 local $localip dev veth-testns dstport 4789" + log_test $? 0 "Create traditional vxlan device" + + # create vni filtering device + run_cmd "ip -netns testns link add vxlan-ext1 type vxlan vnifilter local $localip dev veth-testns dstport 4789" + log_test $? 1 "Cannot create vnifilter device without external flag" + + run_cmd "ip -netns testns link add vxlan-ext1 type vxlan external vnifilter local $localip dev veth-testns dstport 4789" + log_test $? 0 "Creating external vxlan device with vnifilter flag" + + run_cmd "bridge -netns testns vni add dev vxlan-ext1 vni 100" + log_test $? 0 "Cannot set in-use vni id on vnifiltering device" + + run_cmd "bridge -netns testns vni add dev vxlan-ext1 vni 200" + log_test $? 0 "Set new vni id on vnifiltering device" + + run_cmd "ip -netns testns link add vxlan-ext2 type vxlan external vnifilter local $localip dev veth-testns dstport 4789" + log_test $? 0 "Create second external vxlan device with vnifilter flag" + + run_cmd "bridge -netns testns vni add dev vxlan-ext2 vni 200" + log_test $? 255 "Cannot set in-use vni id on vnifiltering device" + + run_cmd "bridge -netns testns vni add dev vxlan-ext2 vni 300" + log_test $? 0 "Set new vni id on vnifiltering device" + + # check in bridge vni show + run_cmd "bridge -netns testns vni add dev vxlan-ext2 vni 300" + log_test $? 0 "Update vni id on vnifiltering device" + + run_cmd "bridge -netns testns vni add dev vxlan-ext2 vni 400" + log_test $? 0 "Add new vni id on vnifiltering device" + + # add multicast group per vni + run_cmd "bridge -netns testns vni add dev vxlan-ext1 vni 200 group $group" + log_test $? 0 "Set multicast group on existing vni" + + # add multicast group per vni + run_cmd "bridge -netns testns vni add dev vxlan-ext2 vni 300 group $group" + log_test $? 0 "Set multicast group on existing vni" + + # set vnifilter on an existing external vxlan device + run_cmd "ip -netns testns link set dev vxlan-ext1 type vxlan external vnifilter" + log_test $? 2 "Cannot set vnifilter flag on a device" + + # change vxlan vnifilter flag + run_cmd "ip -netns testns link set dev vxlan-ext1 type vxlan external novnifilter" + log_test $? 2 "Cannot unset vnifilter flag on a device" +} + +# Sanity test vnifilter datapath +# vnifilter vnis inherit BUM group from +# vxlan device +vxlan_vnifilter_datapath() +{ + hv1addr1="172.16.0.1" + hv2addr1="172.16.0.2" + hv1addr2="2002:fee1::1" + hv2addr2="2002:fee1::2" + + ip link add veth-hv-1 type veth peer name veth-hv-2 + setup-hv-networking 1 $hv1addr1 24 $hv1addr2 64 $hv2addr1 $hv2addr2 + setup-hv-networking 2 $hv2addr1 24 $hv2addr2 64 $hv1addr1 $hv1addr2 + + check_hv_connectivity hv2addr1 hv2addr2 + + setup-vm 1 11 1 10-v4-$hv1addr1-$hv2addr1-vnifilter,20-v4-$hv1addr1-$hv2addr1-vnifilter 0 + setup-vm 1 21 2 10-v6-$hv1addr2-$hv2addr2-vnifilter,20-v6-$hv1addr2-$hv2addr2-vnifilter 0 + + setup-vm 2 12 1 10-v4-$hv2addr1-$hv1addr1-vnifilter,20-v4-$hv2addr1-$hv1addr1-vnifilter 0 + setup-vm 2 22 2 10-v6-$hv2addr2-$hv1addr2-vnifilter,20-v6-$hv2addr2-$hv1addr2-vnifilter 0 + + check_vm_connectivity "vnifiltering vxlan" +} + +# Sanity test vnifilter datapath +# with vnifilter per vni configured BUM +# group/remote +vxlan_vnifilter_datapath_pervni() +{ + hv1addr1="172.16.0.1" + hv2addr1="172.16.0.2" + hv1addr2="2002:fee1::1" + hv2addr2="2002:fee1::2" + + ip link add veth-hv-1 type veth peer name veth-hv-2 + setup-hv-networking 1 $hv1addr1 24 $hv1addr2 64 + setup-hv-networking 2 $hv2addr1 24 $hv2addr2 64 + + check_hv_connectivity hv2addr1 hv2addr2 + + setup-vm 1 11 1 10-v4-$hv1addr1-$hv2addr1-vnifilterg,20-v4-$hv1addr1-$hv2addr1-vnifilterg 0 + setup-vm 1 21 2 10-v6-$hv1addr2-$hv2addr2-vnifilterg,20-v6-$hv1addr2-$hv2addr2-vnifilterg 0 + + setup-vm 2 12 1 10-v4-$hv2addr1-$hv1addr1-vnifilterg,20-v4-$hv2addr1-$hv1addr1-vnifilterg 0 + setup-vm 2 22 2 10-v6-$hv2addr2-$hv1addr2-vnifilterg,20-v6-$hv2addr2-$hv1addr2-vnifilterg 0 + + check_vm_connectivity "vnifiltering vxlan pervni remote" +} + + +vxlan_vnifilter_datapath_mgroup() +{ + hv1addr1="172.16.0.1" + hv2addr1="172.16.0.2" + hv1addr2="2002:fee1::1" + hv2addr2="2002:fee1::2" + group="239.1.1.100" + group6="ff07::1" + + ip link add veth-hv-1 type veth peer name veth-hv-2 + setup-hv-networking 1 $hv1addr1 24 $hv1addr2 64 + setup-hv-networking 2 $hv2addr1 24 $hv2addr2 64 + + check_hv_connectivity hv2addr1 hv2addr2 + + setup-vm 1 11 1 10-v4-$hv1addr1-$group-vnifilter,20-v4-$hv1addr1-$group-vnifilter 1 + setup-vm 1 21 2 "10-v6-$hv1addr2-$group6-vnifilter,20-v6-$hv1addr2-$group6-vnifilter" 1 + + setup-vm 2 12 1 10-v4-$hv2addr1-$group-vnifilter,20-v4-$hv2addr1-$group-vnifilter 1 + setup-vm 2 22 2 10-v6-$hv2addr2-$group6-vnifilter,20-v6-$hv2addr2-$group6-vnifilter 1 + + check_vm_connectivity "vnifiltering vxlan mgroup" +} + +vxlan_vnifilter_datapath_mgroup_pervni() +{ + hv1addr1="172.16.0.1" + hv2addr1="172.16.0.2" + hv1addr2="2002:fee1::1" + hv2addr2="2002:fee1::2" + group="239.1.1.100" + group6="ff07::1" + + ip link add veth-hv-1 type veth peer name veth-hv-2 + setup-hv-networking 1 $hv1addr1 24 $hv1addr2 64 + setup-hv-networking 2 $hv2addr1 24 $hv2addr2 64 + + check_hv_connectivity hv2addr1 hv2addr2 + + setup-vm 1 11 1 10-v4-$hv1addr1-$group-vnifilterg,20-v4-$hv1addr1-$group-vnifilterg 1 + setup-vm 1 21 2 10-v6-$hv1addr2-$group6-vnifilterg,20-v6-$hv1addr2-$group6-vnifilterg 1 + + setup-vm 2 12 1 10-v4-$hv2addr1-$group-vnifilterg,20-v4-$hv2addr1-$group-vnifilterg 1 + setup-vm 2 22 2 10-v6-$hv2addr2-$group6-vnifilterg,20-v6-$hv2addr2-$group6-vnifilterg 1 + + check_vm_connectivity "vnifiltering vxlan pervni mgroup" +} + +vxlan_vnifilter_metadata_and_traditional_mix() +{ + hv1addr1="172.16.0.1" + hv2addr1="172.16.0.2" + hv1addr2="2002:fee1::1" + hv2addr2="2002:fee1::2" + + ip link add veth-hv-1 type veth peer name veth-hv-2 + setup-hv-networking 1 $hv1addr1 24 $hv1addr2 64 + setup-hv-networking 2 $hv2addr1 24 $hv2addr2 64 + + check_hv_connectivity hv2addr1 hv2addr2 + + setup-vm 1 11 1 10-v4-$hv1addr1-$hv2addr1-vnifilter,20-v4-$hv1addr1-$hv2addr1-vnifilter 0 + setup-vm 1 21 2 10-v6-$hv1addr2-$hv2addr2-vnifilter,20-v6-$hv1addr2-$hv2addr2-vnifilter 0 + setup-vm 1 31 3 30-v4-$hv1addr1-$hv2addr1-default-4790,40-v6-$hv1addr2-$hv2addr2-default-4790,50-v4-$hv1addr1-$hv2addr1-metadata-4791 0 + + + setup-vm 2 12 1 10-v4-$hv2addr1-$hv1addr1-vnifilter,20-v4-$hv2addr1-$hv1addr1-vnifilter 0 + setup-vm 2 22 2 10-v6-$hv2addr2-$hv1addr2-vnifilter,20-v6-$hv2addr2-$hv1addr2-vnifilter 0 + setup-vm 2 32 3 30-v4-$hv2addr1-$hv1addr1-default-4790,40-v6-$hv2addr2-$hv1addr2-default-4790,50-v4-$hv2addr1-$hv1addr1-metadata-4791 0 + + check_vm_connectivity "vnifiltering vxlan pervni remote mix" + + # check VM connectivity over traditional/non-vxlan filtering vxlan devices + run_cmd "ip netns exec vm-31 ping -c 1 -W 1 10.0.30.32" + log_test $? 0 "VM connectivity over traditional vxlan (ipv4 default rdst)" + + run_cmd "ip netns exec vm-31 ping -c 1 -W 1 10.0.40.32" + log_test $? 0 "VM connectivity over traditional vxlan (ipv6 default rdst)" + + run_cmd "ip netns exec vm-31 ping -c 1 -W 1 10.0.50.32" + log_test $? 0 "VM connectivity over metadata nonfiltering vxlan (ipv4 default rdst)" +} + +while getopts :t:pP46hv o +do + case $o in + t) TESTS=$OPTARG;; + p) PAUSE_ON_FAIL=yes;; + P) PAUSE=yes;; + v) VERBOSE=$(($VERBOSE + 1));; + h) usage; exit 0;; + *) usage; exit 1;; + esac +done + +# make sure we don't pause twice +[ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no + +if [ "$(id -u)" -ne 0 ];then + echo "SKIP: Need root privileges" + exit $ksft_skip; +fi + +if [ ! -x "$(command -v ip)" ]; then + echo "SKIP: Could not run test without ip tool" + exit $ksft_skip +fi + +ip link help vxlan 2>&1 | grep -q "vnifilter" +if [ $? -ne 0 ]; then + echo "SKIP: iproute2 too old, missing vxlan dev vnifilter setting" + sync + exit $ksft_skip +fi + +bridge vni help 2>&1 | grep -q "Usage: bridge vni" +if [ $? -ne 0 ]; then + echo "SKIP: iproute2 bridge lacks vxlan vnifiltering support" + exit $ksft_skip +fi + +# start clean +cleanup &> /dev/null + +for t in $TESTS +do + case $t in + none) setup; exit 0;; + *) $t; cleanup;; + esac +done + +if [ "$TESTS" != "none" ]; then + printf "\nTests passed: %3d\n" ${nsuccess} + printf "Tests failed: %3d\n" ${nfail} +fi + +exit $ret diff --git a/tools/testing/selftests/net/timestamping.c b/tools/testing/selftests/net/timestamping.c index aee631c5284e..044bc0e9ed81 100644 --- a/tools/testing/selftests/net/timestamping.c +++ b/tools/testing/selftests/net/timestamping.c @@ -325,8 +325,8 @@ int main(int argc, char **argv) struct ifreq device; struct ifreq hwtstamp; struct hwtstamp_config hwconfig, hwconfig_requested; - struct so_timestamping so_timestamping_get = { 0, -1 }; - struct so_timestamping so_timestamping = { 0, -1 }; + struct so_timestamping so_timestamping_get = { 0, 0 }; + struct so_timestamping so_timestamping = { 0, 0 }; struct sockaddr_in addr; struct ip_mreq imr; struct in_addr iaddr; diff --git a/tools/testing/selftests/net/toeplitz.c b/tools/testing/selftests/net/toeplitz.c index c5489341cfb8..90026a27eac0 100644 --- a/tools/testing/selftests/net/toeplitz.c +++ b/tools/testing/selftests/net/toeplitz.c @@ -52,6 +52,8 @@ #include #include +#include "../kselftest.h" + #define TOEPLITZ_KEY_MIN_LEN 40 #define TOEPLITZ_KEY_MAX_LEN 60 @@ -295,7 +297,7 @@ static void __set_filter(int fd, int off_proto, uint8_t proto, int off_dport) struct sock_fprog prog = {}; prog.filter = filter; - prog.len = sizeof(filter) / sizeof(struct sock_filter); + prog.len = ARRAY_SIZE(filter); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog))) error(1, errno, "setsockopt filter"); } @@ -324,7 +326,7 @@ static void set_filter_null(int fd) struct sock_fprog prog = {}; prog.filter = filter; - prog.len = sizeof(filter) / sizeof(struct sock_filter); + prog.len = ARRAY_SIZE(filter); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog))) error(1, errno, "setsockopt filter"); } diff --git a/tools/testing/selftests/net/txtimestamp.c b/tools/testing/selftests/net/txtimestamp.c index fabb1d555ee5..10f2fde3686b 100644 --- a/tools/testing/selftests/net/txtimestamp.c +++ b/tools/testing/selftests/net/txtimestamp.c @@ -161,7 +161,7 @@ static void validate_timestamp(struct timespec *cur, int min_delay) max_delay = min_delay + cfg_delay_tolerance_usec; if (cur64 < start64 + min_delay || cur64 > start64 + max_delay) { - fprintf(stderr, "ERROR: %lu us expected between %d and %d\n", + fprintf(stderr, "ERROR: %" PRId64 " us expected between %d and %d\n", cur64 - start64, min_delay, max_delay); test_failed = true; } @@ -170,9 +170,9 @@ static void validate_timestamp(struct timespec *cur, int min_delay) static void __print_ts_delta_formatted(int64_t ts_delta) { if (cfg_print_nsec) - fprintf(stderr, "%lu ns", ts_delta); + fprintf(stderr, "%" PRId64 " ns", ts_delta); else - fprintf(stderr, "%lu us", ts_delta / NSEC_PER_USEC); + fprintf(stderr, "%" PRId64 " us", ts_delta / NSEC_PER_USEC); } static void __print_timestamp(const char *name, struct timespec *cur, diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c index c0f6a062364d..198ad5f32187 100644 --- a/tools/testing/selftests/ptp/testptp.c +++ b/tools/testing/selftests/ptp/testptp.c @@ -133,6 +133,7 @@ static void usage(char *progname) " 0 - none\n" " 1 - external time stamp\n" " 2 - periodic output\n" + " -n val shift the ptp clock time by 'val' nanoseconds\n" " -p val enable output with a period of 'val' nanoseconds\n" " -H val set output phase to 'val' nanoseconds (requires -p)\n" " -w val set output pulse width to 'val' nanoseconds (requires -p)\n" @@ -165,6 +166,7 @@ int main(int argc, char *argv[]) clockid_t clkid; int adjfreq = 0x7fffffff; int adjtime = 0; + int adjns = 0; int capabilities = 0; int extts = 0; int flagtest = 0; @@ -186,7 +188,7 @@ int main(int argc, char *argv[]) progname = strrchr(argv[0], '/'); progname = progname ? 1+progname : argv[0]; - while (EOF != (c = getopt(argc, argv, "cd:e:f:ghH:i:k:lL:p:P:sSt:T:w:z"))) { + while (EOF != (c = getopt(argc, argv, "cd:e:f:ghH:i:k:lL:n:p:P:sSt:T:w:z"))) { switch (c) { case 'c': capabilities = 1; @@ -223,6 +225,9 @@ int main(int argc, char *argv[]) return -1; } break; + case 'n': + adjns = atoi(optarg); + break; case 'p': perout = atoll(optarg); break; @@ -305,11 +310,16 @@ int main(int argc, char *argv[]) } } - if (adjtime) { + if (adjtime || adjns) { memset(&tx, 0, sizeof(tx)); - tx.modes = ADJ_SETOFFSET; + tx.modes = ADJ_SETOFFSET | ADJ_NANO; tx.time.tv_sec = adjtime; - tx.time.tv_usec = 0; + tx.time.tv_usec = adjns; + while (tx.time.tv_usec < 0) { + tx.time.tv_sec -= 1; + tx.time.tv_usec += 1000000000; + } + if (clock_adjtime(clkid, &tx) < 0) { perror("clock_adjtime"); } else { diff --git a/tools/testing/selftests/tc-testing/tdc_config.py b/tools/testing/selftests/tc-testing/tdc_config.py index ea04f04c173e..ccb0f06ef9e3 100644 --- a/tools/testing/selftests/tc-testing/tdc_config.py +++ b/tools/testing/selftests/tc-testing/tdc_config.py @@ -21,7 +21,7 @@ NAMES = { 'BATCH_FILE': './batch.txt', 'BATCH_DIR': 'tmp', # Length of time in seconds to wait before terminating a command - 'TIMEOUT': 12, + 'TIMEOUT': 24, # Name of the namespace to use 'NS': 'tcut', # Directory containing eBPF test programs diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index 2a3638c0a008..dc577461afc2 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "timeout.h" #include "control.h" @@ -391,6 +393,209 @@ static void test_seqpacket_msg_trunc_server(const struct test_opts *opts) close(fd); } +static time_t current_nsec(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_REALTIME, &ts)) { + perror("clock_gettime(3) failed"); + exit(EXIT_FAILURE); + } + + return (ts.tv_sec * 1000000000ULL) + ts.tv_nsec; +} + +#define RCVTIMEO_TIMEOUT_SEC 1 +#define READ_OVERHEAD_NSEC 250000000 /* 0.25 sec */ + +static void test_seqpacket_timeout_client(const struct test_opts *opts) +{ + int fd; + struct timeval tv; + char dummy; + time_t read_enter_ns; + time_t read_overhead_ns; + + fd = vsock_seqpacket_connect(opts->peer_cid, 1234); + if (fd < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + tv.tv_sec = RCVTIMEO_TIMEOUT_SEC; + tv.tv_usec = 0; + + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv)) == -1) { + perror("setsockopt 'SO_RCVTIMEO'"); + exit(EXIT_FAILURE); + } + + read_enter_ns = current_nsec(); + + if (read(fd, &dummy, sizeof(dummy)) != -1) { + fprintf(stderr, + "expected 'dummy' read(2) failure\n"); + exit(EXIT_FAILURE); + } + + if (errno != EAGAIN) { + perror("EAGAIN expected"); + exit(EXIT_FAILURE); + } + + read_overhead_ns = current_nsec() - read_enter_ns - + 1000000000ULL * RCVTIMEO_TIMEOUT_SEC; + + if (read_overhead_ns > READ_OVERHEAD_NSEC) { + fprintf(stderr, + "too much time in read(2), %lu > %i ns\n", + read_overhead_ns, READ_OVERHEAD_NSEC); + exit(EXIT_FAILURE); + } + + control_writeln("WAITDONE"); + close(fd); +} + +static void test_seqpacket_timeout_server(const struct test_opts *opts) +{ + int fd; + + fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL); + if (fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + control_expectln("WAITDONE"); + close(fd); +} + +#define BUF_PATTERN_1 'a' +#define BUF_PATTERN_2 'b' + +static void test_seqpacket_invalid_rec_buffer_client(const struct test_opts *opts) +{ + int fd; + unsigned char *buf1; + unsigned char *buf2; + int buf_size = getpagesize() * 3; + + fd = vsock_seqpacket_connect(opts->peer_cid, 1234); + if (fd < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + buf1 = malloc(buf_size); + if (!buf1) { + perror("'malloc()' for 'buf1'"); + exit(EXIT_FAILURE); + } + + buf2 = malloc(buf_size); + if (!buf2) { + perror("'malloc()' for 'buf2'"); + exit(EXIT_FAILURE); + } + + memset(buf1, BUF_PATTERN_1, buf_size); + memset(buf2, BUF_PATTERN_2, buf_size); + + if (send(fd, buf1, buf_size, 0) != buf_size) { + perror("send failed"); + exit(EXIT_FAILURE); + } + + if (send(fd, buf2, buf_size, 0) != buf_size) { + perror("send failed"); + exit(EXIT_FAILURE); + } + + close(fd); +} + +static void test_seqpacket_invalid_rec_buffer_server(const struct test_opts *opts) +{ + int fd; + unsigned char *broken_buf; + unsigned char *valid_buf; + int page_size = getpagesize(); + int buf_size = page_size * 3; + ssize_t res; + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + int i; + + fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL); + if (fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + /* Setup first buffer. */ + broken_buf = mmap(NULL, buf_size, prot, flags, -1, 0); + if (broken_buf == MAP_FAILED) { + perror("mmap for 'broken_buf'"); + exit(EXIT_FAILURE); + } + + /* Unmap "hole" in buffer. */ + if (munmap(broken_buf + page_size, page_size)) { + perror("'broken_buf' setup"); + exit(EXIT_FAILURE); + } + + valid_buf = mmap(NULL, buf_size, prot, flags, -1, 0); + if (valid_buf == MAP_FAILED) { + perror("mmap for 'valid_buf'"); + exit(EXIT_FAILURE); + } + + /* Try to fill buffer with unmapped middle. */ + res = read(fd, broken_buf, buf_size); + if (res != -1) { + fprintf(stderr, + "expected 'broken_buf' read(2) failure, got %zi\n", + res); + exit(EXIT_FAILURE); + } + + if (errno != ENOMEM) { + perror("unexpected errno of 'broken_buf'"); + exit(EXIT_FAILURE); + } + + /* Try to fill valid buffer. */ + res = read(fd, valid_buf, buf_size); + if (res < 0) { + perror("unexpected 'valid_buf' read(2) failure"); + exit(EXIT_FAILURE); + } + + if (res != buf_size) { + fprintf(stderr, + "invalid 'valid_buf' read(2), expected %i, got %zi\n", + buf_size, res); + exit(EXIT_FAILURE); + } + + for (i = 0; i < buf_size; i++) { + if (valid_buf[i] != BUF_PATTERN_2) { + fprintf(stderr, + "invalid pattern for 'valid_buf' at %i, expected %hhX, got %hhX\n", + i, BUF_PATTERN_2, valid_buf[i]); + exit(EXIT_FAILURE); + } + } + + /* Unmap buffers. */ + munmap(broken_buf, page_size); + munmap(broken_buf + page_size * 2, page_size); + munmap(valid_buf, buf_size); + close(fd); +} + static struct test_case test_cases[] = { { .name = "SOCK_STREAM connection reset", @@ -431,6 +636,16 @@ static struct test_case test_cases[] = { .run_client = test_seqpacket_msg_trunc_client, .run_server = test_seqpacket_msg_trunc_server, }, + { + .name = "SOCK_SEQPACKET timeout", + .run_client = test_seqpacket_timeout_client, + .run_server = test_seqpacket_timeout_server, + }, + { + .name = "SOCK_SEQPACKET invalid receive buffer", + .run_client = test_seqpacket_invalid_rec_buffer_client, + .run_server = test_seqpacket_invalid_rec_buffer_server, + }, {}, };